faf-cli 4.2.2 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +266 -90
  2. package/assets/project-faf-screenshot.png +0 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +40 -22
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/git.d.ts.map +1 -1
  7. package/dist/commands/git.js +9 -7
  8. package/dist/commands/git.js.map +1 -1
  9. package/dist/commands/readme.d.ts +11 -6
  10. package/dist/commands/readme.d.ts.map +1 -1
  11. package/dist/commands/readme.js +167 -120
  12. package/dist/commands/readme.js.map +1 -1
  13. package/dist/commands/show.d.ts.map +1 -1
  14. package/dist/commands/show.js +22 -7
  15. package/dist/commands/show.js.map +1 -1
  16. package/dist/commands/sixws.d.ts +6 -0
  17. package/dist/commands/sixws.d.ts.map +1 -0
  18. package/dist/commands/sixws.js +154 -0
  19. package/dist/commands/sixws.js.map +1 -0
  20. package/dist/github/current-score-calculator.d.ts +15 -0
  21. package/dist/github/current-score-calculator.d.ts.map +1 -0
  22. package/dist/github/current-score-calculator.js +125 -0
  23. package/dist/github/current-score-calculator.js.map +1 -0
  24. package/dist/github/faf-git-generator.d.ts +58 -0
  25. package/dist/github/faf-git-generator.d.ts.map +1 -0
  26. package/dist/github/faf-git-generator.js +557 -0
  27. package/dist/github/faf-git-generator.js.map +1 -0
  28. package/dist/github/github-extractor.d.ts +4 -0
  29. package/dist/github/github-extractor.d.ts.map +1 -1
  30. package/dist/github/github-extractor.js +27 -0
  31. package/dist/github/github-extractor.js.map +1 -1
  32. package/dist/github/repo-selector.d.ts +2 -2
  33. package/dist/github/repo-selector.d.ts.map +1 -1
  34. package/dist/github/repo-selector.js +30 -23
  35. package/dist/github/repo-selector.js.map +1 -1
  36. package/dist/utils/file-utils.d.ts.map +1 -1
  37. package/dist/utils/file-utils.js +1 -4
  38. package/dist/utils/file-utils.js.map +1 -1
  39. package/dist/utils/slot-counter.d.ts +56 -0
  40. package/dist/utils/slot-counter.d.ts.map +1 -0
  41. package/dist/utils/slot-counter.js +100 -0
  42. package/dist/utils/slot-counter.js.map +1 -0
  43. package/dist/utils/yaml-generator.d.ts.map +1 -1
  44. package/dist/utils/yaml-generator.js +3 -7
  45. package/dist/utils/yaml-generator.js.map +1 -1
  46. package/package.json +7 -2
  47. package/project.faf +5 -9
  48. package/scripts/ANTHROPIC-DEMO.sh +203 -0
  49. package/scripts/boris-ready.sh +169 -0
  50. package/scripts/bundle-yaml.js +87 -0
  51. package/scripts/check-version.js +88 -0
  52. package/scripts/clean-build.js +34 -0
  53. package/scripts/cleanup-unused.sh +54 -0
  54. package/scripts/debug-django.txt +9 -0
  55. package/scripts/debug-mongo.txt +9 -0
  56. package/scripts/debug-react.txt +9 -0
  57. package/scripts/debug-rust.txt +9 -0
  58. package/scripts/debug-whisper.cpp.txt +9 -0
  59. package/scripts/evaluate-family-member.ts +300 -0
  60. package/scripts/generate-docs.ts +358 -0
  61. package/scripts/generate-drift-reports.sh +111 -0
  62. package/scripts/industry-showcase.json +122 -0
  63. package/scripts/mcp-ecosystem-research.sh +58 -0
  64. package/scripts/migrate-yaml-imports.sh +55 -0
  65. package/scripts/migrate-yaml.ts +132 -0
  66. package/scripts/performance-validation.ts +460 -0
  67. package/scripts/postinstall.js +30 -0
  68. package/scripts/prepare-release.ts +421 -0
  69. package/scripts/run-industry-showcase.ts +237 -0
  70. package/scripts/run-test-showcase.ts +244 -0
  71. package/scripts/setup-github-watch.sh +43 -0
  72. package/scripts/sync-version.js +35 -0
  73. package/scripts/test-integration-detection.ts +93 -0
  74. package/scripts/test-integration-simple.js +93 -0
  75. package/scripts/test-medal-progression.sh +143 -0
  76. package/scripts/test-showcase-results.json +109 -0
  77. package/scripts/test-showcase.json +32 -0
  78. package/scripts/update-version.js +148 -0
  79. package/scripts/verify-build.js +343 -0
  80. package/scripts/version-check.js +78 -0
  81. package/scripts/watch-discussions.sh +86 -0
@@ -0,0 +1,421 @@
1
+ #!/usr/bin/env ts-node
2
+
3
+ /**
4
+ * šŸš€ Release Preparation Script - F1-Inspired Championship Releases
5
+ * Automates version bumping, changelog generation, and release validation
6
+ */
7
+
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+ import { execSync } from 'child_process';
11
+ import { FAF_COLORS, FAF_ICONS } from '../src/utils/championship-style';
12
+
13
+ interface ReleaseOptions {
14
+ type: 'patch' | 'minor' | 'major' | 'prerelease';
15
+ dryRun: boolean;
16
+ skipTests: boolean;
17
+ prereleaseId?: string;
18
+ }
19
+
20
+ interface VersionInfo {
21
+ current: string;
22
+ next: string;
23
+ tag: string;
24
+ }
25
+
26
+ interface ChangelogEntry {
27
+ type: 'feat' | 'fix' | 'docs' | 'style' | 'refactor' | 'test' | 'chore';
28
+ scope?: string;
29
+ description: string;
30
+ breaking?: boolean;
31
+ hash: string;
32
+ }
33
+
34
+ /**
35
+ * Main release preparation function
36
+ */
37
+ async function prepareRelease(options: ReleaseOptions): Promise<void> {
38
+ console.log(`${FAF_COLORS.fafCyan('šŸš€ FAF CLI Release Preparation')}`);
39
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} F1-inspired championship engineering`);
40
+ console.log(`${FAF_COLORS.fafCyan('└─')} Release type: ${FAF_COLORS.fafGreen(options.type)}`);
41
+ console.log();
42
+
43
+ try {
44
+ // Pre-flight checks
45
+ await preflightChecks(options);
46
+
47
+ // Calculate version info
48
+ const versionInfo = await calculateVersions(options);
49
+ console.log(`${FAF_COLORS.fafCyan('šŸ“Š Version Info:')}`);
50
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Current: ${versionInfo.current}`);
51
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Next: ${FAF_COLORS.fafGreen(versionInfo.next)}`);
52
+ console.log(`${FAF_COLORS.fafCyan('└─')} Tag: ${versionInfo.tag}`);
53
+ console.log();
54
+
55
+ // Generate changelog
56
+ const changelog = await generateChangelog(versionInfo);
57
+
58
+ // Update package.json
59
+ if (!options.dryRun) {
60
+ await updatePackageVersion(versionInfo.next);
61
+ console.log(`${FAF_COLORS.fafGreen('āœ…')} Updated package.json to ${versionInfo.next}`);
62
+ }
63
+
64
+ // Run tests if not skipped
65
+ if (!options.skipTests) {
66
+ await runTestSuite();
67
+ }
68
+
69
+ // Build project
70
+ await buildProject();
71
+
72
+ // Show release summary
73
+ showReleaseSummary(versionInfo, changelog, options);
74
+
75
+ // Create release commands
76
+ generateReleaseCommands(versionInfo, options);
77
+
78
+ } catch (error) {
79
+ console.error(`${FAF_COLORS.fafOrange('āŒ Release preparation failed:')} ${error}`);
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Pre-flight checks
86
+ */
87
+ async function preflightChecks(options: ReleaseOptions): Promise<void> {
88
+ console.log(`${FAF_COLORS.fafCyan('šŸ” Pre-flight Checks:')}`);
89
+
90
+ // Check git status
91
+ try {
92
+ const gitStatus = execSync('git status --porcelain', { encoding: 'utf8' });
93
+ if (gitStatus.trim() && !options.dryRun) {
94
+ throw new Error('Working directory not clean. Commit or stash changes first.');
95
+ }
96
+ console.log(`${FAF_COLORS.fafGreen('ā”œā”€ āœ…')} Git working directory clean`);
97
+ } catch (error) {
98
+ console.log(`${FAF_COLORS.fafOrange('ā”œā”€ āš ļø')} Git status check failed`);
99
+ if (!options.dryRun) throw error;
100
+ }
101
+
102
+ // Check we're on main branch
103
+ try {
104
+ const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
105
+ if (currentBranch !== 'main' && !options.dryRun) {
106
+ console.log(`${FAF_COLORS.fafOrange('ā”œā”€ āš ļø')} Currently on branch: ${currentBranch} (expected: main)`);
107
+ } else {
108
+ console.log(`${FAF_COLORS.fafGreen('ā”œā”€ āœ…')} On main branch`);
109
+ }
110
+ } catch (error) {
111
+ console.log(`${FAF_COLORS.fafOrange('ā”œā”€ āš ļø')} Branch check failed`);
112
+ }
113
+
114
+ // Check package.json exists
115
+ const packagePath = path.join(process.cwd(), 'package.json');
116
+ try {
117
+ await fs.access(packagePath);
118
+ console.log(`${FAF_COLORS.fafGreen('ā”œā”€ āœ…')} package.json found`);
119
+ } catch {
120
+ throw new Error('package.json not found in current directory');
121
+ }
122
+
123
+ // Check npm credentials (for actual releases)
124
+ if (!options.dryRun) {
125
+ try {
126
+ execSync('npm whoami', { stdio: 'pipe' });
127
+ console.log(`${FAF_COLORS.fafGreen('└─ āœ…')} NPM authentication ready`);
128
+ } catch {
129
+ console.log(`${FAF_COLORS.fafOrange('└─ āš ļø')} NPM authentication not configured`);
130
+ }
131
+ } else {
132
+ console.log(`${FAF_COLORS.fafGreen('└─ āœ…')} Dry run mode - skipping NPM check`);
133
+ }
134
+
135
+ console.log();
136
+ }
137
+
138
+ /**
139
+ * Calculate version information
140
+ */
141
+ async function calculateVersions(options: ReleaseOptions): Promise<VersionInfo> {
142
+ const packagePath = path.join(process.cwd(), 'package.json');
143
+ const packageContent = await fs.readFile(packagePath, 'utf8');
144
+ const packageJson = JSON.parse(packageContent);
145
+ const currentVersion = packageJson.version;
146
+
147
+ // Calculate next version using npm version --dry-run
148
+ let versionArgs = options.type;
149
+ if (options.type === 'prerelease' && options.prereleaseId) {
150
+ versionArgs += ` --preid=${options.prereleaseId}`;
151
+ }
152
+
153
+ const nextVersion = execSync(`npm version ${versionArgs} --dry-run --no-git-tag-version`, {
154
+ encoding: 'utf8'
155
+ }).trim().replace(/^v/, '');
156
+
157
+ return {
158
+ current: currentVersion,
159
+ next: nextVersion,
160
+ tag: `v${nextVersion}`
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Generate changelog from git commits
166
+ */
167
+ async function generateChangelog(versionInfo: VersionInfo): Promise<ChangelogEntry[]> {
168
+ console.log(`${FAF_COLORS.fafCyan('šŸ“ Generating Changelog:')}`);
169
+
170
+ try {
171
+ // Get commits since last tag
172
+ const lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null || echo ""', {
173
+ encoding: 'utf8'
174
+ }).trim();
175
+
176
+ const gitRange = lastTag ? `${lastTag}..HEAD` : 'HEAD';
177
+ const commits = execSync(`git log --pretty=format:"%H|%s" ${gitRange}`, {
178
+ encoding: 'utf8'
179
+ }).trim().split('\n').filter(line => line);
180
+
181
+ const changelog: ChangelogEntry[] = commits.map(commit => {
182
+ const [hash, message] = commit.split('|');
183
+ return parseCommitMessage(message, hash);
184
+ });
185
+
186
+ // Group by type
187
+ const grouped = changelog.reduce((acc, entry) => {
188
+ if (!acc[entry.type]) acc[entry.type] = [];
189
+ acc[entry.type].push(entry);
190
+ return acc;
191
+ }, {} as Record<string, ChangelogEntry[]>);
192
+
193
+ // Display changelog preview
194
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Changes since ${lastTag || 'initial commit'}:`);
195
+
196
+ const typeLabels = {
197
+ feat: 'šŸš€ Features',
198
+ fix: 'šŸ› Bug Fixes',
199
+ docs: 'šŸ“š Documentation',
200
+ style: 'šŸŽØ Style',
201
+ refactor: 'ā™»ļø Refactoring',
202
+ test: '🧪 Tests',
203
+ chore: 'šŸ”§ Maintenance'
204
+ };
205
+
206
+ Object.entries(grouped).forEach(([type, entries]) => {
207
+ if (entries.length > 0) {
208
+ console.log(`${FAF_COLORS.fafCyan('│ ')}${typeLabels[type as keyof typeof typeLabels] || type}: ${entries.length} changes`);
209
+ }
210
+ });
211
+
212
+ console.log(`${FAF_COLORS.fafGreen('└─')} Total commits: ${changelog.length}`);
213
+ console.log();
214
+
215
+ return changelog;
216
+
217
+ } catch (error) {
218
+ console.log(`${FAF_COLORS.fafOrange('└─ āš ļø')} Could not generate changelog: ${error}`);
219
+ return [];
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Parse conventional commit message
225
+ */
226
+ function parseCommitMessage(message: string, hash: string): ChangelogEntry {
227
+ // Parse conventional commit format: type(scope): description
228
+ const conventionalMatch = message.match(/^(feat|fix|docs|style|refactor|test|chore)(?:\\(([^)]+)\\))?: (.+)/);
229
+
230
+ if (conventionalMatch) {
231
+ const [, type, scope, description] = conventionalMatch;
232
+ return {
233
+ type: type as ChangelogEntry['type'],
234
+ scope,
235
+ description,
236
+ breaking: message.includes('BREAKING CHANGE'),
237
+ hash: hash.substring(0, 7)
238
+ };
239
+ }
240
+
241
+ // Fallback for non-conventional commits
242
+ return {
243
+ type: 'chore',
244
+ description: message,
245
+ hash: hash.substring(0, 7)
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Update package.json version
251
+ */
252
+ async function updatePackageVersion(newVersion: string): Promise<void> {
253
+ const packagePath = path.join(process.cwd(), 'package.json');
254
+ const packageContent = await fs.readFile(packagePath, 'utf8');
255
+ const packageJson = JSON.parse(packageContent);
256
+
257
+ packageJson.version = newVersion;
258
+
259
+ await fs.writeFile(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
260
+ }
261
+
262
+ /**
263
+ * Run test suite
264
+ */
265
+ async function runTestSuite(): Promise<void> {
266
+ console.log(`${FAF_COLORS.fafCyan('🧪 Running Test Suite:')}`);
267
+
268
+ try {
269
+ // Run main tests
270
+ execSync('npm test', { stdio: 'inherit' });
271
+ console.log(`${FAF_COLORS.fafGreen('ā”œā”€ āœ…')} Unit tests passed`);
272
+
273
+ // Run audit tests
274
+ execSync('npm run test:audit', { stdio: 'inherit' });
275
+ console.log(`${FAF_COLORS.fafGreen('ā”œā”€ āœ…')} Audit tests passed`);
276
+
277
+ // Run linting
278
+ execSync('npm run lint', { stdio: 'inherit' });
279
+ console.log(`${FAF_COLORS.fafGreen('└─ āœ…')} Linting passed`);
280
+
281
+ } catch (error) {
282
+ throw new Error(`Test suite failed: ${error}`);
283
+ }
284
+
285
+ console.log();
286
+ }
287
+
288
+ /**
289
+ * Build project
290
+ */
291
+ async function buildProject(): Promise<void> {
292
+ console.log(`${FAF_COLORS.fafCyan('šŸ—ļø Building Project:')}`);
293
+
294
+ try {
295
+ execSync('npm run build', { stdio: 'inherit' });
296
+ console.log(`${FAF_COLORS.fafGreen('└─ āœ…')} Build completed successfully`);
297
+ } catch (error) {
298
+ throw new Error(`Build failed: ${error}`);
299
+ }
300
+
301
+ console.log();
302
+ }
303
+
304
+ /**
305
+ * Show release summary
306
+ */
307
+ function showReleaseSummary(
308
+ versionInfo: VersionInfo,
309
+ changelog: ChangelogEntry[],
310
+ options: ReleaseOptions
311
+ ): void {
312
+ console.log(`${FAF_COLORS.fafCyan('šŸ“‹ Release Summary:')}`);
313
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Version: ${versionInfo.current} → ${FAF_COLORS.fafGreen(versionInfo.next)}`);
314
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Type: ${options.type}`);
315
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Changes: ${changelog.length} commits`);
316
+ console.log(`${FAF_COLORS.fafCyan('ā”œā”€')} Mode: ${options.dryRun ? 'DRY RUN' : 'LIVE RELEASE'}`);
317
+ console.log(`${FAF_COLORS.fafCyan('└─')} Ready for championship deployment! šŸŽļø`);
318
+ console.log();
319
+ }
320
+
321
+ /**
322
+ * Generate release commands
323
+ */
324
+ function generateReleaseCommands(versionInfo: VersionInfo, options: ReleaseOptions): void {
325
+ console.log(`${FAF_COLORS.fafCyan('šŸš€ Release Commands:')}`);
326
+ console.log();
327
+
328
+ if (options.dryRun) {
329
+ console.log(`${FAF_COLORS.fafOrange('šŸ“ To complete the release, run:')}`);
330
+ console.log();
331
+ console.log(`${FAF_COLORS.fafCyan('# 1. Commit version change')}`);
332
+ console.log(`git add package.json`);
333
+ console.log(`git commit -m "chore: bump version to ${versionInfo.next}"`);
334
+ console.log();
335
+ console.log(`${FAF_COLORS.fafCyan('# 2. Create and push tag')}`);
336
+ console.log(`git tag ${versionInfo.tag}`);
337
+ console.log(`git push origin main --tags`);
338
+ console.log();
339
+ console.log(`${FAF_COLORS.fafCyan('# 3. GitHub Actions will handle the rest! šŸ')}`);
340
+ } else {
341
+ console.log(`${FAF_COLORS.fafGreen('āœ… Release prepared! Push to trigger automation:')}`);
342
+ console.log();
343
+ console.log(`git push origin main --tags`);
344
+ }
345
+
346
+ console.log();
347
+ console.log(`${FAF_COLORS.fafGreen('šŸ† Championship release engineering complete!')}`);
348
+ }
349
+
350
+ /**
351
+ * CLI argument parsing
352
+ */
353
+ function parseArgs(): ReleaseOptions {
354
+ const args = process.argv.slice(2);
355
+
356
+ const options: ReleaseOptions = {
357
+ type: 'patch',
358
+ dryRun: false,
359
+ skipTests: false
360
+ };
361
+
362
+ for (let i = 0; i < args.length; i++) {
363
+ const arg = args[i];
364
+
365
+ switch (arg) {
366
+ case '--type':
367
+ options.type = args[++i] as ReleaseOptions['type'];
368
+ break;
369
+ case '--dry-run':
370
+ options.dryRun = true;
371
+ break;
372
+ case '--skip-tests':
373
+ options.skipTests = true;
374
+ break;
375
+ case '--preid':
376
+ options.prereleaseId = args[++i];
377
+ break;
378
+ case '--help':
379
+ showHelp();
380
+ process.exit(0);
381
+ break;
382
+ }
383
+ }
384
+
385
+ return options;
386
+ }
387
+
388
+ /**
389
+ * Show help message
390
+ */
391
+ function showHelp(): void {
392
+ console.log(`
393
+ šŸš€ FAF CLI Release Preparation Script
394
+
395
+ Usage: npm run release [options]
396
+
397
+ Options:
398
+ --type <type> Release type: patch|minor|major|prerelease (default: patch)
399
+ --dry-run Show what would be done without making changes
400
+ --skip-tests Skip running test suite
401
+ --preid <id> Pre-release identifier (alpha, beta, rc)
402
+ --help Show this help
403
+
404
+ Examples:
405
+ npm run release # Patch release (1.0.0 → 1.0.1)
406
+ npm run release -- --type minor # Minor release (1.0.0 → 1.1.0)
407
+ npm run release -- --type major # Major release (1.0.0 → 2.0.0)
408
+ npm run release -- --dry-run # Preview changes only
409
+ npm run release -- --type prerelease --preid beta # Beta release
410
+
411
+ šŸŽļø F1-inspired engineering - Championship releases every time!
412
+ `);
413
+ }
414
+
415
+ // Run if called directly
416
+ if (require.main === module) {
417
+ const options = parseArgs();
418
+ prepareRelease(options).catch(console.error);
419
+ }
420
+
421
+ export { prepareRelease };
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Industry Showcase Runner
4
+ *
5
+ * Runs faf git on all famous repos and collects results
6
+ * Output: industry-showcase-results.json
7
+ */
8
+
9
+ import { execSync } from 'child_process';
10
+ import { readFileSync, writeFileSync } from 'fs';
11
+ import { join } from 'path';
12
+ import * as yaml from 'yaml';
13
+
14
+ interface ShowcaseData {
15
+ title: string;
16
+ description: string;
17
+ updated: string;
18
+ categories: Array<{
19
+ name: string;
20
+ repos: string[];
21
+ }>;
22
+ }
23
+
24
+ interface RepoResult {
25
+ repo: string;
26
+ owner: string;
27
+ name: string;
28
+ category: string;
29
+ currentScore: number;
30
+ newScore: number;
31
+ improvement: number;
32
+ tier: string;
33
+ stack: {
34
+ frontend?: string;
35
+ backend?: string;
36
+ runtime?: string;
37
+ build?: string;
38
+ database?: string;
39
+ hosting?: string;
40
+ };
41
+ projectType: string;
42
+ mainLanguage: string;
43
+ description: string;
44
+ status: 'success' | 'failed' | 'rate-limited';
45
+ error?: string;
46
+ }
47
+
48
+ interface ShowcaseResults {
49
+ generated: string;
50
+ totalRepos: number;
51
+ successCount: number;
52
+ failedCount: number;
53
+ avgImprovement: number;
54
+ categories: Array<{
55
+ name: string;
56
+ repos: RepoResult[];
57
+ }>;
58
+ }
59
+
60
+ function parseFafGitOutput(output: string): { currentScore: number; newScore: number; improvement: number; tier: string } | null {
61
+ // v4.3.0 format:
62
+ // Current: No .faf file āš ļø
63
+ // With FAF: 100% šŸ† Trophy
64
+
65
+ const currentMatch = output.match(/Current:\s+No \.faf file/);
66
+ const withFafMatch = output.match(/With FAF:\s+(\d+)%\s+([šŸ†šŸ„‡šŸ„ˆšŸ„‰šŸŸ¢šŸŸ”šŸ”“šŸ¤āš ļø]+)\s*(\w+)?/);
67
+
68
+ if (!withFafMatch) {
69
+ return null;
70
+ }
71
+
72
+ const currentScore = currentMatch ? 0 : 0; // Always 0 for "No .faf file"
73
+ const newScore = parseInt(withFafMatch[1]);
74
+ const tier = withFafMatch[3] || 'Unknown';
75
+ const improvement = newScore - currentScore;
76
+
77
+ return {
78
+ currentScore,
79
+ newScore,
80
+ improvement,
81
+ tier
82
+ };
83
+ }
84
+
85
+ function extractStackFromFaf(fafPath: string): any {
86
+ try {
87
+ const content = readFileSync(fafPath, 'utf-8');
88
+ const parsed = yaml.parse(content);
89
+
90
+ return {
91
+ stack: {
92
+ frontend: parsed.stack?.frontend !== 'slotignored' ? parsed.stack?.frontend : undefined,
93
+ backend: parsed.stack?.backend !== 'slotignored' ? parsed.stack?.backend : undefined,
94
+ runtime: parsed.stack?.runtime !== 'slotignored' ? parsed.stack?.runtime : undefined,
95
+ build: parsed.stack?.build !== 'slotignored' ? parsed.stack?.build : undefined,
96
+ database: parsed.stack?.database !== 'slotignored' ? parsed.stack?.database : undefined,
97
+ hosting: parsed.stack?.hosting !== 'slotignored' ? parsed.stack?.hosting : undefined,
98
+ },
99
+ projectType: parsed.project?.type || 'unknown',
100
+ mainLanguage: parsed.project?.main_language || 'unknown',
101
+ description: parsed.project?.goal || parsed.metadata?.description || ''
102
+ };
103
+ } catch (e) {
104
+ return {
105
+ stack: {},
106
+ projectType: 'unknown',
107
+ mainLanguage: 'unknown',
108
+ description: ''
109
+ };
110
+ }
111
+ }
112
+
113
+ async function runShowcase() {
114
+ console.log('šŸŽļø FAF Industry Showcase Runner\n');
115
+
116
+ // Read showcase data
117
+ const showcaseData: ShowcaseData = JSON.parse(
118
+ readFileSync(join(__dirname, 'industry-showcase.json'), 'utf-8')
119
+ );
120
+
121
+ const results: ShowcaseResults = {
122
+ generated: new Date().toISOString(),
123
+ totalRepos: 0,
124
+ successCount: 0,
125
+ failedCount: 0,
126
+ avgImprovement: 0,
127
+ categories: []
128
+ };
129
+
130
+ let totalImprovement = 0;
131
+
132
+ for (const category of showcaseData.categories) {
133
+ console.log(`\nšŸ“ Category: ${category.name}`);
134
+ console.log(` Testing ${category.repos.length} repos...\n`);
135
+
136
+ const categoryResults: RepoResult[] = [];
137
+
138
+ for (const repo of category.repos) {
139
+ results.totalRepos++;
140
+ const [owner, name] = repo.split('/');
141
+
142
+ process.stdout.write(` ⚔ ${repo}... `);
143
+
144
+ try {
145
+ // Run faf git
146
+ const output = execSync(
147
+ `node ${join(__dirname, '../dist/cli.js')} git ${repo}`,
148
+ {
149
+ encoding: 'utf-8',
150
+ timeout: 30000,
151
+ stdio: 'pipe'
152
+ }
153
+ );
154
+
155
+ // Parse output
156
+ const scores = parseFafGitOutput(output);
157
+
158
+ if (!scores) {
159
+ throw new Error('Failed to parse output');
160
+ }
161
+
162
+ // Extract stack from generated .faf file
163
+ const fafPath = join(process.cwd(), `${name}.faf`);
164
+ const fafData = extractStackFromFaf(fafPath);
165
+
166
+ const result: RepoResult = {
167
+ repo,
168
+ owner,
169
+ name,
170
+ category: category.name,
171
+ currentScore: scores.currentScore,
172
+ newScore: scores.newScore,
173
+ improvement: scores.improvement,
174
+ tier: scores.tier,
175
+ stack: fafData.stack,
176
+ projectType: fafData.projectType,
177
+ mainLanguage: fafData.mainLanguage,
178
+ description: fafData.description,
179
+ status: 'success'
180
+ };
181
+
182
+ categoryResults.push(result);
183
+ results.successCount++;
184
+ totalImprovement += scores.improvement;
185
+
186
+ console.log(`āœ… ${scores.currentScore}% → ${scores.newScore}% (+${scores.improvement})`);
187
+
188
+ } catch (error: any) {
189
+ results.failedCount++;
190
+
191
+ const result: RepoResult = {
192
+ repo,
193
+ owner,
194
+ name,
195
+ category: category.name,
196
+ currentScore: 0,
197
+ newScore: 0,
198
+ improvement: 0,
199
+ tier: 'Failed',
200
+ stack: {},
201
+ projectType: 'unknown',
202
+ mainLanguage: 'unknown',
203
+ description: '',
204
+ status: error.message?.includes('rate limit') ? 'rate-limited' : 'failed',
205
+ error: error.message
206
+ };
207
+
208
+ categoryResults.push(result);
209
+ console.log(`āŒ ${error.message.split('\n')[0]}`);
210
+ }
211
+
212
+ // Rate limit protection
213
+ await new Promise(resolve => setTimeout(resolve, 1000));
214
+ }
215
+
216
+ results.categories.push({
217
+ name: category.name,
218
+ repos: categoryResults
219
+ });
220
+ }
221
+
222
+ // Calculate average improvement
223
+ results.avgImprovement = Math.round(totalImprovement / results.successCount);
224
+
225
+ // Save results
226
+ const outputPath = join(__dirname, 'industry-showcase-results.json');
227
+ writeFileSync(outputPath, JSON.stringify(results, null, 2));
228
+
229
+ console.log(`\n\nšŸ† Showcase Complete!\n`);
230
+ console.log(` Total Repos: ${results.totalRepos}`);
231
+ console.log(` āœ… Success: ${results.successCount}`);
232
+ console.log(` āŒ Failed: ${results.failedCount}`);
233
+ console.log(` šŸ“ˆ Avg Improvement: +${results.avgImprovement} points\n`);
234
+ console.log(`šŸ“„ Results saved to: ${outputPath}\n`);
235
+ }
236
+
237
+ runShowcase().catch(console.error);