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,895 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enhanced Tech Stack Wizard for Wogi Flow v2.0
|
|
5
|
+
* Interactive questionnaire with summary UI, drill-down customization,
|
|
6
|
+
* and "Let AI decide" option for intelligent defaults
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
// Import centralized tech options
|
|
14
|
+
const {
|
|
15
|
+
PLATFORM_TYPES,
|
|
16
|
+
FOCUS_AREAS,
|
|
17
|
+
FRONTEND_FRAMEWORKS,
|
|
18
|
+
BACKEND_FRAMEWORKS,
|
|
19
|
+
MOBILE_FRAMEWORKS,
|
|
20
|
+
STATE_MANAGEMENT,
|
|
21
|
+
FORM_LIBRARIES,
|
|
22
|
+
STYLING_OPTIONS,
|
|
23
|
+
DATA_FETCHING,
|
|
24
|
+
ANIMATION_LIBRARIES,
|
|
25
|
+
VALIDATION_LIBRARIES,
|
|
26
|
+
DATABASE_OPTIONS,
|
|
27
|
+
ORM_OPTIONS,
|
|
28
|
+
AUTH_OPTIONS,
|
|
29
|
+
TESTING_OPTIONS,
|
|
30
|
+
ADDITIONAL_TOOLS,
|
|
31
|
+
MOBILE_TOOLS,
|
|
32
|
+
ECOSYSTEMS,
|
|
33
|
+
BEST_DEFAULTS,
|
|
34
|
+
getOptionsForFramework,
|
|
35
|
+
getEcosystemDefaults,
|
|
36
|
+
collectTechnologiesFromSelections
|
|
37
|
+
} = require('./flow-tech-options');
|
|
38
|
+
|
|
39
|
+
// ============================================
|
|
40
|
+
// COLORS & FORMATTING
|
|
41
|
+
// ============================================
|
|
42
|
+
|
|
43
|
+
const COLORS = {
|
|
44
|
+
reset: '\x1b[0m',
|
|
45
|
+
bold: '\x1b[1m',
|
|
46
|
+
dim: '\x1b[2m',
|
|
47
|
+
green: '\x1b[32m',
|
|
48
|
+
yellow: '\x1b[33m',
|
|
49
|
+
cyan: '\x1b[36m',
|
|
50
|
+
white: '\x1b[37m'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const c = (color, text) => `${COLORS[color]}${text}${COLORS.reset}`;
|
|
54
|
+
|
|
55
|
+
// ============================================
|
|
56
|
+
// WIZARD CLASS
|
|
57
|
+
// ============================================
|
|
58
|
+
|
|
59
|
+
class EnhancedStackWizard {
|
|
60
|
+
constructor() {
|
|
61
|
+
this.rl = readline.createInterface({
|
|
62
|
+
input: process.stdin,
|
|
63
|
+
output: process.stdout
|
|
64
|
+
});
|
|
65
|
+
this.selections = {};
|
|
66
|
+
this.detectedFramework = process.env.DETECTED_FRAMEWORK || null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async run() {
|
|
70
|
+
this.printHeader();
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// Phase 1: Platform & Focus
|
|
74
|
+
await this.askPlatformAndFocus();
|
|
75
|
+
|
|
76
|
+
// Phase 2: Core Frameworks
|
|
77
|
+
await this.askCoreFrameworks();
|
|
78
|
+
|
|
79
|
+
// Phase 3: Show Summary with Defaults & Allow Customization
|
|
80
|
+
await this.showSummaryAndCustomize();
|
|
81
|
+
|
|
82
|
+
// Phase 4: Testing & Tools
|
|
83
|
+
await this.askTestingAndTools();
|
|
84
|
+
|
|
85
|
+
// Phase 4.5: Workflow Steps (v2.2)
|
|
86
|
+
await this.askWorkflowSteps();
|
|
87
|
+
|
|
88
|
+
// Phase 5: Final Summary & Generation
|
|
89
|
+
await this.finalizeAndGenerate();
|
|
90
|
+
|
|
91
|
+
} finally {
|
|
92
|
+
this.rl.close();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return this.selections;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
printHeader() {
|
|
99
|
+
console.log('\n' + c('cyan', '='.repeat(60)));
|
|
100
|
+
console.log(c('cyan', ' Enhanced Tech Stack Wizard'));
|
|
101
|
+
console.log(c('cyan', ' Configure your project and generate framework-specific skills'));
|
|
102
|
+
console.log(c('cyan', '='.repeat(60)) + '\n');
|
|
103
|
+
|
|
104
|
+
if (this.detectedFramework) {
|
|
105
|
+
console.log(c('green', ` Detected framework: ${this.detectedFramework}`));
|
|
106
|
+
console.log(c('dim', ' Recommendations will be based on this detection.\n'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ============================================
|
|
111
|
+
// PHASE 1: Platform & Focus
|
|
112
|
+
// ============================================
|
|
113
|
+
|
|
114
|
+
async askPlatformAndFocus() {
|
|
115
|
+
// Platform type
|
|
116
|
+
console.log(c('bold', 'Step 1: Platform Type\n'));
|
|
117
|
+
this.selections.projectType = await this.askSingleChoice(
|
|
118
|
+
'What platform are you building for?',
|
|
119
|
+
PLATFORM_TYPES,
|
|
120
|
+
this.detectedFramework ? this.inferProjectType() : null
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Focus area (conditional)
|
|
124
|
+
if (this.needsFocusQuestion()) {
|
|
125
|
+
console.log(c('bold', '\nStep 2: Focus Area\n'));
|
|
126
|
+
this.selections.focus = await this.askSingleChoice(
|
|
127
|
+
"What's your focus?",
|
|
128
|
+
FOCUS_AREAS,
|
|
129
|
+
this.inferFocus()
|
|
130
|
+
);
|
|
131
|
+
} else {
|
|
132
|
+
this.selections.focus = this.inferFocus();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
needsFocusQuestion() {
|
|
137
|
+
const type = this.selections.projectType;
|
|
138
|
+
return ['web', 'fullstack', 'other'].includes(type);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
inferProjectType() {
|
|
142
|
+
if (!this.detectedFramework) return null;
|
|
143
|
+
const mobileFrameworks = ['react-native', 'expo', 'flutter'];
|
|
144
|
+
const backendFrameworks = ['nestjs', 'express', 'fastify', 'fastapi', 'django', 'flask'];
|
|
145
|
+
|
|
146
|
+
if (mobileFrameworks.includes(this.detectedFramework)) return 'mobile';
|
|
147
|
+
if (backendFrameworks.includes(this.detectedFramework)) return 'backend';
|
|
148
|
+
return 'fullstack';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
inferFocus() {
|
|
152
|
+
const type = this.selections.projectType;
|
|
153
|
+
if (['backend', 'cli', 'library'].includes(type)) return 'backend';
|
|
154
|
+
if (['mobile', 'desktop'].includes(type)) return 'frontend';
|
|
155
|
+
return 'both';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================
|
|
159
|
+
// PHASE 2: Core Frameworks
|
|
160
|
+
// ============================================
|
|
161
|
+
|
|
162
|
+
async askCoreFrameworks() {
|
|
163
|
+
// Frontend framework
|
|
164
|
+
if (this.needsFrontend()) {
|
|
165
|
+
console.log(c('bold', '\nStep 3: Frontend Framework\n'));
|
|
166
|
+
|
|
167
|
+
const frameworkOptions = this.selections.projectType === 'mobile'
|
|
168
|
+
? MOBILE_FRAMEWORKS
|
|
169
|
+
: FRONTEND_FRAMEWORKS;
|
|
170
|
+
|
|
171
|
+
const defaultFramework = this.detectedFramework || (
|
|
172
|
+
this.selections.projectType === 'mobile' ? 'expo' : 'nextjs'
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
this.selections.frontend = await this.askGroupedChoice(
|
|
176
|
+
'Select your frontend framework:',
|
|
177
|
+
frameworkOptions,
|
|
178
|
+
defaultFramework
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Backend framework
|
|
183
|
+
if (this.needsBackend()) {
|
|
184
|
+
console.log(c('bold', '\nStep 4: Backend Framework\n'));
|
|
185
|
+
|
|
186
|
+
const defaultBackend = this.detectedFramework &&
|
|
187
|
+
BACKEND_FRAMEWORKS.some(f => f.value === this.detectedFramework)
|
|
188
|
+
? this.detectedFramework
|
|
189
|
+
: 'nestjs';
|
|
190
|
+
|
|
191
|
+
this.selections.backend = await this.askGroupedChoice(
|
|
192
|
+
'Select your backend framework:',
|
|
193
|
+
BACKEND_FRAMEWORKS,
|
|
194
|
+
defaultBackend
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
needsFrontend() {
|
|
200
|
+
return ['frontend', 'both'].includes(this.selections.focus);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
needsBackend() {
|
|
204
|
+
return ['backend', 'both'].includes(this.selections.focus);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ============================================
|
|
208
|
+
// PHASE 3: Summary with Defaults + Customization
|
|
209
|
+
// ============================================
|
|
210
|
+
|
|
211
|
+
async showSummaryAndCustomize() {
|
|
212
|
+
// Apply intelligent defaults based on selected frameworks
|
|
213
|
+
this.applyEcosystemDefaults();
|
|
214
|
+
|
|
215
|
+
// Show summary with all defaults
|
|
216
|
+
let continueCustomizing = true;
|
|
217
|
+
|
|
218
|
+
while (continueCustomizing) {
|
|
219
|
+
this.printConfigurationSummary();
|
|
220
|
+
|
|
221
|
+
const choice = await this.askSummaryAction();
|
|
222
|
+
|
|
223
|
+
switch (choice) {
|
|
224
|
+
case '1':
|
|
225
|
+
await this.customizeFrontendStack();
|
|
226
|
+
break;
|
|
227
|
+
case '2':
|
|
228
|
+
await this.customizeBackendStack();
|
|
229
|
+
break;
|
|
230
|
+
case '3':
|
|
231
|
+
await this.customizeTestingStack();
|
|
232
|
+
break;
|
|
233
|
+
case '4':
|
|
234
|
+
// Accept all recommendations
|
|
235
|
+
continueCustomizing = false;
|
|
236
|
+
break;
|
|
237
|
+
case '5':
|
|
238
|
+
// Let AI decide
|
|
239
|
+
this.applyAIDefaults();
|
|
240
|
+
continueCustomizing = false;
|
|
241
|
+
break;
|
|
242
|
+
default:
|
|
243
|
+
continueCustomizing = false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
applyEcosystemDefaults() {
|
|
249
|
+
// Frontend ecosystem defaults
|
|
250
|
+
if (this.selections.frontend) {
|
|
251
|
+
const ecosystem = getEcosystemDefaults(this.selections.frontend);
|
|
252
|
+
if (ecosystem && ecosystem.defaults) {
|
|
253
|
+
if (!this.selections.stateManagement) {
|
|
254
|
+
this.selections.stateManagement = ecosystem.defaults.stateManagement;
|
|
255
|
+
}
|
|
256
|
+
if (!this.selections.forms) {
|
|
257
|
+
this.selections.forms = ecosystem.defaults.forms;
|
|
258
|
+
}
|
|
259
|
+
if (!this.selections.styling) {
|
|
260
|
+
this.selections.styling = ecosystem.defaults.styling;
|
|
261
|
+
}
|
|
262
|
+
if (!this.selections.dataFetching) {
|
|
263
|
+
this.selections.dataFetching = ecosystem.defaults.dataFetching;
|
|
264
|
+
}
|
|
265
|
+
if (!this.selections.validation) {
|
|
266
|
+
this.selections.validation = ecosystem.defaults.validation;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Backend ecosystem defaults
|
|
272
|
+
if (this.selections.backend) {
|
|
273
|
+
const ecosystem = getEcosystemDefaults(this.selections.backend);
|
|
274
|
+
if (ecosystem && ecosystem.defaults) {
|
|
275
|
+
if (!this.selections.orm) {
|
|
276
|
+
this.selections.orm = ecosystem.defaults.orm;
|
|
277
|
+
}
|
|
278
|
+
if (!this.selections.database) {
|
|
279
|
+
this.selections.database = ecosystem.defaults.database;
|
|
280
|
+
}
|
|
281
|
+
if (!this.selections.auth) {
|
|
282
|
+
this.selections.auth = ecosystem.defaults.auth;
|
|
283
|
+
}
|
|
284
|
+
if (!this.selections.validation && ecosystem.defaults.validation) {
|
|
285
|
+
this.selections.validation = ecosystem.defaults.validation;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Testing defaults
|
|
291
|
+
if (!this.selections.testing) {
|
|
292
|
+
this.selections.testing = 'vitest';
|
|
293
|
+
}
|
|
294
|
+
if (!this.selections.e2e) {
|
|
295
|
+
this.selections.e2e = 'playwright';
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
applyAIDefaults() {
|
|
300
|
+
console.log(c('cyan', '\n Applying AI-recommended defaults...\n'));
|
|
301
|
+
|
|
302
|
+
// Use best defaults for everything
|
|
303
|
+
Object.assign(this.selections, {
|
|
304
|
+
stateManagement: BEST_DEFAULTS.stateManagement,
|
|
305
|
+
forms: BEST_DEFAULTS.forms,
|
|
306
|
+
styling: BEST_DEFAULTS.styling,
|
|
307
|
+
dataFetching: BEST_DEFAULTS.dataFetching,
|
|
308
|
+
animation: BEST_DEFAULTS.animation,
|
|
309
|
+
validation: BEST_DEFAULTS.validation,
|
|
310
|
+
orm: BEST_DEFAULTS.orm,
|
|
311
|
+
database: BEST_DEFAULTS.database,
|
|
312
|
+
auth: BEST_DEFAULTS.auth,
|
|
313
|
+
testing: BEST_DEFAULTS.testing,
|
|
314
|
+
e2e: BEST_DEFAULTS.e2e
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Mark as AI-configured
|
|
318
|
+
this.selections.aiConfigured = true;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
printConfigurationSummary() {
|
|
322
|
+
console.log('\n' + c('bold', '━━━ Your Tech Stack Configuration ━━━') + '\n');
|
|
323
|
+
|
|
324
|
+
// Frontend section
|
|
325
|
+
if (this.selections.frontend && this.selections.frontend !== 'none') {
|
|
326
|
+
const frontendLabel = this.getLabel(FRONTEND_FRAMEWORKS, this.selections.frontend) ||
|
|
327
|
+
this.getLabel(MOBILE_FRAMEWORKS, this.selections.frontend);
|
|
328
|
+
console.log(`Frontend: ${c('green', frontendLabel)}`);
|
|
329
|
+
|
|
330
|
+
if (this.selections.stateManagement) {
|
|
331
|
+
console.log(`├── ${this.formatSelection('State Management', this.selections.stateManagement, STATE_MANAGEMENT)}`);
|
|
332
|
+
}
|
|
333
|
+
if (this.selections.forms) {
|
|
334
|
+
console.log(`├── ${this.formatSelection('Forms', this.selections.forms, FORM_LIBRARIES)}`);
|
|
335
|
+
}
|
|
336
|
+
if (this.selections.styling) {
|
|
337
|
+
console.log(`├── ${this.formatSelection('Styling', this.selections.styling, STYLING_OPTIONS)}`);
|
|
338
|
+
}
|
|
339
|
+
if (this.selections.dataFetching) {
|
|
340
|
+
console.log(`├── ${this.formatSelection('Data Fetching', this.selections.dataFetching, DATA_FETCHING)}`);
|
|
341
|
+
}
|
|
342
|
+
if (this.selections.animation) {
|
|
343
|
+
console.log(`└── ${this.formatSelection('Animation', this.selections.animation, ANIMATION_LIBRARIES)}`);
|
|
344
|
+
} else {
|
|
345
|
+
console.log(`└── [ ] Animation: (none selected)`);
|
|
346
|
+
}
|
|
347
|
+
console.log();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Backend section
|
|
351
|
+
if (this.selections.backend && this.selections.backend !== 'none') {
|
|
352
|
+
const backendLabel = this.getLabel(BACKEND_FRAMEWORKS, this.selections.backend);
|
|
353
|
+
console.log(`Backend: ${c('green', backendLabel)}`);
|
|
354
|
+
|
|
355
|
+
if (this.selections.orm) {
|
|
356
|
+
console.log(`├── ${this.formatSelection('ORM', this.selections.orm, ORM_OPTIONS)}`);
|
|
357
|
+
}
|
|
358
|
+
if (this.selections.database) {
|
|
359
|
+
console.log(`├── ${this.formatSelection('Database', this.selections.database, DATABASE_OPTIONS)}`);
|
|
360
|
+
}
|
|
361
|
+
if (this.selections.auth) {
|
|
362
|
+
console.log(`├── ${this.formatSelection('Auth', this.selections.auth, AUTH_OPTIONS)}`);
|
|
363
|
+
}
|
|
364
|
+
if (this.selections.validation) {
|
|
365
|
+
console.log(`└── ${this.formatSelection('Validation', this.selections.validation, VALIDATION_LIBRARIES)}`);
|
|
366
|
+
}
|
|
367
|
+
console.log();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Testing section
|
|
371
|
+
console.log('Testing:');
|
|
372
|
+
if (this.selections.testing) {
|
|
373
|
+
console.log(`├── ${this.formatSelection('Unit', this.selections.testing, TESTING_OPTIONS)}`);
|
|
374
|
+
}
|
|
375
|
+
if (this.selections.e2e) {
|
|
376
|
+
console.log(`└── ${this.formatSelection('E2E', this.selections.e2e, TESTING_OPTIONS)}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.log('\n' + c('bold', '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━') + '\n');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
formatSelection(label, value, options) {
|
|
383
|
+
const opt = options.find(o => o.value === value);
|
|
384
|
+
const displayLabel = opt ? opt.label : value;
|
|
385
|
+
const isRecommended = opt && opt.recommended;
|
|
386
|
+
return `[${c('green', '✓')}] ${label}: ${displayLabel}${isRecommended ? c('dim', ' (Recommended)') : ''}`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
getLabel(options, value) {
|
|
390
|
+
const opt = options.find(o => o.value === value);
|
|
391
|
+
return opt ? opt.label : value;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async askSummaryAction() {
|
|
395
|
+
console.log('Options:');
|
|
396
|
+
if (this.selections.frontend && this.selections.frontend !== 'none') {
|
|
397
|
+
console.log(' (1) Customize Frontend stack');
|
|
398
|
+
}
|
|
399
|
+
if (this.selections.backend && this.selections.backend !== 'none') {
|
|
400
|
+
console.log(' (2) Customize Backend stack');
|
|
401
|
+
}
|
|
402
|
+
console.log(' (3) Customize Testing stack');
|
|
403
|
+
console.log(' (4) Accept all recommendations');
|
|
404
|
+
console.log(' (5) Let AI decide best options for my project');
|
|
405
|
+
console.log();
|
|
406
|
+
|
|
407
|
+
return await this.askQuestion('Your choice [4]: ') || '4';
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ============================================
|
|
411
|
+
// CUSTOMIZATION FUNCTIONS
|
|
412
|
+
// ============================================
|
|
413
|
+
|
|
414
|
+
async customizeFrontendStack() {
|
|
415
|
+
console.log(c('bold', '\n Customizing Frontend Stack\n'));
|
|
416
|
+
|
|
417
|
+
// State management
|
|
418
|
+
const stateOptions = getOptionsForFramework(STATE_MANAGEMENT, this.selections.frontend);
|
|
419
|
+
if (stateOptions.length > 0) {
|
|
420
|
+
this.selections.stateManagement = await this.askSingleChoice(
|
|
421
|
+
'State management:',
|
|
422
|
+
stateOptions,
|
|
423
|
+
this.selections.stateManagement
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Forms
|
|
428
|
+
const formOptions = getOptionsForFramework(FORM_LIBRARIES, this.selections.frontend);
|
|
429
|
+
if (formOptions.length > 0) {
|
|
430
|
+
this.selections.forms = await this.askSingleChoice(
|
|
431
|
+
'Form handling:',
|
|
432
|
+
formOptions,
|
|
433
|
+
this.selections.forms
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Styling
|
|
438
|
+
const styleOptions = getOptionsForFramework(STYLING_OPTIONS, this.selections.frontend);
|
|
439
|
+
this.selections.styling = await this.askSingleChoice(
|
|
440
|
+
'Styling approach:',
|
|
441
|
+
styleOptions,
|
|
442
|
+
this.selections.styling
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// Data fetching
|
|
446
|
+
const dataOptions = getOptionsForFramework(DATA_FETCHING, this.selections.frontend);
|
|
447
|
+
if (dataOptions.length > 0) {
|
|
448
|
+
this.selections.dataFetching = await this.askSingleChoice(
|
|
449
|
+
'Data fetching:',
|
|
450
|
+
dataOptions,
|
|
451
|
+
this.selections.dataFetching
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Animation (optional)
|
|
456
|
+
const animOptions = getOptionsForFramework(ANIMATION_LIBRARIES, this.selections.frontend);
|
|
457
|
+
if (animOptions.length > 0) {
|
|
458
|
+
this.selections.animation = await this.askSingleChoice(
|
|
459
|
+
'Animation (optional):',
|
|
460
|
+
animOptions,
|
|
461
|
+
this.selections.animation
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async customizeBackendStack() {
|
|
467
|
+
console.log(c('bold', '\n Customizing Backend Stack\n'));
|
|
468
|
+
|
|
469
|
+
// Database
|
|
470
|
+
this.selections.database = await this.askGroupedChoice(
|
|
471
|
+
'Database:',
|
|
472
|
+
DATABASE_OPTIONS,
|
|
473
|
+
this.selections.database
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
// ORM
|
|
477
|
+
const ormOptions = getOptionsForFramework(ORM_OPTIONS, this.selections.backend);
|
|
478
|
+
if (ormOptions.length > 0) {
|
|
479
|
+
this.selections.orm = await this.askSingleChoice(
|
|
480
|
+
'ORM / Database client:',
|
|
481
|
+
ormOptions,
|
|
482
|
+
this.selections.orm
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Auth
|
|
487
|
+
const authOptions = getOptionsForFramework(AUTH_OPTIONS, this.selections.backend);
|
|
488
|
+
if (authOptions.length > 0) {
|
|
489
|
+
this.selections.auth = await this.askSingleChoice(
|
|
490
|
+
'Authentication:',
|
|
491
|
+
authOptions,
|
|
492
|
+
this.selections.auth
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Validation
|
|
497
|
+
const validOptions = getOptionsForFramework(VALIDATION_LIBRARIES, this.selections.backend);
|
|
498
|
+
if (validOptions.length > 0) {
|
|
499
|
+
this.selections.validation = await this.askSingleChoice(
|
|
500
|
+
'Validation:',
|
|
501
|
+
validOptions,
|
|
502
|
+
this.selections.validation
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async customizeTestingStack() {
|
|
508
|
+
console.log(c('bold', '\n Customizing Testing Stack\n'));
|
|
509
|
+
|
|
510
|
+
// Unit testing
|
|
511
|
+
const unitOptions = TESTING_OPTIONS.filter(o => o.group === 'Unit' || o.group === null);
|
|
512
|
+
this.selections.testing = await this.askSingleChoice(
|
|
513
|
+
'Unit testing framework:',
|
|
514
|
+
unitOptions,
|
|
515
|
+
this.selections.testing
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
// E2E testing
|
|
519
|
+
const e2eOptions = TESTING_OPTIONS.filter(o => o.group === 'E2E' || o.group === null);
|
|
520
|
+
this.selections.e2e = await this.askSingleChoice(
|
|
521
|
+
'E2E testing framework:',
|
|
522
|
+
e2eOptions,
|
|
523
|
+
this.selections.e2e
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ============================================
|
|
528
|
+
// PHASE 4: Testing & Additional Tools
|
|
529
|
+
// ============================================
|
|
530
|
+
|
|
531
|
+
async askTestingAndTools() {
|
|
532
|
+
// Additional tools (multi-select)
|
|
533
|
+
console.log(c('bold', '\nAdditional Tools (optional)\n'));
|
|
534
|
+
console.log('Select additional tools (comma-separated numbers or "none"):');
|
|
535
|
+
|
|
536
|
+
for (const opt of ADDITIONAL_TOOLS) {
|
|
537
|
+
console.log(` (${opt.key}) ${opt.label}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const answer = await this.askQuestion('\nYour choices [none]: ') || 'none';
|
|
541
|
+
|
|
542
|
+
if (answer.toLowerCase() !== 'none' && answer !== '') {
|
|
543
|
+
const keys = answer.split(',').map(k => k.trim());
|
|
544
|
+
this.selections.additionalTools = keys
|
|
545
|
+
.map(k => ADDITIONAL_TOOLS.find(o => o.key === k))
|
|
546
|
+
.filter(Boolean)
|
|
547
|
+
.map(o => o.value);
|
|
548
|
+
} else {
|
|
549
|
+
this.selections.additionalTools = [];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// ============================================
|
|
554
|
+
// PHASE 4.5: Workflow Steps (v2.2)
|
|
555
|
+
// ============================================
|
|
556
|
+
|
|
557
|
+
async askWorkflowSteps() {
|
|
558
|
+
console.log(c('bold', '\n━━━ Workflow Steps Configuration ━━━'));
|
|
559
|
+
console.log(c('dim', 'These steps run automatically during task execution.\n'));
|
|
560
|
+
|
|
561
|
+
// Define available workflow steps with descriptions
|
|
562
|
+
const workflowSteps = [
|
|
563
|
+
// Testing
|
|
564
|
+
{ key: '1', name: 'regressionTest', label: 'Regression Test', desc: 'Test random completed tasks', default: true, mode: 'warn', when: 'afterTask', category: 'testing' },
|
|
565
|
+
{ key: '2', name: 'browserTest', label: 'Browser Test', desc: 'Suggest browser tests for UI changes', default: true, mode: 'prompt', when: 'afterTask', category: 'testing' },
|
|
566
|
+
{ key: '3', name: 'prTestAnalyzer', label: 'PR Test Analyzer', desc: 'Check coverage + quality for modified files', default: true, mode: 'warn', when: 'beforeCommit', category: 'testing' },
|
|
567
|
+
|
|
568
|
+
// Quality
|
|
569
|
+
{ key: '4', name: 'securityScan', label: 'Security Scan', desc: 'npm audit + secrets check', default: true, mode: 'block', when: 'beforeCommit', category: 'quality' },
|
|
570
|
+
{ key: '5', name: 'codeComplexityCheck', label: 'Code Complexity', desc: 'Quantitative complexity analysis', default: false, mode: 'warn', when: 'afterTask', category: 'quality' },
|
|
571
|
+
{ key: '6', name: 'codeSimplifier', label: 'Code Simplifier', desc: 'AI qualitative suggestions (nesting, long functions)', default: false, mode: 'prompt', when: 'afterTask', category: 'quality' },
|
|
572
|
+
{ key: '7', name: 'codeReview', label: 'Code Review', desc: 'Hybrid multi-agent review for large/high-risk', default: false, mode: 'warn', when: 'afterTask', category: 'quality' },
|
|
573
|
+
{ key: '8', name: 'silentFailureHunter', label: 'Silent Failure Hunter', desc: 'Detect empty catch, swallowed errors', default: false, mode: 'warn', when: 'afterTask', category: 'quality' },
|
|
574
|
+
{ key: '9', name: 'commentAnalyzer', label: 'Comment Analyzer', desc: 'Flag TODOs, stale comments, JSDoc accuracy', default: false, mode: 'warn', when: 'afterTask', category: 'quality' },
|
|
575
|
+
{ key: 'a', name: 'coverageCheck', label: 'Coverage Check', desc: 'Verify test coverage meets threshold', default: false, mode: 'warn', when: 'beforeCommit', category: 'quality' },
|
|
576
|
+
|
|
577
|
+
// Documentation
|
|
578
|
+
{ key: 'b', name: 'updateKnowledgeBase', label: 'Update Knowledge Base', desc: 'Document learnings after tasks', default: false, mode: 'prompt', when: 'afterTask', category: 'docs' },
|
|
579
|
+
{ key: 'c', name: 'updateChangelog', label: 'Update Changelog', desc: 'Add CHANGELOG.md entries', default: false, mode: 'prompt', when: 'beforeCommit', category: 'docs' },
|
|
580
|
+
];
|
|
581
|
+
|
|
582
|
+
console.log('Available steps:\n');
|
|
583
|
+
|
|
584
|
+
// Group by category
|
|
585
|
+
const testing = workflowSteps.filter(s => s.category === 'testing');
|
|
586
|
+
const quality = workflowSteps.filter(s => s.category === 'quality');
|
|
587
|
+
const docs = workflowSteps.filter(s => s.category === 'docs');
|
|
588
|
+
|
|
589
|
+
console.log(c('cyan', 'Testing:'));
|
|
590
|
+
for (const step of testing) {
|
|
591
|
+
const defaultMark = step.default ? c('green', ' (ON)') : '';
|
|
592
|
+
console.log(` (${step.key}) ${step.label} - ${step.desc}${defaultMark}`);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
console.log(c('cyan', '\nQuality Analysis:'));
|
|
596
|
+
console.log(c('dim', ' Note: codeComplexityCheck (quantitative) and codeSimplifier (qualitative)'));
|
|
597
|
+
console.log(c('dim', ' can be enabled together for comprehensive analysis.\n'));
|
|
598
|
+
for (const step of quality) {
|
|
599
|
+
const defaultMark = step.default ? c('green', ' (ON)') : '';
|
|
600
|
+
console.log(` (${step.key}) ${step.label} - ${step.desc}${defaultMark}`);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
console.log(c('cyan', '\nDocumentation:'));
|
|
604
|
+
for (const step of docs) {
|
|
605
|
+
const defaultMark = step.default ? c('green', ' (ON)') : '';
|
|
606
|
+
console.log(` (${step.key}) ${step.label} - ${step.desc}${defaultMark}`);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
console.log(c('dim', '\nType numbers to toggle (e.g., "4,5" to enable knowledge base + changelog)'));
|
|
610
|
+
console.log(c('dim', 'Press Enter to accept defaults, or "all" to enable all steps.'));
|
|
611
|
+
|
|
612
|
+
const answer = await this.askQuestion('\nToggle steps [Enter for defaults]: ');
|
|
613
|
+
|
|
614
|
+
// Build the workflowSteps config
|
|
615
|
+
const config = {};
|
|
616
|
+
for (const step of workflowSteps) {
|
|
617
|
+
config[step.name] = {
|
|
618
|
+
enabled: step.default,
|
|
619
|
+
mode: step.mode,
|
|
620
|
+
when: step.when
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Process user selections
|
|
625
|
+
if (answer && answer.toLowerCase() === 'all') {
|
|
626
|
+
// Enable all
|
|
627
|
+
for (const step of workflowSteps) {
|
|
628
|
+
config[step.name].enabled = true;
|
|
629
|
+
}
|
|
630
|
+
console.log(c('green', '\nAll workflow steps enabled.'));
|
|
631
|
+
} else if (answer && answer.trim() !== '') {
|
|
632
|
+
// Toggle specific steps
|
|
633
|
+
const keys = answer.split(',').map(k => k.trim());
|
|
634
|
+
for (const key of keys) {
|
|
635
|
+
const step = workflowSteps.find(s => s.key === key);
|
|
636
|
+
if (step) {
|
|
637
|
+
config[step.name].enabled = !config[step.name].enabled;
|
|
638
|
+
const status = config[step.name].enabled ? 'enabled' : 'disabled';
|
|
639
|
+
console.log(` ${step.label}: ${status}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
} else {
|
|
643
|
+
console.log(c('dim', '\nUsing default workflow steps configuration.'));
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
this.selections.workflowSteps = config;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ============================================
|
|
650
|
+
// PHASE 5: Final Summary & Generation
|
|
651
|
+
// ============================================
|
|
652
|
+
|
|
653
|
+
async finalizeAndGenerate() {
|
|
654
|
+
// Show final summary
|
|
655
|
+
console.log(c('bold', '\n━━━ Final Tech Stack ━━━\n'));
|
|
656
|
+
this.printFinalSummary();
|
|
657
|
+
|
|
658
|
+
// Confirm and generate
|
|
659
|
+
const proceed = await this.askConfirmation(
|
|
660
|
+
'\nGenerate skills and fetch documentation? [Y/n] '
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
if (proceed) {
|
|
664
|
+
await this.generateSkills();
|
|
665
|
+
} else {
|
|
666
|
+
console.log('\nSkipped skill generation. You can run this later with:');
|
|
667
|
+
console.log(' /wogi-setup-stack\n');
|
|
668
|
+
this.saveSelectionsToFile();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
printFinalSummary() {
|
|
673
|
+
const platform = PLATFORM_TYPES.find(p => p.value === this.selections.projectType);
|
|
674
|
+
console.log(`Platform: ${platform?.label || this.selections.projectType}`);
|
|
675
|
+
|
|
676
|
+
if (this.selections.frontend && this.selections.frontend !== 'none') {
|
|
677
|
+
const fe = [...FRONTEND_FRAMEWORKS, ...MOBILE_FRAMEWORKS].find(f => f.value === this.selections.frontend);
|
|
678
|
+
console.log(`Frontend: ${fe?.label || this.selections.frontend}`);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (this.selections.backend && this.selections.backend !== 'none') {
|
|
682
|
+
const be = BACKEND_FRAMEWORKS.find(f => f.value === this.selections.backend);
|
|
683
|
+
console.log(`Backend: ${be?.label || this.selections.backend}`);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Collect all technologies for display
|
|
687
|
+
const techs = collectTechnologiesFromSelections(this.selections);
|
|
688
|
+
if (techs.length > 0) {
|
|
689
|
+
console.log(`\nTechnologies (${techs.length}):`);
|
|
690
|
+
for (const tech of techs) {
|
|
691
|
+
console.log(` - ${tech.label}`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (this.selections.aiConfigured) {
|
|
696
|
+
console.log(c('dim', '\n Auto-configured with AI recommendations'));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Show workflow steps
|
|
700
|
+
if (this.selections.workflowSteps) {
|
|
701
|
+
const enabled = Object.entries(this.selections.workflowSteps)
|
|
702
|
+
.filter(([_, cfg]) => cfg.enabled)
|
|
703
|
+
.map(([name, _]) => name);
|
|
704
|
+
|
|
705
|
+
if (enabled.length > 0) {
|
|
706
|
+
console.log(`\nWorkflow Steps (${enabled.length} enabled):`);
|
|
707
|
+
for (const name of enabled) {
|
|
708
|
+
console.log(` - ${name}`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
async generateSkills() {
|
|
715
|
+
console.log(c('cyan', '\nGenerating skills...'));
|
|
716
|
+
|
|
717
|
+
const technologies = collectTechnologiesFromSelections(this.selections);
|
|
718
|
+
|
|
719
|
+
if (technologies.length === 0) {
|
|
720
|
+
console.log('No technologies selected that require skill generation.');
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
console.log(`\n Creating skills for ${technologies.length} technologies...`);
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
const generator = require('./flow-skill-generator');
|
|
728
|
+
await generator.generateSkills(technologies, this.selections);
|
|
729
|
+
|
|
730
|
+
console.log(c('green', '\n✅ Skills generated successfully!\n'));
|
|
731
|
+
|
|
732
|
+
// Print generated skills
|
|
733
|
+
const skillsDir = path.join(process.cwd(), '.claude', 'skills');
|
|
734
|
+
if (fs.existsSync(skillsDir)) {
|
|
735
|
+
const skills = fs.readdirSync(skillsDir).filter(f =>
|
|
736
|
+
fs.statSync(path.join(skillsDir, f)).isDirectory() && f !== '_template'
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
if (skills.length > 0) {
|
|
740
|
+
console.log('Skills created in .claude/skills/:');
|
|
741
|
+
for (const skill of skills) {
|
|
742
|
+
console.log(` - ${skill}/`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
console.log(c('yellow', '\n💡 You can review and customize these skills:'));
|
|
748
|
+
console.log(' cat .claude/skills/[skill-name]/skill.md\n');
|
|
749
|
+
console.log('To regenerate with different settings:');
|
|
750
|
+
console.log(' /wogi-setup-stack\n');
|
|
751
|
+
|
|
752
|
+
} catch (error) {
|
|
753
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
754
|
+
console.log(c('yellow', '\nSkill generator not found. Creating placeholder...'));
|
|
755
|
+
this.saveSelectionsToFile();
|
|
756
|
+
} else {
|
|
757
|
+
console.error('\nError generating skills:', error.message);
|
|
758
|
+
console.log('\nSaving selections for later processing...');
|
|
759
|
+
this.saveSelectionsToFile();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
saveSelectionsToFile() {
|
|
765
|
+
const projectRoot = process.cwd();
|
|
766
|
+
const selectionsPath = path.join(projectRoot, '.workflow', 'state', 'stack-selections.json');
|
|
767
|
+
|
|
768
|
+
try {
|
|
769
|
+
fs.mkdirSync(path.dirname(selectionsPath), { recursive: true });
|
|
770
|
+
fs.writeFileSync(selectionsPath, JSON.stringify(this.selections, null, 2));
|
|
771
|
+
console.log(`\nSelections saved to: ${selectionsPath}`);
|
|
772
|
+
console.log('Run skill generation later with: /wogi-setup-stack --generate');
|
|
773
|
+
} catch (error) {
|
|
774
|
+
console.error('Failed to save selections:', error.message);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// ============================================
|
|
779
|
+
// INPUT HELPERS
|
|
780
|
+
// ============================================
|
|
781
|
+
|
|
782
|
+
askQuestion(prompt) {
|
|
783
|
+
return new Promise((resolve) => {
|
|
784
|
+
this.rl.question(prompt, (answer) => {
|
|
785
|
+
resolve(answer.trim());
|
|
786
|
+
});
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
async askSingleChoice(question, options, defaultValue = null) {
|
|
791
|
+
const MAX_RETRIES = 10;
|
|
792
|
+
let retries = 0;
|
|
793
|
+
|
|
794
|
+
console.log(`${question}`);
|
|
795
|
+
|
|
796
|
+
for (const opt of options) {
|
|
797
|
+
const isDefault = opt.value === defaultValue;
|
|
798
|
+
const isRecommended = opt.recommended;
|
|
799
|
+
let marker = '';
|
|
800
|
+
if (isDefault) marker = c('green', ' [current]');
|
|
801
|
+
else if (isRecommended) marker = c('dim', ' (Recommended)');
|
|
802
|
+
console.log(` (${opt.key}) ${opt.label}${marker}`);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
while (retries < MAX_RETRIES) {
|
|
806
|
+
const defaultKey = options.find(o => o.value === defaultValue)?.key || options[0].key;
|
|
807
|
+
const answer = await this.askQuestion(`\nYour choice [${defaultKey}]: `) || defaultKey;
|
|
808
|
+
|
|
809
|
+
const option = options.find(o => o.key === answer);
|
|
810
|
+
if (option) {
|
|
811
|
+
return option.value;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
retries++;
|
|
815
|
+
if (retries < MAX_RETRIES) {
|
|
816
|
+
console.log(` Invalid choice. ${MAX_RETRIES - retries} attempts remaining.`);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
console.log(' Using default.');
|
|
821
|
+
return defaultValue || options[0].value;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
async askGroupedChoice(question, options, defaultValue = null) {
|
|
825
|
+
const MAX_RETRIES = 10;
|
|
826
|
+
let retries = 0;
|
|
827
|
+
|
|
828
|
+
console.log(`${question}`);
|
|
829
|
+
|
|
830
|
+
// Group options
|
|
831
|
+
const groups = {};
|
|
832
|
+
for (const opt of options) {
|
|
833
|
+
const group = opt.group || 'Options';
|
|
834
|
+
if (!groups[group]) groups[group] = [];
|
|
835
|
+
groups[group].push(opt);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Print grouped options
|
|
839
|
+
for (const [groupName, groupOpts] of Object.entries(groups)) {
|
|
840
|
+
if (groupName !== 'Options' && groupName !== null) {
|
|
841
|
+
console.log(` ${c('dim', `[${groupName}]`)}`);
|
|
842
|
+
}
|
|
843
|
+
for (const opt of groupOpts) {
|
|
844
|
+
const isDefault = opt.value === defaultValue;
|
|
845
|
+
const isRecommended = opt.recommended;
|
|
846
|
+
let marker = '';
|
|
847
|
+
if (isDefault) marker = c('green', ' [current]');
|
|
848
|
+
else if (isRecommended) marker = c('dim', ' (Recommended)');
|
|
849
|
+
console.log(` (${opt.key}) ${opt.label}${marker}`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
while (retries < MAX_RETRIES) {
|
|
854
|
+
const defaultKey = options.find(o => o.value === defaultValue)?.key || options[0].key;
|
|
855
|
+
const answer = await this.askQuestion(`\nYour choice [${defaultKey}]: `) || defaultKey;
|
|
856
|
+
|
|
857
|
+
const option = options.find(o => o.key === answer);
|
|
858
|
+
if (option) {
|
|
859
|
+
return option.value;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
retries++;
|
|
863
|
+
if (retries < MAX_RETRIES) {
|
|
864
|
+
console.log(` Invalid choice. ${MAX_RETRIES - retries} attempts remaining.`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
console.log(' Using default.');
|
|
869
|
+
return defaultValue || options[0].value;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
async askConfirmation(prompt) {
|
|
873
|
+
const answer = await this.askQuestion(prompt);
|
|
874
|
+
return answer.toLowerCase() !== 'n';
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// ============================================
|
|
879
|
+
// EXPORTS & CLI
|
|
880
|
+
// ============================================
|
|
881
|
+
|
|
882
|
+
module.exports = { EnhancedStackWizard };
|
|
883
|
+
|
|
884
|
+
// Run if called directly
|
|
885
|
+
if (require.main === module) {
|
|
886
|
+
const wizard = new EnhancedStackWizard();
|
|
887
|
+
wizard.run()
|
|
888
|
+
.then((selections) => {
|
|
889
|
+
process.exit(0);
|
|
890
|
+
})
|
|
891
|
+
.catch((error) => {
|
|
892
|
+
console.error('Wizard error:', error);
|
|
893
|
+
process.exit(1);
|
|
894
|
+
});
|
|
895
|
+
}
|