bunosh 0.2.3 â 0.3.1
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 +570 -13
- package/bunosh.js +90 -4
- package/index.js +12 -7
- package/package.json +7 -3
- package/src/init.js +12 -4
- package/src/io.js +55 -1
- package/src/open-editor.js +95 -0
- package/src/program.js +58 -29
- package/src/task.js +8 -0
- package/src/tasks/ai.js +143 -0
- package/src/tasks/shell.js +119 -0
package/bunosh.js
CHANGED
|
@@ -3,6 +3,7 @@ import program, { BUNOSHFILE, banner } from "./src/program.js";
|
|
|
3
3
|
import { existsSync, readFileSync, statSync } from "fs";
|
|
4
4
|
import init from "./src/init.js";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import color from "chalk";
|
|
6
7
|
import './index.js';
|
|
7
8
|
|
|
8
9
|
// Parse --bunoshfile flag before importing tasks
|
|
@@ -49,8 +50,93 @@ if (!existsSync(tasksFile)) {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
import(tasksFile).then((tasks) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
try {
|
|
54
|
+
const source = readFileSync(tasksFile, "utf-8");
|
|
55
|
+
program(tasks, source);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
handleBunoshfileError(error, tasksFile);
|
|
58
|
+
}
|
|
59
|
+
}).catch((error) => {
|
|
60
|
+
handleBunoshfileError(error, tasksFile);
|
|
56
61
|
});
|
|
62
|
+
|
|
63
|
+
function handleBunoshfileError(error, filePath) {
|
|
64
|
+
banner();
|
|
65
|
+
console.log();
|
|
66
|
+
|
|
67
|
+
// Check for Babel parser syntax errors
|
|
68
|
+
if (error.code === 'BABEL_PARSER_SYNTAX_ERROR' ||
|
|
69
|
+
(error.reasonCode && error.loc) ||
|
|
70
|
+
error.constructor.name === 'SyntaxError') {
|
|
71
|
+
|
|
72
|
+
console.error(`â Syntax Error in ${path.basename(filePath)}:`);
|
|
73
|
+
console.log();
|
|
74
|
+
|
|
75
|
+
if (error.loc) {
|
|
76
|
+
console.error(` Line ${error.loc.line}, Column ${error.loc.column}:`);
|
|
77
|
+
|
|
78
|
+
// Provide specific error messages based on reasonCode
|
|
79
|
+
if (error.reasonCode === 'VarRedeclaration') {
|
|
80
|
+
console.error(` Variable redeclaration - '${error.message}' is already declared`);
|
|
81
|
+
} else if (error.reasonCode && error.reasonCode.includes('Unexpected')) {
|
|
82
|
+
console.error(` ${error.reasonCode}: ${error.message || 'Unexpected token'}`);
|
|
83
|
+
} else {
|
|
84
|
+
console.error(` ${error.message || error.reasonCode || 'Invalid syntax'}`);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
console.error(` ${error.message || 'Invalid JavaScript syntax'}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log();
|
|
91
|
+
console.log('đĄ Common issues:');
|
|
92
|
+
console.log(' âĸ Missing semicolons or commas');
|
|
93
|
+
console.log(' âĸ Unclosed brackets, parentheses, or quotes');
|
|
94
|
+
console.log(' âĸ Invalid variable declarations');
|
|
95
|
+
console.log(' âĸ Mixing import/export with require/module.exports');
|
|
96
|
+
console.log();
|
|
97
|
+
console.log(`đ Edit your Bunoshfile: ${color.blue('bunosh edit')}`);
|
|
98
|
+
console.log(`đ§ Validate syntax: ${color.blue(`bun --check ${path.basename(filePath)}`)}`);
|
|
99
|
+
|
|
100
|
+
} else if (error.message && error.message.includes('SyntaxError')) {
|
|
101
|
+
console.error(`â JavaScript Syntax Error in ${path.basename(filePath)}:`);
|
|
102
|
+
console.log();
|
|
103
|
+
console.error(` ${error.message}`);
|
|
104
|
+
console.log();
|
|
105
|
+
console.log(`đĄ Try running: ${color.blue('bun --check Bunoshfile.js')}`);
|
|
106
|
+
console.log(`đ Edit your Bunoshfile: ${color.blue('bunosh edit')}`);
|
|
107
|
+
|
|
108
|
+
} else if (error.code === 'MODULE_NOT_FOUND' ||
|
|
109
|
+
error.message?.includes('Cannot resolve') ||
|
|
110
|
+
error.message?.includes('Could not resolve')) {
|
|
111
|
+
console.error(`â Module Import Error in ${path.basename(filePath)}:`);
|
|
112
|
+
console.log();
|
|
113
|
+
console.error(` ${error.message}`);
|
|
114
|
+
console.log();
|
|
115
|
+
console.log('đĄ Common solutions:');
|
|
116
|
+
console.log(` âĸ Run: ${color.blue('bun install')}`);
|
|
117
|
+
console.log(' âĸ Check import paths are correct');
|
|
118
|
+
console.log(' âĸ Ensure dependencies are listed in package.json');
|
|
119
|
+
|
|
120
|
+
} else {
|
|
121
|
+
console.error(`â Error loading ${path.basename(filePath)}:`);
|
|
122
|
+
console.log();
|
|
123
|
+
console.error(` ${error.message || error.toString()}`);
|
|
124
|
+
|
|
125
|
+
// Add stack trace for debugging if available
|
|
126
|
+
if (process.env.BUNOSH_DEBUG) {
|
|
127
|
+
console.log();
|
|
128
|
+
console.log('đ Debug stack trace:');
|
|
129
|
+
console.log(error.stack || 'No stack trace available');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log();
|
|
133
|
+
console.log('đĄ Try:');
|
|
134
|
+
console.log(` âĸ Check the file exists: ${color.blue(`ls -la ${path.basename(filePath)}`)}`);
|
|
135
|
+
console.log(` âĸ Validate syntax: ${color.blue(`bun --check ${path.basename(filePath)}`)}`);
|
|
136
|
+
console.log(` âĸ Edit the file: ${color.blue('bunosh edit')}`);
|
|
137
|
+
console.log(` âĸ Run with debug: ${color.blue('BUNOSH_DEBUG=1 bunosh')}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
console.log();
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
package/index.js
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
import exec from "./src/tasks/exec.js";
|
|
2
|
+
import shell from "./src/tasks/shell.js";
|
|
2
3
|
import fetch from "./src/tasks/fetch.js";
|
|
3
4
|
import writeToFile from "./src/tasks/writeToFile.js";
|
|
4
5
|
import copyFile from "./src/tasks/copyFile.js";
|
|
6
|
+
import ai from "./src/tasks/ai.js";
|
|
5
7
|
import { ask, yell, say } from "./src/io.js";
|
|
6
8
|
import { task, stopOnFail, ignoreFail } from "./src/task.js";
|
|
7
9
|
|
|
8
|
-
export { exec, fetch, writeToFile, copyFile, ask, yell, say, task, stopOnFail, ignoreFail };
|
|
9
|
-
|
|
10
|
+
export { exec, shell, fetch, writeToFile, copyFile, ai, ask, yell, say, task, stopOnFail, ignoreFail };
|
|
10
11
|
|
|
11
12
|
export function buildCmd(cmd) {
|
|
12
|
-
return function(args) {
|
|
13
|
-
return exec`${cmd} ${args}
|
|
14
|
-
}
|
|
13
|
+
return function (args) {
|
|
14
|
+
return exec`${cmd} ${args}`;
|
|
15
|
+
};
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
global.bunosh = {
|
|
18
|
-
ask,
|
|
19
|
+
ask,
|
|
20
|
+
yell,
|
|
21
|
+
say,
|
|
19
22
|
fetch,
|
|
20
23
|
exec,
|
|
24
|
+
shell,
|
|
21
25
|
writeToFile,
|
|
22
26
|
copyFile,
|
|
27
|
+
ai,
|
|
23
28
|
stopOnFail,
|
|
24
29
|
ignoreFail,
|
|
25
30
|
task,
|
|
26
31
|
buildCmd,
|
|
27
32
|
$: exec,
|
|
28
|
-
}
|
|
33
|
+
};
|
|
29
34
|
|
|
30
35
|
export default global.bunosh;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunosh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -14,15 +14,19 @@
|
|
|
14
14
|
},
|
|
15
15
|
"types": "types.d.ts",
|
|
16
16
|
"dependencies": {
|
|
17
|
+
"@ai-sdk/anthropic": "^2.0.9",
|
|
18
|
+
"@ai-sdk/groq": "^2.0.16",
|
|
19
|
+
"@ai-sdk/openai": "^2.0.23",
|
|
17
20
|
"@babel/parser": "^7.27.5",
|
|
18
21
|
"@babel/traverse": "^7.27.4",
|
|
22
|
+
"ai": "^5.0.29",
|
|
19
23
|
"chalk": "^5.4.1",
|
|
20
24
|
"commander": "^14.0.0",
|
|
21
25
|
"debug": "^4.4.1",
|
|
22
26
|
"fs-extra": "^11.3.0",
|
|
23
27
|
"inquirer": "^12.6.3",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
28
|
+
"timer-node": "^5.0.9",
|
|
29
|
+
"zod": "^4.1.5"
|
|
26
30
|
},
|
|
27
31
|
"license": "ISC",
|
|
28
32
|
"files": [
|
package/src/init.js
CHANGED
|
@@ -3,8 +3,10 @@ import color from "chalk";
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
|
|
5
5
|
const template = `
|
|
6
|
-
// tasks
|
|
7
|
-
|
|
6
|
+
// Bunosh CLI required to execute tasks from this file
|
|
7
|
+
// Get it here -> https://github.com/DavertMik/bunosh/releases
|
|
8
|
+
|
|
9
|
+
const { exec, fetch, writeToFile, task, ai } = global.bunosh;
|
|
8
10
|
|
|
9
11
|
// input/output
|
|
10
12
|
const { say, ask, yell } = global.bunosh;
|
|
@@ -22,13 +24,19 @@ export async function helloWorld() {
|
|
|
22
24
|
// use fetch() to make HTTP requests
|
|
23
25
|
// await fetch('https://reqres.in/api/users')
|
|
24
26
|
|
|
27
|
+
// use ai() to make AI requests with structured output:
|
|
28
|
+
// REQUIRED env vars: AI_MODEL and any of: OPENAI_API_KEY, ANTHROPIC_API_KEY, or GROQ_API_KEY
|
|
29
|
+
// await ai('Summarize this text: JavaScript is awesome', {
|
|
30
|
+
// summary: 'Brief summary of the text',
|
|
31
|
+
// sentiment: 'Sentiment of the text (positive/negative/neutral)',
|
|
32
|
+
// keyWords: 'Main keywords from the text'
|
|
33
|
+
// });
|
|
34
|
+
|
|
25
35
|
// add arguments and options to this function if needed
|
|
26
36
|
// export async function helloWorld(userName, opts = { force: false })
|
|
27
37
|
//
|
|
28
38
|
// bunosh hello:world 'bob' --force
|
|
29
39
|
|
|
30
|
-
// use ignoreFail(true) to prevent the command from stopping on error
|
|
31
|
-
|
|
32
40
|
yell('Heloo Bunosh!');
|
|
33
41
|
say('Edit me with bunosh edit');
|
|
34
42
|
}
|
package/src/io.js
CHANGED
|
@@ -6,11 +6,65 @@ export function say(...args) {
|
|
|
6
6
|
console.log('!', ...args);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export async function ask(question,
|
|
9
|
+
export async function ask(question, defaultValueOrOptions = {}, options = {}) {
|
|
10
|
+
// Smart parameter detection
|
|
11
|
+
let opts = {};
|
|
12
|
+
|
|
13
|
+
// If second parameter is not an object, it's a default value
|
|
14
|
+
if (defaultValueOrOptions !== null && typeof defaultValueOrOptions !== 'object') {
|
|
15
|
+
opts.default = defaultValueOrOptions;
|
|
16
|
+
opts = { ...opts, ...options }; // Merge with third parameter options
|
|
17
|
+
|
|
18
|
+
// Auto-detect type based on default value
|
|
19
|
+
if (typeof defaultValueOrOptions === 'boolean') {
|
|
20
|
+
opts.type = 'confirm';
|
|
21
|
+
}
|
|
22
|
+
} else if (Array.isArray(defaultValueOrOptions)) {
|
|
23
|
+
// If it's an array, treat as choices
|
|
24
|
+
opts.choices = defaultValueOrOptions;
|
|
25
|
+
opts = { ...opts, ...options }; // Merge with third parameter options
|
|
26
|
+
} else {
|
|
27
|
+
// Traditional object parameter
|
|
28
|
+
opts = { ...defaultValueOrOptions, ...options };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Route to appropriate handler based on options
|
|
32
|
+
if (opts.editor || opts.multiline) {
|
|
33
|
+
return await askWithEditor(question, opts);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (opts.choices) {
|
|
37
|
+
return await askWithChoices(question, opts);
|
|
38
|
+
}
|
|
39
|
+
|
|
10
40
|
const answers = await inquirer.prompt({ name: question, message: question, ...opts })
|
|
11
41
|
return Object.values(answers)[0];
|
|
12
42
|
}
|
|
13
43
|
|
|
44
|
+
async function askWithEditor(question, opts = {}) {
|
|
45
|
+
const answers = await inquirer.prompt({
|
|
46
|
+
name: question,
|
|
47
|
+
message: question,
|
|
48
|
+
type: 'editor',
|
|
49
|
+
...opts
|
|
50
|
+
});
|
|
51
|
+
return Object.values(answers)[0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function askWithChoices(question, opts = {}) {
|
|
55
|
+
const promptType = opts.multiple ? 'checkbox' : 'list';
|
|
56
|
+
|
|
57
|
+
const answers = await inquirer.prompt({
|
|
58
|
+
name: question,
|
|
59
|
+
message: question,
|
|
60
|
+
type: promptType,
|
|
61
|
+
choices: opts.choices,
|
|
62
|
+
...opts
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return Object.values(answers)[0];
|
|
66
|
+
}
|
|
67
|
+
|
|
14
68
|
export function yell(text) {
|
|
15
69
|
console.log();
|
|
16
70
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export default function openEditor(files, options = {}) {
|
|
5
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
6
|
+
throw new Error('Files array is required and cannot be empty');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const editor = options.editor || getDefaultEditor();
|
|
10
|
+
const fileArgs = buildEditorArgs(editor, files);
|
|
11
|
+
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const child = spawn(editor, fileArgs, {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
detached: true
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
child.on('error', (err) => {
|
|
19
|
+
reject(new Error(`Failed to open editor '${editor}': ${err.message}`));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
child.on('spawn', () => {
|
|
23
|
+
resolve();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (child.pid) {
|
|
27
|
+
child.unref();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getDefaultEditor() {
|
|
33
|
+
if (process.env.EDITOR) {
|
|
34
|
+
return process.env.EDITOR;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const editors = ['code', 'subl', 'atom', 'vim', 'nvim', 'nano', 'gedit'];
|
|
38
|
+
|
|
39
|
+
for (const editor of editors) {
|
|
40
|
+
if (isCommandAvailable(editor)) {
|
|
41
|
+
return editor;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return process.platform === 'win32' ? 'notepad' : 'vi';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isCommandAvailable(command) {
|
|
49
|
+
try {
|
|
50
|
+
const { execSync } = require('child_process');
|
|
51
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildEditorArgs(editor, files) {
|
|
59
|
+
const editorName = path.basename(editor);
|
|
60
|
+
const args = [];
|
|
61
|
+
|
|
62
|
+
for (const fileInfo of files) {
|
|
63
|
+
const filePath = typeof fileInfo === 'string' ? fileInfo : fileInfo.file;
|
|
64
|
+
|
|
65
|
+
if (!filePath) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (fileInfo.line && typeof fileInfo === 'object') {
|
|
70
|
+
switch (editorName) {
|
|
71
|
+
case 'code':
|
|
72
|
+
case 'code-insiders':
|
|
73
|
+
args.push('--goto', `${filePath}:${fileInfo.line}:${fileInfo.column || 1}`);
|
|
74
|
+
break;
|
|
75
|
+
case 'subl':
|
|
76
|
+
case 'sublime_text':
|
|
77
|
+
args.push(`${filePath}:${fileInfo.line}:${fileInfo.column || 1}`);
|
|
78
|
+
break;
|
|
79
|
+
case 'vim':
|
|
80
|
+
case 'nvim':
|
|
81
|
+
args.push(`+${fileInfo.line}`, filePath);
|
|
82
|
+
break;
|
|
83
|
+
case 'nano':
|
|
84
|
+
args.push(`+${fileInfo.line}`, filePath);
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
args.push(filePath);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
args.push(filePath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return args;
|
|
95
|
+
}
|
package/src/program.js
CHANGED
|
@@ -4,7 +4,7 @@ import traverseDefault from "@babel/traverse";
|
|
|
4
4
|
const traverse = traverseDefault.default || traverseDefault;
|
|
5
5
|
import color from "chalk";
|
|
6
6
|
import fs from 'fs';
|
|
7
|
-
import openEditor from 'open-editor';
|
|
7
|
+
import openEditor from './open-editor.js';
|
|
8
8
|
import { yell } from './io.js';
|
|
9
9
|
import cprint from "./font.js";
|
|
10
10
|
import { handleCompletion, detectCurrentShell, installCompletion, getCompletionPaths } from './completion.js';
|
|
@@ -37,7 +37,10 @@ export default function bunosh(commands, source) {
|
|
|
37
37
|
showGlobalOptions: false,
|
|
38
38
|
visibleGlobalOptions: _opt => [],
|
|
39
39
|
visibleOptions: _opt => [],
|
|
40
|
-
visibleCommands: cmd =>
|
|
40
|
+
visibleCommands: cmd => {
|
|
41
|
+
const commands = cmd.commands.filter(c => !internalCommands.includes(c));
|
|
42
|
+
return commands.filter(c => !c.name().startsWith('npm:'));
|
|
43
|
+
},
|
|
41
44
|
subcommandTerm: (cmd) => color.white.bold(cmd.name()),
|
|
42
45
|
subcommandDescription: (cmd) => color.gray(cmd.description()),
|
|
43
46
|
});
|
|
@@ -46,13 +49,20 @@ export default function bunosh(commands, source) {
|
|
|
46
49
|
program.showSuggestionAfterError(true);
|
|
47
50
|
program.addHelpCommand(false);
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
let completeAst;
|
|
53
|
+
try {
|
|
54
|
+
completeAst = babelParser.parse(source, {
|
|
55
|
+
sourceType: "module",
|
|
56
|
+
ranges: true,
|
|
57
|
+
tokens: true,
|
|
58
|
+
comments: true,
|
|
59
|
+
attachComment: true,
|
|
60
|
+
});
|
|
61
|
+
} catch (parseError) {
|
|
62
|
+
// Re-throw with more specific error information
|
|
63
|
+
parseError.code = 'BABEL_PARSER_SYNTAX_ERROR';
|
|
64
|
+
throw parseError;
|
|
65
|
+
}
|
|
56
66
|
|
|
57
67
|
const comments = fetchComments();
|
|
58
68
|
|
|
@@ -241,12 +251,16 @@ export default function bunosh(commands, source) {
|
|
|
241
251
|
|
|
242
252
|
const editCmd = program.command('edit')
|
|
243
253
|
.description('Open the bunosh file in your editor. $EDITOR or \'code\' is used.')
|
|
244
|
-
.action(() => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
})
|
|
254
|
+
.action(async () => {
|
|
255
|
+
try {
|
|
256
|
+
await openEditor([{
|
|
257
|
+
file: BUNOSHFILE,
|
|
258
|
+
}]);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error(error.message);
|
|
261
|
+
console.error('Set $EDITOR environment variable to use a different editor');
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
250
264
|
});
|
|
251
265
|
|
|
252
266
|
internalCommands.push(editCmd);
|
|
@@ -282,11 +296,11 @@ export default function bunosh(commands, source) {
|
|
|
282
296
|
try {
|
|
283
297
|
// Detect current shell or use specified shell
|
|
284
298
|
const shell = options.shell || detectCurrentShell();
|
|
285
|
-
|
|
299
|
+
|
|
286
300
|
if (!shell) {
|
|
287
301
|
console.error('â Could not detect your shell. Please specify one:');
|
|
288
302
|
console.log(' bunosh setup-completion --shell bash');
|
|
289
|
-
console.log(' bunosh setup-completion --shell zsh');
|
|
303
|
+
console.log(' bunosh setup-completion --shell zsh');
|
|
290
304
|
console.log(' bunosh setup-completion --shell fish');
|
|
291
305
|
process.exit(1);
|
|
292
306
|
}
|
|
@@ -296,7 +310,7 @@ export default function bunosh(commands, source) {
|
|
|
296
310
|
|
|
297
311
|
// Get paths for this shell
|
|
298
312
|
const paths = getCompletionPaths(shell);
|
|
299
|
-
|
|
313
|
+
|
|
300
314
|
// Check if already installed
|
|
301
315
|
if (!options.force && fs.existsSync(paths.completionFile)) {
|
|
302
316
|
console.log(`â ī¸ Completion already installed at: ${paths.completionFile}`);
|
|
@@ -311,7 +325,7 @@ export default function bunosh(commands, source) {
|
|
|
311
325
|
|
|
312
326
|
// Report success
|
|
313
327
|
console.log(`â
Completion installed: ${color.green(paths.completionFile)}`);
|
|
314
|
-
|
|
328
|
+
|
|
315
329
|
if (result.configFile && result.added) {
|
|
316
330
|
console.log(`đ Updated shell config: ${color.green(result.configFile)}`);
|
|
317
331
|
console.log();
|
|
@@ -327,10 +341,10 @@ export default function bunosh(commands, source) {
|
|
|
327
341
|
console.log(`âšī¸ Shell config already has completion setup: ${result.configFile}`);
|
|
328
342
|
console.log(' Restart your terminal if completion isn\'t working.');
|
|
329
343
|
}
|
|
330
|
-
|
|
344
|
+
|
|
331
345
|
console.log();
|
|
332
346
|
console.log('đ¯ Test completion by typing: ' + color.bold('bunosh <TAB>'));
|
|
333
|
-
|
|
347
|
+
|
|
334
348
|
} catch (error) {
|
|
335
349
|
console.error(`â Setup failed: ${error.message}`);
|
|
336
350
|
process.exit(1);
|
|
@@ -360,9 +374,9 @@ export default function bunosh(commands, source) {
|
|
|
360
374
|
const { getLatestRelease, isNewerVersion } = await import('./upgrade.js');
|
|
361
375
|
const release = await getLatestRelease();
|
|
362
376
|
const latestVersion = release.tag_name;
|
|
363
|
-
|
|
377
|
+
|
|
364
378
|
console.log(`đĻ Latest version: ${color.bold(latestVersion)}`);
|
|
365
|
-
|
|
379
|
+
|
|
366
380
|
if (isNewerVersion(latestVersion, currentVersion)) {
|
|
367
381
|
console.log(`⨠${color.green('Update available!')} ${currentVersion} â ${latestVersion}`);
|
|
368
382
|
console.log('Run ' + color.bold('bunosh upgrade') + ' to update.');
|
|
@@ -405,7 +419,7 @@ export default function bunosh(commands, source) {
|
|
|
405
419
|
|
|
406
420
|
} catch (error) {
|
|
407
421
|
console.error(`â Upgrade failed: ${error.message}`);
|
|
408
|
-
|
|
422
|
+
|
|
409
423
|
if (error.message.includes('Unsupported platform')) {
|
|
410
424
|
console.log();
|
|
411
425
|
console.log('đĄ Supported platforms:');
|
|
@@ -416,21 +430,36 @@ export default function bunosh(commands, source) {
|
|
|
416
430
|
console.log();
|
|
417
431
|
console.log('đĄ Try again later or check your internet connection.');
|
|
418
432
|
}
|
|
419
|
-
|
|
433
|
+
|
|
420
434
|
process.exit(1);
|
|
421
435
|
}
|
|
422
436
|
});
|
|
423
437
|
|
|
424
438
|
internalCommands.push(upgradeCmd);
|
|
425
439
|
|
|
440
|
+
// Add npm scripts help section if npm scripts exist
|
|
441
|
+
const npmScriptNamesForHelp = Object.keys(npmScripts);
|
|
442
|
+
if (npmScriptNamesForHelp.length > 0) {
|
|
443
|
+
const npmCommandsList = npmScriptNamesForHelp.sort().map(scriptName => {
|
|
444
|
+
const commandName = `npm:${scriptName}`;
|
|
445
|
+
const scriptCommand = npmScripts[scriptName];
|
|
446
|
+
return ` ${color.white.bold(commandName.padEnd(18))} ${color.gray(scriptCommand)}`;
|
|
447
|
+
}).join('\n');
|
|
448
|
+
|
|
449
|
+
program.addHelpText('after', `
|
|
450
|
+
|
|
451
|
+
NPM Scripts:
|
|
452
|
+
${npmCommandsList}
|
|
453
|
+
`);
|
|
454
|
+
}
|
|
455
|
+
|
|
426
456
|
program.addHelpText('after', `
|
|
427
457
|
|
|
428
458
|
Special Commands:
|
|
459
|
+
|
|
429
460
|
đ Edit bunosh file: ${color.bold('bunosh edit')}
|
|
430
|
-
đĨ Export scripts to package.json: ${color.bold('bunosh export:scripts')}
|
|
431
|
-
|
|
432
|
-
⥠Auto-setup completion: ${color.bold('bunosh setup-completion')}
|
|
433
|
-
âŦī¸ Upgrade bunosh: ${color.bold('bunosh upgrade')}
|
|
461
|
+
đĨ Export commands as scripts to package.json: ${color.bold('bunosh export:scripts')}
|
|
462
|
+
đĻž Upgrade bunosh: ${color.bold('bunosh upgrade')}
|
|
434
463
|
`);
|
|
435
464
|
|
|
436
465
|
program.on("command:*", (cmd) => {
|
package/src/task.js
CHANGED
|
@@ -138,6 +138,14 @@ export class TaskResult {
|
|
|
138
138
|
this.output = output;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
get hasFailed() {
|
|
142
|
+
return this.status === TaskStatus.FAIL;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get hasSucceeded() {
|
|
146
|
+
return this.status === TaskStatus.SUCCESS;
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
static fail(output = null) {
|
|
142
150
|
return new TaskResult({ status: TaskStatus.FAIL, output });
|
|
143
151
|
}
|