project-compass 4.0.4 → 4.0.5
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 +88 -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,84 @@ 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
|
+
if (provider.id === 'openrouter') {
|
|
51
|
+
response = await fetch(provider.endpoint, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: {
|
|
54
|
+
'Authorization': `Bearer ${token}`,
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'HTTP-Referer': 'https://github.com/CrimsonDevil333333/project-compass',
|
|
57
|
+
'X-Title': 'Project Compass'
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
model: model,
|
|
61
|
+
messages: [{ role: 'user', content: prompt }]
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
// Fallback for others or throw unimplemented for now to avoid "fake" results
|
|
66
|
+
throw new Error(`Real-time agentic analysis for ${provider.name} is arriving in the next patch. Use OpenRouter for live testing now.`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
if (!response.ok) throw new Error(data.error?.message || 'Authentication failed. Check your token.');
|
|
71
|
+
|
|
72
|
+
const aiText = data.choices[0].message.content;
|
|
73
|
+
const jsonMatch = aiText.match(/{.*?}/s);
|
|
74
|
+
if (!jsonMatch) throw new Error("AI returned invalid DNA mapping. Try a different model.");
|
|
75
|
+
|
|
76
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
77
|
+
const mapped = [
|
|
78
|
+
{ label: 'AI Build', command: parsed.build.split(' ') },
|
|
79
|
+
{ label: 'AI Run', command: parsed.run.split(' ') },
|
|
80
|
+
{ label: 'AI Install', command: parsed.install.split(' ') },
|
|
81
|
+
{ label: 'AI Test', command: parsed.test.split(' ') }
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
setSuggestions(mapped);
|
|
85
|
+
const projectKey = selectedProject.path;
|
|
86
|
+
const currentCustom = config.customCommands?.[projectKey] || [];
|
|
87
|
+
const nextConfig = {
|
|
88
|
+
...config,
|
|
89
|
+
customCommands: { ...config.customCommands, [projectKey]: [...currentCustom, ...mapped] }
|
|
90
|
+
};
|
|
91
|
+
setConfig(nextConfig); saveConfig(nextConfig);
|
|
92
|
+
setStatus('done');
|
|
93
|
+
} catch (err) {
|
|
94
|
+
setError(err.message);
|
|
95
|
+
setStatus('ready');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
23
99
|
useInput((input, key) => {
|
|
24
100
|
if (step === 'provider') {
|
|
25
101
|
if (key.upArrow) setProviderIdx(p => (p - 1 + AI_PROVIDERS.length) % AI_PROVIDERS.length);
|
|
@@ -55,32 +131,7 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
55
131
|
}
|
|
56
132
|
} else if (step === 'analyze') {
|
|
57
133
|
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);
|
|
134
|
+
runRealAnalysis();
|
|
84
135
|
}
|
|
85
136
|
if (input === 'r') {
|
|
86
137
|
const nextConfig = { ...config, aiToken: '' };
|
|
@@ -132,13 +183,14 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
132
183
|
create(Text, {dimColor: true}, 'Active: ' + config.aiProvider + ' (' + config.aiModel + ')'),
|
|
133
184
|
|
|
134
185
|
create(Box, {marginTop: 1, flexDirection: 'column'},
|
|
135
|
-
status === 'ready' && create(Text, null, 'Press Enter to
|
|
136
|
-
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳
|
|
186
|
+
status === 'ready' && create(Text, null, 'Press Enter to perform real agentic analysis and auto-configure macros.'),
|
|
187
|
+
status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Contacting AI Agent... mapping project structure...'),
|
|
137
188
|
status === 'done' && create(Box, {flexDirection: 'column'},
|
|
138
|
-
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped!'),
|
|
139
|
-
create(Text, null, '
|
|
189
|
+
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped via AI Agent!'),
|
|
190
|
+
create(Text, null, ' Successfully injected ' + suggestions.length + ' optimized commands into project config.'),
|
|
140
191
|
create(Text, {dimColor: true, marginTop: 1}, 'Return to Navigator to use BRIT shortcuts.')
|
|
141
|
-
)
|
|
192
|
+
),
|
|
193
|
+
error && create(Text, {color: 'red', bold: true, marginTop: 1}, ' ✗ AI ERROR: ' + error)
|
|
142
194
|
),
|
|
143
195
|
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Return, R: Reset Credentials')
|
|
144
196
|
)
|