@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.
Files changed (138) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
  4. package/packages/analyzers/dist/dependency-analyzer.js +11 -425
  5. package/packages/analyzers/dist/diagram-generator.js +4 -86
  6. package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
  7. package/packages/analyzers/dist/index.js +1 -23
  8. package/packages/analyzers/dist/knowledge-producer.js +24 -113
  9. package/packages/analyzers/dist/pattern-analyzer.js +5 -359
  10. package/packages/analyzers/dist/regex-call-graph.js +1 -428
  11. package/packages/analyzers/dist/structure-analyzer.js +4 -258
  12. package/packages/analyzers/dist/symbol-analyzer.js +13 -442
  13. package/packages/analyzers/dist/ts-call-graph.js +1 -160
  14. package/packages/analyzers/dist/types.js +0 -1
  15. package/packages/chunker/dist/call-graph-extractor.js +1 -90
  16. package/packages/chunker/dist/chunker-factory.js +1 -36
  17. package/packages/chunker/dist/chunker.interface.js +0 -1
  18. package/packages/chunker/dist/code-chunker.js +14 -134
  19. package/packages/chunker/dist/generic-chunker.js +5 -72
  20. package/packages/chunker/dist/index.js +1 -21
  21. package/packages/chunker/dist/markdown-chunker.js +7 -119
  22. package/packages/chunker/dist/treesitter-chunker.js +8 -234
  23. package/packages/cli/dist/commands/analyze.js +3 -112
  24. package/packages/cli/dist/commands/context-cmds.js +1 -155
  25. package/packages/cli/dist/commands/environment.js +2 -204
  26. package/packages/cli/dist/commands/execution.js +1 -137
  27. package/packages/cli/dist/commands/graph.js +7 -81
  28. package/packages/cli/dist/commands/init.js +9 -87
  29. package/packages/cli/dist/commands/knowledge.js +1 -139
  30. package/packages/cli/dist/commands/search.js +8 -267
  31. package/packages/cli/dist/commands/system.js +4 -241
  32. package/packages/cli/dist/commands/workspace.js +2 -388
  33. package/packages/cli/dist/context.js +1 -14
  34. package/packages/cli/dist/helpers.js +3 -458
  35. package/packages/cli/dist/index.d.ts +1 -1
  36. package/packages/cli/dist/index.js +3 -69
  37. package/packages/cli/dist/kb-init.js +1 -82
  38. package/packages/cli/dist/types.js +0 -1
  39. package/packages/core/dist/constants.js +1 -43
  40. package/packages/core/dist/content-detector.js +1 -79
  41. package/packages/core/dist/errors.js +1 -40
  42. package/packages/core/dist/index.js +1 -9
  43. package/packages/core/dist/logger.js +1 -34
  44. package/packages/core/dist/types.js +0 -1
  45. package/packages/embeddings/dist/embedder.interface.js +0 -1
  46. package/packages/embeddings/dist/index.js +1 -5
  47. package/packages/embeddings/dist/onnx-embedder.js +1 -82
  48. package/packages/indexer/dist/file-hasher.js +1 -13
  49. package/packages/indexer/dist/filesystem-crawler.js +1 -125
  50. package/packages/indexer/dist/graph-extractor.js +1 -111
  51. package/packages/indexer/dist/incremental-indexer.js +1 -278
  52. package/packages/indexer/dist/index.js +1 -14
  53. package/packages/server/dist/api.js +1 -9
  54. package/packages/server/dist/config.js +1 -75
  55. package/packages/server/dist/curated-manager.js +9 -356
  56. package/packages/server/dist/index.js +1 -134
  57. package/packages/server/dist/replay-interceptor.js +1 -38
  58. package/packages/server/dist/resources/resources.js +2 -40
  59. package/packages/server/dist/server.js +1 -247
  60. package/packages/server/dist/tools/analyze.tools.js +1 -288
  61. package/packages/server/dist/tools/forge.tools.js +11 -499
  62. package/packages/server/dist/tools/forget.tool.js +3 -39
  63. package/packages/server/dist/tools/graph.tool.js +5 -110
  64. package/packages/server/dist/tools/list.tool.js +5 -53
  65. package/packages/server/dist/tools/lookup.tool.js +8 -51
  66. package/packages/server/dist/tools/onboard.tool.js +2 -112
  67. package/packages/server/dist/tools/produce.tool.js +4 -74
  68. package/packages/server/dist/tools/read.tool.js +4 -47
  69. package/packages/server/dist/tools/reindex.tool.js +2 -70
  70. package/packages/server/dist/tools/remember.tool.js +3 -42
  71. package/packages/server/dist/tools/replay.tool.js +6 -88
  72. package/packages/server/dist/tools/search.tool.js +17 -327
  73. package/packages/server/dist/tools/status.tool.js +3 -68
  74. package/packages/server/dist/tools/toolkit.tools.js +20 -1673
  75. package/packages/server/dist/tools/update.tool.js +3 -39
  76. package/packages/server/dist/tools/utility.tools.js +19 -456
  77. package/packages/store/dist/graph-store.interface.js +0 -1
  78. package/packages/store/dist/index.js +1 -9
  79. package/packages/store/dist/lance-store.js +1 -258
  80. package/packages/store/dist/sqlite-graph-store.js +8 -309
  81. package/packages/store/dist/store-factory.js +1 -14
  82. package/packages/store/dist/store.interface.js +0 -1
  83. package/packages/tools/dist/batch.js +1 -45
  84. package/packages/tools/dist/changelog.js +2 -112
  85. package/packages/tools/dist/check.js +2 -59
  86. package/packages/tools/dist/checkpoint.js +2 -43
  87. package/packages/tools/dist/codemod.js +2 -69
  88. package/packages/tools/dist/compact.js +3 -60
  89. package/packages/tools/dist/data-transform.js +1 -124
  90. package/packages/tools/dist/dead-symbols.js +2 -71
  91. package/packages/tools/dist/delegate.js +3 -128
  92. package/packages/tools/dist/diff-parse.js +3 -153
  93. package/packages/tools/dist/digest.js +7 -242
  94. package/packages/tools/dist/encode.js +1 -46
  95. package/packages/tools/dist/env-info.js +1 -58
  96. package/packages/tools/dist/eval.js +3 -79
  97. package/packages/tools/dist/evidence-map.js +3 -203
  98. package/packages/tools/dist/file-summary.js +2 -106
  99. package/packages/tools/dist/file-walk.js +1 -75
  100. package/packages/tools/dist/find-examples.js +3 -48
  101. package/packages/tools/dist/find.js +1 -120
  102. package/packages/tools/dist/forge-classify.js +2 -319
  103. package/packages/tools/dist/forge-ground.js +1 -184
  104. package/packages/tools/dist/git-context.js +3 -46
  105. package/packages/tools/dist/graph-query.js +1 -194
  106. package/packages/tools/dist/health.js +1 -118
  107. package/packages/tools/dist/http-request.js +1 -58
  108. package/packages/tools/dist/index.js +1 -273
  109. package/packages/tools/dist/lane.js +7 -227
  110. package/packages/tools/dist/measure.js +2 -119
  111. package/packages/tools/dist/onboard.js +42 -1136
  112. package/packages/tools/dist/parse-output.js +2 -158
  113. package/packages/tools/dist/process-manager.js +1 -69
  114. package/packages/tools/dist/queue.js +2 -126
  115. package/packages/tools/dist/regex-test.js +1 -39
  116. package/packages/tools/dist/rename.js +2 -70
  117. package/packages/tools/dist/replay.js +6 -108
  118. package/packages/tools/dist/schema-validate.js +1 -141
  119. package/packages/tools/dist/scope-map.js +1 -72
  120. package/packages/tools/dist/snippet.js +1 -80
  121. package/packages/tools/dist/stash.js +2 -60
  122. package/packages/tools/dist/stratum-card.js +5 -238
  123. package/packages/tools/dist/symbol.js +3 -87
  124. package/packages/tools/dist/test-run.js +2 -55
  125. package/packages/tools/dist/text-utils.js +2 -31
  126. package/packages/tools/dist/time-utils.js +1 -135
  127. package/packages/tools/dist/trace.js +2 -114
  128. package/packages/tools/dist/truncation.js +10 -41
  129. package/packages/tools/dist/watch.js +1 -61
  130. package/packages/tools/dist/web-fetch.js +9 -244
  131. package/packages/tools/dist/web-search.js +1 -46
  132. package/packages/tools/dist/workset.js +2 -77
  133. package/packages/tui/dist/App.js +260 -52468
  134. package/packages/tui/dist/index.js +286 -54551
  135. package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
  136. package/packages/tui/dist/panels/LogPanel.js +259 -51703
  137. package/packages/tui/dist/panels/SearchPanel.js +212 -34824
  138. package/packages/tui/dist/panels/StatusPanel.js +211 -34304
@@ -1,1674 +1,21 @@
1
- import {
2
- addToWorkset,
3
- batch,
4
- check,
5
- checkpointLatest,
6
- checkpointList,
7
- checkpointLoad,
8
- checkpointSave,
9
- codemod,
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
- ${result.output}`
447
- }
448
- ]
449
- };
450
- } catch (err) {
451
- console.error("[KB] Eval failed:", err);
452
- return {
453
- content: [{ type: "text", text: `Eval failed: ${err.message}` }],
454
- isError: true
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};