wogiflow 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.workflow/agents/reviewer.md +81 -0
- package/.workflow/agents/security.md +94 -0
- package/.workflow/agents/story-writer.md +58 -0
- package/.workflow/bridges/base-bridge.js +395 -0
- package/.workflow/bridges/claude-bridge.js +434 -0
- package/.workflow/bridges/index.js +130 -0
- package/.workflow/lib/assumption-detector.js +481 -0
- package/.workflow/lib/config-substitution.js +371 -0
- package/.workflow/lib/failure-categories.js +478 -0
- package/.workflow/state/app-map.md.template +15 -0
- package/.workflow/state/architecture.md.template +24 -0
- package/.workflow/state/component-index.json.template +5 -0
- package/.workflow/state/decisions.md.template +15 -0
- package/.workflow/state/feedback-patterns.md.template +9 -0
- package/.workflow/state/knowledge-sync.json.template +6 -0
- package/.workflow/state/progress.md.template +14 -0
- package/.workflow/state/ready.json.template +7 -0
- package/.workflow/state/request-log.md.template +14 -0
- package/.workflow/state/session-state.json.template +11 -0
- package/.workflow/state/stack.md.template +33 -0
- package/.workflow/state/testing.md.template +36 -0
- package/.workflow/templates/claude-md.hbs +257 -0
- package/.workflow/templates/correction-report.md +67 -0
- package/.workflow/templates/gemini-md.hbs +52 -0
- package/README.md +1802 -0
- package/bin/flow +205 -0
- package/lib/index.js +33 -0
- package/lib/installer.js +467 -0
- package/lib/release-channel.js +269 -0
- package/lib/skill-registry.js +526 -0
- package/lib/upgrader.js +401 -0
- package/lib/utils.js +305 -0
- package/package.json +64 -0
- package/scripts/flow +985 -0
- package/scripts/flow-adaptive-learning.js +1259 -0
- package/scripts/flow-aggregate.js +488 -0
- package/scripts/flow-archive +133 -0
- package/scripts/flow-auto-context.js +1015 -0
- package/scripts/flow-auto-learn.js +615 -0
- package/scripts/flow-bridge.js +223 -0
- package/scripts/flow-browser-suggest.js +316 -0
- package/scripts/flow-bug.js +247 -0
- package/scripts/flow-cascade.js +711 -0
- package/scripts/flow-changelog +85 -0
- package/scripts/flow-checkpoint.js +483 -0
- package/scripts/flow-cli.js +403 -0
- package/scripts/flow-code-intelligence.js +760 -0
- package/scripts/flow-complexity.js +502 -0
- package/scripts/flow-config-set.js +152 -0
- package/scripts/flow-constants.js +157 -0
- package/scripts/flow-context +152 -0
- package/scripts/flow-context-init.js +482 -0
- package/scripts/flow-context-monitor.js +384 -0
- package/scripts/flow-context-scoring.js +886 -0
- package/scripts/flow-correct.js +458 -0
- package/scripts/flow-damage-control.js +985 -0
- package/scripts/flow-deps +101 -0
- package/scripts/flow-diff.js +700 -0
- package/scripts/flow-done +151 -0
- package/scripts/flow-done.js +489 -0
- package/scripts/flow-durable-session.js +1541 -0
- package/scripts/flow-entropy-monitor.js +345 -0
- package/scripts/flow-export-profile +349 -0
- package/scripts/flow-export-scanner.js +1046 -0
- package/scripts/flow-figma-confirm.js +400 -0
- package/scripts/flow-figma-extract.js +496 -0
- package/scripts/flow-figma-generate.js +683 -0
- package/scripts/flow-figma-index.js +909 -0
- package/scripts/flow-figma-match.js +617 -0
- package/scripts/flow-figma-mcp-server.js +518 -0
- package/scripts/flow-figma-pipeline.js +414 -0
- package/scripts/flow-file-ops.js +301 -0
- package/scripts/flow-gate-confidence.js +825 -0
- package/scripts/flow-guided-edit.js +659 -0
- package/scripts/flow-health +185 -0
- package/scripts/flow-health.js +413 -0
- package/scripts/flow-hooks.js +556 -0
- package/scripts/flow-http-client.js +249 -0
- package/scripts/flow-hybrid-detect.js +167 -0
- package/scripts/flow-hybrid-interactive.js +591 -0
- package/scripts/flow-hybrid-test.js +152 -0
- package/scripts/flow-import-profile +439 -0
- package/scripts/flow-init +253 -0
- package/scripts/flow-instruction-richness.js +827 -0
- package/scripts/flow-jira-integration.js +579 -0
- package/scripts/flow-knowledge-router.js +522 -0
- package/scripts/flow-knowledge-sync.js +589 -0
- package/scripts/flow-linear-integration.js +631 -0
- package/scripts/flow-links.js +774 -0
- package/scripts/flow-log-manager.js +559 -0
- package/scripts/flow-loop-enforcer.js +1246 -0
- package/scripts/flow-loop-retry-learning.js +630 -0
- package/scripts/flow-lsp.js +923 -0
- package/scripts/flow-map-index +348 -0
- package/scripts/flow-map-sync +201 -0
- package/scripts/flow-memory-blocks.js +668 -0
- package/scripts/flow-memory-compactor.js +350 -0
- package/scripts/flow-memory-db.js +1110 -0
- package/scripts/flow-memory-sync.js +484 -0
- package/scripts/flow-metrics.js +353 -0
- package/scripts/flow-migrate-ids.js +370 -0
- package/scripts/flow-model-adapter.js +802 -0
- package/scripts/flow-model-router.js +884 -0
- package/scripts/flow-models.js +1231 -0
- package/scripts/flow-morning.js +517 -0
- package/scripts/flow-multi-approach.js +660 -0
- package/scripts/flow-new-feature +86 -0
- package/scripts/flow-onboard +1042 -0
- package/scripts/flow-orchestrate-llm.js +459 -0
- package/scripts/flow-orchestrate.js +3592 -0
- package/scripts/flow-output.js +123 -0
- package/scripts/flow-parallel-detector.js +399 -0
- package/scripts/flow-parallel-dispatch.js +987 -0
- package/scripts/flow-parallel.js +428 -0
- package/scripts/flow-pattern-enforcer.js +600 -0
- package/scripts/flow-prd-manager.js +282 -0
- package/scripts/flow-progress.js +323 -0
- package/scripts/flow-project-analyzer.js +975 -0
- package/scripts/flow-prompt-composer.js +487 -0
- package/scripts/flow-providers.js +1381 -0
- package/scripts/flow-queue.js +308 -0
- package/scripts/flow-ready +82 -0
- package/scripts/flow-ready.js +189 -0
- package/scripts/flow-regression.js +396 -0
- package/scripts/flow-response-parser.js +450 -0
- package/scripts/flow-resume.js +284 -0
- package/scripts/flow-rules-sync.js +439 -0
- package/scripts/flow-run-trace.js +718 -0
- package/scripts/flow-safety.js +587 -0
- package/scripts/flow-search +104 -0
- package/scripts/flow-security.js +481 -0
- package/scripts/flow-session-end +106 -0
- package/scripts/flow-session-end.js +437 -0
- package/scripts/flow-session-state.js +671 -0
- package/scripts/flow-setup-hooks +216 -0
- package/scripts/flow-setup-hooks.js +377 -0
- package/scripts/flow-skill-create.js +329 -0
- package/scripts/flow-skill-creator.js +572 -0
- package/scripts/flow-skill-generator.js +1046 -0
- package/scripts/flow-skill-learn.js +880 -0
- package/scripts/flow-skill-matcher.js +578 -0
- package/scripts/flow-spec-generator.js +820 -0
- package/scripts/flow-stack-wizard.js +895 -0
- package/scripts/flow-standup +162 -0
- package/scripts/flow-start +74 -0
- package/scripts/flow-start.js +235 -0
- package/scripts/flow-status +110 -0
- package/scripts/flow-status.js +301 -0
- package/scripts/flow-step-browser.js +83 -0
- package/scripts/flow-step-changelog.js +217 -0
- package/scripts/flow-step-comments.js +306 -0
- package/scripts/flow-step-complexity.js +234 -0
- package/scripts/flow-step-coverage.js +218 -0
- package/scripts/flow-step-knowledge.js +193 -0
- package/scripts/flow-step-pr-tests.js +364 -0
- package/scripts/flow-step-regression.js +89 -0
- package/scripts/flow-step-review.js +516 -0
- package/scripts/flow-step-security.js +162 -0
- package/scripts/flow-step-silent-failures.js +290 -0
- package/scripts/flow-step-simplifier.js +346 -0
- package/scripts/flow-story +105 -0
- package/scripts/flow-story.js +500 -0
- package/scripts/flow-suspend.js +252 -0
- package/scripts/flow-sync-daemon.js +654 -0
- package/scripts/flow-task-analyzer.js +606 -0
- package/scripts/flow-team-dashboard.js +748 -0
- package/scripts/flow-team-sync.js +752 -0
- package/scripts/flow-team.js +977 -0
- package/scripts/flow-tech-options.js +528 -0
- package/scripts/flow-templates.js +812 -0
- package/scripts/flow-tiered-learning.js +728 -0
- package/scripts/flow-trace +204 -0
- package/scripts/flow-transcript-chunking.js +1106 -0
- package/scripts/flow-transcript-digest.js +7918 -0
- package/scripts/flow-transcript-language.js +465 -0
- package/scripts/flow-transcript-parsing.js +1085 -0
- package/scripts/flow-transcript-stories.js +2194 -0
- package/scripts/flow-update-map +224 -0
- package/scripts/flow-utils.js +2242 -0
- package/scripts/flow-verification.js +644 -0
- package/scripts/flow-verify.js +1177 -0
- package/scripts/flow-voice-input.js +638 -0
- package/scripts/flow-watch +168 -0
- package/scripts/flow-workflow-steps.js +521 -0
- package/scripts/flow-workflow.js +1029 -0
- package/scripts/flow-worktree.js +489 -0
- package/scripts/hooks/adapters/base-adapter.js +102 -0
- package/scripts/hooks/adapters/claude-code.js +359 -0
- package/scripts/hooks/adapters/index.js +79 -0
- package/scripts/hooks/core/component-check.js +341 -0
- package/scripts/hooks/core/index.js +35 -0
- package/scripts/hooks/core/loop-check.js +241 -0
- package/scripts/hooks/core/session-context.js +294 -0
- package/scripts/hooks/core/task-gate.js +177 -0
- package/scripts/hooks/core/validation.js +230 -0
- package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
- package/scripts/hooks/entry/claude-code/session-end.js +87 -0
- package/scripts/hooks/entry/claude-code/session-start.js +46 -0
- package/scripts/hooks/entry/claude-code/stop.js +43 -0
- package/scripts/postinstall.js +139 -0
- package/templates/browser-test-flow.json +56 -0
- package/templates/bug-report.md +43 -0
- package/templates/component-detail.md +42 -0
- package/templates/component.stories.tsx +49 -0
- package/templates/context/constraints.md +83 -0
- package/templates/context/conventions.md +177 -0
- package/templates/context/stack.md +60 -0
- package/templates/correction-report.md +90 -0
- package/templates/feature-proposal.md +35 -0
- package/templates/hybrid/_base.md +254 -0
- package/templates/hybrid/_patterns.md +45 -0
- package/templates/hybrid/create-component.md +127 -0
- package/templates/hybrid/create-file.md +56 -0
- package/templates/hybrid/create-hook.md +145 -0
- package/templates/hybrid/create-service.md +70 -0
- package/templates/hybrid/fix-bug.md +33 -0
- package/templates/hybrid/modify-file.md +55 -0
- package/templates/story.md +68 -0
- package/templates/task.json +56 -0
- package/templates/trace.md +69 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Figma Analyzer MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Standalone MCP server that provides component analysis tools.
|
|
7
|
+
* Can be used with any MCP client (Claude Desktop, Cursor, VS Code, etc.)
|
|
8
|
+
*
|
|
9
|
+
* Supports both:
|
|
10
|
+
* - stdio mode (standard MCP protocol for Claude Desktop)
|
|
11
|
+
* - HTTP mode (for testing and web clients)
|
|
12
|
+
*
|
|
13
|
+
* Tools provided:
|
|
14
|
+
* - wogi_figma_analyze: Analyze a Figma screen and match components
|
|
15
|
+
* - wogi_figma_registry: Get the codebase component registry
|
|
16
|
+
* - wogi_figma_match: Match a single component against registry
|
|
17
|
+
* - wogi_figma_generate: Generate code from confirmed decisions
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* node flow-figma-mcp-server.js # stdio mode (for MCP clients)
|
|
21
|
+
* node flow-figma-mcp-server.js --http # HTTP mode (port 3847)
|
|
22
|
+
* node flow-figma-mcp-server.js --http 8080 # HTTP on custom port
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const http = require('http');
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const path = require('path');
|
|
28
|
+
const readline = require('readline');
|
|
29
|
+
|
|
30
|
+
const { ComponentScanner } = require('./flow-figma-index');
|
|
31
|
+
const { FigmaExtractor } = require('./flow-figma-extract');
|
|
32
|
+
const { SimilarityMatcher, MATCH_CONFIG } = require('./flow-figma-match');
|
|
33
|
+
const { CodeGenerator } = require('./flow-figma-generate');
|
|
34
|
+
const { getProjectRoot } = require('./flow-utils');
|
|
35
|
+
|
|
36
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
37
|
+
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
38
|
+
const REGISTRY_PATH = path.join(WORKFLOW_DIR, 'state', 'component-registry.json');
|
|
39
|
+
|
|
40
|
+
// ============================================================
|
|
41
|
+
// Tool Definitions
|
|
42
|
+
// ============================================================
|
|
43
|
+
|
|
44
|
+
const TOOLS = [
|
|
45
|
+
{
|
|
46
|
+
name: 'wogi_figma_analyze',
|
|
47
|
+
description: 'Analyze a Figma screen/component and match against your codebase. Returns components with match suggestions (use existing, add variant, or create new).',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
figma_data: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
description: 'The Figma MCP response data (from get_code, get_metadata, or node data)'
|
|
54
|
+
},
|
|
55
|
+
threshold: {
|
|
56
|
+
type: 'number',
|
|
57
|
+
description: 'Minimum match score (0-100) to consider as a match. Default: 60',
|
|
58
|
+
default: 60
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
required: ['figma_data']
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'wogi_figma_registry',
|
|
66
|
+
description: 'Get or scan the component registry from the codebase. Shows all existing components that can be reused.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
scan: {
|
|
71
|
+
type: 'boolean',
|
|
72
|
+
description: 'If true, rescan the codebase before returning registry. Default: false',
|
|
73
|
+
default: false
|
|
74
|
+
},
|
|
75
|
+
filter: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
description: 'Filter components by type: "atom", "molecule", or "organism"'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'wogi_figma_match',
|
|
84
|
+
description: 'Match a single extracted Figma component against the registry.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
component: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
description: 'The extracted Figma component (from wogi_figma_analyze)'
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
required: ['component']
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'wogi_figma_generate',
|
|
98
|
+
description: 'Generate code prompts and imports from confirmed component decisions.',
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
decisions: {
|
|
103
|
+
type: 'array',
|
|
104
|
+
description: 'Array of component decisions with action (use, add-variant, create-new) and details'
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
required: ['decisions']
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
// ============================================================
|
|
113
|
+
// MCP Handler
|
|
114
|
+
// ============================================================
|
|
115
|
+
|
|
116
|
+
class FigmaAnalyzerMCP {
|
|
117
|
+
constructor() {
|
|
118
|
+
this.tools = TOOLS;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async handleToolCall(toolName, args) {
|
|
122
|
+
switch (toolName) {
|
|
123
|
+
case 'wogi_figma_analyze':
|
|
124
|
+
return this.analyzeScreen(args.figma_data, args.threshold || 60);
|
|
125
|
+
|
|
126
|
+
case 'wogi_figma_registry':
|
|
127
|
+
return this.getRegistry(args.scan, args.filter);
|
|
128
|
+
|
|
129
|
+
case 'wogi_figma_match':
|
|
130
|
+
return this.matchComponent(args.component);
|
|
131
|
+
|
|
132
|
+
case 'wogi_figma_generate':
|
|
133
|
+
return this.generateCode(args.decisions);
|
|
134
|
+
|
|
135
|
+
default:
|
|
136
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async analyzeScreen(figmaData, threshold) {
|
|
141
|
+
// Extract components from Figma data
|
|
142
|
+
const extractor = new FigmaExtractor();
|
|
143
|
+
const extracted = extractor.parse(figmaData);
|
|
144
|
+
|
|
145
|
+
// Load registry
|
|
146
|
+
const registry = this.loadRegistry();
|
|
147
|
+
if (!registry) {
|
|
148
|
+
return {
|
|
149
|
+
error: 'Component registry not found. Call wogi_figma_registry with scan=true first.',
|
|
150
|
+
suggestion: 'Run: wogi_figma_registry({ scan: true })'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Match components
|
|
155
|
+
const matcher = new SimilarityMatcher(registry);
|
|
156
|
+
const results = matcher.matchAll(extracted.components);
|
|
157
|
+
|
|
158
|
+
// Format for readable output
|
|
159
|
+
return {
|
|
160
|
+
summary: results.summary,
|
|
161
|
+
thresholds: MATCH_CONFIG.thresholds,
|
|
162
|
+
components: results.matches.map(m => ({
|
|
163
|
+
name: m.figmaComponent.name,
|
|
164
|
+
type: m.figmaComponent.type,
|
|
165
|
+
figmaType: m.figmaComponent.figmaType,
|
|
166
|
+
bestMatch: m.bestMatch ? {
|
|
167
|
+
name: m.bestMatch.registryComponent.name,
|
|
168
|
+
path: m.bestMatch.registryComponent.path,
|
|
169
|
+
score: m.bestMatch.score,
|
|
170
|
+
breakdown: m.bestMatch.breakdown,
|
|
171
|
+
differences: m.bestMatch.differences?.slice(0, 3) // Limit differences
|
|
172
|
+
} : null,
|
|
173
|
+
recommendation: m.suggestion
|
|
174
|
+
})),
|
|
175
|
+
tokens: extracted.tokens
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async getRegistry(scan = false, filter = null) {
|
|
180
|
+
if (scan) {
|
|
181
|
+
const scanner = new ComponentScanner();
|
|
182
|
+
await scanner.scan();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const registry = this.loadRegistry();
|
|
186
|
+
if (!registry) {
|
|
187
|
+
return {
|
|
188
|
+
error: 'Registry not found. Set scan=true to scan the codebase.',
|
|
189
|
+
suggestion: 'Run: wogi_figma_registry({ scan: true })'
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let components = registry.components;
|
|
194
|
+
|
|
195
|
+
// Apply filter if specified
|
|
196
|
+
if (filter && ['atom', 'molecule', 'organism'].includes(filter)) {
|
|
197
|
+
components = components.filter(c => c.type === filter);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
framework: registry.framework,
|
|
202
|
+
scannedAt: registry.scannedAt,
|
|
203
|
+
componentCount: components.length,
|
|
204
|
+
components: components.map(c => ({
|
|
205
|
+
name: c.name,
|
|
206
|
+
path: c.path,
|
|
207
|
+
type: c.type,
|
|
208
|
+
variants: c.variants,
|
|
209
|
+
props: c.props?.map(p => ({ name: p.name, type: p.type }))
|
|
210
|
+
})),
|
|
211
|
+
tokenCount: {
|
|
212
|
+
colors: Object.keys(registry.tokens?.colors || {}).length,
|
|
213
|
+
spacing: Object.keys(registry.tokens?.spacing || {}).length,
|
|
214
|
+
typography: Object.keys(registry.tokens?.typography || {}).length
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async matchComponent(component) {
|
|
220
|
+
const registry = this.loadRegistry();
|
|
221
|
+
if (!registry) {
|
|
222
|
+
return { error: 'Registry not found' };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const matcher = new SimilarityMatcher(registry);
|
|
226
|
+
const result = matcher.matchComponent(component);
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
figmaComponent: result.figmaComponent,
|
|
230
|
+
matches: result.matches.slice(0, 3).map(m => ({
|
|
231
|
+
name: m.registryComponent.name,
|
|
232
|
+
path: m.registryComponent.path,
|
|
233
|
+
score: m.score,
|
|
234
|
+
suggestion: m.suggestion
|
|
235
|
+
})),
|
|
236
|
+
recommendation: result.suggestion
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async generateCode(decisions) {
|
|
241
|
+
const generator = new CodeGenerator({ decisions });
|
|
242
|
+
const output = generator.generate();
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
framework: output.framework,
|
|
246
|
+
imports: output.imports.map(i => ({
|
|
247
|
+
component: i.componentName,
|
|
248
|
+
import: i.importStatement,
|
|
249
|
+
usage: i.usage
|
|
250
|
+
})),
|
|
251
|
+
newComponents: output.newComponents.map(c => ({
|
|
252
|
+
name: c.componentName,
|
|
253
|
+
path: c.suggestedPath,
|
|
254
|
+
prompt: c.prompt
|
|
255
|
+
})),
|
|
256
|
+
variants: output.variants.map(v => ({
|
|
257
|
+
component: v.componentName,
|
|
258
|
+
variant: v.variantName,
|
|
259
|
+
prompt: v.prompt
|
|
260
|
+
}))
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
loadRegistry() {
|
|
265
|
+
if (fs.existsSync(REGISTRY_PATH)) {
|
|
266
|
+
try {
|
|
267
|
+
return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8'));
|
|
268
|
+
} catch {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ============================================================
|
|
277
|
+
// stdio Mode (Standard MCP Protocol)
|
|
278
|
+
// ============================================================
|
|
279
|
+
|
|
280
|
+
class StdioServer {
|
|
281
|
+
constructor(handler) {
|
|
282
|
+
this.handler = handler;
|
|
283
|
+
this.rl = readline.createInterface({
|
|
284
|
+
input: process.stdin,
|
|
285
|
+
output: process.stdout,
|
|
286
|
+
terminal: false
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
start() {
|
|
291
|
+
this.rl.on('line', async (line) => {
|
|
292
|
+
if (!line.trim()) return;
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const request = JSON.parse(line);
|
|
296
|
+
const response = await this.handleRequest(request);
|
|
297
|
+
console.log(JSON.stringify(response));
|
|
298
|
+
} catch (err) {
|
|
299
|
+
console.log(JSON.stringify({
|
|
300
|
+
jsonrpc: '2.0',
|
|
301
|
+
error: { code: -32700, message: 'Parse error', data: err.message },
|
|
302
|
+
id: null
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Send capabilities on start
|
|
308
|
+
console.error('Wogi Flow Figma Analyzer MCP Server (stdio mode)');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async handleRequest(request) {
|
|
312
|
+
const { method, params, id } = request;
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
let result;
|
|
316
|
+
|
|
317
|
+
switch (method) {
|
|
318
|
+
case 'initialize':
|
|
319
|
+
result = {
|
|
320
|
+
protocolVersion: '2024-11-05',
|
|
321
|
+
capabilities: {
|
|
322
|
+
tools: {}
|
|
323
|
+
},
|
|
324
|
+
serverInfo: {
|
|
325
|
+
name: 'wogi-figma-analyzer',
|
|
326
|
+
version: '1.0.0'
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
break;
|
|
330
|
+
|
|
331
|
+
case 'tools/list':
|
|
332
|
+
result = { tools: this.handler.tools };
|
|
333
|
+
break;
|
|
334
|
+
|
|
335
|
+
case 'tools/call':
|
|
336
|
+
const toolResult = await this.handler.handleToolCall(
|
|
337
|
+
params.name,
|
|
338
|
+
params.arguments || {}
|
|
339
|
+
);
|
|
340
|
+
result = {
|
|
341
|
+
content: [{
|
|
342
|
+
type: 'text',
|
|
343
|
+
text: JSON.stringify(toolResult, null, 2)
|
|
344
|
+
}]
|
|
345
|
+
};
|
|
346
|
+
break;
|
|
347
|
+
|
|
348
|
+
case 'ping':
|
|
349
|
+
result = {};
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
default:
|
|
353
|
+
throw new Error(`Unknown method: ${method}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return { jsonrpc: '2.0', result, id };
|
|
357
|
+
|
|
358
|
+
} catch (err) {
|
|
359
|
+
return {
|
|
360
|
+
jsonrpc: '2.0',
|
|
361
|
+
error: { code: -32603, message: err.message },
|
|
362
|
+
id
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ============================================================
|
|
369
|
+
// HTTP Mode (for testing)
|
|
370
|
+
// ============================================================
|
|
371
|
+
|
|
372
|
+
class HttpServer {
|
|
373
|
+
constructor(handler, port = 3847) {
|
|
374
|
+
this.handler = handler;
|
|
375
|
+
this.port = port;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
start() {
|
|
379
|
+
const server = http.createServer(async (req, res) => {
|
|
380
|
+
// CORS headers
|
|
381
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
382
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
|
|
383
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
384
|
+
|
|
385
|
+
if (req.method === 'OPTIONS') {
|
|
386
|
+
res.writeHead(200);
|
|
387
|
+
res.end();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Health check
|
|
392
|
+
if (req.method === 'GET' && req.url === '/health') {
|
|
393
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
394
|
+
res.end(JSON.stringify({ status: 'ok', tools: this.handler.tools.length }));
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (req.method !== 'POST') {
|
|
399
|
+
res.writeHead(405);
|
|
400
|
+
res.end('Method not allowed');
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
let body = '';
|
|
405
|
+
req.on('data', chunk => body += chunk);
|
|
406
|
+
req.on('end', async () => {
|
|
407
|
+
try {
|
|
408
|
+
const request = JSON.parse(body);
|
|
409
|
+
let response;
|
|
410
|
+
|
|
411
|
+
if (request.method === 'tools/list') {
|
|
412
|
+
response = { tools: this.handler.tools };
|
|
413
|
+
} else if (request.method === 'tools/call') {
|
|
414
|
+
const result = await this.handler.handleToolCall(
|
|
415
|
+
request.params.name,
|
|
416
|
+
request.params.arguments || {}
|
|
417
|
+
);
|
|
418
|
+
response = {
|
|
419
|
+
content: [{
|
|
420
|
+
type: 'text',
|
|
421
|
+
text: JSON.stringify(result, null, 2)
|
|
422
|
+
}]
|
|
423
|
+
};
|
|
424
|
+
} else {
|
|
425
|
+
response = { error: 'Unknown method' };
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
429
|
+
res.end(JSON.stringify(response));
|
|
430
|
+
|
|
431
|
+
} catch (err) {
|
|
432
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
433
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
server.listen(this.port, () => {
|
|
439
|
+
console.log(`
|
|
440
|
+
╔═══════════════════════════════════════════════════════════════════╗
|
|
441
|
+
║ Wogi Flow - Figma Analyzer MCP Server (HTTP) ║
|
|
442
|
+
╚═══════════════════════════════════════════════════════════════════╝
|
|
443
|
+
|
|
444
|
+
Server running at http://localhost:${this.port}
|
|
445
|
+
|
|
446
|
+
Endpoints:
|
|
447
|
+
POST / MCP protocol
|
|
448
|
+
GET /health Health check
|
|
449
|
+
|
|
450
|
+
Available tools:
|
|
451
|
+
• wogi_figma_analyze - Analyze Figma screen
|
|
452
|
+
• wogi_figma_registry - Get component registry
|
|
453
|
+
• wogi_figma_match - Match single component
|
|
454
|
+
• wogi_figma_generate - Generate code from decisions
|
|
455
|
+
|
|
456
|
+
Test with:
|
|
457
|
+
curl -X POST http://localhost:${this.port} \\
|
|
458
|
+
-H "Content-Type: application/json" \\
|
|
459
|
+
-d '{"method":"tools/list"}'
|
|
460
|
+
`);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ============================================================
|
|
466
|
+
// CLI
|
|
467
|
+
// ============================================================
|
|
468
|
+
|
|
469
|
+
function main() {
|
|
470
|
+
const args = process.argv.slice(2);
|
|
471
|
+
const handler = new FigmaAnalyzerMCP();
|
|
472
|
+
|
|
473
|
+
if (args.includes('--http')) {
|
|
474
|
+
const portIndex = args.indexOf('--http') + 1;
|
|
475
|
+
const port = parseInt(args[portIndex]) || 3847;
|
|
476
|
+
const server = new HttpServer(handler, port);
|
|
477
|
+
server.start();
|
|
478
|
+
} else if (args.includes('--help') || args.includes('-h')) {
|
|
479
|
+
console.log(`
|
|
480
|
+
Wogi Flow - Figma Analyzer MCP Server
|
|
481
|
+
|
|
482
|
+
Usage:
|
|
483
|
+
node flow-figma-mcp-server.js # stdio mode (for MCP clients)
|
|
484
|
+
node flow-figma-mcp-server.js --http # HTTP mode (port 3847)
|
|
485
|
+
node flow-figma-mcp-server.js --http 8080 # HTTP on custom port
|
|
486
|
+
|
|
487
|
+
Add to Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
|
|
488
|
+
{
|
|
489
|
+
"mcpServers": {
|
|
490
|
+
"wogi-figma": {
|
|
491
|
+
"command": "node",
|
|
492
|
+
"args": ["${path.resolve(__dirname, 'flow-figma-mcp-server.js')}"]
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
Add to Cursor MCP config:
|
|
498
|
+
{
|
|
499
|
+
"mcpServers": {
|
|
500
|
+
"wogi-figma": {
|
|
501
|
+
"command": "node",
|
|
502
|
+
"args": ["${path.resolve(__dirname, 'flow-figma-mcp-server.js')}"]
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
`);
|
|
507
|
+
} else {
|
|
508
|
+
// Default: stdio mode
|
|
509
|
+
const server = new StdioServer(handler);
|
|
510
|
+
server.start();
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
module.exports = { FigmaAnalyzerMCP, StdioServer, HttpServer };
|
|
515
|
+
|
|
516
|
+
if (require.main === module) {
|
|
517
|
+
main();
|
|
518
|
+
}
|