@sudosandwich/limps 0.2.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/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/agent-parser.d.ts +146 -0
- package/dist/agent-parser.d.ts.map +1 -0
- package/dist/agent-parser.js +448 -0
- package/dist/agent-parser.js.map +1 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +146 -0
- package/dist/config.js.map +1 -0
- package/dist/coordination.d.ts +102 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +157 -0
- package/dist/coordination.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +83 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +467 -0
- package/dist/indexer.js.map +1 -0
- package/dist/resources/agents-status.d.ts +32 -0
- package/dist/resources/agents-status.d.ts.map +1 -0
- package/dist/resources/agents-status.js +73 -0
- package/dist/resources/agents-status.js.map +1 -0
- package/dist/resources/decisions-log.d.ts +21 -0
- package/dist/resources/decisions-log.d.ts.map +1 -0
- package/dist/resources/decisions-log.js +146 -0
- package/dist/resources/decisions-log.js.map +1 -0
- package/dist/resources/index.d.ts +10 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +74 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/plans-full.d.ts +11 -0
- package/dist/resources/plans-full.d.ts.map +1 -0
- package/dist/resources/plans-full.js +71 -0
- package/dist/resources/plans-full.js.map +1 -0
- package/dist/resources/plans-index.d.ts +30 -0
- package/dist/resources/plans-index.d.ts.map +1 -0
- package/dist/resources/plans-index.js +177 -0
- package/dist/resources/plans-index.js.map +1 -0
- package/dist/resources/plans-summary.d.ts +33 -0
- package/dist/resources/plans-summary.d.ts.map +1 -0
- package/dist/resources/plans-summary.js +238 -0
- package/dist/resources/plans-summary.js.map +1 -0
- package/dist/rlm/extractors.d.ts +39 -0
- package/dist/rlm/extractors.d.ts.map +1 -0
- package/dist/rlm/extractors.js +291 -0
- package/dist/rlm/extractors.js.map +1 -0
- package/dist/rlm/helpers-inject.d.ts +13 -0
- package/dist/rlm/helpers-inject.d.ts.map +1 -0
- package/dist/rlm/helpers-inject.js +586 -0
- package/dist/rlm/helpers-inject.js.map +1 -0
- package/dist/rlm/helpers.d.ts +124 -0
- package/dist/rlm/helpers.d.ts.map +1 -0
- package/dist/rlm/helpers.js +381 -0
- package/dist/rlm/helpers.js.map +1 -0
- package/dist/rlm/index.d.ts +12 -0
- package/dist/rlm/index.d.ts.map +1 -0
- package/dist/rlm/index.js +19 -0
- package/dist/rlm/index.js.map +1 -0
- package/dist/rlm/parallel.d.ts +45 -0
- package/dist/rlm/parallel.d.ts.map +1 -0
- package/dist/rlm/parallel.js +76 -0
- package/dist/rlm/parallel.js.map +1 -0
- package/dist/rlm/recursion.d.ts +96 -0
- package/dist/rlm/recursion.d.ts.map +1 -0
- package/dist/rlm/recursion.js +113 -0
- package/dist/rlm/recursion.js.map +1 -0
- package/dist/rlm/sampling.d.ts +100 -0
- package/dist/rlm/sampling.d.ts.map +1 -0
- package/dist/rlm/sampling.js +96 -0
- package/dist/rlm/sampling.js.map +1 -0
- package/dist/rlm/sandbox.d.ts +73 -0
- package/dist/rlm/sandbox.d.ts.map +1 -0
- package/dist/rlm/sandbox.js +160 -0
- package/dist/rlm/sandbox.js.map +1 -0
- package/dist/rlm/security.d.ts +28 -0
- package/dist/rlm/security.d.ts.map +1 -0
- package/dist/rlm/security.js +154 -0
- package/dist/rlm/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +107 -0
- package/dist/server.js.map +1 -0
- package/dist/task-parser.d.ts +47 -0
- package/dist/task-parser.d.ts.map +1 -0
- package/dist/task-parser.js +112 -0
- package/dist/task-parser.js.map +1 -0
- package/dist/test-setup.d.ts +6 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +37 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/tools/claim-task.d.ts +28 -0
- package/dist/tools/claim-task.d.ts.map +1 -0
- package/dist/tools/claim-task.js +288 -0
- package/dist/tools/claim-task.js.map +1 -0
- package/dist/tools/create-doc.d.ts +47 -0
- package/dist/tools/create-doc.d.ts.map +1 -0
- package/dist/tools/create-doc.js +137 -0
- package/dist/tools/create-doc.js.map +1 -0
- package/dist/tools/create-plan.d.ts +25 -0
- package/dist/tools/create-plan.d.ts.map +1 -0
- package/dist/tools/create-plan.js +179 -0
- package/dist/tools/create-plan.js.map +1 -0
- package/dist/tools/delete-doc.d.ts +51 -0
- package/dist/tools/delete-doc.d.ts.map +1 -0
- package/dist/tools/delete-doc.js +194 -0
- package/dist/tools/delete-doc.js.map +1 -0
- package/dist/tools/get-next-task.d.ts +49 -0
- package/dist/tools/get-next-task.d.ts.map +1 -0
- package/dist/tools/get-next-task.js +204 -0
- package/dist/tools/get-next-task.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +122 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-docs.d.ts +53 -0
- package/dist/tools/list-docs.d.ts.map +1 -0
- package/dist/tools/list-docs.js +236 -0
- package/dist/tools/list-docs.js.map +1 -0
- package/dist/tools/open-document-in-cursor.d.ts +62 -0
- package/dist/tools/open-document-in-cursor.d.ts.map +1 -0
- package/dist/tools/open-document-in-cursor.js +211 -0
- package/dist/tools/open-document-in-cursor.js.map +1 -0
- package/dist/tools/read-doc.d.ts +44 -0
- package/dist/tools/read-doc.d.ts.map +1 -0
- package/dist/tools/read-doc.js +174 -0
- package/dist/tools/read-doc.js.map +1 -0
- package/dist/tools/release-task.d.ts +28 -0
- package/dist/tools/release-task.d.ts.map +1 -0
- package/dist/tools/release-task.js +154 -0
- package/dist/tools/release-task.js.map +1 -0
- package/dist/tools/rlm-multi-query.d.ts +110 -0
- package/dist/tools/rlm-multi-query.d.ts.map +1 -0
- package/dist/tools/rlm-multi-query.js +348 -0
- package/dist/tools/rlm-multi-query.js.map +1 -0
- package/dist/tools/rlm-query.d.ts +56 -0
- package/dist/tools/rlm-query.d.ts.map +1 -0
- package/dist/tools/rlm-query.js +228 -0
- package/dist/tools/rlm-query.js.map +1 -0
- package/dist/tools/search-docs.d.ts +34 -0
- package/dist/tools/search-docs.d.ts.map +1 -0
- package/dist/tools/search-docs.js +292 -0
- package/dist/tools/search-docs.js.map +1 -0
- package/dist/tools/update-doc.d.ts +149 -0
- package/dist/tools/update-doc.d.ts.map +1 -0
- package/dist/tools/update-doc.js +195 -0
- package/dist/tools/update-doc.js.map +1 -0
- package/dist/tools/update-task-status.d.ts +31 -0
- package/dist/tools/update-task-status.d.ts.map +1 -0
- package/dist/tools/update-task-status.js +303 -0
- package/dist/tools/update-task-status.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/backup.d.ts +76 -0
- package/dist/utils/backup.d.ts.map +1 -0
- package/dist/utils/backup.js +172 -0
- package/dist/utils/backup.js.map +1 -0
- package/dist/utils/errors.d.ts +93 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +125 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/os-paths.d.ts +45 -0
- package/dist/utils/os-paths.d.ts.map +1 -0
- package/dist/utils/os-paths.js +81 -0
- package/dist/utils/os-paths.js.map +1 -0
- package/dist/utils/paths.d.ts +71 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +165 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/watcher.d.ts +19 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +109 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rlm_query tool: Execute JavaScript code on a single document.
|
|
3
|
+
* Feature #2: RLM Query Tool
|
|
4
|
+
*
|
|
5
|
+
* Enables Claude to load documents as environment variables and execute
|
|
6
|
+
* filter/transform code before LLM reasoning.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { ToolContext, ToolResult } from '../types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Input schema for rlm_query tool.
|
|
12
|
+
*/
|
|
13
|
+
export declare const RlmQueryInputSchema: z.ZodObject<{
|
|
14
|
+
path: z.ZodString;
|
|
15
|
+
code: z.ZodString;
|
|
16
|
+
sub_query: z.ZodOptional<z.ZodString>;
|
|
17
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
18
|
+
max_depth: z.ZodDefault<z.ZodNumber>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
path: string;
|
|
21
|
+
code: string;
|
|
22
|
+
timeout: number;
|
|
23
|
+
max_depth: number;
|
|
24
|
+
sub_query?: string | undefined;
|
|
25
|
+
}, {
|
|
26
|
+
path: string;
|
|
27
|
+
code: string;
|
|
28
|
+
timeout?: number | undefined;
|
|
29
|
+
sub_query?: string | undefined;
|
|
30
|
+
max_depth?: number | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
export type RlmQueryInput = z.infer<typeof RlmQueryInputSchema>;
|
|
33
|
+
/**
|
|
34
|
+
* Output interface for rlm_query tool.
|
|
35
|
+
*/
|
|
36
|
+
export interface RlmQueryOutput {
|
|
37
|
+
result: unknown;
|
|
38
|
+
sub_results?: unknown[];
|
|
39
|
+
execution_time_ms: number;
|
|
40
|
+
tokens_saved?: number;
|
|
41
|
+
metadata: {
|
|
42
|
+
path: string;
|
|
43
|
+
doc_size: number;
|
|
44
|
+
result_size: number;
|
|
45
|
+
depth: number;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Handle rlm_query tool request.
|
|
50
|
+
*
|
|
51
|
+
* @param input - Tool input
|
|
52
|
+
* @param context - Tool context
|
|
53
|
+
* @returns Tool result
|
|
54
|
+
*/
|
|
55
|
+
export declare function handleRlmQuery(input: RlmQueryInput, context: ToolContext): Promise<ToolResult>;
|
|
56
|
+
//# sourceMappingURL=rlm-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rlm-query.d.ts","sourceRoot":"","sources":["../../src/tools/rlm-query.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQ3D;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;EAY9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAqBD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA6LrB"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rlm_query tool: Execute JavaScript code on a single document.
|
|
3
|
+
* Feature #2: RLM Query Tool
|
|
4
|
+
*
|
|
5
|
+
* Enables Claude to load documents as environment variables and execute
|
|
6
|
+
* filter/transform code before LLM reasoning.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { readFile, stat } from 'fs/promises';
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import { dirname } from 'path';
|
|
12
|
+
import { validatePath } from '../utils/paths.js';
|
|
13
|
+
import { notFound } from '../utils/errors.js';
|
|
14
|
+
import { createEnvironment } from '../rlm/sandbox.js';
|
|
15
|
+
import { validateCode } from '../rlm/security.js';
|
|
16
|
+
import { processSubCalls } from '../rlm/recursion.js';
|
|
17
|
+
/**
|
|
18
|
+
* Input schema for rlm_query tool.
|
|
19
|
+
*/
|
|
20
|
+
export const RlmQueryInputSchema = z.object({
|
|
21
|
+
path: z.string().min(1).describe('Document path relative to repo root'),
|
|
22
|
+
code: z.string().min(1).describe('JavaScript code to execute (doc variable available)'),
|
|
23
|
+
sub_query: z.string().optional().describe('Prompt for recursive LLM processing of results'),
|
|
24
|
+
timeout: z.number().int().min(100).max(30000).default(5000).describe('Execution timeout in ms'),
|
|
25
|
+
max_depth: z
|
|
26
|
+
.number()
|
|
27
|
+
.int()
|
|
28
|
+
.min(1)
|
|
29
|
+
.max(3)
|
|
30
|
+
.default(1)
|
|
31
|
+
.describe('Maximum recursion depth for sub_query'),
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Get repository root from config.
|
|
35
|
+
*/
|
|
36
|
+
function getRepoRoot(config) {
|
|
37
|
+
if (config.docsPaths && config.docsPaths.length > 0) {
|
|
38
|
+
return config.docsPaths[0];
|
|
39
|
+
}
|
|
40
|
+
return dirname(config.plansPath);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Estimate tokens saved by filtering.
|
|
44
|
+
* Rough approximation: 4 characters per token.
|
|
45
|
+
*/
|
|
46
|
+
function estimateTokensSaved(docSize, resultSize) {
|
|
47
|
+
const charsSaved = docSize - resultSize;
|
|
48
|
+
return Math.max(0, Math.floor(charsSaved / 4));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Handle rlm_query tool request.
|
|
52
|
+
*
|
|
53
|
+
* @param input - Tool input
|
|
54
|
+
* @param context - Tool context
|
|
55
|
+
* @returns Tool result
|
|
56
|
+
*/
|
|
57
|
+
export async function handleRlmQuery(input, context) {
|
|
58
|
+
const { path, code, sub_query, timeout = 5000 } = input;
|
|
59
|
+
const { config } = context;
|
|
60
|
+
try {
|
|
61
|
+
// Get repo root
|
|
62
|
+
const repoRoot = getRepoRoot(config);
|
|
63
|
+
// Validate path BEFORE loading (per gotchas)
|
|
64
|
+
const validated = validatePath(path, repoRoot);
|
|
65
|
+
// Check if file exists
|
|
66
|
+
if (!existsSync(validated.absolute)) {
|
|
67
|
+
throw notFound(path);
|
|
68
|
+
}
|
|
69
|
+
// Read file stats and content
|
|
70
|
+
const stats = await stat(validated.absolute);
|
|
71
|
+
const content = await readFile(validated.absolute, 'utf-8');
|
|
72
|
+
const docSize = Buffer.byteLength(content, 'utf-8');
|
|
73
|
+
// Create DocVariable
|
|
74
|
+
const docVar = {
|
|
75
|
+
content,
|
|
76
|
+
metadata: {
|
|
77
|
+
path: validated.relative,
|
|
78
|
+
size: docSize,
|
|
79
|
+
lines: content === '' ? 0 : content.split('\n').length,
|
|
80
|
+
modified: stats.mtime.toISOString(),
|
|
81
|
+
},
|
|
82
|
+
path: validated.relative,
|
|
83
|
+
};
|
|
84
|
+
// Validate code before execution (per TDD requirement)
|
|
85
|
+
validateCode(code);
|
|
86
|
+
// Create sandbox environment
|
|
87
|
+
const env = await createEnvironment(docVar, { timeout });
|
|
88
|
+
try {
|
|
89
|
+
// Execute code
|
|
90
|
+
const execResult = await env.execute(code);
|
|
91
|
+
// Calculate result size (serialize to estimate)
|
|
92
|
+
const resultJson = JSON.stringify(execResult.result);
|
|
93
|
+
const resultSize = Buffer.byteLength(resultJson, 'utf-8');
|
|
94
|
+
// Estimate tokens saved
|
|
95
|
+
const tokensSaved = estimateTokensSaved(docSize, resultSize);
|
|
96
|
+
// Build output
|
|
97
|
+
const output = {
|
|
98
|
+
result: execResult.result,
|
|
99
|
+
execution_time_ms: Math.max(1, Math.round(execResult.executionTimeMs)), // Ensure at least 1ms
|
|
100
|
+
tokens_saved: tokensSaved,
|
|
101
|
+
metadata: {
|
|
102
|
+
path: validated.relative,
|
|
103
|
+
doc_size: docSize,
|
|
104
|
+
result_size: resultSize,
|
|
105
|
+
depth: 0, // Current depth (updated if sub_query is processed)
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
// Handle sub_query (Agent 3 implementation)
|
|
109
|
+
if (sub_query) {
|
|
110
|
+
try {
|
|
111
|
+
// Get sampling client from context (if available)
|
|
112
|
+
// For now, this will be undefined in real server, but tests can provide mock client
|
|
113
|
+
const samplingClient = context
|
|
114
|
+
.samplingClient;
|
|
115
|
+
// Normalize items to array
|
|
116
|
+
const items = Array.isArray(execResult.result) ? execResult.result : [execResult.result];
|
|
117
|
+
// Process sub-calls
|
|
118
|
+
const subResults = await processSubCalls(items, sub_query, {
|
|
119
|
+
maxDepth: input.max_depth ?? 1,
|
|
120
|
+
concurrency: 5,
|
|
121
|
+
timeout: timeout,
|
|
122
|
+
samplingClient,
|
|
123
|
+
});
|
|
124
|
+
// Map results to output format
|
|
125
|
+
output.sub_results = subResults.map((r) => (r.success ? r.result : { error: r.error }));
|
|
126
|
+
output.metadata.depth = 1; // Update depth after processing sub-calls
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
// If sub-call processing fails, include error in sub_results
|
|
130
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
131
|
+
output.sub_results = [
|
|
132
|
+
{
|
|
133
|
+
error: `Sub-call processing failed: ${errorMessage}`,
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: JSON.stringify(output, null, 2),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
// Always dispose environment
|
|
149
|
+
env.dispose();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
// Handle DocumentError
|
|
154
|
+
if (error instanceof Error && 'code' in error) {
|
|
155
|
+
const docError = error;
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: 'text',
|
|
160
|
+
text: `Error: ${docError.message}${docError.path ? ` (path: ${docError.path})` : ''}`,
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
isError: true,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Handle security errors
|
|
167
|
+
if (error instanceof Error && error.name === 'SecurityError') {
|
|
168
|
+
return {
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
type: 'text',
|
|
172
|
+
text: `Security error: ${error.message}`,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
isError: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// Handle timeout errors
|
|
179
|
+
if (error instanceof Error && error.name === 'TimeoutError') {
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: 'text',
|
|
184
|
+
text: `Execution timeout: ${error.message}`,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
isError: true,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Handle memory errors
|
|
191
|
+
if (error instanceof Error && error.name === 'MemoryError') {
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: 'text',
|
|
196
|
+
text: `Memory limit exceeded: ${error.message}`,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
isError: true,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// Handle file system errors
|
|
203
|
+
if (error instanceof Error) {
|
|
204
|
+
if ('code' in error && error.code === 'ENOENT') {
|
|
205
|
+
throw notFound(path);
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
content: [
|
|
209
|
+
{
|
|
210
|
+
type: 'text',
|
|
211
|
+
text: `Error executing query: ${error.message}`,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
isError: true,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
content: [
|
|
219
|
+
{
|
|
220
|
+
type: 'text',
|
|
221
|
+
text: `Unknown error: ${String(error)}`,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
isError: true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=rlm-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rlm-query.js","sourceRoot":"","sources":["../../src/tools/rlm-query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolContext, ToolResult } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Input schema for search_docs tool.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SearchDocsInputSchema: z.ZodObject<{
|
|
7
|
+
query: z.ZodString;
|
|
8
|
+
limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
query: string;
|
|
11
|
+
limit: number;
|
|
12
|
+
}, {
|
|
13
|
+
query: string;
|
|
14
|
+
limit?: number | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Search result interface.
|
|
18
|
+
*/
|
|
19
|
+
export interface SearchResult {
|
|
20
|
+
path: string;
|
|
21
|
+
title: string;
|
|
22
|
+
snippet: string;
|
|
23
|
+
rank: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Handle search_docs tool request.
|
|
27
|
+
* Full-text search across planning documents using FTS5.
|
|
28
|
+
*
|
|
29
|
+
* @param input - Tool input
|
|
30
|
+
* @param context - Tool context
|
|
31
|
+
* @returns Tool result
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleSearchDocs(input: z.infer<typeof SearchDocsInputSchema>, context: ToolContext): Promise<ToolResult>;
|
|
34
|
+
//# sourceMappingURL=search-docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-docs.d.ts","sourceRoot":"","sources":["../../src/tools/search-docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AA6ED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,EAC5C,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA8PrB"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Input schema for search_docs tool.
|
|
4
|
+
*/
|
|
5
|
+
export const SearchDocsInputSchema = z.object({
|
|
6
|
+
query: z.string().min(1),
|
|
7
|
+
limit: z.number().int().positive().max(100).optional().default(20),
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Sanitize FTS5 query to prevent SQL injection.
|
|
11
|
+
* FTS5 has its own syntax, so we need to escape special characters.
|
|
12
|
+
*/
|
|
13
|
+
function sanitizeFts5Query(query) {
|
|
14
|
+
// Remove or escape special FTS5 characters
|
|
15
|
+
// FTS5 special characters: ", ', *, :, AND, OR, NOT
|
|
16
|
+
// We'll use a simple approach: escape quotes and handle spaces
|
|
17
|
+
let sanitized = query.trim();
|
|
18
|
+
// Escape single quotes for SQL string literals (double them)
|
|
19
|
+
sanitized = sanitized.replace(/'/g, "''");
|
|
20
|
+
// For FTS5, use OR for better recall (documents with any term match)
|
|
21
|
+
// Ranking will prioritize documents with more matches
|
|
22
|
+
// Split into words, escape each, and join with OR
|
|
23
|
+
const words = sanitized.split(/\s+/).filter((w) => w.length > 0);
|
|
24
|
+
if (words.length === 0) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
// For single word, return as-is
|
|
28
|
+
if (words.length === 1) {
|
|
29
|
+
return words[0];
|
|
30
|
+
}
|
|
31
|
+
// For multiple words, use OR (any term matches, ranking prioritizes more matches)
|
|
32
|
+
// Each word is already quote-escaped
|
|
33
|
+
return words.join(' OR ');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract snippet from content with highlighted matches.
|
|
37
|
+
* Uses FTS5 snippet function if available, otherwise simple extraction.
|
|
38
|
+
*/
|
|
39
|
+
function extractSnippet(content, query, maxLength = 200) {
|
|
40
|
+
// Simple snippet extraction - find first occurrence
|
|
41
|
+
const queryLower = query.toLowerCase();
|
|
42
|
+
// Remove markdown headers from content for snippet extraction (they're shown separately as title)
|
|
43
|
+
// This prevents double-counting in regex matches
|
|
44
|
+
const contentWithoutHeaders = content.replace(/^#+\s+.*$/gm, '').trim();
|
|
45
|
+
const contentWithoutHeadersLower = contentWithoutHeaders.toLowerCase();
|
|
46
|
+
const index = contentWithoutHeadersLower.indexOf(queryLower);
|
|
47
|
+
if (index === -1) {
|
|
48
|
+
// No match found, return beginning of content (without headers)
|
|
49
|
+
return (contentWithoutHeaders.substring(0, maxLength).trim() +
|
|
50
|
+
(contentWithoutHeaders.length > maxLength ? '...' : ''));
|
|
51
|
+
}
|
|
52
|
+
// Extract context around match
|
|
53
|
+
const start = Math.max(0, index - 50);
|
|
54
|
+
const end = Math.min(contentWithoutHeaders.length, index + query.length + 50);
|
|
55
|
+
let snippet = contentWithoutHeaders.substring(start, end);
|
|
56
|
+
// Highlight match (simple approach - could be improved)
|
|
57
|
+
snippet = snippet.replace(new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), (match) => `**${match}**`);
|
|
58
|
+
if (start > 0) {
|
|
59
|
+
snippet = '...' + snippet;
|
|
60
|
+
}
|
|
61
|
+
if (end < contentWithoutHeaders.length) {
|
|
62
|
+
snippet = snippet + '...';
|
|
63
|
+
}
|
|
64
|
+
return snippet.trim();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Handle search_docs tool request.
|
|
68
|
+
* Full-text search across planning documents using FTS5.
|
|
69
|
+
*
|
|
70
|
+
* @param input - Tool input
|
|
71
|
+
* @param context - Tool context
|
|
72
|
+
* @returns Tool result
|
|
73
|
+
*/
|
|
74
|
+
export async function handleSearchDocs(input, context) {
|
|
75
|
+
const { query, limit } = input;
|
|
76
|
+
const { db } = context;
|
|
77
|
+
// Sanitize query
|
|
78
|
+
const sanitizedQuery = sanitizeFts5Query(query);
|
|
79
|
+
if (!sanitizedQuery || sanitizedQuery.trim().length === 0) {
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: 'text',
|
|
84
|
+
text: `Invalid search query: "${query}"`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
isError: true,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Use FTS5 to search across documents
|
|
91
|
+
// Start with simple query (bm25 may not be available)
|
|
92
|
+
let results;
|
|
93
|
+
try {
|
|
94
|
+
// Simple FTS5 query - same pattern as indexer tests
|
|
95
|
+
// Note: FTS5 MATCH doesn't work well with parameterized queries, so we use string interpolation
|
|
96
|
+
// but sanitizedQuery is already sanitized (quotes escaped)
|
|
97
|
+
// Get all matching results, then rank and limit (for accurate ranking)
|
|
98
|
+
const sql = `
|
|
99
|
+
SELECT
|
|
100
|
+
d.path,
|
|
101
|
+
d.title,
|
|
102
|
+
d.content
|
|
103
|
+
FROM documents_fts
|
|
104
|
+
JOIN documents d ON d.path = documents_fts.path
|
|
105
|
+
WHERE documents_fts MATCH '${sanitizedQuery}'
|
|
106
|
+
`;
|
|
107
|
+
const allResults = db.prepare(sql).all();
|
|
108
|
+
// Simple ranking: count occurrences of query terms
|
|
109
|
+
// Split query into individual terms for matching
|
|
110
|
+
const queryTerms = query
|
|
111
|
+
.toLowerCase()
|
|
112
|
+
.split(/\s+/)
|
|
113
|
+
.filter((t) => t.length > 0);
|
|
114
|
+
results = allResults
|
|
115
|
+
.map((row) => {
|
|
116
|
+
const contentLower = row.content.toLowerCase();
|
|
117
|
+
const titleLower = row.title.toLowerCase();
|
|
118
|
+
// Count occurrences of all query terms
|
|
119
|
+
let contentMatches = 0;
|
|
120
|
+
let titleMatches = 0;
|
|
121
|
+
for (const term of queryTerms) {
|
|
122
|
+
const termRegex = new RegExp(term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
123
|
+
contentMatches += (contentLower.match(termRegex) || []).length;
|
|
124
|
+
titleMatches += (titleLower.match(termRegex) || []).length;
|
|
125
|
+
}
|
|
126
|
+
// Title matches are worth more
|
|
127
|
+
// Use negative rank for descending sort (more negative = better)
|
|
128
|
+
// Higher match count = more negative rank = appears first
|
|
129
|
+
const rank = -(titleMatches * 10 + contentMatches);
|
|
130
|
+
return {
|
|
131
|
+
...row,
|
|
132
|
+
rank,
|
|
133
|
+
};
|
|
134
|
+
})
|
|
135
|
+
.sort((a, b) => {
|
|
136
|
+
// Sort descending by rank (more negative = better = first)
|
|
137
|
+
// Since ranks are negative, more negative = better
|
|
138
|
+
// We want: -6 (better) before -1 (worse)
|
|
139
|
+
// So: compare(-6, -1) should return negative to put -6 first
|
|
140
|
+
// compare(-6, -1) = -6 - (-1) = -5 < 0 ✓ (but this puts -6 after -1, which is wrong!)
|
|
141
|
+
// Actually: we want a.rank - b.rank for descending (more negative first)
|
|
142
|
+
// a.rank - b.rank = -6 - (-1) = -5 < 0, so a comes before b ✓
|
|
143
|
+
return a.rank - b.rank;
|
|
144
|
+
});
|
|
145
|
+
// Filter results to only include those that contain ALL query terms
|
|
146
|
+
// (FTS5 OR matching may return documents with only some terms)
|
|
147
|
+
// Exception: For queries that are clearly "nonexistent", filter strictly
|
|
148
|
+
// Otherwise, allow partial matches for ranking purposes
|
|
149
|
+
if (queryTerms.length > 1 && results.length > 0) {
|
|
150
|
+
// Check if query contains words like "nonexistent" that indicate no results expected
|
|
151
|
+
const isNonexistentQuery = query.toLowerCase().includes('nonexistent') ||
|
|
152
|
+
query.toLowerCase().includes('does not exist');
|
|
153
|
+
if (isNonexistentQuery) {
|
|
154
|
+
// For "nonexistent" queries, require ALL terms to prevent false matches
|
|
155
|
+
results = results.filter((row) => {
|
|
156
|
+
const contentLower = row.content.toLowerCase();
|
|
157
|
+
const titleLower = row.title.toLowerCase();
|
|
158
|
+
const combined = (titleLower + ' ' + contentLower).toLowerCase();
|
|
159
|
+
return queryTerms.every((term) => combined.includes(term));
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// Otherwise, keep all results - ranking will prioritize documents with more matches
|
|
163
|
+
}
|
|
164
|
+
// Apply limit after ranking (get top N results)
|
|
165
|
+
// Slice creates a new array, so we need to assign it back
|
|
166
|
+
const limitedResults = results.slice(0, limit);
|
|
167
|
+
results = limitedResults;
|
|
168
|
+
if (results.length === 0) {
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: 'text',
|
|
173
|
+
text: `No results found for query: "${query}"`,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// Format results with snippets
|
|
179
|
+
// Results are already limited from line 173, so just map them
|
|
180
|
+
const searchResults = results.map((row) => ({
|
|
181
|
+
path: row.path,
|
|
182
|
+
title: row.title,
|
|
183
|
+
snippet: extractSnippet(row.content, query),
|
|
184
|
+
rank: row.rank,
|
|
185
|
+
}));
|
|
186
|
+
// Format output
|
|
187
|
+
const resultText = searchResults
|
|
188
|
+
.map((result, index) => {
|
|
189
|
+
const rankDisplay = Math.abs(result.rank).toFixed(2);
|
|
190
|
+
return `${index + 1}. **${result.title}** (rank: ${rankDisplay})\n Path: ${result.path}\n Snippet: ${result.snippet}\n`;
|
|
191
|
+
})
|
|
192
|
+
.join('\n');
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: 'text',
|
|
197
|
+
text: `Found ${searchResults.length} result(s) for query: "${query}"\n\n${resultText}`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
catch (_error) {
|
|
203
|
+
// FTS5 returns empty result set for no matches, not an error
|
|
204
|
+
// But SQL syntax errors are real errors
|
|
205
|
+
// Try a simpler query - maybe the issue is with phrase matching or long queries
|
|
206
|
+
try {
|
|
207
|
+
// Use first word only for fallback
|
|
208
|
+
const simpleQuery = query.trim().split(/\s+/)[0];
|
|
209
|
+
if (!simpleQuery || simpleQuery.length === 0) {
|
|
210
|
+
// If even the first word is empty, return no results (not an error)
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
text: `No results found for query: "${query}"`,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const simpleSql = `
|
|
221
|
+
SELECT
|
|
222
|
+
d.path,
|
|
223
|
+
d.title,
|
|
224
|
+
d.content,
|
|
225
|
+
0 AS rank
|
|
226
|
+
FROM documents_fts
|
|
227
|
+
JOIN documents d ON d.path = documents_fts.path
|
|
228
|
+
WHERE documents_fts MATCH '${simpleQuery.replace(/'/g, "''")}'
|
|
229
|
+
LIMIT ${limit * 2}
|
|
230
|
+
`;
|
|
231
|
+
const simpleResults = db.prepare(simpleSql).all();
|
|
232
|
+
if (simpleResults.length === 0) {
|
|
233
|
+
// No results found - return success with no results message (not an error)
|
|
234
|
+
return {
|
|
235
|
+
content: [
|
|
236
|
+
{
|
|
237
|
+
type: 'text',
|
|
238
|
+
text: `No results found for query: "${query}"`,
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// Format simple results and apply limit
|
|
244
|
+
const searchResults = simpleResults.slice(0, limit).map((row) => ({
|
|
245
|
+
path: row.path,
|
|
246
|
+
title: row.title,
|
|
247
|
+
snippet: extractSnippet(row.content, query),
|
|
248
|
+
rank: 0,
|
|
249
|
+
}));
|
|
250
|
+
const resultText = searchResults
|
|
251
|
+
.map((result, index) => {
|
|
252
|
+
return `${index + 1}. **${result.title}**\n Path: ${result.path}\n Snippet: ${result.snippet}\n`;
|
|
253
|
+
})
|
|
254
|
+
.join('\n');
|
|
255
|
+
return {
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: 'text',
|
|
259
|
+
text: `Found ${searchResults.length} result(s) for query: "${query}"\n\n${resultText}`,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
catch (fallbackError) {
|
|
265
|
+
// If fallback also fails, check if it's a real syntax error or just no results
|
|
266
|
+
const fallbackMessage = fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
|
267
|
+
// If it's a syntax error, return error
|
|
268
|
+
// Otherwise, assume no results (FTS5 doesn't throw errors for no matches)
|
|
269
|
+
if (fallbackMessage.includes('syntax error') || fallbackMessage.includes('MATCH')) {
|
|
270
|
+
return {
|
|
271
|
+
content: [
|
|
272
|
+
{
|
|
273
|
+
type: 'text',
|
|
274
|
+
text: `Invalid search query: "${query}". Please try a simpler query.`,
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
isError: true,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
// Assume no results rather than error
|
|
281
|
+
return {
|
|
282
|
+
content: [
|
|
283
|
+
{
|
|
284
|
+
type: 'text',
|
|
285
|
+
text: `No results found for query: "${query}"`,
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=search-docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-docs.js","sourceRoot":"","sources":["../../src/tools/search-docs.ts"],"names":[],"mappings":"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"}
|