project-compass 3.6.5 โ 3.6.6
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 +1 -1
- package/src/cli.js +5 -5
- package/src/cli.js.bak +679 -0
- package/project-compass-3.5.3.tgz +0 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -630,26 +630,26 @@ async function main() {
|
|
|
630
630
|
}
|
|
631
631
|
if (args.help) {
|
|
632
632
|
console.clear();
|
|
633
|
-
console.log(kleur.
|
|
633
|
+
console.log(kleur.bold(kleur.magenta('๐งญ Project Compass ยท Premium Developer Cockpit')));
|
|
634
634
|
console.log(kleur.dim('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
635
635
|
console.log('');
|
|
636
636
|
console.log(kleur.bold('Usage:'));
|
|
637
637
|
console.log(' project-compass [--dir <path>] [--studio]');
|
|
638
638
|
console.log('');
|
|
639
|
-
console.log(kleur.
|
|
639
|
+
console.log(kleur.bold(kleur.cyan('๐ Core Views:')));
|
|
640
640
|
console.log(' Shift+T ' + kleur.bold('Orbit Task Manager') + ' - Manage background processes & stream logs');
|
|
641
641
|
console.log(' Shift+P ' + kleur.bold('Package Registry') + ' - Direct dependency management (add/remove)');
|
|
642
642
|
console.log(' Shift+N ' + kleur.bold('Project Architect') + ' - Scaffold new projects from templates');
|
|
643
643
|
console.log(' Shift+A ' + kleur.bold('Omni-Studio') + ' - Environment & runtime health audit');
|
|
644
644
|
console.log('');
|
|
645
|
-
console.log(kleur.
|
|
645
|
+
console.log(kleur.bold(kleur.yellow('๐ฎ Navigation & Details:')));
|
|
646
646
|
console.log(' โ / โ Move focus through discovered projects');
|
|
647
647
|
console.log(' Enter Toggle deep detail view (manifests, scripts, frameworks)');
|
|
648
648
|
console.log(' Shift+C Add a persistent custom command to the focused project');
|
|
649
649
|
console.log(' 1-9 Quick-run numbered scripts in detail view');
|
|
650
650
|
console.log(' B/T/R Macro run: Build / Test / Run');
|
|
651
651
|
console.log('');
|
|
652
|
-
console.log(kleur.
|
|
652
|
+
console.log(kleur.bold(kleur.green('๐ ๏ธ Workspace Tools:')));
|
|
653
653
|
console.log(' Shift+B Toggle Art-coded Build Atlas');
|
|
654
654
|
console.log(' Shift+S Toggle Directory Structure Guide');
|
|
655
655
|
console.log(' Shift+H Toggle Navigation Help Cards');
|
|
@@ -657,7 +657,7 @@ async function main() {
|
|
|
657
657
|
console.log(' Shift+X Clear active log buffer');
|
|
658
658
|
console.log(' Shift+E Export current session logs to .txt');
|
|
659
659
|
console.log('');
|
|
660
|
-
console.log(kleur.
|
|
660
|
+
console.log(kleur.bold(kleur.red('๐ช System:')));
|
|
661
661
|
console.log(' Shift+Q Quit application (triggers confirmation if tasks are running)');
|
|
662
662
|
console.log(' Esc Global "Back" key to return to Main Navigator');
|
|
663
663
|
console.log('');
|
package/src/cli.js.bak
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState, memo} from 'react';
|
|
3
|
+
import {render, Box, Text, useApp, useInput} from 'ink';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import {fileURLToPath} from 'url';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import kleur from 'kleur';
|
|
8
|
+
import {execa} from 'execa';
|
|
9
|
+
import {discoverProjects, SCHEMA_GUIDE} from './projectDetection.js';
|
|
10
|
+
import {CONFIG_PATH, ensureConfigDir} from './configPaths.js';
|
|
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
|
+
|
|
18
|
+
const create = React.createElement;
|
|
19
|
+
const ART_CHARS = ['โ', 'โ', 'โ', 'โ
', 'โ'];
|
|
20
|
+
const ART_COLORS = ['magenta', 'blue', 'cyan', 'yellow', 'red'];
|
|
21
|
+
const OUTPUT_WINDOW_SIZE = 8;
|
|
22
|
+
const OUTPUT_WINDOW_HEIGHT = OUTPUT_WINDOW_SIZE + 2;
|
|
23
|
+
const PROJECTS_MIN_WIDTH = 32;
|
|
24
|
+
const DETAILS_MIN_WIDTH = 44;
|
|
25
|
+
const HELP_CARD_MIN_WIDTH = 28;
|
|
26
|
+
const ACTION_MAP = {
|
|
27
|
+
b: 'build',
|
|
28
|
+
t: 'test',
|
|
29
|
+
r: 'run'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function saveConfig(config) {
|
|
33
|
+
try {
|
|
34
|
+
ensureConfigDir();
|
|
35
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error(`Unable to persist config: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function loadConfig() {
|
|
42
|
+
try {
|
|
43
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
44
|
+
const payload = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
45
|
+
const parsed = JSON.parse(payload || '{}');
|
|
46
|
+
return {
|
|
47
|
+
customCommands: {},
|
|
48
|
+
showArtBoard: true,
|
|
49
|
+
showHelpCards: false,
|
|
50
|
+
showStructureGuide: false,
|
|
51
|
+
...parsed,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(`Ignoring corrupt config: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function useScanner(rootPath) {
|
|
61
|
+
const [state, setState] = useState({projects: [], loading: true, error: null});
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
let cancelled = false;
|
|
65
|
+
(async () => {
|
|
66
|
+
try {
|
|
67
|
+
const projects = await discoverProjects(rootPath);
|
|
68
|
+
if (!cancelled) {
|
|
69
|
+
setState({projects, loading: false, error: null});
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (!cancelled) {
|
|
73
|
+
setState({projects: [], loading: false, error: error.message});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
return () => {
|
|
78
|
+
cancelled = true;
|
|
79
|
+
};
|
|
80
|
+
}, [rootPath]);
|
|
81
|
+
|
|
82
|
+
return state;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildDetailCommands(project, config) {
|
|
86
|
+
if (!project) return [];
|
|
87
|
+
const builtins = Object.entries(project.commands || {}).map(([key, command]) => ({
|
|
88
|
+
label: command.label || key,
|
|
89
|
+
command: command.command,
|
|
90
|
+
source: command.source || 'builtin'
|
|
91
|
+
}));
|
|
92
|
+
const custom = (config.customCommands?.[project.path] || []).map((entry) => ({
|
|
93
|
+
label: entry.label,
|
|
94
|
+
command: entry.command,
|
|
95
|
+
source: 'custom'
|
|
96
|
+
}));
|
|
97
|
+
return [...builtins, ...custom];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function CursorText({value, cursorIndex, active = true}) {
|
|
101
|
+
const before = value.slice(0, cursorIndex);
|
|
102
|
+
const charAt = value[cursorIndex] || ' ';
|
|
103
|
+
const after = value.slice(cursorIndex + 1);
|
|
104
|
+
|
|
105
|
+
return create(
|
|
106
|
+
Text,
|
|
107
|
+
null,
|
|
108
|
+
before,
|
|
109
|
+
active ? create(Text, {backgroundColor: 'white', color: 'black'}, charAt) : charAt,
|
|
110
|
+
after
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const OutputPanel = memo(({activeTask, logOffset}) => {
|
|
115
|
+
const logs = activeTask?.logs || [];
|
|
116
|
+
const logWindowStart = Math.max(0, logs.length - OUTPUT_WINDOW_SIZE - logOffset);
|
|
117
|
+
const logWindowEnd = Math.max(0, logs.length - logOffset);
|
|
118
|
+
const visibleLogs = logs.slice(logWindowStart, logWindowEnd);
|
|
119
|
+
|
|
120
|
+
const logNodes = visibleLogs.length
|
|
121
|
+
? visibleLogs.map((line, i) => create(Text, {key: i}, line))
|
|
122
|
+
: [create(Text, {key: 'empty', dimColor: true}, 'Select a task or run a command to see logs.')];
|
|
123
|
+
|
|
124
|
+
return create(
|
|
125
|
+
Box,
|
|
126
|
+
{
|
|
127
|
+
flexDirection: 'column',
|
|
128
|
+
borderStyle: 'round',
|
|
129
|
+
borderColor: 'yellow',
|
|
130
|
+
padding: 1,
|
|
131
|
+
minHeight: OUTPUT_WINDOW_HEIGHT,
|
|
132
|
+
maxHeight: OUTPUT_WINDOW_HEIGHT,
|
|
133
|
+
height: OUTPUT_WINDOW_HEIGHT,
|
|
134
|
+
overflow: 'hidden'
|
|
135
|
+
},
|
|
136
|
+
...logNodes
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
function Compass({rootPath, initialView = 'navigator'}) {
|
|
141
|
+
const {exit} = useApp();
|
|
142
|
+
const {projects, loading, error} = useScanner(rootPath);
|
|
143
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
144
|
+
const [viewMode, setViewMode] = useState('list');
|
|
145
|
+
const [mainView, setMainView] = useState(initialView);
|
|
146
|
+
const [tasks, setTasks] = useState([]);
|
|
147
|
+
const [activeTaskId, setActiveTaskId] = useState(null);
|
|
148
|
+
const [logOffset, setLogOffset] = useState(0);
|
|
149
|
+
const [customMode, setCustomMode] = useState(false);
|
|
150
|
+
const [customInput, setCustomInput] = useState('');
|
|
151
|
+
const [customCursor, setCustomCursor] = useState(0);
|
|
152
|
+
const [renameMode, setRenameMode] = useState(false);
|
|
153
|
+
const [renameInput, setRenameInput] = useState('');
|
|
154
|
+
const [renameCursor, setRenameCursor] = useState(0);
|
|
155
|
+
const [quitConfirm, setQuitConfirm] = useState(false);
|
|
156
|
+
const [config, setConfig] = useState(() => loadConfig());
|
|
157
|
+
const [stdinBuffer, setStdinBuffer] = useState('');
|
|
158
|
+
const [stdinCursor, setStdinCursor] = useState(0);
|
|
159
|
+
const [showHelp, setShowHelp] = useState(false);
|
|
160
|
+
const runningProcessMap = useRef(new Map());
|
|
161
|
+
const lastCommandRef = useRef(null);
|
|
162
|
+
|
|
163
|
+
const activeTask = useMemo(() => tasks.find(t => t.id === activeTaskId), [tasks, activeTaskId]);
|
|
164
|
+
const running = activeTask?.status === 'running';
|
|
165
|
+
const hasRunningTasks = useMemo(() => tasks.some(t => t.status === 'running'), [tasks]);
|
|
166
|
+
const selectedProject = useMemo(() => projects[selectedIndex] || null, [projects, selectedIndex]);
|
|
167
|
+
|
|
168
|
+
const addLogToTask = useCallback((taskId, line) => {
|
|
169
|
+
setTasks(prev => {
|
|
170
|
+
const idx = prev.findIndex(t => t.id === taskId);
|
|
171
|
+
if (idx === -1) return prev;
|
|
172
|
+
const t = prev[idx];
|
|
173
|
+
const normalized = typeof line === 'string' ? line : JSON.stringify(line);
|
|
174
|
+
const newLines = normalized.split(/\r?\n/).filter(l => l.trim().length > 0);
|
|
175
|
+
const nextLogs = [...t.logs, ...newLines];
|
|
176
|
+
const updatedTask = { ...t, logs: nextLogs.length > 500 ? nextLogs.slice(-500) : nextLogs };
|
|
177
|
+
const nextTasks = [...prev];
|
|
178
|
+
nextTasks[idx] = updatedTask;
|
|
179
|
+
return nextTasks;
|
|
180
|
+
});
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
const detailedIndexed = useMemo(() => buildDetailCommands(selectedProject, config).map((command, index) => {
|
|
184
|
+
const isOver9 = index >= 9;
|
|
185
|
+
const shortcut = isOver9 ? `S+${String.fromCharCode(65 + index - 9)}` : `${index + 1}`;
|
|
186
|
+
return { ...command, shortcut };
|
|
187
|
+
}), [selectedProject, config]);
|
|
188
|
+
|
|
189
|
+
const detailShortcutMap = useMemo(() => {
|
|
190
|
+
const map = new Map();
|
|
191
|
+
detailedIndexed.forEach((cmd) => {
|
|
192
|
+
if (cmd.shortcut.startsWith('S+')) {
|
|
193
|
+
map.set(cmd.shortcut.slice(2).toLowerCase(), cmd);
|
|
194
|
+
} else {
|
|
195
|
+
map.set(cmd.shortcut, cmd);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
return map;
|
|
199
|
+
}, [detailedIndexed]);
|
|
200
|
+
|
|
201
|
+
const handleKillTask = useCallback((taskId) => {
|
|
202
|
+
const proc = runningProcessMap.current.get(taskId);
|
|
203
|
+
if (proc) {
|
|
204
|
+
addLogToTask(taskId, kleur.yellow('! Triggering emergency kill sequence...'));
|
|
205
|
+
try {
|
|
206
|
+
if (process.platform === 'win32') {
|
|
207
|
+
execa('taskkill', ['/pid', proc.pid, '/f', '/t']);
|
|
208
|
+
} else if (proc.pid) {
|
|
209
|
+
process.kill(-proc.pid, 'SIGKILL');
|
|
210
|
+
} else {
|
|
211
|
+
proc.kill('SIGKILL');
|
|
212
|
+
}
|
|
213
|
+
} catch (e) {
|
|
214
|
+
addLogToTask(taskId, kleur.red(`โ Kill failed: ${e.message}`));
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
setTasks(prev => prev.filter(t => t.id !== taskId));
|
|
218
|
+
if (activeTaskId === taskId) setActiveTaskId(null);
|
|
219
|
+
}
|
|
220
|
+
}, [activeTaskId, addLogToTask]);
|
|
221
|
+
|
|
222
|
+
const killAllTasks = useCallback(() => {
|
|
223
|
+
runningProcessMap.current.forEach((proc, tid) => {
|
|
224
|
+
handleKillTask(tid);
|
|
225
|
+
});
|
|
226
|
+
runningProcessMap.current.clear();
|
|
227
|
+
}, [handleKillTask]);
|
|
228
|
+
|
|
229
|
+
const runProjectCommand = useCallback(async (commandMeta, targetProject = selectedProject) => {
|
|
230
|
+
const project = targetProject || selectedProject;
|
|
231
|
+
if (!project) return;
|
|
232
|
+
if (!commandMeta || !Array.isArray(commandMeta.command) || commandMeta.command.length === 0) return;
|
|
233
|
+
|
|
234
|
+
const commandLabel = commandMeta.label || commandMeta.command.join(' ');
|
|
235
|
+
const taskId = `task-${Date.now()}`;
|
|
236
|
+
const newTask = {
|
|
237
|
+
id: taskId,
|
|
238
|
+
name: `${project.name} ยท ${commandLabel}`,
|
|
239
|
+
status: 'running',
|
|
240
|
+
logs: [kleur.cyan(`> ${commandMeta.command.join(' ')}`)],
|
|
241
|
+
project: project.name
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
setTasks(prev => [...prev, newTask]);
|
|
245
|
+
setActiveTaskId(taskId);
|
|
246
|
+
lastCommandRef.current = {project, commandMeta};
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const subprocess = execa(commandMeta.command[0], commandMeta.command.slice(1), {
|
|
250
|
+
cwd: project.path,
|
|
251
|
+
env: process.env,
|
|
252
|
+
stdin: 'pipe',
|
|
253
|
+
detached: process.platform !== 'win32',
|
|
254
|
+
cleanup: true
|
|
255
|
+
});
|
|
256
|
+
runningProcessMap.current.set(taskId, subprocess);
|
|
257
|
+
|
|
258
|
+
subprocess.stdout?.on('data', (chunk) => addLogToTask(taskId, chunk.toString()));
|
|
259
|
+
subprocess.stderr?.on('data', (chunk) => addLogToTask(taskId, kleur.red(chunk.toString())));
|
|
260
|
+
|
|
261
|
+
await subprocess;
|
|
262
|
+
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'finished'} : t));
|
|
263
|
+
addLogToTask(taskId, kleur.green(`โ ${commandLabel} finished`));
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error.isCanceled || error.killed || error.signal === 'SIGKILL' || error.signal === 'SIGINT') {
|
|
266
|
+
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'killed'} : t));
|
|
267
|
+
addLogToTask(taskId, kleur.yellow(`! Task killed forcefully`));
|
|
268
|
+
} else {
|
|
269
|
+
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'failed'} : t));
|
|
270
|
+
addLogToTask(taskId, kleur.red(`โ ${commandLabel} failed: ${error.shortMessage || error.message}`));
|
|
271
|
+
}
|
|
272
|
+
} finally {
|
|
273
|
+
runningProcessMap.current.delete(taskId);
|
|
274
|
+
}
|
|
275
|
+
}, [addLogToTask, selectedProject]);
|
|
276
|
+
|
|
277
|
+
const exportLogs = useCallback(() => {
|
|
278
|
+
const taskToExport = tasks.find(t => t.id === activeTaskId);
|
|
279
|
+
if (!taskToExport || !taskToExport.logs.length) return;
|
|
280
|
+
try {
|
|
281
|
+
const exportPath = path.resolve(process.cwd(), `compass-${taskToExport.id}.txt`);
|
|
282
|
+
fs.writeFileSync(exportPath, taskToExport.logs.join('\n'));
|
|
283
|
+
addLogToTask(activeTaskId, kleur.green(`โ Logs exported to ${exportPath}`));
|
|
284
|
+
} catch {
|
|
285
|
+
addLogToTask(activeTaskId, kleur.red('โ Export failed'));
|
|
286
|
+
}
|
|
287
|
+
}, [tasks, activeTaskId, addLogToTask]);
|
|
288
|
+
|
|
289
|
+
useInput((input, key) => {
|
|
290
|
+
if (quitConfirm) {
|
|
291
|
+
if (input?.toLowerCase() === 'y') { killAllTasks(); exit(); return; }
|
|
292
|
+
if (input?.toLowerCase() === 'n' || key.escape) { setQuitConfirm(false); return; }
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const isCtrlC = (key.ctrl && input === 'c') || input === '\u0003';
|
|
297
|
+
|
|
298
|
+
if (customMode) {
|
|
299
|
+
if (key.return) {
|
|
300
|
+
const raw = customInput.trim();
|
|
301
|
+
const selProj = selectedProject;
|
|
302
|
+
if (selProj && raw) {
|
|
303
|
+
const [labelPart, commandPart] = raw.split('|');
|
|
304
|
+
const commandTokens = (commandPart || labelPart).trim().split(/\s+/).filter(Boolean);
|
|
305
|
+
if (commandTokens.length) {
|
|
306
|
+
const label = commandPart ? labelPart.trim() : `Custom ${selProj.name}`;
|
|
307
|
+
setConfig((prev) => {
|
|
308
|
+
const projectKey = selProj.path;
|
|
309
|
+
const existing = prev.customCommands?.[projectKey] || [];
|
|
310
|
+
const nextConfig = { ...prev, customCommands: { ...prev.customCommands, [projectKey]: [...existing, {label, command: commandTokens}] } };
|
|
311
|
+
saveConfig(nextConfig);
|
|
312
|
+
return nextConfig;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
setCustomMode(false); setCustomInput(''); setCustomCursor(0);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (key.escape) { setCustomMode(false); setCustomInput(''); setCustomCursor(0); return; }
|
|
320
|
+
if (key.backspace || key.delete) {
|
|
321
|
+
if (customCursor > 0) {
|
|
322
|
+
setCustomInput((prev) => prev.slice(0, customCursor - 1) + prev.slice(customCursor));
|
|
323
|
+
setCustomCursor(c => Math.max(0, c - 1));
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (key.leftArrow) { setCustomCursor(c => Math.max(0, c - 1)); return; }
|
|
328
|
+
if (key.rightArrow) { setCustomCursor(c => Math.min(customInput.length, c + 1)); return; }
|
|
329
|
+
if (input) {
|
|
330
|
+
setCustomInput((prev) => prev.slice(0, customCursor) + input + prev.slice(customCursor));
|
|
331
|
+
setCustomCursor(c => c + input.length);
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (renameMode) {
|
|
337
|
+
if (key.return) {
|
|
338
|
+
setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, name: renameInput} : t));
|
|
339
|
+
setRenameMode(false); setRenameInput(''); setRenameCursor(0);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (key.escape) { setRenameMode(false); setRenameInput(''); setRenameCursor(0); return; }
|
|
343
|
+
if (key.backspace || key.delete) {
|
|
344
|
+
if (renameCursor > 0) {
|
|
345
|
+
setRenameInput((prev) => prev.slice(0, renameCursor - 1) + prev.slice(renameCursor));
|
|
346
|
+
setRenameCursor(c => Math.max(0, c - 1));
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (key.leftArrow) { setRenameCursor(c => Math.max(0, c - 1)); return; }
|
|
351
|
+
if (key.rightArrow) { setRenameCursor(c => Math.min(renameInput.length, c + 1)); return; }
|
|
352
|
+
if (input) {
|
|
353
|
+
setRenameInput((prev) => prev.slice(0, renameCursor) + input + prev.slice(renameCursor));
|
|
354
|
+
setRenameCursor(c => c + input.length);
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const normalizedInput = input?.toLowerCase();
|
|
360
|
+
const shiftCombo = (char) => key.shift && normalizedInput === char;
|
|
361
|
+
|
|
362
|
+
const clearAndSwitch = (view) => {
|
|
363
|
+
console.clear();
|
|
364
|
+
setMainView(view);
|
|
365
|
+
setViewMode('list');
|
|
366
|
+
setShowHelp(false);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
if (shiftCombo('h')) { console.clear(); setConfig(prev => { const next = {...prev, showHelpCards: !prev.showHelpCards}; saveConfig(next); return next; }); return; }
|
|
370
|
+
if (shiftCombo('s')) { console.clear(); setConfig(prev => { const next = {...prev, showStructureGuide: !prev.showStructureGuide}; saveConfig(next); return next; }); return; }
|
|
371
|
+
if (shiftCombo('a')) { clearAndSwitch(mainView === 'navigator' ? 'studio' : 'navigator'); return; }
|
|
372
|
+
if (shiftCombo('p')) { clearAndSwitch(mainView === 'navigator' ? 'registry' : 'navigator'); return; }
|
|
373
|
+
if (shiftCombo('n')) { clearAndSwitch(mainView === 'navigator' ? 'architect' : 'navigator'); return; }
|
|
374
|
+
if (shiftCombo('x')) { console.clear(); setTasks(prev => prev.map(t => t.id === activeTaskId ? {...t, logs: []} : t)); setLogOffset(0); return; }
|
|
375
|
+
if (shiftCombo('e')) { exportLogs(); return; }
|
|
376
|
+
if (shiftCombo('d')) { console.clear(); setActiveTaskId(null); return; }
|
|
377
|
+
if (shiftCombo('b')) { console.clear(); setConfig(prev => { const next = {...prev, showArtBoard: !prev.showArtBoard}; saveConfig(next); return next; }); return; }
|
|
378
|
+
|
|
379
|
+
if (shiftCombo('t')) {
|
|
380
|
+
setMainView((prev) => {
|
|
381
|
+
console.clear();
|
|
382
|
+
if (prev === 'tasks') return 'navigator';
|
|
383
|
+
if (tasks.length > 0 && !activeTaskId) setActiveTaskId(tasks[0].id);
|
|
384
|
+
return 'tasks';
|
|
385
|
+
});
|
|
386
|
+
setViewMode('list');
|
|
387
|
+
setShowHelp(false);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (key.escape) {
|
|
392
|
+
if (mainView !== 'navigator') {
|
|
393
|
+
clearAndSwitch('navigator');
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const scrollLogs = (delta) => {
|
|
399
|
+
setLogOffset((prev) => {
|
|
400
|
+
const logs = activeTask?.logs || [];
|
|
401
|
+
const maxScroll = Math.max(0, logs.length - OUTPUT_WINDOW_SIZE);
|
|
402
|
+
return Math.max(0, Math.min(maxScroll, prev + delta));
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
if (mainView === 'tasks') {
|
|
407
|
+
if (tasks.length > 0) {
|
|
408
|
+
if (key.upArrow) { setActiveTaskId(prev => tasks[(tasks.findIndex(t => t.id === prev) - 1 + tasks.length) % tasks.length]?.id); return; }
|
|
409
|
+
if (key.downArrow) { setActiveTaskId(prev => tasks[(tasks.findIndex(t => t.id === prev) + 1) % tasks.length]?.id); return; }
|
|
410
|
+
if (shiftCombo('k') && activeTaskId) { handleKillTask(activeTaskId); return; }
|
|
411
|
+
if (shiftCombo('r') && activeTaskId) { setRenameMode(true); setRenameInput(activeTask.name); setRenameCursor(activeTask.name.length); return; }
|
|
412
|
+
if (isCtrlC) { handleKillTask(activeTaskId); return; }
|
|
413
|
+
}
|
|
414
|
+
if (key.return) { setMainView('navigator'); return; }
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (mainView === 'registry' || mainView === 'architect') {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (running && activeTaskId && runningProcessMap.current.has(activeTaskId)) {
|
|
423
|
+
if (isCtrlC) { handleKillTask(activeTaskId); setStdinBuffer(''); setStdinCursor(0); return; }
|
|
424
|
+
if (key.return) {
|
|
425
|
+
const proc = runningProcessMap.current.get(activeTaskId);
|
|
426
|
+
proc?.stdin?.write(stdinBuffer + '\n'); setStdinBuffer(''); setStdinCursor(0); return;
|
|
427
|
+
}
|
|
428
|
+
if (key.backspace || key.delete) {
|
|
429
|
+
if (stdinCursor > 0) {
|
|
430
|
+
setStdinBuffer(prev => prev.slice(0, stdinCursor - 1) + prev.slice(stdinCursor));
|
|
431
|
+
setStdinCursor(c => Math.max(0, c - 1));
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (key.leftArrow) { setStdinCursor(c => Math.max(0, c - 1)); return; }
|
|
436
|
+
if (key.rightArrow) { setStdinCursor(c => Math.min(stdinBuffer.length, c + 1)); return; }
|
|
437
|
+
if (input) {
|
|
438
|
+
setStdinBuffer(prev => prev.slice(0, stdinCursor) + input + prev.slice(stdinCursor));
|
|
439
|
+
setStdinCursor(c => c + input.length);
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (key.shift && key.upArrow) { scrollLogs(1); return; }
|
|
445
|
+
if (key.shift && key.downArrow) { scrollLogs(-1); return; }
|
|
446
|
+
|
|
447
|
+
if (normalizedInput === '?') { console.clear(); setShowHelp((prev) => !prev); return; }
|
|
448
|
+
if (shiftCombo('l') && lastCommandRef.current) { runProjectCommand(lastCommandRef.current.commandMeta, lastCommandRef.current.project); return; }
|
|
449
|
+
|
|
450
|
+
if (key.upArrow && !key.shift && projects.length > 0) { console.clear(); setSelectedIndex((prev) => (prev - 1 + projects.length) % projects.length); return; }
|
|
451
|
+
if (key.downArrow && !key.shift && projects.length > 0) { console.clear(); setSelectedIndex((prev) => (prev + 1) % projects.length); return; }
|
|
452
|
+
if (key.return) {
|
|
453
|
+
if (!selectedProject) return;
|
|
454
|
+
console.clear();
|
|
455
|
+
setViewMode((prev) => (prev === 'detail' ? 'list' : 'detail'));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (shiftCombo('q') || isCtrlC) {
|
|
459
|
+
if (hasRunningTasks) setQuitConfirm(true); else exit();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (shiftCombo('c') && viewMode === 'detail' && selectedProject) { setCustomMode(true); setCustomInput(''); setCustomCursor(0); return; }
|
|
463
|
+
|
|
464
|
+
const actionKey = normalizedInput && ACTION_MAP[normalizedInput];
|
|
465
|
+
if (actionKey) {
|
|
466
|
+
const commandMeta = selectedProject?.commands?.[actionKey];
|
|
467
|
+
runProjectCommand(commandMeta, selectedProject);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (viewMode === 'detail' && normalizedInput && detailShortcutMap.has(normalizedInput)) {
|
|
471
|
+
if (!isNaN(parseInt(normalizedInput))) {
|
|
472
|
+
runProjectCommand(detailShortcutMap.get(normalizedInput), selectedProject);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const reserved = ['a', 'p', 'n', 'x', 'e', 'd', 'b', 't', 'q', 'h', 's', 'l', 'c'];
|
|
476
|
+
if (key.shift && !reserved.includes(normalizedInput)) {
|
|
477
|
+
runProjectCommand(detailShortcutMap.get(normalizedInput), selectedProject);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const projectCountLabel = useMemo(() => `${projects.length} project${projects.length === 1 ? '' : 's'}`, [projects.length]);
|
|
484
|
+
const toggleHint = config.showHelpCards ? 'Shift+H hide help' : 'Shift+H show help';
|
|
485
|
+
const statusHint = activeTask ? `[${activeTask.status.toUpperCase()}] ${activeTask.name}` : 'Idle Navigator';
|
|
486
|
+
const orbitHint = mainView === 'tasks' ? 'Tasks View' : `Orbit: ${tasks.length} tasks`;
|
|
487
|
+
const artHint = config.showArtBoard ? 'Shift+B hide art' : 'Shift+B show art';
|
|
488
|
+
|
|
489
|
+
const projectRows = useMemo(() => {
|
|
490
|
+
if (loading) return [create(Text, {key: 'scanning', dimColor: true}, 'Scanning projectsโฆ')];
|
|
491
|
+
if (error) return [create(Text, {key: 'error', color: 'red'}, `Unable to scan: ${error}`)];
|
|
492
|
+
if (projects.length === 0) return [create(Text, {key: 'empty', dimColor: true}, 'No recognizable project manifests found.')];
|
|
493
|
+
|
|
494
|
+
return projects.map((project, index) => {
|
|
495
|
+
const isSelected = index === selectedIndex;
|
|
496
|
+
const frameworkBadges = (project.frameworks || []).map((frame) => `${frame.icon} ${frame.name}`).join(', ');
|
|
497
|
+
const hasMissingRuntime = project.missingBinaries && project.missingBinaries.length > 0;
|
|
498
|
+
return create(
|
|
499
|
+
Box,
|
|
500
|
+
{key: project.id, flexDirection: 'column', marginBottom: 1, padding: 1},
|
|
501
|
+
create(
|
|
502
|
+
Box,
|
|
503
|
+
{flexDirection: 'row'},
|
|
504
|
+
create(Text, {color: isSelected ? 'cyan' : 'white', bold: isSelected}, `${project.icon} ${project.name}`),
|
|
505
|
+
hasMissingRuntime && create(Text, {color: 'red', bold: true}, ' โ ๏ธ Runtime missing')
|
|
506
|
+
),
|
|
507
|
+
create(Text, {dimColor: true}, ` ${project.type} ยท ${path.relative(rootPath, project.path) || '.'}`),
|
|
508
|
+
frameworkBadges && create(Text, {dimColor: true}, ` ${frameworkBadges}`)
|
|
509
|
+
);
|
|
510
|
+
});
|
|
511
|
+
}, [loading, error, projects, selectedIndex, rootPath]);
|
|
512
|
+
|
|
513
|
+
const detailContent = useMemo(() => {
|
|
514
|
+
if (viewMode !== 'detail' || !selectedProject) {
|
|
515
|
+
return [create(Text, {key: 'e-h', dimColor: true}, 'Press Enter on a project to reveal details.')];
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const content = [
|
|
519
|
+
create(Box, {key: 'title-row', flexDirection: 'row'},
|
|
520
|
+
create(Text, {color: 'cyan', bold: true}, `${selectedProject.icon} ${selectedProject.name}`),
|
|
521
|
+
selectedProject.missingBinaries && selectedProject.missingBinaries.length > 0 && create(Text, {color: 'red', bold: true}, ' โ ๏ธ MISSING RUNTIME')
|
|
522
|
+
),
|
|
523
|
+
create(Text, {key: 'manifest', dimColor: true}, `${selectedProject.type} ยท ${selectedProject.manifest || 'detected manifest'}`),
|
|
524
|
+
create(Text, {key: 'loc', dimColor: true}, `Location: ${path.relative(rootPath, selectedProject.path) || '.'}`)
|
|
525
|
+
];
|
|
526
|
+
if (selectedProject.description) content.push(create(Text, {key: 'desc'}, selectedProject.description));
|
|
527
|
+
const frameworks = (selectedProject.frameworks || []).map((lib) => `${lib.icon} ${lib.name}`).join(', ');
|
|
528
|
+
if (frameworks) content.push(create(Text, {key: 'frames', dimColor: true}, `Frameworks: ${frameworks}`));
|
|
529
|
+
|
|
530
|
+
if (selectedProject.missingBinaries && selectedProject.missingBinaries.length > 0) {
|
|
531
|
+
content.push(
|
|
532
|
+
create(Text, {key: 'm-t', color: 'red', bold: true, marginTop: 1}, 'MISSING BINARIES:'),
|
|
533
|
+
create(Text, {key: 'm-l', color: 'red'}, `Please install: ${selectedProject.missingBinaries.join(', ')}`)
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
content.push(create(Text, {key: 'cmd-header', bold: true, marginTop: 1}, 'Commands'));
|
|
538
|
+
detailedIndexed.forEach((command) => {
|
|
539
|
+
content.push(
|
|
540
|
+
create(Text, {key: `d-${command.shortcut}`}, `${command.shortcut}. ${command.label} ${command.source === 'custom' ? kleur.magenta('(custom)') : command.source === 'framework' ? kleur.cyan('(framework)') : ''}`),
|
|
541
|
+
create(Text, {key: `dl-${command.shortcut}`, dimColor: true}, ` โณ ${command.command.join(' ')}`)
|
|
542
|
+
);
|
|
543
|
+
});
|
|
544
|
+
content.push(create(Text, {key: 'h-l', dimColor: true, marginTop: 1}, 'Press Shift+C โ label|cmd to save custom actions, Enter to close detail view.'));
|
|
545
|
+
return content;
|
|
546
|
+
}, [viewMode, selectedProject, rootPath, detailedIndexed]);
|
|
547
|
+
|
|
548
|
+
const artTileNodes = useMemo(() => [
|
|
549
|
+
{label: 'Pulse', detail: projectCountLabel, accent: 'magenta', icon: 'โ', subtext: `Workspace ยท ${path.basename(rootPath) || rootPath}`},
|
|
550
|
+
{label: 'Focus', detail: selectedProject?.name || 'Selection', accent: 'cyan', icon: 'โ', subtext: `${selectedProject?.type || 'Stack'}`},
|
|
551
|
+
{label: 'Orbit', detail: `${tasks.length} tasks`, accent: 'yellow', icon: 'โ ', subtext: running ? 'Busy streaming...' : 'Idle'}
|
|
552
|
+
].map(tile => create(Box, {key: tile.label, flexDirection: 'column', padding: 1, marginRight: 1, borderStyle: 'single', borderColor: tile.accent, minWidth: 24},
|
|
553
|
+
create(Text, {color: tile.accent, bold: true}, `${tile.icon} ${tile.label}`),
|
|
554
|
+
create(Text, {bold: true}, tile.detail),
|
|
555
|
+
create(Text, {dimColor: true}, tile.subtext)
|
|
556
|
+
)), [projectCountLabel, rootPath, selectedProject, tasks.length, running]);
|
|
557
|
+
|
|
558
|
+
if (quitConfirm) {
|
|
559
|
+
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'));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const renderView = () => {
|
|
563
|
+
switch (mainView) {
|
|
564
|
+
case 'studio': return create(Studio);
|
|
565
|
+
case 'tasks': return create(TaskManager, {tasks, activeTaskId, renameMode, renameInput, renameCursor, CursorText});
|
|
566
|
+
case 'registry': return create(PackageRegistry, {selectedProject, projects, onRunCommand: runProjectCommand, CursorText, onSelectProject: (idx) => setSelectedIndex(idx)});
|
|
567
|
+
case 'architect': return create(ProjectArchitect, {rootPath, onRunCommand: runProjectCommand, CursorText, onReturn: () => setMainView('navigator')});
|
|
568
|
+
default: {
|
|
569
|
+
const navigatorBody = [
|
|
570
|
+
create(Box, {key: 'header', justifyContent: 'space-between'},
|
|
571
|
+
create(Box, {flexDirection: 'column'}, create(Text, {color: 'magenta', bold: true}, 'Project Compass'), create(Text, {dimColor: true}, `${projectCountLabel} detected in ${rootPath}`)),
|
|
572
|
+
create(Box, {flexDirection: 'column', alignItems: 'flex-end'},
|
|
573
|
+
create(Text, {color: running ? 'yellow' : 'green'}, statusHint),
|
|
574
|
+
create(Text, {dimColor: true}, `${toggleHint} ยท ${orbitHint} ยท ${artHint} ยท Shift+Q: Quit`)
|
|
575
|
+
)
|
|
576
|
+
),
|
|
577
|
+
config.showArtBoard && create(Box, {key: 'artboard', flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
|
|
578
|
+
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {color: 'magenta', bold: true}, 'Art-coded build atlas'), create(Text, {dimColor: true}, 'press ? for overlay help')),
|
|
579
|
+
create(Box, {flexDirection: 'row', marginTop: 1}, ...ART_CHARS.map((char, i) => create(Text, {key: i, color: ART_COLORS[i % ART_COLORS.length]}, char.repeat(2)))),
|
|
580
|
+
create(Box, {flexDirection: 'row', marginTop: 1}, ...artTileNodes)
|
|
581
|
+
),
|
|
582
|
+
create(Box, {key: 'projects-row', marginTop: 1, flexDirection: 'row', alignItems: 'stretch', width: '100%', flexWrap: 'wrap'},
|
|
583
|
+
create(Box, {flexGrow: 1, flexBasis: 0, minWidth: PROJECTS_MIN_WIDTH, marginRight: 1, borderStyle: 'round', borderColor: 'magenta', padding: 1}, create(Text, {bold: true, color: 'magenta'}, 'Projects'), create(Box, {flexDirection: 'column', marginTop: 1}, ...projectRows)),
|
|
584
|
+
create(Box, {flexGrow: 1.3, flexBasis: 0, minWidth: DETAILS_MIN_WIDTH, borderStyle: 'round', borderColor: 'cyan', padding: 1, flexDirection: 'column'}, create(Text, {bold: true, color: 'cyan'}, 'Details'), ...detailContent)
|
|
585
|
+
),
|
|
586
|
+
create(Box, {key: 'output-row', marginTop: 1, flexDirection: 'column'},
|
|
587
|
+
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')),
|
|
588
|
+
create(OutputPanel, {activeTask, logOffset}),
|
|
589
|
+
create(Box, {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {dimColor: true}, running ? 'Type to feed stdin; Enter: submit.' : 'Run a command or press Shift+T to switch tasks.'), create(Text, {dimColor: true}, `${toggleHint}, Shift+S: Structure Guide`)),
|
|
590
|
+
create(Box, {marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1}, create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? ' Stdin buffer ' : ' Input ready '), create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running})))
|
|
591
|
+
),
|
|
592
|
+
config.showHelpCards && create(Box, {key: 'help-cards', marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'}, [
|
|
593
|
+
{label: 'Navigation', color: 'magenta', body: ['โ / โ move focus, Enter: details', 'Shift+โ / โ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
594
|
+
{label: 'Management', color: 'cyan', body: ['Shift+P Package Registry', 'Shift+N Project Architect', 'Shift+X clear / Shift+E export']},
|
|
595
|
+
{label: 'Orbit & Studio', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+B art board', 'Shift+S structure / Shift+Q quit']}
|
|
596
|
+
].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))))),
|
|
597
|
+
config.showStructureGuide && create(Box, {key: 'structure', 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(', ')}`))),
|
|
598
|
+
showHelp && create(Box, {key: 'overlay', 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.'))
|
|
599
|
+
];
|
|
600
|
+
return create(Box, {flexDirection: 'column'}, ...navigatorBody);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
return create(Box, {flexDirection: 'column', padding: 1, width: '100%'}, renderView());
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
function parseArgs() {
|
|
610
|
+
const args = {};
|
|
611
|
+
const tokens = process.argv.slice(2);
|
|
612
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
613
|
+
const token = tokens[i];
|
|
614
|
+
if ((token === '--dir' || token === '--path') && tokens[i + 1]) { args.root = tokens[i + 1]; i += 1; }
|
|
615
|
+
else if (token === '--mode' && tokens[i + 1]) { args.mode = tokens[i + 1]; i += 1; }
|
|
616
|
+
else if (token === '--help' || token === '-h') args.help = true;
|
|
617
|
+
else if (token === '--version' || token === '-v') args.version = true;
|
|
618
|
+
else if (token === '--studio') args.view = 'studio';
|
|
619
|
+
}
|
|
620
|
+
return args;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async function main() {
|
|
624
|
+
const args = parseArgs();
|
|
625
|
+
if (args.version) {
|
|
626
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
627
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
628
|
+
console.log(`v${pkg.version}`);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (args.help) {
|
|
632
|
+
console.clear();
|
|
633
|
+
console.log(kleur.bold(kleur.magenta('๐งญ Project Compass ยท Premium Developer Cockpit'));
|
|
634
|
+
console.log(kleur.dim('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
635
|
+
console.log('');
|
|
636
|
+
console.log(kleur.bold('Usage:'));
|
|
637
|
+
console.log(' project-compass [--dir <path>] [--studio]');
|
|
638
|
+
console.log('');
|
|
639
|
+
console.log(kleur.bold(kleur.cyan('๐ Core Views:'));
|
|
640
|
+
console.log(' Shift+T ' + kleur.bold('Orbit Task Manager') + ' - Manage background processes & stream logs');
|
|
641
|
+
console.log(' Shift+P ' + kleur.bold('Package Registry') + ' - Direct dependency management (add/remove)');
|
|
642
|
+
console.log(' Shift+N ' + kleur.bold('Project Architect') + ' - Scaffold new projects from templates');
|
|
643
|
+
console.log(' Shift+A ' + kleur.bold('Omni-Studio') + ' - Environment & runtime health audit');
|
|
644
|
+
console.log('');
|
|
645
|
+
console.log(kleur.bold(kleur.yellow('๐ฎ Navigation & Details:'));
|
|
646
|
+
console.log(' โ / โ Move focus through discovered projects');
|
|
647
|
+
console.log(' Enter Toggle deep detail view (manifests, scripts, frameworks)');
|
|
648
|
+
console.log(' Shift+C Add a persistent custom command to the focused project');
|
|
649
|
+
console.log(' 1-9 Quick-run numbered scripts in detail view');
|
|
650
|
+
console.log(' B/T/R Macro run: Build / Test / Run');
|
|
651
|
+
console.log('');
|
|
652
|
+
console.log(kleur.green.bold('๐ ๏ธ Workspace Tools:'));
|
|
653
|
+
console.log(' Shift+B Toggle Art-coded Build Atlas');
|
|
654
|
+
console.log(' Shift+S Toggle Directory Structure Guide');
|
|
655
|
+
console.log(' Shift+H Toggle Navigation Help Cards');
|
|
656
|
+
console.log(' Shift+โ/โ Scroll the live output log window');
|
|
657
|
+
console.log(' Shift+X Clear active log buffer');
|
|
658
|
+
console.log(' Shift+E Export current session logs to .txt');
|
|
659
|
+
console.log('');
|
|
660
|
+
console.log(kleur.red.bold('๐ช System:'));
|
|
661
|
+
console.log(' Shift+Q Quit application (triggers confirmation if tasks are running)');
|
|
662
|
+
console.log(' Esc Global "Back" key to return to Main Navigator');
|
|
663
|
+
console.log('');
|
|
664
|
+
console.log(kleur.dim('Documentation: https://github.com/CrimsonDevil333333/project-compass'));
|
|
665
|
+
console.log(kleur.magenta('Crafted for performance by Satyaa & Clawdy'));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const rootPath = args.root ? path.resolve(args.root) : process.cwd();
|
|
669
|
+
if (args.mode === 'test') {
|
|
670
|
+
const projects = await discoverProjects(rootPath);
|
|
671
|
+
console.log(`Detected ${projects.length} project(s) under ${rootPath}`);
|
|
672
|
+
projects.forEach((project) => { console.log(` โข [${project.type}] ${project.name} (${project.path})`); });
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
render(create(Compass, {rootPath, initialView: args.view || 'navigator'}));
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
main().catch((error) => { console.error(error); process.exit(1); });
|
|
Binary file
|