openwork-agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +436 -0
- package/package.json +78 -0
- package/src/core/TechDetector.js +351 -0
- package/src/generators/ProjectGenerator.js +1241 -0
- package/src/generators/ProjectGeneratorExtensions.js +14 -0
- package/src/generators/TemplateMethods.js +402 -0
- package/src/generators/index.js +5 -0
- package/src/index.js +152 -0
- package/src/main.js +8 -0
- package/src/templates/common/README.md.hbs +358 -0
- package/src/templates/docker/index.js +518 -0
- package/src/templates/docker.js +58 -0
- package/src/templates/go/basic/api/routes/user.go.hbs +138 -0
- package/src/templates/go/basic/config/config.go.hbs +54 -0
- package/src/templates/go/basic/go.mod.hbs +8 -0
- package/src/templates/go/basic/main.go.hbs +70 -0
- package/src/templates/go/basic/models/user.go.hbs +69 -0
- package/src/templates/go/basic/services/user_service.go.hbs +173 -0
- package/src/templates/java/basic/src/main/java/com/{{snakeCase projectName}}/{{projectName}}/controller/UserController.java.hbs +91 -0
- package/src/templates/java/basic/src/main/java/com/{{snakeCase projectName}}/{{projectName}}/dto/ApiResponse.java.hbs +40 -0
- package/src/templates/java/basic/src/main/java/com/{{snakeCase projectName}}/{{projectName}}/model/User.java.hbs +102 -0
- package/src/templates/java/basic/src/main/java/com/{{snakeCase projectName}}/{{projectName}}/repository/UserRepository.java.hbs +20 -0
- package/src/templates/java/basic/src/main/java/com/{{snakeCase projectName}}/{{projectName}}/service/UserService.java.hbs +65 -0
- package/src/templates/java/basic/src/main/java/com/{{snakeCase projectName}}/{{projectName}}/{{pascalCase projectName}}Application.java.hbs +16 -0
- package/src/templates/node/basic/src/config/database.ts.hbs +18 -0
- package/src/templates/node/basic/src/controllers/UserController.ts.hbs +98 -0
- package/src/templates/node/basic/src/index.ts.hbs +45 -0
- package/src/templates/node/basic/src/middleware/errorHandler.ts.hbs +33 -0
- package/src/templates/node/basic/src/routes/index.ts.hbs +42 -0
- package/src/templates/node/basic/src/types/index.ts.hbs +18 -0
- package/src/templates/python/basic/config/database.py.hbs +36 -0
- package/src/templates/python/basic/main.py.hbs +58 -0
- package/src/templates/python/basic/middleware/error_handler.py.hbs +41 -0
- package/src/templates/python/basic/models/user.py.hbs +40 -0
- package/src/templates/python/basic/routes/__init__.py.hbs +12 -0
- package/src/templates/python/basic/routes/users.py.hbs +64 -0
- package/src/templates/rust/basic/Cargo.toml.hbs +39 -0
- package/src/templates/rust/basic/src/config/database.rs.hbs +27 -0
- package/src/templates/rust/basic/src/handlers/user.rs.hbs +130 -0
- package/src/templates/rust/basic/src/handlers/user_routes.rs.hbs +15 -0
- package/src/templates/rust/basic/src/main.rs.hbs +53 -0
- package/src/templates/rust/basic/src/models/mod.rs.hbs +79 -0
- package/src/templates/rust/basic/src/schema.rs.hbs +10 -0
- package/src/utils/FileManager.js +186 -0
- package/src/utils/Templates.js +231 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class TechDetector {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.techSignatures = {
|
|
7
|
+
'node': {
|
|
8
|
+
files: ['package.json', 'yarn.lock', 'package-lock.json'],
|
|
9
|
+
extensions: ['.js', '.ts', '.mjs', '.cjs'],
|
|
10
|
+
patterns: ['require\\(', 'import.*from', 'module.exports', 'export.*'],
|
|
11
|
+
frameworks: {
|
|
12
|
+
'express': ['express\\(\\)', 'require.*express', 'import.*express'],
|
|
13
|
+
'nestjs': ['@nestjs/core', 'Controller', 'Injectable', 'Module'],
|
|
14
|
+
'fastify': ['fastify\\(\\)', 'require.*fastify', 'import.*fastify'],
|
|
15
|
+
'koa': ['koa\\(\\)', 'require.*koa', 'import.*koa'],
|
|
16
|
+
'hapi': ['@hapi/hapi', 'server\\.route'],
|
|
17
|
+
'sails': ['sails\\.lift', 'require.*sails']
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
'python': {
|
|
21
|
+
files: ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile', 'poetry.lock'],
|
|
22
|
+
extensions: ['.py'],
|
|
23
|
+
patterns: ['import ', 'from .* import', 'def ', 'class '],
|
|
24
|
+
frameworks: {
|
|
25
|
+
'django': ['django-admin', 'manage.py', 'DJANGO_SETTINGS_MODULE', 'from django'],
|
|
26
|
+
'fastapi': ['FastAPI\\(', 'from fastapi import', 'APIRouter'],
|
|
27
|
+
'flask': ['Flask\\(', 'from flask import', '@app.route'],
|
|
28
|
+
'tornado': ['tornado.web', 'from tornado import', 'RequestHandler'],
|
|
29
|
+
'aiohttp': ['aiohttp.web', 'from aiohttp import']
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
'java': {
|
|
33
|
+
files: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
|
|
34
|
+
extensions: ['.java', '.kt'],
|
|
35
|
+
patterns: ['public class', 'import java\\.', 'package ', 'public static void main'],
|
|
36
|
+
frameworks: {
|
|
37
|
+
'spring': ['@SpringBootApplication', 'SpringApplication', '@RestController', '@Autowired'],
|
|
38
|
+
'quarkus': ['@Path', '@ApplicationPath', 'Quarkus', '@GET'],
|
|
39
|
+
'micronaut': ['@Controller', '@Inject', 'Micronaut', '@Get'],
|
|
40
|
+
'vertx': ['Vertx', 'vertx\\.deployVerticle', '@Route']
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
'go': {
|
|
44
|
+
files: ['go.mod', 'go.sum'],
|
|
45
|
+
extensions: ['.go'],
|
|
46
|
+
patterns: ['package ', 'func \\(', 'import \\(', 'type.*struct'],
|
|
47
|
+
frameworks: {
|
|
48
|
+
'gin': ['gin\\.Default\\(', 'gin-gonic', 'gin\\.Engine'],
|
|
49
|
+
'echo': ['echo\\.New\\(', 'echo framework', 'echo\\.Echo'],
|
|
50
|
+
'fiber': ['fiber\\.New\\(', 'gofiber', 'fiber\\.App'],
|
|
51
|
+
'chi': ['chi\\.Router\\(', 'go-chi/chi'],
|
|
52
|
+
'gorilla': ['gorilla/mux', 'mux\\.NewRouter']
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
'rust': {
|
|
56
|
+
files: ['Cargo.toml', 'Cargo.lock'],
|
|
57
|
+
extensions: ['.rs'],
|
|
58
|
+
patterns: ['fn main\\(\\)', 'use ', 'mod ', 'pub fn'],
|
|
59
|
+
frameworks: {
|
|
60
|
+
'actix': ['actix_web', 'HttpServer', 'App'],
|
|
61
|
+
'rocket': ['#\\[get\\(', 'rocket::', '#\\[derive'],
|
|
62
|
+
'warp': ['warp::', 'Filter', 'warp::serve'],
|
|
63
|
+
'axum': ['axum', 'Router', 'extract']
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
'php': {
|
|
67
|
+
files: ['composer.json', 'composer.lock'],
|
|
68
|
+
extensions: ['.php'],
|
|
69
|
+
patterns: ['<?php', 'namespace ', 'class ', 'function '],
|
|
70
|
+
frameworks: {
|
|
71
|
+
'laravel': ['laravel/framework', 'Route::', 'Illuminate'],
|
|
72
|
+
'symfony': ['symfony/framework', '@Route', 'Symfony'],
|
|
73
|
+
'slim': ['slim/framework', 'Slim\\App', '$app->get']
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
'csharp': {
|
|
77
|
+
files: ['project.json', '*.csproj', '*.sln'],
|
|
78
|
+
extensions: ['.cs'],
|
|
79
|
+
patterns: ['using ', 'namespace ', 'public class', 'public static void Main'],
|
|
80
|
+
frameworks: {
|
|
81
|
+
'aspnet': ['Microsoft.AspNetCore', 'ControllerBase', '[HttpGet]'],
|
|
82
|
+
'nancy': ['Nancy', 'NancyModule']
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
this.databaseSignatures = {
|
|
88
|
+
'mongodb': ['mongodb', 'mongoose', 'mongo-client', 'pymongo', 'mongodb-driver'],
|
|
89
|
+
'postgresql': ['pg', 'postgresql', 'psycopg2', 'postgres', 'npgsql'],
|
|
90
|
+
'mysql': ['mysql2', 'mysql', 'pymysql', 'sqlalchemy.*mysql', 'mysql-connector'],
|
|
91
|
+
'sqlite': ['sqlite3', 'sqlite', 'better-sqlite3', 'sqlite-net'],
|
|
92
|
+
'redis': ['redis', 'ioredis', 'redis-py', 'stackexchange.redis'],
|
|
93
|
+
'oracle': ['oracledb', 'cx_Oracle', 'oracle-driver'],
|
|
94
|
+
'sqlserver': ['mssql', 'pymssql', 'sql-server-driver']
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async analyzeCurrentDirectory() {
|
|
99
|
+
const analysis = {
|
|
100
|
+
technologies: [],
|
|
101
|
+
frameworks: [],
|
|
102
|
+
databases: [],
|
|
103
|
+
confidence: {},
|
|
104
|
+
files: [],
|
|
105
|
+
packageInfo: {}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const currentDir = process.cwd();
|
|
109
|
+
const files = await this.getAllFiles(currentDir);
|
|
110
|
+
analysis.files = files;
|
|
111
|
+
|
|
112
|
+
// Check for package managers and extract info
|
|
113
|
+
analysis.packageInfo = await this.extractPackageInfo(currentDir);
|
|
114
|
+
|
|
115
|
+
// Detect technologies
|
|
116
|
+
for (const [tech, config] of Object.entries(this.techSignatures)) {
|
|
117
|
+
const confidence = await this.detectTechnology(files, tech, config);
|
|
118
|
+
if (confidence > 0.3) {
|
|
119
|
+
analysis.technologies.push(tech);
|
|
120
|
+
analysis.confidence[tech] = confidence;
|
|
121
|
+
|
|
122
|
+
// Detect frameworks for this technology
|
|
123
|
+
const detectedFrameworks = await this.detectFrameworks(files, tech, config.frameworks);
|
|
124
|
+
analysis.frameworks.push(...detectedFrameworks);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Detect databases
|
|
129
|
+
analysis.databases = await this.detectDatabases(files);
|
|
130
|
+
|
|
131
|
+
return analysis;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async extractPackageInfo(dir) {
|
|
135
|
+
const info = {};
|
|
136
|
+
|
|
137
|
+
// Node.js package.json
|
|
138
|
+
if (await fs.pathExists(path.join(dir, 'package.json'))) {
|
|
139
|
+
try {
|
|
140
|
+
info.node = await fs.readJson(path.join(dir, 'package.json'));
|
|
141
|
+
} catch (e) {}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Python requirements
|
|
145
|
+
if (await fs.pathExists(path.join(dir, 'requirements.txt'))) {
|
|
146
|
+
try {
|
|
147
|
+
const content = await fs.readFile(path.join(dir, 'requirements.txt'), 'utf8');
|
|
148
|
+
info.python = content.split('\n').filter(line => line.trim() && !line.startsWith('#'));
|
|
149
|
+
} catch (e) {}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Java pom.xml
|
|
153
|
+
if (await fs.pathExists(path.join(dir, 'pom.xml'))) {
|
|
154
|
+
try {
|
|
155
|
+
info.java = await fs.readFile(path.join(dir, 'pom.xml'), 'utf8');
|
|
156
|
+
} catch (e) {}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Go go.mod
|
|
160
|
+
if (await fs.pathExists(path.join(dir, 'go.mod'))) {
|
|
161
|
+
try {
|
|
162
|
+
const content = await fs.readFile(path.join(dir, 'go.mod'), 'utf8');
|
|
163
|
+
const match = content.match(/module\s+(.+)/);
|
|
164
|
+
if (match) info.go = { module: match[1].trim() };
|
|
165
|
+
} catch (e) {}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Rust Cargo.toml
|
|
169
|
+
if (await fs.pathExists(path.join(dir, 'Cargo.toml'))) {
|
|
170
|
+
try {
|
|
171
|
+
info.rust = await fs.readFile(path.join(dir, 'Cargo.toml'), 'utf8');
|
|
172
|
+
} catch (e) {}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return info;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async getAllFiles(dir) {
|
|
179
|
+
let files = [];
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const items = await fs.readdir(dir);
|
|
183
|
+
|
|
184
|
+
for (const item of items) {
|
|
185
|
+
const fullPath = path.join(dir, item);
|
|
186
|
+
const stat = await fs.stat(fullPath);
|
|
187
|
+
|
|
188
|
+
if (stat.isDirectory() && !this.shouldSkipDirectory(item)) {
|
|
189
|
+
files = files.concat(await this.getAllFiles(fullPath));
|
|
190
|
+
} else if (stat.isFile()) {
|
|
191
|
+
files.push(fullPath);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// Skip directories we can't read
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return files;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
shouldSkipDirectory(dirName) {
|
|
202
|
+
const skipDirs = ['.git', 'node_modules', '__pycache__', 'target', 'vendor', '.vscode', '.idea', 'build', 'dist', '.next', '.nuxt'];
|
|
203
|
+
return skipDirs.includes(dirName);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async detectTechnology(files, tech, config) {
|
|
207
|
+
let score = 0;
|
|
208
|
+
let maxScore = 0;
|
|
209
|
+
|
|
210
|
+
// Check for signature files
|
|
211
|
+
maxScore += config.files.length * 10;
|
|
212
|
+
for (const file of config.files) {
|
|
213
|
+
if (files.some(f => f.endsWith(file))) {
|
|
214
|
+
score += 10;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check file extensions
|
|
219
|
+
maxScore += files.length;
|
|
220
|
+
const matchingExtensions = files.filter(f =>
|
|
221
|
+
config.extensions.some(ext => f.endsWith(ext))
|
|
222
|
+
);
|
|
223
|
+
score += matchingExtensions.length;
|
|
224
|
+
|
|
225
|
+
// Check content patterns (sample first 10 matching files)
|
|
226
|
+
const sampleFiles = matchingExtensions.slice(0, 10);
|
|
227
|
+
maxScore += sampleFiles.length * config.patterns.length * 5;
|
|
228
|
+
|
|
229
|
+
for (const file of sampleFiles) {
|
|
230
|
+
try {
|
|
231
|
+
const content = await fs.readFile(file, 'utf8');
|
|
232
|
+
for (const pattern of config.patterns) {
|
|
233
|
+
if (new RegExp(pattern, 'i').test(content)) {
|
|
234
|
+
score += 5;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
// Skip files we can't read
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return maxScore > 0 ? score / maxScore : 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async detectFrameworks(files, tech, frameworks) {
|
|
246
|
+
const detected = [];
|
|
247
|
+
|
|
248
|
+
for (const [framework, patterns] of Object.entries(frameworks)) {
|
|
249
|
+
let confidence = 0;
|
|
250
|
+
let checkedFiles = 0;
|
|
251
|
+
|
|
252
|
+
for (const file of files) {
|
|
253
|
+
const ext = path.extname(file);
|
|
254
|
+
if (!this.techSignatures[tech].extensions.includes(ext)) continue;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const content = await fs.readFile(file, 'utf8');
|
|
258
|
+
for (const pattern of patterns) {
|
|
259
|
+
if (new RegExp(pattern, 'i').test(content)) {
|
|
260
|
+
confidence += 1;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
checkedFiles++;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
// Skip files we can't read
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Threshold detection
|
|
270
|
+
if (confidence >= 2 || (confidence > 0 && checkedFiles > 0)) {
|
|
271
|
+
detected.push({
|
|
272
|
+
technology: tech,
|
|
273
|
+
framework,
|
|
274
|
+
confidence: confidence / Math.max(1, checkedFiles)
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return detected;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async detectDatabases(files) {
|
|
283
|
+
const detected = {};
|
|
284
|
+
|
|
285
|
+
for (const file of files) {
|
|
286
|
+
try {
|
|
287
|
+
const content = await fs.readFile(file, 'utf8');
|
|
288
|
+
|
|
289
|
+
for (const [db, patterns] of Object.entries(this.databaseSignatures)) {
|
|
290
|
+
for (const pattern of patterns) {
|
|
291
|
+
if (new RegExp(pattern, 'i').test(content)) {
|
|
292
|
+
detected[db] = (detected[db] || 0) + 1;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} catch (error) {
|
|
297
|
+
// Skip files we can't read
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Return databases sorted by detection frequency
|
|
302
|
+
return Object.entries(detected)
|
|
303
|
+
.sort(([,a], [,b]) => b - a)
|
|
304
|
+
.map(([db]) => db);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
getAvailableTechnologies() {
|
|
308
|
+
return Object.keys(this.techSignatures);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
getFrameworksForTechnology(tech) {
|
|
312
|
+
const config = this.techSignatures[tech];
|
|
313
|
+
return config ? Object.keys(config.frameworks) : [];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
getAvailableDatabases() {
|
|
317
|
+
return Object.keys(this.databaseSignatures);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
getRecommendedTechStack() {
|
|
321
|
+
return {
|
|
322
|
+
'node': {
|
|
323
|
+
frameworks: ['express', 'nestjs', 'fastify'],
|
|
324
|
+
databases: ['mongodb', 'postgresql', 'mysql'],
|
|
325
|
+
reason: 'Great for APIs, microservices, and rapid development'
|
|
326
|
+
},
|
|
327
|
+
'python': {
|
|
328
|
+
frameworks: ['fastapi', 'django', 'flask'],
|
|
329
|
+
databases: ['postgresql', 'mongodb', 'sqlite'],
|
|
330
|
+
reason: 'Excellent for data processing, ML, and web applications'
|
|
331
|
+
},
|
|
332
|
+
'java': {
|
|
333
|
+
frameworks: ['spring', 'quarkus'],
|
|
334
|
+
databases: ['postgresql', 'mysql', 'oracle'],
|
|
335
|
+
reason: 'Enterprise-grade, high performance, and great for large systems'
|
|
336
|
+
},
|
|
337
|
+
'go': {
|
|
338
|
+
frameworks: ['gin', 'fiber', 'echo'],
|
|
339
|
+
databases: ['postgresql', 'mongodb', 'redis'],
|
|
340
|
+
reason: 'High performance, great for microservices and cloud-native'
|
|
341
|
+
},
|
|
342
|
+
'rust': {
|
|
343
|
+
frameworks: ['actix', 'rocket'],
|
|
344
|
+
databases: ['postgresql', 'sqlite'],
|
|
345
|
+
reason: 'Maximum performance and memory safety'
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
module.exports = TechDetector;
|