bunosh 0.4.6 → 0.4.8

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 CHANGED
@@ -398,41 +398,6 @@ bunosh --bunoshfile Bunoshfile.dev.js -mcp
398
398
 
399
399
  </details>
400
400
 
401
- <details>
402
-
403
- #### Best Practices for MCP
404
-
405
- ### Command Design for AI
406
-
407
- Design your commands to work well with AI assistants:
408
-
409
- * Add comments to all commands
410
- * Describe use cases for each Command
411
- * Describe each param using JSDoc style comments
412
- * Include types when describing parameters
413
-
414
- ```javascript
415
- /**
416
- * Builds the project for specified environment
417
- * @param {string} environment - Target environment: 'development', 'staging', or 'production'
418
- * @param {Object} options - Build options
419
- * @param {boolean} options.verbose - Enable verbose logging
420
- * @param {boolean} options.analyze - Run bundle analysis after build
421
- */
422
- export async function build(environment = 'development', options = {}) {
423
- const config = getBuildConfig(environment);
424
- if (options.verbose) say(`🔨 Building for ${environment}...`);
425
-
426
- await exec`npm run build:${environment}`;
427
-
428
- if (options.analyze) {
429
- await exec`npm run analyze`;
430
- }
431
-
432
- say('✅ Build complete!');
433
- }
434
- ```
435
-
436
401
  ## Tasks
437
402
 
438
403
  Bunosh provides built-in tasks which are available via `global.bunosh`:
@@ -738,7 +703,7 @@ Gracefully handle operations that might fail:
738
703
  * Check service availability
739
704
  */
740
705
  export async function checkServices() {
741
- const dbConnected = await task.try(shell`nc -z localhost 5432`);
706
+ const dbConnected = await task.try(() => shell`nc -z localhost 5432`);
742
707
 
743
708
  if (dbConnected) {
744
709
  say('✅ Database connected');
package/bunosh.js CHANGED
@@ -4,6 +4,7 @@
4
4
  globalThis._bunoshStartTime = Date.now();
5
5
  globalThis._bunoshCommandCompleted = false;
6
6
  globalThis._bunoshGlobalTasksExecuted = [];
7
+ globalThis._bunoshIgnoreFailuresMode = false;
7
8
  globalThis._bunoshGlobalTaskStatus = {
8
9
  RUNNING: 'running',
9
10
  FAIL: 'fail',
@@ -310,39 +311,60 @@ process.on('uncaughtException', (error) => {
310
311
  process.exit(1);
311
312
  });
312
313
 
313
- // Handle exit for task summary
314
314
  process.on('exit', (code) => {
315
315
  if (!process.env.BUNOSH_COMMAND_STARTED) return;
316
-
317
- // Don't print summary if exit was due to stdin closing during an ask operation
318
- // This prevents duplicate output when ask commands don't receive all required input
316
+
319
317
  if (globalThis._bunoshInAskOperation && code === 0) {
320
318
  return;
321
319
  }
322
-
323
- // Access global values directly
320
+
324
321
  const tasksExecuted = globalThis._bunoshGlobalTasksExecuted || [];
325
322
  const TaskStatus = globalThis._bunoshGlobalTaskStatus || { FAIL: 'fail', WARNING: 'warning' };
326
-
327
- // Calculate total time from when the process started
323
+
328
324
  const totalTime = Date.now() - globalThis._bunoshStartTime || 0;
329
325
  const tasksFailed = tasksExecuted.filter(ti => ti.result?.status === TaskStatus.FAIL).length;
330
326
  const tasksWarning = tasksExecuted.filter(ti => ti.result?.status === TaskStatus.WARNING).length;
331
-
332
- // Check if we're in test environment
327
+
328
+ const commandArgs = process.argv.slice(2);
333
329
  const isTestEnvironment = process.env.NODE_ENV === 'test' ||
334
330
  (typeof Bun !== 'undefined' && typeof Bun?.jest !== 'undefined') ||
335
- process.argv.some(arg => arg.includes('vitest') || arg.includes('jest') || arg.includes('--test') || arg.includes('test:'));
336
-
337
- // Set exit code to 1 if any tasks failed AND we're not in test environment
338
- if (tasksFailed > 0 && !isTestEnvironment) {
331
+ commandArgs.some(arg => {
332
+ const lowerArg = arg.toLowerCase();
333
+ return lowerArg.includes('vitest') ||
334
+ lowerArg.includes('jest') ||
335
+ lowerArg === '--test' ||
336
+ lowerArg.startsWith('test:');
337
+ });
338
+
339
+ const ignoreFailuresMode = globalThis._bunoshIgnoreFailuresMode || false;
340
+
341
+ if (process.env.BUNOSH_DEBUG) {
342
+ console.log('\n[DEBUG] Exit handler:');
343
+ console.log(' tasksFailed:', tasksFailed);
344
+ console.log(' isTestEnvironment:', isTestEnvironment);
345
+ console.log(' ignoreFailuresMode:', ignoreFailuresMode);
346
+ console.log(' NODE_ENV:', process.env.NODE_ENV);
347
+ console.log(' commandArgs:', commandArgs);
348
+ console.log(' full process.argv:', process.argv);
349
+ if (isTestEnvironment) {
350
+ const matchingArg = commandArgs.find(arg => {
351
+ const lowerArg = arg.toLowerCase();
352
+ return lowerArg.includes('vitest') ||
353
+ lowerArg.includes('jest') ||
354
+ lowerArg === '--test' ||
355
+ lowerArg.startsWith('test:');
356
+ });
357
+ console.log(' Matched command arg:', matchingArg);
358
+ }
359
+ }
360
+
361
+ if (tasksFailed > 0 && !isTestEnvironment && !ignoreFailuresMode) {
339
362
  process.exitCode = 1;
340
363
  }
341
-
342
- const finalExitCode = (tasksFailed > 0 && !isTestEnvironment) ? 1 : code;
364
+
365
+ const finalExitCode = (tasksFailed > 0 && !isTestEnvironment && !ignoreFailuresMode) ? 1 : code;
343
366
  const success = finalExitCode === 0;
344
367
 
345
- // Debug: Check if this handler has already run
346
368
  if (globalThis._bunoshExitHandlerCalled) {
347
369
  console.log('\n[DEBUG] Exit handler already called, skipping duplicate');
348
370
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunosh",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "type": "module",
5
5
  "module": "index.js",
6
6
  "bin": {
@@ -56,9 +56,8 @@ export class ConsoleFormatter extends BaseFormatter {
56
56
  let line = leftContent + padding + rightContent;
57
57
 
58
58
  if (icon.trim()) {
59
- // Apply underline to the task name/type only, not the status in parentheses
60
- const underlineContent = taskTypeFormatted + taskNameFormatted + padding;
61
- line = icon + ' ' + chalk.underline(underlineContent) + rightContent;
59
+ const underlineContent = taskTypeFormatted + taskNameFormatted;
60
+ line = icon + ' ' + chalk.underline(underlineContent) + padding + rightContent;
62
61
  }
63
62
 
64
63
  let result = line;
@@ -74,7 +73,40 @@ export class ConsoleFormatter extends BaseFormatter {
74
73
 
75
74
  formatOutput(line, isError = false) {
76
75
  if (!line.trim()) return '';
77
- return isError ? chalk.red(line) : line;
76
+
77
+ const indent = ' ';
78
+ const terminalWidth = process.stdout.columns || 100;
79
+ const maxLineWidth = terminalWidth - indent.length;
80
+
81
+ let formattedLine = line;
82
+ const plainLine = this._stripAnsi(line);
83
+
84
+ if (plainLine.length > maxLineWidth) {
85
+ const truncateAt = maxLineWidth - 3;
86
+ let charCount = 0;
87
+ let truncatedLine = '';
88
+
89
+ for (let i = 0; i < line.length; i++) {
90
+ if (line[i] === '\u001b') {
91
+ let j = i;
92
+ while (j < line.length && line[j] !== 'm') j++;
93
+ truncatedLine += line.substring(i, j + 1);
94
+ i = j;
95
+ continue;
96
+ }
97
+
98
+ if (charCount >= truncateAt) break;
99
+
100
+ truncatedLine += line[i];
101
+ charCount++;
102
+ }
103
+
104
+ formattedLine = truncatedLine + '...';
105
+ }
106
+
107
+ formattedLine = indent + formattedLine;
108
+
109
+ return isError ? chalk.red(formattedLine) : formattedLine;
78
110
  }
79
111
 
80
112
  _stripAnsi(str) {
package/src/task.js CHANGED
@@ -28,15 +28,23 @@ export function ignoreFail(enable = true) {
28
28
  }
29
29
 
30
30
  // Global failure mode control
31
- // true = stop on failures (exit with code 1), false = continue on failures
32
31
  let stopOnFailuresMode = false;
32
+ let ignoreFailuresMode = false;
33
33
 
34
34
  export function ignoreFailures() {
35
35
  stopOnFailuresMode = false;
36
+ ignoreFailuresMode = true;
37
+ globalThis._bunoshIgnoreFailuresMode = true;
36
38
  }
37
39
 
38
40
  export function stopOnFailures() {
39
41
  stopOnFailuresMode = true;
42
+ ignoreFailuresMode = false;
43
+ globalThis._bunoshIgnoreFailuresMode = false;
44
+ }
45
+
46
+ export function getIgnoreFailuresMode() {
47
+ return ignoreFailuresMode;
40
48
  }
41
49
 
42
50
  export function silence() {
@@ -110,7 +118,7 @@ export class TaskInfo {
110
118
  }
111
119
  }
112
120
 
113
- export async function tryTask(name, fn, isSilent = false) {
121
+ export async function tryTask(name, fn, isSilent = true) {
114
122
  if (!fn) {
115
123
  fn = name;
116
124
  name = fn.toString().slice(0, 50).replace(/\s+/g, ' ').trim();
@@ -198,9 +206,16 @@ export async function task(name, fn, isSilent = false) {
198
206
  runningTasks.delete(taskInfo.id);
199
207
 
200
208
  // Don't exit during testing
209
+ const commandArgs = process.argv.slice(2);
201
210
  const isTestEnvironment = process.env.NODE_ENV === 'test' ||
202
211
  typeof Bun?.jest !== 'undefined' ||
203
- process.argv.some(arg => arg.includes('vitest') || arg.includes('jest') || arg.includes('--test') || arg.includes('test:'));
212
+ commandArgs.some(arg => {
213
+ const lowerArg = arg.toLowerCase();
214
+ return lowerArg.includes('vitest') ||
215
+ lowerArg.includes('jest') ||
216
+ lowerArg === '--test' ||
217
+ lowerArg.startsWith('test:');
218
+ });
204
219
 
205
220
  // Exit immediately if stopOnFailures mode is enabled
206
221
  if (stopOnFailuresMode && !isTestEnvironment) {