faf-mcp 1.3.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/CLAUDE.md +5 -5
  3. package/README.md +70 -14
  4. package/assets/thumbnail.png +0 -0
  5. package/dist/src/faf-core/commands/agents.d.ts +29 -0
  6. package/dist/src/faf-core/commands/agents.js +140 -0
  7. package/dist/src/faf-core/commands/agents.js.map +1 -0
  8. package/dist/src/faf-core/commands/bi-sync.d.ts +9 -5
  9. package/dist/src/faf-core/commands/bi-sync.js +89 -85
  10. package/dist/src/faf-core/commands/bi-sync.js.map +1 -1
  11. package/dist/src/faf-core/commands/conductor.d.ts +25 -0
  12. package/dist/src/faf-core/commands/conductor.js +157 -0
  13. package/dist/src/faf-core/commands/conductor.js.map +1 -0
  14. package/dist/src/faf-core/commands/cursor.d.ts +29 -0
  15. package/dist/src/faf-core/commands/cursor.js +134 -0
  16. package/dist/src/faf-core/commands/cursor.js.map +1 -0
  17. package/dist/src/faf-core/commands/gemini.d.ts +29 -0
  18. package/dist/src/faf-core/commands/gemini.js +133 -0
  19. package/dist/src/faf-core/commands/gemini.js.map +1 -0
  20. package/dist/src/faf-core/commands/git-context.d.ts +23 -0
  21. package/dist/src/faf-core/commands/git-context.js +66 -0
  22. package/dist/src/faf-core/commands/git-context.js.map +1 -0
  23. package/dist/src/faf-core/commands/human.d.ts +27 -0
  24. package/dist/src/faf-core/commands/human.js +183 -0
  25. package/dist/src/faf-core/commands/human.js.map +1 -0
  26. package/dist/src/faf-core/commands/readme.d.ts +47 -0
  27. package/dist/src/faf-core/commands/readme.js +361 -0
  28. package/dist/src/faf-core/commands/readme.js.map +1 -0
  29. package/dist/src/faf-core/engines/fab-formats-processor.js +3 -3
  30. package/dist/src/faf-core/engines/fab-formats-processor.js.map +1 -1
  31. package/dist/src/faf-core/engines/faf-dna.d.ts +1 -1
  32. package/dist/src/faf-core/engines/faf-dna.js +4 -3
  33. package/dist/src/faf-core/engines/faf-dna.js.map +1 -1
  34. package/dist/src/faf-core/generators/faf-generator-championship.js +9 -8
  35. package/dist/src/faf-core/generators/faf-generator-championship.js.map +1 -1
  36. package/dist/src/faf-core/parsers/agents-parser.d.ts +59 -0
  37. package/dist/src/faf-core/parsers/agents-parser.js +324 -0
  38. package/dist/src/faf-core/parsers/agents-parser.js.map +1 -0
  39. package/dist/src/faf-core/parsers/conductor-parser.d.ts +85 -0
  40. package/dist/src/faf-core/parsers/conductor-parser.js +293 -0
  41. package/dist/src/faf-core/parsers/conductor-parser.js.map +1 -0
  42. package/dist/src/faf-core/parsers/cursorrules-parser.d.ts +57 -0
  43. package/dist/src/faf-core/parsers/cursorrules-parser.js +317 -0
  44. package/dist/src/faf-core/parsers/cursorrules-parser.js.map +1 -0
  45. package/dist/src/faf-core/parsers/faf-git-generator.d.ts +53 -0
  46. package/dist/src/faf-core/parsers/faf-git-generator.js +512 -0
  47. package/dist/src/faf-core/parsers/faf-git-generator.js.map +1 -0
  48. package/dist/src/faf-core/parsers/gemini-parser.d.ts +57 -0
  49. package/dist/src/faf-core/parsers/gemini-parser.js +263 -0
  50. package/dist/src/faf-core/parsers/gemini-parser.js.map +1 -0
  51. package/dist/src/faf-core/parsers/github-extractor.d.ts +61 -0
  52. package/dist/src/faf-core/parsers/github-extractor.js +374 -0
  53. package/dist/src/faf-core/parsers/github-extractor.js.map +1 -0
  54. package/dist/src/faf-core/parsers/index.d.ts +12 -0
  55. package/dist/src/{compiler → faf-core/parsers}/index.js +10 -5
  56. package/dist/src/faf-core/parsers/index.js.map +1 -0
  57. package/dist/src/faf-core/parsers/slot-counter.d.ts +55 -0
  58. package/dist/src/faf-core/parsers/slot-counter.js +100 -0
  59. package/dist/src/faf-core/parsers/slot-counter.js.map +1 -0
  60. package/dist/src/faf-core/utils/balance-visualizer.js +3 -3
  61. package/dist/src/faf-core/utils/balance-visualizer.js.map +1 -1
  62. package/dist/src/handlers/championship-tools.d.ts +10 -5
  63. package/dist/src/handlers/championship-tools.js +259 -232
  64. package/dist/src/handlers/championship-tools.js.map +1 -1
  65. package/dist/src/handlers/engine-adapter.js +238 -5
  66. package/dist/src/handlers/engine-adapter.js.map +1 -1
  67. package/dist/src/handlers/tools.d.ts +61 -0
  68. package/dist/src/handlers/tools.js +2103 -102
  69. package/dist/src/handlers/tools.js.map +1 -1
  70. package/dist/src/index.js +0 -0
  71. package/dist/src/types/tool-visibility.js +51 -10
  72. package/dist/src/types/tool-visibility.js.map +1 -1
  73. package/dist/src/utils/championship-format.js +11 -9
  74. package/dist/src/utils/championship-format.js.map +1 -1
  75. package/dist/src/utils/path-resolver.js +56 -2
  76. package/dist/src/utils/path-resolver.js.map +1 -1
  77. package/dist/src/utils/visual-style.js +7 -5
  78. package/dist/src/utils/visual-style.js.map +1 -1
  79. package/dist/src/version.js +24 -11
  80. package/dist/src/version.js.map +1 -1
  81. package/package.json +5 -3
  82. package/project.faf +14 -18
  83. package/dist/index.json +0 -1
  84. package/dist/src/compiler/index.d.ts +0 -7
  85. package/dist/src/compiler/index.js.map +0 -1
  86. package/dist/src/compiler/scorer.d.ts +0 -53
  87. package/dist/src/compiler/scorer.js +0 -189
  88. package/dist/src/compiler/scorer.js.map +0 -1
  89. package/dist/src/compiler/slot-validator.d.ts +0 -32
  90. package/dist/src/compiler/slot-validator.js +0 -293
  91. package/dist/src/compiler/slot-validator.js.map +0 -1
  92. package/dist/src/compiler/type-detector.d.ts +0 -62
  93. package/dist/src/compiler/type-detector.js +0 -388
  94. package/dist/src/compiler/type-detector.js.map +0 -1
  95. package/dist/src/types/project-types.d.ts +0 -22
  96. package/dist/src/types/project-types.js +0 -85
  97. package/dist/src/types/project-types.js.map +0 -1
  98. package/dist/src/types/slots.d.ts +0 -39
  99. package/dist/src/types/slots.js +0 -162
  100. package/dist/src/types/slots.js.map +0 -1
@@ -36,7 +36,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FafToolHandler = void 0;
37
37
  const fileHandler_1 = require("./fileHandler");
38
38
  const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
39
+ const os = __importStar(require("os"));
40
+ const pathModule = __importStar(require("path"));
40
41
  const fuzzy_detector_1 = require("../utils/fuzzy-detector");
41
42
  const faf_file_finder_js_1 = require("../utils/faf-file-finder.js");
42
43
  const version_1 = require("../version");
@@ -46,105 +47,200 @@ class FafToolHandler {
46
47
  constructor(engineAdapter) {
47
48
  this.engineAdapter = engineAdapter;
48
49
  }
50
+ /**
51
+ * Get the project path - uses explicit path if provided, otherwise returns current context
52
+ * If an explicit path is provided, it also sets the session context for subsequent calls
53
+ */
54
+ getProjectPath(explicitPath) {
55
+ if (explicitPath) {
56
+ // Expand tilde
57
+ const expandedPath = explicitPath.startsWith('~')
58
+ ? pathModule.join(os.homedir(), explicitPath.slice(1))
59
+ : explicitPath;
60
+ // Resolve to absolute path
61
+ const resolvedPath = pathModule.resolve(expandedPath);
62
+ // If it's a file path, get the directory
63
+ const projectDir = fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()
64
+ ? pathModule.dirname(resolvedPath)
65
+ : resolvedPath;
66
+ // Set as the new session context
67
+ if (fs.existsSync(projectDir)) {
68
+ this.engineAdapter.setWorkingDirectory(projectDir);
69
+ }
70
+ return projectDir;
71
+ }
72
+ return this.engineAdapter.getWorkingDirectory();
73
+ }
49
74
  async listTools() {
50
75
  return {
51
76
  tools: [
52
77
  {
53
78
  name: 'faf_about',
54
- description: 'Learn what .faf format is - THE JPEG for AI 🧡⚡️',
79
+ description: 'Learn what .faf format is - project DNA for AI 🧡⚡️',
80
+ annotations: {
81
+ title: 'About FAF',
82
+ readOnlyHint: true,
83
+ openWorldHint: false
84
+ },
55
85
  inputSchema: {
56
86
  type: 'object',
57
87
  properties: {},
88
+ additionalProperties: false
58
89
  }
59
90
  },
60
91
  {
61
92
  name: 'faf_what',
62
- description: 'What is .faf format? Quick explanation of THE JPEG for AI 🧡⚡️',
93
+ description: 'What is .faf format? Quick explanation of project DNA for AI 🧡⚡️',
94
+ annotations: {
95
+ title: 'What is FAF',
96
+ readOnlyHint: true,
97
+ openWorldHint: false
98
+ },
63
99
  inputSchema: {
64
100
  type: 'object',
65
101
  properties: {},
102
+ additionalProperties: false
66
103
  }
67
104
  },
68
105
  {
69
106
  name: 'faf_status',
70
- description: 'Check if your project has project.faf (THE JPEG for AI) - Shows AI-readability status 🧡⚡️',
107
+ description: 'Check if your project has project.faf (project DNA for AI) - Shows AI-readability status 🧡⚡️',
108
+ annotations: {
109
+ title: 'Project Status',
110
+ readOnlyHint: true,
111
+ openWorldHint: false
112
+ },
71
113
  inputSchema: {
72
114
  type: 'object',
73
- properties: {},
115
+ properties: {
116
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
117
+ },
118
+ additionalProperties: false
74
119
  }
75
120
  },
76
121
  {
77
122
  name: 'faf_score',
78
- description: 'Calculate your project\'s AI-readability from project.faf (THE JPEG for AI) - F1-inspired metrics! 🧡⚡️',
123
+ description: 'Calculate your project\'s AI-readability from project.faf (project DNA for AI) - F1-inspired metrics! 🧡⚡️',
124
+ annotations: {
125
+ title: 'AI-Readiness Score',
126
+ readOnlyHint: true,
127
+ openWorldHint: false
128
+ },
79
129
  inputSchema: {
80
130
  type: 'object',
81
131
  properties: {
82
- details: { type: 'boolean', description: 'Include detailed breakdown and improvement suggestions' }
132
+ details: { type: 'boolean', description: 'Include detailed breakdown and improvement suggestions' },
133
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
83
134
  },
135
+ additionalProperties: false
84
136
  }
85
137
  },
86
138
  {
87
139
  name: 'faf_init',
88
- description: 'Create project.faf (THE JPEG for AI) - Makes your project instantly AI-readable 🧡⚡️. Use Projects convention (~/Projects/[name]/project.faf) by default. Run faf_guide for path resolution rules.',
140
+ description: 'Create project.faf (project DNA for AI) - Makes your project instantly AI-readable 🧡⚡️. Just enter path or project name. Examples: ~/Projects/my-app, my-app, /full/path/to/project',
141
+ annotations: {
142
+ title: 'Initialize .faf',
143
+ readOnlyHint: false,
144
+ destructiveHint: false,
145
+ openWorldHint: false
146
+ },
89
147
  inputSchema: {
90
148
  type: 'object',
91
149
  properties: {
92
- force: { type: 'boolean', description: 'Force reinitialize existing FAF context' },
93
- directory: { type: 'string', description: 'Project directory path (supports ~ tilde expansion). Creates directory if it doesn\'t exist.' },
94
- path: { type: 'string', description: 'Alias for directory parameter' },
95
- projectName: { type: 'string', description: 'Project name for path inference (used with Projects convention)' }
150
+ path: {
151
+ type: 'string',
152
+ description: 'Project path or name. Smart resolution: "my-app" finds ~/Projects/my-app OR ~/Code/my-app. Full paths like ~/Projects/app or /Users/me/code/app work too. Omit to use current directory.'
153
+ },
154
+ force: { type: 'boolean', description: 'Overwrite existing project.faf if it exists' }
96
155
  },
156
+ additionalProperties: false
97
157
  }
98
158
  },
99
159
  {
100
160
  name: 'faf_trust',
101
- description: 'Validate project.faf integrity - Trust metrics for THE JPEG for AI 🧡⚡️',
161
+ description: 'Validate project.faf integrity - Trust metrics for project DNA for AI 🧡⚡️',
162
+ annotations: {
163
+ title: 'Trust Score',
164
+ readOnlyHint: true,
165
+ openWorldHint: false
166
+ },
102
167
  inputSchema: {
103
168
  type: 'object',
104
169
  properties: {},
170
+ additionalProperties: false
105
171
  }
106
172
  },
107
173
  {
108
174
  name: 'faf_sync',
109
- description: 'Sync project.faf (THE JPEG for AI) with CLAUDE.md - Bi-directional context 🧡⚡️',
175
+ description: 'Sync project.faf (project DNA for AI) with CLAUDE.md - Bi-directional context 🧡⚡️',
176
+ annotations: {
177
+ title: 'Sync .faf to CLAUDE.md',
178
+ readOnlyHint: false,
179
+ destructiveHint: false,
180
+ openWorldHint: false
181
+ },
110
182
  inputSchema: {
111
183
  type: 'object',
112
- properties: {},
184
+ properties: {
185
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
186
+ },
187
+ additionalProperties: false
113
188
  }
114
189
  },
115
190
  {
116
191
  name: 'faf_enhance',
117
- description: 'Enhance project.faf (THE JPEG for AI) with AI optimization - SPEEDY AI you can TRUST! 🧡⚡️',
192
+ description: 'Enhance project.faf (project DNA for AI) with AI optimization - SPEEDY AI you can TRUST! 🧡⚡️',
193
+ annotations: {
194
+ title: 'Enhance .faf',
195
+ readOnlyHint: false,
196
+ destructiveHint: false,
197
+ openWorldHint: false
198
+ },
118
199
  inputSchema: {
119
200
  type: 'object',
120
201
  properties: {
121
202
  model: { type: 'string', description: 'Target AI model: claude|chatgpt|gemini|universal (default: claude)' },
122
203
  focus: { type: 'string', description: 'Enhancement focus: claude-optimal|human-context|ai-instructions|completeness' },
123
204
  consensus: { type: 'boolean', description: 'Build consensus from multiple AI models' },
124
- dryRun: { type: 'boolean', description: 'Preview enhancement without applying changes' }
205
+ dryRun: { type: 'boolean', description: 'Preview enhancement without applying changes' },
206
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
125
207
  },
208
+ additionalProperties: false
126
209
  }
127
210
  },
128
211
  {
129
212
  name: 'faf_bi_sync',
130
- description: 'Platform-aware sync: project.faf to .cursorrules, .clinerules, .windsurfrules, CLAUDE.md',
213
+ description: 'Bi-directional sync between project.faf and CLAUDE.md. v4.5.0: Also sync to AGENTS.md, .cursorrules, GEMINI.md!',
214
+ annotations: {
215
+ title: 'Bi-directional Sync',
216
+ readOnlyHint: false,
217
+ destructiveHint: false,
218
+ openWorldHint: false
219
+ },
131
220
  inputSchema: {
132
221
  type: 'object',
133
222
  properties: {
134
223
  auto: { type: 'boolean', description: 'Enable automatic synchronization' },
135
224
  watch: { type: 'boolean', description: 'Start real-time file watching for changes' },
136
225
  force: { type: 'boolean', description: 'Force overwrite conflicting changes' },
137
- target: {
138
- type: 'string',
139
- enum: ['auto', '.clinerules', '.cursorrules', '.windsurfrules', 'CLAUDE.md', 'all'],
140
- description: 'Sync target: auto (detect existing), specific platform, or all'
141
- }
226
+ agents: { type: 'boolean', description: 'Also sync to AGENTS.md (OpenAI/Codex format)' },
227
+ cursor: { type: 'boolean', description: 'Also sync to .cursorrules (Cursor IDE format)' },
228
+ gemini: { type: 'boolean', description: 'Also sync to GEMINI.md (Google Gemini format)' },
229
+ all: { type: 'boolean', description: 'Sync to ALL formats: CLAUDE.md + AGENTS.md + .cursorrules + GEMINI.md' },
230
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
142
231
  },
232
+ additionalProperties: false
143
233
  }
144
234
  },
145
235
  {
146
236
  name: 'faf_clear',
147
237
  description: 'Clear caches, temporary files, and reset FAF state for a fresh start',
238
+ annotations: {
239
+ title: 'Clear .faf Data',
240
+ readOnlyHint: false,
241
+ destructiveHint: true,
242
+ openWorldHint: false
243
+ },
148
244
  inputSchema: {
149
245
  type: 'object',
150
246
  properties: {
@@ -153,19 +249,31 @@ class FafToolHandler {
153
249
  backups: { type: 'boolean', description: 'Clear backup files only' },
154
250
  all: { type: 'boolean', description: 'Clear everything (default)' }
155
251
  },
252
+ additionalProperties: false
156
253
  }
157
254
  },
158
255
  {
159
256
  name: 'faf_debug',
160
257
  description: 'Debug Claude FAF MCP environment - show working directory, permissions, and FAF CLI status',
258
+ annotations: {
259
+ title: 'Debug Info',
260
+ readOnlyHint: true,
261
+ openWorldHint: false
262
+ },
161
263
  inputSchema: {
162
264
  type: 'object',
163
265
  properties: {},
266
+ additionalProperties: false
164
267
  }
165
268
  },
166
269
  {
167
270
  name: 'faf_read',
168
271
  description: 'Read content from any file on the local filesystem',
272
+ annotations: {
273
+ title: 'Read .faf File',
274
+ readOnlyHint: true,
275
+ openWorldHint: false
276
+ },
169
277
  inputSchema: {
170
278
  type: 'object',
171
279
  properties: {
@@ -175,11 +283,18 @@ class FafToolHandler {
175
283
  }
176
284
  },
177
285
  required: ['path'],
286
+ additionalProperties: false
178
287
  }
179
288
  },
180
289
  {
181
290
  name: 'faf_write',
182
291
  description: 'Write content to any file on the local filesystem',
292
+ annotations: {
293
+ title: 'Write .faf File',
294
+ readOnlyHint: false,
295
+ destructiveHint: false,
296
+ openWorldHint: false
297
+ },
183
298
  inputSchema: {
184
299
  type: 'object',
185
300
  properties: {
@@ -193,11 +308,17 @@ class FafToolHandler {
193
308
  }
194
309
  },
195
310
  required: ['path', 'content'],
311
+ additionalProperties: false
196
312
  }
197
313
  },
198
314
  {
199
315
  name: 'faf_list',
200
316
  description: 'List directories and discover projects with project.faf files - Essential for FAF discovery workflow',
317
+ annotations: {
318
+ title: 'List .faf Files',
319
+ readOnlyHint: true,
320
+ openWorldHint: false
321
+ },
201
322
  inputSchema: {
202
323
  type: 'object',
203
324
  properties: {
@@ -221,19 +342,31 @@ class FafToolHandler {
221
342
  }
222
343
  },
223
344
  required: ['path'],
345
+ additionalProperties: false
224
346
  }
225
347
  },
226
348
  {
227
349
  name: 'faf_chat',
228
350
  description: '🗣️ Natural language project.faf generation - Ask 6W questions (Who/What/Why/Where/When/How) to build complete human context 🧡⚡️',
351
+ annotations: {
352
+ title: 'Chat about FAF',
353
+ readOnlyHint: true,
354
+ openWorldHint: false
355
+ },
229
356
  inputSchema: {
230
357
  type: 'object',
231
358
  properties: {},
359
+ additionalProperties: false
232
360
  }
233
361
  },
234
362
  {
235
363
  name: 'faf_friday',
236
364
  description: '🎉 Friday Features - Chrome Extension detection, fuzzy matching & more! 🧡⚡️',
365
+ annotations: {
366
+ title: 'Fun FAF Facts',
367
+ readOnlyHint: true,
368
+ openWorldHint: false
369
+ },
237
370
  inputSchema: {
238
371
  type: 'object',
239
372
  properties: {
@@ -242,16 +375,315 @@ class FafToolHandler {
242
375
  description: 'Test fuzzy matching with typos like "raect" or "chr ext"'
243
376
  }
244
377
  },
378
+ additionalProperties: false
245
379
  }
246
380
  },
247
381
  {
248
382
  name: 'faf_guide',
249
383
  description: 'FAF MCP usage guide for Claude Desktop - Projects convention, path resolution, and UX patterns',
384
+ annotations: {
385
+ title: 'Usage Guide',
386
+ readOnlyHint: true,
387
+ openWorldHint: false
388
+ },
250
389
  inputSchema: {
251
390
  type: 'object',
252
391
  properties: {},
392
+ additionalProperties: false
253
393
  }
254
- }
394
+ },
395
+ {
396
+ name: 'faf_readme',
397
+ description: '📖 Extract 6 Ws (Who/What/Why/Where/When/How) from README.md into human_context - Smart pattern matching 🧡⚡️',
398
+ annotations: {
399
+ title: 'Extract from README',
400
+ readOnlyHint: false,
401
+ destructiveHint: false,
402
+ openWorldHint: false
403
+ },
404
+ inputSchema: {
405
+ type: 'object',
406
+ properties: {
407
+ apply: { type: 'boolean', description: 'Apply extracted content to project.faf (default: preview only)' },
408
+ force: { type: 'boolean', description: 'Overwrite existing human_context values (default: only fill empty slots)' },
409
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
410
+ },
411
+ additionalProperties: false
412
+ }
413
+ },
414
+ {
415
+ name: 'faf_human_add',
416
+ description: '🧡 Add a human_context field (who/what/why/where/when/how) - Non-interactive for MCP 🧡⚡️',
417
+ annotations: {
418
+ title: 'Add Human Context',
419
+ readOnlyHint: false,
420
+ destructiveHint: false,
421
+ openWorldHint: false
422
+ },
423
+ inputSchema: {
424
+ type: 'object',
425
+ properties: {
426
+ field: {
427
+ type: 'string',
428
+ enum: ['who', 'what', 'why', 'where', 'when', 'how'],
429
+ description: 'The 6 W field to set'
430
+ },
431
+ value: { type: 'string', description: 'The value to set for the field' },
432
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
433
+ },
434
+ required: ['field', 'value'],
435
+ additionalProperties: false
436
+ }
437
+ },
438
+ {
439
+ name: 'faf_check',
440
+ description: '🔍 Quality inspection for human_context fields + field protection - Shows empty/generic/good/excellent ratings 🧡⚡️',
441
+ annotations: {
442
+ title: 'Check .faf Health',
443
+ readOnlyHint: true,
444
+ openWorldHint: false
445
+ },
446
+ inputSchema: {
447
+ type: 'object',
448
+ properties: {
449
+ protect: { type: 'boolean', description: 'Lock good/excellent fields from being overwritten' },
450
+ unlock: { type: 'boolean', description: 'Remove all field protections' },
451
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
452
+ },
453
+ additionalProperties: false
454
+ }
455
+ },
456
+ {
457
+ name: 'faf_context',
458
+ description: '📂 Set or view active project context - Path is remembered for subsequent faf_ calls 🧡⚡️',
459
+ annotations: {
460
+ title: 'View Context',
461
+ readOnlyHint: true,
462
+ openWorldHint: false
463
+ },
464
+ inputSchema: {
465
+ type: 'object',
466
+ properties: {
467
+ path: { type: 'string', description: 'Set active project path. If omitted, shows current context.' }
468
+ },
469
+ additionalProperties: false
470
+ }
471
+ },
472
+ {
473
+ name: 'faf_go',
474
+ description: '🎯 Guided interview to Gold Code - Claude asks questions till you hit 100%! Returns questions for missing fields, then apply answers to reach Gold Code 🧡⚡️',
475
+ annotations: {
476
+ title: 'Guided Setup',
477
+ readOnlyHint: false,
478
+ destructiveHint: false,
479
+ openWorldHint: false
480
+ },
481
+ inputSchema: {
482
+ type: 'object',
483
+ properties: {
484
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
485
+ answers: {
486
+ type: 'object',
487
+ description: 'Answers to apply. Keys are field paths (e.g., "project.goal", "human_context.why"), values are the answers. If provided, applies answers and returns new score.',
488
+ additionalProperties: { type: 'string' }
489
+ }
490
+ },
491
+ additionalProperties: false
492
+ }
493
+ },
494
+ {
495
+ name: 'faf_auto',
496
+ description: '🏎️ ONE COMMAND TO RULE THEM ALL - Zero to Championship AI context instantly! Runs init + sync + formats + bi-sync + score in one go 🧡⚡️',
497
+ annotations: {
498
+ title: 'Auto-detect Context',
499
+ readOnlyHint: false,
500
+ destructiveHint: false,
501
+ openWorldHint: false
502
+ },
503
+ inputSchema: {
504
+ type: 'object',
505
+ properties: {
506
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
507
+ force: { type: 'boolean', description: 'Force overwrite existing files' }
508
+ },
509
+ additionalProperties: false
510
+ }
511
+ },
512
+ {
513
+ name: 'faf_dna',
514
+ description: '🧬 Show your FAF DNA journey - See your evolution from birth to championship (22% → 85% → 99%) 🧡⚡️',
515
+ annotations: {
516
+ title: 'View Project DNA',
517
+ readOnlyHint: true,
518
+ openWorldHint: false
519
+ },
520
+ inputSchema: {
521
+ type: 'object',
522
+ properties: {
523
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
524
+ },
525
+ additionalProperties: false
526
+ }
527
+ },
528
+ {
529
+ name: 'faf_formats',
530
+ description: '😽 TURBO-CAT format discovery - Discovers all formats in your project (154+ validated types!) and fills stack slots 🧡⚡️',
531
+ annotations: {
532
+ title: 'List Formats',
533
+ readOnlyHint: true,
534
+ openWorldHint: false
535
+ },
536
+ inputSchema: {
537
+ type: 'object',
538
+ properties: {
539
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
540
+ json: { type: 'boolean', description: 'Return results as JSON' }
541
+ },
542
+ additionalProperties: false
543
+ }
544
+ },
545
+ {
546
+ name: 'faf_quick',
547
+ description: '⚡ Lightning-fast .faf creation - One-liner format: "name, description, language, framework, hosting" 🧡⚡️',
548
+ annotations: {
549
+ title: 'Quick Create',
550
+ readOnlyHint: false,
551
+ destructiveHint: false,
552
+ openWorldHint: false
553
+ },
554
+ inputSchema: {
555
+ type: 'object',
556
+ properties: {
557
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
558
+ input: { type: 'string', description: 'Quick input: "project-name, description, language, framework, hosting" (minimum: name, description)' },
559
+ force: { type: 'boolean', description: 'Force overwrite existing .faf file' }
560
+ },
561
+ required: ['input'],
562
+ additionalProperties: false
563
+ }
564
+ },
565
+ {
566
+ name: 'faf_doctor',
567
+ description: '🏥 Health check for your .faf setup - Diagnose and fix common issues 🧡⚡️',
568
+ annotations: {
569
+ title: 'Diagnose Issues',
570
+ readOnlyHint: true,
571
+ openWorldHint: false
572
+ },
573
+ inputSchema: {
574
+ type: 'object',
575
+ properties: {
576
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
577
+ },
578
+ additionalProperties: false
579
+ }
580
+ },
581
+ // ============================================================================
582
+ // v4.5.0 INTEROP TOOLS
583
+ // ============================================================================
584
+ {
585
+ name: 'faf_agents',
586
+ description: 'Import/Export/Sync between AGENTS.md (OpenAI/Codex) and project.faf - AI interop!',
587
+ annotations: {
588
+ title: 'Sync AGENTS.md',
589
+ readOnlyHint: false,
590
+ destructiveHint: false,
591
+ openWorldHint: false
592
+ },
593
+ inputSchema: {
594
+ type: 'object',
595
+ properties: {
596
+ action: { type: 'string', enum: ['import', 'export', 'sync'], description: 'Action: import (AGENTS.md -> .faf), export (.faf -> AGENTS.md), sync (bidirectional)' },
597
+ force: { type: 'boolean', description: 'Force overwrite existing files' },
598
+ merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
599
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
600
+ },
601
+ required: ['action'],
602
+ additionalProperties: false
603
+ }
604
+ },
605
+ {
606
+ name: 'faf_cursor',
607
+ description: 'Import/Export/Sync between .cursorrules (Cursor IDE) and project.faf - AI interop!',
608
+ annotations: {
609
+ title: 'Sync .cursorrules',
610
+ readOnlyHint: false,
611
+ destructiveHint: false,
612
+ openWorldHint: false
613
+ },
614
+ inputSchema: {
615
+ type: 'object',
616
+ properties: {
617
+ action: { type: 'string', enum: ['import', 'export', 'sync'], description: 'Action: import (.cursorrules -> .faf), export (.faf -> .cursorrules), sync (bidirectional)' },
618
+ force: { type: 'boolean', description: 'Force overwrite existing files' },
619
+ merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
620
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
621
+ },
622
+ required: ['action'],
623
+ additionalProperties: false
624
+ }
625
+ },
626
+ {
627
+ name: 'faf_gemini',
628
+ description: 'Import/Export/Sync between GEMINI.md (Google Gemini CLI) and project.faf - AI interop!',
629
+ annotations: {
630
+ title: 'Sync GEMINI.md',
631
+ readOnlyHint: false,
632
+ destructiveHint: false,
633
+ openWorldHint: false
634
+ },
635
+ inputSchema: {
636
+ type: 'object',
637
+ properties: {
638
+ action: { type: 'string', enum: ['import', 'export', 'sync'], description: 'Action: import (GEMINI.md -> .faf), export (.faf -> GEMINI.md), sync (bidirectional)' },
639
+ force: { type: 'boolean', description: 'Force overwrite existing files' },
640
+ merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
641
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
642
+ },
643
+ required: ['action'],
644
+ additionalProperties: false
645
+ }
646
+ },
647
+ {
648
+ name: 'faf_conductor',
649
+ description: 'Import/Export between conductor/ directory (Google Conductor) and project.faf - AI interop!',
650
+ annotations: {
651
+ title: 'Sync Conductor',
652
+ readOnlyHint: false,
653
+ destructiveHint: false,
654
+ openWorldHint: false
655
+ },
656
+ inputSchema: {
657
+ type: 'object',
658
+ properties: {
659
+ action: { type: 'string', enum: ['import', 'export'], description: 'Action: import (conductor/ -> .faf), export (.faf -> conductor/)' },
660
+ force: { type: 'boolean', description: 'Force overwrite existing files' },
661
+ merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
662
+ path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
663
+ },
664
+ required: ['action'],
665
+ additionalProperties: false
666
+ }
667
+ },
668
+ {
669
+ name: 'faf_git',
670
+ description: 'Generate project.faf from any GitHub repo URL - 1-click context extraction!',
671
+ annotations: {
672
+ title: 'Extract from GitHub',
673
+ readOnlyHint: false,
674
+ destructiveHint: false,
675
+ openWorldHint: false
676
+ },
677
+ inputSchema: {
678
+ type: 'object',
679
+ properties: {
680
+ url: { type: 'string', description: 'GitHub repository URL (e.g., https://github.com/owner/repo or owner/repo)' },
681
+ path: { type: 'string', description: 'Output directory for generated project.faf. If omitted, returns content without writing.' }
682
+ },
683
+ required: ['url'],
684
+ additionalProperties: false
685
+ }
686
+ },
255
687
  ]
256
688
  };
257
689
  }
@@ -283,8 +715,15 @@ class FafToolHandler {
283
715
  return await this.handleFafAbout(args);
284
716
  case 'faf_what':
285
717
  return await this.handleFafWhat(args);
286
- case 'faf_read':
287
- return await fileHandler_1.fileHandlers.faf_read(args);
718
+ case 'faf_read': {
719
+ // Handle faf_read specially to set context when reading project.faf files
720
+ const readResult = await fileHandler_1.fileHandlers.faf_read(args);
721
+ // If reading a project.faf file, set the session context
722
+ if (args?.path && (args.path.includes('project.faf') || args.path.endsWith('.faf'))) {
723
+ this.getProjectPath(args.path);
724
+ }
725
+ return readResult;
726
+ }
288
727
  case 'faf_chat':
289
728
  return await this.handleFafChat(args);
290
729
  case 'faf_friday':
@@ -295,13 +734,44 @@ class FafToolHandler {
295
734
  return await this.handleFafList(args);
296
735
  case 'faf_guide':
297
736
  return await this.handleFafGuide(args);
737
+ case 'faf_readme':
738
+ return await this.handleFafReadme(args);
739
+ case 'faf_human_add':
740
+ return await this.handleFafHumanAdd(args);
741
+ case 'faf_check':
742
+ return await this.handleFafCheck(args);
743
+ case 'faf_context':
744
+ return await this.handleFafContext(args);
745
+ case 'faf_go':
746
+ return await this.handleFafGo(args);
747
+ case 'faf_auto':
748
+ return await this.handleFafAuto(args);
749
+ case 'faf_dna':
750
+ return await this.handleFafDna(args);
751
+ case 'faf_formats':
752
+ return await this.handleFafFormats(args);
753
+ case 'faf_quick':
754
+ return await this.handleFafQuick(args);
755
+ case 'faf_doctor':
756
+ return await this.handleFafDoctor(args);
757
+ // v4.5.0 Interop tools
758
+ case 'faf_agents':
759
+ return await this.handleFafAgents(args);
760
+ case 'faf_cursor':
761
+ return await this.handleFafCursor(args);
762
+ case 'faf_gemini':
763
+ return await this.handleFafGemini(args);
764
+ case 'faf_conductor':
765
+ return await this.handleFafConductor(args);
766
+ case 'faf_git':
767
+ return await this.handleFafGit(args);
298
768
  default:
299
769
  throw new Error(`Unknown tool: ${name}`);
300
770
  }
301
771
  }
302
- async handleFafStatus(_args) {
772
+ async handleFafStatus(args) {
303
773
  // Native implementation - no CLI needed!
304
- const cwd = this.engineAdapter.getWorkingDirectory();
774
+ const cwd = this.getProjectPath(args?.path);
305
775
  try {
306
776
  const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
307
777
  if (!fafResult) {
@@ -335,8 +805,8 @@ class FafToolHandler {
335
805
  try {
336
806
  const fs = await import('fs').then(m => m.promises);
337
807
  const path = await import('path');
338
- // Get current working directory from engine adapter (smart detection)
339
- const cwd = this.engineAdapter.getWorkingDirectory();
808
+ // Get current working directory - uses path param or session context
809
+ const cwd = this.getProjectPath(args?.path);
340
810
  // Score calculation components
341
811
  let score = 0;
342
812
  const details = [];
@@ -405,44 +875,40 @@ class FafToolHandler {
405
875
  output += `\n💡 Note: 🍊 Big Orange is a BADGE awarded separately for excellence beyond metrics.`;
406
876
  }
407
877
  }
408
- else if (score >= 99) {
409
- // Maximum technical score
410
- output = `📊 FAF SCORE: 99%\n⚡ Maximum Technical\n🏁 Claude grants 100%\n\n`;
411
- if (args?.details) {
412
- output += details.join('\n');
413
- output += `\n\n💡 Only Claude can grant the final 1% for perfect collaboration!`;
414
- }
415
- }
416
878
  else {
417
- // Regular score
418
- const percentage = Math.min(score, 99);
879
+ // Regular score - FAF standard tiers
880
+ const percentage = Math.min(score, 100);
419
881
  let rating = '';
420
882
  let emoji = '';
421
- if (percentage >= 90) {
422
- rating = 'Excellent';
423
- emoji = '🏆';
883
+ if (percentage >= 99) {
884
+ rating = 'Gold';
885
+ emoji = '🥇';
886
+ }
887
+ else if (percentage >= 95) {
888
+ rating = 'Silver';
889
+ emoji = '🥈';
424
890
  }
425
- else if (percentage >= 80) {
426
- rating = 'Very Good';
427
- emoji = '';
891
+ else if (percentage >= 85) {
892
+ rating = 'Bronze';
893
+ emoji = '🥉';
428
894
  }
429
895
  else if (percentage >= 70) {
430
- rating = 'Good';
431
- emoji = '';
896
+ rating = 'Green';
897
+ emoji = '🟢';
432
898
  }
433
- else if (percentage >= 60) {
434
- rating = 'Improving';
435
- emoji = '📈';
899
+ else if (percentage >= 55) {
900
+ rating = 'Yellow';
901
+ emoji = '🟡';
436
902
  }
437
903
  else {
438
- rating = 'Getting Started';
439
- emoji = '🚀';
904
+ rating = 'Red';
905
+ emoji = '🔴';
440
906
  }
441
907
  // The 3-line killer display
442
- output = `📊 FAF SCORE: ${percentage}%\n${emoji} ${rating}\n🏁 AI-Ready: ${percentage >= 70 ? 'Yes' : 'Building'}\n`;
908
+ output = `📊 FAF SCORE: ${percentage}%\n${emoji} ${rating}\n🏁 AI-Ready: ${percentage >= 85 ? 'Yes' : 'Building'}\n`;
443
909
  if (args?.details) {
444
910
  output += `\n${details.join('\n')}`;
445
- if (percentage < 99) {
911
+ if (percentage < 100) {
446
912
  output += `\n\n💡 Tips to improve:\n`;
447
913
  if (!hasFaf)
448
914
  output += `- Create .faf file with project context\n`;
@@ -473,43 +939,18 @@ class FafToolHandler {
473
939
  }
474
940
  }
475
941
  async handleFafInit(args) {
476
- // Native implementation - creates project.faf with Projects convention!
942
+ // Native implementation - creates project.faf with Pomelli-simple path resolution!
477
943
  try {
478
- // Determine project path using Projects convention or explicit path
479
- let targetDir;
480
- let projectName;
481
- // Accept both 'directory' (legacy) and 'path' (new)
482
- const explicitPath = args?.path || args?.directory;
483
- if (explicitPath) {
484
- // User explicit path always wins
485
- // Expand tilde (~) to home directory
486
- const expandedPath = explicitPath.startsWith('~')
487
- ? path.join(require('os').homedir(), explicitPath.slice(1))
488
- : explicitPath;
489
- targetDir = path.resolve(expandedPath);
490
- projectName = path.basename(targetDir);
491
- // Ensure directory exists
492
- if (!fs.existsSync(targetDir)) {
493
- fs.mkdirSync(targetDir, { recursive: true });
494
- }
495
- }
496
- else if (args?.projectName) {
497
- // Use Projects convention with provided name
498
- (0, path_resolver_1.ensureProjectsDirectory)();
499
- const resolution = (0, path_resolver_1.resolveProjectPath)(args.projectName);
500
- targetDir = resolution.projectPath;
501
- projectName = resolution.projectName;
502
- // Ensure project directory exists
503
- if (!fs.existsSync(targetDir)) {
504
- fs.mkdirSync(targetDir, { recursive: true });
505
- }
506
- }
507
- else {
508
- // Use current working directory (legacy behavior)
509
- targetDir = this.engineAdapter.getWorkingDirectory();
510
- projectName = path.basename(targetDir);
944
+ // Use smart path resolution (supports "my-app", "~/Projects/my-app", "/full/path")
945
+ const userInput = args?.path;
946
+ const resolution = (0, path_resolver_1.resolveProjectPath)(userInput);
947
+ const targetDir = resolution.projectPath;
948
+ const projectName = resolution.projectName;
949
+ const fafPath = resolution.fafFilePath;
950
+ // Ensure project directory exists
951
+ if (!fs.existsSync(targetDir)) {
952
+ fs.mkdirSync(targetDir, { recursive: true });
511
953
  }
512
- const fafPath = path.join(targetDir, 'project.faf');
513
954
  // Check if any FAF file exists and force flag
514
955
  const existingFaf = await (0, faf_file_finder_js_1.findFafFile)(targetDir);
515
956
  if (existingFaf && !args?.force) {
@@ -538,8 +979,7 @@ class FafToolHandler {
538
979
  projectData = (0, fuzzy_detector_1.applyIntelFriday)(projectData);
539
980
  }
540
981
  // Create enhanced .faf content
541
- const fafContent = `# USE>FAF - call FAF-MCP (or faf/cli)
542
- # FAF - Foundational AI Context
982
+ const fafContent = `# FAF - Foundational AI Context
543
983
  project: ${projectData.project}
544
984
  type: ${projectData.project_type}${chromeDetection.detected ? ' 🎯' : ''}
545
985
  context: I⚡🍊
@@ -568,10 +1008,15 @@ build: ${projectData.build}
568
1008
  package_manager: ${projectData.package_manager}` : ''}
569
1009
  `;
570
1010
  fs.writeFileSync(fafPath, fafContent);
1011
+ // Pomelli-style success confirmation with path resolution info
1012
+ const pathConfirmation = (0, path_resolver_1.formatPathConfirmation)(resolution);
1013
+ const sourceExplanation = resolution.source === 'user-name'
1014
+ ? `\n\n💡 Smart resolution: "${userInput}" → ${targetDir}`
1015
+ : '';
571
1016
  return {
572
1017
  content: [{
573
1018
  type: 'text',
574
- text: `🚀 Claude FAF Initialization:\n\n✅ Created project.faf in ${targetDir}\n🍊 Vitamin Context activated!\n⚡ FAFFLESS AI ready!${chromeDetection.detected ? '\n\n🎯 Friday Feature: Chrome Extension detected!\n📈 Auto-filled 7 slots for 90%+ score!' : ''}${chromeDetection.corrected ? `\n📝 Auto-corrected: "${args?.description}" → "${chromeDetection.corrected}"` : ''}`
1019
+ text: `🚀 Claude FAF Initialization:\n\n✅ Created project.faf\n\n${pathConfirmation}${sourceExplanation}\n\n🍊 Vitamin Context activated!\n⚡ FAFFLESS AI ready!${chromeDetection.detected ? '\n\n🎯 Friday Feature: Chrome Extension detected!\n📈 Auto-filled 7 slots for 90%+ score!' : ''}${chromeDetection.corrected ? `\n📝 Auto-corrected: "${args?.description}" → "${chromeDetection.corrected}"` : ''}\n\n🏁 Next steps:\n • Run faf_score for AI-readiness score\n • Run faf_sync to create CLAUDE.md\n • Run faf_enhance to improve context`
575
1020
  }]
576
1021
  };
577
1022
  }
@@ -606,7 +1051,11 @@ package_manager: ${projectData.package_manager}` : ''}
606
1051
  }]
607
1052
  };
608
1053
  }
609
- async handleFafSync(_args) {
1054
+ async handleFafSync(args) {
1055
+ // Set project context if path provided
1056
+ if (args?.path) {
1057
+ this.getProjectPath(args.path);
1058
+ }
610
1059
  const result = await this.engineAdapter.callEngine('sync');
611
1060
  if (!result.success) {
612
1061
  return {
@@ -628,6 +1077,10 @@ package_manager: ${projectData.package_manager}` : ''}
628
1077
  };
629
1078
  }
630
1079
  async handleFafEnhance(args) {
1080
+ // Set project context if path provided
1081
+ if (args?.path) {
1082
+ this.getProjectPath(args.path);
1083
+ }
631
1084
  const enhanceArgs = [];
632
1085
  // Default to Claude optimization if no model specified
633
1086
  const model = args?.model || 'claude';
@@ -653,7 +1106,7 @@ package_manager: ${projectData.package_manager}` : ''}
653
1106
  }
654
1107
  const output = typeof result.data === 'string'
655
1108
  ? result.data
656
- : result.data?.output || JSON.stringify(result.data, null, 2);
1109
+ : result.data?.message || result.data?.output || JSON.stringify(result.data, null, 2);
657
1110
  return {
658
1111
  content: [{
659
1112
  type: 'text',
@@ -662,6 +1115,10 @@ package_manager: ${projectData.package_manager}` : ''}
662
1115
  };
663
1116
  }
664
1117
  async handleFafBiSync(args) {
1118
+ // Set project context if path provided
1119
+ if (args?.path) {
1120
+ this.getProjectPath(args.path);
1121
+ }
665
1122
  const biSyncArgs = [];
666
1123
  if (args?.auto) {
667
1124
  biSyncArgs.push('--auto');
@@ -672,9 +1129,6 @@ package_manager: ${projectData.package_manager}` : ''}
672
1129
  if (args?.force) {
673
1130
  biSyncArgs.push('--force');
674
1131
  }
675
- if (args?.target) {
676
- biSyncArgs.push(`--target=${args.target}`);
677
- }
678
1132
  const result = await this.engineAdapter.callEngine('bi-sync', biSyncArgs);
679
1133
  if (!result.success) {
680
1134
  return {
@@ -740,7 +1194,7 @@ package_manager: ${projectData.package_manager}` : ''}
740
1194
  npm: 'https://www.npmjs.com/package/claude-faf-mcp'
741
1195
  };
742
1196
  const aboutText = `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
743
- 🤖 .faf = THE JPEG for AI
1197
+ 🤖 .faf = project DNA for AI
744
1198
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
745
1199
 
746
1200
  WHAT IS .FAF?
@@ -773,7 +1227,7 @@ HOW IT WORKS:
773
1227
  };
774
1228
  }
775
1229
  async handleFafWhat(_args) {
776
- const whatText = `.faf = THE JPEG for AI
1230
+ const whatText = `.faf = project DNA for AI
777
1231
 
778
1232
  WHAT: .faf = Foundational AI-context Format
779
1233
  (The dot means it's a file format, like .jpg or .pdf)
@@ -800,7 +1254,7 @@ REMEMBER: Always use ".faf" with the dot - it's a FORMAT!
800
1254
  const path = await import('path');
801
1255
  const { exec } = await import('child_process');
802
1256
  const { promisify } = await import('util');
803
- const execAsync = promisify(exec);
1257
+ const _execAsync = promisify(exec);
804
1258
  const cwd = this.engineAdapter.getWorkingDirectory();
805
1259
  const debugInfo = {
806
1260
  workingDirectory: cwd,
@@ -1032,7 +1486,7 @@ All work: \`faf init\`, \`faf init new\`, \`faf init --new\`, \`faf init -new\`
1032
1486
  const showHidden = args?.showHidden || false;
1033
1487
  // Expand tilde
1034
1488
  const expandedPath = targetPath.startsWith('~')
1035
- ? path.join(require('os').homedir(), targetPath.slice(1))
1489
+ ? path.join(os.homedir(), targetPath.slice(1))
1036
1490
  : targetPath;
1037
1491
  const resolvedPath = path.resolve(expandedPath);
1038
1492
  // Check if directory exists
@@ -1137,6 +1591,1553 @@ All work: \`faf init\`, \`faf init new\`, \`faf init --new\`, \`faf init -new\`
1137
1591
  };
1138
1592
  }
1139
1593
  }
1594
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1595
+ // NEW: Human Context Tools (v3.2.0 parity)
1596
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1597
+ async handleFafReadme(args) {
1598
+ try {
1599
+ const path = await import('path');
1600
+ const cwd = this.getProjectPath(args?.path);
1601
+ // Find README.md
1602
+ const readmePath = path.join(cwd, 'README.md');
1603
+ if (!fs.existsSync(readmePath)) {
1604
+ return {
1605
+ content: [{
1606
+ type: 'text',
1607
+ text: `📖 FAF README Extraction:\n\n❌ No README.md found in ${cwd}\n💡 Create a README.md first`
1608
+ }],
1609
+ isError: true
1610
+ };
1611
+ }
1612
+ // Find project.faf
1613
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
1614
+ if (!fafResult) {
1615
+ return {
1616
+ content: [{
1617
+ type: 'text',
1618
+ text: `📖 FAF README Extraction:\n\n❌ No project.faf found in ${cwd}\n💡 Run faf_init first`
1619
+ }],
1620
+ isError: true
1621
+ };
1622
+ }
1623
+ // Read README content
1624
+ const readmeContent = fs.readFileSync(readmePath, 'utf-8');
1625
+ // Extract 6 Ws using simple pattern matching
1626
+ const extracted = this.extractSixWsFromReadme(readmeContent);
1627
+ if (!args?.apply) {
1628
+ // Preview mode
1629
+ let output = `📖 FAF README Extraction (Preview)\n\n`;
1630
+ output += `Found in README.md:\n`;
1631
+ for (const [field, value] of Object.entries(extracted)) {
1632
+ if (value) {
1633
+ output += ` ${field.toUpperCase()}: ${value}\n`;
1634
+ }
1635
+ }
1636
+ output += `\n💡 Use apply: true to save to project.faf`;
1637
+ return { content: [{ type: 'text', text: output }] };
1638
+ }
1639
+ // Apply mode - update project.faf
1640
+ const fafContent = fs.readFileSync(fafResult.path, 'utf-8');
1641
+ const yaml = await import('yaml');
1642
+ const fafData = yaml.parse(fafContent) || {};
1643
+ if (!fafData.human_context) {
1644
+ fafData.human_context = {};
1645
+ }
1646
+ let appliedCount = 0;
1647
+ for (const [field, value] of Object.entries(extracted)) {
1648
+ if (value) {
1649
+ const existingValue = fafData.human_context[field];
1650
+ if (!existingValue || args?.force) {
1651
+ fafData.human_context[field] = value;
1652
+ appliedCount++;
1653
+ }
1654
+ }
1655
+ }
1656
+ fs.writeFileSync(fafResult.path, yaml.stringify(fafData), 'utf-8');
1657
+ return {
1658
+ content: [{
1659
+ type: 'text',
1660
+ text: `📖 FAF README Extraction:\n\n✅ Applied ${appliedCount} field(s) to human_context\n📁 Updated: ${fafResult.filename}`
1661
+ }]
1662
+ };
1663
+ }
1664
+ catch (error) {
1665
+ return {
1666
+ content: [{ type: 'text', text: `📖 FAF README Extraction:\n\n❌ Error: ${error.message}` }],
1667
+ isError: true
1668
+ };
1669
+ }
1670
+ }
1671
+ extractSixWsFromReadme(content) {
1672
+ const result = {
1673
+ who: null, what: null, why: null, where: null, when: null, how: null
1674
+ };
1675
+ // WHAT: First paragraph after title
1676
+ const whatMatch = content.match(/^#\s+[^\n]+\n+(?:\*\*[^*]+\*\*\n+)?([A-Z][^#\n]{30,})/m);
1677
+ if (whatMatch)
1678
+ result.what = whatMatch[1].trim().substring(0, 200);
1679
+ // WHO: Look for "team", "company", "by", "for"
1680
+ const whoMatch = content.match(/(?:built by|created by|maintained by|for|team)\s+([^\n.]{10,50})/i);
1681
+ if (whoMatch)
1682
+ result.who = whoMatch[1].trim();
1683
+ // WHY: Look for "because", "to", benefits
1684
+ const whyMatch = content.match(/(?:because|to help|enables|allows|makes it)\s+([^\n.]{15,100})/i);
1685
+ if (whyMatch)
1686
+ result.why = whyMatch[1].trim();
1687
+ // WHERE: Look for deployment/runtime mentions
1688
+ const whereMatch = content.match(/(?:runs on|deployed to|works with|for)\s+(browser|server|edge|npm|cargo|cloud|local)/i);
1689
+ if (whereMatch)
1690
+ result.where = whereMatch[0].trim();
1691
+ // WHEN: Look for version, date
1692
+ const whenMatch = content.match(/(?:version|v)\s*(\d+\.\d+(?:\.\d+)?)/i);
1693
+ if (whenMatch)
1694
+ result.when = `v${whenMatch[1]}`;
1695
+ // HOW: Look for install/run commands
1696
+ const howMatch = content.match(/(?:npm install|cargo|pip install|brew install)\s+[^\n]+/i);
1697
+ if (howMatch)
1698
+ result.how = howMatch[0].trim();
1699
+ return result;
1700
+ }
1701
+ async handleFafHumanAdd(args) {
1702
+ try {
1703
+ const { field, value } = args;
1704
+ if (!field || !value) {
1705
+ return {
1706
+ content: [{
1707
+ type: 'text',
1708
+ text: `🧡 FAF Human Set:\n\n❌ Both field and value are required\n💡 Example: field="who", value="Development team"`
1709
+ }],
1710
+ isError: true
1711
+ };
1712
+ }
1713
+ const validFields = ['who', 'what', 'why', 'where', 'when', 'how'];
1714
+ if (!validFields.includes(field)) {
1715
+ return {
1716
+ content: [{
1717
+ type: 'text',
1718
+ text: `🧡 FAF Human Set:\n\n❌ Invalid field: ${field}\n💡 Valid fields: ${validFields.join(', ')}`
1719
+ }],
1720
+ isError: true
1721
+ };
1722
+ }
1723
+ const cwd = this.getProjectPath(args?.path);
1724
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
1725
+ if (!fafResult) {
1726
+ return {
1727
+ content: [{
1728
+ type: 'text',
1729
+ text: `🧡 FAF Human Add:\n\n❌ No project.faf found in ${cwd}\n💡 Run faf_init first`
1730
+ }],
1731
+ isError: true
1732
+ };
1733
+ }
1734
+ const fafContent = fs.readFileSync(fafResult.path, 'utf-8');
1735
+ const yaml = await import('yaml');
1736
+ const fafData = yaml.parse(fafContent) || {};
1737
+ if (!fafData.human_context) {
1738
+ fafData.human_context = {};
1739
+ }
1740
+ fafData.human_context[field] = value;
1741
+ fs.writeFileSync(fafResult.path, yaml.stringify(fafData), 'utf-8');
1742
+ return {
1743
+ content: [{
1744
+ type: 'text',
1745
+ text: `🧡 FAF Human Set:\n\n✅ Set ${field.toUpperCase()} = "${value}"\n📁 Updated: ${fafResult.filename}`
1746
+ }]
1747
+ };
1748
+ }
1749
+ catch (error) {
1750
+ return {
1751
+ content: [{ type: 'text', text: `🧡 FAF Human Set:\n\n❌ Error: ${error.message}` }],
1752
+ isError: true
1753
+ };
1754
+ }
1755
+ }
1756
+ async handleFafCheck(args) {
1757
+ try {
1758
+ const cwd = this.getProjectPath(args?.path);
1759
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
1760
+ if (!fafResult) {
1761
+ return {
1762
+ content: [{
1763
+ type: 'text',
1764
+ text: `🔍 FAF Check:\n\n❌ No project.faf found in ${cwd}\n💡 Run faf_init first`
1765
+ }],
1766
+ isError: true
1767
+ };
1768
+ }
1769
+ const fafContent = fs.readFileSync(fafResult.path, 'utf-8');
1770
+ const yaml = await import('yaml');
1771
+ const fafData = yaml.parse(fafContent) || {};
1772
+ const humanContext = fafData.human_context || {};
1773
+ const protectedFields = fafData._protected_fields || [];
1774
+ const fields = ['who', 'what', 'why', 'where', 'when', 'how'];
1775
+ // Handle --unlock
1776
+ if (args?.unlock) {
1777
+ fafData._protected_fields = [];
1778
+ fs.writeFileSync(fafResult.path, yaml.stringify(fafData), 'utf-8');
1779
+ return {
1780
+ content: [{
1781
+ type: 'text',
1782
+ text: `🔓 FAF Check:\n\n✅ All fields unlocked\n📁 Updated: ${fafResult.filename}`
1783
+ }]
1784
+ };
1785
+ }
1786
+ // Assess quality
1787
+ const assessField = (value) => {
1788
+ if (!value || value.trim() === '')
1789
+ return 'empty';
1790
+ if (value.length < 10)
1791
+ return 'generic';
1792
+ if (value.length > 20)
1793
+ return 'good';
1794
+ return 'generic';
1795
+ };
1796
+ const qualities = {};
1797
+ for (const field of fields) {
1798
+ qualities[field] = assessField(humanContext[field]);
1799
+ }
1800
+ // Handle --protect
1801
+ if (args?.protect) {
1802
+ const toProtect = fields.filter(f => qualities[f] === 'good' || qualities[f] === 'excellent');
1803
+ if (toProtect.length === 0) {
1804
+ return {
1805
+ content: [{
1806
+ type: 'text',
1807
+ text: `🔒 FAF Check:\n\n⚠️ No fields qualify for protection (need good or excellent quality)`
1808
+ }]
1809
+ };
1810
+ }
1811
+ fafData._protected_fields = [...new Set([...protectedFields, ...toProtect])];
1812
+ fs.writeFileSync(fafResult.path, yaml.stringify(fafData), 'utf-8');
1813
+ return {
1814
+ content: [{
1815
+ type: 'text',
1816
+ text: `🔒 FAF Check:\n\n✅ Protected ${toProtect.length} field(s): ${toProtect.join(', ')}\n📁 Updated: ${fafResult.filename}`
1817
+ }]
1818
+ };
1819
+ }
1820
+ // Default: show quality report
1821
+ const icons = {
1822
+ empty: '⬜', generic: '🟡', good: '🟢', excellent: '💎'
1823
+ };
1824
+ let output = `🔍 FAF Human Context Quality\n\n`;
1825
+ for (const field of fields) {
1826
+ const q = qualities[field];
1827
+ const locked = protectedFields.includes(field) ? '🔒' : ' ';
1828
+ const value = humanContext[field] || '(empty)';
1829
+ const displayValue = value.length > 40 ? value.substring(0, 37) + '...' : value;
1830
+ output += `${icons[q]} ${locked} ${field.toUpperCase().padEnd(6)} ${displayValue}\n`;
1831
+ }
1832
+ const goodCount = fields.filter(f => qualities[f] === 'good' || qualities[f] === 'excellent').length;
1833
+ const emptyCount = fields.filter(f => qualities[f] === 'empty').length;
1834
+ output += `\n📊 Quality: ${Math.round((goodCount / fields.length) * 100)}%\n`;
1835
+ if (protectedFields.length > 0) {
1836
+ output += `🔒 Protected: ${protectedFields.join(', ')}\n`;
1837
+ }
1838
+ if (emptyCount > 0) {
1839
+ output += `\n💡 Use faf_readme or faf_human_add to fill empty slots`;
1840
+ }
1841
+ return { content: [{ type: 'text', text: output }] };
1842
+ }
1843
+ catch (error) {
1844
+ return {
1845
+ content: [{ type: 'text', text: `🔍 FAF Check:\n\n❌ Error: ${error.message}` }],
1846
+ isError: true
1847
+ };
1848
+ }
1849
+ }
1850
+ async handleFafContext(args) {
1851
+ try {
1852
+ if (args?.path) {
1853
+ // Set the new context
1854
+ const newPath = this.getProjectPath(args.path);
1855
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(newPath);
1856
+ return {
1857
+ content: [{
1858
+ type: 'text',
1859
+ text: `📂 FAF Context Set:\n\n✅ Active project: ${newPath}\n${fafResult ? `✅ project.faf found: ${fafResult.filename}` : '⚠️ No project.faf in this directory'}\n\n💡 Subsequent faf_* calls will use this context`
1860
+ }]
1861
+ };
1862
+ }
1863
+ else {
1864
+ // Show current context
1865
+ const currentPath = this.engineAdapter.getWorkingDirectory();
1866
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(currentPath);
1867
+ return {
1868
+ content: [{
1869
+ type: 'text',
1870
+ text: `📂 FAF Current Context:\n\n📁 Active project: ${currentPath}\n${fafResult ? `✅ project.faf: ${fafResult.filename}` : '⚠️ No project.faf found'}\n\n💡 Use path parameter to change context`
1871
+ }]
1872
+ };
1873
+ }
1874
+ }
1875
+ catch (error) {
1876
+ return {
1877
+ content: [{ type: 'text', text: `📂 FAF Context:\n\n❌ Error: ${error.message}` }],
1878
+ isError: true
1879
+ };
1880
+ }
1881
+ }
1882
+ /**
1883
+ * faf_go - Guided interview to Gold Code
1884
+ *
1885
+ * Two-phase operation:
1886
+ * 1. Without answers: Returns questions for missing fields
1887
+ * 2. With answers: Applies answers to .faf file and returns new score
1888
+ */
1889
+ async handleFafGo(args) {
1890
+ const yaml = await import('yaml');
1891
+ const cwd = this.getProjectPath(args?.path);
1892
+ try {
1893
+ // Find .faf file
1894
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
1895
+ if (!fafResult) {
1896
+ return {
1897
+ content: [{
1898
+ type: 'text',
1899
+ text: JSON.stringify({
1900
+ needsInit: true,
1901
+ context: 'faf_go',
1902
+ message: 'No project.faf found. Run faf_init first to create project DNA.',
1903
+ suggestion: 'Use faf_init to create project.faf, then use faf_go to reach Gold Code.'
1904
+ }, null, 2)
1905
+ }]
1906
+ };
1907
+ }
1908
+ const fafContent = fs.readFileSync(fafResult.path, 'utf-8');
1909
+ const fafData = yaml.parse(fafContent) || {};
1910
+ // Question registry - maps field paths to questions
1911
+ const QUESTION_REGISTRY = {
1912
+ 'project.goal': {
1913
+ question: 'What does this project do? (one sentence)',
1914
+ header: 'Goal',
1915
+ type: 'text',
1916
+ required: true
1917
+ },
1918
+ 'project.name': {
1919
+ question: 'What is the name of this project?',
1920
+ header: 'Name',
1921
+ type: 'text',
1922
+ required: true
1923
+ },
1924
+ 'project.main_language': {
1925
+ question: 'What is the primary programming language?',
1926
+ header: 'Language',
1927
+ type: 'select',
1928
+ required: true,
1929
+ options: [
1930
+ { label: 'TypeScript', value: 'TypeScript', description: 'JavaScript with types' },
1931
+ { label: 'JavaScript', value: 'JavaScript', description: 'Vanilla JS or Node.js' },
1932
+ { label: 'Python', value: 'Python', description: 'Python 3.x' },
1933
+ { label: 'Rust', value: 'Rust', description: 'Systems programming' },
1934
+ { label: 'Go', value: 'Go', description: 'Golang' },
1935
+ { label: 'Other', value: 'Other', description: 'Specify manually' }
1936
+ ]
1937
+ },
1938
+ 'human_context.why': {
1939
+ question: 'Why does this project exist? (motivation)',
1940
+ header: 'Why',
1941
+ type: 'text',
1942
+ required: true
1943
+ },
1944
+ 'human_context.who': {
1945
+ question: 'Who uses this project? (target audience)',
1946
+ header: 'Who',
1947
+ type: 'text',
1948
+ required: false
1949
+ },
1950
+ 'human_context.what': {
1951
+ question: 'What problem does this solve?',
1952
+ header: 'What',
1953
+ type: 'text',
1954
+ required: false
1955
+ },
1956
+ 'human_context.where': {
1957
+ question: 'Where does this run? (environment)',
1958
+ header: 'Where',
1959
+ type: 'text',
1960
+ required: false
1961
+ },
1962
+ 'human_context.when': {
1963
+ question: 'When was this started or what phase is it in?',
1964
+ header: 'When',
1965
+ type: 'text',
1966
+ required: false
1967
+ },
1968
+ 'human_context.how': {
1969
+ question: 'How should AI assist with this project?',
1970
+ header: 'How',
1971
+ type: 'text',
1972
+ required: false
1973
+ },
1974
+ 'stack.frontend': {
1975
+ question: 'What frontend framework do you use?',
1976
+ header: 'Frontend',
1977
+ type: 'select',
1978
+ required: false,
1979
+ options: [
1980
+ { label: 'React', value: 'React', description: 'React.js' },
1981
+ { label: 'Vue', value: 'Vue', description: 'Vue.js' },
1982
+ { label: 'Svelte', value: 'Svelte', description: 'Svelte/SvelteKit' },
1983
+ { label: 'Next.js', value: 'Next.js', description: 'React framework' },
1984
+ { label: 'None', value: 'None', description: 'No frontend' },
1985
+ { label: 'Other', value: 'Other', description: 'Specify manually' }
1986
+ ]
1987
+ },
1988
+ 'stack.backend': {
1989
+ question: 'What backend framework do you use?',
1990
+ header: 'Backend',
1991
+ type: 'select',
1992
+ required: false,
1993
+ options: [
1994
+ { label: 'Express', value: 'Express', description: 'Node.js Express' },
1995
+ { label: 'Fastify', value: 'Fastify', description: 'Node.js Fastify' },
1996
+ { label: 'Django', value: 'Django', description: 'Python Django' },
1997
+ { label: 'FastAPI', value: 'FastAPI', description: 'Python FastAPI' },
1998
+ { label: 'None', value: 'None', description: 'No backend' },
1999
+ { label: 'Other', value: 'Other', description: 'Specify manually' }
2000
+ ]
2001
+ },
2002
+ 'stack.database': {
2003
+ question: 'What database do you use?',
2004
+ header: 'Database',
2005
+ type: 'select',
2006
+ required: false,
2007
+ options: [
2008
+ { label: 'PostgreSQL', value: 'PostgreSQL', description: 'Relational database' },
2009
+ { label: 'MongoDB', value: 'MongoDB', description: 'Document database' },
2010
+ { label: 'SQLite', value: 'SQLite', description: 'File-based database' },
2011
+ { label: 'Supabase', value: 'Supabase', description: 'Postgres + auth' },
2012
+ { label: 'None', value: 'None', description: 'No database' },
2013
+ { label: 'Other', value: 'Other', description: 'Specify manually' }
2014
+ ]
2015
+ },
2016
+ 'stack.hosting': {
2017
+ question: 'Where is this hosted/deployed?',
2018
+ header: 'Hosting',
2019
+ type: 'select',
2020
+ required: false,
2021
+ options: [
2022
+ { label: 'Vercel', value: 'Vercel', description: 'Frontend/serverless' },
2023
+ { label: 'AWS', value: 'AWS', description: 'Amazon Web Services' },
2024
+ { label: 'Cloudflare', value: 'Cloudflare', description: 'Workers/Pages' },
2025
+ { label: 'Railway', value: 'Railway', description: 'App hosting' },
2026
+ { label: 'Local only', value: 'Local', description: 'Not deployed' },
2027
+ { label: 'Other', value: 'Other', description: 'Specify manually' }
2028
+ ]
2029
+ }
2030
+ };
2031
+ // Priority order for questions
2032
+ const priorityOrder = [
2033
+ 'project.goal',
2034
+ 'human_context.why',
2035
+ 'human_context.who',
2036
+ 'human_context.what',
2037
+ 'project.name',
2038
+ 'project.main_language',
2039
+ 'stack.database',
2040
+ 'stack.hosting',
2041
+ 'stack.frontend',
2042
+ 'stack.backend',
2043
+ 'human_context.where',
2044
+ 'human_context.when',
2045
+ 'human_context.how'
2046
+ ];
2047
+ // Helper to get nested value
2048
+ const getNestedValue = (obj, path) => {
2049
+ const parts = path.split('.');
2050
+ let value = obj;
2051
+ for (const part of parts) {
2052
+ if (value && typeof value === 'object' && part in value) {
2053
+ value = value[part];
2054
+ }
2055
+ else {
2056
+ return undefined;
2057
+ }
2058
+ }
2059
+ return value;
2060
+ };
2061
+ // Helper to set nested value
2062
+ const setNestedValue = (obj, path, value) => {
2063
+ const parts = path.split('.');
2064
+ let current = obj;
2065
+ for (let i = 0; i < parts.length - 1; i++) {
2066
+ const part = parts[i];
2067
+ if (!(part in current)) {
2068
+ current[part] = {};
2069
+ }
2070
+ current = current[part];
2071
+ }
2072
+ current[parts[parts.length - 1]] = value;
2073
+ };
2074
+ // Check if value is empty/placeholder
2075
+ const isEmpty = (value) => {
2076
+ return value === undefined ||
2077
+ value === null ||
2078
+ value === '' ||
2079
+ value === 'Unknown' ||
2080
+ value === 'TBD' ||
2081
+ value === 'None' ||
2082
+ (typeof value === 'string' && value.toLowerCase().includes('placeholder'));
2083
+ };
2084
+ // PHASE 2: Apply answers if provided
2085
+ if (args?.answers && typeof args.answers === 'object') {
2086
+ const answers = args.answers;
2087
+ let appliedCount = 0;
2088
+ for (const [fieldPath, answer] of Object.entries(answers)) {
2089
+ if (answer && answer.trim()) {
2090
+ setNestedValue(fafData, fieldPath, answer.trim());
2091
+ appliedCount++;
2092
+ }
2093
+ }
2094
+ // Write updated file
2095
+ fs.writeFileSync(fafResult.path, yaml.stringify(fafData), 'utf-8');
2096
+ // Calculate new score (simple count-based)
2097
+ const totalFields = Object.keys(QUESTION_REGISTRY).length;
2098
+ const filledFields = Object.keys(QUESTION_REGISTRY).filter(field => !isEmpty(getNestedValue(fafData, field))).length;
2099
+ const newScore = Math.round((filledFields / totalFields) * 100);
2100
+ const celebration = newScore >= 100 ? '🏆 GOLD CODE ACHIEVED!' :
2101
+ newScore >= 85 ? '🥇 Championship grade!' :
2102
+ newScore >= 70 ? '🥈 Great progress!' : '📈 Keep going!';
2103
+ return {
2104
+ content: [{
2105
+ type: 'text',
2106
+ text: `🎯 FAF Go - Answers Applied!\n\n✅ Updated ${appliedCount} field(s) in ${fafResult.filename}\n📊 New Score: ${newScore}%\n${celebration}\n\n${newScore < 100 ? '💡 Run faf_go again to continue to Gold Code!' : '✨ Your AI now has complete context!'}`
2107
+ }]
2108
+ };
2109
+ }
2110
+ // PHASE 1: Analyze and return questions
2111
+ const missingFields = [];
2112
+ for (const fieldPath of Object.keys(QUESTION_REGISTRY)) {
2113
+ const value = getNestedValue(fafData, fieldPath);
2114
+ if (isEmpty(value)) {
2115
+ missingFields.push(fieldPath);
2116
+ }
2117
+ }
2118
+ // Calculate current score
2119
+ const totalFields = Object.keys(QUESTION_REGISTRY).length;
2120
+ const filledFields = totalFields - missingFields.length;
2121
+ const currentScore = Math.round((filledFields / totalFields) * 100);
2122
+ // Already at 100%?
2123
+ if (currentScore >= 100) {
2124
+ return {
2125
+ content: [{
2126
+ type: 'text',
2127
+ text: JSON.stringify({
2128
+ complete: true,
2129
+ score: 100,
2130
+ message: '🏆 GOLD CODE ACHIEVED! Your project has 100% AI-Readiness.',
2131
+ context: 'faf_go'
2132
+ }, null, 2)
2133
+ }]
2134
+ };
2135
+ }
2136
+ // No missing fields but score < 100? Content quality issue
2137
+ if (missingFields.length === 0) {
2138
+ return {
2139
+ content: [{
2140
+ type: 'text',
2141
+ text: JSON.stringify({
2142
+ score: currentScore,
2143
+ message: `Score is ${currentScore}%. All fields filled but content may need enhancement.`,
2144
+ suggestion: 'Use faf_enhance to improve content quality.',
2145
+ context: 'faf_go'
2146
+ }, null, 2)
2147
+ }]
2148
+ };
2149
+ }
2150
+ // Sort by priority
2151
+ const prioritizedFields = missingFields.sort((a, b) => {
2152
+ const aIdx = priorityOrder.indexOf(a);
2153
+ const bIdx = priorityOrder.indexOf(b);
2154
+ if (aIdx === -1)
2155
+ return 1;
2156
+ if (bIdx === -1)
2157
+ return -1;
2158
+ return aIdx - bIdx;
2159
+ });
2160
+ // Build questions
2161
+ const questions = prioritizedFields.map(field => {
2162
+ const reg = QUESTION_REGISTRY[field];
2163
+ return {
2164
+ field,
2165
+ question: reg.question,
2166
+ header: reg.header,
2167
+ type: reg.type,
2168
+ required: reg.required,
2169
+ options: reg.options
2170
+ };
2171
+ });
2172
+ return {
2173
+ content: [{
2174
+ type: 'text',
2175
+ text: JSON.stringify({
2176
+ needsInput: true,
2177
+ context: 'faf_go - guided path to Gold Code',
2178
+ currentScore,
2179
+ targetScore: 100,
2180
+ questionsRemaining: questions.length,
2181
+ questions,
2182
+ instructions: 'Use AskUserQuestion to ask these questions, then call faf_go again with the answers parameter to apply them.'
2183
+ }, null, 2)
2184
+ }]
2185
+ };
2186
+ }
2187
+ catch (error) {
2188
+ return {
2189
+ content: [{ type: 'text', text: `🎯 FAF Go:\n\n❌ Error: ${error.message}` }],
2190
+ isError: true
2191
+ };
2192
+ }
2193
+ }
2194
+ /**
2195
+ * faf_auto - ONE COMMAND TO RULE THEM ALL
2196
+ * Zero to Championship in one command
2197
+ * Runs: init + formats + sync + bi-sync + score
2198
+ */
2199
+ async handleFafAuto(args) {
2200
+ const startTime = Date.now();
2201
+ const cwd = this.getProjectPath(args?.path);
2202
+ const yaml = await import('yaml');
2203
+ const path = await import('path');
2204
+ try {
2205
+ const steps = [];
2206
+ let currentScore = 0;
2207
+ // Step 1: Check/Create .faf file
2208
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
2209
+ let fafPath;
2210
+ if (!fafResult) {
2211
+ // Create .faf file
2212
+ const projectName = path.basename(cwd);
2213
+ fafPath = path.join(cwd, 'project.faf');
2214
+ const initFafContent = `# FAF - Foundational AI Context
2215
+ project: ${projectName}
2216
+ type: auto-detected
2217
+ context: I⚡🍊
2218
+ generated: ${new Date().toISOString()}
2219
+ version: ${version_1.VERSION}
2220
+
2221
+ # Quick Context
2222
+ working_directory: ${cwd}
2223
+ initialized_by: claude-faf-mcp-auto
2224
+ vitamin_context: true
2225
+ faffless: true
2226
+ `;
2227
+ fs.writeFileSync(fafPath, initFafContent);
2228
+ steps.push('✅ Created project.faf');
2229
+ }
2230
+ else {
2231
+ fafPath = fafResult.path;
2232
+ steps.push(`✅ Found ${fafResult.filename}`);
2233
+ }
2234
+ // Get initial score
2235
+ const fafContent = fs.readFileSync(fafPath, 'utf-8');
2236
+ const fafData = yaml.parse(fafContent) || {};
2237
+ currentScore = this.calculateSimpleScore(fafData);
2238
+ // Step 2: Run TURBO-CAT format discovery
2239
+ const formatsResult = await this.discoverFormatsInternal(cwd);
2240
+ if (formatsResult.discoveredFormats.length > 0) {
2241
+ // Apply slot fills to .faf
2242
+ if (!fafData.stack)
2243
+ fafData.stack = {};
2244
+ for (const [key, value] of Object.entries(formatsResult.slotFillRecommendations)) {
2245
+ if (!fafData.stack[key] || fafData.stack[key] === 'None') {
2246
+ fafData.stack[key] = value;
2247
+ }
2248
+ }
2249
+ if (formatsResult.stackSignature) {
2250
+ fafData.stack_signature = formatsResult.stackSignature;
2251
+ }
2252
+ fs.writeFileSync(fafPath, yaml.stringify(fafData), 'utf-8');
2253
+ steps.push(`✅ TURBO-CAT discovered ${formatsResult.discoveredFormats.length} formats`);
2254
+ }
2255
+ else {
2256
+ steps.push('⚠️ No additional formats detected');
2257
+ }
2258
+ // Step 3: Extract human context from README
2259
+ const readmePath = path.join(cwd, 'README.md');
2260
+ if (fs.existsSync(readmePath)) {
2261
+ const readmeContent = fs.readFileSync(readmePath, 'utf-8');
2262
+ const extracted = this.extractSixWsFromReadme(readmeContent);
2263
+ if (!fafData.human_context)
2264
+ fafData.human_context = {};
2265
+ let extractedCount = 0;
2266
+ for (const [field, value] of Object.entries(extracted)) {
2267
+ if (value && !fafData.human_context[field]) {
2268
+ fafData.human_context[field] = value;
2269
+ extractedCount++;
2270
+ }
2271
+ }
2272
+ if (extractedCount > 0) {
2273
+ fs.writeFileSync(fafPath, yaml.stringify(fafData), 'utf-8');
2274
+ steps.push(`✅ Extracted ${extractedCount} human context fields from README`);
2275
+ }
2276
+ }
2277
+ // Step 4: Create/Update CLAUDE.md (bi-sync)
2278
+ const claudePath = path.join(cwd, 'CLAUDE.md');
2279
+ if (!fs.existsSync(claudePath)) {
2280
+ const claudeContent = `# 🏎️ CLAUDE.md - AI Telemetry Link
2281
+
2282
+ ## Project: ${fafData.project || path.basename(cwd)}
2283
+ **Championship-Grade Project DNA Foundation**
2284
+
2285
+ ### 🎯 Project Mission
2286
+ ${fafData.human_context?.why || fafData.project?.goal || 'AI-ready project context'}
2287
+
2288
+ ### 🏗️ Architecture Overview
2289
+ ${fafData.stack_signature || 'Auto-detected stack'}
2290
+
2291
+ ---
2292
+
2293
+ **STATUS: BI-SYNC ACTIVE 🔗**
2294
+ *Last Sync: ${new Date().toISOString()}*
2295
+ *Sync Engine: FAF Auto*
2296
+ `;
2297
+ fs.writeFileSync(claudePath, claudeContent);
2298
+ steps.push('✅ Created CLAUDE.md');
2299
+ }
2300
+ else {
2301
+ steps.push('✅ CLAUDE.md already exists');
2302
+ }
2303
+ // Step 5: Calculate final score
2304
+ const updatedContent = fs.readFileSync(fafPath, 'utf-8');
2305
+ const updatedData = yaml.parse(updatedContent) || {};
2306
+ const newScore = this.calculateSimpleScore(updatedData);
2307
+ const scoreDelta = newScore - currentScore;
2308
+ // Calculate elapsed time
2309
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
2310
+ // Format output
2311
+ const deltaDisplay = scoreDelta > 0 ? `(+${scoreDelta}%)` : scoreDelta < 0 ? `(${scoreDelta}%)` : '(no change)';
2312
+ let output = `🏎️⚡️ FAF AUTO - CHAMPIONSHIP MODE!\n`;
2313
+ output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
2314
+ output += steps.join('\n') + '\n\n';
2315
+ output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
2316
+ output += `⏱️ Completed in ${elapsed}s\n`;
2317
+ output += `📊 Before: ${currentScore}% | After: ${newScore}% ${deltaDisplay}\n`;
2318
+ output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
2319
+ if (newScore >= 99) {
2320
+ output += `🏆 CHAMPIONSHIP ACHIEVED! Your AI has complete context.\n`;
2321
+ }
2322
+ else if (newScore >= 85) {
2323
+ output += `🥇 Elite level! ${100 - newScore}% to perfection.\n`;
2324
+ }
2325
+ else if (newScore >= 70) {
2326
+ output += `🥈 Great progress! Run faf_go to reach championship.\n`;
2327
+ }
2328
+ else {
2329
+ output += `🚀 Good start! Run faf_go for guided improvement.\n`;
2330
+ }
2331
+ output += `\n💡 Next: faf_score --details | faf_go | faf_enhance`;
2332
+ return { content: [{ type: 'text', text: output }] };
2333
+ }
2334
+ catch (error) {
2335
+ return {
2336
+ content: [{ type: 'text', text: `🏎️ FAF Auto:\n\n❌ Error: ${error.message}` }],
2337
+ isError: true
2338
+ };
2339
+ }
2340
+ }
2341
+ /**
2342
+ * faf_dna - Show your FAF DNA journey
2343
+ * Displays evolution from birth to current (22% → 85% → 99%)
2344
+ */
2345
+ async handleFafDna(args) {
2346
+ const cwd = this.getProjectPath(args?.path);
2347
+ const path = await import('path');
2348
+ try {
2349
+ const dnaPath = path.join(cwd, '.faf-dna');
2350
+ // Check if DNA file exists
2351
+ if (!fs.existsSync(dnaPath)) {
2352
+ // No DNA yet - check if .faf exists
2353
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
2354
+ if (!fafResult) {
2355
+ return {
2356
+ content: [{
2357
+ type: 'text',
2358
+ text: `🧬 FAF DNA Journey\n\n❌ No FAF DNA found\n💡 Run faf_auto to start your journey!`
2359
+ }]
2360
+ };
2361
+ }
2362
+ // .faf exists but no DNA - create initial DNA
2363
+ const yaml = await import('yaml');
2364
+ const fafContent = fs.readFileSync(fafResult.path, 'utf-8');
2365
+ const fafData = yaml.parse(fafContent) || {};
2366
+ const currentScore = this.calculateSimpleScore(fafData);
2367
+ const dna = {
2368
+ birthCertificate: {
2369
+ born: new Date().toISOString(),
2370
+ birthDNA: currentScore,
2371
+ birthDNASource: 'auto',
2372
+ authenticated: false,
2373
+ certificate: `FAF-${new Date().getFullYear()}-${path.basename(cwd).toUpperCase().slice(0, 8)}-${Math.random().toString(36).slice(2, 6).toUpperCase()}`
2374
+ },
2375
+ current: {
2376
+ score: currentScore,
2377
+ version: 'v1.0.0',
2378
+ lastSync: new Date().toISOString()
2379
+ },
2380
+ milestones: [
2381
+ { type: 'birth', score: currentScore, date: new Date().toISOString(), version: 'v1.0.0' }
2382
+ ],
2383
+ format: 'faf-dna-v1'
2384
+ };
2385
+ fs.writeFileSync(dnaPath, JSON.stringify(dna, null, 2));
2386
+ return {
2387
+ content: [{
2388
+ type: 'text',
2389
+ text: `🧬 FAF DNA Journey\n\n🐣 Birth Certificate Created!\n\n📊 Birth DNA: ${currentScore}%\n📅 Born: ${new Date().toISOString().split('T')[0]}\n🎫 Certificate: ${dna.birthCertificate.certificate}\n\n💡 Your journey begins here! Run faf_auto or faf_go to grow.`
2390
+ }]
2391
+ };
2392
+ }
2393
+ // Load existing DNA
2394
+ const dnaContent = fs.readFileSync(dnaPath, 'utf-8');
2395
+ const dna = JSON.parse(dnaContent);
2396
+ // Build journey string
2397
+ const birthScore = dna.birthCertificate?.birthDNA || 0;
2398
+ const currentScore = dna.current?.score || 0;
2399
+ const milestones = dna.milestones || [];
2400
+ // Find key milestones
2401
+ const _birth = milestones.find((m) => m.type === 'birth');
2402
+ const peak = milestones.find((m) => m.type === 'peak');
2403
+ const championship = milestones.find((m) => m.type === 'championship');
2404
+ const elite = milestones.find((m) => m.type === 'elite');
2405
+ // Build compact journey
2406
+ let journey = `${birthScore}%`;
2407
+ if (championship && championship.score !== birthScore) {
2408
+ journey += ` → ${championship.score}%`;
2409
+ }
2410
+ if (elite && (!championship || elite.score !== championship.score)) {
2411
+ journey += ` → ${elite.score}%`;
2412
+ }
2413
+ if (peak) {
2414
+ journey += ` → ${peak.score}%`;
2415
+ if (currentScore < peak.score) {
2416
+ journey += ` ← ${currentScore}%`;
2417
+ }
2418
+ }
2419
+ else if (currentScore !== birthScore) {
2420
+ journey += ` → ${currentScore}%`;
2421
+ }
2422
+ // Calculate stats
2423
+ const birthDate = new Date(dna.birthCertificate?.born || Date.now());
2424
+ const daysActive = Math.floor((Date.now() - birthDate.getTime()) / (1000 * 60 * 60 * 24));
2425
+ const totalGrowth = currentScore - birthScore;
2426
+ let output = `🧬 YOUR FAF DNA\n\n`;
2427
+ output += ` ${journey}\n\n`;
2428
+ output += `═══════════════════════════════════════════════════\n\n`;
2429
+ output += `📊 QUICK STATS\n`;
2430
+ output += ` Born: ${birthDate.toISOString().split('T')[0]}\n`;
2431
+ output += ` Days Active: ${daysActive}\n`;
2432
+ output += ` Total Growth: +${totalGrowth}%\n`;
2433
+ if (dna.birthCertificate?.authenticated) {
2434
+ output += ` ✅ Authenticated: ${dna.birthCertificate.certificate}\n`;
2435
+ }
2436
+ else {
2437
+ output += ` ⚠️ Not authenticated\n`;
2438
+ }
2439
+ output += `\n🧬 MILESTONES\n`;
2440
+ const milestoneIcons = {
2441
+ birth: '🐣', first_save: '💾', doubled: '2️⃣',
2442
+ championship: '🏆', elite: '⭐', peak: '🏔️', perfect: '💎'
2443
+ };
2444
+ for (const m of milestones) {
2445
+ const icon = milestoneIcons[m.type] || '📍';
2446
+ const isCurrent = m.score === currentScore;
2447
+ output += ` ${icon} ${m.type}: ${m.score}%${isCurrent ? ' ← You are here!' : ''}\n`;
2448
+ }
2449
+ output += `\n═══════════════════════════════════════════════════\n`;
2450
+ // Motivational message
2451
+ if (totalGrowth > 70) {
2452
+ output += `🚀 Incredible journey! You've transformed your AI context!\n`;
2453
+ }
2454
+ else if (totalGrowth > 50) {
2455
+ output += `📈 Great progress! Your context is evolving beautifully.\n`;
2456
+ }
2457
+ else if (totalGrowth > 0) {
2458
+ output += `🌱 Your journey has begun. Every step counts!\n`;
2459
+ }
2460
+ else {
2461
+ output += `🐣 Just born! Your growth story starts now.\n`;
2462
+ }
2463
+ return { content: [{ type: 'text', text: output }] };
2464
+ }
2465
+ catch (error) {
2466
+ return {
2467
+ content: [{ type: 'text', text: `🧬 FAF DNA:\n\n❌ Error: ${error.message}` }],
2468
+ isError: true
2469
+ };
2470
+ }
2471
+ }
2472
+ /**
2473
+ * faf_formats - TURBO-CAT format discovery
2474
+ * Discovers all formats in the project
2475
+ */
2476
+ async handleFafFormats(args) {
2477
+ const cwd = this.getProjectPath(args?.path);
2478
+ const startTime = Date.now();
2479
+ try {
2480
+ const analysis = await this.discoverFormatsInternal(cwd);
2481
+ const elapsed = Date.now() - startTime;
2482
+ if (args?.json) {
2483
+ return {
2484
+ content: [{
2485
+ type: 'text',
2486
+ text: JSON.stringify(analysis, null, 2)
2487
+ }]
2488
+ };
2489
+ }
2490
+ // Format human-readable output
2491
+ let output = `😽 TURBO-CAT™ Format Discovery v2.0.0\n`;
2492
+ output += `═══════════════════════════════════════════════════\n\n`;
2493
+ output += `✅ Found ${analysis.discoveredFormats.length} formats in ${elapsed}ms!\n\n`;
2494
+ output += `📋 Discovered Formats (A-Z):\n`;
2495
+ const sorted = [...analysis.discoveredFormats].sort((a, b) => a.fileName.localeCompare(b.fileName));
2496
+ for (const format of sorted) {
2497
+ output += ` ✅ ${format.fileName}\n`;
2498
+ }
2499
+ output += `\n💡 Stack Signature: ${analysis.stackSignature}\n`;
2500
+ output += `🏆 Intelligence Score: ${analysis.totalIntelligenceScore}\n\n`;
2501
+ if (Object.keys(analysis.slotFillRecommendations).length > 0) {
2502
+ output += `📊 Recommended Slot Fills:\n`;
2503
+ for (const [key, value] of Object.entries(analysis.slotFillRecommendations)) {
2504
+ output += ` • ${key}: ${value}\n`;
2505
+ }
2506
+ output += `\n`;
2507
+ }
2508
+ output += `───────────────────────────────────────────────────\n`;
2509
+ output += `😽 TURBO-CAT™: "I detected ${analysis.discoveredFormats.length} formats and made your stack PURRR!"\n`;
2510
+ return { content: [{ type: 'text', text: output }] };
2511
+ }
2512
+ catch (error) {
2513
+ return {
2514
+ content: [{ type: 'text', text: `😽 TURBO-CAT:\n\n❌ Error: ${error.message}` }],
2515
+ isError: true
2516
+ };
2517
+ }
2518
+ }
2519
+ /**
2520
+ * Internal helper: Discover formats in a directory (TURBO-CAT logic)
2521
+ */
2522
+ async discoverFormatsInternal(projectDir) {
2523
+ const path = await import('path');
2524
+ // Known format files and their categories
2525
+ const KNOWN_FORMATS = {
2526
+ 'package.json': { category: 'package-manager', priority: 35 },
2527
+ 'tsconfig.json': { category: 'typescript-config', priority: 30 },
2528
+ 'Cargo.toml': { category: 'package-manager', priority: 35 },
2529
+ 'pyproject.toml': { category: 'package-manager', priority: 35 },
2530
+ 'requirements.txt': { category: 'package-manager', priority: 25 },
2531
+ 'go.mod': { category: 'package-manager', priority: 35 },
2532
+ 'pom.xml': { category: 'package-manager', priority: 35 },
2533
+ 'README.md': { category: 'documentation', priority: 20 },
2534
+ 'CLAUDE.md': { category: 'ai-context', priority: 40 },
2535
+ 'project.faf': { category: 'faf-context', priority: 45 },
2536
+ '.faf': { category: 'faf-context', priority: 45 },
2537
+ 'Dockerfile': { category: 'docker', priority: 25 },
2538
+ 'docker-compose.yml': { category: 'docker', priority: 25 },
2539
+ 'vercel.json': { category: 'deployment', priority: 20 },
2540
+ 'netlify.toml': { category: 'deployment', priority: 20 },
2541
+ '.eslintrc.json': { category: 'linting', priority: 15 },
2542
+ '.prettierrc': { category: 'linting', priority: 15 },
2543
+ 'jest.config.js': { category: 'testing', priority: 20 },
2544
+ 'vitest.config.ts': { category: 'testing', priority: 20 },
2545
+ 'svelte.config.js': { category: 'framework', priority: 30 },
2546
+ 'next.config.js': { category: 'framework', priority: 30 },
2547
+ 'vite.config.ts': { category: 'build', priority: 25 },
2548
+ 'webpack.config.js': { category: 'build', priority: 25 },
2549
+ '.github': { category: 'ci-cd', priority: 20 },
2550
+ 'manifest.json': { category: 'chrome-extension', priority: 35 }
2551
+ };
2552
+ const discoveredFormats = [];
2553
+ let totalIntelligenceScore = 0;
2554
+ const slotFillRecommendations = {};
2555
+ const extractedContext = {};
2556
+ // Scan directory
2557
+ try {
2558
+ const files = fs.readdirSync(projectDir);
2559
+ for (const file of files) {
2560
+ if (KNOWN_FORMATS[file]) {
2561
+ const format = KNOWN_FORMATS[file];
2562
+ discoveredFormats.push({
2563
+ fileName: file,
2564
+ category: format.category,
2565
+ priority: format.priority
2566
+ });
2567
+ totalIntelligenceScore += format.priority;
2568
+ }
2569
+ }
2570
+ // Extract intelligence from package.json
2571
+ const pkgPath = path.join(projectDir, 'package.json');
2572
+ if (fs.existsSync(pkgPath)) {
2573
+ const pkgContent = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
2574
+ const allDeps = { ...pkgContent.dependencies, ...pkgContent.devDependencies };
2575
+ extractedContext.projectName = pkgContent.name;
2576
+ extractedContext.projectDescription = pkgContent.description;
2577
+ // Detect frameworks and fill slots
2578
+ if (allDeps['typescript'] || allDeps['@types/node']) {
2579
+ slotFillRecommendations['mainLanguage'] = 'TypeScript';
2580
+ }
2581
+ if (allDeps['react'] || allDeps['next']) {
2582
+ slotFillRecommendations['frontend'] = allDeps['next'] ? 'Next.js' : 'React';
2583
+ }
2584
+ if (allDeps['vue'] || allDeps['nuxt']) {
2585
+ slotFillRecommendations['frontend'] = allDeps['nuxt'] ? 'Nuxt' : 'Vue';
2586
+ }
2587
+ if (allDeps['svelte'] || allDeps['@sveltejs/kit']) {
2588
+ slotFillRecommendations['frontend'] = allDeps['@sveltejs/kit'] ? 'SvelteKit' : 'Svelte';
2589
+ }
2590
+ if (allDeps['express']) {
2591
+ slotFillRecommendations['backend'] = 'Express';
2592
+ }
2593
+ if (allDeps['fastify']) {
2594
+ slotFillRecommendations['backend'] = 'Fastify';
2595
+ }
2596
+ if (allDeps['vite']) {
2597
+ slotFillRecommendations['build'] = 'Vite';
2598
+ }
2599
+ if (allDeps['jest'] || allDeps['vitest']) {
2600
+ slotFillRecommendations['testing'] = allDeps['vitest'] ? 'Vitest' : 'Jest';
2601
+ }
2602
+ }
2603
+ // Check for deployment indicators
2604
+ if (fs.existsSync(path.join(projectDir, 'vercel.json'))) {
2605
+ slotFillRecommendations['hosting'] = 'Vercel';
2606
+ }
2607
+ else if (fs.existsSync(path.join(projectDir, 'netlify.toml'))) {
2608
+ slotFillRecommendations['hosting'] = 'Netlify';
2609
+ }
2610
+ }
2611
+ catch (error) {
2612
+ // Ignore errors, return empty results
2613
+ }
2614
+ // Generate stack signature
2615
+ const parts = [];
2616
+ if (slotFillRecommendations['mainLanguage'])
2617
+ parts.push(slotFillRecommendations['mainLanguage'].toLowerCase());
2618
+ if (slotFillRecommendations['frontend'])
2619
+ parts.push(slotFillRecommendations['frontend'].toLowerCase());
2620
+ const stackSignature = parts.length > 0 ? parts.join('-') : 'unknown-stack';
2621
+ return {
2622
+ discoveredFormats,
2623
+ totalIntelligenceScore,
2624
+ stackSignature,
2625
+ slotFillRecommendations,
2626
+ extractedContext
2627
+ };
2628
+ }
2629
+ /**
2630
+ * Internal helper: Calculate simple score from .faf data
2631
+ */
2632
+ calculateSimpleScore(fafData) {
2633
+ let score = 0;
2634
+ const maxScore = 100;
2635
+ // Project section (30 points)
2636
+ if (fafData.project)
2637
+ score += 15;
2638
+ if (fafData.project?.goal || fafData.description)
2639
+ score += 15;
2640
+ // Human context (30 points)
2641
+ const humanContext = fafData.human_context || {};
2642
+ const wFields = ['who', 'what', 'why', 'where', 'when', 'how'];
2643
+ const filledW = wFields.filter(f => humanContext[f] && humanContext[f] !== 'null').length;
2644
+ score += Math.round((filledW / wFields.length) * 30);
2645
+ // Stack section (20 points)
2646
+ const stack = fafData.stack || {};
2647
+ const stackFields = ['frontend', 'backend', 'database', 'hosting', 'build'];
2648
+ const filledStack = stackFields.filter(f => stack[f] && stack[f] !== 'None').length;
2649
+ score += Math.round((filledStack / stackFields.length) * 20);
2650
+ // Files exist bonus (20 points)
2651
+ if (fafData.initialized_by || fafData.generated)
2652
+ score += 10;
2653
+ if (fafData.stack_signature)
2654
+ score += 10;
2655
+ return Math.min(score, maxScore);
2656
+ }
2657
+ /**
2658
+ * faf_quick - Lightning-fast .faf creation
2659
+ * One-liner format: "name, description, language, framework, hosting"
2660
+ */
2661
+ async handleFafQuick(args) {
2662
+ const cwd = this.getProjectPath(args?.path);
2663
+ const path = await import('path');
2664
+ const yaml = await import('yaml');
2665
+ const startTime = Date.now();
2666
+ try {
2667
+ const input = args?.input;
2668
+ if (!input || typeof input !== 'string') {
2669
+ return {
2670
+ content: [{
2671
+ type: 'text',
2672
+ text: `⚡ FAF Quick
2673
+
2674
+ Usage: Provide a comma-separated string:
2675
+ "project-name, description, language, framework, hosting"
2676
+
2677
+ Examples:
2678
+ "my-app, e-commerce platform, typescript, react, vercel"
2679
+ "api-service, REST API for mobile app, python, fastapi, aws"
2680
+ "cli-tool, developer productivity tool, go"
2681
+
2682
+ Minimum: name and description. Rest is auto-detected!`
2683
+ }]
2684
+ };
2685
+ }
2686
+ // Parse the quick input
2687
+ const parts = input.split(',').map((s) => s.trim());
2688
+ if (parts.length < 2) {
2689
+ return {
2690
+ content: [{
2691
+ type: 'text',
2692
+ text: `⚡ FAF Quick: Need at least: project-name, description
2693
+
2694
+ Got: "${input}"
2695
+
2696
+ Example: "my-app, e-commerce platform"`
2697
+ }],
2698
+ isError: true
2699
+ };
2700
+ }
2701
+ const projectName = parts[0] || 'my-project';
2702
+ const projectGoal = parts[1] || 'Build amazing software';
2703
+ const mainLanguage = parts[2] || 'TypeScript';
2704
+ const framework = parts[3] || 'none';
2705
+ const hosting = parts[4] || 'cloud';
2706
+ // Check if .faf exists
2707
+ const fafPath = path.join(cwd, 'project.faf');
2708
+ if (fs.existsSync(fafPath) && !args?.force) {
2709
+ return {
2710
+ content: [{
2711
+ type: 'text',
2712
+ text: `⚡ FAF Quick
2713
+
2714
+ ⚠️ project.faf already exists at: ${fafPath}
2715
+
2716
+ Use force: true to overwrite, or use faf_enhance to modify.`
2717
+ }]
2718
+ };
2719
+ }
2720
+ // Detect project type from inputs
2721
+ const projectType = this.detectProjectTypeFromQuick(projectGoal, framework, mainLanguage);
2722
+ // Build .faf content
2723
+ const fafData = {
2724
+ project: {
2725
+ name: projectName,
2726
+ goal: projectGoal,
2727
+ main_language: mainLanguage
2728
+ },
2729
+ type: projectType,
2730
+ generated: new Date().toISOString(),
2731
+ version: version_1.VERSION,
2732
+ initialized_by: 'claude-faf-mcp-quick'
2733
+ };
2734
+ if (framework && framework !== 'none') {
2735
+ fafData.stack = { frontend: framework };
2736
+ }
2737
+ if (hosting && hosting !== 'cloud') {
2738
+ if (!fafData.stack)
2739
+ fafData.stack = {};
2740
+ fafData.stack.hosting = hosting;
2741
+ }
2742
+ // Write the file
2743
+ fs.writeFileSync(fafPath, yaml.stringify(fafData), 'utf-8');
2744
+ const elapsed = Date.now() - startTime;
2745
+ let output = `⚡ FAF Quick - Created in ${elapsed}ms!\n\n`;
2746
+ output += `📦 Project: ${projectName}\n`;
2747
+ output += `🎯 Purpose: ${projectGoal}\n`;
2748
+ output += `💻 Stack: ${mainLanguage}${framework !== 'none' ? ` + ${framework}` : ''}\n`;
2749
+ output += `📍 Type: ${projectType}\n\n`;
2750
+ output += `✅ Created: ${fafPath}\n\n`;
2751
+ output += `Next steps:\n`;
2752
+ output += ` • faf_score - Check AI-readiness\n`;
2753
+ output += ` • faf_enhance - Improve context\n`;
2754
+ output += ` • faf_go - Guided interview to 100%`;
2755
+ return { content: [{ type: 'text', text: output }] };
2756
+ }
2757
+ catch (error) {
2758
+ return {
2759
+ content: [{ type: 'text', text: `⚡ FAF Quick:\n\n❌ Error: ${error.message}` }],
2760
+ isError: true
2761
+ };
2762
+ }
2763
+ }
2764
+ /**
2765
+ * Helper: Detect project type from quick input
2766
+ */
2767
+ detectProjectTypeFromQuick(goal, framework, language) {
2768
+ const fw = framework?.toLowerCase() || '';
2769
+ const lang = language?.toLowerCase() || '';
2770
+ const g = goal?.toLowerCase() || '';
2771
+ // Framework-based detection
2772
+ if (fw.includes('react') || fw.includes('next'))
2773
+ return 'react';
2774
+ if (fw.includes('vue') || fw.includes('nuxt'))
2775
+ return 'vue';
2776
+ if (fw.includes('svelte') || fw.includes('kit'))
2777
+ return 'svelte';
2778
+ if (fw.includes('angular'))
2779
+ return 'angular';
2780
+ if (fw.includes('fastapi'))
2781
+ return 'python-fastapi';
2782
+ if (fw.includes('django'))
2783
+ return 'python-django';
2784
+ if (fw.includes('flask'))
2785
+ return 'python-flask';
2786
+ if (fw.includes('express'))
2787
+ return 'node-api';
2788
+ // Goal-based detection
2789
+ if (g.includes('chrome extension') || g.includes('browser extension'))
2790
+ return 'chrome-extension';
2791
+ if (g.includes('api') || g.includes('backend'))
2792
+ return 'node-api';
2793
+ if (g.includes('cli') || g.includes('command'))
2794
+ return 'cli-tool';
2795
+ if (g.includes('library') || g.includes('package'))
2796
+ return 'library';
2797
+ if (g.includes('mcp') || g.includes('model context'))
2798
+ return 'mcp-server';
2799
+ // Language-based fallback
2800
+ if (lang.includes('python'))
2801
+ return 'python';
2802
+ if (lang.includes('go'))
2803
+ return 'golang';
2804
+ if (lang.includes('rust'))
2805
+ return 'rust';
2806
+ if (lang.includes('typescript'))
2807
+ return 'typescript';
2808
+ return 'general';
2809
+ }
2810
+ /**
2811
+ * faf_doctor - Health check for .faf setup
2812
+ * Diagnose and fix common issues
2813
+ */
2814
+ async handleFafDoctor(args) {
2815
+ const cwd = this.getProjectPath(args?.path);
2816
+ const path = await import('path');
2817
+ const yaml = await import('yaml');
2818
+ try {
2819
+ const results = [];
2820
+ // Check 1: MCP Version
2821
+ results.push({
2822
+ status: 'ok',
2823
+ message: `claude-faf-mcp version: ${version_1.VERSION}`
2824
+ });
2825
+ // Check 2: .faf file exists
2826
+ const fafResult = await (0, faf_file_finder_js_1.findFafFile)(cwd);
2827
+ if (!fafResult) {
2828
+ results.push({
2829
+ status: 'error',
2830
+ message: 'No .faf file found',
2831
+ fix: 'Run: faf_init, faf_quick, or faf_auto to create one'
2832
+ });
2833
+ }
2834
+ else {
2835
+ results.push({
2836
+ status: 'ok',
2837
+ message: `Found .faf at: ${fafResult.path}`
2838
+ });
2839
+ // Check 3: .faf file validity
2840
+ try {
2841
+ const content = fs.readFileSync(fafResult.path, 'utf-8');
2842
+ const fafData = yaml.parse(content);
2843
+ if (!fafData) {
2844
+ results.push({
2845
+ status: 'error',
2846
+ message: '.faf file is empty',
2847
+ fix: 'Run: faf_init with force option to regenerate'
2848
+ });
2849
+ }
2850
+ else {
2851
+ // Check for required fields
2852
+ const missingFields = [];
2853
+ if (!fafData.project?.name && !fafData.project)
2854
+ missingFields.push('project.name');
2855
+ if (!fafData.project?.goal)
2856
+ missingFields.push('project.goal');
2857
+ if (missingFields.length > 0) {
2858
+ results.push({
2859
+ status: 'warning',
2860
+ message: `Missing important fields: ${missingFields.join(', ')}`,
2861
+ fix: 'Run: faf_enhance or faf_go to add missing info'
2862
+ });
2863
+ }
2864
+ else {
2865
+ results.push({
2866
+ status: 'ok',
2867
+ message: '.faf structure is valid'
2868
+ });
2869
+ }
2870
+ // Check 4: Score
2871
+ const score = this.calculateSimpleScore(fafData);
2872
+ if (score < 30) {
2873
+ results.push({
2874
+ status: 'error',
2875
+ message: `Score too low: ${score}%`,
2876
+ fix: 'Run: faf_enhance or faf_go to improve context'
2877
+ });
2878
+ }
2879
+ else if (score < 70) {
2880
+ results.push({
2881
+ status: 'warning',
2882
+ message: `Score could be better: ${score}%`,
2883
+ fix: 'Target 70%+ for championship AI context'
2884
+ });
2885
+ }
2886
+ else {
2887
+ results.push({
2888
+ status: 'ok',
2889
+ message: `Great score: ${score}%`
2890
+ });
2891
+ }
2892
+ }
2893
+ }
2894
+ catch {
2895
+ results.push({
2896
+ status: 'error',
2897
+ message: '.faf file is corrupted or invalid YAML',
2898
+ fix: 'Run: faf_init with force option to regenerate'
2899
+ });
2900
+ }
2901
+ }
2902
+ // Check 5: CLAUDE.md exists
2903
+ const claudePath = path.join(cwd, 'CLAUDE.md');
2904
+ if (!fs.existsSync(claudePath)) {
2905
+ results.push({
2906
+ status: 'warning',
2907
+ message: 'No CLAUDE.md file',
2908
+ fix: 'Run: faf_auto or faf_bi_sync to create bi-directional sync'
2909
+ });
2910
+ }
2911
+ else {
2912
+ results.push({
2913
+ status: 'ok',
2914
+ message: 'CLAUDE.md found (bi-sync ready)'
2915
+ });
2916
+ }
2917
+ // Check 6: Project detection
2918
+ const packageJsonPath = path.join(cwd, 'package.json');
2919
+ const requirementsPath = path.join(cwd, 'requirements.txt');
2920
+ const goModPath = path.join(cwd, 'go.mod');
2921
+ const cargoPath = path.join(cwd, 'Cargo.toml');
2922
+ if (fs.existsSync(packageJsonPath)) {
2923
+ results.push({
2924
+ status: 'ok',
2925
+ message: 'Node.js/JavaScript project detected'
2926
+ });
2927
+ }
2928
+ else if (fs.existsSync(requirementsPath)) {
2929
+ results.push({
2930
+ status: 'ok',
2931
+ message: 'Python project detected'
2932
+ });
2933
+ }
2934
+ else if (fs.existsSync(goModPath)) {
2935
+ results.push({
2936
+ status: 'ok',
2937
+ message: 'Go project detected'
2938
+ });
2939
+ }
2940
+ else if (fs.existsSync(cargoPath)) {
2941
+ results.push({
2942
+ status: 'ok',
2943
+ message: 'Rust project detected'
2944
+ });
2945
+ }
2946
+ else {
2947
+ results.push({
2948
+ status: 'warning',
2949
+ message: 'No standard project files detected',
2950
+ fix: 'FAF works best with package.json, requirements.txt, go.mod, or Cargo.toml'
2951
+ });
2952
+ }
2953
+ // Build output
2954
+ let output = `🏥 FAF Doctor - Health Check\n`;
2955
+ output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
2956
+ let hasErrors = false;
2957
+ let hasWarnings = false;
2958
+ for (const result of results) {
2959
+ const icon = result.status === 'ok' ? '✅' :
2960
+ result.status === 'warning' ? '⚠️' : '❌';
2961
+ output += `${icon} ${result.message}\n`;
2962
+ if (result.fix) {
2963
+ output += ` 💡 ${result.fix}\n`;
2964
+ }
2965
+ if (result.status === 'error')
2966
+ hasErrors = true;
2967
+ if (result.status === 'warning')
2968
+ hasWarnings = true;
2969
+ }
2970
+ output += `\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
2971
+ if (!hasErrors && !hasWarnings) {
2972
+ output += `🏆 Perfect health! Your FAF setup is championship-ready!`;
2973
+ }
2974
+ else if (!hasErrors) {
2975
+ output += `🎯 Good health with minor improvements suggested.`;
2976
+ }
2977
+ else {
2978
+ output += `⚠️ Issues detected. Follow the fixes above.`;
2979
+ }
2980
+ return { content: [{ type: 'text', text: output }] };
2981
+ }
2982
+ catch (error) {
2983
+ return {
2984
+ content: [{ type: 'text', text: `🏥 FAF Doctor:\n\n❌ Error: ${error.message}` }],
2985
+ isError: true
2986
+ };
2987
+ }
2988
+ }
2989
+ // ============================================================================
2990
+ // v4.5.0 INTEROP HANDLERS
2991
+ // ============================================================================
2992
+ async handleFafAgents(args) {
2993
+ const cwd = this.getProjectPath(args?.path);
2994
+ const action = args?.action || 'sync';
2995
+ try {
2996
+ const result = await this.engineAdapter.callEngine('agents', [
2997
+ cwd,
2998
+ `--action=${action}`,
2999
+ ...(args?.force ? ['--force'] : []),
3000
+ ...(args?.merge ? ['--merge'] : []),
3001
+ ]);
3002
+ if (!result.success) {
3003
+ return {
3004
+ content: [{ type: 'text', text: `AGENTS.md ${action}:\n\n❌ ${result.error}` }],
3005
+ isError: true
3006
+ };
3007
+ }
3008
+ const data = result.data;
3009
+ return {
3010
+ content: [{ type: 'text', text: `AGENTS.md ${action}:\n\n✅ ${data?.message || 'Done'}\n⏱️ ${result.duration}ms` }]
3011
+ };
3012
+ }
3013
+ catch (error) {
3014
+ return {
3015
+ content: [{ type: 'text', text: `AGENTS.md ${action}:\n\n❌ Error: ${error.message}` }],
3016
+ isError: true
3017
+ };
3018
+ }
3019
+ }
3020
+ async handleFafCursor(args) {
3021
+ const cwd = this.getProjectPath(args?.path);
3022
+ const action = args?.action || 'sync';
3023
+ try {
3024
+ const result = await this.engineAdapter.callEngine('cursor', [
3025
+ cwd,
3026
+ `--action=${action}`,
3027
+ ...(args?.force ? ['--force'] : []),
3028
+ ...(args?.merge ? ['--merge'] : []),
3029
+ ]);
3030
+ if (!result.success) {
3031
+ return {
3032
+ content: [{ type: 'text', text: `.cursorrules ${action}:\n\n❌ ${result.error}` }],
3033
+ isError: true
3034
+ };
3035
+ }
3036
+ const data = result.data;
3037
+ return {
3038
+ content: [{ type: 'text', text: `.cursorrules ${action}:\n\n✅ ${data?.message || 'Done'}\n⏱️ ${result.duration}ms` }]
3039
+ };
3040
+ }
3041
+ catch (error) {
3042
+ return {
3043
+ content: [{ type: 'text', text: `.cursorrules ${action}:\n\n❌ Error: ${error.message}` }],
3044
+ isError: true
3045
+ };
3046
+ }
3047
+ }
3048
+ async handleFafGemini(args) {
3049
+ const cwd = this.getProjectPath(args?.path);
3050
+ const action = args?.action || 'sync';
3051
+ try {
3052
+ const result = await this.engineAdapter.callEngine('gemini', [
3053
+ cwd,
3054
+ `--action=${action}`,
3055
+ ...(args?.force ? ['--force'] : []),
3056
+ ...(args?.merge ? ['--merge'] : []),
3057
+ ]);
3058
+ if (!result.success) {
3059
+ return {
3060
+ content: [{ type: 'text', text: `GEMINI.md ${action}:\n\n❌ ${result.error}` }],
3061
+ isError: true
3062
+ };
3063
+ }
3064
+ const data = result.data;
3065
+ return {
3066
+ content: [{ type: 'text', text: `GEMINI.md ${action}:\n\n✅ ${data?.message || 'Done'}\n⏱️ ${result.duration}ms` }]
3067
+ };
3068
+ }
3069
+ catch (error) {
3070
+ return {
3071
+ content: [{ type: 'text', text: `GEMINI.md ${action}:\n\n❌ Error: ${error.message}` }],
3072
+ isError: true
3073
+ };
3074
+ }
3075
+ }
3076
+ async handleFafConductor(args) {
3077
+ const cwd = this.getProjectPath(args?.path);
3078
+ const action = args?.action || 'import';
3079
+ try {
3080
+ const result = await this.engineAdapter.callEngine('conductor', [
3081
+ cwd,
3082
+ `--action=${action}`,
3083
+ ...(args?.force ? ['--force'] : []),
3084
+ ...(args?.merge ? ['--merge'] : []),
3085
+ ]);
3086
+ if (!result.success) {
3087
+ return {
3088
+ content: [{ type: 'text', text: `Conductor ${action}:\n\n❌ ${result.error}` }],
3089
+ isError: true
3090
+ };
3091
+ }
3092
+ const data = result.data;
3093
+ return {
3094
+ content: [{ type: 'text', text: `Conductor ${action}:\n\n✅ ${data?.message || 'Done'}\n⏱️ ${result.duration}ms` }]
3095
+ };
3096
+ }
3097
+ catch (error) {
3098
+ return {
3099
+ content: [{ type: 'text', text: `Conductor ${action}:\n\n❌ Error: ${error.message}` }],
3100
+ isError: true
3101
+ };
3102
+ }
3103
+ }
3104
+ async handleFafGit(args) {
3105
+ const url = args?.url;
3106
+ if (!url) {
3107
+ return {
3108
+ content: [{ type: 'text', text: 'faf_git: Missing required parameter "url"' }],
3109
+ isError: true
3110
+ };
3111
+ }
3112
+ const outputPath = args?.path ? this.getProjectPath(args.path) : undefined;
3113
+ try {
3114
+ const result = await this.engineAdapter.callEngine('git', [
3115
+ url,
3116
+ ...(outputPath ? [outputPath] : []),
3117
+ ]);
3118
+ if (!result.success) {
3119
+ return {
3120
+ content: [{ type: 'text', text: `GitHub Context:\n\n❌ ${result.error}` }],
3121
+ isError: true
3122
+ };
3123
+ }
3124
+ const data = result.data;
3125
+ let output = `GitHub Context:\n\n✅ ${data?.message || 'Done'}\n⏱️ ${result.duration}ms`;
3126
+ // Include generated .faf content if no output path (preview mode)
3127
+ if (!outputPath && data?.data?.fafContent) {
3128
+ output += `\n\n--- Generated project.faf ---\n${data.data.fafContent}`;
3129
+ }
3130
+ return {
3131
+ content: [{ type: 'text', text: output }]
3132
+ };
3133
+ }
3134
+ catch (error) {
3135
+ return {
3136
+ content: [{ type: 'text', text: `GitHub Context:\n\n❌ Error: ${error.message}` }],
3137
+ isError: true
3138
+ };
3139
+ }
3140
+ }
1140
3141
  }
1141
3142
  exports.FafToolHandler = FafToolHandler;
1142
3143
  //# sourceMappingURL=tools.js.map