clanka 0.2.10 → 0.2.12
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/dist/AgentExecutor.d.ts +1 -4
- package/dist/AgentExecutor.d.ts.map +1 -1
- package/dist/AgentExecutor.js +25 -3
- package/dist/AgentExecutor.js.map +1 -1
- package/dist/AgentTools.d.ts +3 -12
- package/dist/AgentTools.d.ts.map +1 -1
- package/dist/AgentTools.js +7 -12
- package/dist/AgentTools.js.map +1 -1
- package/dist/CodeChunker.test.js +2 -1
- package/dist/CodeChunker.test.js.map +1 -1
- package/dist/OutputFormatter.d.ts +3 -1
- package/dist/OutputFormatter.d.ts.map +1 -1
- package/dist/OutputFormatter.js +60 -47
- package/dist/OutputFormatter.js.map +1 -1
- package/dist/ScriptPreprocessing.d.ts +2 -0
- package/dist/ScriptPreprocessing.d.ts.map +1 -0
- package/dist/ScriptPreprocessing.js +314 -0
- package/dist/ScriptPreprocessing.js.map +1 -0
- package/dist/ScriptPreprocessing.test.d.ts +2 -0
- package/dist/ScriptPreprocessing.test.d.ts.map +1 -0
- package/dist/ScriptPreprocessing.test.js +108 -0
- package/dist/ScriptPreprocessing.test.js.map +1 -0
- package/package.json +1 -1
- package/src/AgentExecutor.ts +27 -3
- package/src/AgentTools.ts +8 -12
- package/src/CodeChunker.test.ts +2 -1
- package/src/OutputFormatter.ts +75 -57
- package/src/ScriptPreprocessing.test.ts +171 -0
- package/src/ScriptPreprocessing.ts +420 -0
- package/src/fixtures/patch-broken.txt +187 -0
- package/src/fixtures/patch-fixed.txt +187 -0
- package/src/fixtures/patch2-broken.txt +257 -0
- package/src/fixtures/patch2-fixed.txt +257 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
const patch = `*** Begin Patch
|
|
2
|
+
*** Add File: src/ScriptPreprocessing.ts
|
|
3
|
+
+const isIdentifierChar = (char: string | undefined): boolean =>
|
|
4
|
+
+ char !== undefined && /[A-Za-z0-9_$]/.test(char)
|
|
5
|
+
+
|
|
6
|
+
+const hasIdentifierBoundary = (
|
|
7
|
+
+ text: string,
|
|
8
|
+
+ index: number,
|
|
9
|
+
+ length: number,
|
|
10
|
+
+): boolean =>
|
|
11
|
+
+ !isIdentifierChar(text[index - 1]) && !isIdentifierChar(text[index + length])
|
|
12
|
+
+
|
|
13
|
+
+const findNextIdentifier = (
|
|
14
|
+
+ text: string,
|
|
15
|
+
+ identifier: string,
|
|
16
|
+
+ from: number,
|
|
17
|
+
+): number => {
|
|
18
|
+
+ let index = text.indexOf(identifier, from)
|
|
19
|
+
+ while (index !== -1) {
|
|
20
|
+
+ if (hasIdentifierBoundary(text, index, identifier.length)) {
|
|
21
|
+
+ return index
|
|
22
|
+
+ }
|
|
23
|
+
+ index = text.indexOf(identifier, index + identifier.length)
|
|
24
|
+
+ }
|
|
25
|
+
+ return -1
|
|
26
|
+
+}
|
|
27
|
+
+
|
|
28
|
+
+const skipWhitespace = (text: string, start: number): number => {
|
|
29
|
+
+ let i = start
|
|
30
|
+
+ while (i < text.length && /\\s/.test(text[i]!)) {
|
|
31
|
+
+ i++
|
|
32
|
+
+ }
|
|
33
|
+
+ return i
|
|
34
|
+
+}
|
|
35
|
+
+
|
|
36
|
+
+const isEscaped = (text: string, index: number): boolean => {
|
|
37
|
+
+ let slashCount = 0
|
|
38
|
+
+ let i = index - 1
|
|
39
|
+
+ while (i >= 0 && text[i] === "\\\\") {
|
|
40
|
+
+ slashCount++
|
|
41
|
+
+ i--
|
|
42
|
+
+ }
|
|
43
|
+
+ return slashCount % 2 === 1
|
|
44
|
+
+}
|
|
45
|
+
+
|
|
46
|
+
+const escapeUnescapedBackticks = (text: string): string => {
|
|
47
|
+
+ let out = ""
|
|
48
|
+
+ for (let i = 0; i < text.length; i++) {
|
|
49
|
+
+ const char = text[i]!
|
|
50
|
+
+ if (char === "\`" && !isEscaped(text, i)) {
|
|
51
|
+
+ out += "\\\\\`"
|
|
52
|
+
+ continue
|
|
53
|
+
+ }
|
|
54
|
+
+ out += char
|
|
55
|
+
+ }
|
|
56
|
+
+ return out
|
|
57
|
+
+}
|
|
58
|
+
+
|
|
59
|
+
+const findTemplateEnd = (
|
|
60
|
+
+ text: string,
|
|
61
|
+
+ start: number,
|
|
62
|
+
+ isTerminator: (char: string | undefined) => boolean,
|
|
63
|
+
+): number => {
|
|
64
|
+
+ for (let i = start + 1; i < text.length; i++) {
|
|
65
|
+
+ if (text[i] !== "\`" || isEscaped(text, i)) {
|
|
66
|
+
+ continue
|
|
67
|
+
+ }
|
|
68
|
+
+ const next = skipWhitespace(text, i + 1)
|
|
69
|
+
+ if (isTerminator(text[next])) {
|
|
70
|
+
+ return i
|
|
71
|
+
+ }
|
|
72
|
+
+ }
|
|
73
|
+
+ return -1
|
|
74
|
+
+}
|
|
75
|
+
+
|
|
76
|
+
+const fixCallTemplateArgument = (
|
|
77
|
+
+ script: string,
|
|
78
|
+
+ functionName: string,
|
|
79
|
+
+ isTerminator: (char: string | undefined) => boolean,
|
|
80
|
+
+): string => {
|
|
81
|
+
+ let out = script
|
|
82
|
+
+ let cursor = 0
|
|
83
|
+
+
|
|
84
|
+
+ while (cursor < out.length) {
|
|
85
|
+
+ const callStart = findNextIdentifier(out, functionName, cursor)
|
|
86
|
+
+ if (callStart === -1) {
|
|
87
|
+
+ break
|
|
88
|
+
+ }
|
|
89
|
+
+
|
|
90
|
+
+ const openParen = skipWhitespace(out, callStart + functionName.length)
|
|
91
|
+
+ if (out[openParen] !== "(") {
|
|
92
|
+
+ cursor = callStart + functionName.length
|
|
93
|
+
+ continue
|
|
94
|
+
+ }
|
|
95
|
+
+
|
|
96
|
+
+ const templateStart = skipWhitespace(out, openParen + 1)
|
|
97
|
+
+ if (out[templateStart] !== "\`") {
|
|
98
|
+
+ cursor = openParen + 1
|
|
99
|
+
+ continue
|
|
100
|
+
+ }
|
|
101
|
+
+
|
|
102
|
+
+ const templateEnd = findTemplateEnd(out, templateStart, isTerminator)
|
|
103
|
+
+ if (templateEnd === -1) {
|
|
104
|
+
+ cursor = templateStart + 1
|
|
105
|
+
+ continue
|
|
106
|
+
+ }
|
|
107
|
+
+
|
|
108
|
+
+ const original = out.slice(templateStart + 1, templateEnd)
|
|
109
|
+
+ const escaped = escapeUnescapedBackticks(original)
|
|
110
|
+
+ if (escaped !== original) {
|
|
111
|
+
+ out = \`\${out.slice(0, templateStart + 1)}\${escaped}\${out.slice(templateEnd)}\`
|
|
112
|
+
+ cursor = templateEnd + (escaped.length - original.length) + 1
|
|
113
|
+
+ continue
|
|
114
|
+
+ }
|
|
115
|
+
+
|
|
116
|
+
+ cursor = templateEnd + 1
|
|
117
|
+
+ }
|
|
118
|
+
+
|
|
119
|
+
+ return out
|
|
120
|
+
+}
|
|
121
|
+
+
|
|
122
|
+
+const fixWriteFileContentTemplates = (script: string): string => {
|
|
123
|
+
+ let out = script
|
|
124
|
+
+ let cursor = 0
|
|
125
|
+
+
|
|
126
|
+
+ while (cursor < out.length) {
|
|
127
|
+
+ const callStart = findNextIdentifier(out, "writeFile", cursor)
|
|
128
|
+
+ if (callStart === -1) {
|
|
129
|
+
+ break
|
|
130
|
+
+ }
|
|
131
|
+
+
|
|
132
|
+
+ const openParen = skipWhitespace(out, callStart + "writeFile".length)
|
|
133
|
+
+ if (out[openParen] !== "(") {
|
|
134
|
+
+ cursor = callStart + "writeFile".length
|
|
135
|
+
+ continue
|
|
136
|
+
+ }
|
|
137
|
+
+
|
|
138
|
+
+ const contentKey = findNextIdentifier(out, "content", openParen + 1)
|
|
139
|
+
+ if (contentKey === -1) {
|
|
140
|
+
+ cursor = openParen + 1
|
|
141
|
+
+ continue
|
|
142
|
+
+ }
|
|
143
|
+
+
|
|
144
|
+
+ const colon = skipWhitespace(out, contentKey + "content".length)
|
|
145
|
+
+ if (out[colon] !== ":") {
|
|
146
|
+
+ cursor = contentKey + "content".length
|
|
147
|
+
+ continue
|
|
148
|
+
+ }
|
|
149
|
+
+
|
|
150
|
+
+ const templateStart = skipWhitespace(out, colon + 1)
|
|
151
|
+
+ if (out[templateStart] !== "\`") {
|
|
152
|
+
+ cursor = templateStart + 1
|
|
153
|
+
+ continue
|
|
154
|
+
+ }
|
|
155
|
+
+
|
|
156
|
+
+ const templateEnd = findTemplateEnd(
|
|
157
|
+
+ out,
|
|
158
|
+
+ templateStart,
|
|
159
|
+
+ (char) => char === "}" || char === ",",
|
|
160
|
+
+ )
|
|
161
|
+
+ if (templateEnd === -1) {
|
|
162
|
+
+ cursor = templateStart + 1
|
|
163
|
+
+ continue
|
|
164
|
+
+ }
|
|
165
|
+
+
|
|
166
|
+
+ const original = out.slice(templateStart + 1, templateEnd)
|
|
167
|
+
+ const escaped = escapeUnescapedBackticks(original)
|
|
168
|
+
+ if (escaped !== original) {
|
|
169
|
+
+ out = \`\${out.slice(0, templateStart + 1)}\${escaped}\${out.slice(templateEnd)}\`
|
|
170
|
+
+ cursor = templateEnd + (escaped.length - original.length) + 1
|
|
171
|
+
+ continue
|
|
172
|
+
+ }
|
|
173
|
+
+
|
|
174
|
+
+ cursor = templateEnd + 1
|
|
175
|
+
+ }
|
|
176
|
+
+
|
|
177
|
+
+ return out
|
|
178
|
+
+}
|
|
179
|
+
+
|
|
180
|
+
+export const preprocessScript = (script: string): string =>
|
|
181
|
+
+ ["applyPatch", "taskComplete"].reduce(
|
|
182
|
+
+ (current, functionName) =>
|
|
183
|
+
+ fixCallTemplateArgument(current, functionName, (char) => char === ")"),
|
|
184
|
+
+ fixWriteFileContentTemplates(script),
|
|
185
|
+
+ )
|
|
186
|
+
*** End Patch`;
|
|
187
|
+
console.log(await applyPatch(patch));
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const patch = `*** Begin Patch
|
|
2
|
+
*** Update File: src/ScriptPreprocessing.ts
|
|
3
|
+
@@
|
|
4
|
+
const isIdentifierChar = (char: string | undefined): boolean =>
|
|
5
|
+
char !== undefined && /[A-Za-z0-9_$]/.test(char)
|
|
6
|
+
+
|
|
7
|
+
+const isIdentifierStartChar = (char: string | undefined): boolean =>
|
|
8
|
+
+ char !== undefined && /[A-Za-z_$]/.test(char)
|
|
9
|
+
@@
|
|
10
|
+
const skipWhitespace = (text: string, start: number): number => {
|
|
11
|
+
let i = start
|
|
12
|
+
while (i < text.length && /\\s/.test(text[i]!)) {
|
|
13
|
+
i++
|
|
14
|
+
}
|
|
15
|
+
return i
|
|
16
|
+
}
|
|
17
|
+
+
|
|
18
|
+
+const hasNewline = (text: string): boolean => text.includes("\\n")
|
|
19
|
+
+
|
|
20
|
+
+const parseIdentifier = (
|
|
21
|
+
+ text: string,
|
|
22
|
+
+ start: number,
|
|
23
|
+
+): { readonly name: string; readonly end: number } | undefined => {
|
|
24
|
+
+ if (!isIdentifierStartChar(text[start])) {
|
|
25
|
+
+ return undefined
|
|
26
|
+
+ }
|
|
27
|
+
+ let end = start + 1
|
|
28
|
+
+ while (end < text.length && isIdentifierChar(text[end])) {
|
|
29
|
+
+ end++
|
|
30
|
+
+ }
|
|
31
|
+
+ return {
|
|
32
|
+
+ name: text.slice(start, end),
|
|
33
|
+
+ end,
|
|
34
|
+
+ }
|
|
35
|
+
+}
|
|
36
|
+
@@
|
|
37
|
+
const findTemplateEnd = (
|
|
38
|
+
text: string,
|
|
39
|
+
start: number,
|
|
40
|
+
isTerminator: (char: string | undefined) => boolean,
|
|
41
|
+
@@
|
|
42
|
+
return -1
|
|
43
|
+
}
|
|
44
|
+
+
|
|
45
|
+
+const findTypeAnnotationAssignment = (text: string, start: number): number => {
|
|
46
|
+
+ let i = start
|
|
47
|
+
+ while (i < text.length) {
|
|
48
|
+
+ const char = text[i]!
|
|
49
|
+
+ if (char === "=") {
|
|
50
|
+
+ return i
|
|
51
|
+
+ }
|
|
52
|
+
+ if (char === "\\n" || char === ";") {
|
|
53
|
+
+ return -1
|
|
54
|
+
+ }
|
|
55
|
+
+ i++
|
|
56
|
+
+ }
|
|
57
|
+
+ return -1
|
|
58
|
+
+}
|
|
59
|
+
@@
|
|
60
|
+
const fixCallTemplateArgument = (
|
|
61
|
+
script: string,
|
|
62
|
+
functionName: string,
|
|
63
|
+
isTerminator: (char: string | undefined) => boolean,
|
|
64
|
+
@@
|
|
65
|
+
return out
|
|
66
|
+
}
|
|
67
|
+
+
|
|
68
|
+
+const collectCallArgumentIdentifiers = (
|
|
69
|
+
+ script: string,
|
|
70
|
+
+ functionName: string,
|
|
71
|
+
+): ReadonlySet<string> => {
|
|
72
|
+
+ const out = new Set<string>()
|
|
73
|
+
+ let cursor = 0
|
|
74
|
+
+
|
|
75
|
+
+ while (cursor < script.length) {
|
|
76
|
+
+ const callStart = findNextIdentifier(script, functionName, cursor)
|
|
77
|
+
+ if (callStart === -1) {
|
|
78
|
+
+ break
|
|
79
|
+
+ }
|
|
80
|
+
+
|
|
81
|
+
+ const openParen = skipWhitespace(script, callStart + functionName.length)
|
|
82
|
+
+ if (script[openParen] !== "(") {
|
|
83
|
+
+ cursor = callStart + functionName.length
|
|
84
|
+
+ continue
|
|
85
|
+
+ }
|
|
86
|
+
+
|
|
87
|
+
+ const argumentStart = skipWhitespace(script, openParen + 1)
|
|
88
|
+
+ const identifier = parseIdentifier(script, argumentStart)
|
|
89
|
+
+ if (identifier === undefined) {
|
|
90
|
+
+ cursor = openParen + 1
|
|
91
|
+
+ continue
|
|
92
|
+
+ }
|
|
93
|
+
+
|
|
94
|
+
+ const argumentEnd = skipWhitespace(script, identifier.end)
|
|
95
|
+
+ if (script[argumentEnd] === ")" || script[argumentEnd] === ",") {
|
|
96
|
+
+ out.add(identifier.name)
|
|
97
|
+
+ }
|
|
98
|
+
+
|
|
99
|
+
+ cursor = identifier.end
|
|
100
|
+
+ }
|
|
101
|
+
+
|
|
102
|
+
+ return out
|
|
103
|
+
+}
|
|
104
|
+
@@
|
|
105
|
+
const fixWriteFileContentTemplates = (script: string): string => {
|
|
106
|
+
@@
|
|
107
|
+
return out
|
|
108
|
+
}
|
|
109
|
+
+
|
|
110
|
+
+const collectWriteFileContentIdentifiers = (
|
|
111
|
+
+ script: string,
|
|
112
|
+
+): ReadonlySet<string> => {
|
|
113
|
+
+ const out = new Set<string>()
|
|
114
|
+
+ let cursor = 0
|
|
115
|
+
+
|
|
116
|
+
+ while (cursor < script.length) {
|
|
117
|
+
+ const callStart = findNextIdentifier(script, "writeFile", cursor)
|
|
118
|
+
+ if (callStart === -1) {
|
|
119
|
+
+ break
|
|
120
|
+
+ }
|
|
121
|
+
+
|
|
122
|
+
+ const openParen = skipWhitespace(script, callStart + "writeFile".length)
|
|
123
|
+
+ if (script[openParen] !== "(") {
|
|
124
|
+
+ cursor = callStart + "writeFile".length
|
|
125
|
+
+ continue
|
|
126
|
+
+ }
|
|
127
|
+
+
|
|
128
|
+
+ const contentKey = findNextIdentifier(script, "content", openParen + 1)
|
|
129
|
+
+ if (contentKey === -1) {
|
|
130
|
+
+ cursor = openParen + 1
|
|
131
|
+
+ continue
|
|
132
|
+
+ }
|
|
133
|
+
+
|
|
134
|
+
+ const afterContent = skipWhitespace(script, contentKey + "content".length)
|
|
135
|
+
+ if (script[afterContent] === ":") {
|
|
136
|
+
+ const valueStart = skipWhitespace(script, afterContent + 1)
|
|
137
|
+
+ const identifier = parseIdentifier(script, valueStart)
|
|
138
|
+
+ if (identifier !== undefined) {
|
|
139
|
+
+ const valueEnd = skipWhitespace(script, identifier.end)
|
|
140
|
+
+ if (script[valueEnd] === "}" || script[valueEnd] === ",") {
|
|
141
|
+
+ out.add(identifier.name)
|
|
142
|
+
+ }
|
|
143
|
+
+ }
|
|
144
|
+
+ cursor = valueStart + 1
|
|
145
|
+
+ continue
|
|
146
|
+
+ }
|
|
147
|
+
+
|
|
148
|
+
+ if (script[afterContent] === "}" || script[afterContent] === ",") {
|
|
149
|
+
+ out.add("content")
|
|
150
|
+
+ cursor = afterContent + 1
|
|
151
|
+
+ continue
|
|
152
|
+
+ }
|
|
153
|
+
+
|
|
154
|
+
+ cursor = afterContent + 1
|
|
155
|
+
+ }
|
|
156
|
+
+
|
|
157
|
+
+ return out
|
|
158
|
+
+}
|
|
159
|
+
+
|
|
160
|
+
+const fixAssignedTemplate = (script: string, variableName: string): string => {
|
|
161
|
+
+ let out = script
|
|
162
|
+
+ let cursor = 0
|
|
163
|
+
+
|
|
164
|
+
+ while (cursor < out.length) {
|
|
165
|
+
+ const variableStart = findNextIdentifier(out, variableName, cursor)
|
|
166
|
+
+ if (variableStart === -1) {
|
|
167
|
+
+ break
|
|
168
|
+
+ }
|
|
169
|
+
+
|
|
170
|
+
+ let assignmentStart = skipWhitespace(out, variableStart + variableName.length)
|
|
171
|
+
+ if (out[assignmentStart] === ":") {
|
|
172
|
+
+ assignmentStart = findTypeAnnotationAssignment(out, assignmentStart + 1)
|
|
173
|
+
+ if (assignmentStart === -1) {
|
|
174
|
+
+ cursor = variableStart + variableName.length
|
|
175
|
+
+ continue
|
|
176
|
+
+ }
|
|
177
|
+
+ }
|
|
178
|
+
+
|
|
179
|
+
+ if (
|
|
180
|
+
+ out[assignmentStart] !== "=" ||
|
|
181
|
+
+ out[assignmentStart + 1] === "=" ||
|
|
182
|
+
+ out[assignmentStart + 1] === ">"
|
|
183
|
+
+ ) {
|
|
184
|
+
+ cursor = variableStart + variableName.length
|
|
185
|
+
+ continue
|
|
186
|
+
+ }
|
|
187
|
+
+
|
|
188
|
+
+ const templateStart = skipWhitespace(out, assignmentStart + 1)
|
|
189
|
+
+ if (out[templateStart] !== "`") {
|
|
190
|
+
+ cursor = templateStart + 1
|
|
191
|
+
+ continue
|
|
192
|
+
+ }
|
|
193
|
+
+
|
|
194
|
+
+ const templateEnd = findTemplateEnd(
|
|
195
|
+
+ out,
|
|
196
|
+
+ templateStart,
|
|
197
|
+
+ (char) =>
|
|
198
|
+
+ char === undefined ||
|
|
199
|
+
+ char === ";" ||
|
|
200
|
+
+ char === "," ||
|
|
201
|
+
+ char === ")" ||
|
|
202
|
+
+ char === "}" ||
|
|
203
|
+
+ char === "]",
|
|
204
|
+
+ )
|
|
205
|
+
+ if (templateEnd === -1) {
|
|
206
|
+
+ cursor = templateStart + 1
|
|
207
|
+
+ continue
|
|
208
|
+
+ }
|
|
209
|
+
+
|
|
210
|
+
+ const original = out.slice(templateStart + 1, templateEnd)
|
|
211
|
+
+ const escaped = escapeUnescapedBackticks(original)
|
|
212
|
+
+ if (escaped !== original) {
|
|
213
|
+
+ out = `${out.slice(0, templateStart + 1)}${escaped}${out.slice(templateEnd)}`
|
|
214
|
+
+ cursor = templateEnd + (escaped.length - original.length) + 1
|
|
215
|
+
+ continue
|
|
216
|
+
+ }
|
|
217
|
+
+
|
|
218
|
+
+ cursor = templateEnd + 1
|
|
219
|
+
+ }
|
|
220
|
+
+
|
|
221
|
+
+ return out
|
|
222
|
+
+}
|
|
223
|
+
+
|
|
224
|
+
+const fixAssignedTemplatesForToolCalls = (script: string): string => {
|
|
225
|
+
+ const identifiers = new Set<string>()
|
|
226
|
+
+ for (const name of ["applyPatch", "taskComplete"] as const) {
|
|
227
|
+
+ for (const identifier of collectCallArgumentIdentifiers(script, name)) {
|
|
228
|
+
+ identifiers.add(identifier)
|
|
229
|
+
+ }
|
|
230
|
+
+ }
|
|
231
|
+
+ for (const identifier of collectWriteFileContentIdentifiers(script)) {
|
|
232
|
+
+ identifiers.add(identifier)
|
|
233
|
+
+ }
|
|
234
|
+
+
|
|
235
|
+
+ let out = script
|
|
236
|
+
+ for (const identifier of identifiers) {
|
|
237
|
+
+ out = fixAssignedTemplate(out, identifier)
|
|
238
|
+
+ }
|
|
239
|
+
+
|
|
240
|
+
+ return out
|
|
241
|
+
+}
|
|
242
|
+
|
|
243
|
+
export const preprocessScript = (script: string): string =>
|
|
244
|
+
- ["applyPatch", "taskComplete"].reduce(
|
|
245
|
+
- (current, functionName) =>
|
|
246
|
+
- fixCallTemplateArgument(current, functionName, (char) => char === ")"),
|
|
247
|
+
- fixWriteFileContentTemplates(script),
|
|
248
|
+
- )
|
|
249
|
+
+ fixAssignedTemplatesForToolCalls(
|
|
250
|
+
+ ["applyPatch", "taskComplete"].reduce(
|
|
251
|
+
+ (current, functionName) =>
|
|
252
|
+
+ fixCallTemplateArgument(current, functionName, (char) => char === ")"),
|
|
253
|
+
+ fixWriteFileContentTemplates(script),
|
|
254
|
+
+ ),
|
|
255
|
+
+ )
|
|
256
|
+
*** End Patch`;
|
|
257
|
+
console.log(await applyPatch(patch));
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
const patch = `*** Begin Patch
|
|
2
|
+
*** Update File: src/ScriptPreprocessing.ts
|
|
3
|
+
@@
|
|
4
|
+
const isIdentifierChar = (char: string | undefined): boolean =>
|
|
5
|
+
char !== undefined && /[A-Za-z0-9_$]/.test(char)
|
|
6
|
+
+
|
|
7
|
+
+const isIdentifierStartChar = (char: string | undefined): boolean =>
|
|
8
|
+
+ char !== undefined && /[A-Za-z_$]/.test(char)
|
|
9
|
+
@@
|
|
10
|
+
const skipWhitespace = (text: string, start: number): number => {
|
|
11
|
+
let i = start
|
|
12
|
+
while (i < text.length && /\\\\s/.test(text[i]!)) {
|
|
13
|
+
i++
|
|
14
|
+
}
|
|
15
|
+
return i
|
|
16
|
+
}
|
|
17
|
+
+
|
|
18
|
+
+const hasNewline = (text: string): boolean => text.includes("\\\\n")
|
|
19
|
+
+
|
|
20
|
+
+const parseIdentifier = (
|
|
21
|
+
+ text: string,
|
|
22
|
+
+ start: number,
|
|
23
|
+
+): { readonly name: string; readonly end: number } | undefined => {
|
|
24
|
+
+ if (!isIdentifierStartChar(text[start])) {
|
|
25
|
+
+ return undefined
|
|
26
|
+
+ }
|
|
27
|
+
+ let end = start + 1
|
|
28
|
+
+ while (end < text.length && isIdentifierChar(text[end])) {
|
|
29
|
+
+ end++
|
|
30
|
+
+ }
|
|
31
|
+
+ return {
|
|
32
|
+
+ name: text.slice(start, end),
|
|
33
|
+
+ end,
|
|
34
|
+
+ }
|
|
35
|
+
+}
|
|
36
|
+
@@
|
|
37
|
+
const findTemplateEnd = (
|
|
38
|
+
text: string,
|
|
39
|
+
start: number,
|
|
40
|
+
isTerminator: (char: string | undefined) => boolean,
|
|
41
|
+
@@
|
|
42
|
+
return -1
|
|
43
|
+
}
|
|
44
|
+
+
|
|
45
|
+
+const findTypeAnnotationAssignment = (text: string, start: number): number => {
|
|
46
|
+
+ let i = start
|
|
47
|
+
+ while (i < text.length) {
|
|
48
|
+
+ const char = text[i]!
|
|
49
|
+
+ if (char === "=") {
|
|
50
|
+
+ return i
|
|
51
|
+
+ }
|
|
52
|
+
+ if (char === "\\\\n" || char === ";") {
|
|
53
|
+
+ return -1
|
|
54
|
+
+ }
|
|
55
|
+
+ i++
|
|
56
|
+
+ }
|
|
57
|
+
+ return -1
|
|
58
|
+
+}
|
|
59
|
+
@@
|
|
60
|
+
const fixCallTemplateArgument = (
|
|
61
|
+
script: string,
|
|
62
|
+
functionName: string,
|
|
63
|
+
isTerminator: (char: string | undefined) => boolean,
|
|
64
|
+
@@
|
|
65
|
+
return out
|
|
66
|
+
}
|
|
67
|
+
+
|
|
68
|
+
+const collectCallArgumentIdentifiers = (
|
|
69
|
+
+ script: string,
|
|
70
|
+
+ functionName: string,
|
|
71
|
+
+): ReadonlySet<string> => {
|
|
72
|
+
+ const out = new Set<string>()
|
|
73
|
+
+ let cursor = 0
|
|
74
|
+
+
|
|
75
|
+
+ while (cursor < script.length) {
|
|
76
|
+
+ const callStart = findNextIdentifier(script, functionName, cursor)
|
|
77
|
+
+ if (callStart === -1) {
|
|
78
|
+
+ break
|
|
79
|
+
+ }
|
|
80
|
+
+
|
|
81
|
+
+ const openParen = skipWhitespace(script, callStart + functionName.length)
|
|
82
|
+
+ if (script[openParen] !== "(") {
|
|
83
|
+
+ cursor = callStart + functionName.length
|
|
84
|
+
+ continue
|
|
85
|
+
+ }
|
|
86
|
+
+
|
|
87
|
+
+ const argumentStart = skipWhitespace(script, openParen + 1)
|
|
88
|
+
+ const identifier = parseIdentifier(script, argumentStart)
|
|
89
|
+
+ if (identifier === undefined) {
|
|
90
|
+
+ cursor = openParen + 1
|
|
91
|
+
+ continue
|
|
92
|
+
+ }
|
|
93
|
+
+
|
|
94
|
+
+ const argumentEnd = skipWhitespace(script, identifier.end)
|
|
95
|
+
+ if (script[argumentEnd] === ")" || script[argumentEnd] === ",") {
|
|
96
|
+
+ out.add(identifier.name)
|
|
97
|
+
+ }
|
|
98
|
+
+
|
|
99
|
+
+ cursor = identifier.end
|
|
100
|
+
+ }
|
|
101
|
+
+
|
|
102
|
+
+ return out
|
|
103
|
+
+}
|
|
104
|
+
@@
|
|
105
|
+
const fixWriteFileContentTemplates = (script: string): string => {
|
|
106
|
+
@@
|
|
107
|
+
return out
|
|
108
|
+
}
|
|
109
|
+
+
|
|
110
|
+
+const collectWriteFileContentIdentifiers = (
|
|
111
|
+
+ script: string,
|
|
112
|
+
+): ReadonlySet<string> => {
|
|
113
|
+
+ const out = new Set<string>()
|
|
114
|
+
+ let cursor = 0
|
|
115
|
+
+
|
|
116
|
+
+ while (cursor < script.length) {
|
|
117
|
+
+ const callStart = findNextIdentifier(script, "writeFile", cursor)
|
|
118
|
+
+ if (callStart === -1) {
|
|
119
|
+
+ break
|
|
120
|
+
+ }
|
|
121
|
+
+
|
|
122
|
+
+ const openParen = skipWhitespace(script, callStart + "writeFile".length)
|
|
123
|
+
+ if (script[openParen] !== "(") {
|
|
124
|
+
+ cursor = callStart + "writeFile".length
|
|
125
|
+
+ continue
|
|
126
|
+
+ }
|
|
127
|
+
+
|
|
128
|
+
+ const contentKey = findNextIdentifier(script, "content", openParen + 1)
|
|
129
|
+
+ if (contentKey === -1) {
|
|
130
|
+
+ cursor = openParen + 1
|
|
131
|
+
+ continue
|
|
132
|
+
+ }
|
|
133
|
+
+
|
|
134
|
+
+ const afterContent = skipWhitespace(script, contentKey + "content".length)
|
|
135
|
+
+ if (script[afterContent] === ":") {
|
|
136
|
+
+ const valueStart = skipWhitespace(script, afterContent + 1)
|
|
137
|
+
+ const identifier = parseIdentifier(script, valueStart)
|
|
138
|
+
+ if (identifier !== undefined) {
|
|
139
|
+
+ const valueEnd = skipWhitespace(script, identifier.end)
|
|
140
|
+
+ if (script[valueEnd] === "}" || script[valueEnd] === ",") {
|
|
141
|
+
+ out.add(identifier.name)
|
|
142
|
+
+ }
|
|
143
|
+
+ }
|
|
144
|
+
+ cursor = valueStart + 1
|
|
145
|
+
+ continue
|
|
146
|
+
+ }
|
|
147
|
+
+
|
|
148
|
+
+ if (script[afterContent] === "}" || script[afterContent] === ",") {
|
|
149
|
+
+ out.add("content")
|
|
150
|
+
+ cursor = afterContent + 1
|
|
151
|
+
+ continue
|
|
152
|
+
+ }
|
|
153
|
+
+
|
|
154
|
+
+ cursor = afterContent + 1
|
|
155
|
+
+ }
|
|
156
|
+
+
|
|
157
|
+
+ return out
|
|
158
|
+
+}
|
|
159
|
+
+
|
|
160
|
+
+const fixAssignedTemplate = (script: string, variableName: string): string => {
|
|
161
|
+
+ let out = script
|
|
162
|
+
+ let cursor = 0
|
|
163
|
+
+
|
|
164
|
+
+ while (cursor < out.length) {
|
|
165
|
+
+ const variableStart = findNextIdentifier(out, variableName, cursor)
|
|
166
|
+
+ if (variableStart === -1) {
|
|
167
|
+
+ break
|
|
168
|
+
+ }
|
|
169
|
+
+
|
|
170
|
+
+ let assignmentStart = skipWhitespace(out, variableStart + variableName.length)
|
|
171
|
+
+ if (out[assignmentStart] === ":") {
|
|
172
|
+
+ assignmentStart = findTypeAnnotationAssignment(out, assignmentStart + 1)
|
|
173
|
+
+ if (assignmentStart === -1) {
|
|
174
|
+
+ cursor = variableStart + variableName.length
|
|
175
|
+
+ continue
|
|
176
|
+
+ }
|
|
177
|
+
+ }
|
|
178
|
+
+
|
|
179
|
+
+ if (
|
|
180
|
+
+ out[assignmentStart] !== "=" ||
|
|
181
|
+
+ out[assignmentStart + 1] === "=" ||
|
|
182
|
+
+ out[assignmentStart + 1] === ">"
|
|
183
|
+
+ ) {
|
|
184
|
+
+ cursor = variableStart + variableName.length
|
|
185
|
+
+ continue
|
|
186
|
+
+ }
|
|
187
|
+
+
|
|
188
|
+
+ const templateStart = skipWhitespace(out, assignmentStart + 1)
|
|
189
|
+
+ if (out[templateStart] !== "\`") {
|
|
190
|
+
+ cursor = templateStart + 1
|
|
191
|
+
+ continue
|
|
192
|
+
+ }
|
|
193
|
+
+
|
|
194
|
+
+ const templateEnd = findTemplateEnd(
|
|
195
|
+
+ out,
|
|
196
|
+
+ templateStart,
|
|
197
|
+
+ (char) =>
|
|
198
|
+
+ char === undefined ||
|
|
199
|
+
+ char === ";" ||
|
|
200
|
+
+ char === "," ||
|
|
201
|
+
+ char === ")" ||
|
|
202
|
+
+ char === "}" ||
|
|
203
|
+
+ char === "]",
|
|
204
|
+
+ )
|
|
205
|
+
+ if (templateEnd === -1) {
|
|
206
|
+
+ cursor = templateStart + 1
|
|
207
|
+
+ continue
|
|
208
|
+
+ }
|
|
209
|
+
+
|
|
210
|
+
+ const original = out.slice(templateStart + 1, templateEnd)
|
|
211
|
+
+ const escaped = escapeUnescapedBackticks(original)
|
|
212
|
+
+ if (escaped !== original) {
|
|
213
|
+
+ out = \`\${out.slice(0, templateStart + 1)}\${escaped}\${out.slice(templateEnd)}\`
|
|
214
|
+
+ cursor = templateEnd + (escaped.length - original.length) + 1
|
|
215
|
+
+ continue
|
|
216
|
+
+ }
|
|
217
|
+
+
|
|
218
|
+
+ cursor = templateEnd + 1
|
|
219
|
+
+ }
|
|
220
|
+
+
|
|
221
|
+
+ return out
|
|
222
|
+
+}
|
|
223
|
+
+
|
|
224
|
+
+const fixAssignedTemplatesForToolCalls = (script: string): string => {
|
|
225
|
+
+ const identifiers = new Set<string>()
|
|
226
|
+
+ for (const name of ["applyPatch", "taskComplete"] as const) {
|
|
227
|
+
+ for (const identifier of collectCallArgumentIdentifiers(script, name)) {
|
|
228
|
+
+ identifiers.add(identifier)
|
|
229
|
+
+ }
|
|
230
|
+
+ }
|
|
231
|
+
+ for (const identifier of collectWriteFileContentIdentifiers(script)) {
|
|
232
|
+
+ identifiers.add(identifier)
|
|
233
|
+
+ }
|
|
234
|
+
+
|
|
235
|
+
+ let out = script
|
|
236
|
+
+ for (const identifier of identifiers) {
|
|
237
|
+
+ out = fixAssignedTemplate(out, identifier)
|
|
238
|
+
+ }
|
|
239
|
+
+
|
|
240
|
+
+ return out
|
|
241
|
+
+}
|
|
242
|
+
|
|
243
|
+
export const preprocessScript = (script: string): string =>
|
|
244
|
+
- ["applyPatch", "taskComplete"].reduce(
|
|
245
|
+
- (current, functionName) =>
|
|
246
|
+
- fixCallTemplateArgument(current, functionName, (char) => char === ")"),
|
|
247
|
+
- fixWriteFileContentTemplates(script),
|
|
248
|
+
- )
|
|
249
|
+
+ fixAssignedTemplatesForToolCalls(
|
|
250
|
+
+ ["applyPatch", "taskComplete"].reduce(
|
|
251
|
+
+ (current, functionName) =>
|
|
252
|
+
+ fixCallTemplateArgument(current, functionName, (char) => char === ")"),
|
|
253
|
+
+ fixWriteFileContentTemplates(script),
|
|
254
|
+
+ ),
|
|
255
|
+
+ )
|
|
256
|
+
*** End Patch`;
|
|
257
|
+
console.log(await applyPatch(patch));
|