project-compass 2.5.1 โ 2.7.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 +4 -2
- package/package.json +1 -1
- package/src/cli.js +123 -22
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Project Compass (v2.
|
|
1
|
+
# Project Compass (v2.6.0)
|
|
2
2
|
|
|
3
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
|
|
|
@@ -11,6 +11,7 @@ Project Compass is a futuristic CLI navigator built with [Ink](https://github.co
|
|
|
11
11
|
- ๐ง **Smart Detection**: Support for 20+ frameworks including **Spring Boot** (Maven/Gradle), **ASP.NET Core**, **Rocket/Actix** (Rust), **Laravel** (PHP), **Vite**, **Prisma**, and more.
|
|
12
12
|
- โ ๏ธ **Runtime Health**: Automatically checks if the required language/runtime (e.g., `node`, `python`, `cargo`) is installed and warns you if it's missing.
|
|
13
13
|
- ๐ **Omni-Studio**: A new interactive environment intelligence mode to see all installed runtimes and versions.
|
|
14
|
+
- ๐ **Log Management**: Clear output with **Shift+X** or export logs to a text file with **Shift+E**.
|
|
14
15
|
- ๐ **Extensible**: Add custom commands with **Shift+C** and frameworks via `plugins.json`.
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
@@ -35,7 +36,8 @@ project-compass [--dir /path/to/workspace] [--studio]
|
|
|
35
36
|
| **Shift+A** | Open **Omni-Studio** (Environment View) |
|
|
36
37
|
| **Shift+C** | Add a custom command (`label|cmd`) |
|
|
37
38
|
| **Shift+X** | **Clear output logs** |
|
|
38
|
-
| **Shift
|
|
39
|
+
| **Shift+E** | **Export logs to .txt** |
|
|
40
|
+
| **Shift โ / โ** | Scroll output buffer (Intuitive Direction) |
|
|
39
41
|
| **Shift+L** | Rerun last command |
|
|
40
42
|
| **Shift+H** | Toggle help cards |
|
|
41
43
|
| **Shift+S** | Toggle structure guide |
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -152,6 +152,27 @@ function Studio() {
|
|
|
152
152
|
);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function CursorText({value, cursorIndex, active = true}) {
|
|
156
|
+
const [visible, setVisible] = useState(true);
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (!active) return;
|
|
159
|
+
const interval = setInterval(() => setVisible(v => !v), 500);
|
|
160
|
+
return () => clearInterval(interval);
|
|
161
|
+
}, [active]);
|
|
162
|
+
|
|
163
|
+
const before = value.slice(0, cursorIndex);
|
|
164
|
+
const charAt = value[cursorIndex] || ' ';
|
|
165
|
+
const after = value.slice(cursorIndex + 1);
|
|
166
|
+
|
|
167
|
+
return create(
|
|
168
|
+
Text,
|
|
169
|
+
null,
|
|
170
|
+
before,
|
|
171
|
+
active && visible ? create(Text, {backgroundColor: 'white', color: 'black'}, charAt) : charAt,
|
|
172
|
+
after
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
155
176
|
function Compass({rootPath, initialView = 'navigator'}) {
|
|
156
177
|
const {exit} = useApp();
|
|
157
178
|
const {projects, loading, error} = useScanner(rootPath);
|
|
@@ -164,10 +185,12 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
164
185
|
const [lastAction, setLastAction] = useState(null);
|
|
165
186
|
const [customMode, setCustomMode] = useState(false);
|
|
166
187
|
const [customInput, setCustomInput] = useState('');
|
|
188
|
+
const [customCursor, setCustomCursor] = useState(0);
|
|
167
189
|
const [config, setConfig] = useState(() => loadConfig());
|
|
168
190
|
const [showHelpCards, setShowHelpCards] = useState(false);
|
|
169
191
|
const [showStructureGuide, setShowStructureGuide] = useState(false);
|
|
170
192
|
const [stdinBuffer, setStdinBuffer] = useState('');
|
|
193
|
+
const [stdinCursor, setStdinCursor] = useState(0);
|
|
171
194
|
const [showHelp, setShowHelp] = useState(false);
|
|
172
195
|
const [recentRuns, setRecentRuns] = useState([]);
|
|
173
196
|
const selectedProject = projects[selectedIndex] || null;
|
|
@@ -177,9 +200,9 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
177
200
|
const addLog = useCallback((line) => {
|
|
178
201
|
setLogLines((prev) => {
|
|
179
202
|
const normalized = typeof line === 'string' ? line : JSON.stringify(line);
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
return
|
|
203
|
+
const lines = normalized.split(/\r?\n/).filter(l => l.trim().length > 0);
|
|
204
|
+
const appended = [...prev, ...lines];
|
|
205
|
+
return appended.length > 500 ? appended.slice(appended.length - 500) : appended;
|
|
183
206
|
});
|
|
184
207
|
}, []);
|
|
185
208
|
|
|
@@ -228,10 +251,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
228
251
|
runningProcessRef.current = subprocess;
|
|
229
252
|
|
|
230
253
|
subprocess.stdout?.on('data', (chunk) => {
|
|
231
|
-
addLog(chunk.toString()
|
|
254
|
+
addLog(chunk.toString());
|
|
232
255
|
});
|
|
233
256
|
subprocess.stderr?.on('data', (chunk) => {
|
|
234
|
-
addLog(kleur.red(chunk.toString()
|
|
257
|
+
addLog(kleur.red(chunk.toString()));
|
|
235
258
|
});
|
|
236
259
|
|
|
237
260
|
await subprocess;
|
|
@@ -241,6 +264,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
241
264
|
} finally {
|
|
242
265
|
setRunning(false);
|
|
243
266
|
setStdinBuffer('');
|
|
267
|
+
setStdinCursor(0);
|
|
244
268
|
runningProcessRef.current = null;
|
|
245
269
|
}
|
|
246
270
|
}, [addLog, running, selectedProject]);
|
|
@@ -276,6 +300,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
276
300
|
addLog(kleur.gray('Canceled custom command (empty).'));
|
|
277
301
|
setCustomMode(false);
|
|
278
302
|
setCustomInput('');
|
|
303
|
+
setCustomCursor(0);
|
|
279
304
|
return;
|
|
280
305
|
}
|
|
281
306
|
const [labelPart, commandPart] = raw.split('|');
|
|
@@ -284,14 +309,29 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
284
309
|
addLog(kleur.red('Custom command needs at least one token.'));
|
|
285
310
|
setCustomMode(false);
|
|
286
311
|
setCustomInput('');
|
|
312
|
+
setCustomCursor(0);
|
|
287
313
|
return;
|
|
288
314
|
}
|
|
289
315
|
const label = commandPart ? labelPart.trim() : `Custom ${selectedProject.name}`;
|
|
290
316
|
handleAddCustomCommand(label || 'Custom', commandTokens);
|
|
291
317
|
setCustomMode(false);
|
|
292
318
|
setCustomInput('');
|
|
319
|
+
setCustomCursor(0);
|
|
293
320
|
}, [customInput, selectedProject, handleAddCustomCommand, addLog]);
|
|
294
321
|
|
|
322
|
+
const exportLogs = useCallback(() => {
|
|
323
|
+
if (!logLines.length) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const exportPath = path.resolve(process.cwd(), `compass-logs-${Date.now()}.txt`);
|
|
328
|
+
fs.writeFileSync(exportPath, logLines.join('\n'));
|
|
329
|
+
addLog(kleur.green(`โ Logs exported to ${exportPath}`));
|
|
330
|
+
} catch (err) {
|
|
331
|
+
addLog(kleur.red(`โ Export failed: ${err.message}`));
|
|
332
|
+
}
|
|
333
|
+
}, [logLines, addLog]);
|
|
334
|
+
|
|
295
335
|
useInput((input, key) => {
|
|
296
336
|
if (customMode) {
|
|
297
337
|
if (key.return) {
|
|
@@ -301,14 +341,27 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
301
341
|
if (key.escape) {
|
|
302
342
|
setCustomMode(false);
|
|
303
343
|
setCustomInput('');
|
|
344
|
+
setCustomCursor(0);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (key.backspace || key.delete) {
|
|
348
|
+
if (customCursor > 0) {
|
|
349
|
+
setCustomInput((prev) => prev.slice(0, customCursor - 1) + prev.slice(customCursor));
|
|
350
|
+
setCustomCursor(c => Math.max(0, c - 1));
|
|
351
|
+
}
|
|
304
352
|
return;
|
|
305
353
|
}
|
|
306
|
-
if (key.
|
|
307
|
-
|
|
354
|
+
if (key.leftArrow) {
|
|
355
|
+
setCustomCursor(c => Math.max(0, c - 1));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (key.rightArrow) {
|
|
359
|
+
setCustomCursor(c => Math.min(customInput.length, c + 1));
|
|
308
360
|
return;
|
|
309
361
|
}
|
|
310
362
|
if (input) {
|
|
311
|
-
setCustomInput((prev) => prev + input);
|
|
363
|
+
setCustomInput((prev) => prev.slice(0, customCursor) + input + prev.slice(customCursor));
|
|
364
|
+
setCustomCursor(c => c + input.length);
|
|
312
365
|
}
|
|
313
366
|
return;
|
|
314
367
|
}
|
|
@@ -334,6 +387,10 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
334
387
|
setLogOffset(0);
|
|
335
388
|
return;
|
|
336
389
|
}
|
|
390
|
+
if (shiftCombo('e')) {
|
|
391
|
+
exportLogs();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
337
394
|
|
|
338
395
|
const scrollLogs = (delta) => {
|
|
339
396
|
setLogOffset((prev) => {
|
|
@@ -346,21 +403,33 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
346
403
|
if (key.ctrl && input === 'c') {
|
|
347
404
|
runningProcessRef.current.kill('SIGINT');
|
|
348
405
|
setStdinBuffer('');
|
|
406
|
+
setStdinCursor(0);
|
|
349
407
|
return;
|
|
350
408
|
}
|
|
351
409
|
if (key.return) {
|
|
352
|
-
runningProcessRef.current.stdin?.write('\n');
|
|
410
|
+
runningProcessRef.current.stdin?.write(stdinBuffer + '\n');
|
|
353
411
|
setStdinBuffer('');
|
|
412
|
+
setStdinCursor(0);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (key.backspace || key.delete) {
|
|
416
|
+
if (stdinCursor > 0) {
|
|
417
|
+
setStdinBuffer(prev => prev.slice(0, stdinCursor - 1) + prev.slice(stdinCursor));
|
|
418
|
+
setStdinCursor(c => Math.max(0, c - 1));
|
|
419
|
+
}
|
|
354
420
|
return;
|
|
355
421
|
}
|
|
356
|
-
if (key.
|
|
357
|
-
|
|
358
|
-
|
|
422
|
+
if (key.leftArrow) {
|
|
423
|
+
setStdinCursor(c => Math.max(0, c - 1));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (key.rightArrow) {
|
|
427
|
+
setStdinCursor(c => Math.min(stdinBuffer.length, c + 1));
|
|
359
428
|
return;
|
|
360
429
|
}
|
|
361
430
|
if (input) {
|
|
362
|
-
|
|
363
|
-
|
|
431
|
+
setStdinBuffer(prev => prev.slice(0, stdinCursor) + input + prev.slice(stdinCursor));
|
|
432
|
+
setStdinCursor(c => c + input.length);
|
|
364
433
|
}
|
|
365
434
|
return;
|
|
366
435
|
}
|
|
@@ -405,6 +474,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
405
474
|
if (shiftCombo('c') && viewMode === 'detail' && selectedProject) {
|
|
406
475
|
setCustomMode(true);
|
|
407
476
|
setCustomInput('');
|
|
477
|
+
setCustomCursor(0);
|
|
408
478
|
return;
|
|
409
479
|
}
|
|
410
480
|
const actionKey = normalizedInput && ACTION_MAP[normalizedInput];
|
|
@@ -512,7 +582,14 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
512
582
|
}
|
|
513
583
|
|
|
514
584
|
if (customMode) {
|
|
515
|
-
detailContent.push(
|
|
585
|
+
detailContent.push(
|
|
586
|
+
create(
|
|
587
|
+
Box,
|
|
588
|
+
{key: 'custom-input-box', flexDirection: 'row'},
|
|
589
|
+
create(Text, {color: 'cyan'}, 'Type label|cmd (Enter: save, Esc: cancel): '),
|
|
590
|
+
create(CursorText, {value: customInput, cursorIndex: customCursor})
|
|
591
|
+
)
|
|
592
|
+
);
|
|
516
593
|
}
|
|
517
594
|
|
|
518
595
|
const artTileNodes = useMemo(() => {
|
|
@@ -618,7 +695,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
618
695
|
'B / T / R build/test/run',
|
|
619
696
|
'1-9 run detail commands',
|
|
620
697
|
'Shift+L rerun last command',
|
|
621
|
-
'Shift+X clear
|
|
698
|
+
'Shift+X clear / Shift+E export'
|
|
622
699
|
]
|
|
623
700
|
},
|
|
624
701
|
{
|
|
@@ -689,9 +766,9 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
689
766
|
padding: 1
|
|
690
767
|
},
|
|
691
768
|
create(Text, {color: 'cyan', bold: true}, 'Help overlay ยท press ? to hide'),
|
|
692
|
-
create(Text, null, 'Shift+โ/โ scrolls
|
|
693
|
-
create(Text, null, 'B/T/R run build/test/run; 1-9
|
|
694
|
-
create(Text, null, 'Shift+H
|
|
769
|
+
create(Text, null, 'Shift+โ/โ scrolls logs; Shift+X clears; Shift+E exports to file; Shift+A Omni-Studio.'),
|
|
770
|
+
create(Text, null, 'B/T/R run build/test/run; 1-9 detail commands; Shift+L reruns previous command.'),
|
|
771
|
+
create(Text, null, 'Shift+H help cards, Shift+S structure guide, ? overlay, Shift+Q quits.'),
|
|
695
772
|
create(Text, null, 'Projects + Details stay paired while Output keeps its own full-width band.'),
|
|
696
773
|
create(Text, null, 'Structure guide lists the manifests that trigger each language detection.')
|
|
697
774
|
)
|
|
@@ -801,7 +878,7 @@ function Compass({rootPath, initialView = 'navigator'}) {
|
|
|
801
878
|
paddingX: 1
|
|
802
879
|
},
|
|
803
880
|
create(Text, {bold: true, color: running ? 'green' : 'white'}, running ? ' Stdin buffer ' : ' Input ready '),
|
|
804
|
-
create(
|
|
881
|
+
create(Box, {marginLeft: 1}, create(CursorText, {value: stdinBuffer || (running ? '' : 'Start a command to feed stdin'), cursorIndex: stdinCursor, active: running}))
|
|
805
882
|
)
|
|
806
883
|
),
|
|
807
884
|
helpSection,
|
|
@@ -834,8 +911,32 @@ function parseArgs() {
|
|
|
834
911
|
async function main() {
|
|
835
912
|
const args = parseArgs();
|
|
836
913
|
if (args.help) {
|
|
837
|
-
console.log('Project Compass ยท Ink project runner');
|
|
838
|
-
console.log('
|
|
914
|
+
console.log(kleur.cyan('Project Compass ยท Ink project navigator/runner'));
|
|
915
|
+
console.log('');
|
|
916
|
+
console.log(kleur.bold('Usage:'));
|
|
917
|
+
console.log(' project-compass [--dir <path>] [--studio]');
|
|
918
|
+
console.log('');
|
|
919
|
+
console.log(kleur.bold('Arguments:'));
|
|
920
|
+
console.log(' --dir, --path <path> Specify root workspace directory to scan');
|
|
921
|
+
console.log(' --studio Launch directly into Omni-Studio mode');
|
|
922
|
+
console.log(' --help, -h Show this help menu');
|
|
923
|
+
console.log('');
|
|
924
|
+
console.log(kleur.bold('Core Keybinds:'));
|
|
925
|
+
console.log(' โ / โ Move project focus');
|
|
926
|
+
console.log(' Enter Toggle detail view for selected project');
|
|
927
|
+
console.log(' Shift+A Switch to Omni-Studio (Environment Health)');
|
|
928
|
+
console.log(' Shift+X Clear the output log buffer');
|
|
929
|
+
console.log(' Shift+E Export current logs to a .txt file');
|
|
930
|
+
console.log(' Shift+โ / โ Scroll the output logs back/forward');
|
|
931
|
+
console.log(' Shift+Q Quit application');
|
|
932
|
+
console.log('');
|
|
933
|
+
console.log(kleur.bold('Execution shortcuts:'));
|
|
934
|
+
console.log(' B / T / R Quick run: Build / Test / Run');
|
|
935
|
+
console.log(' 1-9 Run numbered commands in detail view');
|
|
936
|
+
console.log(' Shift+L Rerun the last executed command');
|
|
937
|
+
console.log(' Shift+C Add a custom command (in detail view)');
|
|
938
|
+
console.log('');
|
|
939
|
+
console.log(kleur.dim('Documentation: https://github.com/CrimsonDevil333333/project-compass'));
|
|
839
940
|
return;
|
|
840
941
|
}
|
|
841
942
|
const rootPath = args.root ? path.resolve(args.root) : process.cwd();
|