@greppleai/grip 0.1.3 → 0.1.4

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 (38) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/core/extensions/types.d.ts +5 -1
  3. package/dist/core/extensions/types.d.ts.map +1 -1
  4. package/dist/core/extensions/types.js.map +1 -1
  5. package/dist/core/tools/find.d.ts.map +1 -1
  6. package/dist/core/tools/find.js +2 -3
  7. package/dist/core/tools/find.js.map +1 -1
  8. package/dist/core/tools/grep.d.ts.map +1 -1
  9. package/dist/core/tools/grep.js +2 -3
  10. package/dist/core/tools/grep.js.map +1 -1
  11. package/dist/core/tools/ls.d.ts.map +1 -1
  12. package/dist/core/tools/ls.js +2 -3
  13. package/dist/core/tools/ls.js.map +1 -1
  14. package/dist/core/tools/read.d.ts.map +1 -1
  15. package/dist/core/tools/read.js +3 -3
  16. package/dist/core/tools/read.js.map +1 -1
  17. package/dist/core/tools/render-utils.d.ts +1 -0
  18. package/dist/core/tools/render-utils.d.ts.map +1 -1
  19. package/dist/core/tools/render-utils.js +5 -0
  20. package/dist/core/tools/render-utils.js.map +1 -1
  21. package/dist/core/tools/write.d.ts.map +1 -1
  22. package/dist/core/tools/write.js +2 -3
  23. package/dist/core/tools/write.js.map +1 -1
  24. package/dist/modes/interactive/components/hook-execution.d.ts +3 -0
  25. package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/hook-execution.js +17 -0
  27. package/dist/modes/interactive/components/hook-execution.js.map +1 -1
  28. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  29. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  30. package/dist/modes/interactive/interactive-mode.js +51 -3
  31. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  32. package/docs/extensions.md +3 -0
  33. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  34. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  35. package/examples/extensions/sandbox/package.json +1 -1
  36. package/examples/extensions/with-deps/package.json +1 -1
  37. package/npm-shrinkwrap.json +12 -12
  38. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, dirname, isAbsolute, relative, resolve as resolvePath, sep } from \"node:path\";\nimport type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport type { Api, Model } from \"@greppleai/grip-ai\";\nimport { Text } from \"@greppleai/grip-tui\";\n\nimport { type Static, Type } from \"typebox\";\nimport { getReadmePath } from \"../../config.ts\";\nimport { keyHint, keyText } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { processImage } from \"../../utils/image-process.ts\";\nimport { formatPathRelativeToCwdOrAbsolute } from \"../../utils/paths.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { fmtRegion } from \"./hashline/apply.ts\";\nimport { HASH_SEP, initHasher, lineHashes } from \"./hashline/hash.ts\";\nimport { visLines as hashlineVisLines } from \"./hashline-utils.ts\";\nimport type { ToolOperations } from \"./operations.ts\";\nimport { resolveReadPathAsync, resolveToCwd } from \"./path-utils.ts\";\nimport { formatBackendIcon, getTextOutput, invalidArgText, replaceTabs, shortenPath, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from \"./truncate.ts\";\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\ntype CompactReadClassificationKind = \"docs\" | \"resource\" | \"skill\" | \"rule\";\n\ninterface CompactReadClassification {\n\tkind: CompactReadClassificationKind;\n\tlabel: string;\n}\n\nconst COMPACT_RESOURCE_FILE_NAMES = new Set([\"AGENTS.override.md\", \"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\"]);\n\nexport interface ReadToolOperationsSelection {\n\tkind: \"selection\";\n\toperations: ToolOperations;\n\tpath: string;\n}\n\nexport type ReadToolOperations = ToolOperations | ReadToolOperationsSelection;\n\ntype ReadToolOperationsSelector = (absolutePath: string) => ReadToolOperations | undefined;\n\nexport interface ReadToolOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Select a backend, and optionally a canonical path, for a resolved absolute path. Defaults to the tool backend. */\n\toperationsForPath?: ReadToolOperationsSelector;\n}\n\nfunction isReadToolOperationsSelection(value: ReadToolOperations): value is ReadToolOperationsSelection {\n\treturn \"kind\" in value && value.kind === \"selection\";\n}\n\nfunction getReadToolOperations(value: ReadToolOperations | undefined): ToolOperations | undefined {\n\treturn value && isReadToolOperationsSelection(value) ? value.operations : value;\n}\n\ntype ReadRenderArgs = { path?: string; file_path?: string; offset?: number; limit?: number };\n\nfunction formatReadLineRange(args: ReadRenderArgs | undefined, theme: Theme): string {\n\tif (args?.offset === undefined && args?.limit === undefined) return \"\";\n\tconst startLine = args.offset ?? 1;\n\tconst endLine = args.limit !== undefined ? startLine + args.limit - 1 : \"\";\n\treturn theme.fg(\"warning\", `:${startLine}${endLine ? `-${endLine}` : \"\"}`);\n}\n\nfunction formatReadCall(args: ReadRenderArgs | undefined, theme: Theme): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"read\"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;\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 getNonVisionImageNote(model: Model<Api> | undefined): string | undefined {\n\tif (!model || model.input.includes(\"image\")) {\n\t\treturn undefined;\n\t}\n\treturn \"[Current model does not support images. The image will be omitted from this request.]\";\n}\n\nfunction toPosixPath(filePath: string): string {\n\treturn filePath.split(sep).join(\"/\");\n}\n\nfunction getGripDocsClassification(absolutePath: string): CompactReadClassification | undefined {\n\tconst packageRoot = dirname(getReadmePath());\n\tconst relativePath = relative(resolvePath(packageRoot), resolvePath(absolutePath));\n\tif (\n\t\trelativePath === \"\" ||\n\t\trelativePath === \"..\" ||\n\t\trelativePath.startsWith(`..${sep}`) ||\n\t\tisAbsolute(relativePath)\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst label = toPosixPath(relativePath);\n\tif (label === \"README.md\" || label.startsWith(\"docs/\") || label.startsWith(\"examples/\")) {\n\t\treturn { kind: \"docs\", label };\n\t}\n\treturn undefined;\n}\n\nfunction getCompactReadClassification(\n\targs: ReadRenderArgs | undefined,\n\tcwd: string,\n): CompactReadClassification | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tif (!rawPath) return undefined;\n\n\tconst absolutePath = resolveToCwd(rawPath, cwd);\n\tconst fileName = basename(absolutePath);\n\tif (fileName === \"SKILL.md\") {\n\t\treturn { kind: \"skill\", label: basename(dirname(absolutePath)) || fileName };\n\t}\n\tif (fileName === \"RULES.md\") {\n\t\treturn { kind: \"rule\", label: basename(dirname(absolutePath)) || fileName };\n\t}\n\n\tconst docsClassification = getGripDocsClassification(absolutePath);\n\tif (docsClassification) return docsClassification;\n\n\tif (COMPACT_RESOURCE_FILE_NAMES.has(fileName)) {\n\t\treturn { kind: \"resource\", label: formatPathRelativeToCwdOrAbsolute(absolutePath, cwd) };\n\t}\n\n\treturn undefined;\n}\n\nfunction formatCompactReadCall(\n\tclassification: CompactReadClassification,\n\targs: ReadRenderArgs | undefined,\n\ttheme: Theme,\n): string {\n\tconst expandHint = theme.fg(\"dim\", ` (${keyText(\"app.tools.expand\")} to expand)`);\n\tif (classification.kind === \"skill\" || classification.kind === \"rule\") {\n\t\tconst label = classification.kind === \"skill\" ? \"skill\" : \"rule\";\n\t\treturn (\n\t\t\ttheme.fg(\"customMessageLabel\", `\\x1b[1m[${label}]\\x1b[22m `) +\n\t\t\ttheme.fg(\"customMessageText\", classification.label) +\n\t\t\tformatReadLineRange(args, theme) +\n\t\t\texpandHint\n\t\t);\n\t}\n\n\treturn (\n\t\ttheme.fg(\"toolTitle\", theme.bold(`read ${classification.kind}`)) +\n\t\t\" \" +\n\t\ttheme.fg(\"accent\", classification.label) +\n\t\tformatReadLineRange(args, theme) +\n\t\texpandHint\n\t);\n}\n\nfunction formatReadTruncationWarning(truncation: TruncationResult | undefined, theme: Theme): string {\n\tif (!truncation?.truncated) return \"\";\n\tif (truncation.firstLineExceedsLimit) {\n\t\treturn `\\n${theme.fg(\"warning\", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;\n\t}\n\tif (truncation.truncatedBy === \"lines\") {\n\t\treturn `\\n${theme.fg(\"warning\", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;\n\t}\n\treturn `\\n${theme.fg(\"warning\", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;\n}\n\nfunction formatReadResult(\n\targs: ReadRenderArgs | undefined,\n\tresult: AgentToolResult<ReadToolDetails | undefined>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n\tcwd: string,\n\tisError: boolean,\n): string {\n\tif (!options.expanded && !isError && getCompactReadClassification(args, cwd)) {\n\t\treturn \"\";\n\t}\n\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst output = getTextOutput(result, 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\ttext += formatReadTruncationWarning(result.details?.truncation, theme);\n\treturn text;\n}\n\ninterface ResolvedReadTarget {\n\toperations: ToolOperations;\n\tpath: string;\n}\n\ninterface ReadExecutionOptions {\n\tinput: ReadToolInput;\n\tdefaultOperations: ToolOperations;\n\tcwd: string;\n\toperationsForPath: ReadToolOperationsSelector | undefined;\n\tautoResizeImages: boolean;\n\tmodel: Model<Api> | undefined;\n\tsignal: AbortSignal | undefined;\n}\n\ninterface TextReadOutputOptions {\n\ttruncation: TruncationResult;\n\tallLines: string[];\n\tstartLine: number;\n\tstartLineDisplay: number;\n\tendLine: number;\n\ttotalFileLines: number;\n}\n\nasync function resolveReadTarget(options: ReadExecutionOptions): Promise<ResolvedReadTarget> {\n\tconst absolutePath = await resolveReadPathAsync(options.input.path, options.cwd);\n\tconst selection = options.operationsForPath?.(absolutePath);\n\treturn {\n\t\toperations: getReadToolOperations(selection) ?? options.defaultOperations,\n\t\tpath: selection && isReadToolOperationsSelection(selection) ? selection.path : absolutePath,\n\t};\n}\n\nasync function readImageFile(\n\toperations: ToolOperations,\n\tpath: string,\n\tmimeType: string,\n\tautoResizeImages: boolean,\n\tnonVisionImageNote: string | undefined,\n): Promise<AgentToolResult<ReadToolDetails | undefined>> {\n\tconst buffer = await operations.readFile(path);\n\tconst processed = await processImage(buffer, mimeType, { autoResizeImages });\n\tif (!processed.ok) {\n\t\tlet text = `Read image file [${mimeType}]\\n${processed.message}`;\n\t\tif (nonVisionImageNote) text += `\\n${nonVisionImageNote}`;\n\t\treturn { content: [{ type: \"text\", text }], details: undefined };\n\t}\n\tlet text = `Read image file [${processed.mimeType}]`;\n\tif (processed.hints.length > 0) text += `\\n${processed.hints.join(\"\\n\")}`;\n\tif (nonVisionImageNote) text += `\\n${nonVisionImageNote}`;\n\treturn {\n\t\tcontent: [\n\t\t\t{ type: \"text\", text },\n\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t],\n\t\tdetails: undefined,\n\t};\n}\n\nfunction formatTextReadOutput(options: TextReadOutputOptions): AgentToolResult<ReadToolDetails | undefined> {\n\tconst { truncation } = options;\n\tlet text: string;\n\tlet details: ReadToolDetails | undefined;\n\tif (truncation.firstLineExceedsLimit) {\n\t\tconst firstLineSize = formatSize(Buffer.byteLength(options.allLines[options.startLine] ?? \"\", \"utf-8\"));\n\t\ttext = `[Line ${options.startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Hashline output requires full lines; use bash for this line.]`;\n\t\tdetails = { truncation };\n\t} else if (truncation.truncated) {\n\t\tconst endLineDisplay = options.startLineDisplay + truncation.outputLines - 1;\n\t\tconst nextOffset = endLineDisplay + 1;\n\t\ttext = truncation.content;\n\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\ttext += `\\n\\n[Showing lines ${options.startLineDisplay}-${endLineDisplay} of ${options.totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t} else {\n\t\t\ttext += `\\n\\n[Showing lines ${options.startLineDisplay}-${endLineDisplay} of ${options.totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t}\n\t\tdetails = { truncation };\n\t} else if (options.endLine < options.totalFileLines) {\n\t\tconst remaining = options.totalFileLines - options.endLine;\n\t\ttext = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${options.endLine + 1} to continue.]`;\n\t} else {\n\t\ttext = truncation.content;\n\t}\n\treturn { content: [{ type: \"text\", text }], details };\n}\n\nasync function readTextFile(\n\toperations: ToolOperations,\n\tpath: string,\n\toffset: number | undefined,\n\tlimit: number | undefined,\n): Promise<AgentToolResult<ReadToolDetails | undefined>> {\n\tawait initHasher();\n\tconst buffer = await operations.readFile(path);\n\tconst textContent = buffer.toString(\"utf-8\").replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\tconst allLines = hashlineVisLines(textContent);\n\tconst totalFileLines = allLines.length;\n\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\tconst startLineDisplay = startLine + 1;\n\tif (totalFileLines === 0) {\n\t\tif (startLineDisplay !== 1) throw new Error(`Offset ${offset} is beyond end of file (0 lines total)`);\n\t\tconst emptyHash = (await lineHashes(textContent, path))[0] ?? \"\";\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `${emptyHash}${HASH_SEP}\\n[File is empty. Use edit to insert content.]` }],\n\t\t\tdetails: undefined,\n\t\t};\n\t}\n\tif (startLine >= totalFileLines) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);\n\t}\n\tconst endLine = limit !== undefined ? Math.min(startLine + limit, totalFileLines) : totalFileLines;\n\tconst allHashes = await lineHashes(textContent, path);\n\tconst selectedContent = fmtRegion(allHashes.slice(startLine, endLine), allLines.slice(startLine, endLine));\n\treturn formatTextReadOutput({\n\t\ttruncation: truncateHead(selectedContent),\n\t\tallLines,\n\t\tstartLine,\n\t\tstartLineDisplay,\n\t\tendLine,\n\t\ttotalFileLines,\n\t});\n}\n\nasync function executeReadRequest(\n\toptions: ReadExecutionOptions,\n\tisAborted: () => boolean,\n): Promise<AgentToolResult<ReadToolDetails | undefined> | undefined> {\n\tconst target = await resolveReadTarget(options);\n\tif (isAborted()) return undefined;\n\tawait target.operations.access(target.path, \"read\");\n\tif (isAborted()) return undefined;\n\tconst mimeType = target.operations.detectImageMimeType\n\t\t? await target.operations.detectImageMimeType(target.path)\n\t\t: undefined;\n\tconst nonVisionImageNote = getNonVisionImageNote(options.model);\n\tconst result = mimeType\n\t\t? await readImageFile(target.operations, target.path, mimeType, options.autoResizeImages, nonVisionImageNote)\n\t\t: await readTextFile(target.operations, target.path, options.input.offset, options.input.limit);\n\treturn isAborted() ? undefined : result;\n}\n\nfunction executeAbortableRead(options: ReadExecutionOptions): Promise<AgentToolResult<ReadToolDetails | undefined>> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (options.signal?.aborted) {\n\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\treturn;\n\t\t}\n\t\tlet aborted = false;\n\t\tconst onAbort = () => {\n\t\t\taborted = true;\n\t\t\treject(new Error(\"Operation aborted\"));\n\t\t};\n\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tvoid executeReadRequest(options, () => aborted).then(\n\t\t\t(result) => {\n\t\t\t\tif (result === undefined) return;\n\t\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(result);\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tif (!aborted) reject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport type ReadToolDefinition = ToolDefinition<typeof readSchema, ReadToolDetails | undefined>;\n\nexport function createReadToolDefinition(operations: ToolOperations, options?: ReadToolOptions): ReadToolDefinition {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst ops = operations;\n\tconst cwd = operations.cwd;\n\tconst operationsForPath = options?.operationsForPath;\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. Text files are returned as HASH│content rows for hash-anchored editing. 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 with hashline anchors\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use read to examine files instead of cat or sed.\",\n\t\t\t\"For edits, copy only the 3-character HASH values into edit hash_range_inclusive; do not include HASH│ prefixes in content_lines.\",\n\t\t],\n\t\tparameters: readSchema,\n\t\tasync execute(_toolCallId, input: ReadToolInput, signal?: AbortSignal, _onUpdate?, ctx?) {\n\t\t\treturn executeAbortableRead({\n\t\t\t\tinput,\n\t\t\t\tdefaultOperations: ops,\n\t\t\t\tcwd,\n\t\t\t\toperationsForPath,\n\t\t\t\tautoResizeImages,\n\t\t\t\tmodel: ctx?.model,\n\t\t\t\tsignal,\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\tconst classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;\n\t\t\tconst renderArgs = args as ReadRenderArgs | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst displayOps = rawPath\n\t\t\t\t? (getReadToolOperations(operationsForPath?.(resolveToCwd(rawPath, cwd))) ?? ops)\n\t\t\t\t: ops;\n\t\t\ttext.setText(\n\t\t\t\tformatBackendIcon(displayOps.getBackendInfo?.(), theme) +\n\t\t\t\t\t(classification ? formatCompactReadCall(classification, args, theme) : formatReadCall(args, theme)),\n\t\t\t);\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(\n\t\t\t\tformatReadResult(context.args, result, options, theme, context.showImages, context.cwd, context.isError),\n\t\t\t);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createReadTool(operations: ToolOperations, options?: ReadToolOptions): AgentTool<typeof readSchema> {\n\treturn wrapToolDefinition(createReadToolDefinition(operations, options));\n}\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import { basename, dirname, isAbsolute, relative, resolve as resolvePath, sep } from \"node:path\";\nimport type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport type { Api, Model } from \"@greppleai/grip-ai\";\nimport { Text } from \"@greppleai/grip-tui\";\n\nimport { type Static, Type } from \"typebox\";\nimport { getReadmePath } from \"../../config.ts\";\nimport { keyText } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { processImage } from \"../../utils/image-process.ts\";\nimport { formatPathRelativeToCwdOrAbsolute } from \"../../utils/paths.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { fmtRegion } from \"./hashline/apply.ts\";\nimport { HASH_SEP, initHasher, lineHashes } from \"./hashline/hash.ts\";\nimport { visLines as hashlineVisLines } from \"./hashline-utils.ts\";\nimport type { ToolOperations } from \"./operations.ts\";\nimport { resolveReadPathAsync, resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tformatBackendIcon,\n\tformatHiddenLinesHint,\n\tgetTextOutput,\n\tinvalidArgText,\n\treplaceTabs,\n\tshortenPath,\n\tstr,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from \"./truncate.ts\";\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\ntype CompactReadClassificationKind = \"docs\" | \"resource\" | \"skill\" | \"rule\";\n\ninterface CompactReadClassification {\n\tkind: CompactReadClassificationKind;\n\tlabel: string;\n}\n\nconst COMPACT_RESOURCE_FILE_NAMES = new Set([\"AGENTS.override.md\", \"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\"]);\n\nexport interface ReadToolOperationsSelection {\n\tkind: \"selection\";\n\toperations: ToolOperations;\n\tpath: string;\n}\n\nexport type ReadToolOperations = ToolOperations | ReadToolOperationsSelection;\n\ntype ReadToolOperationsSelector = (absolutePath: string) => ReadToolOperations | undefined;\n\nexport interface ReadToolOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Select a backend, and optionally a canonical path, for a resolved absolute path. Defaults to the tool backend. */\n\toperationsForPath?: ReadToolOperationsSelector;\n}\n\nfunction isReadToolOperationsSelection(value: ReadToolOperations): value is ReadToolOperationsSelection {\n\treturn \"kind\" in value && value.kind === \"selection\";\n}\n\nfunction getReadToolOperations(value: ReadToolOperations | undefined): ToolOperations | undefined {\n\treturn value && isReadToolOperationsSelection(value) ? value.operations : value;\n}\n\ntype ReadRenderArgs = { path?: string; file_path?: string; offset?: number; limit?: number };\n\nfunction formatReadLineRange(args: ReadRenderArgs | undefined, theme: Theme): string {\n\tif (args?.offset === undefined && args?.limit === undefined) return \"\";\n\tconst startLine = args.offset ?? 1;\n\tconst endLine = args.limit !== undefined ? startLine + args.limit - 1 : \"\";\n\treturn theme.fg(\"warning\", `:${startLine}${endLine ? `-${endLine}` : \"\"}`);\n}\n\nfunction formatReadCall(args: ReadRenderArgs | undefined, theme: Theme): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tconst pathDisplay = path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\");\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"read\"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;\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 getNonVisionImageNote(model: Model<Api> | undefined): string | undefined {\n\tif (!model || model.input.includes(\"image\")) {\n\t\treturn undefined;\n\t}\n\treturn \"[Current model does not support images. The image will be omitted from this request.]\";\n}\n\nfunction toPosixPath(filePath: string): string {\n\treturn filePath.split(sep).join(\"/\");\n}\n\nfunction getGripDocsClassification(absolutePath: string): CompactReadClassification | undefined {\n\tconst packageRoot = dirname(getReadmePath());\n\tconst relativePath = relative(resolvePath(packageRoot), resolvePath(absolutePath));\n\tif (\n\t\trelativePath === \"\" ||\n\t\trelativePath === \"..\" ||\n\t\trelativePath.startsWith(`..${sep}`) ||\n\t\tisAbsolute(relativePath)\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst label = toPosixPath(relativePath);\n\tif (label === \"README.md\" || label.startsWith(\"docs/\") || label.startsWith(\"examples/\")) {\n\t\treturn { kind: \"docs\", label };\n\t}\n\treturn undefined;\n}\n\nfunction getCompactReadClassification(\n\targs: ReadRenderArgs | undefined,\n\tcwd: string,\n): CompactReadClassification | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tif (!rawPath) return undefined;\n\n\tconst absolutePath = resolveToCwd(rawPath, cwd);\n\tconst fileName = basename(absolutePath);\n\tif (fileName === \"SKILL.md\") {\n\t\treturn { kind: \"skill\", label: basename(dirname(absolutePath)) || fileName };\n\t}\n\tif (fileName === \"RULES.md\") {\n\t\treturn { kind: \"rule\", label: basename(dirname(absolutePath)) || fileName };\n\t}\n\n\tconst docsClassification = getGripDocsClassification(absolutePath);\n\tif (docsClassification) return docsClassification;\n\n\tif (COMPACT_RESOURCE_FILE_NAMES.has(fileName)) {\n\t\treturn { kind: \"resource\", label: formatPathRelativeToCwdOrAbsolute(absolutePath, cwd) };\n\t}\n\n\treturn undefined;\n}\n\nfunction formatCompactReadCall(\n\tclassification: CompactReadClassification,\n\targs: ReadRenderArgs | undefined,\n\ttheme: Theme,\n): string {\n\tconst expandHint = theme.fg(\"dim\", ` (${keyText(\"app.tools.expand\")} to expand)`);\n\tif (classification.kind === \"skill\" || classification.kind === \"rule\") {\n\t\tconst label = classification.kind === \"skill\" ? \"skill\" : \"rule\";\n\t\treturn (\n\t\t\ttheme.fg(\"customMessageLabel\", `\\x1b[1m[${label}]\\x1b[22m `) +\n\t\t\ttheme.fg(\"customMessageText\", classification.label) +\n\t\t\tformatReadLineRange(args, theme) +\n\t\t\texpandHint\n\t\t);\n\t}\n\n\treturn (\n\t\ttheme.fg(\"toolTitle\", theme.bold(`read ${classification.kind}`)) +\n\t\t\" \" +\n\t\ttheme.fg(\"accent\", classification.label) +\n\t\tformatReadLineRange(args, theme) +\n\t\texpandHint\n\t);\n}\n\nfunction formatReadTruncationWarning(truncation: TruncationResult | undefined, theme: Theme): string {\n\tif (!truncation?.truncated) return \"\";\n\tif (truncation.firstLineExceedsLimit) {\n\t\treturn `\\n${theme.fg(\"warning\", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;\n\t}\n\tif (truncation.truncatedBy === \"lines\") {\n\t\treturn `\\n${theme.fg(\"warning\", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;\n\t}\n\treturn `\\n${theme.fg(\"warning\", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;\n}\n\nfunction formatReadResult(\n\targs: ReadRenderArgs | undefined,\n\tresult: AgentToolResult<ReadToolDetails | undefined>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n\tcwd: string,\n\tisError: boolean,\n): string {\n\tif (!options.expanded && !isError && getCompactReadClassification(args, cwd)) {\n\t\treturn \"\";\n\t}\n\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst output = getTextOutput(result, 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 += `\\n\\n${formatHiddenLinesHint(remaining, theme)}`;\n\t}\n\n\ttext += formatReadTruncationWarning(result.details?.truncation, theme);\n\treturn text;\n}\n\ninterface ResolvedReadTarget {\n\toperations: ToolOperations;\n\tpath: string;\n}\n\ninterface ReadExecutionOptions {\n\tinput: ReadToolInput;\n\tdefaultOperations: ToolOperations;\n\tcwd: string;\n\toperationsForPath: ReadToolOperationsSelector | undefined;\n\tautoResizeImages: boolean;\n\tmodel: Model<Api> | undefined;\n\tsignal: AbortSignal | undefined;\n}\n\ninterface TextReadOutputOptions {\n\ttruncation: TruncationResult;\n\tallLines: string[];\n\tstartLine: number;\n\tstartLineDisplay: number;\n\tendLine: number;\n\ttotalFileLines: number;\n}\n\nasync function resolveReadTarget(options: ReadExecutionOptions): Promise<ResolvedReadTarget> {\n\tconst absolutePath = await resolveReadPathAsync(options.input.path, options.cwd);\n\tconst selection = options.operationsForPath?.(absolutePath);\n\treturn {\n\t\toperations: getReadToolOperations(selection) ?? options.defaultOperations,\n\t\tpath: selection && isReadToolOperationsSelection(selection) ? selection.path : absolutePath,\n\t};\n}\n\nasync function readImageFile(\n\toperations: ToolOperations,\n\tpath: string,\n\tmimeType: string,\n\tautoResizeImages: boolean,\n\tnonVisionImageNote: string | undefined,\n): Promise<AgentToolResult<ReadToolDetails | undefined>> {\n\tconst buffer = await operations.readFile(path);\n\tconst processed = await processImage(buffer, mimeType, { autoResizeImages });\n\tif (!processed.ok) {\n\t\tlet text = `Read image file [${mimeType}]\\n${processed.message}`;\n\t\tif (nonVisionImageNote) text += `\\n${nonVisionImageNote}`;\n\t\treturn { content: [{ type: \"text\", text }], details: undefined };\n\t}\n\tlet text = `Read image file [${processed.mimeType}]`;\n\tif (processed.hints.length > 0) text += `\\n${processed.hints.join(\"\\n\")}`;\n\tif (nonVisionImageNote) text += `\\n${nonVisionImageNote}`;\n\treturn {\n\t\tcontent: [\n\t\t\t{ type: \"text\", text },\n\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t],\n\t\tdetails: undefined,\n\t};\n}\n\nfunction formatTextReadOutput(options: TextReadOutputOptions): AgentToolResult<ReadToolDetails | undefined> {\n\tconst { truncation } = options;\n\tlet text: string;\n\tlet details: ReadToolDetails | undefined;\n\tif (truncation.firstLineExceedsLimit) {\n\t\tconst firstLineSize = formatSize(Buffer.byteLength(options.allLines[options.startLine] ?? \"\", \"utf-8\"));\n\t\ttext = `[Line ${options.startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Hashline output requires full lines; use bash for this line.]`;\n\t\tdetails = { truncation };\n\t} else if (truncation.truncated) {\n\t\tconst endLineDisplay = options.startLineDisplay + truncation.outputLines - 1;\n\t\tconst nextOffset = endLineDisplay + 1;\n\t\ttext = truncation.content;\n\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\ttext += `\\n\\n[Showing lines ${options.startLineDisplay}-${endLineDisplay} of ${options.totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t} else {\n\t\t\ttext += `\\n\\n[Showing lines ${options.startLineDisplay}-${endLineDisplay} of ${options.totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t}\n\t\tdetails = { truncation };\n\t} else if (options.endLine < options.totalFileLines) {\n\t\tconst remaining = options.totalFileLines - options.endLine;\n\t\ttext = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${options.endLine + 1} to continue.]`;\n\t} else {\n\t\ttext = truncation.content;\n\t}\n\treturn { content: [{ type: \"text\", text }], details };\n}\n\nasync function readTextFile(\n\toperations: ToolOperations,\n\tpath: string,\n\toffset: number | undefined,\n\tlimit: number | undefined,\n): Promise<AgentToolResult<ReadToolDetails | undefined>> {\n\tawait initHasher();\n\tconst buffer = await operations.readFile(path);\n\tconst textContent = buffer.toString(\"utf-8\").replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\tconst allLines = hashlineVisLines(textContent);\n\tconst totalFileLines = allLines.length;\n\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\tconst startLineDisplay = startLine + 1;\n\tif (totalFileLines === 0) {\n\t\tif (startLineDisplay !== 1) throw new Error(`Offset ${offset} is beyond end of file (0 lines total)`);\n\t\tconst emptyHash = (await lineHashes(textContent, path))[0] ?? \"\";\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: `${emptyHash}${HASH_SEP}\\n[File is empty. Use edit to insert content.]` }],\n\t\t\tdetails: undefined,\n\t\t};\n\t}\n\tif (startLine >= totalFileLines) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);\n\t}\n\tconst endLine = limit !== undefined ? Math.min(startLine + limit, totalFileLines) : totalFileLines;\n\tconst allHashes = await lineHashes(textContent, path);\n\tconst selectedContent = fmtRegion(allHashes.slice(startLine, endLine), allLines.slice(startLine, endLine));\n\treturn formatTextReadOutput({\n\t\ttruncation: truncateHead(selectedContent),\n\t\tallLines,\n\t\tstartLine,\n\t\tstartLineDisplay,\n\t\tendLine,\n\t\ttotalFileLines,\n\t});\n}\n\nasync function executeReadRequest(\n\toptions: ReadExecutionOptions,\n\tisAborted: () => boolean,\n): Promise<AgentToolResult<ReadToolDetails | undefined> | undefined> {\n\tconst target = await resolveReadTarget(options);\n\tif (isAborted()) return undefined;\n\tawait target.operations.access(target.path, \"read\");\n\tif (isAborted()) return undefined;\n\tconst mimeType = target.operations.detectImageMimeType\n\t\t? await target.operations.detectImageMimeType(target.path)\n\t\t: undefined;\n\tconst nonVisionImageNote = getNonVisionImageNote(options.model);\n\tconst result = mimeType\n\t\t? await readImageFile(target.operations, target.path, mimeType, options.autoResizeImages, nonVisionImageNote)\n\t\t: await readTextFile(target.operations, target.path, options.input.offset, options.input.limit);\n\treturn isAborted() ? undefined : result;\n}\n\nfunction executeAbortableRead(options: ReadExecutionOptions): Promise<AgentToolResult<ReadToolDetails | undefined>> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (options.signal?.aborted) {\n\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\treturn;\n\t\t}\n\t\tlet aborted = false;\n\t\tconst onAbort = () => {\n\t\t\taborted = true;\n\t\t\treject(new Error(\"Operation aborted\"));\n\t\t};\n\t\toptions.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tvoid executeReadRequest(options, () => aborted).then(\n\t\t\t(result) => {\n\t\t\t\tif (result === undefined) return;\n\t\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(result);\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\toptions.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tif (!aborted) reject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport type ReadToolDefinition = ToolDefinition<typeof readSchema, ReadToolDetails | undefined>;\n\nexport function createReadToolDefinition(operations: ToolOperations, options?: ReadToolOptions): ReadToolDefinition {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst ops = operations;\n\tconst cwd = operations.cwd;\n\tconst operationsForPath = options?.operationsForPath;\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. Text files are returned as HASH│content rows for hash-anchored editing. 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 with hashline anchors\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use read to examine files instead of cat or sed.\",\n\t\t\t\"For edits, copy only the 3-character HASH values into edit hash_range_inclusive; do not include HASH│ prefixes in content_lines.\",\n\t\t],\n\t\tparameters: readSchema,\n\t\tasync execute(_toolCallId, input: ReadToolInput, signal?: AbortSignal, _onUpdate?, ctx?) {\n\t\t\treturn executeAbortableRead({\n\t\t\t\tinput,\n\t\t\t\tdefaultOperations: ops,\n\t\t\t\tcwd,\n\t\t\t\toperationsForPath,\n\t\t\t\tautoResizeImages,\n\t\t\t\tmodel: ctx?.model,\n\t\t\t\tsignal,\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\tconst classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;\n\t\t\tconst renderArgs = args as ReadRenderArgs | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst displayOps = rawPath\n\t\t\t\t? (getReadToolOperations(operationsForPath?.(resolveToCwd(rawPath, cwd))) ?? ops)\n\t\t\t\t: ops;\n\t\t\ttext.setText(\n\t\t\t\tformatBackendIcon(displayOps.getBackendInfo?.(), theme) +\n\t\t\t\t\t(classification ? formatCompactReadCall(classification, args, theme) : formatReadCall(args, theme)),\n\t\t\t);\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(\n\t\t\t\tformatReadResult(context.args, result, options, theme, context.showImages, context.cwd, context.isError),\n\t\t\t);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createReadTool(operations: ToolOperations, options?: ReadToolOptions): AgentTool<typeof readSchema> {\n\treturn wrapToolDefinition(createReadToolDefinition(operations, options));\n}\n"]}
@@ -18,6 +18,7 @@ interface ToolRenderTheme {
18
18
  fg(name: ThemeColor, text: string): string;
19
19
  }
20
20
  export declare function getTextOutput(result: ToolTextOutputSource | undefined, showImages: boolean): string;
21
+ export declare function formatHiddenLinesHint(count: number, theme: ToolRenderTheme): string;
21
22
  export type ToolRenderResultLike<TDetails> = {
22
23
  content: (TextContent | ImageContent)[];
23
24
  details: TDetails;
@@ -1 +1 @@
1
- {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAGzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC7B,OAAO,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED,UAAU,eAAe;IACxB,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAsBnG;AAED,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI;IAC5C,OAAO,EAAE,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,eAAe,GAAG,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAI1G","sourcesContent":["import * as os from \"node:os\";\nimport type { ImageContent, TextContent } from \"@greppleai/grip-ai\";\nimport { getCapabilities, getImageDimensions, imageFallback } from \"@greppleai/grip-tui\";\nimport type { ThemeColor } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { ToolBackendInfo } from \"./operations.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport interface ToolTextOutputContent {\n\ttype: string;\n\ttext?: string;\n\tdata?: string;\n\tmimeType?: string;\n}\n\ninterface ToolTextOutputSource {\n\tcontent: ToolTextOutputContent[];\n}\n\ninterface ToolRenderTheme {\n\tfg(name: ThemeColor, text: string): string;\n}\n\nexport function getTextOutput(result: ToolTextOutputSource | undefined, showImages: boolean): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nexport function invalidArgText(theme: ToolRenderTheme): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n\n/**\n * Render a small icon prefix for a tool-call header indicating whether the tool\n * runs against the local machine or a remote daemon backend. Placed at the start\n * of the header so the local/remote target is visible at a glance.\n */\nexport function formatBackendIcon(backendInfo: ToolBackendInfo | undefined, theme: ToolRenderTheme): string {\n\tif (!backendInfo) return \"\";\n\tif (backendInfo.type === \"local\") return theme.fg(\"muted\", \"\\u{1F5A5} \");\n\treturn backendInfo.configured ? theme.fg(\"muted\", \"\\u2601 \") : theme.fg(\"warning\", \"\\u2601 \");\n}\n"]}
1
+ {"version":3,"file":"render-utils.d.ts","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAGzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIjD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC7B,OAAO,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED,UAAU,eAAe;IACxB,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAsBnG;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAKnF;AAED,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI;IAC5C,OAAO,EAAE,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,eAAe,GAAG,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAI1G","sourcesContent":["import * as os from \"node:os\";\nimport type { ImageContent, TextContent } from \"@greppleai/grip-ai\";\nimport { getCapabilities, getImageDimensions, imageFallback } from \"@greppleai/grip-tui\";\nimport { keyText } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { ThemeColor } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { ToolBackendInfo } from \"./operations.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport interface ToolTextOutputContent {\n\ttype: string;\n\ttext?: string;\n\tdata?: string;\n\tmimeType?: string;\n}\n\ninterface ToolTextOutputSource {\n\tcontent: ToolTextOutputContent[];\n}\n\ninterface ToolRenderTheme {\n\tfg(name: ThemeColor, text: string): string;\n}\n\nexport function getTextOutput(result: ToolTextOutputSource | undefined, showImages: boolean): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport function formatHiddenLinesHint(count: number, theme: ToolRenderTheme): string {\n\tconst expandKey = keyText(\"app.tools.expand\");\n\treturn (\n\t\ttheme.fg(\"warning\", `${count} lines hidden`) + (expandKey ? theme.fg(\"accent\", ` · ${expandKey} to expand`) : \"\")\n\t);\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nexport function invalidArgText(theme: ToolRenderTheme): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n\n/**\n * Render a small icon prefix for a tool-call header indicating whether the tool\n * runs against the local machine or a remote daemon backend. Placed at the start\n * of the header so the local/remote target is visible at a glance.\n */\nexport function formatBackendIcon(backendInfo: ToolBackendInfo | undefined, theme: ToolRenderTheme): string {\n\tif (!backendInfo) return \"\";\n\tif (backendInfo.type === \"local\") return theme.fg(\"muted\", \"\\u{1F5A5} \");\n\treturn backendInfo.configured ? theme.fg(\"muted\", \"\\u2601 \") : theme.fg(\"warning\", \"\\u2601 \");\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import * as os from "node:os";
2
2
  import { getCapabilities, getImageDimensions, imageFallback } from "@greppleai/grip-tui";
3
+ import { keyText } from "../../modes/interactive/components/keybinding-hints.js";
3
4
  import { stripAnsi } from "../../utils/ansi.js";
4
5
  import { sanitizeBinaryOutput } from "../../utils/shell.js";
5
6
  export function shortenPath(path) {
@@ -43,6 +44,10 @@ export function getTextOutput(result, showImages) {
43
44
  }
44
45
  return output;
45
46
  }
47
+ export function formatHiddenLinesHint(count, theme) {
48
+ const expandKey = keyText("app.tools.expand");
49
+ return (theme.fg("warning", `${count} lines hidden`) + (expandKey ? theme.fg("accent", ` · ${expandKey} to expand`) : ""));
50
+ }
46
51
  export function invalidArgText(theme) {
47
52
  return theme.fg("error", "[invalid arg]");
48
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,MAAM,UAAU,WAAW,CAAC,IAAa,EAAU;IAClD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,UAAU,GAAG,CAAC,KAAc,EAAiB;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7B,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAU;IACjD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAAA,CAClC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAU;IAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAAA,CAC/B;AAiBD,MAAM,UAAU,aAAa,CAAC,MAAwC,EAAE,UAAmB,EAAU;IACpG,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IACnE,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAErE,IAAI,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhH,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,MAAM,eAAe,GAAG,WAAW;aACjC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,eAAe,CAAC;YACjD,MAAM,IAAI,GACT,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAClG,OAAO,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAAA,CACrC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,eAAe,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;IACrE,CAAC;IAED,OAAO,MAAM,CAAC;AAAA,CACd;AAOD,MAAM,UAAU,cAAc,CAAC,KAAsB,EAAU;IAC9D,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAAA,CAC1C;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAwC,EAAE,KAAsB,EAAU;IAC3G,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,WAAW,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACzE,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAAA,CAC9F","sourcesContent":["import * as os from \"node:os\";\nimport type { ImageContent, TextContent } from \"@greppleai/grip-ai\";\nimport { getCapabilities, getImageDimensions, imageFallback } from \"@greppleai/grip-tui\";\nimport type { ThemeColor } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { ToolBackendInfo } from \"./operations.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport interface ToolTextOutputContent {\n\ttype: string;\n\ttext?: string;\n\tdata?: string;\n\tmimeType?: string;\n}\n\ninterface ToolTextOutputSource {\n\tcontent: ToolTextOutputContent[];\n}\n\ninterface ToolRenderTheme {\n\tfg(name: ThemeColor, text: string): string;\n}\n\nexport function getTextOutput(result: ToolTextOutputSource | undefined, showImages: boolean): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nexport function invalidArgText(theme: ToolRenderTheme): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n\n/**\n * Render a small icon prefix for a tool-call header indicating whether the tool\n * runs against the local machine or a remote daemon backend. Placed at the start\n * of the header so the local/remote target is visible at a glance.\n */\nexport function formatBackendIcon(backendInfo: ToolBackendInfo | undefined, theme: ToolRenderTheme): string {\n\tif (!backendInfo) return \"\";\n\tif (backendInfo.type === \"local\") return theme.fg(\"muted\", \"\\u{1F5A5} \");\n\treturn backendInfo.configured ? theme.fg(\"muted\", \"\\u2601 \") : theme.fg(\"warning\", \"\\u2601 \");\n}\n"]}
1
+ {"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../../../src/core/tools/render-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as os from \"node:os\";\nimport type { ImageContent, TextContent } from \"@greppleai/grip-ai\";\nimport { getCapabilities, getImageDimensions, imageFallback } from \"@greppleai/grip-tui\";\nimport { keyText } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { ThemeColor } from \"../../modes/interactive/theme/theme.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport type { ToolBackendInfo } from \"./operations.ts\";\n\nexport function shortenPath(path: unknown): string {\n\tif (typeof path !== \"string\") return \"\";\n\tconst home = os.homedir();\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\n\nexport function str(value: unknown): string | null {\n\tif (typeof value === \"string\") return value;\n\tif (value == null) return \"\";\n\treturn null;\n}\n\nexport function replaceTabs(text: string): string {\n\treturn text.replace(/\\t/g, \" \");\n}\n\nexport function normalizeDisplayText(text: string): string {\n\treturn text.replace(/\\r/g, \"\");\n}\n\nexport interface ToolTextOutputContent {\n\ttype: string;\n\ttext?: string;\n\tdata?: string;\n\tmimeType?: string;\n}\n\ninterface ToolTextOutputSource {\n\tcontent: ToolTextOutputContent[];\n}\n\ninterface ToolRenderTheme {\n\tfg(name: ThemeColor, text: string): string;\n}\n\nexport function getTextOutput(result: ToolTextOutputSource | undefined, showImages: boolean): string {\n\tif (!result) return \"\";\n\n\tconst textBlocks = result.content.filter((c) => c.type === \"text\");\n\tconst imageBlocks = result.content.filter((c) => c.type === \"image\");\n\n\tlet output = textBlocks.map((c) => sanitizeBinaryOutput(stripAnsi(c.text || \"\")).replace(/\\r/g, \"\")).join(\"\\n\");\n\n\tconst caps = getCapabilities();\n\tif (imageBlocks.length > 0 && (!caps.images || !showImages)) {\n\t\tconst imageIndicators = imageBlocks\n\t\t\t.map((img) => {\n\t\t\t\tconst mimeType = img.mimeType ?? \"image/unknown\";\n\t\t\t\tconst dims =\n\t\t\t\t\timg.data && img.mimeType ? (getImageDimensions(img.data, img.mimeType) ?? undefined) : undefined;\n\t\t\t\treturn imageFallback(mimeType, dims);\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t\toutput = output ? `${output}\\n${imageIndicators}` : imageIndicators;\n\t}\n\n\treturn output;\n}\n\nexport function formatHiddenLinesHint(count: number, theme: ToolRenderTheme): string {\n\tconst expandKey = keyText(\"app.tools.expand\");\n\treturn (\n\t\ttheme.fg(\"warning\", `${count} lines hidden`) + (expandKey ? theme.fg(\"accent\", ` · ${expandKey} to expand`) : \"\")\n\t);\n}\n\nexport type ToolRenderResultLike<TDetails> = {\n\tcontent: (TextContent | ImageContent)[];\n\tdetails: TDetails;\n};\n\nexport function invalidArgText(theme: ToolRenderTheme): string {\n\treturn theme.fg(\"error\", \"[invalid arg]\");\n}\n\n/**\n * Render a small icon prefix for a tool-call header indicating whether the tool\n * runs against the local machine or a remote daemon backend. Placed at the start\n * of the header so the local/remote target is visible at a glance.\n */\nexport function formatBackendIcon(backendInfo: ToolBackendInfo | undefined, theme: ToolRenderTheme): string {\n\tif (!backendInfo) return \"\";\n\tif (backendInfo.type === \"local\") return theme.fg(\"muted\", \"\\u{1F5A5} \");\n\treturn backendInfo.configured ? theme.fg(\"muted\", \"\\u2601 \") : theme.fg(\"warning\", \"\\u2601 \");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,4BAA4B,CAAC;AAG7E,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAEtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAYtD,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,MAAM,WAAW,gBAAgB;CAAG;AA2JpC,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,WAAW,EAAE,SAAS,CAAC,CAAC;AAEhF,wBAAgB,yBAAyB,CACxC,UAAU,EAAE,cAAc,EAC1B,QAAQ,CAAC,EAAE,gBAAgB,GACzB,mBAAmB,CAiFrB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAErH","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport { Container, Text } from \"@greppleai/grip-tui\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport type { ToolOperations } from \"./operations.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tformatBackendIcon,\n\tinvalidArgText,\n\tnormalizeDisplayText,\n\treplaceTabs,\n\tshortenPath,\n\tstr,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolOptions {}\n\ninterface WriteRenderArgs {\n\tpath?: string;\n\tfile_path?: string;\n\tcontent?: string;\n}\n\ninterface WriteRenderResult extends AgentToolResult<undefined> {\n\tisError?: boolean;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\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 formatWriteContentPreview(\n\tfileContent: string | null,\n\trawPath: string | null,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tif (fileContent === null) return `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\tif (!fileContent) return \"\";\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang\n\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t: normalizeDisplayText(fileContent).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\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines, ${lines.length} total,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t}\n\treturn text;\n}\n\nfunction formatWriteCall(\n\targs: WriteRenderArgs | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\")}`;\n\ttext += formatWriteContentPreview(fileContent, rawPath, options, theme, cache);\n\n\treturn text;\n}\n\nfunction formatWriteResult(result: WriteRenderResult, theme: Theme): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport type WriteToolDefinition = ToolDefinition<typeof writeSchema, undefined>;\n\nexport function createWriteToolDefinition(\n\toperations: ToolOperations,\n\t_options?: WriteToolOptions,\n): WriteToolDefinition {\n\tconst ops = operations;\n\tconst cwd = operations.cwd;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(_toolCallId, { path, content }: WriteToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tlet aborted = signal?.aborted ?? false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t};\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (aborted || signal?.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Operation aborted\");\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\ttry {\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir, { recursive: true });\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as WriteRenderArgs | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatBackendIcon(ops.getBackendInfo?.(), theme) +\n\t\t\t\t\tformatWriteCall(\n\t\t\t\t\t\trenderArgs,\n\t\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcomponent.cache,\n\t\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(operations: ToolOperations, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(operations, options));\n}\n"]}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,4BAA4B,CAAC;AAG7E,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAA2B,MAAM,wBAAwB,CAAC;AAEtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAatD,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,MAAM,WAAW,gBAAgB;CAAG;AA2JpC,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,WAAW,EAAE,SAAS,CAAC,CAAC;AAEhF,wBAAgB,yBAAyB,CACxC,UAAU,EAAE,cAAc,EAC1B,QAAQ,CAAC,EAAE,gBAAgB,GACzB,mBAAmB,CAiFrB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAErH","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport { Container, Text } from \"@greppleai/grip-tui\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport type { ToolOperations } from \"./operations.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tformatBackendIcon,\n\tformatHiddenLinesHint,\n\tinvalidArgText,\n\tnormalizeDisplayText,\n\treplaceTabs,\n\tshortenPath,\n\tstr,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolOptions {}\n\ninterface WriteRenderArgs {\n\tpath?: string;\n\tfile_path?: string;\n\tcontent?: string;\n}\n\ninterface WriteRenderResult extends AgentToolResult<undefined> {\n\tisError?: boolean;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\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 formatWriteContentPreview(\n\tfileContent: string | null,\n\trawPath: string | null,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tif (fileContent === null) return `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\tif (!fileContent) return \"\";\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang\n\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t: normalizeDisplayText(fileContent).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\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `\\n\\n${formatHiddenLinesHint(remaining, theme)}`;\n\t}\n\treturn text;\n}\n\nfunction formatWriteCall(\n\targs: WriteRenderArgs | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\")}`;\n\ttext += formatWriteContentPreview(fileContent, rawPath, options, theme, cache);\n\n\treturn text;\n}\n\nfunction formatWriteResult(result: WriteRenderResult, theme: Theme): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport type WriteToolDefinition = ToolDefinition<typeof writeSchema, undefined>;\n\nexport function createWriteToolDefinition(\n\toperations: ToolOperations,\n\t_options?: WriteToolOptions,\n): WriteToolDefinition {\n\tconst ops = operations;\n\tconst cwd = operations.cwd;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(_toolCallId, { path, content }: WriteToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tlet aborted = signal?.aborted ?? false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t};\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (aborted || signal?.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Operation aborted\");\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\ttry {\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir, { recursive: true });\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as WriteRenderArgs | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatBackendIcon(ops.getBackendInfo?.(), theme) +\n\t\t\t\t\tformatWriteCall(\n\t\t\t\t\t\trenderArgs,\n\t\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcomponent.cache,\n\t\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(operations: ToolOperations, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(operations, options));\n}\n"]}
@@ -1,11 +1,10 @@
1
1
  import { Container, Text } from "@greppleai/grip-tui";
2
2
  import { dirname } from "path";
3
3
  import { Type } from "typebox";
4
- import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
5
4
  import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
6
5
  import { withFileMutationQueue } from "./file-mutation-queue.js";
7
6
  import { resolveToCwd } from "./path-utils.js";
8
- import { formatBackendIcon, invalidArgText, normalizeDisplayText, replaceTabs, shortenPath, str, } from "./render-utils.js";
7
+ import { formatBackendIcon, formatHiddenLinesHint, invalidArgText, normalizeDisplayText, replaceTabs, shortenPath, str, } from "./render-utils.js";
9
8
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
10
9
  const writeSchema = Type.Object({
11
10
  path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
@@ -100,7 +99,7 @@ function formatWriteContentPreview(fileContent, rawPath, options, theme, cache)
100
99
  const remaining = lines.length - maxLines;
101
100
  let text = `\n\n${displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
102
101
  if (remaining > 0) {
103
- text += `${theme.fg("muted", `\n... (${remaining} more lines, ${lines.length} total,`)} ${keyHint("app.tools.expand", "to expand")})`;
102
+ text += `\n\n${formatHiddenLinesHint(remaining, theme)}`;
104
103
  }
105
104
  return text;
106
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport { Container, Text } from \"@greppleai/grip-tui\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport type { ToolOperations } from \"./operations.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tformatBackendIcon,\n\tinvalidArgText,\n\tnormalizeDisplayText,\n\treplaceTabs,\n\tshortenPath,\n\tstr,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolOptions {}\n\ninterface WriteRenderArgs {\n\tpath?: string;\n\tfile_path?: string;\n\tcontent?: string;\n}\n\ninterface WriteRenderResult extends AgentToolResult<undefined> {\n\tisError?: boolean;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\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 formatWriteContentPreview(\n\tfileContent: string | null,\n\trawPath: string | null,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tif (fileContent === null) return `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\tif (!fileContent) return \"\";\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang\n\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t: normalizeDisplayText(fileContent).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\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines, ${lines.length} total,`)} ${keyHint(\"app.tools.expand\", \"to expand\")})`;\n\t}\n\treturn text;\n}\n\nfunction formatWriteCall(\n\targs: WriteRenderArgs | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\")}`;\n\ttext += formatWriteContentPreview(fileContent, rawPath, options, theme, cache);\n\n\treturn text;\n}\n\nfunction formatWriteResult(result: WriteRenderResult, theme: Theme): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport type WriteToolDefinition = ToolDefinition<typeof writeSchema, undefined>;\n\nexport function createWriteToolDefinition(\n\toperations: ToolOperations,\n\t_options?: WriteToolOptions,\n): WriteToolDefinition {\n\tconst ops = operations;\n\tconst cwd = operations.cwd;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(_toolCallId, { path, content }: WriteToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tlet aborted = signal?.aborted ?? false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t};\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (aborted || signal?.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Operation aborted\");\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\ttry {\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir, { recursive: true });\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as WriteRenderArgs | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatBackendIcon(ops.getBackendInfo?.(), theme) +\n\t\t\t\t\tformatWriteCall(\n\t\t\t\t\t\trenderArgs,\n\t\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcomponent.cache,\n\t\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(operations: ToolOperations, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(operations, options));\n}\n"]}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAc,MAAM,wCAAwC,CAAC;AAExG,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EACN,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,GAAG,GACH,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;IACtF,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;CACrE,CAAC,CAAC;AAwBH,MAAM,wBAAyB,SAAQ,IAAI;IAC1C,KAAK,CAAuB;IAE5B,cAAc;QACb,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAAA,CAChB;CACD;AAED,MAAM,kCAAkC,GAAG,EAAE,CAAC;AAE9C,SAAS,mBAAmB,CAAC,IAAY,EAAE,IAAY,EAAU;IAChE,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9C,OAAO,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAAA,CAC5B;AAED,SAAS,2BAA2B,CAAC,KAA0B,EAAQ;IACtE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,kCAAkC,EAAE,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAC/F,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO;IAC9B,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,MAAM,iBAAiB,GAAG,aAAa,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAClE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;YACxB,iBAAiB,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1F,CAAC;AAAA,CACD;AAED,SAAS,8BAA8B,CAAC,OAAsB,EAAE,WAAmB,EAAmC;IACrH,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,cAAc,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC;IAC/C,OAAO;QACN,OAAO;QACP,IAAI;QACJ,UAAU,EAAE,WAAW;QACvB,eAAe,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;QACvC,gBAAgB,EAAE,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC;KACjD,CAAC;AAAA,CACF;AAED,SAAS,oCAAoC,CAC5C,KAAsC,EACtC,OAAsB,EACtB,WAAmB,EACe;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,CAAC,KAAK;QAAE,OAAO,8BAA8B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACxE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO;QAAE,OAAO,8BAA8B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAClH,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC;QAAE,OAAO,8BAA8B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC3G,IAAI,WAAW,CAAC,MAAM,KAAK,KAAK,CAAC,UAAU,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAEjE,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC5D,MAAM,YAAY,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,eAAe,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IAClD,KAAK,CAAC,UAAU,GAAG,WAAW,CAAC;IAC/B,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/B,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;IACnD,KAAK,CAAC,eAAe,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;IAChD,KAAK,CAAC,gBAAgB,CAAC,SAAS,CAAC,GAAG,mBAAmB,CAAC,KAAK,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IACtG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3E,CAAC;IACD,2BAA2B,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,sBAAsB,CAAC,KAAe,EAAY;IAC1D,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QACzC,GAAG,EAAE,CAAC;IACP,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAAA,CAC3B;AAED,SAAS,yBAAyB,CACjC,WAA0B,EAC1B,OAAsB,EACtB,OAAgC,EAChC,KAAY,EACZ,KAAsC,EAC7B;IACT,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,yCAAyC,CAAC,EAAE,CAAC;IACvG,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,aAAa,GAAG,IAAI;QACzB,CAAC,CAAC,CAAC,KAAK,EAAE,gBAAgB,IAAI,aAAa,CAAC,WAAW,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAClG,CAAC,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,sBAAsB,CAAC,aAAa,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;IAC1C,IAAI,IAAI,GAAG,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACrH,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACnB,IAAI,IAAI,OAAO,qBAAqB,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,SAAS,eAAe,CACvB,IAAiC,EACjC,OAAgC,EAChC,KAAY,EACZ,KAAsC,EAC7B;IACT,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,SAAS,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,IAAI,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;IAC3J,IAAI,IAAI,yBAAyB,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAE/E,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,SAAS,iBAAiB,CAAC,MAAyB,EAAE,KAAY,EAAsB;IACvF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;SACxB,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;AAAA,CACxC;AAID,MAAM,UAAU,yBAAyB,CACxC,UAA0B,EAC1B,QAA2B,EACL;IACtB,MAAM,GAAG,GAAG,UAAU,CAAC;IACvB,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC;IAC3B,OAAO;QACN,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;QACd,WAAW,EACV,iIAAiI;QAClI,aAAa,EAAE,2BAA2B;QAC1C,gBAAgB,EAAE,CAAC,oDAAoD,CAAC;QACxE,UAAU,EAAE,WAAW;QACvB,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAkB,EAAE,MAAoB,EAAE,SAAU,EAAE,IAAK,EAAE;YACtG,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7C,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;YAClC,OAAO,qBAAqB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC;gBACtD,IAAI,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,KAAK,CAAC;gBACvC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;oBACrB,OAAO,GAAG,IAAI,CAAC;gBAAA,CACf,CAAC;gBACF,MAAM,cAAc,GAAG,GAAS,EAAE,CAAC;oBAClC,IAAI,OAAO,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;wBAChC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;oBACtC,CAAC;gBAAA,CACD,CAAC;gBAEF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3D,IAAI,CAAC;oBACJ,cAAc,EAAE,CAAC;oBACjB,uCAAuC;oBACvC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBAC1C,cAAc,EAAE,CAAC;oBAEjB,2BAA2B;oBAC3B,MAAM,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;oBAC3C,cAAc,EAAE,CAAC;oBAEjB,OAAO;wBACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,OAAO,CAAC,MAAM,aAAa,IAAI,EAAE,EAAE,CAAC;wBAC1F,OAAO,EAAE,SAAS;qBAClB,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACV,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC/C,CAAC;YAAA,CACD,CAAC,CAAC;QAAA,CACH;QACD,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE;YAChC,MAAM,UAAU,GAAG,IAAmC,CAAC;YACvD,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,EAAE,SAAS,IAAI,UAAU,EAAE,IAAI,CAAC,CAAC;YAC/D,MAAM,WAAW,GAAG,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,SAAS,GACb,OAAO,CAAC,aAAsD,IAAI,IAAI,wBAAwB,EAAE,CAAC;YACnG,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBAC1B,SAAS,CAAC,KAAK,GAAG,OAAO,CAAC,YAAY;oBACrC,CAAC,CAAC,8BAA8B,CAAC,OAAO,EAAE,WAAW,CAAC;oBACtD,CAAC,CAAC,oCAAoC,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;YAChF,CAAC;iBAAM,CAAC;gBACP,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC;YAC7B,CAAC;YACD,SAAS,CAAC,OAAO,CAChB,iBAAiB,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,EAAE,KAAK,CAAC;gBAC/C,eAAe,CACd,UAAU,EACV,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EAC5D,KAAK,EACL,SAAS,CAAC,KAAK,CACf,CACF,CAAC;YACF,OAAO,SAAS,CAAC;QAAA,CACjB;QACD,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE;YAC9C,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;YACjF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,MAAM,SAAS,GAAI,OAAO,CAAC,aAAuC,IAAI,IAAI,SAAS,EAAE,CAAC;gBACtF,SAAS,CAAC,KAAK,EAAE,CAAC;gBAClB,OAAO,SAAS,CAAC;YAClB,CAAC;YACD,MAAM,IAAI,GAAI,OAAO,CAAC,aAAkC,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/E,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC;QAAA,CACZ;KACD,CAAC;AAAA,CACF;AAED,MAAM,UAAU,eAAe,CAAC,UAA0B,EAAE,OAA0B,EAAiC;IACtH,OAAO,kBAAkB,CAAC,yBAAyB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;AAAA,CAC1E","sourcesContent":["import type { AgentTool, AgentToolResult } from \"@greppleai/grip-agent-core\";\nimport { Container, Text } from \"@greppleai/grip-tui\";\nimport { dirname } from \"path\";\nimport { type Static, Type } from \"typebox\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport type { ToolOperations } from \"./operations.ts\";\nimport { resolveToCwd } from \"./path-utils.ts\";\nimport {\n\tformatBackendIcon,\n\tformatHiddenLinesHint,\n\tinvalidArgText,\n\tnormalizeDisplayText,\n\treplaceTabs,\n\tshortenPath,\n\tstr,\n} from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport interface WriteToolOptions {}\n\ninterface WriteRenderArgs {\n\tpath?: string;\n\tfile_path?: string;\n\tcontent?: string;\n}\n\ninterface WriteRenderResult extends AgentToolResult<undefined> {\n\tisError?: boolean;\n}\n\ntype WriteHighlightCache = {\n\trawPath: string | null;\n\tlang: string;\n\trawContent: string;\n\tnormalizedLines: string[];\n\thighlightedLines: string[];\n};\n\nclass WriteCallRenderComponent extends Text {\n\tcache?: WriteHighlightCache;\n\n\tconstructor() {\n\t\tsuper(\"\", 0, 0);\n\t}\n}\n\nconst WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;\n\nfunction highlightSingleLine(line: string, lang: string): string {\n\tconst highlighted = highlightCode(line, lang);\n\treturn highlighted[0] ?? \"\";\n}\n\nfunction refreshWriteHighlightPrefix(cache: WriteHighlightCache): void {\n\tconst prefixCount = Math.min(WRITE_PARTIAL_FULL_HIGHLIGHT_LINES, cache.normalizedLines.length);\n\tif (prefixCount === 0) return;\n\tconst prefixSource = cache.normalizedLines.slice(0, prefixCount).join(\"\\n\");\n\tconst prefixHighlighted = highlightCode(prefixSource, cache.lang);\n\tfor (let i = 0; i < prefixCount; i++) {\n\t\tcache.highlightedLines[i] =\n\t\t\tprefixHighlighted[i] ?? highlightSingleLine(cache.normalizedLines[i] ?? \"\", cache.lang);\n\t}\n}\n\nfunction rebuildWriteHighlightCacheFull(rawPath: string | null, fileContent: string): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tconst displayContent = normalizeDisplayText(fileContent);\n\tconst normalized = replaceTabs(displayContent);\n\treturn {\n\t\trawPath,\n\t\tlang,\n\t\trawContent: fileContent,\n\t\tnormalizedLines: normalized.split(\"\\n\"),\n\t\thighlightedLines: highlightCode(normalized, lang),\n\t};\n}\n\nfunction updateWriteHighlightCacheIncremental(\n\tcache: WriteHighlightCache | undefined,\n\trawPath: string | null,\n\tfileContent: string,\n): WriteHighlightCache | undefined {\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tif (!lang) return undefined;\n\tif (!cache) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (cache.lang !== lang || cache.rawPath !== rawPath) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (!fileContent.startsWith(cache.rawContent)) return rebuildWriteHighlightCacheFull(rawPath, fileContent);\n\tif (fileContent.length === cache.rawContent.length) return cache;\n\n\tconst deltaRaw = fileContent.slice(cache.rawContent.length);\n\tconst deltaDisplay = normalizeDisplayText(deltaRaw);\n\tconst deltaNormalized = replaceTabs(deltaDisplay);\n\tcache.rawContent = fileContent;\n\tif (cache.normalizedLines.length === 0) {\n\t\tcache.normalizedLines.push(\"\");\n\t\tcache.highlightedLines.push(\"\");\n\t}\n\n\tconst segments = deltaNormalized.split(\"\\n\");\n\tconst lastIndex = cache.normalizedLines.length - 1;\n\tcache.normalizedLines[lastIndex] += segments[0];\n\tcache.highlightedLines[lastIndex] = highlightSingleLine(cache.normalizedLines[lastIndex], cache.lang);\n\tfor (let i = 1; i < segments.length; i++) {\n\t\tcache.normalizedLines.push(segments[i]);\n\t\tcache.highlightedLines.push(highlightSingleLine(segments[i], cache.lang));\n\t}\n\trefreshWriteHighlightPrefix(cache);\n\treturn cache;\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 formatWriteContentPreview(\n\tfileContent: string | null,\n\trawPath: string | null,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tif (fileContent === null) return `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\tif (!fileContent) return \"\";\n\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\tconst renderedLines = lang\n\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t: normalizeDisplayText(fileContent).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\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\tif (remaining > 0) {\n\t\ttext += `\\n\\n${formatHiddenLinesHint(remaining, theme)}`;\n\t}\n\treturn text;\n}\n\nfunction formatWriteCall(\n\targs: WriteRenderArgs | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst path = rawPath !== null ? shortenPath(rawPath) : null;\n\tconst invalidArg = invalidArgText(theme);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${path === null ? invalidArg : path ? theme.fg(\"accent\", path) : theme.fg(\"toolOutput\", \"...\")}`;\n\ttext += formatWriteContentPreview(fileContent, rawPath, options, theme, cache);\n\n\treturn text;\n}\n\nfunction formatWriteResult(result: WriteRenderResult, theme: Theme): string | undefined {\n\tif (!result.isError) {\n\t\treturn undefined;\n\t}\n\tconst output = result.content\n\t\t.filter((c) => c.type === \"text\")\n\t\t.map((c) => c.text || \"\")\n\t\t.join(\"\\n\");\n\tif (!output) {\n\t\treturn undefined;\n\t}\n\treturn `\\n${theme.fg(\"error\", output)}`;\n}\n\nexport type WriteToolDefinition = ToolDefinition<typeof writeSchema, undefined>;\n\nexport function createWriteToolDefinition(\n\toperations: ToolOperations,\n\t_options?: WriteToolOptions,\n): WriteToolDefinition {\n\tconst ops = operations;\n\tconst cwd = operations.cwd;\n\treturn {\n\t\tname: \"write\",\n\t\tlabel: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tpromptSnippet: \"Create or overwrite files\",\n\t\tpromptGuidelines: [\"Use write only for new files or complete rewrites.\"],\n\t\tparameters: writeSchema,\n\t\tasync execute(_toolCallId, { path, content }: WriteToolInput, signal?: AbortSignal, _onUpdate?, _ctx?) {\n\t\t\tconst absolutePath = resolveToCwd(path, cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tlet aborted = signal?.aborted ?? false;\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\taborted = true;\n\t\t\t\t};\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (aborted || signal?.aborted) {\n\t\t\t\t\t\tthrow new Error(\"Operation aborted\");\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\ttry {\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t\t// Create parent directories if needed.\n\t\t\t\t\tawait ops.mkdir(dir, { recursive: true });\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\t// Write the file contents.\n\t\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\t\tthrowIfAborted();\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote ${content.length} bytes to ${path}` }],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t} finally {\n\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst renderArgs = args as WriteRenderArgs | undefined;\n\t\t\tconst rawPath = str(renderArgs?.file_path ?? renderArgs?.path);\n\t\t\tconst fileContent = str(renderArgs?.content);\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as WriteCallRenderComponent | undefined) ?? new WriteCallRenderComponent();\n\t\t\tif (fileContent !== null) {\n\t\t\t\tcomponent.cache = context.argsComplete\n\t\t\t\t\t? rebuildWriteHighlightCacheFull(rawPath, fileContent)\n\t\t\t\t\t: updateWriteHighlightCacheIncremental(component.cache, rawPath, fileContent);\n\t\t\t} else {\n\t\t\t\tcomponent.cache = undefined;\n\t\t\t}\n\t\t\tcomponent.setText(\n\t\t\t\tformatBackendIcon(ops.getBackendInfo?.(), theme) +\n\t\t\t\t\tformatWriteCall(\n\t\t\t\t\t\trenderArgs,\n\t\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcomponent.cache,\n\t\t\t\t\t),\n\t\t\t);\n\t\t\treturn component;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatWriteResult({ ...result, isError: context.isError }, theme);\n\t\t\tif (!output) {\n\t\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\t\tcomponent.clear();\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(output);\n\t\t\treturn text;\n\t\t},\n\t};\n}\n\nexport function createWriteTool(operations: ToolOperations, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(operations, options));\n}\n"]}
@@ -3,17 +3,20 @@ import type { HookExecutionNotice } from "../../../core/hooks/types.ts";
3
3
  /** TUI-only card for completed hook calls and their model-visible returned prompts. */
4
4
  export declare class HookExecutionComponent extends Container {
5
5
  private readonly notices;
6
+ private readonly annotations;
6
7
  private readonly markdownTheme;
7
8
  private readonly box;
8
9
  private expanded;
9
10
  constructor(notice: HookExecutionNotice, markdownTheme?: MarkdownTheme);
10
11
  appendNotice(notice: HookExecutionNotice): void;
12
+ appendAnnotation(text: string): void;
11
13
  setExpanded(expanded: boolean): void;
12
14
  invalidate(): void;
13
15
  private rebuild;
14
16
  private renderCollapsedNotices;
15
17
  private renderSingleNotice;
16
18
  private renderGroupedNotices;
19
+ private renderAnnotations;
17
20
  private renderPrompts;
18
21
  }
19
22
  //# sourceMappingURL=hook-execution.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hook-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/hook-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAmGjG,uFAAuF;AACvF,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,MAAM,EAAE,mBAAmB,EAAE,aAAa,GAAE,aAAkC,EAYzF;IAED,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAM9C;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAInC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,aAAa;CAuBrB","sourcesContent":["import { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { HookExecutionCallNotice, HookExecutionNotice } from \"../../../core/hooks/types.ts\";\nimport { getMarkdownTheme, type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { keyHint, keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\nconst MAX_CALL_LABEL_CHARS = 300;\n\ninterface HookCallView {\n\tlabel: string;\n\tsource: string;\n\tstatus: string;\n\tstatusColor: ThemeColor;\n}\n\ninterface CountedValue {\n\tvalue: string;\n\tcount: number;\n}\n\ninterface GroupedHookCall {\n\tcall: HookExecutionCallNotice;\n\tinvocationCount: number;\n\ttotalDurationMs: number;\n\tstatuses: CountedValue[];\n}\n\nfunction truncateCallLabel(label: string): string {\n\treturn label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;\n}\n\nfunction hookCallView(call: HookExecutionCallNotice): HookCallView {\n\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\tconst exit = call.exitCode === null ? \"\" : `, exit ${call.exitCode}`;\n\treturn {\n\t\tlabel: `${call.type} ${truncateCallLabel(call.label)}`,\n\t\tsource: `${call.source.kind}: ${call.source.path}`,\n\t\tstatus: `${call.status}${exit}, ${call.durationMs}ms`,\n\t\tstatusColor: successful ? \"success\" : call.status === \"cancelled\" ? \"warning\" : \"error\",\n\t};\n}\n\nfunction callIdentity(call: HookExecutionCallNotice): string {\n\treturn JSON.stringify([call.type, truncateCallLabel(call.label), call.source.kind, call.source.path]);\n}\n\nfunction statusIdentity(call: HookExecutionCallNotice): string {\n\treturn call.exitCode === null ? call.status : `${call.status} (exit ${call.exitCode})`;\n}\n\nfunction countValues(values: Iterable<string>): CountedValue[] {\n\tconst counts = new Map<string, CountedValue>();\n\tfor (const value of values) {\n\t\tconst existing = counts.get(value);\n\t\tif (existing) {\n\t\t\texisting.count += 1;\n\t\t} else {\n\t\t\tcounts.set(value, { value, count: 1 });\n\t\t}\n\t}\n\treturn [...counts.values()];\n}\n\nfunction groupedHookCalls(notices: readonly HookExecutionNotice[]): GroupedHookCall[] {\n\tconst groups = new Map<string, GroupedHookCall>();\n\tfor (const notice of notices) {\n\t\tfor (const call of notice.calls) {\n\t\t\tconst identity = callIdentity(call);\n\t\t\tconst status = statusIdentity(call);\n\t\t\tconst existing = groups.get(identity);\n\t\t\tif (existing) {\n\t\t\t\texisting.invocationCount += 1;\n\t\t\t\texisting.totalDurationMs += call.durationMs;\n\t\t\t\tconst existingStatus = existing.statuses.find((entry) => entry.value === status);\n\t\t\t\tif (existingStatus) existingStatus.count += 1;\n\t\t\t\telse existing.statuses.push({ value: status, count: 1 });\n\t\t\t} else {\n\t\t\t\tgroups.set(identity, {\n\t\t\t\t\tcall,\n\t\t\t\t\tinvocationCount: 1,\n\t\t\t\t\ttotalDurationMs: call.durationMs,\n\t\t\t\t\tstatuses: [{ value: status, count: 1 }],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn [...groups.values()];\n}\n\nfunction groupedCallStatus(group: GroupedHookCall): string {\n\tconst statuses = group.statuses.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\"; \");\n\treturn `${statuses}, ~${(group.totalDurationMs / group.invocationCount).toFixed(1)}ms`;\n}\n\nfunction groupedCallStatusColor(group: GroupedHookCall): ThemeColor {\n\tif (group.statuses.every(({ value }) => value === \"completed\" || value === \"completed (exit 0)\")) return \"success\";\n\tif (group.statuses.every(({ value }) => value.startsWith(\"cancelled\"))) return \"warning\";\n\treturn \"error\";\n}\n\n/** TUI-only card for completed hook calls and their model-visible returned prompts. */\nexport class HookExecutionComponent extends Container {\n\tprivate readonly notices: HookExecutionNotice[];\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly box: MessageCard;\n\tprivate expanded = false;\n\n\tconstructor(notice: HookExecutionNotice, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper();\n\t\tthis.notices = [notice];\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.box = new MessageCard(\n\t\t\t1,\n\t\t\t1,\n\t\t\tnotice.event === \"Stop\" || notice.event === \"StopFailure\" ? \"warning\" : undefined,\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.box);\n\t\tthis.rebuild();\n\t}\n\n\tappendNotice(notice: HookExecutionNotice): void {\n\t\tif (notice.event !== this.notices[0].event) {\n\t\t\tthrow new Error(`Cannot group ${notice.event} notice with ${this.notices[0].event}`);\n\t\t}\n\t\tthis.notices.push(notice);\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this.expanded === expanded) return;\n\t\tthis.expanded = expanded;\n\t\tthis.rebuild();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.box.clear();\n\t\tconst firstNotice = this.notices[0];\n\t\tif (!this.expanded) {\n\t\t\tthis.renderCollapsedNotices(firstNotice);\n\t\t} else if (this.notices.length === 1) {\n\t\t\tthis.renderSingleNotice(firstNotice);\n\t\t} else {\n\t\t\tthis.renderGroupedNotices(firstNotice);\n\t\t}\n\t}\n\n\tprivate renderCollapsedNotices(firstNotice: HookExecutionNotice): void {\n\t\tconst subjects = countValues(this.notices.flatMap((notice) => (notice.subject ? [notice.subject] : [])));\n\t\tconst subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\tconst title = theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`));\n\t\tthis.box.addChild(new Text(title + (subject ? theme.fg(\"muted\", ` · ${subject}`) : \"\"), 0, 0));\n\t\tconst statuses = countValues(\n\t\t\tthis.notices.flatMap((notice) =>\n\t\t\t\tnotice.calls.map((call) => {\n\t\t\t\t\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\t\t\t\t\treturn successful ? \"completed\" : call.status === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tconst summary = statuses\n\t\t\t.map(({ value, count }) => {\n\t\t\t\tconst color = value === \"completed\" ? \"success\" : value === \"cancelled\" ? \"warning\" : \"error\";\n\t\t\t\treturn theme.fg(color, `${count} ${value}`);\n\t\t\t})\n\t\t\t.join(theme.fg(\"muted\", \", \"));\n\t\tthis.box.addChild(new Text(summary || theme.fg(\"muted\", \"0 calls\"), 0, 0));\n\t\tif (keyText(\"app.tools.expand\")) this.box.addChild(new Text(keyHint(\"app.tools.expand\", \"to expand\"), 0, 0));\n\t}\n\n\tprivate renderSingleNotice(notice: HookExecutionNotice): void {\n\t\tconst subject = notice.subject ? theme.fg(\"muted\", ` · ${notice.subject}`) : \"\";\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${notice.event}`)) + subject, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const call of notice.calls.map(hookCallView)) {\n\t\t\tconst status = theme.fg(call.statusColor, call.status);\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(notice.returnedPrompts), false);\n\t}\n\n\tprivate renderGroupedNotices(firstNotice: HookExecutionNotice): void {\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));\n\t\tconst subjects = countValues(\n\t\t\tthis.notices.flatMap((notice) => (notice.subject === undefined ? [] : [notice.subject])),\n\t\t);\n\t\tif (subjects.length > 0) {\n\t\t\tconst summary = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Tools: ${summary}`), 0, 0));\n\t\t}\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const group of groupedHookCalls(this.notices)) {\n\t\t\tconst call = hookCallView(group.call);\n\t\t\tconst status = theme.fg(groupedCallStatusColor(group), groupedCallStatus(group));\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);\n\t}\n\n\tprivate renderPrompts(prompts: readonly CountedValue[], grouped: boolean): void {\n\t\tif (prompts.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tthis.box.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"warning\", theme.bold(prompts.length === 1 && !grouped ? \"Returned prompt\" : \"Returned prompts\")),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tfor (let index = 0; index < prompts.length; index++) {\n\t\t\tconst prompt = prompts[index];\n\t\t\tif (prompts.length > 1 || grouped) {\n\t\t\t\tconst count = prompt.count > 1 ? ` ×${prompt.count}` : \"\";\n\t\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Prompt ${index + 1}${count}`), 0, 0));\n\t\t\t}\n\t\t\tthis.box.addChild(\n\t\t\t\tnew Markdown(prompt.value, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"text\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"hook-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/hook-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAmGjG,uFAAuF;AACvF,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,MAAM,EAAE,mBAAmB,EAAE,aAAa,GAAE,aAAkC,EAYzF;IAED,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAM9C;IAED,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGnC;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAInC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,aAAa;CAuBrB","sourcesContent":["import { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { HookExecutionCallNotice, HookExecutionNotice } from \"../../../core/hooks/types.ts\";\nimport { getMarkdownTheme, type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { keyHint, keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\nconst MAX_CALL_LABEL_CHARS = 300;\n\ninterface HookCallView {\n\tlabel: string;\n\tsource: string;\n\tstatus: string;\n\tstatusColor: ThemeColor;\n}\n\ninterface CountedValue {\n\tvalue: string;\n\tcount: number;\n}\n\ninterface GroupedHookCall {\n\tcall: HookExecutionCallNotice;\n\tinvocationCount: number;\n\ttotalDurationMs: number;\n\tstatuses: CountedValue[];\n}\n\nfunction truncateCallLabel(label: string): string {\n\treturn label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;\n}\n\nfunction hookCallView(call: HookExecutionCallNotice): HookCallView {\n\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\tconst exit = call.exitCode === null ? \"\" : `, exit ${call.exitCode}`;\n\treturn {\n\t\tlabel: `${call.type} ${truncateCallLabel(call.label)}`,\n\t\tsource: `${call.source.kind}: ${call.source.path}`,\n\t\tstatus: `${call.status}${exit}, ${call.durationMs}ms`,\n\t\tstatusColor: successful ? \"success\" : call.status === \"cancelled\" ? \"warning\" : \"error\",\n\t};\n}\n\nfunction callIdentity(call: HookExecutionCallNotice): string {\n\treturn JSON.stringify([call.type, truncateCallLabel(call.label), call.source.kind, call.source.path]);\n}\n\nfunction statusIdentity(call: HookExecutionCallNotice): string {\n\treturn call.exitCode === null ? call.status : `${call.status} (exit ${call.exitCode})`;\n}\n\nfunction countValues(values: Iterable<string>): CountedValue[] {\n\tconst counts = new Map<string, CountedValue>();\n\tfor (const value of values) {\n\t\tconst existing = counts.get(value);\n\t\tif (existing) {\n\t\t\texisting.count += 1;\n\t\t} else {\n\t\t\tcounts.set(value, { value, count: 1 });\n\t\t}\n\t}\n\treturn [...counts.values()];\n}\n\nfunction groupedHookCalls(notices: readonly HookExecutionNotice[]): GroupedHookCall[] {\n\tconst groups = new Map<string, GroupedHookCall>();\n\tfor (const notice of notices) {\n\t\tfor (const call of notice.calls) {\n\t\t\tconst identity = callIdentity(call);\n\t\t\tconst status = statusIdentity(call);\n\t\t\tconst existing = groups.get(identity);\n\t\t\tif (existing) {\n\t\t\t\texisting.invocationCount += 1;\n\t\t\t\texisting.totalDurationMs += call.durationMs;\n\t\t\t\tconst existingStatus = existing.statuses.find((entry) => entry.value === status);\n\t\t\t\tif (existingStatus) existingStatus.count += 1;\n\t\t\t\telse existing.statuses.push({ value: status, count: 1 });\n\t\t\t} else {\n\t\t\t\tgroups.set(identity, {\n\t\t\t\t\tcall,\n\t\t\t\t\tinvocationCount: 1,\n\t\t\t\t\ttotalDurationMs: call.durationMs,\n\t\t\t\t\tstatuses: [{ value: status, count: 1 }],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn [...groups.values()];\n}\n\nfunction groupedCallStatus(group: GroupedHookCall): string {\n\tconst statuses = group.statuses.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\"; \");\n\treturn `${statuses}, ~${(group.totalDurationMs / group.invocationCount).toFixed(1)}ms`;\n}\n\nfunction groupedCallStatusColor(group: GroupedHookCall): ThemeColor {\n\tif (group.statuses.every(({ value }) => value === \"completed\" || value === \"completed (exit 0)\")) return \"success\";\n\tif (group.statuses.every(({ value }) => value.startsWith(\"cancelled\"))) return \"warning\";\n\treturn \"error\";\n}\n\n/** TUI-only card for completed hook calls and their model-visible returned prompts. */\nexport class HookExecutionComponent extends Container {\n\tprivate readonly notices: HookExecutionNotice[];\n\tprivate readonly annotations: string[] = [];\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly box: MessageCard;\n\tprivate expanded = false;\n\n\tconstructor(notice: HookExecutionNotice, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper();\n\t\tthis.notices = [notice];\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.box = new MessageCard(\n\t\t\t1,\n\t\t\t1,\n\t\t\tnotice.event === \"Stop\" || notice.event === \"StopFailure\" ? \"warning\" : undefined,\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.box);\n\t\tthis.rebuild();\n\t}\n\n\tappendNotice(notice: HookExecutionNotice): void {\n\t\tif (notice.event !== this.notices[0].event) {\n\t\t\tthrow new Error(`Cannot group ${notice.event} notice with ${this.notices[0].event}`);\n\t\t}\n\t\tthis.notices.push(notice);\n\t\tthis.rebuild();\n\t}\n\n\tappendAnnotation(text: string): void {\n\t\tthis.annotations.push(text);\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this.expanded === expanded) return;\n\t\tthis.expanded = expanded;\n\t\tthis.rebuild();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.box.clear();\n\t\tconst firstNotice = this.notices[0];\n\t\tif (!this.expanded) {\n\t\t\tthis.renderCollapsedNotices(firstNotice);\n\t\t} else if (this.notices.length === 1) {\n\t\t\tthis.renderSingleNotice(firstNotice);\n\t\t} else {\n\t\t\tthis.renderGroupedNotices(firstNotice);\n\t\t}\n\t}\n\n\tprivate renderCollapsedNotices(firstNotice: HookExecutionNotice): void {\n\t\tconst subjects = countValues(this.notices.flatMap((notice) => (notice.subject ? [notice.subject] : [])));\n\t\tconst subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\tconst title = theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`));\n\t\tthis.box.addChild(new Text(title + (subject ? theme.fg(\"muted\", ` · ${subject}`) : \"\"), 0, 0));\n\t\tif (firstNotice.event === \"Stop\" || firstNotice.event === \"StopFailure\") this.box.addChild(new Spacer(1));\n\t\tconst statuses = countValues(\n\t\t\tthis.notices.flatMap((notice) =>\n\t\t\t\tnotice.calls.map((call) => {\n\t\t\t\t\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\t\t\t\t\treturn successful ? \"completed\" : call.status === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tconst summary = statuses\n\t\t\t.map(({ value, count }) => {\n\t\t\t\tconst color = value === \"completed\" ? \"success\" : value === \"cancelled\" ? \"warning\" : \"error\";\n\t\t\t\treturn theme.fg(color, `${count} ${value}`);\n\t\t\t})\n\t\t\t.join(theme.fg(\"muted\", \", \"));\n\t\tthis.box.addChild(new Text(summary || theme.fg(\"muted\", \"0 calls\"), 0, 0));\n\t\tthis.renderAnnotations();\n\t\tif (keyText(\"app.tools.expand\")) this.box.addChild(new Text(keyHint(\"app.tools.expand\", \"to expand\"), 0, 0));\n\t}\n\n\tprivate renderSingleNotice(notice: HookExecutionNotice): void {\n\t\tconst subject = notice.subject ? theme.fg(\"muted\", ` · ${notice.subject}`) : \"\";\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${notice.event}`)) + subject, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const call of notice.calls.map(hookCallView)) {\n\t\t\tconst status = theme.fg(call.statusColor, call.status);\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(notice.returnedPrompts), false);\n\t\tthis.renderAnnotations();\n\t}\n\n\tprivate renderGroupedNotices(firstNotice: HookExecutionNotice): void {\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));\n\t\tconst subjects = countValues(\n\t\t\tthis.notices.flatMap((notice) => (notice.subject === undefined ? [] : [notice.subject])),\n\t\t);\n\t\tif (subjects.length > 0) {\n\t\t\tconst summary = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Tools: ${summary}`), 0, 0));\n\t\t}\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const group of groupedHookCalls(this.notices)) {\n\t\t\tconst call = hookCallView(group.call);\n\t\t\tconst status = theme.fg(groupedCallStatusColor(group), groupedCallStatus(group));\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);\n\t\tthis.renderAnnotations();\n\t}\n\tprivate renderAnnotations(): void {\n\t\tif (this.annotations.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tfor (const annotation of this.annotations) this.box.addChild(new Text(theme.fg(\"dim\", annotation), 0, 0));\n\t}\n\n\tprivate renderPrompts(prompts: readonly CountedValue[], grouped: boolean): void {\n\t\tif (prompts.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tthis.box.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"warning\", theme.bold(prompts.length === 1 && !grouped ? \"Returned prompt\" : \"Returned prompts\")),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tfor (let index = 0; index < prompts.length; index++) {\n\t\t\tconst prompt = prompts[index];\n\t\t\tif (prompts.length > 1 || grouped) {\n\t\t\t\tconst count = prompt.count > 1 ? ` ×${prompt.count}` : \"\";\n\t\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Prompt ${index + 1}${count}`), 0, 0));\n\t\t\t}\n\t\t\tthis.box.addChild(\n\t\t\t\tnew Markdown(prompt.value, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"text\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -77,6 +77,7 @@ function groupedCallStatusColor(group) {
77
77
  /** TUI-only card for completed hook calls and their model-visible returned prompts. */
78
78
  export class HookExecutionComponent extends Container {
79
79
  notices;
80
+ annotations = [];
80
81
  markdownTheme;
81
82
  box;
82
83
  expanded = false;
@@ -96,6 +97,10 @@ export class HookExecutionComponent extends Container {
96
97
  this.notices.push(notice);
97
98
  this.rebuild();
98
99
  }
100
+ appendAnnotation(text) {
101
+ this.annotations.push(text);
102
+ this.rebuild();
103
+ }
99
104
  setExpanded(expanded) {
100
105
  if (this.expanded === expanded)
101
106
  return;
@@ -124,6 +129,8 @@ export class HookExecutionComponent extends Container {
124
129
  const subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : ""}`).join(", ");
125
130
  const title = theme.fg("warning", theme.bold(`Hook · ${firstNotice.event}`));
126
131
  this.box.addChild(new Text(title + (subject ? theme.fg("muted", ` · ${subject}`) : ""), 0, 0));
132
+ if (firstNotice.event === "Stop" || firstNotice.event === "StopFailure")
133
+ this.box.addChild(new Spacer(1));
127
134
  const statuses = countValues(this.notices.flatMap((notice) => notice.calls.map((call) => {
128
135
  const successful = call.status === "completed" && (call.exitCode === 0 || call.exitCode === null);
129
136
  return successful ? "completed" : call.status === "cancelled" ? "cancelled" : "failed";
@@ -135,6 +142,7 @@ export class HookExecutionComponent extends Container {
135
142
  })
136
143
  .join(theme.fg("muted", ", "));
137
144
  this.box.addChild(new Text(summary || theme.fg("muted", "0 calls"), 0, 0));
145
+ this.renderAnnotations();
138
146
  if (keyText("app.tools.expand"))
139
147
  this.box.addChild(new Text(keyHint("app.tools.expand", "to expand"), 0, 0));
140
148
  }
@@ -148,6 +156,7 @@ export class HookExecutionComponent extends Container {
148
156
  this.box.addChild(new Text(theme.fg("dim", call.source), 0, 0));
149
157
  }
150
158
  this.renderPrompts(countValues(notice.returnedPrompts), false);
159
+ this.renderAnnotations();
151
160
  }
152
161
  renderGroupedNotices(firstNotice) {
153
162
  this.box.addChild(new Text(theme.fg("warning", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));
@@ -164,6 +173,14 @@ export class HookExecutionComponent extends Container {
164
173
  this.box.addChild(new Text(theme.fg("dim", call.source), 0, 0));
165
174
  }
166
175
  this.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);
176
+ this.renderAnnotations();
177
+ }
178
+ renderAnnotations() {
179
+ if (this.annotations.length === 0)
180
+ return;
181
+ this.box.addChild(new Spacer(1));
182
+ for (const annotation of this.annotations)
183
+ this.box.addChild(new Text(theme.fg("dim", annotation), 0, 0));
167
184
  }
168
185
  renderPrompts(prompts, grouped) {
169
186
  if (prompts.length === 0)