faf-cli 3.2.4 → 3.2.6

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 (205) hide show
  1. package/README.md +62 -0
  2. package/dist/big-orange/bigo-push.js +1 -1
  3. package/dist/big-orange/bigo-push.js.map +1 -1
  4. package/dist/big-orange/compare.js +21 -14
  5. package/dist/big-orange/compare.js.map +1 -1
  6. package/dist/big-orange/faf-generator.d.ts +2 -2
  7. package/dist/big-orange/faf-generator.d.ts.map +1 -1
  8. package/dist/big-orange/faf-generator.js +2 -2
  9. package/dist/big-orange/faf-generator.js.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +21 -0
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/ai-analyze.js +8 -10
  14. package/dist/commands/ai-analyze.js.map +1 -1
  15. package/dist/commands/ai-enhance.d.ts +1 -1
  16. package/dist/commands/ai-enhance.d.ts.map +1 -1
  17. package/dist/commands/ai-enhance.js +4 -4
  18. package/dist/commands/ai-enhance.js.map +1 -1
  19. package/dist/commands/audit.js.map +1 -1
  20. package/dist/commands/auto.d.ts.map +1 -1
  21. package/dist/commands/auto.js +51 -4
  22. package/dist/commands/auto.js.map +1 -1
  23. package/dist/commands/bi-sync.d.ts.map +1 -1
  24. package/dist/commands/bi-sync.js +0 -1
  25. package/dist/commands/bi-sync.js.map +1 -1
  26. package/dist/commands/chat.d.ts.map +1 -1
  27. package/dist/commands/chat.js +1 -1
  28. package/dist/commands/chat.js.map +1 -1
  29. package/dist/commands/check.js +1 -1
  30. package/dist/commands/check.js.map +1 -1
  31. package/dist/commands/convert.js +2 -2
  32. package/dist/commands/convert.js.map +1 -1
  33. package/dist/commands/doctor.js +9 -5
  34. package/dist/commands/doctor.js.map +1 -1
  35. package/dist/commands/drift.js +28 -21
  36. package/dist/commands/drift.js.map +1 -1
  37. package/dist/commands/edit-helper.d.ts +1 -1
  38. package/dist/commands/edit-helper.d.ts.map +1 -1
  39. package/dist/commands/edit-helper.js.map +1 -1
  40. package/dist/commands/edit.d.ts.map +1 -1
  41. package/dist/commands/edit.js +1 -1
  42. package/dist/commands/edit.js.map +1 -1
  43. package/dist/commands/enhance-real.d.ts.map +1 -1
  44. package/dist/commands/enhance-real.js +234 -129
  45. package/dist/commands/enhance-real.js.map +1 -1
  46. package/dist/commands/faf-auth.js.map +1 -1
  47. package/dist/commands/faf-dna.js.map +1 -1
  48. package/dist/commands/faf-log.js.map +1 -1
  49. package/dist/commands/faf-recover.js +27 -26
  50. package/dist/commands/faf-recover.js.map +1 -1
  51. package/dist/commands/fam.js +15 -12
  52. package/dist/commands/fam.js.map +1 -1
  53. package/dist/commands/formats.d.ts.map +1 -1
  54. package/dist/commands/formats.js +2 -0
  55. package/dist/commands/formats.js.map +1 -1
  56. package/dist/commands/git.d.ts.map +1 -1
  57. package/dist/commands/git.js +16 -8
  58. package/dist/commands/git.js.map +1 -1
  59. package/dist/commands/human.d.ts.map +1 -1
  60. package/dist/commands/human.js +2 -3
  61. package/dist/commands/human.js.map +1 -1
  62. package/dist/commands/index.d.ts.map +1 -1
  63. package/dist/commands/index.js +8 -6
  64. package/dist/commands/index.js.map +1 -1
  65. package/dist/commands/init.d.ts.map +1 -1
  66. package/dist/commands/init.js +18 -41
  67. package/dist/commands/init.js.map +1 -1
  68. package/dist/commands/migrate.js +2 -2
  69. package/dist/commands/migrate.js.map +1 -1
  70. package/dist/commands/quick.js +31 -16
  71. package/dist/commands/quick.js.map +1 -1
  72. package/dist/commands/readme.js +27 -14
  73. package/dist/commands/readme.js.map +1 -1
  74. package/dist/commands/rename.d.ts.map +1 -1
  75. package/dist/commands/rename.js +4 -3
  76. package/dist/commands/rename.js.map +1 -1
  77. package/dist/commands/score-v3.js +32 -21
  78. package/dist/commands/score-v3.js.map +1 -1
  79. package/dist/commands/search.js +4 -4
  80. package/dist/commands/search.js.map +1 -1
  81. package/dist/commands/share.js +2 -1
  82. package/dist/commands/share.js.map +1 -1
  83. package/dist/commands/show.js.map +1 -1
  84. package/dist/commands/status.d.ts.map +1 -1
  85. package/dist/commands/status.js +2 -2
  86. package/dist/commands/status.js.map +1 -1
  87. package/dist/commands/sync.d.ts.map +1 -1
  88. package/dist/commands/sync.js +21 -12
  89. package/dist/commands/sync.js.map +1 -1
  90. package/dist/commands/todo.js +1 -1
  91. package/dist/commands/todo.js.map +1 -1
  92. package/dist/commands/trust.d.ts.map +1 -1
  93. package/dist/commands/trust.js +2 -5
  94. package/dist/commands/trust.js.map +1 -1
  95. package/dist/commands/tsa.js.map +1 -1
  96. package/dist/commands/validate.js.map +1 -1
  97. package/dist/commands/verify.js +2 -2
  98. package/dist/commands/verify.js.map +1 -1
  99. package/dist/commands/version.js.map +1 -1
  100. package/dist/commands/vibe.d.ts +1 -1
  101. package/dist/commands/vibe.d.ts.map +1 -1
  102. package/dist/commands/vibe.js +2 -2
  103. package/dist/commands/vibe.js.map +1 -1
  104. package/dist/commands/yolo.d.ts +12 -0
  105. package/dist/commands/yolo.d.ts.map +1 -0
  106. package/dist/commands/yolo.js +206 -0
  107. package/dist/commands/yolo.js.map +1 -0
  108. package/dist/compiler/faf-compiler.d.ts.map +1 -1
  109. package/dist/compiler/faf-compiler.js +720 -123
  110. package/dist/compiler/faf-compiler.js.map +1 -1
  111. package/dist/converters/faf-converters.d.ts.map +1 -1
  112. package/dist/converters/faf-converters.js +22 -11
  113. package/dist/converters/faf-converters.js.map +1 -1
  114. package/dist/engines/art-ansi-renderer.js +3 -7
  115. package/dist/engines/art-ansi-renderer.js.map +1 -1
  116. package/dist/engines/dependency-tsa.d.ts.map +1 -1
  117. package/dist/engines/dependency-tsa.js +8 -4
  118. package/dist/engines/dependency-tsa.js.map +1 -1
  119. package/dist/engines/fab-formats-processor.d.ts.map +1 -1
  120. package/dist/engines/fab-formats-processor.js +87 -49
  121. package/dist/engines/fab-formats-processor.js.map +1 -1
  122. package/dist/engines/faf-dna.d.ts +1 -1
  123. package/dist/engines/faf-dna.d.ts.map +1 -1
  124. package/dist/engines/faf-dna.js +20 -27
  125. package/dist/engines/faf-dna.js.map +1 -1
  126. package/dist/engines/relentless-context-extractor.d.ts.map +1 -1
  127. package/dist/engines/relentless-context-extractor.js +42 -31
  128. package/dist/engines/relentless-context-extractor.js.map +1 -1
  129. package/dist/family/registry.d.ts.map +1 -1
  130. package/dist/family/registry.js +8 -6
  131. package/dist/family/registry.js.map +1 -1
  132. package/dist/fix-once/colors.d.ts.map +1 -1
  133. package/dist/fix-once/colors.js +23 -11
  134. package/dist/fix-once/colors.js.map +1 -1
  135. package/dist/fix-once/commander.js +1 -1
  136. package/dist/fix-once/commander.js.map +1 -1
  137. package/dist/generators/faf-generator-championship.d.ts.map +1 -1
  138. package/dist/generators/faf-generator-championship.js +145 -59
  139. package/dist/generators/faf-generator-championship.js.map +1 -1
  140. package/dist/github/github-extractor.d.ts.map +1 -1
  141. package/dist/github/github-extractor.js +55 -28
  142. package/dist/github/github-extractor.js.map +1 -1
  143. package/dist/github/repo-selector.js +24 -14
  144. package/dist/github/repo-selector.js.map +1 -1
  145. package/dist/output/experience-manager.js +6 -3
  146. package/dist/output/experience-manager.js.map +1 -1
  147. package/dist/taf/stats.js +21 -13
  148. package/dist/taf/stats.js.map +1 -1
  149. package/dist/telemetry/analytics.js +1 -1
  150. package/dist/telemetry/analytics.js.map +1 -1
  151. package/dist/utils/balance-visualizer.d.ts.map +1 -1
  152. package/dist/utils/balance-visualizer.js +3 -10
  153. package/dist/utils/balance-visualizer.js.map +1 -1
  154. package/dist/utils/championship-core.js +14 -7
  155. package/dist/utils/championship-core.js.map +1 -1
  156. package/dist/utils/color-utils.d.ts.map +1 -1
  157. package/dist/utils/color-utils.js +1 -0
  158. package/dist/utils/color-utils.js.map +1 -1
  159. package/dist/utils/email-opt-in.js +5 -3
  160. package/dist/utils/email-opt-in.js.map +1 -1
  161. package/dist/utils/fab-formats-engine.d.ts.map +1 -1
  162. package/dist/utils/fab-formats-engine.js +105 -52
  163. package/dist/utils/fab-formats-engine.js.map +1 -1
  164. package/dist/utils/file-utils.js +1 -1
  165. package/dist/utils/file-utils.js.map +1 -1
  166. package/dist/utils/markdown-to-context.js +24 -13
  167. package/dist/utils/markdown-to-context.js.map +1 -1
  168. package/dist/utils/native-cli-parser.d.ts +2 -2
  169. package/dist/utils/native-cli-parser.d.ts.map +1 -1
  170. package/dist/utils/native-cli-parser.js +14 -10
  171. package/dist/utils/native-cli-parser.js.map +1 -1
  172. package/dist/utils/native-file-finder.js +1 -1
  173. package/dist/utils/native-file-finder.js.map +1 -1
  174. package/dist/utils/platform-detector.d.ts.map +1 -1
  175. package/dist/utils/platform-detector.js +3 -1
  176. package/dist/utils/platform-detector.js.map +1 -1
  177. package/dist/utils/technical-credit.js +2 -2
  178. package/dist/utils/technical-credit.js.map +1 -1
  179. package/dist/utils/trust-cache.d.ts.map +1 -1
  180. package/dist/utils/trust-cache.js +1 -2
  181. package/dist/utils/trust-cache.js.map +1 -1
  182. package/dist/utils/turbo-cat-knowledge.d.ts +17 -4
  183. package/dist/utils/turbo-cat-knowledge.d.ts.map +1 -1
  184. package/dist/utils/turbo-cat-knowledge.js +560 -4
  185. package/dist/utils/turbo-cat-knowledge.js.map +1 -1
  186. package/dist/utils/turbo-cat-pyramid.d.ts +14 -4
  187. package/dist/utils/turbo-cat-pyramid.d.ts.map +1 -1
  188. package/dist/utils/turbo-cat-pyramid.js +113 -21
  189. package/dist/utils/turbo-cat-pyramid.js.map +1 -1
  190. package/dist/utils/turbo-cat.d.ts.map +1 -1
  191. package/dist/utils/turbo-cat.js +50 -25
  192. package/dist/utils/turbo-cat.js.map +1 -1
  193. package/dist/utils/universal-fuzzy-detector.js +10 -5
  194. package/dist/utils/universal-fuzzy-detector.js.map +1 -1
  195. package/dist/utils/update-checker.d.ts.map +1 -1
  196. package/dist/utils/update-checker.js +15 -9
  197. package/dist/utils/update-checker.js.map +1 -1
  198. package/dist/utils/vibe-sync.d.ts.map +1 -1
  199. package/dist/utils/vibe-sync.js +12 -7
  200. package/dist/utils/vibe-sync.js.map +1 -1
  201. package/dist/utils/yaml-generator.d.ts +12 -12
  202. package/dist/utils/yaml-generator.d.ts.map +1 -1
  203. package/dist/utils/yaml-generator.js +58 -31
  204. package/dist/utils/yaml-generator.js.map +1 -1
  205. package/package.json +1 -1
@@ -50,6 +50,594 @@ const chrome_extension_detector_1 = require("../utils/chrome-extension-detector"
50
50
  const fab_formats_processor_1 = require("../engines/fab-formats-processor");
51
51
  const path = __importStar(require("path"));
52
52
  // ============================================================================
53
+ // TYPE_DEFINITIONS - Single Source of Truth for Project Types
54
+ // ============================================================================
55
+ // Design: 21 slots always exist. Types define which slots COUNT toward scoring.
56
+ // slot_ignore in .faf file overrides type defaults for edge cases.
57
+ /**
58
+ * All 21 slots in the FAF system
59
+ */
60
+ const ALL_SLOTS = {
61
+ // Project slots (3)
62
+ project: ['project.name', 'project.goal', 'project.main_language'],
63
+ // Frontend slots (4)
64
+ frontend: ['stack.frontend', 'stack.css_framework', 'stack.ui_library', 'stack.state_management'],
65
+ // Backend slots (5)
66
+ backend: ['stack.backend', 'stack.api_type', 'stack.runtime', 'stack.database', 'stack.connection'],
67
+ // Universal slots (3)
68
+ universal: ['stack.hosting', 'stack.build', 'stack.cicd'],
69
+ // Human context slots (6)
70
+ human: ['human.who', 'human.what', 'human.why', 'human.where', 'human.when', 'human.how']
71
+ };
72
+ /**
73
+ * TYPE_DEFINITIONS - Maps project types to their applicable slots
74
+ *
75
+ * Each type specifies which slot categories count toward scoring.
76
+ * slot_ignore in .faf can override these for edge cases.
77
+ */
78
+ const TYPE_DEFINITIONS = {
79
+ // ============================================================================
80
+ // CLI/Tool Types (9 slots: project + human)
81
+ // ============================================================================
82
+ 'cli': {
83
+ description: 'Command-line interface tool',
84
+ categories: ['project', 'human'],
85
+ aliases: ['cli-tool', 'command-line']
86
+ },
87
+ 'cli-tool': {
88
+ description: 'Command-line interface tool',
89
+ categories: ['project', 'human']
90
+ },
91
+ // ============================================================================
92
+ // Library/Package Types (9 slots: project + human)
93
+ // ============================================================================
94
+ 'library': {
95
+ description: 'Reusable code library/package',
96
+ categories: ['project', 'human'],
97
+ aliases: ['lib', 'package']
98
+ },
99
+ 'npm-package': {
100
+ description: 'NPM package',
101
+ categories: ['project', 'human']
102
+ },
103
+ 'pip-package': {
104
+ description: 'Python pip package',
105
+ categories: ['project', 'human'],
106
+ aliases: ['pypi']
107
+ },
108
+ 'crate': {
109
+ description: 'Rust crate',
110
+ categories: ['project', 'human'],
111
+ aliases: ['rust-crate']
112
+ },
113
+ 'gem': {
114
+ description: 'Ruby gem',
115
+ categories: ['project', 'human'],
116
+ aliases: ['ruby-gem']
117
+ },
118
+ // ============================================================================
119
+ // AI/ML Types
120
+ // ============================================================================
121
+ 'mcp-server': {
122
+ description: 'Model Context Protocol server',
123
+ categories: ['project', 'backend', 'human']
124
+ },
125
+ 'data-science': {
126
+ description: 'Data science/analysis project',
127
+ categories: ['project', 'backend', 'human'],
128
+ aliases: ['data-analysis', 'analytics']
129
+ },
130
+ 'ml-model': {
131
+ description: 'Machine learning model',
132
+ categories: ['project', 'backend', 'human'],
133
+ aliases: ['ai-model', 'ml', 'machine-learning']
134
+ },
135
+ 'jupyter': {
136
+ description: 'Jupyter notebook project',
137
+ categories: ['project', 'human'],
138
+ aliases: ['notebook', 'ipynb']
139
+ },
140
+ 'data-pipeline': {
141
+ description: 'Data pipeline/ETL',
142
+ categories: ['project', 'backend', 'human'],
143
+ aliases: ['etl', 'pipeline']
144
+ },
145
+ // ============================================================================
146
+ // API/Backend Types
147
+ // ============================================================================
148
+ 'backend-api': {
149
+ description: 'Backend API service',
150
+ categories: ['project', 'backend', 'universal', 'human'],
151
+ aliases: ['api', 'backend', 'rest-api']
152
+ },
153
+ 'node-api': {
154
+ description: 'Node.js API service',
155
+ categories: ['project', 'backend', 'universal', 'human'],
156
+ aliases: ['express', 'fastify', 'nest']
157
+ },
158
+ 'python-api': {
159
+ description: 'Python API service',
160
+ categories: ['project', 'backend', 'universal', 'human'],
161
+ aliases: ['flask', 'fastapi', 'django-api']
162
+ },
163
+ 'python-app': {
164
+ description: 'Python application',
165
+ categories: ['project', 'backend', 'human']
166
+ },
167
+ 'go-api': {
168
+ description: 'Go API service',
169
+ categories: ['project', 'backend', 'universal', 'human'],
170
+ aliases: ['golang', 'gin', 'fiber']
171
+ },
172
+ 'rust-api': {
173
+ description: 'Rust API service',
174
+ categories: ['project', 'backend', 'universal', 'human'],
175
+ aliases: ['actix', 'axum', 'rocket']
176
+ },
177
+ 'graphql': {
178
+ description: 'GraphQL API service',
179
+ categories: ['project', 'backend', 'universal', 'human'],
180
+ aliases: ['graphql-api']
181
+ },
182
+ 'microservice': {
183
+ description: 'Microservice',
184
+ categories: ['project', 'backend', 'universal', 'human'],
185
+ aliases: ['service']
186
+ },
187
+ // ============================================================================
188
+ // Frontend Types
189
+ // ============================================================================
190
+ 'frontend': {
191
+ description: 'Frontend-only web application',
192
+ categories: ['project', 'frontend', 'universal', 'human']
193
+ },
194
+ 'svelte': {
195
+ description: 'Svelte web application',
196
+ categories: ['project', 'frontend', 'universal', 'human'],
197
+ aliases: ['sveltekit']
198
+ },
199
+ 'react': {
200
+ description: 'React web application',
201
+ categories: ['project', 'frontend', 'universal', 'human'],
202
+ aliases: ['reactjs']
203
+ },
204
+ 'vue': {
205
+ description: 'Vue.js web application',
206
+ categories: ['project', 'frontend', 'universal', 'human'],
207
+ aliases: ['vuejs', 'nuxt']
208
+ },
209
+ 'angular': {
210
+ description: 'Angular web application',
211
+ categories: ['project', 'frontend', 'universal', 'human']
212
+ },
213
+ 'nextjs': {
214
+ description: 'Next.js application',
215
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
216
+ aliases: ['next']
217
+ },
218
+ 'remix': {
219
+ description: 'Remix application',
220
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
221
+ },
222
+ 'astro': {
223
+ description: 'Astro static site',
224
+ categories: ['project', 'frontend', 'universal', 'human']
225
+ },
226
+ 'solid': {
227
+ description: 'SolidJS application',
228
+ categories: ['project', 'frontend', 'universal', 'human'],
229
+ aliases: ['solidjs']
230
+ },
231
+ 'qwik': {
232
+ description: 'Qwik application',
233
+ categories: ['project', 'frontend', 'universal', 'human']
234
+ },
235
+ // ============================================================================
236
+ // Fullstack Types
237
+ // ============================================================================
238
+ 'fullstack': {
239
+ description: 'Full-stack web application',
240
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
241
+ },
242
+ 't3': {
243
+ description: 'T3 Stack (Next.js + tRPC + Prisma)',
244
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
245
+ aliases: ['t3-stack', 'create-t3-app']
246
+ },
247
+ 'mern': {
248
+ description: 'MERN Stack (MongoDB + Express + React + Node)',
249
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
250
+ },
251
+ 'mean': {
252
+ description: 'MEAN Stack (MongoDB + Express + Angular + Node)',
253
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
254
+ },
255
+ 'lamp': {
256
+ description: 'LAMP Stack (Linux + Apache + MySQL + PHP)',
257
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
258
+ },
259
+ 'django': {
260
+ description: 'Django web application',
261
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
262
+ },
263
+ 'rails': {
264
+ description: 'Ruby on Rails application',
265
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
266
+ aliases: ['ruby-on-rails', 'ror']
267
+ },
268
+ 'laravel': {
269
+ description: 'Laravel PHP application',
270
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
271
+ },
272
+ // ============================================================================
273
+ // Mobile Types
274
+ // ============================================================================
275
+ 'mobile': {
276
+ description: 'Mobile application',
277
+ categories: ['project', 'frontend', 'human'],
278
+ aliases: ['mobile-app']
279
+ },
280
+ 'react-native': {
281
+ description: 'React Native mobile app',
282
+ categories: ['project', 'frontend', 'human'],
283
+ aliases: ['rn', 'expo']
284
+ },
285
+ 'flutter': {
286
+ description: 'Flutter mobile app',
287
+ categories: ['project', 'frontend', 'human'],
288
+ aliases: ['dart']
289
+ },
290
+ 'ios': {
291
+ description: 'iOS native app',
292
+ categories: ['project', 'frontend', 'human'],
293
+ aliases: ['swift', 'swiftui']
294
+ },
295
+ 'android': {
296
+ description: 'Android native app',
297
+ categories: ['project', 'frontend', 'human'],
298
+ aliases: ['kotlin', 'kotlin-android']
299
+ },
300
+ 'ionic': {
301
+ description: 'Ionic mobile app',
302
+ categories: ['project', 'frontend', 'human'],
303
+ aliases: ['capacitor']
304
+ },
305
+ // ============================================================================
306
+ // Desktop Types
307
+ // ============================================================================
308
+ 'desktop': {
309
+ description: 'Desktop application',
310
+ categories: ['project', 'frontend', 'human'],
311
+ aliases: ['desktop-app']
312
+ },
313
+ 'electron': {
314
+ description: 'Electron desktop app',
315
+ categories: ['project', 'frontend', 'human']
316
+ },
317
+ 'tauri': {
318
+ description: 'Tauri desktop app',
319
+ categories: ['project', 'frontend', 'human']
320
+ },
321
+ 'qt': {
322
+ description: 'Qt desktop application',
323
+ categories: ['project', 'frontend', 'human'],
324
+ aliases: ['pyqt', 'pyside']
325
+ },
326
+ 'gtk': {
327
+ description: 'GTK desktop application',
328
+ categories: ['project', 'frontend', 'human']
329
+ },
330
+ // ============================================================================
331
+ // Browser Extensions
332
+ // ============================================================================
333
+ 'chrome-extension': {
334
+ description: 'Chrome browser extension',
335
+ categories: ['project', 'human'],
336
+ aliases: ['browser-extension', 'extension']
337
+ },
338
+ 'firefox-extension': {
339
+ description: 'Firefox browser extension',
340
+ categories: ['project', 'human'],
341
+ aliases: ['firefox-addon']
342
+ },
343
+ 'safari-extension': {
344
+ description: 'Safari browser extension',
345
+ categories: ['project', 'human']
346
+ },
347
+ // ============================================================================
348
+ // Automation/Workflow Types
349
+ // ============================================================================
350
+ 'n8n-workflow': {
351
+ description: 'n8n automation workflow',
352
+ categories: ['project', 'backend', 'human'],
353
+ aliases: ['n8n']
354
+ },
355
+ 'zapier': {
356
+ description: 'Zapier integration',
357
+ categories: ['project', 'human']
358
+ },
359
+ 'github-action': {
360
+ description: 'GitHub Action',
361
+ categories: ['project', 'human'],
362
+ aliases: ['gha', 'action']
363
+ },
364
+ // ============================================================================
365
+ // DevOps/Infrastructure Types
366
+ // ============================================================================
367
+ 'terraform': {
368
+ description: 'Terraform infrastructure',
369
+ categories: ['project', 'human'],
370
+ aliases: ['tf', 'iac']
371
+ },
372
+ 'kubernetes': {
373
+ description: 'Kubernetes configuration',
374
+ categories: ['project', 'human'],
375
+ aliases: ['k8s', 'helm']
376
+ },
377
+ 'docker': {
378
+ description: 'Docker/container configuration',
379
+ categories: ['project', 'human'],
380
+ aliases: ['dockerfile', 'container']
381
+ },
382
+ 'ansible': {
383
+ description: 'Ansible playbooks',
384
+ categories: ['project', 'human']
385
+ },
386
+ 'pulumi': {
387
+ description: 'Pulumi infrastructure',
388
+ categories: ['project', 'human']
389
+ },
390
+ 'infrastructure': {
391
+ description: 'Infrastructure as code',
392
+ categories: ['project', 'human'],
393
+ aliases: ['infra', 'devops']
394
+ },
395
+ // ============================================================================
396
+ // Static Sites / Documentation
397
+ // ============================================================================
398
+ 'static-html': {
399
+ description: 'Static HTML website',
400
+ categories: ['project', 'frontend', 'human'],
401
+ aliases: ['static-site', 'html']
402
+ },
403
+ 'landing-page': {
404
+ description: 'Landing page website',
405
+ categories: ['project', 'frontend', 'human'],
406
+ aliases: ['landing']
407
+ },
408
+ 'documentation': {
409
+ description: 'Documentation site',
410
+ categories: ['project', 'frontend', 'human'],
411
+ aliases: ['docs']
412
+ },
413
+ 'docusaurus': {
414
+ description: 'Docusaurus documentation site',
415
+ categories: ['project', 'frontend', 'human']
416
+ },
417
+ 'mkdocs': {
418
+ description: 'MkDocs documentation site',
419
+ categories: ['project', 'human']
420
+ },
421
+ 'vitepress': {
422
+ description: 'VitePress documentation site',
423
+ categories: ['project', 'frontend', 'human']
424
+ },
425
+ 'storybook': {
426
+ description: 'Storybook component library',
427
+ categories: ['project', 'frontend', 'human']
428
+ },
429
+ // ============================================================================
430
+ // CMS Types
431
+ // ============================================================================
432
+ 'wordpress': {
433
+ description: 'WordPress site/plugin/theme',
434
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
435
+ aliases: ['wp']
436
+ },
437
+ 'cms': {
438
+ description: 'Content management system',
439
+ categories: ['project', 'frontend', 'backend', 'human']
440
+ },
441
+ 'strapi': {
442
+ description: 'Strapi headless CMS',
443
+ categories: ['project', 'backend', 'universal', 'human']
444
+ },
445
+ 'sanity': {
446
+ description: 'Sanity.io CMS',
447
+ categories: ['project', 'backend', 'human']
448
+ },
449
+ 'contentful': {
450
+ description: 'Contentful CMS integration',
451
+ categories: ['project', 'human']
452
+ },
453
+ // ============================================================================
454
+ // Game Development Types
455
+ // ============================================================================
456
+ 'game': {
457
+ description: 'Game project',
458
+ categories: ['project', 'frontend', 'human'],
459
+ aliases: ['gamedev']
460
+ },
461
+ 'unity': {
462
+ description: 'Unity game',
463
+ categories: ['project', 'frontend', 'human'],
464
+ aliases: ['unity3d']
465
+ },
466
+ 'godot': {
467
+ description: 'Godot game',
468
+ categories: ['project', 'frontend', 'human']
469
+ },
470
+ 'unreal': {
471
+ description: 'Unreal Engine game',
472
+ categories: ['project', 'frontend', 'human'],
473
+ aliases: ['ue4', 'ue5']
474
+ },
475
+ 'phaser': {
476
+ description: 'Phaser.js game',
477
+ categories: ['project', 'frontend', 'human']
478
+ },
479
+ 'threejs': {
480
+ description: 'Three.js 3D project',
481
+ categories: ['project', 'frontend', 'human'],
482
+ aliases: ['three', '3d', 'webgl']
483
+ },
484
+ // ============================================================================
485
+ // Blockchain/Web3 Types
486
+ // ============================================================================
487
+ 'smart-contract': {
488
+ description: 'Smart contract',
489
+ categories: ['project', 'human'],
490
+ aliases: ['solidity', 'contract']
491
+ },
492
+ 'dapp': {
493
+ description: 'Decentralized application',
494
+ categories: ['project', 'frontend', 'human'],
495
+ aliases: ['web3', 'blockchain']
496
+ },
497
+ 'hardhat': {
498
+ description: 'Hardhat Ethereum project',
499
+ categories: ['project', 'human']
500
+ },
501
+ 'foundry': {
502
+ description: 'Foundry Ethereum project',
503
+ categories: ['project', 'human'],
504
+ aliases: ['forge']
505
+ },
506
+ // ============================================================================
507
+ // Monorepo Types
508
+ // Monorepos are CONTAINERS - they need ALL slots because they can contain
509
+ // any combination of frontend, backend, libraries, etc.
510
+ // The monorepo root .faf describes the overall architecture.
511
+ // Individual packages can have their own .faf files with specific types.
512
+ // ============================================================================
513
+ 'monorepo': {
514
+ description: 'Monorepo - multi-package repository',
515
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
516
+ aliases: ['mono', 'workspace']
517
+ },
518
+ 'turborepo': {
519
+ description: 'Turborepo monorepo',
520
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
521
+ aliases: ['turbo']
522
+ },
523
+ 'nx': {
524
+ description: 'Nx monorepo',
525
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
526
+ },
527
+ 'lerna': {
528
+ description: 'Lerna monorepo',
529
+ categories: ['project', 'frontend', 'backend', 'universal', 'human']
530
+ },
531
+ 'pnpm-workspace': {
532
+ description: 'pnpm workspace monorepo',
533
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
534
+ aliases: ['pnpm-mono']
535
+ },
536
+ 'yarn-workspace': {
537
+ description: 'Yarn workspace monorepo',
538
+ categories: ['project', 'frontend', 'backend', 'universal', 'human'],
539
+ aliases: ['yarn-mono']
540
+ },
541
+ // ============================================================================
542
+ // Embedded/Systems Types
543
+ // ============================================================================
544
+ 'embedded': {
545
+ description: 'Embedded systems',
546
+ categories: ['project', 'human'],
547
+ aliases: ['firmware', 'iot']
548
+ },
549
+ 'arduino': {
550
+ description: 'Arduino project',
551
+ categories: ['project', 'human']
552
+ },
553
+ 'raspberry-pi': {
554
+ description: 'Raspberry Pi project',
555
+ categories: ['project', 'human'],
556
+ aliases: ['rpi']
557
+ },
558
+ 'wasm': {
559
+ description: 'WebAssembly module',
560
+ categories: ['project', 'human'],
561
+ aliases: ['webassembly']
562
+ },
563
+ // ============================================================================
564
+ // Testing Types
565
+ // ============================================================================
566
+ 'test-suite': {
567
+ description: 'Test suite/framework',
568
+ categories: ['project', 'human'],
569
+ aliases: ['testing', 'tests']
570
+ },
571
+ 'e2e-tests': {
572
+ description: 'End-to-end test suite',
573
+ categories: ['project', 'human'],
574
+ aliases: ['e2e', 'playwright', 'cypress']
575
+ },
576
+ // ============================================================================
577
+ // Default
578
+ // ============================================================================
579
+ 'generic': {
580
+ description: 'Generic project (fallback)',
581
+ categories: ['project', 'universal', 'human']
582
+ }
583
+ };
584
+ /**
585
+ * Get applicable slots for a project type
586
+ */
587
+ function getSlotsForType(projectType) {
588
+ // Check for aliases first
589
+ for (const [type, def] of Object.entries(TYPE_DEFINITIONS)) {
590
+ if (def.aliases?.includes(projectType)) {
591
+ projectType = type;
592
+ break;
593
+ }
594
+ }
595
+ const typeDef = TYPE_DEFINITIONS[projectType] || TYPE_DEFINITIONS['generic'];
596
+ const slots = [];
597
+ for (const category of typeDef.categories) {
598
+ slots.push(...ALL_SLOTS[category]);
599
+ }
600
+ return slots;
601
+ }
602
+ /**
603
+ * Parse slot_ignore from .faf content
604
+ * Accepts: slot_ignore: [hosting, cicd] or slot_ignore: hosting, cicd
605
+ */
606
+ function parseSlotIgnore(ast) {
607
+ const slotIgnore = ast.slot_ignore || ast.slotIgnore || ast.ignore_slots;
608
+ if (!slotIgnore) {
609
+ return [];
610
+ }
611
+ // Handle array format
612
+ if (Array.isArray(slotIgnore)) {
613
+ return slotIgnore.map((s) => normalizeSlotName(s));
614
+ }
615
+ // Handle string format (comma-separated)
616
+ if (typeof slotIgnore === 'string') {
617
+ return slotIgnore.split(',').map((s) => normalizeSlotName(s.trim()));
618
+ }
619
+ return [];
620
+ }
621
+ /**
622
+ * Normalize slot names to full path
623
+ * Examples: "hosting" -> "stack.hosting", "who" -> "human.who"
624
+ */
625
+ function normalizeSlotName(slot) {
626
+ // Already has prefix
627
+ if (slot.includes('.')) {
628
+ return slot;
629
+ }
630
+ // Check each category for the slot
631
+ for (const [_category, slots] of Object.entries(ALL_SLOTS)) {
632
+ const fullSlot = slots.find(s => s.endsWith(`.${slot}`));
633
+ if (fullSlot) {
634
+ return fullSlot;
635
+ }
636
+ }
637
+ // Return as-is if not found (will be ignored)
638
+ return slot;
639
+ }
640
+ // ============================================================================
53
641
  // Main Compiler Class
54
642
  // ============================================================================
55
643
  class FafCompiler {
@@ -233,20 +821,27 @@ class FafCompiler {
233
821
  if (analysis.context) {
234
822
  const ctx = analysis.context;
235
823
  // Map discovered items (WITHOUT modifying original)
236
- if (ctx.projectName)
824
+ if (ctx.projectName) {
237
825
  discovered.projectName = ctx.projectName;
238
- if (ctx.mainLanguage)
826
+ }
827
+ if (ctx.mainLanguage) {
239
828
  discovered.mainLanguage = ctx.mainLanguage;
240
- if (ctx.framework)
829
+ }
830
+ if (ctx.framework) {
241
831
  discovered.framework = ctx.framework;
242
- if (ctx.database)
832
+ }
833
+ if (ctx.database) {
243
834
  discovered.database = ctx.database;
244
- if (ctx.backend)
835
+ }
836
+ if (ctx.backend) {
245
837
  discovered.backend = ctx.backend;
246
- if (ctx.hosting)
838
+ }
839
+ if (ctx.hosting) {
247
840
  discovered.hosting = ctx.hosting;
248
- if (ctx.buildTool)
841
+ }
842
+ if (ctx.buildTool) {
249
843
  discovered.buildTool = ctx.buildTool;
844
+ }
250
845
  }
251
846
  return discovered;
252
847
  }
@@ -295,150 +890,123 @@ class FafCompiler {
295
890
  }
296
891
  buildIR(ast) {
297
892
  const slots = [];
298
- // Detect project type to determine applicable stack slots
893
+ // Detect project type to determine applicable slots
299
894
  const projectType = this.detectProjectTypeFromContext(ast);
300
895
  if (process.env.FAF_DEBUG) {
301
896
  console.log(`\n[DEBUG] Project type detected: ${projectType}`);
302
897
  }
303
- const isFrontendProject = this.requiresFrontendStack(projectType);
304
- const isBackendProject = this.requiresBackendStack(projectType);
305
- // Auto-fill project.main_language for n8n workflows BEFORE reading slots
306
- if (projectType === 'n8n-workflow') {
307
- if (!ast.project)
308
- ast.project = {};
309
- if (!ast.project.main_language) {
310
- ast.project.main_language = ast.tech_stack?.primary_language || 'JSON (workflow definition)';
898
+ // Parse slot_ignore from the .faf file
899
+ const ignoredSlots = parseSlotIgnore(ast);
900
+ if (process.env.FAF_DEBUG && ignoredSlots.length > 0) {
901
+ console.log(`[DEBUG] slot_ignore: ${ignoredSlots.join(', ')}`);
902
+ }
903
+ // Get applicable slots for this project type
904
+ const applicableSlots = getSlotsForType(projectType);
905
+ if (process.env.FAF_DEBUG) {
906
+ console.log(`[DEBUG] Applicable slots (${applicableSlots.length}): ${applicableSlots.join(', ')}`);
907
+ }
908
+ // Filter out ignored slots
909
+ const activeSlots = applicableSlots.filter(slot => !ignoredSlots.includes(slot));
910
+ if (process.env.FAF_DEBUG && ignoredSlots.length > 0) {
911
+ console.log(`[DEBUG] Active slots after ignore (${activeSlots.length}): ${activeSlots.join(', ')}`);
912
+ }
913
+ // Helper to get value from ast based on slot path
914
+ const getSlotValue = (slotPath) => {
915
+ const parts = slotPath.split('.');
916
+ if (parts[0] === 'project') {
917
+ return ast.project?.[parts[1]];
918
+ }
919
+ else if (parts[0] === 'stack') {
920
+ return ast.stack?.[parts[1]];
311
921
  }
922
+ else if (parts[0] === 'human') {
923
+ return ast.human_context?.[parts[1]];
924
+ }
925
+ return undefined;
926
+ };
927
+ // Add all active slots to the IR
928
+ for (const slotPath of activeSlots) {
929
+ const value = getSlotValue(slotPath);
930
+ this.addSlot(slots, slotPath, value, 'string', 'original', 1);
312
931
  }
313
- // Project slots (3)
314
- this.addSlot(slots, 'project.name', ast.project?.name, 'string', 'original', 1);
315
- this.addSlot(slots, 'project.goal', ast.project?.goal, 'string', 'original', 1);
316
- this.addSlot(slots, 'project.main_language', ast.project?.main_language, 'string', 'original', 1);
317
- // Chrome Extension auto-fill: If it's a Chrome Extension, intelligently fill slots
932
+ // Special handling for certain project types (auto-fill missing values)
933
+ // Chrome Extension: auto-fill technical context
318
934
  if (projectType === 'chrome-extension') {
319
- // Auto-fill Chrome Extension technical context
320
- if (!ast.stack)
935
+ if (!ast.stack) {
321
936
  ast.stack = {};
322
- if (!ast.stack.runtime)
937
+ }
938
+ if (!ast.stack.runtime) {
323
939
  ast.stack.runtime = 'Chrome/Browser';
324
- if (!ast.stack.hosting)
940
+ }
941
+ if (!ast.stack.hosting) {
325
942
  ast.stack.hosting = 'Chrome Web Store';
326
- if (!ast.stack.api_type)
943
+ }
944
+ if (!ast.stack.api_type) {
327
945
  ast.stack.api_type = 'Chrome Extension APIs';
328
- if (!ast.stack.backend)
946
+ }
947
+ if (!ast.stack.backend) {
329
948
  ast.stack.backend = 'Service Worker';
330
- if (!ast.stack.database)
949
+ }
950
+ if (!ast.stack.database) {
331
951
  ast.stack.database = 'chrome.storage API';
332
- if (!ast.deployment)
333
- ast.deployment = 'Web Store Upload';
334
- // Add Chrome Extension specific slots
335
- this.addSlot(slots, 'stack.runtime', ast.stack.runtime, 'string', 'discovered', 1);
336
- this.addSlot(slots, 'stack.hosting', ast.stack.hosting, 'string', 'discovered', 1);
337
- this.addSlot(slots, 'stack.api_type', ast.stack.api_type, 'string', 'discovered', 1);
338
- this.addSlot(slots, 'stack.backend', ast.stack.backend, 'string', 'discovered', 1);
339
- this.addSlot(slots, 'stack.database', ast.stack.database, 'string', 'discovered', 1);
340
- this.addSlot(slots, 'stack.deployment', ast.deployment, 'string', 'discovered', 1);
341
- // Also include frontend framework if specified (e.g., Svelte Chrome Extension)
342
- if (ast.stack?.frontend) {
343
- this.addSlot(slots, 'stack.frontend', ast.stack.frontend, 'string', 'original', 1);
344
952
  }
345
- if (ast.stack?.build) {
346
- this.addSlot(slots, 'stack.build', ast.stack.build, 'string', 'original', 1);
953
+ // Add auto-filled slots (only if they're in the active slots list)
954
+ const chromeSlots = ['stack.runtime', 'stack.hosting', 'stack.api_type', 'stack.backend', 'stack.database'];
955
+ for (const slot of chromeSlots) {
956
+ if (activeSlots.includes(slot) && !slots.find(s => s.path === slot)) {
957
+ this.addSlot(slots, slot, getSlotValue(slot), 'string', 'discovered', 1);
958
+ }
347
959
  }
348
960
  }
349
- // Static HTML auto-fill: If it's a static HTML site, intelligently fill slots
961
+ // Static HTML: auto-fill technical context
350
962
  if (projectType === 'static-html' || projectType === 'landing-page') {
351
- if (!ast.stack)
963
+ if (!ast.stack) {
352
964
  ast.stack = {};
353
- if (!ast.stack.frontend)
965
+ }
966
+ if (!ast.stack.frontend) {
354
967
  ast.stack.frontend = 'HTML/CSS/JavaScript';
355
- if (!ast.stack.runtime)
968
+ }
969
+ if (!ast.stack.runtime) {
356
970
  ast.stack.runtime = 'Browser';
357
- if (!ast.stack.hosting)
971
+ }
972
+ if (!ast.stack.hosting) {
358
973
  ast.stack.hosting = 'Static Hosting';
359
- if (!ast.stack.build)
974
+ }
975
+ if (!ast.stack.build) {
360
976
  ast.stack.build = 'Direct HTML (no build step)';
361
- this.addSlot(slots, 'stack.frontend', ast.stack.frontend, 'string', 'discovered', 1);
362
- this.addSlot(slots, 'stack.runtime', ast.stack.runtime, 'string', 'discovered', 1);
363
- this.addSlot(slots, 'stack.hosting', ast.stack.hosting, 'string', 'discovered', 1);
364
- this.addSlot(slots, 'stack.build', ast.stack.build, 'string', 'discovered', 1);
365
- // Leave backend, database, cicd as "None" - legitimate for static sites
977
+ }
366
978
  }
367
- // n8n Workflow auto-fill: Map n8n-specific fields to standard 21-slot system
979
+ // n8n Workflow: auto-fill technical context
368
980
  if (projectType === 'n8n-workflow') {
369
- // Map n8n context to existing stack slots (efficient, no slot inflation)
370
- if (!ast.stack)
981
+ if (!ast.project) {
982
+ ast.project = {};
983
+ }
984
+ if (!ast.project.main_language) {
985
+ ast.project.main_language = ast.tech_stack?.primary_language || 'JSON (workflow definition)';
986
+ }
987
+ if (!ast.stack) {
371
988
  ast.stack = {};
372
- // Runtime = workflow engine
989
+ }
373
990
  if (!ast.stack.runtime) {
374
991
  ast.stack.runtime = ast.tech_stack?.workflow_engine || 'n8n';
375
992
  }
376
- // Backend = n8n server runtime
377
993
  if (!ast.stack.backend) {
378
994
  ast.stack.backend = 'Node.js (n8n server)';
379
995
  }
380
- // API type = n8n trigger types
381
996
  if (!ast.stack.api_type) {
382
997
  ast.stack.api_type = 'Webhooks + HTTP';
383
998
  }
384
- // Database = vector DB or workflow state
385
999
  if (!ast.stack.database) {
386
1000
  ast.stack.database = ast.tech_stack?.infrastructure?.vector_db || 'Workflow State';
387
1001
  }
388
- // Hosting = deployment location
389
1002
  if (!ast.stack.hosting) {
390
1003
  ast.stack.hosting = 'n8n Cloud';
391
1004
  }
392
- // Build tool = how workflows are created
393
1005
  if (!ast.stack.build) {
394
1006
  ast.stack.build = 'n8n Visual Editor';
395
1007
  }
396
- // Connection = integrations (maps to stack.connection slot)
397
- if (!ast.stack.connection && ast.tech_stack?.integrations) {
398
- ast.stack.connection = Array.isArray(ast.tech_stack.integrations)
399
- ? ast.tech_stack.integrations.join(', ')
400
- : String(ast.tech_stack.integrations);
401
- }
402
- // Add n8n-specific slots (maps to standard 21-slot system)
403
- // Backend slots (5)
404
- this.addSlot(slots, 'stack.runtime', ast.stack.runtime, 'string', 'discovered', 1);
405
- this.addSlot(slots, 'stack.backend', ast.stack.backend, 'string', 'discovered', 1);
406
- this.addSlot(slots, 'stack.api_type', ast.stack.api_type, 'string', 'discovered', 1);
407
- this.addSlot(slots, 'stack.database', ast.stack.database, 'string', 'discovered', 1);
408
- this.addSlot(slots, 'stack.connection', ast.stack.connection, 'string', 'original', 1);
409
- // Frontend slots (4) - n8n workflows don't have frontend, but we still count them
410
- this.addSlot(slots, 'stack.frontend', ast.stack?.frontend, 'string', 'original', 1);
411
- this.addSlot(slots, 'stack.css_framework', ast.stack?.css_framework, 'string', 'original', 1);
412
- this.addSlot(slots, 'stack.ui_library', ast.stack?.ui_library, 'string', 'original', 1);
413
- this.addSlot(slots, 'stack.state_management', ast.stack?.state_management, 'string', 'original', 1);
414
- // Universal slots (3) will be added below (hosting, build, cicd)
415
- }
416
- // Stack slots - only add relevant ones based on project type
417
- if (isFrontendProject) {
418
- this.addSlot(slots, 'stack.frontend', ast.stack?.frontend, 'string', 'original', 1);
419
- this.addSlot(slots, 'stack.css_framework', ast.stack?.css_framework, 'string', 'original', 1);
420
- this.addSlot(slots, 'stack.ui_library', ast.stack?.ui_library, 'string', 'original', 1);
421
- this.addSlot(slots, 'stack.state_management', ast.stack?.state_management, 'string', 'original', 1);
422
- }
423
- if (isBackendProject) {
424
- this.addSlot(slots, 'stack.backend', ast.stack?.backend, 'string', 'original', 1);
425
- this.addSlot(slots, 'stack.api_type', ast.stack?.api_type, 'string', 'original', 1);
426
- this.addSlot(slots, 'stack.runtime', ast.stack?.runtime, 'string', 'original', 1);
427
- this.addSlot(slots, 'stack.database', ast.stack?.database, 'string', 'original', 1);
428
- this.addSlot(slots, 'stack.connection', ast.stack?.connection, 'string', 'original', 1);
429
- }
430
- // Universal stack slots (apply to all project types)
431
- this.addSlot(slots, 'stack.hosting', ast.stack?.hosting, 'string', 'original', 1);
432
- this.addSlot(slots, 'stack.build', ast.stack?.build, 'string', 'original', 1);
433
- this.addSlot(slots, 'stack.cicd', ast.stack?.cicd, 'string', 'original', 1);
434
- // Human context slots (6) - always applicable
435
- this.addSlot(slots, 'human.who', ast.human_context?.who, 'string', 'original', 1);
436
- this.addSlot(slots, 'human.what', ast.human_context?.what, 'string', 'original', 1);
437
- this.addSlot(slots, 'human.why', ast.human_context?.why, 'string', 'original', 1);
438
- this.addSlot(slots, 'human.where', ast.human_context?.where, 'string', 'original', 1);
439
- this.addSlot(slots, 'human.when', ast.human_context?.when, 'string', 'original', 1);
440
- this.addSlot(slots, 'human.how', ast.human_context?.how, 'string', 'original', 1);
441
- // Discovered slots (if any)
1008
+ }
1009
+ // Discovered slots (if any) - only add if slot is active and not already filled
442
1010
  if (ast._discovered) {
443
1011
  const discovered = ast._discovered;
444
1012
  const mapping = {
@@ -450,9 +1018,12 @@ class FafCompiler {
450
1018
  'hosting': 'stack.hosting',
451
1019
  'buildTool': 'stack.build'
452
1020
  };
453
- for (const [key, path] of Object.entries(mapping)) {
454
- if (discovered[key] && !this.hasValue(ast, path)) {
455
- this.addSlot(slots, `discovery.${path}`, discovered[key], 'string', 'discovered', 1);
1021
+ for (const [key, slotPath] of Object.entries(mapping)) {
1022
+ if (discovered[key] && activeSlots.includes(slotPath) && !this.hasValue(ast, slotPath)) {
1023
+ // Only add to discovery if not already in slots
1024
+ if (!slots.find(s => s.path === slotPath)) {
1025
+ this.addSlot(slots, `discovery.${slotPath}`, discovered[key], 'string', 'discovered', 1);
1026
+ }
456
1027
  }
457
1028
  }
458
1029
  }
@@ -484,8 +1055,9 @@ class FafCompiler {
484
1055
  }
485
1056
  isSlotFilled(value) {
486
1057
  // Handle null, undefined, false explicitly
487
- if (value === null || value === undefined || value === false)
1058
+ if (value === null || value === undefined || value === false) {
488
1059
  return false;
1060
+ }
489
1061
  if (typeof value === 'string') {
490
1062
  // Also treat string representations of null/undefined as empty
491
1063
  const empty = ['', 'None', 'Unknown', 'Not specified', 'N/A', 'null', 'undefined', '~'];
@@ -509,8 +1081,9 @@ class FafCompiler {
509
1081
  }
510
1082
  if (typeof value === 'object') {
511
1083
  // Arrays and objects need content
512
- if (Array.isArray(value))
1084
+ if (Array.isArray(value)) {
513
1085
  return value.length > 0;
1086
+ }
514
1087
  return Object.keys(value).length > 0;
515
1088
  }
516
1089
  return true;
@@ -519,16 +1092,30 @@ class FafCompiler {
519
1092
  const parts = path.split('.');
520
1093
  let current = ast;
521
1094
  for (const part of parts) {
522
- if (!current || !current[part])
1095
+ if (!current || !current[part]) {
523
1096
  return false;
1097
+ }
524
1098
  current = current[part];
525
1099
  }
526
1100
  return this.isSlotFilled(current);
527
1101
  }
528
1102
  detectProjectTypeFromContext(ast) {
529
- // Check for explicit project type
530
- if (ast.project?.type)
531
- return ast.project.type;
1103
+ // Check for explicit project type first
1104
+ if (ast.project?.type) {
1105
+ const explicitType = ast.project.type.toLowerCase();
1106
+ // Resolve aliases to canonical type
1107
+ for (const [type, def] of Object.entries(TYPE_DEFINITIONS)) {
1108
+ if (type === explicitType || def.aliases?.includes(explicitType)) {
1109
+ return type;
1110
+ }
1111
+ }
1112
+ // If the explicit type is in TYPE_DEFINITIONS, use it directly
1113
+ if (TYPE_DEFINITIONS[explicitType]) {
1114
+ return explicitType;
1115
+ }
1116
+ // Unknown explicit type - use as-is (will fall to generic in getSlotsForType)
1117
+ return explicitType;
1118
+ }
532
1119
  // Infer from goal and context
533
1120
  const goal = (ast.project?.goal || '').toLowerCase();
534
1121
  const what = (ast.human_context?.what || '').toLowerCase();
@@ -536,7 +1123,12 @@ class FafCompiler {
536
1123
  // CLI tool indicators (check BEFORE Chrome Extension to avoid false positives)
537
1124
  if (goal.includes('cli') || what.includes('cli') ||
538
1125
  goal.includes('command line') || what.includes('command line')) {
539
- return 'cli-tool';
1126
+ return 'cli';
1127
+ }
1128
+ // MCP Server indicators
1129
+ if (goal.includes('mcp') || what.includes('mcp server') ||
1130
+ goal.includes('model context protocol')) {
1131
+ return 'mcp-server';
540
1132
  }
541
1133
  // Chrome Extension detection with fuzzy matching (only if not CLI)
542
1134
  const goalDetection = chrome_extension_detector_1.ChromeExtensionDetector.detect(goal);
@@ -562,8 +1154,9 @@ class FafCompiler {
562
1154
  }
563
1155
  // Language-based defaults
564
1156
  if (mainLanguage === 'python') {
565
- if (ast.stack?.frontend)
1157
+ if (ast.stack?.frontend) {
566
1158
  return 'fullstack';
1159
+ }
567
1160
  return 'python-app'; // Could be CLI, API, or data science
568
1161
  }
569
1162
  return 'generic';
@@ -592,8 +1185,9 @@ class FafCompiler {
592
1185
  section === 'project' ? 'project' : 'stack';
593
1186
  const sec = sections[sectionKey];
594
1187
  sec.total++;
595
- if (slot.filled)
1188
+ if (slot.filled) {
596
1189
  sec.filled++;
1190
+ }
597
1191
  sec.slots.push({
598
1192
  id: slot.id,
599
1193
  value: slot.value,
@@ -634,8 +1228,9 @@ class FafCompiler {
634
1228
  return { filled, total, breakdown };
635
1229
  }
636
1230
  calculateScore(slots) {
637
- if (slots.total === 0)
1231
+ if (slots.total === 0) {
638
1232
  return 0;
1233
+ }
639
1234
  return (slots.filled / slots.total) * 100;
640
1235
  }
641
1236
  // ============================================================================
@@ -712,16 +1307,18 @@ class FafCompiler {
712
1307
  console.log(`\n❌ ${byType.error.length} Errors:`);
713
1308
  byType.error.forEach(d => {
714
1309
  console.log(` ${d.message}`);
715
- if (d.suggestion)
1310
+ if (d.suggestion) {
716
1311
  console.log(` → ${d.suggestion}`);
1312
+ }
717
1313
  });
718
1314
  }
719
1315
  if (byType.warning.length > 0) {
720
1316
  console.log(`\n⚠️ ${byType.warning.length} Warnings:`);
721
1317
  byType.warning.forEach(d => {
722
1318
  console.log(` ${d.message}`);
723
- if (d.suggestion)
1319
+ if (d.suggestion) {
724
1320
  console.log(` → ${d.suggestion}`);
1321
+ }
725
1322
  });
726
1323
  }
727
1324
  if (byType.info.length > 0) {