projax 0.1.29

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 (38) hide show
  1. package/LINKING.md +86 -0
  2. package/README.md +141 -0
  3. package/dist/core/database.d.ts +66 -0
  4. package/dist/core/database.js +312 -0
  5. package/dist/core/detector.d.ts +9 -0
  6. package/dist/core/detector.js +149 -0
  7. package/dist/core/index.d.ts +11 -0
  8. package/dist/core/index.js +43 -0
  9. package/dist/core/scanner.d.ts +8 -0
  10. package/dist/core/scanner.js +114 -0
  11. package/dist/electron/main.d.ts +1 -0
  12. package/dist/electron/main.js +310 -0
  13. package/dist/electron/port-extractor.d.ts +9 -0
  14. package/dist/electron/port-extractor.js +351 -0
  15. package/dist/electron/port-scanner.d.ts +13 -0
  16. package/dist/electron/port-scanner.js +93 -0
  17. package/dist/electron/port-utils.d.ts +21 -0
  18. package/dist/electron/port-utils.js +200 -0
  19. package/dist/electron/preload.d.ts +49 -0
  20. package/dist/electron/preload.js +21 -0
  21. package/dist/electron/renderer/assets/index-BZ6USRnW.js +42 -0
  22. package/dist/electron/renderer/assets/index-DNtxfrZe.js +42 -0
  23. package/dist/electron/renderer/assets/index-khk3K-qG.css +1 -0
  24. package/dist/electron/renderer/index.html +15 -0
  25. package/dist/electron/script-runner.d.ts +40 -0
  26. package/dist/electron/script-runner.js +651 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +971 -0
  29. package/dist/port-extractor.d.ts +9 -0
  30. package/dist/port-extractor.js +351 -0
  31. package/dist/port-scanner.d.ts +13 -0
  32. package/dist/port-scanner.js +93 -0
  33. package/dist/port-utils.d.ts +21 -0
  34. package/dist/port-utils.js +200 -0
  35. package/dist/script-runner.d.ts +40 -0
  36. package/dist/script-runner.js +651 -0
  37. package/package.json +40 -0
  38. package/rebuild-sqlite.js +82 -0
package/LINKING.md ADDED
@@ -0,0 +1,86 @@
1
+ # Linking the CLI Locally
2
+
3
+ The CLI can be linked locally using `npm link`. This allows you to use the `prx` command globally while developing.
4
+
5
+ ## Option 1: Standard npm link (if you have permissions)
6
+
7
+ ```bash
8
+ cd packages/cli
9
+ npm link
10
+ ```
11
+
12
+ This will create a global symlink. You can then use `prx` from anywhere:
13
+
14
+ ```bash
15
+ prx --help
16
+ prx add /path/to/project
17
+ ```
18
+
19
+ ## Option 2: If you get permission errors
20
+
21
+ If you get `EACCES` permission errors, you have a few options:
22
+
23
+ ### A. Use a user-writable npm prefix (Recommended)
24
+
25
+ Configure npm to use a directory in your home folder:
26
+
27
+ ```bash
28
+ mkdir -p ~/.npm-global
29
+ npm config set prefix '~/.npm-global'
30
+ ```
31
+
32
+ Add to your `~/.zshrc` or `~/.bashrc`:
33
+ ```bash
34
+ export PATH=~/.npm-global/bin:$PATH
35
+ ```
36
+
37
+ Then reload your shell and link:
38
+ ```bash
39
+ source ~/.zshrc # or source ~/.bashrc
40
+ cd packages/cli
41
+ npm link
42
+ ```
43
+
44
+ ### B. Use sudo (Not recommended, but works)
45
+
46
+ ```bash
47
+ cd packages/cli
48
+ sudo npm link
49
+ ```
50
+
51
+ ### C. Use npx to run directly
52
+
53
+ You can also run the CLI directly without linking:
54
+
55
+ ```bash
56
+ # From the project root
57
+ npx --package=./packages/cli prx --help
58
+
59
+ # Or use node directly
60
+ node packages/cli/dist/index.js --help
61
+ ```
62
+
63
+ ## Unlinking
64
+
65
+ To remove the global link:
66
+
67
+ ```bash
68
+ npm unlink -g prx-dashboard
69
+ ```
70
+
71
+ Or from the CLI package directory:
72
+
73
+ ```bash
74
+ cd packages/cli
75
+ npm unlink
76
+ ```
77
+
78
+ ## Testing the Link
79
+
80
+ After linking, test it:
81
+
82
+ ```bash
83
+ prx --help
84
+ prx list
85
+ ```
86
+
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # projax CLI
2
+
3
+ Command-line interface for projax - a project management dashboard for tracking local development projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g projax
9
+ ```
10
+
11
+ ## Commands
12
+
13
+ ### `prx add [path]`
14
+
15
+ Add a project to the dashboard. If no path is provided, you'll be prompted to enter one.
16
+
17
+ ```bash
18
+ prx add /path/to/project
19
+ prx add # Interactive mode
20
+ ```
21
+
22
+ ### `prx list`
23
+
24
+ List all tracked projects.
25
+
26
+ ```bash
27
+ prx list
28
+ prx list --verbose # Show detailed information
29
+ ```
30
+
31
+ ### `prx scan [project]`
32
+
33
+ Scan projects for test files. If no project is specified, all projects are scanned.
34
+
35
+ ```bash
36
+ prx scan # Scan all projects
37
+ prx scan 1 # Scan project with ID 1
38
+ prx scan my-project # Scan project named "my-project"
39
+ ```
40
+
41
+ ### `prx remove <project>`
42
+
43
+ Remove a project from the dashboard.
44
+
45
+ ```bash
46
+ prx remove 1
47
+ prx remove my-project
48
+ prx remove my-project --force # Skip confirmation
49
+ ```
50
+
51
+ ### `prx cd [project]`
52
+
53
+ Get the path to a project directory for quick navigation. Use with command substitution to change directories.
54
+
55
+ ```bash
56
+ cd $(prx cd 1) # Change to project with ID 1
57
+ cd $(prx cd my-project) # Change to project named "my-project"
58
+ prx cd # Interactive selection
59
+ ```
60
+
61
+ **Tip:** For even easier navigation, add this to your shell config (`~/.zshrc` or `~/.bashrc`):
62
+
63
+ ```bash
64
+ prxcd() {
65
+ cd $(prx cd "$@")
66
+ }
67
+ ```
68
+
69
+ Then you can simply use: `prxcd 1` or `prxcd my-project`
70
+
71
+ ### `prx <project> <script> [args...]`
72
+
73
+ Run a script from a project's configuration file. Supports multiple project types:
74
+
75
+ - **Node.js**: Runs scripts from `package.json` using `npm run`
76
+ - **Python**: Runs scripts from `pyproject.toml` (supports Poetry)
77
+ - **Rust**: Runs common `cargo` commands (build, run, test, etc.)
78
+ - **Go**: Runs common `go` commands or Makefile targets
79
+ - **Makefile**: Runs Makefile targets
80
+
81
+ ```bash
82
+ prx 1 dev # Run "dev" script from project ID 1
83
+ prx my-project build # Run "build" script from "my-project"
84
+ prx 2 test --watch # Run "test" script with --watch flag
85
+ prx api-server start --port 3000 # Run "start" script with arguments
86
+ ```
87
+
88
+ ### `prx scripts [project]`
89
+
90
+ List all available scripts for a project.
91
+
92
+ ```bash
93
+ prx scripts # Interactive project selection
94
+ prx scripts 1 # List scripts for project ID 1
95
+ prx scripts my-project # List scripts for "my-project"
96
+ ```
97
+
98
+ ### `prx web`
99
+
100
+ Start the Electron web interface.
101
+
102
+ ```bash
103
+ prx web
104
+ ```
105
+
106
+ ## Examples
107
+
108
+ ```bash
109
+ # Add multiple projects
110
+ prx add ~/projects/api-server
111
+ prx add ~/projects/frontend-app
112
+ prx add ~/projects/mobile-app
113
+
114
+ # List all projects
115
+ prx list
116
+
117
+ # Scan all projects for tests
118
+ prx scan
119
+
120
+ # View detailed project information
121
+ prx list --verbose
122
+
123
+ # Remove a project
124
+ prx remove api-server
125
+
126
+ # Quickly navigate to a project
127
+ cd $(prx cd api-server)
128
+
129
+ # Run scripts from projects
130
+ prx api-server dev
131
+ prx 1 build
132
+ prx frontend-app test --watch
133
+
134
+ # List available scripts
135
+ prx scripts api-server
136
+ ```
137
+
138
+ ## Database
139
+
140
+ The CLI uses a shared SQLite database located at `~/.projax/dashboard.db`. This database is shared with the Electron web interface.
141
+
@@ -0,0 +1,66 @@
1
+ import Database from 'better-sqlite3';
2
+ export interface Project {
3
+ id: number;
4
+ name: string;
5
+ path: string;
6
+ last_scanned: number | null;
7
+ created_at: number;
8
+ }
9
+ export interface Test {
10
+ id: number;
11
+ project_id: number;
12
+ file_path: string;
13
+ framework: string | null;
14
+ status: string | null;
15
+ last_run: number | null;
16
+ created_at: number;
17
+ }
18
+ export interface JenkinsJob {
19
+ id: number;
20
+ project_id: number;
21
+ job_name: string;
22
+ job_url: string;
23
+ last_build_status: string | null;
24
+ last_build_number: number | null;
25
+ last_updated: number | null;
26
+ created_at: number;
27
+ }
28
+ export interface ProjectPort {
29
+ id: number;
30
+ project_id: number;
31
+ port: number;
32
+ script_name: string | null;
33
+ config_source: string;
34
+ last_detected: number;
35
+ created_at: number;
36
+ }
37
+ declare class DatabaseManager {
38
+ private db;
39
+ private dbPath;
40
+ constructor();
41
+ getDatabase(): Database.Database;
42
+ private initializeSchema;
43
+ addProject(name: string, projectPath: string): Project;
44
+ getProject(id: number): Project | null;
45
+ getProjectByPath(projectPath: string): Project | null;
46
+ getAllProjects(): Project[];
47
+ updateProjectLastScanned(id: number): void;
48
+ updateProjectName(id: number, newName: string): Project;
49
+ removeProject(id: number): void;
50
+ addTest(projectId: number, filePath: string, framework?: string | null): Test;
51
+ getTest(id: number): Test | null;
52
+ getTestsByProject(projectId: number): Test[];
53
+ removeTestsByProject(projectId: number): void;
54
+ addJenkinsJob(projectId: number, jobName: string, jobUrl: string): JenkinsJob;
55
+ getJenkinsJob(id: number): JenkinsJob | null;
56
+ getJenkinsJobsByProject(projectId: number): JenkinsJob[];
57
+ addProjectPort(projectId: number, port: number, configSource: string, scriptName?: string | null): ProjectPort;
58
+ getProjectPort(id: number): ProjectPort | null;
59
+ getProjectPorts(projectId: number): ProjectPort[];
60
+ getProjectPortsByScript(projectId: number, scriptName: string): ProjectPort[];
61
+ removeProjectPorts(projectId: number): void;
62
+ updateProjectPortLastDetected(projectId: number, port: number, scriptName: string | null): void;
63
+ close(): void;
64
+ }
65
+ export declare function getDatabaseManager(): DatabaseManager;
66
+ export {};
@@ -0,0 +1,312 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.getDatabaseManager = getDatabaseManager;
40
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
41
+ const path = __importStar(require("path"));
42
+ const os = __importStar(require("os"));
43
+ const fs = __importStar(require("fs"));
44
+ class DatabaseManager {
45
+ db = null;
46
+ dbPath;
47
+ constructor() {
48
+ const dataDir = path.join(os.homedir(), '.projax');
49
+ const oldDataDir = path.join(os.homedir(), '.vids-dashboard');
50
+ const oldDbPath = path.join(oldDataDir, 'dashboard.db');
51
+ // Migrate data from old directory if it exists
52
+ const newDbPath = path.join(dataDir, 'dashboard.db');
53
+ if (fs.existsSync(oldDataDir) && fs.existsSync(oldDbPath) && !fs.existsSync(newDbPath)) {
54
+ console.log('Migrating data from ~/.vids-dashboard to ~/.projax...');
55
+ try {
56
+ // Create new directory
57
+ fs.mkdirSync(dataDir, { recursive: true });
58
+ // Copy database file
59
+ fs.copyFileSync(oldDbPath, newDbPath);
60
+ console.log('✓ Database migrated successfully');
61
+ // Copy other files (processes.json, logs directory)
62
+ const oldProcessesFile = path.join(oldDataDir, 'processes.json');
63
+ if (fs.existsSync(oldProcessesFile)) {
64
+ const newProcessesFile = path.join(dataDir, 'processes.json');
65
+ fs.copyFileSync(oldProcessesFile, newProcessesFile);
66
+ }
67
+ const oldLogsDir = path.join(oldDataDir, 'logs');
68
+ if (fs.existsSync(oldLogsDir)) {
69
+ const newLogsDir = path.join(dataDir, 'logs');
70
+ fs.mkdirSync(newLogsDir, { recursive: true });
71
+ // Copy all log files
72
+ const logFiles = fs.readdirSync(oldLogsDir);
73
+ for (const file of logFiles) {
74
+ const oldLogPath = path.join(oldLogsDir, file);
75
+ const newLogPath = path.join(newLogsDir, file);
76
+ if (fs.statSync(oldLogPath).isFile()) {
77
+ fs.copyFileSync(oldLogPath, newLogPath);
78
+ }
79
+ }
80
+ }
81
+ console.log('✓ Migration complete. You can safely remove ~/.vids-dashboard if desired.');
82
+ }
83
+ catch (error) {
84
+ console.error('⚠️ Migration failed:', error);
85
+ console.error(' Continuing with new directory. Old data remains in ~/.vids-dashboard');
86
+ }
87
+ }
88
+ else if (!fs.existsSync(dataDir)) {
89
+ fs.mkdirSync(dataDir, { recursive: true });
90
+ }
91
+ this.dbPath = path.join(dataDir, 'dashboard.db');
92
+ }
93
+ getDatabase() {
94
+ if (!this.db) {
95
+ this.db = new better_sqlite3_1.default(this.dbPath);
96
+ this.db.pragma('journal_mode = WAL');
97
+ this.initializeSchema();
98
+ }
99
+ return this.db;
100
+ }
101
+ initializeSchema() {
102
+ if (!this.db)
103
+ return;
104
+ // Projects table
105
+ this.db.exec(`
106
+ CREATE TABLE IF NOT EXISTS projects (
107
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
108
+ name TEXT NOT NULL,
109
+ path TEXT NOT NULL UNIQUE,
110
+ last_scanned INTEGER,
111
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
112
+ )
113
+ `);
114
+ // Tests table
115
+ this.db.exec(`
116
+ CREATE TABLE IF NOT EXISTS tests (
117
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
118
+ project_id INTEGER NOT NULL,
119
+ file_path TEXT NOT NULL,
120
+ framework TEXT,
121
+ status TEXT,
122
+ last_run INTEGER,
123
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
124
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
125
+ UNIQUE(project_id, file_path)
126
+ )
127
+ `);
128
+ // Jenkins jobs table (for future integration)
129
+ this.db.exec(`
130
+ CREATE TABLE IF NOT EXISTS jenkins_jobs (
131
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
132
+ project_id INTEGER NOT NULL,
133
+ job_name TEXT NOT NULL,
134
+ job_url TEXT NOT NULL,
135
+ last_build_status TEXT,
136
+ last_build_number INTEGER,
137
+ last_updated INTEGER,
138
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
139
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
140
+ UNIQUE(project_id, job_name)
141
+ )
142
+ `);
143
+ // Project ports table
144
+ this.db.exec(`
145
+ CREATE TABLE IF NOT EXISTS project_ports (
146
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
147
+ project_id INTEGER NOT NULL,
148
+ port INTEGER NOT NULL,
149
+ script_name TEXT,
150
+ config_source TEXT NOT NULL,
151
+ last_detected INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
152
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
153
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
154
+ UNIQUE(project_id, port, script_name)
155
+ )
156
+ `);
157
+ // Create indexes
158
+ this.db.exec(`
159
+ CREATE INDEX IF NOT EXISTS idx_tests_project_id ON tests(project_id);
160
+ CREATE INDEX IF NOT EXISTS idx_jenkins_jobs_project_id ON jenkins_jobs(project_id);
161
+ CREATE INDEX IF NOT EXISTS idx_project_ports_project_id ON project_ports(project_id);
162
+ CREATE INDEX IF NOT EXISTS idx_project_ports_port ON project_ports(port);
163
+ `);
164
+ }
165
+ // Project operations
166
+ addProject(name, projectPath) {
167
+ const db = this.getDatabase();
168
+ const stmt = db.prepare(`
169
+ INSERT INTO projects (name, path)
170
+ VALUES (?, ?)
171
+ `);
172
+ const result = stmt.run(name, projectPath);
173
+ return this.getProject(result.lastInsertRowid);
174
+ }
175
+ getProject(id) {
176
+ const db = this.getDatabase();
177
+ const stmt = db.prepare('SELECT * FROM projects WHERE id = ?');
178
+ return stmt.get(id);
179
+ }
180
+ getProjectByPath(projectPath) {
181
+ const db = this.getDatabase();
182
+ const stmt = db.prepare('SELECT * FROM projects WHERE path = ?');
183
+ return stmt.get(projectPath);
184
+ }
185
+ getAllProjects() {
186
+ const db = this.getDatabase();
187
+ const stmt = db.prepare('SELECT * FROM projects ORDER BY id');
188
+ return stmt.all();
189
+ }
190
+ updateProjectLastScanned(id) {
191
+ const db = this.getDatabase();
192
+ const stmt = db.prepare('UPDATE projects SET last_scanned = strftime("%s", "now") WHERE id = ?');
193
+ stmt.run(id);
194
+ }
195
+ updateProjectName(id, newName) {
196
+ const db = this.getDatabase();
197
+ const stmt = db.prepare('UPDATE projects SET name = ? WHERE id = ?');
198
+ stmt.run(newName, id);
199
+ const updated = this.getProject(id);
200
+ if (!updated) {
201
+ throw new Error('Failed to update project name');
202
+ }
203
+ return updated;
204
+ }
205
+ removeProject(id) {
206
+ const db = this.getDatabase();
207
+ const stmt = db.prepare('DELETE FROM projects WHERE id = ?');
208
+ stmt.run(id);
209
+ }
210
+ // Test operations
211
+ addTest(projectId, filePath, framework = null) {
212
+ const db = this.getDatabase();
213
+ const stmt = db.prepare(`
214
+ INSERT OR REPLACE INTO tests (project_id, file_path, framework)
215
+ VALUES (?, ?, ?)
216
+ `);
217
+ const result = stmt.run(projectId, filePath, framework);
218
+ return this.getTest(result.lastInsertRowid);
219
+ }
220
+ getTest(id) {
221
+ const db = this.getDatabase();
222
+ const stmt = db.prepare('SELECT * FROM tests WHERE id = ?');
223
+ return stmt.get(id);
224
+ }
225
+ getTestsByProject(projectId) {
226
+ const db = this.getDatabase();
227
+ const stmt = db.prepare('SELECT * FROM tests WHERE project_id = ? ORDER BY file_path');
228
+ return stmt.all(projectId);
229
+ }
230
+ removeTestsByProject(projectId) {
231
+ const db = this.getDatabase();
232
+ const stmt = db.prepare('DELETE FROM tests WHERE project_id = ?');
233
+ stmt.run(projectId);
234
+ }
235
+ // Jenkins operations (for future use)
236
+ addJenkinsJob(projectId, jobName, jobUrl) {
237
+ const db = this.getDatabase();
238
+ const stmt = db.prepare(`
239
+ INSERT OR REPLACE INTO jenkins_jobs (project_id, job_name, job_url)
240
+ VALUES (?, ?, ?)
241
+ `);
242
+ const result = stmt.run(projectId, jobName, jobUrl);
243
+ return this.getJenkinsJob(result.lastInsertRowid);
244
+ }
245
+ getJenkinsJob(id) {
246
+ const db = this.getDatabase();
247
+ const stmt = db.prepare('SELECT * FROM jenkins_jobs WHERE id = ?');
248
+ return stmt.get(id);
249
+ }
250
+ getJenkinsJobsByProject(projectId) {
251
+ const db = this.getDatabase();
252
+ const stmt = db.prepare('SELECT * FROM jenkins_jobs WHERE project_id = ? ORDER BY job_name');
253
+ return stmt.all(projectId);
254
+ }
255
+ // Project port operations
256
+ addProjectPort(projectId, port, configSource, scriptName = null) {
257
+ const db = this.getDatabase();
258
+ const stmt = db.prepare(`
259
+ INSERT OR REPLACE INTO project_ports (project_id, port, script_name, config_source, last_detected)
260
+ VALUES (?, ?, ?, ?, strftime('%s', 'now'))
261
+ `);
262
+ const result = stmt.run(projectId, port, scriptName, configSource);
263
+ const portRecord = this.getProjectPort(result.lastInsertRowid);
264
+ if (!portRecord) {
265
+ throw new Error('Failed to create project port record');
266
+ }
267
+ return portRecord;
268
+ }
269
+ getProjectPort(id) {
270
+ const db = this.getDatabase();
271
+ const stmt = db.prepare('SELECT * FROM project_ports WHERE id = ?');
272
+ return stmt.get(id);
273
+ }
274
+ getProjectPorts(projectId) {
275
+ const db = this.getDatabase();
276
+ const stmt = db.prepare('SELECT * FROM project_ports WHERE project_id = ? ORDER BY port');
277
+ return stmt.all(projectId);
278
+ }
279
+ getProjectPortsByScript(projectId, scriptName) {
280
+ const db = this.getDatabase();
281
+ const stmt = db.prepare('SELECT * FROM project_ports WHERE project_id = ? AND script_name = ? ORDER BY port');
282
+ return stmt.all(projectId, scriptName);
283
+ }
284
+ removeProjectPorts(projectId) {
285
+ const db = this.getDatabase();
286
+ const stmt = db.prepare('DELETE FROM project_ports WHERE project_id = ?');
287
+ stmt.run(projectId);
288
+ }
289
+ updateProjectPortLastDetected(projectId, port, scriptName) {
290
+ const db = this.getDatabase();
291
+ const stmt = db.prepare(`
292
+ UPDATE project_ports
293
+ SET last_detected = strftime('%s', 'now')
294
+ WHERE project_id = ? AND port = ? AND (script_name = ? OR (script_name IS NULL AND ? IS NULL))
295
+ `);
296
+ stmt.run(projectId, port, scriptName, scriptName);
297
+ }
298
+ close() {
299
+ if (this.db) {
300
+ this.db.close();
301
+ this.db = null;
302
+ }
303
+ }
304
+ }
305
+ // Singleton instance
306
+ let dbManager = null;
307
+ function getDatabaseManager() {
308
+ if (!dbManager) {
309
+ dbManager = new DatabaseManager();
310
+ }
311
+ return dbManager;
312
+ }
@@ -0,0 +1,9 @@
1
+ export interface TestFramework {
2
+ name: string;
3
+ configFiles: string[];
4
+ testPatterns: RegExp[];
5
+ testDirs: string[];
6
+ }
7
+ export declare const FRAMEWORKS: TestFramework[];
8
+ export declare function detectTestFramework(projectPath: string): string | null;
9
+ export declare function isTestFile(filePath: string, detectedFramework?: string | null): boolean;