@vpxa/kb 0.1.1 → 0.1.3
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/README.md +3 -3
- package/package.json +1 -1
- package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
- package/packages/analyzers/dist/dependency-analyzer.js +11 -425
- package/packages/analyzers/dist/diagram-generator.js +4 -86
- package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
- package/packages/analyzers/dist/index.js +1 -23
- package/packages/analyzers/dist/knowledge-producer.js +24 -113
- package/packages/analyzers/dist/pattern-analyzer.js +5 -359
- package/packages/analyzers/dist/regex-call-graph.js +1 -428
- package/packages/analyzers/dist/structure-analyzer.js +4 -258
- package/packages/analyzers/dist/symbol-analyzer.js +13 -442
- package/packages/analyzers/dist/ts-call-graph.js +1 -160
- package/packages/analyzers/dist/types.js +0 -1
- package/packages/chunker/dist/call-graph-extractor.js +1 -90
- package/packages/chunker/dist/chunker-factory.js +1 -36
- package/packages/chunker/dist/chunker.interface.js +0 -1
- package/packages/chunker/dist/code-chunker.js +14 -134
- package/packages/chunker/dist/generic-chunker.js +5 -72
- package/packages/chunker/dist/index.js +1 -21
- package/packages/chunker/dist/markdown-chunker.js +7 -119
- package/packages/chunker/dist/treesitter-chunker.js +8 -234
- package/packages/cli/dist/commands/analyze.js +3 -112
- package/packages/cli/dist/commands/context-cmds.js +1 -155
- package/packages/cli/dist/commands/environment.js +2 -204
- package/packages/cli/dist/commands/execution.js +1 -137
- package/packages/cli/dist/commands/graph.js +7 -81
- package/packages/cli/dist/commands/init.js +9 -87
- package/packages/cli/dist/commands/knowledge.js +1 -139
- package/packages/cli/dist/commands/search.js +8 -267
- package/packages/cli/dist/commands/system.js +4 -241
- package/packages/cli/dist/commands/workspace.js +2 -388
- package/packages/cli/dist/context.js +1 -14
- package/packages/cli/dist/helpers.js +3 -458
- package/packages/cli/dist/index.d.ts +1 -1
- package/packages/cli/dist/index.js +3 -69
- package/packages/cli/dist/kb-init.js +1 -82
- package/packages/cli/dist/types.js +0 -1
- package/packages/core/dist/constants.js +1 -43
- package/packages/core/dist/content-detector.js +1 -79
- package/packages/core/dist/errors.js +1 -40
- package/packages/core/dist/index.js +1 -9
- package/packages/core/dist/logger.js +1 -34
- package/packages/core/dist/types.js +0 -1
- package/packages/embeddings/dist/embedder.interface.js +0 -1
- package/packages/embeddings/dist/index.js +1 -5
- package/packages/embeddings/dist/onnx-embedder.js +1 -82
- package/packages/indexer/dist/file-hasher.js +1 -13
- package/packages/indexer/dist/filesystem-crawler.js +1 -125
- package/packages/indexer/dist/graph-extractor.js +1 -111
- package/packages/indexer/dist/incremental-indexer.js +1 -278
- package/packages/indexer/dist/index.js +1 -14
- package/packages/server/dist/api.js +1 -9
- package/packages/server/dist/config.js +1 -75
- package/packages/server/dist/curated-manager.js +9 -356
- package/packages/server/dist/index.js +1 -134
- package/packages/server/dist/replay-interceptor.js +1 -38
- package/packages/server/dist/resources/resources.js +2 -40
- package/packages/server/dist/server.js +1 -247
- package/packages/server/dist/tools/analyze.tools.js +1 -288
- package/packages/server/dist/tools/forge.tools.js +11 -499
- package/packages/server/dist/tools/forget.tool.js +3 -39
- package/packages/server/dist/tools/graph.tool.js +5 -110
- package/packages/server/dist/tools/list.tool.js +5 -53
- package/packages/server/dist/tools/lookup.tool.js +8 -51
- package/packages/server/dist/tools/onboard.tool.js +2 -112
- package/packages/server/dist/tools/produce.tool.js +4 -74
- package/packages/server/dist/tools/read.tool.js +4 -47
- package/packages/server/dist/tools/reindex.tool.js +2 -70
- package/packages/server/dist/tools/remember.tool.js +3 -42
- package/packages/server/dist/tools/replay.tool.js +6 -88
- package/packages/server/dist/tools/search.tool.js +17 -327
- package/packages/server/dist/tools/status.tool.js +3 -68
- package/packages/server/dist/tools/toolkit.tools.js +20 -1673
- package/packages/server/dist/tools/update.tool.js +3 -39
- package/packages/server/dist/tools/utility.tools.js +19 -456
- package/packages/store/dist/graph-store.interface.js +0 -1
- package/packages/store/dist/index.js +1 -9
- package/packages/store/dist/lance-store.js +1 -258
- package/packages/store/dist/sqlite-graph-store.js +8 -309
- package/packages/store/dist/store-factory.js +1 -14
- package/packages/store/dist/store.interface.js +0 -1
- package/packages/tools/dist/batch.js +1 -45
- package/packages/tools/dist/changelog.js +2 -112
- package/packages/tools/dist/check.js +2 -59
- package/packages/tools/dist/checkpoint.js +2 -43
- package/packages/tools/dist/codemod.js +2 -69
- package/packages/tools/dist/compact.js +3 -60
- package/packages/tools/dist/data-transform.js +1 -124
- package/packages/tools/dist/dead-symbols.js +2 -71
- package/packages/tools/dist/delegate.js +3 -128
- package/packages/tools/dist/diff-parse.js +3 -153
- package/packages/tools/dist/digest.js +7 -242
- package/packages/tools/dist/encode.js +1 -46
- package/packages/tools/dist/env-info.js +1 -58
- package/packages/tools/dist/eval.js +3 -79
- package/packages/tools/dist/evidence-map.js +3 -203
- package/packages/tools/dist/file-summary.js +2 -106
- package/packages/tools/dist/file-walk.js +1 -75
- package/packages/tools/dist/find-examples.js +3 -48
- package/packages/tools/dist/find.js +1 -120
- package/packages/tools/dist/forge-classify.js +2 -319
- package/packages/tools/dist/forge-ground.js +1 -184
- package/packages/tools/dist/git-context.js +3 -46
- package/packages/tools/dist/graph-query.js +1 -194
- package/packages/tools/dist/health.js +1 -118
- package/packages/tools/dist/http-request.js +1 -58
- package/packages/tools/dist/index.js +1 -273
- package/packages/tools/dist/lane.js +7 -227
- package/packages/tools/dist/measure.js +2 -119
- package/packages/tools/dist/onboard.js +42 -1136
- package/packages/tools/dist/parse-output.js +2 -158
- package/packages/tools/dist/process-manager.js +1 -69
- package/packages/tools/dist/queue.js +2 -126
- package/packages/tools/dist/regex-test.js +1 -39
- package/packages/tools/dist/rename.js +2 -70
- package/packages/tools/dist/replay.js +6 -108
- package/packages/tools/dist/schema-validate.js +1 -141
- package/packages/tools/dist/scope-map.js +1 -72
- package/packages/tools/dist/snippet.js +1 -80
- package/packages/tools/dist/stash.js +2 -60
- package/packages/tools/dist/stratum-card.js +5 -238
- package/packages/tools/dist/symbol.js +3 -87
- package/packages/tools/dist/test-run.js +2 -55
- package/packages/tools/dist/text-utils.js +2 -31
- package/packages/tools/dist/time-utils.js +1 -135
- package/packages/tools/dist/trace.js +2 -114
- package/packages/tools/dist/truncation.js +10 -41
- package/packages/tools/dist/watch.js +1 -61
- package/packages/tools/dist/web-fetch.js +9 -244
- package/packages/tools/dist/web-search.js +1 -46
- package/packages/tools/dist/workset.js +2 -77
- package/packages/tui/dist/App.js +260 -52468
- package/packages/tui/dist/index.js +286 -54551
- package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
- package/packages/tui/dist/panels/LogPanel.js +259 -51703
- package/packages/tui/dist/panels/SearchPanel.js +212 -34824
- package/packages/tui/dist/panels/StatusPanel.js +211 -34304
|
@@ -1,1674 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
compact,
|
|
11
|
-
dataTransform,
|
|
12
|
-
delegate,
|
|
13
|
-
delegateListModels,
|
|
14
|
-
deleteWorkset,
|
|
15
|
-
diffParse,
|
|
16
|
-
evaluate,
|
|
17
|
-
fileSummary,
|
|
18
|
-
find,
|
|
19
|
-
findDeadSymbols,
|
|
20
|
-
findExamples,
|
|
21
|
-
getWorkset,
|
|
22
|
-
gitContext,
|
|
23
|
-
health,
|
|
24
|
-
laneCreate,
|
|
25
|
-
laneDiff,
|
|
26
|
-
laneDiscard,
|
|
27
|
-
laneList,
|
|
28
|
-
laneMerge,
|
|
29
|
-
laneStatus,
|
|
30
|
-
listWorksets,
|
|
31
|
-
parseOutput,
|
|
32
|
-
processList,
|
|
33
|
-
processLogs,
|
|
34
|
-
processStart,
|
|
35
|
-
processStatus,
|
|
36
|
-
processStop,
|
|
37
|
-
queueClear,
|
|
38
|
-
queueCreate,
|
|
39
|
-
queueDelete,
|
|
40
|
-
queueDone,
|
|
41
|
-
queueFail,
|
|
42
|
-
queueGet,
|
|
43
|
-
queueList,
|
|
44
|
-
queueNext,
|
|
45
|
-
queuePush,
|
|
46
|
-
removeFromWorkset,
|
|
47
|
-
rename,
|
|
48
|
-
saveWorkset,
|
|
49
|
-
scopeMap,
|
|
50
|
-
stashClear,
|
|
51
|
-
stashDelete,
|
|
52
|
-
stashGet,
|
|
53
|
-
stashList,
|
|
54
|
-
stashSet,
|
|
55
|
-
symbol,
|
|
56
|
-
testRun,
|
|
57
|
-
trace,
|
|
58
|
-
watchList,
|
|
59
|
-
watchStart,
|
|
60
|
-
watchStop,
|
|
61
|
-
webFetch
|
|
62
|
-
} from "@kb/tools";
|
|
63
|
-
import { z } from "zod";
|
|
64
|
-
function registerCompactTool(server, embedder) {
|
|
65
|
-
server.registerTool(
|
|
66
|
-
"compact",
|
|
67
|
-
{
|
|
68
|
-
description: "Compress text to relevant sections using embedding similarity (no LLM). Ideal for reducing file contents before context injection. Segments by paragraph/sentence/line.",
|
|
69
|
-
inputSchema: {
|
|
70
|
-
text: z.string().describe("The text to compress"),
|
|
71
|
-
query: z.string().describe("Focus query \u2014 what are you trying to understand?"),
|
|
72
|
-
max_chars: z.number().min(100).max(5e4).default(3e3).describe("Target output size in characters"),
|
|
73
|
-
segmentation: z.enum(["paragraph", "sentence", "line"]).default("paragraph").describe("How to split the text for scoring")
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
async ({ text, query, max_chars, segmentation }) => {
|
|
77
|
-
try {
|
|
78
|
-
const result = await compact(embedder, {
|
|
79
|
-
text,
|
|
80
|
-
query,
|
|
81
|
-
maxChars: max_chars,
|
|
82
|
-
segmentation
|
|
83
|
-
});
|
|
84
|
-
const summary = [
|
|
85
|
-
`Compressed ${result.originalChars} \u2192 ${result.compressedChars} chars (${(result.ratio * 100).toFixed(0)}%)`,
|
|
86
|
-
`Kept ${result.segmentsKept}/${result.segmentsTotal} segments`,
|
|
87
|
-
"",
|
|
88
|
-
result.text
|
|
89
|
-
].join("\n");
|
|
90
|
-
return {
|
|
91
|
-
content: [{ type: "text", text: summary }]
|
|
92
|
-
};
|
|
93
|
-
} catch (err) {
|
|
94
|
-
return {
|
|
95
|
-
content: [{ type: "text", text: `Compact failed: ${err.message}` }],
|
|
96
|
-
isError: true
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
function registerScopeMapTool(server, embedder, store) {
|
|
103
|
-
server.registerTool(
|
|
104
|
-
"scope_map",
|
|
105
|
-
{
|
|
106
|
-
description: "Generate a task-scoped reading plan. Given a task description, identifies which files and sections are relevant, with estimated token counts and suggested reading order.",
|
|
107
|
-
inputSchema: {
|
|
108
|
-
task: z.string().describe("Description of the task to scope"),
|
|
109
|
-
max_files: z.number().min(1).max(50).default(15).describe("Maximum files to include"),
|
|
110
|
-
content_type: z.string().optional().describe("Filter by content type")
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
async ({ task, max_files, content_type }) => {
|
|
114
|
-
try {
|
|
115
|
-
const result = await scopeMap(embedder, store, {
|
|
116
|
-
task,
|
|
117
|
-
maxFiles: max_files,
|
|
118
|
-
contentType: content_type
|
|
119
|
-
});
|
|
120
|
-
const lines = [
|
|
121
|
-
`## Scope Map: ${task}`,
|
|
122
|
-
`Total estimated tokens: ~${result.totalEstimatedTokens}`,
|
|
123
|
-
"",
|
|
124
|
-
"### Files (by relevance)",
|
|
125
|
-
...result.files.map(
|
|
126
|
-
(f, i) => `${i + 1}. **${f.path}** (~${f.estimatedTokens} tokens, ${(f.relevance * 100).toFixed(0)}% relevant)
|
|
127
|
-
${f.reason}
|
|
128
|
-
Focus: ${f.focusRanges.map((r) => `L${r.start}-${r.end}`).join(", ")}`
|
|
129
|
-
),
|
|
130
|
-
"",
|
|
131
|
-
"### Suggested Reading Order",
|
|
132
|
-
...result.readingOrder.map((p, i) => `${i + 1}. ${p}`)
|
|
133
|
-
];
|
|
134
|
-
return {
|
|
135
|
-
content: [
|
|
136
|
-
{
|
|
137
|
-
type: "text",
|
|
138
|
-
text: lines.join("\n") + "\n\n---\n_Next: Use `search` to dive into specific files, or `compact` to compress file contents for context._"
|
|
139
|
-
}
|
|
140
|
-
]
|
|
141
|
-
};
|
|
142
|
-
} catch (err) {
|
|
143
|
-
return {
|
|
144
|
-
content: [{ type: "text", text: `Scope map failed: ${err.message}` }],
|
|
145
|
-
isError: true
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
function registerFindTool(server, embedder, store) {
|
|
152
|
-
server.registerTool(
|
|
153
|
-
"find",
|
|
154
|
-
{
|
|
155
|
-
description: "Federated search across vector similarity, keyword (FTS), file glob, and regex pattern. Combines strategies, deduplicates, and returns unified results.",
|
|
156
|
-
inputSchema: {
|
|
157
|
-
query: z.string().optional().describe("Semantic/keyword search query"),
|
|
158
|
-
glob: z.string().optional().describe("File glob pattern"),
|
|
159
|
-
pattern: z.string().optional().describe("Regex pattern to match in content"),
|
|
160
|
-
limit: z.number().min(1).max(50).default(10).describe("Max results"),
|
|
161
|
-
content_type: z.string().optional().describe("Filter by content type")
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
async ({ query, glob, pattern, limit, content_type }) => {
|
|
165
|
-
try {
|
|
166
|
-
const result = await find(embedder, store, {
|
|
167
|
-
query,
|
|
168
|
-
glob,
|
|
169
|
-
pattern,
|
|
170
|
-
limit,
|
|
171
|
-
contentType: content_type
|
|
172
|
-
});
|
|
173
|
-
if (result.results.length === 0) {
|
|
174
|
-
return {
|
|
175
|
-
content: [{ type: "text", text: "No results found." }]
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
const lines = [
|
|
179
|
-
`Found ${result.totalFound} results via ${result.strategies.join(" + ")}`,
|
|
180
|
-
"",
|
|
181
|
-
...result.results.map((r) => {
|
|
182
|
-
const loc = r.lineRange ? `:${r.lineRange.start}-${r.lineRange.end}` : "";
|
|
183
|
-
const preview = r.preview ? `
|
|
184
|
-
${r.preview.slice(0, 100)}...` : "";
|
|
185
|
-
return `- [${r.source}] ${r.path}${loc} (${(r.score * 100).toFixed(0)}%)${preview}`;
|
|
186
|
-
})
|
|
187
|
-
];
|
|
188
|
-
return {
|
|
189
|
-
content: [{ type: "text", text: lines.join("\n") }]
|
|
190
|
-
};
|
|
191
|
-
} catch (err) {
|
|
192
|
-
return {
|
|
193
|
-
content: [{ type: "text", text: `Find failed: ${err.message}` }],
|
|
194
|
-
isError: true
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
function registerParseOutputTool(server) {
|
|
201
|
-
server.registerTool(
|
|
202
|
-
"parse_output",
|
|
203
|
-
{
|
|
204
|
-
description: "Parse structured data from build tool output. Supports tsc, vitest, biome, and git status. Auto-detects the tool or specify explicitly.",
|
|
205
|
-
inputSchema: {
|
|
206
|
-
output: z.string().describe("Raw output text from a build tool"),
|
|
207
|
-
tool: z.enum(["tsc", "vitest", "biome", "git-status"]).optional().describe("Tool to parse as (auto-detects if omitted)")
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
async ({ output, tool }) => {
|
|
211
|
-
try {
|
|
212
|
-
const normalizedOutput = output.replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
213
|
-
const result = parseOutput(normalizedOutput, tool);
|
|
214
|
-
return {
|
|
215
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
216
|
-
};
|
|
217
|
-
} catch (err) {
|
|
218
|
-
return {
|
|
219
|
-
content: [{ type: "text", text: `Parse failed: ${err.message}` }],
|
|
220
|
-
isError: true
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
function registerWorksetTool(server) {
|
|
227
|
-
server.registerTool(
|
|
228
|
-
"workset",
|
|
229
|
-
{
|
|
230
|
-
description: "Manage named file sets (worksets). Save, load, list, add/remove files. Worksets persist across sessions in .kb-state/worksets.json.",
|
|
231
|
-
inputSchema: {
|
|
232
|
-
action: z.enum(["save", "get", "list", "delete", "add", "remove"]).describe("Operation to perform"),
|
|
233
|
-
name: z.string().optional().describe("Workset name (required for all except list)"),
|
|
234
|
-
files: z.array(z.string()).optional().describe("File paths (required for save, add, remove)"),
|
|
235
|
-
description: z.string().optional().describe("Description (for save)")
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
async ({ action, name, files, description }) => {
|
|
239
|
-
try {
|
|
240
|
-
switch (action) {
|
|
241
|
-
case "save": {
|
|
242
|
-
if (!name || !files) throw new Error("name and files required for save");
|
|
243
|
-
const ws = saveWorkset(name, files, { description });
|
|
244
|
-
return {
|
|
245
|
-
content: [
|
|
246
|
-
{
|
|
247
|
-
type: "text",
|
|
248
|
-
text: `Saved workset "${ws.name}" with ${ws.files.length} files.`
|
|
249
|
-
}
|
|
250
|
-
]
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
case "get": {
|
|
254
|
-
if (!name) throw new Error("name required for get");
|
|
255
|
-
const ws = getWorkset(name);
|
|
256
|
-
if (!ws)
|
|
257
|
-
return { content: [{ type: "text", text: `Workset "${name}" not found.` }] };
|
|
258
|
-
return {
|
|
259
|
-
content: [{ type: "text", text: JSON.stringify(ws, null, 2) }]
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
case "list": {
|
|
263
|
-
const all = listWorksets();
|
|
264
|
-
if (all.length === 0)
|
|
265
|
-
return { content: [{ type: "text", text: "No worksets." }] };
|
|
266
|
-
const text = all.map(
|
|
267
|
-
(w) => `- **${w.name}** (${w.files.length} files) \u2014 ${w.description ?? "no description"}`
|
|
268
|
-
).join("\n");
|
|
269
|
-
return { content: [{ type: "text", text }] };
|
|
270
|
-
}
|
|
271
|
-
case "delete": {
|
|
272
|
-
if (!name) throw new Error("name required for delete");
|
|
273
|
-
const deleted = deleteWorkset(name);
|
|
274
|
-
return {
|
|
275
|
-
content: [
|
|
276
|
-
{
|
|
277
|
-
type: "text",
|
|
278
|
-
text: deleted ? `Deleted workset "${name}".` : `Workset "${name}" not found.`
|
|
279
|
-
}
|
|
280
|
-
]
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
case "add": {
|
|
284
|
-
if (!name || !files) throw new Error("name and files required for add");
|
|
285
|
-
const ws = addToWorkset(name, files);
|
|
286
|
-
return {
|
|
287
|
-
content: [
|
|
288
|
-
{
|
|
289
|
-
type: "text",
|
|
290
|
-
text: `Added to workset "${ws.name}": now ${ws.files.length} files.`
|
|
291
|
-
}
|
|
292
|
-
]
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
case "remove": {
|
|
296
|
-
if (!name || !files) throw new Error("name and files required for remove");
|
|
297
|
-
const ws = removeFromWorkset(name, files);
|
|
298
|
-
if (!ws)
|
|
299
|
-
return { content: [{ type: "text", text: `Workset "${name}" not found.` }] };
|
|
300
|
-
return {
|
|
301
|
-
content: [
|
|
302
|
-
{
|
|
303
|
-
type: "text",
|
|
304
|
-
text: `Removed from workset "${ws.name}": now ${ws.files.length} files.`
|
|
305
|
-
}
|
|
306
|
-
]
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
} catch (err) {
|
|
311
|
-
return {
|
|
312
|
-
content: [
|
|
313
|
-
{ type: "text", text: `Workset operation failed: ${err.message}` }
|
|
314
|
-
],
|
|
315
|
-
isError: true
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
function registerCheckTool(server) {
|
|
322
|
-
server.registerTool(
|
|
323
|
-
"check",
|
|
324
|
-
{
|
|
325
|
-
description: "Run incremental typecheck (tsc) and lint (biome) on the project or specific files. Returns structured error and warning lists.",
|
|
326
|
-
inputSchema: {
|
|
327
|
-
files: z.array(z.string()).optional().describe("Specific files to check (if omitted, checks all)"),
|
|
328
|
-
cwd: z.string().optional().describe("Working directory"),
|
|
329
|
-
skip_types: z.boolean().default(false).describe("Skip TypeScript typecheck"),
|
|
330
|
-
skip_lint: z.boolean().default(false).describe("Skip Biome lint")
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
async ({ files, cwd, skip_types, skip_lint }) => {
|
|
334
|
-
try {
|
|
335
|
-
const result = await check({
|
|
336
|
-
files,
|
|
337
|
-
cwd,
|
|
338
|
-
skipTypes: skip_types,
|
|
339
|
-
skipLint: skip_lint
|
|
340
|
-
});
|
|
341
|
-
return {
|
|
342
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
343
|
-
};
|
|
344
|
-
} catch (err) {
|
|
345
|
-
return {
|
|
346
|
-
content: [{ type: "text", text: `Check failed: ${err.message}` }],
|
|
347
|
-
isError: true
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
function registerBatchTool(server, embedder, store) {
|
|
354
|
-
server.registerTool(
|
|
355
|
-
"batch",
|
|
356
|
-
{
|
|
357
|
-
description: "Execute multiple built-in operations in parallel with concurrency control. Supported operation types: search, find, and check.",
|
|
358
|
-
inputSchema: {
|
|
359
|
-
operations: z.array(
|
|
360
|
-
z.object({
|
|
361
|
-
id: z.string().describe("Unique ID for this operation"),
|
|
362
|
-
type: z.enum(["search", "find", "check"]).describe("Built-in operation type"),
|
|
363
|
-
args: z.record(z.string(), z.unknown()).describe("Arguments for the operation")
|
|
364
|
-
})
|
|
365
|
-
).min(1).describe("Operations to execute"),
|
|
366
|
-
concurrency: z.number().min(1).max(20).default(4).describe("Max concurrent operations")
|
|
367
|
-
}
|
|
368
|
-
},
|
|
369
|
-
async ({ operations, concurrency }) => {
|
|
370
|
-
try {
|
|
371
|
-
const result = await batch(
|
|
372
|
-
operations,
|
|
373
|
-
async (operation) => executeToolkitBatchOperation(operation, embedder, store),
|
|
374
|
-
{ concurrency }
|
|
375
|
-
);
|
|
376
|
-
return {
|
|
377
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
378
|
-
};
|
|
379
|
-
} catch (err) {
|
|
380
|
-
return {
|
|
381
|
-
content: [{ type: "text", text: `Batch failed: ${err.message}` }],
|
|
382
|
-
isError: true
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
function registerSymbolTool(server, embedder, store) {
|
|
389
|
-
server.registerTool(
|
|
390
|
-
"symbol",
|
|
391
|
-
{
|
|
392
|
-
description: "Resolve a symbol: find where it is defined, who imports it, and where it is referenced. Works on TypeScript and JavaScript codebases.",
|
|
393
|
-
inputSchema: {
|
|
394
|
-
name: z.string().describe("Symbol name to look up (function, class, type, etc.)"),
|
|
395
|
-
limit: z.number().min(1).max(50).default(20).describe("Max results per category")
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
async ({ name, limit }) => {
|
|
399
|
-
try {
|
|
400
|
-
const result = await symbol(embedder, store, { name, limit });
|
|
401
|
-
return {
|
|
402
|
-
content: [{ type: "text", text: formatSymbolInfo(result) }]
|
|
403
|
-
};
|
|
404
|
-
} catch (err) {
|
|
405
|
-
return {
|
|
406
|
-
content: [
|
|
407
|
-
{ type: "text", text: `Symbol lookup failed: ${err.message}` }
|
|
408
|
-
],
|
|
409
|
-
isError: true
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
function registerEvalTool(server) {
|
|
416
|
-
server.registerTool(
|
|
417
|
-
"eval",
|
|
418
|
-
{
|
|
419
|
-
description: "Execute a JavaScript or TypeScript snippet in a constrained VM sandbox with a timeout. Captures console output and returned values.",
|
|
420
|
-
inputSchema: {
|
|
421
|
-
code: z.string().describe("Code snippet to execute"),
|
|
422
|
-
lang: z.enum(["js", "ts"]).default("js").optional().describe("Language mode: js executes directly, ts strips common type syntax first"),
|
|
423
|
-
timeout: z.number().min(1).max(6e4).default(5e3).optional().describe("Execution timeout in milliseconds")
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
async ({ code, lang, timeout }) => {
|
|
427
|
-
try {
|
|
428
|
-
const result = evaluate({ code, lang, timeout });
|
|
429
|
-
if (!result.success) {
|
|
430
|
-
return {
|
|
431
|
-
content: [
|
|
432
|
-
{
|
|
433
|
-
type: "text",
|
|
434
|
-
text: `Eval failed in ${result.durationMs}ms: ${result.error ?? "Unknown error"}`
|
|
435
|
-
}
|
|
436
|
-
],
|
|
437
|
-
isError: true
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
return {
|
|
441
|
-
content: [
|
|
442
|
-
{
|
|
443
|
-
type: "text",
|
|
444
|
-
text: `Eval succeeded in ${result.durationMs}ms
|
|
1
|
+
import{addToWorkset as x,batch as b,check as m,checkpointLatest as w,checkpointList as S,checkpointLoad as $,checkpointSave as k,codemod as E,compact as v,dataTransform as T,delegate as q,delegateListModels as O,deleteWorkset as M,diffParse as N,evaluate as _,fileSummary as R,find as g,findDeadSymbols as I,findExamples as J,getWorkset as C,gitContext as j,health as L,laneCreate as F,laneDiff as D,laneDiscard as A,laneList as P,laneMerge as W,laneStatus as K,listWorksets as U,parseOutput as B,processList as z,processLogs as G,processStart as Q,processStatus as H,processStop as V,queueClear as X,queueCreate as Y,queueDelete as Z,queueDone as ee,queueFail as te,queueGet as re,queueList as ne,queueNext as oe,queuePush as se,removeFromWorkset as ie,rename as ae,saveWorkset as ce,scopeMap as le,stashClear as de,stashDelete as ue,stashGet as pe,stashList as fe,stashSet as me,symbol as ge,testRun as he,trace as ye,watchList as xe,watchStart as be,watchStop as we,webFetch as Se}from"../../../tools/dist/index.js";import{z as r}from"zod";function Le(t,n){t.registerTool("compact",{description:"Compress text to relevant sections using embedding similarity (no LLM). Ideal for reducing file contents before context injection. Segments by paragraph/sentence/line.",inputSchema:{text:r.string().describe("The text to compress"),query:r.string().describe("Focus query \u2014 what are you trying to understand?"),max_chars:r.number().min(100).max(5e4).default(3e3).describe("Target output size in characters"),segmentation:r.enum(["paragraph","sentence","line"]).default("paragraph").describe("How to split the text for scoring")}},async({text:e,query:s,max_chars:o,segmentation:i})=>{try{const a=await v(n,{text:e,query:s,maxChars:o,segmentation:i});return{content:[{type:"text",text:[`Compressed ${a.originalChars} \u2192 ${a.compressedChars} chars (${(a.ratio*100).toFixed(0)}%)`,`Kept ${a.segmentsKept}/${a.segmentsTotal} segments`,"",a.text].join(`
|
|
2
|
+
`)}]}}catch(a){return{content:[{type:"text",text:`Compact failed: ${a.message}`}],isError:!0}}})}function Fe(t,n,e){t.registerTool("scope_map",{description:"Generate a task-scoped reading plan. Given a task description, identifies which files and sections are relevant, with estimated token counts and suggested reading order.",inputSchema:{task:r.string().describe("Description of the task to scope"),max_files:r.number().min(1).max(50).default(15).describe("Maximum files to include"),content_type:r.string().optional().describe("Filter by content type")}},async({task:s,max_files:o,content_type:i})=>{try{const a=await le(n,e,{task:s,maxFiles:o,contentType:i});return{content:[{type:"text",text:[`## Scope Map: ${s}`,`Total estimated tokens: ~${a.totalEstimatedTokens}`,"","### Files (by relevance)",...a.files.map((c,u)=>`${u+1}. **${c.path}** (~${c.estimatedTokens} tokens, ${(c.relevance*100).toFixed(0)}% relevant)
|
|
3
|
+
${c.reason}
|
|
4
|
+
Focus: ${c.focusRanges.map(d=>`L${d.start}-${d.end}`).join(", ")}`),"","### Suggested Reading Order",...a.readingOrder.map((c,u)=>`${u+1}. ${c}`)].join(`
|
|
5
|
+
`)+"\n\n---\n_Next: Use `search` to dive into specific files, or `compact` to compress file contents for context._"}]}}catch(a){return{content:[{type:"text",text:`Scope map failed: ${a.message}`}],isError:!0}}})}function De(t,n,e){t.registerTool("find",{description:"Federated search across vector similarity, keyword (FTS), file glob, and regex pattern. Combines strategies, deduplicates, and returns unified results.",inputSchema:{query:r.string().optional().describe("Semantic/keyword search query"),glob:r.string().optional().describe("File glob pattern"),pattern:r.string().optional().describe("Regex pattern to match in content"),limit:r.number().min(1).max(50).default(10).describe("Max results"),content_type:r.string().optional().describe("Filter by content type")}},async({query:s,glob:o,pattern:i,limit:a,content_type:l})=>{try{const c=await g(n,e,{query:s,glob:o,pattern:i,limit:a,contentType:l});return c.results.length===0?{content:[{type:"text",text:"No results found."}]}:{content:[{type:"text",text:[`Found ${c.totalFound} results via ${c.strategies.join(" + ")}`,"",...c.results.map(d=>{const h=d.lineRange?`:${d.lineRange.start}-${d.lineRange.end}`:"",y=d.preview?`
|
|
6
|
+
${d.preview.slice(0,100)}...`:"";return`- [${d.source}] ${d.path}${h} (${(d.score*100).toFixed(0)}%)${y}`})].join(`
|
|
7
|
+
`)}]}}catch(c){return{content:[{type:"text",text:`Find failed: ${c.message}`}],isError:!0}}})}function Ae(t){t.registerTool("parse_output",{description:"Parse structured data from build tool output. Supports tsc, vitest, biome, and git status. Auto-detects the tool or specify explicitly.",inputSchema:{output:r.string().describe("Raw output text from a build tool"),tool:r.enum(["tsc","vitest","biome","git-status"]).optional().describe("Tool to parse as (auto-detects if omitted)")}},async({output:n,tool:e})=>{try{const s=n.replace(/\\n/g,`
|
|
8
|
+
`).replace(/\\t/g," "),o=B(s,e);return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(s){return{content:[{type:"text",text:`Parse failed: ${s.message}`}],isError:!0}}})}function Pe(t){t.registerTool("workset",{description:"Manage named file sets (worksets). Save, load, list, add/remove files. Worksets persist across sessions in .kb-state/worksets.json.",inputSchema:{action:r.enum(["save","get","list","delete","add","remove"]).describe("Operation to perform"),name:r.string().optional().describe("Workset name (required for all except list)"),files:r.array(r.string()).optional().describe("File paths (required for save, add, remove)"),description:r.string().optional().describe("Description (for save)")}},async({action:n,name:e,files:s,description:o})=>{try{switch(n){case"save":{if(!e||!s)throw new Error("name and files required for save");const i=ce(e,s,{description:o});return{content:[{type:"text",text:`Saved workset "${i.name}" with ${i.files.length} files.`}]}}case"get":{if(!e)throw new Error("name required for get");const i=C(e);return i?{content:[{type:"text",text:JSON.stringify(i,null,2)}]}:{content:[{type:"text",text:`Workset "${e}" not found.`}]}}case"list":{const i=U();return i.length===0?{content:[{type:"text",text:"No worksets."}]}:{content:[{type:"text",text:i.map(l=>`- **${l.name}** (${l.files.length} files) \u2014 ${l.description??"no description"}`).join(`
|
|
9
|
+
`)}]}}case"delete":{if(!e)throw new Error("name required for delete");return{content:[{type:"text",text:M(e)?`Deleted workset "${e}".`:`Workset "${e}" not found.`}]}}case"add":{if(!e||!s)throw new Error("name and files required for add");const i=x(e,s);return{content:[{type:"text",text:`Added to workset "${i.name}": now ${i.files.length} files.`}]}}case"remove":{if(!e||!s)throw new Error("name and files required for remove");const i=ie(e,s);return i?{content:[{type:"text",text:`Removed from workset "${i.name}": now ${i.files.length} files.`}]}:{content:[{type:"text",text:`Workset "${e}" not found.`}]}}}}catch(i){return{content:[{type:"text",text:`Workset operation failed: ${i.message}`}],isError:!0}}})}function We(t){t.registerTool("check",{description:"Run incremental typecheck (tsc) and lint (biome) on the project or specific files. Returns structured error and warning lists.",inputSchema:{files:r.array(r.string()).optional().describe("Specific files to check (if omitted, checks all)"),cwd:r.string().optional().describe("Working directory"),skip_types:r.boolean().default(!1).describe("Skip TypeScript typecheck"),skip_lint:r.boolean().default(!1).describe("Skip Biome lint")}},async({files:n,cwd:e,skip_types:s,skip_lint:o})=>{try{const i=await m({files:n,cwd:e,skipTypes:s,skipLint:o});return{content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return{content:[{type:"text",text:`Check failed: ${i.message}`}],isError:!0}}})}function Ke(t,n,e){t.registerTool("batch",{description:"Execute multiple built-in operations in parallel with concurrency control. Supported operation types: search, find, and check.",inputSchema:{operations:r.array(r.object({id:r.string().describe("Unique ID for this operation"),type:r.enum(["search","find","check"]).describe("Built-in operation type"),args:r.record(r.string(),r.unknown()).describe("Arguments for the operation")})).min(1).describe("Operations to execute"),concurrency:r.number().min(1).max(20).default(4).describe("Max concurrent operations")}},async({operations:s,concurrency:o})=>{try{const i=await b(s,async a=>ve(a,n,e),{concurrency:o});return{content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(i){return{content:[{type:"text",text:`Batch failed: ${i.message}`}],isError:!0}}})}function Ue(t,n,e){t.registerTool("symbol",{description:"Resolve a symbol: find where it is defined, who imports it, and where it is referenced. Works on TypeScript and JavaScript codebases.",inputSchema:{name:r.string().describe("Symbol name to look up (function, class, type, etc.)"),limit:r.number().min(1).max(50).default(20).describe("Max results per category")}},async({name:s,limit:o})=>{try{const i=await ge(n,e,{name:s,limit:o});return{content:[{type:"text",text:Oe(i)}]}}catch(i){return{content:[{type:"text",text:`Symbol lookup failed: ${i.message}`}],isError:!0}}})}function Be(t){t.registerTool("eval",{description:"Execute a JavaScript or TypeScript snippet in a constrained VM sandbox with a timeout. Captures console output and returned values.",inputSchema:{code:r.string().describe("Code snippet to execute"),lang:r.enum(["js","ts"]).default("js").optional().describe("Language mode: js executes directly, ts strips common type syntax first"),timeout:r.number().min(1).max(6e4).default(5e3).optional().describe("Execution timeout in milliseconds")}},async({code:n,lang:e,timeout:s})=>{try{const o=_({code:n,lang:e,timeout:s});return o.success?{content:[{type:"text",text:`Eval succeeded in ${o.durationMs}ms
|
|
445
10
|
|
|
446
|
-
${
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
function registerTestRunTool(server) {
|
|
461
|
-
server.registerTool(
|
|
462
|
-
"test_run",
|
|
463
|
-
{
|
|
464
|
-
description: "Run Vitest for the current project or a subset of files, then return a structured summary of passing and failing tests.",
|
|
465
|
-
inputSchema: {
|
|
466
|
-
files: z.array(z.string()).optional().describe("Specific test files or patterns to run"),
|
|
467
|
-
grep: z.string().optional().describe("Only run tests whose names match this pattern"),
|
|
468
|
-
cwd: z.string().optional().describe("Working directory for the test run")
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
|
-
async ({ files, grep, cwd }) => {
|
|
472
|
-
try {
|
|
473
|
-
const result = await testRun({ files, grep, cwd });
|
|
474
|
-
return {
|
|
475
|
-
content: [{ type: "text", text: formatTestRunResult(result) }],
|
|
476
|
-
isError: !result.passed
|
|
477
|
-
};
|
|
478
|
-
} catch (err) {
|
|
479
|
-
return {
|
|
480
|
-
content: [{ type: "text", text: `Test run failed: ${err.message}` }],
|
|
481
|
-
isError: true
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
function registerStashTool(server) {
|
|
488
|
-
server.registerTool(
|
|
489
|
-
"stash",
|
|
490
|
-
{
|
|
491
|
-
description: "Persist and retrieve named values in .kb-state/stash.json for intermediate results between tool calls.",
|
|
492
|
-
inputSchema: {
|
|
493
|
-
action: z.enum(["set", "get", "list", "delete", "clear"]).describe("Operation to perform on the stash"),
|
|
494
|
-
key: z.string().optional().describe("Entry key for set/get/delete operations"),
|
|
495
|
-
value: z.string().optional().describe("String or JSON value for set operations")
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
async ({ action, key, value }) => {
|
|
499
|
-
try {
|
|
500
|
-
switch (action) {
|
|
501
|
-
case "set": {
|
|
502
|
-
if (!key) throw new Error("key required for set");
|
|
503
|
-
const entry = stashSet(key, parseMaybeJsonString(value ?? ""));
|
|
504
|
-
return {
|
|
505
|
-
content: [
|
|
506
|
-
{
|
|
507
|
-
type: "text",
|
|
508
|
-
text: `Stored stash entry "${entry.key}" (${entry.type}) at ${entry.storedAt}.`
|
|
509
|
-
}
|
|
510
|
-
]
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
case "get": {
|
|
514
|
-
if (!key) throw new Error("key required for get");
|
|
515
|
-
const entry = stashGet(key);
|
|
516
|
-
return {
|
|
517
|
-
content: [
|
|
518
|
-
{
|
|
519
|
-
type: "text",
|
|
520
|
-
text: entry ? JSON.stringify(entry, null, 2) : `Stash entry "${key}" not found.`
|
|
521
|
-
}
|
|
522
|
-
]
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
case "list": {
|
|
526
|
-
const entries = stashList();
|
|
527
|
-
return {
|
|
528
|
-
content: [
|
|
529
|
-
{
|
|
530
|
-
type: "text",
|
|
531
|
-
text: entries.length === 0 ? "Stash is empty." : entries.map((entry) => `- ${entry.key} (${entry.type}) \u2014 ${entry.storedAt}`).join("\n")
|
|
532
|
-
}
|
|
533
|
-
]
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
case "delete": {
|
|
537
|
-
if (!key) throw new Error("key required for delete");
|
|
538
|
-
const deleted = stashDelete(key);
|
|
539
|
-
return {
|
|
540
|
-
content: [
|
|
541
|
-
{
|
|
542
|
-
type: "text",
|
|
543
|
-
text: deleted ? `Deleted stash entry "${key}".` : `Stash entry "${key}" not found.`
|
|
544
|
-
}
|
|
545
|
-
]
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
case "clear": {
|
|
549
|
-
const count = stashClear();
|
|
550
|
-
return {
|
|
551
|
-
content: [
|
|
552
|
-
{
|
|
553
|
-
type: "text",
|
|
554
|
-
text: `Cleared ${count} stash entr${count === 1 ? "y" : "ies"}.`
|
|
555
|
-
}
|
|
556
|
-
]
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
} catch (err) {
|
|
561
|
-
return {
|
|
562
|
-
content: [
|
|
563
|
-
{ type: "text", text: `Stash operation failed: ${err.message}` }
|
|
564
|
-
],
|
|
565
|
-
isError: true
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
);
|
|
570
|
-
}
|
|
571
|
-
function registerGitContextTool(server) {
|
|
572
|
-
server.registerTool(
|
|
573
|
-
"git_context",
|
|
574
|
-
{
|
|
575
|
-
description: "Summarize the current Git branch, working tree state, recent commits, and optional diff statistics for the repository.",
|
|
576
|
-
inputSchema: {
|
|
577
|
-
cwd: z.string().optional().describe("Repository root or working directory"),
|
|
578
|
-
commit_count: z.number().min(1).max(50).default(5).optional().describe("How many recent commits to include"),
|
|
579
|
-
include_diff: z.boolean().default(false).optional().describe("Include diff stat for working tree changes")
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
async ({ cwd, commit_count, include_diff }) => {
|
|
583
|
-
try {
|
|
584
|
-
const result = await gitContext({
|
|
585
|
-
cwd,
|
|
586
|
-
commitCount: commit_count,
|
|
587
|
-
includeDiff: include_diff
|
|
588
|
-
});
|
|
589
|
-
return {
|
|
590
|
-
content: [{ type: "text", text: formatGitContext(result) }]
|
|
591
|
-
};
|
|
592
|
-
} catch (err) {
|
|
593
|
-
return {
|
|
594
|
-
content: [
|
|
595
|
-
{ type: "text", text: `Git context failed: ${err.message}` }
|
|
596
|
-
],
|
|
597
|
-
isError: true
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
function registerDiffParseTool(server) {
|
|
604
|
-
server.registerTool(
|
|
605
|
-
"diff_parse",
|
|
606
|
-
{
|
|
607
|
-
description: "Parse raw unified diff text into file-level and hunk-level structural changes.",
|
|
608
|
-
inputSchema: {
|
|
609
|
-
diff: z.string().describe("Raw unified diff text")
|
|
610
|
-
}
|
|
611
|
-
},
|
|
612
|
-
async ({ diff }) => {
|
|
613
|
-
try {
|
|
614
|
-
const normalizedDiff = diff.replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
615
|
-
const files = diffParse({ diff: normalizedDiff });
|
|
616
|
-
return {
|
|
617
|
-
content: [{ type: "text", text: formatDiffFiles(files) }]
|
|
618
|
-
};
|
|
619
|
-
} catch (err) {
|
|
620
|
-
return {
|
|
621
|
-
content: [
|
|
622
|
-
{ type: "text", text: `Diff parse failed: ${err.message}` }
|
|
623
|
-
],
|
|
624
|
-
isError: true
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
);
|
|
629
|
-
}
|
|
630
|
-
function registerRenameTool(server) {
|
|
631
|
-
server.registerTool(
|
|
632
|
-
"rename",
|
|
633
|
-
{
|
|
634
|
-
description: "Rename a symbol across files using whole-word regex matching for exports, imports, and general usage references.",
|
|
635
|
-
inputSchema: {
|
|
636
|
-
old_name: z.string().describe("Existing symbol name to replace"),
|
|
637
|
-
new_name: z.string().describe("New symbol name to use"),
|
|
638
|
-
root_path: z.string().describe("Root directory to search within"),
|
|
639
|
-
extensions: z.array(z.string()).optional().describe("Optional file extensions to include, such as .ts,.tsx,.js,.jsx"),
|
|
640
|
-
dry_run: z.boolean().default(true).describe("Preview changes without writing files")
|
|
641
|
-
}
|
|
642
|
-
},
|
|
643
|
-
async ({ old_name, new_name, root_path, extensions, dry_run }) => {
|
|
644
|
-
try {
|
|
645
|
-
const result = await rename({
|
|
646
|
-
oldName: old_name,
|
|
647
|
-
newName: new_name,
|
|
648
|
-
rootPath: root_path,
|
|
649
|
-
extensions,
|
|
650
|
-
dryRun: dry_run
|
|
651
|
-
});
|
|
652
|
-
return {
|
|
653
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
654
|
-
};
|
|
655
|
-
} catch (err) {
|
|
656
|
-
return {
|
|
657
|
-
content: [{ type: "text", text: `Rename failed: ${err.message}` }],
|
|
658
|
-
isError: true
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
function registerCodemodTool(server) {
|
|
665
|
-
server.registerTool(
|
|
666
|
-
"codemod",
|
|
667
|
-
{
|
|
668
|
-
description: "Apply regex-based codemod rules across files and return structured before/after changes for each affected line.",
|
|
669
|
-
inputSchema: {
|
|
670
|
-
root_path: z.string().describe("Root directory to transform within"),
|
|
671
|
-
rules: z.array(
|
|
672
|
-
z.object({
|
|
673
|
-
description: z.string().describe("What the codemod rule does"),
|
|
674
|
-
pattern: z.string().describe("Regex pattern in string form"),
|
|
675
|
-
replacement: z.string().describe("Replacement string with optional capture groups")
|
|
676
|
-
})
|
|
677
|
-
).min(1).describe("Codemod rules to apply"),
|
|
678
|
-
dry_run: z.boolean().default(true).describe("Preview changes without writing files")
|
|
679
|
-
}
|
|
680
|
-
},
|
|
681
|
-
async ({ root_path, rules, dry_run }) => {
|
|
682
|
-
try {
|
|
683
|
-
const result = await codemod({
|
|
684
|
-
rootPath: root_path,
|
|
685
|
-
rules,
|
|
686
|
-
dryRun: dry_run
|
|
687
|
-
});
|
|
688
|
-
return {
|
|
689
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
690
|
-
};
|
|
691
|
-
} catch (err) {
|
|
692
|
-
return {
|
|
693
|
-
content: [{ type: "text", text: `Codemod failed: ${err.message}` }],
|
|
694
|
-
isError: true
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
function registerFileSummaryTool(server) {
|
|
701
|
-
server.registerTool(
|
|
702
|
-
"file_summary",
|
|
703
|
-
{
|
|
704
|
-
description: "Create a concise structural summary of a source file: imports, exports, functions, classes, interfaces, and types.",
|
|
705
|
-
inputSchema: {
|
|
706
|
-
path: z.string().describe("Absolute path to the file to summarize")
|
|
707
|
-
}
|
|
708
|
-
},
|
|
709
|
-
async ({ path }) => {
|
|
710
|
-
try {
|
|
711
|
-
const summary = await fileSummary({ path });
|
|
712
|
-
return {
|
|
713
|
-
content: [{ type: "text", text: formatFileSummary(summary) }]
|
|
714
|
-
};
|
|
715
|
-
} catch (err) {
|
|
716
|
-
return {
|
|
717
|
-
content: [
|
|
718
|
-
{ type: "text", text: `File summary failed: ${err.message}` }
|
|
719
|
-
],
|
|
720
|
-
isError: true
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
function registerCheckpointTool(server) {
|
|
727
|
-
server.registerTool(
|
|
728
|
-
"checkpoint",
|
|
729
|
-
{
|
|
730
|
-
description: "Save and restore lightweight session checkpoints in .kb-state/checkpoints for cross-session continuity.",
|
|
731
|
-
inputSchema: {
|
|
732
|
-
action: z.enum(["save", "load", "list", "latest"]).describe("Checkpoint action to perform"),
|
|
733
|
-
label: z.string().optional().describe("Checkpoint label for save, or checkpoint id for load"),
|
|
734
|
-
data: z.string().optional().describe("JSON object string for save actions"),
|
|
735
|
-
notes: z.string().optional().describe("Optional notes for save actions")
|
|
736
|
-
}
|
|
737
|
-
},
|
|
738
|
-
async ({ action, label, data, notes }) => {
|
|
739
|
-
try {
|
|
740
|
-
switch (action) {
|
|
741
|
-
case "save": {
|
|
742
|
-
if (!label) throw new Error("label required for save");
|
|
743
|
-
const checkpoint = checkpointSave(label, parseRecordString(data), { notes });
|
|
744
|
-
return {
|
|
745
|
-
content: [{ type: "text", text: formatCheckpoint(checkpoint) }]
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
case "load": {
|
|
749
|
-
if (!label) throw new Error("label required for load");
|
|
750
|
-
const checkpoint = checkpointLoad(label);
|
|
751
|
-
return {
|
|
752
|
-
content: [
|
|
753
|
-
{
|
|
754
|
-
type: "text",
|
|
755
|
-
text: checkpoint ? formatCheckpoint(checkpoint) : `Checkpoint "${label}" not found.`
|
|
756
|
-
}
|
|
757
|
-
]
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
case "list": {
|
|
761
|
-
const checkpoints = checkpointList();
|
|
762
|
-
return {
|
|
763
|
-
content: [
|
|
764
|
-
{
|
|
765
|
-
type: "text",
|
|
766
|
-
text: checkpoints.length === 0 ? "No checkpoints saved." : checkpoints.map(
|
|
767
|
-
(checkpoint) => `- ${checkpoint.id} \u2014 ${checkpoint.label} (${checkpoint.createdAt})`
|
|
768
|
-
).join("\n")
|
|
769
|
-
}
|
|
770
|
-
]
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
case "latest": {
|
|
774
|
-
const checkpoint = checkpointLatest();
|
|
775
|
-
return {
|
|
776
|
-
content: [
|
|
777
|
-
{
|
|
778
|
-
type: "text",
|
|
779
|
-
text: checkpoint ? formatCheckpoint(checkpoint) : "No checkpoints saved."
|
|
780
|
-
}
|
|
781
|
-
]
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
} catch (err) {
|
|
786
|
-
return {
|
|
787
|
-
content: [
|
|
788
|
-
{ type: "text", text: `Checkpoint failed: ${err.message}` }
|
|
789
|
-
],
|
|
790
|
-
isError: true
|
|
791
|
-
};
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
function registerDataTransformTool(server) {
|
|
797
|
-
server.registerTool(
|
|
798
|
-
"data_transform",
|
|
799
|
-
{
|
|
800
|
-
description: "Apply small jq-like transforms to JSON input for filtering, projection, grouping, and path extraction.",
|
|
801
|
-
inputSchema: {
|
|
802
|
-
input: z.string().describe("Input JSON string"),
|
|
803
|
-
expression: z.string().describe("Transform expression to apply")
|
|
804
|
-
}
|
|
805
|
-
},
|
|
806
|
-
async ({ input, expression }) => {
|
|
807
|
-
try {
|
|
808
|
-
const result = dataTransform({ input, expression });
|
|
809
|
-
return {
|
|
810
|
-
content: [{ type: "text", text: result.outputString }]
|
|
811
|
-
};
|
|
812
|
-
} catch (err) {
|
|
813
|
-
return {
|
|
814
|
-
content: [
|
|
815
|
-
{ type: "text", text: `Data transform failed: ${err.message}` }
|
|
816
|
-
],
|
|
817
|
-
isError: true
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
function registerTraceTool(server, embedder, store) {
|
|
824
|
-
server.registerTool(
|
|
825
|
-
"trace",
|
|
826
|
-
{
|
|
827
|
-
description: "Trace data flow through a codebase by following imports, call sites, and references from a starting symbol or file location.",
|
|
828
|
-
inputSchema: {
|
|
829
|
-
start: z.string().describe("Starting point \u2014 symbol name or file:line reference"),
|
|
830
|
-
direction: z.enum(["forward", "backward", "both"]).describe("Which direction to trace relationships"),
|
|
831
|
-
max_depth: z.number().min(1).max(10).default(3).optional().describe("Maximum trace depth")
|
|
832
|
-
}
|
|
833
|
-
},
|
|
834
|
-
async ({ start, direction, max_depth }) => {
|
|
835
|
-
try {
|
|
836
|
-
const result = await trace(embedder, store, {
|
|
837
|
-
start,
|
|
838
|
-
direction,
|
|
839
|
-
maxDepth: max_depth
|
|
840
|
-
});
|
|
841
|
-
return {
|
|
842
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
843
|
-
};
|
|
844
|
-
} catch (err) {
|
|
845
|
-
return {
|
|
846
|
-
content: [{ type: "text", text: `Trace failed: ${err.message}` }],
|
|
847
|
-
isError: true
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
|
-
function registerFindExamplesTool(server, embedder, store) {
|
|
854
|
-
server.registerTool(
|
|
855
|
-
"find_examples",
|
|
856
|
-
{
|
|
857
|
-
description: "Find real usage examples of a function, class, or pattern in the indexed codebase.",
|
|
858
|
-
inputSchema: {
|
|
859
|
-
query: z.string().describe("Symbol or pattern to find examples of"),
|
|
860
|
-
limit: z.number().min(1).max(20).default(5).optional().describe("Maximum examples to return"),
|
|
861
|
-
content_type: z.string().optional().describe("Filter by content type")
|
|
862
|
-
}
|
|
863
|
-
},
|
|
864
|
-
async ({ query, limit, content_type }) => {
|
|
865
|
-
try {
|
|
866
|
-
const result = await findExamples(embedder, store, {
|
|
867
|
-
query,
|
|
868
|
-
limit,
|
|
869
|
-
contentType: content_type
|
|
870
|
-
});
|
|
871
|
-
return {
|
|
872
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
873
|
-
};
|
|
874
|
-
} catch (err) {
|
|
875
|
-
return {
|
|
876
|
-
content: [
|
|
877
|
-
{ type: "text", text: `Find examples failed: ${err.message}` }
|
|
878
|
-
],
|
|
879
|
-
isError: true
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
function registerProcessTool(server) {
|
|
886
|
-
server.registerTool(
|
|
887
|
-
"process",
|
|
888
|
-
{
|
|
889
|
-
description: "Start, stop, inspect, list, and tail logs for in-memory managed child processes.",
|
|
890
|
-
inputSchema: {
|
|
891
|
-
action: z.enum(["start", "stop", "status", "list", "logs"]).describe("Process action to perform"),
|
|
892
|
-
id: z.string().optional().describe("Managed process ID"),
|
|
893
|
-
command: z.string().optional().describe("Executable to start"),
|
|
894
|
-
args: z.array(z.string()).optional().describe("Arguments for start actions"),
|
|
895
|
-
tail: z.number().min(1).max(500).optional().describe("Log lines to return for logs actions")
|
|
896
|
-
}
|
|
897
|
-
},
|
|
898
|
-
async ({ action, id, command, args, tail }) => {
|
|
899
|
-
try {
|
|
900
|
-
switch (action) {
|
|
901
|
-
case "start": {
|
|
902
|
-
if (!id || !command) throw new Error("id and command are required for start");
|
|
903
|
-
return {
|
|
904
|
-
content: [
|
|
905
|
-
{
|
|
906
|
-
type: "text",
|
|
907
|
-
text: JSON.stringify(processStart(id, command, args ?? []), null, 2)
|
|
908
|
-
}
|
|
909
|
-
]
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
case "stop": {
|
|
913
|
-
if (!id) throw new Error("id is required for stop");
|
|
914
|
-
return {
|
|
915
|
-
content: [
|
|
916
|
-
{
|
|
917
|
-
type: "text",
|
|
918
|
-
text: JSON.stringify(processStop(id) ?? null, null, 2)
|
|
919
|
-
}
|
|
920
|
-
]
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
case "status": {
|
|
924
|
-
if (!id) throw new Error("id is required for status");
|
|
925
|
-
return {
|
|
926
|
-
content: [
|
|
927
|
-
{
|
|
928
|
-
type: "text",
|
|
929
|
-
text: JSON.stringify(processStatus(id) ?? null, null, 2)
|
|
930
|
-
}
|
|
931
|
-
]
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
case "list": {
|
|
935
|
-
return {
|
|
936
|
-
content: [{ type: "text", text: JSON.stringify(processList(), null, 2) }]
|
|
937
|
-
};
|
|
938
|
-
}
|
|
939
|
-
case "logs": {
|
|
940
|
-
if (!id) throw new Error("id is required for logs");
|
|
941
|
-
return {
|
|
942
|
-
content: [
|
|
943
|
-
{
|
|
944
|
-
type: "text",
|
|
945
|
-
text: JSON.stringify(processLogs(id, tail), null, 2)
|
|
946
|
-
}
|
|
947
|
-
]
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
} catch (err) {
|
|
952
|
-
return {
|
|
953
|
-
content: [
|
|
954
|
-
{ type: "text", text: `Process action failed: ${err.message}` }
|
|
955
|
-
],
|
|
956
|
-
isError: true
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
function registerWatchTool(server) {
|
|
963
|
-
server.registerTool(
|
|
964
|
-
"watch",
|
|
965
|
-
{
|
|
966
|
-
description: "Start, stop, and list in-memory filesystem watchers for a directory.",
|
|
967
|
-
inputSchema: {
|
|
968
|
-
action: z.enum(["start", "stop", "list"]).describe("Watch action to perform"),
|
|
969
|
-
path: z.string().optional().describe("Directory path to watch for start actions"),
|
|
970
|
-
id: z.string().optional().describe("Watcher ID for stop actions")
|
|
971
|
-
}
|
|
972
|
-
},
|
|
973
|
-
async ({ action, path, id }) => {
|
|
974
|
-
try {
|
|
975
|
-
switch (action) {
|
|
976
|
-
case "start": {
|
|
977
|
-
if (!path) throw new Error("path is required for start");
|
|
978
|
-
return {
|
|
979
|
-
content: [
|
|
980
|
-
{
|
|
981
|
-
type: "text",
|
|
982
|
-
text: JSON.stringify(watchStart({ path }), null, 2)
|
|
983
|
-
}
|
|
984
|
-
]
|
|
985
|
-
};
|
|
986
|
-
}
|
|
987
|
-
case "stop": {
|
|
988
|
-
if (!id) throw new Error("id is required for stop");
|
|
989
|
-
return {
|
|
990
|
-
content: [
|
|
991
|
-
{
|
|
992
|
-
type: "text",
|
|
993
|
-
text: JSON.stringify({ stopped: watchStop(id) }, null, 2)
|
|
994
|
-
}
|
|
995
|
-
]
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
case "list": {
|
|
999
|
-
return {
|
|
1000
|
-
content: [{ type: "text", text: JSON.stringify(watchList(), null, 2) }]
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
} catch (err) {
|
|
1005
|
-
return {
|
|
1006
|
-
content: [
|
|
1007
|
-
{ type: "text", text: `Watch action failed: ${err.message}` }
|
|
1008
|
-
],
|
|
1009
|
-
isError: true
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
function registerDeadSymbolsTool(server, embedder, store) {
|
|
1016
|
-
server.registerTool(
|
|
1017
|
-
"dead_symbols",
|
|
1018
|
-
{
|
|
1019
|
-
description: "Find exported symbols that appear to be unused because they are never imported or re-exported.",
|
|
1020
|
-
inputSchema: {
|
|
1021
|
-
limit: z.number().min(1).max(500).default(100).optional().describe("Maximum exported symbols to scan")
|
|
1022
|
-
}
|
|
1023
|
-
},
|
|
1024
|
-
async ({ limit }) => {
|
|
1025
|
-
try {
|
|
1026
|
-
const result = await findDeadSymbols(embedder, store, { limit });
|
|
1027
|
-
return {
|
|
1028
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1029
|
-
};
|
|
1030
|
-
} catch (err) {
|
|
1031
|
-
return {
|
|
1032
|
-
content: [
|
|
1033
|
-
{ type: "text", text: `Dead symbol scan failed: ${err.message}` }
|
|
1034
|
-
],
|
|
1035
|
-
isError: true
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
);
|
|
1040
|
-
}
|
|
1041
|
-
function registerDelegateTool(server) {
|
|
1042
|
-
server.registerTool(
|
|
1043
|
-
"delegate",
|
|
1044
|
-
{
|
|
1045
|
-
description: "Delegate a subtask to a local Ollama model. Use for summarization, classification, naming, or any task that can offload work from the host agent. Fails fast if Ollama is not running.",
|
|
1046
|
-
inputSchema: {
|
|
1047
|
-
prompt: z.string().describe("The task or question to send to the local model"),
|
|
1048
|
-
model: z.string().optional().describe("Ollama model name (default: first available model)"),
|
|
1049
|
-
system: z.string().optional().describe("System prompt for the model"),
|
|
1050
|
-
context: z.string().optional().describe("Context text to include before the prompt (e.g. file contents)"),
|
|
1051
|
-
temperature: z.number().min(0).max(2).default(0.3).optional().describe("Sampling temperature (0=deterministic, default 0.3)"),
|
|
1052
|
-
timeout: z.number().min(1e3).max(6e5).default(12e4).optional().describe("Timeout in milliseconds (default 120000)"),
|
|
1053
|
-
action: z.enum(["generate", "list_models"]).default("generate").optional().describe("Action: generate a response or list available models")
|
|
1054
|
-
}
|
|
1055
|
-
},
|
|
1056
|
-
async ({ prompt, model, system, context, temperature, timeout, action }) => {
|
|
1057
|
-
try {
|
|
1058
|
-
if (action === "list_models") {
|
|
1059
|
-
const models = await delegateListModels();
|
|
1060
|
-
return {
|
|
1061
|
-
content: [
|
|
1062
|
-
{
|
|
1063
|
-
type: "text",
|
|
1064
|
-
text: JSON.stringify(
|
|
1065
|
-
{ models, count: models.length, _Next: "Use delegate with a model name" },
|
|
1066
|
-
null,
|
|
1067
|
-
2
|
|
1068
|
-
)
|
|
1069
|
-
}
|
|
1070
|
-
]
|
|
1071
|
-
};
|
|
1072
|
-
}
|
|
1073
|
-
const result = await delegate({ prompt, model, system, context, temperature, timeout });
|
|
1074
|
-
if (result.error) {
|
|
1075
|
-
return {
|
|
1076
|
-
content: [
|
|
1077
|
-
{
|
|
1078
|
-
type: "text",
|
|
1079
|
-
text: JSON.stringify(
|
|
1080
|
-
{ error: result.error, model: result.model, durationMs: result.durationMs },
|
|
1081
|
-
null,
|
|
1082
|
-
2
|
|
1083
|
-
)
|
|
1084
|
-
}
|
|
1085
|
-
],
|
|
1086
|
-
isError: true
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
return {
|
|
1090
|
-
content: [
|
|
1091
|
-
{
|
|
1092
|
-
type: "text",
|
|
1093
|
-
text: JSON.stringify(
|
|
1094
|
-
{
|
|
1095
|
-
model: result.model,
|
|
1096
|
-
response: result.response,
|
|
1097
|
-
durationMs: result.durationMs,
|
|
1098
|
-
tokenCount: result.tokenCount,
|
|
1099
|
-
_Next: "Use the response in your workflow. stash to save it."
|
|
1100
|
-
},
|
|
1101
|
-
null,
|
|
1102
|
-
2
|
|
1103
|
-
)
|
|
1104
|
-
}
|
|
1105
|
-
]
|
|
1106
|
-
};
|
|
1107
|
-
} catch (err) {
|
|
1108
|
-
return {
|
|
1109
|
-
content: [{ type: "text", text: `Delegate failed: ${err.message}` }],
|
|
1110
|
-
isError: true
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
function registerLaneTool(server) {
|
|
1117
|
-
server.registerTool(
|
|
1118
|
-
"lane",
|
|
1119
|
-
{
|
|
1120
|
-
description: "Manage verified lanes \u2014 isolated file copies for parallel exploration. Create a lane, make changes, diff, merge back, or discard.",
|
|
1121
|
-
inputSchema: {
|
|
1122
|
-
action: z.enum(["create", "list", "status", "diff", "merge", "discard"]).describe("Lane action to perform"),
|
|
1123
|
-
name: z.string().optional().describe("Lane name (required for create/status/diff/merge/discard)"),
|
|
1124
|
-
files: z.array(z.string()).optional().describe("File paths to copy into the lane (required for create)")
|
|
1125
|
-
}
|
|
1126
|
-
},
|
|
1127
|
-
async ({ action, name, files }) => {
|
|
1128
|
-
try {
|
|
1129
|
-
switch (action) {
|
|
1130
|
-
case "create": {
|
|
1131
|
-
if (!name) throw new Error("name is required for create");
|
|
1132
|
-
if (!files || files.length === 0) throw new Error("files are required for create");
|
|
1133
|
-
const meta = laneCreate(name, files);
|
|
1134
|
-
return {
|
|
1135
|
-
content: [{ type: "text", text: JSON.stringify(meta, null, 2) }]
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
case "list": {
|
|
1139
|
-
return {
|
|
1140
|
-
content: [{ type: "text", text: JSON.stringify(laneList(), null, 2) }]
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
case "status": {
|
|
1144
|
-
if (!name) throw new Error("name is required for status");
|
|
1145
|
-
return {
|
|
1146
|
-
content: [{ type: "text", text: JSON.stringify(laneStatus(name), null, 2) }]
|
|
1147
|
-
};
|
|
1148
|
-
}
|
|
1149
|
-
case "diff": {
|
|
1150
|
-
if (!name) throw new Error("name is required for diff");
|
|
1151
|
-
return {
|
|
1152
|
-
content: [{ type: "text", text: JSON.stringify(laneDiff(name), null, 2) }]
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
case "merge": {
|
|
1156
|
-
if (!name) throw new Error("name is required for merge");
|
|
1157
|
-
return {
|
|
1158
|
-
content: [{ type: "text", text: JSON.stringify(laneMerge(name), null, 2) }]
|
|
1159
|
-
};
|
|
1160
|
-
}
|
|
1161
|
-
case "discard": {
|
|
1162
|
-
if (!name) throw new Error("name is required for discard");
|
|
1163
|
-
return {
|
|
1164
|
-
content: [
|
|
1165
|
-
{
|
|
1166
|
-
type: "text",
|
|
1167
|
-
text: JSON.stringify({ discarded: laneDiscard(name) }, null, 2)
|
|
1168
|
-
}
|
|
1169
|
-
]
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
} catch (err) {
|
|
1174
|
-
return {
|
|
1175
|
-
content: [
|
|
1176
|
-
{ type: "text", text: `Lane action failed: ${err.message}` }
|
|
1177
|
-
],
|
|
1178
|
-
isError: true
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
);
|
|
1183
|
-
}
|
|
1184
|
-
function registerHealthTool(server) {
|
|
1185
|
-
server.registerTool(
|
|
1186
|
-
"health",
|
|
1187
|
-
{
|
|
1188
|
-
description: "Run project health checks \u2014 verifies package.json, tsconfig, scripts, lockfile, README, LICENSE, .gitignore.",
|
|
1189
|
-
inputSchema: {
|
|
1190
|
-
path: z.string().optional().describe("Root directory to check (defaults to cwd)")
|
|
1191
|
-
}
|
|
1192
|
-
},
|
|
1193
|
-
async ({ path }) => {
|
|
1194
|
-
try {
|
|
1195
|
-
const result = health(path);
|
|
1196
|
-
return {
|
|
1197
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1198
|
-
};
|
|
1199
|
-
} catch (err) {
|
|
1200
|
-
return {
|
|
1201
|
-
content: [
|
|
1202
|
-
{ type: "text", text: `Health check failed: ${err.message}` }
|
|
1203
|
-
],
|
|
1204
|
-
isError: true
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1210
|
-
function registerQueueTool(server) {
|
|
1211
|
-
server.registerTool(
|
|
1212
|
-
"queue",
|
|
1213
|
-
{
|
|
1214
|
-
description: "Manage task queues for sequential agent operations. Push items, take next, mark done/failed, list queues.",
|
|
1215
|
-
inputSchema: {
|
|
1216
|
-
action: z.enum(["create", "push", "next", "done", "fail", "get", "list", "clear", "delete"]).describe("Queue action"),
|
|
1217
|
-
name: z.string().optional().describe("Queue name (required for most actions)"),
|
|
1218
|
-
title: z.string().optional().describe("Item title (required for push)"),
|
|
1219
|
-
id: z.string().optional().describe("Item ID (required for done/fail)"),
|
|
1220
|
-
data: z.unknown().optional().describe("Arbitrary data to attach to a queue item"),
|
|
1221
|
-
error: z.string().optional().describe("Error message (required for fail)")
|
|
1222
|
-
}
|
|
1223
|
-
},
|
|
1224
|
-
async ({ action, name, title, id, data, error }) => {
|
|
1225
|
-
try {
|
|
1226
|
-
switch (action) {
|
|
1227
|
-
case "create": {
|
|
1228
|
-
if (!name) throw new Error("name is required for create");
|
|
1229
|
-
return {
|
|
1230
|
-
content: [
|
|
1231
|
-
{ type: "text", text: JSON.stringify(queueCreate(name), null, 2) }
|
|
1232
|
-
]
|
|
1233
|
-
};
|
|
1234
|
-
}
|
|
1235
|
-
case "push": {
|
|
1236
|
-
if (!name) throw new Error("name is required for push");
|
|
1237
|
-
if (!title) throw new Error("title is required for push");
|
|
1238
|
-
return {
|
|
1239
|
-
content: [
|
|
1240
|
-
{
|
|
1241
|
-
type: "text",
|
|
1242
|
-
text: JSON.stringify(queuePush(name, title, data), null, 2)
|
|
1243
|
-
}
|
|
1244
|
-
]
|
|
1245
|
-
};
|
|
1246
|
-
}
|
|
1247
|
-
case "next": {
|
|
1248
|
-
if (!name) throw new Error("name is required for next");
|
|
1249
|
-
const item = queueNext(name);
|
|
1250
|
-
return {
|
|
1251
|
-
content: [{ type: "text", text: JSON.stringify(item, null, 2) }]
|
|
1252
|
-
};
|
|
1253
|
-
}
|
|
1254
|
-
case "done": {
|
|
1255
|
-
if (!name) throw new Error("name is required for done");
|
|
1256
|
-
if (!id) throw new Error("id is required for done");
|
|
1257
|
-
return {
|
|
1258
|
-
content: [
|
|
1259
|
-
{ type: "text", text: JSON.stringify(queueDone(name, id), null, 2) }
|
|
1260
|
-
]
|
|
1261
|
-
};
|
|
1262
|
-
}
|
|
1263
|
-
case "fail": {
|
|
1264
|
-
if (!name) throw new Error("name is required for fail");
|
|
1265
|
-
if (!id) throw new Error("id is required for fail");
|
|
1266
|
-
if (!error) throw new Error("error is required for fail");
|
|
1267
|
-
return {
|
|
1268
|
-
content: [
|
|
1269
|
-
{
|
|
1270
|
-
type: "text",
|
|
1271
|
-
text: JSON.stringify(queueFail(name, id, error), null, 2)
|
|
1272
|
-
}
|
|
1273
|
-
]
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
case "get": {
|
|
1277
|
-
if (!name) throw new Error("name is required for get");
|
|
1278
|
-
return {
|
|
1279
|
-
content: [{ type: "text", text: JSON.stringify(queueGet(name), null, 2) }]
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
case "list": {
|
|
1283
|
-
return {
|
|
1284
|
-
content: [{ type: "text", text: JSON.stringify(queueList(), null, 2) }]
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
case "clear": {
|
|
1288
|
-
if (!name) throw new Error("name is required for clear");
|
|
1289
|
-
return {
|
|
1290
|
-
content: [
|
|
1291
|
-
{
|
|
1292
|
-
type: "text",
|
|
1293
|
-
text: JSON.stringify({ cleared: queueClear(name) }, null, 2)
|
|
1294
|
-
}
|
|
1295
|
-
]
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
case "delete": {
|
|
1299
|
-
if (!name) throw new Error("name is required for delete");
|
|
1300
|
-
return {
|
|
1301
|
-
content: [
|
|
1302
|
-
{
|
|
1303
|
-
type: "text",
|
|
1304
|
-
text: JSON.stringify({ deleted: queueDelete(name) }, null, 2)
|
|
1305
|
-
}
|
|
1306
|
-
]
|
|
1307
|
-
};
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
} catch (err) {
|
|
1311
|
-
return {
|
|
1312
|
-
content: [
|
|
1313
|
-
{ type: "text", text: `Queue action failed: ${err.message}` }
|
|
1314
|
-
],
|
|
1315
|
-
isError: true
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
);
|
|
1320
|
-
}
|
|
1321
|
-
const searchArgsSchema = z.object({
|
|
1322
|
-
query: z.string(),
|
|
1323
|
-
limit: z.number().min(1).max(20).default(5).optional(),
|
|
1324
|
-
search_mode: z.enum(["hybrid", "semantic", "keyword"]).default("hybrid").optional(),
|
|
1325
|
-
content_type: z.string().optional(),
|
|
1326
|
-
origin: z.enum(["indexed", "curated", "produced"]).optional(),
|
|
1327
|
-
category: z.string().optional(),
|
|
1328
|
-
tags: z.array(z.string()).optional(),
|
|
1329
|
-
min_score: z.number().min(0).max(1).default(0.25).optional()
|
|
1330
|
-
});
|
|
1331
|
-
const findArgsSchema = z.object({
|
|
1332
|
-
query: z.string().optional(),
|
|
1333
|
-
glob: z.string().optional(),
|
|
1334
|
-
pattern: z.string().optional(),
|
|
1335
|
-
limit: z.number().min(1).max(50).default(10).optional(),
|
|
1336
|
-
content_type: z.string().optional(),
|
|
1337
|
-
cwd: z.string().optional()
|
|
1338
|
-
});
|
|
1339
|
-
const checkArgsSchema = z.object({
|
|
1340
|
-
files: z.array(z.string()).optional(),
|
|
1341
|
-
cwd: z.string().optional(),
|
|
1342
|
-
skip_types: z.boolean().optional(),
|
|
1343
|
-
skip_lint: z.boolean().optional()
|
|
1344
|
-
});
|
|
1345
|
-
async function executeToolkitBatchOperation(operation, embedder, store) {
|
|
1346
|
-
switch (operation.type) {
|
|
1347
|
-
case "search": {
|
|
1348
|
-
const args = searchArgsSchema.parse(operation.args);
|
|
1349
|
-
return executeBatchSearch(embedder, store, args);
|
|
1350
|
-
}
|
|
1351
|
-
case "find": {
|
|
1352
|
-
const args = findArgsSchema.parse(operation.args);
|
|
1353
|
-
if (!args.query && !args.glob && !args.pattern) {
|
|
1354
|
-
throw new Error("find operation requires query, glob, or pattern");
|
|
1355
|
-
}
|
|
1356
|
-
return find(embedder, store, {
|
|
1357
|
-
query: args.query,
|
|
1358
|
-
glob: args.glob,
|
|
1359
|
-
pattern: args.pattern,
|
|
1360
|
-
limit: args.limit,
|
|
1361
|
-
contentType: args.content_type,
|
|
1362
|
-
cwd: args.cwd
|
|
1363
|
-
});
|
|
1364
|
-
}
|
|
1365
|
-
case "check": {
|
|
1366
|
-
const args = checkArgsSchema.parse(operation.args);
|
|
1367
|
-
return check({
|
|
1368
|
-
files: args.files,
|
|
1369
|
-
cwd: args.cwd,
|
|
1370
|
-
skipTypes: args.skip_types,
|
|
1371
|
-
skipLint: args.skip_lint
|
|
1372
|
-
});
|
|
1373
|
-
}
|
|
1374
|
-
default:
|
|
1375
|
-
throw new Error(`Unsupported batch operation type: ${operation.type}`);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
async function executeBatchSearch(embedder, store, args) {
|
|
1379
|
-
const limit = args.limit ?? 5;
|
|
1380
|
-
const filterOptions = {
|
|
1381
|
-
limit,
|
|
1382
|
-
minScore: args.min_score ?? 0.25,
|
|
1383
|
-
contentType: args.content_type,
|
|
1384
|
-
origin: args.origin,
|
|
1385
|
-
category: args.category,
|
|
1386
|
-
tags: args.tags
|
|
1387
|
-
};
|
|
1388
|
-
const embedQuery = embedder.embedQuery?.bind(embedder) ?? embedder.embed.bind(embedder);
|
|
1389
|
-
if (args.search_mode === "keyword") {
|
|
1390
|
-
return (await store.ftsSearch(args.query, filterOptions)).slice(0, limit);
|
|
1391
|
-
}
|
|
1392
|
-
const queryVector = await embedQuery(args.query);
|
|
1393
|
-
if (args.search_mode === "semantic") {
|
|
1394
|
-
return store.search(queryVector, filterOptions);
|
|
1395
|
-
}
|
|
1396
|
-
const [vectorResults, ftsResults] = await Promise.all([
|
|
1397
|
-
store.search(queryVector, { ...filterOptions, limit: limit * 2 }),
|
|
1398
|
-
store.ftsSearch(args.query, { ...filterOptions, limit: limit * 2 })
|
|
1399
|
-
]);
|
|
1400
|
-
return reciprocalRankFusion(vectorResults, ftsResults).slice(0, limit);
|
|
1401
|
-
}
|
|
1402
|
-
function reciprocalRankFusion(vectorResults, ftsResults, k = 60) {
|
|
1403
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1404
|
-
for (let index = 0; index < vectorResults.length; index++) {
|
|
1405
|
-
const result = vectorResults[index];
|
|
1406
|
-
merged.set(result.record.id, { record: result.record, score: 1 / (k + index + 1) });
|
|
1407
|
-
}
|
|
1408
|
-
for (let index = 0; index < ftsResults.length; index++) {
|
|
1409
|
-
const result = ftsResults[index];
|
|
1410
|
-
const existing = merged.get(result.record.id);
|
|
1411
|
-
if (existing) {
|
|
1412
|
-
existing.score += 1 / (k + index + 1);
|
|
1413
|
-
continue;
|
|
1414
|
-
}
|
|
1415
|
-
merged.set(result.record.id, { record: result.record, score: 1 / (k + index + 1) });
|
|
1416
|
-
}
|
|
1417
|
-
return [...merged.values()].sort((a, b) => b.score - a.score);
|
|
1418
|
-
}
|
|
1419
|
-
function formatSymbolInfo(info) {
|
|
1420
|
-
const lines = [`Symbol: ${info.name}`];
|
|
1421
|
-
if (info.definedIn) {
|
|
1422
|
-
lines.push(
|
|
1423
|
-
`Defined in: ${info.definedIn.path}:${info.definedIn.line} (${info.definedIn.kind})`
|
|
1424
|
-
);
|
|
1425
|
-
} else {
|
|
1426
|
-
lines.push("Defined in: not found");
|
|
1427
|
-
}
|
|
1428
|
-
lines.push("", "Imported by:");
|
|
1429
|
-
if (info.importedBy.length === 0) {
|
|
1430
|
-
lines.push(" none");
|
|
1431
|
-
} else {
|
|
1432
|
-
for (const item of info.importedBy) {
|
|
1433
|
-
lines.push(` - ${item.path}:${item.line} ${item.importStatement}`);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
lines.push("", "Referenced in:");
|
|
1437
|
-
if (info.referencedIn.length === 0) {
|
|
1438
|
-
lines.push(" none");
|
|
1439
|
-
} else {
|
|
1440
|
-
for (const item of info.referencedIn) {
|
|
1441
|
-
lines.push(` - ${item.path}:${item.line} ${item.context}`);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
return lines.join("\n");
|
|
1445
|
-
}
|
|
1446
|
-
function formatTestRunResult(result) {
|
|
1447
|
-
const lines = [
|
|
1448
|
-
`Vitest run: ${result.passed ? "passed" : "failed"}`,
|
|
1449
|
-
`Duration: ${result.durationMs}ms`,
|
|
1450
|
-
`Passed: ${result.summary.passed}`,
|
|
1451
|
-
`Failed: ${result.summary.failed}`,
|
|
1452
|
-
`Skipped: ${result.summary.skipped}`
|
|
1453
|
-
];
|
|
1454
|
-
if (result.summary.suites !== void 0) {
|
|
1455
|
-
lines.push(`Suites: ${result.summary.suites}`);
|
|
1456
|
-
}
|
|
1457
|
-
const failedTests = result.summary.tests.filter((test) => test.status === "fail");
|
|
1458
|
-
if (failedTests.length > 0) {
|
|
1459
|
-
lines.push("", "Failed tests:");
|
|
1460
|
-
for (const test of failedTests) {
|
|
1461
|
-
lines.push(`- ${test.name}${test.file ? ` (${test.file})` : ""}`);
|
|
1462
|
-
if (test.error) lines.push(` ${test.error}`);
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
return lines.join("\n");
|
|
1466
|
-
}
|
|
1467
|
-
function formatGitContext(result) {
|
|
1468
|
-
const lines = [
|
|
1469
|
-
`Branch: ${result.branch}`,
|
|
1470
|
-
`Staged: ${result.status.staged.length}`,
|
|
1471
|
-
...result.status.staged.map((file) => ` - ${file}`),
|
|
1472
|
-
`Modified: ${result.status.modified.length}`,
|
|
1473
|
-
...result.status.modified.map((file) => ` - ${file}`),
|
|
1474
|
-
`Untracked: ${result.status.untracked.length}`,
|
|
1475
|
-
...result.status.untracked.map((file) => ` - ${file}`),
|
|
1476
|
-
"",
|
|
1477
|
-
"Recent commits:"
|
|
1478
|
-
];
|
|
1479
|
-
if (result.recentCommits.length === 0) {
|
|
1480
|
-
lines.push(" none");
|
|
1481
|
-
} else {
|
|
1482
|
-
for (const commit of result.recentCommits) {
|
|
1483
|
-
lines.push(` - ${commit.hash} ${commit.message}`);
|
|
1484
|
-
lines.push(` ${commit.author} @ ${commit.date}`);
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
if (result.diff) {
|
|
1488
|
-
lines.push("", "Diff stat:", result.diff);
|
|
1489
|
-
}
|
|
1490
|
-
return lines.join("\n");
|
|
1491
|
-
}
|
|
1492
|
-
function formatDiffFiles(files) {
|
|
1493
|
-
if (files.length === 0) {
|
|
1494
|
-
return "No diff files found.";
|
|
1495
|
-
}
|
|
1496
|
-
const lines = [];
|
|
1497
|
-
for (const file of files) {
|
|
1498
|
-
const renameInfo = file.oldPath ? ` (from ${file.oldPath})` : "";
|
|
1499
|
-
lines.push(
|
|
1500
|
-
`${file.path}${renameInfo} [${file.status}] +${file.additions} -${file.deletions} (${file.hunks.length} hunks)`
|
|
1501
|
-
);
|
|
1502
|
-
for (const hunk of file.hunks) {
|
|
1503
|
-
const suffix = hunk.header ? ` ${hunk.header}` : "";
|
|
1504
|
-
lines.push(
|
|
1505
|
-
` @@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@${suffix}`
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
|
-
return lines.join("\n");
|
|
1510
|
-
}
|
|
1511
|
-
function formatFileSummary(summary) {
|
|
1512
|
-
const lines = [
|
|
1513
|
-
summary.path,
|
|
1514
|
-
`Language: ${summary.language}`,
|
|
1515
|
-
`Lines: ${summary.lines}`,
|
|
1516
|
-
`Estimated tokens: ~${summary.estimatedTokens}`,
|
|
1517
|
-
"",
|
|
1518
|
-
`Imports (${summary.imports.length}):`,
|
|
1519
|
-
...formatIndentedList(summary.imports),
|
|
1520
|
-
"",
|
|
1521
|
-
`Exports (${summary.exports.length}):`,
|
|
1522
|
-
...formatIndentedList(summary.exports),
|
|
1523
|
-
"",
|
|
1524
|
-
`Functions (${summary.functions.length}):`,
|
|
1525
|
-
...formatIndentedList(
|
|
1526
|
-
summary.functions.map(
|
|
1527
|
-
(item) => `${item.name} @ line ${item.line}${item.exported ? " [exported]" : ""}`
|
|
1528
|
-
)
|
|
1529
|
-
),
|
|
1530
|
-
"",
|
|
1531
|
-
`Classes (${summary.classes.length}):`,
|
|
1532
|
-
...formatIndentedList(
|
|
1533
|
-
summary.classes.map(
|
|
1534
|
-
(item) => `${item.name} @ line ${item.line}${item.exported ? " [exported]" : ""}`
|
|
1535
|
-
)
|
|
1536
|
-
),
|
|
1537
|
-
"",
|
|
1538
|
-
`Interfaces (${summary.interfaces.length}):`,
|
|
1539
|
-
...formatIndentedList(summary.interfaces.map((item) => `${item.name} @ line ${item.line}`)),
|
|
1540
|
-
"",
|
|
1541
|
-
`Types (${summary.types.length}):`,
|
|
1542
|
-
...formatIndentedList(summary.types.map((item) => `${item.name} @ line ${item.line}`))
|
|
1543
|
-
];
|
|
1544
|
-
return lines.join("\n");
|
|
1545
|
-
}
|
|
1546
|
-
function formatCheckpoint(checkpoint) {
|
|
1547
|
-
const lines = [checkpoint.id, `Label: ${checkpoint.label}`, `Created: ${checkpoint.createdAt}`];
|
|
1548
|
-
if (checkpoint.notes) lines.push(`Notes: ${checkpoint.notes}`);
|
|
1549
|
-
if (checkpoint.files?.length) {
|
|
1550
|
-
lines.push(`Files: ${checkpoint.files.length}`);
|
|
1551
|
-
for (const file of checkpoint.files) lines.push(` - ${file}`);
|
|
1552
|
-
}
|
|
1553
|
-
lines.push("", "Data:", JSON.stringify(checkpoint.data, null, 2));
|
|
1554
|
-
return lines.join("\n");
|
|
1555
|
-
}
|
|
1556
|
-
function formatIndentedList(items) {
|
|
1557
|
-
return items.length === 0 ? [" none"] : items.map((item) => ` - ${item}`);
|
|
1558
|
-
}
|
|
1559
|
-
function parseMaybeJsonString(value) {
|
|
1560
|
-
const trimmed = value.trim();
|
|
1561
|
-
if (!trimmed) return "";
|
|
1562
|
-
try {
|
|
1563
|
-
return JSON.parse(trimmed);
|
|
1564
|
-
} catch {
|
|
1565
|
-
return value;
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
function parseRecordString(value) {
|
|
1569
|
-
const trimmed = value?.trim();
|
|
1570
|
-
if (!trimmed) return {};
|
|
1571
|
-
const parsed = JSON.parse(trimmed);
|
|
1572
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1573
|
-
throw new Error("data must be a JSON object string");
|
|
1574
|
-
}
|
|
1575
|
-
return parsed;
|
|
1576
|
-
}
|
|
1577
|
-
function registerWebFetchTool(server) {
|
|
1578
|
-
server.registerTool(
|
|
1579
|
-
"web_fetch",
|
|
1580
|
-
{
|
|
1581
|
-
description: "PREFERRED web fetcher \u2014 fetch any URL and convert to LLM-optimized markdown. Supports CSS selectors, 4 output modes (markdown/raw/links/outline), smart paragraph-boundary truncation. Strips scripts/styles/nav automatically.",
|
|
1582
|
-
inputSchema: {
|
|
1583
|
-
url: z.string().url().describe("URL to fetch (http/https only)"),
|
|
1584
|
-
mode: z.enum(["markdown", "raw", "links", "outline"]).default("markdown").describe(
|
|
1585
|
-
"Output mode: markdown (clean content), raw (HTML), links (extracted URLs), outline (heading hierarchy)"
|
|
1586
|
-
),
|
|
1587
|
-
selector: z.string().optional().describe(
|
|
1588
|
-
"CSS selector to extract a specific element instead of auto-detecting main content"
|
|
1589
|
-
),
|
|
1590
|
-
max_length: z.number().min(500).max(1e5).default(15e3).describe("Max characters in output \u2014 truncates at paragraph boundaries"),
|
|
1591
|
-
include_metadata: z.boolean().default(true).describe("Include page title, description, and URL as a header"),
|
|
1592
|
-
include_links: z.boolean().default(false).describe("Append extracted links list at the end"),
|
|
1593
|
-
include_images: z.boolean().default(false).describe("Include image alt texts inline"),
|
|
1594
|
-
timeout: z.number().min(1e3).max(6e4).default(15e3).describe("Request timeout in milliseconds")
|
|
1595
|
-
}
|
|
1596
|
-
},
|
|
1597
|
-
async ({
|
|
1598
|
-
url,
|
|
1599
|
-
mode,
|
|
1600
|
-
selector,
|
|
1601
|
-
max_length,
|
|
1602
|
-
include_metadata,
|
|
1603
|
-
include_links,
|
|
1604
|
-
include_images,
|
|
1605
|
-
timeout
|
|
1606
|
-
}) => {
|
|
1607
|
-
try {
|
|
1608
|
-
const result = await webFetch({
|
|
1609
|
-
url,
|
|
1610
|
-
mode,
|
|
1611
|
-
selector,
|
|
1612
|
-
maxLength: max_length,
|
|
1613
|
-
includeMetadata: include_metadata,
|
|
1614
|
-
includeLinks: include_links,
|
|
1615
|
-
includeImages: include_images,
|
|
1616
|
-
timeout
|
|
1617
|
-
});
|
|
1618
|
-
const meta = [`## ${result.title || "Web Page"}`, "", result.content];
|
|
1619
|
-
if (result.truncated) {
|
|
1620
|
-
meta.push("", `_Original length: ${result.originalLength.toLocaleString()} chars_`);
|
|
1621
|
-
}
|
|
1622
|
-
meta.push(
|
|
1623
|
-
"",
|
|
1624
|
-
"---",
|
|
1625
|
-
`_Next: Use \`remember\` to save key findings, or \`web_fetch\` with a \`selector\` to extract a specific section._`
|
|
1626
|
-
);
|
|
1627
|
-
return {
|
|
1628
|
-
content: [{ type: "text", text: meta.join("\n") }]
|
|
1629
|
-
};
|
|
1630
|
-
} catch (err) {
|
|
1631
|
-
return {
|
|
1632
|
-
content: [
|
|
1633
|
-
{
|
|
1634
|
-
type: "text",
|
|
1635
|
-
text: `Web fetch failed: ${err.message}`
|
|
1636
|
-
}
|
|
1637
|
-
],
|
|
1638
|
-
isError: true
|
|
1639
|
-
};
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
);
|
|
1643
|
-
}
|
|
1644
|
-
export {
|
|
1645
|
-
registerBatchTool,
|
|
1646
|
-
registerCheckTool,
|
|
1647
|
-
registerCheckpointTool,
|
|
1648
|
-
registerCodemodTool,
|
|
1649
|
-
registerCompactTool,
|
|
1650
|
-
registerDataTransformTool,
|
|
1651
|
-
registerDeadSymbolsTool,
|
|
1652
|
-
registerDelegateTool,
|
|
1653
|
-
registerDiffParseTool,
|
|
1654
|
-
registerEvalTool,
|
|
1655
|
-
registerFileSummaryTool,
|
|
1656
|
-
registerFindExamplesTool,
|
|
1657
|
-
registerFindTool,
|
|
1658
|
-
registerGitContextTool,
|
|
1659
|
-
registerHealthTool,
|
|
1660
|
-
registerLaneTool,
|
|
1661
|
-
registerParseOutputTool,
|
|
1662
|
-
registerProcessTool,
|
|
1663
|
-
registerQueueTool,
|
|
1664
|
-
registerRenameTool,
|
|
1665
|
-
registerScopeMapTool,
|
|
1666
|
-
registerStashTool,
|
|
1667
|
-
registerSymbolTool,
|
|
1668
|
-
registerTestRunTool,
|
|
1669
|
-
registerTraceTool,
|
|
1670
|
-
registerWatchTool,
|
|
1671
|
-
registerWebFetchTool,
|
|
1672
|
-
registerWorksetTool
|
|
1673
|
-
};
|
|
1674
|
-
//# sourceMappingURL=toolkit.tools.js.map
|
|
11
|
+
${o.output}`}]}:{content:[{type:"text",text:`Eval failed in ${o.durationMs}ms: ${o.error??"Unknown error"}`}],isError:!0}}catch(o){return console.error("[KB] Eval failed:",o),{content:[{type:"text",text:`Eval failed: ${o.message}`}],isError:!0}}})}function ze(t){t.registerTool("test_run",{description:"Run Vitest for the current project or a subset of files, then return a structured summary of passing and failing tests.",inputSchema:{files:r.array(r.string()).optional().describe("Specific test files or patterns to run"),grep:r.string().optional().describe("Only run tests whose names match this pattern"),cwd:r.string().optional().describe("Working directory for the test run")}},async({files:n,grep:e,cwd:s})=>{try{const o=await he({files:n,grep:e,cwd:s});return{content:[{type:"text",text:Me(o)}],isError:!o.passed}}catch(o){return{content:[{type:"text",text:`Test run failed: ${o.message}`}],isError:!0}}})}function Ge(t){t.registerTool("stash",{description:"Persist and retrieve named values in .kb-state/stash.json for intermediate results between tool calls.",inputSchema:{action:r.enum(["set","get","list","delete","clear"]).describe("Operation to perform on the stash"),key:r.string().optional().describe("Entry key for set/get/delete operations"),value:r.string().optional().describe("String or JSON value for set operations")}},async({action:n,key:e,value:s})=>{try{switch(n){case"set":{if(!e)throw new Error("key required for set");const o=me(e,Ie(s??""));return{content:[{type:"text",text:`Stored stash entry "${o.key}" (${o.type}) at ${o.storedAt}.`}]}}case"get":{if(!e)throw new Error("key required for get");const o=pe(e);return{content:[{type:"text",text:o?JSON.stringify(o,null,2):`Stash entry "${e}" not found.`}]}}case"list":{const o=fe();return{content:[{type:"text",text:o.length===0?"Stash is empty.":o.map(i=>`- ${i.key} (${i.type}) \u2014 ${i.storedAt}`).join(`
|
|
12
|
+
`)}]}}case"delete":{if(!e)throw new Error("key required for delete");return{content:[{type:"text",text:ue(e)?`Deleted stash entry "${e}".`:`Stash entry "${e}" not found.`}]}}case"clear":{const o=de();return{content:[{type:"text",text:`Cleared ${o} stash entr${o===1?"y":"ies"}.`}]}}}}catch(o){return{content:[{type:"text",text:`Stash operation failed: ${o.message}`}],isError:!0}}})}function Qe(t){t.registerTool("git_context",{description:"Summarize the current Git branch, working tree state, recent commits, and optional diff statistics for the repository.",inputSchema:{cwd:r.string().optional().describe("Repository root or working directory"),commit_count:r.number().min(1).max(50).default(5).optional().describe("How many recent commits to include"),include_diff:r.boolean().default(!1).optional().describe("Include diff stat for working tree changes")}},async({cwd:n,commit_count:e,include_diff:s})=>{try{const o=await j({cwd:n,commitCount:e,includeDiff:s});return{content:[{type:"text",text:Ne(o)}]}}catch(o){return{content:[{type:"text",text:`Git context failed: ${o.message}`}],isError:!0}}})}function He(t){t.registerTool("diff_parse",{description:"Parse raw unified diff text into file-level and hunk-level structural changes.",inputSchema:{diff:r.string().describe("Raw unified diff text")}},async({diff:n})=>{try{const e=n.replace(/\\n/g,`
|
|
13
|
+
`).replace(/\\t/g," "),s=N({diff:e});return{content:[{type:"text",text:_e(s)}]}}catch(e){return{content:[{type:"text",text:`Diff parse failed: ${e.message}`}],isError:!0}}})}function Ve(t){t.registerTool("rename",{description:"Rename a symbol across files using whole-word regex matching for exports, imports, and general usage references.",inputSchema:{old_name:r.string().describe("Existing symbol name to replace"),new_name:r.string().describe("New symbol name to use"),root_path:r.string().describe("Root directory to search within"),extensions:r.array(r.string()).optional().describe("Optional file extensions to include, such as .ts,.tsx,.js,.jsx"),dry_run:r.boolean().default(!0).describe("Preview changes without writing files")}},async({old_name:n,new_name:e,root_path:s,extensions:o,dry_run:i})=>{try{const a=await ae({oldName:n,newName:e,rootPath:s,extensions:o,dryRun:i});return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return{content:[{type:"text",text:`Rename failed: ${a.message}`}],isError:!0}}})}function Xe(t){t.registerTool("codemod",{description:"Apply regex-based codemod rules across files and return structured before/after changes for each affected line.",inputSchema:{root_path:r.string().describe("Root directory to transform within"),rules:r.array(r.object({description:r.string().describe("What the codemod rule does"),pattern:r.string().describe("Regex pattern in string form"),replacement:r.string().describe("Replacement string with optional capture groups")})).min(1).describe("Codemod rules to apply"),dry_run:r.boolean().default(!0).describe("Preview changes without writing files")}},async({root_path:n,rules:e,dry_run:s})=>{try{const o=await E({rootPath:n,rules:e,dryRun:s});return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return{content:[{type:"text",text:`Codemod failed: ${o.message}`}],isError:!0}}})}function Ye(t){t.registerTool("file_summary",{description:"Create a concise structural summary of a source file: imports, exports, functions, classes, interfaces, and types.",inputSchema:{path:r.string().describe("Absolute path to the file to summarize")}},async({path:n})=>{try{const e=await R({path:n});return{content:[{type:"text",text:Re(e)}]}}catch(e){return{content:[{type:"text",text:`File summary failed: ${e.message}`}],isError:!0}}})}function Ze(t){t.registerTool("checkpoint",{description:"Save and restore lightweight session checkpoints in .kb-state/checkpoints for cross-session continuity.",inputSchema:{action:r.enum(["save","load","list","latest"]).describe("Checkpoint action to perform"),label:r.string().optional().describe("Checkpoint label for save, or checkpoint id for load"),data:r.string().optional().describe("JSON object string for save actions"),notes:r.string().optional().describe("Optional notes for save actions")}},async({action:n,label:e,data:s,notes:o})=>{try{switch(n){case"save":{if(!e)throw new Error("label required for save");const i=k(e,Je(s),{notes:o});return{content:[{type:"text",text:f(i)}]}}case"load":{if(!e)throw new Error("label required for load");const i=$(e);return{content:[{type:"text",text:i?f(i):`Checkpoint "${e}" not found.`}]}}case"list":{const i=S();return{content:[{type:"text",text:i.length===0?"No checkpoints saved.":i.map(a=>`- ${a.id} \u2014 ${a.label} (${a.createdAt})`).join(`
|
|
14
|
+
`)}]}}case"latest":{const i=w();return{content:[{type:"text",text:i?f(i):"No checkpoints saved."}]}}}}catch(i){return{content:[{type:"text",text:`Checkpoint failed: ${i.message}`}],isError:!0}}})}function et(t){t.registerTool("data_transform",{description:"Apply small jq-like transforms to JSON input for filtering, projection, grouping, and path extraction.",inputSchema:{input:r.string().describe("Input JSON string"),expression:r.string().describe("Transform expression to apply")}},async({input:n,expression:e})=>{try{return{content:[{type:"text",text:T({input:n,expression:e}).outputString}]}}catch(s){return{content:[{type:"text",text:`Data transform failed: ${s.message}`}],isError:!0}}})}function tt(t,n,e){t.registerTool("trace",{description:"Trace data flow through a codebase by following imports, call sites, and references from a starting symbol or file location.",inputSchema:{start:r.string().describe("Starting point \u2014 symbol name or file:line reference"),direction:r.enum(["forward","backward","both"]).describe("Which direction to trace relationships"),max_depth:r.number().min(1).max(10).default(3).optional().describe("Maximum trace depth")}},async({start:s,direction:o,max_depth:i})=>{try{const a=await ye(n,e,{start:s,direction:o,maxDepth:i});return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return{content:[{type:"text",text:`Trace failed: ${a.message}`}],isError:!0}}})}function rt(t,n,e){t.registerTool("find_examples",{description:"Find real usage examples of a function, class, or pattern in the indexed codebase.",inputSchema:{query:r.string().describe("Symbol or pattern to find examples of"),limit:r.number().min(1).max(20).default(5).optional().describe("Maximum examples to return"),content_type:r.string().optional().describe("Filter by content type")}},async({query:s,limit:o,content_type:i})=>{try{const a=await J(n,e,{query:s,limit:o,contentType:i});return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return{content:[{type:"text",text:`Find examples failed: ${a.message}`}],isError:!0}}})}function nt(t){t.registerTool("process",{description:"Start, stop, inspect, list, and tail logs for in-memory managed child processes.",inputSchema:{action:r.enum(["start","stop","status","list","logs"]).describe("Process action to perform"),id:r.string().optional().describe("Managed process ID"),command:r.string().optional().describe("Executable to start"),args:r.array(r.string()).optional().describe("Arguments for start actions"),tail:r.number().min(1).max(500).optional().describe("Log lines to return for logs actions")}},async({action:n,id:e,command:s,args:o,tail:i})=>{try{switch(n){case"start":{if(!e||!s)throw new Error("id and command are required for start");return{content:[{type:"text",text:JSON.stringify(Q(e,s,o??[]),null,2)}]}}case"stop":{if(!e)throw new Error("id is required for stop");return{content:[{type:"text",text:JSON.stringify(V(e)??null,null,2)}]}}case"status":{if(!e)throw new Error("id is required for status");return{content:[{type:"text",text:JSON.stringify(H(e)??null,null,2)}]}}case"list":return{content:[{type:"text",text:JSON.stringify(z(),null,2)}]};case"logs":{if(!e)throw new Error("id is required for logs");return{content:[{type:"text",text:JSON.stringify(G(e,i),null,2)}]}}}}catch(a){return{content:[{type:"text",text:`Process action failed: ${a.message}`}],isError:!0}}})}function ot(t){t.registerTool("watch",{description:"Start, stop, and list in-memory filesystem watchers for a directory.",inputSchema:{action:r.enum(["start","stop","list"]).describe("Watch action to perform"),path:r.string().optional().describe("Directory path to watch for start actions"),id:r.string().optional().describe("Watcher ID for stop actions")}},async({action:n,path:e,id:s})=>{try{switch(n){case"start":{if(!e)throw new Error("path is required for start");return{content:[{type:"text",text:JSON.stringify(be({path:e}),null,2)}]}}case"stop":{if(!s)throw new Error("id is required for stop");return{content:[{type:"text",text:JSON.stringify({stopped:we(s)},null,2)}]}}case"list":return{content:[{type:"text",text:JSON.stringify(xe(),null,2)}]}}}catch(o){return{content:[{type:"text",text:`Watch action failed: ${o.message}`}],isError:!0}}})}function st(t,n,e){t.registerTool("dead_symbols",{description:"Find exported symbols that appear to be unused because they are never imported or re-exported.",inputSchema:{limit:r.number().min(1).max(500).default(100).optional().describe("Maximum exported symbols to scan")}},async({limit:s})=>{try{const o=await I(n,e,{limit:s});return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return{content:[{type:"text",text:`Dead symbol scan failed: ${o.message}`}],isError:!0}}})}function it(t){t.registerTool("delegate",{description:"Delegate a subtask to a local Ollama model. Use for summarization, classification, naming, or any task that can offload work from the host agent. Fails fast if Ollama is not running.",inputSchema:{prompt:r.string().describe("The task or question to send to the local model"),model:r.string().optional().describe("Ollama model name (default: first available model)"),system:r.string().optional().describe("System prompt for the model"),context:r.string().optional().describe("Context text to include before the prompt (e.g. file contents)"),temperature:r.number().min(0).max(2).default(.3).optional().describe("Sampling temperature (0=deterministic, default 0.3)"),timeout:r.number().min(1e3).max(6e5).default(12e4).optional().describe("Timeout in milliseconds (default 120000)"),action:r.enum(["generate","list_models"]).default("generate").optional().describe("Action: generate a response or list available models")}},async({prompt:n,model:e,system:s,context:o,temperature:i,timeout:a,action:l})=>{try{if(l==="list_models"){const u=await O();return{content:[{type:"text",text:JSON.stringify({models:u,count:u.length,_Next:"Use delegate with a model name"},null,2)}]}}const c=await q({prompt:n,model:e,system:s,context:o,temperature:i,timeout:a});return c.error?{content:[{type:"text",text:JSON.stringify({error:c.error,model:c.model,durationMs:c.durationMs},null,2)}],isError:!0}:{content:[{type:"text",text:JSON.stringify({model:c.model,response:c.response,durationMs:c.durationMs,tokenCount:c.tokenCount,_Next:"Use the response in your workflow. stash to save it."},null,2)}]}}catch(c){return{content:[{type:"text",text:`Delegate failed: ${c.message}`}],isError:!0}}})}function at(t){t.registerTool("lane",{description:"Manage verified lanes \u2014 isolated file copies for parallel exploration. Create a lane, make changes, diff, merge back, or discard.",inputSchema:{action:r.enum(["create","list","status","diff","merge","discard"]).describe("Lane action to perform"),name:r.string().optional().describe("Lane name (required for create/status/diff/merge/discard)"),files:r.array(r.string()).optional().describe("File paths to copy into the lane (required for create)")}},async({action:n,name:e,files:s})=>{try{switch(n){case"create":{if(!e)throw new Error("name is required for create");if(!s||s.length===0)throw new Error("files are required for create");const o=F(e,s);return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}case"list":return{content:[{type:"text",text:JSON.stringify(P(),null,2)}]};case"status":{if(!e)throw new Error("name is required for status");return{content:[{type:"text",text:JSON.stringify(K(e),null,2)}]}}case"diff":{if(!e)throw new Error("name is required for diff");return{content:[{type:"text",text:JSON.stringify(D(e),null,2)}]}}case"merge":{if(!e)throw new Error("name is required for merge");return{content:[{type:"text",text:JSON.stringify(W(e),null,2)}]}}case"discard":{if(!e)throw new Error("name is required for discard");return{content:[{type:"text",text:JSON.stringify({discarded:A(e)},null,2)}]}}}}catch(o){return{content:[{type:"text",text:`Lane action failed: ${o.message}`}],isError:!0}}})}function ct(t){t.registerTool("health",{description:"Run project health checks \u2014 verifies package.json, tsconfig, scripts, lockfile, README, LICENSE, .gitignore.",inputSchema:{path:r.string().optional().describe("Root directory to check (defaults to cwd)")}},async({path:n})=>{try{const e=L(n);return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return{content:[{type:"text",text:`Health check failed: ${e.message}`}],isError:!0}}})}function lt(t){t.registerTool("queue",{description:"Manage task queues for sequential agent operations. Push items, take next, mark done/failed, list queues.",inputSchema:{action:r.enum(["create","push","next","done","fail","get","list","clear","delete"]).describe("Queue action"),name:r.string().optional().describe("Queue name (required for most actions)"),title:r.string().optional().describe("Item title (required for push)"),id:r.string().optional().describe("Item ID (required for done/fail)"),data:r.unknown().optional().describe("Arbitrary data to attach to a queue item"),error:r.string().optional().describe("Error message (required for fail)")}},async({action:n,name:e,title:s,id:o,data:i,error:a})=>{try{switch(n){case"create":{if(!e)throw new Error("name is required for create");return{content:[{type:"text",text:JSON.stringify(Y(e),null,2)}]}}case"push":{if(!e)throw new Error("name is required for push");if(!s)throw new Error("title is required for push");return{content:[{type:"text",text:JSON.stringify(se(e,s,i),null,2)}]}}case"next":{if(!e)throw new Error("name is required for next");const l=oe(e);return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}case"done":{if(!e)throw new Error("name is required for done");if(!o)throw new Error("id is required for done");return{content:[{type:"text",text:JSON.stringify(ee(e,o),null,2)}]}}case"fail":{if(!e)throw new Error("name is required for fail");if(!o)throw new Error("id is required for fail");if(!a)throw new Error("error is required for fail");return{content:[{type:"text",text:JSON.stringify(te(e,o,a),null,2)}]}}case"get":{if(!e)throw new Error("name is required for get");return{content:[{type:"text",text:JSON.stringify(re(e),null,2)}]}}case"list":return{content:[{type:"text",text:JSON.stringify(ne(),null,2)}]};case"clear":{if(!e)throw new Error("name is required for clear");return{content:[{type:"text",text:JSON.stringify({cleared:X(e)},null,2)}]}}case"delete":{if(!e)throw new Error("name is required for delete");return{content:[{type:"text",text:JSON.stringify({deleted:Z(e)},null,2)}]}}}}catch(l){return{content:[{type:"text",text:`Queue action failed: ${l.message}`}],isError:!0}}})}const $e=r.object({query:r.string(),limit:r.number().min(1).max(20).default(5).optional(),search_mode:r.enum(["hybrid","semantic","keyword"]).default("hybrid").optional(),content_type:r.string().optional(),origin:r.enum(["indexed","curated","produced"]).optional(),category:r.string().optional(),tags:r.array(r.string()).optional(),min_score:r.number().min(0).max(1).default(.25).optional()}),ke=r.object({query:r.string().optional(),glob:r.string().optional(),pattern:r.string().optional(),limit:r.number().min(1).max(50).default(10).optional(),content_type:r.string().optional(),cwd:r.string().optional()}),Ee=r.object({files:r.array(r.string()).optional(),cwd:r.string().optional(),skip_types:r.boolean().optional(),skip_lint:r.boolean().optional()});async function ve(t,n,e){switch(t.type){case"search":{const s=$e.parse(t.args);return Te(n,e,s)}case"find":{const s=ke.parse(t.args);if(!s.query&&!s.glob&&!s.pattern)throw new Error("find operation requires query, glob, or pattern");return g(n,e,{query:s.query,glob:s.glob,pattern:s.pattern,limit:s.limit,contentType:s.content_type,cwd:s.cwd})}case"check":{const s=Ee.parse(t.args);return m({files:s.files,cwd:s.cwd,skipTypes:s.skip_types,skipLint:s.skip_lint})}default:throw new Error(`Unsupported batch operation type: ${t.type}`)}}async function Te(t,n,e){const s=e.limit??5,o={limit:s,minScore:e.min_score??.25,contentType:e.content_type,origin:e.origin,category:e.category,tags:e.tags},i=t.embedQuery?.bind(t)??t.embed.bind(t);if(e.search_mode==="keyword")return(await n.ftsSearch(e.query,o)).slice(0,s);const a=await i(e.query);if(e.search_mode==="semantic")return n.search(a,o);const[l,c]=await Promise.all([n.search(a,{...o,limit:s*2}),n.ftsSearch(e.query,{...o,limit:s*2})]);return qe(l,c).slice(0,s)}function qe(t,n,e=60){const s=new Map;for(let o=0;o<t.length;o++){const i=t[o];s.set(i.record.id,{record:i.record,score:1/(e+o+1)})}for(let o=0;o<n.length;o++){const i=n[o],a=s.get(i.record.id);if(a){a.score+=1/(e+o+1);continue}s.set(i.record.id,{record:i.record,score:1/(e+o+1)})}return[...s.values()].sort((o,i)=>i.score-o.score)}function Oe(t){const n=[`Symbol: ${t.name}`];if(t.definedIn?n.push(`Defined in: ${t.definedIn.path}:${t.definedIn.line} (${t.definedIn.kind})`):n.push("Defined in: not found"),n.push("","Imported by:"),t.importedBy.length===0)n.push(" none");else for(const e of t.importedBy)n.push(` - ${e.path}:${e.line} ${e.importStatement}`);if(n.push("","Referenced in:"),t.referencedIn.length===0)n.push(" none");else for(const e of t.referencedIn)n.push(` - ${e.path}:${e.line} ${e.context}`);return n.join(`
|
|
15
|
+
`)}function Me(t){const n=[`Vitest run: ${t.passed?"passed":"failed"}`,`Duration: ${t.durationMs}ms`,`Passed: ${t.summary.passed}`,`Failed: ${t.summary.failed}`,`Skipped: ${t.summary.skipped}`];t.summary.suites!==void 0&&n.push(`Suites: ${t.summary.suites}`);const e=t.summary.tests.filter(s=>s.status==="fail");if(e.length>0){n.push("","Failed tests:");for(const s of e)n.push(`- ${s.name}${s.file?` (${s.file})`:""}`),s.error&&n.push(` ${s.error}`)}return n.join(`
|
|
16
|
+
`)}function Ne(t){const n=[`Branch: ${t.branch}`,`Staged: ${t.status.staged.length}`,...t.status.staged.map(e=>` - ${e}`),`Modified: ${t.status.modified.length}`,...t.status.modified.map(e=>` - ${e}`),`Untracked: ${t.status.untracked.length}`,...t.status.untracked.map(e=>` - ${e}`),"","Recent commits:"];if(t.recentCommits.length===0)n.push(" none");else for(const e of t.recentCommits)n.push(` - ${e.hash} ${e.message}`),n.push(` ${e.author} @ ${e.date}`);return t.diff&&n.push("","Diff stat:",t.diff),n.join(`
|
|
17
|
+
`)}function _e(t){if(t.length===0)return"No diff files found.";const n=[];for(const e of t){const s=e.oldPath?` (from ${e.oldPath})`:"";n.push(`${e.path}${s} [${e.status}] +${e.additions} -${e.deletions} (${e.hunks.length} hunks)`);for(const o of e.hunks){const i=o.header?` ${o.header}`:"";n.push(` @@ -${o.oldStart},${o.oldLines} +${o.newStart},${o.newLines} @@${i}`)}}return n.join(`
|
|
18
|
+
`)}function Re(t){return[t.path,`Language: ${t.language}`,`Lines: ${t.lines}`,`Estimated tokens: ~${t.estimatedTokens}`,"",`Imports (${t.imports.length}):`,...p(t.imports),"",`Exports (${t.exports.length}):`,...p(t.exports),"",`Functions (${t.functions.length}):`,...p(t.functions.map(e=>`${e.name} @ line ${e.line}${e.exported?" [exported]":""}`)),"",`Classes (${t.classes.length}):`,...p(t.classes.map(e=>`${e.name} @ line ${e.line}${e.exported?" [exported]":""}`)),"",`Interfaces (${t.interfaces.length}):`,...p(t.interfaces.map(e=>`${e.name} @ line ${e.line}`)),"",`Types (${t.types.length}):`,...p(t.types.map(e=>`${e.name} @ line ${e.line}`))].join(`
|
|
19
|
+
`)}function f(t){const n=[t.id,`Label: ${t.label}`,`Created: ${t.createdAt}`];if(t.notes&&n.push(`Notes: ${t.notes}`),t.files?.length){n.push(`Files: ${t.files.length}`);for(const e of t.files)n.push(` - ${e}`)}return n.push("","Data:",JSON.stringify(t.data,null,2)),n.join(`
|
|
20
|
+
`)}function p(t){return t.length===0?[" none"]:t.map(n=>` - ${n}`)}function Ie(t){const n=t.trim();if(!n)return"";try{return JSON.parse(n)}catch{return t}}function Je(t){const n=t?.trim();if(!n)return{};const e=JSON.parse(n);if(!e||typeof e!="object"||Array.isArray(e))throw new Error("data must be a JSON object string");return e}function dt(t){t.registerTool("web_fetch",{description:"PREFERRED web fetcher \u2014 fetch any URL and convert to LLM-optimized markdown. Supports CSS selectors, 4 output modes (markdown/raw/links/outline), smart paragraph-boundary truncation. Strips scripts/styles/nav automatically.",inputSchema:{url:r.string().url().describe("URL to fetch (http/https only)"),mode:r.enum(["markdown","raw","links","outline"]).default("markdown").describe("Output mode: markdown (clean content), raw (HTML), links (extracted URLs), outline (heading hierarchy)"),selector:r.string().optional().describe("CSS selector to extract a specific element instead of auto-detecting main content"),max_length:r.number().min(500).max(1e5).default(15e3).describe("Max characters in output \u2014 truncates at paragraph boundaries"),include_metadata:r.boolean().default(!0).describe("Include page title, description, and URL as a header"),include_links:r.boolean().default(!1).describe("Append extracted links list at the end"),include_images:r.boolean().default(!1).describe("Include image alt texts inline"),timeout:r.number().min(1e3).max(6e4).default(15e3).describe("Request timeout in milliseconds")}},async({url:n,mode:e,selector:s,max_length:o,include_metadata:i,include_links:a,include_images:l,timeout:c})=>{try{const u=await Se({url:n,mode:e,selector:s,maxLength:o,includeMetadata:i,includeLinks:a,includeImages:l,timeout:c}),d=[`## ${u.title||"Web Page"}`,"",u.content];return u.truncated&&d.push("",`_Original length: ${u.originalLength.toLocaleString()} chars_`),d.push("","---","_Next: Use `remember` to save key findings, or `web_fetch` with a `selector` to extract a specific section._"),{content:[{type:"text",text:d.join(`
|
|
21
|
+
`)}]}}catch(u){return{content:[{type:"text",text:`Web fetch failed: ${u.message}`}],isError:!0}}})}export{Ke as registerBatchTool,We as registerCheckTool,Ze as registerCheckpointTool,Xe as registerCodemodTool,Le as registerCompactTool,et as registerDataTransformTool,st as registerDeadSymbolsTool,it as registerDelegateTool,He as registerDiffParseTool,Be as registerEvalTool,Ye as registerFileSummaryTool,rt as registerFindExamplesTool,De as registerFindTool,Qe as registerGitContextTool,ct as registerHealthTool,at as registerLaneTool,Ae as registerParseOutputTool,nt as registerProcessTool,lt as registerQueueTool,Ve as registerRenameTool,Fe as registerScopeMapTool,Ge as registerStashTool,Ue as registerSymbolTool,ze as registerTestRunTool,tt as registerTraceTool,ot as registerWatchTool,dt as registerWebFetchTool,Pe as registerWorksetTool};
|