faf-cli 2.4.0 → 2.4.2

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 (64) hide show
  1. package/README.md +154 -210
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +55 -143
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/art.d.ts.map +1 -1
  6. package/dist/commands/art.js +3 -2
  7. package/dist/commands/art.js.map +1 -1
  8. package/dist/commands/enhance-real.d.ts +18 -0
  9. package/dist/commands/enhance-real.d.ts.map +1 -0
  10. package/dist/commands/enhance-real.js +1027 -0
  11. package/dist/commands/enhance-real.js.map +1 -0
  12. package/dist/commands/init.d.ts +2 -0
  13. package/dist/commands/init.d.ts.map +1 -1
  14. package/dist/commands/init.js +3 -0
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/notifications.d.ts +12 -0
  17. package/dist/commands/notifications.d.ts.map +1 -0
  18. package/dist/commands/notifications.js +38 -0
  19. package/dist/commands/notifications.js.map +1 -0
  20. package/dist/commands/score-v3.d.ts.map +1 -1
  21. package/dist/commands/score-v3.js +7 -14
  22. package/dist/commands/score-v3.js.map +1 -1
  23. package/dist/commands/score.d.ts.map +1 -1
  24. package/dist/commands/score.js +10 -30
  25. package/dist/commands/score.js.map +1 -1
  26. package/dist/commands/subscribe.d.ts +12 -0
  27. package/dist/commands/subscribe.d.ts.map +1 -0
  28. package/dist/commands/subscribe.js +38 -0
  29. package/dist/commands/subscribe.js.map +1 -0
  30. package/dist/engine-bridge.d.ts +5 -5
  31. package/dist/engine-bridge.d.ts.map +1 -1
  32. package/dist/engine-bridge.js +7 -20
  33. package/dist/engine-bridge.js.map +1 -1
  34. package/dist/types/faf-types.d.ts +144 -0
  35. package/dist/types/faf-types.d.ts.map +1 -0
  36. package/dist/types/faf-types.js +21 -0
  37. package/dist/types/faf-types.js.map +1 -0
  38. package/dist/utils/announcements.d.ts +16 -0
  39. package/dist/utils/announcements.d.ts.map +1 -0
  40. package/dist/utils/announcements.js +136 -0
  41. package/dist/utils/announcements.js.map +1 -0
  42. package/dist/utils/art-gallery.d.ts +9 -1
  43. package/dist/utils/art-gallery.d.ts.map +1 -1
  44. package/dist/utils/art-gallery.js +46 -1
  45. package/dist/utils/art-gallery.js.map +1 -1
  46. package/dist/utils/championship-style.d.ts +1 -1
  47. package/dist/utils/championship-style.d.ts.map +1 -1
  48. package/dist/utils/championship-style.js +13 -17
  49. package/dist/utils/championship-style.js.map +1 -1
  50. package/dist/utils/email-opt-in.d.ts +31 -0
  51. package/dist/utils/email-opt-in.d.ts.map +1 -0
  52. package/dist/utils/email-opt-in.js +195 -0
  53. package/dist/utils/email-opt-in.js.map +1 -0
  54. package/dist/utils/feedback-messages.d.ts +13 -0
  55. package/dist/utils/feedback-messages.d.ts.map +1 -0
  56. package/dist/utils/feedback-messages.js +121 -0
  57. package/dist/utils/feedback-messages.js.map +1 -0
  58. package/dist/utils/score-header.js +16 -10
  59. package/dist/utils/score-header.js.map +1 -1
  60. package/dist/utils/update-checker.d.ts.map +1 -1
  61. package/dist/utils/update-checker.js +3 -12
  62. package/dist/utils/update-checker.js.map +1 -1
  63. package/dist/utils/yaml-generator.js +1 -1
  64. package/package.json +2 -2
@@ -0,0 +1,1027 @@
1
+ "use strict";
2
+ /**
3
+ * 🚀 REAL faf enhance - Actually improves scores with facts, not BS
4
+ * No placeholders, no mocks - real improvements based on project analysis
5
+ * RELENTLESS pursuit of highest score through real data or human input
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.realEnhanceFaf = realEnhanceFaf;
45
+ const chalk_1 = __importDefault(require("chalk"));
46
+ const fs_1 = require("fs");
47
+ const path = __importStar(require("path"));
48
+ const YAML = __importStar(require("yaml"));
49
+ const inquirer_1 = __importDefault(require("inquirer"));
50
+ const file_utils_1 = require("../utils/file-utils");
51
+ const faf_compiler_1 = require("../compiler/faf-compiler");
52
+ const fab_formats_engine_1 = require("../utils/fab-formats-engine");
53
+ const child_process_1 = require("child_process");
54
+ /**
55
+ * REAL Enhancement that RELENTLESSLY improves scores
56
+ * Loops until target score achieved or no more improvements possible
57
+ */
58
+ async function realEnhanceFaf(file, options = {}) {
59
+ try {
60
+ const fafPath = file || (await (0, file_utils_1.findFafFile)());
61
+ if (!fafPath) {
62
+ console.log(chalk_1.default.red("❌ No .faf file found"));
63
+ console.log(chalk_1.default.yellow('💡 Run "faf init" to create one'));
64
+ process.exit(1);
65
+ }
66
+ const projectPath = path.dirname(fafPath);
67
+ console.log(chalk_1.default.cyan("⚡ Real Enhancement Starting..."));
68
+ console.log(chalk_1.default.gray(" No BS, just facts from your project"));
69
+ console.log(chalk_1.default.gray(` Enhancing: ${fafPath}`));
70
+ // Read current .faf file
71
+ const content = await fs_1.promises.readFile(fafPath, "utf-8");
72
+ let fafData = YAML.parse(content) || {};
73
+ // Get REAL current score with detailed breakdown
74
+ const compiler = new faf_compiler_1.FafCompiler();
75
+ let currentScore = await compiler.compile(fafPath);
76
+ const targetScore = options.targetScore || 100; // Changed from 80 to 100
77
+ console.log(chalk_1.default.yellow(`📊 Current Score: ${currentScore.score}%`));
78
+ console.log(chalk_1.default.gray(` Target Score: ${targetScore}%`));
79
+ console.log(chalk_1.default.gray(` Filled: ${currentScore.filled}/${currentScore.total} slots`));
80
+ // Show what's missing
81
+ if (options.verbose) {
82
+ displayMissingSlots(currentScore);
83
+ }
84
+ // RELENTLESS enhancement loop
85
+ let iteration = 0;
86
+ const maxIterations = 10; // Safety limit
87
+ while (currentScore.score < targetScore && iteration < maxIterations) {
88
+ iteration++;
89
+ console.log(chalk_1.default.cyan(`\n🔄 Enhancement Round ${iteration}...`));
90
+ // Analyze project for REAL improvements
91
+ const improvements = await analyzeProjectForRealImprovements(projectPath, fafData, currentScore);
92
+ if (options.verbose) {
93
+ console.log(chalk_1.default.gray(`Found ${Object.keys(improvements).length} automatic improvements`));
94
+ if (Object.keys(improvements).length > 0 && Object.keys(improvements).length <= 3) {
95
+ console.log(chalk_1.default.gray(` Improvements: ${Object.keys(improvements).join(', ')}`));
96
+ }
97
+ }
98
+ // If no automatic improvements found and interactive mode, ask human
99
+ if (Object.keys(improvements).length === 0 && options.interactive !== false) {
100
+ // Check if we're missing critical human context
101
+ const missingHuman = !fafData.human_context?.who || !fafData.human_context?.what ||
102
+ !fafData.human_context?.why || !fafData.human_context?.where ||
103
+ !fafData.human_context?.when || !fafData.human_context?.how;
104
+ if (missingHuman) {
105
+ console.log(chalk_1.default.yellow('\n⚠️ Missing critical Human Context (the 6 W\'s)'));
106
+ console.log(chalk_1.default.gray(' This is the biggest opportunity to improve your score'));
107
+ }
108
+ const humanInput = await askHumanForMissingData(fafData, currentScore);
109
+ Object.assign(improvements, humanInput);
110
+ if (options.verbose && Object.keys(humanInput).length > 0) {
111
+ console.log(chalk_1.default.gray(`Added ${Object.keys(humanInput).length} improvements from human input`));
112
+ }
113
+ }
114
+ // If still no improvements, we're done
115
+ if (Object.keys(improvements).length === 0) {
116
+ console.log(chalk_1.default.yellow("\n⚠️ No more improvements possible with available data"));
117
+ if (options.interactive === false) {
118
+ console.log(chalk_1.default.gray(" (Interactive mode disabled - try with -i true to provide missing data)"));
119
+ }
120
+ break;
121
+ }
122
+ if (options.dryRun) {
123
+ console.log(chalk_1.default.cyan("\n🔍 Improvements Found (Dry Run):"));
124
+ displayImprovements(improvements);
125
+ continue;
126
+ }
127
+ // Apply REAL improvements
128
+ fafData = applyRealEnhancements(fafData, improvements);
129
+ // Save enhanced file
130
+ const enhancedYaml = YAML.stringify(fafData, null, 2);
131
+ await fs_1.promises.writeFile(fafPath, enhancedYaml);
132
+ // Calculate new score
133
+ const newScore = await compiler.compile(fafPath);
134
+ const improvement = newScore.score - currentScore.score;
135
+ if (improvement > 0) {
136
+ console.log(chalk_1.default.green(`✅ Improvement: +${improvement}% (${currentScore.score}% → ${newScore.score}%)`));
137
+ // Show what was actually added
138
+ if (options.verbose) {
139
+ console.log(chalk_1.default.cyan("📝 Changes Made:"));
140
+ displayImprovements(improvements);
141
+ }
142
+ }
143
+ currentScore = newScore;
144
+ }
145
+ // Final results
146
+ console.log(chalk_1.default.cyan("\n" + "=".repeat(60)));
147
+ if (currentScore.score >= targetScore) {
148
+ console.log(chalk_1.default.green(`🎯 TARGET ACHIEVED! Score: ${currentScore.score}%`));
149
+ }
150
+ else {
151
+ console.log(chalk_1.default.yellow(`📊 Final Score: ${currentScore.score}%`));
152
+ console.log(chalk_1.default.gray(` (Target was ${targetScore}%)`));
153
+ }
154
+ console.log(chalk_1.default.cyan("=".repeat(60)));
155
+ }
156
+ catch (error) {
157
+ console.log(chalk_1.default.red("💥 Enhancement failed:"));
158
+ console.log(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
159
+ process.exit(1);
160
+ }
161
+ }
162
+ /**
163
+ * Display what slots are missing for transparency
164
+ */
165
+ function displayMissingSlots(score) {
166
+ const missing = [];
167
+ // Check project slots
168
+ if (score.breakdown.project.filled < score.breakdown.project.total) {
169
+ const projectSlots = score.breakdown.project.slots;
170
+ projectSlots.forEach((slot) => {
171
+ if (!slot.filled && slot.id.includes('project')) {
172
+ const field = slot.id.replace('slot_', '').replace('project.', '');
173
+ missing.push(`Project: ${field}`);
174
+ }
175
+ });
176
+ }
177
+ // Check stack slots
178
+ if (score.breakdown.stack.filled < score.breakdown.stack.total) {
179
+ const stackSlots = score.breakdown.stack.slots;
180
+ stackSlots.forEach((slot) => {
181
+ if (!slot.filled && slot.id.includes('stack')) {
182
+ const field = slot.id.replace('slot_', '').replace('stack.', '');
183
+ missing.push(`Stack: ${field}`);
184
+ }
185
+ });
186
+ }
187
+ // Check human context slots
188
+ if (score.breakdown.human.filled < score.breakdown.human.total) {
189
+ const humanSlots = score.breakdown.human.slots;
190
+ humanSlots.forEach((slot) => {
191
+ if (!slot.filled && slot.id.includes('human')) {
192
+ const field = slot.id.replace('slot_', '').replace('human.', '');
193
+ missing.push(`Human Context: ${field}`);
194
+ }
195
+ });
196
+ }
197
+ if (missing.length > 0) {
198
+ console.log(chalk_1.default.yellow('\n⚠️ Missing Context (affecting score):'));
199
+ missing.forEach(m => console.log(chalk_1.default.gray(` • ${m}`)));
200
+ }
201
+ }
202
+ /**
203
+ * Ask human for missing critical data via inquirer
204
+ * RELENTLESS 1-6 questionnaire for the 6 W's of Human Context
205
+ */
206
+ async function askHumanForMissingData(currentFaf, score) {
207
+ const improvements = {};
208
+ const questions = [];
209
+ // Priority 1: Project data (most critical for AI)
210
+ if (!currentFaf.project?.name) {
211
+ questions.push({
212
+ type: 'input',
213
+ name: 'projectName',
214
+ message: '🎯 What is your project name?',
215
+ validate: (input) => input.trim().length > 0 || 'Project name is required for AI context'
216
+ });
217
+ }
218
+ if (!currentFaf.project?.goal) {
219
+ questions.push({
220
+ type: 'input',
221
+ name: 'projectGoal',
222
+ message: '🎯 What does your project do? (one sentence)',
223
+ validate: (input) => input.trim().length > 10 || 'Please provide a meaningful description'
224
+ });
225
+ }
226
+ // Priority 2: The 6 W's - RELENTLESS Human Context Collection
227
+ // These are CRITICAL for AI understanding and score improvement
228
+ console.log(chalk_1.default.cyan('\n🧠 Human Context - The 6 W\'s (Critical for AI Understanding)'));
229
+ console.log(chalk_1.default.gray(' These 6 questions dramatically improve AI\'s ability to help you'));
230
+ // 1. WHO - Target audience/users
231
+ if (!currentFaf.human_context?.who) {
232
+ questions.push({
233
+ type: 'input',
234
+ name: 'who',
235
+ message: chalk_1.default.yellow('1/6 👤 WHO will use this? (developers, data scientists, designers, etc.)'),
236
+ default: getSmartDefault('who', currentFaf),
237
+ transformer: (input) => chalk_1.default.green(input),
238
+ validate: (input) => {
239
+ if (input.trim().length < 3)
240
+ return 'Please specify your target users';
241
+ if (genericAnswers.who.includes(input.toLowerCase())) {
242
+ return 'Please be more specific than "' + input + '"';
243
+ }
244
+ return true;
245
+ }
246
+ });
247
+ }
248
+ // 2. WHAT - Core problem being solved
249
+ if (!currentFaf.human_context?.what) {
250
+ questions.push({
251
+ type: 'input',
252
+ name: 'what',
253
+ message: chalk_1.default.yellow('2/6 📦 WHAT problem does it solve? (be specific)'),
254
+ default: getSmartDefault('what', currentFaf),
255
+ transformer: (input) => chalk_1.default.green(input),
256
+ validate: (input) => {
257
+ if (input.trim().length < 10)
258
+ return 'Please describe the specific problem';
259
+ if (genericAnswers.what.includes(input.toLowerCase())) {
260
+ return 'Please be more specific about the problem';
261
+ }
262
+ return true;
263
+ }
264
+ });
265
+ }
266
+ // 3. WHY - Importance and impact
267
+ if (!currentFaf.human_context?.why) {
268
+ questions.push({
269
+ type: 'input',
270
+ name: 'why',
271
+ message: chalk_1.default.yellow('3/6 💡 WHY is this important? (what impact/value does it create?)'),
272
+ default: getSmartDefault('why', currentFaf),
273
+ transformer: (input) => chalk_1.default.green(input),
274
+ validate: (input) => {
275
+ if (input.trim().length < 10)
276
+ return 'Please explain why this matters';
277
+ if (genericAnswers.why.includes(input.toLowerCase())) {
278
+ return 'Please be more specific about the value/impact';
279
+ }
280
+ return true;
281
+ }
282
+ });
283
+ }
284
+ // 4. WHERE - Deployment/usage context
285
+ if (!currentFaf.human_context?.where) {
286
+ questions.push({
287
+ type: 'input',
288
+ name: 'where',
289
+ message: chalk_1.default.yellow('4/6 🌍 WHERE will it be used? (cloud, on-premise, mobile, browser, etc.)'),
290
+ default: getSmartDefault('where', currentFaf),
291
+ transformer: (input) => chalk_1.default.green(input),
292
+ validate: (input) => {
293
+ if (input.trim().length < 3)
294
+ return 'Please specify the deployment context';
295
+ return true;
296
+ }
297
+ });
298
+ }
299
+ // 5. WHEN - Timeline/urgency
300
+ if (!currentFaf.human_context?.when) {
301
+ questions.push({
302
+ type: 'input',
303
+ name: 'when',
304
+ message: chalk_1.default.yellow('5/6 ⏰ WHEN do you need this? (now, Q1 2025, ongoing, etc.)'),
305
+ default: getSmartDefault('when', currentFaf),
306
+ transformer: (input) => chalk_1.default.green(input),
307
+ validate: (input) => {
308
+ if (input.trim().length < 3)
309
+ return 'Please specify the timeline';
310
+ return true;
311
+ }
312
+ });
313
+ }
314
+ // 6. HOW - Implementation approach
315
+ if (!currentFaf.human_context?.how) {
316
+ questions.push({
317
+ type: 'input',
318
+ name: 'how',
319
+ message: chalk_1.default.yellow('6/6 🔧 HOW will you build it? (agile, iterative, waterfall, etc.)'),
320
+ default: getSmartDefault('how', currentFaf),
321
+ transformer: (input) => chalk_1.default.green(input),
322
+ validate: (input) => {
323
+ if (input.trim().length < 5)
324
+ return 'Please describe your approach';
325
+ if (genericAnswers.how.includes(input.toLowerCase())) {
326
+ return 'Please be more specific about your methodology';
327
+ }
328
+ return true;
329
+ }
330
+ });
331
+ }
332
+ // Priority 3: Technical stack (only if really needed and missing)
333
+ const needsFrontend = !currentFaf.stack?.frontend &&
334
+ (currentFaf.project?.type?.includes('frontend') || currentFaf.project?.type?.includes('fullstack'));
335
+ const needsBackend = !currentFaf.stack?.backend &&
336
+ (currentFaf.project?.type?.includes('backend') || currentFaf.project?.type?.includes('api'));
337
+ const needsDatabase = !currentFaf.stack?.database && needsBackend;
338
+ const needsHosting = !currentFaf.stack?.hosting;
339
+ if (needsFrontend) {
340
+ questions.push({
341
+ type: 'list',
342
+ name: 'framework',
343
+ message: '🎨 Frontend framework?',
344
+ choices: ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Remix', 'Astro', 'None', 'Skip']
345
+ });
346
+ }
347
+ if (needsBackend) {
348
+ questions.push({
349
+ type: 'list',
350
+ name: 'backend',
351
+ message: '⚙️ Backend framework?',
352
+ choices: ['Express', 'Fastify', 'NestJS', 'Hono', 'Django', 'FastAPI', 'Rails', 'None', 'Skip']
353
+ });
354
+ }
355
+ if (needsDatabase) {
356
+ questions.push({
357
+ type: 'list',
358
+ name: 'database',
359
+ message: '💾 Database?',
360
+ choices: ['PostgreSQL', 'MySQL', 'MongoDB', 'SQLite', 'Redis', 'Supabase', 'PlanetScale', 'None', 'Skip']
361
+ });
362
+ }
363
+ if (needsHosting) {
364
+ questions.push({
365
+ type: 'list',
366
+ name: 'hosting',
367
+ message: '☁️ Hosting platform?',
368
+ choices: ['Vercel', 'Netlify', 'AWS', 'Google Cloud', 'Azure', 'Heroku', 'Railway', 'Fly.io', 'None', 'Skip']
369
+ });
370
+ }
371
+ // Only ask if we have questions
372
+ if (questions.length === 0) {
373
+ return improvements;
374
+ }
375
+ console.log(chalk_1.default.cyan('\n🎯 Let\'s fill the critical gaps to reach your target score:'));
376
+ const answers = await inquirer_1.default.prompt(questions);
377
+ // Map answers to improvements
378
+ Object.entries(answers).forEach(([key, value]) => {
379
+ if (value && value !== 'Skip') {
380
+ improvements[key] = value;
381
+ }
382
+ });
383
+ // Show progress after human input
384
+ const filledCount = Object.keys(improvements).length;
385
+ if (filledCount > 0) {
386
+ console.log(chalk_1.default.green(`\n✅ Added ${filledCount} improvements from your input`));
387
+ }
388
+ return improvements;
389
+ }
390
+ // Generic answers to reject for better specificity
391
+ const genericAnswers = {
392
+ who: ['users', 'people', 'everyone', 'anybody', 'someone'],
393
+ what: ['problems', 'issues', 'things', 'stuff', 'something'],
394
+ why: ['because', 'important', 'needed', 'required', 'necessary'],
395
+ how: ['normal', 'usual', 'standard', 'regular', 'typical']
396
+ };
397
+ // Smart defaults based on project type
398
+ function getSmartDefault(field, currentFaf) {
399
+ const projectType = currentFaf.project?.type || '';
400
+ const projectName = currentFaf.project?.name || '';
401
+ const language = currentFaf.project?.main_language || '';
402
+ const defaults = {
403
+ who: {
404
+ 'cli-tool': 'Developers and DevOps engineers',
405
+ 'frontend': 'End users and web visitors',
406
+ 'backend-api': 'Frontend applications and API consumers',
407
+ 'fullstack': 'Business users and administrators',
408
+ 'library': `Developers using ${language || 'this technology'}`,
409
+ 'chrome-extension': 'Chrome browser users',
410
+ 'default': 'Development teams'
411
+ },
412
+ what: {
413
+ 'cli-tool': 'Automates repetitive command-line tasks',
414
+ 'frontend': 'Provides user interface for data interaction',
415
+ 'backend-api': 'Manages data and business logic',
416
+ 'fullstack': 'Complete solution for business operations',
417
+ 'library': `Simplifies ${language || 'development'} tasks`,
418
+ 'chrome-extension': 'Enhances browser functionality',
419
+ 'default': 'Solves specific technical challenges'
420
+ },
421
+ why: {
422
+ 'cli-tool': 'Saves time and reduces errors in workflows',
423
+ 'frontend': 'Improves user experience and engagement',
424
+ 'backend-api': 'Centralizes data management and processing',
425
+ 'fullstack': 'Streamlines business processes',
426
+ 'library': 'Reduces code duplication and complexity',
427
+ 'chrome-extension': 'Adds missing browser features',
428
+ 'default': 'Increases productivity and quality'
429
+ },
430
+ where: {
431
+ 'cli-tool': 'Local development machines and CI/CD pipelines',
432
+ 'frontend': 'Web browsers and CDN',
433
+ 'backend-api': 'Cloud servers or containers',
434
+ 'fullstack': 'Cloud platform with global availability',
435
+ 'library': 'NPM/PyPI package registry',
436
+ 'chrome-extension': 'Chrome Web Store',
437
+ 'default': 'Cloud infrastructure'
438
+ },
439
+ when: {
440
+ 'default': 'Ongoing development and maintenance'
441
+ },
442
+ how: {
443
+ 'default': 'Agile development with continuous deployment'
444
+ }
445
+ };
446
+ const typeDefaults = defaults[field];
447
+ if (!typeDefaults)
448
+ return '';
449
+ return typeDefaults[projectType] || typeDefaults['default'] || '';
450
+ }
451
+ /**
452
+ * Analyze project for REAL improvements based on actual files
453
+ */
454
+ async function analyzeProjectForRealImprovements(projectPath, currentFaf, currentScore) {
455
+ const improvements = {};
456
+ // Priority order: Fill critical slots first
457
+ // 1. PROJECT SLOTS (name, goal, language) - Most critical for AI
458
+ // 2. HUMAN CONTEXT (6 W's) - Critical for understanding
459
+ // 3. STACK SLOTS - Technical details
460
+ // 1. Scan for package.json to get REAL project info
461
+ try {
462
+ const pkgPath = path.join(projectPath, 'package.json');
463
+ const pkgContent = await fs_1.promises.readFile(pkgPath, 'utf-8');
464
+ const pkg = JSON.parse(pkgContent);
465
+ // Extract REAL project data
466
+ if (!currentFaf.project?.name && pkg.name) {
467
+ improvements.projectName = pkg.name;
468
+ }
469
+ if (!currentFaf.project?.goal && pkg.description) {
470
+ improvements.projectGoal = pkg.description;
471
+ }
472
+ if (!currentFaf.project?.version && pkg.version) {
473
+ improvements.version = pkg.version;
474
+ }
475
+ if (!currentFaf.project?.author && pkg.author) {
476
+ improvements.author = typeof pkg.author === 'string' ? pkg.author : pkg.author.name;
477
+ }
478
+ if (!currentFaf.project?.license && pkg.license) {
479
+ improvements.license = pkg.license;
480
+ }
481
+ // Extract REAL dependencies for stack info
482
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
483
+ const frameworks = detectFrameworks(deps);
484
+ if (frameworks.length > 0 && !currentFaf.stack?.frontend) {
485
+ improvements.framework = frameworks[0];
486
+ }
487
+ // Detect testing framework
488
+ const testFramework = detectTestingFramework(deps);
489
+ if (testFramework && !currentFaf.stack?.testing) {
490
+ improvements.testing = testFramework;
491
+ }
492
+ // Detect database
493
+ const database = detectDatabase(deps);
494
+ if (database && !currentFaf.stack?.database) {
495
+ improvements.database = database;
496
+ }
497
+ // Detect CI/CD from scripts
498
+ if (pkg.scripts?.deploy && !currentFaf.stack?.cicd) {
499
+ improvements.cicd = 'npm deploy';
500
+ }
501
+ // Extract scripts for understanding project
502
+ if (pkg.scripts) {
503
+ const scripts = Object.keys(pkg.scripts);
504
+ if (!currentFaf.stack?.testing && scripts.some(s => s.includes('test'))) {
505
+ improvements.testing = 'jest'; // or detect from deps
506
+ }
507
+ if (!currentFaf.stack?.build && scripts.some(s => s.includes('build'))) {
508
+ improvements.buildTool = detectBuildTool(deps);
509
+ }
510
+ }
511
+ }
512
+ catch (e) {
513
+ // No package.json, try other files
514
+ }
515
+ // 2. Use FAB-Formats to INTELLIGENTLY discover project structure
516
+ const fabEngine = new fab_formats_engine_1.FabFormatsEngine();
517
+ const fabAnalysis = await fabEngine.discoverFormats(projectPath);
518
+ if (fabAnalysis.discoveredFormats && fabAnalysis.discoveredFormats.length > 0) {
519
+ // Add discovered key files
520
+ const keyFiles = fabAnalysis.discoveredFormats
521
+ .map((f) => f.fileName)
522
+ .filter((f) => isImportantFile(f))
523
+ .slice(0, 5);
524
+ if (keyFiles.length > 0 && !currentFaf.instant_context?.key_files) {
525
+ improvements.keyFiles = keyFiles;
526
+ }
527
+ // Use ALL extracted context from FAB-Formats
528
+ const ctx = fabAnalysis.extractedContext;
529
+ // Project information
530
+ if (ctx.projectName && !currentFaf.project?.name) {
531
+ improvements.projectName = ctx.projectName;
532
+ }
533
+ if (ctx.projectDescription && !currentFaf.project?.goal) {
534
+ improvements.projectGoal = ctx.projectDescription;
535
+ }
536
+ if (ctx.projectType && !currentFaf.project?.type) {
537
+ improvements.projectType = ctx.projectType;
538
+ }
539
+ // Stack information from FAB-Formats
540
+ if (ctx.mainLanguage && !currentFaf.project?.main_language) {
541
+ improvements.mainLanguage = ctx.mainLanguage;
542
+ }
543
+ if (ctx.frameworks && ctx.frameworks.length > 0) {
544
+ if (!currentFaf.stack?.frontend && isFrontendFramework(ctx.frameworks[0])) {
545
+ improvements.framework = ctx.frameworks[0];
546
+ }
547
+ if (!currentFaf.stack?.backend && isBackendFramework(ctx.frameworks[0])) {
548
+ improvements.backend = ctx.frameworks[0];
549
+ }
550
+ }
551
+ if (ctx.database && !currentFaf.stack?.database) {
552
+ improvements.database = ctx.database;
553
+ }
554
+ if (ctx.backend && !currentFaf.stack?.backend) {
555
+ improvements.backend = ctx.backend;
556
+ }
557
+ if (ctx.hosting && !currentFaf.stack?.hosting) {
558
+ improvements.hosting = ctx.hosting;
559
+ }
560
+ // These might not exist in ctx, check if they do
561
+ if (ctx.buildTool && !currentFaf.stack?.build) {
562
+ improvements.buildTool = ctx.buildTool;
563
+ }
564
+ if (ctx.testing && !currentFaf.stack?.testing) {
565
+ improvements.testing = ctx.testing;
566
+ }
567
+ if (ctx.cicd && !currentFaf.stack?.cicd) {
568
+ improvements.cicd = ctx.cicd;
569
+ }
570
+ // Use FAB-Formats pattern analysis for better detection
571
+ const patterns = fabAnalysis.discoveredFormats.map((f) => f.pattern);
572
+ if (patterns.includes('dockerfile') && !currentFaf.stack?.hosting) {
573
+ improvements.hosting = 'Docker';
574
+ }
575
+ if (patterns.includes('kubernetes') && !currentFaf.stack?.hosting) {
576
+ improvements.hosting = 'Kubernetes';
577
+ }
578
+ if (patterns.includes('github-actions') && !currentFaf.stack?.cicd) {
579
+ improvements.cicd = 'GitHub Actions';
580
+ }
581
+ if (patterns.includes('gitlab-ci') && !currentFaf.stack?.cicd) {
582
+ improvements.cicd = 'GitLab CI';
583
+ }
584
+ }
585
+ // 3. Detect project type from files
586
+ const files = await fs_1.promises.readdir(projectPath);
587
+ const projectType = detectProjectType(files);
588
+ if (projectType && projectType !== 'unknown' && !currentFaf.project?.type) {
589
+ improvements.projectType = projectType;
590
+ }
591
+ // 3.5 Try to get README for project goal and human context
592
+ try {
593
+ const readmePath = path.join(projectPath, 'README.md');
594
+ const readmeContent = await fs_1.promises.readFile(readmePath, 'utf-8');
595
+ const readmeInfo = extractFromReadme(readmeContent);
596
+ if (readmeInfo.description && !currentFaf.project?.goal) {
597
+ improvements.projectGoal = readmeInfo.description;
598
+ }
599
+ if (readmeInfo.who && !currentFaf.human_context?.who) {
600
+ improvements.who = readmeInfo.who;
601
+ }
602
+ if (readmeInfo.what && !currentFaf.human_context?.what) {
603
+ improvements.what = readmeInfo.what;
604
+ }
605
+ }
606
+ catch (e) {
607
+ // No README or can't parse
608
+ }
609
+ // 4. Add REAL human context from git
610
+ try {
611
+ // Get git remote URL for WHERE context
612
+ if (!currentFaf.human_context?.where) {
613
+ const gitRemote = (0, child_process_1.execSync)('git config --get remote.origin.url', { cwd: projectPath })
614
+ .toString().trim();
615
+ if (gitRemote) {
616
+ if (gitRemote.includes('github.com')) {
617
+ improvements.where = 'GitHub';
618
+ }
619
+ else if (gitRemote.includes('gitlab')) {
620
+ improvements.where = 'GitLab';
621
+ }
622
+ else if (gitRemote.includes('bitbucket')) {
623
+ improvements.where = 'Bitbucket';
624
+ }
625
+ }
626
+ }
627
+ // Get last commit date for WHEN context
628
+ if (!currentFaf.human_context?.when) {
629
+ const lastCommit = (0, child_process_1.execSync)('git log -1 --format=%cd --date=short', { cwd: projectPath })
630
+ .toString().trim();
631
+ if (lastCommit) {
632
+ improvements.when = `Active development since ${lastCommit}`;
633
+ }
634
+ }
635
+ }
636
+ catch (e) {
637
+ // No git or git commands failed
638
+ }
639
+ // 5. Add REAL technical details
640
+ if (!currentFaf.stack?.language) {
641
+ const language = detectLanguage(files);
642
+ if (language) {
643
+ improvements.mainLanguage = language;
644
+ }
645
+ }
646
+ // 6. Analyze file patterns for more intelligent stack detection
647
+ if (!currentFaf.stack?.hosting) {
648
+ if (files.includes('vercel.json'))
649
+ improvements.hosting = 'Vercel';
650
+ else if (files.includes('netlify.toml'))
651
+ improvements.hosting = 'Netlify';
652
+ else if (files.includes('app.yaml'))
653
+ improvements.hosting = 'Google App Engine';
654
+ else if (files.includes('Procfile'))
655
+ improvements.hosting = 'Heroku';
656
+ else if (files.includes('.elasticbeanstalk'))
657
+ improvements.hosting = 'AWS Elastic Beanstalk';
658
+ }
659
+ // 7. Intelligent project type detection from structure
660
+ if (!currentFaf.project?.type) {
661
+ const hasPublicDir = files.includes('public');
662
+ const hasSrcDir = files.includes('src');
663
+ const hasApiDir = files.includes('api');
664
+ const hasServerFile = files.some(f => f.includes('server'));
665
+ const hasIndexHtml = files.includes('index.html');
666
+ if (hasApiDir && hasPublicDir)
667
+ improvements.projectType = 'fullstack';
668
+ else if (hasServerFile && !hasIndexHtml)
669
+ improvements.projectType = 'backend-api';
670
+ else if (hasPublicDir && hasIndexHtml)
671
+ improvements.projectType = 'frontend';
672
+ else if (files.includes('cli.js') || files.includes('cli.ts'))
673
+ improvements.projectType = 'cli-tool';
674
+ }
675
+ // 8. Extract runtime from files
676
+ if (!currentFaf.stack?.runtime) {
677
+ if (files.includes('package.json'))
678
+ improvements.runtime = 'Node.js';
679
+ else if (files.includes('requirements.txt'))
680
+ improvements.runtime = 'Python';
681
+ else if (files.includes('Gemfile'))
682
+ improvements.runtime = 'Ruby';
683
+ else if (files.includes('go.mod'))
684
+ improvements.runtime = 'Go';
685
+ else if (files.includes('Cargo.toml'))
686
+ improvements.runtime = 'Rust';
687
+ }
688
+ // 9. Fill API type if we have a backend
689
+ if (!currentFaf.stack?.api_type && (currentFaf.stack?.backend || improvements.backend)) {
690
+ // Check package.json for API type hints
691
+ try {
692
+ const pkgPath = path.join(projectPath, 'package.json');
693
+ const pkgContent = await fs_1.promises.readFile(pkgPath, 'utf-8');
694
+ const pkg = JSON.parse(pkgContent);
695
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
696
+ if (allDeps['graphql'])
697
+ improvements.api_type = 'GraphQL';
698
+ else if (allDeps['@trpc/server'])
699
+ improvements.api_type = 'tRPC';
700
+ else if (allDeps['socket.io'])
701
+ improvements.api_type = 'WebSocket';
702
+ else
703
+ improvements.api_type = 'REST';
704
+ }
705
+ catch (e) {
706
+ improvements.api_type = 'REST'; // Default to REST if can't read package.json
707
+ }
708
+ }
709
+ return improvements;
710
+ }
711
+ /**
712
+ * Apply REAL enhancements to faf data
713
+ */
714
+ function applyRealEnhancements(fafData, improvements) {
715
+ const enhanced = { ...fafData };
716
+ // Ensure structure exists
717
+ enhanced.project = enhanced.project || {};
718
+ enhanced.stack = enhanced.stack || {};
719
+ enhanced.human_context = enhanced.human_context || {};
720
+ enhanced.instant_context = enhanced.instant_context || {};
721
+ // Apply project improvements (CRITICAL SLOTS)
722
+ if (improvements.projectName) {
723
+ enhanced.project.name = improvements.projectName;
724
+ }
725
+ if (improvements.projectGoal) {
726
+ enhanced.project.goal = improvements.projectGoal;
727
+ }
728
+ if (improvements.mainLanguage) {
729
+ enhanced.project.main_language = improvements.mainLanguage;
730
+ }
731
+ if (improvements.version) {
732
+ enhanced.project.version = improvements.version;
733
+ }
734
+ if (improvements.author) {
735
+ enhanced.project.author = improvements.author;
736
+ }
737
+ if (improvements.license) {
738
+ enhanced.project.license = improvements.license;
739
+ }
740
+ if (improvements.projectType) {
741
+ enhanced.project.type = improvements.projectType;
742
+ }
743
+ // Apply stack improvements (TECHNICAL SLOTS)
744
+ if (improvements.framework) {
745
+ enhanced.stack.frontend = improvements.framework;
746
+ }
747
+ if (improvements.backend) {
748
+ enhanced.stack.backend = improvements.backend;
749
+ }
750
+ if (improvements.database) {
751
+ enhanced.stack.database = improvements.database;
752
+ }
753
+ if (improvements.buildTool) {
754
+ enhanced.stack.build = improvements.buildTool;
755
+ }
756
+ if (improvements.testing) {
757
+ enhanced.stack.testing = improvements.testing;
758
+ }
759
+ if (improvements.cicd) {
760
+ enhanced.stack.cicd = improvements.cicd;
761
+ }
762
+ if (improvements.runtime) {
763
+ enhanced.stack.runtime = improvements.runtime;
764
+ }
765
+ if (improvements.hosting) {
766
+ enhanced.stack.hosting = improvements.hosting;
767
+ }
768
+ if (improvements.api_type) {
769
+ enhanced.stack.api_type = improvements.api_type;
770
+ }
771
+ if (improvements.mainLanguage) {
772
+ enhanced.stack.language = improvements.mainLanguage;
773
+ }
774
+ // Apply human context improvements (6 W's - CRITICAL FOR AI UNDERSTANDING)
775
+ if (improvements.who) {
776
+ enhanced.human_context.who = improvements.who;
777
+ }
778
+ if (improvements.what) {
779
+ enhanced.human_context.what = improvements.what;
780
+ }
781
+ if (improvements.why) {
782
+ enhanced.human_context.why = improvements.why;
783
+ }
784
+ if (improvements.where) {
785
+ enhanced.human_context.where = improvements.where;
786
+ }
787
+ if (improvements.when) {
788
+ enhanced.human_context.when = improvements.when;
789
+ }
790
+ if (improvements.how) {
791
+ enhanced.human_context.how = improvements.how;
792
+ }
793
+ // Apply instant context improvements
794
+ if (improvements.keyFiles) {
795
+ enhanced.instant_context.key_files = improvements.keyFiles;
796
+ }
797
+ // Add metadata
798
+ enhanced.meta = enhanced.meta || {};
799
+ enhanced.meta.last_enhanced = new Date().toISOString();
800
+ enhanced.meta.enhanced_by = 'faf-real-enhance';
801
+ return enhanced;
802
+ }
803
+ /**
804
+ * Helper functions for detection
805
+ */
806
+ function detectFrameworks(deps) {
807
+ const frameworks = [];
808
+ // Frontend frameworks
809
+ if (deps['react'])
810
+ frameworks.push('React');
811
+ if (deps['vue'])
812
+ frameworks.push('Vue');
813
+ if (deps['@angular/core'])
814
+ frameworks.push('Angular');
815
+ if (deps['svelte'])
816
+ frameworks.push('Svelte');
817
+ if (deps['solid-js'])
818
+ frameworks.push('SolidJS');
819
+ if (deps['preact'])
820
+ frameworks.push('Preact');
821
+ if (deps['@ember/core'])
822
+ frameworks.push('Ember');
823
+ // Meta-frameworks
824
+ if (deps['next'])
825
+ frameworks.push('Next.js');
826
+ if (deps['nuxt'])
827
+ frameworks.push('Nuxt');
828
+ if (deps['gatsby'])
829
+ frameworks.push('Gatsby');
830
+ if (deps['@remix-run/react'])
831
+ frameworks.push('Remix');
832
+ if (deps['astro'])
833
+ frameworks.push('Astro');
834
+ if (deps['@sveltekit/adapter-auto'])
835
+ frameworks.push('SvelteKit');
836
+ // Backend frameworks
837
+ if (deps['express'])
838
+ frameworks.push('Express');
839
+ if (deps['fastify'])
840
+ frameworks.push('Fastify');
841
+ if (deps['@nestjs/core'])
842
+ frameworks.push('NestJS');
843
+ if (deps['koa'])
844
+ frameworks.push('Koa');
845
+ if (deps['@hapi/hapi'])
846
+ frameworks.push('Hapi');
847
+ if (deps['@feathersjs/feathers'])
848
+ frameworks.push('Feathers');
849
+ if (deps['@adonisjs/core'])
850
+ frameworks.push('AdonisJS');
851
+ return frameworks;
852
+ }
853
+ function isFrontendFramework(framework) {
854
+ const frontend = ['React', 'Vue', 'Angular', 'Svelte', 'SolidJS', 'Preact', 'Ember',
855
+ 'Next.js', 'Nuxt', 'Gatsby', 'Remix', 'Astro', 'SvelteKit'];
856
+ return frontend.includes(framework);
857
+ }
858
+ function isBackendFramework(framework) {
859
+ const backend = ['Express', 'Fastify', 'NestJS', 'Koa', 'Hapi', 'Feathers', 'AdonisJS'];
860
+ return backend.includes(framework);
861
+ }
862
+ function detectBuildTool(deps) {
863
+ if (deps['vite'])
864
+ return 'Vite';
865
+ if (deps['webpack'])
866
+ return 'Webpack';
867
+ if (deps['@parcel/core'] || deps['parcel'])
868
+ return 'Parcel';
869
+ if (deps['esbuild'])
870
+ return 'ESBuild';
871
+ if (deps['rollup'])
872
+ return 'Rollup';
873
+ if (deps['@swc/core'])
874
+ return 'SWC';
875
+ if (deps['turbopack'])
876
+ return 'Turbopack';
877
+ if (deps['snowpack'])
878
+ return 'Snowpack';
879
+ if (deps['@rspack/core'])
880
+ return 'Rspack';
881
+ return 'npm';
882
+ }
883
+ function detectProjectType(files) {
884
+ if (files.includes('package.json')) {
885
+ if (files.includes('tsconfig.json'))
886
+ return 'typescript-node';
887
+ return 'javascript-node';
888
+ }
889
+ if (files.includes('requirements.txt') || files.includes('setup.py')) {
890
+ return 'python';
891
+ }
892
+ if (files.includes('Cargo.toml'))
893
+ return 'rust';
894
+ if (files.includes('go.mod'))
895
+ return 'go';
896
+ return 'unknown';
897
+ }
898
+ function detectLanguage(files) {
899
+ const extensions = files.map(f => path.extname(f));
900
+ if (extensions.includes('.ts') || extensions.includes('.tsx'))
901
+ return 'TypeScript';
902
+ if (extensions.includes('.js') || extensions.includes('.jsx'))
903
+ return 'JavaScript';
904
+ if (extensions.includes('.py'))
905
+ return 'Python';
906
+ if (extensions.includes('.rs'))
907
+ return 'Rust';
908
+ if (extensions.includes('.go'))
909
+ return 'Go';
910
+ if (extensions.includes('.java'))
911
+ return 'Java';
912
+ return 'Unknown';
913
+ }
914
+ function isImportantFile(filename) {
915
+ const important = [
916
+ 'index', 'main', 'app', 'server', 'cli',
917
+ 'config', 'package.json', 'tsconfig.json',
918
+ 'webpack.config', 'vite.config', 'README'
919
+ ];
920
+ return important.some(i => filename.toLowerCase().includes(i));
921
+ }
922
+ /**
923
+ * Extract info from README
924
+ */
925
+ function extractFromReadme(content) {
926
+ const info = {};
927
+ // Try to get first paragraph as description
928
+ const lines = content.split('\n').filter(l => l.trim());
929
+ const descLine = lines.find(l => !l.startsWith('#') && l.length > 20);
930
+ if (descLine) {
931
+ info.description = descLine.trim();
932
+ }
933
+ // Look for "For" or "Built for" patterns
934
+ const forMatch = content.match(/(?:built for|for|designed for|made for)\s+([^.\n]+)/i);
935
+ if (forMatch) {
936
+ info.who = forMatch[1].trim();
937
+ }
938
+ // Look for problem statements
939
+ const problemMatch = content.match(/(?:solves?|fixes?|addresses?|handles?)\s+([^.\n]+)/i);
940
+ if (problemMatch) {
941
+ info.what = problemMatch[1].trim();
942
+ }
943
+ return info;
944
+ }
945
+ /**
946
+ * Detect testing framework from dependencies
947
+ */
948
+ function detectTestingFramework(deps) {
949
+ // Unit testing
950
+ if (deps['vitest'])
951
+ return 'Vitest';
952
+ if (deps['jest'])
953
+ return 'Jest';
954
+ if (deps['mocha'])
955
+ return 'Mocha';
956
+ if (deps['jasmine'])
957
+ return 'Jasmine';
958
+ if (deps['ava'])
959
+ return 'AVA';
960
+ if (deps['tape'])
961
+ return 'Tape';
962
+ if (deps['uvu'])
963
+ return 'uvu';
964
+ // E2E testing
965
+ if (deps['@playwright/test'] || deps['playwright'])
966
+ return 'Playwright';
967
+ if (deps['cypress'])
968
+ return 'Cypress';
969
+ if (deps['puppeteer'])
970
+ return 'Puppeteer';
971
+ if (deps['webdriverio'])
972
+ return 'WebdriverIO';
973
+ if (deps['nightwatch'])
974
+ return 'Nightwatch';
975
+ // Testing utilities
976
+ if (deps['@testing-library/react'])
977
+ return 'React Testing Library';
978
+ if (deps['@testing-library/vue'])
979
+ return 'Vue Testing Library';
980
+ if (deps['enzyme'])
981
+ return 'Enzyme';
982
+ return null;
983
+ }
984
+ /**
985
+ * Detect database from dependencies
986
+ */
987
+ function detectDatabase(deps) {
988
+ // ORMs that support multiple databases
989
+ if (deps['@prisma/client'])
990
+ return 'Prisma';
991
+ if (deps['typeorm'])
992
+ return 'TypeORM';
993
+ if (deps['sequelize'])
994
+ return 'Sequelize';
995
+ if (deps['drizzle-orm'])
996
+ return 'Drizzle';
997
+ if (deps['kysely'])
998
+ return 'Kysely';
999
+ // Specific databases
1000
+ if (deps['pg'] || deps['postgres'])
1001
+ return 'PostgreSQL';
1002
+ if (deps['mysql'] || deps['mysql2'])
1003
+ return 'MySQL';
1004
+ if (deps['mongodb'] || deps['mongoose'])
1005
+ return 'MongoDB';
1006
+ if (deps['sqlite3'] || deps['better-sqlite3'])
1007
+ return 'SQLite';
1008
+ if (deps['redis'] || deps['ioredis'])
1009
+ return 'Redis';
1010
+ if (deps['@supabase/supabase-js'])
1011
+ return 'Supabase';
1012
+ if (deps['firebase'])
1013
+ return 'Firebase';
1014
+ if (deps['@planetscale/database'])
1015
+ return 'PlanetScale';
1016
+ if (deps['@neondatabase/serverless'])
1017
+ return 'Neon';
1018
+ if (deps['@vercel/postgres'])
1019
+ return 'Vercel Postgres';
1020
+ return null;
1021
+ }
1022
+ function displayImprovements(improvements) {
1023
+ Object.entries(improvements).forEach(([key, value]) => {
1024
+ console.log(chalk_1.default.gray(` • ${key}: ${value}`));
1025
+ });
1026
+ }
1027
+ //# sourceMappingURL=enhance-real.js.map