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.
Files changed (221) hide show
  1. package/.workflow/agents/reviewer.md +81 -0
  2. package/.workflow/agents/security.md +94 -0
  3. package/.workflow/agents/story-writer.md +58 -0
  4. package/.workflow/bridges/base-bridge.js +395 -0
  5. package/.workflow/bridges/claude-bridge.js +434 -0
  6. package/.workflow/bridges/index.js +130 -0
  7. package/.workflow/lib/assumption-detector.js +481 -0
  8. package/.workflow/lib/config-substitution.js +371 -0
  9. package/.workflow/lib/failure-categories.js +478 -0
  10. package/.workflow/state/app-map.md.template +15 -0
  11. package/.workflow/state/architecture.md.template +24 -0
  12. package/.workflow/state/component-index.json.template +5 -0
  13. package/.workflow/state/decisions.md.template +15 -0
  14. package/.workflow/state/feedback-patterns.md.template +9 -0
  15. package/.workflow/state/knowledge-sync.json.template +6 -0
  16. package/.workflow/state/progress.md.template +14 -0
  17. package/.workflow/state/ready.json.template +7 -0
  18. package/.workflow/state/request-log.md.template +14 -0
  19. package/.workflow/state/session-state.json.template +11 -0
  20. package/.workflow/state/stack.md.template +33 -0
  21. package/.workflow/state/testing.md.template +36 -0
  22. package/.workflow/templates/claude-md.hbs +257 -0
  23. package/.workflow/templates/correction-report.md +67 -0
  24. package/.workflow/templates/gemini-md.hbs +52 -0
  25. package/README.md +1802 -0
  26. package/bin/flow +205 -0
  27. package/lib/index.js +33 -0
  28. package/lib/installer.js +467 -0
  29. package/lib/release-channel.js +269 -0
  30. package/lib/skill-registry.js +526 -0
  31. package/lib/upgrader.js +401 -0
  32. package/lib/utils.js +305 -0
  33. package/package.json +64 -0
  34. package/scripts/flow +985 -0
  35. package/scripts/flow-adaptive-learning.js +1259 -0
  36. package/scripts/flow-aggregate.js +488 -0
  37. package/scripts/flow-archive +133 -0
  38. package/scripts/flow-auto-context.js +1015 -0
  39. package/scripts/flow-auto-learn.js +615 -0
  40. package/scripts/flow-bridge.js +223 -0
  41. package/scripts/flow-browser-suggest.js +316 -0
  42. package/scripts/flow-bug.js +247 -0
  43. package/scripts/flow-cascade.js +711 -0
  44. package/scripts/flow-changelog +85 -0
  45. package/scripts/flow-checkpoint.js +483 -0
  46. package/scripts/flow-cli.js +403 -0
  47. package/scripts/flow-code-intelligence.js +760 -0
  48. package/scripts/flow-complexity.js +502 -0
  49. package/scripts/flow-config-set.js +152 -0
  50. package/scripts/flow-constants.js +157 -0
  51. package/scripts/flow-context +152 -0
  52. package/scripts/flow-context-init.js +482 -0
  53. package/scripts/flow-context-monitor.js +384 -0
  54. package/scripts/flow-context-scoring.js +886 -0
  55. package/scripts/flow-correct.js +458 -0
  56. package/scripts/flow-damage-control.js +985 -0
  57. package/scripts/flow-deps +101 -0
  58. package/scripts/flow-diff.js +700 -0
  59. package/scripts/flow-done +151 -0
  60. package/scripts/flow-done.js +489 -0
  61. package/scripts/flow-durable-session.js +1541 -0
  62. package/scripts/flow-entropy-monitor.js +345 -0
  63. package/scripts/flow-export-profile +349 -0
  64. package/scripts/flow-export-scanner.js +1046 -0
  65. package/scripts/flow-figma-confirm.js +400 -0
  66. package/scripts/flow-figma-extract.js +496 -0
  67. package/scripts/flow-figma-generate.js +683 -0
  68. package/scripts/flow-figma-index.js +909 -0
  69. package/scripts/flow-figma-match.js +617 -0
  70. package/scripts/flow-figma-mcp-server.js +518 -0
  71. package/scripts/flow-figma-pipeline.js +414 -0
  72. package/scripts/flow-file-ops.js +301 -0
  73. package/scripts/flow-gate-confidence.js +825 -0
  74. package/scripts/flow-guided-edit.js +659 -0
  75. package/scripts/flow-health +185 -0
  76. package/scripts/flow-health.js +413 -0
  77. package/scripts/flow-hooks.js +556 -0
  78. package/scripts/flow-http-client.js +249 -0
  79. package/scripts/flow-hybrid-detect.js +167 -0
  80. package/scripts/flow-hybrid-interactive.js +591 -0
  81. package/scripts/flow-hybrid-test.js +152 -0
  82. package/scripts/flow-import-profile +439 -0
  83. package/scripts/flow-init +253 -0
  84. package/scripts/flow-instruction-richness.js +827 -0
  85. package/scripts/flow-jira-integration.js +579 -0
  86. package/scripts/flow-knowledge-router.js +522 -0
  87. package/scripts/flow-knowledge-sync.js +589 -0
  88. package/scripts/flow-linear-integration.js +631 -0
  89. package/scripts/flow-links.js +774 -0
  90. package/scripts/flow-log-manager.js +559 -0
  91. package/scripts/flow-loop-enforcer.js +1246 -0
  92. package/scripts/flow-loop-retry-learning.js +630 -0
  93. package/scripts/flow-lsp.js +923 -0
  94. package/scripts/flow-map-index +348 -0
  95. package/scripts/flow-map-sync +201 -0
  96. package/scripts/flow-memory-blocks.js +668 -0
  97. package/scripts/flow-memory-compactor.js +350 -0
  98. package/scripts/flow-memory-db.js +1110 -0
  99. package/scripts/flow-memory-sync.js +484 -0
  100. package/scripts/flow-metrics.js +353 -0
  101. package/scripts/flow-migrate-ids.js +370 -0
  102. package/scripts/flow-model-adapter.js +802 -0
  103. package/scripts/flow-model-router.js +884 -0
  104. package/scripts/flow-models.js +1231 -0
  105. package/scripts/flow-morning.js +517 -0
  106. package/scripts/flow-multi-approach.js +660 -0
  107. package/scripts/flow-new-feature +86 -0
  108. package/scripts/flow-onboard +1042 -0
  109. package/scripts/flow-orchestrate-llm.js +459 -0
  110. package/scripts/flow-orchestrate.js +3592 -0
  111. package/scripts/flow-output.js +123 -0
  112. package/scripts/flow-parallel-detector.js +399 -0
  113. package/scripts/flow-parallel-dispatch.js +987 -0
  114. package/scripts/flow-parallel.js +428 -0
  115. package/scripts/flow-pattern-enforcer.js +600 -0
  116. package/scripts/flow-prd-manager.js +282 -0
  117. package/scripts/flow-progress.js +323 -0
  118. package/scripts/flow-project-analyzer.js +975 -0
  119. package/scripts/flow-prompt-composer.js +487 -0
  120. package/scripts/flow-providers.js +1381 -0
  121. package/scripts/flow-queue.js +308 -0
  122. package/scripts/flow-ready +82 -0
  123. package/scripts/flow-ready.js +189 -0
  124. package/scripts/flow-regression.js +396 -0
  125. package/scripts/flow-response-parser.js +450 -0
  126. package/scripts/flow-resume.js +284 -0
  127. package/scripts/flow-rules-sync.js +439 -0
  128. package/scripts/flow-run-trace.js +718 -0
  129. package/scripts/flow-safety.js +587 -0
  130. package/scripts/flow-search +104 -0
  131. package/scripts/flow-security.js +481 -0
  132. package/scripts/flow-session-end +106 -0
  133. package/scripts/flow-session-end.js +437 -0
  134. package/scripts/flow-session-state.js +671 -0
  135. package/scripts/flow-setup-hooks +216 -0
  136. package/scripts/flow-setup-hooks.js +377 -0
  137. package/scripts/flow-skill-create.js +329 -0
  138. package/scripts/flow-skill-creator.js +572 -0
  139. package/scripts/flow-skill-generator.js +1046 -0
  140. package/scripts/flow-skill-learn.js +880 -0
  141. package/scripts/flow-skill-matcher.js +578 -0
  142. package/scripts/flow-spec-generator.js +820 -0
  143. package/scripts/flow-stack-wizard.js +895 -0
  144. package/scripts/flow-standup +162 -0
  145. package/scripts/flow-start +74 -0
  146. package/scripts/flow-start.js +235 -0
  147. package/scripts/flow-status +110 -0
  148. package/scripts/flow-status.js +301 -0
  149. package/scripts/flow-step-browser.js +83 -0
  150. package/scripts/flow-step-changelog.js +217 -0
  151. package/scripts/flow-step-comments.js +306 -0
  152. package/scripts/flow-step-complexity.js +234 -0
  153. package/scripts/flow-step-coverage.js +218 -0
  154. package/scripts/flow-step-knowledge.js +193 -0
  155. package/scripts/flow-step-pr-tests.js +364 -0
  156. package/scripts/flow-step-regression.js +89 -0
  157. package/scripts/flow-step-review.js +516 -0
  158. package/scripts/flow-step-security.js +162 -0
  159. package/scripts/flow-step-silent-failures.js +290 -0
  160. package/scripts/flow-step-simplifier.js +346 -0
  161. package/scripts/flow-story +105 -0
  162. package/scripts/flow-story.js +500 -0
  163. package/scripts/flow-suspend.js +252 -0
  164. package/scripts/flow-sync-daemon.js +654 -0
  165. package/scripts/flow-task-analyzer.js +606 -0
  166. package/scripts/flow-team-dashboard.js +748 -0
  167. package/scripts/flow-team-sync.js +752 -0
  168. package/scripts/flow-team.js +977 -0
  169. package/scripts/flow-tech-options.js +528 -0
  170. package/scripts/flow-templates.js +812 -0
  171. package/scripts/flow-tiered-learning.js +728 -0
  172. package/scripts/flow-trace +204 -0
  173. package/scripts/flow-transcript-chunking.js +1106 -0
  174. package/scripts/flow-transcript-digest.js +7918 -0
  175. package/scripts/flow-transcript-language.js +465 -0
  176. package/scripts/flow-transcript-parsing.js +1085 -0
  177. package/scripts/flow-transcript-stories.js +2194 -0
  178. package/scripts/flow-update-map +224 -0
  179. package/scripts/flow-utils.js +2242 -0
  180. package/scripts/flow-verification.js +644 -0
  181. package/scripts/flow-verify.js +1177 -0
  182. package/scripts/flow-voice-input.js +638 -0
  183. package/scripts/flow-watch +168 -0
  184. package/scripts/flow-workflow-steps.js +521 -0
  185. package/scripts/flow-workflow.js +1029 -0
  186. package/scripts/flow-worktree.js +489 -0
  187. package/scripts/hooks/adapters/base-adapter.js +102 -0
  188. package/scripts/hooks/adapters/claude-code.js +359 -0
  189. package/scripts/hooks/adapters/index.js +79 -0
  190. package/scripts/hooks/core/component-check.js +341 -0
  191. package/scripts/hooks/core/index.js +35 -0
  192. package/scripts/hooks/core/loop-check.js +241 -0
  193. package/scripts/hooks/core/session-context.js +294 -0
  194. package/scripts/hooks/core/task-gate.js +177 -0
  195. package/scripts/hooks/core/validation.js +230 -0
  196. package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
  197. package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
  198. package/scripts/hooks/entry/claude-code/session-end.js +87 -0
  199. package/scripts/hooks/entry/claude-code/session-start.js +46 -0
  200. package/scripts/hooks/entry/claude-code/stop.js +43 -0
  201. package/scripts/postinstall.js +139 -0
  202. package/templates/browser-test-flow.json +56 -0
  203. package/templates/bug-report.md +43 -0
  204. package/templates/component-detail.md +42 -0
  205. package/templates/component.stories.tsx +49 -0
  206. package/templates/context/constraints.md +83 -0
  207. package/templates/context/conventions.md +177 -0
  208. package/templates/context/stack.md +60 -0
  209. package/templates/correction-report.md +90 -0
  210. package/templates/feature-proposal.md +35 -0
  211. package/templates/hybrid/_base.md +254 -0
  212. package/templates/hybrid/_patterns.md +45 -0
  213. package/templates/hybrid/create-component.md +127 -0
  214. package/templates/hybrid/create-file.md +56 -0
  215. package/templates/hybrid/create-hook.md +145 -0
  216. package/templates/hybrid/create-service.md +70 -0
  217. package/templates/hybrid/fix-bug.md +33 -0
  218. package/templates/hybrid/modify-file.md +55 -0
  219. package/templates/story.md +68 -0
  220. package/templates/task.json +56 -0
  221. 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
+ }