deepdebug-local-agent 0.3.1

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 (50) hide show
  1. package/.dockerignore +24 -0
  2. package/.idea/deepdebug-local-agent.iml +12 -0
  3. package/.idea/modules.xml +8 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/Dockerfile +46 -0
  6. package/cloudbuild.yaml +42 -0
  7. package/index.js +42 -0
  8. package/mcp-server.js +533 -0
  9. package/package.json +22 -0
  10. package/src/ai-engine.js +861 -0
  11. package/src/analyzers/config-analyzer.js +446 -0
  12. package/src/analyzers/controller-analyzer.js +429 -0
  13. package/src/analyzers/dto-analyzer.js +455 -0
  14. package/src/detectors/build-tool-detector.js +0 -0
  15. package/src/detectors/framework-detector.js +91 -0
  16. package/src/detectors/language-detector.js +89 -0
  17. package/src/detectors/multi-project-detector.js +191 -0
  18. package/src/detectors/service-detector.js +244 -0
  19. package/src/detectors.js +30 -0
  20. package/src/exec-utils.js +215 -0
  21. package/src/fs-utils.js +34 -0
  22. package/src/git/base-git-provider.js +384 -0
  23. package/src/git/git-provider-registry.js +110 -0
  24. package/src/git/github-provider.js +502 -0
  25. package/src/mcp-http-server.js +313 -0
  26. package/src/patch/patch-engine.js +339 -0
  27. package/src/patch-manager.js +816 -0
  28. package/src/patch.js +607 -0
  29. package/src/patch_bkp.js +154 -0
  30. package/src/ports.js +69 -0
  31. package/src/routes/workspace.route.js +528 -0
  32. package/src/runtimes/base-runtime.js +290 -0
  33. package/src/runtimes/java/gradle-runtime.js +378 -0
  34. package/src/runtimes/java/java-integrations.js +339 -0
  35. package/src/runtimes/java/maven-runtime.js +418 -0
  36. package/src/runtimes/node/node-integrations.js +247 -0
  37. package/src/runtimes/node/npm-runtime.js +466 -0
  38. package/src/runtimes/node/yarn-runtime.js +354 -0
  39. package/src/runtimes/runtime-registry.js +256 -0
  40. package/src/server-local.js +576 -0
  41. package/src/server.js +4565 -0
  42. package/src/utils/environment-diagnostics.js +666 -0
  43. package/src/utils/exec-utils.js +264 -0
  44. package/src/utils/fs-utils.js +218 -0
  45. package/src/workspace/detect-port.js +176 -0
  46. package/src/workspace/file-reader.js +54 -0
  47. package/src/workspace/git-client.js +0 -0
  48. package/src/workspace/process-manager.js +619 -0
  49. package/src/workspace/scanner.js +72 -0
  50. package/src/workspace-manager.js +172 -0
@@ -0,0 +1,354 @@
1
+ import path from 'path';
2
+ import { BaseRuntime } from '../base-runtime.js';
3
+ import { exists, readFile } from '../../utils/fs-utils.js';
4
+ import { run } from '../../utils/exec-utils.js';
5
+
6
+ /**
7
+ * YarnRuntime
8
+ *
9
+ * Runtime para projetos Node.js com Yarn.
10
+ * Suporta Express, NestJS, Next.js, React, Vue, etc.
11
+ */
12
+ export class YarnRuntime extends BaseRuntime {
13
+
14
+ constructor() {
15
+ super();
16
+ this.framework = null;
17
+ this.packageJson = null;
18
+ this.yarnVersion = 1; // 1 ou 2+
19
+ }
20
+
21
+ // ==========================================
22
+ // IDENTIFICATION
23
+ // ==========================================
24
+
25
+ getId() {
26
+ return 'node-yarn';
27
+ }
28
+
29
+ getName() {
30
+ return 'Node.js (Yarn)';
31
+ }
32
+
33
+ getLanguage() {
34
+ return 'node';
35
+ }
36
+
37
+ getBuildTool() {
38
+ return 'yarn';
39
+ }
40
+
41
+ // ==========================================
42
+ // DETECTION
43
+ // ==========================================
44
+
45
+ getMarkerFiles() {
46
+ return ['yarn.lock', 'package.json'];
47
+ }
48
+
49
+ async isCompatible(workspacePath) {
50
+ const yarnLock = path.join(workspacePath, 'yarn.lock');
51
+ return exists(yarnLock);
52
+ }
53
+
54
+ async detectProject(workspacePath) {
55
+ const pkgPath = path.join(workspacePath, 'package.json');
56
+ const content = await readFile(pkgPath, 'utf8');
57
+ this.packageJson = JSON.parse(content);
58
+
59
+ // Detectar versão do Yarn
60
+ const yarnrcPath = path.join(workspacePath, '.yarnrc.yml');
61
+ if (await exists(yarnrcPath)) {
62
+ this.yarnVersion = 2;
63
+ }
64
+
65
+ const name = this.packageJson.name || path.basename(workspacePath);
66
+ const version = this.packageJson.version || '0.0.1';
67
+
68
+ const framework = this.detectFramework(this.packageJson);
69
+ this.framework = framework;
70
+
71
+ const port = await this.detectPort(workspacePath);
72
+
73
+ return {
74
+ name,
75
+ version,
76
+ language: 'node',
77
+ framework,
78
+ buildTool: 'yarn',
79
+ port
80
+ };
81
+ }
82
+
83
+ detectFramework(pkg) {
84
+ const deps = {
85
+ ...pkg.dependencies,
86
+ ...pkg.devDependencies
87
+ };
88
+
89
+ if (deps['next']) return 'next';
90
+ if (deps['nuxt']) return 'nuxt';
91
+ if (deps['@angular/core']) return 'angular';
92
+ if (deps['vue']) return deps['nuxt'] ? 'nuxt' : 'vue';
93
+ if (deps['react']) return deps['next'] ? 'next' : 'react';
94
+ if (deps['svelte']) return 'svelte';
95
+ if (deps['@nestjs/core']) return 'nestjs';
96
+ if (deps['express']) return 'express';
97
+ if (deps['fastify']) return 'fastify';
98
+ if (deps['koa']) return 'koa';
99
+
100
+ return 'node-plain';
101
+ }
102
+
103
+ // ==========================================
104
+ // BUILD & RUN
105
+ // ==========================================
106
+
107
+ async install(workspacePath, options = {}) {
108
+ const startTime = Date.now();
109
+ const args = this.yarnVersion >= 2 ? ['install'] : [];
110
+
111
+ if (options.production && this.yarnVersion < 2) {
112
+ args.push('--production');
113
+ }
114
+
115
+ const result = await run('yarn', args, workspacePath);
116
+
117
+ return {
118
+ ...result,
119
+ duration: Date.now() - startTime
120
+ };
121
+ }
122
+
123
+ async build(workspacePath, options = {}) {
124
+ const startTime = Date.now();
125
+
126
+ if (!this.packageJson?.scripts?.build) {
127
+ return {
128
+ code: 0,
129
+ stdout: 'No build script defined',
130
+ stderr: '',
131
+ duration: Date.now() - startTime
132
+ };
133
+ }
134
+
135
+ const result = await run('yarn', ['build'], workspacePath);
136
+
137
+ return {
138
+ ...result,
139
+ duration: Date.now() - startTime
140
+ };
141
+ }
142
+
143
+ getStartCommand(options = {}) {
144
+ const framework = options.framework || this.framework || 'node-plain';
145
+ const port = options.port || 3000;
146
+
147
+ const commands = {
148
+ 'next': {
149
+ cmd: 'yarn',
150
+ args: ['dev'],
151
+ env: { PORT: String(port) }
152
+ },
153
+ 'nuxt': {
154
+ cmd: 'yarn',
155
+ args: ['dev'],
156
+ env: { PORT: String(port) }
157
+ },
158
+ 'nestjs': {
159
+ cmd: 'yarn',
160
+ args: ['start:dev'],
161
+ env: { PORT: String(port) }
162
+ },
163
+ 'express': {
164
+ cmd: 'yarn',
165
+ args: ['start'],
166
+ env: { PORT: String(port) }
167
+ },
168
+ 'react': {
169
+ cmd: 'yarn',
170
+ args: ['start'],
171
+ env: { PORT: String(port) }
172
+ },
173
+ 'vue': {
174
+ cmd: 'yarn',
175
+ args: ['serve'],
176
+ env: { PORT: String(port) }
177
+ }
178
+ };
179
+
180
+ return commands[framework] || {
181
+ cmd: 'yarn',
182
+ args: ['start'],
183
+ env: { PORT: String(port) }
184
+ };
185
+ }
186
+
187
+ getReadyPattern() {
188
+ const patterns = {
189
+ 'next': /ready.*started server on/i,
190
+ 'nuxt': /Listening on/i,
191
+ 'nestjs': /Nest application successfully started/i,
192
+ 'express': /listening on port|server (is )?running/i,
193
+ 'react': /compiled successfully|You can now view/i,
194
+ 'vue': /App running at/i
195
+ };
196
+
197
+ return patterns[this.framework] || /listening|started|ready|running/i;
198
+ }
199
+
200
+ getDefaultPort() {
201
+ const defaults = {
202
+ 'next': 3000,
203
+ 'nuxt': 3000,
204
+ 'nestjs': 3000,
205
+ 'express': 3000,
206
+ 'react': 3000,
207
+ 'vue': 8080
208
+ };
209
+
210
+ return defaults[this.framework] || 3000;
211
+ }
212
+
213
+ async detectPort(workspacePath) {
214
+ const envPath = path.join(workspacePath, '.env');
215
+ if (await exists(envPath)) {
216
+ const content = await readFile(envPath, 'utf8');
217
+ const match = content.match(/^PORT\s*=\s*(\d+)/m);
218
+ if (match) return parseInt(match[1]);
219
+ }
220
+
221
+ return this.getDefaultPort();
222
+ }
223
+
224
+ // ==========================================
225
+ // TEST
226
+ // ==========================================
227
+
228
+ async test(workspacePath, options = {}) {
229
+ const startTime = Date.now();
230
+
231
+ if (!this.packageJson?.scripts?.test) {
232
+ return {
233
+ success: true,
234
+ passed: 0,
235
+ failed: 0,
236
+ skipped: 0,
237
+ duration: Date.now() - startTime,
238
+ output: 'No test script defined',
239
+ failures: []
240
+ };
241
+ }
242
+
243
+ const result = await run('yarn', ['test', '--passWithNoTests'], workspacePath);
244
+
245
+ const testResult = this.parseTestOutput(result);
246
+ testResult.duration = Date.now() - startTime;
247
+
248
+ return testResult;
249
+ }
250
+
251
+ async testSingle(workspacePath, testFile) {
252
+ const startTime = Date.now();
253
+
254
+ const result = await run('yarn', ['test', testFile], workspacePath);
255
+
256
+ const testResult = this.parseTestOutput(result);
257
+ testResult.duration = Date.now() - startTime;
258
+
259
+ return testResult;
260
+ }
261
+
262
+ parseTestOutput(result) {
263
+ const output = result.stdout + result.stderr;
264
+
265
+ const jestMatch = output.match(/Tests:\s+(\d+)\s+passed,?\s*(\d+)?\s*failed?,?\s*(\d+)?\s*skipped?/i);
266
+ if (jestMatch) {
267
+ return {
268
+ success: result.code === 0,
269
+ passed: parseInt(jestMatch[1]) || 0,
270
+ failed: parseInt(jestMatch[2]) || 0,
271
+ skipped: parseInt(jestMatch[3]) || 0,
272
+ duration: 0,
273
+ output,
274
+ failures: []
275
+ };
276
+ }
277
+
278
+ return {
279
+ success: result.code === 0,
280
+ passed: result.code === 0 ? 1 : 0,
281
+ failed: result.code === 0 ? 0 : 1,
282
+ skipped: 0,
283
+ duration: 0,
284
+ output,
285
+ failures: []
286
+ };
287
+ }
288
+
289
+ // ==========================================
290
+ // CODE ANALYSIS
291
+ // ==========================================
292
+
293
+ getFileExtensions() {
294
+ return ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'];
295
+ }
296
+
297
+ async findIntegrations(workspacePath) {
298
+ const { NodeIntegrationAnalyzer } = await import('./node-integrations.js');
299
+ const analyzer = new NodeIntegrationAnalyzer(workspacePath);
300
+ return analyzer.findAll();
301
+ }
302
+
303
+ async analyzeImports(filePath, content) {
304
+ const imports = [];
305
+
306
+ const es6Regex = /import\s+(?:{[^}]+}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/g;
307
+ let match;
308
+ while ((match = es6Regex.exec(content)) !== null) {
309
+ imports.push({
310
+ name: match[1],
311
+ type: 'import',
312
+ isExternal: !match[1].startsWith('.') && !match[1].startsWith('/')
313
+ });
314
+ }
315
+
316
+ const cjsRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
317
+ while ((match = cjsRegex.exec(content)) !== null) {
318
+ imports.push({
319
+ name: match[1],
320
+ type: 'require',
321
+ isExternal: !match[1].startsWith('.') && !match[1].startsWith('/')
322
+ });
323
+ }
324
+
325
+ return imports;
326
+ }
327
+
328
+ // ==========================================
329
+ // PATCH
330
+ // ==========================================
331
+
332
+ async validatePatch(workspacePath, diff) {
333
+ const errors = [];
334
+
335
+ const fileRegex = /^[\+\-]{3}\s+[ab]\/(.+)$/gm;
336
+ let match;
337
+
338
+ while ((match = fileRegex.exec(diff)) !== null) {
339
+ const filePath = path.join(workspacePath, match[1]);
340
+ if (match[0].startsWith('---') && !(await exists(filePath))) {
341
+ if (!diff.includes('+++ /dev/null')) {
342
+ errors.push(`File not found: ${match[1]}`);
343
+ }
344
+ }
345
+ }
346
+
347
+ return {
348
+ valid: errors.length === 0,
349
+ errors
350
+ };
351
+ }
352
+ }
353
+
354
+ export default YarnRuntime;
@@ -0,0 +1,256 @@
1
+ import path from 'path';
2
+ import { exists } from '../utils/fs-utils.js';
3
+
4
+ /**
5
+ * RuntimeRegistry
6
+ *
7
+ * Factory/Registry para gerenciar e detectar runtimes de linguagem.
8
+ * Singleton que mantém todos os runtimes registrados.
9
+ */
10
+ export class RuntimeRegistry {
11
+
12
+ constructor() {
13
+ this.runtimes = new Map();
14
+ this.markerIndex = new Map(); // marker file -> runtime id
15
+ }
16
+
17
+ /**
18
+ * Registra um runtime
19
+ * @param {BaseRuntime} runtime - Instância do runtime
20
+ */
21
+ register(runtime) {
22
+ const id = runtime.getId();
23
+ this.runtimes.set(id, runtime);
24
+
25
+ // Indexar por marker files para detecção rápida
26
+ for (const marker of runtime.getMarkerFiles()) {
27
+ if (!this.markerIndex.has(marker)) {
28
+ this.markerIndex.set(marker, []);
29
+ }
30
+ this.markerIndex.get(marker).push(id);
31
+ }
32
+
33
+ console.log(`📦 Registered runtime: ${id}`);
34
+ }
35
+
36
+ /**
37
+ * Obtém um runtime pelo ID
38
+ * @param {string} id - ID do runtime
39
+ * @returns {BaseRuntime|null}
40
+ */
41
+ get(id) {
42
+ return this.runtimes.get(id) || null;
43
+ }
44
+
45
+ /**
46
+ * Lista todos os runtimes registrados
47
+ * @returns {BaseRuntime[]}
48
+ */
49
+ list() {
50
+ return Array.from(this.runtimes.values());
51
+ }
52
+
53
+ /**
54
+ * Lista IDs dos runtimes registrados
55
+ * @returns {string[]}
56
+ */
57
+ listIds() {
58
+ return Array.from(this.runtimes.keys());
59
+ }
60
+
61
+ /**
62
+ * Auto-detecta o runtime de um workspace
63
+ * @param {string} workspacePath - Caminho do workspace
64
+ * @returns {Promise<BaseRuntime|null>}
65
+ */
66
+ async detectRuntime(workspacePath) {
67
+ console.log(`🔍 Detecting runtime for: ${workspacePath}`);
68
+
69
+ // Prioridade de detecção (mais específico primeiro)
70
+ const detectionOrder = [
71
+ // Java
72
+ 'pom.xml',
73
+ 'build.gradle',
74
+ 'build.gradle.kts',
75
+ // Node
76
+ 'package.json',
77
+ // Python
78
+ 'pyproject.toml',
79
+ 'requirements.txt',
80
+ 'setup.py',
81
+ // .NET
82
+ '*.csproj',
83
+ '*.sln',
84
+ // Go
85
+ 'go.mod',
86
+ // SAP
87
+ 'mta.yaml',
88
+ '.cdsrc.json',
89
+ ];
90
+
91
+ for (const marker of detectionOrder) {
92
+ const candidates = this.markerIndex.get(marker) || [];
93
+
94
+ for (const runtimeId of candidates) {
95
+ const runtime = this.runtimes.get(runtimeId);
96
+
97
+ if (await runtime.isCompatible(workspacePath)) {
98
+ console.log(`✅ Detected runtime: ${runtimeId}`);
99
+ return runtime;
100
+ }
101
+ }
102
+ }
103
+
104
+ // Fallback: tentar cada runtime
105
+ for (const runtime of this.runtimes.values()) {
106
+ if (await runtime.isCompatible(workspacePath)) {
107
+ console.log(`✅ Detected runtime (fallback): ${runtime.getId()}`);
108
+ return runtime;
109
+ }
110
+ }
111
+
112
+ console.log(`⚠️ No runtime detected for: ${workspacePath}`);
113
+ return null;
114
+ }
115
+
116
+ /**
117
+ * Detecta runtime e retorna informações do projeto
118
+ * @param {string} workspacePath - Caminho do workspace
119
+ * @returns {Promise<{runtime: BaseRuntime, project: ProjectInfo}|null>}
120
+ */
121
+ async analyzeWorkspace(workspacePath) {
122
+ const runtime = await this.detectRuntime(workspacePath);
123
+
124
+ if (!runtime) {
125
+ return null;
126
+ }
127
+
128
+ const project = await runtime.detectProject(workspacePath);
129
+
130
+ return {
131
+ runtime,
132
+ project
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Obtém runtime por linguagem e build tool
138
+ * @param {string} language - Linguagem
139
+ * @param {string} buildTool - Build tool
140
+ * @returns {BaseRuntime|null}
141
+ */
142
+ getByLanguageAndTool(language, buildTool) {
143
+ const id = `${language}-${buildTool}`;
144
+
145
+ // Tentar match exato
146
+ if (this.runtimes.has(id)) {
147
+ return this.runtimes.get(id);
148
+ }
149
+
150
+ // Tentar match por linguagem
151
+ for (const runtime of this.runtimes.values()) {
152
+ if (runtime.getLanguage() === language &&
153
+ runtime.getBuildTool() === buildTool) {
154
+ return runtime;
155
+ }
156
+ }
157
+
158
+ // Fallback: qualquer runtime da linguagem
159
+ for (const runtime of this.runtimes.values()) {
160
+ if (runtime.getLanguage() === language) {
161
+ return runtime;
162
+ }
163
+ }
164
+
165
+ return null;
166
+ }
167
+
168
+ /**
169
+ * Lista runtimes por linguagem
170
+ * @param {string} language - Linguagem
171
+ * @returns {BaseRuntime[]}
172
+ */
173
+ listByLanguage(language) {
174
+ return Array.from(this.runtimes.values())
175
+ .filter(r => r.getLanguage() === language);
176
+ }
177
+ }
178
+
179
+ // ==========================================
180
+ // SINGLETON INSTANCE
181
+ // ==========================================
182
+
183
+ let instance = null;
184
+
185
+ /**
186
+ * Obtém a instância singleton do RuntimeRegistry
187
+ * @returns {RuntimeRegistry}
188
+ */
189
+ export function getRuntimeRegistry() {
190
+ if (!instance) {
191
+ instance = new RuntimeRegistry();
192
+ }
193
+ return instance;
194
+ }
195
+
196
+ /**
197
+ * Inicializa o registry com todos os runtimes disponíveis
198
+ * @returns {Promise<RuntimeRegistry>}
199
+ */
200
+ export async function initializeRuntimes() {
201
+ const registry = getRuntimeRegistry();
202
+
203
+ // Import dinâmico dos runtimes
204
+ try {
205
+ // Java
206
+ const { MavenRuntime } = await import('./java/maven-runtime.js');
207
+ const { GradleRuntime } = await import('./java/gradle-runtime.js');
208
+ registry.register(new MavenRuntime());
209
+ registry.register(new GradleRuntime());
210
+ } catch (e) {
211
+ console.warn('⚠️ Java runtimes not available:', e.message);
212
+ }
213
+
214
+ try {
215
+ // Node.js
216
+ const { NpmRuntime } = await import('./node/npm-runtime.js');
217
+ const { YarnRuntime } = await import('./node/yarn-runtime.js');
218
+ registry.register(new NpmRuntime());
219
+ registry.register(new YarnRuntime());
220
+ } catch (e) {
221
+ console.warn('⚠️ Node runtimes not available:', e.message);
222
+ }
223
+
224
+ try {
225
+ // Python
226
+ const { PipRuntime } = await import('./python/pip-runtime.js');
227
+ const { PoetryRuntime } = await import('./python/poetry-runtime.js');
228
+ registry.register(new PipRuntime());
229
+ registry.register(new PoetryRuntime());
230
+ } catch (e) {
231
+ console.warn('⚠️ Python runtimes not available:', e.message);
232
+ }
233
+
234
+ try {
235
+ // .NET
236
+ const { DotNetRuntime } = await import('./dotnet/dotnet-runtime.js');
237
+ registry.register(new DotNetRuntime());
238
+ } catch (e) {
239
+ console.warn('⚠️ .NET runtime not available:', e.message);
240
+ }
241
+
242
+ try {
243
+ // Go
244
+ const { GoRuntime } = await import('./go/go-runtime.js');
245
+ registry.register(new GoRuntime());
246
+ } catch (e) {
247
+ console.warn('⚠️ Go runtime not available:', e.message);
248
+ }
249
+
250
+ console.log(`📦 RuntimeRegistry initialized with ${registry.listIds().length} runtimes`);
251
+ console.log(` Available: ${registry.listIds().join(', ')}`);
252
+
253
+ return registry;
254
+ }
255
+
256
+ export default RuntimeRegistry;