@mrpatronz/nexusflow 0.2.17 → 0.2.19

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 (320) hide show
  1. package/.agents/skills/ccc/SKILL.md +71 -0
  2. package/.agents/skills/ccc/references/management.md +110 -0
  3. package/.agents/skills/ccc/references/settings.md +126 -0
  4. package/.github/dependabot.yml +52 -52
  5. package/.github/workflows/ci.yml +44 -0
  6. package/.github/workflows/dependabot-merge.yml +53 -53
  7. package/.github/workflows/release-desktop.yml +67 -0
  8. package/.github/workflows/release-npm.yml +65 -0
  9. package/.github/workflows/release-vscode.yml +61 -0
  10. package/.vscode/launch.json +17 -17
  11. package/.vscode/tasks.json +17 -17
  12. package/GETTING_STARTED.md +114 -114
  13. package/README.md +364 -336
  14. package/desktop/build-installer.js +228 -228
  15. package/desktop/build.js +118 -118
  16. package/desktop/installer/nexusflow.iss +35 -35
  17. package/desktop/neutralino.config.json +38 -38
  18. package/desktop/package-lock.json +1060 -1060
  19. package/desktop/package.json +14 -14
  20. package/dist/commands/adapter.d.ts +27 -0
  21. package/dist/commands/adapter.d.ts.map +1 -0
  22. package/dist/commands/adapter.js +318 -0
  23. package/dist/commands/adapter.js.map +1 -0
  24. package/dist/commands/add-repo.d.ts.map +1 -1
  25. package/dist/commands/add-repo.js +0 -1
  26. package/dist/commands/add-repo.js.map +1 -1
  27. package/dist/commands/commands.test.js +2 -2
  28. package/dist/commands/commands.test.js.map +1 -1
  29. package/dist/commands/config.d.ts +22 -0
  30. package/dist/commands/config.d.ts.map +1 -0
  31. package/dist/commands/config.js +50 -0
  32. package/dist/commands/config.js.map +1 -0
  33. package/dist/commands/create.d.ts.map +1 -1
  34. package/dist/commands/create.js +14 -17
  35. package/dist/commands/create.js.map +1 -1
  36. package/dist/commands/doctor.d.ts.map +1 -1
  37. package/dist/commands/doctor.js +11 -0
  38. package/dist/commands/doctor.js.map +1 -1
  39. package/dist/commands/refresh.d.ts +1 -0
  40. package/dist/commands/refresh.d.ts.map +1 -1
  41. package/dist/commands/refresh.js +2 -14
  42. package/dist/commands/refresh.js.map +1 -1
  43. package/dist/commands/sync.d.ts.map +1 -1
  44. package/dist/commands/sync.js +36 -54
  45. package/dist/commands/sync.js.map +1 -1
  46. package/dist/commands/tui.d.ts.map +1 -1
  47. package/dist/commands/tui.js +63 -10
  48. package/dist/commands/tui.js.map +1 -1
  49. package/dist/core/adapters/local-storage.d.ts +14 -0
  50. package/dist/core/adapters/local-storage.d.ts.map +1 -0
  51. package/dist/core/adapters/local-storage.js +58 -0
  52. package/dist/core/adapters/local-storage.js.map +1 -0
  53. package/dist/core/adapters/obsidian-storage.d.ts +28 -0
  54. package/dist/core/adapters/obsidian-storage.d.ts.map +1 -0
  55. package/dist/core/adapters/obsidian-storage.js +135 -0
  56. package/dist/core/adapters/obsidian-storage.js.map +1 -0
  57. package/dist/core/adapters/registry.d.ts +8 -0
  58. package/dist/core/adapters/registry.d.ts.map +1 -0
  59. package/dist/core/adapters/registry.js +37 -0
  60. package/dist/core/adapters/registry.js.map +1 -0
  61. package/dist/core/adapters/storage.test.d.ts +2 -0
  62. package/dist/core/adapters/storage.test.d.ts.map +1 -0
  63. package/dist/core/adapters/storage.test.js +106 -0
  64. package/dist/core/adapters/storage.test.js.map +1 -0
  65. package/dist/core/adapters/vault-storage.d.ts +16 -0
  66. package/dist/core/adapters/vault-storage.d.ts.map +1 -0
  67. package/dist/core/adapters/vault-storage.js +77 -0
  68. package/dist/core/adapters/vault-storage.js.map +1 -0
  69. package/dist/core/config.d.ts.map +1 -1
  70. package/dist/core/config.js +28 -4
  71. package/dist/core/config.js.map +1 -1
  72. package/dist/core/plugins/index.d.ts +14 -0
  73. package/dist/core/plugins/index.d.ts.map +1 -0
  74. package/dist/core/plugins/index.js +2 -0
  75. package/dist/core/plugins/index.js.map +1 -0
  76. package/dist/core/plugins/loader.d.ts +2 -0
  77. package/dist/core/plugins/loader.d.ts.map +1 -0
  78. package/dist/core/plugins/loader.js +34 -0
  79. package/dist/core/plugins/loader.js.map +1 -0
  80. package/dist/core/ports/storage.d.ts +59 -0
  81. package/dist/core/ports/storage.d.ts.map +1 -0
  82. package/dist/core/ports/storage.js +10 -0
  83. package/dist/core/ports/storage.js.map +1 -0
  84. package/dist/core/storage.d.ts +10 -0
  85. package/dist/core/storage.d.ts.map +1 -0
  86. package/dist/core/storage.js +29 -0
  87. package/dist/core/storage.js.map +1 -0
  88. package/dist/core/sync.d.ts +44 -0
  89. package/dist/core/sync.d.ts.map +1 -0
  90. package/dist/core/sync.js +82 -0
  91. package/dist/core/sync.js.map +1 -0
  92. package/dist/core/workspace-state.d.ts +51 -0
  93. package/dist/core/workspace-state.d.ts.map +1 -0
  94. package/dist/core/workspace-state.js +108 -0
  95. package/dist/core/workspace-state.js.map +1 -0
  96. package/dist/core/workspace-state.test.d.ts +2 -0
  97. package/dist/core/workspace-state.test.d.ts.map +1 -0
  98. package/dist/core/workspace-state.test.js +84 -0
  99. package/dist/core/workspace-state.test.js.map +1 -0
  100. package/dist/core/workspace.d.ts.map +1 -1
  101. package/dist/core/workspace.js +110 -16
  102. package/dist/core/workspace.js.map +1 -1
  103. package/dist/generators/base.d.ts.map +1 -1
  104. package/dist/generators/base.js +80 -79
  105. package/dist/generators/base.js.map +1 -1
  106. package/dist/generators/codex.js +11 -11
  107. package/dist/generators/copilot.js +17 -17
  108. package/dist/generators/cursor.js +5 -5
  109. package/dist/generators/diff-context.d.ts +6 -0
  110. package/dist/generators/diff-context.d.ts.map +1 -0
  111. package/dist/generators/diff-context.js +68 -0
  112. package/dist/generators/diff-context.js.map +1 -0
  113. package/dist/generators/index.d.ts +1 -1
  114. package/dist/generators/index.d.ts.map +1 -1
  115. package/dist/generators/index.js +125 -68
  116. package/dist/generators/index.js.map +1 -1
  117. package/dist/generators/map-generator.d.ts.map +1 -1
  118. package/dist/generators/map-generator.js +21 -25
  119. package/dist/generators/map-generator.js.map +1 -1
  120. package/dist/generators/plan-generator.d.ts.map +1 -1
  121. package/dist/generators/plan-generator.js +3 -5
  122. package/dist/generators/plan-generator.js.map +1 -1
  123. package/dist/generators/skills-generator.js +28 -28
  124. package/dist/gui/assets/index-Bdbld6yY.css +1 -0
  125. package/dist/gui/assets/index-D8iJPvnk.js +26 -0
  126. package/dist/gui/icons.svg +24 -24
  127. package/dist/gui/index.html +18 -18
  128. package/dist/index.js +131 -20
  129. package/dist/index.js.map +1 -1
  130. package/dist/mcp/server.d.ts.map +1 -1
  131. package/dist/mcp/server.js +42 -0
  132. package/dist/mcp/server.js.map +1 -1
  133. package/dist/server.d.ts.map +1 -1
  134. package/dist/server.js +140 -56
  135. package/dist/server.js.map +1 -1
  136. package/dist/server.test.js +105 -48
  137. package/dist/server.test.js.map +1 -1
  138. package/dist/types.d.ts +38 -2
  139. package/dist/types.d.ts.map +1 -1
  140. package/dist/utils/local-ai.d.ts.map +1 -1
  141. package/dist/utils/local-ai.js +7 -3
  142. package/dist/utils/local-ai.js.map +1 -1
  143. package/dist/utils/multi-git.d.ts +33 -8
  144. package/dist/utils/multi-git.d.ts.map +1 -1
  145. package/dist/utils/multi-git.js +91 -19
  146. package/dist/utils/multi-git.js.map +1 -1
  147. package/dist/utils/multi-git.test.d.ts +2 -0
  148. package/dist/utils/multi-git.test.d.ts.map +1 -0
  149. package/dist/utils/multi-git.test.js +125 -0
  150. package/dist/utils/multi-git.test.js.map +1 -0
  151. package/dist/utils/update-check.d.ts.map +1 -1
  152. package/dist/utils/update-check.js +0 -28
  153. package/dist/utils/update-check.js.map +1 -1
  154. package/dist/utils/workflow-advisor.d.ts +16 -0
  155. package/dist/utils/workflow-advisor.d.ts.map +1 -0
  156. package/dist/utils/workflow-advisor.js +100 -0
  157. package/dist/utils/workflow-advisor.js.map +1 -0
  158. package/extension/package-lock.json +6044 -2673
  159. package/extension/package.json +117 -108
  160. package/extension/src/extension.ts +653 -631
  161. package/extension/tsconfig.json +21 -21
  162. package/gui/README.md +73 -73
  163. package/gui/e2e/dashboard.spec.ts +61 -0
  164. package/gui/e2e/wizard.spec.ts +346 -346
  165. package/gui/eslint.config.js +26 -26
  166. package/gui/index.html +17 -17
  167. package/gui/package-lock.json +3137 -3079
  168. package/gui/package.json +37 -36
  169. package/gui/playwright.config.ts +41 -41
  170. package/gui/public/icons.svg +24 -24
  171. package/gui/src/App.css +1 -1
  172. package/gui/src/App.tsx +3573 -3407
  173. package/gui/src/app/AppSidebar.tsx +69 -0
  174. package/gui/src/assets/vite.svg +1 -1
  175. package/gui/src/components/AddRepoPicker.tsx +81 -0
  176. package/gui/src/components/ui/Button.tsx +40 -0
  177. package/gui/src/components/ui/Card.tsx +6 -0
  178. package/gui/src/components/ui/EmptyState.tsx +22 -0
  179. package/gui/src/components/ui/Input.tsx +21 -0
  180. package/gui/src/components/ui/Menu.tsx +71 -0
  181. package/gui/src/components/ui/Modal.tsx +39 -0
  182. package/gui/src/components/ui/PageHeader.tsx +21 -0
  183. package/gui/src/components/ui/RepoStatusStrip.tsx +33 -0
  184. package/gui/src/components/ui/Skeleton.tsx +5 -0
  185. package/gui/src/components/ui/StatusPill.tsx +36 -0
  186. package/gui/src/components/ui/Tabs.tsx +40 -0
  187. package/gui/src/components/ui/cn.ts +4 -0
  188. package/gui/src/components/ui/index.ts +16 -0
  189. package/gui/src/features/changes/ChangesViewer.tsx +388 -388
  190. package/gui/src/features/knowledge/KnowledgeBase.tsx +84 -84
  191. package/gui/src/features/onboarding/OnboardingWizard.tsx +230 -230
  192. package/gui/src/features/plan/ImplementationPlan.tsx +32 -32
  193. package/gui/src/features/services/ServiceConsole.tsx +215 -250
  194. package/gui/src/features/sessions/SessionHistory.tsx +195 -195
  195. package/gui/src/index.css +167 -152
  196. package/gui/src/lib/status.ts +24 -0
  197. package/gui/src/main.tsx +94 -94
  198. package/gui/src/pages/DashboardPage.tsx +129 -0
  199. package/gui/src/pages/WorkspacesPage.tsx +352 -0
  200. package/gui/src/types.ts +81 -62
  201. package/gui/tsconfig.app.json +25 -25
  202. package/gui/tsconfig.json +7 -7
  203. package/gui/tsconfig.node.json +24 -24
  204. package/gui/vite.config.ts +12 -12
  205. package/package.json +55 -56
  206. package/resources/workflows/plan-implement-review.md +8 -8
  207. package/resources/workflows/research-verify.md +6 -6
  208. package/resources/workflows/solo-developer.md +3 -3
  209. package/scripts/simulate-workspaces.ts +55 -55
  210. package/skills-lock.json +11 -0
  211. package/src/analyzers/detect-apis.ts +290 -290
  212. package/src/analyzers/detect-deps.ts +315 -315
  213. package/src/analyzers/detect-existing.ts +74 -74
  214. package/src/analyzers/detect-ports.ts +110 -110
  215. package/src/analyzers/index.ts +97 -97
  216. package/src/analyzers/messaging-analyzer.ts +254 -254
  217. package/src/analyzers/readme-summarizer.ts +102 -102
  218. package/src/analyzers/run-analyzer.ts +269 -269
  219. package/src/analyzers/tech-stack.ts +283 -283
  220. package/src/commands/adapter.ts +348 -0
  221. package/src/commands/add-repo.ts +156 -157
  222. package/src/commands/commands.test.ts +156 -155
  223. package/src/commands/commit.ts +131 -131
  224. package/src/commands/config.ts +52 -0
  225. package/src/commands/create.ts +204 -207
  226. package/src/commands/desktop.ts +128 -128
  227. package/src/commands/diff.ts +125 -125
  228. package/src/commands/doctor.ts +370 -357
  229. package/src/commands/handoff.ts +266 -266
  230. package/src/commands/init.ts +134 -134
  231. package/src/commands/list.ts +46 -46
  232. package/src/commands/logs.ts +63 -63
  233. package/src/commands/mcp.ts +94 -92
  234. package/src/commands/open.ts +129 -129
  235. package/src/commands/refresh.ts +137 -147
  236. package/src/commands/remove.ts +98 -98
  237. package/src/commands/start.ts +117 -117
  238. package/src/commands/status.ts +54 -54
  239. package/src/commands/stop.ts +55 -55
  240. package/src/commands/sync.ts +125 -151
  241. package/src/commands/tui.ts +484 -424
  242. package/src/commands/ui.ts +111 -111
  243. package/src/core/adapters/local-storage.ts +66 -0
  244. package/src/core/adapters/obsidian-storage.ts +151 -0
  245. package/src/core/adapters/registry.ts +44 -0
  246. package/src/core/adapters/storage.test.ts +174 -0
  247. package/src/core/adapters/vault-storage.ts +86 -0
  248. package/src/core/config.test.ts +96 -96
  249. package/src/core/config.ts +140 -115
  250. package/src/core/graph.ts +344 -344
  251. package/src/core/plugins/index.ts +17 -0
  252. package/src/core/plugins/loader.ts +36 -0
  253. package/src/core/ports/storage.ts +71 -0
  254. package/src/core/scanner.test.ts +61 -61
  255. package/src/core/scanner.ts +91 -91
  256. package/src/core/storage.ts +37 -0
  257. package/src/core/sync.ts +121 -0
  258. package/src/core/workspace-state.test.ts +108 -0
  259. package/src/core/workspace-state.ts +130 -0
  260. package/src/core/workspace.ts +487 -380
  261. package/src/core/worktree.ts +120 -120
  262. package/src/generators/antigravity.ts +32 -32
  263. package/src/generators/base.ts +243 -240
  264. package/src/generators/claude.ts +34 -34
  265. package/src/generators/codex.ts +48 -48
  266. package/src/generators/copilot.ts +56 -56
  267. package/src/generators/cursor.ts +44 -44
  268. package/src/generators/diff-context.ts +75 -0
  269. package/src/generators/index.ts +322 -250
  270. package/src/generators/map-generator.test.ts +159 -159
  271. package/src/generators/map-generator.ts +539 -542
  272. package/src/generators/plan-generator.ts +483 -481
  273. package/src/generators/skills-generator.ts +259 -259
  274. package/src/index.ts +539 -426
  275. package/src/mcp/server.ts +338 -294
  276. package/src/orchestration/detect.ts +313 -313
  277. package/src/orchestration/index.ts +7 -7
  278. package/src/orchestration/runner.ts +283 -283
  279. package/src/server.test.ts +618 -555
  280. package/src/server.ts +1402 -1308
  281. package/src/types.ts +451 -408
  282. package/src/utils/detect-ai.test.ts +44 -44
  283. package/src/utils/detect-ai.ts +84 -84
  284. package/src/utils/detect-editors.test.ts +48 -48
  285. package/src/utils/detect-editors.ts +57 -57
  286. package/src/utils/git.test.ts +74 -74
  287. package/src/utils/git.ts +117 -117
  288. package/src/utils/local-ai.test.ts +130 -130
  289. package/src/utils/local-ai.ts +116 -111
  290. package/src/utils/multi-git.test.ts +158 -0
  291. package/src/utils/multi-git.ts +405 -313
  292. package/src/utils/prompts.ts +209 -209
  293. package/src/utils/session-finder.ts +483 -483
  294. package/src/utils/system-scanner.test.ts +89 -89
  295. package/src/utils/system-scanner.ts +96 -96
  296. package/src/utils/update-check.ts +286 -309
  297. package/src/utils/workflow-advisor.ts +118 -0
  298. package/src/utils/workflows.test.ts +114 -114
  299. package/src/utils/workflows.ts +178 -178
  300. package/tsconfig.json +19 -19
  301. package/vitest.config.ts +14 -14
  302. package/.github/workflows/release.yml +0 -112
  303. package/dist/commands/pack.d.ts +0 -7
  304. package/dist/commands/pack.d.ts.map +0 -1
  305. package/dist/commands/pack.js +0 -64
  306. package/dist/commands/pack.js.map +0 -1
  307. package/dist/core/packer.d.ts +0 -14
  308. package/dist/core/packer.d.ts.map +0 -1
  309. package/dist/core/packer.js +0 -87
  310. package/dist/core/packer.js.map +0 -1
  311. package/dist/core/packer.test.d.ts +0 -2
  312. package/dist/core/packer.test.d.ts.map +0 -1
  313. package/dist/core/packer.test.js +0 -82
  314. package/dist/core/packer.test.js.map +0 -1
  315. package/dist/gui/assets/index-Ct3qq-X4.js +0 -25
  316. package/dist/gui/assets/index-P_ZrgHXg.css +0 -1
  317. package/gui/src/features/workspace/WorkspaceList.tsx +0 -666
  318. package/src/commands/pack.ts +0 -73
  319. package/src/core/packer.test.ts +0 -99
  320. package/src/core/packer.ts +0 -107
@@ -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
- };