@liendev/lien 0.11.0 → 0.12.0
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/CURSOR_RULES_TEMPLATE.md +67 -0
- package/dist/index.js +300 -212
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/CURSOR_RULES_TEMPLATE.md
CHANGED
|
@@ -114,6 +114,73 @@ list_functions({
|
|
|
114
114
|
|
|
115
115
|
---
|
|
116
116
|
|
|
117
|
+
## Input Validation & Error Handling
|
|
118
|
+
|
|
119
|
+
Lien uses Zod schemas for runtime type-safe validation of all tool inputs. This provides:
|
|
120
|
+
- **Automatic validation** of all parameters before tool execution
|
|
121
|
+
- **Rich error messages** with field-level feedback
|
|
122
|
+
- **Type safety** with full TypeScript inference
|
|
123
|
+
- **Consistent error structure** across all tools
|
|
124
|
+
|
|
125
|
+
### Understanding Validation Errors
|
|
126
|
+
|
|
127
|
+
When you provide invalid parameters, you'll receive a structured error response:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"error": "Invalid parameters",
|
|
132
|
+
"code": "INVALID_INPUT",
|
|
133
|
+
"details": [
|
|
134
|
+
{
|
|
135
|
+
"field": "query",
|
|
136
|
+
"message": "Query must be at least 3 characters"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"field": "limit",
|
|
140
|
+
"message": "Limit cannot exceed 50"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Common Validation Rules
|
|
147
|
+
|
|
148
|
+
**semantic_search:**
|
|
149
|
+
- `query`: 3-500 characters (required)
|
|
150
|
+
- `limit`: 1-50 (default: 5)
|
|
151
|
+
|
|
152
|
+
**find_similar:**
|
|
153
|
+
- `code`: minimum 10 characters (required)
|
|
154
|
+
- `limit`: 1-20 (default: 5)
|
|
155
|
+
|
|
156
|
+
**get_file_context:**
|
|
157
|
+
- `filepath`: cannot be empty (required)
|
|
158
|
+
- `includeRelated`: boolean (default: true)
|
|
159
|
+
|
|
160
|
+
**list_functions:**
|
|
161
|
+
- `pattern`: optional regex string
|
|
162
|
+
- `language`: optional language filter
|
|
163
|
+
|
|
164
|
+
### Error Codes
|
|
165
|
+
|
|
166
|
+
Lien uses structured error codes for programmatic error handling:
|
|
167
|
+
|
|
168
|
+
- `INVALID_INPUT` - Parameter validation failed
|
|
169
|
+
- `FILE_NOT_FOUND` - Requested file doesn't exist in index
|
|
170
|
+
- `INDEX_NOT_FOUND` - No index found (run `lien index`)
|
|
171
|
+
- `INDEX_CORRUPTED` - Index is corrupted (run `lien reindex`)
|
|
172
|
+
- `EMBEDDING_GENERATION_FAILED` - Embedding model failed (retryable)
|
|
173
|
+
- `INTERNAL_ERROR` - Unexpected internal error
|
|
174
|
+
|
|
175
|
+
### Best Practices
|
|
176
|
+
|
|
177
|
+
1. **Always provide required fields**: Check tool schemas for required parameters
|
|
178
|
+
2. **Respect validation limits**: Don't exceed max values for `limit` parameters
|
|
179
|
+
3. **Use descriptive queries**: Avoid very short or vague queries
|
|
180
|
+
4. **Handle validation errors gracefully**: Parse error details to understand what went wrong
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
117
184
|
## Workflow Patterns (FOLLOW THESE)
|
|
118
185
|
|
|
119
186
|
### Pattern 1: User Asks "Where is X?"
|
package/dist/index.js
CHANGED
|
@@ -210,13 +210,20 @@ var init_migration = __esm({
|
|
|
210
210
|
}
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
// src/errors/codes.ts
|
|
214
|
+
var init_codes = __esm({
|
|
215
|
+
"src/errors/codes.ts"() {
|
|
216
|
+
"use strict";
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
213
220
|
// src/errors/index.ts
|
|
214
221
|
function wrapError(error, context, additionalContext) {
|
|
215
222
|
const message = error instanceof Error ? error.message : String(error);
|
|
216
223
|
const stack = error instanceof Error ? error.stack : void 0;
|
|
217
224
|
const wrappedError = new LienError(
|
|
218
225
|
`${context}: ${message}`,
|
|
219
|
-
"
|
|
226
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
220
227
|
additionalContext
|
|
221
228
|
);
|
|
222
229
|
if (stack) {
|
|
@@ -231,32 +238,61 @@ var LienError, ConfigError, EmbeddingError, DatabaseError;
|
|
|
231
238
|
var init_errors = __esm({
|
|
232
239
|
"src/errors/index.ts"() {
|
|
233
240
|
"use strict";
|
|
241
|
+
init_codes();
|
|
242
|
+
init_codes();
|
|
234
243
|
LienError = class extends Error {
|
|
235
|
-
constructor(message, code, context) {
|
|
244
|
+
constructor(message, code, context, severity = "medium", recoverable = true, retryable = false) {
|
|
236
245
|
super(message);
|
|
237
246
|
this.code = code;
|
|
238
247
|
this.context = context;
|
|
248
|
+
this.severity = severity;
|
|
249
|
+
this.recoverable = recoverable;
|
|
250
|
+
this.retryable = retryable;
|
|
239
251
|
this.name = "LienError";
|
|
240
252
|
if (Error.captureStackTrace) {
|
|
241
253
|
Error.captureStackTrace(this, this.constructor);
|
|
242
254
|
}
|
|
243
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Serialize error to JSON for MCP responses
|
|
258
|
+
*/
|
|
259
|
+
toJSON() {
|
|
260
|
+
return {
|
|
261
|
+
error: this.message,
|
|
262
|
+
code: this.code,
|
|
263
|
+
severity: this.severity,
|
|
264
|
+
recoverable: this.recoverable,
|
|
265
|
+
context: this.context
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Check if this error is retryable
|
|
270
|
+
*/
|
|
271
|
+
isRetryable() {
|
|
272
|
+
return this.retryable;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Check if this error is recoverable
|
|
276
|
+
*/
|
|
277
|
+
isRecoverable() {
|
|
278
|
+
return this.recoverable;
|
|
279
|
+
}
|
|
244
280
|
};
|
|
245
281
|
ConfigError = class extends LienError {
|
|
246
282
|
constructor(message, context) {
|
|
247
|
-
super(message, "
|
|
283
|
+
super(message, "CONFIG_INVALID" /* CONFIG_INVALID */, context, "medium", true, false);
|
|
248
284
|
this.name = "ConfigError";
|
|
249
285
|
}
|
|
250
286
|
};
|
|
251
287
|
EmbeddingError = class extends LienError {
|
|
252
288
|
constructor(message, context) {
|
|
253
|
-
super(message, "
|
|
289
|
+
super(message, "EMBEDDING_GENERATION_FAILED" /* EMBEDDING_GENERATION_FAILED */, context, "high", true, true);
|
|
254
290
|
this.name = "EmbeddingError";
|
|
255
291
|
}
|
|
256
292
|
};
|
|
257
293
|
DatabaseError = class extends LienError {
|
|
258
294
|
constructor(message, context) {
|
|
259
|
-
super(message, "
|
|
295
|
+
super(message, "INTERNAL_ERROR" /* INTERNAL_ERROR */, context, "high", true, true);
|
|
260
296
|
this.name = "DatabaseError";
|
|
261
297
|
}
|
|
262
298
|
};
|
|
@@ -4082,82 +4118,85 @@ import { createRequire as createRequire3 } from "module";
|
|
|
4082
4118
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4083
4119
|
import { dirname as dirname3, join as join3 } from "path";
|
|
4084
4120
|
|
|
4121
|
+
// src/mcp/utils/zod-to-json-schema.ts
|
|
4122
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
4123
|
+
function toMCPToolSchema(zodSchema, name, description) {
|
|
4124
|
+
return {
|
|
4125
|
+
name,
|
|
4126
|
+
description,
|
|
4127
|
+
inputSchema: zodToJsonSchema(zodSchema, {
|
|
4128
|
+
target: "jsonSchema7",
|
|
4129
|
+
$refStrategy: "none"
|
|
4130
|
+
})
|
|
4131
|
+
};
|
|
4132
|
+
}
|
|
4133
|
+
|
|
4134
|
+
// src/mcp/schemas/search.schema.ts
|
|
4135
|
+
import { z } from "zod";
|
|
4136
|
+
var SemanticSearchSchema = z.object({
|
|
4137
|
+
query: z.string().min(3, "Query must be at least 3 characters").max(500, "Query too long (max 500 characters)").describe(
|
|
4138
|
+
"Natural language description of what you're looking for.\n\nUse full sentences describing functionality, not exact names.\n\nGood examples:\n - 'handles user authentication'\n - 'validates email format'\n - 'processes payment transactions'\n\nBad examples:\n - 'auth' (too vague)\n - 'validateEmail' (use grep for exact names)"
|
|
4139
|
+
),
|
|
4140
|
+
limit: z.number().int().min(1, "Limit must be at least 1").max(50, "Limit cannot exceed 50").default(5).describe(
|
|
4141
|
+
"Number of results to return.\n\nDefault: 5\nIncrease to 10-15 for broad exploration."
|
|
4142
|
+
)
|
|
4143
|
+
});
|
|
4144
|
+
|
|
4145
|
+
// src/mcp/schemas/similarity.schema.ts
|
|
4146
|
+
import { z as z2 } from "zod";
|
|
4147
|
+
var FindSimilarSchema = z2.object({
|
|
4148
|
+
code: z2.string().min(10, "Code snippet must be at least 10 characters").describe(
|
|
4149
|
+
"Code snippet to find similar implementations for.\n\nProvide a representative code sample that demonstrates the pattern you want to find similar examples of in the codebase."
|
|
4150
|
+
),
|
|
4151
|
+
limit: z2.number().int().min(1, "Limit must be at least 1").max(20, "Limit cannot exceed 20").default(5).describe(
|
|
4152
|
+
"Number of similar code blocks to return.\n\nDefault: 5"
|
|
4153
|
+
)
|
|
4154
|
+
});
|
|
4155
|
+
|
|
4156
|
+
// src/mcp/schemas/file.schema.ts
|
|
4157
|
+
import { z as z3 } from "zod";
|
|
4158
|
+
var GetFileContextSchema = z3.object({
|
|
4159
|
+
filepath: z3.string().min(1, "Filepath cannot be empty").describe(
|
|
4160
|
+
"Relative path to file from workspace root.\n\nExample: 'src/components/Button.tsx'"
|
|
4161
|
+
),
|
|
4162
|
+
includeRelated: z3.boolean().default(true).describe(
|
|
4163
|
+
"Include semantically related chunks from nearby code.\n\nDefault: true\n\nWhen enabled, also returns related code from other files that are semantically similar to the target file's contents."
|
|
4164
|
+
)
|
|
4165
|
+
});
|
|
4166
|
+
|
|
4167
|
+
// src/mcp/schemas/symbols.schema.ts
|
|
4168
|
+
import { z as z4 } from "zod";
|
|
4169
|
+
var ListFunctionsSchema = z4.object({
|
|
4170
|
+
pattern: z4.string().optional().describe(
|
|
4171
|
+
"Regex pattern to match symbol names.\n\nExamples:\n - '.*Controller.*' to find all Controllers\n - 'handle.*' to find handlers\n - '.*Service$' to find Services\n\nIf omitted, returns all symbols."
|
|
4172
|
+
),
|
|
4173
|
+
language: z4.string().optional().describe(
|
|
4174
|
+
"Filter by programming language.\n\nExamples: 'typescript', 'python', 'javascript', 'php'\n\nIf omitted, searches all languages."
|
|
4175
|
+
)
|
|
4176
|
+
});
|
|
4177
|
+
|
|
4085
4178
|
// src/mcp/tools.ts
|
|
4086
4179
|
var tools = [
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
name: "find_similar",
|
|
4108
|
-
description: "Find code similar to a given code snippet. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity.",
|
|
4109
|
-
inputSchema: {
|
|
4110
|
-
type: "object",
|
|
4111
|
-
properties: {
|
|
4112
|
-
code: {
|
|
4113
|
-
type: "string",
|
|
4114
|
-
description: "Code snippet to find similar implementations"
|
|
4115
|
-
},
|
|
4116
|
-
limit: {
|
|
4117
|
-
type: "number",
|
|
4118
|
-
description: "Maximum number of results to return",
|
|
4119
|
-
default: 5
|
|
4120
|
-
}
|
|
4121
|
-
},
|
|
4122
|
-
required: ["code"]
|
|
4123
|
-
}
|
|
4124
|
-
},
|
|
4125
|
-
{
|
|
4126
|
-
name: "get_file_context",
|
|
4127
|
-
description: "Get all chunks and related context for a specific file. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity.",
|
|
4128
|
-
inputSchema: {
|
|
4129
|
-
type: "object",
|
|
4130
|
-
properties: {
|
|
4131
|
-
filepath: {
|
|
4132
|
-
type: "string",
|
|
4133
|
-
description: "Path to the file (relative to project root)"
|
|
4134
|
-
},
|
|
4135
|
-
includeRelated: {
|
|
4136
|
-
type: "boolean",
|
|
4137
|
-
description: "Include semantically related chunks from other files",
|
|
4138
|
-
default: true
|
|
4139
|
-
}
|
|
4140
|
-
},
|
|
4141
|
-
required: ["filepath"]
|
|
4142
|
-
}
|
|
4143
|
-
},
|
|
4144
|
-
{
|
|
4145
|
-
name: "list_functions",
|
|
4146
|
-
description: "List functions, classes, and interfaces by name pattern and language",
|
|
4147
|
-
inputSchema: {
|
|
4148
|
-
type: "object",
|
|
4149
|
-
properties: {
|
|
4150
|
-
pattern: {
|
|
4151
|
-
type: "string",
|
|
4152
|
-
description: 'Regex pattern to match symbol names (e.g., ".*Service$", "handle.*")'
|
|
4153
|
-
},
|
|
4154
|
-
language: {
|
|
4155
|
-
type: "string",
|
|
4156
|
-
description: 'Language filter (e.g., "typescript", "python", "php")'
|
|
4157
|
-
}
|
|
4158
|
-
}
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4180
|
+
toMCPToolSchema(
|
|
4181
|
+
SemanticSearchSchema,
|
|
4182
|
+
"semantic_search",
|
|
4183
|
+
"Search the codebase semantically for relevant code using natural language. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity."
|
|
4184
|
+
),
|
|
4185
|
+
toMCPToolSchema(
|
|
4186
|
+
FindSimilarSchema,
|
|
4187
|
+
"find_similar",
|
|
4188
|
+
"Find code similar to a given code snippet. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity."
|
|
4189
|
+
),
|
|
4190
|
+
toMCPToolSchema(
|
|
4191
|
+
GetFileContextSchema,
|
|
4192
|
+
"get_file_context",
|
|
4193
|
+
"Get all chunks and related context for a specific file. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity."
|
|
4194
|
+
),
|
|
4195
|
+
toMCPToolSchema(
|
|
4196
|
+
ListFunctionsSchema,
|
|
4197
|
+
"list_functions",
|
|
4198
|
+
"List functions, classes, and interfaces by name pattern and language"
|
|
4199
|
+
)
|
|
4161
4200
|
];
|
|
4162
4201
|
|
|
4163
4202
|
// src/mcp/server.ts
|
|
@@ -4300,6 +4339,64 @@ var FileWatcher = class {
|
|
|
4300
4339
|
|
|
4301
4340
|
// src/mcp/server.ts
|
|
4302
4341
|
init_constants();
|
|
4342
|
+
|
|
4343
|
+
// src/mcp/utils/tool-wrapper.ts
|
|
4344
|
+
init_errors();
|
|
4345
|
+
import { ZodError } from "zod";
|
|
4346
|
+
function wrapToolHandler(schema, handler) {
|
|
4347
|
+
return async (args) => {
|
|
4348
|
+
try {
|
|
4349
|
+
const validated = schema.parse(args);
|
|
4350
|
+
const result = await handler(validated);
|
|
4351
|
+
return {
|
|
4352
|
+
content: [{
|
|
4353
|
+
type: "text",
|
|
4354
|
+
text: JSON.stringify(result, null, 2)
|
|
4355
|
+
}]
|
|
4356
|
+
};
|
|
4357
|
+
} catch (error) {
|
|
4358
|
+
if (error instanceof ZodError) {
|
|
4359
|
+
return {
|
|
4360
|
+
isError: true,
|
|
4361
|
+
content: [{
|
|
4362
|
+
type: "text",
|
|
4363
|
+
text: JSON.stringify({
|
|
4364
|
+
error: "Invalid parameters",
|
|
4365
|
+
code: "INVALID_INPUT" /* INVALID_INPUT */,
|
|
4366
|
+
details: error.errors.map((e) => ({
|
|
4367
|
+
field: e.path.join("."),
|
|
4368
|
+
message: e.message
|
|
4369
|
+
}))
|
|
4370
|
+
}, null, 2)
|
|
4371
|
+
}]
|
|
4372
|
+
};
|
|
4373
|
+
}
|
|
4374
|
+
if (error instanceof LienError) {
|
|
4375
|
+
return {
|
|
4376
|
+
isError: true,
|
|
4377
|
+
content: [{
|
|
4378
|
+
type: "text",
|
|
4379
|
+
text: JSON.stringify(error.toJSON(), null, 2)
|
|
4380
|
+
}]
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
console.error("Unexpected error in tool handler:", error);
|
|
4384
|
+
return {
|
|
4385
|
+
isError: true,
|
|
4386
|
+
content: [{
|
|
4387
|
+
type: "text",
|
|
4388
|
+
text: JSON.stringify({
|
|
4389
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
4390
|
+
code: "INTERNAL_ERROR" /* INTERNAL_ERROR */
|
|
4391
|
+
}, null, 2)
|
|
4392
|
+
}]
|
|
4393
|
+
};
|
|
4394
|
+
}
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
// src/mcp/server.ts
|
|
4399
|
+
init_errors();
|
|
4303
4400
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
4304
4401
|
var __dirname4 = dirname3(__filename4);
|
|
4305
4402
|
var require4 = createRequire3(import.meta.url);
|
|
@@ -4364,148 +4461,139 @@ async function startMCPServer(options) {
|
|
|
4364
4461
|
}, VERSION_CHECK_INTERVAL_MS);
|
|
4365
4462
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
4366
4463
|
const { name, arguments: args } = request.params;
|
|
4464
|
+
log(`Handling tool call: ${name}`);
|
|
4367
4465
|
try {
|
|
4368
|
-
log(`Handling tool call: ${name}`);
|
|
4369
4466
|
switch (name) {
|
|
4370
|
-
case "semantic_search":
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
const fileChunks = allResults.filter(
|
|
4420
|
-
(r) => r.metadata.file.includes(filepath) || filepath.includes(r.metadata.file)
|
|
4421
|
-
);
|
|
4422
|
-
let results = fileChunks;
|
|
4423
|
-
if (includeRelated && fileChunks.length > 0) {
|
|
4424
|
-
const relatedEmbedding = await embeddings.embed(fileChunks[0].content);
|
|
4425
|
-
const related = await vectorDB.search(relatedEmbedding, 5, fileChunks[0].content);
|
|
4426
|
-
const relatedOtherFiles = related.filter(
|
|
4427
|
-
(r) => !r.metadata.file.includes(filepath) && !filepath.includes(r.metadata.file)
|
|
4428
|
-
);
|
|
4429
|
-
results = [...fileChunks, ...relatedOtherFiles];
|
|
4430
|
-
}
|
|
4431
|
-
log(`Found ${results.length} chunks`);
|
|
4432
|
-
const response = {
|
|
4433
|
-
indexInfo: getIndexMetadata(),
|
|
4434
|
-
file: filepath,
|
|
4435
|
-
chunks: results
|
|
4436
|
-
};
|
|
4437
|
-
return {
|
|
4438
|
-
content: [
|
|
4439
|
-
{
|
|
4440
|
-
type: "text",
|
|
4441
|
-
text: JSON.stringify(response, null, 2)
|
|
4467
|
+
case "semantic_search":
|
|
4468
|
+
return await wrapToolHandler(
|
|
4469
|
+
SemanticSearchSchema,
|
|
4470
|
+
async (validatedArgs) => {
|
|
4471
|
+
log(`Searching for: "${validatedArgs.query}"`);
|
|
4472
|
+
await checkAndReconnect();
|
|
4473
|
+
const queryEmbedding = await embeddings.embed(validatedArgs.query);
|
|
4474
|
+
const results = await vectorDB.search(queryEmbedding, validatedArgs.limit, validatedArgs.query);
|
|
4475
|
+
log(`Found ${results.length} results`);
|
|
4476
|
+
return {
|
|
4477
|
+
indexInfo: getIndexMetadata(),
|
|
4478
|
+
results
|
|
4479
|
+
};
|
|
4480
|
+
}
|
|
4481
|
+
)(args);
|
|
4482
|
+
case "find_similar":
|
|
4483
|
+
return await wrapToolHandler(
|
|
4484
|
+
FindSimilarSchema,
|
|
4485
|
+
async (validatedArgs) => {
|
|
4486
|
+
log(`Finding similar code...`);
|
|
4487
|
+
await checkAndReconnect();
|
|
4488
|
+
const codeEmbedding = await embeddings.embed(validatedArgs.code);
|
|
4489
|
+
const results = await vectorDB.search(codeEmbedding, validatedArgs.limit, validatedArgs.code);
|
|
4490
|
+
log(`Found ${results.length} similar chunks`);
|
|
4491
|
+
return {
|
|
4492
|
+
indexInfo: getIndexMetadata(),
|
|
4493
|
+
results
|
|
4494
|
+
};
|
|
4495
|
+
}
|
|
4496
|
+
)(args);
|
|
4497
|
+
case "get_file_context":
|
|
4498
|
+
return await wrapToolHandler(
|
|
4499
|
+
GetFileContextSchema,
|
|
4500
|
+
async (validatedArgs) => {
|
|
4501
|
+
log(`Getting context for: ${validatedArgs.filepath}`);
|
|
4502
|
+
await checkAndReconnect();
|
|
4503
|
+
const fileEmbedding = await embeddings.embed(validatedArgs.filepath);
|
|
4504
|
+
const allResults = await vectorDB.search(fileEmbedding, 50, validatedArgs.filepath);
|
|
4505
|
+
const fileChunks = allResults.filter(
|
|
4506
|
+
(r) => r.metadata.file.includes(validatedArgs.filepath) || validatedArgs.filepath.includes(r.metadata.file)
|
|
4507
|
+
);
|
|
4508
|
+
let results = fileChunks;
|
|
4509
|
+
if (validatedArgs.includeRelated && fileChunks.length > 0) {
|
|
4510
|
+
const relatedEmbedding = await embeddings.embed(fileChunks[0].content);
|
|
4511
|
+
const related = await vectorDB.search(relatedEmbedding, 5, fileChunks[0].content);
|
|
4512
|
+
const relatedOtherFiles = related.filter(
|
|
4513
|
+
(r) => !r.metadata.file.includes(validatedArgs.filepath) && !validatedArgs.filepath.includes(r.metadata.file)
|
|
4514
|
+
);
|
|
4515
|
+
results = [...fileChunks, ...relatedOtherFiles];
|
|
4442
4516
|
}
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
log("Listing functions with symbol metadata...");
|
|
4450
|
-
await checkAndReconnect();
|
|
4451
|
-
let results;
|
|
4452
|
-
let usedMethod = "symbols";
|
|
4453
|
-
try {
|
|
4454
|
-
results = await vectorDB.querySymbols({
|
|
4455
|
-
language,
|
|
4456
|
-
pattern,
|
|
4457
|
-
limit: 50
|
|
4458
|
-
});
|
|
4459
|
-
if (results.length === 0 && (language || pattern)) {
|
|
4460
|
-
log("No symbol results, falling back to content scan...");
|
|
4461
|
-
results = await vectorDB.scanWithFilter({
|
|
4462
|
-
language,
|
|
4463
|
-
pattern,
|
|
4464
|
-
limit: 50
|
|
4465
|
-
});
|
|
4466
|
-
usedMethod = "content";
|
|
4517
|
+
log(`Found ${results.length} chunks`);
|
|
4518
|
+
return {
|
|
4519
|
+
indexInfo: getIndexMetadata(),
|
|
4520
|
+
file: validatedArgs.filepath,
|
|
4521
|
+
chunks: results
|
|
4522
|
+
};
|
|
4467
4523
|
}
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4524
|
+
)(args);
|
|
4525
|
+
case "list_functions":
|
|
4526
|
+
return await wrapToolHandler(
|
|
4527
|
+
ListFunctionsSchema,
|
|
4528
|
+
async (validatedArgs) => {
|
|
4529
|
+
log("Listing functions with symbol metadata...");
|
|
4530
|
+
await checkAndReconnect();
|
|
4531
|
+
let results;
|
|
4532
|
+
let usedMethod = "symbols";
|
|
4533
|
+
try {
|
|
4534
|
+
results = await vectorDB.querySymbols({
|
|
4535
|
+
language: validatedArgs.language,
|
|
4536
|
+
pattern: validatedArgs.pattern,
|
|
4537
|
+
limit: 50
|
|
4538
|
+
});
|
|
4539
|
+
if (results.length === 0 && (validatedArgs.language || validatedArgs.pattern)) {
|
|
4540
|
+
log("No symbol results, falling back to content scan...");
|
|
4541
|
+
results = await vectorDB.scanWithFilter({
|
|
4542
|
+
language: validatedArgs.language,
|
|
4543
|
+
pattern: validatedArgs.pattern,
|
|
4544
|
+
limit: 50
|
|
4545
|
+
});
|
|
4546
|
+
usedMethod = "content";
|
|
4547
|
+
}
|
|
4548
|
+
} catch (error) {
|
|
4549
|
+
log(`Symbol query failed, falling back to content scan: ${error}`);
|
|
4550
|
+
results = await vectorDB.scanWithFilter({
|
|
4551
|
+
language: validatedArgs.language,
|
|
4552
|
+
pattern: validatedArgs.pattern,
|
|
4553
|
+
limit: 50
|
|
4554
|
+
});
|
|
4555
|
+
usedMethod = "content";
|
|
4489
4556
|
}
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4557
|
+
log(`Found ${results.length} matches using ${usedMethod} method`);
|
|
4558
|
+
return {
|
|
4559
|
+
indexInfo: getIndexMetadata(),
|
|
4560
|
+
method: usedMethod,
|
|
4561
|
+
results,
|
|
4562
|
+
note: usedMethod === "content" ? 'Using content search. Run "lien reindex" to enable faster symbol-based queries.' : void 0
|
|
4563
|
+
};
|
|
4564
|
+
}
|
|
4565
|
+
)(args);
|
|
4493
4566
|
default:
|
|
4494
|
-
throw new
|
|
4567
|
+
throw new LienError(
|
|
4568
|
+
`Unknown tool: ${name}`,
|
|
4569
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
4570
|
+
{ requestedTool: name, availableTools: tools.map((t) => t.name) },
|
|
4571
|
+
"medium",
|
|
4572
|
+
false,
|
|
4573
|
+
false
|
|
4574
|
+
);
|
|
4495
4575
|
}
|
|
4496
4576
|
} catch (error) {
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
{
|
|
4577
|
+
if (error instanceof LienError) {
|
|
4578
|
+
return {
|
|
4579
|
+
isError: true,
|
|
4580
|
+
content: [{
|
|
4501
4581
|
type: "text",
|
|
4502
|
-
text: JSON.stringify(
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
isError: true
|
|
4582
|
+
text: JSON.stringify(error.toJSON(), null, 2)
|
|
4583
|
+
}]
|
|
4584
|
+
};
|
|
4585
|
+
}
|
|
4586
|
+
console.error(`Unexpected error handling tool call ${name}:`, error);
|
|
4587
|
+
return {
|
|
4588
|
+
isError: true,
|
|
4589
|
+
content: [{
|
|
4590
|
+
type: "text",
|
|
4591
|
+
text: JSON.stringify({
|
|
4592
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
4593
|
+
code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
4594
|
+
tool: name
|
|
4595
|
+
}, null, 2)
|
|
4596
|
+
}]
|
|
4509
4597
|
};
|
|
4510
4598
|
}
|
|
4511
4599
|
});
|