faf-mcp 2.3.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGELOG.md +26 -3
  2. package/CLAUDE.md +6 -6
  3. package/README.md +71 -72
  4. package/dist/src/faf-core/commands/bi-sync.d.ts +1 -3
  5. package/dist/src/faf-core/commands/bi-sync.js +21 -64
  6. package/dist/src/faf-core/commands/bi-sync.js.map +1 -1
  7. package/dist/src/faf-core/commands/sync.js.map +1 -1
  8. package/dist/src/faf-core/engines/dependency-tsa.js.map +1 -1
  9. package/dist/src/faf-core/fix-once/colors.js +1 -1
  10. package/dist/src/faf-core/fix-once/colors.js.map +1 -1
  11. package/dist/src/faf-core/parsers/agents-parser.d.ts +15 -0
  12. package/dist/src/faf-core/parsers/agents-parser.js +29 -123
  13. package/dist/src/faf-core/parsers/agents-parser.js.map +1 -1
  14. package/dist/src/faf-core/parsers/conductor-parser.js +1 -1
  15. package/dist/src/faf-core/parsers/conductor-parser.js.map +1 -1
  16. package/dist/src/faf-core/parsers/cursorrules-parser.d.ts +15 -0
  17. package/dist/src/faf-core/parsers/cursorrules-parser.js +54 -111
  18. package/dist/src/faf-core/parsers/cursorrules-parser.js.map +1 -1
  19. package/dist/src/faf-core/parsers/faf-git-generator.js +0 -1
  20. package/dist/src/faf-core/parsers/faf-git-generator.js.map +1 -1
  21. package/dist/src/faf-core/parsers/gemini-parser.d.ts +10 -0
  22. package/dist/src/faf-core/parsers/gemini-parser.js +19 -77
  23. package/dist/src/faf-core/parsers/gemini-parser.js.map +1 -1
  24. package/dist/src/handlers/engine-adapter.d.ts +13 -12
  25. package/dist/src/handlers/engine-adapter.js +103 -682
  26. package/dist/src/handlers/engine-adapter.js.map +1 -1
  27. package/dist/src/handlers/resources.d.ts +19 -1
  28. package/dist/src/handlers/resources.js +108 -23
  29. package/dist/src/handlers/resources.js.map +1 -1
  30. package/dist/src/handlers/skills.d.ts +70 -0
  31. package/dist/src/handlers/skills.js +174 -0
  32. package/dist/src/handlers/skills.js.map +1 -0
  33. package/dist/src/handlers/tools.d.ts +41 -5
  34. package/dist/src/handlers/tools.js +213 -258
  35. package/dist/src/handlers/tools.js.map +1 -1
  36. package/dist/src/server.d.ts +4 -0
  37. package/dist/src/server.js +51 -6
  38. package/dist/src/server.js.map +1 -1
  39. package/dist/src/utils/faf-cli-bridge.d.ts +2 -0
  40. package/dist/src/utils/faf-cli-bridge.js +16 -0
  41. package/dist/src/utils/faf-cli-bridge.js.map +1 -1
  42. package/package.json +16 -13
  43. package/project.faf +11 -6
  44. package/scripts/check-engines.mjs +53 -0
  45. package/scripts/sync-version.js +33 -19
  46. package/skills/faf-ide/SKILL.md +40 -0
  47. package/dist/src/config/visibility.d.ts +0 -41
  48. package/dist/src/config/visibility.js +0 -158
  49. package/dist/src/config/visibility.js.map +0 -1
  50. package/dist/src/faf-core/commands/audit.d.ts +0 -21
  51. package/dist/src/faf-core/commands/audit.js +0 -83
  52. package/dist/src/faf-core/commands/audit.js.map +0 -1
  53. package/dist/src/faf-core/commands/auto.d.ts +0 -25
  54. package/dist/src/faf-core/commands/auto.js +0 -74
  55. package/dist/src/faf-core/commands/auto.js.map +0 -1
  56. package/dist/src/faf-core/commands/doctor.d.ts +0 -17
  57. package/dist/src/faf-core/commands/doctor.js +0 -198
  58. package/dist/src/faf-core/commands/doctor.js.map +0 -1
  59. package/dist/src/faf-core/commands/enhance.d.ts +0 -46
  60. package/dist/src/faf-core/commands/enhance.js +0 -360
  61. package/dist/src/faf-core/commands/enhance.js.map +0 -1
  62. package/dist/src/faf-core/commands/formats.d.ts +0 -22
  63. package/dist/src/faf-core/commands/formats.js +0 -117
  64. package/dist/src/faf-core/commands/formats.js.map +0 -1
  65. package/dist/src/faf-core/commands/human.d.ts +0 -27
  66. package/dist/src/faf-core/commands/human.js +0 -183
  67. package/dist/src/faf-core/commands/human.js.map +0 -1
  68. package/dist/src/faf-core/commands/init.d.ts +0 -26
  69. package/dist/src/faf-core/commands/init.js +0 -114
  70. package/dist/src/faf-core/commands/init.js.map +0 -1
  71. package/dist/src/faf-core/commands/innit.d.ts +0 -7
  72. package/dist/src/faf-core/commands/innit.js +0 -13
  73. package/dist/src/faf-core/commands/innit.js.map +0 -1
  74. package/dist/src/faf-core/commands/migrate.d.ts +0 -15
  75. package/dist/src/faf-core/commands/migrate.js +0 -86
  76. package/dist/src/faf-core/commands/migrate.js.map +0 -1
  77. package/dist/src/faf-core/commands/quick.d.ts +0 -16
  78. package/dist/src/faf-core/commands/quick.js +0 -184
  79. package/dist/src/faf-core/commands/quick.js.map +0 -1
  80. package/dist/src/faf-core/commands/readme.d.ts +0 -47
  81. package/dist/src/faf-core/commands/readme.js +0 -361
  82. package/dist/src/faf-core/commands/readme.js.map +0 -1
  83. package/dist/src/faf-core/commands/score.d.ts +0 -47
  84. package/dist/src/faf-core/commands/score.js +0 -49
  85. package/dist/src/faf-core/commands/score.js.map +0 -1
  86. package/dist/src/faf-core/commands/update.d.ts +0 -12
  87. package/dist/src/faf-core/commands/update.js +0 -46
  88. package/dist/src/faf-core/commands/update.js.map +0 -1
  89. package/dist/src/faf-core/commands/validate.d.ts +0 -21
  90. package/dist/src/faf-core/commands/validate.js +0 -81
  91. package/dist/src/faf-core/commands/validate.js.map +0 -1
  92. package/dist/src/faf-core/compiler/faf-compiler.d.ts +0 -138
  93. package/dist/src/faf-core/compiler/faf-compiler.js +0 -1362
  94. package/dist/src/faf-core/compiler/faf-compiler.js.map +0 -1
  95. package/dist/src/faf-core/engines/fab-formats-processor.d.ts +0 -166
  96. package/dist/src/faf-core/engines/fab-formats-processor.js +0 -1274
  97. package/dist/src/faf-core/engines/fab-formats-processor.js.map +0 -1
  98. package/dist/src/faf-core/engines/faf-dna.d.ts +0 -159
  99. package/dist/src/faf-core/engines/faf-dna.js +0 -555
  100. package/dist/src/faf-core/engines/faf-dna.js.map +0 -1
  101. package/dist/src/faf-core/engines/relentless-context-extractor.d.ts +0 -100
  102. package/dist/src/faf-core/engines/relentless-context-extractor.js +0 -625
  103. package/dist/src/faf-core/engines/relentless-context-extractor.js.map +0 -1
  104. package/dist/src/faf-core/fix-once/types.d.ts +0 -257
  105. package/dist/src/faf-core/fix-once/types.js +0 -26
  106. package/dist/src/faf-core/fix-once/types.js.map +0 -1
  107. package/dist/src/faf-core/generators/faf-generator-championship.d.ts +0 -16
  108. package/dist/src/faf-core/generators/faf-generator-championship.js +0 -463
  109. package/dist/src/faf-core/generators/faf-generator-championship.js.map +0 -1
  110. package/dist/src/faf-core/inject.d.ts +0 -19
  111. package/dist/src/faf-core/inject.js +0 -57
  112. package/dist/src/faf-core/inject.js.map +0 -1
  113. package/dist/src/faf-core/parsers/index.d.ts +0 -12
  114. package/dist/src/faf-core/parsers/index.js +0 -29
  115. package/dist/src/faf-core/parsers/index.js.map +0 -1
  116. package/dist/src/faf-core/utils/balance-visualizer.d.ts +0 -37
  117. package/dist/src/faf-core/utils/balance-visualizer.js +0 -197
  118. package/dist/src/faf-core/utils/balance-visualizer.js.map +0 -1
  119. package/dist/src/faf-core/utils/championship-style.d.ts +0 -109
  120. package/dist/src/faf-core/utils/championship-style.js +0 -219
  121. package/dist/src/faf-core/utils/championship-style.js.map +0 -1
  122. package/dist/src/faf-core/utils/chrome-extension-detector.d.ts +0 -73
  123. package/dist/src/faf-core/utils/chrome-extension-detector.js +0 -268
  124. package/dist/src/faf-core/utils/chrome-extension-detector.js.map +0 -1
  125. package/dist/src/faf-core/utils/platform-detector.d.ts +0 -30
  126. package/dist/src/faf-core/utils/platform-detector.js +0 -218
  127. package/dist/src/faf-core/utils/platform-detector.js.map +0 -1
  128. package/dist/src/faf-core/utils/technical-credit.d.ts +0 -35
  129. package/dist/src/faf-core/utils/technical-credit.js +0 -286
  130. package/dist/src/faf-core/utils/technical-credit.js.map +0 -1
  131. package/dist/src/faf-core/utils/yaml-generator.d.ts +0 -41
  132. package/dist/src/faf-core/utils/yaml-generator.js +0 -360
  133. package/dist/src/faf-core/utils/yaml-generator.js.map +0 -1
  134. package/dist/src/handlers/behavioral-instruction.d.ts +0 -16
  135. package/dist/src/handlers/behavioral-instruction.js +0 -43
  136. package/dist/src/handlers/behavioral-instruction.js.map +0 -1
  137. package/dist/src/handlers/championship-tools.d.ts +0 -116
  138. package/dist/src/handlers/championship-tools.js +0 -2320
  139. package/dist/src/handlers/championship-tools.js.map +0 -1
  140. package/dist/src/handlers/cloud-handler.d.ts +0 -59
  141. package/dist/src/handlers/cloud-handler.js +0 -348
  142. package/dist/src/handlers/cloud-handler.js.map +0 -1
  143. package/dist/src/handlers/tool-registry.d.ts +0 -23
  144. package/dist/src/handlers/tool-registry.js +0 -68
  145. package/dist/src/handlers/tool-registry.js.map +0 -1
  146. package/dist/src/handlers/tool-types.d.ts +0 -167
  147. package/dist/src/handlers/tool-types.js +0 -7
  148. package/dist/src/handlers/tool-types.js.map +0 -1
  149. package/dist/src/test-all-functions.d.ts +0 -15
  150. package/dist/src/test-all-functions.js +0 -163
  151. package/dist/src/test-all-functions.js.map +0 -1
  152. package/dist/src/types/mcp-tools.d.ts +0 -53
  153. package/dist/src/types/mcp-tools.js +0 -77
  154. package/dist/src/types/mcp-tools.js.map +0 -1
  155. package/dist/src/types/tool-visibility.d.ts +0 -36
  156. package/dist/src/types/tool-visibility.js +0 -551
  157. package/dist/src/types/tool-visibility.js.map +0 -1
  158. package/dist/src/utils/auto-path-detection.d.ts +0 -26
  159. package/dist/src/utils/auto-path-detection.js +0 -198
  160. package/dist/src/utils/auto-path-detection.js.map +0 -1
  161. package/dist/src/utils/championship-format.d.ts +0 -30
  162. package/dist/src/utils/championship-format.js +0 -81
  163. package/dist/src/utils/championship-format.js.map +0 -1
  164. package/dist/src/utils/cli-detector.d.ts +0 -17
  165. package/dist/src/utils/cli-detector.js +0 -247
  166. package/dist/src/utils/cli-detector.js.map +0 -1
  167. package/dist/src/utils/display-protocol.d.ts +0 -57
  168. package/dist/src/utils/display-protocol.js +0 -131
  169. package/dist/src/utils/display-protocol.js.map +0 -1
  170. package/dist/src/utils/username-detector.d.ts +0 -27
  171. package/dist/src/utils/username-detector.js +0 -90
  172. package/dist/src/utils/username-detector.js.map +0 -1
  173. package/dist/src/utils/visual-style.d.ts +0 -62
  174. package/dist/src/utils/visual-style.js +0 -166
  175. package/dist/src/utils/visual-style.js.map +0 -1
  176. package/scripts/postinstall.js +0 -50
  177. package/skill/SKILL.md +0 -385
@@ -1,1362 +0,0 @@
1
- "use strict";
2
- /**
3
- * 🏎️ FAF Scoring Compiler
4
- * Deterministic, multi-pass compiler for .faf scoring
5
- *
6
- * Philosophy: Like Svelte, we compile away the complexity
7
- * Result: Pure, traceable, reproducible scores
8
- */
9
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- var desc = Object.getOwnPropertyDescriptor(m, k);
12
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
- desc = { enumerable: true, get: function() { return m[k]; } };
14
- }
15
- Object.defineProperty(o, k2, desc);
16
- }) : (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- o[k2] = m[k];
19
- }));
20
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
- Object.defineProperty(o, "default", { enumerable: true, value: v });
22
- }) : function(o, v) {
23
- o["default"] = v;
24
- });
25
- var __importStar = (this && this.__importStar) || (function () {
26
- var ownKeys = function(o) {
27
- ownKeys = Object.getOwnPropertyNames || function (o) {
28
- var ar = [];
29
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
- return ar;
31
- };
32
- return ownKeys(o);
33
- };
34
- return function (mod) {
35
- if (mod && mod.__esModule) return mod;
36
- var result = {};
37
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
- __setModuleDefault(result, mod);
39
- return result;
40
- };
41
- })();
42
- Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.FafCompiler = void 0;
44
- exports.compile = compile;
45
- exports.compileWithTrace = compileWithTrace;
46
- exports.verify = verify;
47
- const yaml_1 = require("../fix-once/yaml");
48
- const crypto = __importStar(require("crypto"));
49
- const chrome_extension_detector_1 = require("../utils/chrome-extension-detector");
50
- const fab_formats_processor_1 = require("../engines/fab-formats-processor");
51
- const path = __importStar(require("path"));
52
- // ============================================================================
53
- // TYPE_DEFINITIONS - Single Source of Truth for Project Types
54
- // Ported from faf-cli v3.2.5 for scoring parity
55
- // ============================================================================
56
- // Design: 21 slots always exist. Types define which slots COUNT toward scoring.
57
- // slot_ignore in .faf file overrides type defaults for edge cases.
58
- /**
59
- * All 21 slots in the FAF system
60
- */
61
- const ALL_SLOTS = {
62
- // Project slots (3)
63
- project: ['project.name', 'project.goal', 'project.main_language'],
64
- // Frontend slots (4)
65
- frontend: ['stack.frontend', 'stack.css_framework', 'stack.ui_library', 'stack.state_management'],
66
- // Backend slots (5)
67
- backend: ['stack.backend', 'stack.api_type', 'stack.runtime', 'stack.database', 'stack.connection'],
68
- // Universal slots (3)
69
- universal: ['stack.hosting', 'stack.build', 'stack.cicd'],
70
- // Human context slots (6)
71
- human: ['human.who', 'human.what', 'human.why', 'human.where', 'human.when', 'human.how']
72
- };
73
- /**
74
- * TYPE_DEFINITIONS - Maps project types to their applicable slots
75
- * 94 project types + 38 aliases from faf-cli v3.2.5
76
- */
77
- const TYPE_DEFINITIONS = {
78
- // CLI/Tool Types (9 slots: project + human)
79
- 'cli': {
80
- description: 'Command-line interface tool',
81
- categories: ['project', 'human'],
82
- aliases: ['cli-tool', 'command-line']
83
- },
84
- 'cli-tool': {
85
- description: 'Command-line interface tool',
86
- categories: ['project', 'human']
87
- },
88
- // Library/Package Types (9 slots: project + human)
89
- 'library': {
90
- description: 'Reusable code library/package',
91
- categories: ['project', 'human'],
92
- aliases: ['lib', 'package']
93
- },
94
- 'npm-package': {
95
- description: 'NPM package',
96
- categories: ['project', 'human']
97
- },
98
- 'pip-package': {
99
- description: 'Python pip package',
100
- categories: ['project', 'human'],
101
- aliases: ['pypi']
102
- },
103
- 'crate': {
104
- description: 'Rust crate',
105
- categories: ['project', 'human'],
106
- aliases: ['rust-crate']
107
- },
108
- 'gem': {
109
- description: 'Ruby gem',
110
- categories: ['project', 'human'],
111
- aliases: ['ruby-gem']
112
- },
113
- // AI/ML Types
114
- 'mcp-server': {
115
- description: 'Model Context Protocol server',
116
- categories: ['project', 'backend', 'human']
117
- },
118
- 'data-science': {
119
- description: 'Data science/analysis project',
120
- categories: ['project', 'backend', 'human'],
121
- aliases: ['data-analysis', 'analytics']
122
- },
123
- 'ml-model': {
124
- description: 'Machine learning model',
125
- categories: ['project', 'backend', 'human'],
126
- aliases: ['ai-model', 'ml', 'machine-learning']
127
- },
128
- 'jupyter': {
129
- description: 'Jupyter notebook project',
130
- categories: ['project', 'human'],
131
- aliases: ['notebook', 'ipynb']
132
- },
133
- 'data-pipeline': {
134
- description: 'Data pipeline/ETL',
135
- categories: ['project', 'backend', 'human'],
136
- aliases: ['etl', 'pipeline']
137
- },
138
- // API/Backend Types
139
- 'backend-api': {
140
- description: 'Backend API service',
141
- categories: ['project', 'backend', 'universal', 'human'],
142
- aliases: ['api', 'backend', 'rest-api']
143
- },
144
- 'node-api': {
145
- description: 'Node.js API service',
146
- categories: ['project', 'backend', 'universal', 'human'],
147
- aliases: ['express', 'fastify', 'nest']
148
- },
149
- 'python-api': {
150
- description: 'Python API service',
151
- categories: ['project', 'backend', 'universal', 'human'],
152
- aliases: ['flask', 'fastapi', 'django-api']
153
- },
154
- 'python-app': {
155
- description: 'Python application',
156
- categories: ['project', 'backend', 'human']
157
- },
158
- 'go-api': {
159
- description: 'Go API service',
160
- categories: ['project', 'backend', 'universal', 'human'],
161
- aliases: ['golang', 'gin', 'fiber']
162
- },
163
- 'rust-api': {
164
- description: 'Rust API service',
165
- categories: ['project', 'backend', 'universal', 'human'],
166
- aliases: ['actix', 'axum', 'rocket']
167
- },
168
- 'graphql': {
169
- description: 'GraphQL API service',
170
- categories: ['project', 'backend', 'universal', 'human'],
171
- aliases: ['graphql-api']
172
- },
173
- 'microservice': {
174
- description: 'Microservice',
175
- categories: ['project', 'backend', 'universal', 'human'],
176
- aliases: ['service']
177
- },
178
- // Frontend Types
179
- 'frontend': {
180
- description: 'Frontend-only web application',
181
- categories: ['project', 'frontend', 'universal', 'human']
182
- },
183
- 'svelte': {
184
- description: 'Svelte web application',
185
- categories: ['project', 'frontend', 'universal', 'human'],
186
- aliases: ['sveltekit']
187
- },
188
- 'react': {
189
- description: 'React web application',
190
- categories: ['project', 'frontend', 'universal', 'human'],
191
- aliases: ['reactjs']
192
- },
193
- 'vue': {
194
- description: 'Vue.js web application',
195
- categories: ['project', 'frontend', 'universal', 'human'],
196
- aliases: ['vuejs', 'nuxt']
197
- },
198
- 'angular': {
199
- description: 'Angular web application',
200
- categories: ['project', 'frontend', 'universal', 'human']
201
- },
202
- 'nextjs': {
203
- description: 'Next.js application',
204
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
205
- aliases: ['next']
206
- },
207
- 'remix': {
208
- description: 'Remix application',
209
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
210
- },
211
- 'astro': {
212
- description: 'Astro static site',
213
- categories: ['project', 'frontend', 'universal', 'human']
214
- },
215
- 'solid': {
216
- description: 'SolidJS application',
217
- categories: ['project', 'frontend', 'universal', 'human'],
218
- aliases: ['solidjs']
219
- },
220
- 'qwik': {
221
- description: 'Qwik application',
222
- categories: ['project', 'frontend', 'universal', 'human']
223
- },
224
- // Fullstack Types
225
- 'fullstack': {
226
- description: 'Full-stack web application',
227
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
228
- },
229
- 't3': {
230
- description: 'T3 Stack (Next.js + tRPC + Prisma)',
231
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
232
- aliases: ['t3-stack', 'create-t3-app']
233
- },
234
- 'mern': {
235
- description: 'MERN Stack (MongoDB + Express + React + Node)',
236
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
237
- },
238
- 'mean': {
239
- description: 'MEAN Stack (MongoDB + Express + Angular + Node)',
240
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
241
- },
242
- 'lamp': {
243
- description: 'LAMP Stack (Linux + Apache + MySQL + PHP)',
244
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
245
- },
246
- 'django': {
247
- description: 'Django web application',
248
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
249
- },
250
- 'rails': {
251
- description: 'Ruby on Rails application',
252
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
253
- aliases: ['ruby-on-rails', 'ror']
254
- },
255
- 'laravel': {
256
- description: 'Laravel PHP application',
257
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
258
- },
259
- // Mobile Types
260
- 'mobile': {
261
- description: 'Mobile application',
262
- categories: ['project', 'frontend', 'human'],
263
- aliases: ['mobile-app']
264
- },
265
- 'react-native': {
266
- description: 'React Native mobile app',
267
- categories: ['project', 'frontend', 'human'],
268
- aliases: ['rn', 'expo']
269
- },
270
- 'flutter': {
271
- description: 'Flutter mobile app',
272
- categories: ['project', 'frontend', 'human'],
273
- aliases: ['dart']
274
- },
275
- 'ios': {
276
- description: 'iOS native app',
277
- categories: ['project', 'frontend', 'human'],
278
- aliases: ['swift', 'swiftui']
279
- },
280
- 'android': {
281
- description: 'Android native app',
282
- categories: ['project', 'frontend', 'human'],
283
- aliases: ['kotlin', 'kotlin-android']
284
- },
285
- 'ionic': {
286
- description: 'Ionic mobile app',
287
- categories: ['project', 'frontend', 'human'],
288
- aliases: ['capacitor']
289
- },
290
- // Desktop Types
291
- 'desktop': {
292
- description: 'Desktop application',
293
- categories: ['project', 'frontend', 'human'],
294
- aliases: ['desktop-app']
295
- },
296
- 'electron': {
297
- description: 'Electron desktop app',
298
- categories: ['project', 'frontend', 'human']
299
- },
300
- 'tauri': {
301
- description: 'Tauri desktop app',
302
- categories: ['project', 'frontend', 'human']
303
- },
304
- 'qt': {
305
- description: 'Qt desktop application',
306
- categories: ['project', 'frontend', 'human'],
307
- aliases: ['pyqt', 'pyside']
308
- },
309
- 'gtk': {
310
- description: 'GTK desktop application',
311
- categories: ['project', 'frontend', 'human']
312
- },
313
- // Browser Extensions
314
- 'chrome-extension': {
315
- description: 'Chrome browser extension',
316
- categories: ['project', 'human'],
317
- aliases: ['browser-extension', 'extension']
318
- },
319
- 'firefox-extension': {
320
- description: 'Firefox browser extension',
321
- categories: ['project', 'human'],
322
- aliases: ['firefox-addon']
323
- },
324
- 'safari-extension': {
325
- description: 'Safari browser extension',
326
- categories: ['project', 'human']
327
- },
328
- // Automation/Workflow Types
329
- 'n8n-workflow': {
330
- description: 'n8n automation workflow',
331
- categories: ['project', 'backend', 'human'],
332
- aliases: ['n8n']
333
- },
334
- 'zapier': {
335
- description: 'Zapier integration',
336
- categories: ['project', 'human']
337
- },
338
- 'github-action': {
339
- description: 'GitHub Action',
340
- categories: ['project', 'human'],
341
- aliases: ['gha', 'action']
342
- },
343
- // DevOps/Infrastructure Types
344
- 'terraform': {
345
- description: 'Terraform infrastructure',
346
- categories: ['project', 'human'],
347
- aliases: ['tf', 'iac']
348
- },
349
- 'kubernetes': {
350
- description: 'Kubernetes configuration',
351
- categories: ['project', 'human'],
352
- aliases: ['k8s', 'helm']
353
- },
354
- 'docker': {
355
- description: 'Docker/container configuration',
356
- categories: ['project', 'human'],
357
- aliases: ['dockerfile', 'container']
358
- },
359
- 'ansible': {
360
- description: 'Ansible playbooks',
361
- categories: ['project', 'human']
362
- },
363
- 'pulumi': {
364
- description: 'Pulumi infrastructure',
365
- categories: ['project', 'human']
366
- },
367
- 'infrastructure': {
368
- description: 'Infrastructure as code',
369
- categories: ['project', 'human'],
370
- aliases: ['infra', 'devops']
371
- },
372
- // Static Sites / Documentation
373
- 'static-html': {
374
- description: 'Static HTML website',
375
- categories: ['project', 'frontend', 'human'],
376
- aliases: ['static-site', 'html']
377
- },
378
- 'landing-page': {
379
- description: 'Landing page website',
380
- categories: ['project', 'frontend', 'human'],
381
- aliases: ['landing']
382
- },
383
- 'documentation': {
384
- description: 'Documentation site',
385
- categories: ['project', 'frontend', 'human'],
386
- aliases: ['docs']
387
- },
388
- 'docusaurus': {
389
- description: 'Docusaurus documentation site',
390
- categories: ['project', 'frontend', 'human']
391
- },
392
- 'mkdocs': {
393
- description: 'MkDocs documentation site',
394
- categories: ['project', 'human']
395
- },
396
- 'vitepress': {
397
- description: 'VitePress documentation site',
398
- categories: ['project', 'frontend', 'human']
399
- },
400
- 'storybook': {
401
- description: 'Storybook component library',
402
- categories: ['project', 'frontend', 'human']
403
- },
404
- // CMS Types
405
- 'wordpress': {
406
- description: 'WordPress site/plugin/theme',
407
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
408
- aliases: ['wp']
409
- },
410
- 'cms': {
411
- description: 'Content management system',
412
- categories: ['project', 'frontend', 'backend', 'human']
413
- },
414
- 'strapi': {
415
- description: 'Strapi headless CMS',
416
- categories: ['project', 'backend', 'universal', 'human']
417
- },
418
- 'sanity': {
419
- description: 'Sanity.io CMS',
420
- categories: ['project', 'backend', 'human']
421
- },
422
- 'contentful': {
423
- description: 'Contentful CMS integration',
424
- categories: ['project', 'human']
425
- },
426
- // Game Development Types
427
- 'game': {
428
- description: 'Game project',
429
- categories: ['project', 'frontend', 'human'],
430
- aliases: ['gamedev']
431
- },
432
- 'unity': {
433
- description: 'Unity game',
434
- categories: ['project', 'frontend', 'human'],
435
- aliases: ['unity3d']
436
- },
437
- 'godot': {
438
- description: 'Godot game',
439
- categories: ['project', 'frontend', 'human']
440
- },
441
- 'unreal': {
442
- description: 'Unreal Engine game',
443
- categories: ['project', 'frontend', 'human'],
444
- aliases: ['ue4', 'ue5']
445
- },
446
- 'phaser': {
447
- description: 'Phaser.js game',
448
- categories: ['project', 'frontend', 'human']
449
- },
450
- 'threejs': {
451
- description: 'Three.js 3D project',
452
- categories: ['project', 'frontend', 'human'],
453
- aliases: ['three', '3d', 'webgl']
454
- },
455
- // Blockchain/Web3 Types
456
- 'smart-contract': {
457
- description: 'Smart contract',
458
- categories: ['project', 'human'],
459
- aliases: ['solidity', 'contract']
460
- },
461
- 'dapp': {
462
- description: 'Decentralized application',
463
- categories: ['project', 'frontend', 'human'],
464
- aliases: ['web3', 'blockchain']
465
- },
466
- 'hardhat': {
467
- description: 'Hardhat Ethereum project',
468
- categories: ['project', 'human']
469
- },
470
- 'foundry': {
471
- description: 'Foundry Ethereum project',
472
- categories: ['project', 'human'],
473
- aliases: ['forge']
474
- },
475
- // Monorepo Types (CONTAINERS - need ALL slots)
476
- 'monorepo': {
477
- description: 'Monorepo - multi-package repository',
478
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
479
- aliases: ['mono', 'workspace']
480
- },
481
- 'turborepo': {
482
- description: 'Turborepo monorepo',
483
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
484
- aliases: ['turbo']
485
- },
486
- 'nx': {
487
- description: 'Nx monorepo',
488
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
489
- },
490
- 'lerna': {
491
- description: 'Lerna monorepo',
492
- categories: ['project', 'frontend', 'backend', 'universal', 'human']
493
- },
494
- 'pnpm-workspace': {
495
- description: 'pnpm workspace monorepo',
496
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
497
- aliases: ['pnpm-mono']
498
- },
499
- 'yarn-workspace': {
500
- description: 'Yarn workspace monorepo',
501
- categories: ['project', 'frontend', 'backend', 'universal', 'human'],
502
- aliases: ['yarn-mono']
503
- },
504
- // Embedded/Systems Types
505
- 'embedded': {
506
- description: 'Embedded systems',
507
- categories: ['project', 'human'],
508
- aliases: ['firmware', 'iot']
509
- },
510
- 'arduino': {
511
- description: 'Arduino project',
512
- categories: ['project', 'human']
513
- },
514
- 'raspberry-pi': {
515
- description: 'Raspberry Pi project',
516
- categories: ['project', 'human'],
517
- aliases: ['rpi']
518
- },
519
- 'wasm': {
520
- description: 'WebAssembly module',
521
- categories: ['project', 'human'],
522
- aliases: ['webassembly']
523
- },
524
- // Testing Types
525
- 'test-suite': {
526
- description: 'Test suite/framework',
527
- categories: ['project', 'human'],
528
- aliases: ['testing', 'tests']
529
- },
530
- 'e2e-tests': {
531
- description: 'End-to-end test suite',
532
- categories: ['project', 'human'],
533
- aliases: ['e2e', 'playwright', 'cypress']
534
- },
535
- // Default
536
- 'generic': {
537
- description: 'Generic project (fallback)',
538
- categories: ['project', 'universal', 'human']
539
- }
540
- };
541
- /**
542
- * Get applicable slots for a project type
543
- */
544
- function getSlotsForType(projectType) {
545
- // Check for aliases first
546
- for (const [type, def] of Object.entries(TYPE_DEFINITIONS)) {
547
- if (def.aliases?.includes(projectType)) {
548
- projectType = type;
549
- break;
550
- }
551
- }
552
- const typeDef = TYPE_DEFINITIONS[projectType] || TYPE_DEFINITIONS['generic'];
553
- const slots = [];
554
- for (const category of typeDef.categories) {
555
- slots.push(...ALL_SLOTS[category]);
556
- }
557
- return slots;
558
- }
559
- /**
560
- * Parse slot_ignore from .faf content
561
- * Accepts: slot_ignore: [hosting, cicd] or slot_ignore: hosting, cicd
562
- */
563
- function parseSlotIgnore(ast) {
564
- const slotIgnore = ast.slot_ignore || ast.slotIgnore || ast.ignore_slots;
565
- if (!slotIgnore)
566
- return [];
567
- // Handle array format
568
- if (Array.isArray(slotIgnore)) {
569
- return slotIgnore.map((s) => normalizeSlotName(s));
570
- }
571
- // Handle string format (comma-separated)
572
- if (typeof slotIgnore === 'string') {
573
- return slotIgnore.split(',').map((s) => normalizeSlotName(s.trim()));
574
- }
575
- return [];
576
- }
577
- /**
578
- * Normalize slot names to full path
579
- * Examples: "hosting" -> "stack.hosting", "who" -> "human.who"
580
- */
581
- function normalizeSlotName(slot) {
582
- // Already has prefix
583
- if (slot.includes('.'))
584
- return slot;
585
- // Check each category for the slot
586
- for (const [_category, slots] of Object.entries(ALL_SLOTS)) {
587
- const fullSlot = slots.find(s => s.endsWith(`.${slot}`));
588
- if (fullSlot)
589
- return fullSlot;
590
- }
591
- // Return as-is if not found (will be ignored)
592
- return slot;
593
- }
594
- // ============================================================================
595
- // Main Compiler Class
596
- // ============================================================================
597
- class FafCompiler {
598
- static VERSION = '3.0.0-compiler';
599
- diagnostics = [];
600
- trace;
601
- ir;
602
- constructor() {
603
- this.trace = {
604
- version: FafCompiler.VERSION,
605
- timestamp: new Date().toISOString(),
606
- inputHash: '',
607
- passes: []
608
- };
609
- this.ir = {
610
- version: FafCompiler.VERSION,
611
- slots: [],
612
- metadata: {}
613
- };
614
- }
615
- /**
616
- * Main compilation pipeline
617
- */
618
- async compile(fafPath) {
619
- // Reset state for new compilation
620
- this.diagnostics = [];
621
- this.trace.passes = [];
622
- this.ir.slots = [];
623
- try {
624
- // PASS 1: Parse
625
- const source = await this.readSource(fafPath);
626
- const ast = this.parse(source);
627
- // PASS 2: Analyze
628
- const analyzed = this.analyze(ast);
629
- // PASS 3: Optimize (with discovery)
630
- const optimized = await this.optimize(analyzed, fafPath);
631
- // PASS 4: Generate
632
- const result = this.generate(optimized);
633
- // Add final checksum
634
- const checksum = this.calculateChecksum(result);
635
- return {
636
- ...result,
637
- trace: this.trace,
638
- diagnostics: this.diagnostics,
639
- ir: this.ir,
640
- checksum
641
- };
642
- }
643
- catch (error) {
644
- const message = error instanceof Error ? error.message : String(error);
645
- this.addDiagnostic('error', `Compilation failed: ${message}`);
646
- throw error;
647
- }
648
- }
649
- // ============================================================================
650
- // PASS 1: Parser
651
- // ============================================================================
652
- async readSource(fafPath) {
653
- const start = Date.now();
654
- const fs = await import('fs/promises');
655
- try {
656
- const source = await fs.readFile(fafPath, 'utf-8');
657
- this.trace.inputHash = crypto.createHash('md5').update(source).digest('hex');
658
- this.recordPass('read', start, fafPath, source.length, [
659
- `Read ${source.length} bytes from ${fafPath}`
660
- ]);
661
- return source;
662
- }
663
- catch (error) {
664
- const message = error instanceof Error ? error.message : String(error);
665
- this.addDiagnostic('error', `Cannot read file: ${fafPath}`, undefined, message);
666
- throw error;
667
- }
668
- }
669
- parse(source) {
670
- const start = Date.now();
671
- try {
672
- const ast = (0, yaml_1.parse)(source);
673
- if (!ast || typeof ast !== 'object') {
674
- this.addDiagnostic('error', 'Invalid YAML: must be an object');
675
- return {};
676
- }
677
- this.recordPass('parse', start, source.length, ast, [
678
- `Parsed YAML into AST with ${Object.keys(ast).length} top-level keys`
679
- ]);
680
- return ast;
681
- }
682
- catch (error) {
683
- const message = error instanceof Error ? error.message : String(error);
684
- this.addDiagnostic('error', `Parse error: ${message}`);
685
- return {};
686
- }
687
- }
688
- // ============================================================================
689
- // PASS 2: Analyzer
690
- // ============================================================================
691
- analyze(ast) {
692
- const start = Date.now();
693
- const analyzed = { ...ast };
694
- const changes = [];
695
- // Validate and normalize structure
696
- if (!analyzed.project) {
697
- analyzed.project = {};
698
- changes.push('Added missing project section');
699
- }
700
- if (!analyzed.stack) {
701
- analyzed.stack = {};
702
- changes.push('Added missing stack section');
703
- }
704
- if (!analyzed.human_context) {
705
- analyzed.human_context = {};
706
- changes.push('Added missing human_context section');
707
- }
708
- // Type validation
709
- this.validateTypes(analyzed);
710
- // Check for deprecated fields
711
- if (analyzed.ai_score !== undefined) {
712
- this.addDiagnostic('warning', 'Embedded ai_score is deprecated and will be ignored');
713
- delete analyzed.ai_score;
714
- changes.push('Removed deprecated ai_score');
715
- }
716
- this.recordPass('analyze', start, ast, analyzed, changes);
717
- return analyzed;
718
- }
719
- validateTypes(data) {
720
- // Project validation
721
- if (data.project?.name && typeof data.project.name !== 'string') {
722
- this.addDiagnostic('error', 'project.name must be a string', {
723
- line: 0, column: 0, field: 'project.name'
724
- });
725
- }
726
- // Stack validation
727
- const stackFields = ['frontend', 'backend', 'database', 'hosting'];
728
- for (const field of stackFields) {
729
- if (data.stack?.[field] && typeof data.stack[field] !== 'string') {
730
- this.addDiagnostic('error', `stack.${field} must be a string`, {
731
- line: 0, column: 0, field: `stack.${field}`
732
- });
733
- }
734
- }
735
- // Human context validation
736
- const humanFields = ['who', 'what', 'why', 'where', 'when', 'how'];
737
- for (const field of humanFields) {
738
- const value = data.human_context?.[field];
739
- if (value && typeof value !== 'string' && typeof value !== 'object') {
740
- this.addDiagnostic('warning', `human_context.${field} should be a string or object`);
741
- }
742
- }
743
- }
744
- // ============================================================================
745
- // PASS 3: Optimizer (with Discovery)
746
- // ============================================================================
747
- async optimize(ast, fafPath) {
748
- const start = Date.now();
749
- const optimized = { ...ast };
750
- const changes = [];
751
- // Discovery phase
752
- if (fafPath) {
753
- try {
754
- const projectDir = path.dirname(fafPath);
755
- const discovered = await this.discover(projectDir);
756
- // Apply discovered values WITHOUT mutation
757
- optimized._discovered = discovered;
758
- changes.push(`Discovered ${Object.keys(discovered).length} items`);
759
- }
760
- catch (error) {
761
- const message = error instanceof Error ? error.message : String(error);
762
- this.addDiagnostic('warning', `Discovery failed: ${message}`);
763
- }
764
- }
765
- // Remove None/Unknown values (optimization)
766
- this.removeDefaults(optimized, changes);
767
- this.recordPass('optimize', start, ast, optimized, changes);
768
- return optimized;
769
- }
770
- async discover(projectDir) {
771
- const processor = new fab_formats_processor_1.FabFormatsProcessor();
772
- const analysis = await processor.processFiles(projectDir);
773
- const discovered = {};
774
- if (analysis.context) {
775
- const ctx = analysis.context;
776
- // Map discovered items (WITHOUT modifying original)
777
- if (ctx.projectName)
778
- discovered.projectName = ctx.projectName;
779
- if (ctx.mainLanguage)
780
- discovered.mainLanguage = ctx.mainLanguage;
781
- if (ctx.framework)
782
- discovered.framework = ctx.framework;
783
- if (ctx.database)
784
- discovered.database = ctx.database;
785
- if (ctx.backend)
786
- discovered.backend = ctx.backend;
787
- if (ctx.hosting)
788
- discovered.hosting = ctx.hosting;
789
- if (ctx.buildTool)
790
- discovered.buildTool = ctx.buildTool;
791
- }
792
- // Subsite detection: no package.json + has index.html = static subsite
793
- const fs = await import('fs/promises');
794
- const hasPackageJson = await fs.access(path.join(projectDir, 'package.json')).then(() => true).catch(() => false);
795
- const hasIndexHtml = await fs.access(path.join(projectDir, 'index.html')).then(() => true).catch(() => false);
796
- if (!hasPackageJson && hasIndexHtml) {
797
- discovered.isSubsite = true;
798
- if (process.env.FAF_DEBUG) {
799
- console.log('[DEBUG] Subsite detected: no package.json + has index.html');
800
- }
801
- }
802
- return discovered;
803
- }
804
- removeDefaults(data, changes) {
805
- const defaults = ['None', 'Unknown', 'Not specified', 'N/A'];
806
- const removeFromObject = (obj, path) => {
807
- for (const key in obj) {
808
- const value = obj[key];
809
- const fullPath = path ? `${path}.${key}` : key;
810
- if (defaults.includes(value)) {
811
- delete obj[key];
812
- changes.push(`Removed default value at ${fullPath}`);
813
- }
814
- else if (typeof value === 'object' && value !== null) {
815
- removeFromObject(value, fullPath);
816
- }
817
- }
818
- };
819
- removeFromObject(data, '');
820
- }
821
- // ============================================================================
822
- // PASS 4: Generator
823
- // ============================================================================
824
- generate(ast) {
825
- const start = Date.now();
826
- // Build IR first
827
- const ir = this.buildIR(ast);
828
- this.ir = {
829
- version: FafCompiler.VERSION,
830
- slots: ir,
831
- metadata: { compiled: new Date().toISOString() }
832
- };
833
- // --- Mk4 Kernel Scoring (WASM) — canonical, aligned with faf-cli/claude/grok ---
834
- // The Bouncer: type detection → applicable slots → slotignored for the rest,
835
- // then kernel.score_faf() returns the canonical Mk4 score. This replaces the
836
- // old pure-TS scorer, which used its own denominator and diverged from
837
- // canonical on partial .faf files. TS stays as a fallback when the kernel
838
- // isn't available. (Same path as claude-faf-mcp / grok-faf-mcp.)
839
- let mk4Score = null;
840
- let mk4Filled = null;
841
- let mk4Total = null;
842
- try {
843
- const kernel = require('faf-scoring-kernel');
844
- const { stringify } = require('yaml');
845
- const projectType = this.detectProjectTypeFromContext(ast);
846
- const applicableSlots = getSlotsForType(projectType);
847
- const userIgnored = parseSlotIgnore(ast);
848
- const allSlotPaths = [
849
- ...ALL_SLOTS.project,
850
- ...ALL_SLOTS.frontend,
851
- ...ALL_SLOTS.backend,
852
- ...ALL_SLOTS.universal,
853
- ...ALL_SLOTS.human
854
- ];
855
- const normalizedAst = JSON.parse(JSON.stringify(ast));
856
- delete normalizedAst._discovered;
857
- const setSlotIgnored = (slotPath) => {
858
- const parts = slotPath.split('.');
859
- if (parts[0] === 'project') {
860
- if (!normalizedAst.project)
861
- normalizedAst.project = {};
862
- normalizedAst.project[parts[1]] = 'slotignored';
863
- }
864
- else if (parts[0] === 'stack') {
865
- if (!normalizedAst.stack)
866
- normalizedAst.stack = {};
867
- normalizedAst.stack[parts[1]] = 'slotignored';
868
- }
869
- else if (parts[0] === 'human') {
870
- if (!normalizedAst.human_context)
871
- normalizedAst.human_context = {};
872
- normalizedAst.human_context[parts[1]] = 'slotignored';
873
- }
874
- };
875
- for (const slotPath of allSlotPaths) {
876
- if (!applicableSlots.includes(slotPath) || userIgnored.includes(slotPath)) {
877
- setSlotIgnored(slotPath);
878
- }
879
- }
880
- const normalizedYaml = stringify(normalizedAst);
881
- const kernelResult = JSON.parse(kernel.score_faf(normalizedYaml));
882
- mk4Score = kernelResult.score;
883
- mk4Filled = kernelResult.populated;
884
- mk4Total = kernelResult.active;
885
- }
886
- catch (err) {
887
- if (process.env.FAF_DEBUG) {
888
- const message = err instanceof Error ? err.message : String(err);
889
- console.log(`[DEBUG] Mk4 kernel unavailable, using Mk3.1 TS fallback: ${message}`);
890
- }
891
- }
892
- // Mk4 score when available, otherwise the Mk3.1 TS fallback
893
- const slots = this.calculateSlots(ir);
894
- const fallbackScore = this.calculateScore(slots);
895
- const result = {
896
- score: mk4Score !== null ? mk4Score : Math.round(fallbackScore),
897
- filled: mk4Filled !== null ? mk4Filled : slots.filled,
898
- total: mk4Total !== null ? mk4Total : slots.total,
899
- breakdown: slots.breakdown
900
- };
901
- this.recordPass('generate', start, ast, result, [
902
- `Generated score: ${result.score}% (${result.filled}/${result.total} slots, ${mk4Score !== null ? 'Mk4 WASM' : 'Mk3.1 TS'})`
903
- ]);
904
- return result;
905
- }
906
- buildIR(ast) {
907
- const slots = [];
908
- // Detect project type to determine applicable slots
909
- const projectType = this.detectProjectTypeFromContext(ast);
910
- if (process.env.FAF_DEBUG) {
911
- console.log(`\n[DEBUG] Project type detected: ${projectType}`);
912
- }
913
- // Parse slot_ignore from the .faf file
914
- const ignoredSlots = parseSlotIgnore(ast);
915
- if (process.env.FAF_DEBUG && ignoredSlots.length > 0) {
916
- console.log(`[DEBUG] slot_ignore: ${ignoredSlots.join(', ')}`);
917
- }
918
- // Get applicable slots for this project type (uses TYPE_DEFINITIONS)
919
- const applicableSlots = getSlotsForType(projectType);
920
- if (process.env.FAF_DEBUG) {
921
- console.log(`[DEBUG] Applicable slots (${applicableSlots.length}): ${applicableSlots.join(', ')}`);
922
- }
923
- // Filter out ignored slots
924
- const activeSlots = applicableSlots.filter(slot => !ignoredSlots.includes(slot));
925
- if (process.env.FAF_DEBUG && ignoredSlots.length > 0) {
926
- console.log(`[DEBUG] Active slots after ignore (${activeSlots.length}): ${activeSlots.join(', ')}`);
927
- }
928
- // Helper to get value from ast based on slot path
929
- const getSlotValue = (slotPath) => {
930
- const parts = slotPath.split('.');
931
- if (parts[0] === 'project') {
932
- return ast.project?.[parts[1]];
933
- }
934
- else if (parts[0] === 'stack') {
935
- return ast.stack?.[parts[1]];
936
- }
937
- else if (parts[0] === 'human') {
938
- return ast.human_context?.[parts[1]];
939
- }
940
- return undefined;
941
- };
942
- // Add all active slots to the IR
943
- for (const slotPath of activeSlots) {
944
- const value = getSlotValue(slotPath);
945
- this.addSlot(slots, slotPath, value, 'string', 'original', 1);
946
- }
947
- // Special handling for certain project types (auto-fill missing values)
948
- // Chrome Extension: auto-fill technical context
949
- if (projectType === 'chrome-extension') {
950
- if (!ast.stack)
951
- ast.stack = {};
952
- if (!ast.stack.runtime)
953
- ast.stack.runtime = 'Chrome/Browser';
954
- if (!ast.stack.hosting)
955
- ast.stack.hosting = 'Chrome Web Store';
956
- if (!ast.stack.api_type)
957
- ast.stack.api_type = 'Chrome Extension APIs';
958
- if (!ast.stack.backend)
959
- ast.stack.backend = 'Service Worker';
960
- if (!ast.stack.database)
961
- ast.stack.database = 'chrome.storage API';
962
- // Add auto-filled slots (only if they're in the active slots list)
963
- const chromeSlots = ['stack.runtime', 'stack.hosting', 'stack.api_type', 'stack.backend', 'stack.database'];
964
- for (const slot of chromeSlots) {
965
- if (activeSlots.includes(slot) && !slots.find(s => s.path === slot)) {
966
- this.addSlot(slots, slot, getSlotValue(slot), 'string', 'discovered', 1);
967
- }
968
- }
969
- }
970
- // Static HTML: auto-fill technical context
971
- if (projectType === 'static-html' || projectType === 'landing-page') {
972
- if (!ast.stack)
973
- ast.stack = {};
974
- if (!ast.stack.frontend)
975
- ast.stack.frontend = 'HTML/CSS/JavaScript';
976
- if (!ast.stack.runtime)
977
- ast.stack.runtime = 'Browser';
978
- if (!ast.stack.hosting)
979
- ast.stack.hosting = 'Static Hosting';
980
- if (!ast.stack.build)
981
- ast.stack.build = 'Direct HTML (no build step)';
982
- }
983
- // n8n Workflow: auto-fill technical context
984
- if (projectType === 'n8n-workflow') {
985
- if (!ast.project)
986
- ast.project = {};
987
- if (!ast.project.main_language) {
988
- ast.project.main_language = ast.tech_stack?.primary_language || 'JSON (workflow definition)';
989
- }
990
- if (!ast.stack)
991
- ast.stack = {};
992
- if (!ast.stack.runtime)
993
- ast.stack.runtime = ast.tech_stack?.workflow_engine || 'n8n';
994
- if (!ast.stack.backend)
995
- ast.stack.backend = 'Node.js (n8n server)';
996
- if (!ast.stack.api_type)
997
- ast.stack.api_type = 'Webhooks + HTTP';
998
- if (!ast.stack.database)
999
- ast.stack.database = ast.tech_stack?.infrastructure?.vector_db || 'Workflow State';
1000
- if (!ast.stack.hosting)
1001
- ast.stack.hosting = 'n8n Cloud';
1002
- if (!ast.stack.build)
1003
- ast.stack.build = 'n8n Visual Editor';
1004
- }
1005
- // Discovered slots (if any) - only add if slot is active and not already filled
1006
- if (ast._discovered) {
1007
- const discovered = ast._discovered;
1008
- const mapping = {
1009
- 'projectName': 'project.name',
1010
- 'mainLanguage': 'project.main_language',
1011
- 'framework': 'stack.frontend',
1012
- 'database': 'stack.database',
1013
- 'backend': 'stack.backend',
1014
- 'hosting': 'stack.hosting',
1015
- 'buildTool': 'stack.build'
1016
- };
1017
- for (const [key, slotPath] of Object.entries(mapping)) {
1018
- if (discovered[key] && activeSlots.includes(slotPath) && !this.hasValue(ast, slotPath)) {
1019
- // Only add to discovery if not already in slots
1020
- if (!slots.find(s => s.path === slotPath)) {
1021
- this.addSlot(slots, `discovery.${slotPath}`, discovered[key], 'string', 'discovered', 1);
1022
- }
1023
- }
1024
- }
1025
- }
1026
- return slots;
1027
- }
1028
- addSlot(slots, path, value, type, source, weight) {
1029
- // Deduplication: Check if slot path already exists
1030
- const exists = slots.some(slot => slot.path === path);
1031
- if (exists) {
1032
- if (process.env.FAF_DEBUG) {
1033
- console.log(`[DEBUG] Skipped duplicate slot: ${path}`);
1034
- }
1035
- return; // Skip duplicate slot
1036
- }
1037
- const filled = this.isSlotFilled(value);
1038
- if (process.env.FAF_DEBUG) {
1039
- const valueStr = JSON.stringify(value) || '';
1040
- console.log(`[DEBUG] Added slot: ${path} | filled: ${filled} | value: ${valueStr.substring(0, 50)}`);
1041
- }
1042
- slots.push({
1043
- id: `slot_${slots.length + 1}`,
1044
- path,
1045
- value,
1046
- type,
1047
- source,
1048
- weight,
1049
- filled
1050
- });
1051
- }
1052
- isSlotFilled(value) {
1053
- // Handle null, undefined, false explicitly
1054
- if (value === null || value === undefined || value === false)
1055
- return false;
1056
- if (typeof value === 'string') {
1057
- // Also treat string representations of null/undefined as empty
1058
- const empty = ['', 'None', 'Unknown', 'Not specified', 'N/A', 'null', 'undefined', '~'];
1059
- // Also reject generic placeholders that provide no real context
1060
- const genericPlaceholders = [
1061
- 'Development teams',
1062
- 'Software development solution',
1063
- 'Improve development efficiency',
1064
- 'Cloud platform',
1065
- 'Ongoing development',
1066
- 'Modern development practices',
1067
- 'Development teams building next-generation software',
1068
- 'AI-powered development infrastructure with trust-driven workflows'
1069
- ];
1070
- const trimmed = value.trim();
1071
- return !empty.includes(trimmed) && !genericPlaceholders.includes(trimmed);
1072
- }
1073
- if (typeof value === 'number') {
1074
- // Numbers are valid unless they're NaN or Infinity
1075
- return !isNaN(value) && isFinite(value);
1076
- }
1077
- if (typeof value === 'object') {
1078
- // Arrays and objects need content
1079
- if (Array.isArray(value))
1080
- return value.length > 0;
1081
- return Object.keys(value).length > 0;
1082
- }
1083
- return true;
1084
- }
1085
- hasValue(ast, path) {
1086
- const parts = path.split('.');
1087
- let current = ast;
1088
- for (const part of parts) {
1089
- if (!current || !current[part])
1090
- return false;
1091
- current = current[part];
1092
- }
1093
- return this.isSlotFilled(current);
1094
- }
1095
- detectProjectTypeFromContext(ast) {
1096
- // Check for explicit project type
1097
- if (ast.project?.type)
1098
- return ast.project.type;
1099
- // Infer from goal and context
1100
- const goal = (ast.project?.goal || '').toLowerCase();
1101
- const what = (ast.human_context?.what || '').toLowerCase();
1102
- const mainLanguage = (ast.project?.main_language || '').toLowerCase();
1103
- // CLI tool indicators (check BEFORE Chrome Extension to avoid false positives)
1104
- if (goal.includes('cli') || what.includes('cli') ||
1105
- goal.includes('command line') || what.includes('command line')) {
1106
- return 'cli-tool';
1107
- }
1108
- // Chrome Extension detection with fuzzy matching (only if not CLI)
1109
- const goalDetection = chrome_extension_detector_1.ChromeExtensionDetector.detect(goal);
1110
- const whatDetection = chrome_extension_detector_1.ChromeExtensionDetector.detect(what);
1111
- if (goalDetection.detected || whatDetection.detected ||
1112
- ast.stack?.framework === 'Chrome Extension') {
1113
- return 'chrome-extension';
1114
- }
1115
- // Library indicators
1116
- if (goal.includes('library') || what.includes('library') ||
1117
- goal.includes('package') || what.includes('npm package')) {
1118
- return 'library';
1119
- }
1120
- // API/Backend indicators
1121
- if (goal.includes('api') || what.includes('api') ||
1122
- goal.includes('backend') || what.includes('backend') ||
1123
- ast.stack?.backend && !ast.stack?.frontend) {
1124
- return 'backend-api';
1125
- }
1126
- // Frontend indicators
1127
- if (ast.stack?.frontend || ast.stack?.css_framework || ast.stack?.ui_library) {
1128
- return ast.stack?.backend ? 'fullstack' : 'frontend';
1129
- }
1130
- // Language-based defaults
1131
- if (mainLanguage === 'python') {
1132
- if (ast.stack?.frontend)
1133
- return 'fullstack';
1134
- return 'python-app'; // Could be CLI, API, or data science
1135
- }
1136
- return 'generic';
1137
- }
1138
- requiresFrontendStack(projectType) {
1139
- const frontendTypes = ['frontend', 'fullstack', 'svelte', 'react', 'vue', 'angular'];
1140
- // Chrome extensions don't need traditional frontend stack
1141
- return frontendTypes.includes(projectType) && projectType !== 'chrome-extension';
1142
- }
1143
- requiresBackendStack(projectType) {
1144
- const backendTypes = ['backend-api', 'fullstack', 'cli-tool', 'library', 'python-app', 'node-api'];
1145
- // Chrome extensions don't need traditional backend stack
1146
- return backendTypes.includes(projectType) && projectType !== 'chrome-extension';
1147
- }
1148
- calculateSlots(ir) {
1149
- const sections = {
1150
- project: { filled: 0, total: 0, slots: [] },
1151
- stack: { filled: 0, total: 0, slots: [] },
1152
- human: { filled: 0, total: 0, slots: [] },
1153
- discovery: { filled: 0, total: 0, slots: [] }
1154
- };
1155
- for (const slot of ir) {
1156
- const [section] = slot.path.split('.');
1157
- const sectionKey = section === 'human' ? 'human' :
1158
- section === 'discovery' ? 'discovery' :
1159
- section === 'project' ? 'project' : 'stack';
1160
- const sec = sections[sectionKey];
1161
- sec.total++;
1162
- if (slot.filled)
1163
- sec.filled++;
1164
- sec.slots.push({
1165
- id: slot.id,
1166
- value: slot.value,
1167
- filled: slot.filled,
1168
- source: slot.source,
1169
- points: slot.filled ? slot.weight : 0
1170
- });
1171
- }
1172
- // Calculate percentages
1173
- const breakdown = {
1174
- project: {
1175
- ...sections.project,
1176
- percentage: sections.project.total > 0
1177
- ? Math.round((sections.project.filled / sections.project.total) * 100)
1178
- : 0
1179
- },
1180
- stack: {
1181
- ...sections.stack,
1182
- percentage: sections.stack.total > 0
1183
- ? Math.round((sections.stack.filled / sections.stack.total) * 100)
1184
- : 0
1185
- },
1186
- human: {
1187
- ...sections.human,
1188
- percentage: sections.human.total > 0
1189
- ? Math.round((sections.human.filled / sections.human.total) * 100)
1190
- : 0
1191
- },
1192
- discovery: {
1193
- ...sections.discovery,
1194
- percentage: sections.discovery.total > 0
1195
- ? Math.round((sections.discovery.filled / sections.discovery.total) * 100)
1196
- : 0
1197
- }
1198
- };
1199
- const filled = Object.values(sections).reduce((sum, sec) => sum + sec.filled, 0);
1200
- const total = Object.values(sections).reduce((sum, sec) => sum + sec.total, 0);
1201
- return { filled, total, breakdown };
1202
- }
1203
- calculateScore(slots) {
1204
- if (slots.total === 0)
1205
- return 0;
1206
- return (slots.filled / slots.total) * 100;
1207
- }
1208
- // ============================================================================
1209
- // Utilities
1210
- // ============================================================================
1211
- calculateChecksum(result) {
1212
- const data = JSON.stringify({
1213
- score: result.score,
1214
- filled: result.filled,
1215
- total: result.total,
1216
- version: FafCompiler.VERSION
1217
- });
1218
- return crypto.createHash('md5').update(data).digest('hex').slice(0, 8);
1219
- }
1220
- recordPass(name, startTime, input, output, changes) {
1221
- this.trace.passes.push({
1222
- name,
1223
- duration: Date.now() - startTime,
1224
- input: this.sanitize(input),
1225
- output: this.sanitize(output),
1226
- changes
1227
- });
1228
- }
1229
- sanitize(data) {
1230
- // Limit size for trace
1231
- const str = JSON.stringify(data);
1232
- if (str.length > 1000) {
1233
- return { _truncated: true, size: str.length };
1234
- }
1235
- return data;
1236
- }
1237
- addDiagnostic(severity, message, location, suggestion) {
1238
- this.diagnostics.push({ severity, message, location, suggestion });
1239
- }
1240
- // ============================================================================
1241
- // Public API
1242
- // ============================================================================
1243
- /**
1244
- * Compile with trace output
1245
- */
1246
- async compileWithTrace(fafPath) {
1247
- const result = await this.compile(fafPath);
1248
- this.printTrace(result);
1249
- return result;
1250
- }
1251
- /**
1252
- * Verify a checksum
1253
- */
1254
- async verify(fafPath, checksum) {
1255
- const result = await this.compile(fafPath);
1256
- return result.checksum === checksum;
1257
- }
1258
- /**
1259
- * Get intermediate representation
1260
- */
1261
- async getIR(fafPath) {
1262
- const result = await this.compile(fafPath);
1263
- return result.ir;
1264
- }
1265
- /**
1266
- * Print diagnostic report
1267
- */
1268
- printDiagnostics() {
1269
- if (this.diagnostics.length === 0) {
1270
- console.log('✓ No issues found');
1271
- return;
1272
- }
1273
- const byType = {
1274
- error: this.diagnostics.filter(d => d.severity === 'error'),
1275
- warning: this.diagnostics.filter(d => d.severity === 'warning'),
1276
- info: this.diagnostics.filter(d => d.severity === 'info')
1277
- };
1278
- if (byType.error.length > 0) {
1279
- console.log(`\n❌ ${byType.error.length} Errors:`);
1280
- byType.error.forEach(d => {
1281
- console.log(` ${d.message}`);
1282
- if (d.suggestion)
1283
- console.log(` → ${d.suggestion}`);
1284
- });
1285
- }
1286
- if (byType.warning.length > 0) {
1287
- console.log(`\n⚠️ ${byType.warning.length} Warnings:`);
1288
- byType.warning.forEach(d => {
1289
- console.log(` ${d.message}`);
1290
- if (d.suggestion)
1291
- console.log(` → ${d.suggestion}`);
1292
- });
1293
- }
1294
- if (byType.info.length > 0) {
1295
- console.log(`\nℹ️ ${byType.info.length} Info:`);
1296
- byType.info.forEach(d => {
1297
- console.log(` ${d.message}`);
1298
- });
1299
- }
1300
- }
1301
- /**
1302
- * Print compilation trace
1303
- */
1304
- printTrace(result) {
1305
- console.log('\n📊 FAF Compilation Trace');
1306
- console.log('═'.repeat(60));
1307
- console.log(`Version: ${result.trace.version}`);
1308
- console.log(`Input Hash: ${result.trace.inputHash}`);
1309
- console.log(`Checksum: ${result.checksum}`);
1310
- console.log();
1311
- console.log('Compilation Passes:');
1312
- result.trace.passes.forEach((pass, i) => {
1313
- console.log(` ${i + 1}. ${pass.name.toUpperCase()} (${pass.duration}ms)`);
1314
- pass.changes.forEach(change => {
1315
- console.log(` → ${change}`);
1316
- });
1317
- });
1318
- console.log();
1319
- console.log('Result:');
1320
- console.log(` Score: ${result.score}%`);
1321
- console.log(` Filled: ${result.filled}/${result.total} slots`);
1322
- console.log();
1323
- console.log('Breakdown:');
1324
- console.log(` Project: ${result.breakdown.project.filled}/${result.breakdown.project.total} (${result.breakdown.project.percentage}%)`);
1325
- console.log(` Stack: ${result.breakdown.stack.filled}/${result.breakdown.stack.total} (${result.breakdown.stack.percentage}%)`);
1326
- console.log(` Human: ${result.breakdown.human.filled}/${result.breakdown.human.total} (${result.breakdown.human.percentage}%)`);
1327
- if (result.breakdown.discovery.total > 0) {
1328
- console.log(` Discovery: ${result.breakdown.discovery.filled}/${result.breakdown.discovery.total} (${result.breakdown.discovery.percentage}%)`);
1329
- }
1330
- if (result.diagnostics.length > 0) {
1331
- console.log();
1332
- this.printDiagnostics();
1333
- }
1334
- console.log('═'.repeat(60));
1335
- }
1336
- }
1337
- exports.FafCompiler = FafCompiler;
1338
- // ============================================================================
1339
- // Export convenience functions
1340
- // ============================================================================
1341
- /**
1342
- * Compile a .faf file
1343
- */
1344
- async function compile(fafPath) {
1345
- const compiler = new FafCompiler();
1346
- return compiler.compile(fafPath);
1347
- }
1348
- /**
1349
- * Compile with trace output
1350
- */
1351
- async function compileWithTrace(fafPath) {
1352
- const compiler = new FafCompiler();
1353
- return compiler.compileWithTrace(fafPath);
1354
- }
1355
- /**
1356
- * Verify a checksum
1357
- */
1358
- async function verify(fafPath, checksum) {
1359
- const compiler = new FafCompiler();
1360
- return compiler.verify(fafPath, checksum);
1361
- }
1362
- //# sourceMappingURL=faf-compiler.js.map