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.
- package/.env.example +11 -0
- package/CLAUDE.md +70 -0
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/agent/agent.d.ts +84 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +233 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/index.d.ts +6 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +6 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/types.d.ts +47 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/components/App.d.ts +14 -0
- package/dist/cli/components/App.d.ts.map +1 -0
- package/dist/cli/components/App.js +395 -0
- package/dist/cli/components/App.js.map +1 -0
- package/dist/cli/components/CommandSuggestions.d.ts +13 -0
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
- package/dist/cli/components/CommandSuggestions.js +32 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -0
- package/dist/cli/components/Header.d.ts +9 -0
- package/dist/cli/components/Header.d.ts.map +1 -0
- package/dist/cli/components/Header.js +13 -0
- package/dist/cli/components/Header.js.map +1 -0
- package/dist/cli/components/Input.d.ts +13 -0
- package/dist/cli/components/Input.d.ts.map +1 -0
- package/dist/cli/components/Input.js +27 -0
- package/dist/cli/components/Input.js.map +1 -0
- package/dist/cli/components/Logo.d.ts +2 -0
- package/dist/cli/components/Logo.d.ts.map +1 -0
- package/dist/cli/components/Logo.js +8 -0
- package/dist/cli/components/Logo.js.map +1 -0
- package/dist/cli/components/Messages.d.ts +37 -0
- package/dist/cli/components/Messages.d.ts.map +1 -0
- package/dist/cli/components/Messages.js +106 -0
- package/dist/cli/components/Messages.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +13 -0
- package/dist/cli/components/ModelSelector.d.ts.map +1 -0
- package/dist/cli/components/ModelSelector.js +72 -0
- package/dist/cli/components/ModelSelector.js.map +1 -0
- package/dist/cli/components/Spinner.d.ts +12 -0
- package/dist/cli/components/Spinner.d.ts.map +1 -0
- package/dist/cli/components/Spinner.js +45 -0
- package/dist/cli/components/Spinner.js.map +1 -0
- package/dist/cli/components/index.d.ts +12 -0
- package/dist/cli/components/index.d.ts.map +1 -0
- package/dist/cli/components/index.js +12 -0
- package/dist/cli/components/index.js.map +1 -0
- package/dist/cli/components/theme.d.ts +31 -0
- package/dist/cli/components/theme.d.ts.map +1 -0
- package/dist/cli/components/theme.js +36 -0
- package/dist/cli/components/theme.js.map +1 -0
- package/dist/cli/index-legacy.d.ts +7 -0
- package/dist/cli/index-legacy.d.ts.map +1 -0
- package/dist/cli/index-legacy.js +431 -0
- package/dist/cli/index-legacy.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ink-cli.d.ts +7 -0
- package/dist/cli/ink-cli.d.ts.map +1 -0
- package/dist/cli/ink-cli.js +105 -0
- package/dist/cli/ink-cli.js.map +1 -0
- package/dist/cli/session-picker.d.ts +16 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +280 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/ui.d.ts +61 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +364 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +31 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +65 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/types.d.ts +22 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +6 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +10 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/init.d.ts +20 -0
- package/dist/memory/init.d.ts.map +1 -0
- package/dist/memory/init.js +332 -0
- package/dist/memory/init.js.map +1 -0
- package/dist/memory/manager.d.ts +85 -0
- package/dist/memory/manager.d.ts.map +1 -0
- package/dist/memory/manager.js +234 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/types.d.ts +74 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +6 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/permissions/index.d.ts +7 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/manager.d.ts +32 -0
- package/dist/permissions/manager.d.ts.map +1 -0
- package/dist/permissions/manager.js +79 -0
- package/dist/permissions/manager.js.map +1 -0
- package/dist/permissions/types.d.ts +14 -0
- package/dist/permissions/types.d.ts.map +1 -0
- package/dist/permissions/types.js +17 -0
- package/dist/permissions/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +20 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +185 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +21 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +241 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/index.d.ts +34 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +72 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +19 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +221 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +125 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +6 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +101 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +295 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/types.d.ts +39 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +10 -0
- package/dist/session/types.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +7 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +80 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit.d.ts +7 -0
- package/dist/tools/builtin/edit.d.ts.map +1 -0
- package/dist/tools/builtin/edit.js +32 -0
- package/dist/tools/builtin/edit.js.map +1 -0
- package/dist/tools/builtin/glob.d.ts +7 -0
- package/dist/tools/builtin/glob.d.ts.map +1 -0
- package/dist/tools/builtin/glob.js +36 -0
- package/dist/tools/builtin/glob.js.map +1 -0
- package/dist/tools/builtin/grep.d.ts +7 -0
- package/dist/tools/builtin/grep.d.ts.map +1 -0
- package/dist/tools/builtin/grep.js +59 -0
- package/dist/tools/builtin/grep.js.map +1 -0
- package/dist/tools/builtin/read.d.ts +7 -0
- package/dist/tools/builtin/read.d.ts.map +1 -0
- package/dist/tools/builtin/read.js +29 -0
- package/dist/tools/builtin/read.js.map +1 -0
- package/dist/tools/builtin/write.d.ts +7 -0
- package/dist/tools/builtin/write.d.ts.map +1 -0
- package/dist/tools/builtin/write.js +24 -0
- package/dist/tools/builtin/write.js.map +1 -0
- package/dist/tools/index.d.ts +38 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +32 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +22 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +71 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +62 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +126 -0
- package/dist/tools/types.js.map +1 -0
- package/docs/README.md +16 -0
- package/docs/proposals/0001-web-fetch-tool.md +293 -0
- package/docs/proposals/0002-web-search-tool.md +306 -0
- package/docs/proposals/0003-task-subagents.md +333 -0
- package/docs/proposals/0004-plan-mode.md +338 -0
- package/docs/proposals/0005-todo-system.md +299 -0
- package/docs/proposals/0006-memory-system.md +539 -0
- package/docs/proposals/0007-context-management.md +429 -0
- package/docs/proposals/0008-checkpointing.md +327 -0
- package/docs/proposals/0009-hooks-system.md +343 -0
- package/docs/proposals/0010-mcp-integration.md +382 -0
- package/docs/proposals/0011-custom-commands.md +374 -0
- package/docs/proposals/0012-ask-user-question.md +317 -0
- package/docs/proposals/0013-multi-edit-tool.md +345 -0
- package/docs/proposals/0014-lsp-tool.md +478 -0
- package/docs/proposals/0015-ls-tool.md +407 -0
- package/docs/proposals/0016-kill-shell-tool.md +455 -0
- package/docs/proposals/0017-background-tasks.md +489 -0
- package/docs/proposals/0018-parallel-tool-execution.md +415 -0
- package/docs/proposals/0019-session-enhancements.md +462 -0
- package/docs/proposals/0020-session-summarization.md +447 -0
- package/docs/proposals/0021-skills-system.md +409 -0
- package/docs/proposals/0022-plugin-system.md +467 -0
- package/docs/proposals/0023-permission-enhancements.md +470 -0
- package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
- package/docs/proposals/0025-cost-tracking.md +447 -0
- package/docs/proposals/0026-git-integration.md +475 -0
- package/docs/proposals/0027-enhanced-read-tool.md +514 -0
- package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
- package/docs/proposals/0029-notebook-edit-tool.md +413 -0
- package/docs/proposals/0030-plugin-marketplace.md +360 -0
- package/docs/proposals/0031-command-suggestions.md +295 -0
- package/docs/proposals/0032-ide-integrations.md +328 -0
- package/docs/proposals/0033-enterprise-deployment.md +221 -0
- package/docs/proposals/0034-sandboxing.md +273 -0
- package/docs/proposals/0035-auto-updater.md +311 -0
- package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
- package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
- package/docs/proposals/0038-interactive-cli-ui.md +373 -0
- package/docs/proposals/0039-streaming-enhancements.md +359 -0
- package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
- package/docs/proposals/README.md +84 -0
- package/docs/proposals/TEMPLATE.md +57 -0
- package/docs/proposals/research/claude-code-research.md +307 -0
- package/examples/agent-demo.ts +115 -0
- package/examples/basic.ts +166 -0
- package/package.json +50 -0
- package/src/agent/agent.ts +276 -0
- package/src/agent/index.ts +6 -0
- package/src/agent/types.ts +62 -0
- package/src/cli/components/App.tsx +565 -0
- package/src/cli/components/CommandSuggestions.tsx +58 -0
- package/src/cli/components/Header.tsx +36 -0
- package/src/cli/components/Input.tsx +60 -0
- package/src/cli/components/Logo.tsx +16 -0
- package/src/cli/components/Messages.tsx +210 -0
- package/src/cli/components/ModelSelector.tsx +135 -0
- package/src/cli/components/Spinner.tsx +72 -0
- package/src/cli/components/index.ts +21 -0
- package/src/cli/components/theme.ts +36 -0
- package/src/cli/index.tsx +136 -0
- package/src/config/index.ts +7 -0
- package/src/config/manager.ts +77 -0
- package/src/config/types.ts +25 -0
- package/src/index.ts +86 -0
- package/src/permissions/index.ts +7 -0
- package/src/permissions/manager.ts +97 -0
- package/src/permissions/types.ts +29 -0
- package/src/providers/anthropic.ts +224 -0
- package/src/providers/gemini.ts +295 -0
- package/src/providers/index.ts +97 -0
- package/src/providers/openai.ts +261 -0
- package/src/providers/types.ts +181 -0
- package/src/session/index.ts +6 -0
- package/src/session/manager.ts +354 -0
- package/src/session/types.ts +49 -0
- package/src/tools/builtin/bash.ts +92 -0
- package/src/tools/builtin/edit.ts +37 -0
- package/src/tools/builtin/glob.ts +42 -0
- package/src/tools/builtin/grep.ts +67 -0
- package/src/tools/builtin/read.ts +34 -0
- package/src/tools/builtin/write.ts +27 -0
- package/src/tools/index.ts +36 -0
- package/src/tools/registry.ts +83 -0
- package/src/tools/types.ts +172 -0
- 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)
|