project-compass 2.0.2 → 2.2.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 CHANGED
@@ -1,18 +1,15 @@
1
- # Project Compass
1
+ # Project Compass (v2.2.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 keystroke access to build, test, or run them.
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 by looking at their manifest files.
8
- - 🎨 Combines the glyph-based art board with a Projects/Details row that keeps everything inside the viewport, while live stdout/stderr logs stay in their own band below.
9
- - 🚀 Press **Enter** on any project to open the detail view, where you can inspect the type, manifest, frameworks, commands, and save custom actions.
10
- - 💡 A dedicated output row with an interactive stdin buffer, plus help tiles that stay in their own band; Shift+↑/↓ scrolls the log window, typing feeds stdin (buffer shows what you type, Enter submits, Ctrl+C aborts), Ctrl+L reruns the last command, Ctrl+H shows/hides the help cards, Ctrl+S opens the structure guide, and `?` pops an overlay with quick tips.
11
- - 🎯 Built-in shortcuts (B/T/R) run the canonical build/test/run workflow, while numeric hotkeys (1, 2, 3...) execute whichever command is listed in the detail view.
12
- - 🧠 Modular detection now scans for Node, Python, Rust, Go, Java, Scala, PHP, Ruby, .NET, shell/Makefile projects plus custom workspaces, and surface setup hints (npm install, pip install, cargo fetch, etc.).
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 15+ frameworks (Vite, Prisma, Tailwind, etc.) with specialized build/run commands and setup hints.
12
+ - 🔌 **Extensible**: Add custom commands with **Shift+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 guide
26
+ ### Keyboard Guide
30
27
 
31
28
  | Key | Action |
32
29
  | --- | --- |
33
- | ↑ / ↓ | Move the project focus, Enter toggles the detail view |
34
- | B / T / R | Run the workspace build/test/run shortcuts |
35
- | 1‑9 | Execute the numbered commands listed in detail view |
36
- | C | Add a custom command (`label|cmd`) that saves to `~/.project-compass/config.json` |
37
- | Shift ↑ / | Scroll the output buffer |
38
- | Ctrl+L | Rerun the most recent command |
39
- | ? | Toggle the help overlay with navigation tips |
40
- | Ctrl+H | Show/hide the navigation/help cards beneath the logs |
41
- | Ctrl+S | Toggle the structure guide listing the manifests per language |
42
- | Ctrl+Q | Quit the app |
43
- | Typing while running | Sends stdin to the running command (buffer shows the text, Enter submits, Ctrl+C aborts) |
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
+ | **Shift+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
- ## Framework & plugin support
42
+ ## Layout & UX
47
43
 
48
- Project Compass detects a wide range of modern stacks—**Next.js**, **React**, **Vue**, **NestJS**, **Angular**, **SvelteKit**, **Nuxt**, **Astro**, **Django**, **Flask**, **FastAPI**, and **Spring Boot**—and shows their badges in the detail view. When a framework is recognized, it injects framework-specific build/run/test commands (e.g., Next dev/build, Django runserver/test, Spring Boot run/test).
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
- You can teach it new frameworks by adding a `plugins.json` file in your config directory (`~/.project-compass/plugins.json`). Each entry can declare the languages, files, dependencies, and commands that identify the framework. A sample plugin entry looks like this:
46
+ ## Frameworks
51
47
 
52
- ```json
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 `Ctrl+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 `Ctrl+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**, **Vite**, **Prisma**, **Tailwind**, 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-compass",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "Ink-based project explorer that detects local repos and lets you build/test/run them without memorizing commands.",
5
5
  "main": "src/cli.js",
6
6
  "type": "module",
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) => ctrlCombo(char) || shiftCombo(char);
268
+ const toggleShortcut = (char) => shiftCombo(char);
269
269
  if (toggleShortcut('h')) {
270
270
  setShowHelpCards((prev) => !prev);
271
271
  return;
@@ -318,7 +318,7 @@ function Compass({rootPath}) {
318
318
  setShowHelp((prev) => !prev);
319
319
  return;
320
320
  }
321
- if (ctrlCombo('l') && lastCommandRef.current) {
321
+ if (shiftCombo('l') && lastCommandRef.current) {
322
322
  runProjectCommand(lastCommandRef.current.commandMeta, lastCommandRef.current.project);
323
323
  return;
324
324
  }
@@ -338,11 +338,11 @@ function Compass({rootPath}) {
338
338
  setViewMode((prev) => (prev === 'detail' ? 'list' : 'detail'));
339
339
  return;
340
340
  }
341
- if (ctrlCombo('q')) {
341
+ if (shiftCombo('q')) {
342
342
  exit();
343
343
  return;
344
344
  }
345
- if (normalizedInput === 'c' && viewMode === 'detail' && selectedProject) {
345
+ if (shiftCombo('c') && viewMode === 'detail' && selectedProject) {
346
346
  setCustomMode(true);
347
347
  setCustomInput('');
348
348
  return;
@@ -412,19 +412,19 @@ const projectRows = [];
412
412
  detailContent.push(create(Text, {bold: true, marginTop: 1}, 'Commands'));
413
413
  detailedIndexed.forEach((command) => {
414
414
  detailContent.push(
415
- create(Text, {key: `detail-${command.shortcut}-${command.label}`}, `${command.shortcut}. ${command.label} ${command.source === 'custom' ? kleur.magenta('(custom)') : command.source === 'framework' ? kleur.cyan('(framework)') : ''}`)
415
+ create(Text, {key: `detail-${command.shortcut}-${command.label}`}, `${command.shortcut}. ${command.label} ${command.source === 'custom' ? kleur.magenta('(custom)') : command.source === 'framework' ? kleur.cyan('(framework)') : command.source === 'plugin' ? kleur.green('(plugin)') : ''}`)
416
416
  );
417
417
  detailContent.push(create(Text, {dimColor: true}, ` ↳ ${command.command.join(' ')}`));
418
418
  });
419
419
  if (!detailedIndexed.length) {
420
- detailContent.push(create(Text, {dimColor: true}, 'No built-in commands yet. Add a custom command with C.'));
420
+ detailContent.push(create(Text, {dimColor: true}, 'No built-in commands yet. Add a custom command with Shift+C.'));
421
421
  }
422
422
  const setupHints = selectedProject.extra?.setupHints || [];
423
423
  if (setupHints.length) {
424
424
  detailContent.push(create(Text, {dimColor: true, marginTop: 1}, 'Setup hints:'));
425
425
  setupHints.forEach((hint) => detailContent.push(create(Text, {dimColor: true}, ` • ${hint}`)));
426
426
  }
427
- detailContent.push(create(Text, {dimColor: true}, 'Press C → label|cmd to save custom actions, Enter to close detail view.'));
427
+ detailContent.push(create(Text, {dimColor: true}, 'Press Shift+C → label|cmd to save custom actions, Enter to close detail view.'));
428
428
  } else {
429
429
  detailContent.push(create(Text, {dimColor: true}, 'Press Enter on a project to reveal details (icons, commands, frameworks, custom actions).'));
430
430
  }
@@ -524,19 +524,20 @@ const projectRows = [];
524
524
  label: 'Navigation',
525
525
  color: 'magenta',
526
526
  body: [
527
- '↑ / ↓ move the project focus, Enter toggles details',
528
- 'Shift+↑ / Shift+↓ scroll only the output buffer',
529
- 'Ctrl+H toggles cards, ? opens the overlay'
527
+ '↑ / ↓ move the project focus',
528
+ 'Enter toggles details view',
529
+ 'Shift+↑ / scroll output buffer',
530
+ 'Shift+H toggles help cards'
530
531
  ]
531
532
  },
532
533
  {
533
534
  label: 'Command flow',
534
535
  color: 'cyan',
535
536
  body: [
536
- 'B / T / R trigger build/test/run shortcuts',
537
- '1-9 execute detail commands in order',
538
- 'Ctrl+L reruns the last command you launched',
539
- 'Ctrl+C aborts; typing feeds stdin (buffer mirrors it)'
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'
540
541
  ]
541
542
  },
542
543
  {
@@ -544,8 +545,9 @@ const projectRows = [];
544
545
  color: 'yellow',
545
546
  body: [
546
547
  recentRuns.length ? `${recentRuns.length} runs recorded` : 'No runs yet · start with B/T/R',
547
- 'Ctrl+S toggles the structure guide when unsure',
548
- 'C label|cmd saves a custom action'
548
+ 'Shift+S toggles structure guide',
549
+ 'Shift+C save custom action',
550
+ 'Shift+Q quit application'
549
551
  ]
550
552
  }
551
553
  ];
@@ -565,16 +567,17 @@ const projectRows = [];
565
567
  marginBottom: 1,
566
568
  borderStyle: 'round',
567
569
  borderColor: card.color,
568
- padding: 1
570
+ padding: 1,
571
+ flexDirection: 'column'
569
572
  },
570
- create(Text, {color: card.color, bold: true}, card.label),
573
+ create(Text, {color: card.color, bold: true, marginBottom: 1}, card.label),
571
574
  ...card.body.map((line, lineIndex) =>
572
575
  create(Text, {key: `${card.label}-${lineIndex}`, dimColor: card.color === 'yellow'}, line)
573
576
  )
574
577
  )
575
578
  )
576
579
  )
577
- : create(Text, {dimColor: true, marginTop: 1}, 'Help cards hidden · press Ctrl+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.');
578
581
 
579
582
  const structureGuide = showStructureGuide
580
583
  ? create(
@@ -586,7 +589,7 @@ const projectRows = [];
586
589
  marginTop: 1,
587
590
  padding: 1
588
591
  },
589
- create(Text, {color: 'cyan', bold: true}, 'Project structure guide · press Ctrl+S to hide'),
592
+ create(Text, {color: 'cyan', bold: true}, 'Project structure guide · press Shift+S to hide'),
590
593
  ...SCHEMA_GUIDE.map((entry) =>
591
594
  create(Text, {key: entry.type, dimColor: true}, `• ${entry.icon} ${entry.label}: ${entry.files.join(', ')}`)
592
595
  ),
@@ -613,10 +616,10 @@ const projectRows = [];
613
616
  )
614
617
  : null;
615
618
 
616
- const toggleHint = showHelpCards ? 'Ctrl+H hides the help cards' : 'Ctrl+H shows the help cards';
619
+ const toggleHint = showHelpCards ? 'Shift+H hides the help cards' : 'Shift+H shows the help cards';
617
620
  const headerHint = viewMode === 'detail'
618
- ? `Detail mode · 1-${Math.max(detailedIndexed.length, 1)} to execute, C: add custom commands, Enter: back to list, Ctrl+Q: quit · ${toggleHint}, Ctrl+S toggles structure guide`
619
- : `Quick run · B/T/R to build/test/run, Enter: view details, Ctrl+Q: quit · ${toggleHint}, Ctrl+S toggles structure guide`;
621
+ ? `Detail mode · 1-${Math.max(detailedIndexed.length, 1)} to execute, Shift+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`;
620
623
 
621
624
  return create(
622
625
  Box,
@@ -701,7 +704,7 @@ const projectRows = [];
701
704
  Box,
702
705
  {marginTop: 1, flexDirection: 'row', justifyContent: 'space-between'},
703
706
  create(Text, {dimColor: true}, running ? 'Type to feed stdin; Enter submits, Ctrl+C aborts.' : 'Run a command or press ? for extra help.'),
704
- create(Text, {dimColor: true}, `${toggleHint}, Ctrl+S toggles the structure guide`)
707
+ create(Text, {dimColor: true}, `${toggleHint}, Shift+S toggles the structure guide`)
705
708
  ),
706
709
  create(
707
710
  Box,
@@ -710,9 +713,9 @@ const projectRows = [];
710
713
  flexDirection: 'row',
711
714
  borderStyle: 'round',
712
715
  borderColor: running ? 'green' : 'gray',
713
- padding: 1
716
+ paddingX: 1
714
717
  },
715
- 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 '),
716
719
  create(Text, {dimColor: true, marginLeft: 1}, running ? (stdinBuffer || '(type to send)') : 'Start a command to feed stdin')
717
720
  )
718
721
  ),
@@ -323,7 +323,7 @@ const builtInFrameworks = [
323
323
  priority: 105,
324
324
  match(project) {
325
325
  const entry = findPythonEntry(project.path);
326
- return Boolean(entry && dependencyMatches(project, 'flask'));
326
+ return Boolean(entry && (dependencyMatches(project, 'flask') || dependencyMatches(project, 'flask-restful') || dependencyMatches(project, 'flask-cors')));
327
327
  },
328
328
  commands(project) {
329
329
  const entry = findPythonEntry(project.path);
@@ -345,7 +345,7 @@ const builtInFrameworks = [
345
345
  priority: 105,
346
346
  match(project) {
347
347
  const entry = findPythonEntry(project.path);
348
- return Boolean(entry && dependencyMatches(project, 'fastapi'));
348
+ return Boolean(entry && (dependencyMatches(project, 'fastapi') || dependencyMatches(project, 'pydantic') || dependencyMatches(project, 'uvicorn')));
349
349
  },
350
350
  commands(project) {
351
351
  const entry = findPythonEntry(project.path);
@@ -359,6 +359,59 @@ const builtInFrameworks = [
359
359
  };
360
360
  }
361
361
  },
362
+ {
363
+ id: 'vite',
364
+ name: 'Vite',
365
+ icon: '⚡',
366
+ description: 'Vite-powered frontend',
367
+ languages: ['Node.js'],
368
+ priority: 100,
369
+ match(project) {
370
+ return hasProjectFile(project.path, 'vite.config.js') || hasProjectFile(project.path, 'vite.config.ts') || dependencyMatches(project, 'vite');
371
+ },
372
+ commands(project) {
373
+ const commands = {};
374
+ const add = (key, label, fallback) => {
375
+ const tokens = resolveScriptCommand(project, key, fallback);
376
+ if (tokens) {
377
+ commands[key] = {label, command: tokens, source: 'framework'};
378
+ }
379
+ };
380
+ add('run', 'Vite dev', () => ['npx', 'vite']);
381
+ add('build', 'Vite build', () => ['npx', 'vite', 'build']);
382
+ add('preview', 'Vite preview', () => ['npx', 'vite', 'preview']);
383
+ return commands;
384
+ }
385
+ },
386
+ {
387
+ id: 'tailwind',
388
+ name: 'Tailwind CSS',
389
+ icon: '🎨',
390
+ description: 'Tailwind utility-first CSS',
391
+ languages: ['Node.js'],
392
+ priority: 50,
393
+ match(project) {
394
+ return hasProjectFile(project.path, 'tailwind.config.js') || hasProjectFile(project.path, 'tailwind.config.ts') || dependencyMatches(project, 'tailwindcss');
395
+ },
396
+ commands() { return {}; }
397
+ },
398
+ {
399
+ id: 'prisma',
400
+ name: 'Prisma',
401
+ icon: '◮',
402
+ description: 'Prisma ORM',
403
+ languages: ['Node.js'],
404
+ priority: 50,
405
+ match(project) {
406
+ return hasProjectFile(project.path, 'prisma/schema.prisma') || dependencyMatches(project, '@prisma/client');
407
+ },
408
+ commands() {
409
+ return {
410
+ generate: {label: 'Prisma generate', command: ['npx', 'prisma', 'generate'], source: 'framework'},
411
+ studio: {label: 'Prisma studio', command: ['npx', 'prisma', 'studio'], source: 'framework'}
412
+ };
413
+ }
414
+ },
362
415
  {
363
416
  id: 'spring',
364
417
  name: 'Spring Boot',