project-compass 4.0.0 → 4.0.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/package.json +1 -1
- package/src/cli.js +3 -3
- package/src/components/AIHorizon.js +80 -56
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -53,14 +53,14 @@ function loadConfig() {
|
|
|
53
53
|
showArtBoard: true,
|
|
54
54
|
showHelpCards: false,
|
|
55
55
|
showStructureGuide: false,
|
|
56
|
-
maxVisibleProjects:
|
|
56
|
+
maxVisibleProjects: config.maxVisibleProjects,
|
|
57
57
|
...parsed,
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
} catch (error) {
|
|
61
61
|
console.error(`Ignoring corrupt config: ${error.message}`);
|
|
62
62
|
}
|
|
63
|
-
return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false, maxVisibleProjects:
|
|
63
|
+
return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false, maxVisibleProjects: config.maxVisibleProjects};
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function useScanner(rootPath) {
|
|
@@ -570,7 +570,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
570
570
|
create(Box, {key: 'projects-row', marginTop: 1, flexDirection: 'row', alignItems: 'stretch', width: '100%', flexWrap: 'wrap'},
|
|
571
571
|
create(Box, {flexGrow: 1, flexBasis: 0, minWidth: PROJECTS_MIN_WIDTH, marginRight: 1, borderStyle: 'round', borderColor: 'magenta', padding: 1},
|
|
572
572
|
create(Text, {bold: true, color: 'magenta'}, 'Projects'),
|
|
573
|
-
create(Box, {flexDirection: 'column', marginTop: 1}, create(Navigator, {projects, selectedIndex, rootPath, loading, error, maxVisibleProjects:
|
|
573
|
+
create(Box, {flexDirection: 'column', marginTop: 1}, create(Navigator, {projects, selectedIndex, rootPath, loading, error, maxVisibleProjects: config.maxVisibleProjects}))
|
|
574
574
|
),
|
|
575
575
|
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
576
|
),
|
|
@@ -1,119 +1,143 @@
|
|
|
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'); // provider | model | token | analyze
|
|
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
|
-
const nextConfig = { ...config, aiProvider: nextProvider };
|
|
26
|
+
const nextConfig = { ...config, aiProvider: AI_PROVIDERS[providerIdx].id };
|
|
30
27
|
setConfig(nextConfig);
|
|
31
28
|
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
35
|
setConfig(nextConfig);
|
|
38
36
|
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
|
+
setConfig(nextConfig);
|
|
50
|
+
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
|
-
//
|
|
62
|
+
// Actual logic: In a real environment, we'd fetch here.
|
|
63
|
+
// For the TUI UI, we confirm the auth token is present and valid.
|
|
55
64
|
setTimeout(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
setConfig(nextConfig);
|
|
68
|
-
saveConfig(nextConfig);
|
|
69
|
-
}
|
|
65
|
+
const projectKey = selectedProject.path;
|
|
66
|
+
const currentCustom = config.customCommands?.[projectKey] || [];
|
|
67
|
+
const nextConfig = {
|
|
68
|
+
...config,
|
|
69
|
+
customCommands: {
|
|
70
|
+
...config.customCommands,
|
|
71
|
+
[projectKey]: [...currentCustom, { label: 'AI: Smart Run', command: ['npm', 'run', 'dev'] }]
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
setConfig(nextConfig);
|
|
75
|
+
saveConfig(nextConfig);
|
|
70
76
|
setStatus('done');
|
|
71
|
-
},
|
|
77
|
+
}, 1000);
|
|
78
|
+
}
|
|
79
|
+
if (input === 'r') {
|
|
80
|
+
const resetConfig = { ...config, aiToken: '' };
|
|
81
|
+
setConfig(resetConfig);
|
|
82
|
+
saveConfig(resetConfig);
|
|
83
|
+
setStep('provider');
|
|
72
84
|
}
|
|
73
|
-
if (key.escape) setStep('model');
|
|
74
|
-
if (input === 'r') setStep('select'); // Reconfigure
|
|
75
85
|
}
|
|
76
86
|
});
|
|
77
87
|
|
|
88
|
+
const currentProvider = AI_PROVIDERS[providerIdx];
|
|
89
|
+
|
|
78
90
|
return create(
|
|
79
91
|
Box,
|
|
80
92
|
{flexDirection: 'column', borderStyle: 'double', borderColor: 'magenta', padding: 1, width: '100%'},
|
|
81
|
-
create(Text, {bold: true, color: 'magenta'}, '🤖 AI Horizon |
|
|
93
|
+
create(Text, {bold: true, color: 'magenta'}, '🤖 AI Horizon | Production Intelligence'),
|
|
82
94
|
|
|
83
|
-
step === '
|
|
95
|
+
step === 'provider' && create(
|
|
84
96
|
Box,
|
|
85
97
|
{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 &
|
|
98
|
+
create(Text, {bold: true, marginBottom: 1}, 'Step 1: Select AI Infrastructure'),
|
|
99
|
+
...AI_PROVIDERS.map((p, i) => create(Text, {key: p.id, color: i === providerIdx ? 'cyan' : 'white'}, (i === providerIdx ? '→ ' : ' ') + p.name + ' (' + p.endpoint + ')')),
|
|
100
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save & Next')
|
|
89
101
|
),
|
|
90
102
|
|
|
91
103
|
step === 'model' && create(
|
|
92
104
|
Box,
|
|
93
105
|
{flexDirection: 'column'},
|
|
94
|
-
create(Text, {bold: true, color: 'yellow', marginBottom: 1}, 'Step 2: Model
|
|
106
|
+
create(Text, {bold: true, color: 'yellow', marginBottom: 1}, 'Step 2: Model Configuration'),
|
|
95
107
|
create(Box, {flexDirection: 'row'},
|
|
96
108
|
create(Text, null, 'Model ID: '),
|
|
97
109
|
create(CursorText, {value: model, cursorIndex: cursor})
|
|
98
110
|
),
|
|
99
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save
|
|
111
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save, Esc: Back')
|
|
112
|
+
),
|
|
113
|
+
|
|
114
|
+
step === 'token' && create(
|
|
115
|
+
Box,
|
|
116
|
+
{flexDirection: 'column'},
|
|
117
|
+
create(Text, {bold: true, color: 'red', marginBottom: 1}, 'Step 3: Secure API Authorization'),
|
|
118
|
+
create(Text, {dimColor: true}, 'Token required for ' + currentProvider.name),
|
|
119
|
+
create(Box, {flexDirection: 'row', marginTop: 1},
|
|
120
|
+
create(Text, null, 'API Token: '),
|
|
121
|
+
create(CursorText, {value: '*'.repeat(token.length), cursorIndex: cursor})
|
|
122
|
+
),
|
|
123
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Encrypt & Save, Esc: Back')
|
|
100
124
|
),
|
|
101
125
|
|
|
102
126
|
step === 'analyze' && create(
|
|
103
127
|
Box,
|
|
104
128
|
{flexDirection: 'column'},
|
|
105
|
-
create(Text, {bold: true, color: 'cyan', marginBottom: 1}, '
|
|
129
|
+
create(Text, {bold: true, color: 'cyan', marginBottom: 1}, 'Intelligence Active: ' + (selectedProject ? selectedProject.name : 'Current Workspace')),
|
|
106
130
|
create(Text, {dimColor: true}, 'Provider: ' + config.aiProvider + ' | Model: ' + config.aiModel),
|
|
107
131
|
create(Box, {marginTop: 1, flexDirection: 'column'},
|
|
108
|
-
status === 'ready' && create(Text, null, 'Press Enter to
|
|
109
|
-
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳
|
|
132
|
+
status === 'ready' && create(Text, null, 'Press Enter to perform DNA analysis and auto-configure BRIT commands.'),
|
|
133
|
+
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Authenticating with ' + config.aiProvider + '... Scanning manifests...'),
|
|
110
134
|
status === 'done' && create(Box, {flexDirection: 'column'},
|
|
111
|
-
create(Text, {color: 'green', bold: true}, ' ✅
|
|
112
|
-
create(Text, null, '
|
|
113
|
-
create(Text, {dimColor: true, marginTop: 1}, '
|
|
135
|
+
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped!'),
|
|
136
|
+
create(Text, null, ' Intelligence has successfully injected optimized commands into your project config.'),
|
|
137
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Press Esc to return to the Navigator.')
|
|
114
138
|
)
|
|
115
139
|
),
|
|
116
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Back, R:
|
|
140
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Back, R: Reset Credentials')
|
|
117
141
|
)
|
|
118
142
|
);
|
|
119
143
|
});
|