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,400 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Interactive Component Confirmation
|
|
5
|
+
*
|
|
6
|
+
* Presents match results to developer and collects decisions
|
|
7
|
+
* for each component (use existing, add variant, create new, skip).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* flow figma confirm <match-results.json> # Interactive mode
|
|
11
|
+
* flow figma confirm --auto # Auto-confirm high matches
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const readline = require('readline');
|
|
17
|
+
const { getProjectRoot, colors: c } = require('./flow-utils');
|
|
18
|
+
|
|
19
|
+
const PROJECT_ROOT = getProjectRoot();
|
|
20
|
+
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
21
|
+
const DECISIONS_PATH = path.join(WORKFLOW_DIR, 'state', 'figma-decisions.json');
|
|
22
|
+
|
|
23
|
+
const sym = {
|
|
24
|
+
check: '✅',
|
|
25
|
+
cross: '❌',
|
|
26
|
+
warning: '⚠️',
|
|
27
|
+
new: '🆕',
|
|
28
|
+
variant: '➕',
|
|
29
|
+
skip: '⏭️',
|
|
30
|
+
match: '🎯'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ============================================================
|
|
34
|
+
// Interactive Confirmer
|
|
35
|
+
// ============================================================
|
|
36
|
+
|
|
37
|
+
class InteractiveConfirmer {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.decisions = [];
|
|
40
|
+
this.rl = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async confirm(matchResults) {
|
|
44
|
+
this.rl = readline.createInterface({
|
|
45
|
+
input: process.stdin,
|
|
46
|
+
output: process.stdout
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Print header
|
|
50
|
+
this.printHeader(matchResults.summary);
|
|
51
|
+
|
|
52
|
+
// Process each component
|
|
53
|
+
for (const match of matchResults.matches) {
|
|
54
|
+
const decision = await this.confirmComponent(match);
|
|
55
|
+
this.decisions.push(decision);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.rl.close();
|
|
59
|
+
|
|
60
|
+
// Print summary
|
|
61
|
+
this.printSummary();
|
|
62
|
+
|
|
63
|
+
// Save decisions
|
|
64
|
+
this.saveDecisions();
|
|
65
|
+
|
|
66
|
+
return this.decisions;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
printHeader(summary) {
|
|
70
|
+
console.log(`
|
|
71
|
+
${c.cyan}╔═══════════════════════════════════════════════════════════════════╗
|
|
72
|
+
║ FIGMA COMPONENT ANALYZER ║
|
|
73
|
+
║ Interactive Confirmation Flow ║
|
|
74
|
+
╚═══════════════════════════════════════════════════════════════════╝${c.reset}
|
|
75
|
+
|
|
76
|
+
${c.bold}Analysis Summary:${c.reset}
|
|
77
|
+
Total components: ${summary.total}
|
|
78
|
+
${c.green}Exact matches: ${summary.exactMatches}${c.reset}
|
|
79
|
+
${c.yellow}Strong matches: ${summary.strongMatches}${c.reset}
|
|
80
|
+
${c.blue}Variant candidates: ${summary.variantCandidates}${c.reset}
|
|
81
|
+
${c.magenta}New components: ${summary.newComponents}${c.reset}
|
|
82
|
+
|
|
83
|
+
Let's go through each component:
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async confirmComponent(match) {
|
|
88
|
+
const { figmaComponent, bestMatch, suggestion } = match;
|
|
89
|
+
|
|
90
|
+
console.log(`\n${'━'.repeat(70)}`);
|
|
91
|
+
console.log(`\n${c.bold}📦 ${figmaComponent.name}${c.reset} (${figmaComponent.type})`);
|
|
92
|
+
|
|
93
|
+
if (!bestMatch) {
|
|
94
|
+
console.log(`\n ${c.dim}No matching components found in codebase${c.reset}`);
|
|
95
|
+
console.log(` Suggestion: ${c.magenta}Create new component${c.reset}`);
|
|
96
|
+
|
|
97
|
+
return this.promptNewComponent(figmaComponent, suggestion);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Show match details
|
|
101
|
+
const score = bestMatch.score;
|
|
102
|
+
const scoreColor = score >= 80 ? c.green : score >= 60 ? c.yellow : c.red;
|
|
103
|
+
|
|
104
|
+
console.log(`\n ${c.bold}Best Match:${c.reset} ${bestMatch.registryComponent.name}`);
|
|
105
|
+
console.log(` ${c.bold}Score:${c.reset} ${scoreColor}${score}%${c.reset}`);
|
|
106
|
+
console.log(` ${c.bold}Path:${c.reset} ${c.dim}${bestMatch.registryComponent.path}${c.reset}`);
|
|
107
|
+
|
|
108
|
+
// Show score breakdown
|
|
109
|
+
console.log(`\n ${c.dim}Score Breakdown:${c.reset}`);
|
|
110
|
+
console.log(` ${c.dim}├─ CSS: ${bestMatch.breakdown.css}%${c.reset}`);
|
|
111
|
+
console.log(` ${c.dim}├─ Structure: ${bestMatch.breakdown.structure}%${c.reset}`);
|
|
112
|
+
console.log(` ${c.dim}├─ Naming: ${bestMatch.breakdown.naming}%${c.reset}`);
|
|
113
|
+
console.log(` ${c.dim}└─ Behavior: ${bestMatch.breakdown.behavior}%${c.reset}`);
|
|
114
|
+
|
|
115
|
+
// Show differences if any
|
|
116
|
+
if (bestMatch.differences && bestMatch.differences.length > 0) {
|
|
117
|
+
console.log(`\n ${c.yellow}Differences:${c.reset}`);
|
|
118
|
+
bestMatch.differences.forEach(diff => {
|
|
119
|
+
console.log(` ${c.dim}• ${diff.description}${c.reset}`);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Show suggestion
|
|
124
|
+
console.log(`\n ${c.bold}Suggestion:${c.reset} ${this.formatSuggestion(suggestion)}`);
|
|
125
|
+
|
|
126
|
+
// Prompt for decision
|
|
127
|
+
return this.promptDecision(figmaComponent, bestMatch, suggestion);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
formatSuggestion(suggestion) {
|
|
131
|
+
switch (suggestion.action) {
|
|
132
|
+
case 'use':
|
|
133
|
+
return `${c.green}${sym.check} Use existing ${suggestion.component}${c.reset}`;
|
|
134
|
+
case 'use-with-adjustments':
|
|
135
|
+
return `${c.yellow}${sym.warning} Use ${suggestion.component} with adjustments${c.reset}`;
|
|
136
|
+
case 'add-variant':
|
|
137
|
+
return `${c.blue}${sym.variant} Add variant "${suggestion.suggestedVariantName || 'new'}" to ${suggestion.component}${c.reset}`;
|
|
138
|
+
case 'create-new':
|
|
139
|
+
return `${c.magenta}${sym.new} Create new component${c.reset}`;
|
|
140
|
+
default:
|
|
141
|
+
return suggestion.action;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async promptDecision(figmaComponent, bestMatch, suggestion) {
|
|
146
|
+
console.log(`\n ${c.bold}Options:${c.reset}`);
|
|
147
|
+
console.log(` ${c.cyan}[1]${c.reset} ${sym.check} Use existing ${bestMatch.registryComponent.name}`);
|
|
148
|
+
console.log(` ${c.cyan}[2]${c.reset} ${sym.variant} Add as variant to ${bestMatch.registryComponent.name}`);
|
|
149
|
+
console.log(` ${c.cyan}[3]${c.reset} ${sym.new} Create new component`);
|
|
150
|
+
console.log(` ${c.cyan}[4]${c.reset} ${sym.skip} Skip - I'll handle manually`);
|
|
151
|
+
console.log(` ${c.cyan}[Enter]${c.reset} Accept suggestion`);
|
|
152
|
+
|
|
153
|
+
const choice = await this.prompt('\n Your choice: ');
|
|
154
|
+
|
|
155
|
+
let action;
|
|
156
|
+
switch (choice.trim()) {
|
|
157
|
+
case '1':
|
|
158
|
+
action = 'use';
|
|
159
|
+
break;
|
|
160
|
+
case '2':
|
|
161
|
+
action = 'add-variant';
|
|
162
|
+
break;
|
|
163
|
+
case '3':
|
|
164
|
+
action = 'create-new';
|
|
165
|
+
break;
|
|
166
|
+
case '4':
|
|
167
|
+
action = 'skip';
|
|
168
|
+
break;
|
|
169
|
+
case '':
|
|
170
|
+
action = suggestion.action;
|
|
171
|
+
break;
|
|
172
|
+
default:
|
|
173
|
+
console.log(` ${c.dim}Invalid choice, using suggestion${c.reset}`);
|
|
174
|
+
action = suggestion.action;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Get additional info based on action
|
|
178
|
+
let extraInfo = {};
|
|
179
|
+
|
|
180
|
+
if (action === 'add-variant') {
|
|
181
|
+
const defaultVariant = suggestion.suggestedVariantName || 'variant';
|
|
182
|
+
const variantName = await this.prompt(` Variant name [${defaultVariant}]: `);
|
|
183
|
+
extraInfo.variantName = variantName.trim() || defaultVariant;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (action === 'create-new') {
|
|
187
|
+
const suggestedName = suggestion.suggestedName || this.suggestName(figmaComponent.name);
|
|
188
|
+
const componentName = await this.prompt(` Component name [${suggestedName}]: `);
|
|
189
|
+
extraInfo.componentName = componentName.trim() || suggestedName;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log(`\n ${c.green}✓ ${this.formatAction(action, bestMatch?.registryComponent, extraInfo)}${c.reset}`);
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
figmaComponent: figmaComponent,
|
|
196
|
+
action: action,
|
|
197
|
+
existingComponent: action !== 'create-new' && action !== 'skip' ? bestMatch.registryComponent : null,
|
|
198
|
+
score: bestMatch?.score || 0,
|
|
199
|
+
...extraInfo
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async promptNewComponent(figmaComponent, suggestion) {
|
|
204
|
+
console.log(`\n ${c.bold}Options:${c.reset}`);
|
|
205
|
+
console.log(` ${c.cyan}[1]${c.reset} ${sym.new} Create new component`);
|
|
206
|
+
console.log(` ${c.cyan}[2]${c.reset} ${sym.skip} Skip - I'll handle manually`);
|
|
207
|
+
|
|
208
|
+
const choice = await this.prompt('\n Your choice [1]: ');
|
|
209
|
+
|
|
210
|
+
if (choice.trim() === '2') {
|
|
211
|
+
return {
|
|
212
|
+
figmaComponent: figmaComponent,
|
|
213
|
+
action: 'skip'
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const suggestedName = suggestion.suggestedName || this.suggestName(figmaComponent.name);
|
|
218
|
+
const componentName = await this.prompt(` Component name [${suggestedName}]: `);
|
|
219
|
+
|
|
220
|
+
console.log(`\n ${c.green}✓ Will create new component: ${componentName || suggestedName}${c.reset}`);
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
figmaComponent: figmaComponent,
|
|
224
|
+
action: 'create-new',
|
|
225
|
+
componentName: componentName.trim() || suggestedName
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
formatAction(action, existingComponent, extraInfo) {
|
|
230
|
+
switch (action) {
|
|
231
|
+
case 'use':
|
|
232
|
+
return `Will use existing ${existingComponent?.name || 'component'}`;
|
|
233
|
+
case 'use-with-adjustments':
|
|
234
|
+
return `Will use ${existingComponent?.name || 'component'} with adjustments`;
|
|
235
|
+
case 'add-variant':
|
|
236
|
+
return `Will add variant "${extraInfo.variantName}" to ${existingComponent?.name || 'component'}`;
|
|
237
|
+
case 'create-new':
|
|
238
|
+
return `Will create new component: ${extraInfo.componentName}`;
|
|
239
|
+
case 'skip':
|
|
240
|
+
return `Skipped - handle manually`;
|
|
241
|
+
default:
|
|
242
|
+
return action;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
suggestName(figmaName) {
|
|
247
|
+
return (figmaName || 'Component')
|
|
248
|
+
.replace(/[^a-zA-Z0-9\s]/g, '')
|
|
249
|
+
.split(/\s+/)
|
|
250
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
251
|
+
.join('');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
prompt(question) {
|
|
255
|
+
return new Promise(resolve => {
|
|
256
|
+
this.rl.question(question, resolve);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
printSummary() {
|
|
261
|
+
const summary = {
|
|
262
|
+
use: 0,
|
|
263
|
+
'use-with-adjustments': 0,
|
|
264
|
+
'add-variant': 0,
|
|
265
|
+
'create-new': 0,
|
|
266
|
+
skip: 0
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
this.decisions.forEach(d => {
|
|
270
|
+
summary[d.action] = (summary[d.action] || 0) + 1;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
console.log(`
|
|
274
|
+
|
|
275
|
+
${'═'.repeat(70)}
|
|
276
|
+
|
|
277
|
+
${c.bold}CONFIRMATION SUMMARY${c.reset}
|
|
278
|
+
|
|
279
|
+
${c.green}${sym.check} Use existing:${c.reset} ${summary.use + summary['use-with-adjustments']}
|
|
280
|
+
${c.blue}${sym.variant} Add variant:${c.reset} ${summary['add-variant']}
|
|
281
|
+
${c.magenta}${sym.new} Create new:${c.reset} ${summary['create-new']}
|
|
282
|
+
${c.dim}${sym.skip} Skipped:${c.reset} ${summary.skip}
|
|
283
|
+
|
|
284
|
+
`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
saveDecisions() {
|
|
288
|
+
const stateDir = path.dirname(DECISIONS_PATH);
|
|
289
|
+
if (!fs.existsSync(stateDir)) {
|
|
290
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
fs.writeFileSync(DECISIONS_PATH, JSON.stringify({
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
decisions: this.decisions
|
|
296
|
+
}, null, 2));
|
|
297
|
+
|
|
298
|
+
console.log(`${c.dim}Decisions saved to: ${path.relative(PROJECT_ROOT, DECISIONS_PATH)}${c.reset}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ============================================================
|
|
303
|
+
// Auto-Confirm Mode
|
|
304
|
+
// ============================================================
|
|
305
|
+
|
|
306
|
+
class AutoConfirmer {
|
|
307
|
+
constructor(threshold = 80) {
|
|
308
|
+
this.threshold = threshold;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
confirm(matchResults) {
|
|
312
|
+
const decisions = [];
|
|
313
|
+
|
|
314
|
+
for (const match of matchResults.matches) {
|
|
315
|
+
const { figmaComponent, bestMatch, suggestion } = match;
|
|
316
|
+
|
|
317
|
+
let decision;
|
|
318
|
+
|
|
319
|
+
if (!bestMatch || bestMatch.score < this.threshold) {
|
|
320
|
+
// Create new for low/no matches
|
|
321
|
+
decision = {
|
|
322
|
+
figmaComponent: figmaComponent,
|
|
323
|
+
action: 'create-new',
|
|
324
|
+
componentName: suggestion.suggestedName || figmaComponent.name
|
|
325
|
+
};
|
|
326
|
+
} else if (bestMatch.score >= 95) {
|
|
327
|
+
// Use directly for very high matches
|
|
328
|
+
decision = {
|
|
329
|
+
figmaComponent: figmaComponent,
|
|
330
|
+
action: 'use',
|
|
331
|
+
existingComponent: bestMatch.registryComponent,
|
|
332
|
+
score: bestMatch.score
|
|
333
|
+
};
|
|
334
|
+
} else {
|
|
335
|
+
// Use with adjustments for medium-high matches
|
|
336
|
+
decision = {
|
|
337
|
+
figmaComponent: figmaComponent,
|
|
338
|
+
action: 'use-with-adjustments',
|
|
339
|
+
existingComponent: bestMatch.registryComponent,
|
|
340
|
+
score: bestMatch.score,
|
|
341
|
+
differences: bestMatch.differences
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
decisions.push(decision);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return decisions;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ============================================================
|
|
353
|
+
// CLI
|
|
354
|
+
// ============================================================
|
|
355
|
+
|
|
356
|
+
async function main() {
|
|
357
|
+
const [,, input, ...args] = process.argv;
|
|
358
|
+
|
|
359
|
+
const autoMode = args.includes('--auto');
|
|
360
|
+
|
|
361
|
+
if (!input || !fs.existsSync(input)) {
|
|
362
|
+
if (input && input !== '--auto') {
|
|
363
|
+
console.error(`File not found: ${input}`);
|
|
364
|
+
}
|
|
365
|
+
console.log(`
|
|
366
|
+
Wogi Flow - Interactive Component Confirmation
|
|
367
|
+
|
|
368
|
+
Usage:
|
|
369
|
+
flow figma confirm <match-results.json> Interactive mode
|
|
370
|
+
flow figma confirm <match-results.json> --auto Auto-confirm high matches
|
|
371
|
+
|
|
372
|
+
Example:
|
|
373
|
+
./scripts/flow-figma-match.js figma-data.json > matches.json
|
|
374
|
+
./scripts/flow-figma-confirm.js matches.json
|
|
375
|
+
`);
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const matchResults = JSON.parse(fs.readFileSync(input, 'utf-8'));
|
|
380
|
+
|
|
381
|
+
let decisions;
|
|
382
|
+
|
|
383
|
+
if (autoMode) {
|
|
384
|
+
const confirmer = new AutoConfirmer();
|
|
385
|
+
decisions = confirmer.confirm(matchResults);
|
|
386
|
+
console.log(JSON.stringify({ decisions }, null, 2));
|
|
387
|
+
} else {
|
|
388
|
+
const confirmer = new InteractiveConfirmer();
|
|
389
|
+
decisions = await confirmer.confirm(matchResults);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
module.exports = { InteractiveConfirmer, AutoConfirmer };
|
|
394
|
+
|
|
395
|
+
if (require.main === module) {
|
|
396
|
+
main().catch(err => {
|
|
397
|
+
console.error(`Error: ${err.message}`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
});
|
|
400
|
+
}
|