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,413 @@
1
+ # Proposal: NotebookEdit Tool
2
+
3
+ - **Proposal ID**: 0029
4
+ - **Author**: mycode team
5
+ - **Status**: Draft
6
+ - **Created**: 2025-01-15
7
+ - **Updated**: 2025-01-15
8
+
9
+ ## Summary
10
+
11
+ Implement a NotebookEdit tool for modifying Jupyter notebook cells, enabling the agent to create, edit, and manage interactive notebooks for data science and documentation workflows.
12
+
13
+ ## Motivation
14
+
15
+ Jupyter notebooks require special handling:
16
+
17
+ 1. **JSON structure**: Can't use regular Edit tool
18
+ 2. **Cell-based editing**: Need cell-level operations
19
+ 3. **Output preservation**: Must maintain cell outputs
20
+ 4. **Metadata handling**: Cell metadata needs preservation
21
+ 5. **Data science workflows**: Notebooks are essential for ML/data work
22
+
23
+ NotebookEdit enables proper notebook manipulation.
24
+
25
+ ## Claude Code Reference
26
+
27
+ Claude Code's NotebookEdit tool provides cell-level operations:
28
+
29
+ ### From Tool Description
30
+ ```
31
+ Completely replaces the contents of a specific cell in a Jupyter notebook.
32
+ - notebook_path: absolute path to the .ipynb file
33
+ - cell_id: ID of the cell to edit (or position for insert)
34
+ - cell_type: 'code' or 'markdown'
35
+ - new_source: The new content for the cell
36
+ - edit_mode: 'replace', 'insert', or 'delete'
37
+ ```
38
+
39
+ ### Operations
40
+ - **replace**: Replace cell content by ID
41
+ - **insert**: Add new cell at position
42
+ - **delete**: Remove cell by ID
43
+
44
+ ## Detailed Design
45
+
46
+ ### API Design
47
+
48
+ ```typescript
49
+ // src/tools/notebook-edit/types.ts
50
+ type CellType = 'code' | 'markdown' | 'raw';
51
+ type EditMode = 'replace' | 'insert' | 'delete';
52
+
53
+ interface NotebookEditInput {
54
+ notebook_path: string;
55
+ cell_id?: string; // Cell ID for replace/delete
56
+ cell_type?: CellType; // Required for insert
57
+ new_source: string; // New cell content
58
+ edit_mode?: EditMode; // Default: 'replace'
59
+ }
60
+
61
+ interface NotebookEditOutput {
62
+ success: boolean;
63
+ cell_id?: string; // ID of affected cell
64
+ cell_index?: number; // Position of affected cell
65
+ error?: string;
66
+ }
67
+
68
+ // Jupyter notebook structure
69
+ interface JupyterNotebook {
70
+ nbformat: number;
71
+ nbformat_minor: number;
72
+ metadata: NotebookMetadata;
73
+ cells: JupyterCell[];
74
+ }
75
+
76
+ interface JupyterCell {
77
+ id?: string; // Cell ID (nbformat 4.5+)
78
+ cell_type: CellType;
79
+ source: string | string[];
80
+ metadata: CellMetadata;
81
+ outputs?: CellOutput[]; // For code cells
82
+ execution_count?: number | null;
83
+ }
84
+
85
+ interface CellMetadata {
86
+ id?: string;
87
+ tags?: string[];
88
+ [key: string]: unknown;
89
+ }
90
+
91
+ interface CellOutput {
92
+ output_type: 'stream' | 'execute_result' | 'display_data' | 'error';
93
+ [key: string]: unknown;
94
+ }
95
+ ```
96
+
97
+ ### NotebookEdit Tool
98
+
99
+ ```typescript
100
+ // src/tools/notebook-edit/notebook-edit-tool.ts
101
+ const notebookEditTool: Tool<NotebookEditInput> = {
102
+ name: 'NotebookEdit',
103
+ description: `Edit Jupyter notebook cells.
104
+
105
+ Parameters:
106
+ - notebook_path: Absolute path to the .ipynb file
107
+ - cell_id: ID of the cell to edit (for replace/delete)
108
+ - cell_type: 'code' or 'markdown' (required for insert)
109
+ - new_source: New content for the cell
110
+ - edit_mode: 'replace' (default), 'insert', or 'delete'
111
+
112
+ Operations:
113
+ - replace: Replace content of existing cell by ID
114
+ - insert: Add new cell after specified cell_id (or at start if not specified)
115
+ - delete: Remove cell by ID
116
+
117
+ Notes:
118
+ - Cell IDs are preserved when editing
119
+ - Cell outputs are cleared when replacing code cells
120
+ - Use 0-indexed position or cell_id for targeting
121
+ `,
122
+ parameters: z.object({
123
+ notebook_path: z.string(),
124
+ cell_id: z.string().optional(),
125
+ cell_type: z.enum(['code', 'markdown', 'raw']).optional(),
126
+ new_source: z.string(),
127
+ edit_mode: z.enum(['replace', 'insert', 'delete']).optional().default('replace')
128
+ }),
129
+ execute: async (input, context) => {
130
+ return notebookEditor.edit(input, context);
131
+ }
132
+ };
133
+ ```
134
+
135
+ ### Notebook Editor
136
+
137
+ ```typescript
138
+ // src/tools/notebook-edit/editor.ts
139
+ class NotebookEditor {
140
+ async edit(input: NotebookEditInput, context: ToolContext): Promise<NotebookEditOutput> {
141
+ const fullPath = path.isAbsolute(input.notebook_path)
142
+ ? input.notebook_path
143
+ : path.resolve(context.cwd, input.notebook_path);
144
+
145
+ // Read notebook
146
+ if (!fs.existsSync(fullPath)) {
147
+ return { success: false, error: 'Notebook not found' };
148
+ }
149
+
150
+ const content = await fs.readFile(fullPath, 'utf-8');
151
+ let notebook: JupyterNotebook;
152
+
153
+ try {
154
+ notebook = JSON.parse(content);
155
+ } catch {
156
+ return { success: false, error: 'Invalid notebook format' };
157
+ }
158
+
159
+ // Perform operation
160
+ let result: NotebookEditOutput;
161
+
162
+ switch (input.edit_mode) {
163
+ case 'replace':
164
+ result = this.replaceCell(notebook, input);
165
+ break;
166
+ case 'insert':
167
+ result = this.insertCell(notebook, input);
168
+ break;
169
+ case 'delete':
170
+ result = this.deleteCell(notebook, input);
171
+ break;
172
+ default:
173
+ return { success: false, error: 'Invalid edit mode' };
174
+ }
175
+
176
+ if (!result.success) return result;
177
+
178
+ // Write notebook back
179
+ await fs.writeFile(fullPath, JSON.stringify(notebook, null, 1), 'utf-8');
180
+
181
+ return result;
182
+ }
183
+
184
+ private replaceCell(
185
+ notebook: JupyterNotebook,
186
+ input: NotebookEditInput
187
+ ): NotebookEditOutput {
188
+ const cellIndex = this.findCellIndex(notebook, input.cell_id);
189
+
190
+ if (cellIndex === -1) {
191
+ return { success: false, error: `Cell not found: ${input.cell_id}` };
192
+ }
193
+
194
+ const cell = notebook.cells[cellIndex];
195
+
196
+ // Update source
197
+ cell.source = this.normalizeSource(input.new_source);
198
+
199
+ // Update cell type if specified
200
+ if (input.cell_type) {
201
+ cell.cell_type = input.cell_type;
202
+ }
203
+
204
+ // Clear outputs for code cells
205
+ if (cell.cell_type === 'code') {
206
+ cell.outputs = [];
207
+ cell.execution_count = null;
208
+ }
209
+
210
+ return {
211
+ success: true,
212
+ cell_id: cell.id || input.cell_id,
213
+ cell_index: cellIndex
214
+ };
215
+ }
216
+
217
+ private insertCell(
218
+ notebook: JupyterNotebook,
219
+ input: NotebookEditInput
220
+ ): NotebookEditOutput {
221
+ if (!input.cell_type) {
222
+ return { success: false, error: 'cell_type required for insert' };
223
+ }
224
+
225
+ const newCell: JupyterCell = {
226
+ id: this.generateCellId(),
227
+ cell_type: input.cell_type,
228
+ source: this.normalizeSource(input.new_source),
229
+ metadata: {}
230
+ };
231
+
232
+ if (input.cell_type === 'code') {
233
+ newCell.outputs = [];
234
+ newCell.execution_count = null;
235
+ }
236
+
237
+ // Find insertion position
238
+ let insertIndex: number;
239
+ if (input.cell_id) {
240
+ const refIndex = this.findCellIndex(notebook, input.cell_id);
241
+ if (refIndex === -1) {
242
+ return { success: false, error: `Reference cell not found: ${input.cell_id}` };
243
+ }
244
+ insertIndex = refIndex + 1;
245
+ } else {
246
+ insertIndex = 0; // Insert at beginning
247
+ }
248
+
249
+ notebook.cells.splice(insertIndex, 0, newCell);
250
+
251
+ return {
252
+ success: true,
253
+ cell_id: newCell.id,
254
+ cell_index: insertIndex
255
+ };
256
+ }
257
+
258
+ private deleteCell(
259
+ notebook: JupyterNotebook,
260
+ input: NotebookEditInput
261
+ ): NotebookEditOutput {
262
+ if (!input.cell_id) {
263
+ return { success: false, error: 'cell_id required for delete' };
264
+ }
265
+
266
+ const cellIndex = this.findCellIndex(notebook, input.cell_id);
267
+
268
+ if (cellIndex === -1) {
269
+ return { success: false, error: `Cell not found: ${input.cell_id}` };
270
+ }
271
+
272
+ notebook.cells.splice(cellIndex, 1);
273
+
274
+ return {
275
+ success: true,
276
+ cell_id: input.cell_id,
277
+ cell_index: cellIndex
278
+ };
279
+ }
280
+
281
+ private findCellIndex(notebook: JupyterNotebook, cellId?: string): number {
282
+ if (!cellId) return -1;
283
+
284
+ // Try to find by ID
285
+ const byId = notebook.cells.findIndex(c => c.id === cellId);
286
+ if (byId !== -1) return byId;
287
+
288
+ // Try to parse as index
289
+ const index = parseInt(cellId, 10);
290
+ if (!isNaN(index) && index >= 0 && index < notebook.cells.length) {
291
+ return index;
292
+ }
293
+
294
+ return -1;
295
+ }
296
+
297
+ private normalizeSource(source: string): string[] {
298
+ // Jupyter stores source as array of lines
299
+ return source.split('\n').map((line, i, arr) =>
300
+ i < arr.length - 1 ? line + '\n' : line
301
+ );
302
+ }
303
+
304
+ private generateCellId(): string {
305
+ return crypto.randomUUID().replace(/-/g, '').slice(0, 8);
306
+ }
307
+ }
308
+
309
+ export const notebookEditor = new NotebookEditor();
310
+ ```
311
+
312
+ ### File Changes
313
+
314
+ | File | Action | Description |
315
+ |------|--------|-------------|
316
+ | `src/tools/notebook-edit/types.ts` | Create | Type definitions |
317
+ | `src/tools/notebook-edit/notebook-edit-tool.ts` | Create | Tool implementation |
318
+ | `src/tools/notebook-edit/editor.ts` | Create | Notebook editing logic |
319
+ | `src/tools/notebook-edit/index.ts` | Create | Module exports |
320
+ | `src/tools/index.ts` | Modify | Register tool |
321
+
322
+ ## User Experience
323
+
324
+ ### Replace Cell
325
+ ```
326
+ Agent: [NotebookEdit:
327
+ notebook_path="analysis.ipynb"
328
+ cell_id="abc123"
329
+ new_source="import pandas as pd\nimport numpy as np"
330
+ ]
331
+
332
+ ✓ Cell updated
333
+ Notebook: analysis.ipynb
334
+ Cell: abc123 (index 0)
335
+ Type: code
336
+ ```
337
+
338
+ ### Insert Cell
339
+ ```
340
+ Agent: [NotebookEdit:
341
+ notebook_path="analysis.ipynb"
342
+ cell_id="abc123"
343
+ cell_type="markdown"
344
+ new_source="# Data Analysis\n\nThis section analyzes..."
345
+ edit_mode="insert"
346
+ ]
347
+
348
+ ✓ Cell inserted
349
+ Notebook: analysis.ipynb
350
+ New cell: def456 (index 1)
351
+ Type: markdown
352
+ Inserted after: abc123
353
+ ```
354
+
355
+ ### Delete Cell
356
+ ```
357
+ Agent: [NotebookEdit:
358
+ notebook_path="analysis.ipynb"
359
+ cell_id="xyz789"
360
+ edit_mode="delete"
361
+ ]
362
+
363
+ ✓ Cell deleted
364
+ Notebook: analysis.ipynb
365
+ Removed: xyz789 (was at index 5)
366
+ ```
367
+
368
+ ## Alternatives Considered
369
+
370
+ ### Alternative 1: Full Notebook Rewrite
371
+ Use Write tool to replace entire notebook.
372
+
373
+ **Pros**: Simpler
374
+ **Cons**: Loses outputs, risky
375
+ **Decision**: Rejected - Need cell-level precision
376
+
377
+ ### Alternative 2: JupyterLab Extension
378
+ Integrate with running JupyterLab.
379
+
380
+ **Pros**: Full notebook capabilities
381
+ **Cons**: Requires running server
382
+ **Decision**: Deferred - Start with file-based
383
+
384
+ ## Security Considerations
385
+
386
+ 1. **Path Validation**: Validate notebook paths
387
+ 2. **JSON Validation**: Verify notebook structure
388
+ 3. **Output Preservation**: Don't execute arbitrary code
389
+ 4. **Backup Creation**: Optionally backup before edit
390
+
391
+ ## Testing Strategy
392
+
393
+ 1. **Unit Tests**:
394
+ - Cell operations
395
+ - ID generation
396
+ - Source normalization
397
+
398
+ 2. **Integration Tests**:
399
+ - Full edit workflows
400
+ - Multiple operations
401
+ - Edge cases
402
+
403
+ ## Migration Path
404
+
405
+ 1. **Phase 1**: Basic replace/insert/delete
406
+ 2. **Phase 2**: Batch operations
407
+ 3. **Phase 3**: Output preservation options
408
+ 4. **Phase 4**: Notebook creation
409
+
410
+ ## References
411
+
412
+ - [Jupyter Notebook Format](https://nbformat.readthedocs.io/)
413
+ - [nbformat Python Package](https://github.com/jupyter/nbformat)