project-compass 4.3.2 → 4.3.3

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,7 +1,7 @@
1
1
  {
2
2
  "name": "project-compass",
3
- "version": "4.3.2",
4
- "description": "🧭 Futuristic TUI workspace navigator & runner - AI-powered project detection for Node, Python, Rust, Go, Java, PHP, Ruby, .NET",
3
+ "version": "4.3.3",
4
+ "description": "\ud83e\udded Futuristic TUI workspace navigator & runner - AI-powered project detection for Node, Python, Rust, Go, Java, PHP, Ruby, .NET",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
7
7
  "project-compass": "src/cli.js"
@@ -23,27 +23,38 @@ function getPythonPackageManager(projectPath) {
23
23
 
24
24
  function gatherPythonDependencies(projectPath) {
25
25
  const deps = new Set();
26
- const addFromRequirements = (filePath) => {
27
- if (!fs.existsSync(filePath)) return;
28
- const raw = fs.readFileSync(filePath, 'utf-8');
26
+
27
+ // Only read requirements.txt files - NOT .py files!
28
+ const reqPath = path.join(projectPath, 'requirements.txt');
29
+ if (fs.existsSync(reqPath)) {
30
+ const raw = fs.readFileSync(reqPath, 'utf-8');
29
31
  raw.split(/\r?\n/).forEach((line) => {
30
32
  const clean = line.trim().split('#')[0].trim();
31
33
  if (!clean || clean.startsWith('-') || clean.startsWith('"') || clean.startsWith("'")) return;
32
34
  const match = clean.match(/^([a-zA-Z0-9_.-]+)/);
33
35
  if (match) deps.add(match[1].toLowerCase());
34
36
  });
35
- };
36
-
37
- addFromRequirements(path.join(projectPath, 'requirements.txt'));
38
- addFromRequirements(path.join(projectPath, 'requirements-dev.txt'));
39
-
37
+ }
38
+
39
+ const reqDevPath = path.join(projectPath, 'requirements-dev.txt');
40
+ if (fs.existsSync(reqDevPath)) {
41
+ const raw = fs.readFileSync(reqDevPath, 'utf-8');
42
+ raw.split(/\r?\n/).forEach((line) => {
43
+ const clean = line.trim().split('#')[0].trim();
44
+ if (!clean || clean.startsWith('-') || clean.startsWith('"') || clean.startsWith("'")) return;
45
+ const match = clean.match(/^([a-zA-Z0-9_.-]+)/);
46
+ if (match) deps.add(match[1].toLowerCase());
47
+ });
48
+ }
49
+
50
+ // Only read pyproject.toml dependencies section - NOT .py files!
40
51
  const pyproject = path.join(projectPath, 'pyproject.toml');
41
52
  if (fs.existsSync(pyproject)) {
42
53
  const content = fs.readFileSync(pyproject, 'utf-8');
43
54
  const depSection = content.match(/(?:dependencies|requires)\s*=\s*\[([^\]]+)\]/g);
44
55
  if (depSection) {
45
56
  depSection.forEach((section) => {
46
- const matches = section.match(/["']([^"']+)["']/g);
57
+ const matches = section.match(/["']([^"']+)/g);
47
58
  if (matches) {
48
59
  matches.forEach((m) => {
49
60
  const dep = m.replace(/["']/g, '').split(/[>=<=~!]/)[0].trim();
@@ -53,27 +64,16 @@ function gatherPythonDependencies(projectPath) {
53
64
  });
54
65
  }
55
66
  }
56
-
57
- const pipfile = path.join(projectPath, 'Pipfile');
58
- if (fs.existsSync(pipfile)) {
59
- const content = fs.readFileSync(pipfile, 'utf-8');
60
- const matches = content.match(/["']([^"']+)["']/g);
61
- if (matches) {
62
- matches.forEach((m) => {
63
- const dep = m.replace(/["']/g, '').split(/[>=<=~!]/)[0].trim();
64
- if (dep) deps.add(dep.toLowerCase());
65
- });
66
- }
67
- }
68
-
67
+
69
68
  return Array.from(deps);
70
69
  }
71
70
 
71
+ // Uses EXACT matching - NOT substring!
72
72
  function detectPythonFramework(deps) {
73
73
  const frameworks = [];
74
74
 
75
75
  const hasDep = (pattern) =>
76
- deps.some(dep => {
76
+ deps.some((dep) => {
77
77
  const depLower = dep.toLowerCase();
78
78
  return depLower === pattern.toLowerCase() ||
79
79
  depLower.startsWith(pattern.toLowerCase() + '==') ||
@@ -82,23 +82,23 @@ function detectPythonFramework(deps) {
82
82
  depLower.startsWith(pattern.toLowerCase() + '~=');
83
83
  });
84
84
 
85
- if (hasDep('fastapi')) frameworks.push({ name: 'FastAPI', icon: '\u26A1' });
86
- if (hasDep('flask')) frameworks.push({ name: 'Flask', icon: '\uD83C\uDF36\uFE0F' });
87
- if (hasDep('django')) frameworks.push({ name: 'Django', icon: '\uD83C\uDF3F' });
88
- if (hasDep('tornado')) frameworks.push({ name: 'Tornado', icon: '\uD83C\uDF2A\uFE0F' });
89
- if (hasDep('aiohttp')) frameworks.push({ name: 'AioHTTP', icon: '\uD83D\uDD04' });
90
- if (hasDep('sanic')) frameworks.push({ name: 'Sanic', icon: '\uD83D\uDE80' });
91
- if (hasDep('pyramid')) frameworks.push({ name: 'Pyramid', icon: '\uD83D\uDE3A' });
92
- if (hasDep('falcon')) frameworks.push({ name: 'Falcon', icon: '\uD83D\uDC05' });
93
- if (hasDep('starlette')) frameworks.push({ name: 'Starlette', icon: '\u2B50' });
94
- if (hasDep('pandas')) frameworks.push({ name: 'Pandas', icon: '\uD83D\uDC3C' });
95
- if (hasDep('numpy')) frameworks.push({ name: 'NumPy', icon: '\uD83D\uDD22' });
96
- if (hasDep('scipy')) frameworks.push({ name: 'SciPy', icon: '\uD83D\uDD2C' });
97
- if (hasDep('torch') || hasDep('pytorch')) frameworks.push({ name: 'PyTorch', icon: '\uD83D\uDD25' });
98
- if (hasDep('tensorflow')) frameworks.push({ name: 'TensorFlow', icon: '\uD83E\uDD20' });
99
- if (hasDep('sqlalchemy')) frameworks.push({ name: 'SQLAlchemy', icon: '\uD83D\uDCC4\uFE0F' });
100
- if (hasDep('pytest')) frameworks.push({ name: 'Pytest', icon: '\u2705' });
101
- if (hasDep('celery')) frameworks.push({ name: 'Celery', icon: '\uD83D\uDE2C' });
85
+ if (hasDep('fastapi')) frameworks.push({ name: 'FastAPI', icon: '' });
86
+ if (hasDep('flask')) frameworks.push({ name: 'Flask', icon: '🌶️' });
87
+ if (hasDep('django')) frameworks.push({ name: 'Django', icon: '🌿' });
88
+ if (hasDep('tornado')) frameworks.push({ name: 'Tornado', icon: '🌪️' });
89
+ if (hasDep('aiohttp')) frameworks.push({ name: 'AioHTTP', icon: '🔄' });
90
+ if (hasDep('sanic')) frameworks.push({ name: 'Sanic', icon: '🚀' });
91
+ if (hasDep('pyramid')) frameworks.push({ name: 'Pyramid', icon: '🔺' });
92
+ if (hasDep('falcon')) frameworks.push({ name: 'Falcon', icon: '🦅' });
93
+ if (hasDep('starlette')) frameworks.push({ name: 'Starlette', icon: '' });
94
+ if (hasDep('pandas')) frameworks.push({ name: 'Pandas', icon: '🐼' });
95
+ if (hasDep('numpy')) frameworks.push({ name: 'NumPy', icon: '🔢' });
96
+ if (hasDep('scipy')) frameworks.push({ name: 'SciPy', icon: '🔬' });
97
+ if (hasDep('torch') || hasDep('pytorch')) frameworks.push({ name: 'PyTorch', icon: '🔥' });
98
+ if (hasDep('tensorflow')) frameworks.push({ name: 'TensorFlow', icon: '🧠' });
99
+ if (hasDep('sqlalchemy')) frameworks.push({ name: 'SQLAlchemy', icon: '🗄️' });
100
+ if (hasDep('pytest')) frameworks.push({ name: 'Pytest', icon: '' });
101
+ if (hasDep('celery')) frameworks.push({ name: 'Celery', icon: '🥬' });
102
102
 
103
103
  return frameworks;
104
104
  }
@@ -117,8 +117,10 @@ export default {
117
117
  const isPoetry = pkgManager === 'poetry';
118
118
  const isPipenv = pkgManager === 'pipenv';
119
119
 
120
+ const allDeps = gatherPythonDependencies(projectPath);
121
+ const detectedFrameworks = detectPythonFramework(allDeps);
122
+
120
123
  const commands = {};
121
-
122
124
  if (isUV) {
123
125
  commands.install = { label: 'UV Sync', command: ['uv', 'sync'], source: 'builtin' };
124
126
  commands.add = { label: 'UV Add', command: ['uv', 'add'], source: 'builtin' };
@@ -134,8 +136,8 @@ export default {
134
136
  commands.install = { label: 'Pip Install', command: ['pip', 'install', '-r', 'requirements.txt'], source: 'builtin' };
135
137
  }
136
138
 
137
- if (hasProjectFile(projectPath, 'pyproject.toml') || hasProjectFile(projectPath, 'setup.py')) {
138
- commands.test = { label: 'Pytest', command: [isUV ? 'uv' : 'python', ...(isUV ? ['run'] : []), 'pytest'], source: 'builtin' };
139
+ if (isUV || isPoetry || isPipenv) {
140
+ commands.test = { label: 'Pytest', command: [isUV ? 'uv' : isPoetry ? 'poetry' : 'pipenv', ...(isUV ? ['run'] : []), 'pytest'], source: 'builtin' };
139
141
  } else {
140
142
  commands.test = { label: 'Unittest', command: ['python', '-m', 'unittest', 'discover'], source: 'builtin' };
141
143
  }
@@ -151,28 +153,16 @@ export default {
151
153
 
152
154
  if (hasProjectFile(projectPath, 'manage.py')) {
153
155
  const djangoCmd = isUV ? ['uv', 'run', 'python', 'manage.py'] :
154
- isPoetry ? ['poetry', 'run', 'python', 'manage.py'] :
155
- ['python', 'manage.py'];
156
+ ['python', 'manage.py'];
156
157
  commands['runserver'] = { label: 'Django Runserver', command: [...djangoCmd, 'runserver'], source: 'builtin' };
157
158
  commands['migrate'] = { label: 'Django Migrate', command: [...djangoCmd, 'migrate'], source: 'builtin' };
158
159
  commands['test'] = { label: 'Django Test', command: [...djangoCmd, 'test'], source: 'builtin' };
159
160
  }
160
161
 
161
- if (hasProjectFile(projectPath, 'fastapi') || hasProjectFile(projectPath, 'main.py')) {
162
- if (hasProjectFile(projectPath, 'main.py')) {
163
- const fastapiCmd = isUV ? ['uv', 'run', 'uvicorn', 'main:app', '--reload'] :
164
- ['uvicorn', 'main:app', '--reload'];
165
- commands['dev'] = { label: 'FastAPI Dev', command: fastapiCmd, source: 'builtin' };
166
- }
167
- }
168
-
169
- const allDeps = gatherPythonDependencies(projectPath);
170
- const detectedFrameworks = detectPythonFramework(allDeps);
171
-
172
162
  const metadata = {
173
163
  dependencies: allDeps,
174
- packageManager: pkgManager,
175
- frameworks: detectedFrameworks
164
+ frameworks: detectedFrameworks,
165
+ packageManager: pkgManager
176
166
  };
177
167
 
178
168
  const setupHints = [];