@burdenoff/vibe-agent 1.0.1 → 1.0.3

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 (94) hide show
  1. package/README.md +14 -14
  2. package/dist/app.d.ts +4 -4
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +145 -130
  5. package/dist/app.js.map +1 -1
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/cli.js +463 -333
  8. package/dist/cli.js.map +1 -1
  9. package/dist/db/schema.d.ts +15 -15
  10. package/dist/db/schema.d.ts.map +1 -1
  11. package/dist/db/schema.js +65 -62
  12. package/dist/db/schema.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +18 -18
  15. package/dist/index.js.map +1 -1
  16. package/dist/middleware/ModuleAuth.d.ts +2 -2
  17. package/dist/middleware/ModuleAuth.d.ts.map +1 -1
  18. package/dist/middleware/ModuleAuth.js +32 -29
  19. package/dist/middleware/ModuleAuth.js.map +1 -1
  20. package/dist/middleware/auth.d.ts +1 -1
  21. package/dist/middleware/auth.d.ts.map +1 -1
  22. package/dist/middleware/auth.js +4 -4
  23. package/dist/middleware/auth.js.map +1 -1
  24. package/dist/migrations/remove-notes-prompts.d.ts.map +1 -1
  25. package/dist/migrations/remove-notes-prompts.js +26 -26
  26. package/dist/migrations/remove-notes-prompts.js.map +1 -1
  27. package/dist/routes/bookmarks.d.ts +1 -1
  28. package/dist/routes/bookmarks.d.ts.map +1 -1
  29. package/dist/routes/bookmarks.js +53 -44
  30. package/dist/routes/bookmarks.js.map +1 -1
  31. package/dist/routes/config.d.ts +1 -1
  32. package/dist/routes/config.d.ts.map +1 -1
  33. package/dist/routes/config.js +29 -27
  34. package/dist/routes/config.js.map +1 -1
  35. package/dist/routes/files.d.ts +1 -1
  36. package/dist/routes/files.d.ts.map +1 -1
  37. package/dist/routes/files.js +175 -134
  38. package/dist/routes/files.js.map +1 -1
  39. package/dist/routes/git.d.ts +1 -1
  40. package/dist/routes/git.d.ts.map +1 -1
  41. package/dist/routes/git.js +183 -169
  42. package/dist/routes/git.js.map +1 -1
  43. package/dist/routes/moduleRegistry.d.ts +3 -3
  44. package/dist/routes/moduleRegistry.d.ts.map +1 -1
  45. package/dist/routes/moduleRegistry.js +58 -58
  46. package/dist/routes/moduleRegistry.js.map +1 -1
  47. package/dist/routes/notifications.d.ts +1 -1
  48. package/dist/routes/notifications.d.ts.map +1 -1
  49. package/dist/routes/notifications.js +69 -64
  50. package/dist/routes/notifications.js.map +1 -1
  51. package/dist/routes/port-forward.d.ts +1 -1
  52. package/dist/routes/port-forward.d.ts.map +1 -1
  53. package/dist/routes/port-forward.js +59 -50
  54. package/dist/routes/port-forward.js.map +1 -1
  55. package/dist/routes/projects.d.ts +1 -1
  56. package/dist/routes/projects.d.ts.map +1 -1
  57. package/dist/routes/projects.js +134 -120
  58. package/dist/routes/projects.js.map +1 -1
  59. package/dist/routes/ssh.d.ts +1 -1
  60. package/dist/routes/ssh.d.ts.map +1 -1
  61. package/dist/routes/ssh.js +47 -47
  62. package/dist/routes/ssh.js.map +1 -1
  63. package/dist/routes/tasks.d.ts +1 -1
  64. package/dist/routes/tasks.d.ts.map +1 -1
  65. package/dist/routes/tasks.js +53 -49
  66. package/dist/routes/tasks.js.map +1 -1
  67. package/dist/routes/tmux.d.ts +1 -1
  68. package/dist/routes/tmux.d.ts.map +1 -1
  69. package/dist/routes/tmux.js +337 -241
  70. package/dist/routes/tmux.js.map +1 -1
  71. package/dist/routes/tunnel.d.ts +2 -2
  72. package/dist/routes/tunnel.d.ts.map +1 -1
  73. package/dist/routes/tunnel.js +115 -74
  74. package/dist/routes/tunnel.js.map +1 -1
  75. package/dist/services/ModulePermissions.d.ts +2 -2
  76. package/dist/services/ModulePermissions.d.ts.map +1 -1
  77. package/dist/services/ModulePermissions.js +50 -40
  78. package/dist/services/ModulePermissions.js.map +1 -1
  79. package/dist/services/ModuleRegistryService.d.ts +10 -10
  80. package/dist/services/ModuleRegistryService.d.ts.map +1 -1
  81. package/dist/services/ModuleRegistryService.js +156 -131
  82. package/dist/services/ModuleRegistryService.js.map +1 -1
  83. package/dist/services/agent.service.d.ts.map +1 -1
  84. package/dist/services/agent.service.js +24 -21
  85. package/dist/services/agent.service.js.map +1 -1
  86. package/dist/services/bootstrap.d.ts +1 -1
  87. package/dist/services/bootstrap.d.ts.map +1 -1
  88. package/dist/services/bootstrap.js +146 -69
  89. package/dist/services/bootstrap.js.map +1 -1
  90. package/dist/services/service-manager.d.ts +2 -2
  91. package/dist/services/service-manager.d.ts.map +1 -1
  92. package/dist/services/service-manager.js +75 -63
  93. package/dist/services/service-manager.js.map +1 -1
  94. package/package.json +1 -1
@@ -1,29 +1,29 @@
1
- import { v4 as uuidv4 } from 'uuid';
2
- import { promises as fs, readFileSync } from 'fs';
3
- import path from 'path';
1
+ import { v4 as uuidv4 } from "uuid";
2
+ import { promises as fs, readFileSync } from "fs";
3
+ import path from "path";
4
4
  export const gitRoutes = async (fastify) => {
5
5
  // Get all git repositories
6
- fastify.get('/', async (_request, _reply) => {
6
+ fastify.get("/", async (_request, _reply) => {
7
7
  const repositories = fastify.db.getAllGitRepositories();
8
8
  return { repositories };
9
9
  });
10
10
  // Get repository by ID
11
- fastify.get('/:id', async (request, reply) => {
11
+ fastify.get("/:id", async (request, reply) => {
12
12
  const { id } = request.params;
13
13
  const repository = fastify.db.getGitRepository(id);
14
14
  if (!repository) {
15
- return reply.code(404).send({ error: 'Repository not found' });
15
+ return reply.code(404).send({ error: "Repository not found" });
16
16
  }
17
17
  return { repository };
18
18
  });
19
19
  // Scan directory for git repositories
20
- fastify.post('/scan', async (request, reply) => {
20
+ fastify.post("/scan", async (request, reply) => {
21
21
  const { directory, includeSubmodules = true } = request.body;
22
22
  try {
23
23
  // Validate directory exists
24
24
  const stats = await fs.stat(directory);
25
25
  if (!stats.isDirectory()) {
26
- return reply.code(400).send({ error: 'Path is not a directory' });
26
+ return reply.code(400).send({ error: "Path is not a directory" });
27
27
  }
28
28
  const repositories = await scanForGitRepositories(directory, includeSubmodules);
29
29
  // Save repositories to database
@@ -39,42 +39,42 @@ export const gitRoutes = async (fastify) => {
39
39
  // Create new repository
40
40
  const newRepo = fastify.db.createGitRepository({
41
41
  id: uuidv4(),
42
- ...repo
42
+ ...repo,
43
43
  });
44
44
  savedRepos.push(newRepo);
45
45
  }
46
46
  }
47
47
  // Emit scan complete event
48
- fastify.io.emit('git:scan-complete', {
48
+ fastify.io.emit("git:scan-complete", {
49
49
  directory,
50
- count: savedRepos.length
50
+ count: savedRepos.length,
51
51
  });
52
52
  return {
53
53
  repositories: savedRepos,
54
54
  scannedPath: directory,
55
- totalFound: savedRepos.length
55
+ totalFound: savedRepos.length,
56
56
  };
57
57
  }
58
58
  catch (error) {
59
59
  return reply.code(500).send({
60
- error: 'Failed to scan directory',
61
- details: error instanceof Error ? error.message : 'Unknown error'
60
+ error: "Failed to scan directory",
61
+ details: error instanceof Error ? error.message : "Unknown error",
62
62
  });
63
63
  }
64
64
  });
65
65
  // Scan remote directory via SSH
66
- fastify.post('/scan-remote', async (request, reply) => {
67
- const { connectionId, directory, includeSubmodules = true } = request.body;
66
+ fastify.post("/scan-remote", async (request, reply) => {
67
+ const { connectionId, directory, includeSubmodules = true, } = request.body;
68
68
  try {
69
69
  const connectionConfig = fastify.db.getSSHConnection(connectionId);
70
70
  if (!connectionConfig) {
71
- return reply.code(404).send({ error: 'SSH connection not found' });
71
+ return reply.code(404).send({ error: "SSH connection not found" });
72
72
  }
73
73
  // Use SSH to scan remote directory
74
- const { Client } = await import('ssh2');
74
+ const { Client } = await import("ssh2");
75
75
  const conn = new Client();
76
76
  return new Promise((resolve) => {
77
- conn.on('ready', () => {
77
+ conn.on("ready", () => {
78
78
  // Command to find git repositories
79
79
  const findCommand = includeSubmodules
80
80
  ? `find ${directory} -name ".git" -type d 2>/dev/null | sed 's/\\/.git$//'`
@@ -83,42 +83,42 @@ export const gitRoutes = async (fastify) => {
83
83
  if (err) {
84
84
  conn.end();
85
85
  return resolve(reply.code(500).send({
86
- error: 'Failed to execute remote command',
87
- details: err.message
86
+ error: "Failed to execute remote command",
87
+ details: err.message,
88
88
  }));
89
89
  }
90
- let output = '';
91
- stream.on('data', (data) => {
90
+ let output = "";
91
+ stream.on("data", (data) => {
92
92
  output += data.toString();
93
93
  });
94
- stream.on('close', async () => {
94
+ stream.on("close", async () => {
95
95
  conn.end();
96
- const paths = output.split('\n').filter(p => p.trim());
96
+ const paths = output.split("\n").filter((p) => p.trim());
97
97
  const repositories = [];
98
98
  for (const repoPath of paths) {
99
99
  const name = path.basename(repoPath);
100
- const parentPath = paths.find(p => p !== repoPath && repoPath.startsWith(p + '/'));
100
+ const parentPath = paths.find((p) => p !== repoPath && repoPath.startsWith(p + "/"));
101
101
  repositories.push({
102
102
  path: repoPath,
103
103
  name,
104
104
  parentPath: parentPath || undefined,
105
105
  isSubmodule: !!parentPath,
106
- projectType: await detectProjectType(repoPath, true)
106
+ projectType: await detectProjectType(repoPath, true),
107
107
  });
108
108
  }
109
109
  resolve({
110
110
  repositories,
111
111
  scannedPath: directory,
112
112
  connectionId,
113
- totalFound: repositories.length
113
+ totalFound: repositories.length,
114
114
  });
115
115
  });
116
116
  });
117
117
  });
118
- conn.on('error', (err) => {
118
+ conn.on("error", (err) => {
119
119
  resolve(reply.code(500).send({
120
- error: 'SSH connection failed',
121
- details: err.message
120
+ error: "SSH connection failed",
121
+ details: err.message,
122
122
  }));
123
123
  });
124
124
  // Connect with appropriate auth
@@ -138,68 +138,67 @@ export const gitRoutes = async (fastify) => {
138
138
  }
139
139
  catch (error) {
140
140
  return reply.code(500).send({
141
- error: 'Failed to scan remote directory',
142
- details: error instanceof Error ? error.message : 'Unknown error'
141
+ error: "Failed to scan remote directory",
142
+ details: error instanceof Error ? error.message : "Unknown error",
143
143
  });
144
144
  }
145
145
  });
146
146
  // Update repository metadata
147
- fastify.put('/:id', async (request, reply) => {
147
+ fastify.put("/:id", async (request, reply) => {
148
148
  const { id } = request.params;
149
149
  const updates = request.body;
150
150
  try {
151
151
  const repository = fastify.db.getGitRepository(id);
152
152
  if (!repository) {
153
- return reply.code(404).send({ error: 'Repository not found' });
153
+ return reply.code(404).send({ error: "Repository not found" });
154
154
  }
155
155
  fastify.db.updateGitRepository(id, updates);
156
156
  return { success: true };
157
157
  }
158
158
  catch (error) {
159
159
  return reply.code(500).send({
160
- error: 'Failed to update repository',
161
- details: error instanceof Error ? error.message : 'Unknown error'
160
+ error: "Failed to update repository",
161
+ details: error instanceof Error ? error.message : "Unknown error",
162
162
  });
163
163
  }
164
164
  });
165
165
  // Delete repository from tracking
166
- fastify.delete('/:id', async (request, reply) => {
166
+ fastify.delete("/:id", async (request, reply) => {
167
167
  const { id } = request.params;
168
168
  try {
169
169
  const repository = fastify.db.getGitRepository(id);
170
170
  if (!repository) {
171
- return reply.code(404).send({ error: 'Repository not found' });
171
+ return reply.code(404).send({ error: "Repository not found" });
172
172
  }
173
173
  fastify.db.deleteGitRepository(id);
174
174
  return { success: true };
175
175
  }
176
176
  catch (error) {
177
177
  return reply.code(500).send({
178
- error: 'Failed to delete repository',
179
- details: error instanceof Error ? error.message : 'Unknown error'
178
+ error: "Failed to delete repository",
179
+ details: error instanceof Error ? error.message : "Unknown error",
180
180
  });
181
181
  }
182
182
  });
183
183
  // Fix repository hierarchy
184
- fastify.post('/fix-hierarchy', async (_request, reply) => {
184
+ fastify.post("/fix-hierarchy", async (_request, reply) => {
185
185
  try {
186
186
  const repositories = fastify.db.getAllGitRepositories();
187
187
  let fixed = 0;
188
188
  for (const repo of repositories) {
189
189
  // Check if this repo is inside another repo
190
- const parentRepo = repositories.find(r => r.path !== repo.path &&
191
- repo.path.startsWith(r.path + '/'));
190
+ const parentRepo = repositories.find((r) => r.path !== repo.path && repo.path.startsWith(r.path + "/"));
192
191
  if (parentRepo && repo.parentPath !== parentRepo.path) {
193
192
  fastify.db.updateGitRepository(repo.id, {
194
193
  parentPath: parentRepo.path,
195
- isSubmodule: true
194
+ isSubmodule: true,
196
195
  });
197
196
  fixed++;
198
197
  }
199
198
  else if (!parentRepo && (repo.parentPath || repo.isSubmodule)) {
200
199
  fastify.db.updateGitRepository(repo.id, {
201
200
  parentPath: undefined,
202
- isSubmodule: false
201
+ isSubmodule: false,
203
202
  });
204
203
  fixed++;
205
204
  }
@@ -207,13 +206,13 @@ export const gitRoutes = async (fastify) => {
207
206
  return {
208
207
  success: true,
209
208
  fixed,
210
- total: repositories.length
209
+ total: repositories.length,
211
210
  };
212
211
  }
213
212
  catch (error) {
214
213
  return reply.code(500).send({
215
- error: 'Failed to fix hierarchy',
216
- details: error instanceof Error ? error.message : 'Unknown error'
214
+ error: "Failed to fix hierarchy",
215
+ details: error instanceof Error ? error.message : "Unknown error",
217
216
  });
218
217
  }
219
218
  });
@@ -224,7 +223,7 @@ async function scanForGitRepositories(directory, includeSubmodules, parentPath)
224
223
  try {
225
224
  const entries = await fs.readdir(directory, { withFileTypes: true });
226
225
  // Check if this directory is a git repository
227
- const hasGit = entries.some(e => e.name === '.git' && e.isDirectory());
226
+ const hasGit = entries.some((e) => e.name === ".git" && e.isDirectory());
228
227
  if (hasGit) {
229
228
  const repo = {
230
229
  path: directory,
@@ -232,7 +231,7 @@ async function scanForGitRepositories(directory, includeSubmodules, parentPath)
232
231
  parentPath,
233
232
  isSubmodule: !!parentPath,
234
233
  projectType: await detectProjectType(directory),
235
- vitePort: await detectVitePort(directory)
234
+ vitePort: await detectVitePort(directory),
236
235
  };
237
236
  repositories.push(repo);
238
237
  // If not including submodules, return early
@@ -250,14 +249,14 @@ async function scanForGitRepositories(directory, includeSubmodules, parentPath)
250
249
  // Recursively scan subdirectories
251
250
  for (const entry of entries) {
252
251
  if (entry.isDirectory() &&
253
- entry.name !== '.git' &&
254
- entry.name !== 'node_modules' &&
255
- entry.name !== '__pycache__' &&
256
- entry.name !== '.venv' &&
257
- entry.name !== 'venv' &&
258
- entry.name !== 'dist' &&
259
- entry.name !== 'build' &&
260
- !entry.name.startsWith('.')) {
252
+ entry.name !== ".git" &&
253
+ entry.name !== "node_modules" &&
254
+ entry.name !== "__pycache__" &&
255
+ entry.name !== ".venv" &&
256
+ entry.name !== "venv" &&
257
+ entry.name !== "dist" &&
258
+ entry.name !== "build" &&
259
+ !entry.name.startsWith(".")) {
261
260
  const subPath = path.join(directory, entry.name);
262
261
  const subRepos = await scanForGitRepositories(subPath, includeSubmodules, parentPath);
263
262
  repositories.push(...subRepos);
@@ -278,172 +277,187 @@ async function detectProjectType(directory, remote = false) {
278
277
  try {
279
278
  const entries = await fs.readdir(directory);
280
279
  // Node.js project (check for specific frameworks too)
281
- if (entries.includes('package.json')) {
280
+ if (entries.includes("package.json")) {
282
281
  // Try to detect specific Node.js frameworks
283
282
  try {
284
- const packageJson = JSON.parse(await fs.readFile(path.join(directory, 'package.json'), 'utf8'));
285
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
286
- if (deps.react || deps['@types/react'])
287
- return 'react';
288
- if (deps.vue || deps['@vue/cli'])
289
- return 'vue';
290
- if (deps.angular || deps['@angular/core'])
291
- return 'angular';
292
- if (deps.next || deps['@types/next'])
293
- return 'nextjs';
294
- if (deps.nuxt || deps['@nuxt/core'])
295
- return 'nuxtjs';
296
- if (deps.svelte || deps['@sveltejs/kit'])
297
- return 'svelte';
283
+ const packageJson = JSON.parse(await fs.readFile(path.join(directory, "package.json"), "utf8"));
284
+ const deps = {
285
+ ...packageJson.dependencies,
286
+ ...packageJson.devDependencies,
287
+ };
288
+ if (deps.react || deps["@types/react"])
289
+ return "react";
290
+ if (deps.vue || deps["@vue/cli"])
291
+ return "vue";
292
+ if (deps.angular || deps["@angular/core"])
293
+ return "angular";
294
+ if (deps.next || deps["@types/next"])
295
+ return "nextjs";
296
+ if (deps.nuxt || deps["@nuxt/core"])
297
+ return "nuxtjs";
298
+ if (deps.svelte || deps["@sveltejs/kit"])
299
+ return "svelte";
298
300
  if (deps.express || deps.fastify || deps.koa)
299
- return 'nodejs-backend';
301
+ return "nodejs-backend";
300
302
  if (deps.electron)
301
- return 'electron';
303
+ return "electron";
302
304
  if (deps.gatsby)
303
- return 'gatsby';
305
+ return "gatsby";
304
306
  if (deps.vite)
305
- return 'vite';
307
+ return "vite";
306
308
  }
307
309
  catch {
308
310
  // Fallback to generic nodejs
309
311
  }
310
- return 'nodejs';
312
+ return "nodejs";
311
313
  }
312
314
  // Python project
313
- if (entries.includes('setup.py') ||
314
- entries.includes('requirements.txt') ||
315
- entries.includes('pyproject.toml') ||
316
- entries.includes('Pipfile') ||
317
- entries.includes('poetry.lock') ||
318
- entries.includes('conda.yml') ||
319
- entries.includes('environment.yml')) {
315
+ if (entries.includes("setup.py") ||
316
+ entries.includes("requirements.txt") ||
317
+ entries.includes("pyproject.toml") ||
318
+ entries.includes("Pipfile") ||
319
+ entries.includes("poetry.lock") ||
320
+ entries.includes("conda.yml") ||
321
+ entries.includes("environment.yml")) {
320
322
  // Try to detect specific Python frameworks
321
323
  try {
322
- if (entries.includes('manage.py'))
323
- return 'django';
324
- if (entries.includes('app.py') || entries.includes('application.py'))
325
- return 'flask';
326
- if (entries.includes('main.py'))
327
- return 'fastapi';
328
- if (entries.includes('streamlit_app.py'))
329
- return 'streamlit';
330
- if (entries.includes('requirements.txt')) {
331
- const reqs = await fs.readFile(path.join(directory, 'requirements.txt'), 'utf8');
332
- if (reqs.includes('django'))
333
- return 'django';
334
- if (reqs.includes('flask'))
335
- return 'flask';
336
- if (reqs.includes('fastapi'))
337
- return 'fastapi';
338
- if (reqs.includes('streamlit'))
339
- return 'streamlit';
340
- if (reqs.includes('tensorflow') || reqs.includes('pytorch'))
341
- return 'python-ml';
342
- if (reqs.includes('jupyter'))
343
- return 'jupyter';
324
+ if (entries.includes("manage.py"))
325
+ return "django";
326
+ if (entries.includes("app.py") || entries.includes("application.py"))
327
+ return "flask";
328
+ if (entries.includes("main.py"))
329
+ return "fastapi";
330
+ if (entries.includes("streamlit_app.py"))
331
+ return "streamlit";
332
+ if (entries.includes("requirements.txt")) {
333
+ const reqs = await fs.readFile(path.join(directory, "requirements.txt"), "utf8");
334
+ if (reqs.includes("django"))
335
+ return "django";
336
+ if (reqs.includes("flask"))
337
+ return "flask";
338
+ if (reqs.includes("fastapi"))
339
+ return "fastapi";
340
+ if (reqs.includes("streamlit"))
341
+ return "streamlit";
342
+ if (reqs.includes("tensorflow") || reqs.includes("pytorch"))
343
+ return "python-ml";
344
+ if (reqs.includes("jupyter"))
345
+ return "jupyter";
344
346
  }
345
347
  }
346
348
  catch {
347
349
  // Fallback to generic python
348
350
  }
349
- return 'python';
351
+ return "python";
350
352
  }
351
353
  // Go project
352
- if (entries.includes('go.mod') || entries.includes('go.sum')) {
353
- return 'go';
354
+ if (entries.includes("go.mod") || entries.includes("go.sum")) {
355
+ return "go";
354
356
  }
355
357
  // Rust project
356
- if (entries.includes('Cargo.toml') || entries.includes('Cargo.lock')) {
357
- return 'rust';
358
+ if (entries.includes("Cargo.toml") || entries.includes("Cargo.lock")) {
359
+ return "rust";
358
360
  }
359
361
  // Java project
360
- if (entries.includes('pom.xml'))
361
- return 'maven';
362
- if (entries.includes('build.gradle') || entries.includes('build.gradle.kts'))
363
- return 'gradle';
364
- if (entries.some(e => e.endsWith('.java')))
365
- return 'java';
362
+ if (entries.includes("pom.xml"))
363
+ return "maven";
364
+ if (entries.includes("build.gradle") ||
365
+ entries.includes("build.gradle.kts"))
366
+ return "gradle";
367
+ if (entries.some((e) => e.endsWith(".java")))
368
+ return "java";
366
369
  // Ruby project
367
- if (entries.includes('Gemfile')) {
370
+ if (entries.includes("Gemfile")) {
368
371
  try {
369
- const gemfile = await fs.readFile(path.join(directory, 'Gemfile'), 'utf8');
370
- if (gemfile.includes('rails'))
371
- return 'rails';
372
- if (gemfile.includes('sinatra'))
373
- return 'sinatra';
372
+ const gemfile = await fs.readFile(path.join(directory, "Gemfile"), "utf8");
373
+ if (gemfile.includes("rails"))
374
+ return "rails";
375
+ if (gemfile.includes("sinatra"))
376
+ return "sinatra";
374
377
  }
375
378
  catch {
376
379
  // Fallback
377
380
  }
378
- return 'ruby';
381
+ return "ruby";
379
382
  }
380
383
  // PHP project
381
- if (entries.includes('composer.json')) {
384
+ if (entries.includes("composer.json")) {
382
385
  try {
383
- const composer = JSON.parse(await fs.readFile(path.join(directory, 'composer.json'), 'utf8'));
384
- const deps = { ...composer.require, ...composer['require-dev'] };
385
- if (deps.laravel || deps['laravel/framework'])
386
- return 'laravel';
387
- if (deps.symfony || deps['symfony/symfony'])
388
- return 'symfony';
389
- if (deps.wordpress || entries.includes('wp-config.php'))
390
- return 'wordpress';
386
+ const composer = JSON.parse(await fs.readFile(path.join(directory, "composer.json"), "utf8"));
387
+ const deps = { ...composer.require, ...composer["require-dev"] };
388
+ if (deps.laravel || deps["laravel/framework"])
389
+ return "laravel";
390
+ if (deps.symfony || deps["symfony/symfony"])
391
+ return "symfony";
392
+ if (deps.wordpress || entries.includes("wp-config.php"))
393
+ return "wordpress";
391
394
  }
392
395
  catch {
393
396
  // Fallback
394
397
  }
395
- return 'php';
398
+ return "php";
396
399
  }
397
400
  // .NET project
398
- if (entries.some(e => e.endsWith('.csproj') || e.endsWith('.sln') || e.endsWith('.fsproj') || e.endsWith('.vbproj'))) {
399
- return 'dotnet';
401
+ if (entries.some((e) => e.endsWith(".csproj") ||
402
+ e.endsWith(".sln") ||
403
+ e.endsWith(".fsproj") ||
404
+ e.endsWith(".vbproj"))) {
405
+ return "dotnet";
400
406
  }
401
407
  // C/C++ project
402
- if (entries.includes('CMakeLists.txt') || entries.includes('Makefile') ||
403
- entries.some(e => e.endsWith('.c') || e.endsWith('.cpp') || e.endsWith('.h') || e.endsWith('.hpp'))) {
404
- return 'cpp';
408
+ if (entries.includes("CMakeLists.txt") ||
409
+ entries.includes("Makefile") ||
410
+ entries.some((e) => e.endsWith(".c") ||
411
+ e.endsWith(".cpp") ||
412
+ e.endsWith(".h") ||
413
+ e.endsWith(".hpp"))) {
414
+ return "cpp";
405
415
  }
406
416
  // Swift project
407
- if (entries.includes('Package.swift') || entries.some(e => e.endsWith('.xcodeproj') || e.endsWith('.xcworkspace'))) {
408
- return 'swift';
417
+ if (entries.includes("Package.swift") ||
418
+ entries.some((e) => e.endsWith(".xcodeproj") || e.endsWith(".xcworkspace"))) {
419
+ return "swift";
409
420
  }
410
421
  // Kotlin project
411
- if (entries.some(e => e.endsWith('.kt') || e.endsWith('.kts'))) {
412
- return 'kotlin';
422
+ if (entries.some((e) => e.endsWith(".kt") || e.endsWith(".kts"))) {
423
+ return "kotlin";
413
424
  }
414
425
  // Scala project
415
- if (entries.includes('build.sbt') || entries.some(e => e.endsWith('.scala'))) {
416
- return 'scala';
426
+ if (entries.includes("build.sbt") ||
427
+ entries.some((e) => e.endsWith(".scala"))) {
428
+ return "scala";
417
429
  }
418
430
  // R project
419
- if (entries.includes('DESCRIPTION') || entries.some(e => e.endsWith('.R') || e.endsWith('.Rmd'))) {
420
- return 'r';
431
+ if (entries.includes("DESCRIPTION") ||
432
+ entries.some((e) => e.endsWith(".R") || e.endsWith(".Rmd"))) {
433
+ return "r";
421
434
  }
422
435
  // Dart/Flutter project
423
- if (entries.includes('pubspec.yaml')) {
424
- return 'flutter';
436
+ if (entries.includes("pubspec.yaml")) {
437
+ return "flutter";
425
438
  }
426
439
  // Unity project
427
- if (entries.includes('ProjectSettings') && entries.includes('Assets')) {
428
- return 'unity';
440
+ if (entries.includes("ProjectSettings") && entries.includes("Assets")) {
441
+ return "unity";
429
442
  }
430
443
  // Docker project
431
- if (entries.includes('Dockerfile') || entries.includes('docker-compose.yml')) {
432
- return 'docker';
444
+ if (entries.includes("Dockerfile") ||
445
+ entries.includes("docker-compose.yml")) {
446
+ return "docker";
433
447
  }
434
448
  // Terraform project
435
- if (entries.some(e => e.endsWith('.tf') || e.endsWith('.tfvars'))) {
436
- return 'terraform';
449
+ if (entries.some((e) => e.endsWith(".tf") || e.endsWith(".tfvars"))) {
450
+ return "terraform";
437
451
  }
438
452
  // Documentation/Static sites
439
- if (entries.includes('_config.yml'))
440
- return 'jekyll';
441
- if (entries.includes('mkdocs.yml'))
442
- return 'mkdocs';
443
- if (entries.includes('docusaurus.config.js'))
444
- return 'docusaurus';
445
- if (entries.includes('hugo.toml') || entries.includes('config.toml'))
446
- return 'hugo';
453
+ if (entries.includes("_config.yml"))
454
+ return "jekyll";
455
+ if (entries.includes("mkdocs.yml"))
456
+ return "mkdocs";
457
+ if (entries.includes("docusaurus.config.js"))
458
+ return "docusaurus";
459
+ if (entries.includes("hugo.toml") || entries.includes("config.toml"))
460
+ return "hugo";
447
461
  }
448
462
  catch {
449
463
  // Ignore errors
@@ -453,8 +467,8 @@ async function detectProjectType(directory, remote = false) {
453
467
  // Helper function to detect Vite port
454
468
  async function detectVitePort(directory) {
455
469
  try {
456
- const viteConfigPath = path.join(directory, 'vite.config.js');
457
- const viteConfigTsPath = path.join(directory, 'vite.config.ts');
470
+ const viteConfigPath = path.join(directory, "vite.config.js");
471
+ const viteConfigTsPath = path.join(directory, "vite.config.ts");
458
472
  let configPath;
459
473
  try {
460
474
  await fs.access(viteConfigPath);
@@ -469,7 +483,7 @@ async function detectVitePort(directory) {
469
483
  return undefined;
470
484
  }
471
485
  }
472
- const content = await fs.readFile(configPath, 'utf-8');
486
+ const content = await fs.readFile(configPath, "utf-8");
473
487
  // Look for port configuration
474
488
  const portMatch = content.match(/port:\s*(\d+)/);
475
489
  if (portMatch) {
@@ -485,7 +499,7 @@ async function detectVitePort(directory) {
485
499
  async function shouldSkipDirectory(directory) {
486
500
  try {
487
501
  // Check for .gitignore
488
- const gitignorePath = path.join(directory, '.gitignore');
502
+ const gitignorePath = path.join(directory, ".gitignore");
489
503
  await fs.access(gitignorePath);
490
504
  // For now, we'll use a simple heuristic
491
505
  // In a real implementation, you'd parse .gitignore properly