@stackguide/mcp-server 2.3.2 → 3.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/README.md +82 -16
- package/dist/config/healthWeights.d.ts +87 -0
- package/dist/config/healthWeights.d.ts.map +1 -0
- package/dist/config/healthWeights.js +238 -0
- package/dist/config/healthWeights.js.map +1 -0
- package/dist/config/types.d.ts +104 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js.map +1 -1
- package/dist/handlers/generate.d.ts +24 -0
- package/dist/handlers/generate.d.ts.map +1 -0
- package/dist/handlers/generate.js +845 -0
- package/dist/handlers/generate.js.map +1 -0
- package/dist/handlers/health.d.ts +19 -0
- package/dist/handlers/health.d.ts.map +1 -0
- package/dist/handlers/health.js +401 -0
- package/dist/handlers/health.js.map +1 -0
- package/dist/handlers/help.d.ts +1 -1
- package/dist/handlers/help.d.ts.map +1 -1
- package/dist/handlers/help.js +70 -10
- package/dist/handlers/help.js.map +1 -1
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/handlers/index.js +2 -0
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/review.d.ts +12 -0
- package/dist/handlers/review.d.ts.map +1 -1
- package/dist/handlers/review.js +220 -21
- package/dist/handlers/review.js.map +1 -1
- package/dist/handlers/setup.d.ts +1 -0
- package/dist/handlers/setup.d.ts.map +1 -1
- package/dist/handlers/setup.js +102 -5
- package/dist/handlers/setup.js.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/services/analysisCache.d.ts +110 -0
- package/dist/services/analysisCache.d.ts.map +1 -0
- package/dist/services/analysisCache.js +233 -0
- package/dist/services/analysisCache.js.map +1 -0
- package/dist/services/codeAnalyzer.d.ts +73 -22
- package/dist/services/codeAnalyzer.d.ts.map +1 -1
- package/dist/services/codeAnalyzer.js +462 -44
- package/dist/services/codeAnalyzer.js.map +1 -1
- package/dist/services/conventionDetector.d.ts +40 -0
- package/dist/services/conventionDetector.d.ts.map +1 -0
- package/dist/services/conventionDetector.js +465 -0
- package/dist/services/conventionDetector.js.map +1 -0
- package/dist/services/cursorDirectory.d.ts +29 -2
- package/dist/services/cursorDirectory.d.ts.map +1 -1
- package/dist/services/cursorDirectory.js +260 -9
- package/dist/services/cursorDirectory.js.map +1 -1
- package/dist/tools/definitions.d.ts +125 -0
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +62 -1
- package/dist/tools/definitions.js.map +1 -1
- package/dist/utils/validation.d.ts +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate handler - Generate boilerplate code from templates
|
|
3
|
+
* Phase 6: Advanced Features
|
|
4
|
+
*
|
|
5
|
+
* Improvements:
|
|
6
|
+
* - Detects project conventions (quotes, semicolons, indentation)
|
|
7
|
+
* - Adapts templates based on state management in use
|
|
8
|
+
* - Respects existing code patterns
|
|
9
|
+
*/
|
|
10
|
+
import { jsonResponse } from './types.js';
|
|
11
|
+
import { logger } from '../utils/logger.js';
|
|
12
|
+
import { detectConventions } from '../services/conventionDetector.js';
|
|
13
|
+
// Cached conventions
|
|
14
|
+
let cachedConventions = null;
|
|
15
|
+
let cachedProjectPath = null;
|
|
16
|
+
/**
|
|
17
|
+
* Get conventions for current project (with caching)
|
|
18
|
+
*/
|
|
19
|
+
function getProjectConventions() {
|
|
20
|
+
const projectPath = process.cwd();
|
|
21
|
+
if (cachedConventions && cachedProjectPath === projectPath) {
|
|
22
|
+
return cachedConventions;
|
|
23
|
+
}
|
|
24
|
+
cachedConventions = detectConventions(projectPath);
|
|
25
|
+
cachedProjectPath = projectPath;
|
|
26
|
+
return cachedConventions;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Apply conventions to generated code
|
|
30
|
+
*/
|
|
31
|
+
function applyConventions(code, conventions) {
|
|
32
|
+
let result = code;
|
|
33
|
+
// Apply indentation
|
|
34
|
+
if (conventions.indentation === 'tabs') {
|
|
35
|
+
result = result.replace(/^( {2})/gm, '\t');
|
|
36
|
+
result = result.replace(/^( {4})/gm, '\t\t');
|
|
37
|
+
result = result.replace(/^( {6})/gm, '\t\t\t');
|
|
38
|
+
result = result.replace(/^( {8})/gm, '\t\t\t\t');
|
|
39
|
+
}
|
|
40
|
+
else if (conventions.indentSize !== 2) {
|
|
41
|
+
const spaces = ' '.repeat(conventions.indentSize);
|
|
42
|
+
result = result.replace(/^ /gm, spaces);
|
|
43
|
+
result = result.replace(/^ /gm, spaces + spaces);
|
|
44
|
+
result = result.replace(/^ /gm, spaces + spaces + spaces);
|
|
45
|
+
}
|
|
46
|
+
// Apply quote style (careful with JSX)
|
|
47
|
+
if (conventions.quotes === 'double') {
|
|
48
|
+
// Only change quotes outside of JSX attributes
|
|
49
|
+
result = result.replace(/(?<!=)'([^']*)'(?!>)/g, '"$1"');
|
|
50
|
+
}
|
|
51
|
+
// Apply semicolons
|
|
52
|
+
if (!conventions.semicolons) {
|
|
53
|
+
// Remove trailing semicolons (simple cases)
|
|
54
|
+
result = result.replace(/;(\s*\n)/g, '$1');
|
|
55
|
+
result = result.replace(/;(\s*$)/g, '$1');
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
// Template generators by type
|
|
60
|
+
const templates = {
|
|
61
|
+
// React Component
|
|
62
|
+
component: (name, opts) => {
|
|
63
|
+
const ts = opts?.typescript !== false;
|
|
64
|
+
const withStyles = opts?.withStyles;
|
|
65
|
+
if (ts) {
|
|
66
|
+
return `import React from 'react';
|
|
67
|
+
${withStyles ? `import styles from './${name}.module.css';\n` : ''}
|
|
68
|
+
interface ${name}Props {
|
|
69
|
+
/** Add your props here */
|
|
70
|
+
className?: string;
|
|
71
|
+
children?: React.ReactNode;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* ${name} Component
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* <${name}>Content</${name}>
|
|
79
|
+
*/
|
|
80
|
+
export const ${name}: React.FC<${name}Props> = ({
|
|
81
|
+
className,
|
|
82
|
+
children,
|
|
83
|
+
}) => {
|
|
84
|
+
return (
|
|
85
|
+
<div className={\`${withStyles ? '${styles.container} ' : ''}$\{className || ''}\`}>
|
|
86
|
+
{children}
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default ${name};
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
return `import React from 'react';
|
|
95
|
+
${withStyles ? `import styles from './${name}.module.css';\n` : ''}
|
|
96
|
+
/**
|
|
97
|
+
* ${name} Component
|
|
98
|
+
*/
|
|
99
|
+
export const ${name} = ({ className, children }) => {
|
|
100
|
+
return (
|
|
101
|
+
<div className={className}>
|
|
102
|
+
{children}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default ${name};
|
|
108
|
+
`;
|
|
109
|
+
},
|
|
110
|
+
// React Hook
|
|
111
|
+
hook: (name, opts) => {
|
|
112
|
+
const hookName = name.startsWith('use') ? name : `use${name}`;
|
|
113
|
+
const ts = opts?.typescript !== false;
|
|
114
|
+
if (ts) {
|
|
115
|
+
return `import { useState, useEffect, useCallback } from 'react';
|
|
116
|
+
|
|
117
|
+
interface ${hookName}Options {
|
|
118
|
+
/** Initial value */
|
|
119
|
+
initialValue?: unknown;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface ${hookName}Result {
|
|
123
|
+
/** Current state */
|
|
124
|
+
data: unknown;
|
|
125
|
+
/** Loading state */
|
|
126
|
+
loading: boolean;
|
|
127
|
+
/** Error state */
|
|
128
|
+
error: Error | null;
|
|
129
|
+
/** Refetch function */
|
|
130
|
+
refetch: () => Promise<void>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* ${hookName} - Custom React Hook
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const { data, loading, error } = ${hookName}();
|
|
138
|
+
*/
|
|
139
|
+
export function ${hookName}(options: ${hookName}Options = {}): ${hookName}Result {
|
|
140
|
+
const [data, setData] = useState<unknown>(options.initialValue);
|
|
141
|
+
const [loading, setLoading] = useState(false);
|
|
142
|
+
const [error, setError] = useState<Error | null>(null);
|
|
143
|
+
|
|
144
|
+
const refetch = useCallback(async () => {
|
|
145
|
+
setLoading(true);
|
|
146
|
+
setError(null);
|
|
147
|
+
try {
|
|
148
|
+
// TODO: Implement fetch logic
|
|
149
|
+
// const result = await fetchData();
|
|
150
|
+
// setData(result);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
setError(e instanceof Error ? e : new Error('Unknown error'));
|
|
153
|
+
} finally {
|
|
154
|
+
setLoading(false);
|
|
155
|
+
}
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
refetch();
|
|
160
|
+
}, [refetch]);
|
|
161
|
+
|
|
162
|
+
return { data, loading, error, refetch };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export default ${hookName};
|
|
166
|
+
`;
|
|
167
|
+
}
|
|
168
|
+
return `import { useState, useEffect, useCallback } from 'react';
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* ${hookName} - Custom React Hook
|
|
172
|
+
*/
|
|
173
|
+
export function ${hookName}(options = {}) {
|
|
174
|
+
const [data, setData] = useState(options.initialValue);
|
|
175
|
+
const [loading, setLoading] = useState(false);
|
|
176
|
+
const [error, setError] = useState(null);
|
|
177
|
+
|
|
178
|
+
const refetch = useCallback(async () => {
|
|
179
|
+
setLoading(true);
|
|
180
|
+
try {
|
|
181
|
+
// TODO: Implement fetch logic
|
|
182
|
+
} catch (e) {
|
|
183
|
+
setError(e);
|
|
184
|
+
} finally {
|
|
185
|
+
setLoading(false);
|
|
186
|
+
}
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
refetch();
|
|
191
|
+
}, [refetch]);
|
|
192
|
+
|
|
193
|
+
return { data, loading, error, refetch };
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
},
|
|
197
|
+
// Service
|
|
198
|
+
service: (name, opts) => {
|
|
199
|
+
const ts = opts?.typescript !== false;
|
|
200
|
+
const serviceName = name.endsWith('Service') ? name : `${name}Service`;
|
|
201
|
+
if (ts) {
|
|
202
|
+
return `/**
|
|
203
|
+
* ${serviceName}
|
|
204
|
+
*
|
|
205
|
+
* Service layer for ${name.replace('Service', '')} operations
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
interface ${serviceName}Config {
|
|
209
|
+
baseUrl?: string;
|
|
210
|
+
timeout?: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
interface ApiResponse<T> {
|
|
214
|
+
data: T;
|
|
215
|
+
status: number;
|
|
216
|
+
message?: string;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
class ${serviceName} {
|
|
220
|
+
private baseUrl: string;
|
|
221
|
+
private timeout: number;
|
|
222
|
+
|
|
223
|
+
constructor(config: ${serviceName}Config = {}) {
|
|
224
|
+
this.baseUrl = config.baseUrl || '/api';
|
|
225
|
+
this.timeout = config.timeout || 10000;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private async request<T>(
|
|
229
|
+
endpoint: string,
|
|
230
|
+
options: RequestInit = {}
|
|
231
|
+
): Promise<ApiResponse<T>> {
|
|
232
|
+
const controller = new AbortController();
|
|
233
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(\`\${this.baseUrl}\${endpoint}\`, {
|
|
237
|
+
...options,
|
|
238
|
+
signal: controller.signal,
|
|
239
|
+
headers: {
|
|
240
|
+
'Content-Type': 'application/json',
|
|
241
|
+
...options.headers,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
clearTimeout(timeoutId);
|
|
246
|
+
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const data = await response.json();
|
|
252
|
+
return { data, status: response.status };
|
|
253
|
+
} catch (error) {
|
|
254
|
+
clearTimeout(timeoutId);
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getAll<T>(): Promise<T[]> {
|
|
260
|
+
const response = await this.request<T[]>('');
|
|
261
|
+
return response.data;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async getById<T>(id: string | number): Promise<T> {
|
|
265
|
+
const response = await this.request<T>(\`/\${id}\`);
|
|
266
|
+
return response.data;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async create<T>(data: Partial<T>): Promise<T> {
|
|
270
|
+
const response = await this.request<T>('', {
|
|
271
|
+
method: 'POST',
|
|
272
|
+
body: JSON.stringify(data),
|
|
273
|
+
});
|
|
274
|
+
return response.data;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async update<T>(id: string | number, data: Partial<T>): Promise<T> {
|
|
278
|
+
const response = await this.request<T>(\`/\${id}\`, {
|
|
279
|
+
method: 'PUT',
|
|
280
|
+
body: JSON.stringify(data),
|
|
281
|
+
});
|
|
282
|
+
return response.data;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async delete(id: string | number): Promise<void> {
|
|
286
|
+
await this.request(\`/\${id}\`, { method: 'DELETE' });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export const ${name.toLowerCase()}Service = new ${serviceName}();
|
|
291
|
+
export default ${serviceName};
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
return `/**
|
|
295
|
+
* ${serviceName}
|
|
296
|
+
*/
|
|
297
|
+
|
|
298
|
+
class ${serviceName} {
|
|
299
|
+
constructor(config = {}) {
|
|
300
|
+
this.baseUrl = config.baseUrl || '/api';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async getAll() {
|
|
304
|
+
const response = await fetch(this.baseUrl);
|
|
305
|
+
return response.json();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async getById(id) {
|
|
309
|
+
const response = await fetch(\`\${this.baseUrl}/\${id}\`);
|
|
310
|
+
return response.json();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async create(data) {
|
|
314
|
+
const response = await fetch(this.baseUrl, {
|
|
315
|
+
method: 'POST',
|
|
316
|
+
headers: { 'Content-Type': 'application/json' },
|
|
317
|
+
body: JSON.stringify(data),
|
|
318
|
+
});
|
|
319
|
+
return response.json();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async update(id, data) {
|
|
323
|
+
const response = await fetch(\`\${this.baseUrl}/\${id}\`, {
|
|
324
|
+
method: 'PUT',
|
|
325
|
+
headers: { 'Content-Type': 'application/json' },
|
|
326
|
+
body: JSON.stringify(data),
|
|
327
|
+
});
|
|
328
|
+
return response.json();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async delete(id) {
|
|
332
|
+
await fetch(\`\${this.baseUrl}/\${id}\`, { method: 'DELETE' });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export default ${serviceName};
|
|
337
|
+
`;
|
|
338
|
+
},
|
|
339
|
+
// Test file
|
|
340
|
+
test: (name, opts) => {
|
|
341
|
+
const framework = opts?.framework || 'vitest';
|
|
342
|
+
if (framework === 'jest') {
|
|
343
|
+
return `/**
|
|
344
|
+
* Tests for ${name}
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
describe('${name}', () => {
|
|
348
|
+
beforeEach(() => {
|
|
349
|
+
// Setup before each test
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
afterEach(() => {
|
|
353
|
+
// Cleanup after each test
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe('initialization', () => {
|
|
357
|
+
it('should initialize correctly', () => {
|
|
358
|
+
// Arrange
|
|
359
|
+
// Act
|
|
360
|
+
// Assert
|
|
361
|
+
expect(true).toBe(true);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe('main functionality', () => {
|
|
366
|
+
it('should perform main operation', () => {
|
|
367
|
+
// TODO: Implement test
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('should handle edge cases', () => {
|
|
371
|
+
// TODO: Implement test
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should handle errors gracefully', () => {
|
|
375
|
+
// TODO: Implement test
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
`;
|
|
380
|
+
}
|
|
381
|
+
return `/**
|
|
382
|
+
* Tests for ${name}
|
|
383
|
+
*/
|
|
384
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
385
|
+
|
|
386
|
+
describe('${name}', () => {
|
|
387
|
+
beforeEach(() => {
|
|
388
|
+
// Setup before each test
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
afterEach(() => {
|
|
392
|
+
vi.restoreAllMocks();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('initialization', () => {
|
|
396
|
+
it('should initialize correctly', () => {
|
|
397
|
+
// Arrange
|
|
398
|
+
// Act
|
|
399
|
+
// Assert
|
|
400
|
+
expect(true).toBe(true);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
describe('main functionality', () => {
|
|
405
|
+
it('should perform main operation', async () => {
|
|
406
|
+
// TODO: Implement test
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('should handle edge cases', () => {
|
|
410
|
+
// TODO: Implement test
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('should handle errors gracefully', () => {
|
|
414
|
+
// TODO: Implement test
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe('integration', () => {
|
|
419
|
+
it('should work with other components', () => {
|
|
420
|
+
// TODO: Implement integration test
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
`;
|
|
425
|
+
},
|
|
426
|
+
// API endpoint
|
|
427
|
+
api: (name, opts) => {
|
|
428
|
+
const ts = opts?.typescript !== false;
|
|
429
|
+
const framework = opts?.framework || 'express';
|
|
430
|
+
if (framework === 'nextjs') {
|
|
431
|
+
return `/**
|
|
432
|
+
* API Route: /api/${name.toLowerCase()}
|
|
433
|
+
*/
|
|
434
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
435
|
+
|
|
436
|
+
// GET /api/${name.toLowerCase()}
|
|
437
|
+
export async function GET(request: NextRequest) {
|
|
438
|
+
try {
|
|
439
|
+
// TODO: Implement GET logic
|
|
440
|
+
const data = { message: '${name} endpoint' };
|
|
441
|
+
|
|
442
|
+
return NextResponse.json(data);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
return NextResponse.json(
|
|
445
|
+
{ error: 'Internal Server Error' },
|
|
446
|
+
{ status: 500 }
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// POST /api/${name.toLowerCase()}
|
|
452
|
+
export async function POST(request: NextRequest) {
|
|
453
|
+
try {
|
|
454
|
+
const body = await request.json();
|
|
455
|
+
|
|
456
|
+
// TODO: Implement POST logic
|
|
457
|
+
// Validate body
|
|
458
|
+
// Create resource
|
|
459
|
+
|
|
460
|
+
return NextResponse.json(
|
|
461
|
+
{ message: 'Created', data: body },
|
|
462
|
+
{ status: 201 }
|
|
463
|
+
);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
return NextResponse.json(
|
|
466
|
+
{ error: 'Internal Server Error' },
|
|
467
|
+
{ status: 500 }
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// PUT /api/${name.toLowerCase()}
|
|
473
|
+
export async function PUT(request: NextRequest) {
|
|
474
|
+
try {
|
|
475
|
+
const body = await request.json();
|
|
476
|
+
|
|
477
|
+
// TODO: Implement PUT logic
|
|
478
|
+
|
|
479
|
+
return NextResponse.json({ message: 'Updated', data: body });
|
|
480
|
+
} catch (error) {
|
|
481
|
+
return NextResponse.json(
|
|
482
|
+
{ error: 'Internal Server Error' },
|
|
483
|
+
{ status: 500 }
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// DELETE /api/${name.toLowerCase()}
|
|
489
|
+
export async function DELETE(request: NextRequest) {
|
|
490
|
+
try {
|
|
491
|
+
// TODO: Implement DELETE logic
|
|
492
|
+
|
|
493
|
+
return NextResponse.json({ message: 'Deleted' });
|
|
494
|
+
} catch (error) {
|
|
495
|
+
return NextResponse.json(
|
|
496
|
+
{ error: 'Internal Server Error' },
|
|
497
|
+
{ status: 500 }
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
`;
|
|
502
|
+
}
|
|
503
|
+
// Express
|
|
504
|
+
if (ts) {
|
|
505
|
+
return `/**
|
|
506
|
+
* ${name} API Routes
|
|
507
|
+
*/
|
|
508
|
+
import { Router, Request, Response, NextFunction } from 'express';
|
|
509
|
+
|
|
510
|
+
const router = Router();
|
|
511
|
+
|
|
512
|
+
// Middleware for this router
|
|
513
|
+
router.use((req: Request, res: Response, next: NextFunction) => {
|
|
514
|
+
// Add any route-specific middleware here
|
|
515
|
+
next();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// GET /api/${name.toLowerCase()}
|
|
519
|
+
router.get('/', async (req: Request, res: Response) => {
|
|
520
|
+
try {
|
|
521
|
+
// TODO: Implement GET logic
|
|
522
|
+
res.json({ message: '${name} list' });
|
|
523
|
+
} catch (error) {
|
|
524
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// GET /api/${name.toLowerCase()}/:id
|
|
529
|
+
router.get('/:id', async (req: Request, res: Response) => {
|
|
530
|
+
try {
|
|
531
|
+
const { id } = req.params;
|
|
532
|
+
// TODO: Implement GET by ID logic
|
|
533
|
+
res.json({ id, message: '${name} details' });
|
|
534
|
+
} catch (error) {
|
|
535
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// POST /api/${name.toLowerCase()}
|
|
540
|
+
router.post('/', async (req: Request, res: Response) => {
|
|
541
|
+
try {
|
|
542
|
+
const data = req.body;
|
|
543
|
+
// TODO: Implement POST logic
|
|
544
|
+
res.status(201).json({ message: 'Created', data });
|
|
545
|
+
} catch (error) {
|
|
546
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// PUT /api/${name.toLowerCase()}/:id
|
|
551
|
+
router.put('/:id', async (req: Request, res: Response) => {
|
|
552
|
+
try {
|
|
553
|
+
const { id } = req.params;
|
|
554
|
+
const data = req.body;
|
|
555
|
+
// TODO: Implement PUT logic
|
|
556
|
+
res.json({ id, message: 'Updated', data });
|
|
557
|
+
} catch (error) {
|
|
558
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// DELETE /api/${name.toLowerCase()}/:id
|
|
563
|
+
router.delete('/:id', async (req: Request, res: Response) => {
|
|
564
|
+
try {
|
|
565
|
+
const { id } = req.params;
|
|
566
|
+
// TODO: Implement DELETE logic
|
|
567
|
+
res.json({ id, message: 'Deleted' });
|
|
568
|
+
} catch (error) {
|
|
569
|
+
res.status(500).json({ error: 'Internal Server Error' });
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
export default router;
|
|
574
|
+
`;
|
|
575
|
+
}
|
|
576
|
+
return `/**
|
|
577
|
+
* ${name} API Routes
|
|
578
|
+
*/
|
|
579
|
+
const express = require('express');
|
|
580
|
+
const router = express.Router();
|
|
581
|
+
|
|
582
|
+
router.get('/', async (req, res) => {
|
|
583
|
+
res.json({ message: '${name} list' });
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
router.get('/:id', async (req, res) => {
|
|
587
|
+
res.json({ id: req.params.id });
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
router.post('/', async (req, res) => {
|
|
591
|
+
res.status(201).json({ data: req.body });
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
router.put('/:id', async (req, res) => {
|
|
595
|
+
res.json({ id: req.params.id, data: req.body });
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
router.delete('/:id', async (req, res) => {
|
|
599
|
+
res.json({ message: 'Deleted' });
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
module.exports = router;
|
|
603
|
+
`;
|
|
604
|
+
},
|
|
605
|
+
// Model/Entity
|
|
606
|
+
model: (name, opts) => {
|
|
607
|
+
const ts = opts?.typescript !== false;
|
|
608
|
+
if (ts) {
|
|
609
|
+
return `/**
|
|
610
|
+
* ${name} Model
|
|
611
|
+
*/
|
|
612
|
+
|
|
613
|
+
export interface ${name} {
|
|
614
|
+
id: string;
|
|
615
|
+
createdAt: Date;
|
|
616
|
+
updatedAt: Date;
|
|
617
|
+
// Add your fields here
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export interface Create${name}Input {
|
|
621
|
+
// Fields required to create a ${name}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export interface Update${name}Input {
|
|
625
|
+
// Fields that can be updated
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* ${name} validation schema
|
|
630
|
+
*/
|
|
631
|
+
export const ${name.toLowerCase()}Schema = {
|
|
632
|
+
id: { type: 'string', required: true },
|
|
633
|
+
createdAt: { type: 'date', required: true },
|
|
634
|
+
updatedAt: { type: 'date', required: true },
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Create a new ${name} instance
|
|
639
|
+
*/
|
|
640
|
+
export function create${name}(input: Create${name}Input): ${name} {
|
|
641
|
+
const now = new Date();
|
|
642
|
+
return {
|
|
643
|
+
id: crypto.randomUUID(),
|
|
644
|
+
createdAt: now,
|
|
645
|
+
updatedAt: now,
|
|
646
|
+
...input,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Validate ${name} data
|
|
652
|
+
*/
|
|
653
|
+
export function validate${name}(data: unknown): data is ${name} {
|
|
654
|
+
if (!data || typeof data !== 'object') return false;
|
|
655
|
+
const obj = data as Record<string, unknown>;
|
|
656
|
+
return (
|
|
657
|
+
typeof obj.id === 'string' &&
|
|
658
|
+
obj.createdAt instanceof Date &&
|
|
659
|
+
obj.updatedAt instanceof Date
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
export default ${name};
|
|
664
|
+
`;
|
|
665
|
+
}
|
|
666
|
+
return `/**
|
|
667
|
+
* ${name} Model
|
|
668
|
+
*/
|
|
669
|
+
|
|
670
|
+
function create${name}(input) {
|
|
671
|
+
const now = new Date();
|
|
672
|
+
return {
|
|
673
|
+
id: crypto.randomUUID(),
|
|
674
|
+
createdAt: now,
|
|
675
|
+
updatedAt: now,
|
|
676
|
+
...input,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function validate${name}(data) {
|
|
681
|
+
return data && typeof data.id === 'string';
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
module.exports = { create${name}, validate${name} };
|
|
685
|
+
`;
|
|
686
|
+
},
|
|
687
|
+
// Utility module
|
|
688
|
+
util: (name, opts) => {
|
|
689
|
+
const ts = opts?.typescript !== false;
|
|
690
|
+
if (ts) {
|
|
691
|
+
return `/**
|
|
692
|
+
* ${name} Utilities
|
|
693
|
+
*/
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Format a value for display
|
|
697
|
+
*/
|
|
698
|
+
export function format${name}(value: unknown): string {
|
|
699
|
+
if (value === null || value === undefined) return '';
|
|
700
|
+
return String(value);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Parse a string value
|
|
705
|
+
*/
|
|
706
|
+
export function parse${name}(value: string): unknown {
|
|
707
|
+
try {
|
|
708
|
+
return JSON.parse(value);
|
|
709
|
+
} catch {
|
|
710
|
+
return value;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Validate a ${name.toLowerCase()} value
|
|
716
|
+
*/
|
|
717
|
+
export function isValid${name}(value: unknown): boolean {
|
|
718
|
+
return value !== null && value !== undefined;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Compare two ${name.toLowerCase()} values
|
|
723
|
+
*/
|
|
724
|
+
export function compare${name}(a: unknown, b: unknown): number {
|
|
725
|
+
const strA = String(a);
|
|
726
|
+
const strB = String(b);
|
|
727
|
+
return strA.localeCompare(strB);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Create a default ${name.toLowerCase()} value
|
|
732
|
+
*/
|
|
733
|
+
export function createDefault${name}(): unknown {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Deep clone a ${name.toLowerCase()} value
|
|
739
|
+
*/
|
|
740
|
+
export function clone${name}<T>(value: T): T {
|
|
741
|
+
return JSON.parse(JSON.stringify(value));
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
export default {
|
|
745
|
+
format${name},
|
|
746
|
+
parse${name},
|
|
747
|
+
isValid${name},
|
|
748
|
+
compare${name},
|
|
749
|
+
createDefault${name},
|
|
750
|
+
clone${name},
|
|
751
|
+
};
|
|
752
|
+
`;
|
|
753
|
+
}
|
|
754
|
+
return `/**
|
|
755
|
+
* ${name} Utilities
|
|
756
|
+
*/
|
|
757
|
+
|
|
758
|
+
function format${name}(value) {
|
|
759
|
+
if (value == null) return '';
|
|
760
|
+
return String(value);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function parse${name}(value) {
|
|
764
|
+
try {
|
|
765
|
+
return JSON.parse(value);
|
|
766
|
+
} catch {
|
|
767
|
+
return value;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function isValid${name}(value) {
|
|
772
|
+
return value != null;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
module.exports = { format${name}, parse${name}, isValid${name} };
|
|
776
|
+
`;
|
|
777
|
+
},
|
|
778
|
+
};
|
|
779
|
+
export async function handleGenerate(args, state) {
|
|
780
|
+
const { type, name, options = {} } = args;
|
|
781
|
+
if (!type || !name) {
|
|
782
|
+
return jsonResponse({
|
|
783
|
+
error: 'Both type and name are required',
|
|
784
|
+
availableTypes: Object.keys(templates),
|
|
785
|
+
example: 'generate type:"component" name:"UserCard"'
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
// Detect project conventions
|
|
789
|
+
const conventions = options.scanProject !== false ? getProjectConventions() : null;
|
|
790
|
+
// Infer TypeScript from project type or conventions
|
|
791
|
+
if (options.typescript === undefined) {
|
|
792
|
+
if (state.activeProjectType?.includes('typescript')) {
|
|
793
|
+
options.typescript = true;
|
|
794
|
+
}
|
|
795
|
+
else if (conventions?.strictMode) {
|
|
796
|
+
options.typescript = true;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
// Infer framework from conventions
|
|
800
|
+
if (!options.framework && conventions) {
|
|
801
|
+
if (conventions.stateManagement === 'zustand') {
|
|
802
|
+
options.framework = 'zustand';
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
const generator = templates[type];
|
|
806
|
+
if (!generator) {
|
|
807
|
+
return jsonResponse({
|
|
808
|
+
error: `Unknown template type: ${type}`,
|
|
809
|
+
availableTypes: Object.keys(templates)
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
logger.info('Generating template', { type, name, options, conventions: conventions?.sources });
|
|
813
|
+
let code = generator(name, options);
|
|
814
|
+
// Apply detected conventions to generated code
|
|
815
|
+
if (conventions && conventions.confidence !== 'low') {
|
|
816
|
+
code = applyConventions(code, conventions);
|
|
817
|
+
}
|
|
818
|
+
const ext = options.typescript !== false ? 'ts' : 'js';
|
|
819
|
+
const filename = type === 'test'
|
|
820
|
+
? `${name}.test.${ext}`
|
|
821
|
+
: type === 'component'
|
|
822
|
+
? `${name}.tsx`
|
|
823
|
+
: `${name}.${ext}`;
|
|
824
|
+
return jsonResponse({
|
|
825
|
+
success: true,
|
|
826
|
+
type,
|
|
827
|
+
name,
|
|
828
|
+
filename,
|
|
829
|
+
code,
|
|
830
|
+
conventions: conventions ? {
|
|
831
|
+
applied: conventions.confidence !== 'low',
|
|
832
|
+
sources: conventions.sources,
|
|
833
|
+
indentation: conventions.indentation,
|
|
834
|
+
quotes: conventions.quotes,
|
|
835
|
+
semicolons: conventions.semicolons
|
|
836
|
+
} : null,
|
|
837
|
+
instructions: [
|
|
838
|
+
`1. Create file: ${filename}`,
|
|
839
|
+
'2. Paste the generated code',
|
|
840
|
+
'3. Customize the TODO sections',
|
|
841
|
+
options.withTests ? `4. Run: npm test ${name}` : null,
|
|
842
|
+
].filter(Boolean)
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
//# sourceMappingURL=generate.js.map
|