project-compass 2.0.1 β 2.1.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/README.md +24 -80
- package/package.json +1 -1
- package/project-compass-2.0.2.tgz +0 -0
- package/src/cli.js +31 -29
- package/src/projectDetection.js +21 -15
package/README.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
# Project Compass
|
|
1
|
+
# Project Compass (v2.1.0)
|
|
2
2
|
|
|
3
|
-
Project Compass is a futuristic CLI navigator built with [Ink](https://github.com/vadimdemedes/ink) that scans your current folder tree for familiar code projects and gives you one
|
|
3
|
+
Project Compass is a futuristic CLI navigator built with [Ink](https://github.com/vadimdemedes/ink) that scans your current folder tree for familiar code projects and gives you one-keystroke access to build, test, or run them.
|
|
4
4
|
|
|
5
5
|
## Highlights
|
|
6
6
|
|
|
7
|
-
- π Scans directories for Node.js, Python, Rust, Go, Java, and Scala projects
|
|
8
|
-
- π¨
|
|
9
|
-
- π
|
|
10
|
-
- π‘
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- π§ Add bespoke commands via **C** in detail view and store them globally (`~/.project-compass/config.json`) so every workspace remembers your favorite invocations.
|
|
14
|
-
- π Extend detection via plugins (JSON specs under `~/.project-compass/plugins.json`) to teach Project Compass about extra frameworks or command sets.
|
|
15
|
-
- π¦ Install globally and invoke `project-compass` from any folder to activate the UI instantly.
|
|
7
|
+
- π Scans directories for Node.js, Python, Rust, Go, Java, and Scala projects.
|
|
8
|
+
- π¨ Futuristic layout with glyph-based art board and split Projects/Details rows.
|
|
9
|
+
- π **New Keyboard-Centric UX**: Shortcuts now use **Shift** instead of Ctrl to avoid terminal interference.
|
|
10
|
+
- π‘ **Refined Output**: Improved stdin buffer with proper spacing and reliable scrolling (Shift+β/β).
|
|
11
|
+
- π§ **Smart Detection**: Support for 12+ frameworks with specialized build/run commands and setup hints.
|
|
12
|
+
- π **Extensible**: Add custom commands with **C** and frameworks via `plugins.json`.
|
|
16
13
|
|
|
17
14
|
## Installation
|
|
18
15
|
|
|
@@ -26,83 +23,30 @@ npm install -g project-compass
|
|
|
26
23
|
project-compass [--dir /path/to/workspace]
|
|
27
24
|
```
|
|
28
25
|
|
|
29
|
-
### Keyboard
|
|
26
|
+
### Keyboard Guide
|
|
30
27
|
|
|
31
28
|
| Key | Action |
|
|
32
29
|
| --- | --- |
|
|
33
|
-
| β / β | Move
|
|
34
|
-
| B / T / R |
|
|
35
|
-
| 1β9 | Execute
|
|
36
|
-
| C | Add a custom command (`label|cmd`)
|
|
37
|
-
| Shift β /
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
| Ctrl+C | Interrupt a running command (works while streaming output) |
|
|
30
|
+
| β / β | Move focus, **Enter**: toggle details |
|
|
31
|
+
| B / T / R | Build / Test / Run |
|
|
32
|
+
| 1β9 | Execute numbered detail commands |
|
|
33
|
+
| C | Add a custom command (`label|cmd`) |
|
|
34
|
+
| **Shift β / β** | Scroll output buffer |
|
|
35
|
+
| **Shift+L** | Rerun last command |
|
|
36
|
+
| **Shift+H** | Toggle help cards |
|
|
37
|
+
| **Shift+S** | Toggle structure guide |
|
|
38
|
+
| **Shift+Q** | Quit app |
|
|
39
|
+
| ? | Toggle help overlay |
|
|
40
|
+
| Ctrl+C | Interrupt running command |
|
|
45
41
|
|
|
46
|
-
##
|
|
42
|
+
## Layout & UX
|
|
47
43
|
|
|
48
|
-
Project Compass
|
|
44
|
+
Project Compass features a split layout where Projects and Details stay paired while Output takes a full-width band. The stdin buffer (at the bottom) now has a clear distinction between the label and your input for better readability. The help cards (Shift+H) have been refactored for a cleaner, more readable look.
|
|
49
45
|
|
|
50
|
-
|
|
46
|
+
## Frameworks
|
|
51
47
|
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
"plugins": [
|
|
55
|
-
{
|
|
56
|
-
"name": "Remix",
|
|
57
|
-
"languages": ["Node.js"],
|
|
58
|
-
"files": ["remix.config.js"],
|
|
59
|
-
"dependencies": ["@remix-run/node"],
|
|
60
|
-
"commands": {
|
|
61
|
-
"run": "npm run dev",
|
|
62
|
-
"build": "npm run build"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Each command value can be a string or an array of tokens. When a plugin matches a project, its commands appear in the detail view with a `framework` badge, and the shortcut keys (B/T/R or numeric) can execute them.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
## Art board & detail view
|
|
73
|
-
|
|
74
|
-
Project Compass now opens with a rounded art board that shuffles your glyph row (ββββ
β with neon accents) and three branded tiles showing workspace pulse, the selected project focus, and the rhythm of commands. The detail view sits beside the project list as a gallery; border colors, badges, and the ambient header hint keep it feeling like a living installation rather than a vanilla CLI.
|
|
75
|
-
|
|
76
|
-
## Layout, output & help
|
|
77
|
-
|
|
78
|
-
Projects and details now occupy the same row, while the output panel takes its own full-width band beneath so long logs no longer stretch the rest of the UI. The output pane scrolls independently (Shift+β/β), feeds keystrokes into stdin while a command runs (type like normal, Enter submits, Ctrl+C aborts), and the buffer below shows exactly what you are typing. A trio of help tiles highlights navigation cues, command flow, and recent runs; they stay hidden by defaultβpress Ctrl+H to reveal the cards, Ctrl+S to show the structure guide that lists the manifest files for each language detection, `?` to open the overlay with extra tips, Ctrl+L to rerun the previous command, and Ctrl+Q to quit.
|
|
79
|
-
|
|
80
|
-
## Structure guide
|
|
81
|
-
|
|
82
|
-
Press `S` to reveal the structure guide that lists which manifest files trigger each language detection (Node.js looks for `package.json`, Python looks for `pyproject.toml` or `requirements.txt`, Rust needs `Cargo.toml`, etc.). Use `H` to hide the help cards if you need every pixel for your output, then bring them back when you want a refresher.
|
|
83
|
-
|
|
84
|
-
## Detection & setup hints
|
|
85
|
-
|
|
86
|
-
Project Compass now has a modular detection engine (`src/projectDetection.js`) that looks at manifests, frameworks, and optional plugins to identify what kind of project you are standing in. Every schema fills `extra.setupHints` (npm install, pip install, go mod tidy, etc.) and those hints show up under the detail view whenever a project needs bootstrapping.
|
|
87
|
-
|
|
88
|
-
Try the sample Python project at `/mnt/ramdisk/daily_builds/test_python_proj` (includes `pyproject.toml`, `requirements.txt`, and `app.py`) so you can run it via Project Compass and confirm stdin/input behavior.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
## Developer notes
|
|
93
|
-
|
|
94
|
-
- `npm start` launches the Ink UI in the current directory.
|
|
95
|
-
- `npm test` runs `node src/cli.js --mode test` to verify the scanner output.
|
|
96
|
-
- Extend support for more languages by editing `SCHEMAS` or add plugin definitions under `~/.project-compass/plugins.json`.
|
|
97
|
-
- Config lives at `~/.project-compass/config.json`. Drop custom commands there if you want to preseed them or share with teammates.
|
|
48
|
+
Detects **Next.js**, **React**, **Vue**, **NestJS**, **FastAPI**, **Django**, and more. Recognizes frameworks and injects specialized commands automatically.
|
|
98
49
|
|
|
99
50
|
## License
|
|
100
51
|
|
|
101
52
|
MIT Β© 2026 Satyaa & Clawdy
|
|
102
|
-
|
|
103
|
-
## Release & packaging
|
|
104
|
-
|
|
105
|
-
- Bump `package.json`/`package-lock.json` versions (e.g., `npm version 1.0.1 --no-git-tag-version`).
|
|
106
|
-
- Run `npm run lint` and `npm run test` to validate the workspace before publishing.
|
|
107
|
-
- Create the release artifact with `npm pack` (produces `project-compass-<version>.tgz` for uploading to GitHub Releases or npm).
|
|
108
|
-
- Tag the repo `git tag v<version>` and push both commits and tags to publish the release.
|
package/package.json
CHANGED
|
Binary file
|
package/src/cli.js
CHANGED
|
@@ -265,7 +265,7 @@ function Compass({rootPath}) {
|
|
|
265
265
|
const normalizedInput = input?.toLowerCase();
|
|
266
266
|
const ctrlCombo = (char) => key.ctrl && normalizedInput === char;
|
|
267
267
|
const shiftCombo = (char) => key.shift && normalizedInput === char;
|
|
268
|
-
const toggleShortcut = (char) =>
|
|
268
|
+
const toggleShortcut = (char) => shiftCombo(char);
|
|
269
269
|
if (toggleShortcut('h')) {
|
|
270
270
|
setShowHelpCards((prev) => !prev);
|
|
271
271
|
return;
|
|
@@ -306,11 +306,11 @@ function Compass({rootPath}) {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
if (key.shift && key.upArrow) {
|
|
309
|
-
scrollLogs(1);
|
|
309
|
+
scrollLogs(-1);
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
312
312
|
if (key.shift && key.downArrow) {
|
|
313
|
-
scrollLogs(
|
|
313
|
+
scrollLogs(1);
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
316
|
|
|
@@ -318,7 +318,7 @@ function Compass({rootPath}) {
|
|
|
318
318
|
setShowHelp((prev) => !prev);
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
|
-
if (
|
|
321
|
+
if (shiftCombo('l') && lastCommandRef.current) {
|
|
322
322
|
runProjectCommand(lastCommandRef.current.commandMeta, lastCommandRef.current.project);
|
|
323
323
|
return;
|
|
324
324
|
}
|
|
@@ -338,7 +338,7 @@ function Compass({rootPath}) {
|
|
|
338
338
|
setViewMode((prev) => (prev === 'detail' ? 'list' : 'detail'));
|
|
339
339
|
return;
|
|
340
340
|
}
|
|
341
|
-
if (
|
|
341
|
+
if (shiftCombo('q')) {
|
|
342
342
|
exit();
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
@@ -525,19 +525,19 @@ const projectRows = [];
|
|
|
525
525
|
color: 'magenta',
|
|
526
526
|
body: [
|
|
527
527
|
'β / β move the project focus',
|
|
528
|
-
'Enter
|
|
529
|
-
'Shift
|
|
530
|
-
'
|
|
528
|
+
'Enter toggles details view',
|
|
529
|
+
'Shift+β / β scroll output buffer',
|
|
530
|
+
'Shift+H toggles help cards'
|
|
531
531
|
]
|
|
532
532
|
},
|
|
533
533
|
{
|
|
534
534
|
label: 'Command flow',
|
|
535
535
|
color: 'cyan',
|
|
536
536
|
body: [
|
|
537
|
-
'B / T / R
|
|
538
|
-
'1-9 execute detail commands
|
|
539
|
-
'
|
|
540
|
-
'Ctrl+C aborts
|
|
537
|
+
'B / T / R run build/test/run',
|
|
538
|
+
'1-9 execute detail commands',
|
|
539
|
+
'Shift+L reruns last command',
|
|
540
|
+
'Ctrl+C aborts; type feeds stdin'
|
|
541
541
|
]
|
|
542
542
|
},
|
|
543
543
|
{
|
|
@@ -545,8 +545,9 @@ const projectRows = [];
|
|
|
545
545
|
color: 'yellow',
|
|
546
546
|
body: [
|
|
547
547
|
recentRuns.length ? `${recentRuns.length} runs recorded` : 'No runs yet Β· start with B/T/R',
|
|
548
|
-
'
|
|
549
|
-
'
|
|
548
|
+
'Shift+S toggles structure guide',
|
|
549
|
+
'C save custom action',
|
|
550
|
+
'Shift+Q quit application'
|
|
550
551
|
]
|
|
551
552
|
}
|
|
552
553
|
];
|
|
@@ -566,16 +567,17 @@ const projectRows = [];
|
|
|
566
567
|
marginBottom: 1,
|
|
567
568
|
borderStyle: 'round',
|
|
568
569
|
borderColor: card.color,
|
|
569
|
-
padding: 1
|
|
570
|
+
padding: 1,
|
|
571
|
+
flexDirection: 'column'
|
|
570
572
|
},
|
|
571
|
-
create(Text, {color: card.color, bold: true}, card.label),
|
|
573
|
+
create(Text, {color: card.color, bold: true, marginBottom: 1}, card.label),
|
|
572
574
|
...card.body.map((line, lineIndex) =>
|
|
573
575
|
create(Text, {key: `${card.label}-${lineIndex}`, dimColor: card.color === 'yellow'}, line)
|
|
574
576
|
)
|
|
575
577
|
)
|
|
576
578
|
)
|
|
577
579
|
)
|
|
578
|
-
: create(Text, {dimColor: true, marginTop: 1}, 'Help cards hidden Β· press H to show navigation, command flow, and recent runs.');
|
|
580
|
+
: create(Text, {dimColor: true, marginTop: 1}, 'Help cards hidden Β· press Shift+H to show navigation, command flow, and recent runs.');
|
|
579
581
|
|
|
580
582
|
const structureGuide = showStructureGuide
|
|
581
583
|
? create(
|
|
@@ -587,7 +589,7 @@ const projectRows = [];
|
|
|
587
589
|
marginTop: 1,
|
|
588
590
|
padding: 1
|
|
589
591
|
},
|
|
590
|
-
create(Text, {color: 'cyan', bold: true}, 'Project structure guide Β· press S to hide'),
|
|
592
|
+
create(Text, {color: 'cyan', bold: true}, 'Project structure guide Β· press Shift+S to hide'),
|
|
591
593
|
...SCHEMA_GUIDE.map((entry) =>
|
|
592
594
|
create(Text, {key: entry.type, dimColor: true}, `β’ ${entry.icon} ${entry.label}: ${entry.files.join(', ')}`)
|
|
593
595
|
),
|
|
@@ -606,18 +608,18 @@ const projectRows = [];
|
|
|
606
608
|
padding: 1
|
|
607
609
|
},
|
|
608
610
|
create(Text, {color: 'cyan', bold: true}, 'Help overlay Β· press ? to hide'),
|
|
609
|
-
create(Text, null, 'Shift
|
|
610
|
-
create(Text, null, '
|
|
611
|
-
create(Text, null, 'Ctrl+H toggles
|
|
612
|
-
create(Text, null, 'Projects + Details stay
|
|
613
|
-
create(Text, null, 'Structure guide lists the manifests that trigger each language detection (
|
|
611
|
+
create(Text, null, 'Shift+β/β scrolls the log buffer while commands stream; type to feed stdin (Enter submits, Ctrl+C aborts).'),
|
|
612
|
+
create(Text, null, 'B/T/R run build/test/run; 1-9 executes detail commands; Ctrl+L reruns the previous command.'),
|
|
613
|
+
create(Text, null, 'Ctrl+H toggles these help cards, Ctrl+S toggles the structure guide, ? toggles this overlay, Ctrl+Q quits.'),
|
|
614
|
+
create(Text, null, 'Projects + Details stay paired while Output keeps its own full-width band.'),
|
|
615
|
+
create(Text, null, 'Structure guide lists the manifests that trigger each language detection (Ctrl+S to toggle).')
|
|
614
616
|
)
|
|
615
617
|
: null;
|
|
616
618
|
|
|
617
|
-
const toggleHint = showHelpCards ? '
|
|
619
|
+
const toggleHint = showHelpCards ? 'Shift+H hides the help cards' : 'Shift+H shows the help cards';
|
|
618
620
|
const headerHint = viewMode === 'detail'
|
|
619
|
-
? `Detail mode Β· 1-${Math.max(detailedIndexed.length, 1)} to execute, C: add custom commands, Enter: back to list,
|
|
620
|
-
: `Quick run Β· B/T/R to build/test/run, Enter: view details,
|
|
621
|
+
? `Detail mode Β· 1-${Math.max(detailedIndexed.length, 1)} to execute, C: add custom commands, Enter: back to list, Shift+Q: quit Β· ${toggleHint}, Shift+S toggles structure guide`
|
|
622
|
+
: `Quick run Β· B/T/R to build/test/run, Enter: view details, Shift+Q: quit Β· ${toggleHint}, Shift+S toggles structure guide`;
|
|
621
623
|
|
|
622
624
|
return create(
|
|
623
625
|
Box,
|
|
@@ -702,7 +704,7 @@ const projectRows = [];
|
|
|
702
704
|
Box,
|
|
703
705
|
{marginTop: 1, flexDirection: 'row', justifyContent: 'space-between'},
|
|
704
706
|
create(Text, {dimColor: true}, running ? 'Type to feed stdin; Enter submits, Ctrl+C aborts.' : 'Run a command or press ? for extra help.'),
|
|
705
|
-
create(Text, {dimColor: true}, `${toggleHint}, S toggles the structure guide`)
|
|
707
|
+
create(Text, {dimColor: true}, `${toggleHint}, Shift+S toggles the structure guide`)
|
|
706
708
|
),
|
|
707
709
|
create(
|
|
708
710
|
Box,
|
|
@@ -711,9 +713,9 @@ const projectRows = [];
|
|
|
711
713
|
flexDirection: 'row',
|
|
712
714
|
borderStyle: 'round',
|
|
713
715
|
borderColor: running ? 'green' : 'gray',
|
|
714
|
-
|
|
716
|
+
paddingX: 1
|
|
715
717
|
},
|
|
716
|
-
create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? 'Stdin buffer' : 'Input ready'),
|
|
718
|
+
create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? ' Stdin buffer ' : ' Input ready '),
|
|
717
719
|
create(Text, {dimColor: true, marginLeft: 1}, running ? (stdinBuffer || '(type to send)') : 'Start a command to feed stdin')
|
|
718
720
|
)
|
|
719
721
|
),
|
package/src/projectDetection.js
CHANGED
|
@@ -5,6 +5,12 @@ import {ensureConfigDir, PLUGIN_FILE} from './configPaths.js';
|
|
|
5
5
|
|
|
6
6
|
const IGNORE_PATTERNS = ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**', '**/target/**'];
|
|
7
7
|
|
|
8
|
+
const PYTHON_ENTRY_FILES = ['main.py', 'app.py', 'src/main.py', 'src/app.py'];
|
|
9
|
+
|
|
10
|
+
function findPythonEntry(projectPath) {
|
|
11
|
+
return PYTHON_ENTRY_FILES.find((file) => hasProjectFile(projectPath, file)) || null;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
function hasProjectFile(projectPath, file) {
|
|
9
15
|
return fs.existsSync(path.join(projectPath, file));
|
|
10
16
|
}
|
|
@@ -316,10 +322,14 @@ const builtInFrameworks = [
|
|
|
316
322
|
languages: ['Python'],
|
|
317
323
|
priority: 105,
|
|
318
324
|
match(project) {
|
|
319
|
-
|
|
325
|
+
const entry = findPythonEntry(project.path);
|
|
326
|
+
return Boolean(entry && dependencyMatches(project, 'flask'));
|
|
320
327
|
},
|
|
321
328
|
commands(project) {
|
|
322
|
-
const entry =
|
|
329
|
+
const entry = findPythonEntry(project.path);
|
|
330
|
+
if (!entry) {
|
|
331
|
+
return {};
|
|
332
|
+
}
|
|
323
333
|
return {
|
|
324
334
|
run: {label: 'Flask app', command: ['python', entry], source: 'framework'},
|
|
325
335
|
test: {label: 'Pytest', command: ['pytest'], source: 'framework'}
|
|
@@ -334,12 +344,17 @@ const builtInFrameworks = [
|
|
|
334
344
|
languages: ['Python'],
|
|
335
345
|
priority: 105,
|
|
336
346
|
match(project) {
|
|
337
|
-
|
|
347
|
+
const entry = findPythonEntry(project.path);
|
|
348
|
+
return Boolean(entry && dependencyMatches(project, 'fastapi'));
|
|
338
349
|
},
|
|
339
350
|
commands(project) {
|
|
340
|
-
const entry =
|
|
351
|
+
const entry = findPythonEntry(project.path);
|
|
352
|
+
if (!entry) {
|
|
353
|
+
return {};
|
|
354
|
+
}
|
|
355
|
+
const moduleName = entry.split('.').slice(0, -1).join('.') || entry;
|
|
341
356
|
return {
|
|
342
|
-
run: {label: 'Uvicorn reload', command: ['uvicorn', `${
|
|
357
|
+
run: {label: 'Uvicorn reload', command: ['uvicorn', `${moduleName}:app`, '--reload'], source: 'framework'},
|
|
343
358
|
test: {label: 'Pytest', command: ['pytest'], source: 'framework'}
|
|
344
359
|
};
|
|
345
360
|
}
|
|
@@ -458,7 +473,7 @@ class SchemaRegistry {
|
|
|
458
473
|
commands.test = {label: 'Unittest', command: ['python', '-m', 'unittest', 'discover']};
|
|
459
474
|
}
|
|
460
475
|
|
|
461
|
-
const entry =
|
|
476
|
+
const entry = findPythonEntry(projectPath);
|
|
462
477
|
if (entry) {
|
|
463
478
|
commands.run = {label: 'Run', command: ['python', entry]};
|
|
464
479
|
}
|
|
@@ -492,15 +507,6 @@ class SchemaRegistry {
|
|
|
492
507
|
}
|
|
493
508
|
};
|
|
494
509
|
},
|
|
495
|
-
findPythonEntry(projectPath) {
|
|
496
|
-
const candidates = ['main.py', 'app.py', 'src/main.py', 'src/app.py'];
|
|
497
|
-
for (const candidate of candidates) {
|
|
498
|
-
if (hasProjectFile(projectPath, candidate)) {
|
|
499
|
-
return candidate;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return null;
|
|
503
|
-
}
|
|
504
510
|
},
|
|
505
511
|
{
|
|
506
512
|
type: 'rust',
|