project-compass 4.0.3 → 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/commands.md +7 -0
- package/package.json +1 -1
- package/src/cli.js +8 -8
- package/src/components/AIHorizon.js +107 -51
package/commands.md
CHANGED
|
@@ -92,3 +92,10 @@ Compass scans for the following manifests and requires their binaries in your PA
|
|
|
92
92
|
- Go (mod init)
|
|
93
93
|
- **Enter**: Confirm selection and move to next step.
|
|
94
94
|
- **Esc / Shift+N**: Exit architect mode.
|
|
95
|
+
|
|
96
|
+
## Advanced Configuration
|
|
97
|
+
|
|
98
|
+
You can manually edit the config file to change defaults:
|
|
99
|
+
- **Location**: `~/.project-compass/config.json`
|
|
100
|
+
- **maxVisibleProjects**: Adjust how many projects appear per page (default: 3).
|
|
101
|
+
- **aiProvider / aiModel / aiToken**: Change your AI intelligence settings.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -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();
|
|
297
|
+
if (input?.toLowerCase() === 'y') { killAllTasks(); process.stdout.write('\x1b[2J\x1b[0;0H'); exit(); return; }
|
|
298
298
|
if (input?.toLowerCase() === 'n' || key.escape) { setQuitConfirm(false); return; }
|
|
299
299
|
return;
|
|
300
300
|
}
|
|
@@ -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; }
|
|
@@ -468,7 +469,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
468
469
|
return;
|
|
469
470
|
}
|
|
470
471
|
if (shiftCombo('q') || isCtrlC) {
|
|
471
|
-
if (hasRunningTasks) setQuitConfirm(true); else {
|
|
472
|
+
if (hasRunningTasks) setQuitConfirm(true); else { process.stdout.write('\x1b[2J\x1b[0;0H'); exit(); }
|
|
472
473
|
return;
|
|
473
474
|
}
|
|
474
475
|
|
|
@@ -581,9 +582,9 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
581
582
|
create(Footer, {toggleHint, running, stdinBuffer, stdinCursor, CursorText})
|
|
582
583
|
),
|
|
583
584
|
config.showHelpCards && create(Box, {key: 'help-cards', marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'}, [
|
|
584
|
-
{label: 'Navigation', color: 'magenta', body: ['
|
|
585
|
+
{label: 'Navigation', color: 'magenta', body: ['↑/↓: focus, PgUp/Dn: Page, Enter: Details', 'Shift+↑ / ↓ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
585
586
|
{label: 'Management', color: 'cyan', body: ['Shift+P Package Registry', 'Shift+N Project Architect', 'Shift+X clear / Shift+E export']},
|
|
586
|
-
{label: 'Orbit & AI', color: 'yellow', body: ['Shift+T
|
|
587
|
+
{label: 'Orbit & AI', color: 'yellow', body: ['Shift+T: Tasks, Shift+O: AI, 0: Analyze', 'Shift+A studio / Shift+O AI Horizon', 'Shift+S structure / Shift+Q quit']}
|
|
587
588
|
].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))))),
|
|
588
589
|
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(', ')}`))),
|
|
589
590
|
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.'))
|
|
@@ -622,7 +623,6 @@ async function main() {
|
|
|
622
623
|
return;
|
|
623
624
|
}
|
|
624
625
|
if (args.help) {
|
|
625
|
-
console.clear();
|
|
626
626
|
console.log(kleur.bold(kleur.magenta('🧭 Project Compass · Premium Developer Cockpit')));
|
|
627
627
|
console.log(kleur.dim('───────────────────────────────────────────────────'));
|
|
628
628
|
console.log('');
|
|
@@ -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,6 +18,83 @@ 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);
|
|
22
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
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
|
+
};
|
|
21
98
|
|
|
22
99
|
useInput((input, key) => {
|
|
23
100
|
if (step === 'provider') {
|
|
@@ -25,18 +102,14 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
25
102
|
if (key.downArrow) setProviderIdx(p => (p + 1) % AI_PROVIDERS.length);
|
|
26
103
|
if (key.return) {
|
|
27
104
|
const nextConfig = { ...config, aiProvider: AI_PROVIDERS[providerIdx].id };
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
setStep('model');
|
|
31
|
-
setCursor(model.length);
|
|
105
|
+
setConfig(nextConfig); saveConfig(nextConfig);
|
|
106
|
+
setStep('model'); setCursor(model.length);
|
|
32
107
|
}
|
|
33
108
|
} else if (step === 'model') {
|
|
34
109
|
if (key.return) {
|
|
35
110
|
const nextConfig = { ...config, aiModel: model };
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
setStep('token');
|
|
39
|
-
setCursor(token.length);
|
|
111
|
+
setConfig(nextConfig); saveConfig(nextConfig);
|
|
112
|
+
setStep('token'); setCursor(token.length);
|
|
40
113
|
}
|
|
41
114
|
if (key.escape) setStep('provider');
|
|
42
115
|
if (key.backspace || key.delete) {
|
|
@@ -47,8 +120,7 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
47
120
|
} else if (step === 'token') {
|
|
48
121
|
if (key.return) {
|
|
49
122
|
const nextConfig = { ...config, aiToken: token };
|
|
50
|
-
|
|
51
|
-
if (saveConfig) saveConfig(nextConfig);
|
|
123
|
+
setConfig(nextConfig); saveConfig(nextConfig);
|
|
52
124
|
setStep('analyze');
|
|
53
125
|
}
|
|
54
126
|
if (key.escape) setStep('model');
|
|
@@ -59,33 +131,16 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
59
131
|
}
|
|
60
132
|
} else if (step === 'analyze') {
|
|
61
133
|
if (key.return && status === 'ready' && selectedProject) {
|
|
62
|
-
|
|
63
|
-
setTimeout(() => {
|
|
64
|
-
const projectKey = selectedProject.path;
|
|
65
|
-
const currentCustom = config.customCommands?.[projectKey] || [];
|
|
66
|
-
const nextConfig = {
|
|
67
|
-
...config,
|
|
68
|
-
customCommands: {
|
|
69
|
-
...config.customCommands,
|
|
70
|
-
[projectKey]: [...currentCustom, { label: 'AI: Optimized Run', command: ['npm', 'run', 'dev'] }]
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
if (setConfig) setConfig(nextConfig);
|
|
74
|
-
if (saveConfig) saveConfig(nextConfig);
|
|
75
|
-
setStatus('done');
|
|
76
|
-
}, 1000);
|
|
134
|
+
runRealAnalysis();
|
|
77
135
|
}
|
|
78
136
|
if (input === 'r') {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
if (saveConfig) saveConfig(resetConfig);
|
|
137
|
+
const nextConfig = { ...config, aiToken: '' };
|
|
138
|
+
setConfig(nextConfig); saveConfig(nextConfig);
|
|
82
139
|
setStep('provider');
|
|
83
140
|
}
|
|
84
141
|
}
|
|
85
142
|
});
|
|
86
143
|
|
|
87
|
-
const currentProvider = AI_PROVIDERS[providerIdx];
|
|
88
|
-
|
|
89
144
|
return create(
|
|
90
145
|
Box,
|
|
91
146
|
{flexDirection: 'column', borderStyle: 'double', borderColor: 'magenta', padding: 1, width: '100%'},
|
|
@@ -95,7 +150,7 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
95
150
|
Box,
|
|
96
151
|
{flexDirection: 'column'},
|
|
97
152
|
create(Text, {bold: true, marginBottom: 1}, 'Step 1: Select AI Infrastructure'),
|
|
98
|
-
...AI_PROVIDERS.map((p, i) => create(Text, {key: p.id, color: i === providerIdx ? 'cyan' : 'white'}, (i === providerIdx ? '→ ' : ' ') + p.name
|
|
153
|
+
...AI_PROVIDERS.map((p, i) => create(Text, {key: p.id, color: i === providerIdx ? 'cyan' : 'white'}, (i === providerIdx ? '→ ' : ' ') + p.name)),
|
|
99
154
|
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save & Next')
|
|
100
155
|
),
|
|
101
156
|
|
|
@@ -107,36 +162,37 @@ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveCon
|
|
|
107
162
|
create(Text, null, 'Model ID: '),
|
|
108
163
|
create(CursorText, {value: model, cursorIndex: cursor})
|
|
109
164
|
),
|
|
110
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save, Esc: Back')
|
|
165
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save Model, Esc: Back')
|
|
111
166
|
),
|
|
112
167
|
|
|
113
168
|
step === 'token' && create(
|
|
114
169
|
Box,
|
|
115
170
|
{flexDirection: 'column'},
|
|
116
|
-
create(Text, {bold: true, color: 'red', marginBottom: 1}, 'Step 3:
|
|
117
|
-
create(
|
|
118
|
-
|
|
119
|
-
create(Text, null, 'API Token: '),
|
|
171
|
+
create(Text, {bold: true, color: 'red', marginBottom: 1}, 'Step 3: API Token Authorization'),
|
|
172
|
+
create(Box, {flexDirection: 'row'},
|
|
173
|
+
create(Text, null, 'Token: '),
|
|
120
174
|
create(CursorText, {value: '*'.repeat(token.length), cursorIndex: cursor})
|
|
121
175
|
),
|
|
122
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Enter:
|
|
176
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Enter: Save Token, Esc: Back')
|
|
123
177
|
),
|
|
124
178
|
|
|
125
179
|
step === 'analyze' && create(
|
|
126
180
|
Box,
|
|
127
181
|
{flexDirection: 'column'},
|
|
128
|
-
create(Text, {bold: true, color: 'cyan', marginBottom: 1}, '
|
|
129
|
-
create(Text, {dimColor: true}, '
|
|
182
|
+
create(Text, {bold: true, color: 'cyan', marginBottom: 1}, 'Ready to analyze: ' + (selectedProject ? selectedProject.name : 'No project selected')),
|
|
183
|
+
create(Text, {dimColor: true}, 'Active: ' + config.aiProvider + ' (' + config.aiModel + ')'),
|
|
184
|
+
|
|
130
185
|
create(Box, {marginTop: 1, flexDirection: 'column'},
|
|
131
|
-
status === 'ready' && create(Text, null, 'Press Enter to perform
|
|
132
|
-
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...'),
|
|
133
188
|
status === 'done' && create(Box, {flexDirection: 'column'},
|
|
134
|
-
create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped!'),
|
|
135
|
-
create(Text, null, '
|
|
136
|
-
create(Text, {dimColor: true, marginTop: 1}, '
|
|
137
|
-
)
|
|
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.'),
|
|
191
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Return to Navigator to use BRIT shortcuts.')
|
|
192
|
+
),
|
|
193
|
+
error && create(Text, {color: 'red', bold: true, marginTop: 1}, ' ✗ AI ERROR: ' + error)
|
|
138
194
|
),
|
|
139
|
-
create(Text, {dimColor: true, marginTop: 1}, 'Esc:
|
|
195
|
+
create(Text, {dimColor: true, marginTop: 1}, 'Esc: Return, R: Reset Credentials')
|
|
140
196
|
)
|
|
141
197
|
);
|
|
142
198
|
});
|