@mrpatronz/nexusflow 0.2.18 → 1.3.0

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 (236) hide show
  1. package/README.md +27 -3
  2. package/dist/analyzers/index.d.ts +23 -0
  3. package/dist/analyzers/index.d.ts.map +1 -1
  4. package/dist/analyzers/index.js +62 -0
  5. package/dist/analyzers/index.js.map +1 -1
  6. package/dist/commands/commands.test.js +20 -6
  7. package/dist/commands/commands.test.js.map +1 -1
  8. package/dist/commands/commit.d.ts +2 -0
  9. package/dist/commands/commit.d.ts.map +1 -1
  10. package/dist/commands/commit.js +27 -5
  11. package/dist/commands/commit.js.map +1 -1
  12. package/dist/commands/diff.d.ts +9 -2
  13. package/dist/commands/diff.d.ts.map +1 -1
  14. package/dist/commands/diff.js +36 -12
  15. package/dist/commands/diff.js.map +1 -1
  16. package/dist/commands/doctor.js +2 -2
  17. package/dist/commands/doctor.js.map +1 -1
  18. package/dist/commands/handoff.js +2 -2
  19. package/dist/commands/handoff.js.map +1 -1
  20. package/dist/commands/refresh.d.ts +4 -2
  21. package/dist/commands/refresh.d.ts.map +1 -1
  22. package/dist/commands/refresh.js +20 -36
  23. package/dist/commands/refresh.js.map +1 -1
  24. package/dist/commands/schedule.d.ts +35 -0
  25. package/dist/commands/schedule.d.ts.map +1 -0
  26. package/dist/commands/schedule.js +151 -0
  27. package/dist/commands/schedule.js.map +1 -0
  28. package/dist/commands/sync.d.ts.map +1 -1
  29. package/dist/commands/sync.js +58 -50
  30. package/dist/commands/sync.js.map +1 -1
  31. package/dist/core/analysis-cache.d.ts +58 -0
  32. package/dist/core/analysis-cache.d.ts.map +1 -0
  33. package/dist/core/analysis-cache.js +116 -0
  34. package/dist/core/analysis-cache.js.map +1 -0
  35. package/dist/core/analysis-cache.test.d.ts +2 -0
  36. package/dist/core/analysis-cache.test.d.ts.map +1 -0
  37. package/dist/core/analysis-cache.test.js +108 -0
  38. package/dist/core/analysis-cache.test.js.map +1 -0
  39. package/dist/core/refresh.d.ts +41 -0
  40. package/dist/core/refresh.d.ts.map +1 -0
  41. package/dist/core/refresh.js +60 -0
  42. package/dist/core/refresh.js.map +1 -0
  43. package/dist/core/scheduler.d.ts +117 -0
  44. package/dist/core/scheduler.d.ts.map +1 -0
  45. package/dist/core/scheduler.js +247 -0
  46. package/dist/core/scheduler.js.map +1 -0
  47. package/dist/core/scheduler.test.d.ts +2 -0
  48. package/dist/core/scheduler.test.d.ts.map +1 -0
  49. package/dist/core/scheduler.test.js +111 -0
  50. package/dist/core/scheduler.test.js.map +1 -0
  51. package/dist/core/sync.d.ts +50 -0
  52. package/dist/core/sync.d.ts.map +1 -0
  53. package/dist/core/sync.js +92 -0
  54. package/dist/core/sync.js.map +1 -0
  55. package/dist/core/workspace-state.d.ts +51 -0
  56. package/dist/core/workspace-state.d.ts.map +1 -0
  57. package/dist/core/workspace-state.js +108 -0
  58. package/dist/core/workspace-state.js.map +1 -0
  59. package/dist/core/workspace-state.test.d.ts +2 -0
  60. package/dist/core/workspace-state.test.d.ts.map +1 -0
  61. package/dist/core/workspace-state.test.js +84 -0
  62. package/dist/core/workspace-state.test.js.map +1 -0
  63. package/dist/generators/index.d.ts +7 -1
  64. package/dist/generators/index.d.ts.map +1 -1
  65. package/dist/generators/index.js +14 -1
  66. package/dist/generators/index.js.map +1 -1
  67. package/dist/gui/assets/index-Bdbld6yY.css +1 -0
  68. package/dist/gui/assets/index-D8iJPvnk.js +26 -0
  69. package/dist/gui/index.html +2 -2
  70. package/dist/index.js +107 -4
  71. package/dist/index.js.map +1 -1
  72. package/dist/mcp/server.d.ts.map +1 -1
  73. package/dist/mcp/server.js +42 -0
  74. package/dist/mcp/server.js.map +1 -1
  75. package/dist/server.d.ts.map +1 -1
  76. package/dist/server.js +175 -13
  77. package/dist/server.js.map +1 -1
  78. package/dist/server.test.js +66 -0
  79. package/dist/server.test.js.map +1 -1
  80. package/dist/types.d.ts +31 -0
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/utils/multi-git.d.ts +54 -8
  83. package/dist/utils/multi-git.d.ts.map +1 -1
  84. package/dist/utils/multi-git.js +129 -21
  85. package/dist/utils/multi-git.js.map +1 -1
  86. package/dist/utils/multi-git.test.d.ts +2 -0
  87. package/dist/utils/multi-git.test.d.ts.map +1 -0
  88. package/dist/utils/multi-git.test.js +125 -0
  89. package/dist/utils/multi-git.test.js.map +1 -0
  90. package/package.json +13 -1
  91. package/.agents/skills/ccc/SKILL.md +0 -71
  92. package/.agents/skills/ccc/references/management.md +0 -110
  93. package/.agents/skills/ccc/references/settings.md +0 -126
  94. package/.github/dependabot.yml +0 -52
  95. package/.github/workflows/ci.yml +0 -44
  96. package/.github/workflows/dependabot-merge.yml +0 -53
  97. package/.github/workflows/release-desktop.yml +0 -67
  98. package/.github/workflows/release-npm.yml +0 -65
  99. package/.github/workflows/release-vscode.yml +0 -61
  100. package/.vscode/launch.json +0 -17
  101. package/.vscode/tasks.json +0 -17
  102. package/GETTING_STARTED.md +0 -114
  103. package/desktop/build-installer.js +0 -228
  104. package/desktop/build.js +0 -118
  105. package/desktop/installer/nexusflow.iss +0 -35
  106. package/desktop/neutralino.config.json +0 -39
  107. package/desktop/package-lock.json +0 -1060
  108. package/desktop/package.json +0 -14
  109. package/dist/gui/assets/index-CB-jWded.css +0 -1
  110. package/dist/gui/assets/index-DDXpZZu3.js +0 -25
  111. package/extension/media/icon.svg +0 -1
  112. package/extension/package-lock.json +0 -6044
  113. package/extension/package.json +0 -117
  114. package/extension/src/extension.ts +0 -653
  115. package/extension/tsconfig.json +0 -21
  116. package/gui/README.md +0 -73
  117. package/gui/e2e/wizard.spec.ts +0 -346
  118. package/gui/eslint.config.js +0 -26
  119. package/gui/index.html +0 -17
  120. package/gui/package-lock.json +0 -3079
  121. package/gui/package.json +0 -36
  122. package/gui/playwright.config.ts +0 -41
  123. package/gui/public/app_logo.png +0 -0
  124. package/gui/public/favicon.svg +0 -1
  125. package/gui/public/icons.svg +0 -24
  126. package/gui/src/App.css +0 -1
  127. package/gui/src/App.tsx +0 -3632
  128. package/gui/src/assets/hero.png +0 -0
  129. package/gui/src/assets/react.svg +0 -1
  130. package/gui/src/assets/vite.svg +0 -1
  131. package/gui/src/features/changes/ChangesViewer.tsx +0 -388
  132. package/gui/src/features/knowledge/KnowledgeBase.tsx +0 -84
  133. package/gui/src/features/onboarding/OnboardingWizard.tsx +0 -230
  134. package/gui/src/features/plan/ImplementationPlan.tsx +0 -32
  135. package/gui/src/features/services/ServiceConsole.tsx +0 -250
  136. package/gui/src/features/sessions/SessionHistory.tsx +0 -195
  137. package/gui/src/features/workspace/WorkspaceList.tsx +0 -666
  138. package/gui/src/index.css +0 -152
  139. package/gui/src/main.tsx +0 -94
  140. package/gui/src/types.ts +0 -62
  141. package/gui/tsconfig.app.json +0 -25
  142. package/gui/tsconfig.json +0 -7
  143. package/gui/tsconfig.node.json +0 -24
  144. package/gui/vite.config.ts +0 -12
  145. package/resources/workflows/plan-implement-review.md +0 -8
  146. package/resources/workflows/research-verify.md +0 -6
  147. package/resources/workflows/solo-developer.md +0 -3
  148. package/scripts/simulate-workspaces.ts +0 -55
  149. package/skills-lock.json +0 -11
  150. package/src/analyzers/detect-apis.ts +0 -290
  151. package/src/analyzers/detect-deps.ts +0 -315
  152. package/src/analyzers/detect-existing.ts +0 -74
  153. package/src/analyzers/detect-ports.ts +0 -110
  154. package/src/analyzers/index.ts +0 -97
  155. package/src/analyzers/messaging-analyzer.ts +0 -254
  156. package/src/analyzers/readme-summarizer.ts +0 -102
  157. package/src/analyzers/run-analyzer.ts +0 -269
  158. package/src/analyzers/tech-stack.ts +0 -283
  159. package/src/commands/adapter.ts +0 -348
  160. package/src/commands/add-repo.ts +0 -156
  161. package/src/commands/commands.test.ts +0 -156
  162. package/src/commands/commit.ts +0 -131
  163. package/src/commands/config.ts +0 -52
  164. package/src/commands/create.ts +0 -204
  165. package/src/commands/desktop.ts +0 -128
  166. package/src/commands/diff.ts +0 -125
  167. package/src/commands/doctor.ts +0 -370
  168. package/src/commands/handoff.ts +0 -266
  169. package/src/commands/init.ts +0 -134
  170. package/src/commands/list.ts +0 -46
  171. package/src/commands/logs.ts +0 -63
  172. package/src/commands/mcp.ts +0 -94
  173. package/src/commands/open.ts +0 -129
  174. package/src/commands/refresh.ts +0 -137
  175. package/src/commands/remove.ts +0 -98
  176. package/src/commands/start.ts +0 -117
  177. package/src/commands/status.ts +0 -54
  178. package/src/commands/stop.ts +0 -55
  179. package/src/commands/sync.ts +0 -147
  180. package/src/commands/tui.ts +0 -484
  181. package/src/commands/ui.ts +0 -111
  182. package/src/core/adapters/local-storage.ts +0 -66
  183. package/src/core/adapters/obsidian-storage.ts +0 -151
  184. package/src/core/adapters/registry.ts +0 -44
  185. package/src/core/adapters/storage.test.ts +0 -174
  186. package/src/core/adapters/vault-storage.ts +0 -86
  187. package/src/core/config.test.ts +0 -96
  188. package/src/core/config.ts +0 -140
  189. package/src/core/graph.ts +0 -344
  190. package/src/core/plugins/index.ts +0 -17
  191. package/src/core/plugins/loader.ts +0 -36
  192. package/src/core/ports/storage.ts +0 -71
  193. package/src/core/scanner.test.ts +0 -61
  194. package/src/core/scanner.ts +0 -91
  195. package/src/core/storage.ts +0 -37
  196. package/src/core/workspace.ts +0 -487
  197. package/src/core/worktree.ts +0 -120
  198. package/src/generators/antigravity.ts +0 -32
  199. package/src/generators/base.ts +0 -243
  200. package/src/generators/claude.ts +0 -34
  201. package/src/generators/codex.ts +0 -48
  202. package/src/generators/copilot.ts +0 -56
  203. package/src/generators/cursor.ts +0 -44
  204. package/src/generators/diff-context.ts +0 -75
  205. package/src/generators/index.ts +0 -322
  206. package/src/generators/map-generator.test.ts +0 -159
  207. package/src/generators/map-generator.ts +0 -539
  208. package/src/generators/plan-generator.ts +0 -483
  209. package/src/generators/skills-generator.ts +0 -259
  210. package/src/index.ts +0 -539
  211. package/src/mcp/server.ts +0 -294
  212. package/src/orchestration/detect.ts +0 -313
  213. package/src/orchestration/index.ts +0 -7
  214. package/src/orchestration/runner.ts +0 -283
  215. package/src/server.test.ts +0 -618
  216. package/src/server.ts +0 -1314
  217. package/src/types.ts +0 -415
  218. package/src/utils/detect-ai.test.ts +0 -44
  219. package/src/utils/detect-ai.ts +0 -84
  220. package/src/utils/detect-editors.test.ts +0 -48
  221. package/src/utils/detect-editors.ts +0 -57
  222. package/src/utils/git.test.ts +0 -74
  223. package/src/utils/git.ts +0 -117
  224. package/src/utils/local-ai.test.ts +0 -130
  225. package/src/utils/local-ai.ts +0 -116
  226. package/src/utils/multi-git.ts +0 -313
  227. package/src/utils/prompts.ts +0 -209
  228. package/src/utils/session-finder.ts +0 -483
  229. package/src/utils/system-scanner.test.ts +0 -89
  230. package/src/utils/system-scanner.ts +0 -96
  231. package/src/utils/update-check.ts +0 -286
  232. package/src/utils/workflow-advisor.ts +0 -118
  233. package/src/utils/workflows.test.ts +0 -114
  234. package/src/utils/workflows.ts +0 -178
  235. package/tsconfig.json +0 -19
  236. package/vitest.config.ts +0 -14
@@ -1,666 +0,0 @@
1
- import React from 'react';
2
- import { Play, RefreshCw, FolderGit2, Sparkles, ExternalLink, Trash2, Terminal, Cpu, Layers, Copy, Check } from 'lucide-react';
3
- import type { Feature, RunningService, ServiceConfig, OrchestrationDetection, RepoInfo, DetectedAI } from '../../types.js';
4
-
5
- // Feature subcomponents
6
- import { SessionHistory } from '../sessions/SessionHistory.js';
7
- import { ServiceConsole } from '../services/ServiceConsole.js';
8
- import { ChangesViewer } from '../changes/ChangesViewer.js';
9
- import { KnowledgeBase } from '../knowledge/KnowledgeBase.js';
10
- import { ImplementationPlan } from '../plan/ImplementationPlan.js';
11
-
12
- interface WorkspaceListProps {
13
- workspacesLoading: boolean;
14
- workspaces: Feature[];
15
- activeWsId: string | null;
16
- setActiveWsId: (id: string | null) => void;
17
- resumingWs: string | null;
18
- handleResumeSession: (ws: Feature, sessionId?: string, assistant?: string) => Promise<void>;
19
- handleCopyPrompt: (ws: Feature) => void;
20
- handleOpenInEditor: (workspacePath: string) => Promise<void>;
21
- fetchWorkspaces: () => Promise<void>;
22
-
23
- // Deletion and repo addition props
24
- repos: RepoInfo[];
25
- deleteWsLoading: string | null;
26
- addRepoLoading: boolean;
27
- handleDeleteWorkspace: (wsName: string) => Promise<void>;
28
- handleAddRepo: (wsName: string, repoPath: string) => Promise<void>;
29
-
30
- // ServiceConsole props
31
- services: ServiceConfig[];
32
- runningServices: RunningService[];
33
- selectedLogService: string | null;
34
- serviceLogs: string;
35
- logsEndRef: React.RefObject<HTMLDivElement | null>;
36
- setSelectedLogService: (val: string | null) => void;
37
- handleStartServices: (wsId: string) => Promise<void>;
38
- handleStopServices: (wsId: string) => Promise<void>;
39
- orchTools: OrchestrationDetection[];
40
- servicesLoading: boolean;
41
-
42
-
43
- // ChangesViewer props
44
- gitChanges: any[];
45
- gitChangesLoading: boolean;
46
- syncLoading: boolean;
47
- syncResults: any[] | null;
48
- commitMessage: string;
49
- showCommitModal: boolean;
50
- commitLoading: boolean;
51
- commitResults: any[] | null;
52
- setSyncResults: (val: any[] | null) => void;
53
- setCommitResults: (val: any[] | null) => void;
54
- setCommitMessage: (val: string) => void;
55
- setShowCommitModal: (val: boolean) => void;
56
- fetchGitChanges: (wsId: string) => Promise<void>;
57
- handleSyncAll: (wsId: string) => Promise<void>;
58
- handleCommitAll: (wsId: string) => Promise<void>;
59
-
60
- // KnowledgeBase props
61
- knowledgeContent: string;
62
- knowledgeLoading: boolean;
63
- isEditingKnowledge: boolean;
64
- editedKnowledge: string;
65
- saveKnowledgeLoading: boolean;
66
- setEditedKnowledge: (val: string) => void;
67
- setIsEditingKnowledge: (val: boolean) => void;
68
- handleSaveKnowledge: (wsId: string) => Promise<void>;
69
-
70
- // ImplementationPlan props
71
- planContent: string;
72
- planLoading: boolean;
73
-
74
- // SessionHistory props
75
- sessions: any[];
76
- sessionsLoading: boolean;
77
- activeSession: any | null;
78
- transcript: any[];
79
- transcriptLoading: boolean;
80
- setActiveSession: (val: any | null) => void;
81
- setTranscript: (val: any[]) => void;
82
- fetchSessionTranscript: (assistant: string, sessionId: string) => Promise<void>;
83
-
84
- // Navigation tab states
85
- subTab: 'overview' | 'sessions' | 'services' | 'changes' | 'knowledge' | 'plan';
86
- setSubTab: (tab: 'overview' | 'sessions' | 'services' | 'changes' | 'knowledge' | 'plan') => void;
87
- aiAssistants: DetectedAI[];
88
- }
89
-
90
- const parseInlineStyles = (text: string): React.ReactNode => {
91
- const tokenRegex = /(\*\*.*?\*\*|`.*?`|\*.*?\*)/g;
92
- const parts = text.split(tokenRegex);
93
-
94
- return parts.map((part, index) => {
95
- if (part.startsWith('**') && part.endsWith('**')) {
96
- return <strong key={index} className="font-bold text-gray-200">{part.slice(2, -2)}</strong>;
97
- }
98
- if (part.startsWith('`') && part.endsWith('`')) {
99
- return <code key={index} className="px-1.5 py-0.5 bg-gray-900 border border-gray-800 text-cyan-400 rounded font-mono text-[10px]">{part.slice(1, -1)}</code>;
100
- }
101
- if (part.startsWith('*') && part.endsWith('*')) {
102
- return <em key={index} className="italic text-gray-300">{part.slice(1, -1)}</em>;
103
- }
104
- return part;
105
- });
106
- };
107
-
108
- const renderFormattedDescription = (text: string) => {
109
- if (!text) return null;
110
-
111
- const lines = text.split('\n');
112
- const elements: React.ReactNode[] = [];
113
- let currentList: { type: 'ul' | 'ol'; items: string[] } | null = null;
114
-
115
- const flushList = (key: number) => {
116
- if (currentList) {
117
- const ListTag = currentList.type;
118
- elements.push(
119
- <ListTag key={`list-${key}`} className={currentList.type === 'ul' ? 'list-disc pl-5 my-1.5 space-y-1' : 'list-decimal pl-5 my-1.5 space-y-1'}>
120
- {currentList.items.map((item, idx) => (
121
- <li key={`item-${idx}`} className="leading-relaxed">{parseInlineStyles(item)}</li>
122
- ))}
123
- </ListTag>
124
- );
125
- currentList = null;
126
- }
127
- };
128
-
129
- lines.forEach((line, idx) => {
130
- const trimmed = line.trim();
131
-
132
- // Check for bullet list item
133
- const bulletMatch = line.match(/^(\s*)[-*+]\s+(.*)/);
134
- if (bulletMatch) {
135
- if (!currentList || currentList.type !== 'ul') {
136
- flushList(idx);
137
- currentList = { type: 'ul', items: [] };
138
- }
139
- currentList.items.push(bulletMatch[2]);
140
- return;
141
- }
142
-
143
- // Check for numbered list item
144
- const numberMatch = line.match(/^(\s*)\d+\.\s+(.*)/);
145
- if (numberMatch) {
146
- if (!currentList || currentList.type !== 'ol') {
147
- flushList(idx);
148
- currentList = { type: 'ol', items: [] };
149
- }
150
- currentList.items.push(numberMatch[2]);
151
- return;
152
- }
153
-
154
- // If not a list item, flush any active list first
155
- flushList(idx);
156
-
157
- if (trimmed === '') {
158
- elements.push(<div key={`empty-${idx}`} className="h-2" />);
159
- } else {
160
- elements.push(
161
- <div key={`line-${idx}`} className="leading-relaxed">
162
- {parseInlineStyles(line)}
163
- </div>
164
- );
165
- }
166
- });
167
-
168
- flushList(lines.length);
169
- return <div className="space-y-1">{elements}</div>;
170
- };
171
-
172
- export const WorkspaceList: React.FC<WorkspaceListProps> = ({
173
- workspacesLoading,
174
- workspaces,
175
- activeWsId,
176
- setActiveWsId,
177
- resumingWs,
178
- handleResumeSession,
179
- handleCopyPrompt,
180
- handleOpenInEditor,
181
- fetchWorkspaces,
182
- repos,
183
- deleteWsLoading,
184
- addRepoLoading,
185
- handleDeleteWorkspace,
186
- handleAddRepo,
187
-
188
- // ServiceConsole
189
- services,
190
- runningServices,
191
- selectedLogService,
192
- serviceLogs,
193
- logsEndRef,
194
- setSelectedLogService,
195
- handleStartServices,
196
- handleStopServices,
197
- orchTools,
198
- servicesLoading,
199
-
200
-
201
- // ChangesViewer
202
- gitChanges,
203
- gitChangesLoading,
204
- syncLoading,
205
- syncResults,
206
- commitMessage,
207
- showCommitModal,
208
- commitLoading,
209
- commitResults,
210
- setSyncResults,
211
- setCommitResults,
212
- setCommitMessage,
213
- setShowCommitModal,
214
- fetchGitChanges,
215
- handleSyncAll,
216
- handleCommitAll,
217
-
218
- // KnowledgeBase
219
- knowledgeContent,
220
- knowledgeLoading,
221
- isEditingKnowledge,
222
- editedKnowledge,
223
- saveKnowledgeLoading,
224
- setEditedKnowledge,
225
- setIsEditingKnowledge,
226
- handleSaveKnowledge,
227
-
228
- // ImplementationPlan
229
- planContent,
230
- planLoading,
231
-
232
- // SessionHistory
233
- sessions,
234
- sessionsLoading,
235
- activeSession,
236
- transcript,
237
- transcriptLoading,
238
- setActiveSession,
239
- setTranscript,
240
- fetchSessionTranscript,
241
-
242
- // Tabs
243
- subTab,
244
- setSubTab,
245
- aiAssistants,
246
- }) => {
247
- const [copiedText, setCopiedText] = React.useState<string | null>(null);
248
- const handleCopy = (text: string) => {
249
- navigator.clipboard.writeText(text);
250
- setCopiedText(text);
251
- setTimeout(() => setCopiedText(null), 2000);
252
- };
253
-
254
- return (
255
- <div className="max-w-5xl mx-auto">
256
- <header className="flex justify-between items-center mb-8">
257
- <div>
258
- <h1 className="text-3xl font-extrabold tracking-tight text-white mb-2">Active Workspaces</h1>
259
- <p className="text-sm text-gray-400">Monitor running servers, libraries, and processes on your development branches.</p>
260
- </div>
261
- <button
262
- className="inline-flex items-center justify-center gap-2 px-4 py-2 bg-gray-900 border border-gray-800 hover:bg-gray-800 hover:border-gray-700 rounded-lg text-xs font-semibold transition-all cursor-pointer"
263
- onClick={fetchWorkspaces}
264
- disabled={workspacesLoading}
265
- >
266
- <RefreshCw size={14} className={workspacesLoading ? 'animate-spin text-cyan-400' : ''} /> Refresh
267
- </button>
268
- </header>
269
-
270
- {workspacesLoading ? (
271
- <div className="flex flex-col items-center py-40 gap-4 text-gray-400">
272
- <RefreshCw className="animate-spin text-cyan-400" size={32} />
273
- <span className="text-sm">Fetching workspace details...</span>
274
- </div>
275
- ) : workspaces.length === 0 ? (
276
- <div className="flex flex-col items-center justify-center py-24 bg-[#111827]/20 border border-gray-800/80 rounded-xl text-center">
277
- <FolderGit2 size={44} className="text-gray-600 mb-4" />
278
- <h3 className="text-lg font-bold text-white">No Active Workspaces</h3>
279
- <p className="text-xs text-gray-505 max-w-sm mt-1">
280
- No feature workspaces were detected in your development folder. Create a workspace to start.
281
- </p>
282
- </div>
283
- ) : (
284
- <div className="flex flex-col gap-6">
285
- {workspaces.map((ws) => {
286
- const isExpanded = activeWsId === ws.branchName;
287
- return (
288
- <div key={ws.id} className="glass-card p-8 rounded-2xl shadow-xl hover:shadow-2xl border border-slate-800/40 relative overflow-hidden transition-all duration-300 before:absolute before:inset-x-0 before:top-0 before:h-[1px] before:bg-gradient-to-r before:from-cyan-500/10 via-violet-500/10 to-transparent">
289
- <div className="flex justify-between items-start gap-4 mb-4">
290
- <div>
291
- <h3 className="text-lg font-bold text-white hover:text-cyan-400 transition-colors">{ws.branchName}</h3>
292
- <div className="flex items-center gap-3 text-xs text-slate-500 mt-1">
293
- <span>Created: {new Date(ws.createdAt).toLocaleDateString()}</span>
294
- <span className="h-1 w-1 rounded-full bg-slate-850"></span>
295
- <span>{ws.repos.length} repos</span>
296
- </div>
297
- </div>
298
- <div className="flex gap-2">
299
- <button
300
- className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 bg-emerald-650 hover:bg-emerald-600 text-white rounded-xl text-xs font-semibold transition-all cursor-pointer shadow-md shadow-emerald-600/10 disabled:opacity-40 disabled:cursor-not-allowed"
301
- onClick={() => handleResumeSession(ws)}
302
- disabled={resumingWs === ws.branchName}
303
- >
304
- <Play size={12} className={resumingWs === ws.branchName ? 'animate-spin' : ''} />
305
- {resumingWs === ws.branchName ? 'Resuming...' : 'Resume Session'}
306
- </button>
307
- <button
308
- className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 bg-slate-900 border border-slate-800/80 hover:bg-slate-800 hover:border-slate-700 rounded-xl text-xs font-semibold transition-all cursor-pointer text-slate-350 hover:text-white"
309
- onClick={() => handleCopyPrompt(ws)}
310
- >
311
- <Sparkles size={12} className="text-cyan-400" /> Copy Prompt
312
- </button>
313
- <button
314
- className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 bg-slate-900 border border-slate-800/80 hover:bg-slate-800 hover:border-slate-700 rounded-xl text-xs font-semibold transition-all cursor-pointer text-slate-350 hover:text-white"
315
- onClick={() => handleOpenInEditor(ws.workspacePath)}
316
- >
317
- <ExternalLink size={12} /> Open
318
- </button>
319
- <button
320
- className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 bg-cyan-600 hover:bg-cyan-500 text-white rounded-xl text-xs font-semibold transition-all cursor-pointer shadow-md shadow-cyan-650/10"
321
- onClick={() => {
322
- if (isExpanded) {
323
- setActiveWsId(null);
324
- } else {
325
- setActiveWsId(ws.branchName);
326
- }
327
- }}
328
- >
329
- {isExpanded ? 'Hide Runner' : 'Orchestrate'}
330
- </button>
331
- <button
332
- className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 bg-red-950/40 border border-red-900/60 hover:bg-red-900/60 hover:border-red-800 rounded-xl text-xs font-semibold text-red-200 transition-all cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed animate-fade-in"
333
- onClick={() => handleDeleteWorkspace(ws.branchName)}
334
- disabled={deleteWsLoading === ws.branchName}
335
- >
336
- <Trash2 size={12} className={deleteWsLoading === ws.branchName ? 'animate-spin' : ''} />
337
- {deleteWsLoading === ws.branchName ? 'Deleting...' : 'Delete'}
338
- </button>
339
- </div>
340
- </div>
341
-
342
- <div className="text-xs text-slate-350 bg-slate-950/60 border-l-2 border-cyan-500/80 rounded-r-xl px-5 py-3.5 mb-5 font-normal leading-relaxed relative overflow-hidden before:absolute before:inset-y-0 before:right-0 before:w-12 before:bg-gradient-to-r before:from-transparent before:to-cyan-500/2">
343
- {renderFormattedDescription(ws.description)}
344
- </div>
345
-
346
- <div className="flex items-center gap-3 mb-4">
347
- <div className="flex flex-wrap gap-2">
348
- {ws.repos.map((repoPath) => (
349
- <span key={repoPath} className="text-[10px] px-2.5 py-1.5 bg-slate-950/60 border border-slate-850 text-slate-300 rounded-lg font-semibold select-all">
350
- {repoPath.split(/[\\/]/).pop()}
351
- </span>
352
- ))}
353
- </div>
354
-
355
- {repos.filter((r) => !ws.repos.includes(r.path)).length > 0 && (
356
- <div className="flex items-center gap-1.5 ml-auto">
357
- <select
358
- id={`add-repo-select-feature-${ws.branchName}`}
359
- className="bg-slate-905 border border-slate-800 text-slate-300 rounded-lg text-[10px] px-3 py-1.5 font-semibold outline-none focus:border-cyan-500 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition-all hover:border-slate-700"
360
- defaultValue=""
361
- onChange={async (e) => {
362
- const val = e.target.value;
363
- if (val) {
364
- if (window.confirm(`Are you sure you want to add repository "${val.split(/[\\/]/).pop()}" to this workspace?\nThis will create a new git worktree and re-run analysis.`)) {
365
- await handleAddRepo(ws.branchName, val);
366
- }
367
- e.target.value = "";
368
- }
369
- }}
370
- disabled={addRepoLoading}
371
- >
372
- <option value="" disabled>+ Add Repository</option>
373
- {repos
374
- .filter((r) => !ws.repos.includes(r.path))
375
- .map((r) => (
376
- <option key={r.path} value={r.path}>{r.name}</option>
377
- ))
378
- }
379
- </select>
380
- </div>
381
- )}
382
- </div>
383
-
384
- {/* Expansion: Process management console */}
385
- {isExpanded && (
386
- <div className="mt-6 pt-6 border-t border-slate-850/80">
387
- {/* Sub-tab Navigation */}
388
- <div className="flex flex-wrap items-center gap-1.5 p-1 bg-slate-950/60 rounded-xl border border-slate-900/60 mb-6 w-max max-w-full overflow-x-auto">
389
- <button
390
- className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
391
- subTab === 'overview'
392
- ? 'bg-cyan-950/30 border border-cyan-900/60 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.1)]'
393
- : 'text-slate-500 hover:text-slate-300 hover:bg-slate-900/40 border border-transparent'
394
- }`}
395
- onClick={() => setSubTab('overview')}
396
- >
397
- Harness & Capabilities
398
- </button>
399
- <button
400
- className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
401
- subTab === 'sessions'
402
- ? 'bg-cyan-950/30 border border-cyan-900/60 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.1)]'
403
- : 'text-slate-500 hover:text-slate-300 hover:bg-slate-900/40 border border-transparent'
404
- }`}
405
- onClick={() => setSubTab('sessions')}
406
- >
407
- AI Session History
408
- </button>
409
- <button
410
- className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
411
- subTab === 'services'
412
- ? 'bg-cyan-950/30 border border-cyan-900/60 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.1)]'
413
- : 'text-slate-500 hover:text-slate-300 hover:bg-slate-900/40 border border-transparent'
414
- }`}
415
- onClick={() => setSubTab('services')}
416
- >
417
- Orchestrated Services
418
- </button>
419
- <button
420
- className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
421
- subTab === 'changes'
422
- ? 'bg-cyan-950/30 border border-cyan-900/60 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.1)]'
423
- : 'text-slate-500 hover:text-slate-300 hover:bg-slate-900/40 border border-transparent'
424
- }`}
425
- onClick={() => setSubTab('changes')}
426
- >
427
- Active Changes (AI Diffs)
428
- </button>
429
- <button
430
- className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
431
- subTab === 'knowledge'
432
- ? 'bg-cyan-950/30 border border-cyan-900/60 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.1)]'
433
- : 'text-slate-500 hover:text-slate-300 hover:bg-slate-900/40 border border-transparent'
434
- }`}
435
- onClick={() => setSubTab('knowledge')}
436
- >
437
- Knowledge Base
438
- </button>
439
- <button
440
- className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
441
- subTab === 'plan'
442
- ? 'bg-cyan-950/30 border border-cyan-900/60 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.1)]'
443
- : 'text-slate-500 hover:text-slate-300 hover:bg-slate-900/40 border border-transparent'
444
- }`}
445
- onClick={() => setSubTab('plan')}
446
- >
447
- Implementation Plan
448
- </button>
449
- </div>
450
-
451
- {subTab === 'overview' && (
452
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm text-gray-300 animate-fade-in">
453
- {/* Section 1: AI Assistant Harnesses */}
454
- <div className="bg-gray-950/20 border border-gray-800/80 rounded-xl p-5">
455
- <div className="flex items-center gap-2 mb-4 border-b border-gray-800/80 pb-3">
456
- <Cpu size={18} className="text-cyan-400" />
457
- <h4 className="font-bold text-white">AI Assistant Harnesses</h4>
458
- </div>
459
- <p className="text-xs text-gray-400 mb-4">
460
- These coding assistants are configured to run contextually inside this workspace. Select an assistant to see its setup.
461
- </p>
462
- <div className="flex flex-col gap-3">
463
- {['antigravity', 'claude', 'copilot', 'cursor', 'codex'].map((name) => {
464
- const isConfigured = ws.assistants.includes(name as any);
465
- const isDetected = aiAssistants.find((a) => a.name === name)?.detected;
466
- let displayName = name.charAt(0).toUpperCase() + name.slice(1);
467
- if (name === 'antigravity') displayName = 'Antigravity (Ollama/Gemini)';
468
- else if (name === 'codex') displayName = 'OpenAI Codex';
469
- else if (name === 'copilot') displayName = 'GitHub Copilot';
470
- else if (name === 'claude') displayName = 'Claude Code';
471
-
472
- let resumeCmd = '';
473
- if (name === 'antigravity') resumeCmd = 'agy --continue';
474
- else if (name === 'claude') resumeCmd = 'claude --resume';
475
- else if (name === 'codex') resumeCmd = 'codex resume';
476
- else if (name === 'copilot') resumeCmd = 'copilot --resume';
477
-
478
- return (
479
- <div key={name} className="flex flex-col p-3 bg-gray-900/40 border border-gray-800/60 rounded-lg">
480
- <div className="flex items-center justify-between gap-2">
481
- <div className="flex items-center gap-2">
482
- <span className={`h-2 w-2 rounded-full ${isConfigured ? 'bg-cyan-500 shadow-sm' : 'bg-gray-700'}`}></span>
483
- <span className="font-semibold text-xs text-gray-200">{displayName}</span>
484
- </div>
485
- <div className="flex items-center gap-1.5 text-[10px] font-medium">
486
- {isConfigured && <span className="text-cyan-400 bg-cyan-950/20 px-2 py-0.5 rounded border border-cyan-900/40 text-[9px] font-bold uppercase tracking-wider">Active</span>}
487
- {isDetected ? (
488
- <span className="text-emerald-400 bg-emerald-500/10 px-2 py-0.5 rounded border border-emerald-500/20 text-[9px] font-bold uppercase tracking-wider">Installed</span>
489
- ) : (
490
- <span className="text-gray-500 bg-gray-950 px-2 py-0.5 rounded border border-gray-800/60 text-[9px] font-bold uppercase tracking-wider">Not Probed</span>
491
- )}
492
- </div>
493
- </div>
494
- {isConfigured && resumeCmd && (
495
- <div className="mt-2.5 flex items-center justify-between gap-2 bg-gray-950/80 px-2.5 py-1.5 rounded border border-gray-800/60 font-mono text-[10px]">
496
- <span className="text-cyan-400 truncate">{resumeCmd}</span>
497
- <button
498
- onClick={() => handleCopy(resumeCmd)}
499
- className="text-gray-400 hover:text-white transition-colors cursor-pointer"
500
- title="Copy Resume Command"
501
- >
502
- {copiedText === resumeCmd ? <Check size={11} className="text-emerald-400" /> : <Copy size={11} />}
503
- </button>
504
- </div>
505
- )}
506
- </div>
507
- );
508
- })}
509
- </div>
510
- </div>
511
-
512
- {/* Section 2: Model Context Protocol (MCP) */}
513
- <div className="flex flex-col gap-6">
514
- <div className="bg-gray-950/20 border border-gray-800/80 rounded-xl p-5">
515
- <div className="flex items-center gap-2 mb-4 border-b border-gray-800/80 pb-3">
516
- <Layers size={18} className="text-cyan-400" />
517
- <h4 className="font-bold text-white">Local MCP Server (Tools)</h4>
518
- </div>
519
- <p className="text-xs text-gray-400 mb-4">
520
- NexusFlow runs a Model Context Protocol (MCP) server that exposes local workspace-scoped tools to your AI agents.
521
- </p>
522
- <div className="flex flex-col gap-3.5">
523
- <div className="flex flex-col gap-1">
524
- <div className="flex items-center gap-2 text-xs font-semibold text-cyan-455">
525
- <code>search_workspace</code>
526
- </div>
527
- <span className="text-[11px] text-gray-400">
528
- Extremely fast query/regex search across all microservices and repos in the workspace.
529
- </span>
530
- </div>
531
- <div className="flex flex-col gap-1 border-t border-gray-800/40 pt-2.5">
532
- <div className="flex items-center gap-2 text-xs font-semibold text-cyan-455">
533
- <code>get_service_logs</code>
534
- </div>
535
- <span className="text-[11px] text-gray-400">
536
- Fetch and parse recent stdout/stderr output from any background service running in the workspace.
537
- </span>
538
- </div>
539
- <div className="flex flex-col gap-1 border-t border-gray-800/40 pt-2.5">
540
- <div className="flex items-center gap-2 text-xs font-semibold text-cyan-455">
541
- <code>delegate_to_local_agent</code>
542
- </div>
543
- <span className="text-[11px] text-gray-400">
544
- Delegate sub-tasks or log analysis to a lightweight LLM running locally to optimize remote token usage.
545
- </span>
546
- </div>
547
- </div>
548
- </div>
549
-
550
- {/* CLI shortcuts */}
551
- <div className="bg-gray-950/20 border border-gray-800/80 rounded-xl p-5">
552
- <div className="flex items-center gap-2 mb-4 border-b border-gray-800/80 pb-3">
553
- <Terminal size={18} className="text-cyan-400" />
554
- <h4 className="font-bold text-white">Workspace CLI Shortcuts</h4>
555
- </div>
556
- <div className="flex flex-col gap-2.5">
557
- {[
558
- { cmd: 'nexusflow start', desc: 'Spin up detected backend/frontend services' },
559
- { cmd: 'nexusflow sync', desc: 'Rebase worktrees with upstream defaults' },
560
- { cmd: 'nexusflow commit -m "update"', desc: 'Stage and commit changes across repos' },
561
- { cmd: 'nexusflow diff', desc: 'Unified codebase diff across repos' },
562
- { cmd: 'nexusflow handoff', desc: 'Generate resume context file' }
563
- ].map(({ cmd, desc }) => (
564
- <div key={cmd} className="flex flex-col gap-1">
565
- <div className="flex items-center justify-between gap-2">
566
- <code className="text-xs text-cyan-400">{cmd}</code>
567
- <button
568
- onClick={() => handleCopy(cmd)}
569
- className="text-gray-400 hover:text-white transition-colors cursor-pointer"
570
- title="Copy Command"
571
- >
572
- {copiedText === cmd ? <Check size={11} className="text-emerald-400" /> : <Copy size={11} />}
573
- </button>
574
- </div>
575
- <span className="text-[11px] text-gray-550">{desc}</span>
576
- </div>
577
- ))}
578
- </div>
579
- </div>
580
- </div>
581
- </div>
582
- )}
583
-
584
- {subTab === 'sessions' && (
585
- <SessionHistory
586
- ws={ws}
587
- sessions={sessions}
588
- sessionsLoading={sessionsLoading}
589
- activeSession={activeSession}
590
- transcript={transcript}
591
- transcriptLoading={transcriptLoading}
592
- workspaces={workspaces}
593
- setActiveSession={setActiveSession}
594
- setTranscript={setTranscript}
595
- fetchSessionTranscript={fetchSessionTranscript}
596
- handleResumeSession={handleResumeSession}
597
- />
598
- )}
599
-
600
- {subTab === 'services' && (
601
- <ServiceConsole
602
- ws={ws}
603
- services={services}
604
- runningServices={runningServices}
605
- selectedLogService={selectedLogService}
606
- serviceLogs={serviceLogs}
607
- logsEndRef={logsEndRef}
608
- setSelectedLogService={setSelectedLogService}
609
- handleStartServices={handleStartServices}
610
- handleStopServices={handleStopServices}
611
- orchTools={orchTools}
612
- servicesLoading={servicesLoading}
613
- />
614
- )}
615
-
616
- {subTab === 'changes' && (
617
- <ChangesViewer
618
- ws={ws}
619
- gitChanges={gitChanges}
620
- gitChangesLoading={gitChangesLoading}
621
- syncLoading={syncLoading}
622
- syncResults={syncResults}
623
- commitMessage={commitMessage}
624
- showCommitModal={showCommitModal}
625
- commitLoading={commitLoading}
626
- commitResults={commitResults}
627
- setSyncResults={setSyncResults}
628
- setCommitResults={setCommitResults}
629
- setCommitMessage={setCommitMessage}
630
- setShowCommitModal={setShowCommitModal}
631
- fetchGitChanges={fetchGitChanges}
632
- handleSyncAll={handleSyncAll}
633
- handleCommitAll={handleCommitAll}
634
- />
635
- )}
636
-
637
- {subTab === 'knowledge' && (
638
- <KnowledgeBase
639
- ws={ws}
640
- knowledgeContent={knowledgeContent}
641
- knowledgeLoading={knowledgeLoading}
642
- isEditingKnowledge={isEditingKnowledge}
643
- editedKnowledge={editedKnowledge}
644
- saveKnowledgeLoading={saveKnowledgeLoading}
645
- setEditedKnowledge={setEditedKnowledge}
646
- setIsEditingKnowledge={setIsEditingKnowledge}
647
- handleSaveKnowledge={handleSaveKnowledge}
648
- />
649
- )}
650
-
651
- {subTab === 'plan' && (
652
- <ImplementationPlan
653
- planContent={planContent}
654
- planLoading={planLoading}
655
- />
656
- )}
657
- </div>
658
- )}
659
- </div>
660
- );
661
- })}
662
- </div>
663
- )}
664
- </div>
665
- );
666
- };