@ai-agent-forge/plugin-coding-tools 0.88.1

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 (56) hide show
  1. package/README.md +3 -0
  2. package/agent-forge.json +19 -0
  3. package/dist/bash.d.ts +49 -0
  4. package/dist/bash.d.ts.map +1 -0
  5. package/dist/bash.js +323 -0
  6. package/dist/bash.js.map +1 -0
  7. package/dist/capability.d.ts +25 -0
  8. package/dist/capability.d.ts.map +1 -0
  9. package/dist/capability.js +98 -0
  10. package/dist/capability.js.map +1 -0
  11. package/dist/config.d.ts +8 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +8 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/edit.d.ts +55 -0
  16. package/dist/edit.d.ts.map +1 -0
  17. package/dist/edit.js +308 -0
  18. package/dist/edit.js.map +1 -0
  19. package/dist/entry.d.ts +14 -0
  20. package/dist/entry.d.ts.map +1 -0
  21. package/dist/entry.js +23 -0
  22. package/dist/entry.js.map +1 -0
  23. package/dist/find.d.ts +42 -0
  24. package/dist/find.d.ts.map +1 -0
  25. package/dist/find.js +305 -0
  26. package/dist/find.js.map +1 -0
  27. package/dist/grep.d.ts +41 -0
  28. package/dist/grep.d.ts.map +1 -0
  29. package/dist/grep.js +311 -0
  30. package/dist/grep.js.map +1 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +17 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/ls.d.ts +41 -0
  36. package/dist/ls.d.ts.map +1 -0
  37. package/dist/ls.js +175 -0
  38. package/dist/ls.js.map +1 -0
  39. package/dist/powershell.d.ts +17 -0
  40. package/dist/powershell.d.ts.map +1 -0
  41. package/dist/powershell.js +42 -0
  42. package/dist/powershell.js.map +1 -0
  43. package/dist/read.d.ts +39 -0
  44. package/dist/read.d.ts.map +1 -0
  45. package/dist/read.js +288 -0
  46. package/dist/read.js.map +1 -0
  47. package/dist/types.d.ts +74 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +28 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/write.d.ts +30 -0
  52. package/dist/write.d.ts.map +1 -0
  53. package/dist/write.js +205 -0
  54. package/dist/write.js.map +1 -0
  55. package/package.json +53 -0
  56. package/plugin.json +10 -0
package/dist/read.js ADDED
@@ -0,0 +1,288 @@
1
+ // Migrated from packages/agent-forge/src/core/tools/read.ts (batch 1, copy-then-delete);
2
+ // host modules are imported through supply keys (@agent-forge/host/*).
3
+ import { constants } from "node:fs";
4
+ import { access as fsAccess, readFile as fsReadFile } from "node:fs/promises";
5
+ import { basename, dirname, isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
6
+ import { t } from "@agent-forge/host/i18n";
7
+ import { processImage } from "@agent-forge/host/image-process";
8
+ import { keyHint, keyText } from "@agent-forge/host/keybinding-hints";
9
+ import { detectSupportedImageMimeTypeFromFile } from "@agent-forge/host/mime";
10
+ import { resolveReadPathAsync, resolveToCwd } from "@agent-forge/host/path-utils";
11
+ import { formatPathRelativeToCwdOrAbsolute } from "@agent-forge/host/paths";
12
+ import { getTextOutput, renderToolPath, replaceTabs, str } from "@agent-forge/host/render-utils";
13
+ import { getLanguageFromPath, highlightCode } from "@agent-forge/host/theme";
14
+ import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, } from "@agent-forge/host/truncate";
15
+ import { Text } from "@agent-forge/tui";
16
+ import { Type } from "typebox";
17
+ import { getReadmePath } from "./config.js";
18
+ import { wrapToolDefinition } from "./types.js";
19
+ const readSchema = Type.Object({
20
+ path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
21
+ offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
22
+ limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
23
+ });
24
+ export const readToolSystemPromptContribution = {
25
+ snippet: "Read file contents",
26
+ guidelines: ["Use read to examine files instead of cat or sed."],
27
+ };
28
+ const COMPACT_RESOURCE_FILE_NAMES = new Set(["AGENTS.override.md", "AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD"]);
29
+ const defaultReadOperations = {
30
+ readFile: (path) => fsReadFile(path),
31
+ access: (path) => fsAccess(path, constants.R_OK),
32
+ detectImageMimeType: detectSupportedImageMimeTypeFromFile,
33
+ };
34
+ function formatReadLineRange(args, theme) {
35
+ if (args?.offset === undefined && args?.limit === undefined)
36
+ return "";
37
+ const startLine = args.offset ?? 1;
38
+ const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
39
+ return theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
40
+ }
41
+ function formatReadCall(args, theme, cwd) {
42
+ const pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);
43
+ return `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;
44
+ }
45
+ function trimTrailingEmptyLines(lines) {
46
+ let end = lines.length;
47
+ while (end > 0 && lines[end - 1] === "") {
48
+ end--;
49
+ }
50
+ return lines.slice(0, end);
51
+ }
52
+ function getNonVisionImageNote(model) {
53
+ if (!model || model.input.includes("image")) {
54
+ return undefined;
55
+ }
56
+ return "[Current model does not support images. The image will be omitted from this request.]";
57
+ }
58
+ function toPosixPath(filePath) {
59
+ return filePath.split(sep).join("/");
60
+ }
61
+ function getPiDocsClassification(absolutePath) {
62
+ const packageRoot = dirname(getReadmePath());
63
+ const relativePath = relative(resolvePath(packageRoot), resolvePath(absolutePath));
64
+ if (relativePath === "" ||
65
+ relativePath === ".." ||
66
+ relativePath.startsWith(`..${sep}`) ||
67
+ isAbsolute(relativePath)) {
68
+ return undefined;
69
+ }
70
+ const label = toPosixPath(relativePath);
71
+ if (label === "README.md" || label.startsWith("docs/") || label.startsWith("examples/")) {
72
+ return { kind: "docs", label };
73
+ }
74
+ return undefined;
75
+ }
76
+ function getCompactReadClassification(args, cwd) {
77
+ const rawPath = str(args?.file_path ?? args?.path);
78
+ if (!rawPath)
79
+ return undefined;
80
+ const absolutePath = resolveToCwd(rawPath, cwd);
81
+ const fileName = basename(absolutePath);
82
+ if (fileName === "SKILL.md") {
83
+ return { kind: "skill", label: basename(dirname(absolutePath)) || fileName };
84
+ }
85
+ const docsClassification = getPiDocsClassification(absolutePath);
86
+ if (docsClassification)
87
+ return docsClassification;
88
+ if (COMPACT_RESOURCE_FILE_NAMES.has(fileName)) {
89
+ return { kind: "resource", label: formatPathRelativeToCwdOrAbsolute(absolutePath, cwd) };
90
+ }
91
+ return undefined;
92
+ }
93
+ function formatCompactReadCall(classification, args, theme) {
94
+ const expandHint = theme.fg("dim", ` (${keyText("app.tools.expand")} ${t("to expand")})`);
95
+ if (classification.kind === "skill") {
96
+ return (theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m `) +
97
+ theme.fg("customMessageText", classification.label) +
98
+ formatReadLineRange(args, theme) +
99
+ expandHint);
100
+ }
101
+ return (theme.fg("toolTitle", theme.bold(`read ${classification.kind}`)) +
102
+ " " +
103
+ theme.fg("accent", classification.label) +
104
+ formatReadLineRange(args, theme) +
105
+ expandHint);
106
+ }
107
+ function formatReadResult(args, result, options, theme, showImages, _cwd, isError) {
108
+ if (!options.expanded && !isError) {
109
+ return "";
110
+ }
111
+ const rawPath = str(args?.file_path ?? args?.path);
112
+ const output = getTextOutput(result, showImages);
113
+ const lang = !isError && rawPath ? getLanguageFromPath(rawPath) : undefined;
114
+ const renderedLines = lang ? highlightCode(replaceTabs(output), lang) : output.split("\n");
115
+ const lines = trimTrailingEmptyLines(renderedLines);
116
+ const maxLines = options.expanded ? lines.length : 10;
117
+ const displayLines = lines.slice(0, maxLines);
118
+ const remaining = lines.length - maxLines;
119
+ let text = `\n${displayLines.map((line) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
120
+ if (remaining > 0) {
121
+ text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
122
+ }
123
+ const truncation = result.details?.truncation;
124
+ if (truncation?.truncated) {
125
+ if (truncation.firstLineExceedsLimit) {
126
+ text += `\n${theme.fg("warning", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;
127
+ }
128
+ else if (truncation.truncatedBy === "lines") {
129
+ text += `\n${theme.fg("warning", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;
130
+ }
131
+ else {
132
+ text += `\n${theme.fg("warning", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;
133
+ }
134
+ }
135
+ return text;
136
+ }
137
+ export function createReadToolDefinition(cwd, options) {
138
+ const autoResizeImages = options?.autoResizeImages ?? true;
139
+ const ops = options?.operations ?? defaultReadOperations;
140
+ return {
141
+ name: "read",
142
+ label: "read",
143
+ readonlySafe: true,
144
+ description: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,
145
+ promptSnippet: readToolSystemPromptContribution.snippet,
146
+ promptGuidelines: [...readToolSystemPromptContribution.guidelines],
147
+ parameters: readSchema,
148
+ constrainedSampling: { type: "json_schema", strict: "prefer" },
149
+ async execute(_toolCallId, { path, offset, limit }, signal, _onUpdate, ctx) {
150
+ return new Promise((resolve, reject) => {
151
+ if (signal?.aborted) {
152
+ reject(new Error("Operation aborted"));
153
+ return;
154
+ }
155
+ let aborted = false;
156
+ const onAbort = () => {
157
+ aborted = true;
158
+ reject(new Error("Operation aborted"));
159
+ };
160
+ signal?.addEventListener("abort", onAbort, { once: true });
161
+ (async () => {
162
+ try {
163
+ const absolutePath = await resolveReadPathAsync(path, ctx?.cwd || cwd);
164
+ if (aborted)
165
+ return;
166
+ // Check if file exists and is readable.
167
+ await ops.access(absolutePath);
168
+ if (aborted)
169
+ return;
170
+ const mimeType = ops.detectImageMimeType ? await ops.detectImageMimeType(absolutePath) : undefined;
171
+ let content;
172
+ let details;
173
+ const nonVisionImageNote = getNonVisionImageNote(ctx?.model);
174
+ if (mimeType) {
175
+ // Read image as binary.
176
+ const buffer = await ops.readFile(absolutePath);
177
+ const processed = await processImage(buffer, mimeType, { autoResizeImages });
178
+ if (!processed.ok) {
179
+ let textNote = `Read image file [${mimeType}]\n${processed.message}`;
180
+ if (nonVisionImageNote)
181
+ textNote += `\n${nonVisionImageNote}`;
182
+ content = [{ type: "text", text: textNote }];
183
+ }
184
+ else {
185
+ let textNote = `Read image file [${processed.mimeType}]`;
186
+ if (processed.hints.length > 0)
187
+ textNote += `\n${processed.hints.join("\n")}`;
188
+ if (nonVisionImageNote)
189
+ textNote += `\n${nonVisionImageNote}`;
190
+ content = [
191
+ { type: "text", text: textNote },
192
+ { type: "image", data: processed.data, mimeType: processed.mimeType },
193
+ ];
194
+ }
195
+ }
196
+ else {
197
+ // Read text content.
198
+ const buffer = await ops.readFile(absolutePath);
199
+ const textContent = buffer.toString("utf-8");
200
+ const allLines = textContent.split("\n");
201
+ const totalFileLines = allLines.length;
202
+ // Apply offset if specified. Convert from 1-indexed input to 0-indexed array access.
203
+ const startLine = offset ? Math.max(0, offset - 1) : 0;
204
+ const startLineDisplay = startLine + 1;
205
+ // Check if offset is out of bounds.
206
+ if (startLine >= allLines.length) {
207
+ throw new Error(t("Offset {offset} is beyond end of file ({totalLines} lines total)", {
208
+ offset: offset ?? 1,
209
+ totalLines: allLines.length,
210
+ }));
211
+ }
212
+ let selectedContent;
213
+ let userLimitedLines;
214
+ // If limit is specified by the user, honor it first. Otherwise truncateHead decides.
215
+ if (limit !== undefined) {
216
+ const endLine = Math.min(startLine + limit, allLines.length);
217
+ selectedContent = allLines.slice(startLine, endLine).join("\n");
218
+ userLimitedLines = endLine - startLine;
219
+ }
220
+ else {
221
+ selectedContent = allLines.slice(startLine).join("\n");
222
+ }
223
+ // Apply truncation, respecting both line and byte limits.
224
+ const truncation = truncateHead(selectedContent);
225
+ let outputText;
226
+ if (truncation.firstLineExceedsLimit) {
227
+ // First line alone exceeds the byte limit. Point the model at a bash fallback.
228
+ const firstLineSize = formatSize(Buffer.byteLength(allLines[startLine], "utf-8"));
229
+ outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;
230
+ details = { truncation };
231
+ }
232
+ else if (truncation.truncated) {
233
+ // Truncation occurred. Build an actionable continuation notice.
234
+ const endLineDisplay = startLineDisplay + truncation.outputLines - 1;
235
+ const nextOffset = endLineDisplay + 1;
236
+ outputText = truncation.content;
237
+ if (truncation.truncatedBy === "lines") {
238
+ outputText += `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;
239
+ }
240
+ else {
241
+ outputText += `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
242
+ }
243
+ details = { truncation };
244
+ }
245
+ else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {
246
+ // User-specified limit stopped early, but the file still has more content.
247
+ const remaining = allLines.length - (startLine + userLimitedLines);
248
+ const nextOffset = startLine + userLimitedLines + 1;
249
+ outputText = `${truncation.content}\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;
250
+ }
251
+ else {
252
+ // No truncation and no remaining user-limited content.
253
+ outputText = truncation.content;
254
+ }
255
+ content = [{ type: "text", text: outputText }];
256
+ }
257
+ if (aborted)
258
+ return;
259
+ signal?.removeEventListener("abort", onAbort);
260
+ resolve({ content, details });
261
+ }
262
+ catch (error) {
263
+ signal?.removeEventListener("abort", onAbort);
264
+ if (!aborted)
265
+ reject(error);
266
+ }
267
+ })();
268
+ });
269
+ },
270
+ renderCall(args, theme, context) {
271
+ const text = context.lastComponent ?? new Text("", 0, 0);
272
+ const classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;
273
+ text.setText(classification
274
+ ? formatCompactReadCall(classification, args, theme)
275
+ : formatReadCall(args, theme, context.cwd));
276
+ return text;
277
+ },
278
+ renderResult(result, options, theme, context) {
279
+ const text = context.lastComponent ?? new Text("", 0, 0);
280
+ text.setText(formatReadResult(context.args, result, options, theme, context.showImages, context.cwd, context.isError));
281
+ return text;
282
+ },
283
+ };
284
+ }
285
+ export function createReadTool(cwd, options) {
286
+ return wrapToolDefinition(createReadToolDefinition(cwd, options));
287
+ }
288
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../src/read.ts"],"names":[],"mappings":"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","sourcesContent":["// Migrated from packages/agent-forge/src/core/tools/read.ts (batch 1, copy-then-delete);\n// host modules are imported through supply keys (@agent-forge/host/*).\n\nimport { constants } from \"node:fs\";\nimport { access as fsAccess, readFile as fsReadFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, relative, resolve as resolvePath, sep } from \"node:path\";\nimport { t } from \"@agent-forge/host/i18n\";\nimport { processImage } from \"@agent-forge/host/image-process\";\nimport { keyHint, keyText } from \"@agent-forge/host/keybinding-hints\";\nimport { detectSupportedImageMimeTypeFromFile } from \"@agent-forge/host/mime\";\nimport { resolveReadPathAsync, resolveToCwd } from \"@agent-forge/host/path-utils\";\nimport { formatPathRelativeToCwdOrAbsolute } from \"@agent-forge/host/paths\";\nimport { getTextOutput, renderToolPath, replaceTabs, str } from \"@agent-forge/host/render-utils\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"@agent-forge/host/theme\";\nimport type { ToolDefinition, ToolExecuteContext, ToolRenderResultOptions } from \"@agent-forge/host/tool-definition\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationResult,\n\ttruncateHead,\n} from \"@agent-forge/host/truncate\";\nimport { Text } from \"@agent-forge/tui\";\nimport { type Static, Type } from \"typebox\";\nimport { getReadmePath } from \"./config.ts\";\nimport type { AgentTool, ImageContent, TextContent, ToolModel } from \"./types.ts\";\nimport { wrapToolDefinition } from \"./types.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 const readToolSystemPromptContribution = {\n\tsnippet: \"Read file contents\",\n\tguidelines: [\"Use read to examine files instead of cat or sed.\"],\n} as const;\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\nexport interface ReadToolDetails {\n\ttruncation?: TruncationResult;\n}\n\ninterface CompactReadClassification {\n\tkind: \"docs\" | \"resource\" | \"skill\";\n\tlabel: string;\n}\n\nconst COMPACT_RESOURCE_FILE_NAMES = new Set([\"AGENTS.override.md\", \"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\"]);\n\n/**\n * Pluggable operations for the read tool.\n * Override these to delegate file reading to remote systems (for example SSH).\n */\nexport interface ReadOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Check if file is readable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n\t/** Detect image MIME type, return null or undefined for non-images */\n\tdetectImageMimeType?: (absolutePath: string) => Promise<string | null | undefined>;\n}\n\nconst defaultReadOperations: ReadOperations = {\n\treadFile: (path) => fsReadFile(path),\n\taccess: (path) => fsAccess(path, constants.R_OK),\n\tdetectImageMimeType: detectSupportedImageMimeTypeFromFile,\n};\n\nexport interface ReadToolOptions {\n\t/** Whether to auto-resize images to 2000x2000 max. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Custom operations for file reading. Default: local filesystem */\n\toperations?: ReadOperations;\n}\n\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, cwd: string): string {\n\tconst pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);\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: ToolModel | 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 getPiDocsClassification(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\n\tconst docsClassification = getPiDocsClassification(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\")} ${t(\"to expand\")})`);\n\tif (classification.kind === \"skill\") {\n\t\treturn (\n\t\t\ttheme.fg(\"customMessageLabel\", `\\x1b[1m[skill]\\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 formatReadResult(\n\targs: ReadRenderArgs | undefined,\n\tresult: { content: (TextContent | ImageContent)[]; details?: ReadToolDetails },\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tshowImages: boolean,\n\t_cwd: string,\n\tisError: boolean,\n): string {\n\tif (!options.expanded && !isError) {\n\t\treturn \"\";\n\t}\n\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst output = getTextOutput(result, showImages);\n\tconst lang = !isError && 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\")}${theme.fg(\"muted\", \")\")}`;\n\t}\n\n\tconst truncation = result.details?.truncation;\n\tif (truncation?.truncated) {\n\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;\n\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;\n\t\t} else {\n\t\t\ttext += `\\n${theme.fg(\"warning\", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;\n\t\t}\n\t}\n\treturn text;\n}\n\nexport function createReadToolDefinition(\n\tcwd: string,\n\toptions?: ReadToolOptions,\n): ToolDefinition<typeof readSchema, ReadToolDetails | undefined> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst ops = options?.operations ?? defaultReadOperations;\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\treadonlySafe: true,\n\t\tdescription: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,\n\t\tpromptSnippet: readToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...readToolSystemPromptContribution.guidelines],\n\t\tparameters: readSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, offset, limit }: { path: string; offset?: number; limit?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\tctx?: ToolExecuteContext,\n\t\t) {\n\t\t\treturn new Promise<{ content: (TextContent | ImageContent)[]; details: ReadToolDetails | undefined }>(\n\t\t\t\t(resolve, reject) => {\n\t\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet aborted = false;\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\taborted = true;\n\t\t\t\t\t\treject(new Error(\"Operation aborted\"));\n\t\t\t\t\t};\n\t\t\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst absolutePath = await resolveReadPathAsync(path, ctx?.cwd || cwd);\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\t// Check if file exists and is readable.\n\t\t\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tconst mimeType = ops.detectImageMimeType ? await ops.detectImageMimeType(absolutePath) : undefined;\n\t\t\t\t\t\t\tlet content: (TextContent | ImageContent)[];\n\t\t\t\t\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\t\t\t\t\tconst nonVisionImageNote = getNonVisionImageNote(ctx?.model);\n\t\t\t\t\t\t\tif (mimeType) {\n\t\t\t\t\t\t\t\t// Read image as binary.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst processed = await processImage(buffer, mimeType, { autoResizeImages });\n\t\t\t\t\t\t\t\tif (!processed.ok) {\n\t\t\t\t\t\t\t\t\tlet textNote = `Read image file [${mimeType}]\\n${processed.message}`;\n\t\t\t\t\t\t\t\t\tif (nonVisionImageNote) textNote += `\\n${nonVisionImageNote}`;\n\t\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: textNote }];\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tlet textNote = `Read image file [${processed.mimeType}]`;\n\t\t\t\t\t\t\t\t\tif (processed.hints.length > 0) textNote += `\\n${processed.hints.join(\"\\n\")}`;\n\t\t\t\t\t\t\t\t\tif (nonVisionImageNote) textNote += `\\n${nonVisionImageNote}`;\n\t\t\t\t\t\t\t\t\tcontent = [\n\t\t\t\t\t\t\t\t\t\t{ type: \"text\", text: textNote },\n\t\t\t\t\t\t\t\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Read text content.\n\t\t\t\t\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\t\t\t\t\tconst textContent = buffer.toString(\"utf-8\");\n\t\t\t\t\t\t\t\tconst allLines = textContent.split(\"\\n\");\n\t\t\t\t\t\t\t\tconst totalFileLines = allLines.length;\n\t\t\t\t\t\t\t\t// Apply offset if specified. Convert from 1-indexed input to 0-indexed array access.\n\t\t\t\t\t\t\t\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\t\t\t\t\t\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\t\t\t\t\t\t// Check if offset is out of bounds.\n\t\t\t\t\t\t\t\tif (startLine >= allLines.length) {\n\t\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t\tt(\"Offset {offset} is beyond end of file ({totalLines} lines total)\", {\n\t\t\t\t\t\t\t\t\t\t\toffset: offset ?? 1,\n\t\t\t\t\t\t\t\t\t\t\ttotalLines: allLines.length,\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlet selectedContent: string;\n\t\t\t\t\t\t\t\tlet userLimitedLines: number | undefined;\n\t\t\t\t\t\t\t\t// If limit is specified by the user, honor it first. Otherwise truncateHead decides.\n\t\t\t\t\t\t\t\tif (limit !== undefined) {\n\t\t\t\t\t\t\t\t\tconst endLine = Math.min(startLine + limit, allLines.length);\n\t\t\t\t\t\t\t\t\tselectedContent = allLines.slice(startLine, endLine).join(\"\\n\");\n\t\t\t\t\t\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tselectedContent = allLines.slice(startLine).join(\"\\n\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t// Apply truncation, respecting both line and byte limits.\n\t\t\t\t\t\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\t\t\t\t\t\tlet outputText: string;\n\t\t\t\t\t\t\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\t\t\t\t\t\t\t// First line alone exceeds the byte limit. Point the model at a bash fallback.\n\t\t\t\t\t\t\t\t\tconst firstLineSize = formatSize(Buffer.byteLength(allLines[startLine], \"utf-8\"));\n\t\t\t\t\t\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\t\t\t\t\t\tdetails = { truncation };\n\t\t\t\t\t\t\t\t} else if (truncation.truncated) {\n\t\t\t\t\t\t\t\t\t// Truncation occurred. Build an actionable continuation notice.\n\t\t\t\t\t\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\t\t\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\t\t\t\t\t\toutputText = truncation.content;\n\t\t\t\t\t\t\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdetails = { truncation };\n\t\t\t\t\t\t\t\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\t\t\t\t\t\t// User-specified limit stopped early, but the file still has more content.\n\t\t\t\t\t\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\t\t\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\t\t\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// No truncation and no remaining user-limited content.\n\t\t\t\t\t\t\t\t\toutputText = truncation.content;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcontent = [{ type: \"text\", text: outputText }];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (aborted) return;\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tresolve({ content, details });\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\t\t\t\tif (!aborted) reject(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\tconst classification = !context.expanded ? getCompactReadClassification(args, context.cwd) : undefined;\n\t\t\ttext.setText(\n\t\t\t\tclassification\n\t\t\t\t\t? formatCompactReadCall(classification, args, theme)\n\t\t\t\t\t: formatReadCall(args, theme, context.cwd),\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(cwd: string, options?: ReadToolOptions): AgentTool<typeof readSchema> {\n\treturn wrapToolDefinition(createReadToolDefinition(cwd, options));\n}\n"]}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Plugin-local structural types for the coding tools plugin.
3
+ *
4
+ * Migrated from `packages/agent-forge/src/core/tools/` (batch 1, copy-then-delete).
5
+ * The host's `ToolDefinition` contract is imported (type-only) from the host
6
+ * supply module `@agent-forge/host/tool-definition`; this file only carries the
7
+ * auxiliary types that contract reaches into from `@agent-forge/agent-core` and
8
+ * `@agent-forge/ai` — replaced by structural equivalents so the package stays
9
+ * independent of the agent runtime and AI provider packages.
10
+ */
11
+ import type { ToolDefinition, ToolExecuteContext } from "@agent-forge/host/tool-definition";
12
+ import type { Static, TSchema } from "typebox";
13
+ /** Minimal structural TextContent (mirrors the AI package's TextContent face). */
14
+ export interface TextContent {
15
+ type: "text";
16
+ text: string;
17
+ textSignature?: string;
18
+ }
19
+ /** Minimal structural ImageContent (mirrors the AI package's ImageContent face). */
20
+ export interface ImageContent {
21
+ type: "image";
22
+ data: string;
23
+ mimeType: string;
24
+ }
25
+ /** Content blocks a tool result may return to the model. */
26
+ export type ToolResultContent = TextContent | ImageContent;
27
+ /** Final or partial result produced by a tool (structural AgentToolResult subset). */
28
+ export interface AgentToolResult<T> {
29
+ content: ToolResultContent[];
30
+ details: T;
31
+ }
32
+ /** Callback used by tools to stream partial execution updates. */
33
+ export type AgentToolUpdateCallback<T = unknown> = (partialResult: AgentToolResult<T>) => void;
34
+ /** Minimal structural stand-in for the model facts a tool execution reads. */
35
+ export type ToolModel = {
36
+ provider: string;
37
+ id: string;
38
+ input: readonly string[];
39
+ };
40
+ /** Per-tool execution mode override. */
41
+ export type ToolExecutionMode = "sequential" | "parallel";
42
+ /**
43
+ * Provider-side constrained sampling request. The grammar arm stays opaque:
44
+ * this plugin never constructs one locally.
45
+ */
46
+ export type ToolConstrainedSamplingConfig = {
47
+ type: "json_schema";
48
+ strict: "prefer" | "require";
49
+ } | {
50
+ type: "grammar";
51
+ variants: unknown;
52
+ };
53
+ /**
54
+ * Structural tool face for the agent runtime (structural equivalent of the
55
+ * agent-core `AgentTool`): a ToolDefinition projected to the runtime face,
56
+ * without prompt metadata and renderers.
57
+ */
58
+ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unknown> {
59
+ name: string;
60
+ label: string;
61
+ description: string;
62
+ parameters: TParameters;
63
+ constrainedSampling?: false | ToolConstrainedSamplingConfig;
64
+ prepareArguments?: (args: unknown) => Static<TParameters>;
65
+ executionMode?: ToolExecutionMode;
66
+ execute: (toolCallId: string, params: Static<TParameters>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<TDetails>) => Promise<AgentToolResult<TDetails>>;
67
+ }
68
+ /**
69
+ * Plugin-local equivalent of the host's `wrapToolDefinition` (the host wrapper
70
+ * is not part of the supply base): projects a ToolDefinition onto the
71
+ * structural AgentTool face for the core runtime.
72
+ */
73
+ export declare function wrapToolDefinition<TParams extends TSchema, TDetails = unknown>(definition: ToolDefinition<TParams, TDetails>, _ctxFactory?: () => ToolExecuteContext): AgentTool<TParams, TDetails>;
74
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAE/C,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,4DAA4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,YAAY,CAAC;AAE3D,sFAAsF;AACtF,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,OAAO,EAAE,CAAC,CAAC;CACX;AAED,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAE/F,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,wCAAwC;AACxC,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,UAAU,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GACtC;IACA,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC5B,GACD;IACA,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACjB,CAAC;AAEL;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,WAAW,SAAS,OAAO,GAAG,OAAO,EAAE,QAAQ,GAAG,OAAO;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,WAAW,CAAC;IACxB,mBAAmB,CAAC,EAAE,KAAK,GAAG,6BAA6B,CAAC;IAC5D,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1D,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,OAAO,EAAE,CACR,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,EAC3B,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,uBAAuB,CAAC,QAAQ,CAAC,KACxC,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,OAAO,EAAE,QAAQ,GAAG,OAAO,EAC7E,UAAU,EAAE,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,EAC7C,WAAW,CAAC,EAAE,MAAM,kBAAkB,GACpC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAY9B","sourcesContent":["/**\n * Plugin-local structural types for the coding tools plugin.\n *\n * Migrated from `packages/agent-forge/src/core/tools/` (batch 1, copy-then-delete).\n * The host's `ToolDefinition` contract is imported (type-only) from the host\n * supply module `@agent-forge/host/tool-definition`; this file only carries the\n * auxiliary types that contract reaches into from `@agent-forge/agent-core` and\n * `@agent-forge/ai` — replaced by structural equivalents so the package stays\n * independent of the agent runtime and AI provider packages.\n */\n\nimport type { ToolDefinition, ToolExecuteContext } from \"@agent-forge/host/tool-definition\";\nimport type { Static, TSchema } from \"typebox\";\n\n/** Minimal structural TextContent (mirrors the AI package's TextContent face). */\nexport interface TextContent {\n\ttype: \"text\";\n\ttext: string;\n\ttextSignature?: string;\n}\n\n/** Minimal structural ImageContent (mirrors the AI package's ImageContent face). */\nexport interface ImageContent {\n\ttype: \"image\";\n\tdata: string;\n\tmimeType: string;\n}\n\n/** Content blocks a tool result may return to the model. */\nexport type ToolResultContent = TextContent | ImageContent;\n\n/** Final or partial result produced by a tool (structural AgentToolResult subset). */\nexport interface AgentToolResult<T> {\n\tcontent: ToolResultContent[];\n\tdetails: T;\n}\n\n/** Callback used by tools to stream partial execution updates. */\nexport type AgentToolUpdateCallback<T = unknown> = (partialResult: AgentToolResult<T>) => void;\n\n/** Minimal structural stand-in for the model facts a tool execution reads. */\nexport type ToolModel = {\n\tprovider: string;\n\tid: string;\n\tinput: readonly string[];\n};\n\n/** Per-tool execution mode override. */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/**\n * Provider-side constrained sampling request. The grammar arm stays opaque:\n * this plugin never constructs one locally.\n */\nexport type ToolConstrainedSamplingConfig =\n\t| {\n\t\t\ttype: \"json_schema\";\n\t\t\tstrict: \"prefer\" | \"require\";\n\t }\n\t| {\n\t\t\ttype: \"grammar\";\n\t\t\tvariants: unknown;\n\t };\n\n/**\n * Structural tool face for the agent runtime (structural equivalent of the\n * agent-core `AgentTool`): a ToolDefinition projected to the runtime face,\n * without prompt metadata and renderers.\n */\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unknown> {\n\tname: string;\n\tlabel: string;\n\tdescription: string;\n\tparameters: TParameters;\n\tconstrainedSampling?: false | ToolConstrainedSamplingConfig;\n\tprepareArguments?: (args: unknown) => Static<TParameters>;\n\texecutionMode?: ToolExecutionMode;\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n}\n\n/**\n * Plugin-local equivalent of the host's `wrapToolDefinition` (the host wrapper\n * is not part of the supply base): projects a ToolDefinition onto the\n * structural AgentTool face for the core runtime.\n */\nexport function wrapToolDefinition<TParams extends TSchema, TDetails = unknown>(\n\tdefinition: ToolDefinition<TParams, TDetails>,\n\t_ctxFactory?: () => ToolExecuteContext,\n): AgentTool<TParams, TDetails> {\n\treturn {\n\t\tname: definition.name,\n\t\tlabel: definition.label,\n\t\tdescription: definition.description,\n\t\tparameters: definition.parameters,\n\t\tconstrainedSampling: definition.constrainedSampling,\n\t\tprepareArguments: definition.prepareArguments,\n\t\texecutionMode: definition.executionMode,\n\t\texecute: (toolCallId, params, signal, onUpdate) =>\n\t\t\tdefinition.execute(toolCallId, params, signal, onUpdate, _ctxFactory?.() as ToolExecuteContext),\n\t};\n}\n"]}
package/dist/types.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Plugin-local structural types for the coding tools plugin.
3
+ *
4
+ * Migrated from `packages/agent-forge/src/core/tools/` (batch 1, copy-then-delete).
5
+ * The host's `ToolDefinition` contract is imported (type-only) from the host
6
+ * supply module `@agent-forge/host/tool-definition`; this file only carries the
7
+ * auxiliary types that contract reaches into from `@agent-forge/agent-core` and
8
+ * `@agent-forge/ai` — replaced by structural equivalents so the package stays
9
+ * independent of the agent runtime and AI provider packages.
10
+ */
11
+ /**
12
+ * Plugin-local equivalent of the host's `wrapToolDefinition` (the host wrapper
13
+ * is not part of the supply base): projects a ToolDefinition onto the
14
+ * structural AgentTool face for the core runtime.
15
+ */
16
+ export function wrapToolDefinition(definition, _ctxFactory) {
17
+ return {
18
+ name: definition.name,
19
+ label: definition.label,
20
+ description: definition.description,
21
+ parameters: definition.parameters,
22
+ constrainedSampling: definition.constrainedSampling,
23
+ prepareArguments: definition.prepareArguments,
24
+ executionMode: definition.executionMode,
25
+ execute: (toolCallId, params, signal, onUpdate) => definition.execute(toolCallId, params, signal, onUpdate, _ctxFactory?.()),
26
+ };
27
+ }
28
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA4EH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CACjC,UAA6C,EAC7C,WAAsC,EACP;IAC/B,OAAO;QACN,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,mBAAmB,EAAE,UAAU,CAAC,mBAAmB;QACnD,gBAAgB,EAAE,UAAU,CAAC,gBAAgB;QAC7C,aAAa,EAAE,UAAU,CAAC,aAAa;QACvC,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CACjD,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAwB,CAAC;KAChG,CAAC;AAAA,CACF","sourcesContent":["/**\n * Plugin-local structural types for the coding tools plugin.\n *\n * Migrated from `packages/agent-forge/src/core/tools/` (batch 1, copy-then-delete).\n * The host's `ToolDefinition` contract is imported (type-only) from the host\n * supply module `@agent-forge/host/tool-definition`; this file only carries the\n * auxiliary types that contract reaches into from `@agent-forge/agent-core` and\n * `@agent-forge/ai` — replaced by structural equivalents so the package stays\n * independent of the agent runtime and AI provider packages.\n */\n\nimport type { ToolDefinition, ToolExecuteContext } from \"@agent-forge/host/tool-definition\";\nimport type { Static, TSchema } from \"typebox\";\n\n/** Minimal structural TextContent (mirrors the AI package's TextContent face). */\nexport interface TextContent {\n\ttype: \"text\";\n\ttext: string;\n\ttextSignature?: string;\n}\n\n/** Minimal structural ImageContent (mirrors the AI package's ImageContent face). */\nexport interface ImageContent {\n\ttype: \"image\";\n\tdata: string;\n\tmimeType: string;\n}\n\n/** Content blocks a tool result may return to the model. */\nexport type ToolResultContent = TextContent | ImageContent;\n\n/** Final or partial result produced by a tool (structural AgentToolResult subset). */\nexport interface AgentToolResult<T> {\n\tcontent: ToolResultContent[];\n\tdetails: T;\n}\n\n/** Callback used by tools to stream partial execution updates. */\nexport type AgentToolUpdateCallback<T = unknown> = (partialResult: AgentToolResult<T>) => void;\n\n/** Minimal structural stand-in for the model facts a tool execution reads. */\nexport type ToolModel = {\n\tprovider: string;\n\tid: string;\n\tinput: readonly string[];\n};\n\n/** Per-tool execution mode override. */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/**\n * Provider-side constrained sampling request. The grammar arm stays opaque:\n * this plugin never constructs one locally.\n */\nexport type ToolConstrainedSamplingConfig =\n\t| {\n\t\t\ttype: \"json_schema\";\n\t\t\tstrict: \"prefer\" | \"require\";\n\t }\n\t| {\n\t\t\ttype: \"grammar\";\n\t\t\tvariants: unknown;\n\t };\n\n/**\n * Structural tool face for the agent runtime (structural equivalent of the\n * agent-core `AgentTool`): a ToolDefinition projected to the runtime face,\n * without prompt metadata and renderers.\n */\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unknown> {\n\tname: string;\n\tlabel: string;\n\tdescription: string;\n\tparameters: TParameters;\n\tconstrainedSampling?: false | ToolConstrainedSamplingConfig;\n\tprepareArguments?: (args: unknown) => Static<TParameters>;\n\texecutionMode?: ToolExecutionMode;\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n}\n\n/**\n * Plugin-local equivalent of the host's `wrapToolDefinition` (the host wrapper\n * is not part of the supply base): projects a ToolDefinition onto the\n * structural AgentTool face for the core runtime.\n */\nexport function wrapToolDefinition<TParams extends TSchema, TDetails = unknown>(\n\tdefinition: ToolDefinition<TParams, TDetails>,\n\t_ctxFactory?: () => ToolExecuteContext,\n): AgentTool<TParams, TDetails> {\n\treturn {\n\t\tname: definition.name,\n\t\tlabel: definition.label,\n\t\tdescription: definition.description,\n\t\tparameters: definition.parameters,\n\t\tconstrainedSampling: definition.constrainedSampling,\n\t\tprepareArguments: definition.prepareArguments,\n\t\texecutionMode: definition.executionMode,\n\t\texecute: (toolCallId, params, signal, onUpdate) =>\n\t\t\tdefinition.execute(toolCallId, params, signal, onUpdate, _ctxFactory?.() as ToolExecuteContext),\n\t};\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import type { ToolDefinition } from "@agent-forge/host/tool-definition";
2
+ import { type Static, Type } from "typebox";
3
+ import { type AgentTool } from "./types.ts";
4
+ declare const writeSchema: Type.TObject<{
5
+ path: Type.TString;
6
+ content: Type.TString;
7
+ }>;
8
+ export declare const writeToolSystemPromptContribution: {
9
+ readonly snippet: "Create or overwrite files";
10
+ readonly guidelines: readonly ["Use write only for new files or complete rewrites."];
11
+ };
12
+ export type WriteToolInput = Static<typeof writeSchema>;
13
+ /**
14
+ * Pluggable operations for the write tool.
15
+ * Override these to delegate file writing to remote systems (for example SSH).
16
+ */
17
+ export interface WriteOperations {
18
+ /** Write content to a file */
19
+ writeFile: (absolutePath: string, content: string) => Promise<void>;
20
+ /** Create directory recursively */
21
+ mkdir: (dir: string) => Promise<void>;
22
+ }
23
+ export interface WriteToolOptions {
24
+ /** Custom operations for file writing. Default: local filesystem */
25
+ operations?: WriteOperations;
26
+ }
27
+ export declare function createWriteToolDefinition(cwd: string, options?: WriteToolOptions): ToolDefinition<typeof writeSchema, undefined>;
28
+ export declare function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema>;
29
+ export {};
30
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAA+C,MAAM,mCAAmC,CAAC;AAErH,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,SAAS,EAAsB,MAAM,YAAY,CAAC;AAEhE,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,eAAO,MAAM,iCAAiC;;;CAGpC,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mCAAmC;IACnC,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAOD,MAAM,WAAW,gBAAgB;IAChC,oEAAoE;IACpE,UAAU,CAAC,EAAE,eAAe,CAAC;CAC7B;AA6ID,wBAAgB,yBAAyB,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,GACxB,cAAc,CAAC,OAAO,WAAW,EAAE,SAAS,CAAC,CAgF/C;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC,OAAO,WAAW,CAAC,CAEtG","sourcesContent":["// Migrated from packages/agent-forge/src/core/tools/write (batch 1, copy-then-delete);\n// host modules are imported through supply keys (@agent-forge/host/*).\n\nimport { mkdir as fsMkdir, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { withFileMutationQueue } from \"@agent-forge/host/file-mutation-queue\";\nimport { t } from \"@agent-forge/host/i18n\";\nimport { keyHint } from \"@agent-forge/host/keybinding-hints\";\nimport { resolveToCwd } from \"@agent-forge/host/path-utils\";\nimport { normalizeDisplayText, renderToolPath, replaceTabs, str } from \"@agent-forge/host/render-utils\";\nimport { getLanguageFromPath, highlightCode, type Theme } from \"@agent-forge/host/theme\";\nimport type { ToolDefinition, ToolExecuteContext, ToolRenderResultOptions } from \"@agent-forge/host/tool-definition\";\nimport { Container, Text } from \"@agent-forge/tui\";\nimport { type Static, Type } from \"typebox\";\nimport { type AgentTool, wrapToolDefinition } from \"./types.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 const writeToolSystemPromptContribution = {\n\tsnippet: \"Create or overwrite files\",\n\tguidelines: [\"Use write only for new files or complete rewrites.\"],\n} as const;\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\n/**\n * Pluggable operations for the write tool.\n * Override these to delegate file writing to remote systems (for example SSH).\n */\nexport interface WriteOperations {\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Create directory recursively */\n\tmkdir: (dir: string) => Promise<void>;\n}\n\nconst defaultWriteOperations: WriteOperations = {\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\tmkdir: (dir) => fsMkdir(dir, { recursive: true }).then(() => {}),\n};\n\nexport interface WriteToolOptions {\n\t/** Custom operations for file writing. Default: local filesystem */\n\toperations?: WriteOperations;\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 formatWriteCall(\n\targs: { path?: string; file_path?: string; content?: string } | undefined,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tcache: WriteHighlightCache | undefined,\n\tcwd: string,\n): string {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst fileContent = str(args?.content);\n\tconst pathDisplay = renderToolPath(rawPath, theme, cwd);\n\tlet text = `${theme.fg(\"toolTitle\", theme.bold(\"write\"))} ${pathDisplay}`;\n\n\tif (fileContent === null) {\n\t\ttext += `\\n\\n${theme.fg(\"error\", \"[invalid content arg - expected string]\")}`;\n\t} else if (fileContent) {\n\t\tconst lang = rawPath ? getLanguageFromPath(rawPath) : undefined;\n\t\tconst renderedLines = lang\n\t\t\t? (cache?.highlightedLines ?? highlightCode(replaceTabs(normalizeDisplayText(fileContent)), lang))\n\t\t\t: normalizeDisplayText(fileContent).split(\"\\n\");\n\t\tconst lines = trimTrailingEmptyLines(renderedLines);\n\t\tconst totalLines = lines.length;\n\t\tconst maxLines = options.expanded ? lines.length : 10;\n\t\tconst displayLines = lines.slice(0, maxLines);\n\t\tconst remaining = lines.length - maxLines;\n\t\ttext += `\\n\\n${displayLines.map((line) => (lang ? line : theme.fg(\"toolOutput\", replaceTabs(line)))).join(\"\\n\")}`;\n\t\tif (remaining > 0) {\n\t\t\ttext += `${theme.fg(\"muted\", `\\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t}\n\t}\n\n\treturn text;\n}\n\nfunction formatWriteResult(\n\tresult: { content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>; isError?: boolean },\n\ttheme: Theme,\n): 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 function createWriteToolDefinition(\n\tcwd: string,\n\toptions?: WriteToolOptions,\n): ToolDefinition<typeof writeSchema, undefined> {\n\tconst ops = options?.operations ?? defaultWriteOperations;\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: writeToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...writeToolSystemPromptContribution.guidelines],\n\t\tparameters: writeSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ path, content }: { path: string; content: string },\n\t\t\tsignal?: AbortSignal,\n\t\t\t_onUpdate?,\n\t\t\tctx?: ToolExecuteContext,\n\t\t) {\n\t\t\tconst absolutePath = resolveToCwd(path, ctx?.cwd || cwd);\n\t\t\tconst dir = dirname(absolutePath);\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(t(\"Operation aborted\"));\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\t\t\t\t// Create parent directories if needed.\n\t\t\t\tawait ops.mkdir(dir);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Write the file contents.\n\t\t\t\tawait ops.writeFile(absolutePath, content);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully wrote to ${path}` }],\n\t\t\t\t\tdetails: undefined,\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 { path?: string; file_path?: string; content?: string } | 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\tformatWriteCall(\n\t\t\t\t\trenderArgs,\n\t\t\t\t\t{ expanded: context.expanded, isPartial: context.isPartial },\n\t\t\t\t\ttheme,\n\t\t\t\t\tcomponent.cache,\n\t\t\t\t\tcontext.cwd,\n\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(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema> {\n\treturn wrapToolDefinition(createWriteToolDefinition(cwd, options));\n}\n"]}