projax 1.3.10 → 1.3.12

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 (34) hide show
  1. package/dist/api/database.d.ts +1 -0
  2. package/dist/api/database.d.ts.map +1 -1
  3. package/dist/api/database.js +19 -1
  4. package/dist/api/database.js.map +1 -1
  5. package/dist/api/package.json +9 -8
  6. package/dist/api/routes/projects.d.ts.map +1 -1
  7. package/dist/api/routes/projects.js +21 -7
  8. package/dist/api/routes/projects.js.map +1 -1
  9. package/dist/api/types.d.ts +1 -0
  10. package/dist/api/types.d.ts.map +1 -1
  11. package/dist/core/database.d.ts +1 -0
  12. package/dist/electron/core/database.d.ts +1 -0
  13. package/dist/electron/main.js +37 -40
  14. package/dist/electron/preload.d.ts +1 -0
  15. package/dist/electron/preload.js +1 -0
  16. package/dist/electron/renderer/assets/index-59AhiV_K.css +1 -0
  17. package/dist/electron/renderer/assets/index-DAfjuYKX.js +61 -0
  18. package/dist/electron/renderer/index.html +2 -2
  19. package/dist/index.js +375 -1
  20. package/package.json +15 -3
  21. package/dist/core.d.ts +0 -2
  22. package/dist/core.js +0 -76
  23. package/dist/electron/renderer/assets/index-7KIJIiIM.js +0 -42
  24. package/dist/electron/renderer/assets/index-BTFVUZ9M.js +0 -46
  25. package/dist/electron/renderer/assets/index-BwyNdCvJ.js +0 -46
  26. package/dist/electron/renderer/assets/index-Cs0biDoD.css +0 -1
  27. package/dist/electron/renderer/assets/index-D5r8Ki4V.js +0 -46
  28. package/dist/electron/renderer/assets/index-D8AivBJO.js +0 -42
  29. package/dist/electron/renderer/assets/index-DtjxFwRT.css +0 -1
  30. package/dist/electron/renderer/assets/index-FdqNz85q.css +0 -1
  31. package/dist/electron/renderer/assets/index-ezVMxZrM.css +0 -1
  32. package/dist/prxi.d.ts +0 -2
  33. package/dist/prxi.js +0 -297
  34. package/dist/prxi.mjs +0 -34059
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' http://localhost:* ws://localhost:*;" />
7
7
  <title>projax</title>
8
- <script type="module" crossorigin src="./assets/index-D5r8Ki4V.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-DtjxFwRT.css">
8
+ <script type="module" crossorigin src="./assets/index-DAfjuYKX.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-59AhiV_K.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/dist/index.js CHANGED
@@ -249,6 +249,8 @@ program
249
249
  .description('Add a project to the dashboard')
250
250
  .argument('[path]', 'Path to the project directory')
251
251
  .option('-n, --name <name>', 'Custom name for the project (defaults to directory name)')
252
+ .option('-d, --description <description>', 'Description for the project')
253
+ .option('--tags <tags>', 'Comma-separated list of tags')
252
254
  .action(async (projectPath, options) => {
253
255
  try {
254
256
  let finalPath = projectPath;
@@ -316,8 +318,25 @@ program
316
318
  projectName = nameAnswer.name.trim();
317
319
  }
318
320
  const project = db.addProject(projectName, resolvedPath);
321
+ // Update description and tags if provided
322
+ const updates = {};
323
+ if (options?.description !== undefined) {
324
+ updates.description = options.description.trim() || null;
325
+ }
326
+ if (options?.tags) {
327
+ updates.tags = options.tags.split(',').map(t => t.trim()).filter(Boolean);
328
+ }
329
+ if (Object.keys(updates).length > 0) {
330
+ db.updateProject(project.id, updates);
331
+ }
319
332
  console.log(`✓ Added project: ${project.name} (ID: ${project.id})`);
320
333
  console.log(` Path: ${project.path}`);
334
+ if (options?.description) {
335
+ console.log(` Description: ${options.description}`);
336
+ }
337
+ if (options?.tags) {
338
+ console.log(` Tags: ${options.tags}`);
339
+ }
321
340
  // Ask if user wants to scan for tests
322
341
  const inquirer = (await Promise.resolve().then(() => __importStar(require('inquirer')))).default;
323
342
  const scanAnswer = await inquirer.prompt([
@@ -570,6 +589,262 @@ program
570
589
  process.exit(1);
571
590
  }
572
591
  });
592
+ // Description command
593
+ program
594
+ .command('desc')
595
+ .alias('description')
596
+ .description('Set or get project description')
597
+ .argument('<project>', 'Project ID or name')
598
+ .argument('[description]', 'New description (leave empty to view current)')
599
+ .action((projectIdentifier, description) => {
600
+ try {
601
+ const db = (0, core_bridge_1.getDatabaseManager)();
602
+ const projects = (0, core_bridge_1.getAllProjects)();
603
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
604
+ if (!project) {
605
+ console.error(`Error: Project not found: ${projectIdentifier}`);
606
+ process.exit(1);
607
+ }
608
+ if (description === undefined) {
609
+ // Show current description
610
+ if (project.description) {
611
+ console.log(project.description);
612
+ }
613
+ else {
614
+ console.log('No description set.');
615
+ }
616
+ }
617
+ else {
618
+ // Update description
619
+ const trimmedDesc = description.trim() || null;
620
+ db.updateProject(project.id, { description: trimmedDesc });
621
+ if (trimmedDesc) {
622
+ console.log(`✓ Updated description for "${project.name}": ${trimmedDesc}`);
623
+ }
624
+ else {
625
+ console.log(`✓ Removed description for "${project.name}"`);
626
+ }
627
+ }
628
+ }
629
+ catch (error) {
630
+ console.error('Error managing description:', error instanceof Error ? error.message : error);
631
+ process.exit(1);
632
+ }
633
+ });
634
+ // Tags command
635
+ program
636
+ .command('tags')
637
+ .description('Manage project tags')
638
+ .argument('<project>', 'Project ID or name')
639
+ .argument('[action]', 'Action: add, remove, or list (default: list)', 'list')
640
+ .argument('[tag]', 'Tag name (required for add/remove)')
641
+ .action((projectIdentifier, action = 'list', tag) => {
642
+ try {
643
+ const db = (0, core_bridge_1.getDatabaseManager)();
644
+ const projects = (0, core_bridge_1.getAllProjects)();
645
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
646
+ if (!project) {
647
+ console.error(`Error: Project not found: ${projectIdentifier}`);
648
+ process.exit(1);
649
+ }
650
+ const currentTags = project.tags || [];
651
+ if (action === 'list' || !action) {
652
+ // List tags
653
+ if (currentTags.length === 0) {
654
+ console.log('No tags set for this project.');
655
+ }
656
+ else {
657
+ console.log(`Tags for "${project.name}":`);
658
+ currentTags.forEach(t => console.log(` - ${t}`));
659
+ }
660
+ }
661
+ else if (action === 'add') {
662
+ if (!tag || !tag.trim()) {
663
+ console.error('Error: Tag name is required for add action');
664
+ process.exit(1);
665
+ }
666
+ const newTag = tag.trim();
667
+ if (currentTags.includes(newTag)) {
668
+ console.log(`Tag "${newTag}" already exists for "${project.name}"`);
669
+ }
670
+ else {
671
+ db.updateProject(project.id, { tags: [...currentTags, newTag] });
672
+ console.log(`✓ Added tag "${newTag}" to "${project.name}"`);
673
+ }
674
+ }
675
+ else if (action === 'remove') {
676
+ if (!tag || !tag.trim()) {
677
+ console.error('Error: Tag name is required for remove action');
678
+ process.exit(1);
679
+ }
680
+ const tagToRemove = tag.trim();
681
+ if (!currentTags.includes(tagToRemove)) {
682
+ console.log(`Tag "${tagToRemove}" does not exist for "${project.name}"`);
683
+ }
684
+ else {
685
+ db.updateProject(project.id, { tags: currentTags.filter(t => t !== tagToRemove) });
686
+ console.log(`✓ Removed tag "${tagToRemove}" from "${project.name}"`);
687
+ }
688
+ }
689
+ else {
690
+ console.error(`Error: Unknown action "${action}". Use: list, add, or remove`);
691
+ process.exit(1);
692
+ }
693
+ }
694
+ catch (error) {
695
+ console.error('Error managing tags:', error instanceof Error ? error.message : error);
696
+ process.exit(1);
697
+ }
698
+ });
699
+ // Open command - open project in editor
700
+ program
701
+ .command('open')
702
+ .description('Open project in editor')
703
+ .argument('<project>', 'Project ID or name')
704
+ .action(async (projectIdentifier) => {
705
+ try {
706
+ const projects = (0, core_bridge_1.getAllProjects)();
707
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
708
+ if (!project) {
709
+ console.error(`Error: Project not found: ${projectIdentifier}`);
710
+ process.exit(1);
711
+ }
712
+ // Load settings from core
713
+ const corePath = path.join(__dirname, '..', '..', 'core', 'dist', 'settings');
714
+ const localCorePath = path.join(__dirname, '..', '..', '..', 'core', 'dist', 'settings');
715
+ let settings;
716
+ try {
717
+ settings = require(corePath);
718
+ }
719
+ catch {
720
+ try {
721
+ settings = require(localCorePath);
722
+ }
723
+ catch {
724
+ console.error('Error: Could not load settings');
725
+ process.exit(1);
726
+ }
727
+ }
728
+ const editorSettings = settings.getEditorSettings();
729
+ let command;
730
+ const args = [project.path];
731
+ if (editorSettings.type === 'custom' && editorSettings.customPath) {
732
+ command = editorSettings.customPath;
733
+ }
734
+ else {
735
+ switch (editorSettings.type) {
736
+ case 'vscode':
737
+ command = 'code';
738
+ break;
739
+ case 'cursor':
740
+ command = 'cursor';
741
+ break;
742
+ case 'windsurf':
743
+ command = 'windsurf';
744
+ break;
745
+ case 'zed':
746
+ command = 'zed';
747
+ break;
748
+ default:
749
+ command = 'code';
750
+ }
751
+ }
752
+ const { spawn } = require('child_process');
753
+ spawn(command, args, {
754
+ detached: true,
755
+ stdio: 'ignore',
756
+ }).unref();
757
+ console.log(`✓ Opening "${project.name}" in ${editorSettings.type || 'editor'}...`);
758
+ }
759
+ catch (error) {
760
+ console.error('Error opening project:', error instanceof Error ? error.message : error);
761
+ process.exit(1);
762
+ }
763
+ });
764
+ // Files command - open project directory
765
+ program
766
+ .command('files')
767
+ .description('Open project directory in file manager')
768
+ .argument('<project>', 'Project ID or name')
769
+ .action((projectIdentifier) => {
770
+ try {
771
+ const projects = (0, core_bridge_1.getAllProjects)();
772
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
773
+ if (!project) {
774
+ console.error(`Error: Project not found: ${projectIdentifier}`);
775
+ process.exit(1);
776
+ }
777
+ let command;
778
+ const args = [project.path];
779
+ if (os.platform() === 'darwin') {
780
+ command = 'open';
781
+ }
782
+ else if (os.platform() === 'win32') {
783
+ command = 'explorer';
784
+ }
785
+ else {
786
+ command = 'xdg-open';
787
+ }
788
+ const { spawn } = require('child_process');
789
+ spawn(command, args, {
790
+ detached: true,
791
+ stdio: 'ignore',
792
+ }).unref();
793
+ console.log(`✓ Opening "${project.name}" directory...`);
794
+ }
795
+ catch (error) {
796
+ console.error('Error opening directory:', error instanceof Error ? error.message : error);
797
+ process.exit(1);
798
+ }
799
+ });
800
+ // URLs command - list detected URLs for a project
801
+ program
802
+ .command('urls')
803
+ .description('List detected URLs for a project')
804
+ .argument('<project>', 'Project ID or name')
805
+ .action(async (projectIdentifier) => {
806
+ try {
807
+ const projects = (0, core_bridge_1.getAllProjects)();
808
+ const project = projects.find(p => p.id.toString() === projectIdentifier || p.name === projectIdentifier);
809
+ if (!project) {
810
+ console.error(`Error: Project not found: ${projectIdentifier}`);
811
+ process.exit(1);
812
+ }
813
+ const urls = new Set();
814
+ // Get URLs from running processes
815
+ const { getRunningProcessesClean } = await Promise.resolve().then(() => __importStar(require('./script-runner')));
816
+ const runningProcesses = await getRunningProcessesClean();
817
+ const projectProcesses = runningProcesses.filter((p) => p.projectPath === project.path);
818
+ for (const process of projectProcesses) {
819
+ if (process.detectedUrls && Array.isArray(process.detectedUrls)) {
820
+ for (const url of process.detectedUrls) {
821
+ urls.add(url);
822
+ }
823
+ }
824
+ }
825
+ // Get URLs from detected ports
826
+ const db = (0, core_bridge_1.getDatabaseManager)();
827
+ const projectPorts = db.getProjectPorts(project.id);
828
+ for (const portInfo of projectPorts) {
829
+ const url = `http://localhost:${portInfo.port}`;
830
+ urls.add(url);
831
+ }
832
+ const urlArray = Array.from(urls).sort();
833
+ if (urlArray.length === 0) {
834
+ console.log(`No URLs detected for "${project.name}"`);
835
+ }
836
+ else {
837
+ console.log(`\nDetected URLs for "${project.name}":`);
838
+ urlArray.forEach((url, idx) => {
839
+ console.log(` ${idx + 1}. ${url}`);
840
+ });
841
+ }
842
+ }
843
+ catch (error) {
844
+ console.error('Error listing URLs:', error instanceof Error ? error.message : error);
845
+ process.exit(1);
846
+ }
847
+ });
573
848
  // Remove command
574
849
  program
575
850
  .command('remove')
@@ -1056,6 +1331,105 @@ program
1056
1331
  process.exit(1);
1057
1332
  }
1058
1333
  });
1334
+ // Start Documentation Site command
1335
+ program
1336
+ .command('docs')
1337
+ .description('Start the documentation site')
1338
+ .option('--dev', 'Start in development mode (with hot reload)')
1339
+ .option('--build', 'Build the documentation site')
1340
+ .action(async (options) => {
1341
+ try {
1342
+ // Check for local docsite (development mode)
1343
+ const localDocsitePath = path.join(__dirname, '..', '..', 'docsite');
1344
+ const isLocalDev = fs.existsSync(localDocsitePath) && fs.existsSync(path.join(localDocsitePath, 'package.json'));
1345
+ if (!isLocalDev) {
1346
+ console.error('Error: Documentation site not found.');
1347
+ console.error('\nThe documentation site is only available in local development.');
1348
+ console.error('Please run this command from the projax repository root.');
1349
+ process.exit(1);
1350
+ }
1351
+ if (options.build) {
1352
+ // Build the documentation site
1353
+ console.log('Building documentation site...');
1354
+ const { execSync } = require('child_process');
1355
+ try {
1356
+ execSync('npm run build', {
1357
+ cwd: localDocsitePath,
1358
+ stdio: 'inherit'
1359
+ });
1360
+ console.log('\n✓ Documentation site built successfully!');
1361
+ console.log('Run "npm run serve" in packages/docsite to serve the built site.');
1362
+ }
1363
+ catch (error) {
1364
+ console.error('\nBuild failed.');
1365
+ process.exit(1);
1366
+ }
1367
+ return;
1368
+ }
1369
+ if (options.dev) {
1370
+ // Development mode - start Docusaurus dev server
1371
+ console.log('Starting documentation site in development mode...');
1372
+ const { spawn } = require('child_process');
1373
+ const docusaurusProcess = spawn('npm', ['start'], {
1374
+ cwd: localDocsitePath,
1375
+ stdio: 'inherit',
1376
+ shell: true,
1377
+ });
1378
+ // Handle process termination
1379
+ process.on('SIGINT', () => {
1380
+ docusaurusProcess.kill();
1381
+ process.exit(0);
1382
+ });
1383
+ process.on('SIGTERM', () => {
1384
+ docusaurusProcess.kill();
1385
+ process.exit(0);
1386
+ });
1387
+ return;
1388
+ }
1389
+ // Production mode - check if built, then serve
1390
+ const buildPath = path.join(localDocsitePath, 'build');
1391
+ if (!fs.existsSync(buildPath)) {
1392
+ console.log('Documentation site not built.');
1393
+ console.log('Building documentation site...');
1394
+ const { execSync } = require('child_process');
1395
+ try {
1396
+ execSync('npm run build', {
1397
+ cwd: localDocsitePath,
1398
+ stdio: 'inherit'
1399
+ });
1400
+ }
1401
+ catch (error) {
1402
+ console.error('\nBuild failed. Try running in dev mode: prx docs --dev');
1403
+ console.error('Or manually build: cd packages/docsite && npm run build');
1404
+ process.exit(1);
1405
+ }
1406
+ }
1407
+ console.log('Starting documentation site server...');
1408
+ const { spawn } = require('child_process');
1409
+ const serveProcess = spawn('npm', ['run', 'serve'], {
1410
+ cwd: localDocsitePath,
1411
+ stdio: 'inherit',
1412
+ shell: true,
1413
+ });
1414
+ // Handle process termination
1415
+ process.on('SIGINT', () => {
1416
+ serveProcess.kill();
1417
+ process.exit(0);
1418
+ });
1419
+ process.on('SIGTERM', () => {
1420
+ serveProcess.kill();
1421
+ process.exit(0);
1422
+ });
1423
+ }
1424
+ catch (error) {
1425
+ console.error('Error starting documentation site:', error instanceof Error ? error.message : error);
1426
+ console.log('\nTroubleshooting:');
1427
+ console.log('1. Try dev mode: prx docs --dev');
1428
+ console.log('2. Or build manually: npm run build:docsite');
1429
+ console.log('3. Or run dev server: cd packages/docsite && npm start');
1430
+ process.exit(1);
1431
+ }
1432
+ });
1059
1433
  // API command - show API info and manage API server
1060
1434
  program
1061
1435
  .command('api')
@@ -1151,7 +1525,7 @@ program
1151
1525
  // Check if first argument is not a known command
1152
1526
  (async () => {
1153
1527
  const args = process.argv.slice(2);
1154
- const knownCommands = ['prxi', 'i', 'add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'run', 'ps', 'stop', 'web', 'desktop', 'ui', 'scripts', 'scan-ports', 'api', '--help', '-h', '--version', '-V'];
1528
+ const knownCommands = ['prxi', 'i', 'add', 'list', 'scan', 'remove', 'rn', 'rename', 'cd', 'pwd', 'run', 'ps', 'stop', 'web', 'desktop', 'ui', 'scripts', 'scan-ports', 'api', 'docs', 'desc', 'description', 'tags', 'open', 'files', 'urls', '--help', '-h', '--version', '-V'];
1155
1529
  // If we have at least 1 argument and first is not a known command, treat as project identifier
1156
1530
  if (args.length >= 1 && !knownCommands.includes(args[0])) {
1157
1531
  const projectIdentifier = args[0];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "projax",
3
- "version": "1.3.10",
4
- "description": "CLI tool for managing local development projects",
3
+ "version": "1.3.12",
4
+ "description": "Cross-platform project management dashboard for tracking local development projects. Features CLI, Terminal UI, Desktop app, REST API, and built-in tools for test detection, port management, and script execution.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "prx": "./dist/index.js"
@@ -37,7 +37,19 @@
37
37
  "project-management",
38
38
  "dashboard",
39
39
  "cli",
40
- "developer-tools"
40
+ "developer-tools",
41
+ "project-tracker",
42
+ "development-workflow",
43
+ "test-detection",
44
+ "port-management",
45
+ "script-runner",
46
+ "electron",
47
+ "terminal-ui",
48
+ "rest-api",
49
+ "nodejs",
50
+ "python",
51
+ "rust",
52
+ "go"
41
53
  ],
42
54
  "author": "",
43
55
  "license": "MIT"
package/dist/core.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export declare const getDatabaseManager: typeof import("projax-core").getDatabaseManager, getAllProjects: typeof import("projax-core").getAllProjects, addProject: typeof import("projax-core").addProject, removeProject: typeof import("projax-core").removeProject, scanProject: typeof import("projax-core").scanProject, scanAllProjects: typeof import("projax-core").scanAllProjects;
2
- export type { Project, Test, ProjectPort } from 'projax-core';
package/dist/core.js DELETED
@@ -1,76 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.scanAllProjects = exports.scanProject = exports.removeProject = exports.addProject = exports.getAllProjects = exports.getDatabaseManager = void 0;
37
- const path = __importStar(require("path"));
38
- let cachedCore = null;
39
- function tryRequire(candidate) {
40
- try {
41
- // eslint-disable-next-line @typescript-eslint/no-var-requires
42
- return require(candidate);
43
- }
44
- catch (error) {
45
- if (!(error instanceof Error) ||
46
- !('code' in error) ||
47
- error.code !== 'MODULE_NOT_FOUND') {
48
- throw error;
49
- }
50
- return null;
51
- }
52
- }
53
- function loadCore() {
54
- if (cachedCore) {
55
- return cachedCore;
56
- }
57
- const candidates = [
58
- // When running the published CLI (core is copied into dist/core)
59
- path.join(__dirname, 'core'),
60
- // When running the CLI directly from the workspace build output
61
- path.join(__dirname, '..', 'core', 'dist'),
62
- path.join(__dirname, '..', '..', 'core', 'dist'),
63
- // Fallback to installed module (useful during development)
64
- 'projax-core',
65
- ];
66
- for (const candidate of candidates) {
67
- const mod = tryRequire(candidate);
68
- if (mod) {
69
- cachedCore = mod;
70
- return mod;
71
- }
72
- }
73
- throw new Error(`Unable to load projax core module. Tried locations: ${candidates.join(', ')}`);
74
- }
75
- const core = loadCore();
76
- exports.getDatabaseManager = core.getDatabaseManager, exports.getAllProjects = core.getAllProjects, exports.addProject = core.addProject, exports.removeProject = core.removeProject, exports.scanProject = core.scanProject, exports.scanAllProjects = core.scanAllProjects;