@mrpatronz/nexusflow 0.2.12 ā 0.2.13
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.
- package/dist/commands/add-repo.d.ts.map +1 -1
- package/dist/commands/add-repo.js +24 -14
- package/dist/commands/add-repo.js.map +1 -1
- package/dist/commands/commit.js +1 -1
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/diff.js +1 -1
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/handoff.js +1 -1
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/logs.js +1 -1
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/open.d.ts.map +1 -1
- package/dist/commands/open.js +12 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/pack.js.map +1 -1
- package/dist/commands/refresh.js +13 -8
- package/dist/commands/refresh.js.map +1 -1
- package/dist/commands/remove.d.ts.map +1 -1
- package/dist/commands/remove.js +12 -7
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/start.js +1 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/stop.js +1 -1
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/sync.js +13 -8
- package/dist/commands/sync.js.map +1 -1
- package/dist/core/workspace.d.ts +7 -0
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +45 -1
- package/dist/core/workspace.js.map +1 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +14 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/gui/assets/index-CFWwqFux.js +23 -0
- package/dist/gui/assets/index-Dvc9QMvl.css +2 -0
- package/dist/gui/index.html +2 -2
- package/dist/orchestration/runner.d.ts +5 -6
- package/dist/orchestration/runner.d.ts.map +1 -1
- package/dist/orchestration/runner.js +105 -102
- package/dist/orchestration/runner.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +53 -1
- package/dist/server.js.map +1 -1
- package/dist/utils/detect-editors.js +1 -1
- package/dist/utils/detect-editors.js.map +1 -1
- package/dist/utils/detect-editors.test.js +3 -3
- package/dist/utils/detect-editors.test.js.map +1 -1
- package/dist/utils/prompts.d.ts +1 -0
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +77 -15
- package/dist/utils/prompts.js.map +1 -1
- package/extension/package.json +9 -0
- package/extension/src/extension.ts +37 -1
- package/gui/eslint.config.js +4 -0
- package/gui/src/App.tsx +375 -151
- package/gui/src/features/changes/ChangesViewer.tsx +246 -70
- package/gui/src/features/services/ServiceConsole.tsx +162 -71
- package/gui/src/features/workspace/WorkspaceList.tsx +184 -32
- package/gui/src/index.css +95 -2
- package/package.json +2 -1
- package/src/commands/add-repo.ts +30 -14
- package/src/commands/commit.ts +1 -1
- package/src/commands/diff.ts +1 -1
- package/src/commands/doctor.ts +1 -1
- package/src/commands/handoff.ts +1 -1
- package/src/commands/logs.ts +1 -1
- package/src/commands/open.ts +15 -7
- package/src/commands/pack.ts +1 -1
- package/src/commands/refresh.ts +16 -8
- package/src/commands/remove.ts +15 -7
- package/src/commands/start.ts +1 -1
- package/src/commands/status.ts +1 -1
- package/src/commands/stop.ts +1 -1
- package/src/commands/sync.ts +16 -8
- package/src/core/workspace.ts +48 -1
- package/src/generators/index.ts +20 -0
- package/src/orchestration/runner.ts +112 -108
- package/src/server.ts +58 -1
- package/src/utils/detect-editors.test.ts +3 -3
- package/src/utils/detect-editors.ts +1 -1
- package/src/utils/prompts.ts +88 -14
- package/dist/gui/assets/index-Ca2VRMrQ.js +0 -22
- package/dist/gui/assets/index-fj0RJiOb.css +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Play, RefreshCw, FolderGit2, Sparkles, ExternalLink, Trash2 } from 'lucide-react';
|
|
3
|
-
import type { Feature, RunningService, ServiceConfig, OrchestrationDetection, RepoInfo } from '../../types.js';
|
|
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
4
|
|
|
5
5
|
// Feature subcomponents
|
|
6
6
|
import { SessionHistory } from '../sessions/SessionHistory.js';
|
|
@@ -82,8 +82,9 @@ interface WorkspaceListProps {
|
|
|
82
82
|
fetchSessionTranscript: (assistant: string, sessionId: string) => Promise<void>;
|
|
83
83
|
|
|
84
84
|
// Navigation tab states
|
|
85
|
-
subTab:
|
|
86
|
-
setSubTab: (tab:
|
|
85
|
+
subTab: 'overview' | 'sessions' | 'services' | 'changes' | 'knowledge' | 'plan';
|
|
86
|
+
setSubTab: (tab: 'overview' | 'sessions' | 'services' | 'changes' | 'knowledge' | 'plan') => void;
|
|
87
|
+
aiAssistants: DetectedAI[];
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
const parseInlineStyles = (text: string): React.ReactNode => {
|
|
@@ -241,7 +242,15 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
241
242
|
// Tabs
|
|
242
243
|
subTab,
|
|
243
244
|
setSubTab,
|
|
245
|
+
aiAssistants,
|
|
244
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
|
+
|
|
245
254
|
return (
|
|
246
255
|
<div className="max-w-5xl mx-auto">
|
|
247
256
|
<header className="flex justify-between items-center mb-8">
|
|
@@ -276,19 +285,19 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
276
285
|
{workspaces.map((ws) => {
|
|
277
286
|
const isExpanded = activeWsId === ws.branchName;
|
|
278
287
|
return (
|
|
279
|
-
<div key={ws.id} className="
|
|
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-indigo-500/10 via-purple-500/10 to-transparent">
|
|
280
289
|
<div className="flex justify-between items-start gap-4 mb-4">
|
|
281
290
|
<div>
|
|
282
291
|
<h3 className="text-lg font-bold text-white hover:text-indigo-400 transition-colors">{ws.branchName}</h3>
|
|
283
|
-
<div className="flex items-center gap-3 text-xs text-
|
|
292
|
+
<div className="flex items-center gap-3 text-xs text-slate-500 mt-1">
|
|
284
293
|
<span>Created: {new Date(ws.createdAt).toLocaleDateString()}</span>
|
|
285
|
-
<span className="h-1 w-1 rounded-full bg-
|
|
294
|
+
<span className="h-1 w-1 rounded-full bg-slate-850"></span>
|
|
286
295
|
<span>{ws.repos.length} repos</span>
|
|
287
296
|
</div>
|
|
288
297
|
</div>
|
|
289
298
|
<div className="flex gap-2">
|
|
290
299
|
<button
|
|
291
|
-
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 bg-emerald-650 hover:bg-emerald-600 text-white rounded-
|
|
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"
|
|
292
301
|
onClick={() => handleResumeSession(ws)}
|
|
293
302
|
disabled={resumingWs === ws.branchName}
|
|
294
303
|
>
|
|
@@ -296,19 +305,19 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
296
305
|
{resumingWs === ws.branchName ? 'Resuming...' : 'Resume Session'}
|
|
297
306
|
</button>
|
|
298
307
|
<button
|
|
299
|
-
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 bg-
|
|
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"
|
|
300
309
|
onClick={() => handleCopyPrompt(ws)}
|
|
301
310
|
>
|
|
302
311
|
<Sparkles size={12} className="text-cyan-400" /> Copy Prompt
|
|
303
312
|
</button>
|
|
304
313
|
<button
|
|
305
|
-
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 bg-
|
|
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"
|
|
306
315
|
onClick={() => handleOpenInEditor(ws.workspacePath)}
|
|
307
316
|
>
|
|
308
317
|
<ExternalLink size={12} /> Open
|
|
309
318
|
</button>
|
|
310
319
|
<button
|
|
311
|
-
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 bg-indigo-500 hover:bg-indigo-600 text-white rounded-
|
|
320
|
+
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2.5 bg-indigo-500 hover:bg-indigo-600 text-white rounded-xl text-xs font-semibold transition-all cursor-pointer shadow-md shadow-indigo-500/10"
|
|
312
321
|
onClick={() => {
|
|
313
322
|
if (isExpanded) {
|
|
314
323
|
setActiveWsId(null);
|
|
@@ -320,7 +329,7 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
320
329
|
{isExpanded ? 'Hide Runner' : 'Orchestrate'}
|
|
321
330
|
</button>
|
|
322
331
|
<button
|
|
323
|
-
className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 bg-red-950/40 border border-red-900/60 hover:bg-red-900/60 hover:border-red-800 rounded-
|
|
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"
|
|
324
333
|
onClick={() => handleDeleteWorkspace(ws.branchName)}
|
|
325
334
|
disabled={deleteWsLoading === ws.branchName}
|
|
326
335
|
>
|
|
@@ -330,14 +339,14 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
330
339
|
</div>
|
|
331
340
|
</div>
|
|
332
341
|
|
|
333
|
-
<div className="text-xs text-
|
|
342
|
+
<div className="text-xs text-slate-350 bg-slate-950/60 border-l-2 border-indigo-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-indigo-500/2">
|
|
334
343
|
{renderFormattedDescription(ws.description)}
|
|
335
344
|
</div>
|
|
336
345
|
|
|
337
346
|
<div className="flex items-center gap-3 mb-4">
|
|
338
347
|
<div className="flex flex-wrap gap-2">
|
|
339
348
|
{ws.repos.map((repoPath) => (
|
|
340
|
-
<span key={repoPath} className="text-[10px] px-2.5 py-1 bg-
|
|
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">
|
|
341
350
|
{repoPath.split(/[\\/]/).pop()}
|
|
342
351
|
</span>
|
|
343
352
|
))}
|
|
@@ -347,7 +356,7 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
347
356
|
<div className="flex items-center gap-1.5 ml-auto">
|
|
348
357
|
<select
|
|
349
358
|
id={`add-repo-select-feature-${ws.branchName}`}
|
|
350
|
-
className="bg-
|
|
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-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer transition-all hover:border-slate-700"
|
|
351
360
|
defaultValue=""
|
|
352
361
|
onChange={async (e) => {
|
|
353
362
|
const val = e.target.value;
|
|
@@ -374,54 +383,64 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
374
383
|
|
|
375
384
|
{/* Expansion: Process management console */}
|
|
376
385
|
{isExpanded && (
|
|
377
|
-
<div className="mt-6 pt-6 border-t border-
|
|
386
|
+
<div className="mt-6 pt-6 border-t border-slate-850/80">
|
|
378
387
|
{/* Sub-tab Navigation */}
|
|
379
|
-
<div className="flex
|
|
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-indigo-500/15 border border-indigo-500/35 text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.05)]'
|
|
393
|
+
: 'text-slate-500 hover:text-slate-350 hover:bg-slate-900/30 border border-transparent'
|
|
394
|
+
}`}
|
|
395
|
+
onClick={() => setSubTab('overview')}
|
|
396
|
+
>
|
|
397
|
+
Harness & Capabilities
|
|
398
|
+
</button>
|
|
380
399
|
<button
|
|
381
|
-
className={`px-4 py-2
|
|
400
|
+
className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
|
|
382
401
|
subTab === 'sessions'
|
|
383
|
-
? 'border-indigo-500 text-
|
|
384
|
-
: '
|
|
402
|
+
? 'bg-indigo-500/15 border border-indigo-500/35 text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.05)]'
|
|
403
|
+
: 'text-slate-500 hover:text-slate-350 hover:bg-slate-900/30 border border-transparent'
|
|
385
404
|
}`}
|
|
386
405
|
onClick={() => setSubTab('sessions')}
|
|
387
406
|
>
|
|
388
407
|
AI Session History
|
|
389
408
|
</button>
|
|
390
409
|
<button
|
|
391
|
-
className={`px-4 py-2
|
|
410
|
+
className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
|
|
392
411
|
subTab === 'services'
|
|
393
|
-
? 'border-indigo-500 text-
|
|
394
|
-
: '
|
|
412
|
+
? 'bg-indigo-500/15 border border-indigo-500/35 text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.05)]'
|
|
413
|
+
: 'text-slate-500 hover:text-slate-350 hover:bg-slate-900/30 border border-transparent'
|
|
395
414
|
}`}
|
|
396
415
|
onClick={() => setSubTab('services')}
|
|
397
416
|
>
|
|
398
417
|
Orchestrated Services
|
|
399
418
|
</button>
|
|
400
419
|
<button
|
|
401
|
-
className={`px-4 py-2
|
|
420
|
+
className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
|
|
402
421
|
subTab === 'changes'
|
|
403
|
-
? 'border-indigo-500 text-
|
|
404
|
-
: '
|
|
422
|
+
? 'bg-indigo-500/15 border border-indigo-500/35 text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.05)]'
|
|
423
|
+
: 'text-slate-500 hover:text-slate-350 hover:bg-slate-900/30 border border-transparent'
|
|
405
424
|
}`}
|
|
406
425
|
onClick={() => setSubTab('changes')}
|
|
407
426
|
>
|
|
408
427
|
Active Changes (AI Diffs)
|
|
409
428
|
</button>
|
|
410
429
|
<button
|
|
411
|
-
className={`px-4 py-2
|
|
430
|
+
className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
|
|
412
431
|
subTab === 'knowledge'
|
|
413
|
-
? 'border-indigo-500 text-
|
|
414
|
-
: '
|
|
432
|
+
? 'bg-indigo-500/15 border border-indigo-500/35 text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.05)]'
|
|
433
|
+
: 'text-slate-500 hover:text-slate-350 hover:bg-slate-900/30 border border-transparent'
|
|
415
434
|
}`}
|
|
416
435
|
onClick={() => setSubTab('knowledge')}
|
|
417
436
|
>
|
|
418
437
|
Knowledge Base
|
|
419
438
|
</button>
|
|
420
439
|
<button
|
|
421
|
-
className={`px-4 py-2
|
|
440
|
+
className={`px-4 py-2 rounded-lg text-xs font-bold transition-all cursor-pointer select-none ${
|
|
422
441
|
subTab === 'plan'
|
|
423
|
-
? 'border-indigo-500 text-
|
|
424
|
-
: '
|
|
442
|
+
? 'bg-indigo-500/15 border border-indigo-500/35 text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.05)]'
|
|
443
|
+
: 'text-slate-500 hover:text-slate-350 hover:bg-slate-900/30 border border-transparent'
|
|
425
444
|
}`}
|
|
426
445
|
onClick={() => setSubTab('plan')}
|
|
427
446
|
>
|
|
@@ -429,6 +448,139 @@ export const WorkspaceList: React.FC<WorkspaceListProps> = ({
|
|
|
429
448
|
</button>
|
|
430
449
|
</div>
|
|
431
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-indigo-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-indigo-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-indigo-400 bg-indigo-500/10 px-2 py-0.5 rounded border border-indigo-500/20 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-indigo-300 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-indigo-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-indigo-300">
|
|
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-indigo-300">
|
|
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-indigo-300">
|
|
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-indigo-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-indigo-300">{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
|
+
|
|
432
584
|
{subTab === 'sessions' && (
|
|
433
585
|
<SessionHistory
|
|
434
586
|
ws={ws}
|
package/gui/src/index.css
CHANGED
|
@@ -1,12 +1,62 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
+
/* Custom Sleek Scrollbars */
|
|
4
|
+
::-webkit-scrollbar {
|
|
5
|
+
width: 6px;
|
|
6
|
+
height: 6px;
|
|
7
|
+
}
|
|
8
|
+
::-webkit-scrollbar-track {
|
|
9
|
+
background: transparent;
|
|
10
|
+
}
|
|
11
|
+
::-webkit-scrollbar-thumb {
|
|
12
|
+
background: rgba(99, 102, 241, 0.15);
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
border: 1px solid rgba(255, 255, 255, 0.03);
|
|
15
|
+
}
|
|
16
|
+
::-webkit-scrollbar-thumb:hover {
|
|
17
|
+
background: rgba(99, 102, 241, 0.3);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Glassmorphism and Modern UI Helpers */
|
|
21
|
+
@utility glass-card {
|
|
22
|
+
background: rgba(17, 24, 39, 0.45);
|
|
23
|
+
backdrop-filter: blur(16px);
|
|
24
|
+
-webkit-backdrop-filter: blur(16px);
|
|
25
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
26
|
+
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@utility glass-card-glow {
|
|
30
|
+
box-shadow: 0 0 15px rgba(99, 102, 241, 0.1), inset 0 0 1px 1px rgba(255, 255, 255, 0.05);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@utility text-glow-indigo {
|
|
34
|
+
text-shadow: 0 0 12px rgba(99, 102, 241, 0.4);
|
|
35
|
+
}
|
|
36
|
+
|
|
3
37
|
@utility animate-slide-in {
|
|
4
|
-
animation: slide-in 0.
|
|
38
|
+
animation: slide-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@utility animate-fade-in {
|
|
42
|
+
animation: fade-in 0.2s ease-out forwards;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@utility animate-spin-slow {
|
|
46
|
+
animation: spin 8s linear infinite;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@utility animate-pulse-glow-green {
|
|
50
|
+
animation: pulse-glow-green 2s infinite ease-in-out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@utility animate-pulse-glow-indigo {
|
|
54
|
+
animation: pulse-glow-indigo 2.5s infinite ease-in-out;
|
|
5
55
|
}
|
|
6
56
|
|
|
7
57
|
@keyframes slide-in {
|
|
8
58
|
from {
|
|
9
|
-
transform: translateY(
|
|
59
|
+
transform: translateY(0.75rem);
|
|
10
60
|
opacity: 0;
|
|
11
61
|
}
|
|
12
62
|
to {
|
|
@@ -15,3 +65,46 @@
|
|
|
15
65
|
}
|
|
16
66
|
}
|
|
17
67
|
|
|
68
|
+
@keyframes fade-in {
|
|
69
|
+
from {
|
|
70
|
+
opacity: 0;
|
|
71
|
+
}
|
|
72
|
+
to {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@keyframes pulse-glow-green {
|
|
78
|
+
0%, 100% {
|
|
79
|
+
box-shadow: 0 0 4px rgba(52, 211, 153, 0.2);
|
|
80
|
+
opacity: 0.8;
|
|
81
|
+
}
|
|
82
|
+
50% {
|
|
83
|
+
box-shadow: 0 0 12px rgba(52, 211, 153, 0.6);
|
|
84
|
+
opacity: 1;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@keyframes pulse-glow-indigo {
|
|
89
|
+
0%, 100% {
|
|
90
|
+
box-shadow: 0 0 4px rgba(99, 102, 241, 0.2);
|
|
91
|
+
}
|
|
92
|
+
50% {
|
|
93
|
+
box-shadow: 0 0 14px rgba(99, 102, 241, 0.5);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Custom terminal colors */
|
|
98
|
+
.term-matrix {
|
|
99
|
+
color: #10b981;
|
|
100
|
+
background-color: #030712;
|
|
101
|
+
}
|
|
102
|
+
.term-dracula {
|
|
103
|
+
color: #f8f8f2;
|
|
104
|
+
background-color: #282a36;
|
|
105
|
+
}
|
|
106
|
+
.term-classic {
|
|
107
|
+
color: #e5e7eb;
|
|
108
|
+
background-color: #0b0f19;
|
|
109
|
+
}
|
|
110
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrpatronz/nexusflow",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "Combine multiple repos into a workspace with rich AI assistant context",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"globby": "^14.1.0",
|
|
44
44
|
"hono": "^4.12.23",
|
|
45
45
|
"ora": "^8.1.0",
|
|
46
|
+
"pm2": "^7.0.1",
|
|
46
47
|
"repomix": "^1.14.1"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
package/src/commands/add-repo.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import ora from 'ora';
|
|
8
|
-
import { select } from '@inquirer/prompts';
|
|
8
|
+
import { select, search } from '@inquirer/prompts';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
|
|
11
11
|
import { loadConfig } from '../core/config.js';
|
|
@@ -57,7 +57,7 @@ export async function addRepoCommand(
|
|
|
57
57
|
} else {
|
|
58
58
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
59
59
|
if (cwdFeature) {
|
|
60
|
-
workspacePath =
|
|
60
|
+
workspacePath = cwdFeature.workspacePath;
|
|
61
61
|
workspaceName = cwdFeature.branchName;
|
|
62
62
|
} else {
|
|
63
63
|
const workspaces = await listWorkspaces(config.workspacesDir);
|
|
@@ -66,12 +66,20 @@ export async function addRepoCommand(
|
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const selected = await
|
|
70
|
-
message: '
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
const selected = await search({
|
|
70
|
+
message: 'Search and select a workspace to add a repository to:',
|
|
71
|
+
source: async (input) => {
|
|
72
|
+
const query = (input || '').toLowerCase();
|
|
73
|
+
const filtered = workspaces.filter(
|
|
74
|
+
(ws) =>
|
|
75
|
+
ws.branchName.toLowerCase().includes(query) ||
|
|
76
|
+
ws.workspacePath.toLowerCase().includes(query)
|
|
77
|
+
);
|
|
78
|
+
return filtered.map((ws) => ({
|
|
79
|
+
name: `${ws.branchName} ${chalk.dim(`(${ws.repos.length} repos)`)}`,
|
|
80
|
+
value: ws,
|
|
81
|
+
}));
|
|
82
|
+
},
|
|
75
83
|
});
|
|
76
84
|
|
|
77
85
|
workspacePath = selected.workspacePath;
|
|
@@ -113,12 +121,20 @@ export async function addRepoCommand(
|
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
115
123
|
|
|
116
|
-
repoPathToAdd = await
|
|
117
|
-
message: '
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
repoPathToAdd = await search({
|
|
125
|
+
message: 'Search and select a repository to add:',
|
|
126
|
+
source: async (input) => {
|
|
127
|
+
const query = (input || '').toLowerCase();
|
|
128
|
+
const filtered = availableRepos.filter(
|
|
129
|
+
(r) =>
|
|
130
|
+
r.name.toLowerCase().includes(query) ||
|
|
131
|
+
r.path.toLowerCase().includes(query)
|
|
132
|
+
);
|
|
133
|
+
return filtered.map((r) => ({
|
|
134
|
+
name: `${r.name} ${chalk.dim(`(${r.path})`)}`,
|
|
135
|
+
value: r.path,
|
|
136
|
+
}));
|
|
137
|
+
},
|
|
122
138
|
});
|
|
123
139
|
}
|
|
124
140
|
|
package/src/commands/commit.ts
CHANGED
|
@@ -109,7 +109,7 @@ async function resolveWorkspace(workspaceArg?: string): Promise<string | null> {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
112
|
-
if (cwdFeature) return
|
|
112
|
+
if (cwdFeature) return cwdFeature.workspacePath;
|
|
113
113
|
|
|
114
114
|
const config = await loadConfig();
|
|
115
115
|
const workspaces = await listWorkspaces(config.workspacesDir);
|
package/src/commands/diff.ts
CHANGED
|
@@ -103,7 +103,7 @@ async function resolveWorkspace(workspaceArg?: string): Promise<string | null> {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
106
|
-
if (cwdFeature) return
|
|
106
|
+
if (cwdFeature) return cwdFeature.workspacePath;
|
|
107
107
|
|
|
108
108
|
const config = await loadConfig();
|
|
109
109
|
const workspaces = await listWorkspaces(config.workspacesDir);
|
package/src/commands/doctor.ts
CHANGED
|
@@ -335,7 +335,7 @@ async function resolveWorkspace(workspaceArg?: string): Promise<string | null> {
|
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
338
|
-
if (cwdFeature) return
|
|
338
|
+
if (cwdFeature) return cwdFeature.workspacePath;
|
|
339
339
|
|
|
340
340
|
const config = await loadConfig();
|
|
341
341
|
const workspaces = await listWorkspaces(config.workspacesDir);
|
package/src/commands/handoff.ts
CHANGED
|
@@ -244,7 +244,7 @@ async function resolveWorkspace(workspaceArg?: string): Promise<string | null> {
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
247
|
-
if (cwdFeature) return
|
|
247
|
+
if (cwdFeature) return cwdFeature.workspacePath;
|
|
248
248
|
|
|
249
249
|
const config = await loadConfig();
|
|
250
250
|
const workspaces = await listWorkspaces(config.workspacesDir);
|
package/src/commands/logs.ts
CHANGED
|
@@ -41,7 +41,7 @@ async function resolveWorkspace(workspaceArg?: string): Promise<string | null> {
|
|
|
41
41
|
if (workspaceArg) return workspaceArg;
|
|
42
42
|
|
|
43
43
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
44
|
-
if (cwdFeature) return
|
|
44
|
+
if (cwdFeature) return cwdFeature.workspacePath;
|
|
45
45
|
|
|
46
46
|
const config = await loadConfig();
|
|
47
47
|
const workspaces = await listWorkspaces(config.workspacesDir);
|
package/src/commands/open.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { execa } from 'execa';
|
|
8
|
-
import { select, confirm } from '@inquirer/prompts';
|
|
8
|
+
import { select, confirm, search } from '@inquirer/prompts';
|
|
9
9
|
|
|
10
10
|
import { loadConfig } from '../core/config.js';
|
|
11
11
|
import { listWorkspaces } from '../core/workspace.js';
|
|
@@ -29,12 +29,20 @@ export async function openCommand(): Promise<void> {
|
|
|
29
29
|
console.log(chalk.bold.cyan('\nš Open Workspace\n'));
|
|
30
30
|
|
|
31
31
|
// Let user pick a workspace
|
|
32
|
-
const selected = await
|
|
33
|
-
message: '
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
const selected = await search({
|
|
33
|
+
message: 'Search and select a workspace to open:',
|
|
34
|
+
source: async (input) => {
|
|
35
|
+
const query = (input || '').toLowerCase();
|
|
36
|
+
const filtered = workspaces.filter(
|
|
37
|
+
(ws) =>
|
|
38
|
+
ws.branchName.toLowerCase().includes(query) ||
|
|
39
|
+
ws.workspacePath.toLowerCase().includes(query)
|
|
40
|
+
);
|
|
41
|
+
return filtered.map((ws) => ({
|
|
42
|
+
name: `${ws.branchName} ${chalk.dim(`(${ws.repos.length} repos, ${ws.assistants.join(', ')})`)}`,
|
|
43
|
+
value: ws.workspacePath,
|
|
44
|
+
}));
|
|
45
|
+
},
|
|
38
46
|
});
|
|
39
47
|
|
|
40
48
|
// Let user pick an editor
|
package/src/commands/pack.ts
CHANGED
|
@@ -51,7 +51,7 @@ async function resolveWorkspace(workspaceArg?: string): Promise<string | null> {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const cwdFeature = await loadFeatureConfig(process.cwd());
|
|
54
|
-
if (cwdFeature) return
|
|
54
|
+
if (cwdFeature) return cwdFeature.workspacePath;
|
|
55
55
|
|
|
56
56
|
const config = await loadConfig();
|
|
57
57
|
const workspaces = await listWorkspaces(config.workspacesDir);
|