project-compass 4.0.4 → 4.0.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 +4 -3
- package/src/components/AIHorizon.js +122 -36
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -451,10 +451,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
451
451
|
if (key.shift && key.upArrow) { scrollLogs(1); return; }
|
|
452
452
|
if (key.shift && key.downArrow) { scrollLogs(-1); return; }
|
|
453
453
|
|
|
454
|
-
const
|
|
454
|
+
const pageLimit = config.maxVisibleProjects || 3;
|
|
455
|
+
const pageStep = Math.max(1, pageLimit);
|
|
455
456
|
const clampIndex = (value) => Math.max(0, Math.min(projects.length - 1, value));
|
|
456
|
-
if (key.pageUp && projects.length >
|
|
457
|
-
if (key.pageDown && projects.length >
|
|
457
|
+
if (key.pageUp && projects.length > pageLimit) { console.clear(); setSelectedIndex((prev) => clampIndex(prev - pageStep)); return; }
|
|
458
|
+
if (key.pageDown && projects.length > pageLimit) { console.clear(); setSelectedIndex((prev) => clampIndex(prev + pageStep)); return; }
|
|
458
459
|
|
|
459
460
|
if (normalizedInput === '?') { console.clear(); setShowHelp((prev) => !prev); return; }
|
|
460
461
|
if (shiftCombo('l') && lastCommandRef.current) { runProjectCommand(lastCommandRef.current.commandMeta, lastCommandRef.current.project); return; }
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/* global setTimeout */
|
|
1
|
+
/* global setTimeout, fetch */
|
|
2
2
|
import React, {useState, memo} from 'react';
|
|
3
3
|
import {Box, Text, useInput} from 'ink';
|
|
4
4
|
|
|
5
5
|
const create = React.createElement;
|
|
6
6
|
|
|
7
7
|
const AI_PROVIDERS = [
|
|
8
|
-
{ id: 'openrouter', name: 'OpenRouter', endpoint: 'https://openrouter.ai/api/v1' },
|
|
9
|
-
{ id: 'gemini', name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com' },
|
|
10
|
-
{ id: 'claude', name: 'Anthropic Claude', endpoint: 'https://api.anthropic.com' },
|
|
11
|
-
{ id: 'ollama', name: 'Ollama (Local)', endpoint: 'http://localhost:11434' }
|
|
8
|
+
{ id: 'openrouter', name: 'OpenRouter', endpoint: 'https://openrouter.ai/api/v1/chat/completions' },
|
|
9
|
+
{ id: 'gemini', name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent' },
|
|
10
|
+
{ id: 'claude', name: 'Anthropic Claude', endpoint: 'https://api.anthropic.com/v1/messages' },
|
|
11
|
+
{ id: 'ollama', name: 'Ollama (Local)', endpoint: 'http://localhost:11434/api/generate' }
|
|
12
12
|
];
|
|
13
13
|
|
|
14
14
|
const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveConfig}) => {
|
|
@@ -18,8 +18,118 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
18
18
|
const [token, setToken] = useState(config?.aiToken || '');
|
|
19
19
|
const [cursor, setCursor] = useState(0);
|
|
20
20
|
const [status, setStatus] = useState('ready');
|
|
21
|
+
const [error, setError] = useState(null);
|
|
21
22
|
const [suggestions, setSuggestions] = useState([]);
|
|
22
23
|
|
|
24
|
+
const runRealAnalysis = async () => {
|
|
25
|
+
setStatus('busy');
|
|
26
|
+
setError(null);
|
|
27
|
+
const provider = AI_PROVIDERS[providerIdx];
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const projectData = JSON.stringify({
|
|
31
|
+
name: selectedProject.name,
|
|
32
|
+
type: selectedProject.type,
|
|
33
|
+
manifest: selectedProject.manifest,
|
|
34
|
+
scripts: selectedProject.metadata?.scripts || {},
|
|
35
|
+
dependencies: selectedProject.metadata?.dependencies || []
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const prompt = `Analyze this project structure and suggest valid CLI commands for:
|
|
39
|
+
1. Build
|
|
40
|
+
2. Run
|
|
41
|
+
3. Install
|
|
42
|
+
4. Test
|
|
43
|
+
|
|
44
|
+
Project Data: ${projectData}
|
|
45
|
+
|
|
46
|
+
Return ONLY a JSON object with this structure: {"build": "cmd", "run": "cmd", "install": "cmd", "test": "cmd"}.
|
|
47
|
+
Use the project's detected type (${selectedProject.type}) to ensure commands are correct (e.g., npm, pip, cargo).`;
|
|
48
|
+
|
|
49
|
+
let response;
|
|
50
|
+
let aiText = '';
|
|
51
|
+
|
|
52
|
+
if (provider.id === 'openrouter') {
|
|
53
|
+
response = await fetch(provider.endpoint, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
'Authorization': `Bearer ${token}`,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
'HTTP-Referer': 'https://github.com/CrimsonDevil333333/project-compass',
|
|
59
|
+
'X-Title': 'Project Compass'
|
|
60
|
+
},
|
|
61
|
+
body: JSON.stringify({
|
|
62
|
+
model: model,
|
|
63
|
+
messages: [{ role: 'user', content: prompt }]
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
if (!response.ok) throw new Error(data.error?.message || 'OpenRouter Error');
|
|
68
|
+
aiText = data.choices[0].message.content;
|
|
69
|
+
} else if (provider.id === 'gemini') {
|
|
70
|
+
const url = provider.endpoint.replace('{model}', model) + `?key=${token}`;
|
|
71
|
+
response = await fetch(url, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: { 'Content-Type': 'application/json' },
|
|
74
|
+
body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] })
|
|
75
|
+
});
|
|
76
|
+
const data = await response.json();
|
|
77
|
+
if (!response.ok) throw new Error(data.error?.message || 'Gemini Error');
|
|
78
|
+
aiText = data.candidates[0].content.parts[0].text;
|
|
79
|
+
} else if (provider.id === 'claude') {
|
|
80
|
+
response = await fetch(provider.endpoint, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'x-api-key': token,
|
|
84
|
+
'anthropic-version': '2023-06-01',
|
|
85
|
+
'Content-Type': 'application/json'
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
model: model,
|
|
89
|
+
max_tokens: 1024,
|
|
90
|
+
messages: [{ role: 'user', content: prompt }]
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
const data = await response.json();
|
|
94
|
+
if (!response.ok) throw new Error(data.error?.message || 'Claude Error');
|
|
95
|
+
aiText = data.content[0].text;
|
|
96
|
+
} else if (provider.id === 'ollama') {
|
|
97
|
+
response = await fetch(provider.endpoint, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: { 'Content-Type': 'application/json' },
|
|
100
|
+
body: JSON.stringify({ model: model, prompt: prompt, stream: false })
|
|
101
|
+
});
|
|
102
|
+
const data = await response.json();
|
|
103
|
+
if (!response.ok) throw new Error(data.error || 'Ollama Error');
|
|
104
|
+
aiText = data.response;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const jsonMatch = aiText.match(/{.*?}/s);
|
|
108
|
+
if (!jsonMatch) throw new Error("AI returned invalid DNA mapping format.");
|
|
109
|
+
|
|
110
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
111
|
+
const mapped = [
|
|
112
|
+
{ label: 'AI Build', command: parsed.build.split(' ') },
|
|
113
|
+
{ label: 'AI Run', command: parsed.run.split(' ') },
|
|
114
|
+
{ label: 'AI Install', command: parsed.install.split(' ') },
|
|
115
|
+
{ label: 'AI Test', command: parsed.test.split(' ') }
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
setSuggestions(mapped);
|
|
119
|
+
const projectKey = selectedProject.path;
|
|
120
|
+
const currentCustom = config.customCommands?.[projectKey] || [];
|
|
121
|
+
const nextConfig = {
|
|
122
|
+
...config,
|
|
123
|
+
customCommands: { ...config.customCommands, [projectKey]: [...currentCustom, ...mapped] }
|
|
124
|
+
};
|
|
125
|
+
setConfig(nextConfig); saveConfig(nextConfig);
|
|
126
|
+
setStatus('done');
|
|
127
|
+
} catch (err) {
|
|
128
|
+
setError(err.message);
|
|
129
|
+
setStatus('ready');
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
23
133
|
useInput((input, key) => {
|
|
24
134
|
if (step === 'provider') {
|
|
25
135
|
if (key.upArrow) setProviderIdx(p => (p - 1 + AI_PROVIDERS.length) % AI_PROVIDERS.length);
|
|
@@ -55,32 +165,7 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
55
165
|
}
|
|
56
166
|
} else if (step === 'analyze') {
|
|
57
167
|
if (key.return && status === 'ready' && selectedProject) {
|
|
58
|
-
|
|
59
|
-
setTimeout(() => {
|
|
60
|
-
// REAL DNA MAPPING: Check project scripts and suggest real matches
|
|
61
|
-
const scripts = selectedProject.metadata?.scripts || {};
|
|
62
|
-
const suggested = [];
|
|
63
|
-
|
|
64
|
-
if (scripts.build) suggested.push({ label: 'AI Build', command: ['npm', 'run', 'build'] });
|
|
65
|
-
if (scripts.start || scripts.dev) suggested.push({ label: 'AI Run', command: ['npm', 'run', scripts.dev ? 'dev' : 'start'] });
|
|
66
|
-
if (scripts.test) suggested.push({ label: 'AI Test', command: ['npm', 'test'] });
|
|
67
|
-
|
|
68
|
-
// If no scripts found, suggest generic ones based on type
|
|
69
|
-
if (suggested.length === 0) {
|
|
70
|
-
if (selectedProject.type === 'Node.js') suggested.push({ label: 'AI Init', command: ['npm', 'install'] });
|
|
71
|
-
else if (selectedProject.type === 'Python') suggested.push({ label: 'AI Run', command: ['python', 'main.py'] });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
setSuggestions(suggested);
|
|
75
|
-
const projectKey = selectedProject.path;
|
|
76
|
-
const currentCustom = config.customCommands?.[projectKey] || [];
|
|
77
|
-
const nextConfig = {
|
|
78
|
-
...config,
|
|
79
|
-
customCommands: { ...config.customCommands, [projectKey]: [...currentCustom, ...suggested] }
|
|
80
|
-
};
|
|
81
|
-
setConfig(nextConfig); saveConfig(nextConfig);
|
|
82
|
-
setStatus('done');
|
|
83
|
-
}, 1200);
|
|
168
|
+
runRealAnalysis();
|
|
84
169
|
}
|
|
85
170
|
if (input === 'r') {
|
|
86
171
|
const nextConfig = { ...config, aiToken: '' };
|
|
@@ -132,13 +217,14 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
132
217
|
create(Text, {dimColor: true}, 'Active: ' + config.aiProvider + ' (' + config.aiModel + ')'),
|
|
133
218
|
|
|
134
219
|
create(Box, {marginTop: 1, flexDirection: 'column'},
|
|
135
|
-
status === 'ready' && create(Text, null, 'Press Enter to
|
|
136
|
-
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳
|
|
220
|
+
status === 'ready' && create(Text, null, 'Press Enter to perform real agentic analysis and auto-configure macros.'),
|
|
221
|
+
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Contacting AI Agent... mapping project structure...'),
|
|
137
222
|
status === 'done' && create(Box, {flexDirection: 'column'},
|
|
138
|
-
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped!'),
|
|
139
|
-
create(Text, null, '
|
|
223
|
+
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped via AI Agent!'),
|
|
224
|
+
create(Text, null, ' Successfully injected ' + suggestions.length + ' optimized commands into project config.'),
|
|
140
225
|
create(Text, {dimColor: true, marginTop: 1}, 'Return to Navigator to use BRIT shortcuts.')
|
|
141
|
-
)
|
|
226
|
+
),
|
|
227
|
+
error && create(Text, {color: 'red', bold: true, marginTop: 1}, ' ✗ AI ERROR: ' + error)
|
|
142
228
|
),
|
|
143
229
|
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Return, R: Reset Credentials')
|
|
144
230
|
)
|