project-compass 3.6.6 โ 3.8.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/AGENTS.md +36 -0
- package/ARCHITECTURE.md +21 -0
- package/CONTRIBUTING.md +23 -0
- package/LICENSE +0 -0
- package/README.md +15 -3
- package/assets/screenshots/architect.jpg +0 -0
- package/assets/screenshots/artboard.jpg +0 -0
- package/assets/screenshots/exit-confirm.jpg +0 -0
- package/assets/screenshots/navigator.jpg +0 -0
- package/assets/screenshots/overlays.jpg +0 -0
- package/assets/screenshots/registry.jpg +0 -0
- package/assets/screenshots/studio.jpg +0 -0
- package/assets/screenshots/taskmanager.jpg +0 -0
- package/commands.md +12 -2
- package/eslint.config.cjs +0 -0
- package/memory/2026-03-02.md +28 -0
- package/memory/2026-03-03.md +22 -0
- package/package.json +1 -1
- package/src/cli.js +20 -38
- package/src/components/Footer.js +25 -0
- package/src/components/Header.js +21 -0
- package/src/components/Navigator.js +56 -0
- package/src/components/PackageRegistry.js +44 -20
- package/src/components/ProjectArchitect.js +4 -1
- package/src/components/Studio.js +0 -0
- package/src/components/TaskManager.js +0 -0
- package/src/configPaths.js +0 -0
- package/src/detectors/frameworks.js +226 -0
- package/src/detectors/generic.js +28 -0
- package/src/detectors/go.js +35 -0
- package/src/detectors/java.js +46 -0
- package/src/detectors/node.js +82 -0
- package/src/detectors/python.js +97 -0
- package/src/detectors/rust.js +35 -0
- package/src/detectors/utils.js +60 -0
- package/src/projectDetection.js +53 -1002
- package/src/store/useProjectStore.js +32 -0
- package/src/cli.js.bak +0 -679
package/AGENTS.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# AGENTS.md - Project Compass Workspace
|
|
2
|
+
|
|
3
|
+
Welcome to Project Compass. This file provides context for AI agents (Clawdy, Claude, Copilot, etc.) working on this repository.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
Project Compass is a futuristic project navigator and runner designed for modern polyglot development (Node.js, Python, Rust, Go). It provides a high-fidelity terminal UI (using Ink) to manage complex workspaces.
|
|
7
|
+
|
|
8
|
+
## Project Structure
|
|
9
|
+
- `src/cli.js`: Entry point. Handles argument parsing and the main Ink UI loop.
|
|
10
|
+
- `src/projectDetection.js`: Logic for identifying projects and their frameworks.
|
|
11
|
+
- `src/components/`: React/Ink components for the TUI.
|
|
12
|
+
- `TaskManager.js`: Orbit Task Manager (background processes).
|
|
13
|
+
- `PackageRegistry.js`: Dependency management interface.
|
|
14
|
+
- `ProjectArchitect.js`: Scaffolding and templates.
|
|
15
|
+
- `Studio.js`: Omni-Studio health audit.
|
|
16
|
+
- `assets/`: UI-related assets (screenshots, etc.).
|
|
17
|
+
|
|
18
|
+
## Tech Stack
|
|
19
|
+
- **Runtime:** Node.js (ESM)
|
|
20
|
+
- **UI Framework:** [Ink](https://github.com/vadimdemedes/ink) (React for CLI)
|
|
21
|
+
- **Styling:** `kleur` (Note: Use `kleur.bold(kleur.magenta('text'))` instead of chained `.magenta.bold`)
|
|
22
|
+
- **Execution:** `execa` for robust subprocess management.
|
|
23
|
+
|
|
24
|
+
## Development Rules
|
|
25
|
+
1. **Zero Mock Data:** Always aim for live system/project data.
|
|
26
|
+
2. **ESM Only:** The project uses `"type": "module"`. Use `import/export`.
|
|
27
|
+
3. **UI Consistency:** Keep the "Futuristic Cockpit" aesthetic (use symbols, borders, and dim colors where appropriate).
|
|
28
|
+
4. **Performance:** Heavy operations (like workspace-wide globbing) should be optimized or backgrounded.
|
|
29
|
+
|
|
30
|
+
## Common Tasks
|
|
31
|
+
- `npm start`: Launch the navigator.
|
|
32
|
+
- `npm run lint`: Check code style.
|
|
33
|
+
- `npm version <patch|minor|major>`: Update version and sync with npm.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
*Created for the CrimsonDevil333333 Ecosystem.*
|
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Project Compass Architecture
|
|
2
|
+
|
|
3
|
+
This document describes the high-level architecture of Project Compass.
|
|
4
|
+
|
|
5
|
+
## Data Flow
|
|
6
|
+
1. **Initialization:** `cli.js` resolves the working directory (defaults to current folder).
|
|
7
|
+
2. **Discovery:** `projectDetection.js` performs 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, 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.
|
|
11
|
+
|
|
12
|
+
## 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.
|
|
16
|
+
|
|
17
|
+
## Security
|
|
18
|
+
Project Compass respects workspace boundaries and does not execute arbitrary code unless explicitly requested by the user.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
*Built for scale and precision.*
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Contributing to Project Compass
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in helping to navigate the project universe! ๐
|
|
4
|
+
|
|
5
|
+
## Core Principles
|
|
6
|
+
1. **High Fidelity:** The UI should feel like a cockpit, not just a text list.
|
|
7
|
+
2. **Safety First:** Commands should be executed safely, and critical actions (like `rm` or `kill`) should have a confirmation gate.
|
|
8
|
+
3. **Speed:** Operations must be non-blocking. Use Ink's async components or background processes via `execa`.
|
|
9
|
+
|
|
10
|
+
## Working with AI Agents
|
|
11
|
+
This repository is "AI-First." Always update `AGENTS.md` after making structural changes to the code or adding new core components. This helps subsequent agents (and humans!) maintain high-velocity context.
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
1. **Fork and Branch:** Create a branch for your feature or fix.
|
|
15
|
+
2. **Code Style:** Follow the existing ESM pattern.
|
|
16
|
+
3. **Testing:** Run `npm start` in a large workspace (like a multi-repo folder) to ensure the UI remains performant.
|
|
17
|
+
4. **Commits:** Use conventional commits (`feat:`, `fix:`, `docs:`, `refactor:`).
|
|
18
|
+
|
|
19
|
+
## Deployment
|
|
20
|
+
All production versions are managed by Satyaa and the primary agent, Clawdy. Any `npm version` bump must include an update to `package-lock.json`.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
*Navigate the future.*
|
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -19,9 +19,15 @@ npm install -g project-compass
|
|
|
19
19
|
## ๐ Premium Features
|
|
20
20
|
|
|
21
21
|
### ๐ The Navigator (Main Interface)
|
|
22
|
-
- **Automatic Discovery**: Instantly
|
|
22
|
+
- **Automatic Discovery**: Instantly identifies **Node.js (Next.js, React, Vue, NestJS, Angular, SvelteKit, Nuxt, Astro)**, **Python (Django, Flask, FastAPI)**, **Rust (Rocket, Actix)**, **Go**, **Java (Spring Boot)**, **.NET (ASP.NET Core)**, **PHP (Laravel)**, and **Ruby**.
|
|
23
|
+
- **Package Manager Intelligence**: Context-aware detection of `pnpm`, `bun`, `yarn`, and `npm`. No hardcoded commandsโCompass uses your project's preferred tool.
|
|
23
24
|
- **Deep Detail View**: Press `Enter` to reveal project manifests, detected frameworks, and available scripts.
|
|
24
25
|
- **Custom Actions**: Save persistent, project-specific commands with `Shift+C` (e.g., `deploy|npm run deploy --prod`).
|
|
26
|
+
- **Macro Commands**: High-speed access to core workflows:
|
|
27
|
+
- `B`: Build
|
|
28
|
+
- `T`: Test
|
|
29
|
+
- `R`: Run / Start
|
|
30
|
+
- `I`: Install Dependencies (New!)
|
|
25
31
|
- **Live Output Panel**: Stream real-time logs from active processes with dedicated scrolling (`Shift+โ/โ`).
|
|
26
32
|
|
|
27
33
|
### ๐ฐ๏ธ Orbit Task Manager (`Shift+T`)
|
|
@@ -35,12 +41,17 @@ npm install -g project-compass
|
|
|
35
41
|
### ๐ฆ Package Registry (`Shift+P`)
|
|
36
42
|
- **Context-Aware Management**: Add or remove dependencies without leaving the app.
|
|
37
43
|
- **Internal Switcher**: Quick-swap projects within the registry view using `S`.
|
|
38
|
-
- **Multi-Runtime Support**: Handles `npm`, `pip`, and more based on project type.
|
|
44
|
+
- **Multi-Runtime Support**: Handles `npm`, `pnpm`, `bun`, `pip`, and more based on project type.
|
|
39
45
|
|
|
40
46
|

|
|
41
47
|
|
|
42
48
|
### ๐๏ธ Project Architect (`Shift+N`)
|
|
43
|
-
- **Rapid Scaffolding**: Create new projects from scratch using industry-standard templates
|
|
49
|
+
- **Rapid Scaffolding**: Create new projects from scratch using industry-standard templates:
|
|
50
|
+
- **Next.js** (Standard & Bun variants)
|
|
51
|
+
- **React/Vue** (Vite-powered with pnpm/npm support)
|
|
52
|
+
- **Rust** (Cargo binary)
|
|
53
|
+
- **Django** (Python web framework)
|
|
54
|
+
- **Go** (Module initialization)
|
|
44
55
|
- **Interactive Prompts**: Safe, guided setup for directory structure and initial manifests.
|
|
45
56
|
|
|
46
57
|

|
|
@@ -58,6 +69,7 @@ npm install -g project-compass
|
|
|
58
69
|
| :--- | :--- |
|
|
59
70
|
| `โ / โ` | Move project focus |
|
|
60
71
|
| `Enter` | Toggle project Detail View |
|
|
72
|
+
| `B / T / R / I`| Macro: Build, Test, Run, **Install** |
|
|
61
73
|
| `Shift + T` | **Orbit**: Task Manager |
|
|
62
74
|
| `Shift + P` | **Registry**: Package Manager |
|
|
63
75
|
| `Shift + N` | **Architect**: Project Creator |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/commands.md
CHANGED
|
@@ -8,6 +8,7 @@ This document lists all supported languages, frameworks, and their built-in comm
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| โ / โ | Move project focus |
|
|
10
10
|
| Enter | Toggle deep detail view / Switch back from sub-views |
|
|
11
|
+
| **B / T / R / I**| **Macro Launch**: Build, Test, Run, **Install** |
|
|
11
12
|
| **Esc** | **Global Back**: Return to Main Navigator from any view |
|
|
12
13
|
| **Shift+A** | Open **Omni-Studio** (Environment & Runtime audit) |
|
|
13
14
|
| **Shift+T** | Open **Orbit Task Manager** (Manage background processes) |
|
|
@@ -20,6 +21,7 @@ This document lists all supported languages, frameworks, and their built-in comm
|
|
|
20
21
|
| **Shift+X** | **Clear** active task output logs |
|
|
21
22
|
| **Shift+E** | **Export** logs to a timestamped `.txt` file |
|
|
22
23
|
| **Shift+L** | **Rerun** the last executed command |
|
|
24
|
+
| Page Up / Page Down | Jump a full page of projects in the Navigator |
|
|
23
25
|
| **Shift+C** | Add a **Custom Command** (`label|cmd`) in detail view |
|
|
24
26
|
| **Shift+Q** | **Quit** application (Confirms if tasks are running) |
|
|
25
27
|
| Shift+โ / โ | Scroll output logs |
|
|
@@ -30,7 +32,7 @@ This document lists all supported languages, frameworks, and their built-in comm
|
|
|
30
32
|
|
|
31
33
|
Compass scans for the following manifests and requires their binaries in your PATH:
|
|
32
34
|
|
|
33
|
-
- **Node.js** (`node`, `npm`): `package.json`
|
|
35
|
+
- **Node.js** (`node`, `npm`, `pnpm`, `bun`): `package.json`
|
|
34
36
|
- **Python** (`python3`, `pip`): `pyproject.toml`, `requirements.txt`, `Pipfile`, `setup.py`
|
|
35
37
|
- **Rust** (`cargo`): `Cargo.toml`
|
|
36
38
|
- **Go** (`go`): `go.mod`
|
|
@@ -77,10 +79,18 @@ Compass scans for the following manifests and requires their binaries in your PA
|
|
|
77
79
|
- **A**: Add a new package to the project.
|
|
78
80
|
- **R**: Remove an existing package.
|
|
79
81
|
- **S**: **Internal Switcher**: Quick-swap between detected projects.
|
|
82
|
+
- Add/Remove commands automatically run the workspace's preferred package manager (npm/pnpm/yarn/bun, pip, cargo, composer, dotnet).
|
|
80
83
|
- **Esc / Shift+P**: Return to Navigator.
|
|
81
84
|
|
|
82
85
|
## Project Architect Shortcuts (Shift+N)
|
|
83
86
|
|
|
84
|
-
- **โ / โ**: Select a project template
|
|
87
|
+
- **โ / โ**: Select a project template:
|
|
88
|
+
- Next.js (npm / Bun)
|
|
89
|
+
- React (Vite - pnpm / npm)
|
|
90
|
+
- Vue (Vite)
|
|
91
|
+
- Rust (Cargo Binary)
|
|
92
|
+
- Django (startproject)
|
|
93
|
+
- Python (Basic)
|
|
94
|
+
- Go (mod init)
|
|
85
95
|
- **Enter**: Confirm selection and move to next step.
|
|
86
96
|
- **Esc / Shift+N**: Exit architect mode.
|
package/eslint.config.cjs
CHANGED
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 2026-03-02 - Project Compass Final Polish
|
|
2
|
+
|
|
3
|
+
## Task: Finalize and Polish project-compass (v3.7.0)
|
|
4
|
+
- **Status**: In Progress
|
|
5
|
+
- **Location**: /mnt/ramdisk/project-compass/
|
|
6
|
+
|
|
7
|
+
### Work Log:
|
|
8
|
+
- Initializing final polish sub-agent.
|
|
9
|
+
- Goal: Production-grade refactor, documentation update, and framework support verification.
|
|
10
|
+
- **Documentation Update**:
|
|
11
|
+
- Revamped `README.md` and `commands.md`.
|
|
12
|
+
- Included all new framework support (Next.js Bun, Django, Rust, etc.).
|
|
13
|
+
- Documented the new 'Install' (I) macro command.
|
|
14
|
+
- **Refactor (src/projectDetection.js)**:
|
|
15
|
+
- Updated `getPackageManager` to return 'npm' as default but prioritized `bun.lockb`, `pnpm-lock.yaml`.
|
|
16
|
+
- Hardcoded 'npm' replaced with dynamic `pm` everywhere in Node.js framework detection.
|
|
17
|
+
- Extended Next.js detection to include `.mjs` and `.ts` config files.
|
|
18
|
+
- Updated Vite, Tailwind, and Prisma commands to use project-specific package managers and `npx`-equivalent logic.
|
|
19
|
+
- **CLI Enhancement (src/cli.js)**:
|
|
20
|
+
- Added 'i' (install) to `ACTION_MAP`.
|
|
21
|
+
- Updated help menu and reserved keys to include 'i'.
|
|
22
|
+
- **Architect Polish (src/components/ProjectArchitect.js)**:
|
|
23
|
+
- Added Next.js (Bun), React (pnpm), and Django templates.
|
|
24
|
+
- Ensured robust command generation for scaffolding.
|
|
25
|
+
- **Verification**:
|
|
26
|
+
- Confirmed `metadata.packageManager` is used in all critical Node.js paths.
|
|
27
|
+
- Bumbed version to 3.7.0 in `package.json`.
|
|
28
|
+
- **Status**: Production Ready.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog - 2026-03-03
|
|
2
|
+
|
|
3
|
+
## [3.8.0] - Architectural Overhaul
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Paginated Navigator**: Implemented pagination for the project list. Now handles large workspaces (50+ projects) gracefully without breaking the TUI layout.
|
|
7
|
+
- **Configurable Limits**: Added `maxVisibleProjects` setting to `config.json` (default: 8).
|
|
8
|
+
- **Modular Architecture**:
|
|
9
|
+
- Broke down the massive `projectDetection.js` into specialized detectors in `src/detectors/` (Node, Python, Rust, Go, Java, Generic).
|
|
10
|
+
- Modularized TUI logic from `cli.js` into standalone React components: `Navigator.js`, `Header.js`, and `Footer.js`.
|
|
11
|
+
- **Production Hardening**: Added robust error boundaries and try-catch blocks in the project discovery pipeline to ensure one corrupt manifest doesn't crash the entire CLI.
|
|
12
|
+
|
|
13
|
+
### Refactored
|
|
14
|
+
- **src/projectDetection.js**: Now acts as a clean orchestrator for modular detectors.
|
|
15
|
+
- **src/cli.js**: Significantly reduced complexity by delegating UI rendering to sub-components.
|
|
16
|
+
- **State Management**: Introduced `src/store/` (preparing for full context-based state management).
|
|
17
|
+
|
|
18
|
+
### Technical Details
|
|
19
|
+
- New file structure:
|
|
20
|
+
- `src/detectors/node.js`, `python.js`, `rust.js`, etc.
|
|
21
|
+
- `src/components/Navigator.js`, `Header.js`, `Footer.js`.
|
|
22
|
+
- `src/detectors/utils.js` for shared detection logic.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -14,6 +14,9 @@ import Studio from './components/Studio.js';
|
|
|
14
14
|
import TaskManager from './components/TaskManager.js';
|
|
15
15
|
import PackageRegistry from './components/PackageRegistry.js';
|
|
16
16
|
import ProjectArchitect from './components/ProjectArchitect.js';
|
|
17
|
+
import Navigator from './components/Navigator.js';
|
|
18
|
+
import Header from './components/Header.js';
|
|
19
|
+
import Footer from './components/Footer.js';
|
|
17
20
|
|
|
18
21
|
const create = React.createElement;
|
|
19
22
|
const ART_CHARS = ['โ', 'โ', 'โ', 'โ
', 'โ'];
|
|
@@ -26,7 +29,8 @@ const HELP_CARD_MIN_WIDTH = 28;
|
|
|
26
29
|
const ACTION_MAP = {
|
|
27
30
|
b: 'build',
|
|
28
31
|
t: 'test',
|
|
29
|
-
r: 'run'
|
|
32
|
+
r: 'run',
|
|
33
|
+
i: 'install'
|
|
30
34
|
};
|
|
31
35
|
|
|
32
36
|
function saveConfig(config) {
|
|
@@ -48,13 +52,14 @@ function loadConfig() {
|
|
|
48
52
|
showArtBoard: true,
|
|
49
53
|
showHelpCards: false,
|
|
50
54
|
showStructureGuide: false,
|
|
55
|
+
maxVisibleProjects: 8,
|
|
51
56
|
...parsed,
|
|
52
57
|
};
|
|
53
58
|
}
|
|
54
59
|
} catch (error) {
|
|
55
60
|
console.error(`Ignoring corrupt config: ${error.message}`);
|
|
56
61
|
}
|
|
57
|
-
return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false};
|
|
62
|
+
return {customCommands: {}, showArtBoard: true, showHelpCards: false, showStructureGuide: false, maxVisibleProjects: 8};
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
function useScanner(rootPath) {
|
|
@@ -444,6 +449,11 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
444
449
|
if (key.shift && key.upArrow) { scrollLogs(1); return; }
|
|
445
450
|
if (key.shift && key.downArrow) { scrollLogs(-1); return; }
|
|
446
451
|
|
|
452
|
+
const pageStep = Math.max(1, config.maxVisibleProjects || 8);
|
|
453
|
+
const clampIndex = (value) => Math.max(0, Math.min(projects.length - 1, value));
|
|
454
|
+
if (key.pageUp && projects.length > 0) { console.clear(); setSelectedIndex((prev) => clampIndex(prev - pageStep)); return; }
|
|
455
|
+
if (key.pageDown && projects.length > 0) { console.clear(); setSelectedIndex((prev) => clampIndex(prev + pageStep)); return; }
|
|
456
|
+
|
|
447
457
|
if (normalizedInput === '?') { console.clear(); setShowHelp((prev) => !prev); return; }
|
|
448
458
|
if (shiftCombo('l') && lastCommandRef.current) { runProjectCommand(lastCommandRef.current.commandMeta, lastCommandRef.current.project); return; }
|
|
449
459
|
|
|
@@ -472,7 +482,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
472
482
|
runProjectCommand(detailShortcutMap.get(normalizedInput), selectedProject);
|
|
473
483
|
return;
|
|
474
484
|
}
|
|
475
|
-
const reserved = ['a', 'p', 'n', 'x', 'e', 'd', 'b', 't', 'q', 'h', 's', 'l', 'c'];
|
|
485
|
+
const reserved = ['a', 'p', 'n', 'x', 'e', 'd', 'b', 't', 'q', 'h', 's', 'l', 'c', 'i'];
|
|
476
486
|
if (key.shift && !reserved.includes(normalizedInput)) {
|
|
477
487
|
runProjectCommand(detailShortcutMap.get(normalizedInput), selectedProject);
|
|
478
488
|
return;
|
|
@@ -486,30 +496,6 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
486
496
|
const orbitHint = mainView === 'tasks' ? 'Tasks View' : `Orbit: ${tasks.length} tasks`;
|
|
487
497
|
const artHint = config.showArtBoard ? 'Shift+B hide art' : 'Shift+B show art';
|
|
488
498
|
|
|
489
|
-
const projectRows = useMemo(() => {
|
|
490
|
-
if (loading) return [create(Text, {key: 'scanning', dimColor: true}, 'Scanning projectsโฆ')];
|
|
491
|
-
if (error) return [create(Text, {key: 'error', color: 'red'}, `Unable to scan: ${error}`)];
|
|
492
|
-
if (projects.length === 0) return [create(Text, {key: 'empty', dimColor: true}, 'No recognizable project manifests found.')];
|
|
493
|
-
|
|
494
|
-
return projects.map((project, index) => {
|
|
495
|
-
const isSelected = index === selectedIndex;
|
|
496
|
-
const frameworkBadges = (project.frameworks || []).map((frame) => `${frame.icon} ${frame.name}`).join(', ');
|
|
497
|
-
const hasMissingRuntime = project.missingBinaries && project.missingBinaries.length > 0;
|
|
498
|
-
return create(
|
|
499
|
-
Box,
|
|
500
|
-
{key: project.id, flexDirection: 'column', marginBottom: 1, padding: 1},
|
|
501
|
-
create(
|
|
502
|
-
Box,
|
|
503
|
-
{flexDirection: 'row'},
|
|
504
|
-
create(Text, {color: isSelected ? 'cyan' : 'white', bold: isSelected}, `${project.icon} ${project.name}`),
|
|
505
|
-
hasMissingRuntime && create(Text, {color: 'red', bold: true}, ' โ ๏ธ Runtime missing')
|
|
506
|
-
),
|
|
507
|
-
create(Text, {dimColor: true}, ` ${project.type} ยท ${path.relative(rootPath, project.path) || '.'}`),
|
|
508
|
-
frameworkBadges && create(Text, {dimColor: true}, ` ${frameworkBadges}`)
|
|
509
|
-
);
|
|
510
|
-
});
|
|
511
|
-
}, [loading, error, projects, selectedIndex, rootPath]);
|
|
512
|
-
|
|
513
499
|
const detailContent = useMemo(() => {
|
|
514
500
|
if (viewMode !== 'detail' || !selectedProject) {
|
|
515
501
|
return [create(Text, {key: 'e-h', dimColor: true}, 'Press Enter on a project to reveal details.')];
|
|
@@ -567,27 +553,23 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
567
553
|
case 'architect': return create(ProjectArchitect, {rootPath, onRunCommand: runProjectCommand, CursorText, onReturn: () => setMainView('navigator')});
|
|
568
554
|
default: {
|
|
569
555
|
const navigatorBody = [
|
|
570
|
-
create(
|
|
571
|
-
create(Box, {flexDirection: 'column'}, create(Text, {color: 'magenta', bold: true}, 'Project Compass'), create(Text, {dimColor: true}, `${projectCountLabel} detected in ${rootPath}`)),
|
|
572
|
-
create(Box, {flexDirection: 'column', alignItems: 'flex-end'},
|
|
573
|
-
create(Text, {color: running ? 'yellow' : 'green'}, statusHint),
|
|
574
|
-
create(Text, {dimColor: true}, `${toggleHint} ยท ${orbitHint} ยท ${artHint} ยท Shift+Q: Quit`)
|
|
575
|
-
)
|
|
576
|
-
),
|
|
556
|
+
create(Header, {projectCountLabel, rootPath, running, statusHint, toggleHint, orbitHint, artHint}),
|
|
577
557
|
config.showArtBoard && create(Box, {key: 'artboard', flexDirection: 'column', marginTop: 1, borderStyle: 'round', borderColor: 'gray', padding: 1},
|
|
578
558
|
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {color: 'magenta', bold: true}, 'Art-coded build atlas'), create(Text, {dimColor: true}, 'press ? for overlay help')),
|
|
579
559
|
create(Box, {flexDirection: 'row', marginTop: 1}, ...ART_CHARS.map((char, i) => create(Text, {key: i, color: ART_COLORS[i % ART_COLORS.length]}, char.repeat(2)))),
|
|
580
560
|
create(Box, {flexDirection: 'row', marginTop: 1}, ...artTileNodes)
|
|
581
561
|
),
|
|
582
562
|
create(Box, {key: 'projects-row', marginTop: 1, flexDirection: 'row', alignItems: 'stretch', width: '100%', flexWrap: 'wrap'},
|
|
583
|
-
create(Box, {flexGrow: 1, flexBasis: 0, minWidth: PROJECTS_MIN_WIDTH, marginRight: 1, borderStyle: 'round', borderColor: 'magenta', padding: 1},
|
|
563
|
+
create(Box, {flexGrow: 1, flexBasis: 0, minWidth: PROJECTS_MIN_WIDTH, marginRight: 1, borderStyle: 'round', borderColor: 'magenta', padding: 1},
|
|
564
|
+
create(Text, {bold: true, color: 'magenta'}, 'Projects'),
|
|
565
|
+
create(Box, {flexDirection: 'column', marginTop: 1}, create(Navigator, {projects, selectedIndex, rootPath, loading, error, maxVisibleProjects: config.maxVisibleProjects}))
|
|
566
|
+
),
|
|
584
567
|
create(Box, {flexGrow: 1.3, flexBasis: 0, minWidth: DETAILS_MIN_WIDTH, borderStyle: 'round', borderColor: 'cyan', padding: 1, flexDirection: 'column'}, create(Text, {bold: true, color: 'cyan'}, 'Details'), ...detailContent)
|
|
585
568
|
),
|
|
586
569
|
create(Box, {key: 'output-row', marginTop: 1, flexDirection: 'column'},
|
|
587
570
|
create(Box, {flexDirection: 'row', justifyContent: 'space-between'}, create(Text, {bold: true, color: 'yellow'}, `Output: ${activeTask?.name || 'None'}`), create(Text, {dimColor: true}, logOffset ? `Scrolled ${logOffset} lines` : 'Live log view')),
|
|
588
571
|
create(OutputPanel, {activeTask, logOffset}),
|
|
589
|
-
create(
|
|
590
|
-
create(Box, {marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1}, create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? ' Stdin buffer ' : ' Input ready '), create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running})))
|
|
572
|
+
create(Footer, {toggleHint, running, stdinBuffer, stdinCursor, CursorText})
|
|
591
573
|
),
|
|
592
574
|
config.showHelpCards && create(Box, {key: 'help-cards', marginTop: 1, flexDirection: 'row', justifyContent: 'space-between', flexWrap: 'wrap'}, [
|
|
593
575
|
{label: 'Navigation', color: 'magenta', body: ['โ / โ move focus, Enter: details', 'Shift+โ / โ scroll output', 'Shift+H toggle help cards', 'Shift+D detach from task']},
|
|
@@ -647,7 +629,7 @@ async function main() {
|
|
|
647
629
|
console.log(' Enter Toggle deep detail view (manifests, scripts, frameworks)');
|
|
648
630
|
console.log(' Shift+C Add a persistent custom command to the focused project');
|
|
649
631
|
console.log(' 1-9 Quick-run numbered scripts in detail view');
|
|
650
|
-
console.log(' B/T/R
|
|
632
|
+
console.log(' B/T/R/I Macro run: Build / Test / Run / Install');
|
|
651
633
|
console.log('');
|
|
652
634
|
console.log(kleur.bold(kleur.green('๐ ๏ธ Workspace Tools:')));
|
|
653
635
|
console.log(' Shift+B Toggle Art-coded Build Atlas');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
|
|
4
|
+
export default function Footer({ toggleHint, running, stdinBuffer, stdinCursor, CursorText }) {
|
|
5
|
+
return React.createElement(
|
|
6
|
+
Box,
|
|
7
|
+
{ flexDirection: 'column', marginTop: 1 },
|
|
8
|
+
React.createElement(
|
|
9
|
+
Box,
|
|
10
|
+
{ flexDirection: 'row', justifyContent: 'space-between' },
|
|
11
|
+
React.createElement(Text, { dimColor: true }, running ? 'Type to feed stdin; Enter: submit.' : 'Run a command or press Shift+T to switch tasks.'),
|
|
12
|
+
React.createElement(Text, { dimColor: true }, `${toggleHint}, Shift+S: Structure Guide`)
|
|
13
|
+
),
|
|
14
|
+
React.createElement(
|
|
15
|
+
Box,
|
|
16
|
+
{ marginTop: 1, flexDirection: 'row', borderStyle: 'round', borderColor: running ? 'green' : 'gray', paddingX: 1 },
|
|
17
|
+
React.createElement(Text, { bold: true, color: running ? 'green' : 'white' }, running ? ' Stdin buffer ' : ' Input ready '),
|
|
18
|
+
React.createElement(
|
|
19
|
+
Box,
|
|
20
|
+
{ marginLeft: 1 },
|
|
21
|
+
React.createElement(CursorText, { value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running })
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
|
|
4
|
+
export default function Header({ projectCountLabel, rootPath, running, statusHint, toggleHint, orbitHint, artHint }) {
|
|
5
|
+
return React.createElement(
|
|
6
|
+
Box,
|
|
7
|
+
{ justifyContent: 'space-between' },
|
|
8
|
+
React.createElement(
|
|
9
|
+
Box,
|
|
10
|
+
{ flexDirection: 'column' },
|
|
11
|
+
React.createElement(Text, { color: 'magenta', bold: true }, 'Project Compass'),
|
|
12
|
+
React.createElement(Text, { dimColor: true }, `${projectCountLabel} detected in ${rootPath}`)
|
|
13
|
+
),
|
|
14
|
+
React.createElement(
|
|
15
|
+
Box,
|
|
16
|
+
{ flexDirection: 'column', alignItems: 'flex-end' },
|
|
17
|
+
React.createElement(Text, { color: running ? 'yellow' : 'green' }, statusHint),
|
|
18
|
+
React.createElement(Text, { dimColor: true }, `${toggleHint} ยท ${orbitHint} ยท ${artHint} ยท Shift+Q: Quit`)
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export default function Navigator({
|
|
6
|
+
projects,
|
|
7
|
+
selectedIndex,
|
|
8
|
+
rootPath,
|
|
9
|
+
loading,
|
|
10
|
+
error,
|
|
11
|
+
maxVisibleProjects = 8
|
|
12
|
+
}) {
|
|
13
|
+
const page = Math.floor(selectedIndex / maxVisibleProjects);
|
|
14
|
+
const start = page * maxVisibleProjects;
|
|
15
|
+
const end = start + maxVisibleProjects;
|
|
16
|
+
const visibleProjects = projects.slice(start, end);
|
|
17
|
+
|
|
18
|
+
const projectRows = useMemo(() => {
|
|
19
|
+
if (loading) return [React.createElement(Text, { key: 'scanning', dimColor: true }, 'Scanning projectsโฆ')];
|
|
20
|
+
if (error) return [React.createElement(Text, { key: 'error', color: 'red' }, `Unable to scan: ${error}`)];
|
|
21
|
+
if (projects.length === 0) return [React.createElement(Text, { key: 'empty', dimColor: true }, 'No recognizable project manifests found.')];
|
|
22
|
+
|
|
23
|
+
return visibleProjects.map((project, index) => {
|
|
24
|
+
const absoluteIndex = start + index;
|
|
25
|
+
const isSelected = absoluteIndex === selectedIndex;
|
|
26
|
+
const frameworkBadges = (project.frameworks || []).map((frame) => `${frame.icon} ${frame.name}`).join(', ');
|
|
27
|
+
const hasMissingRuntime = project.missingBinaries && project.missingBinaries.length > 0;
|
|
28
|
+
|
|
29
|
+
return React.createElement(
|
|
30
|
+
Box,
|
|
31
|
+
{ key: project.id, flexDirection: 'column', marginBottom: 1, padding: 1 },
|
|
32
|
+
React.createElement(
|
|
33
|
+
Box,
|
|
34
|
+
{ flexDirection: 'row' },
|
|
35
|
+
React.createElement(Text, { color: isSelected ? 'cyan' : 'white', bold: isSelected }, `${project.icon} ${project.name}`),
|
|
36
|
+
hasMissingRuntime && React.createElement(Text, { color: 'red', bold: true }, ' โ ๏ธ Runtime missing')
|
|
37
|
+
),
|
|
38
|
+
React.createElement(Text, { dimColor: true }, ` ${project.type} ยท ${path.relative(rootPath, project.path) || '.'}`),
|
|
39
|
+
frameworkBadges && React.createElement(Text, { dimColor: true }, ` ${frameworkBadges}`)
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
}, [loading, error, projects.length, visibleProjects, selectedIndex, start, rootPath]);
|
|
43
|
+
|
|
44
|
+
const totalPages = Math.ceil(projects.length / maxVisibleProjects);
|
|
45
|
+
|
|
46
|
+
return React.createElement(
|
|
47
|
+
Box,
|
|
48
|
+
{ flexDirection: 'column' },
|
|
49
|
+
...projectRows,
|
|
50
|
+
projects.length > maxVisibleProjects && React.createElement(
|
|
51
|
+
Box,
|
|
52
|
+
{ marginTop: 1, justifyContent: 'center' },
|
|
53
|
+
React.createElement(Text, { dimColor: true }, `Page ${page + 1} of ${totalPages} (Total: ${projects.length})`)
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -3,6 +3,48 @@ import {Box, Text, useInput} from 'ink';
|
|
|
3
3
|
|
|
4
4
|
const create = React.createElement;
|
|
5
5
|
|
|
6
|
+
const NODE_PACKAGE_COMMANDS = {
|
|
7
|
+
npm: { add: ['npm', 'install'], remove: ['npm', 'uninstall'] },
|
|
8
|
+
pnpm: { add: ['pnpm', 'add'], remove: ['pnpm', 'remove'] },
|
|
9
|
+
yarn: { add: ['yarn', 'add'], remove: ['yarn', 'remove'] },
|
|
10
|
+
bun: { add: ['bun', 'add'], remove: ['bun', 'remove'] }
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const resolveNodePackageCommand = (project, pkg, action) => {
|
|
14
|
+
if (!project || !pkg) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const manager = (project.metadata?.packageManager || 'npm').toLowerCase();
|
|
18
|
+
const template = (NODE_PACKAGE_COMMANDS[manager] || NODE_PACKAGE_COMMANDS.npm)[action];
|
|
19
|
+
return template ? [...template, pkg] : null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getAddCmd = (project, pkg) => {
|
|
23
|
+
if (!project || !pkg) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const type = project.type;
|
|
27
|
+
if (type === 'Node.js') return resolveNodePackageCommand(project, pkg, 'add');
|
|
28
|
+
if (type === 'Python') return ['pip', 'install', pkg];
|
|
29
|
+
if (type === 'Rust') return ['cargo', 'add', pkg];
|
|
30
|
+
if (type === '.NET') return ['dotnet', 'add', 'package', pkg];
|
|
31
|
+
if (type === 'PHP') return ['composer', 'require', pkg];
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const getRemoveCmd = (project, pkg) => {
|
|
36
|
+
if (!project || !pkg) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const type = project.type;
|
|
40
|
+
if (type === 'Node.js') return resolveNodePackageCommand(project, pkg, 'remove');
|
|
41
|
+
if (type === 'Python') return ['pip', 'uninstall', '-y', pkg];
|
|
42
|
+
if (type === 'Rust') return ['cargo', 'remove', pkg];
|
|
43
|
+
if (type === '.NET') return ['dotnet', 'remove', 'package', pkg];
|
|
44
|
+
if (type === 'PHP') return ['composer', 'remove', pkg];
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
|
|
6
48
|
const PackageRegistry = memo(({selectedProject, projects = [], onRunCommand, CursorText, onSelectProject}) => {
|
|
7
49
|
const [view, setView] = useState(selectedProject ? 'manage' : 'select'); // select | manage
|
|
8
50
|
const [mode, setMode] = useState('list'); // list | add | remove
|
|
@@ -28,8 +70,8 @@ const PackageRegistry = memo(({selectedProject, projects = [], onRunCommand, Cur
|
|
|
28
70
|
|
|
29
71
|
if (mode === 'add' || mode === 'remove') {
|
|
30
72
|
if (key.return) {
|
|
31
|
-
if (input.trim()) {
|
|
32
|
-
const cmd = mode === 'add' ? getAddCmd(
|
|
73
|
+
if (input.trim() && activeProject) {
|
|
74
|
+
const cmd = mode === 'add' ? getAddCmd(activeProject, input.trim()) : getRemoveCmd(activeProject, input.trim());
|
|
33
75
|
if (cmd) onRunCommand({label: `${mode === 'add' ? 'Add' : 'Remove'} ${input}`, command: cmd}, activeProject);
|
|
34
76
|
}
|
|
35
77
|
setMode('list'); setInput(''); setCursor(0);
|
|
@@ -60,24 +102,6 @@ const PackageRegistry = memo(({selectedProject, projects = [], onRunCommand, Cur
|
|
|
60
102
|
}
|
|
61
103
|
});
|
|
62
104
|
|
|
63
|
-
const getAddCmd = (type, pkg) => {
|
|
64
|
-
if (type === 'Node.js') return ['npm', 'install', pkg];
|
|
65
|
-
if (type === 'Python') return ['pip', 'install', pkg];
|
|
66
|
-
if (type === 'Rust') return ['cargo', 'add', pkg];
|
|
67
|
-
if (type === '.NET') return ['dotnet', 'add', 'package', pkg];
|
|
68
|
-
if (type === 'PHP') return ['composer', 'require', pkg];
|
|
69
|
-
return null;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const getRemoveCmd = (type, pkg) => {
|
|
73
|
-
if (type === 'Node.js') return ['npm', 'uninstall', pkg];
|
|
74
|
-
if (type === 'Python') return ['pip', 'uninstall', '-y', pkg];
|
|
75
|
-
if (type === 'Rust') return ['cargo', 'remove', pkg];
|
|
76
|
-
if (type === '.NET') return ['dotnet', 'remove', 'package', pkg];
|
|
77
|
-
if (type === 'PHP') return ['composer', 'remove', pkg];
|
|
78
|
-
return null;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
105
|
if (view === 'select') {
|
|
82
106
|
return create(
|
|
83
107
|
Box,
|
|
@@ -13,9 +13,12 @@ const ProjectArchitect = memo(({rootPath, onRunCommand, CursorText, onReturn}) =
|
|
|
13
13
|
|
|
14
14
|
const frameworks = [
|
|
15
15
|
{name: 'Next.js', cmd: (p, n) => ['npx', 'create-next-app@latest', path.join(p, n)]},
|
|
16
|
-
{name: '
|
|
16
|
+
{name: 'Next.js (Bun)', cmd: (p, n) => ['bun', 'create', 'next-app', path.join(p, n)]},
|
|
17
|
+
{name: 'React (Vite/pnpm)', cmd: (p, n) => ['pnpm', 'create', 'vite', path.join(p, n), '--template', 'react']},
|
|
18
|
+
{name: 'React (Vite/npm)', cmd: (p, n) => ['npm', 'create', 'vite@latest', path.join(p, n), '--', '--template', 'react']},
|
|
17
19
|
{name: 'Vue (Vite)', cmd: (p, n) => ['npm', 'create', 'vite@latest', path.join(p, n), '--', '--template', 'vue']},
|
|
18
20
|
{name: 'Rust (Binary)', cmd: (p, n) => ['cargo', 'new', path.join(p, n)]},
|
|
21
|
+
{name: 'Django Project', cmd: (p, n) => ['django-admin', 'startproject', n, path.join(p, n)]},
|
|
19
22
|
{name: 'Python (Basic)', cmd: (p, n) => ['mkdir', '-p', path.join(p, n)]},
|
|
20
23
|
{name: 'Go Module', cmd: (p, n) => ['mkdir', '-p', path.join(p, n), '&&', 'cd', path.join(p, n), '&&', 'go', 'mod', 'init', n]}
|
|
21
24
|
];
|
package/src/components/Studio.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/configPaths.js
CHANGED
|
File without changes
|