faf-mcp 1.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 +34 -0
- package/CLAUDE.md +73 -0
- package/LICENSE +22 -0
- package/README.md +165 -0
- package/assets/Project-faf-pckg-json-README.png +0 -0
- package/assets/icons/faf-icon-128.png +0 -0
- package/assets/icons/faf-icon-256.png +0 -0
- package/assets/icons/faf-icon-48.png +0 -0
- package/assets/icons/faf-icon-512.png +0 -0
- package/assets/icons/orange-smiley.svg +6 -0
- package/dist/src/compiler/index.d.ts +7 -0
- package/dist/src/compiler/index.js +24 -0
- package/dist/src/compiler/index.js.map +1 -0
- package/dist/src/compiler/scorer.d.ts +53 -0
- package/dist/src/compiler/scorer.js +189 -0
- package/dist/src/compiler/scorer.js.map +1 -0
- package/dist/src/compiler/slot-validator.d.ts +32 -0
- package/dist/src/compiler/slot-validator.js +293 -0
- package/dist/src/compiler/slot-validator.js.map +1 -0
- package/dist/src/compiler/type-detector.d.ts +62 -0
- package/dist/src/compiler/type-detector.js +388 -0
- package/dist/src/compiler/type-detector.js.map +1 -0
- package/dist/src/config/visibility.d.ts +41 -0
- package/dist/src/config/visibility.js +158 -0
- package/dist/src/config/visibility.js.map +1 -0
- package/dist/src/faf-core/commands/audit.d.ts +21 -0
- package/dist/src/faf-core/commands/audit.js +83 -0
- package/dist/src/faf-core/commands/audit.js.map +1 -0
- package/dist/src/faf-core/commands/auto.d.ts +25 -0
- package/dist/src/faf-core/commands/auto.js +74 -0
- package/dist/src/faf-core/commands/auto.js.map +1 -0
- package/dist/src/faf-core/commands/bi-sync.d.ts +26 -0
- package/dist/src/faf-core/commands/bi-sync.js +157 -0
- package/dist/src/faf-core/commands/bi-sync.js.map +1 -0
- package/dist/src/faf-core/commands/doctor.d.ts +17 -0
- package/dist/src/faf-core/commands/doctor.js +198 -0
- package/dist/src/faf-core/commands/doctor.js.map +1 -0
- package/dist/src/faf-core/commands/enhance.d.ts +46 -0
- package/dist/src/faf-core/commands/enhance.js +360 -0
- package/dist/src/faf-core/commands/enhance.js.map +1 -0
- package/dist/src/faf-core/commands/formats.d.ts +22 -0
- package/dist/src/faf-core/commands/formats.js +117 -0
- package/dist/src/faf-core/commands/formats.js.map +1 -0
- package/dist/src/faf-core/commands/init.d.ts +26 -0
- package/dist/src/faf-core/commands/init.js +114 -0
- package/dist/src/faf-core/commands/init.js.map +1 -0
- package/dist/src/faf-core/commands/innit.d.ts +7 -0
- package/dist/src/faf-core/commands/innit.js +13 -0
- package/dist/src/faf-core/commands/innit.js.map +1 -0
- package/dist/src/faf-core/commands/migrate.d.ts +15 -0
- package/dist/src/faf-core/commands/migrate.js +86 -0
- package/dist/src/faf-core/commands/migrate.js.map +1 -0
- package/dist/src/faf-core/commands/quick.d.ts +16 -0
- package/dist/src/faf-core/commands/quick.js +184 -0
- package/dist/src/faf-core/commands/quick.js.map +1 -0
- package/dist/src/faf-core/commands/score.d.ts +47 -0
- package/dist/src/faf-core/commands/score.js +49 -0
- package/dist/src/faf-core/commands/score.js.map +1 -0
- package/dist/src/faf-core/commands/sync.d.ts +16 -0
- package/dist/src/faf-core/commands/sync.js +210 -0
- package/dist/src/faf-core/commands/sync.js.map +1 -0
- package/dist/src/faf-core/commands/update.d.ts +12 -0
- package/dist/src/faf-core/commands/update.js +46 -0
- package/dist/src/faf-core/commands/update.js.map +1 -0
- package/dist/src/faf-core/commands/validate.d.ts +21 -0
- package/dist/src/faf-core/commands/validate.js +81 -0
- package/dist/src/faf-core/commands/validate.js.map +1 -0
- package/dist/src/faf-core/compiler/faf-compiler.d.ts +138 -0
- package/dist/src/faf-core/compiler/faf-compiler.js +794 -0
- package/dist/src/faf-core/compiler/faf-compiler.js.map +1 -0
- package/dist/src/faf-core/engines/dependency-tsa.d.ts +88 -0
- package/dist/src/faf-core/engines/dependency-tsa.js +361 -0
- package/dist/src/faf-core/engines/dependency-tsa.js.map +1 -0
- package/dist/src/faf-core/engines/fab-formats-processor.d.ts +166 -0
- package/dist/src/faf-core/engines/fab-formats-processor.js +1274 -0
- package/dist/src/faf-core/engines/fab-formats-processor.js.map +1 -0
- package/dist/src/faf-core/engines/faf-dna.d.ts +159 -0
- package/dist/src/faf-core/engines/faf-dna.js +554 -0
- package/dist/src/faf-core/engines/faf-dna.js.map +1 -0
- package/dist/src/faf-core/engines/relentless-context-extractor.d.ts +100 -0
- package/dist/src/faf-core/engines/relentless-context-extractor.js +625 -0
- package/dist/src/faf-core/engines/relentless-context-extractor.js.map +1 -0
- package/dist/src/faf-core/fix-once/colors.d.ts +104 -0
- package/dist/src/faf-core/fix-once/colors.js +236 -0
- package/dist/src/faf-core/fix-once/colors.js.map +1 -0
- package/dist/src/faf-core/fix-once/types.d.ts +257 -0
- package/dist/src/faf-core/fix-once/types.js +26 -0
- package/dist/src/faf-core/fix-once/types.js.map +1 -0
- package/dist/src/faf-core/fix-once/yaml.d.ts +57 -0
- package/dist/src/faf-core/fix-once/yaml.js +172 -0
- package/dist/src/faf-core/fix-once/yaml.js.map +1 -0
- package/dist/src/faf-core/generators/faf-generator-championship.d.ts +16 -0
- package/dist/src/faf-core/generators/faf-generator-championship.js +462 -0
- package/dist/src/faf-core/generators/faf-generator-championship.js.map +1 -0
- package/dist/src/faf-core/utils/balance-visualizer.d.ts +37 -0
- package/dist/src/faf-core/utils/balance-visualizer.js +197 -0
- package/dist/src/faf-core/utils/balance-visualizer.js.map +1 -0
- package/dist/src/faf-core/utils/championship-style.d.ts +109 -0
- package/dist/src/faf-core/utils/championship-style.js +219 -0
- package/dist/src/faf-core/utils/championship-style.js.map +1 -0
- package/dist/src/faf-core/utils/chrome-extension-detector.d.ts +73 -0
- package/dist/src/faf-core/utils/chrome-extension-detector.js +268 -0
- package/dist/src/faf-core/utils/chrome-extension-detector.js.map +1 -0
- package/dist/src/faf-core/utils/fafignore-parser.d.ts +20 -0
- package/dist/src/faf-core/utils/fafignore-parser.js +178 -0
- package/dist/src/faf-core/utils/fafignore-parser.js.map +1 -0
- package/dist/src/faf-core/utils/file-utils.d.ts +112 -0
- package/dist/src/faf-core/utils/file-utils.js +846 -0
- package/dist/src/faf-core/utils/file-utils.js.map +1 -0
- package/dist/src/faf-core/utils/native-file-finder.d.ts +115 -0
- package/dist/src/faf-core/utils/native-file-finder.js +211 -0
- package/dist/src/faf-core/utils/native-file-finder.js.map +1 -0
- package/dist/src/faf-core/utils/platform-detector.d.ts +30 -0
- package/dist/src/faf-core/utils/platform-detector.js +218 -0
- package/dist/src/faf-core/utils/platform-detector.js.map +1 -0
- package/dist/src/faf-core/utils/technical-credit.d.ts +35 -0
- package/dist/src/faf-core/utils/technical-credit.js +286 -0
- package/dist/src/faf-core/utils/technical-credit.js.map +1 -0
- package/dist/src/faf-core/utils/yaml-generator.d.ts +41 -0
- package/dist/src/faf-core/utils/yaml-generator.js +360 -0
- package/dist/src/faf-core/utils/yaml-generator.js.map +1 -0
- package/dist/src/handlers/behavioral-instruction.d.ts +16 -0
- package/dist/src/handlers/behavioral-instruction.js +43 -0
- package/dist/src/handlers/behavioral-instruction.js.map +1 -0
- package/dist/src/handlers/championship-tools.d.ts +113 -0
- package/dist/src/handlers/championship-tools.js +2602 -0
- package/dist/src/handlers/championship-tools.js.map +1 -0
- package/dist/src/handlers/engine-adapter.d.ts +28 -0
- package/dist/src/handlers/engine-adapter.js +603 -0
- package/dist/src/handlers/engine-adapter.js.map +1 -0
- package/dist/src/handlers/fileHandler.d.ts +36 -0
- package/dist/src/handlers/fileHandler.js +246 -0
- package/dist/src/handlers/fileHandler.js.map +1 -0
- package/dist/src/handlers/resources.d.ts +18 -0
- package/dist/src/handlers/resources.js +78 -0
- package/dist/src/handlers/resources.js.map +1 -0
- package/dist/src/handlers/tool-registry.d.ts +23 -0
- package/dist/src/handlers/tool-registry.js +68 -0
- package/dist/src/handlers/tool-registry.js.map +1 -0
- package/dist/src/handlers/tool-types.d.ts +167 -0
- package/dist/src/handlers/tool-types.js +7 -0
- package/dist/src/handlers/tool-types.js.map +1 -0
- package/dist/src/handlers/tools.d.ts +25 -0
- package/dist/src/handlers/tools.js +1168 -0
- package/dist/src/handlers/tools.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +17 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/server.d.ts +28 -0
- package/dist/src/server.js +179 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/test-all-functions.d.ts +15 -0
- package/dist/src/test-all-functions.js +163 -0
- package/dist/src/test-all-functions.js.map +1 -0
- package/dist/src/types/mcp-tools.d.ts +53 -0
- package/dist/src/types/mcp-tools.js +77 -0
- package/dist/src/types/mcp-tools.js.map +1 -0
- package/dist/src/types/project-types.d.ts +22 -0
- package/dist/src/types/project-types.js +85 -0
- package/dist/src/types/project-types.js.map +1 -0
- package/dist/src/types/slots.d.ts +39 -0
- package/dist/src/types/slots.js +162 -0
- package/dist/src/types/slots.js.map +1 -0
- package/dist/src/types/tool-visibility.d.ts +36 -0
- package/dist/src/types/tool-visibility.js +510 -0
- package/dist/src/types/tool-visibility.js.map +1 -0
- package/dist/src/utils/auto-path-detection.d.ts +26 -0
- package/dist/src/utils/auto-path-detection.js +198 -0
- package/dist/src/utils/auto-path-detection.js.map +1 -0
- package/dist/src/utils/championship-format.d.ts +30 -0
- package/dist/src/utils/championship-format.js +79 -0
- package/dist/src/utils/championship-format.js.map +1 -0
- package/dist/src/utils/cli-detector.d.ts +20 -0
- package/dist/src/utils/cli-detector.js +230 -0
- package/dist/src/utils/cli-detector.js.map +1 -0
- package/dist/src/utils/display-protocol.d.ts +57 -0
- package/dist/src/utils/display-protocol.js +131 -0
- package/dist/src/utils/display-protocol.js.map +1 -0
- package/dist/src/utils/faf-file-finder.d.ts +59 -0
- package/dist/src/utils/faf-file-finder.js +139 -0
- package/dist/src/utils/faf-file-finder.js.map +1 -0
- package/dist/src/utils/fuzzy-detector.d.ts +56 -0
- package/dist/src/utils/fuzzy-detector.js +221 -0
- package/dist/src/utils/fuzzy-detector.js.map +1 -0
- package/dist/src/utils/path-resolver.d.ts +51 -0
- package/dist/src/utils/path-resolver.js +214 -0
- package/dist/src/utils/path-resolver.js.map +1 -0
- package/dist/src/utils/type-guards.d.ts +9 -0
- package/dist/src/utils/type-guards.js +27 -0
- package/dist/src/utils/type-guards.js.map +1 -0
- package/dist/src/utils/username-detector.d.ts +27 -0
- package/dist/src/utils/username-detector.js +90 -0
- package/dist/src/utils/username-detector.js.map +1 -0
- package/dist/src/utils/visual-style.d.ts +62 -0
- package/dist/src/utils/visual-style.js +164 -0
- package/dist/src/utils/visual-style.js.map +1 -0
- package/dist/src/version.d.ts +9 -0
- package/dist/src/version.js +37 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +114 -0
- package/scripts/discord-sync-curated.js +233 -0
- package/scripts/discord-sync-final.js +218 -0
- package/scripts/discord-sync-simple.js +175 -0
- package/scripts/discord-sync-working.js +187 -0
- package/scripts/discord-sync.js +181 -0
- package/scripts/postinstall.js +46 -0
- package/skill/SKILL.md +385 -0
|
@@ -0,0 +1,794 @@
|
|
|
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
|
+
// Main Compiler Class
|
|
54
|
+
// ============================================================================
|
|
55
|
+
class FafCompiler {
|
|
56
|
+
static VERSION = '3.0.0-compiler';
|
|
57
|
+
diagnostics = [];
|
|
58
|
+
trace;
|
|
59
|
+
ir;
|
|
60
|
+
constructor() {
|
|
61
|
+
this.trace = {
|
|
62
|
+
version: FafCompiler.VERSION,
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
inputHash: '',
|
|
65
|
+
passes: []
|
|
66
|
+
};
|
|
67
|
+
this.ir = {
|
|
68
|
+
version: FafCompiler.VERSION,
|
|
69
|
+
slots: [],
|
|
70
|
+
metadata: {}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Main compilation pipeline
|
|
75
|
+
*/
|
|
76
|
+
async compile(fafPath) {
|
|
77
|
+
// Reset state for new compilation
|
|
78
|
+
this.diagnostics = [];
|
|
79
|
+
this.trace.passes = [];
|
|
80
|
+
this.ir.slots = [];
|
|
81
|
+
try {
|
|
82
|
+
// PASS 1: Parse
|
|
83
|
+
const source = await this.readSource(fafPath);
|
|
84
|
+
const ast = this.parse(source);
|
|
85
|
+
// PASS 2: Analyze
|
|
86
|
+
const analyzed = this.analyze(ast);
|
|
87
|
+
// PASS 3: Optimize (with discovery)
|
|
88
|
+
const optimized = await this.optimize(analyzed, fafPath);
|
|
89
|
+
// PASS 4: Generate
|
|
90
|
+
const result = this.generate(optimized);
|
|
91
|
+
// Add final checksum
|
|
92
|
+
const checksum = this.calculateChecksum(result);
|
|
93
|
+
return {
|
|
94
|
+
...result,
|
|
95
|
+
trace: this.trace,
|
|
96
|
+
diagnostics: this.diagnostics,
|
|
97
|
+
ir: this.ir,
|
|
98
|
+
checksum
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
103
|
+
this.addDiagnostic('error', `Compilation failed: ${message}`);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// PASS 1: Parser
|
|
109
|
+
// ============================================================================
|
|
110
|
+
async readSource(fafPath) {
|
|
111
|
+
const start = Date.now();
|
|
112
|
+
const fs = await import('fs/promises');
|
|
113
|
+
try {
|
|
114
|
+
const source = await fs.readFile(fafPath, 'utf-8');
|
|
115
|
+
this.trace.inputHash = crypto.createHash('md5').update(source).digest('hex');
|
|
116
|
+
this.recordPass('read', start, fafPath, source.length, [
|
|
117
|
+
`Read ${source.length} bytes from ${fafPath}`
|
|
118
|
+
]);
|
|
119
|
+
return source;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
this.addDiagnostic('error', `Cannot read file: ${fafPath}`, undefined, message);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
parse(source) {
|
|
128
|
+
const start = Date.now();
|
|
129
|
+
try {
|
|
130
|
+
const ast = (0, yaml_1.parse)(source);
|
|
131
|
+
if (!ast || typeof ast !== 'object') {
|
|
132
|
+
this.addDiagnostic('error', 'Invalid YAML: must be an object');
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
this.recordPass('parse', start, source.length, ast, [
|
|
136
|
+
`Parsed YAML into AST with ${Object.keys(ast).length} top-level keys`
|
|
137
|
+
]);
|
|
138
|
+
return ast;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
142
|
+
this.addDiagnostic('error', `Parse error: ${message}`);
|
|
143
|
+
return {};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// PASS 2: Analyzer
|
|
148
|
+
// ============================================================================
|
|
149
|
+
analyze(ast) {
|
|
150
|
+
const start = Date.now();
|
|
151
|
+
const analyzed = { ...ast };
|
|
152
|
+
const changes = [];
|
|
153
|
+
// Validate and normalize structure
|
|
154
|
+
if (!analyzed.project) {
|
|
155
|
+
analyzed.project = {};
|
|
156
|
+
changes.push('Added missing project section');
|
|
157
|
+
}
|
|
158
|
+
if (!analyzed.stack) {
|
|
159
|
+
analyzed.stack = {};
|
|
160
|
+
changes.push('Added missing stack section');
|
|
161
|
+
}
|
|
162
|
+
if (!analyzed.human_context) {
|
|
163
|
+
analyzed.human_context = {};
|
|
164
|
+
changes.push('Added missing human_context section');
|
|
165
|
+
}
|
|
166
|
+
// Type validation
|
|
167
|
+
this.validateTypes(analyzed);
|
|
168
|
+
// Check for deprecated fields
|
|
169
|
+
if (analyzed.ai_score !== undefined) {
|
|
170
|
+
this.addDiagnostic('warning', 'Embedded ai_score is deprecated and will be ignored');
|
|
171
|
+
delete analyzed.ai_score;
|
|
172
|
+
changes.push('Removed deprecated ai_score');
|
|
173
|
+
}
|
|
174
|
+
this.recordPass('analyze', start, ast, analyzed, changes);
|
|
175
|
+
return analyzed;
|
|
176
|
+
}
|
|
177
|
+
validateTypes(data) {
|
|
178
|
+
// Project validation
|
|
179
|
+
if (data.project?.name && typeof data.project.name !== 'string') {
|
|
180
|
+
this.addDiagnostic('error', 'project.name must be a string', {
|
|
181
|
+
line: 0, column: 0, field: 'project.name'
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// Stack validation
|
|
185
|
+
const stackFields = ['frontend', 'backend', 'database', 'hosting'];
|
|
186
|
+
for (const field of stackFields) {
|
|
187
|
+
if (data.stack?.[field] && typeof data.stack[field] !== 'string') {
|
|
188
|
+
this.addDiagnostic('error', `stack.${field} must be a string`, {
|
|
189
|
+
line: 0, column: 0, field: `stack.${field}`
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Human context validation
|
|
194
|
+
const humanFields = ['who', 'what', 'why', 'where', 'when', 'how'];
|
|
195
|
+
for (const field of humanFields) {
|
|
196
|
+
const value = data.human_context?.[field];
|
|
197
|
+
if (value && typeof value !== 'string' && typeof value !== 'object') {
|
|
198
|
+
this.addDiagnostic('warning', `human_context.${field} should be a string or object`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// PASS 3: Optimizer (with Discovery)
|
|
204
|
+
// ============================================================================
|
|
205
|
+
async optimize(ast, fafPath) {
|
|
206
|
+
const start = Date.now();
|
|
207
|
+
const optimized = { ...ast };
|
|
208
|
+
const changes = [];
|
|
209
|
+
// Discovery phase
|
|
210
|
+
if (fafPath) {
|
|
211
|
+
try {
|
|
212
|
+
const projectDir = path.dirname(fafPath);
|
|
213
|
+
const discovered = await this.discover(projectDir);
|
|
214
|
+
// Apply discovered values WITHOUT mutation
|
|
215
|
+
optimized._discovered = discovered;
|
|
216
|
+
changes.push(`Discovered ${Object.keys(discovered).length} items`);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
220
|
+
this.addDiagnostic('warning', `Discovery failed: ${message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Remove None/Unknown values (optimization)
|
|
224
|
+
this.removeDefaults(optimized, changes);
|
|
225
|
+
this.recordPass('optimize', start, ast, optimized, changes);
|
|
226
|
+
return optimized;
|
|
227
|
+
}
|
|
228
|
+
async discover(projectDir) {
|
|
229
|
+
const processor = new fab_formats_processor_1.FabFormatsProcessor();
|
|
230
|
+
const analysis = await processor.processFiles(projectDir);
|
|
231
|
+
const discovered = {};
|
|
232
|
+
if (analysis.context) {
|
|
233
|
+
const ctx = analysis.context;
|
|
234
|
+
// Map discovered items (WITHOUT modifying original)
|
|
235
|
+
if (ctx.projectName)
|
|
236
|
+
discovered.projectName = ctx.projectName;
|
|
237
|
+
if (ctx.mainLanguage)
|
|
238
|
+
discovered.mainLanguage = ctx.mainLanguage;
|
|
239
|
+
if (ctx.framework)
|
|
240
|
+
discovered.framework = ctx.framework;
|
|
241
|
+
if (ctx.database)
|
|
242
|
+
discovered.database = ctx.database;
|
|
243
|
+
if (ctx.backend)
|
|
244
|
+
discovered.backend = ctx.backend;
|
|
245
|
+
if (ctx.hosting)
|
|
246
|
+
discovered.hosting = ctx.hosting;
|
|
247
|
+
if (ctx.buildTool)
|
|
248
|
+
discovered.buildTool = ctx.buildTool;
|
|
249
|
+
}
|
|
250
|
+
return discovered;
|
|
251
|
+
}
|
|
252
|
+
removeDefaults(data, changes) {
|
|
253
|
+
const defaults = ['None', 'Unknown', 'Not specified', 'N/A'];
|
|
254
|
+
const removeFromObject = (obj, path) => {
|
|
255
|
+
for (const key in obj) {
|
|
256
|
+
const value = obj[key];
|
|
257
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
258
|
+
if (defaults.includes(value)) {
|
|
259
|
+
delete obj[key];
|
|
260
|
+
changes.push(`Removed default value at ${fullPath}`);
|
|
261
|
+
}
|
|
262
|
+
else if (typeof value === 'object' && value !== null) {
|
|
263
|
+
removeFromObject(value, fullPath);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
removeFromObject(data, '');
|
|
268
|
+
}
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// PASS 4: Generator
|
|
271
|
+
// ============================================================================
|
|
272
|
+
generate(ast) {
|
|
273
|
+
const start = Date.now();
|
|
274
|
+
// Build IR first
|
|
275
|
+
const ir = this.buildIR(ast);
|
|
276
|
+
this.ir = {
|
|
277
|
+
version: FafCompiler.VERSION,
|
|
278
|
+
slots: ir,
|
|
279
|
+
metadata: { compiled: new Date().toISOString() }
|
|
280
|
+
};
|
|
281
|
+
// Calculate from IR
|
|
282
|
+
const slots = this.calculateSlots(ir);
|
|
283
|
+
const score = this.calculateScore(slots);
|
|
284
|
+
const result = {
|
|
285
|
+
score: Math.round(score),
|
|
286
|
+
filled: slots.filled,
|
|
287
|
+
total: slots.total,
|
|
288
|
+
breakdown: slots.breakdown
|
|
289
|
+
};
|
|
290
|
+
this.recordPass('generate', start, ast, result, [
|
|
291
|
+
`Generated score: ${result.score}% (${result.filled}/${result.total} slots)`
|
|
292
|
+
]);
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
buildIR(ast) {
|
|
296
|
+
const slots = [];
|
|
297
|
+
// Detect project type to determine applicable stack slots
|
|
298
|
+
const projectType = this.detectProjectTypeFromContext(ast);
|
|
299
|
+
if (process.env.FAF_DEBUG) {
|
|
300
|
+
console.log(`\n[DEBUG] Project type detected: ${projectType}`);
|
|
301
|
+
}
|
|
302
|
+
const isFrontendProject = this.requiresFrontendStack(projectType);
|
|
303
|
+
const isBackendProject = this.requiresBackendStack(projectType);
|
|
304
|
+
// Auto-fill project.main_language for n8n workflows BEFORE reading slots
|
|
305
|
+
if (projectType === 'n8n-workflow') {
|
|
306
|
+
if (!ast.project)
|
|
307
|
+
ast.project = {};
|
|
308
|
+
if (!ast.project.main_language) {
|
|
309
|
+
ast.project.main_language = ast.tech_stack?.primary_language || 'JSON (workflow definition)';
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Project slots (3)
|
|
313
|
+
this.addSlot(slots, 'project.name', ast.project?.name, 'string', 'original', 1);
|
|
314
|
+
this.addSlot(slots, 'project.goal', ast.project?.goal, 'string', 'original', 1);
|
|
315
|
+
this.addSlot(slots, 'project.main_language', ast.project?.main_language, 'string', 'original', 1);
|
|
316
|
+
// Chrome Extension auto-fill: If it's a Chrome Extension, intelligently fill slots
|
|
317
|
+
if (projectType === 'chrome-extension') {
|
|
318
|
+
// Auto-fill Chrome Extension technical context
|
|
319
|
+
if (!ast.stack)
|
|
320
|
+
ast.stack = {};
|
|
321
|
+
if (!ast.stack.runtime)
|
|
322
|
+
ast.stack.runtime = 'Chrome/Browser';
|
|
323
|
+
if (!ast.stack.hosting)
|
|
324
|
+
ast.stack.hosting = 'Chrome Web Store';
|
|
325
|
+
if (!ast.stack.api_type)
|
|
326
|
+
ast.stack.api_type = 'Chrome Extension APIs';
|
|
327
|
+
if (!ast.stack.backend)
|
|
328
|
+
ast.stack.backend = 'Service Worker';
|
|
329
|
+
if (!ast.stack.database)
|
|
330
|
+
ast.stack.database = 'chrome.storage API';
|
|
331
|
+
if (!ast.deployment)
|
|
332
|
+
ast.deployment = 'Web Store Upload';
|
|
333
|
+
// Add Chrome Extension specific slots
|
|
334
|
+
this.addSlot(slots, 'stack.runtime', ast.stack.runtime, 'string', 'discovered', 1);
|
|
335
|
+
this.addSlot(slots, 'stack.hosting', ast.stack.hosting, 'string', 'discovered', 1);
|
|
336
|
+
this.addSlot(slots, 'stack.api_type', ast.stack.api_type, 'string', 'discovered', 1);
|
|
337
|
+
this.addSlot(slots, 'stack.backend', ast.stack.backend, 'string', 'discovered', 1);
|
|
338
|
+
this.addSlot(slots, 'stack.database', ast.stack.database, 'string', 'discovered', 1);
|
|
339
|
+
this.addSlot(slots, 'stack.deployment', ast.deployment, 'string', 'discovered', 1);
|
|
340
|
+
// Also include frontend framework if specified (e.g., Svelte Chrome Extension)
|
|
341
|
+
if (ast.stack?.frontend) {
|
|
342
|
+
this.addSlot(slots, 'stack.frontend', ast.stack.frontend, 'string', 'original', 1);
|
|
343
|
+
}
|
|
344
|
+
if (ast.stack?.build) {
|
|
345
|
+
this.addSlot(slots, 'stack.build', ast.stack.build, 'string', 'original', 1);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Static HTML auto-fill: If it's a static HTML site, intelligently fill slots
|
|
349
|
+
if (projectType === 'static-html' || projectType === 'landing-page') {
|
|
350
|
+
if (!ast.stack)
|
|
351
|
+
ast.stack = {};
|
|
352
|
+
if (!ast.stack.frontend)
|
|
353
|
+
ast.stack.frontend = 'HTML/CSS/JavaScript';
|
|
354
|
+
if (!ast.stack.runtime)
|
|
355
|
+
ast.stack.runtime = 'Browser';
|
|
356
|
+
if (!ast.stack.hosting)
|
|
357
|
+
ast.stack.hosting = 'Static Hosting';
|
|
358
|
+
if (!ast.stack.build)
|
|
359
|
+
ast.stack.build = 'None';
|
|
360
|
+
this.addSlot(slots, 'stack.frontend', ast.stack.frontend, 'string', 'discovered', 1);
|
|
361
|
+
this.addSlot(slots, 'stack.runtime', ast.stack.runtime, 'string', 'discovered', 1);
|
|
362
|
+
this.addSlot(slots, 'stack.hosting', ast.stack.hosting, 'string', 'discovered', 1);
|
|
363
|
+
this.addSlot(slots, 'stack.build', ast.stack.build, 'string', 'discovered', 1);
|
|
364
|
+
// Leave backend, database, cicd as "None" - legitimate for static sites
|
|
365
|
+
}
|
|
366
|
+
// n8n Workflow auto-fill: Map n8n-specific fields to standard 21-slot system
|
|
367
|
+
if (projectType === 'n8n-workflow') {
|
|
368
|
+
// Map n8n context to existing stack slots (efficient, no slot inflation)
|
|
369
|
+
if (!ast.stack)
|
|
370
|
+
ast.stack = {};
|
|
371
|
+
// Runtime = workflow engine
|
|
372
|
+
if (!ast.stack.runtime) {
|
|
373
|
+
ast.stack.runtime = ast.tech_stack?.workflow_engine || 'n8n';
|
|
374
|
+
}
|
|
375
|
+
// Backend = n8n server runtime
|
|
376
|
+
if (!ast.stack.backend) {
|
|
377
|
+
ast.stack.backend = 'Node.js (n8n server)';
|
|
378
|
+
}
|
|
379
|
+
// API type = n8n trigger types
|
|
380
|
+
if (!ast.stack.api_type) {
|
|
381
|
+
ast.stack.api_type = 'Webhooks + HTTP';
|
|
382
|
+
}
|
|
383
|
+
// Database = vector DB or workflow state
|
|
384
|
+
if (!ast.stack.database) {
|
|
385
|
+
ast.stack.database = ast.tech_stack?.infrastructure?.vector_db || 'Workflow State';
|
|
386
|
+
}
|
|
387
|
+
// Hosting = deployment location
|
|
388
|
+
if (!ast.stack.hosting) {
|
|
389
|
+
ast.stack.hosting = 'n8n Cloud';
|
|
390
|
+
}
|
|
391
|
+
// Build tool = how workflows are created
|
|
392
|
+
if (!ast.stack.build) {
|
|
393
|
+
ast.stack.build = 'n8n Visual Editor';
|
|
394
|
+
}
|
|
395
|
+
// Connection = integrations (maps to stack.connection slot)
|
|
396
|
+
if (!ast.stack.connection && ast.tech_stack?.integrations) {
|
|
397
|
+
ast.stack.connection = Array.isArray(ast.tech_stack.integrations)
|
|
398
|
+
? ast.tech_stack.integrations.join(', ')
|
|
399
|
+
: String(ast.tech_stack.integrations);
|
|
400
|
+
}
|
|
401
|
+
// Add n8n-specific slots (maps to standard 21-slot system)
|
|
402
|
+
// Backend slots (5)
|
|
403
|
+
this.addSlot(slots, 'stack.runtime', ast.stack.runtime, 'string', 'discovered', 1);
|
|
404
|
+
this.addSlot(slots, 'stack.backend', ast.stack.backend, 'string', 'discovered', 1);
|
|
405
|
+
this.addSlot(slots, 'stack.api_type', ast.stack.api_type, 'string', 'discovered', 1);
|
|
406
|
+
this.addSlot(slots, 'stack.database', ast.stack.database, 'string', 'discovered', 1);
|
|
407
|
+
this.addSlot(slots, 'stack.connection', ast.stack.connection, 'string', 'original', 1);
|
|
408
|
+
// Frontend slots (4) - n8n workflows don't have frontend, but we still count them
|
|
409
|
+
this.addSlot(slots, 'stack.frontend', ast.stack?.frontend, 'string', 'original', 1);
|
|
410
|
+
this.addSlot(slots, 'stack.css_framework', ast.stack?.css_framework, 'string', 'original', 1);
|
|
411
|
+
this.addSlot(slots, 'stack.ui_library', ast.stack?.ui_library, 'string', 'original', 1);
|
|
412
|
+
this.addSlot(slots, 'stack.state_management', ast.stack?.state_management, 'string', 'original', 1);
|
|
413
|
+
// Universal slots (3) will be added below (hosting, build, cicd)
|
|
414
|
+
}
|
|
415
|
+
// Stack slots - only add relevant ones based on project type
|
|
416
|
+
if (isFrontendProject) {
|
|
417
|
+
this.addSlot(slots, 'stack.frontend', ast.stack?.frontend, 'string', 'original', 1);
|
|
418
|
+
this.addSlot(slots, 'stack.css_framework', ast.stack?.css_framework, 'string', 'original', 1);
|
|
419
|
+
this.addSlot(slots, 'stack.ui_library', ast.stack?.ui_library, 'string', 'original', 1);
|
|
420
|
+
this.addSlot(slots, 'stack.state_management', ast.stack?.state_management, 'string', 'original', 1);
|
|
421
|
+
}
|
|
422
|
+
if (isBackendProject) {
|
|
423
|
+
this.addSlot(slots, 'stack.backend', ast.stack?.backend, 'string', 'original', 1);
|
|
424
|
+
this.addSlot(slots, 'stack.api_type', ast.stack?.api_type, 'string', 'original', 1);
|
|
425
|
+
this.addSlot(slots, 'stack.runtime', ast.stack?.runtime, 'string', 'original', 1);
|
|
426
|
+
this.addSlot(slots, 'stack.database', ast.stack?.database, 'string', 'original', 1);
|
|
427
|
+
this.addSlot(slots, 'stack.connection', ast.stack?.connection, 'string', 'original', 1);
|
|
428
|
+
}
|
|
429
|
+
// Universal stack slots (apply to all project types)
|
|
430
|
+
this.addSlot(slots, 'stack.hosting', ast.stack?.hosting, 'string', 'original', 1);
|
|
431
|
+
this.addSlot(slots, 'stack.build', ast.stack?.build, 'string', 'original', 1);
|
|
432
|
+
this.addSlot(slots, 'stack.cicd', ast.stack?.cicd, 'string', 'original', 1);
|
|
433
|
+
// Human context slots (6) - always applicable
|
|
434
|
+
this.addSlot(slots, 'human.who', ast.human_context?.who, 'string', 'original', 1);
|
|
435
|
+
this.addSlot(slots, 'human.what', ast.human_context?.what, 'string', 'original', 1);
|
|
436
|
+
this.addSlot(slots, 'human.why', ast.human_context?.why, 'string', 'original', 1);
|
|
437
|
+
this.addSlot(slots, 'human.where', ast.human_context?.where, 'string', 'original', 1);
|
|
438
|
+
this.addSlot(slots, 'human.when', ast.human_context?.when, 'string', 'original', 1);
|
|
439
|
+
this.addSlot(slots, 'human.how', ast.human_context?.how, 'string', 'original', 1);
|
|
440
|
+
// Discovered slots (if any)
|
|
441
|
+
if (ast._discovered) {
|
|
442
|
+
const discovered = ast._discovered;
|
|
443
|
+
const mapping = {
|
|
444
|
+
'projectName': 'project.name',
|
|
445
|
+
'mainLanguage': 'project.main_language',
|
|
446
|
+
'framework': 'stack.frontend',
|
|
447
|
+
'database': 'stack.database',
|
|
448
|
+
'backend': 'stack.backend',
|
|
449
|
+
'hosting': 'stack.hosting',
|
|
450
|
+
'buildTool': 'stack.build'
|
|
451
|
+
};
|
|
452
|
+
for (const [key, path] of Object.entries(mapping)) {
|
|
453
|
+
if (discovered[key] && !this.hasValue(ast, path)) {
|
|
454
|
+
this.addSlot(slots, `discovery.${path}`, discovered[key], 'string', 'discovered', 1);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return slots;
|
|
459
|
+
}
|
|
460
|
+
addSlot(slots, path, value, type, source, weight) {
|
|
461
|
+
// Deduplication: Check if slot path already exists
|
|
462
|
+
const exists = slots.some(slot => slot.path === path);
|
|
463
|
+
if (exists) {
|
|
464
|
+
if (process.env.FAF_DEBUG) {
|
|
465
|
+
console.log(`[DEBUG] Skipped duplicate slot: ${path}`);
|
|
466
|
+
}
|
|
467
|
+
return; // Skip duplicate slot
|
|
468
|
+
}
|
|
469
|
+
const filled = this.isSlotFilled(value);
|
|
470
|
+
if (process.env.FAF_DEBUG) {
|
|
471
|
+
const valueStr = JSON.stringify(value) || '';
|
|
472
|
+
console.log(`[DEBUG] Added slot: ${path} | filled: ${filled} | value: ${valueStr.substring(0, 50)}`);
|
|
473
|
+
}
|
|
474
|
+
slots.push({
|
|
475
|
+
id: `slot_${slots.length + 1}`,
|
|
476
|
+
path,
|
|
477
|
+
value,
|
|
478
|
+
type,
|
|
479
|
+
source,
|
|
480
|
+
weight,
|
|
481
|
+
filled
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
isSlotFilled(value) {
|
|
485
|
+
// Handle null, undefined, false explicitly
|
|
486
|
+
if (value === null || value === undefined || value === false)
|
|
487
|
+
return false;
|
|
488
|
+
if (typeof value === 'string') {
|
|
489
|
+
// Also treat string representations of null/undefined as empty
|
|
490
|
+
const empty = ['', 'None', 'Unknown', 'Not specified', 'N/A', 'null', 'undefined', '~'];
|
|
491
|
+
// Also reject generic placeholders that provide no real context
|
|
492
|
+
const genericPlaceholders = [
|
|
493
|
+
'Development teams',
|
|
494
|
+
'Software development solution',
|
|
495
|
+
'Improve development efficiency',
|
|
496
|
+
'Cloud platform',
|
|
497
|
+
'Ongoing development',
|
|
498
|
+
'Modern development practices',
|
|
499
|
+
'Development teams building next-generation software',
|
|
500
|
+
'AI-powered development infrastructure with trust-driven workflows'
|
|
501
|
+
];
|
|
502
|
+
const trimmed = value.trim();
|
|
503
|
+
return !empty.includes(trimmed) && !genericPlaceholders.includes(trimmed);
|
|
504
|
+
}
|
|
505
|
+
if (typeof value === 'number') {
|
|
506
|
+
// Numbers are valid unless they're NaN or Infinity
|
|
507
|
+
return !isNaN(value) && isFinite(value);
|
|
508
|
+
}
|
|
509
|
+
if (typeof value === 'object') {
|
|
510
|
+
// Arrays and objects need content
|
|
511
|
+
if (Array.isArray(value))
|
|
512
|
+
return value.length > 0;
|
|
513
|
+
return Object.keys(value).length > 0;
|
|
514
|
+
}
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
hasValue(ast, path) {
|
|
518
|
+
const parts = path.split('.');
|
|
519
|
+
let current = ast;
|
|
520
|
+
for (const part of parts) {
|
|
521
|
+
if (!current || !current[part])
|
|
522
|
+
return false;
|
|
523
|
+
current = current[part];
|
|
524
|
+
}
|
|
525
|
+
return this.isSlotFilled(current);
|
|
526
|
+
}
|
|
527
|
+
detectProjectTypeFromContext(ast) {
|
|
528
|
+
// Check for explicit project type
|
|
529
|
+
if (ast.project?.type)
|
|
530
|
+
return ast.project.type;
|
|
531
|
+
// Infer from goal and context
|
|
532
|
+
const goal = (ast.project?.goal || '').toLowerCase();
|
|
533
|
+
const what = (ast.human_context?.what || '').toLowerCase();
|
|
534
|
+
const mainLanguage = (ast.project?.main_language || '').toLowerCase();
|
|
535
|
+
// CLI tool indicators (check BEFORE Chrome Extension to avoid false positives)
|
|
536
|
+
if (goal.includes('cli') || what.includes('cli') ||
|
|
537
|
+
goal.includes('command line') || what.includes('command line')) {
|
|
538
|
+
return 'cli-tool';
|
|
539
|
+
}
|
|
540
|
+
// Chrome Extension detection with fuzzy matching (only if not CLI)
|
|
541
|
+
const goalDetection = chrome_extension_detector_1.ChromeExtensionDetector.detect(goal);
|
|
542
|
+
const whatDetection = chrome_extension_detector_1.ChromeExtensionDetector.detect(what);
|
|
543
|
+
if (goalDetection.detected || whatDetection.detected ||
|
|
544
|
+
ast.stack?.framework === 'Chrome Extension') {
|
|
545
|
+
return 'chrome-extension';
|
|
546
|
+
}
|
|
547
|
+
// Library indicators
|
|
548
|
+
if (goal.includes('library') || what.includes('library') ||
|
|
549
|
+
goal.includes('package') || what.includes('npm package')) {
|
|
550
|
+
return 'library';
|
|
551
|
+
}
|
|
552
|
+
// API/Backend indicators
|
|
553
|
+
if (goal.includes('api') || what.includes('api') ||
|
|
554
|
+
goal.includes('backend') || what.includes('backend') ||
|
|
555
|
+
ast.stack?.backend && !ast.stack?.frontend) {
|
|
556
|
+
return 'backend-api';
|
|
557
|
+
}
|
|
558
|
+
// Frontend indicators
|
|
559
|
+
if (ast.stack?.frontend || ast.stack?.css_framework || ast.stack?.ui_library) {
|
|
560
|
+
return ast.stack?.backend ? 'fullstack' : 'frontend';
|
|
561
|
+
}
|
|
562
|
+
// Language-based defaults
|
|
563
|
+
if (mainLanguage === 'python') {
|
|
564
|
+
if (ast.stack?.frontend)
|
|
565
|
+
return 'fullstack';
|
|
566
|
+
return 'python-app'; // Could be CLI, API, or data science
|
|
567
|
+
}
|
|
568
|
+
return 'generic';
|
|
569
|
+
}
|
|
570
|
+
requiresFrontendStack(projectType) {
|
|
571
|
+
const frontendTypes = ['frontend', 'fullstack', 'svelte', 'react', 'vue', 'angular'];
|
|
572
|
+
// Chrome extensions don't need traditional frontend stack
|
|
573
|
+
return frontendTypes.includes(projectType) && projectType !== 'chrome-extension';
|
|
574
|
+
}
|
|
575
|
+
requiresBackendStack(projectType) {
|
|
576
|
+
const backendTypes = ['backend-api', 'fullstack', 'cli-tool', 'library', 'python-app', 'node-api'];
|
|
577
|
+
// Chrome extensions don't need traditional backend stack
|
|
578
|
+
return backendTypes.includes(projectType) && projectType !== 'chrome-extension';
|
|
579
|
+
}
|
|
580
|
+
calculateSlots(ir) {
|
|
581
|
+
const sections = {
|
|
582
|
+
project: { filled: 0, total: 0, slots: [] },
|
|
583
|
+
stack: { filled: 0, total: 0, slots: [] },
|
|
584
|
+
human: { filled: 0, total: 0, slots: [] },
|
|
585
|
+
discovery: { filled: 0, total: 0, slots: [] }
|
|
586
|
+
};
|
|
587
|
+
for (const slot of ir) {
|
|
588
|
+
const [section] = slot.path.split('.');
|
|
589
|
+
const sectionKey = section === 'human' ? 'human' :
|
|
590
|
+
section === 'discovery' ? 'discovery' :
|
|
591
|
+
section === 'project' ? 'project' : 'stack';
|
|
592
|
+
const sec = sections[sectionKey];
|
|
593
|
+
sec.total++;
|
|
594
|
+
if (slot.filled)
|
|
595
|
+
sec.filled++;
|
|
596
|
+
sec.slots.push({
|
|
597
|
+
id: slot.id,
|
|
598
|
+
value: slot.value,
|
|
599
|
+
filled: slot.filled,
|
|
600
|
+
source: slot.source,
|
|
601
|
+
points: slot.filled ? slot.weight : 0
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
// Calculate percentages
|
|
605
|
+
const breakdown = {
|
|
606
|
+
project: {
|
|
607
|
+
...sections.project,
|
|
608
|
+
percentage: sections.project.total > 0
|
|
609
|
+
? Math.round((sections.project.filled / sections.project.total) * 100)
|
|
610
|
+
: 0
|
|
611
|
+
},
|
|
612
|
+
stack: {
|
|
613
|
+
...sections.stack,
|
|
614
|
+
percentage: sections.stack.total > 0
|
|
615
|
+
? Math.round((sections.stack.filled / sections.stack.total) * 100)
|
|
616
|
+
: 0
|
|
617
|
+
},
|
|
618
|
+
human: {
|
|
619
|
+
...sections.human,
|
|
620
|
+
percentage: sections.human.total > 0
|
|
621
|
+
? Math.round((sections.human.filled / sections.human.total) * 100)
|
|
622
|
+
: 0
|
|
623
|
+
},
|
|
624
|
+
discovery: {
|
|
625
|
+
...sections.discovery,
|
|
626
|
+
percentage: sections.discovery.total > 0
|
|
627
|
+
? Math.round((sections.discovery.filled / sections.discovery.total) * 100)
|
|
628
|
+
: 0
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
const filled = Object.values(sections).reduce((sum, sec) => sum + sec.filled, 0);
|
|
632
|
+
const total = Object.values(sections).reduce((sum, sec) => sum + sec.total, 0);
|
|
633
|
+
return { filled, total, breakdown };
|
|
634
|
+
}
|
|
635
|
+
calculateScore(slots) {
|
|
636
|
+
if (slots.total === 0)
|
|
637
|
+
return 0;
|
|
638
|
+
return (slots.filled / slots.total) * 100;
|
|
639
|
+
}
|
|
640
|
+
// ============================================================================
|
|
641
|
+
// Utilities
|
|
642
|
+
// ============================================================================
|
|
643
|
+
calculateChecksum(result) {
|
|
644
|
+
const data = JSON.stringify({
|
|
645
|
+
score: result.score,
|
|
646
|
+
filled: result.filled,
|
|
647
|
+
total: result.total,
|
|
648
|
+
version: FafCompiler.VERSION
|
|
649
|
+
});
|
|
650
|
+
return crypto.createHash('md5').update(data).digest('hex').slice(0, 8);
|
|
651
|
+
}
|
|
652
|
+
recordPass(name, startTime, input, output, changes) {
|
|
653
|
+
this.trace.passes.push({
|
|
654
|
+
name,
|
|
655
|
+
duration: Date.now() - startTime,
|
|
656
|
+
input: this.sanitize(input),
|
|
657
|
+
output: this.sanitize(output),
|
|
658
|
+
changes
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
sanitize(data) {
|
|
662
|
+
// Limit size for trace
|
|
663
|
+
const str = JSON.stringify(data);
|
|
664
|
+
if (str.length > 1000) {
|
|
665
|
+
return { _truncated: true, size: str.length };
|
|
666
|
+
}
|
|
667
|
+
return data;
|
|
668
|
+
}
|
|
669
|
+
addDiagnostic(severity, message, location, suggestion) {
|
|
670
|
+
this.diagnostics.push({ severity, message, location, suggestion });
|
|
671
|
+
}
|
|
672
|
+
// ============================================================================
|
|
673
|
+
// Public API
|
|
674
|
+
// ============================================================================
|
|
675
|
+
/**
|
|
676
|
+
* Compile with trace output
|
|
677
|
+
*/
|
|
678
|
+
async compileWithTrace(fafPath) {
|
|
679
|
+
const result = await this.compile(fafPath);
|
|
680
|
+
this.printTrace(result);
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Verify a checksum
|
|
685
|
+
*/
|
|
686
|
+
async verify(fafPath, checksum) {
|
|
687
|
+
const result = await this.compile(fafPath);
|
|
688
|
+
return result.checksum === checksum;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Get intermediate representation
|
|
692
|
+
*/
|
|
693
|
+
async getIR(fafPath) {
|
|
694
|
+
const result = await this.compile(fafPath);
|
|
695
|
+
return result.ir;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Print diagnostic report
|
|
699
|
+
*/
|
|
700
|
+
printDiagnostics() {
|
|
701
|
+
if (this.diagnostics.length === 0) {
|
|
702
|
+
console.log('✓ No issues found');
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const byType = {
|
|
706
|
+
error: this.diagnostics.filter(d => d.severity === 'error'),
|
|
707
|
+
warning: this.diagnostics.filter(d => d.severity === 'warning'),
|
|
708
|
+
info: this.diagnostics.filter(d => d.severity === 'info')
|
|
709
|
+
};
|
|
710
|
+
if (byType.error.length > 0) {
|
|
711
|
+
console.log(`\n❌ ${byType.error.length} Errors:`);
|
|
712
|
+
byType.error.forEach(d => {
|
|
713
|
+
console.log(` ${d.message}`);
|
|
714
|
+
if (d.suggestion)
|
|
715
|
+
console.log(` → ${d.suggestion}`);
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
if (byType.warning.length > 0) {
|
|
719
|
+
console.log(`\n⚠️ ${byType.warning.length} Warnings:`);
|
|
720
|
+
byType.warning.forEach(d => {
|
|
721
|
+
console.log(` ${d.message}`);
|
|
722
|
+
if (d.suggestion)
|
|
723
|
+
console.log(` → ${d.suggestion}`);
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
if (byType.info.length > 0) {
|
|
727
|
+
console.log(`\nℹ️ ${byType.info.length} Info:`);
|
|
728
|
+
byType.info.forEach(d => {
|
|
729
|
+
console.log(` ${d.message}`);
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Print compilation trace
|
|
735
|
+
*/
|
|
736
|
+
printTrace(result) {
|
|
737
|
+
console.log('\n📊 FAF Compilation Trace');
|
|
738
|
+
console.log('═'.repeat(60));
|
|
739
|
+
console.log(`Version: ${result.trace.version}`);
|
|
740
|
+
console.log(`Input Hash: ${result.trace.inputHash}`);
|
|
741
|
+
console.log(`Checksum: ${result.checksum}`);
|
|
742
|
+
console.log();
|
|
743
|
+
console.log('Compilation Passes:');
|
|
744
|
+
result.trace.passes.forEach((pass, i) => {
|
|
745
|
+
console.log(` ${i + 1}. ${pass.name.toUpperCase()} (${pass.duration}ms)`);
|
|
746
|
+
pass.changes.forEach(change => {
|
|
747
|
+
console.log(` → ${change}`);
|
|
748
|
+
});
|
|
749
|
+
});
|
|
750
|
+
console.log();
|
|
751
|
+
console.log('Result:');
|
|
752
|
+
console.log(` Score: ${result.score}%`);
|
|
753
|
+
console.log(` Filled: ${result.filled}/${result.total} slots`);
|
|
754
|
+
console.log();
|
|
755
|
+
console.log('Breakdown:');
|
|
756
|
+
console.log(` Project: ${result.breakdown.project.filled}/${result.breakdown.project.total} (${result.breakdown.project.percentage}%)`);
|
|
757
|
+
console.log(` Stack: ${result.breakdown.stack.filled}/${result.breakdown.stack.total} (${result.breakdown.stack.percentage}%)`);
|
|
758
|
+
console.log(` Human: ${result.breakdown.human.filled}/${result.breakdown.human.total} (${result.breakdown.human.percentage}%)`);
|
|
759
|
+
if (result.breakdown.discovery.total > 0) {
|
|
760
|
+
console.log(` Discovery: ${result.breakdown.discovery.filled}/${result.breakdown.discovery.total} (${result.breakdown.discovery.percentage}%)`);
|
|
761
|
+
}
|
|
762
|
+
if (result.diagnostics.length > 0) {
|
|
763
|
+
console.log();
|
|
764
|
+
this.printDiagnostics();
|
|
765
|
+
}
|
|
766
|
+
console.log('═'.repeat(60));
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
exports.FafCompiler = FafCompiler;
|
|
770
|
+
// ============================================================================
|
|
771
|
+
// Export convenience functions
|
|
772
|
+
// ============================================================================
|
|
773
|
+
/**
|
|
774
|
+
* Compile a .faf file
|
|
775
|
+
*/
|
|
776
|
+
async function compile(fafPath) {
|
|
777
|
+
const compiler = new FafCompiler();
|
|
778
|
+
return compiler.compile(fafPath);
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Compile with trace output
|
|
782
|
+
*/
|
|
783
|
+
async function compileWithTrace(fafPath) {
|
|
784
|
+
const compiler = new FafCompiler();
|
|
785
|
+
return compiler.compileWithTrace(fafPath);
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Verify a checksum
|
|
789
|
+
*/
|
|
790
|
+
async function verify(fafPath, checksum) {
|
|
791
|
+
const compiler = new FafCompiler();
|
|
792
|
+
return compiler.verify(fafPath, checksum);
|
|
793
|
+
}
|
|
794
|
+
//# sourceMappingURL=faf-compiler.js.map
|