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.
- package/CHANGELOG.md +26 -3
- package/CLAUDE.md +6 -6
- package/README.md +71 -72
- package/dist/src/faf-core/commands/bi-sync.d.ts +1 -3
- package/dist/src/faf-core/commands/bi-sync.js +21 -64
- package/dist/src/faf-core/commands/bi-sync.js.map +1 -1
- package/dist/src/faf-core/commands/sync.js.map +1 -1
- package/dist/src/faf-core/engines/dependency-tsa.js.map +1 -1
- package/dist/src/faf-core/fix-once/colors.js +1 -1
- package/dist/src/faf-core/fix-once/colors.js.map +1 -1
- package/dist/src/faf-core/parsers/agents-parser.d.ts +15 -0
- package/dist/src/faf-core/parsers/agents-parser.js +29 -123
- package/dist/src/faf-core/parsers/agents-parser.js.map +1 -1
- package/dist/src/faf-core/parsers/conductor-parser.js +1 -1
- package/dist/src/faf-core/parsers/conductor-parser.js.map +1 -1
- package/dist/src/faf-core/parsers/cursorrules-parser.d.ts +15 -0
- package/dist/src/faf-core/parsers/cursorrules-parser.js +54 -111
- package/dist/src/faf-core/parsers/cursorrules-parser.js.map +1 -1
- package/dist/src/faf-core/parsers/faf-git-generator.js +0 -1
- package/dist/src/faf-core/parsers/faf-git-generator.js.map +1 -1
- package/dist/src/faf-core/parsers/gemini-parser.d.ts +10 -0
- package/dist/src/faf-core/parsers/gemini-parser.js +19 -77
- package/dist/src/faf-core/parsers/gemini-parser.js.map +1 -1
- package/dist/src/handlers/engine-adapter.d.ts +13 -12
- package/dist/src/handlers/engine-adapter.js +103 -682
- package/dist/src/handlers/engine-adapter.js.map +1 -1
- package/dist/src/handlers/resources.d.ts +19 -1
- package/dist/src/handlers/resources.js +108 -23
- package/dist/src/handlers/resources.js.map +1 -1
- package/dist/src/handlers/skills.d.ts +70 -0
- package/dist/src/handlers/skills.js +174 -0
- package/dist/src/handlers/skills.js.map +1 -0
- package/dist/src/handlers/tools.d.ts +41 -5
- package/dist/src/handlers/tools.js +213 -258
- package/dist/src/handlers/tools.js.map +1 -1
- package/dist/src/server.d.ts +4 -0
- package/dist/src/server.js +51 -6
- package/dist/src/server.js.map +1 -1
- package/dist/src/utils/faf-cli-bridge.d.ts +2 -0
- package/dist/src/utils/faf-cli-bridge.js +16 -0
- package/dist/src/utils/faf-cli-bridge.js.map +1 -1
- package/package.json +16 -13
- package/project.faf +11 -6
- package/scripts/check-engines.mjs +53 -0
- package/scripts/sync-version.js +33 -19
- package/skills/faf-ide/SKILL.md +40 -0
- package/dist/src/config/visibility.d.ts +0 -41
- package/dist/src/config/visibility.js +0 -158
- package/dist/src/config/visibility.js.map +0 -1
- package/dist/src/faf-core/commands/audit.d.ts +0 -21
- package/dist/src/faf-core/commands/audit.js +0 -83
- package/dist/src/faf-core/commands/audit.js.map +0 -1
- package/dist/src/faf-core/commands/auto.d.ts +0 -25
- package/dist/src/faf-core/commands/auto.js +0 -74
- package/dist/src/faf-core/commands/auto.js.map +0 -1
- package/dist/src/faf-core/commands/doctor.d.ts +0 -17
- package/dist/src/faf-core/commands/doctor.js +0 -198
- package/dist/src/faf-core/commands/doctor.js.map +0 -1
- package/dist/src/faf-core/commands/enhance.d.ts +0 -46
- package/dist/src/faf-core/commands/enhance.js +0 -360
- package/dist/src/faf-core/commands/enhance.js.map +0 -1
- package/dist/src/faf-core/commands/formats.d.ts +0 -22
- package/dist/src/faf-core/commands/formats.js +0 -117
- package/dist/src/faf-core/commands/formats.js.map +0 -1
- package/dist/src/faf-core/commands/human.d.ts +0 -27
- package/dist/src/faf-core/commands/human.js +0 -183
- package/dist/src/faf-core/commands/human.js.map +0 -1
- package/dist/src/faf-core/commands/init.d.ts +0 -26
- package/dist/src/faf-core/commands/init.js +0 -114
- package/dist/src/faf-core/commands/init.js.map +0 -1
- package/dist/src/faf-core/commands/innit.d.ts +0 -7
- package/dist/src/faf-core/commands/innit.js +0 -13
- package/dist/src/faf-core/commands/innit.js.map +0 -1
- package/dist/src/faf-core/commands/migrate.d.ts +0 -15
- package/dist/src/faf-core/commands/migrate.js +0 -86
- package/dist/src/faf-core/commands/migrate.js.map +0 -1
- package/dist/src/faf-core/commands/quick.d.ts +0 -16
- package/dist/src/faf-core/commands/quick.js +0 -184
- package/dist/src/faf-core/commands/quick.js.map +0 -1
- package/dist/src/faf-core/commands/readme.d.ts +0 -47
- package/dist/src/faf-core/commands/readme.js +0 -361
- package/dist/src/faf-core/commands/readme.js.map +0 -1
- package/dist/src/faf-core/commands/score.d.ts +0 -47
- package/dist/src/faf-core/commands/score.js +0 -49
- package/dist/src/faf-core/commands/score.js.map +0 -1
- package/dist/src/faf-core/commands/update.d.ts +0 -12
- package/dist/src/faf-core/commands/update.js +0 -46
- package/dist/src/faf-core/commands/update.js.map +0 -1
- package/dist/src/faf-core/commands/validate.d.ts +0 -21
- package/dist/src/faf-core/commands/validate.js +0 -81
- package/dist/src/faf-core/commands/validate.js.map +0 -1
- package/dist/src/faf-core/compiler/faf-compiler.d.ts +0 -138
- package/dist/src/faf-core/compiler/faf-compiler.js +0 -1362
- package/dist/src/faf-core/compiler/faf-compiler.js.map +0 -1
- package/dist/src/faf-core/engines/fab-formats-processor.d.ts +0 -166
- package/dist/src/faf-core/engines/fab-formats-processor.js +0 -1274
- package/dist/src/faf-core/engines/fab-formats-processor.js.map +0 -1
- package/dist/src/faf-core/engines/faf-dna.d.ts +0 -159
- package/dist/src/faf-core/engines/faf-dna.js +0 -555
- package/dist/src/faf-core/engines/faf-dna.js.map +0 -1
- package/dist/src/faf-core/engines/relentless-context-extractor.d.ts +0 -100
- package/dist/src/faf-core/engines/relentless-context-extractor.js +0 -625
- package/dist/src/faf-core/engines/relentless-context-extractor.js.map +0 -1
- package/dist/src/faf-core/fix-once/types.d.ts +0 -257
- package/dist/src/faf-core/fix-once/types.js +0 -26
- package/dist/src/faf-core/fix-once/types.js.map +0 -1
- package/dist/src/faf-core/generators/faf-generator-championship.d.ts +0 -16
- package/dist/src/faf-core/generators/faf-generator-championship.js +0 -463
- package/dist/src/faf-core/generators/faf-generator-championship.js.map +0 -1
- package/dist/src/faf-core/inject.d.ts +0 -19
- package/dist/src/faf-core/inject.js +0 -57
- package/dist/src/faf-core/inject.js.map +0 -1
- package/dist/src/faf-core/parsers/index.d.ts +0 -12
- package/dist/src/faf-core/parsers/index.js +0 -29
- package/dist/src/faf-core/parsers/index.js.map +0 -1
- package/dist/src/faf-core/utils/balance-visualizer.d.ts +0 -37
- package/dist/src/faf-core/utils/balance-visualizer.js +0 -197
- package/dist/src/faf-core/utils/balance-visualizer.js.map +0 -1
- package/dist/src/faf-core/utils/championship-style.d.ts +0 -109
- package/dist/src/faf-core/utils/championship-style.js +0 -219
- package/dist/src/faf-core/utils/championship-style.js.map +0 -1
- package/dist/src/faf-core/utils/chrome-extension-detector.d.ts +0 -73
- package/dist/src/faf-core/utils/chrome-extension-detector.js +0 -268
- package/dist/src/faf-core/utils/chrome-extension-detector.js.map +0 -1
- package/dist/src/faf-core/utils/platform-detector.d.ts +0 -30
- package/dist/src/faf-core/utils/platform-detector.js +0 -218
- package/dist/src/faf-core/utils/platform-detector.js.map +0 -1
- package/dist/src/faf-core/utils/technical-credit.d.ts +0 -35
- package/dist/src/faf-core/utils/technical-credit.js +0 -286
- package/dist/src/faf-core/utils/technical-credit.js.map +0 -1
- package/dist/src/faf-core/utils/yaml-generator.d.ts +0 -41
- package/dist/src/faf-core/utils/yaml-generator.js +0 -360
- package/dist/src/faf-core/utils/yaml-generator.js.map +0 -1
- package/dist/src/handlers/behavioral-instruction.d.ts +0 -16
- package/dist/src/handlers/behavioral-instruction.js +0 -43
- package/dist/src/handlers/behavioral-instruction.js.map +0 -1
- package/dist/src/handlers/championship-tools.d.ts +0 -116
- package/dist/src/handlers/championship-tools.js +0 -2320
- package/dist/src/handlers/championship-tools.js.map +0 -1
- package/dist/src/handlers/cloud-handler.d.ts +0 -59
- package/dist/src/handlers/cloud-handler.js +0 -348
- package/dist/src/handlers/cloud-handler.js.map +0 -1
- package/dist/src/handlers/tool-registry.d.ts +0 -23
- package/dist/src/handlers/tool-registry.js +0 -68
- package/dist/src/handlers/tool-registry.js.map +0 -1
- package/dist/src/handlers/tool-types.d.ts +0 -167
- package/dist/src/handlers/tool-types.js +0 -7
- package/dist/src/handlers/tool-types.js.map +0 -1
- package/dist/src/test-all-functions.d.ts +0 -15
- package/dist/src/test-all-functions.js +0 -163
- package/dist/src/test-all-functions.js.map +0 -1
- package/dist/src/types/mcp-tools.d.ts +0 -53
- package/dist/src/types/mcp-tools.js +0 -77
- package/dist/src/types/mcp-tools.js.map +0 -1
- package/dist/src/types/tool-visibility.d.ts +0 -36
- package/dist/src/types/tool-visibility.js +0 -551
- package/dist/src/types/tool-visibility.js.map +0 -1
- package/dist/src/utils/auto-path-detection.d.ts +0 -26
- package/dist/src/utils/auto-path-detection.js +0 -198
- package/dist/src/utils/auto-path-detection.js.map +0 -1
- package/dist/src/utils/championship-format.d.ts +0 -30
- package/dist/src/utils/championship-format.js +0 -81
- package/dist/src/utils/championship-format.js.map +0 -1
- package/dist/src/utils/cli-detector.d.ts +0 -17
- package/dist/src/utils/cli-detector.js +0 -247
- package/dist/src/utils/cli-detector.js.map +0 -1
- package/dist/src/utils/display-protocol.d.ts +0 -57
- package/dist/src/utils/display-protocol.js +0 -131
- package/dist/src/utils/display-protocol.js.map +0 -1
- package/dist/src/utils/username-detector.d.ts +0 -27
- package/dist/src/utils/username-detector.js +0 -90
- package/dist/src/utils/username-detector.js.map +0 -1
- package/dist/src/utils/visual-style.d.ts +0 -62
- package/dist/src/utils/visual-style.js +0 -166
- package/dist/src/utils/visual-style.js.map +0 -1
- package/scripts/postinstall.js +0 -50
- 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
|