@link-assistant/agent 0.0.8 → 0.0.11
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/EXAMPLES.md +80 -1
- package/MODELS.md +72 -24
- package/README.md +95 -2
- package/TOOLS.md +20 -0
- package/package.json +36 -2
- package/src/agent/agent.ts +68 -54
- package/src/auth/claude-oauth.ts +426 -0
- package/src/auth/index.ts +28 -26
- package/src/auth/plugins.ts +876 -0
- package/src/bun/index.ts +53 -43
- package/src/bus/global.ts +5 -5
- package/src/bus/index.ts +59 -53
- package/src/cli/bootstrap.js +12 -12
- package/src/cli/bootstrap.ts +6 -6
- package/src/cli/cmd/agent.ts +97 -92
- package/src/cli/cmd/auth.ts +468 -0
- package/src/cli/cmd/cmd.ts +2 -2
- package/src/cli/cmd/export.ts +41 -41
- package/src/cli/cmd/mcp.ts +210 -53
- package/src/cli/cmd/models.ts +30 -29
- package/src/cli/cmd/run.ts +269 -213
- package/src/cli/cmd/stats.ts +185 -146
- package/src/cli/error.ts +17 -13
- package/src/cli/ui.ts +78 -0
- package/src/command/index.ts +26 -26
- package/src/config/config.ts +528 -288
- package/src/config/markdown.ts +15 -15
- package/src/file/ripgrep.ts +201 -169
- package/src/file/time.ts +21 -18
- package/src/file/watcher.ts +51 -42
- package/src/file.ts +1 -1
- package/src/flag/flag.ts +26 -11
- package/src/format/formatter.ts +206 -162
- package/src/format/index.ts +61 -61
- package/src/global/index.ts +21 -21
- package/src/id/id.ts +47 -33
- package/src/index.js +554 -332
- package/src/json-standard/index.ts +173 -0
- package/src/mcp/index.ts +135 -128
- package/src/patch/index.ts +336 -267
- package/src/project/bootstrap.ts +15 -15
- package/src/project/instance.ts +43 -36
- package/src/project/project.ts +47 -47
- package/src/project/state.ts +37 -33
- package/src/provider/models-macro.ts +5 -5
- package/src/provider/models.ts +32 -32
- package/src/provider/opencode.js +19 -19
- package/src/provider/provider.ts +518 -277
- package/src/provider/transform.ts +143 -102
- package/src/server/project.ts +21 -21
- package/src/server/server.ts +111 -105
- package/src/session/agent.js +66 -60
- package/src/session/compaction.ts +136 -111
- package/src/session/index.ts +189 -156
- package/src/session/message-v2.ts +312 -268
- package/src/session/message.ts +73 -57
- package/src/session/processor.ts +180 -166
- package/src/session/prompt.ts +678 -533
- package/src/session/retry.ts +26 -23
- package/src/session/revert.ts +76 -62
- package/src/session/status.ts +26 -26
- package/src/session/summary.ts +97 -76
- package/src/session/system.ts +77 -63
- package/src/session/todo.ts +22 -16
- package/src/snapshot/index.ts +92 -76
- package/src/storage/storage.ts +157 -120
- package/src/tool/bash.ts +116 -106
- package/src/tool/batch.ts +73 -59
- package/src/tool/codesearch.ts +60 -53
- package/src/tool/edit.ts +319 -263
- package/src/tool/glob.ts +32 -28
- package/src/tool/grep.ts +72 -53
- package/src/tool/invalid.ts +7 -7
- package/src/tool/ls.ts +77 -64
- package/src/tool/multiedit.ts +30 -21
- package/src/tool/patch.ts +121 -94
- package/src/tool/read.ts +140 -122
- package/src/tool/registry.ts +38 -38
- package/src/tool/task.ts +93 -60
- package/src/tool/todo.ts +16 -16
- package/src/tool/tool.ts +45 -36
- package/src/tool/webfetch.ts +97 -74
- package/src/tool/websearch.ts +78 -64
- package/src/tool/write.ts +21 -15
- package/src/util/binary.ts +27 -19
- package/src/util/context.ts +8 -8
- package/src/util/defer.ts +7 -5
- package/src/util/error.ts +24 -19
- package/src/util/eventloop.ts +16 -10
- package/src/util/filesystem.ts +37 -33
- package/src/util/fn.ts +11 -8
- package/src/util/iife.ts +1 -1
- package/src/util/keybind.ts +44 -44
- package/src/util/lazy.ts +7 -7
- package/src/util/locale.ts +20 -16
- package/src/util/lock.ts +43 -38
- package/src/util/log.ts +95 -85
- package/src/util/queue.ts +8 -8
- package/src/util/rpc.ts +35 -23
- package/src/util/scrap.ts +4 -4
- package/src/util/signal.ts +5 -5
- package/src/util/timeout.ts +6 -6
- package/src/util/token.ts +2 -2
- package/src/util/wildcard.ts +38 -27
package/src/patch/index.ts
CHANGED
|
@@ -1,147 +1,166 @@
|
|
|
1
|
-
import z from
|
|
2
|
-
import * as path from
|
|
3
|
-
import * as fs from
|
|
4
|
-
import { Log } from
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import { Log } from '../util/log';
|
|
5
5
|
|
|
6
6
|
export namespace Patch {
|
|
7
|
-
const log = Log.create({ service:
|
|
7
|
+
const log = Log.create({ service: 'patch' });
|
|
8
8
|
|
|
9
9
|
// Schema definitions
|
|
10
10
|
export const PatchSchema = z.object({
|
|
11
|
-
patchText: z
|
|
12
|
-
|
|
11
|
+
patchText: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe('The full patch text that describes all changes to be made'),
|
|
14
|
+
});
|
|
13
15
|
|
|
14
|
-
export type PatchParams = z.infer<typeof PatchSchema
|
|
16
|
+
export type PatchParams = z.infer<typeof PatchSchema>;
|
|
15
17
|
|
|
16
18
|
// Core types matching the Rust implementation
|
|
17
19
|
export interface ApplyPatchArgs {
|
|
18
|
-
patch: string
|
|
19
|
-
hunks: Hunk[]
|
|
20
|
-
workdir?: string
|
|
20
|
+
patch: string;
|
|
21
|
+
hunks: Hunk[];
|
|
22
|
+
workdir?: string;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export type Hunk =
|
|
24
|
-
| { type:
|
|
25
|
-
| { type:
|
|
26
|
-
| {
|
|
26
|
+
| { type: 'add'; path: string; contents: string }
|
|
27
|
+
| { type: 'delete'; path: string }
|
|
28
|
+
| {
|
|
29
|
+
type: 'update';
|
|
30
|
+
path: string;
|
|
31
|
+
move_path?: string;
|
|
32
|
+
chunks: UpdateFileChunk[];
|
|
33
|
+
};
|
|
27
34
|
|
|
28
35
|
export interface UpdateFileChunk {
|
|
29
|
-
old_lines: string[]
|
|
30
|
-
new_lines: string[]
|
|
31
|
-
change_context?: string
|
|
32
|
-
is_end_of_file?: boolean
|
|
36
|
+
old_lines: string[];
|
|
37
|
+
new_lines: string[];
|
|
38
|
+
change_context?: string;
|
|
39
|
+
is_end_of_file?: boolean;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
export interface ApplyPatchAction {
|
|
36
|
-
changes: Map<string, ApplyPatchFileChange
|
|
37
|
-
patch: string
|
|
38
|
-
cwd: string
|
|
43
|
+
changes: Map<string, ApplyPatchFileChange>;
|
|
44
|
+
patch: string;
|
|
45
|
+
cwd: string;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
export type ApplyPatchFileChange =
|
|
42
|
-
| { type:
|
|
43
|
-
| { type:
|
|
44
|
-
| {
|
|
49
|
+
| { type: 'add'; content: string }
|
|
50
|
+
| { type: 'delete'; content: string }
|
|
51
|
+
| {
|
|
52
|
+
type: 'update';
|
|
53
|
+
unified_diff: string;
|
|
54
|
+
move_path?: string;
|
|
55
|
+
new_content: string;
|
|
56
|
+
};
|
|
45
57
|
|
|
46
58
|
export interface AffectedPaths {
|
|
47
|
-
added: string[]
|
|
48
|
-
modified: string[]
|
|
49
|
-
deleted: string[]
|
|
59
|
+
added: string[];
|
|
60
|
+
modified: string[];
|
|
61
|
+
deleted: string[];
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
export enum ApplyPatchError {
|
|
53
|
-
ParseError =
|
|
54
|
-
IoError =
|
|
55
|
-
ComputeReplacements =
|
|
56
|
-
ImplicitInvocation =
|
|
65
|
+
ParseError = 'ParseError',
|
|
66
|
+
IoError = 'IoError',
|
|
67
|
+
ComputeReplacements = 'ComputeReplacements',
|
|
68
|
+
ImplicitInvocation = 'ImplicitInvocation',
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
export enum MaybeApplyPatch {
|
|
60
|
-
Body =
|
|
61
|
-
ShellParseError =
|
|
62
|
-
PatchParseError =
|
|
63
|
-
NotApplyPatch =
|
|
72
|
+
Body = 'Body',
|
|
73
|
+
ShellParseError = 'ShellParseError',
|
|
74
|
+
PatchParseError = 'PatchParseError',
|
|
75
|
+
NotApplyPatch = 'NotApplyPatch',
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
export enum MaybeApplyPatchVerified {
|
|
67
|
-
Body =
|
|
68
|
-
ShellParseError =
|
|
69
|
-
CorrectnessError =
|
|
70
|
-
NotApplyPatch =
|
|
79
|
+
Body = 'Body',
|
|
80
|
+
ShellParseError = 'ShellParseError',
|
|
81
|
+
CorrectnessError = 'CorrectnessError',
|
|
82
|
+
NotApplyPatch = 'NotApplyPatch',
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
// Parser implementation
|
|
74
86
|
function parsePatchHeader(
|
|
75
87
|
lines: string[],
|
|
76
|
-
startIdx: number
|
|
88
|
+
startIdx: number
|
|
77
89
|
): { filePath: string; movePath?: string; nextIdx: number } | null {
|
|
78
|
-
const line = lines[startIdx]
|
|
90
|
+
const line = lines[startIdx];
|
|
79
91
|
|
|
80
|
-
if (line.startsWith(
|
|
81
|
-
const filePath = line.split(
|
|
82
|
-
return filePath ? { filePath, nextIdx: startIdx + 1 } : null
|
|
92
|
+
if (line.startsWith('*** Add File:')) {
|
|
93
|
+
const filePath = line.split(':', 2)[1]?.trim();
|
|
94
|
+
return filePath ? { filePath, nextIdx: startIdx + 1 } : null;
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
if (line.startsWith(
|
|
86
|
-
const filePath = line.split(
|
|
87
|
-
return filePath ? { filePath, nextIdx: startIdx + 1 } : null
|
|
97
|
+
if (line.startsWith('*** Delete File:')) {
|
|
98
|
+
const filePath = line.split(':', 2)[1]?.trim();
|
|
99
|
+
return filePath ? { filePath, nextIdx: startIdx + 1 } : null;
|
|
88
100
|
}
|
|
89
101
|
|
|
90
|
-
if (line.startsWith(
|
|
91
|
-
const filePath = line.split(
|
|
92
|
-
let movePath: string | undefined
|
|
93
|
-
let nextIdx = startIdx + 1
|
|
102
|
+
if (line.startsWith('*** Update File:')) {
|
|
103
|
+
const filePath = line.split(':', 2)[1]?.trim();
|
|
104
|
+
let movePath: string | undefined;
|
|
105
|
+
let nextIdx = startIdx + 1;
|
|
94
106
|
|
|
95
107
|
// Check for move directive
|
|
96
|
-
if (nextIdx < lines.length && lines[nextIdx].startsWith(
|
|
97
|
-
movePath = lines[nextIdx].split(
|
|
98
|
-
nextIdx
|
|
108
|
+
if (nextIdx < lines.length && lines[nextIdx].startsWith('*** Move to:')) {
|
|
109
|
+
movePath = lines[nextIdx].split(':', 2)[1]?.trim();
|
|
110
|
+
nextIdx++;
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
return filePath ? { filePath, movePath, nextIdx } : null
|
|
113
|
+
return filePath ? { filePath, movePath, nextIdx } : null;
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
return null
|
|
116
|
+
return null;
|
|
105
117
|
}
|
|
106
118
|
|
|
107
|
-
function parseUpdateFileChunks(
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
function parseUpdateFileChunks(
|
|
120
|
+
lines: string[],
|
|
121
|
+
startIdx: number
|
|
122
|
+
): { chunks: UpdateFileChunk[]; nextIdx: number } {
|
|
123
|
+
const chunks: UpdateFileChunk[] = [];
|
|
124
|
+
let i = startIdx;
|
|
110
125
|
|
|
111
|
-
while (i < lines.length && !lines[i].startsWith(
|
|
112
|
-
if (lines[i].startsWith(
|
|
126
|
+
while (i < lines.length && !lines[i].startsWith('***')) {
|
|
127
|
+
if (lines[i].startsWith('@@')) {
|
|
113
128
|
// Parse context line
|
|
114
|
-
const contextLine = lines[i].substring(2).trim()
|
|
115
|
-
i
|
|
129
|
+
const contextLine = lines[i].substring(2).trim();
|
|
130
|
+
i++;
|
|
116
131
|
|
|
117
|
-
const oldLines: string[] = []
|
|
118
|
-
const newLines: string[] = []
|
|
119
|
-
let isEndOfFile = false
|
|
132
|
+
const oldLines: string[] = [];
|
|
133
|
+
const newLines: string[] = [];
|
|
134
|
+
let isEndOfFile = false;
|
|
120
135
|
|
|
121
136
|
// Parse change lines
|
|
122
|
-
while (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
137
|
+
while (
|
|
138
|
+
i < lines.length &&
|
|
139
|
+
!lines[i].startsWith('@@') &&
|
|
140
|
+
!lines[i].startsWith('***')
|
|
141
|
+
) {
|
|
142
|
+
const changeLine = lines[i];
|
|
143
|
+
|
|
144
|
+
if (changeLine === '*** End of File') {
|
|
145
|
+
isEndOfFile = true;
|
|
146
|
+
i++;
|
|
147
|
+
break;
|
|
129
148
|
}
|
|
130
149
|
|
|
131
|
-
if (changeLine.startsWith(
|
|
150
|
+
if (changeLine.startsWith(' ')) {
|
|
132
151
|
// Keep line - appears in both old and new
|
|
133
|
-
const content = changeLine.substring(1)
|
|
134
|
-
oldLines.push(content)
|
|
135
|
-
newLines.push(content)
|
|
136
|
-
} else if (changeLine.startsWith(
|
|
152
|
+
const content = changeLine.substring(1);
|
|
153
|
+
oldLines.push(content);
|
|
154
|
+
newLines.push(content);
|
|
155
|
+
} else if (changeLine.startsWith('-')) {
|
|
137
156
|
// Remove line - only in old
|
|
138
|
-
oldLines.push(changeLine.substring(1))
|
|
139
|
-
} else if (changeLine.startsWith(
|
|
157
|
+
oldLines.push(changeLine.substring(1));
|
|
158
|
+
} else if (changeLine.startsWith('+')) {
|
|
140
159
|
// Add line - only in new
|
|
141
|
-
newLines.push(changeLine.substring(1))
|
|
160
|
+
newLines.push(changeLine.substring(1));
|
|
142
161
|
}
|
|
143
162
|
|
|
144
|
-
i
|
|
163
|
+
i++;
|
|
145
164
|
}
|
|
146
165
|
|
|
147
166
|
chunks.push({
|
|
@@ -149,380 +168,419 @@ export namespace Patch {
|
|
|
149
168
|
new_lines: newLines,
|
|
150
169
|
change_context: contextLine || undefined,
|
|
151
170
|
is_end_of_file: isEndOfFile || undefined,
|
|
152
|
-
})
|
|
171
|
+
});
|
|
153
172
|
} else {
|
|
154
|
-
i
|
|
173
|
+
i++;
|
|
155
174
|
}
|
|
156
175
|
}
|
|
157
176
|
|
|
158
|
-
return { chunks, nextIdx: i }
|
|
177
|
+
return { chunks, nextIdx: i };
|
|
159
178
|
}
|
|
160
179
|
|
|
161
|
-
function parseAddFileContent(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
180
|
+
function parseAddFileContent(
|
|
181
|
+
lines: string[],
|
|
182
|
+
startIdx: number
|
|
183
|
+
): { content: string; nextIdx: number } {
|
|
184
|
+
let content = '';
|
|
185
|
+
let i = startIdx;
|
|
186
|
+
|
|
187
|
+
while (i < lines.length && !lines[i].startsWith('***')) {
|
|
188
|
+
if (lines[i].startsWith('+')) {
|
|
189
|
+
content += lines[i].substring(1) + '\n';
|
|
168
190
|
}
|
|
169
|
-
i
|
|
191
|
+
i++;
|
|
170
192
|
}
|
|
171
193
|
|
|
172
194
|
// Remove trailing newline
|
|
173
|
-
if (content.endsWith(
|
|
174
|
-
content = content.slice(0, -1)
|
|
195
|
+
if (content.endsWith('\n')) {
|
|
196
|
+
content = content.slice(0, -1);
|
|
175
197
|
}
|
|
176
198
|
|
|
177
|
-
return { content, nextIdx: i }
|
|
199
|
+
return { content, nextIdx: i };
|
|
178
200
|
}
|
|
179
201
|
|
|
180
202
|
export function parsePatch(patchText: string): { hunks: Hunk[] } {
|
|
181
|
-
const lines = patchText.split(
|
|
182
|
-
const hunks: Hunk[] = []
|
|
183
|
-
let i = 0
|
|
203
|
+
const lines = patchText.split('\n');
|
|
204
|
+
const hunks: Hunk[] = [];
|
|
205
|
+
let i = 0;
|
|
184
206
|
|
|
185
207
|
// Look for Begin/End patch markers
|
|
186
|
-
const beginMarker =
|
|
187
|
-
const endMarker =
|
|
208
|
+
const beginMarker = '*** Begin Patch';
|
|
209
|
+
const endMarker = '*** End Patch';
|
|
188
210
|
|
|
189
|
-
const beginIdx = lines.findIndex((line) => line.trim() === beginMarker)
|
|
190
|
-
const endIdx = lines.findIndex((line) => line.trim() === endMarker)
|
|
211
|
+
const beginIdx = lines.findIndex((line) => line.trim() === beginMarker);
|
|
212
|
+
const endIdx = lines.findIndex((line) => line.trim() === endMarker);
|
|
191
213
|
|
|
192
214
|
if (beginIdx === -1 || endIdx === -1 || beginIdx >= endIdx) {
|
|
193
|
-
throw new Error(
|
|
215
|
+
throw new Error('Invalid patch format: missing Begin/End markers');
|
|
194
216
|
}
|
|
195
217
|
|
|
196
218
|
// Parse content between markers
|
|
197
|
-
i = beginIdx + 1
|
|
219
|
+
i = beginIdx + 1;
|
|
198
220
|
|
|
199
221
|
while (i < endIdx) {
|
|
200
|
-
const header = parsePatchHeader(lines, i)
|
|
222
|
+
const header = parsePatchHeader(lines, i);
|
|
201
223
|
if (!header) {
|
|
202
|
-
i
|
|
203
|
-
continue
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
204
226
|
}
|
|
205
227
|
|
|
206
|
-
if (lines[i].startsWith(
|
|
207
|
-
const { content, nextIdx } = parseAddFileContent(lines, header.nextIdx)
|
|
228
|
+
if (lines[i].startsWith('*** Add File:')) {
|
|
229
|
+
const { content, nextIdx } = parseAddFileContent(lines, header.nextIdx);
|
|
208
230
|
hunks.push({
|
|
209
|
-
type:
|
|
231
|
+
type: 'add',
|
|
210
232
|
path: header.filePath,
|
|
211
233
|
contents: content,
|
|
212
|
-
})
|
|
213
|
-
i = nextIdx
|
|
214
|
-
} else if (lines[i].startsWith(
|
|
234
|
+
});
|
|
235
|
+
i = nextIdx;
|
|
236
|
+
} else if (lines[i].startsWith('*** Delete File:')) {
|
|
215
237
|
hunks.push({
|
|
216
|
-
type:
|
|
238
|
+
type: 'delete',
|
|
217
239
|
path: header.filePath,
|
|
218
|
-
})
|
|
219
|
-
i = header.nextIdx
|
|
220
|
-
} else if (lines[i].startsWith(
|
|
221
|
-
const { chunks, nextIdx } = parseUpdateFileChunks(
|
|
240
|
+
});
|
|
241
|
+
i = header.nextIdx;
|
|
242
|
+
} else if (lines[i].startsWith('*** Update File:')) {
|
|
243
|
+
const { chunks, nextIdx } = parseUpdateFileChunks(
|
|
244
|
+
lines,
|
|
245
|
+
header.nextIdx
|
|
246
|
+
);
|
|
222
247
|
hunks.push({
|
|
223
|
-
type:
|
|
248
|
+
type: 'update',
|
|
224
249
|
path: header.filePath,
|
|
225
250
|
move_path: header.movePath,
|
|
226
251
|
chunks,
|
|
227
|
-
})
|
|
228
|
-
i = nextIdx
|
|
252
|
+
});
|
|
253
|
+
i = nextIdx;
|
|
229
254
|
} else {
|
|
230
|
-
i
|
|
255
|
+
i++;
|
|
231
256
|
}
|
|
232
257
|
}
|
|
233
258
|
|
|
234
|
-
return { hunks }
|
|
259
|
+
return { hunks };
|
|
235
260
|
}
|
|
236
261
|
|
|
237
262
|
// Apply patch functionality
|
|
238
263
|
export function maybeParseApplyPatch(
|
|
239
|
-
argv: string[]
|
|
264
|
+
argv: string[]
|
|
240
265
|
):
|
|
241
266
|
| { type: MaybeApplyPatch.Body; args: ApplyPatchArgs }
|
|
242
267
|
| { type: MaybeApplyPatch.PatchParseError; error: Error }
|
|
243
268
|
| { type: MaybeApplyPatch.NotApplyPatch } {
|
|
244
|
-
const APPLY_PATCH_COMMANDS = [
|
|
269
|
+
const APPLY_PATCH_COMMANDS = ['apply_patch', 'applypatch'];
|
|
245
270
|
|
|
246
271
|
// Direct invocation: apply_patch <patch>
|
|
247
272
|
if (argv.length === 2 && APPLY_PATCH_COMMANDS.includes(argv[0])) {
|
|
248
273
|
try {
|
|
249
|
-
const { hunks } = parsePatch(argv[1])
|
|
274
|
+
const { hunks } = parsePatch(argv[1]);
|
|
250
275
|
return {
|
|
251
276
|
type: MaybeApplyPatch.Body,
|
|
252
277
|
args: {
|
|
253
278
|
patch: argv[1],
|
|
254
279
|
hunks,
|
|
255
280
|
},
|
|
256
|
-
}
|
|
281
|
+
};
|
|
257
282
|
} catch (error) {
|
|
258
283
|
return {
|
|
259
284
|
type: MaybeApplyPatch.PatchParseError,
|
|
260
285
|
error: error as Error,
|
|
261
|
-
}
|
|
286
|
+
};
|
|
262
287
|
}
|
|
263
288
|
}
|
|
264
289
|
|
|
265
290
|
// Bash heredoc form: bash -lc 'apply_patch <<"EOF" ...'
|
|
266
|
-
if (argv.length === 3 && argv[0] ===
|
|
291
|
+
if (argv.length === 3 && argv[0] === 'bash' && argv[1] === '-lc') {
|
|
267
292
|
// Simple extraction - in real implementation would need proper bash parsing
|
|
268
|
-
const script = argv[2]
|
|
269
|
-
const heredocMatch = script.match(
|
|
293
|
+
const script = argv[2];
|
|
294
|
+
const heredocMatch = script.match(
|
|
295
|
+
/apply_patch\s*<<['"](\w+)['"]\s*\n([\s\S]*?)\n\1/
|
|
296
|
+
);
|
|
270
297
|
|
|
271
298
|
if (heredocMatch) {
|
|
272
|
-
const patchContent = heredocMatch[2]
|
|
299
|
+
const patchContent = heredocMatch[2];
|
|
273
300
|
try {
|
|
274
|
-
const { hunks } = parsePatch(patchContent)
|
|
301
|
+
const { hunks } = parsePatch(patchContent);
|
|
275
302
|
return {
|
|
276
303
|
type: MaybeApplyPatch.Body,
|
|
277
304
|
args: {
|
|
278
305
|
patch: patchContent,
|
|
279
306
|
hunks,
|
|
280
307
|
},
|
|
281
|
-
}
|
|
308
|
+
};
|
|
282
309
|
} catch (error) {
|
|
283
310
|
return {
|
|
284
311
|
type: MaybeApplyPatch.PatchParseError,
|
|
285
312
|
error: error as Error,
|
|
286
|
-
}
|
|
313
|
+
};
|
|
287
314
|
}
|
|
288
315
|
}
|
|
289
316
|
}
|
|
290
317
|
|
|
291
|
-
return { type: MaybeApplyPatch.NotApplyPatch }
|
|
318
|
+
return { type: MaybeApplyPatch.NotApplyPatch };
|
|
292
319
|
}
|
|
293
320
|
|
|
294
321
|
// File content manipulation
|
|
295
322
|
interface ApplyPatchFileUpdate {
|
|
296
|
-
unified_diff: string
|
|
297
|
-
content: string
|
|
323
|
+
unified_diff: string;
|
|
324
|
+
content: string;
|
|
298
325
|
}
|
|
299
326
|
|
|
300
|
-
export function deriveNewContentsFromChunks(
|
|
327
|
+
export function deriveNewContentsFromChunks(
|
|
328
|
+
filePath: string,
|
|
329
|
+
chunks: UpdateFileChunk[]
|
|
330
|
+
): ApplyPatchFileUpdate {
|
|
301
331
|
// Read original file content
|
|
302
|
-
let originalContent: string
|
|
332
|
+
let originalContent: string;
|
|
303
333
|
try {
|
|
304
|
-
originalContent = require(
|
|
334
|
+
originalContent = require('fs').readFileSync(filePath, 'utf-8');
|
|
305
335
|
} catch (error) {
|
|
306
|
-
throw new Error(`Failed to read file ${filePath}: ${error}`)
|
|
336
|
+
throw new Error(`Failed to read file ${filePath}: ${error}`);
|
|
307
337
|
}
|
|
308
338
|
|
|
309
|
-
let originalLines = originalContent.split(
|
|
339
|
+
let originalLines = originalContent.split('\n');
|
|
310
340
|
|
|
311
341
|
// Drop trailing empty element for consistent line counting
|
|
312
|
-
if (
|
|
313
|
-
originalLines.
|
|
342
|
+
if (
|
|
343
|
+
originalLines.length > 0 &&
|
|
344
|
+
originalLines[originalLines.length - 1] === ''
|
|
345
|
+
) {
|
|
346
|
+
originalLines.pop();
|
|
314
347
|
}
|
|
315
348
|
|
|
316
|
-
const replacements = computeReplacements(originalLines, filePath, chunks)
|
|
317
|
-
let newLines = applyReplacements(originalLines, replacements)
|
|
349
|
+
const replacements = computeReplacements(originalLines, filePath, chunks);
|
|
350
|
+
let newLines = applyReplacements(originalLines, replacements);
|
|
318
351
|
|
|
319
352
|
// Ensure trailing newline
|
|
320
|
-
if (newLines.length === 0 || newLines[newLines.length - 1] !==
|
|
321
|
-
newLines.push(
|
|
353
|
+
if (newLines.length === 0 || newLines[newLines.length - 1] !== '') {
|
|
354
|
+
newLines.push('');
|
|
322
355
|
}
|
|
323
356
|
|
|
324
|
-
const newContent = newLines.join(
|
|
357
|
+
const newContent = newLines.join('\n');
|
|
325
358
|
|
|
326
359
|
// Generate unified diff
|
|
327
|
-
const unifiedDiff = generateUnifiedDiff(originalContent, newContent)
|
|
360
|
+
const unifiedDiff = generateUnifiedDiff(originalContent, newContent);
|
|
328
361
|
|
|
329
362
|
return {
|
|
330
363
|
unified_diff: unifiedDiff,
|
|
331
364
|
content: newContent,
|
|
332
|
-
}
|
|
365
|
+
};
|
|
333
366
|
}
|
|
334
367
|
|
|
335
368
|
function computeReplacements(
|
|
336
369
|
originalLines: string[],
|
|
337
370
|
filePath: string,
|
|
338
|
-
chunks: UpdateFileChunk[]
|
|
371
|
+
chunks: UpdateFileChunk[]
|
|
339
372
|
): Array<[number, number, string[]]> {
|
|
340
|
-
const replacements: Array<[number, number, string[]]> = []
|
|
341
|
-
let lineIndex = 0
|
|
373
|
+
const replacements: Array<[number, number, string[]]> = [];
|
|
374
|
+
let lineIndex = 0;
|
|
342
375
|
|
|
343
376
|
for (const chunk of chunks) {
|
|
344
377
|
// Handle context-based seeking
|
|
345
378
|
if (chunk.change_context) {
|
|
346
|
-
const contextIdx = seekSequence(
|
|
379
|
+
const contextIdx = seekSequence(
|
|
380
|
+
originalLines,
|
|
381
|
+
[chunk.change_context],
|
|
382
|
+
lineIndex
|
|
383
|
+
);
|
|
347
384
|
if (contextIdx === -1) {
|
|
348
|
-
throw new Error(
|
|
385
|
+
throw new Error(
|
|
386
|
+
`Failed to find context '${chunk.change_context}' in ${filePath}`
|
|
387
|
+
);
|
|
349
388
|
}
|
|
350
|
-
lineIndex = contextIdx + 1
|
|
389
|
+
lineIndex = contextIdx + 1;
|
|
351
390
|
}
|
|
352
391
|
|
|
353
392
|
// Handle pure addition (no old lines)
|
|
354
393
|
if (chunk.old_lines.length === 0) {
|
|
355
394
|
const insertionIdx =
|
|
356
|
-
originalLines.length > 0 &&
|
|
395
|
+
originalLines.length > 0 &&
|
|
396
|
+
originalLines[originalLines.length - 1] === ''
|
|
357
397
|
? originalLines.length - 1
|
|
358
|
-
: originalLines.length
|
|
359
|
-
replacements.push([insertionIdx, 0, chunk.new_lines])
|
|
360
|
-
continue
|
|
398
|
+
: originalLines.length;
|
|
399
|
+
replacements.push([insertionIdx, 0, chunk.new_lines]);
|
|
400
|
+
continue;
|
|
361
401
|
}
|
|
362
402
|
|
|
363
403
|
// Try to match old lines in the file
|
|
364
|
-
let pattern = chunk.old_lines
|
|
365
|
-
let newSlice = chunk.new_lines
|
|
366
|
-
let found = seekSequence(originalLines, pattern, lineIndex)
|
|
404
|
+
let pattern = chunk.old_lines;
|
|
405
|
+
let newSlice = chunk.new_lines;
|
|
406
|
+
let found = seekSequence(originalLines, pattern, lineIndex);
|
|
367
407
|
|
|
368
408
|
// Retry without trailing empty line if not found
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
409
|
+
if (
|
|
410
|
+
found === -1 &&
|
|
411
|
+
pattern.length > 0 &&
|
|
412
|
+
pattern[pattern.length - 1] === ''
|
|
413
|
+
) {
|
|
414
|
+
pattern = pattern.slice(0, -1);
|
|
415
|
+
if (newSlice.length > 0 && newSlice[newSlice.length - 1] === '') {
|
|
416
|
+
newSlice = newSlice.slice(0, -1);
|
|
373
417
|
}
|
|
374
|
-
found = seekSequence(originalLines, pattern, lineIndex)
|
|
418
|
+
found = seekSequence(originalLines, pattern, lineIndex);
|
|
375
419
|
}
|
|
376
420
|
|
|
377
421
|
if (found !== -1) {
|
|
378
|
-
replacements.push([found, pattern.length, newSlice])
|
|
379
|
-
lineIndex = found + pattern.length
|
|
422
|
+
replacements.push([found, pattern.length, newSlice]);
|
|
423
|
+
lineIndex = found + pattern.length;
|
|
380
424
|
} else {
|
|
381
|
-
throw new Error(
|
|
425
|
+
throw new Error(
|
|
426
|
+
`Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join('\n')}`
|
|
427
|
+
);
|
|
382
428
|
}
|
|
383
429
|
}
|
|
384
430
|
|
|
385
431
|
// Sort replacements by index to apply in order
|
|
386
|
-
replacements.sort((a, b) => a[0] - b[0])
|
|
432
|
+
replacements.sort((a, b) => a[0] - b[0]);
|
|
387
433
|
|
|
388
|
-
return replacements
|
|
434
|
+
return replacements;
|
|
389
435
|
}
|
|
390
436
|
|
|
391
|
-
function applyReplacements(
|
|
437
|
+
function applyReplacements(
|
|
438
|
+
lines: string[],
|
|
439
|
+
replacements: Array<[number, number, string[]]>
|
|
440
|
+
): string[] {
|
|
392
441
|
// Apply replacements in reverse order to avoid index shifting
|
|
393
|
-
const result = [...lines]
|
|
442
|
+
const result = [...lines];
|
|
394
443
|
|
|
395
444
|
for (let i = replacements.length - 1; i >= 0; i--) {
|
|
396
|
-
const [startIdx, oldLen, newSegment] = replacements[i]
|
|
445
|
+
const [startIdx, oldLen, newSegment] = replacements[i];
|
|
397
446
|
|
|
398
447
|
// Remove old lines
|
|
399
|
-
result.splice(startIdx, oldLen)
|
|
448
|
+
result.splice(startIdx, oldLen);
|
|
400
449
|
|
|
401
450
|
// Insert new lines
|
|
402
451
|
for (let j = 0; j < newSegment.length; j++) {
|
|
403
|
-
result.splice(startIdx + j, 0, newSegment[j])
|
|
452
|
+
result.splice(startIdx + j, 0, newSegment[j]);
|
|
404
453
|
}
|
|
405
454
|
}
|
|
406
455
|
|
|
407
|
-
return result
|
|
456
|
+
return result;
|
|
408
457
|
}
|
|
409
458
|
|
|
410
|
-
function seekSequence(
|
|
411
|
-
|
|
459
|
+
function seekSequence(
|
|
460
|
+
lines: string[],
|
|
461
|
+
pattern: string[],
|
|
462
|
+
startIndex: number
|
|
463
|
+
): number {
|
|
464
|
+
if (pattern.length === 0) return -1;
|
|
412
465
|
|
|
413
466
|
// Simple substring search implementation
|
|
414
467
|
for (let i = startIndex; i <= lines.length - pattern.length; i++) {
|
|
415
|
-
let matches = true
|
|
468
|
+
let matches = true;
|
|
416
469
|
|
|
417
470
|
for (let j = 0; j < pattern.length; j++) {
|
|
418
471
|
if (lines[i + j] !== pattern[j]) {
|
|
419
|
-
matches = false
|
|
420
|
-
break
|
|
472
|
+
matches = false;
|
|
473
|
+
break;
|
|
421
474
|
}
|
|
422
475
|
}
|
|
423
476
|
|
|
424
477
|
if (matches) {
|
|
425
|
-
return i
|
|
478
|
+
return i;
|
|
426
479
|
}
|
|
427
480
|
}
|
|
428
481
|
|
|
429
|
-
return -1
|
|
482
|
+
return -1;
|
|
430
483
|
}
|
|
431
484
|
|
|
432
485
|
function generateUnifiedDiff(oldContent: string, newContent: string): string {
|
|
433
|
-
const oldLines = oldContent.split(
|
|
434
|
-
const newLines = newContent.split(
|
|
486
|
+
const oldLines = oldContent.split('\n');
|
|
487
|
+
const newLines = newContent.split('\n');
|
|
435
488
|
|
|
436
489
|
// Simple diff generation - in a real implementation you'd use a proper diff algorithm
|
|
437
|
-
let diff =
|
|
490
|
+
let diff = '@@ -1 +1 @@\n';
|
|
438
491
|
|
|
439
492
|
// Find changes (simplified approach)
|
|
440
|
-
const maxLen = Math.max(oldLines.length, newLines.length)
|
|
441
|
-
let hasChanges = false
|
|
493
|
+
const maxLen = Math.max(oldLines.length, newLines.length);
|
|
494
|
+
let hasChanges = false;
|
|
442
495
|
|
|
443
496
|
for (let i = 0; i < maxLen; i++) {
|
|
444
|
-
const oldLine = oldLines[i] ||
|
|
445
|
-
const newLine = newLines[i] ||
|
|
497
|
+
const oldLine = oldLines[i] || '';
|
|
498
|
+
const newLine = newLines[i] || '';
|
|
446
499
|
|
|
447
500
|
if (oldLine !== newLine) {
|
|
448
|
-
if (oldLine) diff += `-${oldLine}\n
|
|
449
|
-
if (newLine) diff += `+${newLine}\n
|
|
450
|
-
hasChanges = true
|
|
501
|
+
if (oldLine) diff += `-${oldLine}\n`;
|
|
502
|
+
if (newLine) diff += `+${newLine}\n`;
|
|
503
|
+
hasChanges = true;
|
|
451
504
|
} else if (oldLine) {
|
|
452
|
-
diff += ` ${oldLine}\n
|
|
505
|
+
diff += ` ${oldLine}\n`;
|
|
453
506
|
}
|
|
454
507
|
}
|
|
455
508
|
|
|
456
|
-
return hasChanges ? diff :
|
|
509
|
+
return hasChanges ? diff : '';
|
|
457
510
|
}
|
|
458
511
|
|
|
459
512
|
// Apply hunks to filesystem
|
|
460
|
-
export async function applyHunksToFiles(
|
|
513
|
+
export async function applyHunksToFiles(
|
|
514
|
+
hunks: Hunk[]
|
|
515
|
+
): Promise<AffectedPaths> {
|
|
461
516
|
if (hunks.length === 0) {
|
|
462
|
-
throw new Error(
|
|
517
|
+
throw new Error('No files were modified.');
|
|
463
518
|
}
|
|
464
519
|
|
|
465
|
-
const added: string[] = []
|
|
466
|
-
const modified: string[] = []
|
|
467
|
-
const deleted: string[] = []
|
|
520
|
+
const added: string[] = [];
|
|
521
|
+
const modified: string[] = [];
|
|
522
|
+
const deleted: string[] = [];
|
|
468
523
|
|
|
469
524
|
for (const hunk of hunks) {
|
|
470
525
|
switch (hunk.type) {
|
|
471
|
-
case
|
|
526
|
+
case 'add':
|
|
472
527
|
// Create parent directories
|
|
473
|
-
const addDir = path.dirname(hunk.path)
|
|
474
|
-
if (addDir !==
|
|
475
|
-
await fs.mkdir(addDir, { recursive: true })
|
|
528
|
+
const addDir = path.dirname(hunk.path);
|
|
529
|
+
if (addDir !== '.' && addDir !== '/') {
|
|
530
|
+
await fs.mkdir(addDir, { recursive: true });
|
|
476
531
|
}
|
|
477
532
|
|
|
478
|
-
await fs.writeFile(hunk.path, hunk.contents,
|
|
479
|
-
added.push(hunk.path)
|
|
480
|
-
log.info(`Added file: ${hunk.path}`)
|
|
481
|
-
break
|
|
533
|
+
await fs.writeFile(hunk.path, hunk.contents, 'utf-8');
|
|
534
|
+
added.push(hunk.path);
|
|
535
|
+
log.info(`Added file: ${hunk.path}`);
|
|
536
|
+
break;
|
|
482
537
|
|
|
483
|
-
case
|
|
484
|
-
await fs.unlink(hunk.path)
|
|
485
|
-
deleted.push(hunk.path)
|
|
486
|
-
log.info(`Deleted file: ${hunk.path}`)
|
|
487
|
-
break
|
|
538
|
+
case 'delete':
|
|
539
|
+
await fs.unlink(hunk.path);
|
|
540
|
+
deleted.push(hunk.path);
|
|
541
|
+
log.info(`Deleted file: ${hunk.path}`);
|
|
542
|
+
break;
|
|
488
543
|
|
|
489
|
-
case
|
|
490
|
-
const fileUpdate = deriveNewContentsFromChunks(
|
|
544
|
+
case 'update':
|
|
545
|
+
const fileUpdate = deriveNewContentsFromChunks(
|
|
546
|
+
hunk.path,
|
|
547
|
+
hunk.chunks
|
|
548
|
+
);
|
|
491
549
|
|
|
492
550
|
if (hunk.move_path) {
|
|
493
551
|
// Handle file move
|
|
494
|
-
const moveDir = path.dirname(hunk.move_path)
|
|
495
|
-
if (moveDir !==
|
|
496
|
-
await fs.mkdir(moveDir, { recursive: true })
|
|
552
|
+
const moveDir = path.dirname(hunk.move_path);
|
|
553
|
+
if (moveDir !== '.' && moveDir !== '/') {
|
|
554
|
+
await fs.mkdir(moveDir, { recursive: true });
|
|
497
555
|
}
|
|
498
556
|
|
|
499
|
-
await fs.writeFile(hunk.move_path, fileUpdate.content,
|
|
500
|
-
await fs.unlink(hunk.path)
|
|
501
|
-
modified.push(hunk.move_path)
|
|
502
|
-
log.info(`Moved file: ${hunk.path} -> ${hunk.move_path}`)
|
|
557
|
+
await fs.writeFile(hunk.move_path, fileUpdate.content, 'utf-8');
|
|
558
|
+
await fs.unlink(hunk.path);
|
|
559
|
+
modified.push(hunk.move_path);
|
|
560
|
+
log.info(`Moved file: ${hunk.path} -> ${hunk.move_path}`);
|
|
503
561
|
} else {
|
|
504
562
|
// Regular update
|
|
505
|
-
await fs.writeFile(hunk.path, fileUpdate.content,
|
|
506
|
-
modified.push(hunk.path)
|
|
507
|
-
log.info(`Updated file: ${hunk.path}`)
|
|
563
|
+
await fs.writeFile(hunk.path, fileUpdate.content, 'utf-8');
|
|
564
|
+
modified.push(hunk.path);
|
|
565
|
+
log.info(`Updated file: ${hunk.path}`);
|
|
508
566
|
}
|
|
509
|
-
break
|
|
567
|
+
break;
|
|
510
568
|
}
|
|
511
569
|
}
|
|
512
570
|
|
|
513
|
-
return { added, modified, deleted }
|
|
571
|
+
return { added, modified, deleted };
|
|
514
572
|
}
|
|
515
573
|
|
|
516
574
|
// Main patch application function
|
|
517
575
|
export async function applyPatch(patchText: string): Promise<AffectedPaths> {
|
|
518
|
-
const { hunks } = parsePatch(patchText)
|
|
519
|
-
return applyHunksToFiles(hunks)
|
|
576
|
+
const { hunks } = parsePatch(patchText);
|
|
577
|
+
return applyHunksToFiles(hunks);
|
|
520
578
|
}
|
|
521
579
|
|
|
522
580
|
// Async version of maybeParseApplyPatchVerified
|
|
523
581
|
export async function maybeParseApplyPatchVerified(
|
|
524
582
|
argv: string[],
|
|
525
|
-
cwd: string
|
|
583
|
+
cwd: string
|
|
526
584
|
): Promise<
|
|
527
585
|
| { type: MaybeApplyPatchVerified.Body; action: ApplyPatchAction }
|
|
528
586
|
| { type: MaybeApplyPatchVerified.CorrectnessError; error: Error }
|
|
@@ -531,72 +589,83 @@ export namespace Patch {
|
|
|
531
589
|
// Detect implicit patch invocation (raw patch without apply_patch command)
|
|
532
590
|
if (argv.length === 1) {
|
|
533
591
|
try {
|
|
534
|
-
parsePatch(argv[0])
|
|
592
|
+
parsePatch(argv[0]);
|
|
535
593
|
return {
|
|
536
594
|
type: MaybeApplyPatchVerified.CorrectnessError,
|
|
537
595
|
error: new Error(ApplyPatchError.ImplicitInvocation),
|
|
538
|
-
}
|
|
596
|
+
};
|
|
539
597
|
} catch {
|
|
540
598
|
// Not a patch, continue
|
|
541
599
|
}
|
|
542
600
|
}
|
|
543
601
|
|
|
544
|
-
const result = maybeParseApplyPatch(argv)
|
|
602
|
+
const result = maybeParseApplyPatch(argv);
|
|
545
603
|
|
|
546
604
|
switch (result.type) {
|
|
547
605
|
case MaybeApplyPatch.Body:
|
|
548
|
-
const { args } = result
|
|
549
|
-
const effectiveCwd = args.workdir
|
|
550
|
-
|
|
606
|
+
const { args } = result;
|
|
607
|
+
const effectiveCwd = args.workdir
|
|
608
|
+
? path.resolve(cwd, args.workdir)
|
|
609
|
+
: cwd;
|
|
610
|
+
const changes = new Map<string, ApplyPatchFileChange>();
|
|
551
611
|
|
|
552
612
|
for (const hunk of args.hunks) {
|
|
553
613
|
const resolvedPath = path.resolve(
|
|
554
614
|
effectiveCwd,
|
|
555
|
-
hunk.type ===
|
|
556
|
-
|
|
615
|
+
hunk.type === 'update' && hunk.move_path
|
|
616
|
+
? hunk.move_path
|
|
617
|
+
: hunk.path
|
|
618
|
+
);
|
|
557
619
|
|
|
558
620
|
switch (hunk.type) {
|
|
559
|
-
case
|
|
621
|
+
case 'add':
|
|
560
622
|
changes.set(resolvedPath, {
|
|
561
|
-
type:
|
|
623
|
+
type: 'add',
|
|
562
624
|
content: hunk.contents,
|
|
563
|
-
})
|
|
564
|
-
break
|
|
625
|
+
});
|
|
626
|
+
break;
|
|
565
627
|
|
|
566
|
-
case
|
|
628
|
+
case 'delete':
|
|
567
629
|
// For delete, we need to read the current content
|
|
568
|
-
const deletePath = path.resolve(effectiveCwd, hunk.path)
|
|
630
|
+
const deletePath = path.resolve(effectiveCwd, hunk.path);
|
|
569
631
|
try {
|
|
570
|
-
const content = await fs.readFile(deletePath,
|
|
632
|
+
const content = await fs.readFile(deletePath, 'utf-8');
|
|
571
633
|
changes.set(resolvedPath, {
|
|
572
|
-
type:
|
|
634
|
+
type: 'delete',
|
|
573
635
|
content,
|
|
574
|
-
})
|
|
636
|
+
});
|
|
575
637
|
} catch (error) {
|
|
576
638
|
return {
|
|
577
639
|
type: MaybeApplyPatchVerified.CorrectnessError,
|
|
578
|
-
error: new Error(
|
|
579
|
-
|
|
640
|
+
error: new Error(
|
|
641
|
+
`Failed to read file for deletion: ${deletePath}`
|
|
642
|
+
),
|
|
643
|
+
};
|
|
580
644
|
}
|
|
581
|
-
break
|
|
645
|
+
break;
|
|
582
646
|
|
|
583
|
-
case
|
|
584
|
-
const updatePath = path.resolve(effectiveCwd, hunk.path)
|
|
647
|
+
case 'update':
|
|
648
|
+
const updatePath = path.resolve(effectiveCwd, hunk.path);
|
|
585
649
|
try {
|
|
586
|
-
const fileUpdate = deriveNewContentsFromChunks(
|
|
650
|
+
const fileUpdate = deriveNewContentsFromChunks(
|
|
651
|
+
updatePath,
|
|
652
|
+
hunk.chunks
|
|
653
|
+
);
|
|
587
654
|
changes.set(resolvedPath, {
|
|
588
|
-
type:
|
|
655
|
+
type: 'update',
|
|
589
656
|
unified_diff: fileUpdate.unified_diff,
|
|
590
|
-
move_path: hunk.move_path
|
|
657
|
+
move_path: hunk.move_path
|
|
658
|
+
? path.resolve(effectiveCwd, hunk.move_path)
|
|
659
|
+
: undefined,
|
|
591
660
|
new_content: fileUpdate.content,
|
|
592
|
-
})
|
|
661
|
+
});
|
|
593
662
|
} catch (error) {
|
|
594
663
|
return {
|
|
595
664
|
type: MaybeApplyPatchVerified.CorrectnessError,
|
|
596
665
|
error: error as Error,
|
|
597
|
-
}
|
|
666
|
+
};
|
|
598
667
|
}
|
|
599
|
-
break
|
|
668
|
+
break;
|
|
600
669
|
}
|
|
601
670
|
}
|
|
602
671
|
|
|
@@ -607,16 +676,16 @@ export namespace Patch {
|
|
|
607
676
|
patch: args.patch,
|
|
608
677
|
cwd: effectiveCwd,
|
|
609
678
|
},
|
|
610
|
-
}
|
|
679
|
+
};
|
|
611
680
|
|
|
612
681
|
case MaybeApplyPatch.PatchParseError:
|
|
613
682
|
return {
|
|
614
683
|
type: MaybeApplyPatchVerified.CorrectnessError,
|
|
615
684
|
error: result.error,
|
|
616
|
-
}
|
|
685
|
+
};
|
|
617
686
|
|
|
618
687
|
case MaybeApplyPatch.NotApplyPatch:
|
|
619
|
-
return { type: MaybeApplyPatchVerified.NotApplyPatch }
|
|
688
|
+
return { type: MaybeApplyPatchVerified.NotApplyPatch };
|
|
620
689
|
}
|
|
621
690
|
}
|
|
622
691
|
}
|