project-compass 3.3.8 → 3.4.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.
- package/package.json +11 -10
- package/src/cli.js +29 -110
- package/src/components/PackageRegistry.js +109 -0
- package/src/components/ProjectArchitect.js +83 -0
- package/src/components/Studio.js +65 -0
- package/src/components/TaskManager.js +24 -0
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-compass",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.4.0",
|
|
4
|
+
"description": "Futuristic project navigator and runner for Node, Python, Rust, and Go",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
|
-
"type": "module",
|
|
7
6
|
"bin": {
|
|
8
7
|
"project-compass": "src/cli.js"
|
|
9
8
|
},
|
|
9
|
+
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node src/cli.js",
|
|
12
12
|
"test": "node src/cli.js --mode test",
|
|
@@ -14,21 +14,22 @@
|
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"cli",
|
|
17
|
+
"navigator",
|
|
17
18
|
"ink",
|
|
18
|
-
"project",
|
|
19
19
|
"runner",
|
|
20
|
-
"
|
|
20
|
+
"projects"
|
|
21
21
|
],
|
|
22
22
|
"author": "Satyaa & Clawdy",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"execa": "^9.
|
|
25
|
+
"execa": "^9.5.2",
|
|
26
26
|
"fast-glob": "^3.3.3",
|
|
27
|
-
"ink": "^
|
|
28
|
-
"kleur": "^4.1.5"
|
|
27
|
+
"ink": "^5.1.0",
|
|
28
|
+
"kleur": "^4.1.5",
|
|
29
|
+
"react": "^19.0.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"
|
|
32
|
-
"eslint": "^
|
|
32
|
+
"eslint": "^9.19.0",
|
|
33
|
+
"@eslint/js": "^9.19.0"
|
|
33
34
|
}
|
|
34
35
|
}
|
package/src/cli.js
CHANGED
|
@@ -5,9 +5,15 @@ import path from 'path';
|
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import kleur from 'kleur';
|
|
7
7
|
import {execa} from 'execa';
|
|
8
|
-
import {discoverProjects, SCHEMA_GUIDE
|
|
8
|
+
import {discoverProjects, SCHEMA_GUIDE} from './projectDetection.js';
|
|
9
9
|
import {CONFIG_PATH, ensureConfigDir} from './configPaths.js';
|
|
10
10
|
|
|
11
|
+
// Modular Components
|
|
12
|
+
import Studio from './components/Studio.js';
|
|
13
|
+
import TaskManager from './components/TaskManager.js';
|
|
14
|
+
import PackageRegistry from './components/PackageRegistry.js';
|
|
15
|
+
import ProjectArchitect from './components/ProjectArchitect.js';
|
|
16
|
+
|
|
11
17
|
const create = React.createElement;
|
|
12
18
|
const ART_CHARS = ['▁', '▃', '▄', '▅', '▇'];
|
|
13
19
|
const ART_COLORS = ['magenta', 'blue', 'cyan', 'yellow', 'red'];
|
|
@@ -90,81 +96,6 @@ function buildDetailCommands(project, config) {
|
|
|
90
96
|
return [...builtins, ...custom];
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
const Studio = memo(() => {
|
|
94
|
-
const [runtimes, setRuntimes] = useState([]);
|
|
95
|
-
const [loading, setLoading] = useState(true);
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
const checks = [
|
|
99
|
-
{name: 'Node.js', binary: 'node', versionCmd: ['-v']},
|
|
100
|
-
{name: 'npm', binary: 'npm', versionCmd: ['-v']},
|
|
101
|
-
{name: 'Python', binary: process.platform === 'win32' ? 'python' : 'python3', versionCmd: ['--version']},
|
|
102
|
-
{name: 'Rust (Cargo)', binary: 'cargo', versionCmd: ['--version']},
|
|
103
|
-
{name: 'Go', binary: 'go', versionCmd: ['version']},
|
|
104
|
-
{name: 'Java', binary: 'java', versionCmd: ['-version']},
|
|
105
|
-
{name: 'PHP', binary: 'php', versionCmd: ['-v']},
|
|
106
|
-
{name: 'Ruby', binary: 'ruby', versionCmd: ['-v']},
|
|
107
|
-
{name: '.NET', binary: 'dotnet', versionCmd: ['--version']}
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
(async () => {
|
|
111
|
-
const results = await Promise.all(checks.map(async (lang) => {
|
|
112
|
-
if (!checkBinary(lang.binary)) {
|
|
113
|
-
return {...lang, status: 'missing', version: 'not installed'};
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
const {stdout, stderr} = await execa(lang.binary, lang.versionCmd);
|
|
117
|
-
const version = (stdout || stderr || '').split('\n')[0].trim();
|
|
118
|
-
return {...lang, status: 'ok', version};
|
|
119
|
-
} catch {
|
|
120
|
-
return {...lang, status: 'error', version: 'failed to check'};
|
|
121
|
-
}
|
|
122
|
-
}));
|
|
123
|
-
setRuntimes(results);
|
|
124
|
-
setLoading(false);
|
|
125
|
-
})();
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
return create(
|
|
129
|
-
Box,
|
|
130
|
-
{flexDirection: 'column', borderStyle: 'double', borderColor: 'blue', padding: 1},
|
|
131
|
-
create(Text, {bold: true, color: 'blue'}, '💎 Omni-Studio | Environment Intelligence'),
|
|
132
|
-
create(Text, {dimColor: true, marginBottom: 1}, 'Overview of installed languages and build tools.'),
|
|
133
|
-
loading
|
|
134
|
-
? create(Text, {dimColor: true}, 'Gathering intelligence...')
|
|
135
|
-
: create(
|
|
136
|
-
Box,
|
|
137
|
-
{flexDirection: 'column'},
|
|
138
|
-
...runtimes.map(r => create(
|
|
139
|
-
Box,
|
|
140
|
-
{key: r.name, marginBottom: 0},
|
|
141
|
-
create(Text, {width: 20, color: r.status === 'ok' ? 'green' : 'red'}, `${r.status === 'ok' ? '✓' : '✗'} ${r.name}`),
|
|
142
|
-
create(Text, {dimColor: r.status !== 'ok'}, `: ${r.version}`)
|
|
143
|
-
)),
|
|
144
|
-
create(Text, {marginTop: 1, color: 'yellow'}, '🛠️ Interactive Project Creator coming soon in v3.0'),
|
|
145
|
-
create(Text, {dimColor: true}, 'Press Shift+A to return to Navigator.')
|
|
146
|
-
)
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
const TaskManager = memo(({tasks, activeTaskId, renameMode, renameInput, renameCursor}) => {
|
|
151
|
-
return create(
|
|
152
|
-
Box,
|
|
153
|
-
{flexDirection: 'column', borderStyle: 'round', borderColor: 'yellow', padding: 1},
|
|
154
|
-
create(Text, {bold: true, color: 'yellow'}, '🛰️ Task Manager | Background Processes'),
|
|
155
|
-
create(Text, {dimColor: true, marginBottom: 1}, 'Up/Down: focus, Shift+K: Force Kill, Shift+R: Rename'),
|
|
156
|
-
...tasks.map(t => create(
|
|
157
|
-
Box,
|
|
158
|
-
{key: t.id, marginBottom: 0, flexDirection: 'column'},
|
|
159
|
-
t.id === activeTaskId && renameMode
|
|
160
|
-
? create(Box, {flexDirection: 'row'}, create(Text, {color: 'cyan'}, '→ Rename to: '), create(CursorText, {value: renameInput, cursorIndex: renameCursor}))
|
|
161
|
-
: create(Text, {color: t.id === activeTaskId ? 'cyan' : 'white', bold: t.id === activeTaskId}, `${t.id === activeTaskId ? '→' : ' '} [${t.status.toUpperCase()}] ${t.name}`)
|
|
162
|
-
)),
|
|
163
|
-
!tasks.length && create(Text, {dimColor: true}, 'No active or background tasks.'),
|
|
164
|
-
create(Text, {marginTop: 1, dimColor: true}, 'Press Enter or Shift+T to return to Navigator.')
|
|
165
|
-
);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
99
|
function CursorText({value, cursorIndex, active = true}) {
|
|
169
100
|
const before = value.slice(0, cursorIndex);
|
|
170
101
|
const charAt = value[cursorIndex] || ' ';
|
|
@@ -427,34 +358,15 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
427
358
|
const normalizedInput = input?.toLowerCase();
|
|
428
359
|
const shiftCombo = (char) => key.shift && normalizedInput === char;
|
|
429
360
|
|
|
430
|
-
if (shiftCombo('h')) {
|
|
431
|
-
|
|
432
|
-
const next = {...prev, showHelpCards: !prev.showHelpCards};
|
|
433
|
-
saveConfig(next);
|
|
434
|
-
return next;
|
|
435
|
-
});
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
if (shiftCombo('s')) {
|
|
439
|
-
setConfig(prev => {
|
|
440
|
-
const next = {...prev, showStructureGuide: !prev.showStructureGuide};
|
|
441
|
-
saveConfig(next);
|
|
442
|
-
return next;
|
|
443
|
-
});
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
361
|
+
if (shiftCombo('h')) { setConfig(prev => { const next = {...prev, showHelpCards: !prev.showHelpCards}; saveConfig(next); return next; }); return; }
|
|
362
|
+
if (shiftCombo('s')) { setConfig(prev => { const next = {...prev, showStructureGuide: !prev.showStructureGuide}; saveConfig(next); return next; }); return; }
|
|
446
363
|
if (shiftCombo('a')) { setMainView((prev) => (prev === 'navigator' ? 'studio' : 'navigator')); return; }
|
|
364
|
+
if (shiftCombo('p')) { setMainView((prev) => (prev === 'navigator' ? 'registry' : 'navigator')); return; }
|
|
365
|
+
if (shiftCombo('n')) { setMainView((prev) => (prev === 'navigator' ? 'architect' : 'navigator')); return; }
|
|
447
366
|
if (shiftCombo('x')) { setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, logs: []} : t)); setLogOffset(0); return; }
|
|
448
367
|
if (shiftCombo('e')) { exportLogs(); return; }
|
|
449
368
|
if (shiftCombo('d')) { setActiveTaskId(null); return; }
|
|
450
|
-
if (shiftCombo('b')) {
|
|
451
|
-
setConfig(prev => {
|
|
452
|
-
const next = {...prev, showArtBoard: !prev.showArtBoard};
|
|
453
|
-
saveConfig(next);
|
|
454
|
-
return next;
|
|
455
|
-
});
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
369
|
+
if (shiftCombo('b')) { setConfig(prev => { const next = {...prev, showArtBoard: !prev.showArtBoard}; saveConfig(next); return next; }); return; }
|
|
458
370
|
|
|
459
371
|
if (shiftCombo('t')) {
|
|
460
372
|
setMainView((prev) => {
|
|
@@ -485,6 +397,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
485
397
|
return;
|
|
486
398
|
}
|
|
487
399
|
|
|
400
|
+
if (mainView === 'registry' || mainView === 'architect') {
|
|
401
|
+
// Inputs handled inside components
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
488
405
|
if (running && activeTaskId && runningProcessMap.current.has(activeTaskId)) {
|
|
489
406
|
if (isCtrlC) { handleKillTask(activeTaskId); setStdinBuffer(''); setStdinCursor(0); return; }
|
|
490
407
|
if (key.return) {
|
|
@@ -612,18 +529,14 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
612
529
|
create(Text, {dimColor: true}, tile.subtext)
|
|
613
530
|
)), [projectCountLabel, rootPath, selectedProject, tasks.length, running]);
|
|
614
531
|
|
|
615
|
-
const helpCards = [
|
|
616
|
-
{label: 'Navigation', color: 'magenta', body: ['↑ / ↓ move focus, Enter: details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
617
|
-
{label: 'Commands', color: 'cyan', body: ['B / T / R build/test/run', '1-9 / S+A-Z numbered commands', 'Shift+L rerun last command', 'Shift+X clear / Shift+E export']},
|
|
618
|
-
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+S structure / Shift+Q quit']}
|
|
619
|
-
];
|
|
620
|
-
|
|
621
532
|
if (quitConfirm) {
|
|
622
533
|
return create(Box, {flexDirection: 'column', borderStyle: 'round', borderColor: 'red', padding: 1}, create(Text, {bold: true, color: 'red'}, '⚠️ Confirm Exit'), create(Text, null, `There are ${tasks.filter(t=>t.status==='running').length} tasks still running in the background.`), create(Text, null, 'Are you sure you want to quit and stop all processes?'), create(Text, {marginTop: 1}, kleur.bold('Y') + ' to Quit, ' + kleur.bold('N') + ' to Cancel'));
|
|
623
534
|
}
|
|
624
535
|
|
|
625
536
|
if (mainView === 'studio') return create(Studio);
|
|
626
|
-
if (mainView === 'tasks') return create(TaskManager, {tasks, activeTaskId, renameMode, renameInput, renameCursor});
|
|
537
|
+
if (mainView === 'tasks') return create(TaskManager, {tasks, activeTaskId, setActiveTaskId, renameMode, renameInput, renameCursor, CursorText});
|
|
538
|
+
if (mainView === 'registry') return create(PackageRegistry, {selectedProject, onRunCommand: runProjectCommand, CursorText});
|
|
539
|
+
if (mainView === 'architect') return create(ProjectArchitect, {onRunCommand: runProjectCommand, CursorText});
|
|
627
540
|
|
|
628
541
|
return create(Box, {flexDirection: 'column', padding: 1},
|
|
629
542
|
create(Box, {justifyContent: 'space-between'},
|
|
@@ -646,11 +559,15 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
646
559
|
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {bold: true, color: 'yellow'}, `Output: ${activeTask?.name || 'None'}`), create(Text, {dimColor: true}, logOffset ? `Scrolled ${logOffset} lines` : 'Live log view')),
|
|
647
560
|
create(OutputPanel, {activeTask, logOffset}),
|
|
648
561
|
create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {dimColor: true}, running ? 'Type to feed stdin; Enter: submit, Ctrl+C: abort.' : 'Run a command or press Shift+T to switch tasks.'), create(Text, {dimColor: true}, `${toggleHint}, Shift+S: Structure Guide`)),
|
|
649
|
-
create(Box, {marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1}, create(Text, {bold: true, color:
|
|
562
|
+
create(Box, {marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1}, create(Text, {bold: true, color: 'green'}, running ? ' Stdin buffer ' : ' Input ready '), create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running})))
|
|
650
563
|
),
|
|
651
|
-
config.showHelpCards && create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'},
|
|
564
|
+
config.showHelpCards && create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'}, [
|
|
565
|
+
{label: 'Navigation', color: 'magenta', body: ['↑ / ↓ move focus, Enter: details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
566
|
+
{label: 'Management', color: 'cyan', body: ['Shift+P Package Registry', 'Shift+N Project Architect', 'Shift+X clear / Shift+E export']},
|
|
567
|
+
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+S structure / Shift+Q quit']}
|
|
568
|
+
].map((card, idx) => create(Box, {key: card.label, flexGrow: 1, flexBasis: 0, minWidth: HELP_CARD_MIN_WIDTH, marginRight: idx < 2 ? 1 : 0, marginBottom: 1, borderStyle: 'round', borderColor: card.color, padding: 1, flexDirection: 'column'}, create(Text, {color: card.color, bold: true, marginBottom: 1}, card.label), ...card.body.map((line, lidx) => create(Text, {key: lidx, dimColor: card.color === 'yellow'}, line))))),
|
|
652
569
|
config.showStructureGuide && create(Box, {flexDirection: 'column', borderStyle: 'round', borderColor: 'blue', marginTop: 1, padding: 1}, create(Text, {color: 'cyan', bold: true}, 'Structure guide · press Shift+S to hide'), ...SCHEMA_GUIDE.map(e => create(Text, {key: e.type, dimColor: true}, `• ${e.icon} ${e.label}: ${e.files.join(', ')}`))),
|
|
653
|
-
showHelp && create(Box, {flexDirection: 'column', borderStyle: 'double', borderColor: 'cyan', marginTop: 1, padding: 1}, create(Text, {color: 'cyan', bold: true}, 'Help overlay'), create(Text, null, 'Shift+↑/↓ scrolls logs; Shift+X clears; Shift+E exports; Shift+A Studio; Shift+T Tasks; Shift+D Detach; Shift+B Toggle Art Board.'))
|
|
570
|
+
showHelp && create(Box, {flexDirection: 'column', borderStyle: 'double', borderColor: 'cyan', marginTop: 1, padding: 1}, create(Text, {color: 'cyan', bold: true}, 'Help overlay'), create(Text, null, 'Shift+↑/↓ scrolls logs; Shift+X clears; Shift+E exports; Shift+A Studio; Shift+T Tasks; Shift+D Detach; Shift+B Toggle Art Board; Shift+P Packages; Shift+N Creator.'))
|
|
654
571
|
);
|
|
655
572
|
}
|
|
656
573
|
|
|
@@ -686,6 +603,8 @@ async function main() {
|
|
|
686
603
|
console.log(' Enter Toggle detail view for selected project');
|
|
687
604
|
console.log(' Shift+A Switch to Omni-Studio (Environment Health)');
|
|
688
605
|
console.log(' Shift+T Open Orbit Task Manager (Manage background processes)');
|
|
606
|
+
console.log(' Shift+P Open Package Registry (Add/Remove packages)');
|
|
607
|
+
console.log(' Shift+N Open Project Architect (Scaffold new projects)');
|
|
689
608
|
console.log(' Shift+D Detach from active task (Keep it running in background)');
|
|
690
609
|
console.log(' Shift+B Toggle Art Board visibility');
|
|
691
610
|
console.log(' Shift+H Toggle Help Cards visibility');
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, {useState, memo} from 'react';
|
|
2
|
+
import {Box, Text, useInput} from 'ink';
|
|
3
|
+
|
|
4
|
+
const create = React.createElement;
|
|
5
|
+
|
|
6
|
+
const PackageRegistry = memo(({selectedProject, onRunCommand, CursorText}) => {
|
|
7
|
+
const [mode, setMode] = useState('list'); // list | add | remove
|
|
8
|
+
const [input, setInput] = useState('');
|
|
9
|
+
const [cursor, setCursor] = useState(0);
|
|
10
|
+
|
|
11
|
+
const projectType = selectedProject?.type || 'Unknown';
|
|
12
|
+
const deps = selectedProject?.metadata?.dependencies || [];
|
|
13
|
+
|
|
14
|
+
useInput((inputStr, key) => {
|
|
15
|
+
if (mode === 'add' || mode === 'remove') {
|
|
16
|
+
if (key.return) {
|
|
17
|
+
if (input.trim()) {
|
|
18
|
+
const cmd = mode === 'add' ? getAddCmd(projectType, input.trim()) : getRemoveCmd(projectType, input.trim());
|
|
19
|
+
if (cmd) onRunCommand({label: `${mode === 'add' ? 'Add' : 'Remove'} ${input}`, command: cmd});
|
|
20
|
+
}
|
|
21
|
+
setMode('list'); setInput(''); setCursor(0);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (key.escape) { setMode('list'); setInput(''); setCursor(0); return; }
|
|
25
|
+
if (key.backspace || key.delete) {
|
|
26
|
+
if (cursor > 0) {
|
|
27
|
+
setInput(prev => prev.slice(0, cursor - 1) + prev.slice(cursor));
|
|
28
|
+
setCursor(c => Math.max(0, c - 1));
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (key.leftArrow) { setCursor(c => Math.max(0, c - 1)); return; }
|
|
33
|
+
if (key.rightArrow) { setCursor(c => Math.min(input.length, c + 1)); return; }
|
|
34
|
+
if (inputStr) {
|
|
35
|
+
setInput(prev => prev.slice(0, cursor) + inputStr + prev.slice(cursor));
|
|
36
|
+
setCursor(c => c + inputStr.length);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (inputStr.toLowerCase() === 'a') { setMode('add'); setInput(''); setCursor(0); }
|
|
42
|
+
if (inputStr.toLowerCase() === 'r') { setMode('remove'); setInput(''); setCursor(0); }
|
|
43
|
+
if (inputStr.toLowerCase() === 'v' && projectType === 'Python') {
|
|
44
|
+
onRunCommand({label: 'Create venv', command: ['python3', '-m', 'venv', '.venv']});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const getAddCmd = (type, pkg) => {
|
|
49
|
+
if (type === 'Node.js') return ['npm', 'install', pkg];
|
|
50
|
+
if (type === 'Python') return ['pip', 'install', pkg];
|
|
51
|
+
if (type === 'Rust') return ['cargo', 'add', pkg];
|
|
52
|
+
if (type === '.NET') return ['dotnet', 'add', 'package', pkg];
|
|
53
|
+
if (type === 'PHP') return ['composer', 'require', pkg];
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getRemoveCmd = (type, pkg) => {
|
|
58
|
+
if (type === 'Node.js') return ['npm', 'uninstall', pkg];
|
|
59
|
+
if (type === 'Python') return ['pip', 'uninstall', '-y', pkg];
|
|
60
|
+
if (type === 'Rust') return ['cargo', 'remove', pkg];
|
|
61
|
+
if (type === '.NET') return ['dotnet', 'remove', 'package', pkg];
|
|
62
|
+
if (type === 'PHP') return ['composer', 'remove', pkg];
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return create(
|
|
67
|
+
Box,
|
|
68
|
+
{flexDirection: 'column', borderStyle: 'round', borderColor: 'magenta', padding: 1},
|
|
69
|
+
create(
|
|
70
|
+
Box,
|
|
71
|
+
{justifyContent: 'space-between'},
|
|
72
|
+
create(Text, {bold: true, color: 'magenta'}, `📦 Package Registry | ${selectedProject?.name}`),
|
|
73
|
+
create(Text, {dimColor: true}, projectType)
|
|
74
|
+
),
|
|
75
|
+
create(Text, {dimColor: true, marginBottom: 1}, 'Manage dependencies and environments for this project.'),
|
|
76
|
+
mode === 'list'
|
|
77
|
+
? create(
|
|
78
|
+
Box,
|
|
79
|
+
{flexDirection: 'column'},
|
|
80
|
+
create(Text, {bold: true}, `Installed Dependencies (${deps.length}):`),
|
|
81
|
+
create(
|
|
82
|
+
Box,
|
|
83
|
+
{flexDirection: 'row', flexWrap: 'wrap'},
|
|
84
|
+
...deps.map(d => create(Text, {key: d, dimColor: true}, ` ${d} `))
|
|
85
|
+
),
|
|
86
|
+
create(
|
|
87
|
+
Box,
|
|
88
|
+
{marginTop: 1, flexDirection: 'column'},
|
|
89
|
+
create(Text, {color: 'cyan'}, 'A: Add Package | R: Remove Package'),
|
|
90
|
+
projectType === 'Python' && create(Text, {color: 'yellow'}, 'V: Create .venv'),
|
|
91
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Press Shift+P to return to Navigator.')
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
: create(
|
|
95
|
+
Box,
|
|
96
|
+
{flexDirection: 'column'},
|
|
97
|
+
create(Text, {bold: true, color: 'cyan'}, mode === 'add' ? 'ADD NEW PACKAGE' : 'REMOVE PACKAGE'),
|
|
98
|
+
create(
|
|
99
|
+
Box,
|
|
100
|
+
{flexDirection: 'row'},
|
|
101
|
+
create(Text, null, 'Enter package name: '),
|
|
102
|
+
create(CursorText, {value: input, cursorIndex: cursor})
|
|
103
|
+
),
|
|
104
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Confirm, Esc: Cancel')
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export default PackageRegistry;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, {useState, memo} from 'react';
|
|
2
|
+
import {Box, Text, useInput} from 'ink';
|
|
3
|
+
|
|
4
|
+
const create = React.createElement;
|
|
5
|
+
|
|
6
|
+
const ProjectArchitect = memo(({onRunCommand, CursorText}) => {
|
|
7
|
+
const [step, setStep] = useState('framework'); // framework | name
|
|
8
|
+
const [selectedIdx, setSelectedIdx] = useState(0);
|
|
9
|
+
const [name, setName] = useState('');
|
|
10
|
+
const [cursor, setCursor] = useState(0);
|
|
11
|
+
|
|
12
|
+
const frameworks = [
|
|
13
|
+
{name: 'Next.js', cmd: (n) => ['npx', 'create-next-app@latest', n]},
|
|
14
|
+
{name: 'React (Vite)', cmd: (n) => ['npm', 'create', 'vite@latest', n, '--', '--template', 'react']},
|
|
15
|
+
{name: 'Vue (Vite)', cmd: (n) => ['npm', 'create', 'vite@latest', n, '--', '--template', 'vue']},
|
|
16
|
+
{name: 'Rust (Binary)', cmd: (n) => ['cargo', 'new', n]},
|
|
17
|
+
{name: 'Python (Basic)', cmd: (n) => ['mkdir', n]},
|
|
18
|
+
{name: 'Go Module', cmd: (n) => ['mkdir', n, '&&', 'cd', n, '&&', 'go', 'mod', 'init', n]}
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
useInput((inputStr, key) => {
|
|
22
|
+
if (step === 'framework') {
|
|
23
|
+
if (key.upArrow) setSelectedIdx(prev => (prev - 1 + frameworks.length) % frameworks.length);
|
|
24
|
+
if (key.downArrow) setSelectedIdx(prev => (prev + 1) % frameworks.length);
|
|
25
|
+
if (key.return) setStep('name');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (step === 'name') {
|
|
30
|
+
if (key.return) {
|
|
31
|
+
if (name.trim()) {
|
|
32
|
+
const f = frameworks[selectedIdx];
|
|
33
|
+
onRunCommand({label: `Create ${f.name} project: ${name}`, command: f.cmd(name.trim())});
|
|
34
|
+
}
|
|
35
|
+
setStep('framework'); setName(''); setCursor(0);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (key.escape) { setStep('framework'); setName(''); setCursor(0); return; }
|
|
39
|
+
if (key.backspace || key.delete) {
|
|
40
|
+
if (cursor > 0) {
|
|
41
|
+
setName(prev => prev.slice(0, cursor - 1) + prev.slice(cursor));
|
|
42
|
+
setCursor(c => Math.max(0, c - 1));
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (key.leftArrow) { setCursor(c => Math.max(0, c - 1)); return; }
|
|
47
|
+
if (key.rightArrow) { setCursor(c => Math.min(name.length, c + 1)); return; }
|
|
48
|
+
if (inputStr) {
|
|
49
|
+
setName(prev => prev.slice(0, cursor) + inputStr + prev.slice(cursor));
|
|
50
|
+
setCursor(c => c + inputStr.length);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return create(
|
|
56
|
+
Box,
|
|
57
|
+
{flexDirection: 'column', borderStyle: 'round', borderColor: 'cyan', padding: 1},
|
|
58
|
+
create(Text, {bold: true, color: 'cyan'}, '🏗️ Project Architect | Generator'),
|
|
59
|
+
create(Text, {dimColor: true, marginBottom: 1}, 'Create new projects from industry-standard templates.'),
|
|
60
|
+
step === 'framework'
|
|
61
|
+
? create(
|
|
62
|
+
Box,
|
|
63
|
+
{flexDirection: 'column'},
|
|
64
|
+
create(Text, {bold: true}, 'Select Framework:'),
|
|
65
|
+
...frameworks.map((f, i) => create(Text, {key: f.name, color: i === selectedIdx ? 'cyan' : 'white'}, `${i === selectedIdx ? '→' : ' '} ${f.name}`)),
|
|
66
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Pick Framework, Shift+N: Return to Navigator.')
|
|
67
|
+
)
|
|
68
|
+
: create(
|
|
69
|
+
Box,
|
|
70
|
+
{flexDirection: 'column'},
|
|
71
|
+
create(Text, {bold: true, color: 'cyan'}, 'PROJECT NAME'),
|
|
72
|
+
create(
|
|
73
|
+
Box,
|
|
74
|
+
{flexDirection: 'row'},
|
|
75
|
+
create(Text, null, 'Enter name: '),
|
|
76
|
+
create(CursorText, {value: name, cursorIndex: cursor})
|
|
77
|
+
),
|
|
78
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Create Project, Esc: Back to Frameworks')
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export default ProjectArchitect;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, {useState, useEffect, memo} from 'react';
|
|
2
|
+
import {Box, Text} from 'ink';
|
|
3
|
+
import {execa} from 'execa';
|
|
4
|
+
import {checkBinary} from '../projectDetection.js';
|
|
5
|
+
|
|
6
|
+
const create = React.createElement;
|
|
7
|
+
|
|
8
|
+
const Studio = memo(() => {
|
|
9
|
+
const [runtimes, setRuntimes] = useState([]);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const checks = [
|
|
14
|
+
{name: 'Node.js', binary: 'node', versionCmd: ['-v']},
|
|
15
|
+
{name: 'npm', binary: 'npm', versionCmd: ['-v']},
|
|
16
|
+
{name: 'Python', binary: process.platform === 'win32' ? 'python' : 'python3', versionCmd: ['--version']},
|
|
17
|
+
{name: 'Rust (Cargo)', binary: 'cargo', versionCmd: ['--version']},
|
|
18
|
+
{name: 'Go', binary: 'go', versionCmd: ['version']},
|
|
19
|
+
{name: 'Java', binary: 'java', versionCmd: ['-version']},
|
|
20
|
+
{name: 'PHP', binary: 'php', versionCmd: ['-v']},
|
|
21
|
+
{name: 'Ruby', binary: 'ruby', versionCmd: ['-v']},
|
|
22
|
+
{name: '.NET', binary: 'dotnet', versionCmd: ['--version']}
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
(async () => {
|
|
26
|
+
const results = await Promise.all(checks.map(async (lang) => {
|
|
27
|
+
if (!checkBinary(lang.binary)) {
|
|
28
|
+
return {...lang, status: 'missing', version: 'not installed'};
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const {stdout, stderr} = await execa(lang.binary, lang.versionCmd);
|
|
32
|
+
const version = (stdout || stderr || '').split('\n')[0].trim();
|
|
33
|
+
return {...lang, status: 'ok', version};
|
|
34
|
+
} catch {
|
|
35
|
+
return {...lang, status: 'error', version: 'failed to check'};
|
|
36
|
+
}
|
|
37
|
+
}));
|
|
38
|
+
setRuntimes(results);
|
|
39
|
+
setLoading(false);
|
|
40
|
+
})();
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
return create(
|
|
44
|
+
Box,
|
|
45
|
+
{flexDirection: 'column', borderStyle: 'double', borderColor: 'blue', padding: 1},
|
|
46
|
+
create(Text, {bold: true, color: 'blue'}, '💎 Omni-Studio | Environment Intelligence'),
|
|
47
|
+
create(Text, {dimColor: true, marginBottom: 1}, 'Overview of installed languages and build tools.'),
|
|
48
|
+
loading
|
|
49
|
+
? create(Text, {dimColor: true}, 'Gathering intelligence...')
|
|
50
|
+
: create(
|
|
51
|
+
Box,
|
|
52
|
+
{flexDirection: 'column'},
|
|
53
|
+
...runtimes.map(r => create(
|
|
54
|
+
Box,
|
|
55
|
+
{key: r.name, marginBottom: 0},
|
|
56
|
+
create(Text, {width: 20, color: r.status === 'ok' ? 'green' : 'red'}, `${r.status === 'ok' ? '✓' : '✗'} ${r.name}`),
|
|
57
|
+
create(Text, {dimColor: r.status !== 'ok'}, `: ${r.version}`)
|
|
58
|
+
)),
|
|
59
|
+
create(Text, {marginTop: 1, color: 'yellow'}, '🛠️ Interactive Project Creator coming soon in v3.0'),
|
|
60
|
+
create(Text, {dimColor: true}, 'Press Shift+A to return to Navigator.')
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export default Studio;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, {memo} from 'react';
|
|
2
|
+
import {Box, Text} from 'ink';
|
|
3
|
+
|
|
4
|
+
const create = React.createElement;
|
|
5
|
+
|
|
6
|
+
const TaskManager = memo(({tasks, activeTaskId, renameMode, renameInput, renameCursor, CursorText}) => {
|
|
7
|
+
return create(
|
|
8
|
+
Box,
|
|
9
|
+
{flexDirection: 'column', borderStyle: 'round', borderColor: 'yellow', padding: 1},
|
|
10
|
+
create(Text, {bold: true, color: 'yellow'}, '🛰️ Orbit Task Manager | Background Processes'),
|
|
11
|
+
create(Text, {dimColor: true, marginBottom: 1}, 'Up/Down: focus, Shift+K: Force Kill, Shift+R: Rename'),
|
|
12
|
+
...tasks.map(t => create(
|
|
13
|
+
Box,
|
|
14
|
+
{key: t.id, marginBottom: 0, flexDirection: 'column'},
|
|
15
|
+
t.id === activeTaskId && renameMode
|
|
16
|
+
? create(Box, {flexDirection: 'row'}, create(Text, {color: 'cyan'}, '→ Rename to: '), create(CursorText, {value: renameInput, cursorIndex: renameCursor}))
|
|
17
|
+
: create(Text, {color: t.id === activeTaskId ? 'cyan' : 'white', bold: t.id === activeTaskId}, `${t.id === activeTaskId ? '→' : ' '} [${t.status.toUpperCase()}] ${t.name}`)
|
|
18
|
+
)),
|
|
19
|
+
!tasks.length && create(Text, {dimColor: true}, 'No active or background tasks.'),
|
|
20
|
+
create(Text, {marginTop: 1, dimColor: true}, 'Press Enter or Shift+T to return to Navigator.')
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default TaskManager;
|