@mrpatronz/nexusflow 2.5.0 → 2.5.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.
Files changed (40) hide show
  1. package/dist/commands/finish.d.ts.map +1 -1
  2. package/dist/commands/finish.js +7 -0
  3. package/dist/commands/finish.js.map +1 -1
  4. package/dist/commands/list.d.ts +3 -1
  5. package/dist/commands/list.d.ts.map +1 -1
  6. package/dist/commands/list.js +5 -1
  7. package/dist/commands/list.js.map +1 -1
  8. package/dist/commands/schedule.d.ts.map +1 -1
  9. package/dist/commands/schedule.js +6 -0
  10. package/dist/commands/schedule.js.map +1 -1
  11. package/dist/commands/ui.d.ts +1 -0
  12. package/dist/commands/ui.d.ts.map +1 -1
  13. package/dist/commands/ui.js +1 -1
  14. package/dist/commands/ui.js.map +1 -1
  15. package/dist/core/doctor.d.ts.map +1 -1
  16. package/dist/core/doctor.js +24 -0
  17. package/dist/core/doctor.js.map +1 -1
  18. package/dist/gui/assets/{AgentsPage-BNHR9qux.js → AgentsPage-gHbRof2y.js} +1 -1
  19. package/dist/gui/assets/{DashboardPage-BScH3Cxp.js → DashboardPage-CkXIpoFX.js} +1 -1
  20. package/dist/gui/assets/{GettingStartedPage-AWlI-176.js → GettingStartedPage-DRL_nbb6.js} +1 -1
  21. package/dist/gui/assets/{ProjectsPage-Bojj7gxI.js → ProjectsPage-C8XQ9dRr.js} +1 -1
  22. package/dist/gui/assets/{ScaffoldRepoInline-DJMbeHVM.js → ScaffoldRepoInline-ys4l8ho4.js} +1 -1
  23. package/dist/gui/assets/{SettingsPage-BAdXO8X0.js → SettingsPage-CRVDsRwi.js} +1 -1
  24. package/dist/gui/assets/{SkillsPage-CF6Db1FA.js → SkillsPage-BfQu6PT3.js} +1 -1
  25. package/dist/gui/assets/StartWorkPage-BNzeMcFc.js +1 -0
  26. package/dist/gui/assets/{StrategiesPage-nGbR9TAc.js → StrategiesPage-Bh6x7zhl.js} +1 -1
  27. package/dist/gui/assets/{WorkspacesPage-7XRtVJNF.js → WorkspacesPage-DYQt7CQ8.js} +1 -1
  28. package/dist/gui/assets/{alert-dialog-67uzXJi4.js → alert-dialog-D7jNzvYQ.js} +1 -1
  29. package/dist/gui/assets/{index-V1dDX1O_.js → index-C6qbn9Zq.js} +3 -3
  30. package/dist/gui/assets/{vsc-Di05BTNr.js → vsc-CvlJSs-5.js} +1 -1
  31. package/dist/gui/index.html +1 -1
  32. package/dist/index.js +115 -502
  33. package/dist/index.js.map +1 -1
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/server.js +48 -11
  36. package/dist/server.js.map +1 -1
  37. package/dist/server.test.js +1 -0
  38. package/dist/server.test.js.map +1 -1
  39. package/package.json +3 -3
  40. package/dist/gui/assets/StartWorkPage-BQid3uIw.js +0 -1
package/dist/index.js CHANGED
@@ -54,136 +54,83 @@ program.hook('preAction', (thisCommand) => {
54
54
  process.env.NEXUSFLOW_DEBUG = '1';
55
55
  }
56
56
  });
57
+ function isDebugMode() {
58
+ return process.argv.includes('--debug') || Boolean(process.env.NEXUSFLOW_DEBUG);
59
+ }
60
+ /**
61
+ * Wraps a command action: clean exit on prompt cancellation (Ctrl+C inside an
62
+ * inquirer prompt), clean red error message by default, and exit code 1.
63
+ * When --debug is provided, prints the full error stack.
64
+ */
65
+ function runAction(fn) {
66
+ return async (...args) => {
67
+ try {
68
+ await fn(...args);
69
+ }
70
+ catch (error) {
71
+ if (error instanceof Error && error.message.includes('User force closed')) {
72
+ console.log('\nCancelled.');
73
+ process.exit(0);
74
+ }
75
+ const message = error instanceof Error ? error.message : String(error);
76
+ console.error(chalk.red(`\n✖ ${message}`));
77
+ if (isDebugMode() && error instanceof Error && error.stack) {
78
+ console.error(chalk.dim(error.stack));
79
+ }
80
+ else if (!isDebugMode()) {
81
+ console.error(chalk.dim(' (Run with --debug for details)\n'));
82
+ }
83
+ process.exit(1);
84
+ }
85
+ };
86
+ }
57
87
  program
58
88
  .command('create')
59
89
  .description('Create a new feature workspace — pick repos, pick AI assistants, generate context')
60
- .action(async () => {
61
- try {
62
- await createCommand();
63
- }
64
- catch (error) {
65
- if (error instanceof Error && error.message.includes('User force closed')) {
66
- console.log('\nCancelled.');
67
- process.exit(0);
68
- }
69
- console.error(error);
70
- process.exit(1);
71
- }
72
- });
90
+ .action(runAction(createCommand));
73
91
  program
74
92
  .command('list')
75
93
  .alias('ls')
76
94
  .description('List all existing workspaces')
77
- .action(async () => {
78
- try {
79
- await listCommand();
80
- }
81
- catch (error) {
82
- console.error(error);
83
- process.exit(1);
84
- }
85
- });
95
+ .option('--json', 'Output as JSON')
96
+ .action(runAction(listCommand));
86
97
  program
87
98
  .command('open')
88
99
  .description('Re-open an existing workspace in an editor')
89
- .action(async () => {
90
- try {
91
- await openCommand();
92
- }
93
- catch (error) {
94
- if (error instanceof Error && error.message.includes('User force closed')) {
95
- console.log('\nCancelled.');
96
- process.exit(0);
97
- }
98
- console.error(error);
99
- process.exit(1);
100
- }
101
- });
100
+ .action(runAction(openCommand));
102
101
  program
103
102
  .command('init')
104
103
  .description('Initialize NexusFlow configuration')
105
- .action(async () => {
106
- try {
107
- await initCommand();
108
- }
109
- catch (error) {
110
- if (error instanceof Error && error.message.includes('User force closed')) {
111
- console.log('\nCancelled.');
112
- process.exit(0);
113
- }
114
- console.error(error);
115
- process.exit(1);
116
- }
117
- });
104
+ .action(runAction(initCommand));
118
105
  program
119
106
  .command('start')
120
107
  .description('Start all services in a workspace')
121
108
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
122
- .action(async (workspace) => {
123
- try {
124
- await startCommand(workspace);
125
- }
126
- catch (error) {
127
- if (error instanceof Error && error.message.includes('User force closed')) {
128
- console.log('\nCancelled.');
129
- process.exit(0);
130
- }
131
- console.error(error);
132
- process.exit(1);
133
- }
134
- });
109
+ .action(runAction(async (workspace) => {
110
+ await startCommand(workspace);
111
+ }));
135
112
  program
136
113
  .command('stop')
137
114
  .description('Stop all running services in a workspace')
138
115
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
139
- .action(async (workspace) => {
140
- try {
141
- await stopCommand(workspace);
142
- }
143
- catch (error) {
144
- if (error instanceof Error && error.message.includes('User force closed')) {
145
- console.log('\nCancelled.');
146
- process.exit(0);
147
- }
148
- console.error(error);
149
- process.exit(1);
150
- }
151
- });
116
+ .action(runAction(async (workspace) => {
117
+ await stopCommand(workspace);
118
+ }));
152
119
  program
153
120
  .command('logs')
154
121
  .description('Show logs from running services')
155
122
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
156
123
  .option('-n, --lines <number>', 'Number of lines per service', '30')
157
- .action(async (workspace, options) => {
158
- try {
159
- await logsCommand(workspace, parseInt(options.lines, 10));
160
- }
161
- catch (error) {
162
- if (error instanceof Error && error.message.includes('User force closed')) {
163
- console.log('\nCancelled.');
164
- process.exit(0);
165
- }
166
- console.error(error);
167
- process.exit(1);
168
- }
169
- });
124
+ .action(runAction(async (workspace, options) => {
125
+ await logsCommand(workspace, parseInt(options.lines, 10));
126
+ }));
170
127
  program
171
128
  .command('status')
172
129
  .description('Show status of running services')
173
130
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
174
- .action(async (workspace) => {
175
- try {
176
- await statusCommand(workspace);
177
- }
178
- catch (error) {
179
- if (error instanceof Error && error.message.includes('User force closed')) {
180
- console.log('\nCancelled.');
181
- process.exit(0);
182
- }
183
- console.error(error);
184
- process.exit(1);
185
- }
186
- });
131
+ .action(runAction(async (workspace) => {
132
+ await statusCommand(workspace);
133
+ }));
187
134
  program
188
135
  .command('ui')
189
136
  .description('Start the NexusFlow dashboard server (the backend the desktop app embeds)')
@@ -192,15 +139,9 @@ program
192
139
  .option('--open', 'Also open the dashboard in your default browser')
193
140
  .option('--server-only', '(deprecated — server-only is now the default)')
194
141
  .option('--strict-port', 'Fail instead of auto-incrementing when the port is in use')
195
- .action(async (options) => {
196
- try {
197
- await uiCommand(options);
198
- }
199
- catch (error) {
200
- console.error(error);
201
- process.exit(1);
202
- }
203
- });
142
+ .action(runAction(async (options) => {
143
+ await uiCommand(options);
144
+ }));
204
145
  program
205
146
  .command('dashboard')
206
147
  .alias('dash')
@@ -271,57 +212,27 @@ program
271
212
  .description('Display a unified summary of changes across all repositories (including unpushed commits)')
272
213
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
273
214
  .option('-r, --repo <repos...>', 'Only show the given repositories (by name)')
274
- .action(async (workspace, options) => {
275
- try {
276
- await diffCommand(workspace, options);
277
- }
278
- catch (error) {
279
- if (error instanceof Error && error.message.includes('User force closed')) {
280
- console.log('\nCancelled.');
281
- process.exit(0);
282
- }
283
- console.error(error);
284
- process.exit(1);
285
- }
286
- });
215
+ .action(runAction(async (workspace, options) => {
216
+ await diffCommand(workspace, options);
217
+ }));
287
218
  // Pack command removed.
288
219
  program
289
220
  .command('remove')
290
221
  .alias('rm')
291
222
  .description('Delete a workspace and cleanly prune/remove its git worktrees')
292
223
  .argument('[workspace]', 'Workspace name or path')
293
- .action(async (workspace) => {
294
- try {
295
- await removeCommand(workspace);
296
- }
297
- catch (error) {
298
- if (error instanceof Error && error.message.includes('User force closed')) {
299
- console.log('\nCancelled.');
300
- process.exit(0);
301
- }
302
- console.error(error);
303
- process.exit(1);
304
- }
305
- });
224
+ .action(runAction(async (workspace) => {
225
+ await removeCommand(workspace);
226
+ }));
306
227
  program
307
228
  .command('add-repo')
308
229
  .alias('add')
309
230
  .description('Add a repository to an existing workspace and update configurations')
310
231
  .argument('[repo-path]', 'Path to the repository to add')
311
232
  .argument('[workspace]', 'Workspace name or path')
312
- .action(async (repoPath, workspace) => {
313
- try {
314
- await addRepoCommand(repoPath, workspace);
315
- }
316
- catch (error) {
317
- if (error instanceof Error && error.message.includes('User force closed')) {
318
- console.log('\nCancelled.');
319
- process.exit(0);
320
- }
321
- console.error(error);
322
- process.exit(1);
323
- }
324
- });
233
+ .action(runAction(async (repoPath, workspace) => {
234
+ await addRepoCommand(repoPath, workspace);
235
+ }));
325
236
  program
326
237
  .command('isolate')
327
238
  .description('Dynamically isolate a repository in an in-place workspace into a dedicated worktree on demand')
@@ -330,60 +241,32 @@ program
330
241
  .option('-b, --branch <branch>', 'Target feature branch name')
331
242
  .option('--base <base>', 'Base branch to branch off')
332
243
  .option('-w, --workspace <workspace>', 'Workspace name or path')
333
- .action(async (repo, branchArg, options) => {
334
- try {
335
- await isolateCommand(repo, branchArg, options);
336
- }
337
- catch (error) {
338
- if (error instanceof Error && error.message.includes('User force closed')) {
339
- console.log('\nCancelled.');
340
- process.exit(0);
341
- }
342
- console.error(error);
343
- process.exit(1);
344
- }
345
- });
244
+ .action(runAction(async (repo, branchArg, options) => {
245
+ await isolateCommand(repo, branchArg, options);
246
+ }));
346
247
  program
347
248
  .command('handoff')
348
249
  .description('Generate a compact handoff bundle (nexusflow-handoff.md) for session resumption')
349
250
  .argument('[workspace]', 'Path to the workspace')
350
- .action(async (workspace) => {
351
- try {
352
- await handoffCommand(workspace);
353
- }
354
- catch (error) {
355
- console.error(error);
356
- process.exit(1);
357
- }
358
- });
251
+ .action(runAction(async (workspace) => {
252
+ await handoffCommand(workspace);
253
+ }));
359
254
  program
360
255
  .command('refresh')
361
256
  .description('Refresh workspace context, plan and handoff files (re-analyzes only changed repos)')
362
257
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
363
258
  .option('-f, --force', 'Ignore the analysis cache and re-analyze every repository')
364
259
  .option('-s, --strategy <id>', 'Update the teamwork strategy (use template ID, or "auto" for AI suggestion)')
365
- .action(async (workspace, options) => {
366
- try {
367
- await refreshCommand(options, workspace);
368
- }
369
- catch (error) {
370
- console.error(error);
371
- process.exit(1);
372
- }
373
- });
260
+ .action(runAction(async (workspace, options) => {
261
+ await refreshCommand(options, workspace);
262
+ }));
374
263
  program
375
264
  .command('doctor')
376
265
  .description('Run diagnostics to verify workspace health and check for local loop issues')
377
266
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
378
- .action(async (workspace) => {
379
- try {
380
- await doctorCommand(workspace);
381
- }
382
- catch (error) {
383
- console.error(error);
384
- process.exit(1);
385
- }
386
- });
267
+ .action(runAction(async (workspace) => {
268
+ await doctorCommand(workspace);
269
+ }));
387
270
  program
388
271
  .command('finish')
389
272
  .description('Finish a feature — commit & push everything, open PRs, promote learnings, optionally remove the workspace')
@@ -394,19 +277,9 @@ program
394
277
  .option('--cleanup', 'Remove the workspace after everything is confirmed pushed (still asks for confirmation)')
395
278
  .option('-y, --yes', 'Accept defaults for non-destructive prompts')
396
279
  .option('--dry-run', 'Show what finish would do without changing anything')
397
- .action(async (workspace, options) => {
398
- try {
399
- await finishCommand(workspace, options);
400
- }
401
- catch (error) {
402
- if (error instanceof Error && error.message.includes('User force closed')) {
403
- console.log('\nCancelled.');
404
- process.exit(0);
405
- }
406
- console.error(error);
407
- process.exit(1);
408
- }
409
- });
280
+ .action(runAction(async (workspace, options) => {
281
+ await finishCommand(workspace, options);
282
+ }));
410
283
  // Knowledge command group
411
284
  const knowledgeCmd = program
412
285
  .command('knowledge')
@@ -420,38 +293,18 @@ knowledgeCmd
420
293
  .requiredOption('-m, --message <msg>', 'The learning to record, as a rule plus its reason (max 300 chars)')
421
294
  .option('--title <title>', 'Short title (used for decision headings)')
422
295
  .option('-r, --repo <repo>', "Write to this repo's persistent base knowledge instead")
423
- .action(async (workspace, options) => {
424
- try {
425
- await knowledgeAddCommand(workspace, options);
426
- }
427
- catch (error) {
428
- if (error instanceof Error && error.message.includes('User force closed')) {
429
- console.log('\nCancelled.');
430
- process.exit(0);
431
- }
432
- console.error(error);
433
- process.exit(1);
434
- }
435
- });
296
+ .action(runAction(async (workspace, options) => {
297
+ await knowledgeAddCommand(workspace, options);
298
+ }));
436
299
  knowledgeCmd
437
300
  .command('show')
438
301
  .description("Print the workspace knowledge file (or a repo's base knowledge)")
439
302
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
440
303
  .option('-s, --section <name>', 'Only show one section')
441
304
  .option('-r, --repo <repo>', "Show the repo's base knowledge file instead")
442
- .action(async (workspace, options) => {
443
- try {
444
- await knowledgeShowCommand(workspace, options);
445
- }
446
- catch (error) {
447
- if (error instanceof Error && error.message.includes('User force closed')) {
448
- console.log('\nCancelled.');
449
- process.exit(0);
450
- }
451
- console.error(error);
452
- process.exit(1);
453
- }
454
- });
305
+ .action(runAction(async (workspace, options) => {
306
+ await knowledgeShowCommand(workspace, options);
307
+ }));
455
308
  knowledgeCmd
456
309
  .command('promote')
457
310
  .description('Copy workspace learnings into per-repo base knowledge so they persist across features')
@@ -461,39 +314,9 @@ knowledgeCmd
461
314
  .option('-m, --message <msg>', 'Promote this text directly (non-interactive)')
462
315
  .option('--move', 'Remove the entry from the workspace file after promoting (default: copy)')
463
316
  .option('--all', 'Promote all decisions, gotchas and assumptions without prompting')
464
- .action(async (workspace, options) => {
465
- try {
466
- await knowledgePromoteCommand(workspace, options);
467
- }
468
- catch (error) {
469
- if (error instanceof Error && error.message.includes('User force closed')) {
470
- console.log('\nCancelled.');
471
- process.exit(0);
472
- }
473
- console.error(error);
474
- process.exit(1);
475
- }
476
- });
477
- /**
478
- * Wraps a command action: clean exit on prompt cancellation (Ctrl+C inside an
479
- * inquirer prompt), exit code 1 with the error otherwise. New command
480
- * registrations should use this instead of copy-pasting the try/catch.
481
- */
482
- function runAction(fn) {
483
- return async (...args) => {
484
- try {
485
- await fn(...args);
486
- }
487
- catch (error) {
488
- if (error instanceof Error && error.message.includes('User force closed')) {
489
- console.log('\nCancelled.');
490
- process.exit(0);
491
- }
492
- console.error(error);
493
- process.exit(1);
494
- }
495
- };
496
- }
317
+ .action(runAction(async (workspace, options) => {
318
+ await knowledgePromoteCommand(workspace, options);
319
+ }));
497
320
  // Project command group
498
321
  const projectCmd = program
499
322
  .command('project')
@@ -532,216 +355,76 @@ strategyCmd
532
355
  .command('list')
533
356
  .alias('ls')
534
357
  .description('List all available strategy templates (built-in and custom)')
535
- .action(async () => {
536
- try {
537
- await strategyListCommand();
538
- }
539
- catch (error) {
540
- if (error instanceof Error && error.message.includes('User force closed')) {
541
- console.log('\nCancelled.');
542
- process.exit(0);
543
- }
544
- console.error(error);
545
- process.exit(1);
546
- }
547
- });
358
+ .action(runAction(strategyListCommand));
548
359
  strategyCmd
549
360
  .command('create')
550
361
  .description('Create a new custom strategy template')
551
362
  .option('-n, --name <name>', 'Strategy name')
552
363
  .option('-f, --file <path>', 'Load content from a .md or .txt file')
553
- .action(async (options) => {
554
- try {
555
- await strategyCreateCommand(options);
556
- }
557
- catch (error) {
558
- if (error instanceof Error && error.message.includes('User force closed')) {
559
- console.log('\nCancelled.');
560
- process.exit(0);
561
- }
562
- console.error(error);
563
- process.exit(1);
564
- }
565
- });
364
+ .action(runAction(strategyCreateCommand));
566
365
  strategyCmd
567
366
  .command('edit')
568
367
  .description('Edit an existing custom strategy template')
569
368
  .option('--id <id>', 'Template ID to edit (skips the selection prompt)')
570
- .action(async (options) => {
571
- try {
572
- await strategyEditCommand(options);
573
- }
574
- catch (error) {
575
- if (error instanceof Error && error.message.includes('User force closed')) {
576
- console.log('\nCancelled.');
577
- process.exit(0);
578
- }
579
- console.error(error);
580
- process.exit(1);
581
- }
582
- });
369
+ .action(runAction(strategyEditCommand));
583
370
  strategyCmd
584
371
  .command('delete')
585
372
  .alias('rm')
586
373
  .description('Delete a custom strategy template')
587
374
  .option('--id <id>', 'Template ID to delete (skips the selection prompt)')
588
375
  .option('-y, --yes', 'Skip confirmation prompt')
589
- .action(async (options) => {
590
- try {
591
- await strategyDeleteCommand(options);
592
- }
593
- catch (error) {
594
- if (error instanceof Error && error.message.includes('User force closed')) {
595
- console.log('\nCancelled.');
596
- process.exit(0);
597
- }
598
- console.error(error);
599
- process.exit(1);
600
- }
601
- });
376
+ .action(runAction(strategyDeleteCommand));
602
377
  strategyCmd
603
378
  .command('show')
604
379
  .description('Display the full content of a strategy template')
605
380
  .option('--id <id>', 'Template ID to show (skips the selection prompt)')
606
- .action(async (options) => {
607
- try {
608
- await strategyShowCommand(options);
609
- }
610
- catch (error) {
611
- if (error instanceof Error && error.message.includes('User force closed')) {
612
- console.log('\nCancelled.');
613
- process.exit(0);
614
- }
615
- console.error(error);
616
- process.exit(1);
617
- }
618
- });
381
+ .action(runAction(strategyShowCommand));
619
382
  program
620
383
  .command('desktop')
621
384
  .description('Launch the NexusFlow desktop application')
622
- .action(async () => {
623
- try {
624
- await desktopCommand();
625
- }
626
- catch (error) {
627
- console.error(error);
628
- process.exit(1);
629
- }
630
- });
385
+ .action(runAction(desktopCommand));
631
386
  // Config command group
632
387
  const configCmd = program.command('config').description('View and update NexusFlow configuration');
633
388
  configCmd
634
389
  .command('show')
635
390
  .description('Display the current configuration')
636
- .action(async () => {
637
- try {
638
- await configShowCommand();
639
- }
640
- catch (error) {
641
- console.error(error);
642
- process.exit(1);
643
- }
644
- });
391
+ .action(runAction(configShowCommand));
645
392
  configCmd
646
393
  .command('get')
647
394
  .description('Get a specific configuration key')
648
395
  .argument('<key>', 'Configuration key to read')
649
- .action(async (key) => {
650
- try {
651
- await configGetCommand(key);
652
- }
653
- catch (error) {
654
- console.error(error);
655
- process.exit(1);
656
- }
657
- });
396
+ .action(runAction(configGetCommand));
658
397
  configCmd
659
398
  .command('set')
660
399
  .description('Set a configuration key to a value')
661
400
  .argument('<key>', 'Configuration key to set')
662
401
  .argument('<value>', 'Value to assign')
663
- .action(async (key, value) => {
664
- try {
665
- await configSetCommand(key, value);
666
- }
667
- catch (error) {
668
- console.error(error);
669
- process.exit(1);
670
- }
671
- });
402
+ .action(runAction(configSetCommand));
672
403
  // Default action when 'nexusflow config' is run without a subcommand
673
- configCmd.action(async () => {
674
- try {
675
- await configShowCommand();
676
- }
677
- catch (error) {
678
- console.error(error);
679
- process.exit(1);
680
- }
681
- });
404
+ configCmd.action(runAction(configShowCommand));
682
405
  // Adapter command group
683
406
  const adapterCmd = program.command('adapter').description('Manage storage adapters — list, switch, configure, or create new ones');
684
407
  adapterCmd
685
408
  .command('list')
686
409
  .description('List all available storage adapters')
687
- .action(async () => {
688
- try {
689
- await adapterListCommand();
690
- }
691
- catch (error) {
692
- console.error(error);
693
- process.exit(1);
694
- }
695
- });
410
+ .action(runAction(adapterListCommand));
696
411
  adapterCmd
697
412
  .command('use')
698
413
  .description('Switch to a different storage adapter (prompts for config if needed)')
699
414
  .argument('<name>', 'Adapter name to activate')
700
- .action(async (name) => {
701
- try {
702
- await adapterUseCommand(name);
703
- }
704
- catch (error) {
705
- console.error(error);
706
- process.exit(1);
707
- }
708
- });
415
+ .action(runAction(adapterUseCommand));
709
416
  adapterCmd
710
417
  .command('info')
711
418
  .description('Show detailed information about an adapter')
712
419
  .argument('<name>', 'Adapter name to inspect')
713
- .action(async (name) => {
714
- try {
715
- await adapterInfoCommand(name);
716
- }
717
- catch (error) {
718
- console.error(error);
719
- process.exit(1);
720
- }
721
- });
420
+ .action(runAction(adapterInfoCommand));
722
421
  adapterCmd
723
422
  .command('init')
724
423
  .description('Scaffold a new adapter plugin project')
725
424
  .argument('<name>', 'Name for the new adapter')
726
- .action(async (name) => {
727
- try {
728
- await adapterInitCommand(name);
729
- }
730
- catch (error) {
731
- console.error(error);
732
- process.exit(1);
733
- }
734
- });
425
+ .action(runAction(adapterInitCommand));
735
426
  // Default action when 'nexusflow adapter' is run without a subcommand
736
- adapterCmd.action(async () => {
737
- try {
738
- await adapterListCommand();
739
- }
740
- catch (error) {
741
- console.error(error);
742
- process.exit(1);
743
- }
744
- });
427
+ adapterCmd.action(runAction(adapterListCommand));
745
428
  // Schedule command group
746
429
  const scheduleCmd = program
747
430
  .command('schedule')
@@ -752,124 +435,54 @@ scheduleCmd
752
435
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
753
436
  .option('-t, --task <task>', 'Job to run: "sync" or "refresh"', 'sync')
754
437
  .requiredOption('-e, --every <interval>', 'How often to run, e.g. 30m, 2h, 1d')
755
- .action(async (workspace, options) => {
756
- try {
757
- await scheduleAddCommand(workspace, options);
758
- }
759
- catch (error) {
760
- if (error instanceof Error && error.message.includes('User force closed')) {
761
- console.log('\nCancelled.');
762
- process.exit(0);
763
- }
764
- console.error(error);
765
- process.exit(1);
766
- }
767
- });
438
+ .action(runAction(async (workspace, options) => {
439
+ await scheduleAddCommand(workspace, options);
440
+ }));
768
441
  scheduleCmd
769
442
  .command('list')
770
443
  .alias('ls')
771
444
  .description('List all scheduled jobs')
772
- .action(async () => {
773
- try {
774
- await scheduleListCommand();
775
- }
776
- catch (error) {
777
- console.error(error);
778
- process.exit(1);
779
- }
780
- });
445
+ .action(runAction(scheduleListCommand));
781
446
  scheduleCmd
782
447
  .command('remove')
783
448
  .alias('rm')
784
449
  .description('Remove a scheduled job')
785
450
  .argument('<id>', 'Job id (see "nexusflow schedule list")')
786
- .action(async (id) => {
787
- try {
788
- await scheduleRemoveCommand(id);
789
- }
790
- catch (error) {
791
- console.error(error);
792
- process.exit(1);
793
- }
794
- });
451
+ .action(runAction(scheduleRemoveCommand));
795
452
  scheduleCmd
796
453
  .command('enable')
797
454
  .description('Enable a scheduled job')
798
455
  .argument('<id>', 'Job id (see "nexusflow schedule list")')
799
- .action(async (id) => {
800
- try {
801
- await scheduleToggleCommand(id, true);
802
- }
803
- catch (error) {
804
- console.error(error);
805
- process.exit(1);
806
- }
807
- });
456
+ .action(runAction(async (id) => {
457
+ await scheduleToggleCommand(id, true);
458
+ }));
808
459
  scheduleCmd
809
460
  .command('disable')
810
461
  .description('Disable a scheduled job without removing it')
811
462
  .argument('<id>', 'Job id (see "nexusflow schedule list")')
812
- .action(async (id) => {
813
- try {
814
- await scheduleToggleCommand(id, false);
815
- }
816
- catch (error) {
817
- console.error(error);
818
- process.exit(1);
819
- }
820
- });
463
+ .action(runAction(async (id) => {
464
+ await scheduleToggleCommand(id, false);
465
+ }));
821
466
  scheduleCmd
822
467
  .command('run')
823
468
  .description('Run a scheduled job immediately')
824
469
  .argument('<id>', 'Job id (see "nexusflow schedule list")')
825
- .action(async (id) => {
826
- try {
827
- await scheduleRunCommand(id);
828
- }
829
- catch (error) {
830
- console.error(error);
831
- process.exit(1);
832
- }
833
- });
470
+ .action(runAction(scheduleRunCommand));
834
471
  // Default action when 'nexusflow schedule' is run without a subcommand
835
- scheduleCmd.action(async () => {
836
- try {
837
- await scheduleListCommand();
838
- }
839
- catch (error) {
840
- console.error(error);
841
- process.exit(1);
842
- }
843
- });
472
+ scheduleCmd.action(runAction(scheduleListCommand));
844
473
  const mcp = program.command('mcp').description('Manage the NexusFlow MCP Server for AI assistants');
845
474
  mcp
846
475
  .command('run')
847
476
  .description('Start the NexusFlow MCP Server (typically called automatically by AI assistants)')
848
477
  .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
849
- .action(async (workspace) => {
850
- try {
851
- await mcpRunCommand(workspace);
852
- }
853
- catch (error) {
854
- console.error(error);
855
- process.exit(1);
856
- }
857
- });
478
+ .action(runAction(mcpRunCommand));
858
479
  mcp
859
480
  .command('setup')
860
481
  .description('Automatically configure your AI environments (Claude Desktop, Cursor, VS Code) to use the NexusFlow MCP Server')
861
- .action(async () => {
862
- try {
863
- await mcpSetupCommand();
864
- }
865
- catch (error) {
866
- console.error(error);
867
- process.exit(1);
868
- }
869
- });
482
+ .action(runAction(mcpSetupCommand));
870
483
  program.hook('postAction', async (thisCommand, actionCommand) => {
871
- // Skip update check for MCP run to prevent contaminating stdout stream
872
- if (actionCommand.name() === 'run' && actionCommand.parent?.name() === 'mcp') {
484
+ // Skip update check for non-TTY streams and MCP runs to prevent contaminating stdout stream
485
+ if (!process.stdout.isTTY || (actionCommand.name() === 'run' && actionCommand.parent?.name() === 'mcp')) {
873
486
  return;
874
487
  }
875
488
  try {