project-compass 3.3.9 → 3.4.1
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/README.md +1 -1
- package/package.json +11 -10
- package/src/cli.js +37 -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/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "project-compass",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.4.1",
|
|
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": "^18.2.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
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
import React, {useCallback, useEffect, useMemo, useRef, useState, memo} from 'react';
|
|
3
3
|
import {render, Box, Text, useApp, useInput} from 'ink';
|
|
4
4
|
import path from 'path';
|
|
5
|
+
import {fileURLToPath} from 'url';
|
|
5
6
|
import fs from 'fs';
|
|
6
7
|
import kleur from 'kleur';
|
|
7
8
|
import {execa} from 'execa';
|
|
8
|
-
import {discoverProjects, SCHEMA_GUIDE
|
|
9
|
+
import {discoverProjects, SCHEMA_GUIDE} from './projectDetection.js';
|
|
9
10
|
import {CONFIG_PATH, ensureConfigDir} from './configPaths.js';
|
|
10
11
|
|
|
12
|
+
// Modular Components
|
|
13
|
+
import Studio from './components/Studio.js';
|
|
14
|
+
import TaskManager from './components/TaskManager.js';
|
|
15
|
+
import PackageRegistry from './components/PackageRegistry.js';
|
|
16
|
+
import ProjectArchitect from './components/ProjectArchitect.js';
|
|
17
|
+
|
|
11
18
|
const create = React.createElement;
|
|
12
19
|
const ART_CHARS = ['▁', '▃', '▄', '▅', '▇'];
|
|
13
20
|
const ART_COLORS = ['magenta', 'blue', 'cyan', 'yellow', 'red'];
|
|
@@ -90,81 +97,6 @@ function buildDetailCommands(project, config) {
|
|
|
90
97
|
return [...builtins, ...custom];
|
|
91
98
|
}
|
|
92
99
|
|
|
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
100
|
function CursorText({value, cursorIndex, active = true}) {
|
|
169
101
|
const before = value.slice(0, cursorIndex);
|
|
170
102
|
const charAt = value[cursorIndex] || ' ';
|
|
@@ -427,34 +359,15 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
427
359
|
const normalizedInput = input?.toLowerCase();
|
|
428
360
|
const shiftCombo = (char) => key.shift && normalizedInput === char;
|
|
429
361
|
|
|
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
|
-
}
|
|
362
|
+
if (shiftCombo('h')) { setConfig(prev => { const next = {...prev, showHelpCards: !prev.showHelpCards}; saveConfig(next); return next; }); return; }
|
|
363
|
+
if (shiftCombo('s')) { setConfig(prev => { const next = {...prev, showStructureGuide: !prev.showStructureGuide}; saveConfig(next); return next; }); return; }
|
|
446
364
|
if (shiftCombo('a')) { setMainView((prev) => (prev === 'navigator' ? 'studio' : 'navigator')); return; }
|
|
365
|
+
if (shiftCombo('p')) { setMainView((prev) => (prev === 'navigator' ? 'registry' : 'navigator')); return; }
|
|
366
|
+
if (shiftCombo('n')) { setMainView((prev) => (prev === 'navigator' ? 'architect' : 'navigator')); return; }
|
|
447
367
|
if (shiftCombo('x')) { setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, logs: []} : t)); setLogOffset(0); return; }
|
|
448
368
|
if (shiftCombo('e')) { exportLogs(); return; }
|
|
449
369
|
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
|
-
}
|
|
370
|
+
if (shiftCombo('b')) { setConfig(prev => { const next = {...prev, showArtBoard: !prev.showArtBoard}; saveConfig(next); return next; }); return; }
|
|
458
371
|
|
|
459
372
|
if (shiftCombo('t')) {
|
|
460
373
|
setMainView((prev) => {
|
|
@@ -485,6 +398,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
485
398
|
return;
|
|
486
399
|
}
|
|
487
400
|
|
|
401
|
+
if (mainView === 'registry' || mainView === 'architect') {
|
|
402
|
+
// Inputs handled inside components
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
488
406
|
if (running && activeTaskId && runningProcessMap.current.has(activeTaskId)) {
|
|
489
407
|
if (isCtrlC) { handleKillTask(activeTaskId); setStdinBuffer(''); setStdinCursor(0); return; }
|
|
490
408
|
if (key.return) {
|
|
@@ -612,18 +530,14 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
612
530
|
create(Text, {dimColor: true}, tile.subtext)
|
|
613
531
|
)), [projectCountLabel, rootPath, selectedProject, tasks.length, running]);
|
|
614
532
|
|
|
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
533
|
if (quitConfirm) {
|
|
622
534
|
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
535
|
}
|
|
624
536
|
|
|
625
537
|
if (mainView === 'studio') return create(Studio);
|
|
626
|
-
if (mainView === 'tasks') return create(TaskManager, {tasks, activeTaskId, renameMode, renameInput, renameCursor});
|
|
538
|
+
if (mainView === 'tasks') return create(TaskManager, {tasks, activeTaskId, setActiveTaskId, renameMode, renameInput, renameCursor, CursorText});
|
|
539
|
+
if (mainView === 'registry') return create(PackageRegistry, {selectedProject, onRunCommand: runProjectCommand, CursorText});
|
|
540
|
+
if (mainView === 'architect') return create(ProjectArchitect, {onRunCommand: runProjectCommand, CursorText});
|
|
627
541
|
|
|
628
542
|
return create(Box, {flexDirection: 'column', padding: 1},
|
|
629
543
|
create(Box, {justifyContent: 'space-between'},
|
|
@@ -646,11 +560,15 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
646
560
|
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
561
|
create(OutputPanel, {activeTask, logOffset}),
|
|
648
562
|
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:
|
|
563
|
+
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
564
|
),
|
|
651
|
-
config.showHelpCards && create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'},
|
|
565
|
+
config.showHelpCards && create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'}, [
|
|
566
|
+
{label: 'Navigation', color: 'magenta', body: ['↑ / ↓ move focus, Enter: details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
567
|
+
{label: 'Management', color: 'cyan', body: ['Shift+P Package Registry', 'Shift+N Project Architect', 'Shift+X clear / Shift+E export']},
|
|
568
|
+
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+S structure / Shift+Q quit']}
|
|
569
|
+
].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
570
|
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.'))
|
|
571
|
+
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
572
|
);
|
|
655
573
|
}
|
|
656
574
|
|
|
@@ -663,6 +581,7 @@ function parseArgs() {
|
|
|
663
581
|
if ((token === '--dir' || token === '--path') && tokens[i + 1]) { args.root = tokens[i + 1]; i += 1; }
|
|
664
582
|
else if (token === '--mode' && tokens[i + 1]) { args.mode = tokens[i + 1]; i += 1; }
|
|
665
583
|
else if (token === '--help' || token === '-h') args.help = true;
|
|
584
|
+
else if (token === '--version' || token === '-v') args.version = true;
|
|
666
585
|
else if (token === '--studio') args.view = 'studio';
|
|
667
586
|
}
|
|
668
587
|
return args;
|
|
@@ -670,6 +589,12 @@ function parseArgs() {
|
|
|
670
589
|
|
|
671
590
|
async function main() {
|
|
672
591
|
const args = parseArgs();
|
|
592
|
+
if (args.version) {
|
|
593
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
594
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
595
|
+
console.log(`v${pkg.version}`);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
673
598
|
if (args.help) {
|
|
674
599
|
console.log(kleur.cyan('Project Compass · Ink project navigator/runner'));
|
|
675
600
|
console.log('');
|
|
@@ -686,6 +611,8 @@ async function main() {
|
|
|
686
611
|
console.log(' Enter Toggle detail view for selected project');
|
|
687
612
|
console.log(' Shift+A Switch to Omni-Studio (Environment Health)');
|
|
688
613
|
console.log(' Shift+T Open Orbit Task Manager (Manage background processes)');
|
|
614
|
+
console.log(' Shift+P Open Package Registry (Add/Remove packages)');
|
|
615
|
+
console.log(' Shift+N Open Project Architect (Scaffold new projects)');
|
|
689
616
|
console.log(' Shift+D Detach from active task (Keep it running in background)');
|
|
690
617
|
console.log(' Shift+B Toggle Art Board visibility');
|
|
691
618
|
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;
|