@wonderwhy-er/desktop-commander 0.2.22 → 0.2.24

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 (60) hide show
  1. package/README.md +14 -55
  2. package/dist/custom-stdio.d.ts +1 -0
  3. package/dist/custom-stdio.js +19 -0
  4. package/dist/handlers/filesystem-handlers.d.ts +4 -0
  5. package/dist/handlers/filesystem-handlers.js +120 -14
  6. package/dist/handlers/node-handlers.d.ts +6 -0
  7. package/dist/handlers/node-handlers.js +73 -0
  8. package/dist/index.js +5 -3
  9. package/dist/search-manager.d.ts +25 -0
  10. package/dist/search-manager.js +212 -0
  11. package/dist/server.js +161 -107
  12. package/dist/terminal-manager.d.ts +56 -2
  13. package/dist/terminal-manager.js +169 -13
  14. package/dist/tools/edit.d.ts +28 -4
  15. package/dist/tools/edit.js +87 -4
  16. package/dist/tools/filesystem.d.ts +23 -12
  17. package/dist/tools/filesystem.js +201 -416
  18. package/dist/tools/improved-process-tools.d.ts +2 -2
  19. package/dist/tools/improved-process-tools.js +244 -214
  20. package/dist/tools/mime-types.d.ts +1 -0
  21. package/dist/tools/mime-types.js +7 -0
  22. package/dist/tools/pdf/extract-images.d.ts +34 -0
  23. package/dist/tools/pdf/extract-images.js +132 -0
  24. package/dist/tools/pdf/index.d.ts +6 -0
  25. package/dist/tools/pdf/index.js +3 -0
  26. package/dist/tools/pdf/lib/pdf2md.d.ts +36 -0
  27. package/dist/tools/pdf/lib/pdf2md.js +76 -0
  28. package/dist/tools/pdf/manipulations.d.ts +13 -0
  29. package/dist/tools/pdf/manipulations.js +96 -0
  30. package/dist/tools/pdf/markdown.d.ts +7 -0
  31. package/dist/tools/pdf/markdown.js +37 -0
  32. package/dist/tools/pdf/utils.d.ts +12 -0
  33. package/dist/tools/pdf/utils.js +34 -0
  34. package/dist/tools/prompts.js +0 -10
  35. package/dist/tools/schemas.d.ts +167 -12
  36. package/dist/tools/schemas.js +54 -5
  37. package/dist/types.d.ts +2 -1
  38. package/dist/utils/feature-flags.js +7 -4
  39. package/dist/utils/files/base.d.ts +167 -0
  40. package/dist/utils/files/base.js +5 -0
  41. package/dist/utils/files/binary.d.ts +21 -0
  42. package/dist/utils/files/binary.js +65 -0
  43. package/dist/utils/files/excel.d.ts +24 -0
  44. package/dist/utils/files/excel.js +416 -0
  45. package/dist/utils/files/factory.d.ts +40 -0
  46. package/dist/utils/files/factory.js +101 -0
  47. package/dist/utils/files/image.d.ts +21 -0
  48. package/dist/utils/files/image.js +78 -0
  49. package/dist/utils/files/index.d.ts +10 -0
  50. package/dist/utils/files/index.js +13 -0
  51. package/dist/utils/files/pdf.d.ts +32 -0
  52. package/dist/utils/files/pdf.js +142 -0
  53. package/dist/utils/files/text.d.ts +63 -0
  54. package/dist/utils/files/text.js +357 -0
  55. package/dist/utils/ripgrep-resolver.js +3 -2
  56. package/dist/utils/system-info.d.ts +5 -0
  57. package/dist/utils/system-info.js +71 -3
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/package.json +14 -3
@@ -30,15 +30,21 @@ export declare const StartProcessArgsSchema: z.ZodObject<{
30
30
  export declare const ReadProcessOutputArgsSchema: z.ZodObject<{
31
31
  pid: z.ZodNumber;
32
32
  timeout_ms: z.ZodOptional<z.ZodNumber>;
33
+ offset: z.ZodOptional<z.ZodNumber>;
34
+ length: z.ZodOptional<z.ZodNumber>;
33
35
  verbose_timing: z.ZodOptional<z.ZodBoolean>;
34
36
  }, "strip", z.ZodTypeAny, {
35
37
  pid: number;
38
+ length?: number | undefined;
36
39
  timeout_ms?: number | undefined;
37
40
  verbose_timing?: boolean | undefined;
41
+ offset?: number | undefined;
38
42
  }, {
39
43
  pid: number;
44
+ length?: number | undefined;
40
45
  timeout_ms?: number | undefined;
41
46
  verbose_timing?: boolean | undefined;
47
+ offset?: number | undefined;
42
48
  }>;
43
49
  export declare const ForceTerminateArgsSchema: z.ZodObject<{
44
50
  pid: z.ZodNumber;
@@ -60,16 +66,25 @@ export declare const ReadFileArgsSchema: z.ZodObject<{
60
66
  isUrl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
61
67
  offset: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
62
68
  length: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
69
+ sheet: z.ZodOptional<z.ZodString>;
70
+ range: z.ZodOptional<z.ZodString>;
71
+ options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
63
72
  }, "strip", z.ZodTypeAny, {
64
73
  length: number;
65
74
  path: string;
66
- isUrl: boolean;
67
75
  offset: number;
76
+ isUrl: boolean;
77
+ options?: Record<string, any> | undefined;
78
+ sheet?: string | undefined;
79
+ range?: string | undefined;
68
80
  }, {
69
81
  path: string;
70
82
  length?: number | undefined;
71
- isUrl?: boolean | undefined;
83
+ options?: Record<string, any> | undefined;
72
84
  offset?: number | undefined;
85
+ isUrl?: boolean | undefined;
86
+ sheet?: string | undefined;
87
+ range?: string | undefined;
73
88
  }>;
74
89
  export declare const ReadMultipleFilesArgsSchema: z.ZodObject<{
75
90
  paths: z.ZodArray<z.ZodString, "many">;
@@ -91,6 +106,124 @@ export declare const WriteFileArgsSchema: z.ZodObject<{
91
106
  content: string;
92
107
  mode?: "rewrite" | "append" | undefined;
93
108
  }>;
109
+ export declare const PdfInsertOperationSchema: z.ZodObject<{
110
+ type: z.ZodLiteral<"insert">;
111
+ pageIndex: z.ZodNumber;
112
+ markdown: z.ZodOptional<z.ZodString>;
113
+ sourcePdfPath: z.ZodOptional<z.ZodString>;
114
+ pdfOptions: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
115
+ }, "strip", z.ZodTypeAny, {
116
+ type: "insert";
117
+ pageIndex: number;
118
+ markdown?: string | undefined;
119
+ sourcePdfPath?: string | undefined;
120
+ pdfOptions?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
121
+ }, {
122
+ type: "insert";
123
+ pageIndex: number;
124
+ markdown?: string | undefined;
125
+ sourcePdfPath?: string | undefined;
126
+ pdfOptions?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
127
+ }>;
128
+ export declare const PdfDeleteOperationSchema: z.ZodObject<{
129
+ type: z.ZodLiteral<"delete">;
130
+ pageIndexes: z.ZodArray<z.ZodNumber, "many">;
131
+ }, "strip", z.ZodTypeAny, {
132
+ type: "delete";
133
+ pageIndexes: number[];
134
+ }, {
135
+ type: "delete";
136
+ pageIndexes: number[];
137
+ }>;
138
+ export declare const PdfOperationSchema: z.ZodUnion<[z.ZodObject<{
139
+ type: z.ZodLiteral<"insert">;
140
+ pageIndex: z.ZodNumber;
141
+ markdown: z.ZodOptional<z.ZodString>;
142
+ sourcePdfPath: z.ZodOptional<z.ZodString>;
143
+ pdfOptions: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
144
+ }, "strip", z.ZodTypeAny, {
145
+ type: "insert";
146
+ pageIndex: number;
147
+ markdown?: string | undefined;
148
+ sourcePdfPath?: string | undefined;
149
+ pdfOptions?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
150
+ }, {
151
+ type: "insert";
152
+ pageIndex: number;
153
+ markdown?: string | undefined;
154
+ sourcePdfPath?: string | undefined;
155
+ pdfOptions?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
156
+ }>, z.ZodObject<{
157
+ type: z.ZodLiteral<"delete">;
158
+ pageIndexes: z.ZodArray<z.ZodNumber, "many">;
159
+ }, "strip", z.ZodTypeAny, {
160
+ type: "delete";
161
+ pageIndexes: number[];
162
+ }, {
163
+ type: "delete";
164
+ pageIndexes: number[];
165
+ }>]>;
166
+ export declare const WritePdfArgsSchema: z.ZodObject<{
167
+ path: z.ZodString;
168
+ content: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodUnion<[z.ZodObject<{
169
+ type: z.ZodLiteral<"insert">;
170
+ pageIndex: z.ZodNumber;
171
+ markdown: z.ZodOptional<z.ZodString>;
172
+ sourcePdfPath: z.ZodOptional<z.ZodString>;
173
+ pdfOptions: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
174
+ }, "strip", z.ZodTypeAny, {
175
+ type: "insert";
176
+ pageIndex: number;
177
+ markdown?: string | undefined;
178
+ sourcePdfPath?: string | undefined;
179
+ pdfOptions?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
180
+ }, {
181
+ type: "insert";
182
+ pageIndex: number;
183
+ markdown?: string | undefined;
184
+ sourcePdfPath?: string | undefined;
185
+ pdfOptions?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
186
+ }>, z.ZodObject<{
187
+ type: z.ZodLiteral<"delete">;
188
+ pageIndexes: z.ZodArray<z.ZodNumber, "many">;
189
+ }, "strip", z.ZodTypeAny, {
190
+ type: "delete";
191
+ pageIndexes: number[];
192
+ }, {
193
+ type: "delete";
194
+ pageIndexes: number[];
195
+ }>]>, "many">]>, string | ({
196
+ type: "insert";
197
+ pageIndex: number;
198
+ markdown?: string | undefined;
199
+ sourcePdfPath?: string | undefined;
200
+ pdfOptions?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
201
+ } | {
202
+ type: "delete";
203
+ pageIndexes: number[];
204
+ })[], unknown>;
205
+ outputPath: z.ZodOptional<z.ZodString>;
206
+ options: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
207
+ }, "strip", z.ZodTypeAny, {
208
+ path: string;
209
+ content: string | ({
210
+ type: "insert";
211
+ pageIndex: number;
212
+ markdown?: string | undefined;
213
+ sourcePdfPath?: string | undefined;
214
+ pdfOptions?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
215
+ } | {
216
+ type: "delete";
217
+ pageIndexes: number[];
218
+ })[];
219
+ options?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
220
+ outputPath?: string | undefined;
221
+ }, {
222
+ path: string;
223
+ options?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
224
+ content?: unknown;
225
+ outputPath?: string | undefined;
226
+ }>;
94
227
  export declare const CreateDirectoryArgsSchema: z.ZodObject<{
95
228
  path: z.ZodString;
96
229
  }, "strip", z.ZodTypeAny, {
@@ -125,20 +258,45 @@ export declare const GetFileInfoArgsSchema: z.ZodObject<{
125
258
  }, {
126
259
  path: string;
127
260
  }>;
128
- export declare const EditBlockArgsSchema: z.ZodObject<{
261
+ export declare const EditBlockArgsSchema: z.ZodEffects<z.ZodObject<{
129
262
  file_path: z.ZodString;
130
- old_string: z.ZodString;
131
- new_string: z.ZodString;
263
+ old_string: z.ZodOptional<z.ZodString>;
264
+ new_string: z.ZodOptional<z.ZodString>;
132
265
  expected_replacements: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
266
+ range: z.ZodOptional<z.ZodString>;
267
+ content: z.ZodOptional<z.ZodAny>;
268
+ options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
133
269
  }, "strip", z.ZodTypeAny, {
134
270
  file_path: string;
135
- old_string: string;
136
- new_string: string;
137
271
  expected_replacements: number;
272
+ options?: Record<string, any> | undefined;
273
+ range?: string | undefined;
274
+ content?: any;
275
+ old_string?: string | undefined;
276
+ new_string?: string | undefined;
277
+ }, {
278
+ file_path: string;
279
+ options?: Record<string, any> | undefined;
280
+ range?: string | undefined;
281
+ content?: any;
282
+ old_string?: string | undefined;
283
+ new_string?: string | undefined;
284
+ expected_replacements?: number | undefined;
285
+ }>, {
286
+ file_path: string;
287
+ expected_replacements: number;
288
+ options?: Record<string, any> | undefined;
289
+ range?: string | undefined;
290
+ content?: any;
291
+ old_string?: string | undefined;
292
+ new_string?: string | undefined;
138
293
  }, {
139
294
  file_path: string;
140
- old_string: string;
141
- new_string: string;
295
+ options?: Record<string, any> | undefined;
296
+ range?: string | undefined;
297
+ content?: any;
298
+ old_string?: string | undefined;
299
+ new_string?: string | undefined;
142
300
  expected_replacements?: number | undefined;
143
301
  }>;
144
302
  export declare const InteractWithProcessArgsSchema: z.ZodObject<{
@@ -223,15 +381,12 @@ export declare const ListSearchesArgsSchema: z.ZodObject<{}, "strip", z.ZodTypeA
223
381
  export declare const GetPromptsArgsSchema: z.ZodObject<{
224
382
  action: z.ZodEnum<["get_prompt"]>;
225
383
  promptId: z.ZodString;
226
- anonymous_user_use_case: z.ZodOptional<z.ZodString>;
227
384
  }, "strip", z.ZodTypeAny, {
228
385
  action: "get_prompt";
229
386
  promptId: string;
230
- anonymous_user_use_case?: string | undefined;
231
387
  }, {
232
388
  action: "get_prompt";
233
389
  promptId: string;
234
- anonymous_user_use_case?: string | undefined;
235
390
  }>;
236
391
  export declare const GetRecentToolCallsArgsSchema: z.ZodObject<{
237
392
  maxResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
@@ -23,6 +23,8 @@ export const StartProcessArgsSchema = z.object({
23
23
  export const ReadProcessOutputArgsSchema = z.object({
24
24
  pid: z.number(),
25
25
  timeout_ms: z.number().optional(),
26
+ offset: z.number().optional(), // Line offset: 0=from last read, positive=absolute, negative=tail
27
+ length: z.number().optional(), // Max lines to return (default from config.fileReadLineLimit)
26
28
  verbose_timing: z.boolean().optional(),
27
29
  });
28
30
  export const ForceTerminateArgsSchema = z.object({
@@ -38,6 +40,9 @@ export const ReadFileArgsSchema = z.object({
38
40
  isUrl: z.boolean().optional().default(false),
39
41
  offset: z.number().optional().default(0),
40
42
  length: z.number().optional().default(1000),
43
+ sheet: z.string().optional(), // String only for MCP client compatibility (Cursor doesn't support union types in JSON Schema)
44
+ range: z.string().optional(),
45
+ options: z.record(z.any()).optional()
41
46
  });
42
47
  export const ReadMultipleFilesArgsSchema = z.object({
43
48
  paths: z.array(z.string()),
@@ -47,6 +52,39 @@ export const WriteFileArgsSchema = z.object({
47
52
  content: z.string(),
48
53
  mode: z.enum(['rewrite', 'append']).default('rewrite'),
49
54
  });
55
+ // PDF modification schemas - exported for reuse
56
+ export const PdfInsertOperationSchema = z.object({
57
+ type: z.literal('insert'),
58
+ pageIndex: z.number(),
59
+ markdown: z.string().optional(),
60
+ sourcePdfPath: z.string().optional(),
61
+ pdfOptions: z.object({}).passthrough().optional(),
62
+ });
63
+ export const PdfDeleteOperationSchema = z.object({
64
+ type: z.literal('delete'),
65
+ pageIndexes: z.array(z.number()),
66
+ });
67
+ export const PdfOperationSchema = z.union([PdfInsertOperationSchema, PdfDeleteOperationSchema]);
68
+ export const WritePdfArgsSchema = z.object({
69
+ path: z.string(),
70
+ // Preprocess content to handle JSON strings that should be parsed as arrays
71
+ content: z.preprocess((val) => {
72
+ // If it's a string that looks like JSON array, parse it
73
+ if (typeof val === 'string' && val.trim().startsWith('[')) {
74
+ try {
75
+ return JSON.parse(val);
76
+ }
77
+ catch {
78
+ // If parsing fails, return as-is (might be markdown content)
79
+ return val;
80
+ }
81
+ }
82
+ // Otherwise return as-is
83
+ return val;
84
+ }, z.union([z.string(), z.array(PdfOperationSchema)])),
85
+ outputPath: z.string().optional(),
86
+ options: z.object({}).passthrough().optional(), // Allow passing options to md-to-pdf
87
+ });
50
88
  export const CreateDirectoryArgsSchema = z.object({
51
89
  path: z.string(),
52
90
  });
@@ -61,13 +99,23 @@ export const MoveFileArgsSchema = z.object({
61
99
  export const GetFileInfoArgsSchema = z.object({
62
100
  path: z.string(),
63
101
  });
64
- // Edit tools schema
102
+ // Edit tools schema - SIMPLIFIED from three modes to two
103
+ // Previously supported: text replacement, location-based edits (edits array), and range rewrites
104
+ // Now supports only: text replacement and range rewrites
105
+ // Removed 'edits' array parameter - location-based surgical edits were complex and unnecessary
106
+ // Range rewrites are more powerful and cover all structured file editing needs
65
107
  export const EditBlockArgsSchema = z.object({
66
108
  file_path: z.string(),
67
- old_string: z.string(),
68
- new_string: z.string(),
109
+ // Text file string replacement
110
+ old_string: z.string().optional(),
111
+ new_string: z.string().optional(),
69
112
  expected_replacements: z.number().optional().default(1),
70
- });
113
+ // Structured file range rewrite (Excel, etc.)
114
+ range: z.string().optional(),
115
+ content: z.any().optional(),
116
+ options: z.record(z.any()).optional()
117
+ }).refine(data => (data.old_string !== undefined && data.new_string !== undefined) ||
118
+ (data.range !== undefined && data.content !== undefined), { message: "Must provide either (old_string + new_string) or (range + content)" });
71
119
  // Send input to process schema
72
120
  export const InteractWithProcessArgsSchema = z.object({
73
121
  pid: z.number(),
@@ -114,7 +162,8 @@ export const ListSearchesArgsSchema = z.object({});
114
162
  export const GetPromptsArgsSchema = z.object({
115
163
  action: z.enum(['get_prompt']),
116
164
  promptId: z.string(),
117
- anonymous_user_use_case: z.string().optional(),
165
+ // Disabled to check if it makes sense or should be removed or changed
166
+ // anonymous_user_use_case: z.string().optional(),
118
167
  });
119
168
  // Tool history schema
120
169
  export const GetRecentToolCallsArgsSchema = z.object({
package/dist/types.d.ts CHANGED
@@ -13,7 +13,8 @@ export interface ProcessInfo {
13
13
  export interface TerminalSession {
14
14
  pid: number;
15
15
  process: ChildProcess;
16
- lastOutput: string;
16
+ outputLines: string[];
17
+ lastReadIndex: number;
17
18
  isBlocked: boolean;
18
19
  startTime: Date;
19
20
  }
@@ -32,6 +32,9 @@ class FeatureFlagManager {
32
32
  logger.debug('Periodic flag fetch failed:', err.message);
33
33
  });
34
34
  }, this.cacheMaxAge);
35
+ // Allow process to exit even if interval is pending
36
+ // This is critical for proper cleanup when MCP client disconnects
37
+ this.refreshInterval.unref();
35
38
  logger.info(`Feature flags initialized (refresh every ${this.cacheMaxAge / 1000}s)`);
36
39
  }
37
40
  catch (error) {
@@ -89,7 +92,7 @@ class FeatureFlagManager {
89
92
  */
90
93
  async fetchFlags() {
91
94
  try {
92
- logger.debug('Fetching feature flags from:', this.flagUrl);
95
+ // Don't log here - runs async and can interfere with MCP clients
93
96
  const controller = new AbortController();
94
97
  const timeout = setTimeout(() => controller.abort(), 5000);
95
98
  const response = await fetch(this.flagUrl, {
@@ -107,9 +110,9 @@ class FeatureFlagManager {
107
110
  if (config.flags) {
108
111
  this.flags = config.flags;
109
112
  this.lastFetch = Date.now();
110
- // Save to cache
113
+ // Save to cache (silently - don't log during async operations
114
+ // as it can interfere with MCP clients that close quickly)
111
115
  await this.saveToCache(config);
112
- logger.info(`Feature flags updated: ${Object.keys(this.flags).length} flags`);
113
116
  }
114
117
  }
115
118
  catch (error) {
@@ -127,7 +130,7 @@ class FeatureFlagManager {
127
130
  await fs.mkdir(configDir, { recursive: true });
128
131
  }
129
132
  await fs.writeFile(this.cachePath, JSON.stringify(config, null, 2), 'utf8');
130
- logger.debug('Saved feature flags to cache');
133
+ // Don't log here - this runs async and can cause issues with MCP clients
131
134
  }
132
135
  catch (error) {
133
136
  logger.warning('Failed to save feature flags to cache:', error);
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Base interfaces and types for file handling system
3
+ * All file handlers implement the FileHandler interface
4
+ */
5
+ /**
6
+ * Base interface that all file handlers must implement
7
+ */
8
+ export interface FileHandler {
9
+ /**
10
+ * Read file content
11
+ * @param path Validated file path
12
+ * @param options Read options (offset, length, sheet, etc.)
13
+ * @returns File result with content and metadata
14
+ */
15
+ read(path: string, options?: ReadOptions): Promise<FileResult>;
16
+ /**
17
+ * Write file (complete rewrite or append)
18
+ * @param path Validated file path
19
+ * @param content Content to write
20
+ * @param mode Write mode: 'rewrite' (default) or 'append'
21
+ */
22
+ write(path: string, content: any, mode?: 'rewrite' | 'append'): Promise<void>;
23
+ /**
24
+ * Edit a specific range (bulk rewrite)
25
+ * PRIMARY METHOD for structured file editing (Excel, etc.)
26
+ * Simpler and more powerful than location-based edits
27
+ * Supports:
28
+ * - Cell ranges: "Sheet1!A1:C10" with 2D array content
29
+ * - Whole sheets: "Sheet1" to replace entire sheet
30
+ * - Chunking: Update 1000 rows at a time for large files
31
+ *
32
+ * Currently implemented by: ExcelFileHandler
33
+ * TECHNICAL DEBT: TextFileHandler should also implement this for search/replace
34
+ * (logic currently in src/tools/edit.ts - see comments there)
35
+ *
36
+ * @param path Validated file path
37
+ * @param range Range identifier (e.g., "Sheet1!A1:C10" or "Sheet1")
38
+ * @param content New content for the range (2D array for Excel)
39
+ * @param options Additional format-specific options
40
+ * @returns Result with success status
41
+ */
42
+ editRange?(path: string, range: string, content: any, options?: Record<string, any>): Promise<EditResult>;
43
+ /**
44
+ * Get file metadata
45
+ * @param path Validated file path
46
+ * @returns File information including type-specific metadata
47
+ */
48
+ getInfo(path: string): Promise<FileInfo>;
49
+ /**
50
+ * Check if this handler can handle the given file
51
+ * @param path File path
52
+ * @returns true if this handler supports this file type (can be async for content-based checks)
53
+ */
54
+ canHandle(path: string): boolean | Promise<boolean>;
55
+ }
56
+ /**
57
+ * Options for reading files
58
+ */
59
+ export interface ReadOptions {
60
+ /** Whether the path is a URL */
61
+ isUrl?: boolean;
62
+ /** Starting line/row number (for text/excel) */
63
+ offset?: number;
64
+ /** Maximum number of lines/rows to read */
65
+ length?: number;
66
+ /** Excel-specific: Sheet name or index */
67
+ sheet?: string | number;
68
+ /** Excel-specific: Cell range (e.g., "A1:C10") */
69
+ range?: string;
70
+ /** Whether to include status messages (default: true) */
71
+ includeStatusMessage?: boolean;
72
+ }
73
+ /**
74
+ * Result from reading a file
75
+ */
76
+ export interface FileResult {
77
+ /** File content (string for text/csv, Buffer for binary, base64 string for images) */
78
+ content: string | Buffer;
79
+ /** MIME type of the content */
80
+ mimeType: string;
81
+ /** Type-specific metadata */
82
+ metadata?: FileMetadata;
83
+ }
84
+ /**
85
+ * File-type specific metadata
86
+ */
87
+ export interface FileMetadata {
88
+ /** For images */
89
+ isImage?: boolean;
90
+ /** For binary files */
91
+ isBinary?: boolean;
92
+ /** For Excel files */
93
+ isExcelFile?: boolean;
94
+ sheets?: ExcelSheet[];
95
+ fileSize?: number;
96
+ isLargeFile?: boolean;
97
+ /** For text files */
98
+ lineCount?: number;
99
+ /** For PDF files */
100
+ isPdf?: boolean;
101
+ author?: string;
102
+ title?: string;
103
+ totalPages?: number;
104
+ pages?: PdfPageItem[];
105
+ /** Error information if operation failed */
106
+ error?: boolean;
107
+ errorMessage?: string;
108
+ }
109
+ /**
110
+ * PDF page content item
111
+ */
112
+ export interface PdfPageItem {
113
+ pageNumber: number;
114
+ text: string;
115
+ images: Array<{
116
+ data: string;
117
+ mimeType: string;
118
+ }>;
119
+ }
120
+ /**
121
+ * Excel sheet metadata
122
+ */
123
+ export interface ExcelSheet {
124
+ /** Sheet name */
125
+ name: string;
126
+ /** Number of rows in sheet */
127
+ rowCount: number;
128
+ /** Number of columns in sheet */
129
+ colCount: number;
130
+ }
131
+ /**
132
+ * Result from edit operation (used by editRange)
133
+ */
134
+ export interface EditResult {
135
+ /** Whether all edits succeeded */
136
+ success: boolean;
137
+ /** Number of edits successfully applied */
138
+ editsApplied: number;
139
+ /** Errors that occurred during editing */
140
+ errors?: Array<{
141
+ location: string;
142
+ error: string;
143
+ }>;
144
+ }
145
+ /**
146
+ * File information and metadata
147
+ */
148
+ export interface FileInfo {
149
+ /** File size in bytes */
150
+ size: number;
151
+ /** Creation time */
152
+ created: Date;
153
+ /** Last modification time */
154
+ modified: Date;
155
+ /** Last access time */
156
+ accessed: Date;
157
+ /** Is this a directory */
158
+ isDirectory: boolean;
159
+ /** Is this a regular file */
160
+ isFile: boolean;
161
+ /** File permissions (octal string) */
162
+ permissions: string;
163
+ /** File type classification */
164
+ fileType: 'text' | 'excel' | 'image' | 'binary';
165
+ /** Type-specific metadata */
166
+ metadata?: FileMetadata;
167
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Base interfaces and types for file handling system
3
+ * All file handlers implement the FileHandler interface
4
+ */
5
+ export {};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Binary file handler
3
+ * Handles binary files that aren't supported by other handlers (Excel, Image)
4
+ * Uses isBinaryFile for content-based detection
5
+ * Returns instructions to use start_process with appropriate tools
6
+ */
7
+ import { FileHandler, ReadOptions, FileResult, FileInfo } from './base.js';
8
+ /**
9
+ * Binary file handler implementation
10
+ * Uses content-based detection via isBinaryFile
11
+ */
12
+ export declare class BinaryFileHandler implements FileHandler {
13
+ canHandle(filePath: string): Promise<boolean>;
14
+ read(filePath: string, options?: ReadOptions): Promise<FileResult>;
15
+ write(path: string, content: any): Promise<void>;
16
+ getInfo(path: string): Promise<FileInfo>;
17
+ /**
18
+ * Generate instructions for handling binary files
19
+ */
20
+ private getBinaryInstructions;
21
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Binary file handler
3
+ * Handles binary files that aren't supported by other handlers (Excel, Image)
4
+ * Uses isBinaryFile for content-based detection
5
+ * Returns instructions to use start_process with appropriate tools
6
+ */
7
+ import fs from "fs/promises";
8
+ import path from "path";
9
+ import { isBinaryFile } from 'isbinaryfile';
10
+ /**
11
+ * Binary file handler implementation
12
+ * Uses content-based detection via isBinaryFile
13
+ */
14
+ export class BinaryFileHandler {
15
+ async canHandle(filePath) {
16
+ // Content-based binary detection using isBinaryFile
17
+ try {
18
+ return await isBinaryFile(filePath);
19
+ }
20
+ catch (error) {
21
+ // If we can't check (file doesn't exist, etc.), don't handle it
22
+ return false;
23
+ }
24
+ }
25
+ async read(filePath, options) {
26
+ const instructions = this.getBinaryInstructions(filePath);
27
+ return {
28
+ content: instructions,
29
+ mimeType: 'text/plain',
30
+ metadata: {
31
+ isBinary: true
32
+ }
33
+ };
34
+ }
35
+ async write(path, content) {
36
+ throw new Error('Cannot write binary files directly. Use start_process with appropriate tools (Python, Node.js libraries, command-line utilities).');
37
+ }
38
+ async getInfo(path) {
39
+ const stats = await fs.stat(path);
40
+ return {
41
+ size: stats.size,
42
+ created: stats.birthtime,
43
+ modified: stats.mtime,
44
+ accessed: stats.atime,
45
+ isDirectory: stats.isDirectory(),
46
+ isFile: stats.isFile(),
47
+ permissions: stats.mode.toString(8).slice(-3),
48
+ fileType: 'binary',
49
+ metadata: {
50
+ isBinary: true
51
+ }
52
+ };
53
+ }
54
+ /**
55
+ * Generate instructions for handling binary files
56
+ */
57
+ getBinaryInstructions(filePath) {
58
+ const fileName = path.basename(filePath);
59
+ return `Cannot read binary file as text: ${fileName}
60
+
61
+ Use start_process + interact_with_process to analyze binary files with appropriate tools (Node.js or Python libraries, command-line utilities, etc.).
62
+
63
+ The read_file tool only handles text files, images, and Excel files.`;
64
+ }
65
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Excel file handler using ExcelJS
3
+ * Handles reading, writing, and editing Excel files (.xlsx, .xls, .xlsm)
4
+ */
5
+ import { FileHandler, ReadOptions, FileResult, EditResult, FileInfo } from './base.js';
6
+ /**
7
+ * Excel file handler implementation using ExcelJS
8
+ * Supports: .xlsx, .xls, .xlsm files
9
+ */
10
+ export declare class ExcelFileHandler implements FileHandler {
11
+ canHandle(path: string): boolean;
12
+ read(path: string, options?: ReadOptions): Promise<FileResult>;
13
+ write(path: string, content: any, mode?: 'rewrite' | 'append'): Promise<void>;
14
+ editRange(path: string, range: string, content: any, options?: Record<string, any>): Promise<EditResult>;
15
+ getInfo(path: string): Promise<FileInfo>;
16
+ private checkFileSize;
17
+ private extractMetadata;
18
+ private worksheetToArray;
19
+ private writeDataToSheet;
20
+ private writeRowsStartingAt;
21
+ private parseRange;
22
+ private parseCellRange;
23
+ private columnToNumber;
24
+ }