project-compass 4.3.3 → 4.3.7

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.
@@ -58,7 +58,7 @@ export default {
58
58
  const commands = {
59
59
  install: { label: 'Bundle install', command: ['bundle', 'install'], source: 'builtin' },
60
60
  update: { label: 'Bundle update', command: ['bundle', 'update'], source: 'builtin' },
61
- console: { label: 'Ruby console', command: ['ruby', '-e', 'puts "IRB"'], source: 'builtin' }
61
+ console: { label: 'Ruby console', command: ['irb'], source: 'builtin' }
62
62
  };
63
63
 
64
64
  if (hasProjectFile(projectPath, 'bin/rails')) {
@@ -40,8 +40,8 @@ function parseCargoToml(content) {
40
40
  }
41
41
 
42
42
  if (inDependencies && trimmed && !trimmed.startsWith('#')) {
43
- const depName = trimmed.split('=')[0]?.trim() || trimmed.split('{')[0]?.trim();
44
- if (depName) metadata.dependencies.push(depName);
43
+ const depName = trimmed.split(/[={]/)[0]?.trim();
44
+ if (depName && !depName.startsWith('#')) metadata.dependencies.push(depName);
45
45
  }
46
46
 
47
47
  if (inBin && trimmed.startsWith('name')) {
@@ -98,13 +98,12 @@ export function dependencyMatches(project, needle) {
98
98
  return '';
99
99
  }).filter(Boolean);
100
100
  const target = needle.toLowerCase();
101
- return dependencies.some((value) =>
102
- value === target ||
103
- value.startsWith(`${target}@`) ||
104
- value.includes(`/${target}`) ||
105
- value.startsWith(`${target}/`) ||
106
- value.endsWith(`/${target}`)
107
- );
101
+ return dependencies.some((value) => {
102
+ if (value === target) return true;
103
+ if (value.startsWith(`${target}@`) || value.startsWith(`${target}==`) || value.startsWith(`${target}>=`) || value.startsWith(`${target}~=`)) return true;
104
+ if (value.startsWith(`${target}/`) || value.endsWith(`/${target}`)) return true;
105
+ return false;
106
+ });
108
107
  }
109
108
 
110
109
  export function parseCommandTokens(value) {
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import fastGlob from 'fast-glob';
4
- import { ensureConfigDir, PLUGIN_FILE } from './configPaths.js';
4
+ import { ensureConfigDir, PLUGIN_FILE, CONFIG_PATH } from './configPaths.js';
5
5
  import { dependencyMatches, hasProjectFile, parseCommandTokens, checkBinary } from './detectors/utils.js';
6
6
  import nodeDetector from './detectors/node.js';
7
7
  import pythonDetector from './detectors/python.js';
@@ -13,6 +13,7 @@ import rubyDetector from './detectors/ruby.js';
13
13
  import dotnetDetector from './detectors/dotnet.js';
14
14
  import genericDetector from './detectors/generic.js';
15
15
  import { builtInFrameworks } from './detectors/frameworks.js';
16
+ import { loadProjectConfig } from './detectors/compass-config.js';
16
17
 
17
18
  const IGNORE_PATTERNS = ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**', '**/target/**'];
18
19
 
@@ -71,11 +72,20 @@ function loadUserFrameworks() {
71
72
  }
72
73
 
73
74
  let cachedFrameworkPlugins = null;
75
+ let cachedPluginMtime = 0;
74
76
 
75
77
  function getFrameworkPlugins() {
76
- if (cachedFrameworkPlugins) {
78
+ let currentMtime = 0;
79
+ try {
80
+ if (fs.existsSync(PLUGIN_FILE)) {
81
+ currentMtime = fs.statSync(PLUGIN_FILE).mtimeMs;
82
+ }
83
+ } catch { /* ignore */ }
84
+
85
+ if (cachedFrameworkPlugins && currentMtime <= cachedPluginMtime) {
77
86
  return cachedFrameworkPlugins;
78
87
  }
88
+ cachedPluginMtime = currentMtime;
79
89
  cachedFrameworkPlugins = [...builtInFrameworks, ...loadUserFrameworks()];
80
90
  return cachedFrameworkPlugins;
81
91
  }
@@ -114,13 +124,16 @@ function matchesPlugin(project, plugin) {
114
124
  function applyFrameworkPlugins(project) {
115
125
  const plugins = getFrameworkPlugins();
116
126
  let commands = { ...project.commands };
117
- const frameworks = [];
127
+ const frameworks = [...(project.frameworks || [])];
118
128
  let maxPriority = project.priority || 0;
119
129
  for (const plugin of plugins) {
120
130
  if (!matchesPlugin(project, plugin)) {
121
131
  continue;
122
132
  }
123
- frameworks.push({ id: plugin.id, name: plugin.name, icon: plugin.icon, description: plugin.description });
133
+ const exists = frameworks.some(f => f.name === plugin.name);
134
+ if (!exists) {
135
+ frameworks.push({ id: plugin.id, name: plugin.name, icon: plugin.icon, description: plugin.description });
136
+ }
124
137
  if (plugin.priority && plugin.priority > maxPriority) {
125
138
  maxPriority = plugin.priority;
126
139
  }
@@ -166,6 +179,13 @@ export async function discoverProjects(root) {
166
179
  if (!entry) {
167
180
  continue;
168
181
  }
182
+ const projectConfig = await loadProjectConfig(projectDir);
183
+ if (projectConfig) {
184
+ entry.commands = { ...entry.commands, ...(projectConfig.commands || {}) };
185
+ if (projectConfig.frameworks) {
186
+ entry.frameworks = [...(entry.frameworks || []), ...projectConfig.frameworks];
187
+ }
188
+ }
169
189
  const withFrameworks = applyFrameworkPlugins(entry);
170
190
  projectMap.set(projectDir, withFrameworks);
171
191
  } catch (innerError) {
@@ -176,7 +196,23 @@ export async function discoverProjects(root) {
176
196
  console.error(`Error in detector ${detector.type}: ${error.message}`);
177
197
  }
178
198
  }
179
- return Array.from(projectMap.values()).sort((a, b) => b.priority - a.priority);
199
+ const sorted = Array.from(projectMap.values()).sort((a, b) => b.priority - a.priority);
200
+
201
+ let savedMeta = {};
202
+ try {
203
+ if (fs.existsSync(CONFIG_PATH)) {
204
+ savedMeta = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'))?.projectMeta || {};
205
+ }
206
+ } catch { /* ignore */ }
207
+
208
+ let portBase = 3000;
209
+ for (const project of sorted) {
210
+ const saved = savedMeta[project.path];
211
+ const existingPort = saved?.port || project.metadata?.port;
212
+ project.metadata = { ...project.metadata, port: existingPort || String(portBase) };
213
+ if (!existingPort) portBase++;
214
+ }
215
+ return sorted;
180
216
  }
181
217
 
182
218
  export const SCHEMA_GUIDE = detectors.map((d) => ({
@@ -1,32 +0,0 @@
1
- import { useState, useCallback, useMemo } from 'react';
2
-
3
- export function useProjectStore(initialProjects = []) {
4
- const [projects, setProjects] = useState(initialProjects);
5
- const [selectedIndex, setSelectedIndex] = useState(0);
6
- const [activeTab, setActiveTab] = useState('navigator');
7
- const [selectedProjectId, setSelectedProjectId] = useState(null);
8
- const [config, setConfig] = useState({ maxVisibleProjects: 8 });
9
-
10
- const selectedProject = useMemo(() => {
11
- return projects.find(p => p.id === selectedProjectId) || projects[selectedIndex] || null;
12
- }, [projects, selectedIndex, selectedProjectId]);
13
-
14
- const selectProject = useCallback((index) => {
15
- setSelectedIndex(index);
16
- if (projects[index]) {
17
- setSelectedProjectId(projects[index].id);
18
- }
19
- }, [projects]);
20
-
21
- return {
22
- projects,
23
- setProjects,
24
- selectedIndex,
25
- setSelectedIndex: selectProject,
26
- activeTab,
27
- setActiveTab,
28
- selectedProject,
29
- config,
30
- setConfig
31
- };
32
- }