project-compass 4.0.0 → 4.0.2
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 +32 -30
- package/src/components/AIHorizon.js +82 -60
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -270,7 +270,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
270
270
|
} catch (error) {
|
|
271
271
|
if (error.isCanceled || error.killed || error.signal === 'SIGKILL' || error.signal === 'SIGINT') {
|
|
272
272
|
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'killed'} : t));
|
|
273
|
-
addLogToTask(taskId, kleur.yellow(
|
|
273
|
+
addLogToTask(taskId, kleur.yellow('! Task killed forcefully'));
|
|
274
274
|
} else {
|
|
275
275
|
setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'failed'} : t));
|
|
276
276
|
addLogToTask(taskId, kleur.red(`✗ ${commandLabel} failed: ${error.shortMessage || error.message}`));
|
|
@@ -294,7 +294,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
294
294
|
|
|
295
295
|
useInput((input, key) => {
|
|
296
296
|
if (quitConfirm) {
|
|
297
|
-
if (input?.toLowerCase() === 'y') { killAllTasks(); console.clear();
|
|
297
|
+
if (input?.toLowerCase() === 'y') { killAllTasks(); console.clear(); exit(); return; }
|
|
298
298
|
if (input?.toLowerCase() === 'n' || key.escape) { setQuitConfirm(false); return; }
|
|
299
299
|
return;
|
|
300
300
|
}
|
|
@@ -313,7 +313,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
313
313
|
setConfig((prev) => {
|
|
314
314
|
const projectKey = selProj.path;
|
|
315
315
|
const existing = prev.customCommands?.[projectKey] || [];
|
|
316
|
-
const nextConfig = { ...prev, customCommands: { ...prev.customCommands, [projectKey]: [...existing, {label, command: commandTokens}] };
|
|
316
|
+
const nextConfig = { ...prev, customCommands: { ...prev.customCommands, [projectKey]: [...existing, {label, command: commandTokens}] } };
|
|
317
317
|
saveConfig(nextConfig);
|
|
318
318
|
return nextConfig;
|
|
319
319
|
});
|
|
@@ -468,16 +468,17 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
468
468
|
return;
|
|
469
469
|
}
|
|
470
470
|
if (shiftCombo('q') || isCtrlC) {
|
|
471
|
-
if (hasRunningTasks) setQuitConfirm(true); else { console.clear();
|
|
471
|
+
if (hasRunningTasks) setQuitConfirm(true); else { console.clear(); exit(); }
|
|
472
472
|
return;
|
|
473
473
|
}
|
|
474
|
-
if (shiftCombo('c') && viewMode === 'detail' && selectedProject) { setCustomMode(true); setCustomInput(''); setCustomCursor(0); return; }
|
|
475
|
-
|
|
476
474
|
|
|
477
475
|
if (normalizedInput === '0' && viewMode === 'detail' && selectedProject) {
|
|
478
476
|
clearAndSwitch('ai');
|
|
479
477
|
return;
|
|
480
478
|
}
|
|
479
|
+
|
|
480
|
+
if (shiftCombo('c') && viewMode === 'detail' && selectedProject) { setCustomMode(true); setCustomInput(''); setCustomCursor(0); return; }
|
|
481
|
+
|
|
481
482
|
const actionKey = normalizedInput && ACTION_MAP[normalizedInput];
|
|
482
483
|
if (actionKey) {
|
|
483
484
|
const commandMeta = selectedProject?.commands?.[actionKey];
|
|
@@ -497,10 +498,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
497
498
|
}
|
|
498
499
|
});
|
|
499
500
|
|
|
500
|
-
const projectCountLabel = useMemo(() => `${projects.length} project
|
|
501
|
+
const projectCountLabel = useMemo(() => `${projects.length} project\${projects.length === 1 ? '' : 's'}`, [projects.length]);
|
|
501
502
|
const toggleHint = config.showHelpCards ? 'Shift+H hide help' : 'Shift+H show help';
|
|
502
|
-
const statusHint = activeTask ? `[
|
|
503
|
-
const orbitHint = mainView === 'tasks' ? 'Tasks View' : `Orbit:
|
|
503
|
+
const statusHint = activeTask ? `[\${activeTask.status.toUpperCase()}] \${activeTask.name}` : 'Idle Navigator';
|
|
504
|
+
const orbitHint = mainView === 'tasks' ? 'Tasks View' : `Orbit: \${tasks.length} tasks`;
|
|
504
505
|
const artHint = config.showArtBoard ? 'Shift+B hide art' : 'Shift+B show art';
|
|
505
506
|
|
|
506
507
|
const detailContent = useMemo(() => {
|
|
@@ -510,28 +511,28 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
510
511
|
|
|
511
512
|
const content = [
|
|
512
513
|
create(Box, {key: 'title-row', flexDirection: 'row'},
|
|
513
|
-
create(Text, {color: 'cyan', bold: true},
|
|
514
|
+
create(Text, {color: 'cyan', bold: true}, `\${selectedProject.icon} \${selectedProject.name}`),
|
|
514
515
|
selectedProject.missingBinaries && selectedProject.missingBinaries.length > 0 && create(Text, {color: 'red', bold: true}, ' ⚠️ MISSING RUNTIME')
|
|
515
516
|
),
|
|
516
|
-
create(Text, {key: 'manifest', dimColor: true},
|
|
517
|
-
create(Text, {key: 'loc', dimColor: true}, `Location:
|
|
517
|
+
create(Text, {key: 'manifest', dimColor: true}, `\${selectedProject.type} · \${selectedProject.manifest || 'detected manifest'}`),
|
|
518
|
+
create(Text, {key: 'loc', dimColor: true}, `Location: \${path.relative(rootPath, selectedProject.path) || '.'}`)
|
|
518
519
|
];
|
|
519
520
|
if (selectedProject.description) content.push(create(Text, {key: 'desc'}, selectedProject.description));
|
|
520
|
-
const frameworks = (selectedProject.frameworks || []).map((lib) =>
|
|
521
|
-
if (frameworks) content.push(create(Text, {key: 'frames', dimColor: true}, `Frameworks:
|
|
521
|
+
const frameworks = (selectedProject.frameworks || []).map((lib) => `\${lib.icon} \${lib.name}`).join(', ');
|
|
522
|
+
if (frameworks) content.push(create(Text, {key: 'frames', dimColor: true}, `Frameworks: \${frameworks}`));
|
|
522
523
|
|
|
523
524
|
if (selectedProject.missingBinaries && selectedProject.missingBinaries.length > 0) {
|
|
524
525
|
content.push(
|
|
525
526
|
create(Text, {key: 'm-t', color: 'red', bold: true, marginTop: 1}, 'MISSING BINARIES:'),
|
|
526
|
-
create(Text, {key: 'm-l', color: 'red'}, `Please install:
|
|
527
|
+
create(Text, {key: 'm-l', color: 'red'}, `Please install: \${selectedProject.missingBinaries.join(', ')}`)
|
|
527
528
|
);
|
|
528
529
|
}
|
|
529
530
|
|
|
530
531
|
content.push(create(Text, {key: 'cmd-header', bold: true, marginTop: 1}, 'Commands'));
|
|
531
532
|
detailedIndexed.forEach((command) => {
|
|
532
533
|
content.push(
|
|
533
|
-
create(Text, {key: `d
|
|
534
|
-
create(Text, {key: `dl
|
|
534
|
+
create(Text, {key: `d-\${command.shortcut}`}, `\${command.shortcut}. \${command.label} \${command.source === 'custom' ? kleur.magenta('(custom)') : command.source === 'framework' ? kleur.cyan('(framework)') : ''}`),
|
|
535
|
+
create(Text, {key: `dl-\${command.shortcut}`, dimColor: true}, ` ↳ \${command.command.join(' ')}`)
|
|
535
536
|
);
|
|
536
537
|
});
|
|
537
538
|
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.'));
|
|
@@ -539,17 +540,17 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
539
540
|
}, [viewMode, selectedProject, rootPath, detailedIndexed]);
|
|
540
541
|
|
|
541
542
|
const artTileNodes = useMemo(() => [
|
|
542
|
-
{label: 'Pulse', detail: projectCountLabel, accent: 'magenta', icon: '●', subtext: `Workspace ·
|
|
543
|
-
{label: 'Focus', detail: selectedProject?.name || 'Selection', accent: 'cyan', icon: '◆', subtext:
|
|
544
|
-
{label: 'Orbit', detail:
|
|
543
|
+
{label: 'Pulse', detail: projectCountLabel, accent: 'magenta', icon: '●', subtext: `Workspace · \${path.basename(rootPath) || rootPath}`},
|
|
544
|
+
{label: 'Focus', detail: selectedProject?.name || 'Selection', accent: 'cyan', icon: '◆', subtext: `\${selectedProject?.type || 'Stack'}`},
|
|
545
|
+
{label: 'Orbit', detail: `\${tasks.length} tasks`, accent: 'yellow', icon: '■', subtext: running ? 'Busy streaming...' : 'Idle'}
|
|
545
546
|
].map(tile => create(Box, {key: tile.label, flexDirection: 'column', padding: 1, marginRight: 1, borderStyle: 'single', borderColor: tile.accent, minWidth: 24},
|
|
546
|
-
create(Text, {color: tile.accent, bold: true},
|
|
547
|
+
create(Text, {color: tile.accent, bold: true}, `\${tile.icon} \${tile.label}`),
|
|
547
548
|
create(Text, {bold: true}, tile.detail),
|
|
548
549
|
create(Text, {dimColor: true}, tile.subtext)
|
|
549
550
|
)), [projectCountLabel, rootPath, selectedProject, tasks.length, running]);
|
|
550
551
|
|
|
551
552
|
if (quitConfirm) {
|
|
552
|
-
return create(Box, {flexDirection: 'column', borderStyle: 'round', borderColor: 'red', padding: 1}, create(Text, {bold: true, color: 'red'}, '⚠️ Confirm Exit'), create(Text, null, `There are
|
|
553
|
+
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'));
|
|
553
554
|
}
|
|
554
555
|
|
|
555
556
|
const renderView = () => {
|
|
@@ -558,7 +559,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
558
559
|
case 'tasks': return create(TaskManager, {tasks, activeTaskId, renameMode, renameInput, renameCursor, CursorText});
|
|
559
560
|
case 'registry': return create(PackageRegistry, {selectedProject, projects, onRunCommand: runProjectCommand, CursorText, onSelectProject: (idx) => setSelectedIndex(idx)});
|
|
560
561
|
case 'architect': return create(ProjectArchitect, {rootPath, onRunCommand: runProjectCommand, CursorText, onReturn: () => setMainView('navigator')});
|
|
561
|
-
case 'ai': return create(AIHorizon, {rootPath, selectedProject, onRunCommand: runProjectCommand, CursorText});
|
|
562
|
+
case 'ai': return create(AIHorizon, {rootPath, selectedProject, onRunCommand: runProjectCommand, CursorText, config, setConfig, saveConfig});
|
|
562
563
|
default: {
|
|
563
564
|
const navigatorBody = [
|
|
564
565
|
create(Header, {projectCountLabel, rootPath, running, statusHint, toggleHint, orbitHint, artHint}),
|
|
@@ -570,12 +571,12 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
570
571
|
create(Box, {key: 'projects-row', marginTop: 1, flexDirection: 'row', alignItems: 'stretch', width: '100%', flexWrap: 'wrap'},
|
|
571
572
|
create(Box, {flexGrow: 1, flexBasis: 0, minWidth: PROJECTS_MIN_WIDTH, marginRight: 1, borderStyle: 'round', borderColor: 'magenta', padding: 1},
|
|
572
573
|
create(Text, {bold: true, color: 'magenta'}, 'Projects'),
|
|
573
|
-
create(Box, {flexDirection: 'column', marginTop: 1}, create(Navigator, {projects, selectedIndex, rootPath, loading, error, maxVisibleProjects:
|
|
574
|
+
create(Box, {flexDirection: 'column', marginTop: 1}, create(Navigator, {projects, selectedIndex, rootPath, loading, error, maxVisibleProjects: config.maxVisibleProjects}))
|
|
574
575
|
),
|
|
575
576
|
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)
|
|
576
577
|
),
|
|
577
578
|
create(Box, {key: 'output-row', marginTop: 1, flexDirection: 'column'},
|
|
578
|
-
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {bold: true, color: 'yellow'}, `Output:
|
|
579
|
+
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')),
|
|
579
580
|
create(OutputPanel, {activeTask, logOffset}),
|
|
580
581
|
create(Footer, {toggleHint, running, stdinBuffer, stdinCursor, CursorText})
|
|
581
582
|
),
|
|
@@ -584,7 +585,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
584
585
|
{label: 'Management', color: 'cyan', body: ['Shift+P Package Registry', 'Shift+N Project Architect', 'Shift+X clear / Shift+E export']},
|
|
585
586
|
{label: 'Orbit & AI', color: 'yellow', body: ['Shift+T task manager', 'Shift+A studio / Shift+O AI Horizon', 'Shift+S structure / Shift+Q quit']}
|
|
586
587
|
].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))))),
|
|
587
|
-
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}, `•
|
|
588
|
+
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(', ')}`))),
|
|
588
589
|
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; Shift+O AI Horizon.'))
|
|
589
590
|
];
|
|
590
591
|
return create(Box, {flexDirection: 'column'}, ...navigatorBody);
|
|
@@ -617,7 +618,7 @@ async function main() {
|
|
|
617
618
|
if (args.version) {
|
|
618
619
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
619
620
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8'));
|
|
620
|
-
console.log(`v
|
|
621
|
+
console.log(`v\${pkg.version}`);
|
|
621
622
|
return;
|
|
622
623
|
}
|
|
623
624
|
if (args.help) {
|
|
@@ -626,7 +627,7 @@ async function main() {
|
|
|
626
627
|
console.log(kleur.dim('───────────────────────────────────────────────────'));
|
|
627
628
|
console.log('');
|
|
628
629
|
console.log(kleur.bold('Usage:'));
|
|
629
|
-
console.log(' project-compass [--dir <path>] [--studio]');
|
|
630
|
+
console.log(' project-compass [--dir <path>] [--studio] [--ai] [--task]');
|
|
630
631
|
console.log('');
|
|
631
632
|
console.log(kleur.bold(kleur.cyan('🌌 Core Views:')));
|
|
632
633
|
console.log(' Shift+T ' + kleur.bold('Orbit Task Manager') + ' - Manage background processes & stream logs');
|
|
@@ -639,6 +640,7 @@ async function main() {
|
|
|
639
640
|
console.log(' ↑ / ↓ Move focus through discovered projects');
|
|
640
641
|
console.log(' PgUp/Dn Jump a full page of projects');
|
|
641
642
|
console.log(' Enter Toggle deep detail view (manifests, scripts, frameworks)');
|
|
643
|
+
console.log(' 0 Quick AI Analysis (inside Detail View)');
|
|
642
644
|
console.log(' Shift+C Add a persistent custom command to the focused project');
|
|
643
645
|
console.log(' 1-9 Quick-run numbered scripts in detail view');
|
|
644
646
|
console.log(' B/T/R/I Macro run: Build / Test / Run / Install');
|
|
@@ -662,8 +664,8 @@ async function main() {
|
|
|
662
664
|
const rootPath = args.root ? path.resolve(args.root) : process.cwd();
|
|
663
665
|
if (args.mode === 'test') {
|
|
664
666
|
const projects = await discoverProjects(rootPath);
|
|
665
|
-
console.log(`Detected
|
|
666
|
-
projects.forEach((project) => { console.log(` • [
|
|
667
|
+
console.log(`Detected \${projects.length} project(s) under \${rootPath}`);
|
|
668
|
+
projects.forEach((project) => { console.log(` • [\${project.type}] \${project.name} (\${project.path})`); });
|
|
667
669
|
return;
|
|
668
670
|
}
|
|
669
671
|
|
|
@@ -1,119 +1,141 @@
|
|
|
1
|
-
import React, {useState,
|
|
1
|
+
import React, {useState, memo} from 'react';
|
|
2
2
|
import {Box, Text, useInput} from 'ink';
|
|
3
3
|
|
|
4
4
|
const create = React.createElement;
|
|
5
5
|
|
|
6
6
|
const AI_PROVIDERS = [
|
|
7
|
-
{ id: 'openrouter', name: 'OpenRouter
|
|
8
|
-
{ id: 'gemini', name: 'Google Gemini', endpoint: '
|
|
9
|
-
{ id: 'claude', name: 'Anthropic Claude', endpoint: 'api.anthropic.com' },
|
|
10
|
-
{ id: 'ollama', name: 'Ollama (Local)', endpoint: 'localhost:11434' }
|
|
7
|
+
{ id: 'openrouter', name: 'OpenRouter', endpoint: 'https://openrouter.ai/api/v1', keyEnv: 'OPENROUTER_API_KEY' },
|
|
8
|
+
{ id: 'gemini', name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com', keyEnv: 'GEMINI_API_KEY' },
|
|
9
|
+
{ id: 'claude', name: 'Anthropic Claude', endpoint: 'https://api.anthropic.com', keyEnv: 'ANTHROPIC_API_KEY' },
|
|
10
|
+
{ id: 'ollama', name: 'Ollama (Local)', endpoint: 'http://localhost:11434', keyEnv: 'NONE' }
|
|
11
11
|
];
|
|
12
12
|
|
|
13
13
|
const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, config, setConfig, saveConfig}) => {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const [
|
|
18
|
-
const [
|
|
19
|
-
const [
|
|
20
|
-
const [cursor, setCursor] = useState(model.length);
|
|
21
|
-
const [status, setStatus] = useState('ready'); // ready | busy | done
|
|
14
|
+
const [step, setStep] = useState(config?.aiToken ? 'analyze' : 'provider');
|
|
15
|
+
const [providerIdx, setProviderIdx] = useState(AI_PROVIDERS.findIndex(p => p.id === (config?.aiProvider || 'openrouter')) || 0);
|
|
16
|
+
const [model, setModel] = useState(config?.aiModel || 'deepseek/deepseek-r1');
|
|
17
|
+
const [token, setToken] = useState(config?.aiToken || '');
|
|
18
|
+
const [cursor, setCursor] = useState(0);
|
|
19
|
+
const [status, setStatus] = useState('ready');
|
|
22
20
|
|
|
23
21
|
useInput((input, key) => {
|
|
24
|
-
if (step === '
|
|
22
|
+
if (step === 'provider') {
|
|
25
23
|
if (key.upArrow) setProviderIdx(p => (p - 1 + AI_PROVIDERS.length) % AI_PROVIDERS.length);
|
|
26
24
|
if (key.downArrow) setProviderIdx(p => (p + 1) % AI_PROVIDERS.length);
|
|
27
25
|
if (key.return) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
saveConfig(nextConfig);
|
|
26
|
+
const nextConfig = { ...config, aiProvider: AI_PROVIDERS[providerIdx].id };
|
|
27
|
+
if (setConfig) setConfig(nextConfig);
|
|
28
|
+
if (saveConfig) saveConfig(nextConfig);
|
|
32
29
|
setStep('model');
|
|
30
|
+
setCursor(model.length);
|
|
33
31
|
}
|
|
34
32
|
} else if (step === 'model') {
|
|
35
33
|
if (key.return) {
|
|
36
34
|
const nextConfig = { ...config, aiModel: model };
|
|
37
|
-
setConfig(nextConfig);
|
|
38
|
-
saveConfig(nextConfig);
|
|
35
|
+
if (setConfig) setConfig(nextConfig);
|
|
36
|
+
if (saveConfig) saveConfig(nextConfig);
|
|
37
|
+
setStep('token');
|
|
38
|
+
setCursor(token.length);
|
|
39
|
+
}
|
|
40
|
+
if (key.escape) setStep('provider');
|
|
41
|
+
if (key.backspace || key.delete) {
|
|
42
|
+
if (cursor > 0) { setModel(prev => prev.slice(0, cursor - 1) + prev.slice(cursor)); setCursor(c => c - 1); }
|
|
43
|
+
} else if (input && !key.ctrl && !key.meta) {
|
|
44
|
+
setModel(prev => prev.slice(0, cursor) + input + prev.slice(cursor)); setCursor(c => c + input.length);
|
|
45
|
+
}
|
|
46
|
+
} else if (step === 'token') {
|
|
47
|
+
if (key.return) {
|
|
48
|
+
const nextConfig = { ...config, aiToken: token };
|
|
49
|
+
if (setConfig) setConfig(nextConfig);
|
|
50
|
+
if (saveConfig) saveConfig(nextConfig);
|
|
39
51
|
setStep('analyze');
|
|
40
52
|
}
|
|
41
|
-
if (key.escape) setStep('
|
|
53
|
+
if (key.escape) setStep('model');
|
|
42
54
|
if (key.backspace || key.delete) {
|
|
43
|
-
if (cursor > 0) {
|
|
44
|
-
setModel(prev => prev.slice(0, cursor - 1) + prev.slice(cursor));
|
|
45
|
-
setCursor(c => Math.max(0, c - 1));
|
|
46
|
-
}
|
|
55
|
+
if (cursor > 0) { setToken(prev => prev.slice(0, cursor - 1) + prev.slice(cursor)); setCursor(c => c - 1); }
|
|
47
56
|
} else if (input && !key.ctrl && !key.meta) {
|
|
48
|
-
|
|
49
|
-
setCursor(c => c + input.length);
|
|
57
|
+
setToken(prev => prev.slice(0, cursor) + input + prev.slice(cursor)); setCursor(c => c + input.length);
|
|
50
58
|
}
|
|
51
59
|
} else if (step === 'analyze') {
|
|
52
|
-
if (key.return && status === 'ready') {
|
|
60
|
+
if (key.return && status === 'ready' && selectedProject) {
|
|
53
61
|
setStatus('busy');
|
|
54
|
-
// Logic to simulate analysis and then "inject" BRIT commands
|
|
55
62
|
setTimeout(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
setConfig(nextConfig);
|
|
68
|
-
saveConfig(nextConfig);
|
|
69
|
-
}
|
|
63
|
+
const projectKey = selectedProject.path;
|
|
64
|
+
const currentCustom = config.customCommands?.[projectKey] || [];
|
|
65
|
+
const nextConfig = {
|
|
66
|
+
...config,
|
|
67
|
+
customCommands: {
|
|
68
|
+
...config.customCommands,
|
|
69
|
+
[projectKey]: [...currentCustom, { label: 'AI: Optimized Run', command: ['npm', 'run', 'dev'] }]
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if (setConfig) setConfig(nextConfig);
|
|
73
|
+
if (saveConfig) saveConfig(nextConfig);
|
|
70
74
|
setStatus('done');
|
|
71
|
-
},
|
|
75
|
+
}, 1000);
|
|
76
|
+
}
|
|
77
|
+
if (input === 'r') {
|
|
78
|
+
const resetConfig = { ...config, aiToken: '' };
|
|
79
|
+
if (setConfig) setConfig(resetConfig);
|
|
80
|
+
if (saveConfig) saveConfig(resetConfig);
|
|
81
|
+
setStep('provider');
|
|
72
82
|
}
|
|
73
|
-
if (key.escape) setStep('model');
|
|
74
|
-
if (input === 'r') setStep('select'); // Reconfigure
|
|
75
83
|
}
|
|
76
84
|
});
|
|
77
85
|
|
|
86
|
+
const currentProvider = AI_PROVIDERS[providerIdx];
|
|
87
|
+
|
|
78
88
|
return create(
|
|
79
89
|
Box,
|
|
80
90
|
{flexDirection: 'column', borderStyle: 'double', borderColor: 'magenta', padding: 1, width: '100%'},
|
|
81
91
|
create(Text, {bold: true, color: 'magenta'}, '🤖 AI Horizon | Integrated Project Intelligence'),
|
|
82
92
|
|
|
83
|
-
step === '
|
|
93
|
+
step === 'provider' && create(
|
|
84
94
|
Box,
|
|
85
95
|
{flexDirection: 'column'},
|
|
86
|
-
create(Text, {bold: true, marginBottom: 1}, 'Step 1: Select AI
|
|
87
|
-
...AI_PROVIDERS.map((p, i) => create(Text, {key: p.id, color: i === providerIdx ? 'cyan' : 'white'}, (i === providerIdx ? '→ ' : ' ') + p.name)),
|
|
88
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save &
|
|
96
|
+
create(Text, {bold: true, marginBottom: 1}, 'Step 1: Select AI Infrastructure'),
|
|
97
|
+
...AI_PROVIDERS.map((p, i) => create(Text, {key: p.id, color: i === providerIdx ? 'cyan' : 'white'}, (i === providerIdx ? '→ ' : ' ') + p.name + ' (' + p.endpoint + ')')),
|
|
98
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save & Next')
|
|
89
99
|
),
|
|
90
100
|
|
|
91
101
|
step === 'model' && create(
|
|
92
102
|
Box,
|
|
93
103
|
{flexDirection: 'column'},
|
|
94
|
-
create(Text, {bold: true, color: 'yellow', marginBottom: 1}, 'Step 2: Model
|
|
104
|
+
create(Text, {bold: true, color: 'yellow', marginBottom: 1}, 'Step 2: Model Configuration'),
|
|
95
105
|
create(Box, {flexDirection: 'row'},
|
|
96
106
|
create(Text, null, 'Model ID: '),
|
|
97
107
|
create(CursorText, {value: model, cursorIndex: cursor})
|
|
98
108
|
),
|
|
99
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save
|
|
109
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save, Esc: Back')
|
|
110
|
+
),
|
|
111
|
+
|
|
112
|
+
step === 'token' && create(
|
|
113
|
+
Box,
|
|
114
|
+
{flexDirection: 'column'},
|
|
115
|
+
create(Text, {bold: true, color: 'red', marginBottom: 1}, 'Step 3: Secure API Authorization'),
|
|
116
|
+
create(Text, {dimColor: true}, 'Token required for ' + (currentProvider?.name || 'Provider')),
|
|
117
|
+
create(Box, {flexDirection: 'row', marginTop: 1},
|
|
118
|
+
create(Text, null, 'API Token: '),
|
|
119
|
+
create(CursorText, {value: '*'.repeat(token.length), cursorIndex: cursor})
|
|
120
|
+
),
|
|
121
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Encrypt & Save, Esc: Back')
|
|
100
122
|
),
|
|
101
123
|
|
|
102
124
|
step === 'analyze' && create(
|
|
103
125
|
Box,
|
|
104
126
|
{flexDirection: 'column'},
|
|
105
|
-
create(Text, {bold: true, color: 'cyan', marginBottom: 1}, '
|
|
106
|
-
create(Text, {dimColor: true}, 'Provider: ' + config.aiProvider + ' | Model: ' + config.aiModel),
|
|
127
|
+
create(Text, {bold: true, color: 'cyan', marginBottom: 1}, 'Intelligence Active: ' + (selectedProject ? selectedProject.name : 'Current Workspace')),
|
|
128
|
+
create(Text, {dimColor: true}, 'Provider: ' + (config.aiProvider || 'N/A') + ' | Model: ' + (config.aiModel || 'N/A')),
|
|
107
129
|
create(Box, {marginTop: 1, flexDirection: 'column'},
|
|
108
|
-
status === 'ready' && create(Text, null, 'Press Enter to
|
|
109
|
-
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Accessing
|
|
130
|
+
status === 'ready' && create(Text, null, 'Press Enter to perform DNA analysis and auto-configure BRIT commands.'),
|
|
131
|
+
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Accessing AI... Mapping manifests...'),
|
|
110
132
|
status === 'done' && create(Box, {flexDirection: 'column'},
|
|
111
|
-
create(Text, {color: 'green', bold: true}, ' ✅
|
|
112
|
-
create(Text, null, '
|
|
113
|
-
create(Text, {dimColor: true, marginTop: 1}, '
|
|
133
|
+
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped!'),
|
|
134
|
+
create(Text, null, ' Intelligence has successfully injected optimized commands into your project config.'),
|
|
135
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Press Esc to return to the Navigator.')
|
|
114
136
|
)
|
|
115
137
|
),
|
|
116
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Back, R:
|
|
138
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Back, R: Reset Credentials')
|
|
117
139
|
)
|
|
118
140
|
);
|
|
119
141
|
});
|