@howaboua/pi-codex-conversion 1.0.8 → 1.0.9
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/README.md +37 -0
- package/package.json +7 -2
- package/src/adapter/runtime-shell.ts +13 -0
- package/src/index.ts +2 -1
- package/src/prompt/build-system-prompt.ts +4 -1
- package/src/shell/bash.ts +261 -0
- package/src/shell/parse-command.ts +616 -0
- package/src/shell/parse.ts +370 -93
- package/src/shell/summary.ts +19 -51
- package/src/shell/tokenize.ts +28 -5
- package/src/tools/exec-session-manager.ts +61 -5
package/src/shell/parse.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ShellAction } from "./types.ts";
|
|
2
|
-
import { isAbsoluteLike, joinPaths, shortDisplayPath } from "./tokenize.ts";
|
|
2
|
+
import { isAbsoluteLike, joinCommandTokens, joinPaths, shortDisplayPath } from "./tokenize.ts";
|
|
3
3
|
|
|
4
4
|
export function parseShellPart(tokens: string[], cwd?: string): ShellAction | null {
|
|
5
5
|
if (tokens.length === 0) return null;
|
|
@@ -18,6 +18,13 @@ export function parseShellPart(tokens: string[], cwd?: string): ShellAction | nu
|
|
|
18
18
|
path: joinPaths(cwd, parsed.path),
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
if (parsed.kind === "list" && cwd && !parsed.path) {
|
|
22
|
+
return {
|
|
23
|
+
...parsed,
|
|
24
|
+
path: shortDisplayPath(cwd),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
return parsed;
|
|
22
29
|
}
|
|
23
30
|
|
|
@@ -28,30 +35,97 @@ export function nextCwd(currentCwd: string | undefined, tokens: string[]): strin
|
|
|
28
35
|
return currentCwd ? joinPaths(currentCwd, target) : target;
|
|
29
36
|
}
|
|
30
37
|
|
|
38
|
+
export function isSmallFormattingCommand(tokens: string[]): boolean {
|
|
39
|
+
if (tokens.length === 0) return false;
|
|
40
|
+
const [head, ...tail] = tokens;
|
|
41
|
+
if (
|
|
42
|
+
head === "wc" ||
|
|
43
|
+
head === "tr" ||
|
|
44
|
+
head === "cut" ||
|
|
45
|
+
head === "sort" ||
|
|
46
|
+
head === "uniq" ||
|
|
47
|
+
head === "tee" ||
|
|
48
|
+
head === "column" ||
|
|
49
|
+
head === "yes" ||
|
|
50
|
+
head === "printf"
|
|
51
|
+
) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (head === "xargs") {
|
|
55
|
+
return !xargsIsMutatingSubcommand(tail);
|
|
56
|
+
}
|
|
57
|
+
if (head === "awk") {
|
|
58
|
+
return awkDataFileOperand(tail) === undefined;
|
|
59
|
+
}
|
|
60
|
+
if (head === "head") {
|
|
61
|
+
if (tail.length === 0) return true;
|
|
62
|
+
if (tail.length === 1) return tail[0].startsWith("-");
|
|
63
|
+
if (tail.length === 2 && (tail[0] === "-n" || tail[0] === "-c") && /^\d+$/.test(tail[1])) return true;
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (head === "tail") {
|
|
67
|
+
if (tail.length === 0) return true;
|
|
68
|
+
if (tail.length === 1) return tail[0].startsWith("-");
|
|
69
|
+
if (tail.length === 2 && (tail[0] === "-n" || tail[0] === "-c")) {
|
|
70
|
+
const value = tail[1]?.startsWith("+") ? tail[1].slice(1) : tail[1];
|
|
71
|
+
if (value && /^\d+$/.test(value)) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (head === "sed") {
|
|
76
|
+
return sedReadPath(tail) === undefined;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
31
81
|
function parseMainTokens(tokens: string[]): ShellAction | null {
|
|
32
82
|
const [head, ...tail] = tokens;
|
|
33
83
|
if (!head) return null;
|
|
34
|
-
|
|
35
|
-
|
|
84
|
+
const command = joinCommandTokens(tokens);
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
head === "echo" ||
|
|
88
|
+
head === "true" ||
|
|
89
|
+
head === "printf" ||
|
|
90
|
+
head === "wc" ||
|
|
91
|
+
head === "tr" ||
|
|
92
|
+
head === "cut" ||
|
|
93
|
+
head === "sort" ||
|
|
94
|
+
head === "uniq" ||
|
|
95
|
+
head === "tee" ||
|
|
96
|
+
head === "column" ||
|
|
97
|
+
head === "yes"
|
|
98
|
+
) {
|
|
36
99
|
return null;
|
|
37
100
|
}
|
|
38
101
|
|
|
39
|
-
if (head === "
|
|
102
|
+
if (head === "xargs") {
|
|
103
|
+
return xargsIsMutatingSubcommand(tail) ? { kind: "run", command } : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (head === "ls" || head === "eza" || head === "exa") {
|
|
40
107
|
const flagsWithValues =
|
|
41
|
-
head === "
|
|
42
|
-
? ["-
|
|
43
|
-
:
|
|
44
|
-
? ["-d", "--max-depth", "-B", "--block-size", "--exclude", "--time-style"]
|
|
45
|
-
: head === "ls"
|
|
46
|
-
? ["-I", "--ignore", "-w", "--block-size", "--format", "--time-style", "--color"]
|
|
47
|
-
: ["-I", "--ignore-glob", "--color", "--sort", "--time-style", "--time"];
|
|
108
|
+
head === "ls"
|
|
109
|
+
? ["-I", "-w", "--block-size", "--format", "--time-style", "--color", "--quoting-style"]
|
|
110
|
+
: ["-I", "--ignore-glob", "--color", "--sort", "--time-style", "--time"];
|
|
48
111
|
const path = firstNonFlagOperand(tail, flagsWithValues);
|
|
49
|
-
return { kind: "list", command
|
|
112
|
+
return { kind: "list", command, path: path ? shortDisplayPath(path) : undefined };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (head === "tree") {
|
|
116
|
+
const path = firstNonFlagOperand(tail, ["-L", "-P", "-I", "--charset", "--filelimit", "--sort"]);
|
|
117
|
+
return { kind: "list", command, path: path ? shortDisplayPath(path) : undefined };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (head === "du") {
|
|
121
|
+
const path = firstNonFlagOperand(tail, ["-d", "--max-depth", "-B", "--block-size", "--exclude", "--time-style"]);
|
|
122
|
+
return { kind: "list", command, path: path ? shortDisplayPath(path) : undefined };
|
|
50
123
|
}
|
|
51
124
|
|
|
52
125
|
if (head === "rg" || head === "rga" || head === "ripgrep-all") {
|
|
53
|
-
const
|
|
54
|
-
const
|
|
126
|
+
const args = trimAtConnector(tail);
|
|
127
|
+
const hasFilesFlag = args.includes("--files");
|
|
128
|
+
const candidates = skipFlagValues(args, [
|
|
55
129
|
"-g",
|
|
56
130
|
"--glob",
|
|
57
131
|
"--iglob",
|
|
@@ -70,63 +144,63 @@ function parseMainTokens(tokens: string[]): ShellAction | null {
|
|
|
70
144
|
const nonFlags = candidates.filter((token) => !token.startsWith("-"));
|
|
71
145
|
if (hasFilesFlag) {
|
|
72
146
|
const path = nonFlags[0];
|
|
73
|
-
return { kind: "list", command
|
|
147
|
+
return { kind: "list", command, path: path ? shortDisplayPath(path) : undefined };
|
|
74
148
|
}
|
|
75
149
|
return {
|
|
76
150
|
kind: "search",
|
|
77
|
-
command
|
|
151
|
+
command,
|
|
78
152
|
query: nonFlags[0],
|
|
79
153
|
path: nonFlags[1] ? shortDisplayPath(nonFlags[1]) : undefined,
|
|
80
154
|
};
|
|
81
155
|
}
|
|
82
156
|
|
|
83
157
|
if (head === "git" && tail[0] === "grep") {
|
|
84
|
-
return parseGrepLike(
|
|
158
|
+
return parseGrepLike(command, tail.slice(1));
|
|
85
159
|
}
|
|
86
160
|
|
|
87
161
|
if (head === "git" && tail[0] === "ls-files") {
|
|
88
162
|
const path = firstNonFlagOperand(tail.slice(1), ["--exclude", "--exclude-from", "--pathspec-from-file"]);
|
|
89
|
-
return { kind: "list", command
|
|
163
|
+
return { kind: "list", command, path: path ? shortDisplayPath(path) : undefined };
|
|
90
164
|
}
|
|
91
165
|
|
|
92
166
|
if (head === "fd") {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (nonFlags.length === 0) {
|
|
97
|
-
return { kind: "list", command: tokens.join(" ") };
|
|
98
|
-
}
|
|
99
|
-
if (nonFlags.length === 1) {
|
|
100
|
-
return { kind: "list", command: tokens.join(" "), path: shortDisplayPath(nonFlags[0]) };
|
|
167
|
+
const [query, path] = parseFdQueryAndPath(tail);
|
|
168
|
+
if (query) {
|
|
169
|
+
return { kind: "search", command, query, path };
|
|
101
170
|
}
|
|
102
|
-
return {
|
|
103
|
-
kind: "search",
|
|
104
|
-
command: tokens.join(" "),
|
|
105
|
-
query: nonFlags[0],
|
|
106
|
-
path: shortDisplayPath(nonFlags[1]),
|
|
107
|
-
};
|
|
171
|
+
return { kind: "list", command, path };
|
|
108
172
|
}
|
|
109
173
|
|
|
110
174
|
if (head === "find") {
|
|
111
|
-
const path = tail
|
|
112
|
-
const nameIndex = tail.findIndex((token) => token === "-name" || token === "-iname");
|
|
113
|
-
const query = nameIndex !== -1 ? tail[nameIndex + 1] : undefined;
|
|
175
|
+
const [query, path] = parseFindQueryAndPath(tail);
|
|
114
176
|
if (query) {
|
|
115
|
-
return {
|
|
116
|
-
kind: "search",
|
|
117
|
-
command: tokens.join(" "),
|
|
118
|
-
query,
|
|
119
|
-
path: path ? shortDisplayPath(path) : undefined,
|
|
120
|
-
};
|
|
177
|
+
return { kind: "search", command, query, path };
|
|
121
178
|
}
|
|
122
|
-
return { kind: "list", command
|
|
179
|
+
return { kind: "list", command, path };
|
|
123
180
|
}
|
|
124
181
|
|
|
125
|
-
if (head === "grep" || head === "egrep" || head === "fgrep"
|
|
126
|
-
return parseGrepLike(
|
|
182
|
+
if (head === "grep" || head === "egrep" || head === "fgrep") {
|
|
183
|
+
return parseGrepLike(command, tail);
|
|
127
184
|
}
|
|
128
185
|
|
|
129
|
-
if (head === "
|
|
186
|
+
if (head === "ag" || head === "ack" || head === "pt") {
|
|
187
|
+
const args = trimAtConnector(tail);
|
|
188
|
+
const candidates = skipFlagValues(args, ["-G", "-g", "--file-search-regex", "--ignore-dir", "--ignore-file", "--path-to-ignore"]);
|
|
189
|
+
const nonFlags = candidates.filter((token) => !token.startsWith("-"));
|
|
190
|
+
return {
|
|
191
|
+
kind: "search",
|
|
192
|
+
command,
|
|
193
|
+
query: nonFlags[0],
|
|
194
|
+
path: nonFlags[1] ? shortDisplayPath(nonFlags[1]) : undefined,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (head === "cat") {
|
|
199
|
+
const path = singleNonFlagOperand(tail, []);
|
|
200
|
+
return path ? readAction(command, path) : { kind: "run", command };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (head === "bat" || head === "batcat") {
|
|
130
204
|
const path = singleNonFlagOperand(tail, [
|
|
131
205
|
"--theme",
|
|
132
206
|
"--language",
|
|
@@ -135,6 +209,12 @@ function parseMainTokens(tokens: string[]): ShellAction | null {
|
|
|
135
209
|
"--tabs",
|
|
136
210
|
"--line-range",
|
|
137
211
|
"--map-syntax",
|
|
212
|
+
]);
|
|
213
|
+
return path ? readAction(command, path) : { kind: "run", command };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (head === "less") {
|
|
217
|
+
const path = singleNonFlagOperand(tail, [
|
|
138
218
|
"-p",
|
|
139
219
|
"-P",
|
|
140
220
|
"-x",
|
|
@@ -143,56 +223,136 @@ function parseMainTokens(tokens: string[]): ShellAction | null {
|
|
|
143
223
|
"-j",
|
|
144
224
|
"--pattern",
|
|
145
225
|
"--prompt",
|
|
226
|
+
"--tabs",
|
|
146
227
|
"--shift",
|
|
147
228
|
"--jump-target",
|
|
148
229
|
]);
|
|
149
|
-
return path ? readAction(
|
|
230
|
+
return path ? readAction(command, path) : { kind: "run", command };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (head === "more") {
|
|
234
|
+
const path = singleNonFlagOperand(tail, []);
|
|
235
|
+
return path ? readAction(command, path) : { kind: "run", command };
|
|
150
236
|
}
|
|
151
237
|
|
|
152
238
|
if (head === "head") {
|
|
153
239
|
const path = readPathFromHeadTail(tail, "head");
|
|
154
|
-
return path ? readAction(
|
|
240
|
+
return path ? readAction(command, path) : null;
|
|
155
241
|
}
|
|
156
242
|
|
|
157
243
|
if (head === "tail") {
|
|
158
244
|
const path = readPathFromHeadTail(tail, "tail");
|
|
159
|
-
return path ? readAction(
|
|
245
|
+
return path ? readAction(command, path) : null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (head === "awk") {
|
|
249
|
+
const path = awkDataFileOperand(tail);
|
|
250
|
+
return path ? readAction(command, path) : { kind: "run", command };
|
|
160
251
|
}
|
|
161
252
|
|
|
162
253
|
if (head === "nl") {
|
|
163
254
|
const candidates = skipFlagValues(tail, ["-s", "-w", "-v", "-i", "-b"]);
|
|
164
255
|
const path = candidates.find((token) => !token.startsWith("-"));
|
|
165
|
-
return path ? readAction(
|
|
256
|
+
return path ? readAction(command, path) : null;
|
|
166
257
|
}
|
|
167
258
|
|
|
168
259
|
if (head === "sed") {
|
|
169
260
|
const path = sedReadPath(tail);
|
|
170
|
-
return path ? readAction(
|
|
261
|
+
return path ? readAction(command, path) : null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (isPythonCommand(head)) {
|
|
265
|
+
return pythonWalksFiles(tail) ? { kind: "list", command } : { kind: "run", command };
|
|
171
266
|
}
|
|
172
267
|
|
|
173
|
-
return { kind: "run", command
|
|
268
|
+
return { kind: "run", command };
|
|
174
269
|
}
|
|
175
270
|
|
|
176
271
|
function parseGrepLike(command: string, tail: string[]): ShellAction {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
272
|
+
const args = trimAtConnector(tail);
|
|
273
|
+
const operands: string[] = [];
|
|
274
|
+
let pattern: string | undefined;
|
|
275
|
+
let afterDoubleDash = false;
|
|
276
|
+
|
|
277
|
+
for (let index = 0; index < args.length; index++) {
|
|
278
|
+
const arg = args[index];
|
|
279
|
+
if (afterDoubleDash) {
|
|
280
|
+
operands.push(arg);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (arg === "--") {
|
|
284
|
+
afterDoubleDash = true;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (arg === "-e" || arg === "--regexp") {
|
|
288
|
+
if (!pattern) pattern = args[index + 1];
|
|
289
|
+
index += 1;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (arg === "-f" || arg === "--file") {
|
|
293
|
+
if (!pattern) pattern = args[index + 1];
|
|
294
|
+
index += 1;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (
|
|
298
|
+
arg === "-m" ||
|
|
299
|
+
arg === "--max-count" ||
|
|
300
|
+
arg === "-C" ||
|
|
301
|
+
arg === "--context" ||
|
|
302
|
+
arg === "-A" ||
|
|
303
|
+
arg === "--after-context" ||
|
|
304
|
+
arg === "-B" ||
|
|
305
|
+
arg === "--before-context"
|
|
306
|
+
) {
|
|
307
|
+
index += 1;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (arg.startsWith("-")) continue;
|
|
311
|
+
operands.push(arg);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const hasPattern = pattern !== undefined;
|
|
315
|
+
const query = pattern ?? operands[0];
|
|
316
|
+
const pathIndex = hasPattern ? 0 : 1;
|
|
317
|
+
const path = operands[pathIndex] ? shortDisplayPath(operands[pathIndex]) : undefined;
|
|
318
|
+
|
|
319
|
+
return { kind: "search", command, query, path };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function parseFdQueryAndPath(tail: string[]): [string | undefined, string | undefined] {
|
|
323
|
+
const args = trimAtConnector(tail);
|
|
324
|
+
const candidates = skipFlagValues(args, ["-t", "--type", "-e", "--extension", "-E", "--exclude", "--search-path"]);
|
|
189
325
|
const nonFlags = candidates.filter((token) => !token.startsWith("-"));
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
326
|
+
if (nonFlags.length === 1) {
|
|
327
|
+
if (isPathish(nonFlags[0])) return [undefined, shortDisplayPath(nonFlags[0])];
|
|
328
|
+
return [nonFlags[0], undefined];
|
|
329
|
+
}
|
|
330
|
+
if (nonFlags.length >= 2) {
|
|
331
|
+
return [nonFlags[0], shortDisplayPath(nonFlags[1])];
|
|
332
|
+
}
|
|
333
|
+
return [undefined, undefined];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function parseFindQueryAndPath(tail: string[]): [string | undefined, string | undefined] {
|
|
337
|
+
const args = trimAtConnector(tail);
|
|
338
|
+
let path: string | undefined;
|
|
339
|
+
for (const arg of args) {
|
|
340
|
+
if (!arg.startsWith("-") && arg !== "!" && arg !== "(" && arg !== ")") {
|
|
341
|
+
path = shortDisplayPath(arg);
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
let query: string | undefined;
|
|
347
|
+
for (let index = 0; index < args.length; index++) {
|
|
348
|
+
const arg = args[index];
|
|
349
|
+
if (arg === "-name" || arg === "-iname" || arg === "-path" || arg === "-regex") {
|
|
350
|
+
query = args[index + 1];
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return [query, path];
|
|
196
356
|
}
|
|
197
357
|
|
|
198
358
|
function readAction(command: string, path: string): ShellAction {
|
|
@@ -209,7 +369,7 @@ function readPathFromHeadTail(args: string[], tool: "head" | "tail"): string | u
|
|
|
209
369
|
return args[0];
|
|
210
370
|
}
|
|
211
371
|
|
|
212
|
-
const tokens =
|
|
372
|
+
const tokens = trimAtConnector(args);
|
|
213
373
|
let index = 0;
|
|
214
374
|
while (index < tokens.length) {
|
|
215
375
|
const token = tokens[index];
|
|
@@ -232,12 +392,13 @@ function readPathFromHeadTail(args: string[], tool: "head" | "tail"): string | u
|
|
|
232
392
|
}
|
|
233
393
|
|
|
234
394
|
function sedReadPath(args: string[]): string | undefined {
|
|
235
|
-
|
|
395
|
+
const tokens = trimAtConnector(args);
|
|
396
|
+
if (!tokens.includes("-n")) return undefined;
|
|
236
397
|
|
|
237
398
|
let hasRangeScript = false;
|
|
238
|
-
for (let index = 0; index <
|
|
239
|
-
const token =
|
|
240
|
-
if ((token === "-e" || token === "--expression") && isValidSedRange(
|
|
399
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
400
|
+
const token = tokens[index];
|
|
401
|
+
if ((token === "-e" || token === "--expression") && isValidSedRange(tokens[index + 1])) {
|
|
241
402
|
hasRangeScript = true;
|
|
242
403
|
}
|
|
243
404
|
if (!token.startsWith("-") && isValidSedRange(token)) {
|
|
@@ -246,12 +407,10 @@ function sedReadPath(args: string[]): string | undefined {
|
|
|
246
407
|
}
|
|
247
408
|
if (!hasRangeScript) return undefined;
|
|
248
409
|
|
|
249
|
-
const candidates = skipFlagValues(
|
|
410
|
+
const candidates = skipFlagValues(tokens, ["-e", "-f", "--expression", "--file"]);
|
|
250
411
|
const nonFlags = candidates.filter((token) => !token.startsWith("-"));
|
|
251
412
|
if (nonFlags.length === 0) return undefined;
|
|
252
|
-
if (isValidSedRange(nonFlags[0]))
|
|
253
|
-
return nonFlags[1];
|
|
254
|
-
}
|
|
413
|
+
if (isValidSedRange(nonFlags[0])) return nonFlags[1];
|
|
255
414
|
return nonFlags[0];
|
|
256
415
|
}
|
|
257
416
|
|
|
@@ -262,36 +421,154 @@ function isValidSedRange(value: string | undefined): boolean {
|
|
|
262
421
|
return parts.length >= 1 && parts.length <= 2 && parts.every((part) => part.length > 0 && /^\d+$/.test(part));
|
|
263
422
|
}
|
|
264
423
|
|
|
265
|
-
function
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
function singleNonFlagOperand(args: string[], flagsWithValues: string[]): string | undefined {
|
|
270
|
-
const nonFlags = skipFlagValues(args, flagsWithValues).filter((token) => !token.startsWith("-"));
|
|
271
|
-
return nonFlags.length === 1 ? nonFlags[0] : undefined;
|
|
424
|
+
function trimAtConnector(tokens: string[]): string[] {
|
|
425
|
+
const index = tokens.findIndex((token) => token === "|" || token === "&&" || token === "||" || token === ";");
|
|
426
|
+
return index === -1 ? [...tokens] : tokens.slice(0, index);
|
|
272
427
|
}
|
|
273
428
|
|
|
274
429
|
function skipFlagValues(args: string[], flagsWithValues: string[]): string[] {
|
|
275
430
|
const out: string[] = [];
|
|
431
|
+
let skipNext = false;
|
|
276
432
|
for (let index = 0; index < args.length; index++) {
|
|
277
433
|
const token = args[index];
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
434
|
+
if (skipNext) {
|
|
435
|
+
skipNext = false;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (token === "--") {
|
|
439
|
+
out.push(...args.slice(index + 1));
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
if (token.startsWith("--") && token.includes("=")) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (flagsWithValues.includes(token)) {
|
|
446
|
+
if (index + 1 < args.length) skipNext = true;
|
|
447
|
+
continue;
|
|
281
448
|
}
|
|
449
|
+
out.push(token);
|
|
282
450
|
}
|
|
283
451
|
return out;
|
|
284
452
|
}
|
|
285
453
|
|
|
286
|
-
function
|
|
454
|
+
function positionalOperands(args: string[], flagsWithValues: string[]): string[] {
|
|
455
|
+
const out: string[] = [];
|
|
456
|
+
let afterDoubleDash = false;
|
|
457
|
+
let skipNext = false;
|
|
287
458
|
for (let index = 0; index < args.length; index++) {
|
|
288
|
-
const
|
|
289
|
-
if (
|
|
459
|
+
const arg = args[index];
|
|
460
|
+
if (skipNext) {
|
|
461
|
+
skipNext = false;
|
|
290
462
|
continue;
|
|
291
463
|
}
|
|
292
|
-
if (
|
|
293
|
-
|
|
464
|
+
if (afterDoubleDash) {
|
|
465
|
+
out.push(arg);
|
|
466
|
+
continue;
|
|
294
467
|
}
|
|
468
|
+
if (arg === "--") {
|
|
469
|
+
afterDoubleDash = true;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (flagsWithValues.includes(arg)) {
|
|
476
|
+
if (index + 1 < args.length) skipNext = true;
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (arg.startsWith("-")) continue;
|
|
480
|
+
out.push(arg);
|
|
481
|
+
}
|
|
482
|
+
return out;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function firstNonFlagOperand(args: string[], flagsWithValues: string[]): string | undefined {
|
|
486
|
+
return positionalOperands(args, flagsWithValues)[0];
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function singleNonFlagOperand(args: string[], flagsWithValues: string[]): string | undefined {
|
|
490
|
+
const operands = positionalOperands(args, flagsWithValues);
|
|
491
|
+
return operands.length === 1 ? operands[0] : undefined;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function awkDataFileOperand(args: string[]): string | undefined {
|
|
495
|
+
if (args.length === 0) return undefined;
|
|
496
|
+
const tokens = trimAtConnector(args);
|
|
497
|
+
const hasScriptFile = tokens.some((arg) => arg === "-f" || arg === "--file");
|
|
498
|
+
const candidates = skipFlagValues(tokens, ["-F", "-v", "-f", "--field-separator", "--assign", "--file"]);
|
|
499
|
+
const nonFlags = candidates.filter((arg) => !arg.startsWith("-"));
|
|
500
|
+
if (hasScriptFile) return nonFlags[0];
|
|
501
|
+
return nonFlags.length >= 2 ? nonFlags[1] : undefined;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function pythonWalksFiles(args: string[]): boolean {
|
|
505
|
+
const tokens = trimAtConnector(args);
|
|
506
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
507
|
+
if (tokens[index] !== "-c") continue;
|
|
508
|
+
const script = tokens[index + 1];
|
|
509
|
+
if (!script) continue;
|
|
510
|
+
return (
|
|
511
|
+
script.includes("os.walk") ||
|
|
512
|
+
script.includes("os.listdir") ||
|
|
513
|
+
script.includes("os.scandir") ||
|
|
514
|
+
script.includes("glob.glob") ||
|
|
515
|
+
script.includes("glob.iglob") ||
|
|
516
|
+
script.includes("pathlib.Path") ||
|
|
517
|
+
script.includes(".rglob(")
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function isPythonCommand(command: string): boolean {
|
|
524
|
+
return (
|
|
525
|
+
command === "python" ||
|
|
526
|
+
command === "python2" ||
|
|
527
|
+
command === "python3" ||
|
|
528
|
+
command.startsWith("python2.") ||
|
|
529
|
+
command.startsWith("python3.")
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function isPathish(value: string): boolean {
|
|
534
|
+
return value === "." || value === ".." || value.startsWith("./") || value.startsWith("../") || value.includes("/") || value.includes("\\");
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function xargsIsMutatingSubcommand(tokens: string[]): boolean {
|
|
538
|
+
const subcommand = xargsSubcommand(tokens);
|
|
539
|
+
if (!subcommand || subcommand.length === 0) return false;
|
|
540
|
+
const [head, ...tail] = subcommand;
|
|
541
|
+
if (head === "perl" || head === "ruby") return xargsHasInPlaceFlag(tail);
|
|
542
|
+
if (head === "sed") return xargsHasInPlaceFlag(tail) || tail.includes("--in-place");
|
|
543
|
+
if (head === "rg") return tail.includes("--replace");
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function xargsSubcommand(tokens: string[]): string[] | undefined {
|
|
548
|
+
let index = 0;
|
|
549
|
+
while (index < tokens.length) {
|
|
550
|
+
const token = tokens[index];
|
|
551
|
+
if (token === "--") return tokens.slice(index + 1);
|
|
552
|
+
if (!token.startsWith("-")) return tokens.slice(index);
|
|
553
|
+
const takesValue = token === "-E" || token === "-e" || token === "-I" || token === "-L" || token === "-n" || token === "-P" || token === "-s";
|
|
554
|
+
index += takesValue && token.length === 2 ? 2 : 1;
|
|
295
555
|
}
|
|
296
556
|
return undefined;
|
|
297
557
|
}
|
|
558
|
+
|
|
559
|
+
function xargsHasInPlaceFlag(tokens: string[]): boolean {
|
|
560
|
+
return tokens.some((token) => token === "-i" || token.startsWith("-i") || token === "-pi" || token.startsWith("-pi"));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function cdTarget(args: string[]): string | undefined {
|
|
564
|
+
if (args.length === 0) return undefined;
|
|
565
|
+
let target: string | undefined;
|
|
566
|
+
for (let index = 0; index < args.length; index++) {
|
|
567
|
+
const arg = args[index];
|
|
568
|
+
if (arg === "--") return args[index + 1];
|
|
569
|
+
if (arg === "-L" || arg === "-P") continue;
|
|
570
|
+
if (arg.startsWith("-")) continue;
|
|
571
|
+
target = arg;
|
|
572
|
+
}
|
|
573
|
+
return target;
|
|
574
|
+
}
|