gencode-ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/.env.example +11 -0
  2. package/CLAUDE.md +70 -0
  3. package/LICENSE +21 -0
  4. package/README.md +117 -0
  5. package/dist/agent/agent.d.ts +84 -0
  6. package/dist/agent/agent.d.ts.map +1 -0
  7. package/dist/agent/agent.js +233 -0
  8. package/dist/agent/agent.js.map +1 -0
  9. package/dist/agent/index.d.ts +6 -0
  10. package/dist/agent/index.d.ts.map +1 -0
  11. package/dist/agent/index.js +6 -0
  12. package/dist/agent/index.js.map +1 -0
  13. package/dist/agent/types.d.ts +47 -0
  14. package/dist/agent/types.d.ts.map +1 -0
  15. package/dist/agent/types.js +5 -0
  16. package/dist/agent/types.js.map +1 -0
  17. package/dist/cli/components/App.d.ts +14 -0
  18. package/dist/cli/components/App.d.ts.map +1 -0
  19. package/dist/cli/components/App.js +395 -0
  20. package/dist/cli/components/App.js.map +1 -0
  21. package/dist/cli/components/CommandSuggestions.d.ts +13 -0
  22. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
  23. package/dist/cli/components/CommandSuggestions.js +32 -0
  24. package/dist/cli/components/CommandSuggestions.js.map +1 -0
  25. package/dist/cli/components/Header.d.ts +9 -0
  26. package/dist/cli/components/Header.d.ts.map +1 -0
  27. package/dist/cli/components/Header.js +13 -0
  28. package/dist/cli/components/Header.js.map +1 -0
  29. package/dist/cli/components/Input.d.ts +13 -0
  30. package/dist/cli/components/Input.d.ts.map +1 -0
  31. package/dist/cli/components/Input.js +27 -0
  32. package/dist/cli/components/Input.js.map +1 -0
  33. package/dist/cli/components/Logo.d.ts +2 -0
  34. package/dist/cli/components/Logo.d.ts.map +1 -0
  35. package/dist/cli/components/Logo.js +8 -0
  36. package/dist/cli/components/Logo.js.map +1 -0
  37. package/dist/cli/components/Messages.d.ts +37 -0
  38. package/dist/cli/components/Messages.d.ts.map +1 -0
  39. package/dist/cli/components/Messages.js +106 -0
  40. package/dist/cli/components/Messages.js.map +1 -0
  41. package/dist/cli/components/ModelSelector.d.ts +13 -0
  42. package/dist/cli/components/ModelSelector.d.ts.map +1 -0
  43. package/dist/cli/components/ModelSelector.js +72 -0
  44. package/dist/cli/components/ModelSelector.js.map +1 -0
  45. package/dist/cli/components/Spinner.d.ts +12 -0
  46. package/dist/cli/components/Spinner.d.ts.map +1 -0
  47. package/dist/cli/components/Spinner.js +45 -0
  48. package/dist/cli/components/Spinner.js.map +1 -0
  49. package/dist/cli/components/index.d.ts +12 -0
  50. package/dist/cli/components/index.d.ts.map +1 -0
  51. package/dist/cli/components/index.js +12 -0
  52. package/dist/cli/components/index.js.map +1 -0
  53. package/dist/cli/components/theme.d.ts +31 -0
  54. package/dist/cli/components/theme.d.ts.map +1 -0
  55. package/dist/cli/components/theme.js +36 -0
  56. package/dist/cli/components/theme.js.map +1 -0
  57. package/dist/cli/index-legacy.d.ts +7 -0
  58. package/dist/cli/index-legacy.d.ts.map +1 -0
  59. package/dist/cli/index-legacy.js +431 -0
  60. package/dist/cli/index-legacy.js.map +1 -0
  61. package/dist/cli/index.d.ts +7 -0
  62. package/dist/cli/index.d.ts.map +1 -0
  63. package/dist/cli/index.js +116 -0
  64. package/dist/cli/index.js.map +1 -0
  65. package/dist/cli/ink-cli.d.ts +7 -0
  66. package/dist/cli/ink-cli.d.ts.map +1 -0
  67. package/dist/cli/ink-cli.js +105 -0
  68. package/dist/cli/ink-cli.js.map +1 -0
  69. package/dist/cli/session-picker.d.ts +16 -0
  70. package/dist/cli/session-picker.d.ts.map +1 -0
  71. package/dist/cli/session-picker.js +280 -0
  72. package/dist/cli/session-picker.js.map +1 -0
  73. package/dist/cli/ui.d.ts +61 -0
  74. package/dist/cli/ui.d.ts.map +1 -0
  75. package/dist/cli/ui.js +364 -0
  76. package/dist/cli/ui.js.map +1 -0
  77. package/dist/config/index.d.ts +7 -0
  78. package/dist/config/index.d.ts.map +1 -0
  79. package/dist/config/index.js +6 -0
  80. package/dist/config/index.js.map +1 -0
  81. package/dist/config/manager.d.ts +31 -0
  82. package/dist/config/manager.d.ts.map +1 -0
  83. package/dist/config/manager.js +65 -0
  84. package/dist/config/manager.js.map +1 -0
  85. package/dist/config/types.d.ts +22 -0
  86. package/dist/config/types.d.ts.map +1 -0
  87. package/dist/config/types.js +6 -0
  88. package/dist/config/types.js.map +1 -0
  89. package/dist/index.d.ts +12 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +21 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/memory/index.d.ts +10 -0
  94. package/dist/memory/index.d.ts.map +1 -0
  95. package/dist/memory/index.js +9 -0
  96. package/dist/memory/index.js.map +1 -0
  97. package/dist/memory/init.d.ts +20 -0
  98. package/dist/memory/init.d.ts.map +1 -0
  99. package/dist/memory/init.js +332 -0
  100. package/dist/memory/init.js.map +1 -0
  101. package/dist/memory/manager.d.ts +85 -0
  102. package/dist/memory/manager.d.ts.map +1 -0
  103. package/dist/memory/manager.js +234 -0
  104. package/dist/memory/manager.js.map +1 -0
  105. package/dist/memory/types.d.ts +74 -0
  106. package/dist/memory/types.d.ts.map +1 -0
  107. package/dist/memory/types.js +6 -0
  108. package/dist/memory/types.js.map +1 -0
  109. package/dist/permissions/index.d.ts +7 -0
  110. package/dist/permissions/index.d.ts.map +1 -0
  111. package/dist/permissions/index.js +6 -0
  112. package/dist/permissions/index.js.map +1 -0
  113. package/dist/permissions/manager.d.ts +32 -0
  114. package/dist/permissions/manager.d.ts.map +1 -0
  115. package/dist/permissions/manager.js +79 -0
  116. package/dist/permissions/manager.js.map +1 -0
  117. package/dist/permissions/types.d.ts +14 -0
  118. package/dist/permissions/types.d.ts.map +1 -0
  119. package/dist/permissions/types.js +17 -0
  120. package/dist/permissions/types.js.map +1 -0
  121. package/dist/providers/anthropic.d.ts +20 -0
  122. package/dist/providers/anthropic.d.ts.map +1 -0
  123. package/dist/providers/anthropic.js +185 -0
  124. package/dist/providers/anthropic.js.map +1 -0
  125. package/dist/providers/gemini.d.ts +21 -0
  126. package/dist/providers/gemini.d.ts.map +1 -0
  127. package/dist/providers/gemini.js +241 -0
  128. package/dist/providers/gemini.js.map +1 -0
  129. package/dist/providers/index.d.ts +34 -0
  130. package/dist/providers/index.d.ts.map +1 -0
  131. package/dist/providers/index.js +72 -0
  132. package/dist/providers/index.js.map +1 -0
  133. package/dist/providers/openai.d.ts +19 -0
  134. package/dist/providers/openai.d.ts.map +1 -0
  135. package/dist/providers/openai.js +221 -0
  136. package/dist/providers/openai.js.map +1 -0
  137. package/dist/providers/types.d.ts +125 -0
  138. package/dist/providers/types.d.ts.map +1 -0
  139. package/dist/providers/types.js +6 -0
  140. package/dist/providers/types.js.map +1 -0
  141. package/dist/session/index.d.ts +6 -0
  142. package/dist/session/index.d.ts.map +1 -0
  143. package/dist/session/index.js +6 -0
  144. package/dist/session/index.js.map +1 -0
  145. package/dist/session/manager.d.ts +101 -0
  146. package/dist/session/manager.d.ts.map +1 -0
  147. package/dist/session/manager.js +295 -0
  148. package/dist/session/manager.js.map +1 -0
  149. package/dist/session/types.d.ts +39 -0
  150. package/dist/session/types.d.ts.map +1 -0
  151. package/dist/session/types.js +10 -0
  152. package/dist/session/types.js.map +1 -0
  153. package/dist/tools/builtin/bash.d.ts +7 -0
  154. package/dist/tools/builtin/bash.d.ts.map +1 -0
  155. package/dist/tools/builtin/bash.js +80 -0
  156. package/dist/tools/builtin/bash.js.map +1 -0
  157. package/dist/tools/builtin/edit.d.ts +7 -0
  158. package/dist/tools/builtin/edit.d.ts.map +1 -0
  159. package/dist/tools/builtin/edit.js +32 -0
  160. package/dist/tools/builtin/edit.js.map +1 -0
  161. package/dist/tools/builtin/glob.d.ts +7 -0
  162. package/dist/tools/builtin/glob.d.ts.map +1 -0
  163. package/dist/tools/builtin/glob.js +36 -0
  164. package/dist/tools/builtin/glob.js.map +1 -0
  165. package/dist/tools/builtin/grep.d.ts +7 -0
  166. package/dist/tools/builtin/grep.d.ts.map +1 -0
  167. package/dist/tools/builtin/grep.js +59 -0
  168. package/dist/tools/builtin/grep.js.map +1 -0
  169. package/dist/tools/builtin/read.d.ts +7 -0
  170. package/dist/tools/builtin/read.d.ts.map +1 -0
  171. package/dist/tools/builtin/read.js +29 -0
  172. package/dist/tools/builtin/read.js.map +1 -0
  173. package/dist/tools/builtin/write.d.ts +7 -0
  174. package/dist/tools/builtin/write.d.ts.map +1 -0
  175. package/dist/tools/builtin/write.js +24 -0
  176. package/dist/tools/builtin/write.js.map +1 -0
  177. package/dist/tools/index.d.ts +38 -0
  178. package/dist/tools/index.d.ts.map +1 -0
  179. package/dist/tools/index.js +32 -0
  180. package/dist/tools/index.js.map +1 -0
  181. package/dist/tools/registry.d.ts +22 -0
  182. package/dist/tools/registry.d.ts.map +1 -0
  183. package/dist/tools/registry.js +71 -0
  184. package/dist/tools/registry.js.map +1 -0
  185. package/dist/tools/types.d.ts +62 -0
  186. package/dist/tools/types.d.ts.map +1 -0
  187. package/dist/tools/types.js +126 -0
  188. package/dist/tools/types.js.map +1 -0
  189. package/docs/README.md +16 -0
  190. package/docs/proposals/0001-web-fetch-tool.md +293 -0
  191. package/docs/proposals/0002-web-search-tool.md +306 -0
  192. package/docs/proposals/0003-task-subagents.md +333 -0
  193. package/docs/proposals/0004-plan-mode.md +338 -0
  194. package/docs/proposals/0005-todo-system.md +299 -0
  195. package/docs/proposals/0006-memory-system.md +539 -0
  196. package/docs/proposals/0007-context-management.md +429 -0
  197. package/docs/proposals/0008-checkpointing.md +327 -0
  198. package/docs/proposals/0009-hooks-system.md +343 -0
  199. package/docs/proposals/0010-mcp-integration.md +382 -0
  200. package/docs/proposals/0011-custom-commands.md +374 -0
  201. package/docs/proposals/0012-ask-user-question.md +317 -0
  202. package/docs/proposals/0013-multi-edit-tool.md +345 -0
  203. package/docs/proposals/0014-lsp-tool.md +478 -0
  204. package/docs/proposals/0015-ls-tool.md +407 -0
  205. package/docs/proposals/0016-kill-shell-tool.md +455 -0
  206. package/docs/proposals/0017-background-tasks.md +489 -0
  207. package/docs/proposals/0018-parallel-tool-execution.md +415 -0
  208. package/docs/proposals/0019-session-enhancements.md +462 -0
  209. package/docs/proposals/0020-session-summarization.md +447 -0
  210. package/docs/proposals/0021-skills-system.md +409 -0
  211. package/docs/proposals/0022-plugin-system.md +467 -0
  212. package/docs/proposals/0023-permission-enhancements.md +470 -0
  213. package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
  214. package/docs/proposals/0025-cost-tracking.md +447 -0
  215. package/docs/proposals/0026-git-integration.md +475 -0
  216. package/docs/proposals/0027-enhanced-read-tool.md +514 -0
  217. package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
  218. package/docs/proposals/0029-notebook-edit-tool.md +413 -0
  219. package/docs/proposals/0030-plugin-marketplace.md +360 -0
  220. package/docs/proposals/0031-command-suggestions.md +295 -0
  221. package/docs/proposals/0032-ide-integrations.md +328 -0
  222. package/docs/proposals/0033-enterprise-deployment.md +221 -0
  223. package/docs/proposals/0034-sandboxing.md +273 -0
  224. package/docs/proposals/0035-auto-updater.md +311 -0
  225. package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
  226. package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
  227. package/docs/proposals/0038-interactive-cli-ui.md +373 -0
  228. package/docs/proposals/0039-streaming-enhancements.md +359 -0
  229. package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
  230. package/docs/proposals/README.md +84 -0
  231. package/docs/proposals/TEMPLATE.md +57 -0
  232. package/docs/proposals/research/claude-code-research.md +307 -0
  233. package/examples/agent-demo.ts +115 -0
  234. package/examples/basic.ts +166 -0
  235. package/package.json +50 -0
  236. package/src/agent/agent.ts +276 -0
  237. package/src/agent/index.ts +6 -0
  238. package/src/agent/types.ts +62 -0
  239. package/src/cli/components/App.tsx +565 -0
  240. package/src/cli/components/CommandSuggestions.tsx +58 -0
  241. package/src/cli/components/Header.tsx +36 -0
  242. package/src/cli/components/Input.tsx +60 -0
  243. package/src/cli/components/Logo.tsx +16 -0
  244. package/src/cli/components/Messages.tsx +210 -0
  245. package/src/cli/components/ModelSelector.tsx +135 -0
  246. package/src/cli/components/Spinner.tsx +72 -0
  247. package/src/cli/components/index.ts +21 -0
  248. package/src/cli/components/theme.ts +36 -0
  249. package/src/cli/index.tsx +136 -0
  250. package/src/config/index.ts +7 -0
  251. package/src/config/manager.ts +77 -0
  252. package/src/config/types.ts +25 -0
  253. package/src/index.ts +86 -0
  254. package/src/permissions/index.ts +7 -0
  255. package/src/permissions/manager.ts +97 -0
  256. package/src/permissions/types.ts +29 -0
  257. package/src/providers/anthropic.ts +224 -0
  258. package/src/providers/gemini.ts +295 -0
  259. package/src/providers/index.ts +97 -0
  260. package/src/providers/openai.ts +261 -0
  261. package/src/providers/types.ts +181 -0
  262. package/src/session/index.ts +6 -0
  263. package/src/session/manager.ts +354 -0
  264. package/src/session/types.ts +49 -0
  265. package/src/tools/builtin/bash.ts +92 -0
  266. package/src/tools/builtin/edit.ts +37 -0
  267. package/src/tools/builtin/glob.ts +42 -0
  268. package/src/tools/builtin/grep.ts +67 -0
  269. package/src/tools/builtin/read.ts +34 -0
  270. package/src/tools/builtin/write.ts +27 -0
  271. package/src/tools/index.ts +36 -0
  272. package/src/tools/registry.ts +83 -0
  273. package/src/tools/types.ts +172 -0
  274. package/tsconfig.json +21 -0
@@ -0,0 +1,514 @@
1
+ # Proposal: Enhanced Read Tool
2
+
3
+ - **Proposal ID**: 0027
4
+ - **Author**: mycode team
5
+ - **Status**: Draft
6
+ - **Created**: 2025-01-15
7
+ - **Updated**: 2025-01-15
8
+
9
+ ## Summary
10
+
11
+ Enhance the Read tool with additional capabilities including image reading (multimodal), Jupyter notebook support, line range selection, and smart truncation. This enables the agent to handle diverse file types effectively.
12
+
13
+ ## Motivation
14
+
15
+ The current Read tool is basic:
16
+
17
+ 1. **Text only**: Can't read images
18
+ 2. **No notebooks**: Can't handle .ipynb files
19
+ 3. **Full file loading**: Always reads entire file
20
+ 4. **Simple truncation**: Just cuts off at limit
21
+ 5. **No syntax awareness**: Treats all files as plain text
22
+
23
+ Enhanced reading enables multimodal and structured file access.
24
+
25
+ ## Claude Code Reference
26
+
27
+ Claude Code's Read tool provides rich file reading:
28
+
29
+ ### From Tool Description
30
+ ```
31
+ - This tool allows Claude Code to read images (PNG, JPG, etc).
32
+ When reading an image file the contents are presented visually.
33
+ - This tool can read Jupyter notebooks (.ipynb files) and returns
34
+ all cells with their outputs.
35
+ - By default, reads up to 2000 lines from the beginning
36
+ - Optionally specify line offset and limit
37
+ - Lines longer than 2000 characters will be truncated
38
+ - Results returned using cat -n format (line numbers starting at 1)
39
+ ```
40
+
41
+ ### Key Features
42
+ - Multimodal image reading
43
+ - Jupyter notebook cell/output display
44
+ - Line number prefixes
45
+ - Offset and limit parameters
46
+ - Smart truncation
47
+
48
+ ## Detailed Design
49
+
50
+ ### API Design
51
+
52
+ ```typescript
53
+ // src/tools/read/types.ts
54
+ interface ReadInput {
55
+ file_path: string;
56
+ offset?: number; // Line number to start from (1-based)
57
+ limit?: number; // Number of lines to read
58
+ }
59
+
60
+ interface ReadOutput {
61
+ success: boolean;
62
+ content?: string;
63
+ contentType: ContentType;
64
+ lineCount: number;
65
+ truncated: boolean;
66
+ metadata?: FileMetadata;
67
+ error?: string;
68
+ }
69
+
70
+ type ContentType =
71
+ | 'text'
72
+ | 'image'
73
+ | 'notebook'
74
+ | 'binary';
75
+
76
+ interface FileMetadata {
77
+ size: number;
78
+ modified: Date;
79
+ encoding?: string;
80
+ mimeType?: string;
81
+ // For images
82
+ dimensions?: { width: number; height: number };
83
+ // For notebooks
84
+ cellCount?: number;
85
+ }
86
+
87
+ // Notebook types
88
+ interface NotebookCell {
89
+ cellType: 'code' | 'markdown' | 'raw';
90
+ source: string;
91
+ outputs?: CellOutput[];
92
+ executionCount?: number;
93
+ }
94
+
95
+ interface NotebookContent {
96
+ cells: NotebookCell[];
97
+ metadata: {
98
+ kernelspec?: { language: string; name: string };
99
+ };
100
+ }
101
+ ```
102
+
103
+ ### Enhanced Read Implementation
104
+
105
+ ```typescript
106
+ // src/tools/read/read-tool.ts
107
+ const readTool: Tool<ReadInput> = {
108
+ name: 'Read',
109
+ description: `Read file contents from the filesystem.
110
+
111
+ Parameters:
112
+ - file_path: Absolute path to the file
113
+ - offset: Line number to start from (1-based, optional)
114
+ - limit: Number of lines to read (default: 2000)
115
+
116
+ Features:
117
+ - Reads text files with line numbers
118
+ - Displays images visually (PNG, JPG, GIF, etc.)
119
+ - Renders Jupyter notebooks with cell outputs
120
+ - Smart truncation for large files
121
+ - Handles various encodings
122
+
123
+ Returns content in appropriate format based on file type.
124
+ `,
125
+ parameters: z.object({
126
+ file_path: z.string(),
127
+ offset: z.number().int().positive().optional(),
128
+ limit: z.number().int().positive().optional().default(2000)
129
+ }),
130
+ execute: async (input, context) => {
131
+ const fullPath = path.isAbsolute(input.file_path)
132
+ ? input.file_path
133
+ : path.resolve(context.cwd, input.file_path);
134
+
135
+ // Check file exists
136
+ if (!fs.existsSync(fullPath)) {
137
+ return { success: false, error: 'File does not exist' };
138
+ }
139
+
140
+ const stats = await fs.stat(fullPath);
141
+ const ext = path.extname(fullPath).toLowerCase();
142
+
143
+ // Route to appropriate handler
144
+ if (isImageExtension(ext)) {
145
+ return readImage(fullPath, stats);
146
+ }
147
+
148
+ if (ext === '.ipynb') {
149
+ return readNotebook(fullPath, stats);
150
+ }
151
+
152
+ if (isBinaryExtension(ext)) {
153
+ return { success: false, error: 'Binary files cannot be read as text' };
154
+ }
155
+
156
+ return readTextFile(fullPath, input.offset, input.limit, stats);
157
+ }
158
+ };
159
+ ```
160
+
161
+ ### Text File Reading
162
+
163
+ ```typescript
164
+ // src/tools/read/readers/text.ts
165
+ async function readTextFile(
166
+ filePath: string,
167
+ offset: number = 1,
168
+ limit: number = 2000,
169
+ stats: fs.Stats
170
+ ): Promise<ReadOutput> {
171
+ const content = await fs.readFile(filePath, 'utf-8');
172
+ const lines = content.split('\n');
173
+
174
+ const totalLines = lines.length;
175
+ const startLine = Math.max(1, offset) - 1; // Convert to 0-indexed
176
+ const endLine = Math.min(startLine + limit, totalLines);
177
+
178
+ const selectedLines = lines.slice(startLine, endLine);
179
+ let truncated = false;
180
+
181
+ // Format with line numbers (cat -n style)
182
+ const formatted = selectedLines.map((line, idx) => {
183
+ const lineNum = startLine + idx + 1;
184
+ const padding = String(totalLines).length;
185
+ const numStr = String(lineNum).padStart(padding, ' ');
186
+
187
+ // Truncate long lines
188
+ let displayLine = line;
189
+ if (line.length > 2000) {
190
+ displayLine = line.slice(0, 2000) + '... (truncated)';
191
+ truncated = true;
192
+ }
193
+
194
+ return `${numStr}\t${displayLine}`;
195
+ }).join('\n');
196
+
197
+ return {
198
+ success: true,
199
+ content: formatted,
200
+ contentType: 'text',
201
+ lineCount: selectedLines.length,
202
+ truncated: truncated || endLine < totalLines,
203
+ metadata: {
204
+ size: stats.size,
205
+ modified: stats.mtime,
206
+ encoding: 'utf-8'
207
+ }
208
+ };
209
+ }
210
+ ```
211
+
212
+ ### Image Reading
213
+
214
+ ```typescript
215
+ // src/tools/read/readers/image.ts
216
+ async function readImage(
217
+ filePath: string,
218
+ stats: fs.Stats
219
+ ): Promise<ReadOutput> {
220
+ const ext = path.extname(filePath).toLowerCase();
221
+ const mimeType = getMimeType(ext);
222
+
223
+ // Read as base64 for LLM consumption
224
+ const buffer = await fs.readFile(filePath);
225
+ const base64 = buffer.toString('base64');
226
+
227
+ // Get dimensions if possible
228
+ let dimensions: { width: number; height: number } | undefined;
229
+ try {
230
+ dimensions = await getImageDimensions(filePath);
231
+ } catch {
232
+ // Dimensions not available
233
+ }
234
+
235
+ return {
236
+ success: true,
237
+ content: `data:${mimeType};base64,${base64}`,
238
+ contentType: 'image',
239
+ lineCount: 0,
240
+ truncated: false,
241
+ metadata: {
242
+ size: stats.size,
243
+ modified: stats.mtime,
244
+ mimeType,
245
+ dimensions
246
+ }
247
+ };
248
+ }
249
+
250
+ function getMimeType(ext: string): string {
251
+ const mimeTypes: Record<string, string> = {
252
+ '.png': 'image/png',
253
+ '.jpg': 'image/jpeg',
254
+ '.jpeg': 'image/jpeg',
255
+ '.gif': 'image/gif',
256
+ '.webp': 'image/webp',
257
+ '.svg': 'image/svg+xml',
258
+ '.bmp': 'image/bmp'
259
+ };
260
+ return mimeTypes[ext] || 'application/octet-stream';
261
+ }
262
+ ```
263
+
264
+ ### Notebook Reading
265
+
266
+ ```typescript
267
+ // src/tools/read/readers/notebook.ts
268
+ async function readNotebook(
269
+ filePath: string,
270
+ stats: fs.Stats
271
+ ): Promise<ReadOutput> {
272
+ const content = await fs.readFile(filePath, 'utf-8');
273
+ const notebook = JSON.parse(content) as NotebookContent;
274
+
275
+ const formatted = formatNotebook(notebook);
276
+
277
+ return {
278
+ success: true,
279
+ content: formatted,
280
+ contentType: 'notebook',
281
+ lineCount: formatted.split('\n').length,
282
+ truncated: false,
283
+ metadata: {
284
+ size: stats.size,
285
+ modified: stats.mtime,
286
+ cellCount: notebook.cells.length
287
+ }
288
+ };
289
+ }
290
+
291
+ function formatNotebook(notebook: NotebookContent): string {
292
+ const parts: string[] = [];
293
+
294
+ // Header
295
+ const kernel = notebook.metadata?.kernelspec?.language || 'unknown';
296
+ parts.push(`# Jupyter Notebook (${kernel})`);
297
+ parts.push(`# ${notebook.cells.length} cells\n`);
298
+
299
+ // Cells
300
+ notebook.cells.forEach((cell, idx) => {
301
+ parts.push(`## Cell ${idx + 1} [${cell.cellType}]`);
302
+
303
+ if (cell.executionCount !== undefined) {
304
+ parts.push(`In [${cell.executionCount}]:`);
305
+ }
306
+
307
+ // Source
308
+ const source = Array.isArray(cell.source)
309
+ ? cell.source.join('')
310
+ : cell.source;
311
+
312
+ if (cell.cellType === 'code') {
313
+ parts.push('```');
314
+ parts.push(source.trim());
315
+ parts.push('```');
316
+ } else {
317
+ parts.push(source.trim());
318
+ }
319
+
320
+ // Outputs
321
+ if (cell.outputs?.length) {
322
+ parts.push('\n**Output:**');
323
+ for (const output of cell.outputs) {
324
+ parts.push(formatCellOutput(output));
325
+ }
326
+ }
327
+
328
+ parts.push(''); // Empty line between cells
329
+ });
330
+
331
+ return parts.join('\n');
332
+ }
333
+
334
+ function formatCellOutput(output: CellOutput): string {
335
+ switch (output.output_type) {
336
+ case 'stream':
337
+ return output.text?.join('') || '';
338
+ case 'execute_result':
339
+ case 'display_data':
340
+ if (output.data?.['text/plain']) {
341
+ return output.data['text/plain'].join('');
342
+ }
343
+ if (output.data?.['image/png']) {
344
+ return '[Image output]';
345
+ }
346
+ return '[Output data]';
347
+ case 'error':
348
+ return `Error: ${output.ename}: ${output.evalue}`;
349
+ default:
350
+ return '[Unknown output type]';
351
+ }
352
+ }
353
+ ```
354
+
355
+ ### File Changes
356
+
357
+ | File | Action | Description |
358
+ |------|--------|-------------|
359
+ | `src/tools/read/types.ts` | Create | Type definitions |
360
+ | `src/tools/read/read-tool.ts` | Modify | Enhanced tool |
361
+ | `src/tools/read/readers/text.ts` | Create | Text file reader |
362
+ | `src/tools/read/readers/image.ts` | Create | Image reader |
363
+ | `src/tools/read/readers/notebook.ts` | Create | Notebook reader |
364
+ | `src/tools/read/utils.ts` | Create | Shared utilities |
365
+ | `src/tools/read/index.ts` | Modify | Updated exports |
366
+
367
+ ## User Experience
368
+
369
+ ### Text File with Line Numbers
370
+ ```
371
+ Agent: [Read: file_path="src/auth.ts"]
372
+
373
+ 1 import { verify } from 'jsonwebtoken';
374
+ 2
375
+ 3 export interface AuthConfig {
376
+ 4 secret: string;
377
+ 5 expiresIn: string;
378
+ 6 }
379
+ 7
380
+ 8 export function validateToken(token: string): boolean {
381
+ 9 try {
382
+ 10 verify(token, process.env.JWT_SECRET);
383
+ 11 return true;
384
+ 12 } catch {
385
+ 13 return false;
386
+ 14 }
387
+ 15 }
388
+ ```
389
+
390
+ ### Image Reading
391
+ ```
392
+ Agent: [Read: file_path="docs/architecture.png"]
393
+
394
+ ┌─ Image: architecture.png ─────────────────────────┐
395
+ │ [Displayed as image in conversation] │
396
+ │ │
397
+ │ Size: 245 KB │
398
+ │ Dimensions: 1200 x 800 │
399
+ │ Type: PNG │
400
+ └───────────────────────────────────────────────────┘
401
+
402
+ This architecture diagram shows the three-tier design...
403
+ ```
404
+
405
+ ### Jupyter Notebook
406
+ ```
407
+ Agent: [Read: file_path="analysis.ipynb"]
408
+
409
+ # Jupyter Notebook (python)
410
+ # 5 cells
411
+
412
+ ## Cell 1 [code]
413
+ In [1]:
414
+ ```
415
+ import pandas as pd
416
+ import numpy as np
417
+ ```
418
+
419
+ ## Cell 2 [markdown]
420
+ # Data Analysis
421
+ This notebook analyzes the sales data...
422
+
423
+ ## Cell 3 [code]
424
+ In [2]:
425
+ ```
426
+ df = pd.read_csv('sales.csv')
427
+ df.head()
428
+ ```
429
+
430
+ **Output:**
431
+ date product sales
432
+ 0 2024-01-01 Widget A 100
433
+ 1 2024-01-02 Widget B 150
434
+ ...
435
+ ```
436
+
437
+ ### Range Reading
438
+ ```
439
+ Agent: [Read: file_path="large-file.ts", offset=100, limit=50]
440
+
441
+ Reading lines 100-149 of large-file.ts:
442
+
443
+ 100 async function processData() {
444
+ 101 const results = await fetch(url);
445
+ ...
446
+ 149 }
447
+
448
+ (Showing 50 of 1,234 total lines)
449
+ ```
450
+
451
+ ## Alternatives Considered
452
+
453
+ ### Alternative 1: Separate Image Tool
454
+ Create dedicated image reading tool.
455
+
456
+ **Pros**: Clear separation
457
+ **Cons**: More tools to manage
458
+ **Decision**: Rejected - Unified Read is cleaner
459
+
460
+ ### Alternative 2: External Libraries
461
+ Use sharp/jimp for image processing.
462
+
463
+ **Pros**: More image capabilities
464
+ **Cons**: Heavy dependencies
465
+ **Decision**: Deferred - Start with basic reading
466
+
467
+ ### Alternative 3: Always Stream
468
+ Stream all file reading.
469
+
470
+ **Pros**: Memory efficient
471
+ **Cons**: Complex for small files
472
+ **Decision**: Rejected - Simple read is fine for most
473
+
474
+ ## Security Considerations
475
+
476
+ 1. **Size Limits**: Limit file sizes (e.g., 50MB)
477
+ 2. **Path Validation**: Prevent path traversal
478
+ 3. **Binary Detection**: Don't read arbitrary binaries
479
+ 4. **Memory Management**: Handle large files carefully
480
+ 5. **Image Validation**: Validate image formats
481
+
482
+ ## Testing Strategy
483
+
484
+ 1. **Unit Tests**:
485
+ - Text file reading
486
+ - Line range selection
487
+ - Image base64 encoding
488
+ - Notebook parsing
489
+
490
+ 2. **Integration Tests**:
491
+ - Various file types
492
+ - Large files
493
+ - Edge cases
494
+
495
+ 3. **Manual Testing**:
496
+ - Real notebooks
497
+ - Various image formats
498
+ - Corrupted files
499
+
500
+ ## Migration Path
501
+
502
+ 1. **Phase 1**: Line numbers and range support
503
+ 2. **Phase 2**: Image reading
504
+ 3. **Phase 3**: Notebook support
505
+ 4. **Phase 4**: Better truncation
506
+ 5. **Phase 5**: Streaming for large files
507
+
508
+ Backward compatible with existing Read usage.
509
+
510
+ ## References
511
+
512
+ - [Jupyter Notebook Format](https://nbformat.readthedocs.io/)
513
+ - [Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)
514
+ - [File Type Detection](https://github.com/sindresorhus/file-type)