fuzzrunx 0.1.5 → 0.1.7
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 +1 -0
- package/package.json +3 -2
- package/scripts/postinstall.js +22 -3
- package/scripts/postuninstall.js +10 -0
- package/src/cli.js +392 -431
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ On install, FuzzRun auto-enables shell hooks and will print:
|
|
|
19
19
|
|
|
20
20
|
If you want to skip auto-enable, set `FUZZRUN_SKIP_ENABLE=1` during install.
|
|
21
21
|
If auto-enable didn't run (scripts disabled or local install), running any command with `fuzzrun` will attempt a one-time auto-enable unless `FUZZRUN_SKIP_ENABLE=1` is set.
|
|
22
|
+
On uninstall, FuzzRun automatically removes its shell hooks.
|
|
22
23
|
|
|
23
24
|
### Bash/Zsh hook (auto-run on typos)
|
|
24
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fuzzrunx",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Auto-correct mistyped commands and subcommands and re-run them safely.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fuzzrun": "bin/fuzzrun.js"
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node bin/fuzzrun.js",
|
|
12
|
-
"postinstall": "node scripts/postinstall.js"
|
|
12
|
+
"postinstall": "node scripts/postinstall.js",
|
|
13
|
+
"postuninstall": "node scripts/postuninstall.js"
|
|
13
14
|
}
|
|
14
15
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
3
6
|
const installer = require('../src/installer');
|
|
4
7
|
|
|
5
8
|
const skip = process.env.FUZZRUN_SKIP_ENABLE === '1';
|
|
6
9
|
|
|
10
|
+
function getStatePath() {
|
|
11
|
+
return path.join(os.homedir(), '.fuzzrun', 'state.json');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function writeState(next) {
|
|
15
|
+
const filePath = getStatePath();
|
|
16
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17
|
+
fs.writeFileSync(filePath, JSON.stringify(next, null, 2), 'utf8');
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
if (skip) {
|
|
8
|
-
process.
|
|
21
|
+
process.stderr.write('fuzzrun: auto-enable skipped\n');
|
|
9
22
|
process.exit(0);
|
|
10
23
|
}
|
|
11
24
|
|
|
25
|
+
const foregroundScripts =
|
|
26
|
+
process.env.npm_config_foreground_scripts === 'true' ||
|
|
27
|
+
process.env.npm_config_foreground_scripts === '1';
|
|
28
|
+
|
|
12
29
|
try {
|
|
13
30
|
installer.enable({});
|
|
14
|
-
process.
|
|
31
|
+
process.stderr.write('FuzzRun is automatically enabled. Run "fuzzrun disable" to deactivate.\n');
|
|
32
|
+
writeState({ bannerShown: foregroundScripts, enableSucceeded: true });
|
|
15
33
|
} catch (err) {
|
|
16
|
-
process.
|
|
34
|
+
process.stderr.write(`fuzzrun: auto-enable failed: ${err.message}\n`);
|
|
35
|
+
writeState({ bannerShown: false, enableSucceeded: false, lastError: err.message });
|
|
17
36
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const installer = require('../src/installer');
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
installer.disable();
|
|
7
|
+
process.stderr.write('FuzzRun hooks removed. Restart your terminal if needed.\n');
|
|
8
|
+
} catch (err) {
|
|
9
|
+
process.stderr.write(`fuzzrun: postuninstall cleanup failed: ${err.message}\n`);
|
|
10
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -5,40 +5,41 @@
|
|
|
5
5
|
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
const fs = require('fs');
|
|
8
|
+
const os = require('os');
|
|
8
9
|
const path = require('path');
|
|
9
10
|
const installer = require('./installer');
|
|
10
|
-
|
|
11
|
-
const MAX_DISTANCE = Number.isFinite(Number(process.env.FUZZRUN_MAX_DISTANCE))
|
|
12
|
-
? Math.max(1, Number(process.env.FUZZRUN_MAX_DISTANCE))
|
|
13
|
-
: 1;
|
|
14
|
-
|
|
15
|
-
const DEFAULT_PRIORITY_BASES = [
|
|
16
|
-
'git',
|
|
17
|
-
'npm',
|
|
18
|
-
'yarn',
|
|
19
|
-
'pnpm',
|
|
20
|
-
'node',
|
|
21
|
-
'python',
|
|
22
|
-
'python3',
|
|
23
|
-
'pip',
|
|
24
|
-
'pip3',
|
|
25
|
-
'docker',
|
|
26
|
-
'kubectl',
|
|
27
|
-
'gh',
|
|
28
|
-
'go',
|
|
29
|
-
'cargo',
|
|
30
|
-
'dotnet',
|
|
31
|
-
'java',
|
|
32
|
-
'mvn',
|
|
33
|
-
'gradle'
|
|
34
|
-
];
|
|
35
|
-
const ENV_PRIORITY_BASES = (process.env.FUZZRUN_PREFER_BASES || '')
|
|
36
|
-
.split(',')
|
|
37
|
-
.map((value) => normalizeToken(value).trim())
|
|
38
|
-
.filter(Boolean);
|
|
39
|
-
const PRIORITY_BASES = new Set([...DEFAULT_PRIORITY_BASES, ...ENV_PRIORITY_BASES]);
|
|
40
|
-
|
|
41
|
-
const DANGEROUS_BASE = new Set(['rm', 'mv', 'dd', 'shutdown', 'reboot', 'halt', 'poweroff']);
|
|
11
|
+
|
|
12
|
+
const MAX_DISTANCE = Number.isFinite(Number(process.env.FUZZRUN_MAX_DISTANCE))
|
|
13
|
+
? Math.max(1, Number(process.env.FUZZRUN_MAX_DISTANCE))
|
|
14
|
+
: 1;
|
|
15
|
+
|
|
16
|
+
const DEFAULT_PRIORITY_BASES = [
|
|
17
|
+
'git',
|
|
18
|
+
'npm',
|
|
19
|
+
'yarn',
|
|
20
|
+
'pnpm',
|
|
21
|
+
'node',
|
|
22
|
+
'python',
|
|
23
|
+
'python3',
|
|
24
|
+
'pip',
|
|
25
|
+
'pip3',
|
|
26
|
+
'docker',
|
|
27
|
+
'kubectl',
|
|
28
|
+
'gh',
|
|
29
|
+
'go',
|
|
30
|
+
'cargo',
|
|
31
|
+
'dotnet',
|
|
32
|
+
'java',
|
|
33
|
+
'mvn',
|
|
34
|
+
'gradle'
|
|
35
|
+
];
|
|
36
|
+
const ENV_PRIORITY_BASES = (process.env.FUZZRUN_PREFER_BASES || '')
|
|
37
|
+
.split(',')
|
|
38
|
+
.map((value) => normalizeToken(value).trim())
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
const PRIORITY_BASES = new Set([...DEFAULT_PRIORITY_BASES, ...ENV_PRIORITY_BASES]);
|
|
41
|
+
|
|
42
|
+
const DANGEROUS_BASE = new Set(['rm', 'mv', 'dd', 'shutdown', 'reboot', 'halt', 'poweroff']);
|
|
42
43
|
|
|
43
44
|
const COMMON_SUBCOMMANDS = {
|
|
44
45
|
git: [
|
|
@@ -174,31 +175,31 @@ const COMMON_SUBCOMMANDS = {
|
|
|
174
175
|
'drain',
|
|
175
176
|
'uncordon'
|
|
176
177
|
],
|
|
177
|
-
gh: ['auth', 'repo', 'issue', 'pr', 'gist', 'alias', 'api', 'search', 'run', 'workflow', 'status', 'label']
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const SAFE_SUBCOMMAND_BASES = new Set(Object.keys(COMMON_SUBCOMMANDS));
|
|
181
|
-
const ALLOW_ANY_SUBCOMMANDS = process.env.FUZZRUN_ALLOW_ANY_SUBCOMMANDS === '1';
|
|
182
|
-
const SCRIPT_BASES = new Set(['npm', 'yarn', 'pnpm']);
|
|
183
|
-
const RISKY_ARG_PATTERNS = [
|
|
184
|
-
/^-f$/,
|
|
185
|
-
/^-rf$/,
|
|
186
|
-
/^-fr$/,
|
|
187
|
-
/^--force$/i,
|
|
188
|
-
/^--hard$/i,
|
|
189
|
-
/^--delete$/i,
|
|
190
|
-
/^--purge$/i,
|
|
191
|
-
/^--no-preserve-root$/i
|
|
192
|
-
];
|
|
193
|
-
const SCRIPT_ERROR_PATTERNS = [
|
|
194
|
-
/missing script/i,
|
|
195
|
-
/unknown script/i,
|
|
196
|
-
/script.*not found/i,
|
|
197
|
-
/couldn'?t find.*script/i,
|
|
198
|
-
/command ".*" not found/i
|
|
199
|
-
];
|
|
200
|
-
const GIT_PATHSPEC_PATTERN = /pathspec .* did not match/i;
|
|
201
|
-
|
|
178
|
+
gh: ['auth', 'repo', 'issue', 'pr', 'gist', 'alias', 'api', 'search', 'run', 'workflow', 'status', 'label']
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const SAFE_SUBCOMMAND_BASES = new Set(Object.keys(COMMON_SUBCOMMANDS));
|
|
182
|
+
const ALLOW_ANY_SUBCOMMANDS = process.env.FUZZRUN_ALLOW_ANY_SUBCOMMANDS === '1';
|
|
183
|
+
const SCRIPT_BASES = new Set(['npm', 'yarn', 'pnpm']);
|
|
184
|
+
const RISKY_ARG_PATTERNS = [
|
|
185
|
+
/^-f$/,
|
|
186
|
+
/^-rf$/,
|
|
187
|
+
/^-fr$/,
|
|
188
|
+
/^--force$/i,
|
|
189
|
+
/^--hard$/i,
|
|
190
|
+
/^--delete$/i,
|
|
191
|
+
/^--purge$/i,
|
|
192
|
+
/^--no-preserve-root$/i
|
|
193
|
+
];
|
|
194
|
+
const SCRIPT_ERROR_PATTERNS = [
|
|
195
|
+
/missing script/i,
|
|
196
|
+
/unknown script/i,
|
|
197
|
+
/script.*not found/i,
|
|
198
|
+
/couldn'?t find.*script/i,
|
|
199
|
+
/command ".*" not found/i
|
|
200
|
+
];
|
|
201
|
+
const GIT_PATHSPEC_PATTERN = /pathspec .* did not match/i;
|
|
202
|
+
|
|
202
203
|
const suggestionPatterns = [
|
|
203
204
|
/The most similar command is\s+([^\s]+)/i,
|
|
204
205
|
/The most similar commands are:\s*\n\s*([^\s]+)/i,
|
|
@@ -207,58 +208,87 @@ const suggestionPatterns = [
|
|
|
207
208
|
/Perhaps you meant\s+['"]?([A-Za-z0-9:_-]+)['"]?\??/i
|
|
208
209
|
];
|
|
209
210
|
|
|
210
|
-
function
|
|
211
|
-
return
|
|
211
|
+
function getStatePath() {
|
|
212
|
+
return path.join(os.homedir(), '.fuzzrun', 'state.json');
|
|
212
213
|
}
|
|
213
214
|
|
|
214
|
-
function
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
function readState() {
|
|
216
|
+
try {
|
|
217
|
+
const raw = fs.readFileSync(getStatePath(), 'utf8');
|
|
218
|
+
return JSON.parse(raw);
|
|
219
|
+
} catch (err) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
218
222
|
}
|
|
219
223
|
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
function writeState(next) {
|
|
225
|
+
try {
|
|
226
|
+
const filePath = getStatePath();
|
|
227
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
228
|
+
fs.writeFileSync(filePath, JSON.stringify(next, null, 2), 'utf8');
|
|
229
|
+
} catch (err) {
|
|
230
|
+
// Best-effort only.
|
|
226
231
|
}
|
|
227
|
-
return false;
|
|
228
232
|
}
|
|
229
233
|
|
|
230
|
-
function
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
);
|
|
249
|
-
if (i > 1 && j > 1 && aNorm[i - 1] === bNorm[j - 2] && aNorm[i - 2] === bNorm[j - 1]) {
|
|
250
|
-
value = Math.min(value, dp[i - 2][j - 2] + 1);
|
|
251
|
-
}
|
|
252
|
-
dp[i][j] = value;
|
|
253
|
-
if (value < rowMin) rowMin = value;
|
|
254
|
-
}
|
|
255
|
-
if (rowMin > maxDistance) {
|
|
256
|
-
return maxDistance + 1;
|
|
257
|
-
}
|
|
234
|
+
function updateState(patch) {
|
|
235
|
+
const state = readState() || {};
|
|
236
|
+
const next = { ...state, ...patch };
|
|
237
|
+
writeState(next);
|
|
238
|
+
return next;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function showInstallBannerOnce() {
|
|
242
|
+
const state = readState() || {};
|
|
243
|
+
if (state.bannerShown || state.disabled) return;
|
|
244
|
+
const message =
|
|
245
|
+
state.enableSucceeded === false
|
|
246
|
+
? 'FuzzRun auto-enable failed during install. Run "fuzzrun enable" to activate.\n'
|
|
247
|
+
: 'FuzzRun is automatically enabled. Run "fuzzrun disable" to deactivate.\n';
|
|
248
|
+
process.stderr.write(message);
|
|
249
|
+
state.bannerShown = true;
|
|
250
|
+
if (typeof state.enableSucceeded === 'undefined') {
|
|
251
|
+
state.enableSucceeded = true;
|
|
258
252
|
}
|
|
259
|
-
|
|
253
|
+
writeState(state);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeToken(value) {
|
|
257
|
+
return String(value || '').toLowerCase();
|
|
260
258
|
}
|
|
261
259
|
|
|
260
|
+
function damerauLevenshtein(a, b, maxDistance = 2) {
|
|
261
|
+
const aNorm = normalizeToken(a);
|
|
262
|
+
const bNorm = normalizeToken(b);
|
|
263
|
+
if (aNorm === bNorm) return 0;
|
|
264
|
+
if (Math.abs(aNorm.length - bNorm.length) > maxDistance) {
|
|
265
|
+
return maxDistance + 1;
|
|
266
|
+
}
|
|
267
|
+
const dp = Array.from({ length: aNorm.length + 1 }, () => new Array(bNorm.length + 1).fill(0));
|
|
268
|
+
for (let i = 0; i <= aNorm.length; i += 1) dp[i][0] = i;
|
|
269
|
+
for (let j = 0; j <= bNorm.length; j += 1) dp[0][j] = j;
|
|
270
|
+
for (let i = 1; i <= aNorm.length; i += 1) {
|
|
271
|
+
let rowMin = maxDistance + 1;
|
|
272
|
+
for (let j = 1; j <= bNorm.length; j += 1) {
|
|
273
|
+
const cost = aNorm[i - 1] === bNorm[j - 1] ? 0 : 1;
|
|
274
|
+
let value = Math.min(
|
|
275
|
+
dp[i - 1][j] + 1,
|
|
276
|
+
dp[i][j - 1] + 1,
|
|
277
|
+
dp[i - 1][j - 1] + cost
|
|
278
|
+
);
|
|
279
|
+
if (i > 1 && j > 1 && aNorm[i - 1] === bNorm[j - 2] && aNorm[i - 2] === bNorm[j - 1]) {
|
|
280
|
+
value = Math.min(value, dp[i - 2][j - 2] + 1);
|
|
281
|
+
}
|
|
282
|
+
dp[i][j] = value;
|
|
283
|
+
if (value < rowMin) rowMin = value;
|
|
284
|
+
}
|
|
285
|
+
if (rowMin > maxDistance) {
|
|
286
|
+
return maxDistance + 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return dp[aNorm.length][bNorm.length];
|
|
290
|
+
}
|
|
291
|
+
|
|
262
292
|
function collectPathCommands() {
|
|
263
293
|
const names = new Set();
|
|
264
294
|
const pathEntries = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
@@ -291,103 +321,46 @@ function collectPathCommands() {
|
|
|
291
321
|
return names;
|
|
292
322
|
}
|
|
293
323
|
|
|
294
|
-
const PATH_COMMANDS = collectPathCommands();
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
if (fs.existsSync(candidate)) {
|
|
335
|
-
resolved = candidate;
|
|
336
|
-
break;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
const result = spawnSync('which', [command], { encoding: 'utf8' });
|
|
343
|
-
if (result.status === 0) {
|
|
344
|
-
const line = String(result.stdout || '').trim();
|
|
345
|
-
if (line) resolved = line;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
RESOLVE_CACHE.set(command, resolved || command);
|
|
350
|
-
return resolved || command;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function normalizePowerShellGetPrefix(command) {
|
|
354
|
-
if (!command) return command;
|
|
355
|
-
const lowered = normalizeToken(command);
|
|
356
|
-
if (!lowered.startsWith('get-')) return command;
|
|
357
|
-
if (PATH_COMMANDS.has(command) || PATH_COMMANDS.has(lowered)) return command;
|
|
358
|
-
const stripped = command.slice(4);
|
|
359
|
-
if (!stripped) return command;
|
|
360
|
-
if (PATH_COMMANDS.has(stripped)) return stripped;
|
|
361
|
-
const match = findBestMatch(PATH_COMMANDS, stripped, MAX_DISTANCE);
|
|
362
|
-
if (match) return stripped;
|
|
363
|
-
return command;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function findBestMatch(candidates, target, maxDistance = MAX_DISTANCE) {
|
|
367
|
-
if (!candidates || !target) return null;
|
|
368
|
-
let best = null;
|
|
369
|
-
let bestDistance = maxDistance + 1;
|
|
370
|
-
let ties = [];
|
|
371
|
-
for (const candidate of candidates || []) {
|
|
372
|
-
const dist = damerauLevenshtein(candidate, target, maxDistance);
|
|
373
|
-
if (dist < bestDistance) {
|
|
374
|
-
best = candidate;
|
|
375
|
-
bestDistance = dist;
|
|
376
|
-
ties = [candidate];
|
|
377
|
-
} else if (dist === bestDistance) {
|
|
378
|
-
ties.push(candidate);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
if (!best || bestDistance > maxDistance) return null;
|
|
382
|
-
if (ties.length > 1) {
|
|
383
|
-
const preferred = ties.filter((value) => PRIORITY_BASES.has(normalizeToken(value)));
|
|
384
|
-
if (preferred.length === 1) {
|
|
385
|
-
return { match: preferred[0], distance: bestDistance };
|
|
386
|
-
}
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
return { match: best, distance: bestDistance };
|
|
390
|
-
}
|
|
324
|
+
const PATH_COMMANDS = collectPathCommands();
|
|
325
|
+
|
|
326
|
+
function normalizePowerShellGetPrefix(command) {
|
|
327
|
+
if (!command) return command;
|
|
328
|
+
const lowered = normalizeToken(command);
|
|
329
|
+
if (!lowered.startsWith('get-')) return command;
|
|
330
|
+
if (PATH_COMMANDS.has(command) || PATH_COMMANDS.has(lowered)) return command;
|
|
331
|
+
const stripped = command.slice(4);
|
|
332
|
+
if (!stripped) return command;
|
|
333
|
+
if (PATH_COMMANDS.has(stripped)) return stripped;
|
|
334
|
+
const match = findBestMatch(PATH_COMMANDS, stripped, MAX_DISTANCE);
|
|
335
|
+
if (match) return stripped;
|
|
336
|
+
return command;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function findBestMatch(candidates, target, maxDistance = MAX_DISTANCE) {
|
|
340
|
+
if (!candidates || !target) return null;
|
|
341
|
+
let best = null;
|
|
342
|
+
let bestDistance = maxDistance + 1;
|
|
343
|
+
let ties = [];
|
|
344
|
+
for (const candidate of candidates || []) {
|
|
345
|
+
const dist = damerauLevenshtein(candidate, target, maxDistance);
|
|
346
|
+
if (dist < bestDistance) {
|
|
347
|
+
best = candidate;
|
|
348
|
+
bestDistance = dist;
|
|
349
|
+
ties = [candidate];
|
|
350
|
+
} else if (dist === bestDistance) {
|
|
351
|
+
ties.push(candidate);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (!best || bestDistance > maxDistance) return null;
|
|
355
|
+
if (ties.length > 1) {
|
|
356
|
+
const preferred = ties.filter((value) => PRIORITY_BASES.has(normalizeToken(value)));
|
|
357
|
+
if (preferred.length === 1) {
|
|
358
|
+
return { match: preferred[0], distance: bestDistance };
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
return { match: best, distance: bestDistance };
|
|
363
|
+
}
|
|
391
364
|
|
|
392
365
|
function parseSuggestion(text) {
|
|
393
366
|
for (const pattern of suggestionPatterns) {
|
|
@@ -399,151 +372,134 @@ function parseSuggestion(text) {
|
|
|
399
372
|
return null;
|
|
400
373
|
}
|
|
401
374
|
|
|
402
|
-
function run(cmd, args) {
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
375
|
+
function run(cmd, args) {
|
|
376
|
+
const result = spawnSync(cmd, args, {
|
|
377
|
+
encoding: 'utf8',
|
|
378
|
+
stdio: ['inherit', 'pipe', 'pipe']
|
|
379
|
+
});
|
|
380
|
+
return {
|
|
381
|
+
code: typeof result.status === 'number' ? result.status : result.error ? 1 : 0,
|
|
382
|
+
stdout: result.stdout || '',
|
|
383
|
+
stderr: result.stderr || '',
|
|
384
|
+
error: result.error
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function logFix(from, to) {
|
|
389
|
+
process.stderr.write(`fuzzrun: auto-correcting "${from}" -> "${to}"\n`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function hasRiskyArgs(args) {
|
|
393
|
+
return args.some((arg) => RISKY_ARG_PATTERNS.some((pattern) => pattern.test(arg)));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function tryBaseCorrection(command, args) {
|
|
397
|
+
if (hasRiskyArgs(args)) return null;
|
|
398
|
+
const suggestion = findBestMatch(PATH_COMMANDS, command, MAX_DISTANCE);
|
|
399
|
+
if (suggestion && !DANGEROUS_BASE.has(suggestion.match) && suggestion.match !== command) {
|
|
400
|
+
logFix(command, suggestion.match);
|
|
401
|
+
return {
|
|
402
|
+
command: suggestion.match,
|
|
403
|
+
args,
|
|
404
|
+
result: run(suggestion.match, args)
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function trySubcommandCorrection(command, args, combinedOutput) {
|
|
411
|
+
if (!SAFE_SUBCOMMAND_BASES.has(command) && !ALLOW_ANY_SUBCOMMANDS) return null;
|
|
412
|
+
if (!args.length) return null;
|
|
413
|
+
const attemptedSub = args[0];
|
|
414
|
+
if (attemptedSub.startsWith('-')) return null;
|
|
415
|
+
if (hasRiskyArgs(args)) return null;
|
|
416
|
+
const fromOutput = parseSuggestion(combinedOutput);
|
|
417
|
+
const candidates = COMMON_SUBCOMMANDS[command] || [];
|
|
418
|
+
const fromDict = findBestMatch(candidates, attemptedSub, MAX_DISTANCE);
|
|
419
|
+
const outputDistance = fromOutput
|
|
420
|
+
? damerauLevenshtein(fromOutput, attemptedSub, MAX_DISTANCE)
|
|
421
|
+
: MAX_DISTANCE + 1;
|
|
422
|
+
const choice = outputDistance <= MAX_DISTANCE ? fromOutput : fromDict ? fromDict.match : null;
|
|
423
|
+
|
|
424
|
+
if (choice && choice !== attemptedSub && damerauLevenshtein(choice, attemptedSub, MAX_DISTANCE) <= MAX_DISTANCE) {
|
|
425
|
+
logFix(`${command} ${attemptedSub}`, `${command} ${choice}`);
|
|
426
|
+
return run(command, [choice, ...args.slice(1)]);
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function findPackageJson(startDir) {
|
|
432
|
+
let current = startDir;
|
|
433
|
+
while (current && current !== path.dirname(current)) {
|
|
434
|
+
const candidate = path.join(current, 'package.json');
|
|
435
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
436
|
+
current = path.dirname(current);
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function getPackageScripts(cwd) {
|
|
442
|
+
const pkgPath = findPackageJson(cwd);
|
|
443
|
+
if (!pkgPath) return [];
|
|
444
|
+
try {
|
|
445
|
+
const raw = fs.readFileSync(pkgPath, 'utf8');
|
|
446
|
+
const parsed = JSON.parse(raw);
|
|
447
|
+
return Object.keys(parsed.scripts || {});
|
|
448
|
+
} catch (err) {
|
|
449
|
+
return [];
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function isScriptError(output) {
|
|
454
|
+
return SCRIPT_ERROR_PATTERNS.some((pattern) => pattern.test(output));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function tryScriptCorrection(command, args, combinedOutput) {
|
|
458
|
+
if (!SCRIPT_BASES.has(command)) return null;
|
|
459
|
+
if (args.length < 2) return null;
|
|
460
|
+
if (args[0] !== 'run') return null;
|
|
461
|
+
const scriptName = args[1];
|
|
462
|
+
if (!scriptName || scriptName.startsWith('-')) return null;
|
|
463
|
+
if (!isScriptError(combinedOutput)) return null;
|
|
464
|
+
if (hasRiskyArgs(args)) return null;
|
|
465
|
+
|
|
466
|
+
const scripts = getPackageScripts(process.cwd());
|
|
467
|
+
const match = findBestMatch(scripts, scriptName, MAX_DISTANCE);
|
|
468
|
+
if (match) {
|
|
469
|
+
logFix(`${command} run ${scriptName}`, `${command} run ${match.match}`);
|
|
470
|
+
return run(command, ['run', match.match, ...args.slice(2)]);
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function getGitBranches() {
|
|
476
|
+
const result = spawnSync('git', ['branch', '--format=%(refname:short)'], { encoding: 'utf8' });
|
|
477
|
+
if (result.status !== 0) return [];
|
|
478
|
+
return (result.stdout || '')
|
|
479
|
+
.split(/\r?\n/)
|
|
480
|
+
.map((line) => line.trim())
|
|
481
|
+
.filter(Boolean);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function tryGitBranchCorrection(command, args, combinedOutput) {
|
|
485
|
+
if (command !== 'git') return null;
|
|
486
|
+
if (args.length < 2) return null;
|
|
487
|
+
const subcommand = args[0];
|
|
488
|
+
if (subcommand !== 'checkout' && subcommand !== 'switch') return null;
|
|
489
|
+
const branch = args[1];
|
|
490
|
+
if (!branch || branch.startsWith('-')) return null;
|
|
491
|
+
if (!GIT_PATHSPEC_PATTERN.test(combinedOutput)) return null;
|
|
492
|
+
if (hasRiskyArgs(args)) return null;
|
|
493
|
+
|
|
494
|
+
const branches = getGitBranches();
|
|
495
|
+
const match = findBestMatch(branches, branch, MAX_DISTANCE);
|
|
496
|
+
if (match) {
|
|
497
|
+
logFix(`${command} ${subcommand} ${branch}`, `${command} ${subcommand} ${match.match}`);
|
|
498
|
+
return run(command, [subcommand, match.match, ...args.slice(2)]);
|
|
499
|
+
}
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
431
502
|
|
|
432
|
-
function logFix(from, to) {
|
|
433
|
-
process.stderr.write(`fuzzrun: auto-correcting "${from}" -> "${to}"\n`);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function hasRiskyArgs(args) {
|
|
437
|
-
return args.some((arg) => RISKY_ARG_PATTERNS.some((pattern) => pattern.test(arg)));
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function tryBaseCorrection(command, args) {
|
|
441
|
-
if (hasRiskyArgs(args)) return null;
|
|
442
|
-
const suggestion = findBestMatch(PATH_COMMANDS, command, MAX_DISTANCE);
|
|
443
|
-
if (suggestion && !DANGEROUS_BASE.has(suggestion.match) && suggestion.match !== command) {
|
|
444
|
-
logFix(command, suggestion.match);
|
|
445
|
-
return {
|
|
446
|
-
command: suggestion.match,
|
|
447
|
-
args,
|
|
448
|
-
result: run(suggestion.match, args)
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function trySubcommandCorrection(command, args, combinedOutput) {
|
|
455
|
-
if (!SAFE_SUBCOMMAND_BASES.has(command) && !ALLOW_ANY_SUBCOMMANDS) return null;
|
|
456
|
-
if (!args.length) return null;
|
|
457
|
-
const attemptedSub = args[0];
|
|
458
|
-
if (attemptedSub.startsWith('-')) return null;
|
|
459
|
-
if (hasRiskyArgs(args)) return null;
|
|
460
|
-
const fromOutput = parseSuggestion(combinedOutput);
|
|
461
|
-
const candidates = COMMON_SUBCOMMANDS[command] || [];
|
|
462
|
-
const fromDict = findBestMatch(candidates, attemptedSub, MAX_DISTANCE);
|
|
463
|
-
const outputDistance = fromOutput
|
|
464
|
-
? damerauLevenshtein(fromOutput, attemptedSub, MAX_DISTANCE)
|
|
465
|
-
: MAX_DISTANCE + 1;
|
|
466
|
-
const choice = outputDistance <= MAX_DISTANCE ? fromOutput : fromDict ? fromDict.match : null;
|
|
467
|
-
|
|
468
|
-
if (choice && choice !== attemptedSub && damerauLevenshtein(choice, attemptedSub, MAX_DISTANCE) <= MAX_DISTANCE) {
|
|
469
|
-
logFix(`${command} ${attemptedSub}`, `${command} ${choice}`);
|
|
470
|
-
return run(command, [choice, ...args.slice(1)]);
|
|
471
|
-
}
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
function findPackageJson(startDir) {
|
|
476
|
-
let current = startDir;
|
|
477
|
-
while (current && current !== path.dirname(current)) {
|
|
478
|
-
const candidate = path.join(current, 'package.json');
|
|
479
|
-
if (fs.existsSync(candidate)) return candidate;
|
|
480
|
-
current = path.dirname(current);
|
|
481
|
-
}
|
|
482
|
-
return null;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
function getPackageScripts(cwd) {
|
|
486
|
-
const pkgPath = findPackageJson(cwd);
|
|
487
|
-
if (!pkgPath) return [];
|
|
488
|
-
try {
|
|
489
|
-
const raw = fs.readFileSync(pkgPath, 'utf8');
|
|
490
|
-
const parsed = JSON.parse(raw);
|
|
491
|
-
return Object.keys(parsed.scripts || {});
|
|
492
|
-
} catch (err) {
|
|
493
|
-
return [];
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function isScriptError(output) {
|
|
498
|
-
return SCRIPT_ERROR_PATTERNS.some((pattern) => pattern.test(output));
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function tryScriptCorrection(command, args, combinedOutput) {
|
|
502
|
-
if (!SCRIPT_BASES.has(command)) return null;
|
|
503
|
-
if (args.length < 2) return null;
|
|
504
|
-
if (args[0] !== 'run') return null;
|
|
505
|
-
const scriptName = args[1];
|
|
506
|
-
if (!scriptName || scriptName.startsWith('-')) return null;
|
|
507
|
-
if (!isScriptError(combinedOutput)) return null;
|
|
508
|
-
if (hasRiskyArgs(args)) return null;
|
|
509
|
-
|
|
510
|
-
const scripts = getPackageScripts(process.cwd());
|
|
511
|
-
const match = findBestMatch(scripts, scriptName, MAX_DISTANCE);
|
|
512
|
-
if (match) {
|
|
513
|
-
logFix(`${command} run ${scriptName}`, `${command} run ${match.match}`);
|
|
514
|
-
return run(command, ['run', match.match, ...args.slice(2)]);
|
|
515
|
-
}
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function getGitBranches() {
|
|
520
|
-
const result = spawnSync('git', ['branch', '--format=%(refname:short)'], { encoding: 'utf8' });
|
|
521
|
-
if (result.status !== 0) return [];
|
|
522
|
-
return (result.stdout || '')
|
|
523
|
-
.split(/\r?\n/)
|
|
524
|
-
.map((line) => line.trim())
|
|
525
|
-
.filter(Boolean);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
function tryGitBranchCorrection(command, args, combinedOutput) {
|
|
529
|
-
if (command !== 'git') return null;
|
|
530
|
-
if (args.length < 2) return null;
|
|
531
|
-
const subcommand = args[0];
|
|
532
|
-
if (subcommand !== 'checkout' && subcommand !== 'switch') return null;
|
|
533
|
-
const branch = args[1];
|
|
534
|
-
if (!branch || branch.startsWith('-')) return null;
|
|
535
|
-
if (!GIT_PATHSPEC_PATTERN.test(combinedOutput)) return null;
|
|
536
|
-
if (hasRiskyArgs(args)) return null;
|
|
537
|
-
|
|
538
|
-
const branches = getGitBranches();
|
|
539
|
-
const match = findBestMatch(branches, branch, MAX_DISTANCE);
|
|
540
|
-
if (match) {
|
|
541
|
-
logFix(`${command} ${subcommand} ${branch}`, `${command} ${subcommand} ${match.match}`);
|
|
542
|
-
return run(command, [subcommand, match.match, ...args.slice(2)]);
|
|
543
|
-
}
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
503
|
function main() {
|
|
548
504
|
const argv = process.argv.slice(2);
|
|
549
505
|
if (!argv.length) {
|
|
@@ -551,116 +507,121 @@ function main() {
|
|
|
551
507
|
process.exit(1);
|
|
552
508
|
}
|
|
553
509
|
|
|
510
|
+
showInstallBannerOnce();
|
|
511
|
+
|
|
554
512
|
const action = argv[0];
|
|
555
513
|
if (action === 'enable') {
|
|
556
514
|
const results = installer.enable({});
|
|
557
515
|
const updated = results.some((item) => item.updated);
|
|
558
516
|
process.stdout.write(updated ? 'FuzzRun enabled. Restart your shell to apply changes.\n' : 'FuzzRun already enabled.\n');
|
|
517
|
+
updateState({ disabled: false, enableSucceeded: true });
|
|
559
518
|
process.exit(0);
|
|
560
519
|
}
|
|
561
520
|
if (action === 'disable') {
|
|
562
521
|
const results = installer.disable();
|
|
563
522
|
const updated = results.some((item) => item.updated);
|
|
564
523
|
process.stdout.write(updated ? 'FuzzRun disabled. Restart your shell to apply changes.\n' : 'FuzzRun already disabled.\n');
|
|
524
|
+
updateState({ disabled: true });
|
|
565
525
|
process.exit(0);
|
|
566
526
|
}
|
|
567
527
|
if (action === 'status') {
|
|
568
|
-
const results = installer.status();
|
|
569
|
-
for (const item of results) {
|
|
570
|
-
process.stdout.write(`${item.enabled ? 'enabled' : 'disabled'}: ${item.path}\n`);
|
|
571
|
-
}
|
|
572
|
-
process.exit(0);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
528
|
+
const results = installer.status();
|
|
529
|
+
for (const item of results) {
|
|
530
|
+
process.stdout.write(`${item.enabled ? 'enabled' : 'disabled'}: ${item.path}\n`);
|
|
531
|
+
}
|
|
532
|
+
process.exit(0);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const state = readState() || {};
|
|
536
|
+
if (!state.disabled && process.env.FUZZRUN_SKIP_ENABLE !== '1') {
|
|
576
537
|
try {
|
|
577
538
|
const status = installer.status();
|
|
578
539
|
const anyEnabled = status.some((item) => item.enabled);
|
|
579
540
|
if (!anyEnabled) {
|
|
580
541
|
const results = installer.enable({});
|
|
581
542
|
const updated = results.some((item) => item.updated);
|
|
582
|
-
if (updated) {
|
|
583
|
-
process.stdout.write('FuzzRun auto-enabled. Restart your shell to apply changes.\n');
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
} catch (err) {
|
|
587
|
-
process.stderr.write(`fuzzrun: auto-enable failed: ${err.message}\n`);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
let baseCommand = argv[0];
|
|
592
|
-
const rest = argv.slice(1);
|
|
593
|
-
baseCommand = normalizePowerShellGetPrefix(baseCommand);
|
|
594
|
-
const firstRun = run(baseCommand, rest);
|
|
595
|
-
|
|
596
|
-
if (firstRun.error && firstRun.error.code === 'ENOENT') {
|
|
597
|
-
const corrected = tryBaseCorrection(baseCommand, rest);
|
|
598
|
-
if (corrected) {
|
|
599
|
-
const { result } = corrected;
|
|
600
|
-
if (result.code !== 0) {
|
|
601
|
-
const combinedOutput = `${result.stderr}\n${result.stdout}`;
|
|
602
|
-
const correctedSub = trySubcommandCorrection(corrected.command, corrected.args, combinedOutput);
|
|
603
|
-
if (correctedSub) {
|
|
604
|
-
process.stdout.write(correctedSub.stdout);
|
|
605
|
-
process.stderr.write(correctedSub.stderr);
|
|
606
|
-
process.exit(correctedSub.code);
|
|
607
|
-
}
|
|
608
|
-
const correctedScript = tryScriptCorrection(corrected.command, corrected.args, combinedOutput);
|
|
609
|
-
if (correctedScript) {
|
|
610
|
-
process.stdout.write(correctedScript.stdout);
|
|
611
|
-
process.stderr.write(correctedScript.stderr);
|
|
612
|
-
process.exit(correctedScript.code);
|
|
613
|
-
}
|
|
614
|
-
const correctedBranch = tryGitBranchCorrection(corrected.command, corrected.args, combinedOutput);
|
|
615
|
-
if (correctedBranch) {
|
|
616
|
-
process.stdout.write(correctedBranch.stdout);
|
|
617
|
-
process.stderr.write(correctedBranch.stderr);
|
|
618
|
-
process.exit(correctedBranch.code);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
process.stdout.write(result.stdout);
|
|
622
|
-
process.stderr.write(result.stderr);
|
|
623
|
-
process.exit(result.code);
|
|
624
|
-
}
|
|
625
|
-
process.stderr.write(firstRun.error.message ? `${firstRun.error.message}\n` : `fuzzrun: command not found: ${baseCommand}\n`);
|
|
626
|
-
process.exit(firstRun.code);
|
|
627
|
-
}
|
|
543
|
+
if (updated) {
|
|
544
|
+
process.stdout.write('FuzzRun auto-enabled. Restart your shell to apply changes.\n');
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} catch (err) {
|
|
548
|
+
process.stderr.write(`fuzzrun: auto-enable failed: ${err.message}\n`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
628
551
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const combinedOutput = `${firstRun.stderr}\n${firstRun.stdout}`;
|
|
636
|
-
const correctedSub = trySubcommandCorrection(baseCommand, rest, combinedOutput);
|
|
637
|
-
if (correctedSub) {
|
|
638
|
-
process.stdout.write(correctedSub.stdout);
|
|
639
|
-
process.stderr.write(correctedSub.stderr);
|
|
640
|
-
process.exit(correctedSub.code);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
const correctedScript = tryScriptCorrection(baseCommand, rest, combinedOutput);
|
|
644
|
-
if (correctedScript) {
|
|
645
|
-
process.stdout.write(correctedScript.stdout);
|
|
646
|
-
process.stderr.write(correctedScript.stderr);
|
|
647
|
-
process.exit(correctedScript.code);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
const correctedBranch = tryGitBranchCorrection(baseCommand, rest, combinedOutput);
|
|
651
|
-
if (correctedBranch) {
|
|
652
|
-
process.stdout.write(correctedBranch.stdout);
|
|
653
|
-
process.stderr.write(correctedBranch.stderr);
|
|
654
|
-
process.exit(correctedBranch.code);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
process.stdout.write(firstRun.stdout);
|
|
658
|
-
process.stderr.write(firstRun.stderr);
|
|
659
|
-
process.exit(firstRun.code);
|
|
660
|
-
}
|
|
552
|
+
let baseCommand = argv[0];
|
|
553
|
+
const rest = argv.slice(1);
|
|
554
|
+
baseCommand = normalizePowerShellGetPrefix(baseCommand);
|
|
555
|
+
const firstRun = run(baseCommand, rest);
|
|
661
556
|
|
|
662
|
-
if (
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
557
|
+
if (firstRun.error && firstRun.error.code === 'ENOENT') {
|
|
558
|
+
const corrected = tryBaseCorrection(baseCommand, rest);
|
|
559
|
+
if (corrected) {
|
|
560
|
+
const { result } = corrected;
|
|
561
|
+
if (result.code !== 0) {
|
|
562
|
+
const combinedOutput = `${result.stderr}\n${result.stdout}`;
|
|
563
|
+
const correctedSub = trySubcommandCorrection(corrected.command, corrected.args, combinedOutput);
|
|
564
|
+
if (correctedSub) {
|
|
565
|
+
process.stdout.write(correctedSub.stdout);
|
|
566
|
+
process.stderr.write(correctedSub.stderr);
|
|
567
|
+
process.exit(correctedSub.code);
|
|
568
|
+
}
|
|
569
|
+
const correctedScript = tryScriptCorrection(corrected.command, corrected.args, combinedOutput);
|
|
570
|
+
if (correctedScript) {
|
|
571
|
+
process.stdout.write(correctedScript.stdout);
|
|
572
|
+
process.stderr.write(correctedScript.stderr);
|
|
573
|
+
process.exit(correctedScript.code);
|
|
574
|
+
}
|
|
575
|
+
const correctedBranch = tryGitBranchCorrection(corrected.command, corrected.args, combinedOutput);
|
|
576
|
+
if (correctedBranch) {
|
|
577
|
+
process.stdout.write(correctedBranch.stdout);
|
|
578
|
+
process.stderr.write(correctedBranch.stderr);
|
|
579
|
+
process.exit(correctedBranch.code);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
process.stdout.write(result.stdout);
|
|
583
|
+
process.stderr.write(result.stderr);
|
|
584
|
+
process.exit(result.code);
|
|
585
|
+
}
|
|
586
|
+
process.stderr.write(firstRun.error.message ? `${firstRun.error.message}\n` : `fuzzrun: command not found: ${baseCommand}\n`);
|
|
587
|
+
process.exit(firstRun.code);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (firstRun.code === 0) {
|
|
591
|
+
process.stdout.write(firstRun.stdout);
|
|
592
|
+
process.stderr.write(firstRun.stderr);
|
|
593
|
+
process.exit(0);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const combinedOutput = `${firstRun.stderr}\n${firstRun.stdout}`;
|
|
597
|
+
const correctedSub = trySubcommandCorrection(baseCommand, rest, combinedOutput);
|
|
598
|
+
if (correctedSub) {
|
|
599
|
+
process.stdout.write(correctedSub.stdout);
|
|
600
|
+
process.stderr.write(correctedSub.stderr);
|
|
601
|
+
process.exit(correctedSub.code);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const correctedScript = tryScriptCorrection(baseCommand, rest, combinedOutput);
|
|
605
|
+
if (correctedScript) {
|
|
606
|
+
process.stdout.write(correctedScript.stdout);
|
|
607
|
+
process.stderr.write(correctedScript.stderr);
|
|
608
|
+
process.exit(correctedScript.code);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const correctedBranch = tryGitBranchCorrection(baseCommand, rest, combinedOutput);
|
|
612
|
+
if (correctedBranch) {
|
|
613
|
+
process.stdout.write(correctedBranch.stdout);
|
|
614
|
+
process.stderr.write(correctedBranch.stderr);
|
|
615
|
+
process.exit(correctedBranch.code);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
process.stdout.write(firstRun.stdout);
|
|
619
|
+
process.stderr.write(firstRun.stderr);
|
|
620
|
+
process.exit(firstRun.code);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (require.main === module) {
|
|
624
|
+
main();
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
module.exports = { main };
|