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,975 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Project Analyzer
5
+ *
6
+ * Analyzes a project and populates config.json with hybrid projectContext settings.
7
+ * Called during onboarding to ensure the local LLM has all the context it needs.
8
+ *
9
+ * Usage:
10
+ * node flow-project-analyzer.js [project-root]
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { getProjectRoot } = require('./flow-utils');
16
+
17
+ const PROJECT_ROOT = process.argv[2] || getProjectRoot();
18
+ const CONFIG_PATH = path.join(PROJECT_ROOT, '.workflow/config.json');
19
+
20
+ // ============================================================
21
+ // Detection Functions
22
+ // ============================================================
23
+
24
+ /**
25
+ * Detect UI framework from package.json and project files
26
+ */
27
+ function detectUIFramework() {
28
+ const packageJsonPath = path.join(PROJECT_ROOT, 'package.json');
29
+ if (!fs.existsSync(packageJsonPath)) return null;
30
+
31
+ try {
32
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
33
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
34
+
35
+ if (deps['next']) return 'next';
36
+ if (deps['@angular/core']) return 'angular';
37
+ if (deps['vue']) return 'vue';
38
+ if (deps['svelte']) return 'svelte';
39
+ if (deps['react-native']) return 'react-native';
40
+ if (deps['react']) return 'react';
41
+ if (deps['@nestjs/core']) return 'nestjs';
42
+ if (deps['express']) return 'express';
43
+ if (deps['fastify']) return 'fastify';
44
+
45
+ return null;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Detect styling approach from dependencies and project files
53
+ */
54
+ function detectStylingApproach() {
55
+ const packageJsonPath = path.join(PROJECT_ROOT, 'package.json');
56
+ if (!fs.existsSync(packageJsonPath)) return null;
57
+
58
+ try {
59
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
60
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
61
+
62
+ // Check dependencies
63
+ if (deps['styled-components']) return 'styled-components';
64
+ if (deps['@emotion/react'] || deps['@emotion/styled']) return 'emotion';
65
+ if (deps['tailwindcss']) return 'tailwind';
66
+ if (deps['sass'] || deps['node-sass']) return 'sass';
67
+ if (deps['less']) return 'less';
68
+
69
+ // Check for tailwind config
70
+ if (fs.existsSync(path.join(PROJECT_ROOT, 'tailwind.config.js')) ||
71
+ fs.existsSync(path.join(PROJECT_ROOT, 'tailwind.config.ts'))) {
72
+ return 'tailwind';
73
+ }
74
+
75
+ // Check for CSS modules usage
76
+ const srcDir = path.join(PROJECT_ROOT, 'src');
77
+ if (fs.existsSync(srcDir)) {
78
+ const hasCSSModules = findFiles(srcDir, /\.module\.css$/).length > 0;
79
+ if (hasCSSModules) return 'css-modules';
80
+ }
81
+
82
+ return null;
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Find files matching a pattern in a directory
90
+ */
91
+ function findFiles(dir, pattern, results = [], depth = 0) {
92
+ if (depth > 5) return results; // Limit depth
93
+
94
+ try {
95
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
96
+
97
+ for (const entry of entries) {
98
+ const fullPath = path.join(dir, entry.name);
99
+
100
+ // Skip common excluded directories
101
+ if (entry.isDirectory()) {
102
+ if (['node_modules', '.git', 'dist', 'build', '.next', '.nuxt'].includes(entry.name)) continue;
103
+ findFiles(fullPath, pattern, results, depth + 1);
104
+ } else if (pattern.test(entry.name)) {
105
+ results.push(fullPath);
106
+ }
107
+ }
108
+ } catch {
109
+ // Ignore read errors
110
+ }
111
+
112
+ return results;
113
+ }
114
+
115
+ /**
116
+ * Find component directories in the project
117
+ */
118
+ function findComponentDirs() {
119
+ const possibleDirs = [
120
+ 'src/components',
121
+ 'components',
122
+ 'src/shared/components',
123
+ 'apps/web/src/components',
124
+ 'packages/ui/src',
125
+ 'src/ui',
126
+ ];
127
+
128
+ return possibleDirs.filter(dir => {
129
+ const fullPath = path.join(PROJECT_ROOT, dir);
130
+ return fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory();
131
+ });
132
+ }
133
+
134
+ /**
135
+ * Find type file directories/patterns
136
+ */
137
+ function findTypeDirs() {
138
+ const possiblePatterns = [
139
+ 'src/types',
140
+ 'types',
141
+ 'src/@types',
142
+ '@types',
143
+ ];
144
+
145
+ const foundDirs = possiblePatterns.filter(dir => {
146
+ const fullPath = path.join(PROJECT_ROOT, dir);
147
+ return fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory();
148
+ });
149
+
150
+ // Also look for types.ts files in features
151
+ const typeFiles = findFiles(path.join(PROJECT_ROOT, 'src'), /types\.ts$/);
152
+ if (typeFiles.length > 0) {
153
+ // Extract patterns from found type files
154
+ const patterns = new Set();
155
+ for (const file of typeFiles) {
156
+ const relative = path.relative(PROJECT_ROOT, file);
157
+ // Create a pattern from the path
158
+ if (relative.includes('features/')) {
159
+ patterns.add('src/features/*/api/types.ts');
160
+ } else if (relative.includes('modules/')) {
161
+ patterns.add('src/modules/*/types.ts');
162
+ }
163
+ }
164
+ foundDirs.push(...patterns);
165
+ }
166
+
167
+ return foundDirs.length > 0 ? foundDirs : ['src/types/*.ts'];
168
+ }
169
+
170
+ /**
171
+ * Scan a component directory and extract available components with their exports
172
+ */
173
+ function scanComponentExports(componentDir) {
174
+ const components = {};
175
+ const fullDir = path.join(PROJECT_ROOT, componentDir);
176
+
177
+ if (!fs.existsSync(fullDir)) return components;
178
+
179
+ try {
180
+ const entries = fs.readdirSync(fullDir, { withFileTypes: true });
181
+
182
+ for (const entry of entries) {
183
+ if (!entry.isDirectory()) continue;
184
+
185
+ const compPath = path.join(fullDir, entry.name);
186
+ const indexPath = path.join(compPath, 'index.ts');
187
+ const indexTsxPath = path.join(compPath, 'index.tsx');
188
+ const mainFile = path.join(compPath, `${entry.name}.tsx`);
189
+
190
+ let exports = [];
191
+
192
+ // Try to find exports from index file
193
+ for (const indexFile of [indexPath, indexTsxPath]) {
194
+ if (fs.existsSync(indexFile)) {
195
+ const content = fs.readFileSync(indexFile, 'utf-8');
196
+
197
+ // Match export { X, Y, Z }
198
+ const reExports = content.match(/export\s+{\s*([^}]+)\s*}/g);
199
+ if (reExports) {
200
+ for (const match of reExports) {
201
+ const names = match.replace(/export\s*{\s*/, '').replace(/\s*}/, '').split(',');
202
+ exports.push(...names.map(n => n.trim().split(' ')[0]).filter(n => n));
203
+ }
204
+ }
205
+
206
+ // Match export const/function/class X
207
+ const namedExports = content.match(/export\s+(?:const|function|class)\s+(\w+)/g);
208
+ if (namedExports) {
209
+ for (const match of namedExports) {
210
+ const name = match.split(/\s+/).pop();
211
+ if (name && !exports.includes(name)) exports.push(name);
212
+ }
213
+ }
214
+
215
+ break;
216
+ }
217
+ }
218
+
219
+ // If no index, try main file
220
+ if (exports.length === 0 && fs.existsSync(mainFile)) {
221
+ const content = fs.readFileSync(mainFile, 'utf-8');
222
+ const namedExports = content.match(/export\s+(?:const|function|class)\s+(\w+)/g);
223
+ if (namedExports) {
224
+ for (const match of namedExports) {
225
+ const name = match.split(/\s+/).pop();
226
+ if (name) exports.push(name);
227
+ }
228
+ }
229
+ }
230
+
231
+ if (exports.length > 0) {
232
+ components[entry.name] = {
233
+ exports: [...new Set(exports)],
234
+ importPath: `@/components/${entry.name}`
235
+ };
236
+ }
237
+ }
238
+ } catch {
239
+ // Ignore scan errors
240
+ }
241
+
242
+ return components;
243
+ }
244
+
245
+ /**
246
+ * Generate glob patterns for component discovery based on detected framework
247
+ * This is a simplified, one-time detection that generates patterns for later use
248
+ */
249
+ function generateComponentGlobPatterns(uiFramework, componentDirs) {
250
+ const patterns = [];
251
+
252
+ // Base component patterns
253
+ for (const dir of componentDirs) {
254
+ patterns.push(`${dir}/**/*.tsx`);
255
+ patterns.push(`${dir}/**/*.jsx`);
256
+ }
257
+
258
+ // Framework-specific patterns
259
+ switch (uiFramework) {
260
+ case 'next':
261
+ // Next.js app router components
262
+ patterns.push('app/**/*.tsx');
263
+ patterns.push('app/**/page.tsx');
264
+ patterns.push('app/**/layout.tsx');
265
+ // Pages router
266
+ patterns.push('pages/**/*.tsx');
267
+ break;
268
+
269
+ case 'react':
270
+ case 'react-native':
271
+ // Common React patterns
272
+ patterns.push('src/**/*.tsx');
273
+ patterns.push('src/**/*.jsx');
274
+ break;
275
+
276
+ case 'vue':
277
+ patterns.push('src/**/*.vue');
278
+ patterns.push('components/**/*.vue');
279
+ break;
280
+
281
+ case 'angular':
282
+ patterns.push('src/**/*.component.ts');
283
+ patterns.push('src/**/*.component.html');
284
+ break;
285
+
286
+ case 'svelte':
287
+ patterns.push('src/**/*.svelte');
288
+ break;
289
+
290
+ case 'nestjs':
291
+ // NestJS modules and controllers
292
+ patterns.push('src/**/*.module.ts');
293
+ patterns.push('src/**/*.controller.ts');
294
+ patterns.push('src/**/*.service.ts');
295
+ break;
296
+
297
+ case 'express':
298
+ case 'fastify':
299
+ patterns.push('src/**/*.ts');
300
+ patterns.push('src/routes/**/*.ts');
301
+ patterns.push('src/controllers/**/*.ts');
302
+ break;
303
+ }
304
+
305
+ // Default fallback if no framework detected
306
+ if (patterns.length === 0) {
307
+ patterns.push('src/**/*.ts');
308
+ patterns.push('src/**/*.tsx');
309
+ patterns.push('src/**/*.js');
310
+ patterns.push('src/**/*.jsx');
311
+ }
312
+
313
+ return [...new Set(patterns)]; // Dedupe
314
+ }
315
+
316
+ /**
317
+ * Generate simplified framework config for storing in config.json
318
+ * This provides all the info needed without re-detection
319
+ */
320
+ function generateFrameworkConfig(analysis) {
321
+ return {
322
+ framework: analysis.uiFramework,
323
+ styling: analysis.stylingApproach,
324
+ componentPatterns: generateComponentGlobPatterns(analysis.uiFramework, analysis.componentDirs),
325
+ testPatterns: generateTestGlobPatterns(analysis.uiFramework),
326
+ configFiles: detectConfigFiles(),
327
+ detectedAt: new Date().toISOString()
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Generate test file glob patterns based on framework
333
+ */
334
+ function generateTestGlobPatterns(uiFramework) {
335
+ const patterns = [
336
+ '**/*.test.ts',
337
+ '**/*.test.tsx',
338
+ '**/*.spec.ts',
339
+ '**/*.spec.tsx',
340
+ '__tests__/**/*.ts',
341
+ '__tests__/**/*.tsx',
342
+ ];
343
+
344
+ // Framework-specific test patterns
345
+ if (uiFramework === 'angular') {
346
+ patterns.push('**/*.spec.ts');
347
+ }
348
+
349
+ if (uiFramework === 'nestjs') {
350
+ patterns.push('test/**/*.e2e-spec.ts');
351
+ }
352
+
353
+ return patterns;
354
+ }
355
+
356
+ /**
357
+ * Detect important config files in the project
358
+ */
359
+ function detectConfigFiles() {
360
+ const configFiles = {};
361
+ const checkFiles = [
362
+ 'tsconfig.json',
363
+ 'package.json',
364
+ 'tailwind.config.js',
365
+ 'tailwind.config.ts',
366
+ 'next.config.js',
367
+ 'next.config.mjs',
368
+ 'vite.config.ts',
369
+ 'webpack.config.js',
370
+ '.eslintrc',
371
+ '.eslintrc.js',
372
+ '.eslintrc.json',
373
+ 'jest.config.js',
374
+ 'vitest.config.ts',
375
+ ];
376
+
377
+ for (const file of checkFiles) {
378
+ const fullPath = path.join(PROJECT_ROOT, file);
379
+ if (fs.existsSync(fullPath)) {
380
+ configFiles[file] = true;
381
+ }
382
+ }
383
+
384
+ return configFiles;
385
+ }
386
+
387
+ /**
388
+ * Detect type import locations based on project structure
389
+ */
390
+ function detectTypeLocations() {
391
+ const locations = {};
392
+
393
+ // Check for common patterns
394
+ const featureTypesExist = findFiles(path.join(PROJECT_ROOT, 'src'), /\/api\/types\.ts$/).length > 0;
395
+ if (featureTypesExist) {
396
+ locations['features'] = '../api/types';
397
+ }
398
+
399
+ const sharedTypesDir = path.join(PROJECT_ROOT, 'src/types');
400
+ if (fs.existsSync(sharedTypesDir)) {
401
+ locations['shared'] = '@/types';
402
+ }
403
+
404
+ return locations;
405
+ }
406
+
407
+ /**
408
+ * Generate warnings based on detected framework
409
+ */
410
+ function generateWarnings(uiFramework, stylingApproach) {
411
+ const warnings = [];
412
+
413
+ // Framework-specific warnings
414
+ if (uiFramework === 'react' || uiFramework === 'next') {
415
+ // Check React version for JSX transform
416
+ const packageJsonPath = path.join(PROJECT_ROOT, 'package.json');
417
+ if (fs.existsSync(packageJsonPath)) {
418
+ try {
419
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
420
+ const reactVersion = pkg.dependencies?.react || pkg.devDependencies?.react || '';
421
+ if (reactVersion && !reactVersion.includes('16.')) {
422
+ warnings.push("Don't import React directly - use named imports (useState, useCallback)");
423
+ }
424
+ } catch {}
425
+ }
426
+ }
427
+
428
+ // Styling-specific warnings
429
+ if (stylingApproach === 'styled-components') {
430
+ warnings.push('Use transient props ($propName) to prevent DOM warnings');
431
+ }
432
+
433
+ if (stylingApproach === 'tailwind') {
434
+ // Check if cn utility exists
435
+ const utilsPath = path.join(PROJECT_ROOT, 'src/lib/utils.ts');
436
+ if (!fs.existsSync(utilsPath)) {
437
+ warnings.push("cn() utility may not exist - use clsx or className directly");
438
+ }
439
+ }
440
+
441
+ return warnings;
442
+ }
443
+
444
+ /**
445
+ * Detect directories to exclude from type scanning
446
+ */
447
+ function detectExcludeDirectories() {
448
+ // Always exclude these
449
+ const excludes = ['__tests__', '__mocks__', 'node_modules', '.git', 'dist', 'build'];
450
+
451
+ // Check for monorepo structure and add internal packages
452
+ const packagesDir = path.join(PROJECT_ROOT, 'packages');
453
+ if (fs.existsSync(packagesDir)) {
454
+ try {
455
+ const packages = fs.readdirSync(packagesDir);
456
+ // Internal packages often have types that aren't relevant to app code
457
+ // User can customize this via config
458
+ } catch {}
459
+ }
460
+
461
+ return excludes;
462
+ }
463
+
464
+ /**
465
+ * Detect type patterns to exclude (project-specific internal types)
466
+ */
467
+ function detectExcludeTypePatterns() {
468
+ // Start with empty - let users configure this per project
469
+ // During onboarding, we'll ask if there are internal types to exclude
470
+ return [];
471
+ }
472
+
473
+ // ============================================================
474
+ // Main Analysis Function
475
+ // ============================================================
476
+
477
+ function analyzeProject() {
478
+ console.log('Analyzing project for hybrid mode configuration...\n');
479
+
480
+ const analysis = {
481
+ uiFramework: detectUIFramework(),
482
+ stylingApproach: detectStylingApproach(),
483
+ componentDirs: findComponentDirs(),
484
+ typeDirs: findTypeDirs(),
485
+ availableComponents: {},
486
+ typeLocations: detectTypeLocations(),
487
+ doNotImport: ['React'], // Default for React 17+
488
+ excludeTypePatterns: detectExcludeTypePatterns(),
489
+ excludeDirectories: detectExcludeDirectories(),
490
+ projectWarnings: [],
491
+ customRules: [],
492
+ };
493
+
494
+ // Scan components
495
+ for (const dir of analysis.componentDirs) {
496
+ const components = scanComponentExports(dir);
497
+ Object.assign(analysis.availableComponents, components);
498
+ }
499
+
500
+ // Generate warnings
501
+ analysis.projectWarnings = generateWarnings(analysis.uiFramework, analysis.stylingApproach);
502
+
503
+ // Report findings
504
+ console.log(`UI Framework: ${analysis.uiFramework || 'not detected'}`);
505
+ console.log(`Styling: ${analysis.stylingApproach || 'not detected'}`);
506
+ console.log(`Component dirs: ${analysis.componentDirs.length > 0 ? analysis.componentDirs.join(', ') : 'none found'}`);
507
+ console.log(`Components found: ${Object.keys(analysis.availableComponents).length}`);
508
+ console.log(`Type locations: ${Object.keys(analysis.typeLocations).length > 0 ? JSON.stringify(analysis.typeLocations) : 'default'}`);
509
+ console.log('');
510
+
511
+ return analysis;
512
+ }
513
+
514
+ /**
515
+ * Update config.json with analyzed project context
516
+ */
517
+ function updateConfig(analysis) {
518
+ if (!fs.existsSync(CONFIG_PATH)) {
519
+ console.log('Warning: config.json not found. Run flow init first.');
520
+ return false;
521
+ }
522
+
523
+ try {
524
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
525
+
526
+ // Ensure hybrid section exists
527
+ if (!config.hybrid) config.hybrid = {};
528
+ if (!config.hybrid.projectContext) config.hybrid.projectContext = {};
529
+
530
+ // Update project context
531
+ const ctx = config.hybrid.projectContext;
532
+ ctx.uiFramework = analysis.uiFramework;
533
+ ctx.stylingApproach = analysis.stylingApproach;
534
+ ctx.componentDirs = analysis.componentDirs;
535
+ ctx.typeDirs = analysis.typeDirs;
536
+ ctx.availableComponents = analysis.availableComponents;
537
+ ctx.typeLocations = analysis.typeLocations;
538
+ ctx.doNotImport = analysis.doNotImport;
539
+ ctx.excludeTypePatterns = analysis.excludeTypePatterns;
540
+ ctx.excludeDirectories = analysis.excludeDirectories;
541
+ ctx.projectWarnings = analysis.projectWarnings;
542
+ ctx.customRules = analysis.customRules;
543
+
544
+ // Add simplified framework config with glob patterns
545
+ // This is the one-time detection result that can be used without re-scanning
546
+ config.frameworkConfig = generateFrameworkConfig(analysis);
547
+
548
+ // Write back
549
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
550
+
551
+ console.log('✓ Updated config.json with project context');
552
+ console.log(` Framework: ${analysis.uiFramework || 'not detected'}`);
553
+ console.log(` Styling: ${analysis.stylingApproach || 'not detected'}`);
554
+ console.log(` Component patterns: ${config.frameworkConfig.componentPatterns.length}`);
555
+ return true;
556
+ } catch (err) {
557
+ console.log(`Error updating config: ${err.message}`);
558
+ return false;
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Delete cached context to force regeneration
564
+ */
565
+ function clearContextCache() {
566
+ const cachePath = path.join(PROJECT_ROOT, '.workflow/state/hybrid-context.md');
567
+ if (fs.existsSync(cachePath)) {
568
+ fs.unlinkSync(cachePath);
569
+ console.log('✓ Cleared hybrid context cache');
570
+ }
571
+ }
572
+
573
+ // ============================================================
574
+ // Codebase Insights Generation
575
+ // ============================================================
576
+
577
+ /**
578
+ * Detect architecture pattern based on directory structure
579
+ */
580
+ function detectArchitecturePattern() {
581
+ const indicators = {
582
+ monorepo: fs.existsSync(path.join(PROJECT_ROOT, 'packages')) ||
583
+ fs.existsSync(path.join(PROJECT_ROOT, 'apps')),
584
+ modular: fs.existsSync(path.join(PROJECT_ROOT, 'src/modules')) ||
585
+ fs.existsSync(path.join(PROJECT_ROOT, 'src/features')),
586
+ layered: fs.existsSync(path.join(PROJECT_ROOT, 'src/controllers')) &&
587
+ fs.existsSync(path.join(PROJECT_ROOT, 'src/services')),
588
+ componentBased: fs.existsSync(path.join(PROJECT_ROOT, 'src/components')),
589
+ pagesBased: fs.existsSync(path.join(PROJECT_ROOT, 'pages')) ||
590
+ fs.existsSync(path.join(PROJECT_ROOT, 'app'))
591
+ };
592
+
593
+ if (indicators.monorepo) return { pattern: 'Monorepo', description: 'Multi-package workspace with shared code' };
594
+ if (indicators.modular) return { pattern: 'Modular Monolith', description: 'Feature-based module structure in single deployable' };
595
+ if (indicators.layered) return { pattern: 'Layered Architecture', description: 'Separated controllers, services, and repositories' };
596
+ if (indicators.pagesBased && indicators.componentBased) return { pattern: 'Page-Component Architecture', description: 'Page-based routing with shared components' };
597
+ if (indicators.componentBased) return { pattern: 'Component-Based', description: 'UI component focused structure' };
598
+
599
+ return { pattern: 'Simple/Flat', description: 'Basic project structure' };
600
+ }
601
+
602
+ /**
603
+ * Detect naming conventions from file samples
604
+ */
605
+ function detectConventions() {
606
+ const conventions = {
607
+ files: 'unknown',
608
+ components: 'unknown',
609
+ functions: 'unknown',
610
+ constants: 'unknown',
611
+ imports: 'unknown'
612
+ };
613
+
614
+ const srcDir = path.join(PROJECT_ROOT, 'src');
615
+ if (!fs.existsSync(srcDir)) return conventions;
616
+
617
+ try {
618
+ // Sample some files
619
+ const tsFiles = findFiles(srcDir, /\.tsx?$/).slice(0, 20);
620
+
621
+ // Check file naming
622
+ const fileNames = tsFiles.map(f => path.basename(f, path.extname(f)));
623
+ const kebabCount = fileNames.filter(n => /^[a-z]+(-[a-z]+)*$/.test(n)).length;
624
+ const pascalCount = fileNames.filter(n => /^[A-Z][a-zA-Z]*$/.test(n)).length;
625
+ const camelCount = fileNames.filter(n => /^[a-z][a-zA-Z]*$/.test(n)).length;
626
+
627
+ if (kebabCount > pascalCount && kebabCount > camelCount) conventions.files = 'kebab-case';
628
+ else if (pascalCount > camelCount) conventions.files = 'PascalCase';
629
+ else if (camelCount > 0) conventions.files = 'camelCase';
630
+
631
+ // Check component naming (from .tsx files)
632
+ const componentFiles = tsFiles.filter(f => f.endsWith('.tsx'));
633
+ if (componentFiles.length > 0) {
634
+ const content = fs.readFileSync(componentFiles[0], 'utf-8');
635
+ if (/export\s+(default\s+)?function\s+[A-Z]/.test(content)) {
636
+ conventions.components = 'PascalCase function components';
637
+ } else if (/const\s+[A-Z][a-zA-Z]+\s*=\s*\(/.test(content)) {
638
+ conventions.components = 'PascalCase arrow function components';
639
+ }
640
+ }
641
+
642
+ // Check import style
643
+ if (tsFiles.length > 0) {
644
+ const content = fs.readFileSync(tsFiles[0], 'utf-8');
645
+ if (content.includes('@/')) conventions.imports = 'Absolute with @/ alias';
646
+ else if (content.includes('~/')) conventions.imports = 'Absolute with ~/ alias';
647
+ else if (/from\s+['"]\.\.?\//.test(content)) conventions.imports = 'Relative imports';
648
+ }
649
+
650
+ } catch {
651
+ // Ignore errors
652
+ }
653
+
654
+ return conventions;
655
+ }
656
+
657
+ /**
658
+ * Detect potential issues in the codebase
659
+ */
660
+ function detectPotentialIssues() {
661
+ const issues = [];
662
+ const srcDir = path.join(PROJECT_ROOT, 'src');
663
+
664
+ if (!fs.existsSync(srcDir)) return issues;
665
+
666
+ try {
667
+ // Check for large files (>500 lines)
668
+ const allFiles = findFiles(srcDir, /\.(ts|tsx|js|jsx)$/);
669
+ for (const file of allFiles.slice(0, 100)) {
670
+ try {
671
+ const content = fs.readFileSync(file, 'utf-8');
672
+ const lines = content.split('\n').length;
673
+ if (lines > 500) {
674
+ issues.push({
675
+ type: 'large-file',
676
+ severity: 'warning',
677
+ file: path.relative(PROJECT_ROOT, file),
678
+ message: `Large file (${lines} lines) - consider splitting`
679
+ });
680
+ }
681
+ } catch {}
682
+ }
683
+
684
+ // Check for files without tests
685
+ const componentFiles = allFiles.filter(f =>
686
+ f.includes('/components/') &&
687
+ !f.includes('.test.') &&
688
+ !f.includes('.spec.') &&
689
+ !f.includes('.stories.')
690
+ );
691
+ const testFiles = findFiles(srcDir, /\.(test|spec)\.(ts|tsx|js|jsx)$/);
692
+ const testedComponents = new Set(
693
+ testFiles.map(f => path.basename(f).replace(/\.(test|spec)\.(ts|tsx|js|jsx)$/, ''))
694
+ );
695
+
696
+ for (const comp of componentFiles.slice(0, 20)) {
697
+ const baseName = path.basename(comp).replace(/\.(ts|tsx|js|jsx)$/, '');
698
+ if (!testedComponents.has(baseName) && baseName !== 'index') {
699
+ issues.push({
700
+ type: 'missing-test',
701
+ severity: 'info',
702
+ file: path.relative(PROJECT_ROOT, comp),
703
+ message: 'No test file found'
704
+ });
705
+ }
706
+ }
707
+
708
+ // Check for console.log statements
709
+ let consoleCount = 0;
710
+ for (const file of allFiles.slice(0, 50)) {
711
+ try {
712
+ const content = fs.readFileSync(file, 'utf-8');
713
+ const matches = content.match(/console\.(log|warn|error)\(/g);
714
+ if (matches) consoleCount += matches.length;
715
+ } catch {}
716
+ }
717
+ if (consoleCount > 10) {
718
+ issues.push({
719
+ type: 'console-statements',
720
+ severity: 'info',
721
+ message: `Found ${consoleCount} console statements - consider removing for production`
722
+ });
723
+ }
724
+
725
+ } catch {
726
+ // Ignore errors
727
+ }
728
+
729
+ return issues;
730
+ }
731
+
732
+ /**
733
+ * Gather project statistics
734
+ */
735
+ function gatherStatistics() {
736
+ const stats = {
737
+ totalFiles: 0,
738
+ typeScriptFiles: 0,
739
+ javaScriptFiles: 0,
740
+ testFiles: 0,
741
+ componentCount: 0,
742
+ hookCount: 0,
743
+ serviceCount: 0
744
+ };
745
+
746
+ const srcDir = path.join(PROJECT_ROOT, 'src');
747
+ if (!fs.existsSync(srcDir)) return stats;
748
+
749
+ try {
750
+ const allFiles = findFiles(srcDir, /\.(ts|tsx|js|jsx)$/);
751
+ stats.totalFiles = allFiles.length;
752
+ stats.typeScriptFiles = allFiles.filter(f => /\.tsx?$/.test(f)).length;
753
+ stats.javaScriptFiles = allFiles.filter(f => /\.jsx?$/.test(f)).length;
754
+ stats.testFiles = allFiles.filter(f => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f)).length;
755
+
756
+ // Count components (files in components/ directory or .tsx files with PascalCase names)
757
+ stats.componentCount = allFiles.filter(f =>
758
+ (f.includes('/components/') || f.includes('/ui/')) &&
759
+ !f.includes('.test.') &&
760
+ !f.includes('.stories.')
761
+ ).length;
762
+
763
+ // Count hooks (use*.ts files)
764
+ stats.hookCount = allFiles.filter(f =>
765
+ /\/use[A-Z][a-zA-Z]*\.(ts|tsx)$/.test(f)
766
+ ).length;
767
+
768
+ // Count services
769
+ stats.serviceCount = allFiles.filter(f =>
770
+ f.includes('.service.') || f.includes('/services/')
771
+ ).length;
772
+
773
+ } catch {
774
+ // Ignore errors
775
+ }
776
+
777
+ return stats;
778
+ }
779
+
780
+ /**
781
+ * Generate codebase insights markdown file
782
+ */
783
+ function generateCodebaseInsights() {
784
+ const architecture = detectArchitecturePattern();
785
+ const conventions = detectConventions();
786
+ const issues = detectPotentialIssues();
787
+ const stats = gatherStatistics();
788
+ const framework = detectUIFramework();
789
+ const styling = detectStylingApproach();
790
+
791
+ const tsRatio = stats.totalFiles > 0
792
+ ? Math.round((stats.typeScriptFiles / stats.totalFiles) * 100)
793
+ : 0;
794
+
795
+ let markdown = `# Codebase Insights
796
+
797
+ Generated: ${new Date().toISOString().split('T')[0]}
798
+
799
+ ## Architecture Pattern
800
+
801
+ **${architecture.pattern}**
802
+
803
+ ${architecture.description}
804
+
805
+ ## Tech Stack
806
+
807
+ - **Framework**: ${framework || 'Not detected'}
808
+ - **Styling**: ${styling || 'Not detected'}
809
+ - **TypeScript**: ${tsRatio}% of codebase
810
+
811
+ ## Conventions Detected
812
+
813
+ | Aspect | Convention |
814
+ |--------|------------|
815
+ | Files | ${conventions.files} |
816
+ | Components | ${conventions.components} |
817
+ | Imports | ${conventions.imports} |
818
+
819
+ ## Statistics
820
+
821
+ | Metric | Count |
822
+ |--------|-------|
823
+ | Total source files | ${stats.totalFiles} |
824
+ | TypeScript files | ${stats.typeScriptFiles} |
825
+ | Test files | ${stats.testFiles} |
826
+ | Components | ${stats.componentCount} |
827
+ | Hooks | ${stats.hookCount} |
828
+ | Services | ${stats.serviceCount} |
829
+
830
+ `;
831
+
832
+ // Add issues section if any
833
+ if (issues.length > 0) {
834
+ markdown += `## Potential Issues
835
+
836
+ `;
837
+ const grouped = {
838
+ 'large-file': [],
839
+ 'missing-test': [],
840
+ 'console-statements': [],
841
+ 'other': []
842
+ };
843
+
844
+ for (const issue of issues) {
845
+ const group = grouped[issue.type] || grouped['other'];
846
+ group.push(issue);
847
+ }
848
+
849
+ if (grouped['large-file'].length > 0) {
850
+ markdown += `### Large Files\n`;
851
+ for (const issue of grouped['large-file'].slice(0, 5)) {
852
+ markdown += `- [ ] \`${issue.file}\` - ${issue.message}\n`;
853
+ }
854
+ markdown += '\n';
855
+ }
856
+
857
+ if (grouped['missing-test'].length > 0) {
858
+ markdown += `### Missing Tests (${grouped['missing-test'].length} files)\n`;
859
+ for (const issue of grouped['missing-test'].slice(0, 5)) {
860
+ markdown += `- [ ] \`${issue.file}\`\n`;
861
+ }
862
+ if (grouped['missing-test'].length > 5) {
863
+ markdown += `- ... and ${grouped['missing-test'].length - 5} more\n`;
864
+ }
865
+ markdown += '\n';
866
+ }
867
+
868
+ if (grouped['console-statements'].length > 0) {
869
+ markdown += `### Code Quality\n`;
870
+ for (const issue of grouped['console-statements']) {
871
+ markdown += `- [ ] ${issue.message}\n`;
872
+ }
873
+ markdown += '\n';
874
+ }
875
+ } else {
876
+ markdown += `## Code Health
877
+
878
+ No significant issues detected.\n`;
879
+ }
880
+
881
+ return markdown;
882
+ }
883
+
884
+ /**
885
+ * Save codebase insights to file
886
+ */
887
+ function saveCodebaseInsights() {
888
+ const insightsPath = path.join(PROJECT_ROOT, '.workflow', 'state', 'codebase-insights.md');
889
+ const dir = path.dirname(insightsPath);
890
+
891
+ if (!fs.existsSync(dir)) {
892
+ fs.mkdirSync(dir, { recursive: true });
893
+ }
894
+
895
+ const markdown = generateCodebaseInsights();
896
+ fs.writeFileSync(insightsPath, markdown);
897
+
898
+ console.log(`✓ Generated codebase insights: ${insightsPath}`);
899
+ return insightsPath;
900
+ }
901
+
902
+ // ============================================================
903
+ // CLI
904
+ // ============================================================
905
+
906
+ function printUsage() {
907
+ console.log(`
908
+ Wogi Flow - Project Analyzer
909
+
910
+ Analyzes your project and configures hybrid mode settings so the local LLM
911
+ has all the context it needs to generate correct code.
912
+
913
+ Usage:
914
+ node flow-project-analyzer.js [project-root]
915
+
916
+ What it detects:
917
+ - UI framework (React, Next.js, Vue, Angular, etc.)
918
+ - Styling approach (styled-components, Tailwind, CSS modules, etc.)
919
+ - Component directories and their exports
920
+ - Type file locations
921
+ - Import conventions
922
+
923
+ The results are saved to config.json -> hybrid.projectContext
924
+ `);
925
+ }
926
+
927
+ // Main
928
+ if (require.main === module) {
929
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
930
+ printUsage();
931
+ process.exit(0);
932
+ }
933
+
934
+ // Generate insights only
935
+ if (process.argv.includes('--insights')) {
936
+ saveCodebaseInsights();
937
+ process.exit(0);
938
+ }
939
+
940
+ const analysis = analyzeProject();
941
+ const success = updateConfig(analysis);
942
+ clearContextCache();
943
+
944
+ // Also generate codebase insights during full analysis
945
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
946
+ if (config.codebaseInsights?.enabled !== false) {
947
+ saveCodebaseInsights();
948
+ }
949
+
950
+ if (success) {
951
+ console.log('\n✓ Project analysis complete!');
952
+ console.log(' The local LLM will now have accurate context about your project.');
953
+ console.log(' Run "flow hybrid enable" to start using hybrid mode.');
954
+ }
955
+
956
+ process.exit(success ? 0 : 1);
957
+ }
958
+
959
+ module.exports = {
960
+ analyzeProject,
961
+ updateConfig,
962
+ detectUIFramework,
963
+ detectStylingApproach,
964
+ scanComponentExports,
965
+ generateComponentGlobPatterns,
966
+ generateFrameworkConfig,
967
+ detectConfigFiles,
968
+ // Codebase insights
969
+ detectArchitecturePattern,
970
+ detectConventions,
971
+ detectPotentialIssues,
972
+ gatherStatistics,
973
+ generateCodebaseInsights,
974
+ saveCodebaseInsights
975
+ };