indusagi-coding-agent 0.1.25 → 0.1.27

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 (95) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/dist/cli/file-processor.js +1 -1
  3. package/dist/cli/file-processor.js.map +1 -1
  4. package/dist/core/agent-session.d.ts +1 -1
  5. package/dist/core/agent-session.d.ts.map +1 -1
  6. package/dist/core/agent-session.js +3 -3
  7. package/dist/core/agent-session.js.map +1 -1
  8. package/dist/core/bash-executor.d.ts +1 -1
  9. package/dist/core/bash-executor.d.ts.map +1 -1
  10. package/dist/core/bash-executor.js +1 -1
  11. package/dist/core/bash-executor.js.map +1 -1
  12. package/dist/core/extensions/types.d.ts +1 -3
  13. package/dist/core/extensions/types.d.ts.map +1 -1
  14. package/dist/core/settings-manager.js +1 -1
  15. package/dist/core/settings-manager.js.map +1 -1
  16. package/dist/core/task-session-manager.d.ts.map +1 -1
  17. package/dist/core/task-session-manager.js +1 -7
  18. package/dist/core/task-session-manager.js.map +1 -1
  19. package/dist/core/tools/bg-process.d.ts +1 -1
  20. package/dist/core/tools/index.d.ts +60 -59
  21. package/dist/core/tools/index.d.ts.map +1 -1
  22. package/dist/core/tools/index.js +89 -24
  23. package/dist/core/tools/index.js.map +1 -1
  24. package/dist/core/tools/task.d.ts +21 -16
  25. package/dist/core/tools/task.d.ts.map +1 -1
  26. package/dist/core/tools/task.js +82 -43
  27. package/dist/core/tools/task.js.map +1 -1
  28. package/dist/core/tools/todo.d.ts +31 -20
  29. package/dist/core/tools/todo.d.ts.map +1 -1
  30. package/dist/core/tools/todo.js +67 -44
  31. package/dist/core/tools/todo.js.map +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
  37. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  38. package/dist/modes/interactive/components/bash-execution.js +1 -1
  39. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  40. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  41. package/dist/modes/interactive/components/tool-execution.js +2 -2
  42. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  43. package/package.json +3 -3
  44. package/dist/core/todo-store.d.ts +0 -20
  45. package/dist/core/todo-store.d.ts.map +0 -1
  46. package/dist/core/todo-store.js +0 -55
  47. package/dist/core/todo-store.js.map +0 -1
  48. package/dist/core/tools/bash.d.ts +0 -428
  49. package/dist/core/tools/bash.d.ts.map +0 -1
  50. package/dist/core/tools/bash.js +0 -498
  51. package/dist/core/tools/bash.js.map +0 -1
  52. package/dist/core/tools/edit-diff.d.ts +0 -63
  53. package/dist/core/tools/edit-diff.d.ts.map +0 -1
  54. package/dist/core/tools/edit-diff.js +0 -243
  55. package/dist/core/tools/edit-diff.js.map +0 -1
  56. package/dist/core/tools/edit.d.ts +0 -315
  57. package/dist/core/tools/edit.d.ts.map +0 -1
  58. package/dist/core/tools/edit.js +0 -384
  59. package/dist/core/tools/edit.js.map +0 -1
  60. package/dist/core/tools/find.d.ts +0 -201
  61. package/dist/core/tools/find.d.ts.map +0 -1
  62. package/dist/core/tools/find.js +0 -342
  63. package/dist/core/tools/find.js.map +0 -1
  64. package/dist/core/tools/grep.d.ts +0 -323
  65. package/dist/core/tools/grep.d.ts.map +0 -1
  66. package/dist/core/tools/grep.js +0 -486
  67. package/dist/core/tools/grep.js.map +0 -1
  68. package/dist/core/tools/ls.d.ts +0 -44
  69. package/dist/core/tools/ls.d.ts.map +0 -1
  70. package/dist/core/tools/ls.js +0 -124
  71. package/dist/core/tools/ls.js.map +0 -1
  72. package/dist/core/tools/path-utils.d.ts +0 -8
  73. package/dist/core/tools/path-utils.d.ts.map +0 -1
  74. package/dist/core/tools/path-utils.js +0 -53
  75. package/dist/core/tools/path-utils.js.map +0 -1
  76. package/dist/core/tools/read.d.ts +0 -338
  77. package/dist/core/tools/read.d.ts.map +0 -1
  78. package/dist/core/tools/read.js +0 -397
  79. package/dist/core/tools/read.js.map +0 -1
  80. package/dist/core/tools/truncate.d.ts +0 -70
  81. package/dist/core/tools/truncate.d.ts.map +0 -1
  82. package/dist/core/tools/truncate.js +0 -205
  83. package/dist/core/tools/truncate.js.map +0 -1
  84. package/dist/core/tools/webfetch.d.ts +0 -174
  85. package/dist/core/tools/webfetch.d.ts.map +0 -1
  86. package/dist/core/tools/webfetch.js +0 -380
  87. package/dist/core/tools/webfetch.js.map +0 -1
  88. package/dist/core/tools/websearch.d.ts +0 -190
  89. package/dist/core/tools/websearch.d.ts.map +0 -1
  90. package/dist/core/tools/websearch.js +0 -267
  91. package/dist/core/tools/websearch.js.map +0 -1
  92. package/dist/core/tools/write.d.ts +0 -273
  93. package/dist/core/tools/write.d.ts.map +0 -1
  94. package/dist/core/tools/write.js +0 -288
  95. package/dist/core/tools/write.js.map +0 -1
@@ -1,397 +0,0 @@
1
- /**
2
- * Read Tool - Read file contents with automatic image handling
3
- *
4
- * @module core/tools/read
5
- * @description
6
- * Implements the read tool for the agent. Reads file contents with:
7
- * - Support for line-based offset/limit
8
- * - Automatic image detection and resizing
9
- * - Large file truncation handling
10
- * - Remote execution support (SSH, containers)
11
- *
12
- * Based on file reading tools from Anthropic's Claude API and common Unix utilities.
13
- * Refactored for agent multimodal content handling.
14
- * - Proper path resolution and validation
15
- *
16
- * ## Tool Interface
17
- *
18
- * **Input Parameters:**
19
- * - `path` (required): File path to read (relative or absolute)
20
- * - `offset` (optional): Starting line number (1-indexed)
21
- * - `limit` (optional): Maximum number of lines to read
22
- *
23
- * **Output:**
24
- * - Text files: Line content as text
25
- * - Image files: Image data with metadata
26
- * - Large files: Truncated with temp file path
27
- *
28
- * **Error Behavior:**
29
- * - File not found → Tool error "ENOENT: no such file"
30
- * - Permission denied → Tool error "EACCES: permission denied"
31
- * - Invalid path → Tool error "Path outside working directory"
32
- * - Large file not truncable → Tool error "File too large"
33
- *
34
- * ## Usage in Agent
35
- *
36
- * The agent uses this tool to:
37
- * - Read source code files for analysis
38
- * - Check configuration files
39
- * - Read documentation and READMEs
40
- * - View images and diagrams
41
- * - Inspect logs and build output
42
- *
43
- * ## Features
44
- *
45
- * **Line-based Access:**
46
- * - offset: Start reading from line N (1-indexed)
47
- * - limit: Read at most N lines
48
- * - Useful for large files
49
- *
50
- * **Image Handling:**
51
- * - Auto-detect image MIME type
52
- * - Resize large images (configurable)
53
- * - Include dimension information
54
- * - Provide helpful notes for vision models
55
- *
56
- * **Truncation:**
57
- * - Truncate large files at front (keeps last N bytes)
58
- * - Write full content to temp file if needed
59
- * - Return truncation info for reference
60
- *
61
- * **Path Safety:**
62
- * - Resolve relative paths relative to cwd
63
- * - Prevent path traversal attacks (no ../../../etc/passwd)
64
- * - Validate paths are accessible
65
- *
66
- * ## Security
67
- *
68
- * **Path validation:**
69
- * - All paths resolved relative to cwd
70
- * - No symlink following by default
71
- * - Directory traversal prevented
72
- *
73
- * **Permission checks:**
74
- * - File must be readable
75
- * - Working directory must exist
76
- * - Errors returned for permission denied
77
- *
78
- * **Data safety:**
79
- * - Large files truncated (not read into memory)
80
- * - Binary files handled gracefully
81
- * - No automatic execution of file contents
82
- *
83
- * ## Customization
84
- *
85
- * **Image Resizing:**
86
- * ```typescript
87
- * const tool = createReadTool(cwd, {
88
- * autoResizeImages: true,
89
- * imageMaxBytes: 500 * 1024, // 500KB limit
90
- * });
91
- * ```
92
- *
93
- * **Remote Execution (SSH):**
94
- * ```typescript
95
- * const sshTool = createReadTool(cwd, {
96
- * operations: {
97
- * readFile: (path) => sshExec(`cat ${path}`),
98
- * access: (path) => sshExec(`test -r ${path}`),
99
- * },
100
- * });
101
- * ```
102
- *
103
- * ## Examples
104
- *
105
- * ### Read a source file
106
- * ```typescript
107
- * // Agent calls: read {path: "src/main.ts"}
108
- * // Returns: Full file content or truncated with temp file path
109
- * ```
110
- *
111
- * ### Read specific lines
112
- * ```typescript
113
- * // Agent calls: read {path: "test.txt", offset: 10, limit: 5}
114
- * // Returns: Lines 10-14
115
- * ```
116
- *
117
- * ### Auto-resized image
118
- * ```typescript
119
- * // Agent calls: read {path: "diagram.png"}
120
- * // Returns: Image content, resized if > 2MB, with dimensions
121
- * ```
122
- *
123
- * ## Attribution
124
- *
125
- * Based on: indusagi-agent read tool
126
- * Modifications:
127
- * - Added image auto-detection and resizing
128
- * - Added line-based offset/limit support
129
- * - Added custom operations interface
130
- * - Improved truncation handling
131
- */
132
- import { Type } from "@sinclair/typebox";
133
- import { constants } from "fs";
134
- import { access as fsAccess, readFile as fsReadFile } from "fs/promises";
135
- import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
136
- import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime.js";
137
- import { resolveReadPath } from "./path-utils.js";
138
- import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead } from "./truncate.js";
139
- // ============================================================================
140
- // Schema & Types
141
- // ============================================================================
142
- /**
143
- * JSON Schema for read tool parameters
144
- * @internal
145
- */
146
- const readSchema = Type.Object({
147
- path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
148
- offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
149
- limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
150
- });
151
- /**
152
- * Default read operations using local filesystem
153
- * @internal
154
- */
155
- const defaultReadOperations = {
156
- readFile: (path) => fsReadFile(path),
157
- access: (path) => fsAccess(path, constants.R_OK),
158
- detectImageMimeType: detectSupportedImageMimeTypeFromFile,
159
- };
160
- // ============================================================================
161
- // Tool Factory & Exports
162
- // ============================================================================
163
- /**
164
- * Create a read tool with custom configuration
165
- *
166
- * Creates an AgentTool that can read files with:
167
- * - Line-based offset and limit
168
- * - Automatic image detection and resizing
169
- * - Large file truncation
170
- * - Remote execution support
171
- * - Proper path resolution
172
- *
173
- * **Tool Behavior:**
174
- * - Resolves paths relative to working directory
175
- * - Prevents path traversal (no ../../../etc/passwd)
176
- * - Detects images and handles specially
177
- * - Truncates large files at head (keeps tail)
178
- * - Returns truncation info for reference
179
- *
180
- * **Tool Parameters:**
181
- * - `path` (required): File to read
182
- * - `offset` (optional): Starting line (1-indexed)
183
- * - `limit` (optional): Maximum lines to read
184
- *
185
- * **Tool Output:**
186
- * - Text files: File content (possibly truncated)
187
- * - Image files: Image data with metadata
188
- * - Details: Truncation info if file was too large
189
- *
190
- * **Error Handling:**
191
- * - File not found: Tool error "ENOENT: no such file"
192
- * - Permission denied: Tool error "EACCES: permission denied"
193
- * - Invalid path: Tool error "Path outside working directory"
194
- * - Other errors: Tool error with message
195
- *
196
- * @param cwd - Working directory for relative path resolution
197
- * - All relative paths resolved from this directory
198
- * - Prevents directory traversal attacks
199
- * - Can be overridden per execution
200
- *
201
- * @param options - Optional configuration
202
- * - autoResizeImages: Enable/disable image resizing (default: true)
203
- * - operations: Custom file reading backend (default: filesystem)
204
- *
205
- * @returns AgentTool instance ready to use
206
- *
207
- * @example
208
- * ```typescript
209
- * // Basic tool
210
- * const tool = createReadTool(process.cwd());
211
- *
212
- * // With image resizing disabled
213
- * const noResizeTool = createReadTool(cwd, {
214
- * autoResizeImages: false,
215
- * });
216
- *
217
- * // With remote backend (SSH)
218
- * const remoteTool = createReadTool(cwd, {
219
- * operations: sshOperations,
220
- * });
221
- * ```
222
- *
223
- * @see ReadToolOptions for detailed configuration
224
- * @see ReadOperations for custom backend interface
225
- */
226
- export function createReadTool(cwd, options) {
227
- const autoResizeImages = options?.autoResizeImages ?? true;
228
- const ops = options?.operations ?? defaultReadOperations;
229
- return {
230
- name: "read",
231
- label: "read",
232
- description: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,
233
- parameters: readSchema,
234
- execute: async (_toolCallId, { path, offset, limit }, signal) => {
235
- const absolutePath = resolveReadPath(path, cwd);
236
- return new Promise((resolve, reject) => {
237
- // Check if already aborted
238
- if (signal?.aborted) {
239
- reject(new Error("Operation aborted"));
240
- return;
241
- }
242
- let aborted = false;
243
- // Set up abort handler
244
- const onAbort = () => {
245
- aborted = true;
246
- reject(new Error("Operation aborted"));
247
- };
248
- if (signal) {
249
- signal.addEventListener("abort", onAbort, { once: true });
250
- }
251
- // Perform the read operation
252
- (async () => {
253
- try {
254
- // Check if file exists
255
- await ops.access(absolutePath);
256
- // Check if aborted before reading
257
- if (aborted) {
258
- return;
259
- }
260
- const mimeType = ops.detectImageMimeType ? await ops.detectImageMimeType(absolutePath) : undefined;
261
- // Read the file based on type
262
- let content;
263
- let details;
264
- if (mimeType) {
265
- // Read as image (binary)
266
- const buffer = await ops.readFile(absolutePath);
267
- const base64 = buffer.toString("base64");
268
- if (autoResizeImages) {
269
- // Resize image if needed
270
- const resized = await resizeImage({ type: "image", data: base64, mimeType });
271
- const dimensionNote = formatDimensionNote(resized);
272
- let textNote = `Read image file [${resized.mimeType}]`;
273
- if (dimensionNote) {
274
- textNote += `\n${dimensionNote}`;
275
- }
276
- content = [
277
- { type: "text", text: textNote },
278
- { type: "image", data: resized.data, mimeType: resized.mimeType },
279
- ];
280
- }
281
- else {
282
- const textNote = `Read image file [${mimeType}]`;
283
- content = [
284
- { type: "text", text: textNote },
285
- { type: "image", data: base64, mimeType },
286
- ];
287
- }
288
- }
289
- else {
290
- // Read as text
291
- const buffer = await ops.readFile(absolutePath);
292
- const textContent = buffer.toString("utf-8");
293
- const allLines = textContent.split("\n");
294
- const totalFileLines = allLines.length;
295
- // Apply offset if specified (1-indexed to 0-indexed)
296
- const startLine = offset ? Math.max(0, offset - 1) : 0;
297
- const startLineDisplay = startLine + 1; // For display (1-indexed)
298
- // Check if offset is out of bounds
299
- if (startLine >= allLines.length) {
300
- throw new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);
301
- }
302
- // If limit is specified by user, use it; otherwise we'll let truncateHead decide
303
- let selectedContent;
304
- let userLimitedLines;
305
- if (limit !== undefined) {
306
- const endLine = Math.min(startLine + limit, allLines.length);
307
- selectedContent = allLines.slice(startLine, endLine).join("\n");
308
- userLimitedLines = endLine - startLine;
309
- }
310
- else {
311
- selectedContent = allLines.slice(startLine).join("\n");
312
- }
313
- // Apply truncation (respects both line and byte limits)
314
- const truncation = truncateHead(selectedContent);
315
- let outputText;
316
- if (truncation.firstLineExceedsLimit) {
317
- // First line at offset exceeds 30KB - tell model to use bash
318
- const firstLineSize = formatSize(Buffer.byteLength(allLines[startLine], "utf-8"));
319
- outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;
320
- details = { truncation };
321
- }
322
- else if (truncation.truncated) {
323
- // Truncation occurred - build actionable notice
324
- const endLineDisplay = startLineDisplay + truncation.outputLines - 1;
325
- const nextOffset = endLineDisplay + 1;
326
- outputText = truncation.content;
327
- if (truncation.truncatedBy === "lines") {
328
- outputText += `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;
329
- }
330
- else {
331
- outputText += `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
332
- }
333
- details = { truncation };
334
- }
335
- else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {
336
- // User specified limit, there's more content, but no truncation
337
- const remaining = allLines.length - (startLine + userLimitedLines);
338
- const nextOffset = startLine + userLimitedLines + 1;
339
- outputText = truncation.content;
340
- outputText += `\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;
341
- }
342
- else {
343
- // No truncation, no user limit exceeded
344
- outputText = truncation.content;
345
- }
346
- content = [{ type: "text", text: outputText }];
347
- }
348
- // Check if aborted after reading
349
- if (aborted) {
350
- return;
351
- }
352
- // Clean up abort handler
353
- if (signal) {
354
- signal.removeEventListener("abort", onAbort);
355
- }
356
- resolve({ content, details });
357
- }
358
- catch (error) {
359
- // Clean up abort handler
360
- if (signal) {
361
- signal.removeEventListener("abort", onAbort);
362
- }
363
- if (!aborted) {
364
- reject(error);
365
- }
366
- }
367
- })();
368
- });
369
- },
370
- };
371
- }
372
- /** Default read tool using process.cwd() - for backwards compatibility */
373
- /**
374
- * Default read tool instance
375
- *
376
- * Pre-created read tool using current process working directory.
377
- * Supports line-based reading, image resizing, and truncation.
378
- *
379
- * Equivalent to:
380
- * ```typescript
381
- * const readTool = createReadTool(process.cwd(), {
382
- * autoResizeImages: true,
383
- * });
384
- * ```
385
- *
386
- * For custom configuration or remote backends, use createReadTool() directly.
387
- *
388
- * @example
389
- * ```typescript
390
- * import { readTool } from "./tools/read.js";
391
- * const agent = new Agent({ tools: [readTool] });
392
- * ```
393
- *
394
- * @see createReadTool for custom configuration
395
- */
396
- export const readTool = createReadTool(process.cwd());
397
- //# sourceMappingURL=read.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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"}
@@ -1,70 +0,0 @@
1
- /**
2
- * Shared truncation utilities for tool outputs.
3
- *
4
- * Truncation is based on two independent limits - whichever is hit first wins:
5
- * - Line limit (default: 2000 lines)
6
- * - Byte limit (default: 50KB)
7
- *
8
- * Never returns partial lines (except bash tail truncation edge case).
9
- */
10
- export declare const DEFAULT_MAX_LINES = 2000;
11
- export declare const DEFAULT_MAX_BYTES: number;
12
- export declare const GREP_MAX_LINE_LENGTH = 500;
13
- export interface TruncationResult {
14
- /** The truncated content */
15
- content: string;
16
- /** Whether truncation occurred */
17
- truncated: boolean;
18
- /** Which limit was hit: "lines", "bytes", or null if not truncated */
19
- truncatedBy: "lines" | "bytes" | null;
20
- /** Total number of lines in the original content */
21
- totalLines: number;
22
- /** Total number of bytes in the original content */
23
- totalBytes: number;
24
- /** Number of complete lines in the truncated output */
25
- outputLines: number;
26
- /** Number of bytes in the truncated output */
27
- outputBytes: number;
28
- /** Whether the last line was partially truncated (only for tail truncation edge case) */
29
- lastLinePartial: boolean;
30
- /** Whether the first line exceeded the byte limit (for head truncation) */
31
- firstLineExceedsLimit: boolean;
32
- /** The max lines limit that was applied */
33
- maxLines: number;
34
- /** The max bytes limit that was applied */
35
- maxBytes: number;
36
- }
37
- export interface TruncationOptions {
38
- /** Maximum number of lines (default: 2000) */
39
- maxLines?: number;
40
- /** Maximum number of bytes (default: 50KB) */
41
- maxBytes?: number;
42
- }
43
- /**
44
- * Format bytes as human-readable size.
45
- */
46
- export declare function formatSize(bytes: number): string;
47
- /**
48
- * Truncate content from the head (keep first N lines/bytes).
49
- * Suitable for file reads where you want to see the beginning.
50
- *
51
- * Never returns partial lines. If first line exceeds byte limit,
52
- * returns empty content with firstLineExceedsLimit=true.
53
- */
54
- export declare function truncateHead(content: string, options?: TruncationOptions): TruncationResult;
55
- /**
56
- * Truncate content from the tail (keep last N lines/bytes).
57
- * Suitable for bash output where you want to see the end (errors, final results).
58
- *
59
- * May return partial first line if the last line of original content exceeds byte limit.
60
- */
61
- export declare function truncateTail(content: string, options?: TruncationOptions): TruncationResult;
62
- /**
63
- * Truncate a single line to max characters, adding [truncated] suffix.
64
- * Used for grep match lines.
65
- */
66
- export declare function truncateLine(line: string, maxChars?: number): {
67
- text: string;
68
- wasTruncated: boolean;
69
- };
70
- //# sourceMappingURL=truncate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../../src/core/tools/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAChC,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,sEAAsE;IACtE,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;IACtC,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,eAAe,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,qBAAqB,EAAE,OAAO,CAAC;IAC/B,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAkF/F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAyE/F;AAuBD;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAA6B,GACrC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAKzC"}
@@ -1,205 +0,0 @@
1
- /**
2
- * Shared truncation utilities for tool outputs.
3
- *
4
- * Truncation is based on two independent limits - whichever is hit first wins:
5
- * - Line limit (default: 2000 lines)
6
- * - Byte limit (default: 50KB)
7
- *
8
- * Never returns partial lines (except bash tail truncation edge case).
9
- */
10
- export const DEFAULT_MAX_LINES = 2000;
11
- export const DEFAULT_MAX_BYTES = 50 * 1024; // 50KB
12
- export const GREP_MAX_LINE_LENGTH = 500; // Max chars per grep match line
13
- /**
14
- * Format bytes as human-readable size.
15
- */
16
- export function formatSize(bytes) {
17
- if (bytes < 1024) {
18
- return `${bytes}B`;
19
- }
20
- else if (bytes < 1024 * 1024) {
21
- return `${(bytes / 1024).toFixed(1)}KB`;
22
- }
23
- else {
24
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
25
- }
26
- }
27
- /**
28
- * Truncate content from the head (keep first N lines/bytes).
29
- * Suitable for file reads where you want to see the beginning.
30
- *
31
- * Never returns partial lines. If first line exceeds byte limit,
32
- * returns empty content with firstLineExceedsLimit=true.
33
- */
34
- export function truncateHead(content, options = {}) {
35
- const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
36
- const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
37
- const totalBytes = Buffer.byteLength(content, "utf-8");
38
- const lines = content.split("\n");
39
- const totalLines = lines.length;
40
- // Check if no truncation needed
41
- if (totalLines <= maxLines && totalBytes <= maxBytes) {
42
- return {
43
- content,
44
- truncated: false,
45
- truncatedBy: null,
46
- totalLines,
47
- totalBytes,
48
- outputLines: totalLines,
49
- outputBytes: totalBytes,
50
- lastLinePartial: false,
51
- firstLineExceedsLimit: false,
52
- maxLines,
53
- maxBytes,
54
- };
55
- }
56
- // Check if first line alone exceeds byte limit
57
- const firstLineBytes = Buffer.byteLength(lines[0], "utf-8");
58
- if (firstLineBytes > maxBytes) {
59
- return {
60
- content: "",
61
- truncated: true,
62
- truncatedBy: "bytes",
63
- totalLines,
64
- totalBytes,
65
- outputLines: 0,
66
- outputBytes: 0,
67
- lastLinePartial: false,
68
- firstLineExceedsLimit: true,
69
- maxLines,
70
- maxBytes,
71
- };
72
- }
73
- // Collect complete lines that fit
74
- const outputLinesArr = [];
75
- let outputBytesCount = 0;
76
- let truncatedBy = "lines";
77
- for (let i = 0; i < lines.length && i < maxLines; i++) {
78
- const line = lines[i];
79
- const lineBytes = Buffer.byteLength(line, "utf-8") + (i > 0 ? 1 : 0); // +1 for newline
80
- if (outputBytesCount + lineBytes > maxBytes) {
81
- truncatedBy = "bytes";
82
- break;
83
- }
84
- outputLinesArr.push(line);
85
- outputBytesCount += lineBytes;
86
- }
87
- // If we exited due to line limit
88
- if (outputLinesArr.length >= maxLines && outputBytesCount <= maxBytes) {
89
- truncatedBy = "lines";
90
- }
91
- const outputContent = outputLinesArr.join("\n");
92
- const finalOutputBytes = Buffer.byteLength(outputContent, "utf-8");
93
- return {
94
- content: outputContent,
95
- truncated: true,
96
- truncatedBy,
97
- totalLines,
98
- totalBytes,
99
- outputLines: outputLinesArr.length,
100
- outputBytes: finalOutputBytes,
101
- lastLinePartial: false,
102
- firstLineExceedsLimit: false,
103
- maxLines,
104
- maxBytes,
105
- };
106
- }
107
- /**
108
- * Truncate content from the tail (keep last N lines/bytes).
109
- * Suitable for bash output where you want to see the end (errors, final results).
110
- *
111
- * May return partial first line if the last line of original content exceeds byte limit.
112
- */
113
- export function truncateTail(content, options = {}) {
114
- const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
115
- const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
116
- const totalBytes = Buffer.byteLength(content, "utf-8");
117
- const lines = content.split("\n");
118
- const totalLines = lines.length;
119
- // Check if no truncation needed
120
- if (totalLines <= maxLines && totalBytes <= maxBytes) {
121
- return {
122
- content,
123
- truncated: false,
124
- truncatedBy: null,
125
- totalLines,
126
- totalBytes,
127
- outputLines: totalLines,
128
- outputBytes: totalBytes,
129
- lastLinePartial: false,
130
- firstLineExceedsLimit: false,
131
- maxLines,
132
- maxBytes,
133
- };
134
- }
135
- // Work backwards from the end
136
- const outputLinesArr = [];
137
- let outputBytesCount = 0;
138
- let truncatedBy = "lines";
139
- let lastLinePartial = false;
140
- for (let i = lines.length - 1; i >= 0 && outputLinesArr.length < maxLines; i--) {
141
- const line = lines[i];
142
- const lineBytes = Buffer.byteLength(line, "utf-8") + (outputLinesArr.length > 0 ? 1 : 0); // +1 for newline
143
- if (outputBytesCount + lineBytes > maxBytes) {
144
- truncatedBy = "bytes";
145
- // Edge case: if we haven't added ANY lines yet and this line exceeds maxBytes,
146
- // take the end of the line (partial)
147
- if (outputLinesArr.length === 0) {
148
- const truncatedLine = truncateStringToBytesFromEnd(line, maxBytes);
149
- outputLinesArr.unshift(truncatedLine);
150
- outputBytesCount = Buffer.byteLength(truncatedLine, "utf-8");
151
- lastLinePartial = true;
152
- }
153
- break;
154
- }
155
- outputLinesArr.unshift(line);
156
- outputBytesCount += lineBytes;
157
- }
158
- // If we exited due to line limit
159
- if (outputLinesArr.length >= maxLines && outputBytesCount <= maxBytes) {
160
- truncatedBy = "lines";
161
- }
162
- const outputContent = outputLinesArr.join("\n");
163
- const finalOutputBytes = Buffer.byteLength(outputContent, "utf-8");
164
- return {
165
- content: outputContent,
166
- truncated: true,
167
- truncatedBy,
168
- totalLines,
169
- totalBytes,
170
- outputLines: outputLinesArr.length,
171
- outputBytes: finalOutputBytes,
172
- lastLinePartial,
173
- firstLineExceedsLimit: false,
174
- maxLines,
175
- maxBytes,
176
- };
177
- }
178
- /**
179
- * Truncate a string to fit within a byte limit (from the end).
180
- * Handles multi-byte UTF-8 characters correctly.
181
- */
182
- function truncateStringToBytesFromEnd(str, maxBytes) {
183
- const buf = Buffer.from(str, "utf-8");
184
- if (buf.length <= maxBytes) {
185
- return str;
186
- }
187
- // Start from the end, skip maxBytes back
188
- let start = buf.length - maxBytes;
189
- // Find a valid UTF-8 boundary (start of a character)
190
- while (start < buf.length && (buf[start] & 0xc0) === 0x80) {
191
- start++;
192
- }
193
- return buf.slice(start).toString("utf-8");
194
- }
195
- /**
196
- * Truncate a single line to max characters, adding [truncated] suffix.
197
- * Used for grep match lines.
198
- */
199
- export function truncateLine(line, maxChars = GREP_MAX_LINE_LENGTH) {
200
- if (line.length <= maxChars) {
201
- return { text: line, wasTruncated: false };
202
- }
203
- return { text: `${line.slice(0, maxChars)}... [truncated]`, wasTruncated: true };
204
- }
205
- //# sourceMappingURL=truncate.js.map