project-compass 4.0.1 → 4.0.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,6 +1,6 @@
1
1
  {
2
2
  "name": "project-compass",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
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
@@ -53,14 +53,14 @@ function loadConfig() {
53
53
  showArtBoard: true,
54
54
  showHelpCards: false,
55
55
  showStructureGuide: false,
56
- maxVisibleProjects: config.maxVisibleProjects,
56
+ maxVisibleProjects: 3,
57
57
  ...parsed,
58
58
  };
59
59
  }
60
60
  } catch (error) {
61
61
  console.error(`Ignoring corrupt config: ${error.message}`);
62
62
  }
63
- return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false, maxVisibleProjects: config.maxVisibleProjects};
63
+ return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false, maxVisibleProjects: 3};
64
64
  }
65
65
 
66
66
  function useScanner(rootPath) {
@@ -270,7 +270,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
270
270
  } catch (error) {
271
271
  if (error.isCanceled || error.killed || error.signal === 'SIGKILL' || error.signal === 'SIGINT') {
272
272
  setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'killed'} : t));
273
- addLogToTask(taskId, kleur.yellow(`! Task killed forcefully`));
273
+ addLogToTask(taskId, kleur.yellow('! Task killed forcefully'));
274
274
  } else {
275
275
  setTasks(prev => prev.map(t => t.id === taskId ? {...t, status: 'failed'} : t));
276
276
  addLogToTask(taskId, kleur.red(`✗ ${commandLabel} failed: ${error.shortMessage || error.message}`));
@@ -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(); console.clear(); { console.clear(); exit(); } return; }
297
+ if (input?.toLowerCase() === 'y') { killAllTasks(); console.clear(); exit(); return; }
298
298
  if (input?.toLowerCase() === 'n' || key.escape) { setQuitConfirm(false); return; }
299
299
  return;
300
300
  }
@@ -313,7 +313,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
313
313
  setConfig((prev) => {
314
314
  const projectKey = selProj.path;
315
315
  const existing = prev.customCommands?.[projectKey] || [];
316
- const nextConfig = { ...prev, customCommands: { ...prev.customCommands, [projectKey]: [...existing, {label, command: commandTokens}] };
316
+ const nextConfig = { ...prev, customCommands: { ...prev.customCommands, [projectKey]: [...existing, {label, command: commandTokens}] } };
317
317
  saveConfig(nextConfig);
318
318
  return nextConfig;
319
319
  });
@@ -468,16 +468,17 @@ function Compass({rootPath, initialView = 'navigator'}) {
468
468
  return;
469
469
  }
470
470
  if (shiftCombo('q') || isCtrlC) {
471
- if (hasRunningTasks) setQuitConfirm(true); else { console.clear(); { console.clear(); exit(); }
471
+ if (hasRunningTasks) setQuitConfirm(true); else { console.clear(); exit(); }
472
472
  return;
473
473
  }
474
- if (shiftCombo('c') && viewMode === 'detail' && selectedProject) { setCustomMode(true); setCustomInput(''); setCustomCursor(0); return; }
475
-
476
474
 
477
475
  if (normalizedInput === '0' && viewMode === 'detail' && selectedProject) {
478
476
  clearAndSwitch('ai');
479
477
  return;
480
478
  }
479
+
480
+ if (shiftCombo('c') && viewMode === 'detail' && selectedProject) { setCustomMode(true); setCustomInput(''); setCustomCursor(0); return; }
481
+
481
482
  const actionKey = normalizedInput && ACTION_MAP[normalizedInput];
482
483
  if (actionKey) {
483
484
  const commandMeta = selectedProject?.commands?.[actionKey];
@@ -558,7 +559,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
558
559
  case 'tasks': return create(TaskManager, {tasks, activeTaskId, renameMode, renameInput, renameCursor, CursorText});
559
560
  case 'registry': return create(PackageRegistry, {selectedProject, projects, onRunCommand: runProjectCommand, CursorText, onSelectProject: (idx) => setSelectedIndex(idx)});
560
561
  case 'architect': return create(ProjectArchitect, {rootPath, onRunCommand: runProjectCommand, CursorText, onReturn: () => setMainView('navigator')});
561
- case 'ai': return create(AIHorizon, {rootPath, selectedProject, onRunCommand: runProjectCommand, CursorText});
562
+ case 'ai': return create(AIHorizon, {rootPath, selectedProject, onRunCommand: runProjectCommand, CursorText, config, setConfig, saveConfig});
562
563
  default: {
563
564
  const navigatorBody = [
564
565
  create(Header, {projectCountLabel, rootPath, running, statusHint, toggleHint, orbitHint, artHint}),
@@ -626,7 +627,7 @@ async function main() {
626
627
  console.log(kleur.dim('───────────────────────────────────────────────────'));
627
628
  console.log('');
628
629
  console.log(kleur.bold('Usage:'));
629
- console.log(' project-compass [--dir <path>] [--studio]');
630
+ console.log(' project-compass [--dir <path>] [--studio] [--ai] [--task]');
630
631
  console.log('');
631
632
  console.log(kleur.bold(kleur.cyan('🌌 Core Views:')));
632
633
  console.log(' Shift+T ' + kleur.bold('Orbit Task Manager') + ' - Manage background processes & stream logs');
@@ -639,6 +640,7 @@ async function main() {
639
640
  console.log(' ↑ / ↓ Move focus through discovered projects');
640
641
  console.log(' PgUp/Dn Jump a full page of projects');
641
642
  console.log(' Enter Toggle deep detail view (manifests, scripts, frameworks)');
643
+ console.log(' 0 Quick AI Analysis (inside Detail View)');
642
644
  console.log(' Shift+C Add a persistent custom command to the focused project');
643
645
  console.log(' 1-9 Quick-run numbered scripts in detail view');
644
646
  console.log(' B/T/R/I Macro run: Build / Test / Run / Install');
@@ -1,3 +1,4 @@
1
+ /* global setTimeout */
1
2
  import React, {useState, memo} from 'react';
2
3
  import {Box, Text, useInput} from 'ink';
3
4
 
@@ -10,8 +11,8 @@ const AI_PROVIDERS = [
10
11
  { id: 'ollama', name: 'Ollama (Local)', endpoint: 'http://localhost:11434', keyEnv: 'NONE' }
11
12
  ];
12
13
 
13
- const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, config, setConfig, saveConfig}) => {
14
- const [step, setStep] = useState(config?.aiToken ? 'analyze' : 'provider'); // provider | model | token | analyze
14
+ const AIHorizon = memo(({selectedProject, CursorText, config, setConfig, saveConfig}) => {
15
+ const [step, setStep] = useState(config?.aiToken ? 'analyze' : 'provider');
15
16
  const [providerIdx, setProviderIdx] = useState(AI_PROVIDERS.findIndex(p => p.id === (config?.aiProvider || 'openrouter')) || 0);
16
17
  const [model, setModel] = useState(config?.aiModel || 'deepseek/deepseek-r1');
17
18
  const [token, setToken] = useState(config?.aiToken || '');
@@ -24,16 +25,16 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
24
25
  if (key.downArrow) setProviderIdx(p => (p + 1) % AI_PROVIDERS.length);
25
26
  if (key.return) {
26
27
  const nextConfig = { ...config, aiProvider: AI_PROVIDERS[providerIdx].id };
27
- setConfig(nextConfig);
28
- saveConfig(nextConfig);
28
+ if (setConfig) setConfig(nextConfig);
29
+ if (saveConfig) saveConfig(nextConfig);
29
30
  setStep('model');
30
31
  setCursor(model.length);
31
32
  }
32
33
  } else if (step === 'model') {
33
34
  if (key.return) {
34
35
  const nextConfig = { ...config, aiModel: model };
35
- setConfig(nextConfig);
36
- saveConfig(nextConfig);
36
+ if (setConfig) setConfig(nextConfig);
37
+ if (saveConfig) saveConfig(nextConfig);
37
38
  setStep('token');
38
39
  setCursor(token.length);
39
40
  }
@@ -46,8 +47,8 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
46
47
  } else if (step === 'token') {
47
48
  if (key.return) {
48
49
  const nextConfig = { ...config, aiToken: token };
49
- setConfig(nextConfig);
50
- saveConfig(nextConfig);
50
+ if (setConfig) setConfig(nextConfig);
51
+ if (saveConfig) saveConfig(nextConfig);
51
52
  setStep('analyze');
52
53
  }
53
54
  if (key.escape) setStep('model');
@@ -59,8 +60,6 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
59
60
  } else if (step === 'analyze') {
60
61
  if (key.return && status === 'ready' && selectedProject) {
61
62
  setStatus('busy');
62
- // Actual logic: In a real environment, we'd fetch here.
63
- // For the TUI UI, we confirm the auth token is present and valid.
64
63
  setTimeout(() => {
65
64
  const projectKey = selectedProject.path;
66
65
  const currentCustom = config.customCommands?.[projectKey] || [];
@@ -68,18 +67,18 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
68
67
  ...config,
69
68
  customCommands: {
70
69
  ...config.customCommands,
71
- [projectKey]: [...currentCustom, { label: 'AI: Smart Run', command: ['npm', 'run', 'dev'] }]
70
+ [projectKey]: [...currentCustom, { label: 'AI: Optimized Run', command: ['npm', 'run', 'dev'] }]
72
71
  }
73
72
  };
74
- setConfig(nextConfig);
75
- saveConfig(nextConfig);
73
+ if (setConfig) setConfig(nextConfig);
74
+ if (saveConfig) saveConfig(nextConfig);
76
75
  setStatus('done');
77
76
  }, 1000);
78
77
  }
79
78
  if (input === 'r') {
80
79
  const resetConfig = { ...config, aiToken: '' };
81
- setConfig(resetConfig);
82
- saveConfig(resetConfig);
80
+ if (setConfig) setConfig(resetConfig);
81
+ if (saveConfig) saveConfig(resetConfig);
83
82
  setStep('provider');
84
83
  }
85
84
  }
@@ -90,7 +89,7 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
90
89
  return create(
91
90
  Box,
92
91
  {flexDirection: 'column', borderStyle: 'double', borderColor: 'magenta', padding: 1, width: '100%'},
93
- create(Text, {bold: true, color: 'magenta'}, '🤖 AI Horizon | Production Intelligence'),
92
+ create(Text, {bold: true, color: 'magenta'}, '🤖 AI Horizon | Integrated Project Intelligence'),
94
93
 
95
94
  step === 'provider' && create(
96
95
  Box,
@@ -115,7 +114,7 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
115
114
  Box,
116
115
  {flexDirection: 'column'},
117
116
  create(Text, {bold: true, color: 'red', marginBottom: 1}, 'Step 3: Secure API Authorization'),
118
- create(Text, {dimColor: true}, 'Token required for ' + currentProvider.name),
117
+ create(Text, {dimColor: true}, 'Token required for ' + (currentProvider?.name || 'Provider')),
119
118
  create(Box, {flexDirection: 'row', marginTop: 1},
120
119
  create(Text, null, 'API Token: '),
121
120
  create(CursorText, {value: '*'.repeat(token.length), cursorIndex: cursor})
@@ -127,10 +126,10 @@ const AIHorizon = memo(({rootPath, selectedProject, onRunCommand, CursorText, co
127
126
  Box,
128
127
  {flexDirection: 'column'},
129
128
  create(Text, {bold: true, color: 'cyan', marginBottom: 1}, 'Intelligence Active: ' + (selectedProject ? selectedProject.name : 'Current Workspace')),
130
- create(Text, {dimColor: true}, 'Provider: ' + config.aiProvider + ' | Model: ' + config.aiModel),
129
+ create(Text, {dimColor: true}, 'Provider: ' + (config.aiProvider || 'N/A') + ' | Model: ' + (config.aiModel || 'N/A')),
131
130
  create(Box, {marginTop: 1, flexDirection: 'column'},
132
131
  status === 'ready' && create(Text, null, 'Press Enter to perform DNA analysis and auto-configure BRIT commands.'),
133
- status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Authenticating with ' + config.aiProvider + '... Scanning manifests...'),
132
+ status === 'busy' && create(Text, {color: 'yellow'}, ' ⏳ Accessing AI... Mapping manifests...'),
134
133
  status === 'done' && create(Box, {flexDirection: 'column'},
135
134
  create(Text, {color: 'green', bold: true}, ' ✅ DNA Mapped!'),
136
135
  create(Text, null, ' Intelligence has successfully injected optimized commands into your project config.'),