@mrpatronz/nexusflow 0.2.12 → 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 (233) 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/add-repo.d.ts.map +1 -1
  15. package/dist/commands/add-repo.js +24 -14
  16. package/dist/commands/add-repo.js.map +1 -1
  17. package/dist/commands/commit.js +1 -1
  18. package/dist/commands/commit.js.map +1 -1
  19. package/dist/commands/desktop.d.ts +12 -0
  20. package/dist/commands/desktop.d.ts.map +1 -0
  21. package/dist/commands/desktop.js +119 -0
  22. package/dist/commands/desktop.js.map +1 -0
  23. package/dist/commands/diff.js +1 -1
  24. package/dist/commands/diff.js.map +1 -1
  25. package/dist/commands/doctor.js +1 -1
  26. package/dist/commands/doctor.js.map +1 -1
  27. package/dist/commands/handoff.js +1 -1
  28. package/dist/commands/handoff.js.map +1 -1
  29. package/dist/commands/logs.js +1 -1
  30. package/dist/commands/logs.js.map +1 -1
  31. package/dist/commands/open.d.ts.map +1 -1
  32. package/dist/commands/open.js +12 -7
  33. package/dist/commands/open.js.map +1 -1
  34. package/dist/commands/pack.js +1 -1
  35. package/dist/commands/pack.js.map +1 -1
  36. package/dist/commands/refresh.js +13 -8
  37. package/dist/commands/refresh.js.map +1 -1
  38. package/dist/commands/remove.d.ts.map +1 -1
  39. package/dist/commands/remove.js +12 -7
  40. package/dist/commands/remove.js.map +1 -1
  41. package/dist/commands/start.js +1 -1
  42. package/dist/commands/start.js.map +1 -1
  43. package/dist/commands/status.js +1 -1
  44. package/dist/commands/status.js.map +1 -1
  45. package/dist/commands/stop.js +1 -1
  46. package/dist/commands/stop.js.map +1 -1
  47. package/dist/commands/sync.d.ts.map +1 -1
  48. package/dist/commands/sync.js +14 -11
  49. package/dist/commands/sync.js.map +1 -1
  50. package/dist/commands/tui.d.ts +8 -0
  51. package/dist/commands/tui.d.ts.map +1 -0
  52. package/dist/commands/tui.js +386 -0
  53. package/dist/commands/tui.js.map +1 -0
  54. package/dist/commands/ui.d.ts +3 -1
  55. package/dist/commands/ui.d.ts.map +1 -1
  56. package/dist/commands/ui.js +76 -19
  57. package/dist/commands/ui.js.map +1 -1
  58. package/dist/core/workspace.d.ts +7 -0
  59. package/dist/core/workspace.d.ts.map +1 -1
  60. package/dist/core/workspace.js +45 -1
  61. package/dist/core/workspace.js.map +1 -1
  62. package/dist/generators/base.d.ts.map +1 -1
  63. package/dist/generators/base.js +78 -66
  64. package/dist/generators/base.js.map +1 -1
  65. package/dist/generators/codex.js +11 -11
  66. package/dist/generators/copilot.js +17 -17
  67. package/dist/generators/cursor.js +5 -5
  68. package/dist/generators/index.d.ts.map +1 -1
  69. package/dist/generators/index.js +64 -50
  70. package/dist/generators/index.js.map +1 -1
  71. package/dist/generators/map-generator.js +14 -14
  72. package/dist/generators/skills-generator.js +28 -28
  73. package/dist/gui/app_logo.png +0 -0
  74. package/dist/gui/assets/index-Ct3qq-X4.js +25 -0
  75. package/dist/gui/assets/index-P_ZrgHXg.css +1 -0
  76. package/dist/gui/icons.svg +24 -24
  77. package/dist/gui/index.html +18 -18
  78. package/dist/index.js +43 -0
  79. package/dist/index.js.map +1 -1
  80. package/dist/orchestration/runner.d.ts +5 -6
  81. package/dist/orchestration/runner.d.ts.map +1 -1
  82. package/dist/orchestration/runner.js +105 -102
  83. package/dist/orchestration/runner.js.map +1 -1
  84. package/dist/server.d.ts.map +1 -1
  85. package/dist/server.js +235 -2
  86. package/dist/server.js.map +1 -1
  87. package/dist/server.test.js +78 -0
  88. package/dist/server.test.js.map +1 -1
  89. package/dist/types.d.ts +4 -0
  90. package/dist/types.d.ts.map +1 -1
  91. package/dist/utils/detect-editors.js +1 -1
  92. package/dist/utils/detect-editors.js.map +1 -1
  93. package/dist/utils/detect-editors.test.js +3 -3
  94. package/dist/utils/detect-editors.test.js.map +1 -1
  95. package/dist/utils/git.d.ts +16 -0
  96. package/dist/utils/git.d.ts.map +1 -1
  97. package/dist/utils/git.js +52 -0
  98. package/dist/utils/git.js.map +1 -1
  99. package/dist/utils/multi-git.d.ts +2 -0
  100. package/dist/utils/multi-git.d.ts.map +1 -1
  101. package/dist/utils/multi-git.js +5 -2
  102. package/dist/utils/multi-git.js.map +1 -1
  103. package/dist/utils/prompts.d.ts +1 -0
  104. package/dist/utils/prompts.d.ts.map +1 -1
  105. package/dist/utils/prompts.js +77 -15
  106. package/dist/utils/prompts.js.map +1 -1
  107. package/dist/utils/update-check.d.ts +3 -1
  108. package/dist/utils/update-check.d.ts.map +1 -1
  109. package/dist/utils/update-check.js +23 -6
  110. package/dist/utils/update-check.js.map +1 -1
  111. package/dist/utils/workflows.d.ts +11 -0
  112. package/dist/utils/workflows.d.ts.map +1 -0
  113. package/dist/utils/workflows.js +156 -0
  114. package/dist/utils/workflows.js.map +1 -0
  115. package/dist/utils/workflows.test.d.ts +2 -0
  116. package/dist/utils/workflows.test.d.ts.map +1 -0
  117. package/dist/utils/workflows.test.js +86 -0
  118. package/dist/utils/workflows.test.js.map +1 -0
  119. package/extension/media/icon.svg +1 -0
  120. package/extension/package-lock.json +2673 -2811
  121. package/extension/package.json +108 -61
  122. package/extension/src/extension.ts +631 -168
  123. package/extension/tsconfig.json +21 -19
  124. package/gui/README.md +73 -73
  125. package/gui/e2e/wizard.spec.ts +346 -327
  126. package/gui/eslint.config.js +26 -22
  127. package/gui/index.html +17 -17
  128. package/gui/package-lock.json +3079 -3118
  129. package/gui/package.json +36 -36
  130. package/gui/playwright.config.ts +41 -41
  131. package/gui/public/app_logo.png +0 -0
  132. package/gui/public/icons.svg +24 -24
  133. package/gui/src/App.css +1 -1
  134. package/gui/src/App.tsx +3407 -2498
  135. package/gui/src/assets/vite.svg +1 -1
  136. package/gui/src/features/changes/ChangesViewer.tsx +388 -212
  137. package/gui/src/features/knowledge/KnowledgeBase.tsx +84 -84
  138. package/gui/src/features/onboarding/OnboardingWizard.tsx +230 -230
  139. package/gui/src/features/plan/ImplementationPlan.tsx +32 -32
  140. package/gui/src/features/services/ServiceConsole.tsx +250 -159
  141. package/gui/src/features/sessions/SessionHistory.tsx +195 -195
  142. package/gui/src/features/workspace/WorkspaceList.tsx +666 -514
  143. package/gui/src/index.css +152 -17
  144. package/gui/src/main.tsx +94 -10
  145. package/gui/src/types.ts +62 -62
  146. package/gui/tsconfig.app.json +25 -25
  147. package/gui/tsconfig.json +7 -7
  148. package/gui/tsconfig.node.json +24 -24
  149. package/gui/vite.config.ts +12 -12
  150. package/package.json +56 -55
  151. package/resources/workflows/plan-implement-review.md +8 -0
  152. package/resources/workflows/research-verify.md +6 -0
  153. package/resources/workflows/solo-developer.md +3 -0
  154. package/scripts/simulate-workspaces.ts +55 -55
  155. package/src/analyzers/detect-apis.ts +290 -290
  156. package/src/analyzers/detect-deps.ts +315 -315
  157. package/src/analyzers/detect-existing.ts +74 -74
  158. package/src/analyzers/detect-ports.ts +110 -110
  159. package/src/analyzers/index.ts +97 -97
  160. package/src/analyzers/messaging-analyzer.ts +254 -254
  161. package/src/analyzers/readme-summarizer.ts +102 -102
  162. package/src/analyzers/run-analyzer.ts +269 -269
  163. package/src/analyzers/tech-stack.ts +283 -283
  164. package/src/commands/add-repo.ts +157 -141
  165. package/src/commands/commands.test.ts +155 -155
  166. package/src/commands/commit.ts +131 -131
  167. package/src/commands/create.ts +207 -207
  168. package/src/commands/desktop.ts +128 -0
  169. package/src/commands/diff.ts +125 -125
  170. package/src/commands/doctor.ts +357 -357
  171. package/src/commands/handoff.ts +266 -266
  172. package/src/commands/init.ts +134 -134
  173. package/src/commands/list.ts +46 -46
  174. package/src/commands/logs.ts +63 -63
  175. package/src/commands/mcp.ts +92 -92
  176. package/src/commands/open.ts +129 -121
  177. package/src/commands/pack.ts +73 -73
  178. package/src/commands/refresh.ts +147 -139
  179. package/src/commands/remove.ts +98 -90
  180. package/src/commands/start.ts +117 -117
  181. package/src/commands/status.ts +54 -54
  182. package/src/commands/stop.ts +55 -55
  183. package/src/commands/sync.ts +151 -145
  184. package/src/commands/tui.ts +424 -0
  185. package/src/commands/ui.ts +111 -47
  186. package/src/core/config.test.ts +96 -96
  187. package/src/core/config.ts +115 -115
  188. package/src/core/graph.ts +344 -344
  189. package/src/core/packer.test.ts +99 -99
  190. package/src/core/packer.ts +107 -107
  191. package/src/core/scanner.test.ts +61 -61
  192. package/src/core/scanner.ts +91 -91
  193. package/src/core/workspace.ts +380 -333
  194. package/src/core/worktree.ts +120 -120
  195. package/src/generators/antigravity.ts +32 -32
  196. package/src/generators/base.ts +240 -227
  197. package/src/generators/claude.ts +34 -34
  198. package/src/generators/codex.ts +48 -48
  199. package/src/generators/copilot.ts +56 -56
  200. package/src/generators/cursor.ts +44 -44
  201. package/src/generators/index.ts +250 -230
  202. package/src/generators/map-generator.test.ts +159 -159
  203. package/src/generators/map-generator.ts +542 -542
  204. package/src/generators/plan-generator.ts +481 -481
  205. package/src/generators/skills-generator.ts +259 -259
  206. package/src/index.ts +426 -382
  207. package/src/mcp/server.ts +294 -294
  208. package/src/orchestration/detect.ts +313 -313
  209. package/src/orchestration/index.ts +7 -7
  210. package/src/orchestration/runner.ts +283 -279
  211. package/src/server.test.ts +555 -464
  212. package/src/server.ts +1308 -1047
  213. package/src/types.ts +408 -403
  214. package/src/utils/detect-ai.test.ts +44 -44
  215. package/src/utils/detect-ai.ts +84 -84
  216. package/src/utils/detect-editors.test.ts +48 -48
  217. package/src/utils/detect-editors.ts +57 -57
  218. package/src/utils/git.test.ts +74 -74
  219. package/src/utils/git.ts +117 -61
  220. package/src/utils/local-ai.test.ts +130 -130
  221. package/src/utils/local-ai.ts +111 -111
  222. package/src/utils/multi-git.ts +313 -306
  223. package/src/utils/prompts.ts +209 -135
  224. package/src/utils/session-finder.ts +483 -483
  225. package/src/utils/system-scanner.test.ts +89 -89
  226. package/src/utils/system-scanner.ts +96 -96
  227. package/src/utils/update-check.ts +309 -275
  228. package/src/utils/workflows.test.ts +114 -0
  229. package/src/utils/workflows.ts +178 -0
  230. package/tsconfig.json +19 -19
  231. package/vitest.config.ts +14 -14
  232. package/dist/gui/assets/index-Ca2VRMrQ.js +0 -22
  233. package/dist/gui/assets/index-fj0RJiOb.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
+