project-compass 4.0.5 → 4.1.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-compass",
3
- "version": "4.0.5",
3
+ "version": "4.1.0",
4
4
  "description": "Futuristic project navigator and runner for Node, Python, Rust, and Go",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -453,9 +453,26 @@ function Compass({rootPath, initialView = 'navigator'}) {
453
453
 
454
454
  const pageLimit = config.maxVisibleProjects || 3;
455
455
  const pageStep = Math.max(1, pageLimit);
456
- const clampIndex = (value) => Math.max(0, Math.min(projects.length - 1, value));
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; }
456
+ const clampIndex = (value) => {
457
+ const idx = Math.max(0, Math.min(projects.length - 1, value));
458
+ return isNaN(idx) ? 0 : idx;
459
+ };
460
+ if (key.pageUp && projects.length > pageLimit) {
461
+ console.clear();
462
+ setSelectedIndex((prev) => {
463
+ const next = prev - pageStep;
464
+ return next < 0 ? 0 : next;
465
+ });
466
+ return;
467
+ }
468
+ if (key.pageDown && projects.length > pageLimit) {
469
+ console.clear();
470
+ setSelectedIndex((prev) => {
471
+ const next = prev + pageStep;
472
+ return next >= projects.length ? (Math.floor((projects.length - 1) / pageLimit) * pageLimit) : next;
473
+ });
474
+ return;
475
+ }
459
476
 
460
477
  if (normalizedInput === '?') { console.clear(); setShowHelp((prev) => !prev); return; }
461
478
  if (shiftCombo('l') && lastCommandRef.current) { runProjectCommand(lastCommandRef.current.commandMeta, lastCommandRef.current.project); return; }
@@ -47,6 +47,8 @@ Return ONLY a JSON object with this structure: {"build": "cmd", "run": "cmd", "i
47
47
  Use the project's detected type (${selectedProject.type}) to ensure commands are correct (e.g., npm, pip, cargo).`;
48
48
 
49
49
  let response;
50
+ let aiText = '';
51
+
50
52
  if (provider.id === 'openrouter') {
51
53
  response = await fetch(provider.endpoint, {
52
54
  method: 'POST',
@@ -61,17 +63,49 @@ Use the project's detected type (${selectedProject.type}) to ensure commands are
61
63
  messages: [{ role: 'user', content: prompt }]
62
64
  })
63
65
  });
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.`);
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;
67
105
  }
68
106
 
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
107
  const jsonMatch = aiText.match(/{.*?}/s);
74
- if (!jsonMatch) throw new Error("AI returned invalid DNA mapping. Try a different model.");
108
+ if (!jsonMatch) throw new Error("AI returned invalid DNA mapping format.");
75
109
 
76
110
  const parsed = JSON.parse(jsonMatch[0]);
77
111
  const mapped = [
@@ -187,7 +221,7 @@ Use the project's detected type (${selectedProject.type}) to ensure commands are
187
221
  status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Contacting AI Agent... mapping project structure...'),
188
222
  status === 'done' && create(Box, {flexDirection: 'column'},
189
223
  create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped via AI Agent!'),
190
- create(Text, null, ' Successfully injected ' + suggestions.length + ' optimized commands into project config.'),
224
+ create(Text, null, ' Successfully injected ' + suggestions.length + ' optimized commands. AI detected potential port conflicts? Checking...'),
191
225
  create(Text, {dimColor: true, marginTop: 1}, 'Return to Navigator to use BRIT shortcuts.')
192
226
  ),
193
227
  error && create(Text, {color: 'red', bold: true, marginTop: 1}, ' ✗ AI ERROR: ' + error)
@@ -0,0 +1,13 @@
1
+ import path from 'path';
2
+ import { checkBinary } from './utils.js';
3
+ export default {
4
+ type: 'dotnet', label: '.NET', icon: '🎯', priority: 65, files: ['*.csproj', '*.sln'], binaries: ['dotnet'],
5
+ async build(projectPath, manifest) {
6
+ const missingBinaries = this.binaries.filter(b => !checkBinary(b));
7
+ return {
8
+ id: `${projectPath}::dotnet`, path: projectPath, name: path.basename(projectPath), type: '.NET', icon: '🎯',
9
+ priority: this.priority, commands: { install: { label: 'dotnet restore', command: ['dotnet', 'restore'] } },
10
+ metadata: {}, manifest: path.basename(manifest), description: '', missingBinaries, extra: {}
11
+ };
12
+ }
13
+ };
@@ -4,7 +4,7 @@ import { checkBinary, hasProjectFile } from './utils.js';
4
4
  export default {
5
5
  type: 'java',
6
6
  label: 'Java',
7
- icon: '☕️',
7
+ icon: '',
8
8
  priority: 80,
9
9
  files: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
10
10
  binaries: ['java', 'javac'],
@@ -31,7 +31,7 @@ export default {
31
31
  path: projectPath,
32
32
  name: path.basename(projectPath),
33
33
  type: 'Java',
34
- icon: '☕️',
34
+ icon: '',
35
35
  priority: this.priority,
36
36
  commands,
37
37
  metadata: {},
@@ -0,0 +1,13 @@
1
+ import path from 'path';
2
+ import { checkBinary } from './utils.js';
3
+ export default {
4
+ type: 'php', label: 'PHP', icon: '🐘', priority: 65, files: ['composer.json'], binaries: ['php', 'composer'],
5
+ async build(projectPath, manifest) {
6
+ const missingBinaries = this.binaries.filter(b => !checkBinary(b));
7
+ return {
8
+ id: `${projectPath}::php`, path: projectPath, name: path.basename(projectPath), type: 'PHP', icon: '🐘',
9
+ priority: this.priority, commands: { install: { label: 'Composer install', command: ['composer', 'install'] } },
10
+ metadata: {}, manifest: path.basename(manifest), description: '', missingBinaries, extra: {}
11
+ };
12
+ }
13
+ };
@@ -0,0 +1,13 @@
1
+ import path from 'path';
2
+ import { checkBinary } from './utils.js';
3
+ export default {
4
+ type: 'ruby', label: 'Ruby', icon: '💎', priority: 65, files: ['Gemfile'], binaries: ['ruby', 'bundle'],
5
+ async build(projectPath, manifest) {
6
+ const missingBinaries = this.binaries.filter(b => !checkBinary(b));
7
+ return {
8
+ id: `${projectPath}::ruby`, path: projectPath, name: path.basename(projectPath), type: 'Ruby', icon: '💎',
9
+ priority: this.priority, commands: { install: { label: 'Bundle install', command: ['bundle', 'install'] } },
10
+ metadata: {}, manifest: path.basename(manifest), description: '', missingBinaries, extra: {}
11
+ };
12
+ }
13
+ };
@@ -8,6 +8,9 @@ import pythonDetector from './detectors/python.js';
8
8
  import rustDetector from './detectors/rust.js';
9
9
  import goDetector from './detectors/go.js';
10
10
  import javaDetector from './detectors/java.js';
11
+ import phpDetector from './detectors/php.js';
12
+ import rubyDetector from './detectors/ruby.js';
13
+ import dotnetDetector from './detectors/dotnet.js';
11
14
  import genericDetector from './detectors/generic.js';
12
15
  import { builtInFrameworks } from './detectors/frameworks.js';
13
16
 
@@ -19,6 +22,9 @@ const detectors = [
19
22
  rustDetector,
20
23
  goDetector,
21
24
  javaDetector,
25
+ phpDetector,
26
+ rubyDetector,
27
+ dotnetDetector,
22
28
  genericDetector
23
29
  ];
24
30