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