project-compass 4.3.6 → 4.3.8

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.
Files changed (41) hide show
  1. package/AGENTS.md +1121 -22
  2. package/ARCHITECTURE.md +826 -10
  3. package/CONTRIBUTING.md +974 -10
  4. package/PROJECT_CONTEXT.md +404 -0
  5. package/README.md +637 -67
  6. package/assets/screenshots/ai_mode.png +0 -0
  7. package/assets/screenshots/art_bar.png +0 -0
  8. package/assets/screenshots/help_structure.png +0 -0
  9. package/assets/screenshots/home.png +0 -0
  10. package/assets/screenshots/languages_checker_omni_studio.png +0 -0
  11. package/assets/screenshots/task_manager.png +0 -0
  12. package/commands.md +841 -104
  13. package/package.json +5 -4
  14. package/src/cli.js +310 -169
  15. package/src/components/AIHorizon.js +138 -255
  16. package/src/components/Footer.js +8 -64
  17. package/src/components/Header.js +8 -47
  18. package/src/components/Navigator.js +16 -70
  19. package/src/components/PackageRegistry.js +4 -3
  20. package/src/components/ProjectArchitect.js +6 -1
  21. package/src/components/TaskManager.js +12 -70
  22. package/src/detectors/dotnet.js +3 -2
  23. package/src/detectors/frameworks.js +28 -28
  24. package/src/detectors/go.js +6 -6
  25. package/src/detectors/java.js +2 -1
  26. package/src/detectors/node.js +3 -2
  27. package/src/detectors/php.js +1 -1
  28. package/src/detectors/python.js +11 -4
  29. package/src/detectors/ruby.js +1 -1
  30. package/src/detectors/rust.js +2 -2
  31. package/src/detectors/utils.js +6 -7
  32. package/src/projectDetection.js +41 -5
  33. package/assets/screenshots/architect.jpg +0 -0
  34. package/assets/screenshots/artboard.jpg +0 -0
  35. package/assets/screenshots/exit-confirm.jpg +0 -0
  36. package/assets/screenshots/navigator.jpg +0 -0
  37. package/assets/screenshots/overlays.jpg +0 -0
  38. package/assets/screenshots/registry.jpg +0 -0
  39. package/assets/screenshots/studio.jpg +0 -0
  40. package/assets/screenshots/taskmanager.jpg +0 -0
  41. package/src/store/useProjectStore.js +0 -32
package/ARCHITECTURE.md CHANGED
@@ -1,21 +1,837 @@
1
1
  # Project Compass Architecture
2
2
 
3
- This document describes the high-level architecture of Project Compass.
3
+ This document describes the complete high-level architecture of Project Compass (v4.3.6).
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Overview](#overview)
10
+ 2. [Data Flow](#data-flow)
11
+ 3. [Project Structure](#project-structure)
12
+ 4. [Core Components](#core-components)
13
+ 5. [Detection System](#detection-system)
14
+ 6. [Framework Plugin System](#framework-plugin-system)
15
+ 7. [State Management](#state-management)
16
+ 8. [UI Rendering](#ui-rendering)
17
+ 9. [Command Execution](#command-execution)
18
+ 10. [Configuration System](#configuration-system)
19
+ 11. [Recent Architecture Changes](#recent-architecture-changes)
20
+ 12. [Design Patterns](#design-patterns)
21
+ 13. [Security](#security)
22
+ 14. [Performance](#performance)
23
+
24
+ ---
25
+
26
+ ## Overview
27
+
28
+ Project Compass is a terminal-based project navigator and runner built with:
29
+ - **Runtime**: Node.js (ESM modules)
30
+ - **UI Framework**: Ink (React for CLI)
31
+ - **Styling**: kleur (terminal colors)
32
+ - **Execution**: execa (robust subprocess management)
33
+ - **File Search**: fast-glob (fast file globbing)
34
+ - **Intelligence**: Native fetch API (AI provider integration)
35
+
36
+ ---
4
37
 
5
38
  ## Data Flow
6
- 1. **Initialization:** `cli.js` resolves the working directory (defaults to current folder).
7
- 2. **Discovery:** `projectDetection.js` orchestrates modular detectors in `src/detectors/` to perform a high-speed glob search for common manifest files (`package.json`, `cargo.toml`, `go.mod`, etc.).
8
- 3. **State Management:** The discovered projects and their metadata (frameworks, scripts, dependencies) are passed into an Ink React tree.
9
- 4. **Rendering:** Components in `src/components` handle specific views (Task Manager, Architect, AI Horizon, etc.) based on user keypresses.
10
- 5. **Execution:** User-triggered scripts (like running `npm test`) are managed by `TaskManager.js` using `execa` with streaming logs.
39
+
40
+ ### Complete Flow Diagram
41
+ ```
42
+ User Command/Input
43
+
44
+ [CLI Argument Parsing] (src/cli.js:766-781)
45
+
46
+ [Mode Detection]
47
+ ├─ --help │ --version → Show info & exit
48
+ ├─ --mode test │ --list-projects → Headless detection
49
+ ├─ --run "cmd" → Execute command & exit
50
+ ├─ --studio-check → Runtime check & exit
51
+ ├─ --scaffold → Create project & exit
52
+ ├─ --add-pkg │ --remove-pkg → Package mgmt & exit
53
+ └─ (default) → Launch TUI
54
+
55
+ [TUI Mode] → React/Ink Render Loop (src/cli.js:163-763)
56
+
57
+ [Project Detection] (src/projectDetection.js:146-180)
58
+
59
+ [Detector Orchestration] → Run detectors in priority order
60
+
61
+ [fast-glob Scanning] → Find manifest files (package.json, Cargo.toml, etc.)
62
+
63
+ [Per-Detector Build] (e.g., src/detectors/node.js:58-139)
64
+
65
+ [Framework Plugin Application] (src/projectDetection.js:114-144)
66
+
67
+ [compass-config.js Loading] (src/detectors/compass-config.js)
68
+
69
+ [Project Object Creation] → {id, path, name, type, commands, frameworks, metadata}
70
+
71
+ [State Update] → React state in Compass component
72
+
73
+ [UI Render] → Ink components (Navigator, TaskManager, etc.)
74
+ ```
75
+
76
+ ### Detailed Steps
77
+
78
+ 1. **Initialization**: `cli.js` resolves the working directory (defaults to current folder) and parses CLI arguments.
79
+
80
+ 2. **Discovery**: `projectDetection.js` orchestrates modular detectors in `src/detectors/` to perform a high-speed glob search for common manifest files:
81
+ - `package.json` (Node.js)
82
+ - `pyproject.toml`, `requirements.txt`, `setup.py`, `Pipfile`, `manage.py` (Python)
83
+ - `Cargo.toml` (Rust)
84
+ - `go.mod` (Go)
85
+ - `pom.xml`, `build.gradle`, `build.gradle.kts` (Java)
86
+ - `composer.json` (PHP)
87
+ - `Gemfile` (Ruby)
88
+ - `*.csproj`, `*.fsproj` (.NET)
89
+
90
+ 3. **Config Loading**: `compass-config.js` is loaded from each project directory (if exists) and merged into project data.
91
+
92
+ 4. **Framework Detection**: `frameworks.js` applies built-in (40+) and user plugins to detect frameworks based on actual dependencies (not file existence).
93
+
94
+ 5. **State Management**: The discovered projects and their metadata (frameworks, scripts, dependencies) are passed into an Ink React tree.
95
+
96
+ 6. **Rendering**: Components in `src/components` handle specific views:
97
+ - Navigator (main project list)
98
+ - TaskManager (background processes)
99
+ - PackageRegistry (dependency management)
100
+ - ProjectArchitect (scaffolding)
101
+ - AIHorizon (AI analysis)
102
+ - Studio (environment health)
103
+ - Header (top bar)
104
+ - Footer (bottom bar with stdin)
105
+
106
+ 7. **Execution**: User-triggered scripts (like running `npm test`) are managed by `TaskManager.js` using `execa` with streaming logs.
107
+
108
+ ---
109
+
110
+ ## Project Structure
111
+
112
+ ```
113
+ project-compass/
114
+ ├── package.json # NPM package config (v4.3.6)
115
+ ├── README.md # Main documentation (COMPREHENSIVE)
116
+ ├── ARCHITECTURE.md # This file (full architecture)
117
+ ├── commands.md # All commands & shortcuts
118
+ ├── CONTRIBUTING.md # Contribution guidelines
119
+ ├── AGENTS.md # AI agent context
120
+ ├── PROJECT_CONTEXT.md # Technical context for agents
121
+ ├── LICENSE # MIT License
122
+ ├── eslint.config.cjs # ESLint configuration
123
+ ├── src/
124
+ │ ├── cli.js # Entry point (840+ lines)
125
+ │ │ # - Argument parsing (parseArgs)
126
+ │ │ # - Main React component (Compass)
127
+ │ │ # - Global input handling (useInput)
128
+ │ │ # - Project scanning (useScanner)
129
+ │ │ # - Command execution (runProjectCommand)
130
+ │ │ # - Task management (addLogToTask, killAllTasks)
131
+ │ ├── projectDetection.js # Orchestrator (189 lines)
132
+ │ │ # - discoverProjects(root)
133
+ │ │ # - applyFrameworkPlugins(project)
134
+ │ │ # - matchesPlugin(project, plugin)
135
+ │ │ # - getFrameworkPlugins()
136
+ │ ├── configPaths.js # Config directory paths
137
+ │ │ # - CONFIG_DIR: ~/.project-compass/
138
+ │ │ # - CONFIG_PATH: ~/.project-compass/config.json
139
+ │ │ # - PLUGIN_FILE: ~/.project-compass/plugins.json
140
+ │ │ # - ensureConfigDir()
141
+ │ ├── detectors/
142
+ │ │ ├── utils.js # Shared utilities (148 lines)
143
+ │ │ │ # - checkBinary(name)
144
+ │ │ │ # - hasProjectFile(projectPath, file)
145
+ │ │ │ # - getPackageManager(projectPath, language)
146
+ │ │ │ # - dependencyMatches(project, needle)
147
+ │ │ │ # - parseCommandTokens(value)
148
+ │ │ │ # - getLockfileInfo(projectPath)
149
+ │ │ ├── node.js # Node.js detection (140 lines)
150
+ │ │ │ # Priority: 100
151
+ │ │ │ # Files: package.json
152
+ │ │ │ # Binaries: node, npm
153
+ │ │ ├── python.js # Python detection (208 lines)
154
+ │ │ │ # Priority: 95
155
+ │ │ │ # Files: pyproject.toml, requirements.txt, setup.py, Pipfile, manage.py
156
+ │ │ │ # Binaries: python3, python, uv
157
+ │ │ ├── rust.js # Rust detection (136 lines)
158
+ │ │ │ # Priority: 90
159
+ │ │ │ # Files: Cargo.toml
160
+ │ │ │ # Binaries: cargo, rustc
161
+ │ │ ├── go.js # Go detection
162
+ │ │ │ # Priority: 85
163
+ │ │ │ # Files: go.mod
164
+ │ │ │ # Binaries: go
165
+ │ │ ├── java.js # Java detection
166
+ │ │ │ # Priority: 80
167
+ │ │ │ # Files: pom.xml, build.gradle, build.gradle.kts
168
+ │ │ │ # Binaries: java, mvn, gradle
169
+ │ │ ├── php.js # PHP detection
170
+ │ │ │ # Priority: 75
171
+ │ │ │ # Files: composer.json
172
+ │ │ │ # Binaries: php, composer
173
+ │ │ ├── ruby.js # Ruby detection
174
+ │ │ │ # Priority: 70
175
+ │ │ │ # Files: Gemfile
176
+ │ │ │ # Binaries: ruby, bundle
177
+ │ │ ├── dotnet.js # .NET detection
178
+ │ │ │ # Priority: 65
179
+ │ │ │ # Files: *.csproj, *.fsproj
180
+ │ │ │ # Binaries: dotnet
181
+ │ │ ├── generic.js # Generic fallback detector
182
+ │ │ │ # Priority: 10
183
+ │ │ │ # Files: Makefile, build.sh
184
+ │ │ ├── compass-config.js # Project-specific config loader (39 lines)
185
+ │ │ │ # - loadProjectConfig(projectPath)
186
+ │ │ │ # - saveProjectConfig(projectPath, config)
187
+ │ │ └── frameworks.js # 40+ built-in framework plugins (877 lines)
188
+ │ │ # Node.js: Next.js, React, Vue, NestJS, Express, etc.
189
+ │ │ # Python: FastAPI, Flask, Django, etc.
190
+ │ │ # Rust: Actix, Rocket, Axum, etc.
191
+ │ │ # Go: Gin, Echo, Fiber, etc.
192
+ │ │ # Java: Spring Boot, Quarkus, etc.
193
+ │ │ # PHP: Laravel, Symfony, etc.
194
+ │ │ # Ruby: Rails, Sinatra, etc.
195
+ │ │ # .NET: ASP.NET Core, Blazor, etc.
196
+ │ │ # ML/Data: Pandas, PyTorch, TensorFlow
197
+ ├── components/
198
+ │ ├── Navigator.js # Paginated project list (110 lines)
199
+ │ ├── Header.js # Top bar with logo, status, time (60 lines)
200
+ │ ├── Footer.js # Bottom bar with stdin input (81 lines)
201
+ │ ├── TaskManager.js # Orbit Task Manager (82 lines)
202
+ │ ├── PackageRegistry.js # Dependency management (156 lines)
203
+ │ ├── ProjectArchitect.js # Scaffolding templates (113 lines)
204
+ │ ├── AIHorizon.js # AI-powered analysis (426 lines)
205
+ │ └── Studio.js # Environment health check (64 lines)
206
+ ├── assets/ # Screenshots and branding
207
+ └── node_modules/ # Dependencies
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Core Components
213
+
214
+ ### Entry Point: `src/cli.js`
215
+
216
+ **Lines**: 840+
217
+ **Purpose**: Main entry point, argument parsing, global state management, input handling
218
+
219
+ #### Key Functions:
220
+ - `parseArgs()` (lines 766-781): Parse CLI arguments
221
+ - `main()` (lines 783-840): Main async entry
222
+ - `saveConfig(config)`: Persist config to disk
223
+ - `loadConfig()`: Load config from `~/.project-compass/config.json`
224
+
225
+ #### Key React Component: `Compass` (lines 163-763)
226
+ - **State Variables**:
227
+ - `projects` - Detected projects array
228
+ - `selectedIndex` - Currently selected project index
229
+ - `viewMode` - 'list' or 'detail'
230
+ - `mainView` - 'navigator', 'tasks', 'registry', 'architect', 'ai', 'studio'
231
+ - `tasks` - Array of running/completed tasks
232
+ - `activeTaskId` - Currently selected task
233
+ - `config` - Loaded from `~/.project-compass/config.json`
234
+
235
+ - **Ref Objects**:
236
+ - `runningProcessMap` - Map of task IDs to child processes
237
+ - `lastCommandRef` - Last executed command for replay (Shift+L)
238
+
239
+ #### Hooks Used:
240
+ - `useScanner(rootPath)` - Async project detection
241
+ - `useInput()` - Global keyboard input handling
242
+ - `useState()` - Multiple state variables
243
+ - `useMemo()` - Memoized computations
244
+ - `useCallback()` - Memoized callbacks
245
+ - `useEffect()` - Side effects (timers, scanning)
246
+
247
+ ---
248
+
249
+ ## Detection System
250
+
251
+ ### Detector Interface
252
+
253
+ Each detector in `src/detectors/` exports an object with:
254
+
255
+ ```javascript
256
+ export default {
257
+ type: 'python', // Language identifier
258
+ label: 'Python', // Display name
259
+ icon: '🐍', // Emoji icon
260
+ priority: 95, // Numeric priority (higher = preferred)
261
+ files: ['pyproject.toml', 'requirements.txt', ...], // Manifest files to match
262
+ binaries: ['python3', 'python', 'uv'], // Required binaries to check
263
+ async build(projectPath, manifest) {
264
+ // Return project object or null
265
+ return {
266
+ id: `${projectPath}::python`,
267
+ path: projectPath,
268
+ name: path.basename(projectPath),
269
+ type: 'Python',
270
+ icon: '🐍',
271
+ priority: this.priority,
272
+ commands: { ... },
273
+ metadata: { ... },
274
+ manifest: path.basename(manifest),
275
+ description: '...',
276
+ missingBinaries: [...],
277
+ frameworks: [...],
278
+ extra: { ... }
279
+ };
280
+ }
281
+ }
282
+ ```
283
+
284
+ ### Detection Priority Order
285
+
286
+ Detectors are run in this order (highest priority wins):
287
+
288
+ 1. **Node.js** (100) - `package.json`
289
+ 2. **Python** (95) - `pyproject.toml`, `requirements.txt`, `setup.py`, `Pipfile`, `manage.py`
290
+ 3. **Rust** (90) - `Cargo.toml`
291
+ 4. **Go** (85) - `go.mod`
292
+ 5. **Java** (80) - `pom.xml`, `build.gradle`
293
+ 6. **PHP** (75) - `composer.json`
294
+ 7. **Ruby** (70) - `Gemfile`
295
+ 8. **.NET** (65) - `*.csproj`, `*.fsproj`
296
+ 9. **Generic** (10) - `Makefile`, `build.sh` (fallback)
297
+
298
+ ### Framework Plugin System
299
+
300
+ #### Built-in Frameworks (`src/detectors/frameworks.js`)
301
+
302
+ **40+ frameworks** with the following structure:
303
+
304
+ ```javascript
305
+ {
306
+ id: 'fastapi',
307
+ name: 'FastAPI',
308
+ icon: '⚡',
309
+ description: 'Modern fast web framework for Python',
310
+ languages: ['Python'], // Which languages this applies to
311
+ priority: 112, // Plugin priority (boosts project priority)
312
+ match(project) {
313
+ // Return true if this framework is detected
314
+ return dependencyMatches(project, 'fastapi');
315
+ },
316
+ commands(project) {
317
+ // Return commands specific to this framework
318
+ return {
319
+ install: { label: 'FastAPI deps', command: ['pip', 'install', '-r', 'requirements.txt'], source: 'framework' },
320
+ run: { label: 'FastAPI dev', command: ['uvicorn', 'main:app', '--reload'], source: 'framework' },
321
+ test: { label: 'FastAPI test', command: ['pytest'], source: 'framework' }
322
+ };
323
+ }
324
+ }
325
+ ```
326
+
327
+ #### Custom Plugins (`~/.project-compass/plugins.json`)
328
+
329
+ Users can add custom framework plugins:
330
+
331
+ ```json
332
+ {
333
+ "plugins": [
334
+ {
335
+ "name": "My Framework",
336
+ "icon": "🚀",
337
+ "languages": ["Node.js"],
338
+ "files": ["my-framework.config.js"],
339
+ "dependencies": ["my-framework"],
340
+ "priority": 100,
341
+ "commands": {
342
+ "dev": { "label": "Dev", "command": ["my-cli", "dev"] }
343
+ }
344
+ }
345
+ ]
346
+ }
347
+ ```
348
+
349
+ ---
350
+
351
+ ## State Management
352
+
353
+ ### Current Implementation (in `Compass` component)
354
+
355
+ All state is managed directly in the `Compass` component:
356
+
357
+ ```javascript
358
+ const [projects, setProjects] = useState([]);
359
+ const [selectedIndex, setSelectedIndex] = useState(0);
360
+ const [viewMode, setViewMode] = useState('list');
361
+ const [mainView, setMainView] = useState(initialView);
362
+ const [tasks, setTasks] = useState([]);
363
+ const [activeTaskId, setActiveTaskId] = useState(null);
364
+ const [logOffset, setLogOffset] = useState(0);
365
+ const [customMode, setCustomMode] = useState(false);
366
+ const [portConfigMode, setPortConfigMode] = useState(false);
367
+ // ... more state variables
368
+ ```
369
+
370
+ ---
371
+
372
+ ## UI Rendering
373
+
374
+ ### Component Tree
375
+
376
+ ```
377
+ <Compass>
378
+ ├─ useState: projects, selectedIndex, mainView, viewMode, tasks, etc.
379
+ ├─ useScanner(rootPath) → projects
380
+ ├─ useEffect: startup animation
381
+ ├─ useInput: global keyboard handler
382
+ └─ renderView()
383
+ ├─ Startup Screen (if startup)
384
+ └─ Main Views:
385
+ ├─ Navigator View (mainView === 'navigator')
386
+ │ ├─ <Header> (projects count, time, status)
387
+ │ ├─ Quick Actions Bar (B/T/R/I/0)
388
+ │ ├─ Art Board (if showArtBoard)
389
+ │ ├─ Projects Row:
390
+ │ │ ├─ <Navigator> (project list with pagination)
391
+ │ │ └─ Details Panel:
392
+ │ │ ├─ Project name, type, path
393
+ │ │ ├─ Frameworks
394
+ │ │ ├─ Commands (builtin + custom)
395
+ │ │ └─ Missing binaries warning
396
+ │ ├─ Output Panel: <OutputPanel>
397
+ │ ├─ <Footer> (stdin input, toggle hints)
398
+ │ ├─ Help Cards (if showHelpCards)
399
+ │ ├─ Structure Guide (if showStructureGuide)
400
+ │ └─ Help Overlay (if showHelp)
401
+
402
+ ├─ Tasks View (mainView === 'tasks')
403
+ │ └─ <TaskManager>
404
+
405
+ ├─ Registry View (mainView === 'registry')
406
+ │ └─ <PackageRegistry>
407
+
408
+ ├─ Architect View (mainView === 'architect')
409
+ │ └─ <ProjectArchitect>
410
+
411
+ ├─ AI View (mainView === 'ai')
412
+ │ └─ <AIHorizon>
413
+
414
+ └─ Studio View (mainView === 'studio')
415
+ └─ <Studio>
416
+ ```
417
+
418
+ ### Component Details
419
+
420
+ #### `<Navigator>` (src/components/Navigator.js)
421
+ - **Props**: `projects`, `selectedIndex`, `rootPath`, `loading`, `error`, `maxVisibleProjects`
422
+ - **Features**:
423
+ - Paginated project list (page size = `maxVisibleProjects`, default 3)
424
+ - Loading spinner animation
425
+ - Error display
426
+ - Empty state message
427
+ - Framework badges display
428
+ - Missing runtime warnings
429
+
430
+ #### `<TaskManager>` (src/components/TaskManager.js)
431
+ - **Props**: `tasks`, `activeTaskId`, `renameMode`, `renameInput`, `renameCursor`, `CursorText`
432
+ - **Features**:
433
+ - Task list with status colors
434
+ - Active task highlighting
435
+ - Mini log preview (last 5 lines)
436
+ - Task renaming
437
+ - Keyboard shortcuts display
438
+
439
+ #### `<AIHorizon>` (src/components/AIHorizon.js)
440
+ - **Props**: `selectedProject`, `CursorText`, `config`, `setConfig`, `saveConfig`
441
+ - **Features**:
442
+ - Multi-step flow: provider → model → token → analyze → review
443
+ - AI provider selection (OpenRouter, Gemini, Claude, Ollama)
444
+ - Project context building (README, main file, config)
445
+ - Raw AI response display
446
+ - Editable suggestions
447
+ - Config persistence
448
+
449
+ #### `<PackageRegistry>` (src/components/PackageRegistry.js)
450
+ - **Props**: `selectedProject`, `projects`, `onRunCommand`, `CursorText`, `onSelectProject`
451
+ - **Features**:
452
+ - Project selection sub-view
453
+ - Package listing
454
+ - Add/remove packages
455
+ - Python venv creation
456
+ - Native package manager detection
457
+
458
+ #### `<ProjectArchitect>` (src/components/ProjectArchitect.js)
459
+ - **Props**: `rootPath`, `onRunCommand`, `CursorText`, `onReturn`
460
+ - **Features**:
461
+ - 7+ templates (Next.js, React, Vue, Rust, Django, Python, Go)
462
+ - Multi-step: framework → path → name
463
+ - Command execution via Orbit
464
+
465
+ #### `<Studio>` (src/components/Studio.js)
466
+ - **Props**: None (checks binaries)
467
+ - **Features**:
468
+ - Runtime version checking
469
+ - 9 languages checked (Node, npm, Python, Rust, Go, Java, PHP, Ruby, .NET)
470
+ - Status display (✓/✗)
471
+
472
+ ---
473
+
474
+ ## Command Execution
475
+
476
+ ### Execution Flow
477
+
478
+ ```
479
+ User presses key (B/T/R/I or Enter on detail view)
480
+
481
+ useInput handler in Compass component (src/cli.js:324-592)
482
+
483
+ runProjectCommand(commandMeta, project)
484
+
485
+ execa(commandMeta.command[0], commandMeta.command.slice(1), {
486
+ cwd: project.path,
487
+ env: process.env,
488
+ stdin: 'pipe', // For interactive input
489
+ detached: process.platform !== 'win32' // For proper cleanup
490
+ })
491
+
492
+ subprocess.stdout?.on('data', ...) // Stream stdout
493
+ subprocess.stderr?.on('data', ...) // Stream stderr
494
+
495
+ addLogToTask(taskId, line) // Append to task logs
496
+
497
+ Task status updates: 'running' → 'finished' / 'failed' / 'killed'
498
+ ```
499
+
500
+ ### Task Object Structure
501
+
502
+ ```javascript
503
+ {
504
+ id: 'task-' + Date.now(), // Unique task ID
505
+ name: `${project.name} · ${commandLabel}`, // Display name
506
+ status: 'running' | 'finished' | 'failed' | 'killed',
507
+ logs: ['line1', 'line2', ...], // Log lines (capped at 500)
508
+ project: 'Project Name' // Source project name
509
+ }
510
+ ```
511
+
512
+ ### Process Management
513
+
514
+ - **Process Map**: `runningProcessMap` (useRef Map)
515
+ - Stores references to child processes
516
+ - Key: taskId
517
+ - Value: execa subprocess object
518
+
519
+ - **Kill Process** (`handleKillTask`):
520
+ - Windows: `taskkill /pid <pid> /f /t`
521
+ - Unix: `process.kill(-pid, 'SIGKILL')` (process group)
522
+ - Fallback: `proc.kill('SIGKILL')`
523
+
524
+ - **Kill All** (`killAllTasks`):
525
+ - Iterates `runningProcessMap`
526
+ - Kills each process
527
+ - Clears the map
528
+
529
+ ### Stdin Input
530
+
531
+ When a process is running and `activeTaskId` is set:
532
+ - User typing is captured by `useInput`
533
+ - Displayed in Footer's input area (with cursor)
534
+ - `Enter` sends `stdinBuffer + '\n'` to `proc.stdin`
535
+ - `Backspace/Delete` removes characters
536
+ - `Left/Right Arrow` moves cursor
537
+
538
+ ---
539
+
540
+ ## Configuration System
541
+
542
+ ### Config File: `~/.project-compass/config.json`
543
+
544
+ ```json
545
+ {
546
+ "customCommands": {
547
+ "/path/to/project": [
548
+ { "label": "My Command", "command": ["echo", "hello"], "source": "custom" }
549
+ ]
550
+ },
551
+ "showArtBoard": true,
552
+ "showHelpCards": false,
553
+ "showStructureGuide": false,
554
+ "maxVisibleProjects": 3,
555
+ "aiProvider": "openrouter",
556
+ "aiModel": "deepseek/deepseek-r1",
557
+ "aiToken": "your-api-token-here",
558
+ "projectMeta": {
559
+ "/path/to/project": { "port": "3000" }
560
+ }
561
+ }
562
+ ```
563
+
564
+ ### Config Options
565
+
566
+ | Option | Type | Default | Description |
567
+ |--------|------|---------|-------------|
568
+ | `customCommands` | Object | `{}` | Per-project custom commands |
569
+ | `showArtBoard` | Boolean | `true` | Show/hide the art board |
570
+ | `showHelpCards` | Boolean | `false` | Show/hide help cards |
571
+ | `showStructureGuide` | Boolean | `false` | Show/hide structure guide |
572
+ | `maxVisibleProjects` | Number | `3` | Projects per page in navigator |
573
+ | `aiProvider` | String | `"openrouter"` | AI provider ID (openrouter, gemini, claude, ollama) |
574
+ | `aiModel` | String | `"deepseek/deepseek-r1"` | AI model to use |
575
+ | `aiToken` | String | `""` | API token for AI provider |
576
+ | `projectMeta` | Object | `{}` | Per-project metadata (ports, etc.) |
577
+
578
+ ### Loading & Saving
579
+
580
+ ```javascript
581
+ function loadConfig() {
582
+ try {
583
+ if (fs.existsSync(CONFIG_PATH)) {
584
+ const payload = fs.readFileSync(CONFIG_PATH, 'utf-8');
585
+ const parsed = JSON.parse(payload || '{}');
586
+ return {
587
+ customCommands: {},
588
+ showArtBoard: true,
589
+ showHelpCards: false,
590
+ showStructureGuide: false,
591
+ maxVisibleProjects: 3,
592
+ ...parsed,
593
+ };
594
+ }
595
+ } catch (error) {
596
+ console.error(`Ignoring corrupt config: ${error.message}`);
597
+ }
598
+ return { customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false, maxVisibleProjects: 3 };
599
+ }
600
+
601
+ function saveConfig(config) {
602
+ try {
603
+ ensureConfigDir();
604
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
605
+ } catch (error) {
606
+ console.error(`Unable to persist config: ${error.message}`);
607
+ }
608
+ }
609
+ ```
610
+
611
+ ### Project-Specific Config: `compass.config.js`
612
+
613
+ Create in your project root:
614
+
615
+ ```javascript
616
+ export default {
617
+ commands: {
618
+ custom: {
619
+ label: 'My Command',
620
+ command: ['echo', 'hello'],
621
+ source: 'config'
622
+ }
623
+ },
624
+ frameworks: [
625
+ { name: 'MyFramework', icon: '🚀' }
626
+ ]
627
+ };
628
+ ```
629
+
630
+ This file is:
631
+ 1. Checked during `discoverProjects()`
632
+ 2. Loaded via dynamic `import()` (ESM)
633
+ 3. Merged into project data (commands + frameworks)
634
+ 4. Applied BEFORE framework plugin detection
635
+
636
+ ---
637
+
638
+ ## Recent Architecture Changes (v4.3.6)
639
+
640
+ ### 1. Framework Hallucination Bug FIXED
641
+
642
+ **Problem**: Projects without frameworks were showing random frameworks (e.g., simple Python project with `main.py` was detected as FastAPI).
643
+
644
+ **Root Cause**: Framework matchers in `frameworks.js` used file existence (`hasProjectFile`) instead of dependency matching.
645
+
646
+ **Fix Applied** (src/detectors/frameworks.js):
647
+ - `fastapi` matcher: Removed `|| hasProjectFile(project.path, 'main.py')` (line 328)
648
+ - `django` matcher: Removed `|| hasProjectFile(project.path, 'manage.py')` (line 370)
649
+ - `spring-boot` matcher: Removed `|| hasProjectFile(project.path, 'pom.xml') || hasProjectFile(project.path, 'build.gradle')`
650
+ - `quarkus` matcher: Removed `|| hasProjectFile(project.path, 'pom.xml')`
651
+ - `micronaut` matcher: Removed `|| hasProjectFile(project.path, 'pom.xml')`
652
+ - `rails` matcher: Changed to use `dependencyMatches(project, 'rails')` instead of file checks
653
+ - `sinatra` matcher: Removed `|| hasProjectFile(project.path, 'config.ru')`
654
+ - `.NET` matchers: Now use dependency checks instead of `*.csproj` file existence.
655
+
656
+ **Result**: Projects without explicit framework dependencies now correctly show `Frameworks: none`.
657
+
658
+ ### 2. compass-config.js Integration
659
+
660
+ **Problem**: `compass-config.js` existed but was never integrated into project detection.
661
+
662
+ **Fix Applied** (src/projectDetection.js):
663
+ - Added import of `loadProjectConfig` in `projectDetection.js`
664
+ - Integrated into `discoverProjects()` function to load `compass.config.js` from project directories
665
+ - Project-specific commands and frameworks from `compass.config.js` are now merged into project data
666
+
667
+ ### 3. AI Horizon Improvements
668
+
669
+ **Problem**: AI Horizon didn't properly show raw AI output and had poor JSON parsing.
670
+
671
+ **Fix Applied** (src/components/AIHorizon.js):
672
+ - Added `rawAIResponse` state to store raw AI output
673
+ - Improved JSON parsing to handle markdown code blocks (```json ... ```)
674
+ - Raw AI response is now displayed in the UI during review step
675
+ - Better error messages showing partial AI response if JSON parsing fails
676
+
677
+ ### 4. Node.js Detector Fixed
678
+
679
+ **Problem**: `node.js` detector was adding "Node.js" as a framework.
680
+
681
+ **Fix Applied** (src/detectors/node.js):
682
+ - Detector now only adds framework if it's not the generic "Node.js" type
683
+ - Projects using plain Node.js without frameworks now show `Frameworks: none`
684
+
685
+ ### 5. Framework Deduplication
686
+
687
+ **Problem**: `applyFrameworkPlugins()` could add duplicate frameworks.
688
+
689
+ **Fix Applied** (src/projectDetection.js):
690
+ - Added check to avoid adding duplicate frameworks
691
+ - Now preserves detector-detected frameworks and merges with plugin-detected ones
692
+
693
+ ---
11
694
 
12
695
  ## Design Patterns
13
- - **React-for-CLI:** Leveraging React's lifecycle and state management for a terminal environment.
14
- - **Component-Driven:** Each view is an isolated component in `src/components`.
15
- - **Async Execution:** Heavy lifting (globbing, command execution) is offloaded from the main render loop to prevent UI lag.
696
+
697
+ ### 1. React-for-CLI
698
+
699
+ Leveraging React's lifecycle and state management for a terminal environment.
700
+
701
+ - **Components**: Use `React.createElement` (aliased as `create`), NOT JSX
702
+ - **Rendering**: Ink handles terminal rendering (not DOM)
703
+ - **Hooks**: Full usage of useState, useEffect, useMemo, useCallback, useRef
704
+ - **Events**: `useInput` from Ink for keyboard handling
705
+
706
+ ### 2. Component-Driven
707
+
708
+ Each view is an isolated component in `src/components`:
709
+
710
+ - **Modular**: Each component has a single responsibility
711
+ - **Props**: Data flows down via props
712
+ - **Callbacks**: Actions flow up via callback props
713
+ - **Memoization**: Use `React.memo()` for performance
714
+
715
+ ### 3. Async Execution
716
+
717
+ Heavy lifting (globbing, command execution) is offloaded from the main render loop to prevent UI lag:
718
+
719
+ - **Project Scanning**: `useScanner()` uses `useEffect` + async/await
720
+ - **Command Execution**: `execa` handles subprocesses with streaming
721
+ - **AI Calls**: `fetch` API with async/await
722
+
723
+ ### 4. ESM Modules
724
+
725
+ All code uses ECMAScript modules (`import/export`):
726
+
727
+ ```javascript
728
+ // Import
729
+ import { discoverProjects } from './projectDetection.js';
730
+ import { checkBinary } from './projectDetection.js';
731
+
732
+ // Export
733
+ export default {
734
+ type: 'python',
735
+ // ...
736
+ };
737
+ ```
738
+
739
+ ### 5. Framework Plugin System
740
+
741
+ Extensible framework detection via `~/.project-compass/plugins.json`:
742
+
743
+ - **Built-in**: 40+ frameworks in `frameworks.js`
744
+ - **Custom**: User-defined in `plugins.json`
745
+ - **Detection**: Based on `dependencyMatches()` (not file existence)
746
+ - **Priority**: Plugins can boost project priority
747
+
748
+ ---
16
749
 
17
750
  ## Security
751
+
752
+ ### 1. No Arbitrary Execution
753
+
18
754
  Project Compass respects workspace boundaries and does not execute arbitrary code unless explicitly requested by the user.
19
755
 
756
+ - **Explicit Actions**: Commands only run when user presses B/T/R/I or Enter
757
+ - **Config Validation**: `compass.config.js` is loaded via ESM import (sandboxed)
758
+ - **No Auto-Run**: Detection does not execute any project scripts
759
+
760
+ ### 2. Local Storage
761
+
762
+ API tokens are stored locally in `~/.project-compass/config.json`:
763
+
764
+ - **File Permissions**: Standard filesystem permissions apply
765
+ - **No Cloud**: Tokens never leave your machine except to AI provider
766
+ - **User Responsibility**: Users should protect their config file
767
+
768
+ ### 3. Process Isolation
769
+
770
+ Background tasks are managed via `execa` with proper cleanup:
771
+
772
+ - **Detached Mode**: Unix uses detached processes for proper process group management
773
+ - **Kill Handling**: `SIGKILL` for forceful termination
774
+ - **Map Tracking**: `runningProcessMap` tracks all child processes
775
+
776
+ ### 4. Input Sanitization
777
+
778
+ - **Command Tokens**: User input is split via `split(/\s+/)` and filtered
779
+ - **Path Resolution**: `path.resolve()` for safe path handling
780
+ - **No Injection**: Commands are executed as arrays (not shell strings)
781
+
20
782
  ---
21
- *Built for scale and precision.*
783
+
784
+ ## Performance
785
+
786
+ ### 1. Fast Scanning
787
+
788
+ Uses `fast-glob` for high-speed project discovery:
789
+
790
+ - **Deep Scan**: Default depth of 5 directories
791
+ - **Ignore Patterns**: `node_modules`, `.git`, `dist`, `build`, `target`
792
+ - **Priority Order**: Higher priority detectors run first (fail-fast)
793
+
794
+ ### 2. Non-Blocking
795
+
796
+ Heavy operations (globbing, command execution) are offloaded from the main render loop:
797
+
798
+ - **Async/Await**: All I/O operations use async/await
799
+ - **Streaming**: Log output streams in real-time
800
+ - **Timers**: Startup animation uses `setInterval` (cleaned up)
801
+
802
+ ### 3. Smart Caching
803
+
804
+ Framework plugins are cached after first load:
805
+
806
+ ```javascript
807
+ let cachedFrameworkPlugins = null;
808
+
809
+ function getFrameworkPlugins() {
810
+ if (cachedFrameworkPlugins) {
811
+ return cachedFrameworkPlugins; // Return cache
812
+ }
813
+ cachedFrameworkPlugins = [...builtInFrameworks, ...loadUserFrameworks()];
814
+ return cachedFrameworkPlugins;
815
+ }
816
+ ```
817
+
818
+ ### 4. Memory Efficient
819
+
820
+ Log buffers capped at 500 lines per task:
821
+
822
+ ```javascript
823
+ const nextLogs = [...t.logs, ...newLines];
824
+ const updatedTask = { ...t, logs: nextLogs.length > 500 ? nextLogs.slice(-500) : nextLogs };
825
+ ```
826
+
827
+ ### 5. Pagination
828
+
829
+ Navigator uses pagination to handle large workspaces:
830
+
831
+ - **Configurable**: `maxVisibleProjects` (default: 3)
832
+ - **Page Navigation**: `PgUp/PgDn` for full page jumps
833
+ - **Boundary Guards**: Prevents out-of-bounds selection
834
+
835
+ ---
836
+
837
+ *Built for scale and precision.*