@dreb/coding-agent 2.32.1 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +4 -0
  2. package/dist/core/agent-session.d.ts +14 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +59 -8
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/settings-manager.d.ts +18 -0
  7. package/dist/core/settings-manager.d.ts.map +1 -1
  8. package/dist/core/settings-manager.js +22 -0
  9. package/dist/core/settings-manager.js.map +1 -1
  10. package/dist/core/tools/bash.d.ts.map +1 -1
  11. package/dist/core/tools/bash.js +4 -4
  12. package/dist/core/tools/bash.js.map +1 -1
  13. package/dist/core/tools/edit.d.ts.map +1 -1
  14. package/dist/core/tools/edit.js +2 -2
  15. package/dist/core/tools/edit.js.map +1 -1
  16. package/dist/core/tools/find.d.ts.map +1 -1
  17. package/dist/core/tools/find.js +2 -2
  18. package/dist/core/tools/find.js.map +1 -1
  19. package/dist/core/tools/grep.d.ts.map +1 -1
  20. package/dist/core/tools/grep.js +2 -2
  21. package/dist/core/tools/grep.js.map +1 -1
  22. package/dist/core/tools/ls.d.ts.map +1 -1
  23. package/dist/core/tools/ls.js +2 -2
  24. package/dist/core/tools/ls.js.map +1 -1
  25. package/dist/core/tools/read.d.ts.map +1 -1
  26. package/dist/core/tools/read.js +2 -2
  27. package/dist/core/tools/read.js.map +1 -1
  28. package/dist/core/tools/search.d.ts.map +1 -1
  29. package/dist/core/tools/search.js +2 -2
  30. package/dist/core/tools/search.js.map +1 -1
  31. package/dist/core/tools/skill.d.ts.map +1 -1
  32. package/dist/core/tools/skill.js +2 -2
  33. package/dist/core/tools/skill.js.map +1 -1
  34. package/dist/core/tools/subagent.d.ts.map +1 -1
  35. package/dist/core/tools/subagent.js +2 -2
  36. package/dist/core/tools/subagent.js.map +1 -1
  37. package/dist/core/tools/suggest-next.d.ts.map +1 -1
  38. package/dist/core/tools/suggest-next.js +5 -5
  39. package/dist/core/tools/suggest-next.js.map +1 -1
  40. package/dist/core/tools/tasks.d.ts.map +1 -1
  41. package/dist/core/tools/tasks.js +2 -2
  42. package/dist/core/tools/tasks.js.map +1 -1
  43. package/dist/core/tools/wait.d.ts.map +1 -1
  44. package/dist/core/tools/wait.js +2 -2
  45. package/dist/core/tools/wait.js.map +1 -1
  46. package/dist/core/tools/web.d.ts.map +1 -1
  47. package/dist/core/tools/web.js +4 -4
  48. package/dist/core/tools/web.js.map +1 -1
  49. package/dist/core/tools/write.d.ts.map +1 -1
  50. package/dist/core/tools/write.js +1 -1
  51. package/dist/core/tools/write.js.map +1 -1
  52. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/assistant-message.js +5 -10
  54. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  55. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  56. package/dist/modes/interactive/components/bash-execution.js +4 -4
  57. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  58. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/branch-summary-message.js +1 -1
  60. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  61. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
  63. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  64. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/custom-message.js +1 -1
  66. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  67. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  68. package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
  69. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  70. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  71. package/dist/modes/interactive/components/tool-execution.js +2 -2
  72. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  73. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  74. package/dist/modes/interactive/components/user-message.js +1 -1
  75. package/dist/modes/interactive/components/user-message.js.map +1 -1
  76. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  77. package/dist/modes/interactive/interactive-mode.js +36 -29
  78. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  79. package/docs/settings.md +20 -0
  80. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@dreb/agent-core\";\nimport type { ImageContent, TextContent } from \"@dreb/ai\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile } from \"fs/promises\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { getLanguageFromPath, highlightCode } from \"../../modes/interactive/theme/theme.js\";\nimport { formatDimensionNote, resizeImage } from \"../../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../../utils/mime.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { resolveReadPath } from \"./path-utils.js\";\nimport { getTextOutput, invalidArgText, replaceTabs, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from \"./truncate.js\";\n\nconst readSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n\toffset: Type.Optional(Type.Number({ description: \"Line number to start reading from (1-indexed)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\nexport interface ReadToolDetails {\n\ttruncation?: TruncationResult;\n}\n\n/**\n * Pluggable operations for the read tool.\n * Override these to delegate file reading to remote systems (for example SSH).\n */\nexport interface ReadOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Check if file is readable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n\t/** Detect image MIME type, return null or undefined for non-images */\n\tdetectImageMimeType?: (absolutePath: string) => Promise<string | null | undefined>;\n}\n\nconst defaultReadOperations: ReadOperations = {\n\treadFile: (path) => fsReadFile(path),\n\taccess: (path) => fsAccess(path, constants.R_OK),\n\tdetectImageMimeType: detectSupportedImageMimeTypeFromFile,\n};\n\nexport interface ReadToolOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Custom operations for file reading. Default: local filesystem */\n\toperations?: ReadOperations;\n}\n\nfunction formatReadCall(\n\targs: { path?: string; file_path?: string; offset?: number; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst offset = args?.offset;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\tif (offset !== undefined || limit !== undefined) {\n\t\tconst startLine = offset ?? 1;\n\t\tconst endLine = limit !== undefined ? startLine + limit - 1 : \"\";\n\t\tpathDisplay += theme.fg(\"warning\", `:${startLine}${endLine ? `-${endLine}` : \"\"}`);\n\t}\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"read\"))} ${pathDisplay}`;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatReadResult(\n\targs: { path?: string; file_path?: string; offset?: number; limit?: number } | undefined,\n\tresult: { content: (TextContent | ImageContent)[]; details?: ReadToolDetails },\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tshowImages: boolean,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst output = getTextOutput(result as any, showImages);\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang ? highlightCode(replaceTabs(output), lang) : output.split(\"\\n\");\n\tconst lines = trimTrailingEmptyLines(renderedLines);\n\tconst maxLines = options.expanded ? lines.length : 10;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\tlet text = `\\n${displayLines.map((line) => (lang ? replaceTabs(line) : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t}\n\n\tconst truncation = result.details?.truncation;\n\tif (truncation?.truncated) {\n\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;\n\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;\n\t\t} else {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;\n\t\t}\n\t}\n\treturn text;\n}\n\nexport function createReadToolDefinition(\n\tcwd: string,\n\toptions?: ReadToolOptions,\n): ToolDefinition<typeof readSchema, ReadToolDetails | undefined> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst ops = options?.operations ?? defaultReadOperations;\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,\n\t\tpromptSnippet: \"Read file contents\",\n\t\tpromptGuidelines: [\"Use read to examine files instead of cat or sed.\"],\n\t\tparameters: readSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, offset, limit }: { path: string; offset?: number; limit?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst absolutePath = resolveReadPath(path, cwd);\n\t\t\treturn new Promise<{ content: (TextContent | ImageContent)[]; details: ReadToolDetails | undefined }>(\n\t\t\t\t(resolve, reject) => {\n\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet aborted = false;\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Check if file exists and is readable.\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tconst mimeType = ops.detectImageMimeType ? await ops.detectImageMimeType(absolutePath) : undefined;\n\t\t\t\t\t\t\tlet content: (TextContent | ImageContent)[];\n\t\t\t\t\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\t\t\t\t\tif (mimeType) {\n\t\t\t\t\t\t\t\t// Read image as binary.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst base64 = buffer.toString(\"base64\");\n\t\t\t\t\t\t\t\tif (autoResizeImages) {\n\t\t\t\t\t\t\t\t\t// Resize image if needed before sending it back to the model.\n\t\t\t\t\t\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64, mimeType });\n\t\t\t\t\t\t\t\t\tif (!resized) {\n\t\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\t\ttext: `Read image file [${mimeType}]\\n[Image omitted: could not be resized below the inline image size limit.]`,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconst dimensionNote = formatDimensionNote(resized);\n\t\t\t\t\t\t\t\t\t\tlet textNote = `Read image file [${resized.mimeType}]`;\n\t\t\t\t\t\t\t\t\t\tif (dimensionNote) textNote += `\\n${dimensionNote}`;\n\t\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: textNote },\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"image\", data: resized.data, mimeType: resized.mimeType },\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${mimeType}]` },\n\t\t\t\t\t\t\t\t\t\t{ type: \"image\", data: base64, mimeType },\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Read text content.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst textContent = buffer.toString(\"utf-8\");\n\t\t\t\t\t\t\t\tconst allLines = textContent.split(\"\\n\");\n\t\t\t\t\t\t\t\tconst totalFileLines = allLines.length;\n\t\t\t\t\t\t\t\t// Apply offset if specified. Convert from 1-indexed input to 0-indexed array access.\n\t\t\t\t\t\t\t\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\t\t\t\t\t\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\t\t\t\t\t\t// Check if offset is out of bounds.\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length) {\n\t\t\t\t\t\t\t\t\tthrow new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlet selectedContent: string;\n\t\t\t\t\t\t\t\tlet userLimitedLines: number | undefined;\n\t\t\t\t\t\t\t\t// If limit is specified by the user, honor it first. Otherwise truncateHead decides.\n\t\t\t\t\t\t\t\tif (limit !== undefined) {\n\t\t\t\t\t\t\t\t\tconst endLine = Math.min(startLine + limit, allLines.length);\n\t\t\t\t\t\t\t\t\tselectedContent = allLines.slice(startLine, endLine).join(\"\\n\");\n\t\t\t\t\t\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tselectedContent = allLines.slice(startLine).join(\"\\n\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Apply truncation, respecting both line and byte limits.\n\t\t\t\t\t\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\t\t\t\t\t\tlet outputText: string;\n\t\t\t\t\t\t\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\t\t\t\t\t\t\t// First line alone exceeds the byte limit. Point the model at a bash fallback.\n\t\t\t\t\t\t\t\t\tconst firstLineSize = formatSize(Buffer.byteLength(allLines[startLine], \"utf-8\"));\n\t\t\t\t\t\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\t\t\t\t\t\tdetails = { truncation };\n\t\t\t\t\t\t\t\t} else if (truncation.truncated) {\n\t\t\t\t\t\t\t\t\t// Truncation occurred. Build an actionable continuation notice.\n\t\t\t\t\t\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\t\t\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\t\t\t\t\t\toutputText = truncation.content;\n\t\t\t\t\t\t\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdetails = { truncation };\n\t\t\t\t\t\t\t\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\t\t\t\t\t\t// User-specified limit stopped early, but the file still has more content.\n\t\t\t\t\t\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\t\t\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\t\t\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// No truncation and no remaining user-limited content.\n\t\t\t\t\t\t\t\t\toutputText = truncation.content;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: outputText }];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tresolve({ content, details });\n\t\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tif (!aborted) reject(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatReadCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatReadResult(context.args, result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createReadTool(cwd: string, options?: ReadToolOptions): AgentTool<typeof readSchema> {\n\treturn wrapToolDefinition(createReadToolDefinition(cwd, options));\n}\n\n/** Default read tool using process.cwd() for backwards compatibility. */\nexport const readToolDefinition = createReadToolDefinition(process.cwd());\nexport const readTool = createReadTool(process.cwd());\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@dreb/agent-core\";\nimport type { ImageContent, TextContent } from \"@dreb/ai\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile } from \"fs/promises\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.js\";\nimport { getLanguageFromPath, highlightCode } from \"../../modes/interactive/theme/theme.js\";\nimport { formatDimensionNote, resizeImage } from \"../../utils/image-resize.js\";\nimport { detectSupportedImageMimeTypeFromFile } from \"../../utils/mime.js\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { resolveReadPath } from \"./path-utils.js\";\nimport { getTextOutput, invalidArgText, replaceTabs, shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from \"./truncate.js\";\n\nconst readSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n\toffset: Type.Optional(Type.Number({ description: \"Line number to start reading from (1-indexed)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\nexport interface ReadToolDetails {\n\ttruncation?: TruncationResult;\n}\n\n/**\n * Pluggable operations for the read tool.\n * Override these to delegate file reading to remote systems (for example SSH).\n */\nexport interface ReadOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Check if file is readable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n\t/** Detect image MIME type, return null or undefined for non-images */\n\tdetectImageMimeType?: (absolutePath: string) => Promise<string | null | undefined>;\n}\n\nconst defaultReadOperations: ReadOperations = {\n\treadFile: (path) => fsReadFile(path),\n\taccess: (path) => fsAccess(path, constants.R_OK),\n\tdetectImageMimeType: detectSupportedImageMimeTypeFromFile,\n};\n\nexport interface ReadToolOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Custom operations for file reading. Default: local filesystem */\n\toperations?: ReadOperations;\n}\n\nfunction formatReadCall(\n\targs: { path?: string; file_path?: string; offset?: number; limit?: number } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst offset = args?.offset;\n\tconst limit = args?.limit;\n\tconst invalidArg = invalidArgText(theme);\n\tlet pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\tif (offset !== undefined || limit !== undefined) {\n\t\tconst startLine = offset ?? 1;\n\t\tconst endLine = limit !== undefined ? startLine + limit - 1 : \"\";\n\t\tpathDisplay += theme.fg(\"warning\", `:${startLine}${endLine ? `-${endLine}` : \"\"}`);\n\t}\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"read\"))} ${pathDisplay}`;\n}\n\nfunction trimTrailingEmptyLines(lines: string[]): string[] {\n\tlet end = lines.length;\n\twhile (end > 0 && lines[end - 1] === \"\") {\n\t\tend--;\n\t}\n\treturn lines.slice(0, end);\n}\n\nfunction formatReadResult(\n\targs: { path?: string; file_path?: string; offset?: number; limit?: number } | undefined,\n\tresult: { content: (TextContent | ImageContent)[]; details?: ReadToolDetails },\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n\tshowImages: boolean,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst output = getTextOutput(result as any, showImages);\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang ? highlightCode(replaceTabs(output), lang) : output.split(\"\\n\");\n\tconst lines = trimTrailingEmptyLines(renderedLines);\n\tconst maxLines = options.expanded ? lines.length : 10;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\tlet text = `\\n${displayLines.map((line) => (lang ? replaceTabs(line) : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t}\n\n\tconst truncation = result.details?.truncation;\n\tif (truncation?.truncated) {\n\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;\n\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;\n\t\t} else {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;\n\t\t}\n\t}\n\treturn text;\n}\n\nexport function createReadToolDefinition(\n\tcwd: string,\n\toptions?: ReadToolOptions,\n): ToolDefinition<typeof readSchema, ReadToolDetails | undefined> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst ops = options?.operations ?? defaultReadOperations;\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,\n\t\tpromptSnippet: \"Read file contents\",\n\t\tpromptGuidelines: [\"Use read to examine files instead of cat or sed.\"],\n\t\tparameters: readSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, offset, limit }: { path: string; offset?: number; limit?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\t_ctx?,\n\t\t) {\n\t\t\tconst absolutePath = resolveReadPath(path, cwd);\n\t\t\treturn new Promise<{ content: (TextContent | ImageContent)[]; details: ReadToolDetails | undefined }>(\n\t\t\t\t(resolve, reject) => {\n\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet aborted = false;\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Check if file exists and is readable.\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tconst mimeType = ops.detectImageMimeType ? await ops.detectImageMimeType(absolutePath) : undefined;\n\t\t\t\t\t\t\tlet content: (TextContent | ImageContent)[];\n\t\t\t\t\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\t\t\t\t\tif (mimeType) {\n\t\t\t\t\t\t\t\t// Read image as binary.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst base64 = buffer.toString(\"base64\");\n\t\t\t\t\t\t\t\tif (autoResizeImages) {\n\t\t\t\t\t\t\t\t\t// Resize image if needed before sending it back to the model.\n\t\t\t\t\t\t\t\t\tconst resized = await resizeImage({ type: \"image\", data: base64, mimeType });\n\t\t\t\t\t\t\t\t\tif (!resized) {\n\t\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\t\t\ttext: `Read image file [${mimeType}]\\n[Image omitted: could not be resized below the inline image size limit.]`,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconst dimensionNote = formatDimensionNote(resized);\n\t\t\t\t\t\t\t\t\t\tlet textNote = `Read image file [${resized.mimeType}]`;\n\t\t\t\t\t\t\t\t\t\tif (dimensionNote) textNote += `\\n${dimensionNote}`;\n\t\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: textNote },\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"image\", data: resized.data, mimeType: resized.mimeType },\n\t\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${mimeType}]` },\n\t\t\t\t\t\t\t\t\t\t{ type: \"image\", data: base64, mimeType },\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Read text content.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst textContent = buffer.toString(\"utf-8\");\n\t\t\t\t\t\t\t\tconst allLines = textContent.split(\"\\n\");\n\t\t\t\t\t\t\t\tconst totalFileLines = allLines.length;\n\t\t\t\t\t\t\t\t// Apply offset if specified. Convert from 1-indexed input to 0-indexed array access.\n\t\t\t\t\t\t\t\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\t\t\t\t\t\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\t\t\t\t\t\t// Check if offset is out of bounds.\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length) {\n\t\t\t\t\t\t\t\t\tthrow new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlet selectedContent: string;\n\t\t\t\t\t\t\t\tlet userLimitedLines: number | undefined;\n\t\t\t\t\t\t\t\t// If limit is specified by the user, honor it first. Otherwise truncateHead decides.\n\t\t\t\t\t\t\t\tif (limit !== undefined) {\n\t\t\t\t\t\t\t\t\tconst endLine = Math.min(startLine + limit, allLines.length);\n\t\t\t\t\t\t\t\t\tselectedContent = allLines.slice(startLine, endLine).join(\"\\n\");\n\t\t\t\t\t\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tselectedContent = allLines.slice(startLine).join(\"\\n\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Apply truncation, respecting both line and byte limits.\n\t\t\t\t\t\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\t\t\t\t\t\tlet outputText: string;\n\t\t\t\t\t\t\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\t\t\t\t\t\t\t// First line alone exceeds the byte limit. Point the model at a bash fallback.\n\t\t\t\t\t\t\t\t\tconst firstLineSize = formatSize(Buffer.byteLength(allLines[startLine], \"utf-8\"));\n\t\t\t\t\t\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\t\t\t\t\t\tdetails = { truncation };\n\t\t\t\t\t\t\t\t} else if (truncation.truncated) {\n\t\t\t\t\t\t\t\t\t// Truncation occurred. Build an actionable continuation notice.\n\t\t\t\t\t\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\t\t\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\t\t\t\t\t\toutputText = truncation.content;\n\t\t\t\t\t\t\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdetails = { truncation };\n\t\t\t\t\t\t\t\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\t\t\t\t\t\t// User-specified limit stopped early, but the file still has more content.\n\t\t\t\t\t\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\t\t\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\t\t\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// No truncation and no remaining user-limited content.\n\t\t\t\t\t\t\t\t\toutputText = truncation.content;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: outputText }];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tresolve({ content, details });\n\t\t\t\t\t\t} catch (error: any) {\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tif (!aborted) reject(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatReadCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatReadResult(context.args, result as any, options, theme, context.showImages));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createReadTool(cwd: string, options?: ReadToolOptions): AgentTool<typeof readSchema> {\n\treturn wrapToolDefinition(createReadToolDefinition(cwd, options));\n}\n\n/** Default read tool using process.cwd() for backwards compatibility. */\nexport const readToolDefinition = createReadToolDefinition(process.cwd());\nexport const readTool = createReadTool(process.cwd());\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAUtF,QAAA,MAAM,YAAY;;;;;;EAgBhB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAM1D,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAMD,sCAAsC;AACtC,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,EACnH,KAAK,EAAE,cAAc,wCAAwC,EAAE,KAAK,GAClE,MAAM,CAkBR;AAED,sCAAsC;AACtC,wBAAgB,kBAAkB,CACjC,MAAM,EAAE;IACP,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC5B,EACD,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,cAAc,wCAAwC,EAAE,KAAK,GAClE,MAAM,CAoBR;AAMD,oEAAoE;AACpE,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAmBD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,OAAO,YAAY,EAAE,iBAAiB,CAAC,CAiH9G;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,YAAY,CAAC,CAE5E;AAED,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB;;;;;;2BAA4C,CAAC;AAC9E,eAAO,MAAM,UAAU;;;;;;QAAkC,CAAC","sourcesContent":["/**\n * Semantic codebase search tool.\n *\n * Uses embeddings + FTS5 to support natural language queries over the codebase.\n * Feature-gated on `node:sqlite` availability (Node 22+).\n */\n\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { formatResults, SearchEngine } from \"@dreb/semantic-search\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { getDrebToolVisibleDirs } from \"./dreb-paths.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst searchSchema = Type.Object({\n\tquery: Type.String({ description: \"The search query (natural language, identifier, or path)\" }),\n\trestrictToDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Filter results to files under this path (relative to searchDir or cwd). Does not affect indexing — the entire searchDir is still indexed.\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results to return (default: 20)\" })),\n\tsearchDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Directory to index and search instead of cwd (useful when cwd is ~/). The entire contents of this directory are scanned and indexed.\",\n\t\t}),\n\t),\n\trebuild: Type.Optional(Type.Boolean({ description: \"Force a clean rebuild of the search index (default: false)\" })),\n});\n\nexport type SearchToolInput = Static<typeof searchSchema>;\n\n// ============================================================================\n// Details\n// ============================================================================\n\nexport interface SearchToolDetails {\n\tresultCount: number;\n\tindexBuilt: boolean;\n\tindexStats?: { files: number; chunks: number };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\n/** @internal Exported for testing. */\nexport function formatSearchCall(\n\targs: { query?: string; restrictToDir?: string; limit?: number; searchDir?: string; rebuild?: boolean } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst query = str(args?.query);\n\tconst restrictToDir = str(args?.restrictToDir);\n\tconst searchDir = str(args?.searchDir);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"search\"))} ${theme.fg(\"accent\", `\"${query ?? \"\"}\"`)}`;\n\tif (searchDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` project ${shortenPath(searchDir)}`);\n\t}\n\tif (restrictToDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` in ${shortenPath(restrictToDir)}`);\n\t}\n\tif (args?.rebuild) {\n\t\ttext += theme.fg(\"toolOutput\", \" [rebuild]\");\n\t}\n\tif (args?.limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` limit ${args.limit}`);\n\t}\n\treturn text;\n}\n\n/** @internal Exported for testing. */\nexport function formatSearchResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t\tdetails?: SearchToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst output = result.content[0]?.text?.trim() ?? \"\";\n\tif (!output) return \"\";\n\n\tconst lines = output.split(\"\\n\");\n\tconst maxLines = options.expanded ? lines.length : 20;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\n\tlet text = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t}\n\n\tif (result.details?.indexStats) {\n\t\tconst { files, chunks } = result.details.indexStats;\n\t\ttext += `\\n${theme.fg(\"muted\", `[Index: ${files} files, ${chunks} chunks]`)}`;\n\t}\n\n\treturn text;\n}\n\n// ============================================================================\n// Tool Definition\n// ============================================================================\n\n/** Check if the search tool is available (requires node:sqlite). */\nexport function isSearchAvailable(): boolean {\n\treturn SearchEngine.isAvailable();\n}\n\n// Cache search engines per project root to reuse index across calls within a session\nconst engineCache = new Map<string, SearchEngine>();\n\nfunction getSearchEngine(projectRoot: string): SearchEngine {\n\tlet engine = engineCache.get(projectRoot);\n\tif (!engine) {\n\t\tengine = new SearchEngine(projectRoot, {\n\t\t\tindexDir: path.join(projectRoot, \".dreb\", \"index\"),\n\t\t\tglobalMemoryDir: path.join(homedir(), \".dreb\", \"memory\"),\n\t\t\tmodelCacheDir: path.join(homedir(), \".dreb\", \"agent\", \"models\"),\n\t\t\tvisibleDirs: getDrebToolVisibleDirs,\n\t\t});\n\t\tengineCache.set(projectRoot, engine);\n\t}\n\treturn engine;\n}\n\nexport function createSearchToolDefinition(cwd: string): ToolDefinition<typeof searchSchema, SearchToolDetails> {\n\treturn {\n\t\tname: \"search\",\n\t\tlabel: \"search\",\n\t\tdescription:\n\t\t\t\"Search the codebase using natural language queries. Returns ranked code/doc results using semantic similarity and keyword matching. First query builds the index (may take a moment); subsequent queries are fast. Supports identifier queries (e.g. 'AuthMiddleware'), natural language (e.g. 'where is rate limiting handled'), and path queries (e.g. 'src/auth/').\",\n\t\tpromptSnippet: \"Semantic codebase search — natural language queries over code and docs\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `search` as your default exploration tool — for understanding code, finding where things are, and answering questions about the codebase. Use `grep` when you already know the exact text or pattern you're looking for.\",\n\t\t\t\"The first search query builds an index (may take 10-60s). Subsequent queries are fast.\",\n\t\t],\n\t\tparameters: searchSchema,\n\n\t\tasync execute(_toolCallId, params, signal, onUpdate, _ctx) {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\tif (!isSearchAvailable()) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Semantic search requires Node.js 22+ (for built-in SQLite). Current Node.js version does not support node:sqlite.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst { query, restrictToDir, limit, searchDir, rebuild } = params;\n\n\t\t\tif (!query || query.trim().length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Search query cannot be empty.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst resolvedSearchDir = searchDir ? resolveToCwd(searchDir, cwd) : cwd;\n\n\t\t\tif (searchDir && (!existsSync(resolvedSearchDir) || !statSync(resolvedSearchDir).isDirectory())) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `searchDir does not exist or is not a directory: ${resolvedSearchDir}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst engine = getSearchEngine(resolvedSearchDir);\n\n\t\t\tif (rebuild) {\n\t\t\t\tawait engine.resetIndex();\n\t\t\t}\n\n\t\t\tlet indexBuilt = false;\n\t\t\tconst results = await engine.search(query, {\n\t\t\t\tlimit: typeof limit === \"number\" && limit > 0 ? Math.floor(limit) : 20,\n\t\t\t\tpathFilter: restrictToDir,\n\t\t\t\tonProgress: (phase, current, total) => {\n\t\t\t\t\tif (phase === \"indexing\" || phase === \"scanning\" || phase === \"loading model\" || phase === \"embedding\") {\n\t\t\t\t\t\tindexBuilt = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `${phase}: ${current}/${total}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: true } as SearchToolDetails,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No results found.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = formatResults(results);\n\n\t\t\t// Get index stats from the existing engine (no new connection)\n\t\t\tconst stats = engine.getStats();\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: {\n\t\t\t\t\tresultCount: results.length,\n\t\t\t\t\tindexBuilt,\n\t\t\t\t\tindexStats: stats ?? undefined,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSearchCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSearchResult(result as any, options, theme));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSearchTool(cwd: string): AgentTool<typeof searchSchema> {\n\treturn wrapToolDefinition(createSearchToolDefinition(cwd));\n}\n\n/** Default search tool using process.cwd() for backwards compatibility. */\nexport const searchToolDefinition = createSearchToolDefinition(process.cwd());\nexport const searchTool = createSearchTool(process.cwd());\n"]}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAUtF,QAAA,MAAM,YAAY;;;;;;EAgBhB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AAM1D,MAAM,WAAW,iBAAiB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAMD,sCAAsC;AACtC,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,EACnH,KAAK,EAAE,cAAc,wCAAwC,EAAE,KAAK,GAClE,MAAM,CAkBR;AAED,sCAAsC;AACtC,wBAAgB,kBAAkB,CACjC,MAAM,EAAE;IACP,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC5B,EACD,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,cAAc,wCAAwC,EAAE,KAAK,GAClE,MAAM,CAoBR;AAMD,oEAAoE;AACpE,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAmBD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,OAAO,YAAY,EAAE,iBAAiB,CAAC,CAiH9G;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,OAAO,YAAY,CAAC,CAE5E;AAED,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB;;;;;;2BAA4C,CAAC;AAC9E,eAAO,MAAM,UAAU;;;;;;QAAkC,CAAC","sourcesContent":["/**\n * Semantic codebase search tool.\n *\n * Uses embeddings + FTS5 to support natural language queries over the codebase.\n * Feature-gated on `node:sqlite` availability (Node 22+).\n */\n\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { formatResults, SearchEngine } from \"@dreb/semantic-search\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { getDrebToolVisibleDirs } from \"./dreb-paths.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst searchSchema = Type.Object({\n\tquery: Type.String({ description: \"The search query (natural language, identifier, or path)\" }),\n\trestrictToDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Filter results to files under this path (relative to searchDir or cwd). Does not affect indexing — the entire searchDir is still indexed.\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results to return (default: 20)\" })),\n\tsearchDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Directory to index and search instead of cwd (useful when cwd is ~/). The entire contents of this directory are scanned and indexed.\",\n\t\t}),\n\t),\n\trebuild: Type.Optional(Type.Boolean({ description: \"Force a clean rebuild of the search index (default: false)\" })),\n});\n\nexport type SearchToolInput = Static<typeof searchSchema>;\n\n// ============================================================================\n// Details\n// ============================================================================\n\nexport interface SearchToolDetails {\n\tresultCount: number;\n\tindexBuilt: boolean;\n\tindexStats?: { files: number; chunks: number };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\n/** @internal Exported for testing. */\nexport function formatSearchCall(\n\targs: { query?: string; restrictToDir?: string; limit?: number; searchDir?: string; rebuild?: boolean } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst query = str(args?.query);\n\tconst restrictToDir = str(args?.restrictToDir);\n\tconst searchDir = str(args?.searchDir);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"search\"))} ${theme.fg(\"accent\", `\"${query ?? \"\"}\"`)}`;\n\tif (searchDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` project ${shortenPath(searchDir)}`);\n\t}\n\tif (restrictToDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` in ${shortenPath(restrictToDir)}`);\n\t}\n\tif (args?.rebuild) {\n\t\ttext += theme.fg(\"toolOutput\", \" [rebuild]\");\n\t}\n\tif (args?.limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` limit ${args.limit}`);\n\t}\n\treturn text;\n}\n\n/** @internal Exported for testing. */\nexport function formatSearchResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t\tdetails?: SearchToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst output = result.content[0]?.text?.trim() ?? \"\";\n\tif (!output) return \"\";\n\n\tconst lines = output.split(\"\\n\");\n\tconst maxLines = options.expanded ? lines.length : 20;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\n\tlet text = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t}\n\n\tif (result.details?.indexStats) {\n\t\tconst { files, chunks } = result.details.indexStats;\n\t\ttext += `\\n${theme.fg(\"muted\", `[Index: ${files} files, ${chunks} chunks]`)}`;\n\t}\n\n\treturn text;\n}\n\n// ============================================================================\n// Tool Definition\n// ============================================================================\n\n/** Check if the search tool is available (requires node:sqlite). */\nexport function isSearchAvailable(): boolean {\n\treturn SearchEngine.isAvailable();\n}\n\n// Cache search engines per project root to reuse index across calls within a session\nconst engineCache = new Map<string, SearchEngine>();\n\nfunction getSearchEngine(projectRoot: string): SearchEngine {\n\tlet engine = engineCache.get(projectRoot);\n\tif (!engine) {\n\t\tengine = new SearchEngine(projectRoot, {\n\t\t\tindexDir: path.join(projectRoot, \".dreb\", \"index\"),\n\t\t\tglobalMemoryDir: path.join(homedir(), \".dreb\", \"memory\"),\n\t\t\tmodelCacheDir: path.join(homedir(), \".dreb\", \"agent\", \"models\"),\n\t\t\tvisibleDirs: getDrebToolVisibleDirs,\n\t\t});\n\t\tengineCache.set(projectRoot, engine);\n\t}\n\treturn engine;\n}\n\nexport function createSearchToolDefinition(cwd: string): ToolDefinition<typeof searchSchema, SearchToolDetails> {\n\treturn {\n\t\tname: \"search\",\n\t\tlabel: \"search\",\n\t\tdescription:\n\t\t\t\"Search the codebase using natural language queries. Returns ranked code/doc results using semantic similarity and keyword matching. First query builds the index (may take a moment); subsequent queries are fast. Supports identifier queries (e.g. 'AuthMiddleware'), natural language (e.g. 'where is rate limiting handled'), and path queries (e.g. 'src/auth/').\",\n\t\tpromptSnippet: \"Semantic codebase search — natural language queries over code and docs\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `search` as your default exploration tool — for understanding code, finding where things are, and answering questions about the codebase. Use `grep` when you already know the exact text or pattern you're looking for.\",\n\t\t\t\"The first search query builds an index (may take 10-60s). Subsequent queries are fast.\",\n\t\t],\n\t\tparameters: searchSchema,\n\n\t\tasync execute(_toolCallId, params, signal, onUpdate, _ctx) {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\tif (!isSearchAvailable()) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Semantic search requires Node.js 22+ (for built-in SQLite). Current Node.js version does not support node:sqlite.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst { query, restrictToDir, limit, searchDir, rebuild } = params;\n\n\t\t\tif (!query || query.trim().length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Search query cannot be empty.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst resolvedSearchDir = searchDir ? resolveToCwd(searchDir, cwd) : cwd;\n\n\t\t\tif (searchDir && (!existsSync(resolvedSearchDir) || !statSync(resolvedSearchDir).isDirectory())) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `searchDir does not exist or is not a directory: ${resolvedSearchDir}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst engine = getSearchEngine(resolvedSearchDir);\n\n\t\t\tif (rebuild) {\n\t\t\t\tawait engine.resetIndex();\n\t\t\t}\n\n\t\t\tlet indexBuilt = false;\n\t\t\tconst results = await engine.search(query, {\n\t\t\t\tlimit: typeof limit === \"number\" && limit > 0 ? Math.floor(limit) : 20,\n\t\t\t\tpathFilter: restrictToDir,\n\t\t\t\tonProgress: (phase, current, total) => {\n\t\t\t\t\tif (phase === \"indexing\" || phase === \"scanning\" || phase === \"loading model\" || phase === \"embedding\") {\n\t\t\t\t\t\tindexBuilt = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `${phase}: ${current}/${total}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: true } as SearchToolDetails,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No results found.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = formatResults(results);\n\n\t\t\t// Get index stats from the existing engine (no new connection)\n\t\t\tconst stats = engine.getStats();\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: {\n\t\t\t\t\tresultCount: results.length,\n\t\t\t\t\tindexBuilt,\n\t\t\t\t\tindexStats: stats ?? undefined,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatSearchCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatSearchResult(result as any, options, theme));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSearchTool(cwd: string): AgentTool<typeof searchSchema> {\n\treturn wrapToolDefinition(createSearchToolDefinition(cwd));\n}\n\n/** Default search tool using process.cwd() for backwards compatibility. */\nexport const searchToolDefinition = createSearchToolDefinition(process.cwd());\nexport const searchTool = createSearchTool(process.cwd());\n"]}
@@ -180,12 +180,12 @@ export function createSearchToolDefinition(cwd) {
180
180
  };
181
181
  },
182
182
  renderCall(args, theme, context) {
183
- const text = context.lastComponent ?? new Text("", 0, 0);
183
+ const text = context.lastComponent ?? new Text("", 0, 0, undefined, true);
184
184
  text.setText(formatSearchCall(args, theme));
185
185
  return text;
186
186
  },
187
187
  renderResult(result, options, theme, context) {
188
- const text = context.lastComponent ?? new Text("", 0, 0);
188
+ const text = context.lastComponent ?? new Text("", 0, 0, undefined, true);
189
189
  text.setText(formatSearchResult(result, options, theme));
190
190
  return text;
191
191
  },
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Semantic codebase search tool.\n *\n * Uses embeddings + FTS5 to support natural language queries over the codebase.\n * Feature-gated on `node:sqlite` availability (Node 22+).\n */\n\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { formatResults, SearchEngine } from \"@dreb/semantic-search\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { getDrebToolVisibleDirs } from \"./dreb-paths.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst searchSchema = Type.Object({\n\tquery: Type.String({ description: \"The search query (natural language, identifier, or path)\" }),\n\trestrictToDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Filter results to files under this path (relative to searchDir or cwd). Does not affect indexing — the entire searchDir is still indexed.\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results to return (default: 20)\" })),\n\tsearchDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Directory to index and search instead of cwd (useful when cwd is ~/). The entire contents of this directory are scanned and indexed.\",\n\t\t}),\n\t),\n\trebuild: Type.Optional(Type.Boolean({ description: \"Force a clean rebuild of the search index (default: false)\" })),\n});\n\nexport type SearchToolInput = Static<typeof searchSchema>;\n\n// ============================================================================\n// Details\n// ============================================================================\n\nexport interface SearchToolDetails {\n\tresultCount: number;\n\tindexBuilt: boolean;\n\tindexStats?: { files: number; chunks: number };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\n/** @internal Exported for testing. */\nexport function formatSearchCall(\n\targs: { query?: string; restrictToDir?: string; limit?: number; searchDir?: string; rebuild?: boolean } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst query = str(args?.query);\n\tconst restrictToDir = str(args?.restrictToDir);\n\tconst searchDir = str(args?.searchDir);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"search\"))} ${theme.fg(\"accent\", `\"${query ?? \"\"}\"`)}`;\n\tif (searchDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` project ${shortenPath(searchDir)}`);\n\t}\n\tif (restrictToDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` in ${shortenPath(restrictToDir)}`);\n\t}\n\tif (args?.rebuild) {\n\t\ttext += theme.fg(\"toolOutput\", \" [rebuild]\");\n\t}\n\tif (args?.limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` limit ${args.limit}`);\n\t}\n\treturn text;\n}\n\n/** @internal Exported for testing. */\nexport function formatSearchResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t\tdetails?: SearchToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst output = result.content[0]?.text?.trim() ?? \"\";\n\tif (!output) return \"\";\n\n\tconst lines = output.split(\"\\n\");\n\tconst maxLines = options.expanded ? lines.length : 20;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\n\tlet text = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t}\n\n\tif (result.details?.indexStats) {\n\t\tconst { files, chunks } = result.details.indexStats;\n\t\ttext += `\\n${theme.fg(\"muted\", `[Index: ${files} files, ${chunks} chunks]`)}`;\n\t}\n\n\treturn text;\n}\n\n// ============================================================================\n// Tool Definition\n// ============================================================================\n\n/** Check if the search tool is available (requires node:sqlite). */\nexport function isSearchAvailable(): boolean {\n\treturn SearchEngine.isAvailable();\n}\n\n// Cache search engines per project root to reuse index across calls within a session\nconst engineCache = new Map<string, SearchEngine>();\n\nfunction getSearchEngine(projectRoot: string): SearchEngine {\n\tlet engine = engineCache.get(projectRoot);\n\tif (!engine) {\n\t\tengine = new SearchEngine(projectRoot, {\n\t\t\tindexDir: path.join(projectRoot, \".dreb\", \"index\"),\n\t\t\tglobalMemoryDir: path.join(homedir(), \".dreb\", \"memory\"),\n\t\t\tmodelCacheDir: path.join(homedir(), \".dreb\", \"agent\", \"models\"),\n\t\t\tvisibleDirs: getDrebToolVisibleDirs,\n\t\t});\n\t\tengineCache.set(projectRoot, engine);\n\t}\n\treturn engine;\n}\n\nexport function createSearchToolDefinition(cwd: string): ToolDefinition<typeof searchSchema, SearchToolDetails> {\n\treturn {\n\t\tname: \"search\",\n\t\tlabel: \"search\",\n\t\tdescription:\n\t\t\t\"Search the codebase using natural language queries. Returns ranked code/doc results using semantic similarity and keyword matching. First query builds the index (may take a moment); subsequent queries are fast. Supports identifier queries (e.g. 'AuthMiddleware'), natural language (e.g. 'where is rate limiting handled'), and path queries (e.g. 'src/auth/').\",\n\t\tpromptSnippet: \"Semantic codebase search — natural language queries over code and docs\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `search` as your default exploration tool — for understanding code, finding where things are, and answering questions about the codebase. Use `grep` when you already know the exact text or pattern you're looking for.\",\n\t\t\t\"The first search query builds an index (may take 10-60s). Subsequent queries are fast.\",\n\t\t],\n\t\tparameters: searchSchema,\n\n\t\tasync execute(_toolCallId, params, signal, onUpdate, _ctx) {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\tif (!isSearchAvailable()) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Semantic search requires Node.js 22+ (for built-in SQLite). Current Node.js version does not support node:sqlite.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst { query, restrictToDir, limit, searchDir, rebuild } = params;\n\n\t\t\tif (!query || query.trim().length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Search query cannot be empty.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst resolvedSearchDir = searchDir ? resolveToCwd(searchDir, cwd) : cwd;\n\n\t\t\tif (searchDir && (!existsSync(resolvedSearchDir) || !statSync(resolvedSearchDir).isDirectory())) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `searchDir does not exist or is not a directory: ${resolvedSearchDir}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst engine = getSearchEngine(resolvedSearchDir);\n\n\t\t\tif (rebuild) {\n\t\t\t\tawait engine.resetIndex();\n\t\t\t}\n\n\t\t\tlet indexBuilt = false;\n\t\t\tconst results = await engine.search(query, {\n\t\t\t\tlimit: typeof limit === \"number\" && limit > 0 ? Math.floor(limit) : 20,\n\t\t\t\tpathFilter: restrictToDir,\n\t\t\t\tonProgress: (phase, current, total) => {\n\t\t\t\t\tif (phase === \"indexing\" || phase === \"scanning\" || phase === \"loading model\" || phase === \"embedding\") {\n\t\t\t\t\t\tindexBuilt = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `${phase}: ${current}/${total}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: true } as SearchToolDetails,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No results found.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = formatResults(results);\n\n\t\t\t// Get index stats from the existing engine (no new connection)\n\t\t\tconst stats = engine.getStats();\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: {\n\t\t\t\t\tresultCount: results.length,\n\t\t\t\t\tindexBuilt,\n\t\t\t\t\tindexStats: stats ?? undefined,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSearchCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatSearchResult(result as any, options, theme));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSearchTool(cwd: string): AgentTool<typeof searchSchema> {\n\treturn wrapToolDefinition(createSearchToolDefinition(cwd));\n}\n\n/** Default search tool using process.cwd() for backwards compatibility. */\nexport const searchToolDefinition = createSearchToolDefinition(process.cwd());\nexport const searchTool = createSearchTool(process.cwd());\n"]}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Semantic codebase search tool.\n *\n * Uses embeddings + FTS5 to support natural language queries over the codebase.\n * Feature-gated on `node:sqlite` availability (Node 22+).\n */\n\nimport { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { formatResults, SearchEngine } from \"@dreb/semantic-search\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.js\";\nimport { getDrebToolVisibleDirs } from \"./dreb-paths.js\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport { shortenPath, str } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nconst searchSchema = Type.Object({\n\tquery: Type.String({ description: \"The search query (natural language, identifier, or path)\" }),\n\trestrictToDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Filter results to files under this path (relative to searchDir or cwd). Does not affect indexing — the entire searchDir is still indexed.\",\n\t\t}),\n\t),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of results to return (default: 20)\" })),\n\tsearchDir: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Directory to index and search instead of cwd (useful when cwd is ~/). The entire contents of this directory are scanned and indexed.\",\n\t\t}),\n\t),\n\trebuild: Type.Optional(Type.Boolean({ description: \"Force a clean rebuild of the search index (default: false)\" })),\n});\n\nexport type SearchToolInput = Static<typeof searchSchema>;\n\n// ============================================================================\n// Details\n// ============================================================================\n\nexport interface SearchToolDetails {\n\tresultCount: number;\n\tindexBuilt: boolean;\n\tindexStats?: { files: number; chunks: number };\n}\n\n// ============================================================================\n// Rendering\n// ============================================================================\n\n/** @internal Exported for testing. */\nexport function formatSearchCall(\n\targs: { query?: string; restrictToDir?: string; limit?: number; searchDir?: string; rebuild?: boolean } | undefined,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst query = str(args?.query);\n\tconst restrictToDir = str(args?.restrictToDir);\n\tconst searchDir = str(args?.searchDir);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"search\"))} ${theme.fg(\"accent\", `\"${query ?? \"\"}\"`)}`;\n\tif (searchDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` project ${shortenPath(searchDir)}`);\n\t}\n\tif (restrictToDir) {\n\t\ttext += theme.fg(\"toolOutput\", ` in ${shortenPath(restrictToDir)}`);\n\t}\n\tif (args?.rebuild) {\n\t\ttext += theme.fg(\"toolOutput\", \" [rebuild]\");\n\t}\n\tif (args?.limit !== undefined) {\n\t\ttext += theme.fg(\"toolOutput\", ` limit ${args.limit}`);\n\t}\n\treturn text;\n}\n\n/** @internal Exported for testing. */\nexport function formatSearchResult(\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t\tdetails?: SearchToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\ttheme: typeof import(\"../../modes/interactive/theme/theme.js\").theme,\n): string {\n\tconst output = result.content[0]?.text?.trim() ?? \"\";\n\tif (!output) return \"\";\n\n\tconst lines = output.split(\"\\n\");\n\tconst maxLines = options.expanded ? lines.length : 20;\n\tconst displayLines = lines.slice(0, maxLines);\n\tconst remaining = lines.length - maxLines;\n\n\tlet text = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t}\n\n\tif (result.details?.indexStats) {\n\t\tconst { files, chunks } = result.details.indexStats;\n\t\ttext += `\\n${theme.fg(\"muted\", `[Index: ${files} files, ${chunks} chunks]`)}`;\n\t}\n\n\treturn text;\n}\n\n// ============================================================================\n// Tool Definition\n// ============================================================================\n\n/** Check if the search tool is available (requires node:sqlite). */\nexport function isSearchAvailable(): boolean {\n\treturn SearchEngine.isAvailable();\n}\n\n// Cache search engines per project root to reuse index across calls within a session\nconst engineCache = new Map<string, SearchEngine>();\n\nfunction getSearchEngine(projectRoot: string): SearchEngine {\n\tlet engine = engineCache.get(projectRoot);\n\tif (!engine) {\n\t\tengine = new SearchEngine(projectRoot, {\n\t\t\tindexDir: path.join(projectRoot, \".dreb\", \"index\"),\n\t\t\tglobalMemoryDir: path.join(homedir(), \".dreb\", \"memory\"),\n\t\t\tmodelCacheDir: path.join(homedir(), \".dreb\", \"agent\", \"models\"),\n\t\t\tvisibleDirs: getDrebToolVisibleDirs,\n\t\t});\n\t\tengineCache.set(projectRoot, engine);\n\t}\n\treturn engine;\n}\n\nexport function createSearchToolDefinition(cwd: string): ToolDefinition<typeof searchSchema, SearchToolDetails> {\n\treturn {\n\t\tname: \"search\",\n\t\tlabel: \"search\",\n\t\tdescription:\n\t\t\t\"Search the codebase using natural language queries. Returns ranked code/doc results using semantic similarity and keyword matching. First query builds the index (may take a moment); subsequent queries are fast. Supports identifier queries (e.g. 'AuthMiddleware'), natural language (e.g. 'where is rate limiting handled'), and path queries (e.g. 'src/auth/').\",\n\t\tpromptSnippet: \"Semantic codebase search — natural language queries over code and docs\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use `search` as your default exploration tool — for understanding code, finding where things are, and answering questions about the codebase. Use `grep` when you already know the exact text or pattern you're looking for.\",\n\t\t\t\"The first search query builds an index (may take 10-60s). Subsequent queries are fast.\",\n\t\t],\n\t\tparameters: searchSchema,\n\n\t\tasync execute(_toolCallId, params, signal, onUpdate, _ctx) {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\tif (!isSearchAvailable()) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: \"Semantic search requires Node.js 22+ (for built-in SQLite). Current Node.js version does not support node:sqlite.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst { query, restrictToDir, limit, searchDir, rebuild } = params;\n\n\t\t\tif (!query || query.trim().length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Search query cannot be empty.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst resolvedSearchDir = searchDir ? resolveToCwd(searchDir, cwd) : cwd;\n\n\t\t\tif (searchDir && (!existsSync(resolvedSearchDir) || !statSync(resolvedSearchDir).isDirectory())) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `searchDir does not exist or is not a directory: ${resolvedSearchDir}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst engine = getSearchEngine(resolvedSearchDir);\n\n\t\t\tif (rebuild) {\n\t\t\t\tawait engine.resetIndex();\n\t\t\t}\n\n\t\t\tlet indexBuilt = false;\n\t\t\tconst results = await engine.search(query, {\n\t\t\t\tlimit: typeof limit === \"number\" && limit > 0 ? Math.floor(limit) : 20,\n\t\t\t\tpathFilter: restrictToDir,\n\t\t\t\tonProgress: (phase, current, total) => {\n\t\t\t\t\tif (phase === \"indexing\" || phase === \"scanning\" || phase === \"loading model\" || phase === \"embedding\") {\n\t\t\t\t\t\tindexBuilt = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `${phase}: ${current}/${total}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { resultCount: 0, indexBuilt: true } as SearchToolDetails,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"No results found.\" }],\n\t\t\t\t\tdetails: { resultCount: 0, indexBuilt },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst text = formatResults(results);\n\n\t\t\t// Get index stats from the existing engine (no new connection)\n\t\t\tconst stats = engine.getStats();\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\t\tdetails: {\n\t\t\t\t\tresultCount: results.length,\n\t\t\t\t\tindexBuilt,\n\t\t\t\t\tindexStats: stats ?? undefined,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatSearchCall(args, theme));\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\ttext.setText(formatSearchResult(result as any, options, theme));\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSearchTool(cwd: string): AgentTool<typeof searchSchema> {\n\treturn wrapToolDefinition(createSearchToolDefinition(cwd));\n}\n\n/** Default search tool using process.cwd() for backwards compatibility. */\nexport const searchToolDefinition = createSearchToolDefinition(process.cwd());\nexport const searchTool = createSearchTool(process.cwd());\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../../src/core/tools/skill.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAQ1C,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,MAAM,WAAW,gBAAgB;IAChC,qGAAqG;IACrG,SAAS,EAAE,MAAM,KAAK,EAAE,CAAC;IACzB,wIAAwI;IACxI,YAAY,EAAE,MAAM,MAAM,CAAC;CAC3B;AAMD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAgBxF;AAMD,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACvB,cAAc,CAAC,OAAO,WAAW,EAAE,gBAAgB,CAAC,CA8FtD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAErG","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { stripFrontmatter } from \"../../utils/frontmatter.js\";\nimport { escapeXml } from \"../../utils/xml.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { parseCommandArgs, substituteArgs } from \"../prompt-templates.js\";\nimport type { Skill } from \"../skills.js\";\nimport { getTextOutput } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst skillSchema = Type.Object({\n\tskill: Type.String({ description: 'The skill name to invoke (e.g. \"review-pr\", \"telegram-send\")' }),\n\targs: Type.Optional(Type.String({ description: \"Optional arguments to pass to the skill\" })),\n});\n\nexport type SkillToolInput = Static<typeof skillSchema>;\n\nexport interface SkillToolDetails {\n\tskillName: string;\n\tfound: boolean;\n\twarned: boolean;\n\terror?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface SkillToolOptions {\n\t/** Returns the current list of loaded skills. Called on each invocation so reloads are reflected. */\n\tgetSkills: () => Skill[];\n\t/** Returns the current session ID for ${DREB_SESSION_ID} substitution. Called on each invocation so session rotations are reflected. */\n\tgetSessionId: () => string;\n}\n\n// ---------------------------------------------------------------------------\n// Skill expansion — shared between skill tool and _expandSkillCommand\n// ---------------------------------------------------------------------------\n\nexport function expandSkillContent(skill: Skill, args: string, sessionId: string): string {\n\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\tlet body = stripFrontmatter(content).trim();\n\n\tconst parsedArgs = parseCommandArgs(args);\n\t// $0 is an alias for first argument (per spec). Replace BEFORE substituteArgs\n\t// so that argument values containing \"$0\" aren't re-substituted.\n\t// Negative lookahead avoids matching $00, $01, etc.\n\tbody = body.replace(/\\$0(?![0-9])/g, parsedArgs[0] ?? \"\");\n\tbody = substituteArgs(body, parsedArgs);\n\t// Environment-style placeholders\n\tbody = body.replace(/\\$\\{DREB_SKILL_DIR\\}/g, skill.baseDir);\n\tbody = body.replace(/\\$\\{DREB_SESSION_ID\\}/g, sessionId);\n\n\tconst skillBlock = `<skill name=\"${escapeXml(skill.name)}\" location=\"${escapeXml(skill.filePath)}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\treturn skillBlock;\n}\n\n// ---------------------------------------------------------------------------\n// Tool definition\n// ---------------------------------------------------------------------------\n\nexport function createSkillToolDefinition(\n\t_cwd: string,\n\toptions: SkillToolOptions,\n): ToolDefinition<typeof skillSchema, SkillToolDetails> {\n\tconst { getSkills, getSessionId } = options;\n\n\treturn {\n\t\tname: \"skill\",\n\t\tlabel: \"skill\",\n\t\tdescription:\n\t\t\t\"Invoke a skill by name. Skills provide specialized instructions for specific tasks. \" +\n\t\t\t\"Use this tool when a task matches a skill's description from the available_skills list in the system prompt.\",\n\t\tpromptSnippet: \"Invoke a skill to get specialized instructions for a task\",\n\t\tparameters: skillSchema,\n\n\t\tasync execute(_toolCallId, params: { skill: string; args?: string }) {\n\t\t\tconst skills = getSkills();\n\t\t\tconst skill = skills.find((s) => s.name === params.skill);\n\n\t\t\tif (!skill) {\n\t\t\t\tconst available = skills\n\t\t\t\t\t.filter((s) => !s.disableModelInvocation)\n\t\t\t\t\t.map((s) => ` - ${s.name}: ${s.description}`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Unknown skill \"${params.skill}\". Available skills:\\n${available || \" (none loaded)\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: params.skill, found: false, warned: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (skill.disableModelInvocation) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t`The skill \"${skill.name}\" has model invocation disabled. ` +\n\t\t\t\t\t\t\t\t`This skill is intended to be invoked explicitly by the user (via /skill:${skill.name}), not by the model. ` +\n\t\t\t\t\t\t\t\t`Please ask the user for clarification before proceeding.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst expanded = expandSkillContent(skill, params.args ?? \"\", getSessionId());\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: expanded }],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false },\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Error loading skill \"${skill.name}\": ${message}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false, error: message },\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\tconst skillName = args?.skill ?? \"...\";\n\t\t\tconst argsStr = args?.args ? ` ${theme.fg(\"accent\", args.args)}` : \"\";\n\t\t\ttext.setText(`${theme.fg(\"toolTitle\", theme.bold(\"skill\"))} ${theme.fg(\"accent\", skillName)}${argsStr}`);\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\tconst output = getTextOutput(result, context.showImages).trim();\n\t\t\tif (!output) {\n\t\t\t\ttext.setText(\"\");\n\t\t\t\treturn text;\n\t\t\t}\n\t\t\tconst lines = output.split(\"\\n\");\n\t\t\tconst maxLines = options.expanded ? lines.length : 15;\n\t\t\tconst displayLines = lines.slice(0, maxLines);\n\t\t\tlet display = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\t\tconst remaining = lines.length - maxLines;\n\t\t\tif (remaining > 0) {\n\t\t\t\tdisplay += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t\t\t}\n\t\t\ttext.setText(display);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSkillTool(cwd: string, options: SkillToolOptions): AgentTool<typeof skillSchema> {\n\treturn wrapToolDefinition(createSkillToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../../src/core/tools/skill.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAQ1C,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,MAAM,WAAW,gBAAgB;IAChC,qGAAqG;IACrG,SAAS,EAAE,MAAM,KAAK,EAAE,CAAC;IACzB,wIAAwI;IACxI,YAAY,EAAE,MAAM,MAAM,CAAC;CAC3B;AAMD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAgBxF;AAMD,wBAAgB,yBAAyB,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACvB,cAAc,CAAC,OAAO,WAAW,EAAE,gBAAgB,CAAC,CA8FtD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAErG","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { stripFrontmatter } from \"../../utils/frontmatter.js\";\nimport { escapeXml } from \"../../utils/xml.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { parseCommandArgs, substituteArgs } from \"../prompt-templates.js\";\nimport type { Skill } from \"../skills.js\";\nimport { getTextOutput } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst skillSchema = Type.Object({\n\tskill: Type.String({ description: 'The skill name to invoke (e.g. \"review-pr\", \"telegram-send\")' }),\n\targs: Type.Optional(Type.String({ description: \"Optional arguments to pass to the skill\" })),\n});\n\nexport type SkillToolInput = Static<typeof skillSchema>;\n\nexport interface SkillToolDetails {\n\tskillName: string;\n\tfound: boolean;\n\twarned: boolean;\n\terror?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface SkillToolOptions {\n\t/** Returns the current list of loaded skills. Called on each invocation so reloads are reflected. */\n\tgetSkills: () => Skill[];\n\t/** Returns the current session ID for ${DREB_SESSION_ID} substitution. Called on each invocation so session rotations are reflected. */\n\tgetSessionId: () => string;\n}\n\n// ---------------------------------------------------------------------------\n// Skill expansion — shared between skill tool and _expandSkillCommand\n// ---------------------------------------------------------------------------\n\nexport function expandSkillContent(skill: Skill, args: string, sessionId: string): string {\n\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\tlet body = stripFrontmatter(content).trim();\n\n\tconst parsedArgs = parseCommandArgs(args);\n\t// $0 is an alias for first argument (per spec). Replace BEFORE substituteArgs\n\t// so that argument values containing \"$0\" aren't re-substituted.\n\t// Negative lookahead avoids matching $00, $01, etc.\n\tbody = body.replace(/\\$0(?![0-9])/g, parsedArgs[0] ?? \"\");\n\tbody = substituteArgs(body, parsedArgs);\n\t// Environment-style placeholders\n\tbody = body.replace(/\\$\\{DREB_SKILL_DIR\\}/g, skill.baseDir);\n\tbody = body.replace(/\\$\\{DREB_SESSION_ID\\}/g, sessionId);\n\n\tconst skillBlock = `<skill name=\"${escapeXml(skill.name)}\" location=\"${escapeXml(skill.filePath)}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\treturn skillBlock;\n}\n\n// ---------------------------------------------------------------------------\n// Tool definition\n// ---------------------------------------------------------------------------\n\nexport function createSkillToolDefinition(\n\t_cwd: string,\n\toptions: SkillToolOptions,\n): ToolDefinition<typeof skillSchema, SkillToolDetails> {\n\tconst { getSkills, getSessionId } = options;\n\n\treturn {\n\t\tname: \"skill\",\n\t\tlabel: \"skill\",\n\t\tdescription:\n\t\t\t\"Invoke a skill by name. Skills provide specialized instructions for specific tasks. \" +\n\t\t\t\"Use this tool when a task matches a skill's description from the available_skills list in the system prompt.\",\n\t\tpromptSnippet: \"Invoke a skill to get specialized instructions for a task\",\n\t\tparameters: skillSchema,\n\n\t\tasync execute(_toolCallId, params: { skill: string; args?: string }) {\n\t\t\tconst skills = getSkills();\n\t\t\tconst skill = skills.find((s) => s.name === params.skill);\n\n\t\t\tif (!skill) {\n\t\t\t\tconst available = skills\n\t\t\t\t\t.filter((s) => !s.disableModelInvocation)\n\t\t\t\t\t.map((s) => ` - ${s.name}: ${s.description}`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Unknown skill \"${params.skill}\". Available skills:\\n${available || \" (none loaded)\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: params.skill, found: false, warned: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (skill.disableModelInvocation) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t`The skill \"${skill.name}\" has model invocation disabled. ` +\n\t\t\t\t\t\t\t\t`This skill is intended to be invoked explicitly by the user (via /skill:${skill.name}), not by the model. ` +\n\t\t\t\t\t\t\t\t`Please ask the user for clarification before proceeding.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst expanded = expandSkillContent(skill, params.args ?? \"\", getSessionId());\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: expanded }],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false },\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Error loading skill \"${skill.name}\": ${message}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false, error: message },\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst skillName = args?.skill ?? \"...\";\n\t\t\tconst argsStr = args?.args ? ` ${theme.fg(\"accent\", args.args)}` : \"\";\n\t\t\ttext.setText(`${theme.fg(\"toolTitle\", theme.bold(\"skill\"))} ${theme.fg(\"accent\", skillName)}${argsStr}`);\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst output = getTextOutput(result, context.showImages).trim();\n\t\t\tif (!output) {\n\t\t\t\ttext.setText(\"\");\n\t\t\t\treturn text;\n\t\t\t}\n\t\t\tconst lines = output.split(\"\\n\");\n\t\t\tconst maxLines = options.expanded ? lines.length : 15;\n\t\t\tconst displayLines = lines.slice(0, maxLines);\n\t\t\tlet display = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\t\tconst remaining = lines.length - maxLines;\n\t\t\tif (remaining > 0) {\n\t\t\t\tdisplay += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t\t\t}\n\t\t\ttext.setText(display);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSkillTool(cwd: string, options: SkillToolOptions): AgentTool<typeof skillSchema> {\n\treturn wrapToolDefinition(createSkillToolDefinition(cwd, options));\n}\n"]}
@@ -95,14 +95,14 @@ export function createSkillToolDefinition(_cwd, options) {
95
95
  }
96
96
  },
97
97
  renderCall(args, theme, context) {
98
- const text = context.lastComponent ?? new Text("", 0, 0);
98
+ const text = context.lastComponent ?? new Text("", 0, 0, undefined, true);
99
99
  const skillName = args?.skill ?? "...";
100
100
  const argsStr = args?.args ? ` ${theme.fg("accent", args.args)}` : "";
101
101
  text.setText(`${theme.fg("toolTitle", theme.bold("skill"))} ${theme.fg("accent", skillName)}${argsStr}`);
102
102
  return text;
103
103
  },
104
104
  renderResult(result, options, theme, context) {
105
- const text = context.lastComponent ?? new Text("", 0, 0);
105
+ const text = context.lastComponent ?? new Text("", 0, 0, undefined, true);
106
106
  const output = getTextOutput(result, context.showImages).trim();
107
107
  if (!output) {
108
108
  text.setText("");
@@ -1 +1 @@
1
- {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../../src/core/tools/skill.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { stripFrontmatter } from \"../../utils/frontmatter.js\";\nimport { escapeXml } from \"../../utils/xml.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { parseCommandArgs, substituteArgs } from \"../prompt-templates.js\";\nimport type { Skill } from \"../skills.js\";\nimport { getTextOutput } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst skillSchema = Type.Object({\n\tskill: Type.String({ description: 'The skill name to invoke (e.g. \"review-pr\", \"telegram-send\")' }),\n\targs: Type.Optional(Type.String({ description: \"Optional arguments to pass to the skill\" })),\n});\n\nexport type SkillToolInput = Static<typeof skillSchema>;\n\nexport interface SkillToolDetails {\n\tskillName: string;\n\tfound: boolean;\n\twarned: boolean;\n\terror?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface SkillToolOptions {\n\t/** Returns the current list of loaded skills. Called on each invocation so reloads are reflected. */\n\tgetSkills: () => Skill[];\n\t/** Returns the current session ID for ${DREB_SESSION_ID} substitution. Called on each invocation so session rotations are reflected. */\n\tgetSessionId: () => string;\n}\n\n// ---------------------------------------------------------------------------\n// Skill expansion — shared between skill tool and _expandSkillCommand\n// ---------------------------------------------------------------------------\n\nexport function expandSkillContent(skill: Skill, args: string, sessionId: string): string {\n\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\tlet body = stripFrontmatter(content).trim();\n\n\tconst parsedArgs = parseCommandArgs(args);\n\t// $0 is an alias for first argument (per spec). Replace BEFORE substituteArgs\n\t// so that argument values containing \"$0\" aren't re-substituted.\n\t// Negative lookahead avoids matching $00, $01, etc.\n\tbody = body.replace(/\\$0(?![0-9])/g, parsedArgs[0] ?? \"\");\n\tbody = substituteArgs(body, parsedArgs);\n\t// Environment-style placeholders\n\tbody = body.replace(/\\$\\{DREB_SKILL_DIR\\}/g, skill.baseDir);\n\tbody = body.replace(/\\$\\{DREB_SESSION_ID\\}/g, sessionId);\n\n\tconst skillBlock = `<skill name=\"${escapeXml(skill.name)}\" location=\"${escapeXml(skill.filePath)}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\treturn skillBlock;\n}\n\n// ---------------------------------------------------------------------------\n// Tool definition\n// ---------------------------------------------------------------------------\n\nexport function createSkillToolDefinition(\n\t_cwd: string,\n\toptions: SkillToolOptions,\n): ToolDefinition<typeof skillSchema, SkillToolDetails> {\n\tconst { getSkills, getSessionId } = options;\n\n\treturn {\n\t\tname: \"skill\",\n\t\tlabel: \"skill\",\n\t\tdescription:\n\t\t\t\"Invoke a skill by name. Skills provide specialized instructions for specific tasks. \" +\n\t\t\t\"Use this tool when a task matches a skill's description from the available_skills list in the system prompt.\",\n\t\tpromptSnippet: \"Invoke a skill to get specialized instructions for a task\",\n\t\tparameters: skillSchema,\n\n\t\tasync execute(_toolCallId, params: { skill: string; args?: string }) {\n\t\t\tconst skills = getSkills();\n\t\t\tconst skill = skills.find((s) => s.name === params.skill);\n\n\t\t\tif (!skill) {\n\t\t\t\tconst available = skills\n\t\t\t\t\t.filter((s) => !s.disableModelInvocation)\n\t\t\t\t\t.map((s) => ` - ${s.name}: ${s.description}`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Unknown skill \"${params.skill}\". Available skills:\\n${available || \" (none loaded)\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: params.skill, found: false, warned: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (skill.disableModelInvocation) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t`The skill \"${skill.name}\" has model invocation disabled. ` +\n\t\t\t\t\t\t\t\t`This skill is intended to be invoked explicitly by the user (via /skill:${skill.name}), not by the model. ` +\n\t\t\t\t\t\t\t\t`Please ask the user for clarification before proceeding.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst expanded = expandSkillContent(skill, params.args ?? \"\", getSessionId());\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: expanded }],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false },\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Error loading skill \"${skill.name}\": ${message}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false, error: message },\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\tconst skillName = args?.skill ?? \"...\";\n\t\t\tconst argsStr = args?.args ? ` ${theme.fg(\"accent\", args.args)}` : \"\";\n\t\t\ttext.setText(`${theme.fg(\"toolTitle\", theme.bold(\"skill\"))} ${theme.fg(\"accent\", skillName)}${argsStr}`);\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\tconst output = getTextOutput(result, context.showImages).trim();\n\t\t\tif (!output) {\n\t\t\t\ttext.setText(\"\");\n\t\t\t\treturn text;\n\t\t\t}\n\t\t\tconst lines = output.split(\"\\n\");\n\t\t\tconst maxLines = options.expanded ? lines.length : 15;\n\t\t\tconst displayLines = lines.slice(0, maxLines);\n\t\t\tlet display = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\t\tconst remaining = lines.length - maxLines;\n\t\t\tif (remaining > 0) {\n\t\t\t\tdisplay += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t\t\t}\n\t\t\ttext.setText(display);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSkillTool(cwd: string, options: SkillToolOptions): AgentTool<typeof skillSchema> {\n\treturn wrapToolDefinition(createSkillToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../../src/core/tools/skill.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { AgentTool } from \"@dreb/agent-core\";\nimport { Text } from \"@dreb/tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { stripFrontmatter } from \"../../utils/frontmatter.js\";\nimport { escapeXml } from \"../../utils/xml.js\";\nimport type { ToolDefinition } from \"../extensions/types.js\";\nimport { parseCommandArgs, substituteArgs } from \"../prompt-templates.js\";\nimport type { Skill } from \"../skills.js\";\nimport { getTextOutput } from \"./render-utils.js\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.js\";\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst skillSchema = Type.Object({\n\tskill: Type.String({ description: 'The skill name to invoke (e.g. \"review-pr\", \"telegram-send\")' }),\n\targs: Type.Optional(Type.String({ description: \"Optional arguments to pass to the skill\" })),\n});\n\nexport type SkillToolInput = Static<typeof skillSchema>;\n\nexport interface SkillToolDetails {\n\tskillName: string;\n\tfound: boolean;\n\twarned: boolean;\n\terror?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface SkillToolOptions {\n\t/** Returns the current list of loaded skills. Called on each invocation so reloads are reflected. */\n\tgetSkills: () => Skill[];\n\t/** Returns the current session ID for ${DREB_SESSION_ID} substitution. Called on each invocation so session rotations are reflected. */\n\tgetSessionId: () => string;\n}\n\n// ---------------------------------------------------------------------------\n// Skill expansion — shared between skill tool and _expandSkillCommand\n// ---------------------------------------------------------------------------\n\nexport function expandSkillContent(skill: Skill, args: string, sessionId: string): string {\n\tconst content = readFileSync(skill.filePath, \"utf-8\");\n\tlet body = stripFrontmatter(content).trim();\n\n\tconst parsedArgs = parseCommandArgs(args);\n\t// $0 is an alias for first argument (per spec). Replace BEFORE substituteArgs\n\t// so that argument values containing \"$0\" aren't re-substituted.\n\t// Negative lookahead avoids matching $00, $01, etc.\n\tbody = body.replace(/\\$0(?![0-9])/g, parsedArgs[0] ?? \"\");\n\tbody = substituteArgs(body, parsedArgs);\n\t// Environment-style placeholders\n\tbody = body.replace(/\\$\\{DREB_SKILL_DIR\\}/g, skill.baseDir);\n\tbody = body.replace(/\\$\\{DREB_SESSION_ID\\}/g, sessionId);\n\n\tconst skillBlock = `<skill name=\"${escapeXml(skill.name)}\" location=\"${escapeXml(skill.filePath)}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n\treturn skillBlock;\n}\n\n// ---------------------------------------------------------------------------\n// Tool definition\n// ---------------------------------------------------------------------------\n\nexport function createSkillToolDefinition(\n\t_cwd: string,\n\toptions: SkillToolOptions,\n): ToolDefinition<typeof skillSchema, SkillToolDetails> {\n\tconst { getSkills, getSessionId } = options;\n\n\treturn {\n\t\tname: \"skill\",\n\t\tlabel: \"skill\",\n\t\tdescription:\n\t\t\t\"Invoke a skill by name. Skills provide specialized instructions for specific tasks. \" +\n\t\t\t\"Use this tool when a task matches a skill's description from the available_skills list in the system prompt.\",\n\t\tpromptSnippet: \"Invoke a skill to get specialized instructions for a task\",\n\t\tparameters: skillSchema,\n\n\t\tasync execute(_toolCallId, params: { skill: string; args?: string }) {\n\t\t\tconst skills = getSkills();\n\t\t\tconst skill = skills.find((s) => s.name === params.skill);\n\n\t\t\tif (!skill) {\n\t\t\t\tconst available = skills\n\t\t\t\t\t.filter((s) => !s.disableModelInvocation)\n\t\t\t\t\t.map((s) => ` - ${s.name}: ${s.description}`)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Unknown skill \"${params.skill}\". Available skills:\\n${available || \" (none loaded)\"}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: params.skill, found: false, warned: false },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (skill.disableModelInvocation) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\t`The skill \"${skill.name}\" has model invocation disabled. ` +\n\t\t\t\t\t\t\t\t`This skill is intended to be invoked explicitly by the user (via /skill:${skill.name}), not by the model. ` +\n\t\t\t\t\t\t\t\t`Please ask the user for clarification before proceeding.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: true },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst expanded = expandSkillContent(skill, params.args ?? \"\", getSessionId());\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: expanded }],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false },\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\ttext: `Error loading skill \"${skill.name}\": ${message}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { skillName: skill.name, found: true, warned: false, error: message },\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst skillName = args?.skill ?? \"...\";\n\t\t\tconst argsStr = args?.args ? ` ${theme.fg(\"accent\", args.args)}` : \"\";\n\t\t\ttext.setText(`${theme.fg(\"toolTitle\", theme.bold(\"skill\"))} ${theme.fg(\"accent\", skillName)}${argsStr}`);\n\t\t\treturn text;\n\t\t},\n\n\t\trenderResult(result, options, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0, undefined, true);\n\t\t\tconst output = getTextOutput(result, context.showImages).trim();\n\t\t\tif (!output) {\n\t\t\t\ttext.setText(\"\");\n\t\t\t\treturn text;\n\t\t\t}\n\t\t\tconst lines = output.split(\"\\n\");\n\t\t\tconst maxLines = options.expanded ? lines.length : 15;\n\t\t\tconst displayLines = lines.slice(0, maxLines);\n\t\t\tlet display = `\\n${displayLines.map((line) => theme.fg(\"toolOutput\", line)).join(\"\\n\")}`;\n\t\t\tconst remaining = lines.length - maxLines;\n\t\t\tif (remaining > 0) {\n\t\t\t\tdisplay += `\\n${theme.fg(\"muted\", `... (${remaining} more lines)`)}`;\n\t\t\t}\n\t\t\ttext.setText(display);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createSkillTool(cwd: string, options: SkillToolOptions): AgentTool<typeof skillSchema> {\n\treturn wrapToolDefinition(createSkillToolDefinition(cwd, options));\n}\n"]}