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,345 @@
|
|
|
1
|
+
# Proposal: MultiEdit Tool
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0013
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Implement a MultiEdit tool that performs batch string replacements across multiple files in a single atomic operation. This enables efficient refactoring, renaming, and coordinated multi-file changes without multiple individual Edit tool calls.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
Currently, mycode's Edit tool operates on one file at a time. This leads to:
|
|
16
|
+
|
|
17
|
+
1. **Inefficiency**: Renaming a variable across 10 files requires 10 separate Edit calls
|
|
18
|
+
2. **Non-atomic changes**: Partial failures leave codebase in inconsistent state
|
|
19
|
+
3. **Context overhead**: Each Edit call consumes conversation context
|
|
20
|
+
4. **Poor UX**: Users see many individual tool calls for conceptually single operation
|
|
21
|
+
5. **Error-prone**: Easy to miss files when doing manual multi-file edits
|
|
22
|
+
|
|
23
|
+
A MultiEdit tool enables atomic, efficient, multi-file operations.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code provides batch editing through its Edit tool with `replace_all` flag and coordinated multi-call patterns:
|
|
28
|
+
|
|
29
|
+
### Current Edit Tool Pattern
|
|
30
|
+
```typescript
|
|
31
|
+
Edit({
|
|
32
|
+
file_path: "/path/to/file.ts",
|
|
33
|
+
old_string: "oldName",
|
|
34
|
+
new_string: "newName",
|
|
35
|
+
replace_all: true // Replace all occurrences in file
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### BatchTool Pattern (from /init)
|
|
40
|
+
Claude Code uses BatchTool for coordinated operations:
|
|
41
|
+
```typescript
|
|
42
|
+
BatchTool({
|
|
43
|
+
description: "Rename variable across files",
|
|
44
|
+
invocations: [
|
|
45
|
+
{ tool_name: "Edit", input: { file_path: "file1.ts", ... } },
|
|
46
|
+
{ tool_name: "Edit", input: { file_path: "file2.ts", ... } },
|
|
47
|
+
{ tool_name: "Edit", input: { file_path: "file3.ts", ... } }
|
|
48
|
+
]
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Key Characteristics
|
|
53
|
+
- Atomic: All or nothing execution
|
|
54
|
+
- Parallel execution where possible
|
|
55
|
+
- Rollback on failure
|
|
56
|
+
- Single result summary
|
|
57
|
+
|
|
58
|
+
## Detailed Design
|
|
59
|
+
|
|
60
|
+
### API Design
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// src/tools/multi-edit/types.ts
|
|
64
|
+
interface FileEdit {
|
|
65
|
+
file_path: string;
|
|
66
|
+
old_string: string;
|
|
67
|
+
new_string: string;
|
|
68
|
+
replace_all?: boolean; // Default: false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface MultiEditInput {
|
|
72
|
+
description: string; // What this batch edit does
|
|
73
|
+
edits: FileEdit[]; // Array of file edits
|
|
74
|
+
atomic?: boolean; // Rollback all on any failure (default: true)
|
|
75
|
+
dry_run?: boolean; // Preview changes without applying
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface EditResult {
|
|
79
|
+
file_path: string;
|
|
80
|
+
success: boolean;
|
|
81
|
+
replacements: number; // Number of replacements made
|
|
82
|
+
error?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface MultiEditOutput {
|
|
86
|
+
success: boolean;
|
|
87
|
+
results: EditResult[];
|
|
88
|
+
summary: {
|
|
89
|
+
total_files: number;
|
|
90
|
+
successful_files: number;
|
|
91
|
+
failed_files: number;
|
|
92
|
+
total_replacements: number;
|
|
93
|
+
};
|
|
94
|
+
error?: string;
|
|
95
|
+
rollback_performed?: boolean;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// src/tools/multi-edit/multi-edit-tool.ts
|
|
101
|
+
const multiEditTool: Tool<MultiEditInput> = {
|
|
102
|
+
name: 'MultiEdit',
|
|
103
|
+
description: `Perform batch edits across multiple files atomically.
|
|
104
|
+
|
|
105
|
+
Features:
|
|
106
|
+
- Edit multiple files in a single operation
|
|
107
|
+
- Atomic mode: rollback all changes if any edit fails
|
|
108
|
+
- Dry run mode: preview changes without applying
|
|
109
|
+
- Replace all occurrences within each file
|
|
110
|
+
|
|
111
|
+
Usage:
|
|
112
|
+
- Provide array of edits with file paths and replacements
|
|
113
|
+
- Each edit specifies old_string and new_string
|
|
114
|
+
- Use replace_all: true for global replacement in file
|
|
115
|
+
- Use dry_run: true to preview changes
|
|
116
|
+
|
|
117
|
+
Best for:
|
|
118
|
+
- Variable/function renaming across codebase
|
|
119
|
+
- Import path updates
|
|
120
|
+
- Configuration value changes
|
|
121
|
+
- License header updates
|
|
122
|
+
`,
|
|
123
|
+
parameters: z.object({
|
|
124
|
+
description: z.string().min(1),
|
|
125
|
+
edits: z.array(z.object({
|
|
126
|
+
file_path: z.string(),
|
|
127
|
+
old_string: z.string(),
|
|
128
|
+
new_string: z.string(),
|
|
129
|
+
replace_all: z.boolean().optional()
|
|
130
|
+
})).min(1),
|
|
131
|
+
atomic: z.boolean().optional().default(true),
|
|
132
|
+
dry_run: z.boolean().optional().default(false)
|
|
133
|
+
}),
|
|
134
|
+
execute: async (input, context) => { ... }
|
|
135
|
+
};
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Implementation Approach
|
|
139
|
+
|
|
140
|
+
1. **Pre-validation**: Verify all files exist and are readable
|
|
141
|
+
2. **Backup creation**: Store original content for rollback
|
|
142
|
+
3. **Parallel execution**: Process independent files concurrently
|
|
143
|
+
4. **Atomic commit**: Apply all changes or rollback on failure
|
|
144
|
+
5. **Result aggregation**: Summarize all edit results
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Core implementation
|
|
148
|
+
async function executeMultiEdit(
|
|
149
|
+
input: MultiEditInput,
|
|
150
|
+
context: ToolContext
|
|
151
|
+
): Promise<MultiEditOutput> {
|
|
152
|
+
const { edits, atomic, dry_run } = input;
|
|
153
|
+
const backups: Map<string, string> = new Map();
|
|
154
|
+
const results: EditResult[] = [];
|
|
155
|
+
|
|
156
|
+
// Phase 1: Validate and backup
|
|
157
|
+
for (const edit of edits) {
|
|
158
|
+
const fullPath = resolvePath(edit.file_path, context.cwd);
|
|
159
|
+
if (!await fileExists(fullPath)) {
|
|
160
|
+
if (atomic) {
|
|
161
|
+
return { success: false, error: `File not found: ${edit.file_path}` };
|
|
162
|
+
}
|
|
163
|
+
results.push({ file_path: edit.file_path, success: false, error: 'File not found' });
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const content = await readFile(fullPath);
|
|
167
|
+
backups.set(fullPath, content);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Phase 2: Apply edits
|
|
171
|
+
for (const edit of edits) {
|
|
172
|
+
const fullPath = resolvePath(edit.file_path, context.cwd);
|
|
173
|
+
const original = backups.get(fullPath);
|
|
174
|
+
if (!original) continue;
|
|
175
|
+
|
|
176
|
+
// Check if old_string exists
|
|
177
|
+
if (!original.includes(edit.old_string)) {
|
|
178
|
+
if (atomic) {
|
|
179
|
+
await rollbackAll(backups);
|
|
180
|
+
return { success: false, error: `String not found in ${edit.file_path}` };
|
|
181
|
+
}
|
|
182
|
+
results.push({ file_path: edit.file_path, success: false, error: 'String not found' });
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Perform replacement
|
|
187
|
+
const newContent = edit.replace_all
|
|
188
|
+
? original.replaceAll(edit.old_string, edit.new_string)
|
|
189
|
+
: original.replace(edit.old_string, edit.new_string);
|
|
190
|
+
|
|
191
|
+
const replacements = countReplacements(original, newContent, edit.old_string);
|
|
192
|
+
|
|
193
|
+
if (!dry_run) {
|
|
194
|
+
await writeFile(fullPath, newContent);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
results.push({
|
|
198
|
+
file_path: edit.file_path,
|
|
199
|
+
success: true,
|
|
200
|
+
replacements
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Phase 3: Generate summary
|
|
205
|
+
return {
|
|
206
|
+
success: results.every(r => r.success),
|
|
207
|
+
results,
|
|
208
|
+
summary: {
|
|
209
|
+
total_files: edits.length,
|
|
210
|
+
successful_files: results.filter(r => r.success).length,
|
|
211
|
+
failed_files: results.filter(r => !r.success).length,
|
|
212
|
+
total_replacements: results.reduce((sum, r) => sum + (r.replacements || 0), 0)
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### File Changes
|
|
219
|
+
|
|
220
|
+
| File | Action | Description |
|
|
221
|
+
|------|--------|-------------|
|
|
222
|
+
| `src/tools/multi-edit/types.ts` | Create | Type definitions |
|
|
223
|
+
| `src/tools/multi-edit/multi-edit-tool.ts` | Create | Tool implementation |
|
|
224
|
+
| `src/tools/multi-edit/index.ts` | Create | Module exports |
|
|
225
|
+
| `src/tools/index.ts` | Modify | Register MultiEdit tool |
|
|
226
|
+
|
|
227
|
+
## User Experience
|
|
228
|
+
|
|
229
|
+
### Basic Usage - Variable Rename
|
|
230
|
+
```
|
|
231
|
+
User: Rename the function 'getUserData' to 'fetchUserProfile' across the codebase
|
|
232
|
+
|
|
233
|
+
Agent: I'll search for all occurrences and perform a batch rename.
|
|
234
|
+
[Grep: getUserData]
|
|
235
|
+
|
|
236
|
+
Found in 5 files. Performing batch edit:
|
|
237
|
+
|
|
238
|
+
┌─ MultiEdit ───────────────────────────────────────┐
|
|
239
|
+
│ Description: Rename getUserData to fetchUserProfile │
|
|
240
|
+
│ Files: 5 │
|
|
241
|
+
│ Mode: Atomic │
|
|
242
|
+
└───────────────────────────────────────────────────┘
|
|
243
|
+
|
|
244
|
+
Results:
|
|
245
|
+
✓ src/api/users.ts (3 replacements)
|
|
246
|
+
✓ src/services/auth.ts (2 replacements)
|
|
247
|
+
✓ src/hooks/useUser.ts (1 replacement)
|
|
248
|
+
✓ src/tests/users.test.ts (4 replacements)
|
|
249
|
+
✓ src/types/api.ts (1 replacement)
|
|
250
|
+
|
|
251
|
+
Summary: 5/5 files updated, 11 total replacements
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Dry Run Preview
|
|
255
|
+
```
|
|
256
|
+
Agent: Let me preview the changes first:
|
|
257
|
+
|
|
258
|
+
[MultiEdit: dry_run=true]
|
|
259
|
+
|
|
260
|
+
Preview (no changes made):
|
|
261
|
+
• src/api/users.ts: Would make 3 replacements
|
|
262
|
+
• src/services/auth.ts: Would make 2 replacements
|
|
263
|
+
...
|
|
264
|
+
|
|
265
|
+
Proceed with these changes?
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Atomic Rollback
|
|
269
|
+
```
|
|
270
|
+
Agent: [MultiEdit: atomic=true]
|
|
271
|
+
|
|
272
|
+
Error in src/services/auth.ts: String not found
|
|
273
|
+
Rolling back changes to 3 files...
|
|
274
|
+
Rollback complete. No files were modified.
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Alternatives Considered
|
|
278
|
+
|
|
279
|
+
### Alternative 1: Enhanced Single Edit
|
|
280
|
+
Add multi-file support to existing Edit tool.
|
|
281
|
+
|
|
282
|
+
**Pros**: No new tool, simpler API
|
|
283
|
+
**Cons**: Overloads Edit semantics, breaks single-responsibility
|
|
284
|
+
**Decision**: Rejected - Separate tool is cleaner
|
|
285
|
+
|
|
286
|
+
### Alternative 2: Parallel Edit Calls
|
|
287
|
+
Let agent make multiple Edit calls in parallel.
|
|
288
|
+
|
|
289
|
+
**Pros**: No new implementation needed
|
|
290
|
+
**Cons**: No atomicity, no rollback, context overhead
|
|
291
|
+
**Decision**: Rejected - Atomicity is key feature
|
|
292
|
+
|
|
293
|
+
### Alternative 3: File Glob Pattern
|
|
294
|
+
Support glob patterns for target files.
|
|
295
|
+
|
|
296
|
+
**Pros**: Concise syntax for many files
|
|
297
|
+
**Cons**: Less precise, risk of unintended edits
|
|
298
|
+
**Decision**: Deferred - Can add later
|
|
299
|
+
|
|
300
|
+
## Security Considerations
|
|
301
|
+
|
|
302
|
+
1. **Path Validation**: Ensure all paths are within allowed directories
|
|
303
|
+
2. **Backup Storage**: Store backups securely in temp directory
|
|
304
|
+
3. **Size Limits**: Limit number of files and total content size
|
|
305
|
+
4. **Permission Check**: Verify write permission before starting
|
|
306
|
+
5. **No Secret Exposure**: Don't log file contents in error messages
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
const MAX_FILES = 100;
|
|
310
|
+
const MAX_TOTAL_SIZE = 50 * 1024 * 1024; // 50MB
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Testing Strategy
|
|
314
|
+
|
|
315
|
+
1. **Unit Tests**:
|
|
316
|
+
- Single file edit
|
|
317
|
+
- Multi-file edit
|
|
318
|
+
- Atomic rollback
|
|
319
|
+
- Dry run mode
|
|
320
|
+
- Error handling
|
|
321
|
+
|
|
322
|
+
2. **Integration Tests**:
|
|
323
|
+
- Large batch edits
|
|
324
|
+
- Permission scenarios
|
|
325
|
+
- Concurrent access
|
|
326
|
+
|
|
327
|
+
3. **Manual Testing**:
|
|
328
|
+
- Variable renaming workflow
|
|
329
|
+
- Import path updates
|
|
330
|
+
- Cross-platform paths
|
|
331
|
+
|
|
332
|
+
## Migration Path
|
|
333
|
+
|
|
334
|
+
1. **Phase 1**: Basic multi-edit with atomic mode
|
|
335
|
+
2. **Phase 2**: Dry run preview
|
|
336
|
+
3. **Phase 3**: Rollback functionality
|
|
337
|
+
4. **Phase 4**: Performance optimization for large batches
|
|
338
|
+
|
|
339
|
+
No breaking changes to existing Edit tool.
|
|
340
|
+
|
|
341
|
+
## References
|
|
342
|
+
|
|
343
|
+
- [Claude Code Edit Tool Documentation](https://code.claude.com/docs/en/tools)
|
|
344
|
+
- [Atomic File Operations Best Practices](https://en.wikipedia.org/wiki/Atomicity_(database_systems))
|
|
345
|
+
- [Existing Edit Tool Implementation](../../../src/tools/builtin/edit.ts)
|