kimi-vercel-ai-sdk-provider 0.3.0 → 0.5.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 +567 -17
- package/dist/index.d.mts +1750 -3
- package/dist/index.d.ts +1750 -3
- package/dist/index.js +2317 -161
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2292 -160
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/auto-detect.test.ts +140 -0
- package/src/__tests__/code-validation.test.ts +267 -0
- package/src/__tests__/ensemble.test.ts +242 -0
- package/src/__tests__/file-cache.test.ts +310 -0
- package/src/__tests__/model-config.test.ts +120 -0
- package/src/__tests__/multi-agent.test.ts +201 -0
- package/src/__tests__/project-tools.test.ts +181 -0
- package/src/__tests__/reasoning-utils.test.ts +164 -0
- package/src/__tests__/tools.test.ts +76 -8
- package/src/chat/kimi-chat-language-model.ts +21 -2
- package/src/chat/kimi-chat-settings.ts +15 -1
- package/src/code-validation/detector.ts +319 -0
- package/src/code-validation/index.ts +31 -0
- package/src/code-validation/types.ts +291 -0
- package/src/code-validation/validator.ts +547 -0
- package/src/core/errors.ts +91 -0
- package/src/core/index.ts +15 -3
- package/src/core/types.ts +57 -2
- package/src/core/utils.ts +138 -0
- package/src/ensemble/index.ts +17 -0
- package/src/ensemble/multi-sampler.ts +433 -0
- package/src/ensemble/types.ts +279 -0
- package/src/files/attachment-processor.ts +51 -4
- package/src/files/file-cache.ts +260 -0
- package/src/files/index.ts +16 -1
- package/src/index.ts +102 -3
- package/src/kimi-provider.ts +354 -1
- package/src/multi-agent/index.ts +21 -0
- package/src/multi-agent/types.ts +312 -0
- package/src/multi-agent/workflows.ts +539 -0
- package/src/project-tools/index.ts +16 -0
- package/src/project-tools/scaffolder.ts +494 -0
- package/src/project-tools/types.ts +244 -0
- package/src/tools/auto-detect.ts +276 -0
- package/src/tools/index.ts +6 -2
- package/src/tools/prepare-tools.ts +179 -4
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project scaffolder implementation.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { OutputFormat, ProjectFile, ProjectMetadata, ProjectType, ScaffoldConfig, ScaffoldResult } from './types';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Function type for generating text.
|
|
14
|
+
*/
|
|
15
|
+
export type GenerateTextFunction = (prompt: string) => Promise<{ text: string }>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Options for creating a scaffolder.
|
|
19
|
+
*/
|
|
20
|
+
export interface ScaffolderOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Function to generate text from the model.
|
|
23
|
+
*/
|
|
24
|
+
generateText: GenerateTextFunction;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Default model ID to use.
|
|
28
|
+
*/
|
|
29
|
+
modelId?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// ProjectScaffolder Class
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Scaffolder for generating project structures.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const scaffolder = new ProjectScaffolder({
|
|
42
|
+
* generateText: async (prompt) => {
|
|
43
|
+
* const result = await generateText({ model, prompt });
|
|
44
|
+
* return { text: result.text };
|
|
45
|
+
* },
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* const project = await scaffolder.scaffold(
|
|
49
|
+
* 'A REST API for todo management',
|
|
50
|
+
* { type: 'express', includeTests: true }
|
|
51
|
+
* );
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export class ProjectScaffolder {
|
|
55
|
+
private generateText: GenerateTextFunction;
|
|
56
|
+
|
|
57
|
+
constructor(options: ScaffolderOptions) {
|
|
58
|
+
this.generateText = options.generateText;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Scaffold a new project based on a description.
|
|
63
|
+
*
|
|
64
|
+
* @param description - Description of the project to create
|
|
65
|
+
* @param config - Scaffold configuration
|
|
66
|
+
* @returns Scaffold result with files and instructions
|
|
67
|
+
*/
|
|
68
|
+
async scaffold(description: string, config: ScaffoldConfig = {}): Promise<ScaffoldResult> {
|
|
69
|
+
const {
|
|
70
|
+
type = 'auto',
|
|
71
|
+
includeTests = true,
|
|
72
|
+
includeCI = false,
|
|
73
|
+
includeDocs = true,
|
|
74
|
+
includeDocker = false,
|
|
75
|
+
includeLinting = true,
|
|
76
|
+
outputFormat = 'files',
|
|
77
|
+
useTypeScript = true,
|
|
78
|
+
features = [],
|
|
79
|
+
customTemplate
|
|
80
|
+
} = config;
|
|
81
|
+
|
|
82
|
+
// Build the prompt for project generation
|
|
83
|
+
const prompt = this.buildScaffoldPrompt(description, {
|
|
84
|
+
type,
|
|
85
|
+
includeTests,
|
|
86
|
+
includeCI,
|
|
87
|
+
includeDocs,
|
|
88
|
+
includeDocker,
|
|
89
|
+
includeLinting,
|
|
90
|
+
useTypeScript,
|
|
91
|
+
features,
|
|
92
|
+
customTemplate
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Generate the project structure
|
|
96
|
+
const result = await this.generateText(prompt);
|
|
97
|
+
|
|
98
|
+
// Parse the response
|
|
99
|
+
const parsed = this.parseResponse(result.text, type);
|
|
100
|
+
|
|
101
|
+
// Format output based on config
|
|
102
|
+
return this.formatResult(parsed, outputFormat, result.text);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Build the scaffold prompt.
|
|
107
|
+
*/
|
|
108
|
+
private buildScaffoldPrompt(
|
|
109
|
+
description: string,
|
|
110
|
+
config: {
|
|
111
|
+
type: ProjectType;
|
|
112
|
+
includeTests: boolean;
|
|
113
|
+
includeCI: boolean;
|
|
114
|
+
includeDocs: boolean;
|
|
115
|
+
includeDocker: boolean;
|
|
116
|
+
includeLinting: boolean;
|
|
117
|
+
useTypeScript: boolean;
|
|
118
|
+
features: string[];
|
|
119
|
+
customTemplate?: string;
|
|
120
|
+
}
|
|
121
|
+
): string {
|
|
122
|
+
const parts: string[] = [];
|
|
123
|
+
|
|
124
|
+
parts.push('Generate a complete project structure for the following description:');
|
|
125
|
+
parts.push(`"${description}"`);
|
|
126
|
+
parts.push('');
|
|
127
|
+
|
|
128
|
+
if (config.type !== 'auto') {
|
|
129
|
+
parts.push(`Framework/Type: ${config.type}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
parts.push(`Language: ${config.useTypeScript ? 'TypeScript' : 'JavaScript/Python/Go (as appropriate)'}`);
|
|
133
|
+
|
|
134
|
+
const includes: string[] = [];
|
|
135
|
+
if (config.includeTests) {
|
|
136
|
+
includes.push('comprehensive test files');
|
|
137
|
+
}
|
|
138
|
+
if (config.includeCI) {
|
|
139
|
+
includes.push('GitHub Actions CI/CD workflow');
|
|
140
|
+
}
|
|
141
|
+
if (config.includeDocs) {
|
|
142
|
+
includes.push('README with setup instructions');
|
|
143
|
+
}
|
|
144
|
+
if (config.includeDocker) {
|
|
145
|
+
includes.push('Dockerfile and docker-compose.yml');
|
|
146
|
+
}
|
|
147
|
+
if (config.includeLinting) {
|
|
148
|
+
includes.push('ESLint/linting configuration');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (includes.length > 0) {
|
|
152
|
+
parts.push(`Include: ${includes.join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (config.features.length > 0) {
|
|
156
|
+
parts.push(`Additional features: ${config.features.join(', ')}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (config.customTemplate) {
|
|
160
|
+
parts.push('');
|
|
161
|
+
parts.push('Custom requirements:');
|
|
162
|
+
parts.push(config.customTemplate);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
parts.push('');
|
|
166
|
+
parts.push('Respond with a JSON object in this exact format:');
|
|
167
|
+
parts.push('```json');
|
|
168
|
+
parts.push('{');
|
|
169
|
+
parts.push(' "projectName": "project-name",');
|
|
170
|
+
parts.push(' "projectType": "detected-type",');
|
|
171
|
+
parts.push(' "technologies": ["tech1", "tech2"],');
|
|
172
|
+
parts.push(' "files": [');
|
|
173
|
+
parts.push(' {');
|
|
174
|
+
parts.push(' "path": "relative/path/to/file.ts",');
|
|
175
|
+
parts.push(' "content": "full file content here",');
|
|
176
|
+
parts.push(' "description": "what this file does"');
|
|
177
|
+
parts.push(' }');
|
|
178
|
+
parts.push(' ],');
|
|
179
|
+
parts.push(' "setupCommands": ["npm install", "npm run dev"],');
|
|
180
|
+
parts.push(' "estimatedSetupTime": "5 minutes"');
|
|
181
|
+
parts.push('}');
|
|
182
|
+
parts.push('```');
|
|
183
|
+
parts.push('');
|
|
184
|
+
parts.push(
|
|
185
|
+
'Include ALL necessary files for a working project: package.json/requirements.txt, source files, config files, etc.'
|
|
186
|
+
);
|
|
187
|
+
parts.push('Make sure file contents are complete and functional, not placeholders.');
|
|
188
|
+
|
|
189
|
+
return parts.join('\n');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Parse the model response into structured data.
|
|
194
|
+
*/
|
|
195
|
+
private parseResponse(
|
|
196
|
+
text: string,
|
|
197
|
+
defaultType: ProjectType
|
|
198
|
+
): {
|
|
199
|
+
files: ProjectFile[];
|
|
200
|
+
metadata: ProjectMetadata;
|
|
201
|
+
setupCommands: string[];
|
|
202
|
+
} {
|
|
203
|
+
// Try to extract JSON from the response
|
|
204
|
+
const jsonMatch = text.match(/```json\n?([\s\S]*?)```/) || text.match(/\{[\s\S]*"files"[\s\S]*\}/);
|
|
205
|
+
|
|
206
|
+
if (jsonMatch) {
|
|
207
|
+
try {
|
|
208
|
+
const json = JSON.parse(jsonMatch[1] || jsonMatch[0]);
|
|
209
|
+
const files: ProjectFile[] = (json.files || []).map((f: ProjectFile) => {
|
|
210
|
+
return {
|
|
211
|
+
path: f.path,
|
|
212
|
+
content: f.content,
|
|
213
|
+
description: f.description
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// If projectType is explicitly provided in JSON, use it
|
|
218
|
+
// If defaultType is 'auto', try to detect from files
|
|
219
|
+
// Otherwise use the defaultType
|
|
220
|
+
let projectType: ProjectType;
|
|
221
|
+
if (json.projectType) {
|
|
222
|
+
projectType = json.projectType as ProjectType;
|
|
223
|
+
} else if (defaultType === 'auto') {
|
|
224
|
+
projectType = this.detectProjectType(files);
|
|
225
|
+
} else {
|
|
226
|
+
projectType = defaultType;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
files,
|
|
231
|
+
metadata: {
|
|
232
|
+
projectType,
|
|
233
|
+
projectName: json.projectName || 'my-project',
|
|
234
|
+
fileCount: files.length,
|
|
235
|
+
totalSize: files.reduce((sum: number, f: ProjectFile) => sum + (f.content?.length || 0), 0),
|
|
236
|
+
estimatedSetupTime: json.estimatedSetupTime || 'unknown',
|
|
237
|
+
technologies: json.technologies || [],
|
|
238
|
+
features: []
|
|
239
|
+
},
|
|
240
|
+
setupCommands: json.setupCommands || []
|
|
241
|
+
};
|
|
242
|
+
} catch {
|
|
243
|
+
// JSON parsing failed, try fallback
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Fallback: extract files from code blocks
|
|
248
|
+
return this.parseFromCodeBlocks(text, defaultType);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Parse files from markdown code blocks.
|
|
253
|
+
*/
|
|
254
|
+
private parseFromCodeBlocks(
|
|
255
|
+
text: string,
|
|
256
|
+
defaultType: ProjectType
|
|
257
|
+
): {
|
|
258
|
+
files: ProjectFile[];
|
|
259
|
+
metadata: ProjectMetadata;
|
|
260
|
+
setupCommands: string[];
|
|
261
|
+
} {
|
|
262
|
+
const files: ProjectFile[] = [];
|
|
263
|
+
|
|
264
|
+
// Match code blocks with file paths in the language annotation
|
|
265
|
+
// e.g., ```typescript:src/index.ts or ```path/to/file.ts
|
|
266
|
+
const fileBlockRegex = /```(?:(\w+):)?([\w./-]+)\n([\s\S]*?)```/g;
|
|
267
|
+
let match: RegExpExecArray | null = fileBlockRegex.exec(text);
|
|
268
|
+
|
|
269
|
+
while (match !== null) {
|
|
270
|
+
const path = match[2];
|
|
271
|
+
const content = match[3].trim();
|
|
272
|
+
|
|
273
|
+
// Skip if path doesn't look like a file
|
|
274
|
+
if (path.includes('.')) {
|
|
275
|
+
files.push({
|
|
276
|
+
path,
|
|
277
|
+
content,
|
|
278
|
+
description: undefined
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
match = fileBlockRegex.exec(text);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// If no files found with paths, try generic code blocks
|
|
285
|
+
if (files.length === 0) {
|
|
286
|
+
const genericBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
|
|
287
|
+
let blockMatch: RegExpExecArray | null = genericBlockRegex.exec(text);
|
|
288
|
+
let index = 0;
|
|
289
|
+
|
|
290
|
+
while (blockMatch !== null) {
|
|
291
|
+
const lang = blockMatch[1] || 'txt';
|
|
292
|
+
const content = blockMatch[2].trim();
|
|
293
|
+
|
|
294
|
+
if (content.length > 10) {
|
|
295
|
+
// Skip trivial blocks
|
|
296
|
+
const ext = this.getExtensionForLanguage(lang);
|
|
297
|
+
files.push({
|
|
298
|
+
path: `file${index}.${ext}`,
|
|
299
|
+
content
|
|
300
|
+
});
|
|
301
|
+
index++;
|
|
302
|
+
}
|
|
303
|
+
blockMatch = genericBlockRegex.exec(text);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
files,
|
|
309
|
+
metadata: {
|
|
310
|
+
projectType: defaultType === 'auto' ? this.detectProjectType(files) : defaultType,
|
|
311
|
+
projectName: 'my-project',
|
|
312
|
+
fileCount: files.length,
|
|
313
|
+
totalSize: files.reduce((sum, f) => sum + f.content.length, 0),
|
|
314
|
+
estimatedSetupTime: 'unknown',
|
|
315
|
+
technologies: [],
|
|
316
|
+
features: []
|
|
317
|
+
},
|
|
318
|
+
setupCommands: []
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Detect project type from files.
|
|
324
|
+
*/
|
|
325
|
+
private detectProjectType(files: ProjectFile[]): ProjectType {
|
|
326
|
+
const paths = files.map((f) => f.path.toLowerCase());
|
|
327
|
+
const contents = files.map((f) => f.content);
|
|
328
|
+
|
|
329
|
+
if (paths.some((p) => p.includes('next.config'))) {
|
|
330
|
+
return 'nextjs';
|
|
331
|
+
}
|
|
332
|
+
if (contents.some((c) => c.includes('from fastapi'))) {
|
|
333
|
+
return 'fastapi';
|
|
334
|
+
}
|
|
335
|
+
if (contents.some((c) => c.includes('from flask'))) {
|
|
336
|
+
return 'flask';
|
|
337
|
+
}
|
|
338
|
+
if (paths.some((p) => p.includes('package.json'))) {
|
|
339
|
+
const pkgFile = files.find((f) => f.path.includes('package.json'));
|
|
340
|
+
if (pkgFile) {
|
|
341
|
+
if (pkgFile.content.includes('"react"')) {
|
|
342
|
+
return 'react';
|
|
343
|
+
}
|
|
344
|
+
if (pkgFile.content.includes('"vue"')) {
|
|
345
|
+
return 'vue';
|
|
346
|
+
}
|
|
347
|
+
if (pkgFile.content.includes('"express"')) {
|
|
348
|
+
return 'express';
|
|
349
|
+
}
|
|
350
|
+
if (pkgFile.content.includes('"fastify"')) {
|
|
351
|
+
return 'fastify';
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return 'node';
|
|
355
|
+
}
|
|
356
|
+
if (paths.some((p) => p.includes('requirements.txt') || p.endsWith('.py'))) {
|
|
357
|
+
return 'python';
|
|
358
|
+
}
|
|
359
|
+
if (paths.some((p) => p.includes('go.mod') || p.endsWith('.go'))) {
|
|
360
|
+
return 'go';
|
|
361
|
+
}
|
|
362
|
+
if (paths.some((p) => p.includes('cargo.toml') || p.endsWith('.rs'))) {
|
|
363
|
+
return 'rust';
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return 'node';
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Get file extension for a language.
|
|
371
|
+
*/
|
|
372
|
+
private getExtensionForLanguage(lang: string): string {
|
|
373
|
+
const map: Record<string, string> = {
|
|
374
|
+
typescript: 'ts',
|
|
375
|
+
javascript: 'js',
|
|
376
|
+
python: 'py',
|
|
377
|
+
go: 'go',
|
|
378
|
+
rust: 'rs',
|
|
379
|
+
json: 'json',
|
|
380
|
+
yaml: 'yml',
|
|
381
|
+
markdown: 'md',
|
|
382
|
+
html: 'html',
|
|
383
|
+
css: 'css',
|
|
384
|
+
shell: 'sh',
|
|
385
|
+
bash: 'sh',
|
|
386
|
+
dockerfile: 'dockerfile',
|
|
387
|
+
sql: 'sql'
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return map[lang.toLowerCase()] || 'txt';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Format the result based on output format.
|
|
395
|
+
*/
|
|
396
|
+
private formatResult(
|
|
397
|
+
parsed: {
|
|
398
|
+
files: ProjectFile[];
|
|
399
|
+
metadata: ProjectMetadata;
|
|
400
|
+
setupCommands: string[];
|
|
401
|
+
},
|
|
402
|
+
format: OutputFormat,
|
|
403
|
+
rawResponse: string
|
|
404
|
+
): ScaffoldResult {
|
|
405
|
+
const instructions = this.generateInstructions(parsed);
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
files: format === 'instructions' ? [] : parsed.files,
|
|
409
|
+
instructions,
|
|
410
|
+
setupCommands: parsed.setupCommands,
|
|
411
|
+
metadata: parsed.metadata,
|
|
412
|
+
rawResponse: format === 'json' ? rawResponse : undefined
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Generate setup instructions.
|
|
418
|
+
*/
|
|
419
|
+
private generateInstructions(parsed: {
|
|
420
|
+
files: ProjectFile[];
|
|
421
|
+
metadata: ProjectMetadata;
|
|
422
|
+
setupCommands: string[];
|
|
423
|
+
}): string {
|
|
424
|
+
const lines: string[] = [];
|
|
425
|
+
|
|
426
|
+
lines.push(`# ${parsed.metadata.projectName} Setup`);
|
|
427
|
+
lines.push('');
|
|
428
|
+
lines.push(`Project type: ${parsed.metadata.projectType}`);
|
|
429
|
+
lines.push(`Files: ${parsed.metadata.fileCount}`);
|
|
430
|
+
lines.push('');
|
|
431
|
+
|
|
432
|
+
if (parsed.metadata.technologies.length > 0) {
|
|
433
|
+
lines.push('## Technologies');
|
|
434
|
+
lines.push(parsed.metadata.technologies.map((t) => `- ${t}`).join('\n'));
|
|
435
|
+
lines.push('');
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
lines.push('## Quick Start');
|
|
439
|
+
lines.push('');
|
|
440
|
+
lines.push('1. Create project directory:');
|
|
441
|
+
lines.push('```bash');
|
|
442
|
+
lines.push(`mkdir ${parsed.metadata.projectName}`);
|
|
443
|
+
lines.push(`cd ${parsed.metadata.projectName}`);
|
|
444
|
+
lines.push('```');
|
|
445
|
+
lines.push('');
|
|
446
|
+
|
|
447
|
+
lines.push('2. Create the following files:');
|
|
448
|
+
for (const file of parsed.files.slice(0, 10)) {
|
|
449
|
+
lines.push(` - \`${file.path}\`${file.description ? `: ${file.description}` : ''}`);
|
|
450
|
+
}
|
|
451
|
+
if (parsed.files.length > 10) {
|
|
452
|
+
lines.push(` - ... and ${parsed.files.length - 10} more files`);
|
|
453
|
+
}
|
|
454
|
+
lines.push('');
|
|
455
|
+
|
|
456
|
+
if (parsed.setupCommands.length > 0) {
|
|
457
|
+
lines.push('3. Run setup commands:');
|
|
458
|
+
lines.push('```bash');
|
|
459
|
+
lines.push(parsed.setupCommands.join('\n'));
|
|
460
|
+
lines.push('```');
|
|
461
|
+
lines.push('');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (parsed.metadata.estimatedSetupTime !== 'unknown') {
|
|
465
|
+
lines.push(`Estimated setup time: ${parsed.metadata.estimatedSetupTime}`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return lines.join('\n');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ============================================================================
|
|
473
|
+
// Utility Functions
|
|
474
|
+
// ============================================================================
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Create an empty scaffold result.
|
|
478
|
+
*/
|
|
479
|
+
export function createEmptyScaffoldResult(error: string): ScaffoldResult {
|
|
480
|
+
return {
|
|
481
|
+
files: [],
|
|
482
|
+
instructions: `Error: ${error}`,
|
|
483
|
+
setupCommands: [],
|
|
484
|
+
metadata: {
|
|
485
|
+
projectType: 'auto',
|
|
486
|
+
projectName: 'unknown',
|
|
487
|
+
fileCount: 0,
|
|
488
|
+
totalSize: 0,
|
|
489
|
+
estimatedSetupTime: 'unknown',
|
|
490
|
+
technologies: [],
|
|
491
|
+
features: []
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|