@mrpatronz/nexusflow 0.2.13 → 0.2.17

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 (186) hide show
  1. package/.github/dependabot.yml +52 -52
  2. package/.github/workflows/dependabot-merge.yml +53 -0
  3. package/.github/workflows/release.yml +112 -100
  4. package/.vscode/launch.json +17 -17
  5. package/.vscode/tasks.json +17 -17
  6. package/GETTING_STARTED.md +114 -109
  7. package/README.md +336 -311
  8. package/desktop/build-installer.js +228 -0
  9. package/desktop/build.js +118 -0
  10. package/desktop/installer/nexusflow.iss +35 -0
  11. package/desktop/neutralino.config.json +39 -0
  12. package/desktop/package-lock.json +1060 -0
  13. package/desktop/package.json +14 -0
  14. package/dist/commands/desktop.d.ts +12 -0
  15. package/dist/commands/desktop.d.ts.map +1 -0
  16. package/dist/commands/desktop.js +119 -0
  17. package/dist/commands/desktop.js.map +1 -0
  18. package/dist/commands/sync.d.ts.map +1 -1
  19. package/dist/commands/sync.js +1 -3
  20. package/dist/commands/sync.js.map +1 -1
  21. package/dist/commands/tui.d.ts +8 -0
  22. package/dist/commands/tui.d.ts.map +1 -0
  23. package/dist/commands/tui.js +386 -0
  24. package/dist/commands/tui.js.map +1 -0
  25. package/dist/commands/ui.d.ts +3 -1
  26. package/dist/commands/ui.d.ts.map +1 -1
  27. package/dist/commands/ui.js +76 -19
  28. package/dist/commands/ui.js.map +1 -1
  29. package/dist/generators/base.d.ts.map +1 -1
  30. package/dist/generators/base.js +78 -66
  31. package/dist/generators/base.js.map +1 -1
  32. package/dist/generators/codex.js +11 -11
  33. package/dist/generators/copilot.js +17 -17
  34. package/dist/generators/cursor.js +5 -5
  35. package/dist/generators/index.js +50 -50
  36. package/dist/generators/map-generator.js +14 -14
  37. package/dist/generators/skills-generator.js +28 -28
  38. package/dist/gui/app_logo.png +0 -0
  39. package/dist/gui/assets/index-Ct3qq-X4.js +25 -0
  40. package/dist/gui/assets/index-P_ZrgHXg.css +1 -0
  41. package/dist/gui/icons.svg +24 -24
  42. package/dist/gui/index.html +18 -18
  43. package/dist/index.js +43 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/server.d.ts.map +1 -1
  46. package/dist/server.js +182 -1
  47. package/dist/server.js.map +1 -1
  48. package/dist/server.test.js +78 -0
  49. package/dist/server.test.js.map +1 -1
  50. package/dist/types.d.ts +4 -0
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/utils/git.d.ts +16 -0
  53. package/dist/utils/git.d.ts.map +1 -1
  54. package/dist/utils/git.js +52 -0
  55. package/dist/utils/git.js.map +1 -1
  56. package/dist/utils/multi-git.d.ts +2 -0
  57. package/dist/utils/multi-git.d.ts.map +1 -1
  58. package/dist/utils/multi-git.js +5 -2
  59. package/dist/utils/multi-git.js.map +1 -1
  60. package/dist/utils/update-check.d.ts +3 -1
  61. package/dist/utils/update-check.d.ts.map +1 -1
  62. package/dist/utils/update-check.js +23 -6
  63. package/dist/utils/update-check.js.map +1 -1
  64. package/dist/utils/workflows.d.ts +11 -0
  65. package/dist/utils/workflows.d.ts.map +1 -0
  66. package/dist/utils/workflows.js +156 -0
  67. package/dist/utils/workflows.js.map +1 -0
  68. package/dist/utils/workflows.test.d.ts +2 -0
  69. package/dist/utils/workflows.test.d.ts.map +1 -0
  70. package/dist/utils/workflows.test.js +86 -0
  71. package/dist/utils/workflows.test.js.map +1 -0
  72. package/extension/media/icon.svg +1 -0
  73. package/extension/package-lock.json +2673 -2811
  74. package/extension/package.json +108 -70
  75. package/extension/src/extension.ts +631 -204
  76. package/extension/tsconfig.json +21 -19
  77. package/gui/README.md +73 -73
  78. package/gui/e2e/wizard.spec.ts +346 -327
  79. package/gui/eslint.config.js +26 -26
  80. package/gui/index.html +17 -17
  81. package/gui/package-lock.json +3079 -3118
  82. package/gui/package.json +36 -36
  83. package/gui/playwright.config.ts +41 -41
  84. package/gui/public/app_logo.png +0 -0
  85. package/gui/public/icons.svg +24 -24
  86. package/gui/src/App.css +1 -1
  87. package/gui/src/App.tsx +3407 -2722
  88. package/gui/src/assets/vite.svg +1 -1
  89. package/gui/src/features/changes/ChangesViewer.tsx +388 -388
  90. package/gui/src/features/knowledge/KnowledgeBase.tsx +84 -84
  91. package/gui/src/features/onboarding/OnboardingWizard.tsx +230 -230
  92. package/gui/src/features/plan/ImplementationPlan.tsx +32 -32
  93. package/gui/src/features/services/ServiceConsole.tsx +250 -250
  94. package/gui/src/features/sessions/SessionHistory.tsx +195 -195
  95. package/gui/src/features/workspace/WorkspaceList.tsx +666 -666
  96. package/gui/src/index.css +152 -110
  97. package/gui/src/main.tsx +94 -10
  98. package/gui/src/types.ts +62 -62
  99. package/gui/tsconfig.app.json +25 -25
  100. package/gui/tsconfig.json +7 -7
  101. package/gui/tsconfig.node.json +24 -24
  102. package/gui/vite.config.ts +12 -12
  103. package/package.json +56 -56
  104. package/resources/workflows/plan-implement-review.md +8 -0
  105. package/resources/workflows/research-verify.md +6 -0
  106. package/resources/workflows/solo-developer.md +3 -0
  107. package/scripts/simulate-workspaces.ts +55 -55
  108. package/src/analyzers/detect-apis.ts +290 -290
  109. package/src/analyzers/detect-deps.ts +315 -315
  110. package/src/analyzers/detect-existing.ts +74 -74
  111. package/src/analyzers/detect-ports.ts +110 -110
  112. package/src/analyzers/index.ts +97 -97
  113. package/src/analyzers/messaging-analyzer.ts +254 -254
  114. package/src/analyzers/readme-summarizer.ts +102 -102
  115. package/src/analyzers/run-analyzer.ts +269 -269
  116. package/src/analyzers/tech-stack.ts +283 -283
  117. package/src/commands/add-repo.ts +157 -157
  118. package/src/commands/commands.test.ts +155 -155
  119. package/src/commands/commit.ts +131 -131
  120. package/src/commands/create.ts +207 -207
  121. package/src/commands/desktop.ts +128 -0
  122. package/src/commands/diff.ts +125 -125
  123. package/src/commands/doctor.ts +357 -357
  124. package/src/commands/handoff.ts +266 -266
  125. package/src/commands/init.ts +134 -134
  126. package/src/commands/list.ts +46 -46
  127. package/src/commands/logs.ts +63 -63
  128. package/src/commands/mcp.ts +92 -92
  129. package/src/commands/open.ts +129 -129
  130. package/src/commands/pack.ts +73 -73
  131. package/src/commands/refresh.ts +147 -147
  132. package/src/commands/remove.ts +98 -98
  133. package/src/commands/start.ts +117 -117
  134. package/src/commands/status.ts +54 -54
  135. package/src/commands/stop.ts +55 -55
  136. package/src/commands/sync.ts +151 -153
  137. package/src/commands/tui.ts +424 -0
  138. package/src/commands/ui.ts +111 -47
  139. package/src/core/config.test.ts +96 -96
  140. package/src/core/config.ts +115 -115
  141. package/src/core/graph.ts +344 -344
  142. package/src/core/packer.test.ts +99 -99
  143. package/src/core/packer.ts +107 -107
  144. package/src/core/scanner.test.ts +61 -61
  145. package/src/core/scanner.ts +91 -91
  146. package/src/core/workspace.ts +380 -380
  147. package/src/core/worktree.ts +120 -120
  148. package/src/generators/antigravity.ts +32 -32
  149. package/src/generators/base.ts +240 -227
  150. package/src/generators/claude.ts +34 -34
  151. package/src/generators/codex.ts +48 -48
  152. package/src/generators/copilot.ts +56 -56
  153. package/src/generators/cursor.ts +44 -44
  154. package/src/generators/index.ts +250 -250
  155. package/src/generators/map-generator.test.ts +159 -159
  156. package/src/generators/map-generator.ts +542 -542
  157. package/src/generators/plan-generator.ts +481 -481
  158. package/src/generators/skills-generator.ts +259 -259
  159. package/src/index.ts +426 -382
  160. package/src/mcp/server.ts +294 -294
  161. package/src/orchestration/detect.ts +313 -313
  162. package/src/orchestration/index.ts +7 -7
  163. package/src/orchestration/runner.ts +283 -283
  164. package/src/server.test.ts +555 -464
  165. package/src/server.ts +1308 -1104
  166. package/src/types.ts +408 -403
  167. package/src/utils/detect-ai.test.ts +44 -44
  168. package/src/utils/detect-ai.ts +84 -84
  169. package/src/utils/detect-editors.test.ts +48 -48
  170. package/src/utils/detect-editors.ts +57 -57
  171. package/src/utils/git.test.ts +74 -74
  172. package/src/utils/git.ts +117 -61
  173. package/src/utils/local-ai.test.ts +130 -130
  174. package/src/utils/local-ai.ts +111 -111
  175. package/src/utils/multi-git.ts +313 -306
  176. package/src/utils/prompts.ts +209 -209
  177. package/src/utils/session-finder.ts +483 -483
  178. package/src/utils/system-scanner.test.ts +89 -89
  179. package/src/utils/system-scanner.ts +96 -96
  180. package/src/utils/update-check.ts +309 -275
  181. package/src/utils/workflows.test.ts +114 -0
  182. package/src/utils/workflows.ts +178 -0
  183. package/tsconfig.json +19 -19
  184. package/vitest.config.ts +14 -14
  185. package/dist/gui/assets/index-CFWwqFux.js +0 -23
  186. package/dist/gui/assets/index-Dvc9QMvl.css +0 -2
package/src/index.ts CHANGED
@@ -1,382 +1,426 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @module index
5
- * NexusFlow CLI entry point.
6
- *
7
- * Combines multiple repositories into a workspace with rich AI assistant
8
- * context files so tools like Claude, Codex, Copilot, and Cursor understand
9
- * your entire project.
10
- */
11
-
12
- import { Command } from 'commander';
13
-
14
- import { createCommand } from './commands/create.js';
15
- import { listCommand } from './commands/list.js';
16
- import { openCommand } from './commands/open.js';
17
- import { initCommand } from './commands/init.js';
18
- import { startCommand } from './commands/start.js';
19
- import { stopCommand } from './commands/stop.js';
20
- import { logsCommand } from './commands/logs.js';
21
- import { statusCommand } from './commands/status.js';
22
- import { uiCommand } from './commands/ui.js';
23
- import { syncCommand } from './commands/sync.js';
24
- import { commitCommand } from './commands/commit.js';
25
- import { diffCommand } from './commands/diff.js';
26
- import { packCommand } from './commands/pack.js';
27
- import { removeCommand } from './commands/remove.js';
28
- import { addRepoCommand } from './commands/add-repo.js';
29
- import { mcpRunCommand, mcpSetupCommand } from './commands/mcp.js';
30
- import { handoffCommand } from './commands/handoff.js';
31
- import { refreshCommand } from './commands/refresh.js';
32
- import { doctorCommand } from './commands/doctor.js';
33
- import { getCurrentVersion, checkForUpdates, printUpdateBanner } from './utils/update-check.js';
34
-
35
- const program = new Command();
36
-
37
- program
38
- .name('nexusflow')
39
- .description(
40
- 'Combine multiple repos into a workspace with rich AI assistant context',
41
- )
42
- .version(getCurrentVersion());
43
-
44
- program
45
- .command('create')
46
- .description(
47
- 'Create a new feature workspace — pick repos, pick AI assistants, generate context',
48
- )
49
- .action(async () => {
50
- try {
51
- await createCommand();
52
- } catch (error) {
53
- if (error instanceof Error && error.message.includes('User force closed')) {
54
- console.log('\nCancelled.');
55
- process.exit(0);
56
- }
57
- console.error(error);
58
- process.exit(1);
59
- }
60
- });
61
-
62
- program
63
- .command('list')
64
- .alias('ls')
65
- .description('List all existing workspaces')
66
- .action(async () => {
67
- try {
68
- await listCommand();
69
- } catch (error) {
70
- console.error(error);
71
- process.exit(1);
72
- }
73
- });
74
-
75
- program
76
- .command('open')
77
- .description('Re-open an existing workspace in an editor')
78
- .action(async () => {
79
- try {
80
- await openCommand();
81
- } catch (error) {
82
- if (error instanceof Error && error.message.includes('User force closed')) {
83
- console.log('\nCancelled.');
84
- process.exit(0);
85
- }
86
- console.error(error);
87
- process.exit(1);
88
- }
89
- });
90
-
91
- program
92
- .command('init')
93
- .description('Initialize NexusFlow configuration')
94
- .action(async () => {
95
- try {
96
- await initCommand();
97
- } catch (error) {
98
- if (error instanceof Error && error.message.includes('User force closed')) {
99
- console.log('\nCancelled.');
100
- process.exit(0);
101
- }
102
- console.error(error);
103
- process.exit(1);
104
- }
105
- });
106
-
107
- program
108
- .command('start')
109
- .description('Start all services in a workspace')
110
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
111
- .action(async (workspace?: string) => {
112
- try {
113
- await startCommand(workspace);
114
- } catch (error) {
115
- if (error instanceof Error && error.message.includes('User force closed')) {
116
- console.log('\nCancelled.');
117
- process.exit(0);
118
- }
119
- console.error(error);
120
- process.exit(1);
121
- }
122
- });
123
-
124
- program
125
- .command('stop')
126
- .description('Stop all running services in a workspace')
127
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
128
- .action(async (workspace?: string) => {
129
- try {
130
- await stopCommand(workspace);
131
- } catch (error) {
132
- if (error instanceof Error && error.message.includes('User force closed')) {
133
- console.log('\nCancelled.');
134
- process.exit(0);
135
- }
136
- console.error(error);
137
- process.exit(1);
138
- }
139
- });
140
-
141
- program
142
- .command('logs')
143
- .description('Show logs from running services')
144
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
145
- .option('-n, --lines <number>', 'Number of lines per service', '30')
146
- .action(async (workspace: string | undefined, options: { lines: string }) => {
147
- try {
148
- await logsCommand(workspace, parseInt(options.lines, 10));
149
- } catch (error) {
150
- if (error instanceof Error && error.message.includes('User force closed')) {
151
- console.log('\nCancelled.');
152
- process.exit(0);
153
- }
154
- console.error(error);
155
- process.exit(1);
156
- }
157
- });
158
-
159
- program
160
- .command('status')
161
- .description('Show status of running services')
162
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
163
- .action(async (workspace?: string) => {
164
- try {
165
- await statusCommand(workspace);
166
- } catch (error) {
167
- if (error instanceof Error && error.message.includes('User force closed')) {
168
- console.log('\nCancelled.');
169
- process.exit(0);
170
- }
171
- console.error(error);
172
- process.exit(1);
173
- }
174
- });
175
-
176
- program
177
- .command('ui')
178
- .description('Open the web GUI dashboard for NexusFlow')
179
- .option('-p, --port <number>', 'Port to run the dashboard server on', '3000')
180
- .action(async (options: { port?: string }) => {
181
- try {
182
- await uiCommand(options);
183
- } catch (error) {
184
- console.error(error);
185
- process.exit(1);
186
- }
187
- });
188
-
189
- program
190
- .command('sync')
191
- .description('Sync all repositories in a workspace')
192
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
193
- .action(async (workspace?: string) => {
194
- try {
195
- await syncCommand(workspace);
196
- } catch (error) {
197
- if (error instanceof Error && error.message.includes('User force closed')) {
198
- console.log('\nCancelled.');
199
- process.exit(0);
200
- }
201
- console.error(error);
202
- process.exit(1);
203
- }
204
- });
205
-
206
- program
207
- .command('commit')
208
- .description('Commit changes across all repositories in a workspace')
209
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
210
- .requiredOption('-m, --message <msg>', 'Commit message')
211
- .option('--no-push', 'Stage and commit changes without pushing to remote')
212
- .option('--dry-run', 'Preview changes without committing')
213
- .action(async (workspace: string | undefined, options: { message: string; noPush?: boolean; dryRun?: boolean }) => {
214
- try {
215
- await commitCommand(options.message, workspace, options);
216
- } catch (error) {
217
- if (error instanceof Error && error.message.includes('User force closed')) {
218
- console.log('\nCancelled.');
219
- process.exit(0);
220
- }
221
- console.error(error);
222
- process.exit(1);
223
- }
224
- });
225
-
226
- program
227
- .command('diff')
228
- .description('Display a unified summary of changes across all repositories')
229
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
230
- .action(async (workspace?: string) => {
231
- try {
232
- await diffCommand(workspace);
233
- } catch (error) {
234
- if (error instanceof Error && error.message.includes('User force closed')) {
235
- console.log('\nCancelled.');
236
- process.exit(0);
237
- }
238
- console.error(error);
239
- process.exit(1);
240
- }
241
- });
242
-
243
- program
244
- .command('pack')
245
- .description('Pack the workspace codebase into a single token-efficient XML file for AI consumption')
246
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
247
- .option('--no-compress', 'Do not compress files (strip comments, empty lines)')
248
- .action(async (workspace: string | undefined, options: { compress?: boolean }) => {
249
- try {
250
- await packCommand(workspace, options);
251
- } catch (error) {
252
- if (error instanceof Error && error.message.includes('User force closed')) {
253
- console.log('\nCancelled.');
254
- process.exit(0);
255
- }
256
- console.error(error);
257
- process.exit(1);
258
- }
259
- });
260
-
261
- program
262
- .command('remove')
263
- .alias('rm')
264
- .description('Delete a workspace and cleanly prune/remove its git worktrees')
265
- .argument('[workspace]', 'Workspace name or path')
266
- .action(async (workspace?: string) => {
267
- try {
268
- await removeCommand(workspace);
269
- } catch (error) {
270
- if (error instanceof Error && error.message.includes('User force closed')) {
271
- console.log('\nCancelled.');
272
- process.exit(0);
273
- }
274
- console.error(error);
275
- process.exit(1);
276
- }
277
- });
278
-
279
- program
280
- .command('add-repo')
281
- .alias('add')
282
- .description('Add a repository to an existing workspace and update configurations')
283
- .argument('[repo-path]', 'Path to the repository to add')
284
- .argument('[workspace]', 'Workspace name or path')
285
- .action(async (repoPath?: string, workspace?: string) => {
286
- try {
287
- await addRepoCommand(repoPath, workspace);
288
- } catch (error) {
289
- if (error instanceof Error && error.message.includes('User force closed')) {
290
- console.log('\nCancelled.');
291
- process.exit(0);
292
- }
293
- console.error(error);
294
- process.exit(1);
295
- }
296
- });
297
-
298
- program
299
- .command('handoff')
300
- .description('Generate a compact handoff bundle (nexusflow-handoff.md) for session resumption')
301
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
302
- .action(async (workspace?: string) => {
303
- try {
304
- await handoffCommand(workspace);
305
- } catch (error) {
306
- console.error(error);
307
- process.exit(1);
308
- }
309
- });
310
-
311
- program
312
- .command('refresh')
313
- .description('Refresh workspace context, maps, plans and handoff files')
314
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
315
- .option('-r, --repo <repo>', 'Only refresh the map for a specific repository')
316
- .action(async (workspace: string | undefined, options: { repo?: string }) => {
317
- try {
318
- await refreshCommand(options, workspace);
319
- } catch (error) {
320
- console.error(error);
321
- process.exit(1);
322
- }
323
- });
324
-
325
- program
326
- .command('doctor')
327
- .description('Run diagnostics to verify workspace health and check for local loop issues')
328
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
329
- .action(async (workspace?: string) => {
330
- try {
331
- await doctorCommand(workspace);
332
- } catch (error) {
333
- console.error(error);
334
- process.exit(1);
335
- }
336
- });
337
-
338
- const mcp = program.command('mcp').description('Manage the NexusFlow MCP Server for AI assistants');
339
-
340
- mcp
341
- .command('run')
342
- .description('Start the NexusFlow MCP Server (typically called automatically by AI assistants)')
343
- .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
344
- .action(async (workspace?: string) => {
345
- try {
346
- await mcpRunCommand(workspace);
347
- } catch (error) {
348
- console.error(error);
349
- process.exit(1);
350
- }
351
- });
352
-
353
- mcp
354
- .command('setup')
355
- .description('Automatically configure your AI environments (Claude Desktop, Cursor, VS Code) to use the NexusFlow MCP Server')
356
- .action(async () => {
357
- try {
358
- await mcpSetupCommand();
359
- } catch (error) {
360
- console.error(error);
361
- process.exit(1);
362
- }
363
- });
364
-
365
- program.hook('postAction', async (thisCommand, actionCommand) => {
366
- // Skip update check for MCP run to prevent contaminating stdout stream
367
- if (actionCommand.name() === 'run' && actionCommand.parent?.name() === 'mcp') {
368
- return;
369
- }
370
-
371
- try {
372
- const status = await checkForUpdates();
373
- if (status) {
374
- printUpdateBanner(status);
375
- }
376
- } catch {
377
- // Silently ignore to prevent crashing CLI
378
- }
379
- });
380
-
381
- program.parse();
382
-
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @module index
5
+ * NexusFlow CLI entry point.
6
+ *
7
+ * Combines multiple repositories into a workspace with rich AI assistant
8
+ * context files so tools like Claude, Codex, Copilot, and Cursor understand
9
+ * your entire project.
10
+ */
11
+
12
+ import { Command } from 'commander';
13
+
14
+ import { createCommand } from './commands/create.js';
15
+ import { listCommand } from './commands/list.js';
16
+ import { openCommand } from './commands/open.js';
17
+ import { initCommand } from './commands/init.js';
18
+ import { startCommand } from './commands/start.js';
19
+ import { stopCommand } from './commands/stop.js';
20
+ import { logsCommand } from './commands/logs.js';
21
+ import { statusCommand } from './commands/status.js';
22
+ import { uiCommand } from './commands/ui.js';
23
+ import { tuiCommand } from './commands/tui.js';
24
+ import { syncCommand } from './commands/sync.js';
25
+ import { commitCommand } from './commands/commit.js';
26
+ import { diffCommand } from './commands/diff.js';
27
+ import { packCommand } from './commands/pack.js';
28
+ import { removeCommand } from './commands/remove.js';
29
+ import { addRepoCommand } from './commands/add-repo.js';
30
+ import { mcpRunCommand, mcpSetupCommand } from './commands/mcp.js';
31
+ import { handoffCommand } from './commands/handoff.js';
32
+ import { refreshCommand } from './commands/refresh.js';
33
+ import { doctorCommand } from './commands/doctor.js';
34
+ import { desktopCommand } from './commands/desktop.js';
35
+ import { getCurrentVersion, checkForUpdates, printUpdateBanner } from './utils/update-check.js';
36
+
37
+ const program = new Command();
38
+
39
+ program
40
+ .name('nexusflow')
41
+ .description(
42
+ 'Combine multiple repos into a workspace with rich AI assistant context',
43
+ )
44
+ .version(getCurrentVersion());
45
+
46
+ program
47
+ .command('create')
48
+ .description(
49
+ 'Create a new feature workspace — pick repos, pick AI assistants, generate context',
50
+ )
51
+ .action(async () => {
52
+ try {
53
+ await createCommand();
54
+ } catch (error) {
55
+ if (error instanceof Error && error.message.includes('User force closed')) {
56
+ console.log('\nCancelled.');
57
+ process.exit(0);
58
+ }
59
+ console.error(error);
60
+ process.exit(1);
61
+ }
62
+ });
63
+
64
+ program
65
+ .command('list')
66
+ .alias('ls')
67
+ .description('List all existing workspaces')
68
+ .action(async () => {
69
+ try {
70
+ await listCommand();
71
+ } catch (error) {
72
+ console.error(error);
73
+ process.exit(1);
74
+ }
75
+ });
76
+
77
+ program
78
+ .command('open')
79
+ .description('Re-open an existing workspace in an editor')
80
+ .action(async () => {
81
+ try {
82
+ await openCommand();
83
+ } catch (error) {
84
+ if (error instanceof Error && error.message.includes('User force closed')) {
85
+ console.log('\nCancelled.');
86
+ process.exit(0);
87
+ }
88
+ console.error(error);
89
+ process.exit(1);
90
+ }
91
+ });
92
+
93
+ program
94
+ .command('init')
95
+ .description('Initialize NexusFlow configuration')
96
+ .action(async () => {
97
+ try {
98
+ await initCommand();
99
+ } catch (error) {
100
+ if (error instanceof Error && error.message.includes('User force closed')) {
101
+ console.log('\nCancelled.');
102
+ process.exit(0);
103
+ }
104
+ console.error(error);
105
+ process.exit(1);
106
+ }
107
+ });
108
+
109
+ program
110
+ .command('start')
111
+ .description('Start all services in a workspace')
112
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
113
+ .action(async (workspace?: string) => {
114
+ try {
115
+ await startCommand(workspace);
116
+ } catch (error) {
117
+ if (error instanceof Error && error.message.includes('User force closed')) {
118
+ console.log('\nCancelled.');
119
+ process.exit(0);
120
+ }
121
+ console.error(error);
122
+ process.exit(1);
123
+ }
124
+ });
125
+
126
+ program
127
+ .command('stop')
128
+ .description('Stop all running services in a workspace')
129
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
130
+ .action(async (workspace?: string) => {
131
+ try {
132
+ await stopCommand(workspace);
133
+ } catch (error) {
134
+ if (error instanceof Error && error.message.includes('User force closed')) {
135
+ console.log('\nCancelled.');
136
+ process.exit(0);
137
+ }
138
+ console.error(error);
139
+ process.exit(1);
140
+ }
141
+ });
142
+
143
+ program
144
+ .command('logs')
145
+ .description('Show logs from running services')
146
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
147
+ .option('-n, --lines <number>', 'Number of lines per service', '30')
148
+ .action(async (workspace: string | undefined, options: { lines: string }) => {
149
+ try {
150
+ await logsCommand(workspace, parseInt(options.lines, 10));
151
+ } catch (error) {
152
+ if (error instanceof Error && error.message.includes('User force closed')) {
153
+ console.log('\nCancelled.');
154
+ process.exit(0);
155
+ }
156
+ console.error(error);
157
+ process.exit(1);
158
+ }
159
+ });
160
+
161
+ program
162
+ .command('status')
163
+ .description('Show status of running services')
164
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
165
+ .action(async (workspace?: string) => {
166
+ try {
167
+ await statusCommand(workspace);
168
+ } catch (error) {
169
+ if (error instanceof Error && error.message.includes('User force closed')) {
170
+ console.log('\nCancelled.');
171
+ process.exit(0);
172
+ }
173
+ console.error(error);
174
+ process.exit(1);
175
+ }
176
+ });
177
+
178
+ program
179
+ .command('ui')
180
+ .description('Open the web GUI dashboard for NexusFlow')
181
+ .option('-p, --port <number>', 'Port to run the dashboard server on', '3000')
182
+ .option('-d, --daemon', 'Run the dashboard server in the background (daemon mode)')
183
+ .option('--server-only', 'Start the dashboard server without opening the browser')
184
+ .action(async (options: { port?: string; daemon?: boolean; serverOnly?: boolean }) => {
185
+ try {
186
+ await uiCommand(options);
187
+ } catch (error) {
188
+ console.error(error);
189
+ process.exit(1);
190
+ }
191
+ });
192
+
193
+ program
194
+ .command('dashboard')
195
+ .alias('dash')
196
+ .description('Instantly open the web GUI dashboard in your default browser')
197
+ .option('-p, --port <number>', 'Port to run the dashboard server on', '3000')
198
+ .action(async (options: { port?: string }) => {
199
+ try {
200
+ await uiCommand({ ...options, daemon: true });
201
+ } catch (error) {
202
+ console.error(error);
203
+ process.exit(1);
204
+ }
205
+ });
206
+
207
+
208
+ program
209
+ .command('tui')
210
+ .description('Open the interactive terminal GUI (TUI) dashboard')
211
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
212
+ .action(async (workspace?: string) => {
213
+ try {
214
+ await tuiCommand({ workspace });
215
+ } catch (error) {
216
+ console.error(error);
217
+ process.exit(1);
218
+ }
219
+ });
220
+
221
+ program
222
+ .command('sync')
223
+ .description('Sync all repositories in a workspace')
224
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
225
+ .action(async (workspace?: string) => {
226
+ try {
227
+ await syncCommand(workspace);
228
+ } catch (error) {
229
+ if (error instanceof Error && error.message.includes('User force closed')) {
230
+ console.log('\nCancelled.');
231
+ process.exit(0);
232
+ }
233
+ console.error(error);
234
+ process.exit(1);
235
+ }
236
+ });
237
+
238
+ program
239
+ .command('commit')
240
+ .description('Commit changes across all repositories in a workspace')
241
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
242
+ .requiredOption('-m, --message <msg>', 'Commit message')
243
+ .option('--no-push', 'Stage and commit changes without pushing to remote')
244
+ .option('--dry-run', 'Preview changes without committing')
245
+ .action(async (workspace: string | undefined, options: { message: string; noPush?: boolean; dryRun?: boolean }) => {
246
+ try {
247
+ await commitCommand(options.message, workspace, options);
248
+ } catch (error) {
249
+ if (error instanceof Error && error.message.includes('User force closed')) {
250
+ console.log('\nCancelled.');
251
+ process.exit(0);
252
+ }
253
+ console.error(error);
254
+ process.exit(1);
255
+ }
256
+ });
257
+
258
+ program
259
+ .command('diff')
260
+ .description('Display a unified summary of changes across all repositories')
261
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
262
+ .action(async (workspace?: string) => {
263
+ try {
264
+ await diffCommand(workspace);
265
+ } catch (error) {
266
+ if (error instanceof Error && error.message.includes('User force closed')) {
267
+ console.log('\nCancelled.');
268
+ process.exit(0);
269
+ }
270
+ console.error(error);
271
+ process.exit(1);
272
+ }
273
+ });
274
+
275
+ program
276
+ .command('pack')
277
+ .description('Pack the workspace codebase into a single token-efficient XML file for AI consumption')
278
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
279
+ .option('--no-compress', 'Do not compress files (strip comments, empty lines)')
280
+ .action(async (workspace: string | undefined, options: { compress?: boolean }) => {
281
+ try {
282
+ await packCommand(workspace, options);
283
+ } catch (error) {
284
+ if (error instanceof Error && error.message.includes('User force closed')) {
285
+ console.log('\nCancelled.');
286
+ process.exit(0);
287
+ }
288
+ console.error(error);
289
+ process.exit(1);
290
+ }
291
+ });
292
+
293
+ program
294
+ .command('remove')
295
+ .alias('rm')
296
+ .description('Delete a workspace and cleanly prune/remove its git worktrees')
297
+ .argument('[workspace]', 'Workspace name or path')
298
+ .action(async (workspace?: string) => {
299
+ try {
300
+ await removeCommand(workspace);
301
+ } catch (error) {
302
+ if (error instanceof Error && error.message.includes('User force closed')) {
303
+ console.log('\nCancelled.');
304
+ process.exit(0);
305
+ }
306
+ console.error(error);
307
+ process.exit(1);
308
+ }
309
+ });
310
+
311
+ program
312
+ .command('add-repo')
313
+ .alias('add')
314
+ .description('Add a repository to an existing workspace and update configurations')
315
+ .argument('[repo-path]', 'Path to the repository to add')
316
+ .argument('[workspace]', 'Workspace name or path')
317
+ .action(async (repoPath?: string, workspace?: string) => {
318
+ try {
319
+ await addRepoCommand(repoPath, workspace);
320
+ } catch (error) {
321
+ if (error instanceof Error && error.message.includes('User force closed')) {
322
+ console.log('\nCancelled.');
323
+ process.exit(0);
324
+ }
325
+ console.error(error);
326
+ process.exit(1);
327
+ }
328
+ });
329
+
330
+ program
331
+ .command('handoff')
332
+ .description('Generate a compact handoff bundle (nexusflow-handoff.md) for session resumption')
333
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
334
+ .action(async (workspace?: string) => {
335
+ try {
336
+ await handoffCommand(workspace);
337
+ } catch (error) {
338
+ console.error(error);
339
+ process.exit(1);
340
+ }
341
+ });
342
+
343
+ program
344
+ .command('refresh')
345
+ .description('Refresh workspace context, maps, plans and handoff files')
346
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
347
+ .option('-r, --repo <repo>', 'Only refresh the map for a specific repository')
348
+ .action(async (workspace: string | undefined, options: { repo?: string }) => {
349
+ try {
350
+ await refreshCommand(options, workspace);
351
+ } catch (error) {
352
+ console.error(error);
353
+ process.exit(1);
354
+ }
355
+ });
356
+
357
+ program
358
+ .command('doctor')
359
+ .description('Run diagnostics to verify workspace health and check for local loop issues')
360
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
361
+ .action(async (workspace?: string) => {
362
+ try {
363
+ await doctorCommand(workspace);
364
+ } catch (error) {
365
+ console.error(error);
366
+ process.exit(1);
367
+ }
368
+ });
369
+
370
+ program
371
+ .command('desktop')
372
+ .description('Launch the NexusFlow desktop application')
373
+ .action(async () => {
374
+ try {
375
+ await desktopCommand();
376
+ } catch (error) {
377
+ console.error(error);
378
+ process.exit(1);
379
+ }
380
+ });
381
+
382
+ const mcp = program.command('mcp').description('Manage the NexusFlow MCP Server for AI assistants');
383
+
384
+ mcp
385
+ .command('run')
386
+ .description('Start the NexusFlow MCP Server (typically called automatically by AI assistants)')
387
+ .argument('[workspace]', 'Path to workspace (auto-detects from CWD)')
388
+ .action(async (workspace?: string) => {
389
+ try {
390
+ await mcpRunCommand(workspace);
391
+ } catch (error) {
392
+ console.error(error);
393
+ process.exit(1);
394
+ }
395
+ });
396
+
397
+ mcp
398
+ .command('setup')
399
+ .description('Automatically configure your AI environments (Claude Desktop, Cursor, VS Code) to use the NexusFlow MCP Server')
400
+ .action(async () => {
401
+ try {
402
+ await mcpSetupCommand();
403
+ } catch (error) {
404
+ console.error(error);
405
+ process.exit(1);
406
+ }
407
+ });
408
+
409
+ program.hook('postAction', async (thisCommand, actionCommand) => {
410
+ // Skip update check for MCP run to prevent contaminating stdout stream
411
+ if (actionCommand.name() === 'run' && actionCommand.parent?.name() === 'mcp') {
412
+ return;
413
+ }
414
+
415
+ try {
416
+ const status = await checkForUpdates();
417
+ if (status) {
418
+ printUpdateBanner(status);
419
+ }
420
+ } catch {
421
+ // Silently ignore to prevent crashing CLI
422
+ }
423
+ });
424
+
425
+ program.parse();
426
+