bunosh 0.4.14 β 0.5.0
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 +93 -565
- package/bunosh.js +33 -155
- package/package.json +1 -1
- package/src/error-formatter.js +80 -0
- package/src/program.js +85 -331
package/README.md
CHANGED
|
@@ -1,87 +1,33 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bunosh
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="assets/logo.png" alt="Logo" width="150">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
|
|
8
|
+
A task runner for JavaScript. Transform functions into CLI commands.
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
-
<p align="center">
|
|
12
|
-
Transform JavaScript functions into CLI commands.
|
|
13
|
-
</p>
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## What is Bunosh?
|
|
18
|
-
|
|
19
|
-
Bunosh is a modern task runner that turns your JavaScript functions into CLI commands instantly. No configuration, no boilerplate - just write functions and run them from the terminal.
|
|
20
|
-
|
|
21
11
|
> *Named after **banosh**, a traditional Ukrainian dish from cornmeal cooked with various ingredients*
|
|
22
12
|
|
|
13
|
+
---
|
|
23
14
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
No nore words, just code:
|
|
15
|
+
## Quick Example
|
|
27
16
|
|
|
28
17
|
```js
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
say(`π Hello, ${name}!`);
|
|
35
|
-
const city = await ask('Which city do you live in?')
|
|
36
|
-
const result = await fetch(`https://wttr.in/${city}?format=3`)
|
|
37
|
-
say(`Weather in your city ${result.output}`)
|
|
38
|
-
|
|
39
|
-
const toCleanup = await ask('Do you want me to cleanup tmp for you?', true);
|
|
40
|
-
|
|
41
|
-
if (!toCleanup) {
|
|
42
|
-
say('Bye, then!');
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
await shell`rm -rf ${require('os').tmpdir()}/*`;
|
|
47
|
-
say('π§Ή Cleaned up! Have a great day!');
|
|
18
|
+
// Bunoshfile.js β run with: bunosh deploy
|
|
19
|
+
export async function deploy(env = 'production') {
|
|
20
|
+
await exec`npm run build`.env({ NODE_ENV: env });
|
|
21
|
+
await exec`rsync -az dist/ server:/var/www/`;
|
|
22
|
+
say(`Deployed to ${env}`);
|
|
48
23
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
## Why Choose Bunosh?
|
|
52
|
-
|
|
53
|
-
| Comparison | π Bash Scripts | π¦ npm scripts | π οΈ Task Runners | π² **Bunosh** |
|
|
54
|
-
|------------|-----------------|----------------|------------------------------|----------------|
|
|
55
|
-
| **Syntax** | bash/zsh | Simple commands | Custom DSL | β
JavaScript |
|
|
56
|
-
| **Cross-platform** | No | Yes | Yes | β
Yes |
|
|
57
|
-
| **Ecosystem** | CLI tools | npm packages | Plugin dependent | β
Bash + npm |
|
|
58
|
-
| **Composability** | Commands | Separate scripts | Task dependencies | β
Import any JS code |
|
|
59
|
-
|
|
60
|
-
** Migrate to Bunosh**
|
|
61
|
-
|
|
62
|
-
- [Migrating from Bash Scripts](docs/bash-migration-guide.md)
|
|
63
|
-
- [Migrating from Node.js Scripts](docs/nodejs-migration-guide.md)
|
|
64
|
-
|
|
65
|
-
Hint: Provide this link to a coding agent and make it convert scripts into Bunosh!
|
|
66
|
-
|
|
67
|
-
## TOC
|
|
68
|
-
|
|
69
|
-
- [Installation](#installation)
|
|
70
|
-
- [MCP Integration](#mcp-integration)
|
|
71
|
-
- [Quickstart](#quickstart)
|
|
72
|
-
- [Commands](#commands)
|
|
73
|
-
- [Tasks](#tasks)
|
|
74
|
-
- [Input/Output](#inputoutput)
|
|
75
|
-
- [Task Control](#task-control)
|
|
76
|
-
- [AI Integration](#ai-integration)
|
|
77
|
-
- [MCP](#mcp)
|
|
78
|
-
- [Examples](#examples)
|
|
24
|
+
```
|
|
79
25
|
|
|
80
26
|
## Installation
|
|
81
27
|
|
|
82
|
-
###
|
|
28
|
+
### Single Executable (Recommended)
|
|
83
29
|
|
|
84
|
-
Download the standalone executable
|
|
30
|
+
Download the standalone executable β no Node.js or Bun required:
|
|
85
31
|
|
|
86
32
|
**macOS:**
|
|
87
33
|
```bash
|
|
@@ -102,7 +48,7 @@ Expand-Archive -Path "bunosh.zip" -DestinationPath .
|
|
|
102
48
|
Move-Item "bunosh-windows-x64.exe" "bunosh.exe"
|
|
103
49
|
```
|
|
104
50
|
|
|
105
|
-
###
|
|
51
|
+
### Package Managers
|
|
106
52
|
|
|
107
53
|
```bash
|
|
108
54
|
# Using Bun
|
|
@@ -119,7 +65,7 @@ npm install -g bunosh
|
|
|
119
65
|
bunosh init
|
|
120
66
|
```
|
|
121
67
|
|
|
122
|
-
2. **Write
|
|
68
|
+
2. **Write a command:**
|
|
123
69
|
```javascript
|
|
124
70
|
// Bunoshfile.js
|
|
125
71
|
const { exec, say } = global.bunosh;
|
|
@@ -128,63 +74,51 @@ const { exec, say } = global.bunosh;
|
|
|
128
74
|
* Builds the project for production
|
|
129
75
|
*/
|
|
130
76
|
export async function build(env = 'production') {
|
|
131
|
-
say(`π¨ Building for ${env}...`);
|
|
132
77
|
await exec`npm run build`.env({ NODE_ENV: env });
|
|
133
|
-
say('
|
|
78
|
+
say('Build complete');
|
|
134
79
|
}
|
|
135
80
|
```
|
|
136
81
|
|
|
82
|
+
3. **Run it:**
|
|
83
|
+
```bash
|
|
84
|
+
bunosh build
|
|
85
|
+
bunosh build staging
|
|
86
|
+
```
|
|
87
|
+
|
|
137
88
|
## Commands
|
|
138
89
|
|
|
139
90
|
By default, Bunosh loads commands from `Bunoshfile.js` in the current directory.
|
|
140
91
|
|
|
141
92
|
```
|
|
142
|
-
# reads Bunoshfile form cwd and runs hello()
|
|
143
93
|
bunosh hello
|
|
144
94
|
```
|
|
145
95
|
|
|
146
|
-
You can specify custom
|
|
96
|
+
You can specify a custom file using `--bunoshfile` or the `BUNOSHFILE` environment variable:
|
|
147
97
|
|
|
148
98
|
```bash
|
|
149
|
-
# Load commands from a different file
|
|
150
99
|
bunosh --bunoshfile Bunoshfile.dev.js hello
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
or via environment variable:
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
# Set default bunoshfile for session
|
|
157
|
-
export BUNOSHFILE=Bunoshfile.dev.js
|
|
158
|
-
bunosh hello # Uses Bunoshfile.dev.js
|
|
159
|
-
|
|
160
|
-
# One-time usage
|
|
161
100
|
BUNOSHFILE=Bunoshfile.prod.js bunosh deploy
|
|
162
101
|
```
|
|
163
102
|
|
|
164
|
-
|
|
165
103
|
### Creating Commands
|
|
166
104
|
|
|
167
105
|
Every exported function in `Bunoshfile.js` becomes a CLI command:
|
|
168
106
|
|
|
169
107
|
```javascript
|
|
170
|
-
// Simple command
|
|
171
108
|
export function hello() {
|
|
172
109
|
console.log('Hello, World!');
|
|
173
110
|
}
|
|
174
111
|
|
|
175
|
-
// Command with parameters
|
|
176
112
|
export function greet(name = 'friend') {
|
|
177
113
|
console.log(`Hello, ${name}!`);
|
|
178
114
|
}
|
|
179
115
|
|
|
180
|
-
// Command with options
|
|
181
116
|
export function deploy(env = 'staging', options = { force: false, verbose: false }) {
|
|
182
117
|
if (options.verbose) console.log('Verbose mode enabled');
|
|
183
118
|
console.log(`Deploying to ${env}${options.force ? ' (forced)' : ''}`);
|
|
184
119
|
}
|
|
185
120
|
```
|
|
186
121
|
|
|
187
|
-
**CLI Usage:**
|
|
188
122
|
```bash
|
|
189
123
|
bunosh hello
|
|
190
124
|
bunosh greet John
|
|
@@ -193,7 +127,7 @@ bunosh deploy production --force --verbose
|
|
|
193
127
|
|
|
194
128
|
### Arguments and Options
|
|
195
129
|
|
|
196
|
-
Bunosh
|
|
130
|
+
Bunosh maps function parameters to CLI arguments automatically:
|
|
197
131
|
|
|
198
132
|
```javascript
|
|
199
133
|
/**
|
|
@@ -212,7 +146,6 @@ export async function feature(name, base = 'main', options = { push: false }) {
|
|
|
212
146
|
}
|
|
213
147
|
```
|
|
214
148
|
|
|
215
|
-
**Generated CLI:**
|
|
216
149
|
```bash
|
|
217
150
|
bunosh feature my-feature # Creates from main
|
|
218
151
|
bunosh feature my-feature develop # Creates from develop
|
|
@@ -230,211 +163,56 @@ Functions are automatically converted to kebab-case commands:
|
|
|
230
163
|
| `npmInstall` | `bunosh npm:install` |
|
|
231
164
|
| `buildAndDeploy` | `bunosh build:and-deploy` |
|
|
232
165
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
166
|
### Project Namespaces
|
|
237
167
|
|
|
238
|
-
Organize
|
|
168
|
+
Organize tasks by creating multiple Bunoshfiles. Files named `Bunoshfile.<namespace>.js` register commands under that namespace:
|
|
239
169
|
|
|
240
|
-
**Create namespace files:**
|
|
241
170
|
```bash
|
|
242
|
-
#
|
|
243
|
-
Bunoshfile.js
|
|
244
|
-
|
|
245
|
-
# Development tasks
|
|
246
|
-
Bunoshfile.dev.js
|
|
247
|
-
|
|
248
|
-
# API tasks
|
|
249
|
-
Bunoshfile.api.js
|
|
250
|
-
|
|
251
|
-
# Database tasks
|
|
252
|
-
Bunoshfile.db.js
|
|
171
|
+
Bunoshfile.js # bunosh build, bunosh test
|
|
172
|
+
Bunoshfile.dev.js # bunosh dev:start, bunosh dev:debug
|
|
173
|
+
Bunoshfile.api.js # bunosh api:deploy, bunosh api:test
|
|
253
174
|
```
|
|
254
175
|
|
|
255
|
-
|
|
256
|
-
```javascript
|
|
257
|
-
// Bunoshfile.js - Core project tasks
|
|
258
|
-
export function build() {
|
|
259
|
-
console.log('Building project...');
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function test() {
|
|
263
|
-
console.log('Running tests...');
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Bunoshfile.dev.js - Development tasks
|
|
267
|
-
export function start() {
|
|
268
|
-
console.log('Starting dev server...');
|
|
269
|
-
}
|
|
176
|
+
## Comparison
|
|
270
177
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
console.log('Deploying API...');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export function test() {
|
|
281
|
-
console.log('Running API tests...');
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Usage:**
|
|
286
|
-
```bash
|
|
287
|
-
# Core tasks (no namespace)
|
|
288
|
-
bunosh build
|
|
289
|
-
bunosh test
|
|
290
|
-
|
|
291
|
-
# Namespaced tasks
|
|
292
|
-
bunosh dev:start
|
|
293
|
-
bunosh dev:debug
|
|
294
|
-
bunosh api:deploy
|
|
295
|
-
bunosh api:test
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## MCP
|
|
299
|
-
|
|
300
|
-
Bunosh supports the **Model Context Protocol (MCP)**, allowing you to expose your Bunoshfile commands as tools for AI assistants like Claude Desktop, Cursor, and other MCP-compatible applications.
|
|
301
|
-
|
|
302
|
-
### Quick Start
|
|
303
|
-
|
|
304
|
-
1. **Start MCP server** in your project directory:
|
|
305
|
-
```bash
|
|
306
|
-
# Uses Bunoshfile.js from current directory
|
|
307
|
-
bunosh -mcp
|
|
308
|
-
|
|
309
|
-
# Or with custom Bunoshfile
|
|
310
|
-
bunosh --bunoshfile Bunoshfile.dev.js -mcp
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
2. **Configure your AI assistant** to use Bunosh as an MCP server (see instructions below).
|
|
314
|
-
|
|
315
|
-
<details>
|
|
316
|
-
<summary>Claude Desktop Setup</summary>
|
|
317
|
-
|
|
318
|
-
1. **Edit Claude Desktop configuration** (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
319
|
-
|
|
320
|
-
```json
|
|
321
|
-
{
|
|
322
|
-
"mcpServers": {
|
|
323
|
-
"bunosh": {
|
|
324
|
-
"command": "bunosh",
|
|
325
|
-
"args": ["-mcp"],
|
|
326
|
-
"cwd": "/path/to/your/project"
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
2. **Restart Claude Desktop** - your Bunosh commands will now be available as tools.
|
|
333
|
-
|
|
334
|
-
3. **Use your commands** in Claude:
|
|
335
|
-
- "Build my project with bunosh"
|
|
336
|
-
- "Run tests using bunosh"
|
|
337
|
-
- "Deploy to staging with bunosh"
|
|
338
|
-
|
|
339
|
-
**Multiple Projects:**
|
|
340
|
-
```json
|
|
341
|
-
{
|
|
342
|
-
"mcpServers": {
|
|
343
|
-
"my-app": {
|
|
344
|
-
"command": "bunosh",
|
|
345
|
-
"args": ["-mcp"],
|
|
346
|
-
"cwd": "/path/to/my-app"
|
|
347
|
-
},
|
|
348
|
-
"my-api": {
|
|
349
|
-
"command": "bunosh",
|
|
350
|
-
"args": ["-mcp"],
|
|
351
|
-
"cwd": "/path/to/my-api"
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
</details>
|
|
358
|
-
|
|
359
|
-
<details>
|
|
360
|
-
<summary>Cursor Setup</summary>
|
|
361
|
-
|
|
362
|
-
1. **Open Cursor settings** (`Cmd/Ctrl + ,`)
|
|
363
|
-
|
|
364
|
-
2. **Navigate to** `Extensions` β `MCP Servers`
|
|
365
|
-
|
|
366
|
-
3. **Add new MCP server:**
|
|
367
|
-
- **Name**: `bunosh`
|
|
368
|
-
- **Command**: `bunosh`
|
|
369
|
-
- **Arguments**: `-mcp`
|
|
370
|
-
- **Working Directory**: `/path/to/your/project`
|
|
371
|
-
|
|
372
|
-
4. **Save and restart** Cursor
|
|
373
|
-
|
|
374
|
-
5. **Your Bunosh commands** will now appear in the AI chat sidebar as available tools.
|
|
375
|
-
|
|
376
|
-
</details>
|
|
377
|
-
|
|
378
|
-
<details>
|
|
379
|
-
<summary>Cline Setup (VS Code Extension)</summary>
|
|
380
|
-
|
|
381
|
-
1. **Install Cline** extension from VS Code marketplace
|
|
382
|
-
|
|
383
|
-
2. **Open Cline settings** (click the gear icon in Cline panel)
|
|
384
|
-
|
|
385
|
-
3. **Add MCP server** under "MCP Servers" section:
|
|
386
|
-
```json
|
|
387
|
-
{
|
|
388
|
-
"name": "bunosh",
|
|
389
|
-
"command": "bunosh",
|
|
390
|
-
"args": ["-mcp"],
|
|
391
|
-
"cwd": "/path/to/your/project"
|
|
392
|
-
}
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
4. **Save and reload** the VS Code window
|
|
396
|
-
|
|
397
|
-
5. **Your commands** will be available in Cline's tool selection
|
|
398
|
-
|
|
399
|
-
</details>
|
|
178
|
+
| | Bash Scripts | npm scripts | Task Runners | **Bunosh** |
|
|
179
|
+
|--|--|--|--|--|
|
|
180
|
+
| **Syntax** | bash/zsh | Simple commands | Custom DSL | JavaScript |
|
|
181
|
+
| **Cross-platform** | No | Yes | Yes | Yes |
|
|
182
|
+
| **Ecosystem** | CLI tools | npm packages | Plugin dependent | Bash + npm |
|
|
183
|
+
| **Composability** | Commands | Separate scripts | Task dependencies | Import any JS code |
|
|
400
184
|
|
|
401
185
|
## Tasks
|
|
402
186
|
|
|
403
|
-
|
|
187
|
+
Built-in tasks are available via `global.bunosh`:
|
|
404
188
|
|
|
405
189
|
```javascript
|
|
406
190
|
const { exec, shell, fetch, writeToFile, copyFile, task } = global.bunosh;
|
|
407
191
|
```
|
|
408
192
|
|
|
409
|
-
>
|
|
410
|
-
|
|
193
|
+
> Global variables are used instead of imports so bunosh works with the single-executable on any platform.
|
|
411
194
|
|
|
412
195
|
* Async tasks: `exec`, `shell`, `fetch`
|
|
413
196
|
* Sync tasks: `writeToFile`, `copyFile`
|
|
414
197
|
* Task wrapper: `task`
|
|
415
198
|
|
|
416
|
-
Each
|
|
199
|
+
Each task returns a `TaskResult` object:
|
|
417
200
|
|
|
418
201
|
```js
|
|
419
202
|
const result = await shell`echo "Hello"`;
|
|
420
|
-
console.log(result.status);
|
|
421
|
-
console.log(result.output);
|
|
422
|
-
console.log(result.hasFailed);
|
|
203
|
+
console.log(result.status); // 'success', 'fail', or 'warning'
|
|
204
|
+
console.log(result.output); // Command output
|
|
205
|
+
console.log(result.hasFailed); // true if status is 'fail'
|
|
423
206
|
console.log(result.hasSucceeded); // true if status is 'success'
|
|
424
|
-
console.log(result.hasWarning); // true if status is 'warning'
|
|
425
207
|
|
|
426
|
-
|
|
427
|
-
const json = await result.json();
|
|
208
|
+
const json = await result.json(); // Structured data
|
|
428
209
|
```
|
|
429
210
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
#### `task`
|
|
211
|
+
### `task`
|
|
433
212
|
|
|
434
|
-
|
|
213
|
+
Wraps a function into a named task with tracking and output:
|
|
435
214
|
|
|
436
215
|
```js
|
|
437
|
-
// register operation as a task
|
|
438
216
|
const result = task('Fetch Readme file', () => {
|
|
439
217
|
const content = fs.readFileSync('README.md', 'utf8');
|
|
440
218
|
console.log(content);
|
|
@@ -442,17 +220,15 @@ const result = task('Fetch Readme file', () => {
|
|
|
442
220
|
});
|
|
443
221
|
```
|
|
444
222
|
|
|
445
|
-
If
|
|
223
|
+
If another task runs inside a task function, its description is appended to child tasks.
|
|
446
224
|
|
|
447
|
-
|
|
225
|
+
### `exec`
|
|
448
226
|
|
|
449
|
-
|
|
227
|
+
Runs a command using [child process `spawn`](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options):
|
|
450
228
|
|
|
451
229
|
```javascript
|
|
452
|
-
// Complex commands with pipes and streaming output
|
|
453
230
|
await exec`npm install --verbose`;
|
|
454
231
|
await exec`docker build . | tee build.log`;
|
|
455
|
-
await exec`find . -name "*.js" | grep -v node_modules | wc -l`;
|
|
456
232
|
|
|
457
233
|
// With environment variables
|
|
458
234
|
await exec`echo $NODE_ENV`.env({ NODE_ENV: 'production' });
|
|
@@ -460,75 +236,58 @@ await exec`echo $NODE_ENV`.env({ NODE_ENV: 'production' });
|
|
|
460
236
|
// In specific directory
|
|
461
237
|
await exec`npm install`.cwd('/tmp/project');
|
|
462
238
|
|
|
463
|
-
//
|
|
239
|
+
// Structured output
|
|
464
240
|
const result = await exec`git status --porcelain`;
|
|
465
241
|
const data = await result.json();
|
|
466
242
|
// Returns: { stdout: "...", stderr: "...", exitCode: 0, lines: [...] }
|
|
467
243
|
```
|
|
468
244
|
|
|
469
|
-
By default
|
|
245
|
+
By default tasks print live output from stdout and stderr. To disable, use `silent`:
|
|
470
246
|
|
|
471
247
|
```javascript
|
|
472
|
-
|
|
473
|
-
// disable printing output
|
|
474
248
|
await task.silent(() => exec`npm install`);
|
|
475
249
|
|
|
476
|
-
// disable
|
|
477
|
-
|
|
250
|
+
// Or disable for all commands
|
|
251
|
+
task.silence();
|
|
478
252
|
```
|
|
479
253
|
|
|
480
|
-
|
|
254
|
+
### `shell`
|
|
481
255
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
Optimized for simple, fast commands when running under Bun:
|
|
256
|
+
Optimized for simple commands when running under Bun:
|
|
485
257
|
|
|
486
258
|
```javascript
|
|
487
|
-
// Simple, fast commands
|
|
488
259
|
await shell`pwd`;
|
|
489
260
|
await shell`ls -la`;
|
|
490
261
|
await shell`cat package.json`;
|
|
491
262
|
|
|
492
|
-
// Get structured output with stdout, stderr, exit code and lines
|
|
493
263
|
const result = await shell`ls -la`;
|
|
494
264
|
const data = await result.json();
|
|
495
|
-
// Returns: { stdout: "...", stderr: "...", exitCode: 0, lines: [...] }
|
|
496
265
|
```
|
|
497
266
|
|
|
498
|
-
For
|
|
499
|
-
|
|
500
|
-
`shell` vs `exec`
|
|
267
|
+
For details see the [Bun shell](https://bun.sh/docs/runtime/shell) reference.
|
|
501
268
|
|
|
502
|
-
|
|
503
|
-
|---------|----------|-----------|----------------|---------------|
|
|
504
|
-
| `exec` | Single command execution | single command | spawn process | NodeJS + Bun but platform dependent |
|
|
505
|
-
| `shell` | Multiple cross-platform shell commands | exec + `pwd`, `ls`, `echo`, `cat`, basic file ops | bun shell | Bun only but Cross-platform |
|
|
269
|
+
**`shell` vs `exec`:**
|
|
506
270
|
|
|
507
|
-
|
|
271
|
+
| Command | Best For | Implementation | Compatibility |
|
|
272
|
+
|---------|----------|----------------|---------------|
|
|
273
|
+
| `exec` | Single command execution | spawn process | Node.js + Bun, platform dependent |
|
|
274
|
+
| `shell` | Cross-platform shell commands | Bun shell | Bun only, cross-platform |
|
|
508
275
|
|
|
509
|
-
|
|
276
|
+
### `fetch`
|
|
510
277
|
|
|
511
|
-
|
|
278
|
+
Wraps the fetch API as a task:
|
|
512
279
|
|
|
513
280
|
```javascript
|
|
514
|
-
/**
|
|
515
|
-
* Check service health
|
|
516
|
-
*/
|
|
517
281
|
export async function healthCheck(url) {
|
|
518
282
|
const response = await fetch(url);
|
|
519
283
|
|
|
520
284
|
if (response.ok) {
|
|
521
285
|
const data = await response.json();
|
|
522
|
-
say(
|
|
286
|
+
say(`Service healthy: ${data.status}`);
|
|
523
287
|
} else {
|
|
524
|
-
yell(
|
|
288
|
+
yell(`Service down: ${response.status}`);
|
|
525
289
|
}
|
|
526
290
|
}
|
|
527
|
-
|
|
528
|
-
// Get JSON response data directly
|
|
529
|
-
const apiResponse = await fetch('https://api.example.com/data');
|
|
530
|
-
const jsonData = await apiResponse.json();
|
|
531
|
-
// Calls response.json() method internally
|
|
532
291
|
```
|
|
533
292
|
|
|
534
293
|
### File Operations
|
|
@@ -536,9 +295,6 @@ const jsonData = await apiResponse.json();
|
|
|
536
295
|
Template-based file writing and copying:
|
|
537
296
|
|
|
538
297
|
```javascript
|
|
539
|
-
/**
|
|
540
|
-
* Generate configuration file
|
|
541
|
-
*/
|
|
542
298
|
export function generatePage(name, description = '') {
|
|
543
299
|
writeToFile('index.mdx', (line) => {
|
|
544
300
|
line`name": "${name}",`;
|
|
@@ -548,103 +304,62 @@ export function generatePage(name, description = '') {
|
|
|
548
304
|
line`---`;
|
|
549
305
|
});
|
|
550
306
|
|
|
551
|
-
|
|
307
|
+
copyFile('template.env', '.env');
|
|
552
308
|
}
|
|
553
|
-
|
|
554
|
-
// Copy files
|
|
555
|
-
copyFile('template.env', '.env');
|
|
556
309
|
```
|
|
557
310
|
|
|
558
|
-
|
|
559
311
|
## Input/Output
|
|
560
312
|
|
|
561
|
-
### `say`
|
|
313
|
+
### `say`
|
|
562
314
|
|
|
563
|
-
Standard output
|
|
315
|
+
Standard output:
|
|
564
316
|
|
|
565
317
|
```javascript
|
|
566
318
|
say('Building project...');
|
|
567
|
-
say('π¦ Dependencies installed');
|
|
568
319
|
say(`Found ${count} files to process`);
|
|
569
320
|
```
|
|
570
321
|
|
|
571
|
-
### `ask`
|
|
322
|
+
### `ask`
|
|
572
323
|
|
|
573
|
-
|
|
324
|
+
User input with smart parameter detection:
|
|
574
325
|
|
|
575
326
|
```javascript
|
|
576
|
-
// Text input with default
|
|
577
327
|
const name = await ask('Project name:', 'my-app');
|
|
578
|
-
|
|
579
|
-
// Boolean confirmation (auto-detects)
|
|
580
328
|
const proceed = await ask('Continue?', true);
|
|
581
|
-
|
|
582
|
-
// Single selection (auto-detects from array)
|
|
583
329
|
const env = await ask('Select environment:', ['dev', 'staging', 'prod']);
|
|
584
|
-
|
|
585
|
-
// Multiple selection
|
|
586
|
-
const features = await ask('Select features:',
|
|
587
|
-
['TypeScript', 'ESLint', 'Tests'],
|
|
588
|
-
{ multiple: true }
|
|
589
|
-
);
|
|
590
|
-
|
|
591
|
-
// Password input
|
|
330
|
+
const features = await ask('Select features:', ['TypeScript', 'ESLint', 'Tests'], { multiple: true });
|
|
592
331
|
const password = await ask('Enter password:', { type: 'password' });
|
|
593
|
-
|
|
594
|
-
// Multiline editor input
|
|
595
332
|
const description = await ask('Enter description:', { editor: true });
|
|
596
333
|
```
|
|
597
334
|
|
|
598
|
-
| Parameter/Option | Type | Description | Example |
|
|
599
|
-
|------------------|------|-------------|---------|
|
|
600
|
-
| **Smart Detection** | | |
|
|
601
|
-
| `defaultValue` | String/Number | Sets default value for text/number input | `'John'`, `3000` |
|
|
602
|
-
| `defaultValue` | Boolean | Auto-detects as confirmation prompt | `true`, `false` |
|
|
603
|
-
| `choices` | Array | Auto-detects as selection list | `['A', 'B', 'C']` |
|
|
604
|
-
| **Options Object** | | |
|
|
605
|
-
| `multiple` | Boolean | Enables multiple selections (requires `choices`) | `true` |
|
|
606
|
-
| `multiline` | Boolean | Opens system editor for multi-line input | `true` |
|
|
607
|
-
| `editor` | Boolean | Opens system editor for multi-line input (same as `multiline`) | `true` |
|
|
608
|
-
| `default` | Any | Default value or content (when using options object) | `'default value'` |
|
|
609
|
-
| `type` | String | Input type: `'input'`, `'confirm'`, `'password'`, `'number'` | `'password'` |
|
|
610
|
-
| `validate` | Function | Custom validation function | `(input) => input.length > 0` |
|
|
611
|
-
|
|
612
|
-
|
|
613
335
|
### `yell`
|
|
614
336
|
|
|
615
|
-
Emphasized Output
|
|
616
|
-
|
|
617
337
|
ASCII art output for important messages:
|
|
618
338
|
|
|
619
339
|
```javascript
|
|
620
340
|
yell('BUILD COMPLETE!');
|
|
621
|
-
yell('DEPLOYMENT SUCCESSFUL!');
|
|
622
341
|
```
|
|
623
342
|
|
|
624
343
|
### `silent`
|
|
625
344
|
|
|
626
|
-
|
|
345
|
+
Disable realtime output:
|
|
627
346
|
|
|
628
347
|
```javascript
|
|
629
|
-
// Silence all task output
|
|
630
|
-
task.silence();
|
|
348
|
+
task.silence(); // Silence all task output
|
|
631
349
|
await shell`npm build`;
|
|
350
|
+
task.prints(); // Restore output
|
|
632
351
|
|
|
633
|
-
//
|
|
634
|
-
task.prints();
|
|
635
|
-
|
|
636
|
-
// Silent specific task
|
|
352
|
+
// Silence a specific task
|
|
637
353
|
const labels = await task.silent(() => shell(`gh api repos/:org/:repo/labels`));
|
|
638
354
|
```
|
|
639
355
|
|
|
640
356
|
## Task Control
|
|
641
357
|
|
|
642
|
-
### Parallel
|
|
358
|
+
### Parallel Execution
|
|
643
359
|
|
|
644
|
-
|
|
360
|
+
Use `Promise.all()` to run tasks in parallel:
|
|
645
361
|
|
|
646
362
|
```javascript
|
|
647
|
-
// Parallel tasks
|
|
648
363
|
const results = await Promise.all([
|
|
649
364
|
exec`npm run build:frontend`,
|
|
650
365
|
exec`npm run build:backend`,
|
|
@@ -654,61 +369,49 @@ const results = await Promise.all([
|
|
|
654
369
|
|
|
655
370
|
### Custom Tasks
|
|
656
371
|
|
|
657
|
-
Name and group
|
|
372
|
+
Name and group operations:
|
|
658
373
|
|
|
659
374
|
```js
|
|
660
|
-
await task('Build', () => {
|
|
661
|
-
await exec`npm run build:frontend
|
|
662
|
-
await exec`npm run build:docs
|
|
375
|
+
await task('Build', async () => {
|
|
376
|
+
await exec`npm run build:frontend`;
|
|
377
|
+
await exec`npm run build:docs`;
|
|
663
378
|
});
|
|
664
|
-
|
|
379
|
+
```
|
|
665
380
|
|
|
666
381
|
### Stop on Failure
|
|
667
382
|
|
|
668
|
-
By default bunosh
|
|
669
|
-
|
|
383
|
+
By default bunosh continues execution when tasks fail. To stop immediately on failure:
|
|
670
384
|
|
|
671
385
|
```javascript
|
|
672
|
-
/**
|
|
673
|
-
* Strict deployment - stop on any failure
|
|
674
|
-
*/
|
|
675
386
|
export async function deployStrict() {
|
|
676
|
-
task.stopOnFailures();
|
|
387
|
+
task.stopOnFailures();
|
|
677
388
|
|
|
678
389
|
await exec`npm test`;
|
|
679
390
|
await exec`npm run build`;
|
|
680
391
|
await exec`deploy-script`;
|
|
681
|
-
// If any task fails, script exits immediately
|
|
682
392
|
}
|
|
683
393
|
|
|
684
|
-
/**
|
|
685
|
-
* Cleanup - continue despite failures
|
|
686
|
-
*/
|
|
687
394
|
export async function cleanup() {
|
|
688
|
-
task.ignoreFailures();
|
|
395
|
+
task.ignoreFailures();
|
|
689
396
|
|
|
690
397
|
await task('Remove temp files', () => shell`rm -rf tmp/*`);
|
|
691
398
|
await task('Clear logs', () => shell`rm -f logs/*.log`);
|
|
692
399
|
await task('Reset cache', () => shell`rm -rf .cache`);
|
|
693
|
-
// All tasks run regardless of failures
|
|
694
400
|
}
|
|
695
401
|
```
|
|
696
402
|
|
|
697
403
|
### Try Operations
|
|
698
404
|
|
|
699
|
-
|
|
405
|
+
Handle operations that might fail:
|
|
700
406
|
|
|
701
407
|
```javascript
|
|
702
|
-
/**
|
|
703
|
-
* Check service availability
|
|
704
|
-
*/
|
|
705
408
|
export async function checkServices() {
|
|
706
409
|
const dbConnected = await task.try(() => shell`nc -z localhost 5432`);
|
|
707
410
|
|
|
708
411
|
if (dbConnected) {
|
|
709
|
-
say('
|
|
412
|
+
say('Database connected');
|
|
710
413
|
} else {
|
|
711
|
-
say('
|
|
414
|
+
say('Database unavailable, using fallback');
|
|
712
415
|
await useFallbackDatabase();
|
|
713
416
|
}
|
|
714
417
|
|
|
@@ -720,189 +423,14 @@ export async function checkServices() {
|
|
|
720
423
|
}
|
|
721
424
|
```
|
|
722
425
|
|
|
723
|
-
##
|
|
724
|
-
|
|
725
|
-
Built-in AI support for code generation, documentation, and automation.
|
|
726
|
-
Automatically responds to structured JSON output.
|
|
727
|
-
|
|
728
|
-
AI provider automatically detected, but you need to provide API key and model name.
|
|
729
|
-
Use `.env` file with `AI_MODEL` and `OPENAI_API_KEY` variables.
|
|
730
|
-
In case you use provider other than OpenAI, Anthropic, Groq, you may need to configure it manually in top of Bunoshfile
|
|
731
|
-
|
|
732
|
-
```bash
|
|
733
|
-
# Choose your AI model
|
|
734
|
-
export AI_MODEL=gpt-5 # or claude-4-sonnet, llama-3.3-70b, etc.
|
|
735
|
-
|
|
736
|
-
# Set API key for your provider
|
|
737
|
-
export OPENAI_API_KEY=your_key_here # For OpenAI
|
|
738
|
-
# export ANTHROPIC_API_KEY=your_key_here # For Claude
|
|
739
|
-
# export GROQ_API_KEY=your_key_here # For Groq
|
|
740
|
-
```
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
Use the `ai` function to interact with the AI.
|
|
744
|
-
|
|
745
|
-
```js
|
|
746
|
-
const resp = await ai(message, { field1: 'what should be there', field2: 'what should be there' })
|
|
747
|
-
```
|
|
748
|
-
|
|
749
|
-
### Usage
|
|
750
|
-
|
|
751
|
-
```javascript
|
|
752
|
-
const { ai, writeToFile } = global.bunosh;
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Generate commit message from staged changes
|
|
756
|
-
*/
|
|
757
|
-
export async function commit() {
|
|
758
|
-
const diff = await exec`git diff --staged`;
|
|
759
|
-
|
|
760
|
-
if (!diff.output.trim()) {
|
|
761
|
-
say('No staged changes');
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
const response = await ai(
|
|
766
|
-
`Generate a conventional commit message for: ${diff.output}`,
|
|
767
|
-
{
|
|
768
|
-
type: 'Commit type (feat/fix/docs/chore)',
|
|
769
|
-
scope: 'Commit scope (optional)',
|
|
770
|
-
subject: 'Brief subject line (50 chars max)',
|
|
771
|
-
body: 'Detailed explanation'
|
|
772
|
-
}
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
const commit = await response.json();
|
|
776
|
-
|
|
777
|
-
const message = commit.scope
|
|
778
|
-
? `${commit.type}(${commit.scope}): ${commit.subject}\n\n${commit.body}`
|
|
779
|
-
: `${commit.type}: ${commit.subject}\n\n${commit.body}`;
|
|
780
|
-
|
|
781
|
-
await exec`git commit -m "${message}"`;
|
|
782
|
-
say('β
AI-generated commit created');
|
|
783
|
-
}
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
See more ai usage examples in [docs/examples.md](docs/examples.md)
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
## Execute JavaScript Code
|
|
790
|
-
|
|
791
|
-
Bunosh supports executing JavaScript code directly using the `-e` flag, allowing for powerful one-liners and integration with shell scripts and CI/CD systems.
|
|
792
|
-
|
|
793
|
-
### Basic Usage
|
|
794
|
-
|
|
795
|
-
```bash
|
|
796
|
-
# Execute inline JavaScript
|
|
797
|
-
bunosh -e "say('Hello')"
|
|
798
|
-
|
|
799
|
-
# Execute JavaScript from stdin
|
|
800
|
-
echo "say('Hello')" | bunosh -e
|
|
801
|
-
```
|
|
802
|
-
|
|
803
|
-
### Heredoc Syntax
|
|
804
|
-
|
|
805
|
-
For multi-line scripts, use heredoc syntax for clean, readable code:
|
|
806
|
-
|
|
807
|
-
```bash
|
|
808
|
-
bunosh -e << 'EOF'
|
|
809
|
-
say('π Starting build process...')
|
|
810
|
-
await task('Install Dependencies', () => shell`npm ci`)
|
|
811
|
-
await task('Build', () => shell`npm run build`)
|
|
812
|
-
await task('Test', () => shell`npm test`)
|
|
813
|
-
say('β
All tasks completed successfully!')
|
|
814
|
-
EOF
|
|
815
|
-
```
|
|
816
|
-
|
|
817
|
-
### With Environment Variables and Control Flow
|
|
818
|
-
|
|
819
|
-
```bash
|
|
820
|
-
# Complex script with conditions
|
|
821
|
-
bunosh -e << 'EOF'
|
|
822
|
-
const env = process.env.NODE_ENV || 'development'
|
|
823
|
-
say(`Building for ${env}...`)
|
|
824
|
-
|
|
825
|
-
if (env === 'production') {
|
|
826
|
-
await shell`npm run build:prod`
|
|
827
|
-
await task('Deploy', () => shell`./deploy.sh`)
|
|
828
|
-
} else {
|
|
829
|
-
await shell`npm run build:dev`
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
yell('BUILD COMPLETE!')
|
|
833
|
-
EOF
|
|
834
|
-
```
|
|
835
|
-
|
|
836
|
-
### Error Handling
|
|
837
|
-
|
|
838
|
-
```bash
|
|
839
|
-
# Script with error handling
|
|
840
|
-
bunosh -e << 'EOF'
|
|
841
|
-
task.stopOnFailures()
|
|
842
|
-
|
|
843
|
-
try {
|
|
844
|
-
await shell`npm test`
|
|
845
|
-
await shell`npm run build`
|
|
846
|
-
say('β
Success!')
|
|
847
|
-
} catch (error) {
|
|
848
|
-
yell(`β Build failed: ${error.message}`)
|
|
849
|
-
process.exit(1)
|
|
850
|
-
}
|
|
851
|
-
EOF
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
### JavaScript Execution in GitHub Actions
|
|
855
|
-
|
|
856
|
-
Use JavaScript execution to run Bunosh scripts inside CI/CD workflows without creating separate files:
|
|
857
|
-
|
|
858
|
-
```yaml
|
|
859
|
-
- name: Build and Deploy
|
|
860
|
-
run: |
|
|
861
|
-
bunosh -e << 'EOF'
|
|
862
|
-
say('π Starting deployment...')
|
|
863
|
-
|
|
864
|
-
if (!process.env.NODE_ENV === 'production') return;
|
|
865
|
-
|
|
866
|
-
shell`./deploy.sh`
|
|
867
|
-
|
|
868
|
-
const response = await fetch('${{ secrets.DEPLOY_WEBHOOK }}', {
|
|
869
|
-
method: 'POST',
|
|
870
|
-
headers: { 'Authorization': 'Bearer ${{ secrets.API_TOKEN }}' }
|
|
871
|
-
})
|
|
872
|
-
|
|
873
|
-
if (response.ok) {
|
|
874
|
-
yell('π DEPLOYMENT COMPLETE!')
|
|
875
|
-
} else {
|
|
876
|
-
yell('β DEPLOYMENT FAILED!')
|
|
877
|
-
process.exit(1)
|
|
878
|
-
}
|
|
879
|
-
EOF
|
|
880
|
-
env:
|
|
881
|
-
NODE_ENV: production
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
### Shell Integration
|
|
885
|
-
|
|
886
|
-
```bash
|
|
887
|
-
bunosh -e << 'EOF'
|
|
888
|
-
say('Running database migrations...')
|
|
889
|
-
await shell`npm run migrate`
|
|
890
|
-
say('Migrations completed')
|
|
891
|
-
EOF
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
## Examples
|
|
896
|
-
|
|
897
|
-
For comprehensive examples of Bunosh in action, see [docs/examples.md](docs/examples.md).
|
|
426
|
+
## Documentation
|
|
898
427
|
|
|
899
|
-
|
|
900
|
-
-
|
|
901
|
-
- AI
|
|
902
|
-
-
|
|
903
|
-
-
|
|
904
|
-
-
|
|
905
|
-
- And more practical examples
|
|
428
|
+
- **[Examples](docs/examples.md)** β Real-world examples and workflows
|
|
429
|
+
- **[AI Integration](docs/ai.md)** β Built-in AI support
|
|
430
|
+
- **[MCP Integration](docs/mcp.md)** β Expose commands to AI assistants (Claude, Cursor, etc.)
|
|
431
|
+
- **[JavaScript Execution](docs/javascript-execution.md)** β Execute JavaScript directly via CLI
|
|
432
|
+
- **[Bash Migration Guide](docs/bash-migration-guide.md)** β Convert bash scripts to Bunosh
|
|
433
|
+
- **[Node.js Migration Guide](docs/nodejs-migration-guide.md)** β Migrate from Node.js scripts
|
|
906
434
|
|
|
907
435
|
## License
|
|
908
436
|
|
|
@@ -910,4 +438,4 @@ MIT License - see LICENSE file for details.
|
|
|
910
438
|
|
|
911
439
|
---
|
|
912
440
|
|
|
913
|
-
|
|
441
|
+
Made in Ukraine
|