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,827 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Instruction Richness Module
5
+ *
6
+ * Controls how much detail to include for the local LLM.
7
+ *
8
+ * KEY INSIGHT: Local LLM tokens are FREE. The goal is to give the LLM
9
+ * everything it needs for 90%+ success rate. Failed executions cost more
10
+ * (in Claude retry tokens) than generous upfront context.
11
+ *
12
+ * This module provides GUIDANCE on context richness, not hard limits.
13
+ * When in doubt, include MORE context - it's free for the local LLM!
14
+ *
15
+ * Usage:
16
+ * const { getInstructionRichness } = require('./flow-instruction-richness');
17
+ * const richness = getInstructionRichness('large');
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+
23
+ // LSP integration for accurate type information
24
+ let lspModule = null;
25
+ try {
26
+ lspModule = require('./flow-lsp');
27
+ } catch (err) {
28
+ // LSP module not available, will use fallback
29
+ }
30
+
31
+ // ============================================================
32
+ // Instruction Richness Levels (Guidance, NOT Limits)
33
+ // ============================================================
34
+
35
+ /**
36
+ * Richness levels guide what context to include for the local LLM.
37
+ *
38
+ * IMPORTANT: These are MINIMUMS for each complexity level.
39
+ * Always include MORE context if there's any doubt - local LLM tokens are free!
40
+ *
41
+ * The goal is 90%+ success rate, not minimizing tokens.
42
+ */
43
+ const INSTRUCTION_RICHNESS = {
44
+ minimal: {
45
+ // Even "minimal" should include enough for success
46
+ includeProjectContext: true, // Always include
47
+ includeTypeDefinitions: true, // Always include - prevents type errors
48
+ includeRelatedCode: false,
49
+ includeExamples: false,
50
+ includePatterns: true, // Always include - consistency matters
51
+ includeFullFileContents: false,
52
+ templateVerbosity: 'standard', // Upgraded from 'concise'
53
+ description: 'Simple changes - but still include types and patterns for accuracy'
54
+ },
55
+
56
+ standard: {
57
+ includeProjectContext: true,
58
+ includeTypeDefinitions: true,
59
+ includeRelatedCode: true, // Include related code for context
60
+ includeExamples: true, // Include examples - they help!
61
+ includePatterns: true,
62
+ includeFullFileContents: false,
63
+ templateVerbosity: 'detailed', // Upgraded from 'standard'
64
+ description: 'Typical tasks - include examples and related code for best results'
65
+ },
66
+
67
+ rich: {
68
+ includeProjectContext: true,
69
+ includeTypeDefinitions: true,
70
+ includeRelatedCode: true,
71
+ includeExamples: true,
72
+ includePatterns: true,
73
+ includeFullFileContents: true, // Include full files for complex tasks
74
+ templateVerbosity: 'comprehensive',
75
+ description: 'Complex tasks - full context for highest success rate'
76
+ },
77
+
78
+ maximum: {
79
+ includeProjectContext: true,
80
+ includeTypeDefinitions: true,
81
+ includeRelatedCode: true,
82
+ includeExamples: true,
83
+ includePatterns: true,
84
+ includeFullFileContents: true,
85
+ templateVerbosity: 'comprehensive',
86
+ description: 'XL tasks - everything available, maximum context'
87
+ }
88
+ };
89
+
90
+ // ============================================================
91
+ // Complexity to Richness Mapping
92
+ // ============================================================
93
+
94
+ /**
95
+ * Maps complexity level (from flow-complexity.js) to instruction richness
96
+ */
97
+ const COMPLEXITY_TO_RICHNESS = {
98
+ 'small': 'minimal',
99
+ 'medium': 'standard',
100
+ 'large': 'rich',
101
+ 'xl': 'maximum'
102
+ };
103
+
104
+ /**
105
+ * Gets the instruction richness configuration for a complexity level
106
+ *
107
+ * @param {string} complexityLevel - 'small', 'medium', 'large', or 'xl'
108
+ * @param {Object} config - Optional config overrides from config.json
109
+ * @returns {Object} - Richness configuration
110
+ */
111
+ function getInstructionRichness(complexityLevel, config = {}) {
112
+ // Map complexity to richness level
113
+ let richnessLevel = COMPLEXITY_TO_RICHNESS[complexityLevel] || 'standard';
114
+
115
+ // Check for minimum richness override in config
116
+ const minRichness = config.minRichness;
117
+ if (minRichness) {
118
+ const levels = ['minimal', 'standard', 'rich', 'maximum'];
119
+ const currentIndex = levels.indexOf(richnessLevel);
120
+ const minIndex = levels.indexOf(minRichness);
121
+ if (minIndex > currentIndex) {
122
+ richnessLevel = minRichness;
123
+ }
124
+ }
125
+
126
+ const richness = { ...INSTRUCTION_RICHNESS[richnessLevel] };
127
+
128
+ // Add level name for reference
129
+ richness.level = richnessLevel;
130
+
131
+ return richness;
132
+ }
133
+
134
+ // ============================================================
135
+ // Context Loaders
136
+ // ============================================================
137
+
138
+ /**
139
+ * Loads project context from workflow state
140
+ */
141
+ function loadProjectContext(projectRoot) {
142
+ const contextPath = path.join(projectRoot, '.workflow', 'state', 'hybrid-context.md');
143
+ const projectPath = path.join(projectRoot, '.workflow', 'specs', 'project.md');
144
+
145
+ let context = '';
146
+
147
+ // Try hybrid-context first (optimized for hybrid mode)
148
+ if (fs.existsSync(contextPath)) {
149
+ context += fs.readFileSync(contextPath, 'utf-8');
150
+ }
151
+
152
+ // Add project overview
153
+ if (fs.existsSync(projectPath)) {
154
+ const projectMd = fs.readFileSync(projectPath, 'utf-8');
155
+ // Extract just the summary section
156
+ const summaryMatch = projectMd.match(/## Summary[\s\S]*?(?=##|$)/);
157
+ if (summaryMatch) {
158
+ context += '\n\n### Project Summary\n' + summaryMatch[0];
159
+ }
160
+ }
161
+
162
+ return context || null;
163
+ }
164
+
165
+ /**
166
+ * Loads ALL coding patterns from decisions.md
167
+ * Extracts all ## sections, not just Coding Standards and Component Architecture
168
+ */
169
+ function loadPatterns(projectRoot) {
170
+ const decisionsPath = path.join(projectRoot, '.workflow', 'state', 'decisions.md');
171
+
172
+ if (!fs.existsSync(decisionsPath)) {
173
+ return null;
174
+ }
175
+
176
+ const content = fs.readFileSync(decisionsPath, 'utf-8');
177
+
178
+ // Extract ALL ## sections from decisions.md
179
+ // This includes: Naming Conventions, File Structure, Error Handling, etc.
180
+ const sections = content.match(/## [^\n]+[\s\S]*?(?=\n## |$)/g);
181
+
182
+ if (!sections || sections.length === 0) {
183
+ // Fallback: return full content if no sections found
184
+ return content.trim() || null;
185
+ }
186
+
187
+ return sections.map(s => s.trim()).join('\n\n');
188
+ }
189
+
190
+ /**
191
+ * Extracts keywords from task description for relevance filtering
192
+ */
193
+ function extractTaskKeywords(taskDescription) {
194
+ if (!taskDescription) return [];
195
+
196
+ // Extract meaningful words (nouns, component names, etc.)
197
+ const words = taskDescription
198
+ .replace(/[^a-zA-Z0-9\s]/g, ' ')
199
+ .split(/\s+/)
200
+ .filter(w => w.length > 2)
201
+ .map(w => w.toLowerCase());
202
+
203
+ // Also extract PascalCase component names
204
+ const pascalCaseNames = taskDescription.match(/[A-Z][a-z]+(?:[A-Z][a-z]+)*/g) || [];
205
+
206
+ return [...new Set([...words, ...pascalCaseNames.map(n => n.toLowerCase())])];
207
+ }
208
+
209
+ /**
210
+ * Checks if a type definition is relevant to the task
211
+ */
212
+ function isTypeRelevant(typeDefinition, keywords, filename) {
213
+ if (!keywords || keywords.length === 0) return true;
214
+
215
+ const typeLower = typeDefinition.toLowerCase();
216
+ const filenameLower = filename.toLowerCase();
217
+
218
+ // Always include types that match the filename
219
+ if (typeLower.includes(filenameLower) || filenameLower.includes(typeLower.split(/\s+/)[1]?.toLowerCase() || '')) {
220
+ return true;
221
+ }
222
+
223
+ // Check if any keyword appears in the type definition
224
+ return keywords.some(keyword => typeLower.includes(keyword));
225
+ }
226
+
227
+ /**
228
+ * Finds TypeScript types relevant to a file path and task
229
+ * @param {string} projectRoot - Project root directory
230
+ * @param {string} filePath - Target file path
231
+ * @param {Object} options - Options including taskDescription and maxTypes
232
+ */
233
+ function loadRelevantTypes(projectRoot, filePath, options = {}) {
234
+ if (!filePath) return null;
235
+
236
+ const { taskDescription = '', maxTypes = 5 } = options;
237
+ const keywords = extractTaskKeywords(taskDescription);
238
+ const types = [];
239
+ const dir = path.dirname(filePath);
240
+ const filename = path.basename(filePath, path.extname(filePath));
241
+
242
+ // Common type file locations - prioritize closest first
243
+ const typeLocations = [
244
+ path.join(dir, 'types.ts'),
245
+ path.join(dir, 'types.d.ts'),
246
+ path.join(dir, '..', 'types.ts'),
247
+ path.join(dir, '..', 'types', 'index.ts'),
248
+ path.join(dir, '..', 'api', 'types.ts'),
249
+ path.join(projectRoot, 'src', 'types', 'index.ts'),
250
+ path.join(projectRoot, 'src', 'types.ts')
251
+ ];
252
+
253
+ for (const typePath of typeLocations) {
254
+ const fullPath = path.isAbsolute(typePath) ? typePath : path.join(projectRoot, typePath);
255
+ if (fs.existsSync(fullPath)) {
256
+ try {
257
+ const content = fs.readFileSync(fullPath, 'utf-8');
258
+ // Extract interface/type definitions (simplified)
259
+ const typeMatches = content.match(/(?:export\s+)?(?:interface|type)\s+\w+[\s\S]*?(?=\n(?:export\s+)?(?:interface|type|const|function)|$)/g);
260
+ if (typeMatches) {
261
+ // Filter types by relevance
262
+ const relevantTypes = typeMatches.filter(t => isTypeRelevant(t, keywords, filename));
263
+
264
+ if (relevantTypes.length > 0) {
265
+ types.push(`// From ${path.relative(projectRoot, fullPath)}`);
266
+ types.push(...relevantTypes.slice(0, maxTypes - types.length));
267
+ }
268
+ }
269
+ } catch {
270
+ // Ignore read errors
271
+ }
272
+ }
273
+
274
+ // Stop if we have enough types
275
+ if (types.length >= maxTypes) break;
276
+ }
277
+
278
+ return types.length > 0 ? types.join('\n\n') : null;
279
+ }
280
+
281
+ /**
282
+ * LSP-enhanced type loading
283
+ * Uses Language Server Protocol when available for accurate type info,
284
+ * falls back to regex-based loading otherwise.
285
+ *
286
+ * @param {string} projectRoot - Project root directory
287
+ * @param {string} filePath - Target file path
288
+ * @param {object} options - Options (taskDescription, maxTypes)
289
+ * @returns {Promise<string|null>} Formatted type information
290
+ */
291
+ async function loadRelevantTypesWithLSP(projectRoot, filePath, options = {}) {
292
+ const { getConfig } = require('./flow-utils');
293
+ const config = getConfig();
294
+
295
+ // Check if LSP is enabled
296
+ if (!config.lsp?.enabled || !lspModule) {
297
+ return loadRelevantTypes(projectRoot, filePath, options);
298
+ }
299
+
300
+ try {
301
+ const lsp = await lspModule.getLSP(projectRoot);
302
+ if (!lsp) {
303
+ return loadRelevantTypes(projectRoot, filePath, options);
304
+ }
305
+
306
+ const types = [];
307
+ const { taskDescription = '', maxTypes = 5 } = options;
308
+ const keywords = extractTaskKeywords(taskDescription);
309
+
310
+ // If we have a target file, extract identifiers and get their types
311
+ const absPath = path.isAbsolute(filePath) ? filePath : path.join(projectRoot, filePath);
312
+ if (fs.existsSync(absPath)) {
313
+ const content = fs.readFileSync(absPath, 'utf-8');
314
+ const identifiers = extractIdentifiersForLSP(content, keywords);
315
+
316
+ // Get types for identified positions
317
+ for (const id of identifiers.slice(0, maxTypes)) {
318
+ try {
319
+ const typeInfo = await lsp.getTypeAtPosition(absPath, id.line, id.character);
320
+ if (typeInfo) {
321
+ types.push(`// ${id.name}\n${typeInfo}`);
322
+ }
323
+ } catch (err) {
324
+ // Skip individual errors
325
+ }
326
+ }
327
+ }
328
+
329
+ // If LSP didn't find enough types, supplement with regex-based loading
330
+ if (types.length < maxTypes) {
331
+ const regexTypes = loadRelevantTypes(projectRoot, filePath, {
332
+ ...options,
333
+ maxTypes: maxTypes - types.length
334
+ });
335
+ if (regexTypes) {
336
+ types.push(regexTypes);
337
+ }
338
+ }
339
+
340
+ return types.length > 0 ? types.join('\n\n') : null;
341
+ } catch (err) {
342
+ // Fallback to regex-based loading on any error
343
+ return loadRelevantTypes(projectRoot, filePath, options);
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Extract identifiers from code that we want to get types for
349
+ * @param {string} content - File content
350
+ * @param {string[]} keywords - Task-related keywords to prioritize
351
+ * @returns {Array<{name: string, line: number, character: number}>}
352
+ */
353
+ function extractIdentifiersForLSP(content, keywords = []) {
354
+ const identifiers = [];
355
+ const lines = content.split('\n');
356
+
357
+ for (let lineNum = 0; lineNum < lines.length; lineNum++) {
358
+ const line = lines[lineNum];
359
+
360
+ // Look for function/const/interface declarations
361
+ const patterns = [
362
+ // function name(
363
+ /\bfunction\s+(\w+)\s*\(/g,
364
+ // const name = or const name:
365
+ /\bconst\s+(\w+)\s*[=:]/g,
366
+ // interface Name
367
+ /\binterface\s+(\w+)/g,
368
+ // type Name
369
+ /\btype\s+(\w+)\s*=/g,
370
+ // : TypeName (for type annotations)
371
+ /:\s*(\w+)(?:[<\s,\[\]>|&]|$)/g
372
+ ];
373
+
374
+ for (const pattern of patterns) {
375
+ let match;
376
+ while ((match = pattern.exec(line)) !== null) {
377
+ const name = match[1];
378
+ const character = match.index + match[0].indexOf(name);
379
+
380
+ // Prioritize keywords
381
+ const priority = keywords.some(k =>
382
+ name.toLowerCase().includes(k.toLowerCase())
383
+ ) ? 0 : 1;
384
+
385
+ identifiers.push({
386
+ name,
387
+ line: lineNum,
388
+ character,
389
+ priority
390
+ });
391
+ }
392
+ }
393
+ }
394
+
395
+ // Sort by priority (keyword matches first)
396
+ identifiers.sort((a, b) => a.priority - b.priority);
397
+
398
+ return identifiers;
399
+ }
400
+
401
+ /**
402
+ * Finds related code files (similar components, hooks, etc.)
403
+ */
404
+ function loadRelatedCode(projectRoot, filePath, stepType) {
405
+ if (!filePath) return null;
406
+
407
+ const related = [];
408
+ const dir = path.dirname(filePath);
409
+ const ext = path.extname(filePath);
410
+
411
+ // Find siblings or similar files
412
+ const searchDirs = [dir, path.join(dir, '..'), path.join(dir, '..', '..')];
413
+
414
+ for (const searchDir of searchDirs) {
415
+ const fullSearchDir = path.isAbsolute(searchDir) ? searchDir : path.join(projectRoot, searchDir);
416
+ if (!fs.existsSync(fullSearchDir)) continue;
417
+
418
+ try {
419
+ const files = fs.readdirSync(fullSearchDir);
420
+
421
+ for (const file of files) {
422
+ if (!file.endsWith('.tsx') && !file.endsWith('.ts')) continue;
423
+ if (file.includes('.test.') || file.includes('.spec.')) continue;
424
+
425
+ const fullFilePath = path.join(fullSearchDir, file);
426
+ if (fullFilePath === filePath) continue;
427
+
428
+ // Limit to first 2 related files
429
+ if (related.length >= 2) break;
430
+
431
+ try {
432
+ const content = fs.readFileSync(fullFilePath, 'utf-8');
433
+ // Take first 50 lines as example
434
+ const preview = content.split('\n').slice(0, 50).join('\n');
435
+ related.push(`// Example from ${path.relative(projectRoot, fullFilePath)}\n${preview}`);
436
+ } catch {
437
+ // Ignore read errors
438
+ }
439
+ }
440
+ } catch {
441
+ // Ignore dir read errors
442
+ }
443
+
444
+ if (related.length >= 2) break;
445
+ }
446
+
447
+ return related.length > 0 ? related.join('\n\n---\n\n') : null;
448
+ }
449
+
450
+ /**
451
+ * Simple glob pattern matcher for finding example files
452
+ */
453
+ function globSync(basePath, pattern) {
454
+ const results = [];
455
+ const parts = pattern.split('/');
456
+
457
+ const searchDir = (currentPath, remainingParts) => {
458
+ if (remainingParts.length === 0) {
459
+ if (fs.existsSync(currentPath) && fs.statSync(currentPath).isFile()) {
460
+ results.push(currentPath);
461
+ }
462
+ return;
463
+ }
464
+
465
+ const [current, ...rest] = remainingParts;
466
+
467
+ if (current === '**') {
468
+ try {
469
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
470
+ for (const entry of entries) {
471
+ const fullPath = path.join(currentPath, entry.name);
472
+ if (entry.isDirectory()) {
473
+ // Continue with ** (recurse deeper)
474
+ searchDir(fullPath, remainingParts);
475
+ // Also try without ** (match at this level)
476
+ searchDir(fullPath, rest);
477
+ } else if (rest.length === 0) {
478
+ results.push(fullPath);
479
+ } else if (rest.length === 1 && matchGlobPart(entry.name, rest[0])) {
480
+ results.push(fullPath);
481
+ }
482
+ }
483
+ } catch { /* ignore permission errors */ }
484
+ } else if (current.includes('*')) {
485
+ try {
486
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
487
+ for (const entry of entries) {
488
+ if (matchGlobPart(entry.name, current)) {
489
+ const fullPath = path.join(currentPath, entry.name);
490
+ if (entry.isDirectory()) {
491
+ searchDir(fullPath, rest);
492
+ } else if (rest.length === 0) {
493
+ results.push(fullPath);
494
+ }
495
+ }
496
+ }
497
+ } catch { /* ignore permission errors */ }
498
+ } else {
499
+ const nextPath = path.join(currentPath, current);
500
+ if (fs.existsSync(nextPath)) {
501
+ searchDir(nextPath, rest);
502
+ }
503
+ }
504
+ };
505
+
506
+ // Handle src/ prefix - check both with and without
507
+ const srcPath = path.join(basePath, 'src');
508
+ if (fs.existsSync(srcPath)) {
509
+ searchDir(srcPath, parts);
510
+ }
511
+ searchDir(basePath, parts);
512
+
513
+ return [...new Set(results)]; // Dedupe
514
+ }
515
+
516
+ /**
517
+ * Match a filename against a glob pattern part (e.g., *.tsx)
518
+ */
519
+ function matchGlobPart(filename, pattern) {
520
+ const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
521
+ return regex.test(filename);
522
+ }
523
+
524
+ /**
525
+ * Truncate file content to a reasonable size for examples
526
+ */
527
+ function truncateForExample(content, maxLines = 60) {
528
+ const lines = content.split('\n');
529
+ if (lines.length <= maxLines) return content;
530
+
531
+ // Keep imports and first part of file
532
+ const imports = [];
533
+ let importEnd = 0;
534
+ for (let i = 0; i < lines.length; i++) {
535
+ if (lines[i].startsWith('import ') || lines[i].startsWith('from ') || lines[i].trim() === '') {
536
+ imports.push(lines[i]);
537
+ importEnd = i + 1;
538
+ } else if (imports.length > 0) {
539
+ break;
540
+ }
541
+ }
542
+
543
+ const remaining = maxLines - imports.length - 3;
544
+ const body = lines.slice(importEnd, importEnd + remaining);
545
+
546
+ return [
547
+ ...imports,
548
+ ...body,
549
+ '',
550
+ `// ... (${lines.length - importEnd - remaining} more lines truncated)`,
551
+ ''
552
+ ].join('\n');
553
+ }
554
+
555
+ /**
556
+ * Finds examples of similar implementations
557
+ */
558
+ function loadSimilarExamples(projectRoot, stepType, maxExamples = 2) {
559
+ // Map step types to example search patterns
560
+ const patterns = {
561
+ 'create-component': [
562
+ 'components/**/*.tsx',
563
+ 'features/**/components/*.tsx',
564
+ 'app/**/components/*.tsx',
565
+ 'ui/**/*.tsx'
566
+ ],
567
+ 'create-hook': [
568
+ 'hooks/**/*.ts',
569
+ 'hooks/**/*.tsx',
570
+ 'features/**/hooks/*.ts',
571
+ 'lib/hooks/*.ts'
572
+ ],
573
+ 'create-service': [
574
+ 'services/**/*.ts',
575
+ 'api/**/*.ts',
576
+ 'lib/api/*.ts',
577
+ 'features/**/api/*.ts'
578
+ ],
579
+ 'create-util': [
580
+ 'utils/**/*.ts',
581
+ 'lib/**/*.ts',
582
+ 'helpers/**/*.ts'
583
+ ],
584
+ 'create-test': [
585
+ '**/*.test.ts',
586
+ '**/*.test.tsx',
587
+ '**/*.spec.ts',
588
+ '__tests__/**/*.ts'
589
+ ],
590
+ 'modify-file': [] // No examples needed for modifications
591
+ };
592
+
593
+ const searchPatterns = patterns[stepType] || patterns['create-component'];
594
+ if (searchPatterns.length === 0) return null;
595
+
596
+ const examples = [];
597
+ const seen = new Set();
598
+
599
+ for (const pattern of searchPatterns) {
600
+ if (examples.length >= maxExamples) break;
601
+
602
+ const files = globSync(projectRoot, pattern);
603
+
604
+ // Sort by file size (prefer smaller, simpler examples)
605
+ const sorted = files
606
+ .map(f => ({ path: f, size: fs.statSync(f).size }))
607
+ .sort((a, b) => a.size - b.size)
608
+ .slice(0, 5); // Consider top 5 smallest
609
+
610
+ for (const { path: filePath } of sorted) {
611
+ if (examples.length >= maxExamples) break;
612
+ if (seen.has(filePath)) continue;
613
+
614
+ try {
615
+ const content = fs.readFileSync(filePath, 'utf8');
616
+ // Skip files that are too short (likely stubs) or too long
617
+ const lineCount = content.split('\n').length;
618
+ if (lineCount < 10 || lineCount > 500) continue;
619
+
620
+ seen.add(filePath);
621
+ const relativePath = path.relative(projectRoot, filePath);
622
+ const truncated = truncateForExample(content);
623
+
624
+ examples.push(`### Example: ${relativePath}\n\`\`\`typescript\n${truncated}\`\`\``);
625
+ } catch { /* ignore read errors */ }
626
+ }
627
+ }
628
+
629
+ if (examples.length === 0) return null;
630
+
631
+ return `## Similar Examples in This Project\n\nUse these as reference for style and patterns:\n\n${examples.join('\n\n')}`;
632
+ }
633
+
634
+ // ============================================================
635
+ // Verbosity Guidance
636
+ // ============================================================
637
+
638
+ /**
639
+ * Returns guidance text based on verbosity level
640
+ *
641
+ * NOTE: All levels now emphasize completeness over brevity.
642
+ * Local LLM tokens are free - include everything needed for success!
643
+ */
644
+ function getVerbosityGuidance(verbosity) {
645
+ const guidance = {
646
+ standard: `
647
+ Include enough context for success. Local LLM tokens are free!
648
+ - Clear description of the task
649
+ - All imports with exact paths
650
+ - Type definitions for all interfaces
651
+ - Mention patterns to follow`,
652
+
653
+ detailed: `
654
+ Be thorough. Include everything the local LLM needs to succeed first try.
655
+ - Show exact import paths and type signatures
656
+ - Include ALL props for components being used
657
+ - Show existing patterns to follow
658
+ - Provide examples of similar code
659
+ - List all edge cases and error handling requirements`,
660
+
661
+ comprehensive: `
662
+ Maximum detail. The local LLM should have complete knowledge to implement
663
+ this without guessing anything. Local LLM tokens are FREE - don't hold back!
664
+ - Include full file contents of related files
665
+ - Show complete type definitions with all fields
666
+ - Provide multiple usage examples
667
+ - Document all integration points
668
+ - Include testing requirements
669
+ - Show exact prop values (variant="primary" not variant={variants.primary})`
670
+ };
671
+
672
+ return guidance[verbosity] || guidance.detailed;
673
+ }
674
+
675
+ // ============================================================
676
+ // Exports
677
+ // ============================================================
678
+
679
+ // ============================================================
680
+ // Lightweight Type Hints Generator
681
+ // ============================================================
682
+
683
+ /**
684
+ * Generate lightweight type hints for common project types
685
+ * These are condensed summaries to help LLMs understand type shapes
686
+ * without loading entire type files.
687
+ *
688
+ * @param {string} projectRoot - Project root directory
689
+ * @param {Object} options - Options
690
+ * @returns {string|null} Formatted type hints
691
+ */
692
+ function generateTypeHints(projectRoot, options = {}) {
693
+ const { maxTypes = 10, taskDescription = '' } = options;
694
+ const typeHints = [];
695
+
696
+ // Common type file locations
697
+ const typeFiles = [
698
+ path.join(projectRoot, 'src', 'types', 'index.ts'),
699
+ path.join(projectRoot, 'src', 'types.ts'),
700
+ path.join(projectRoot, 'types', 'index.ts'),
701
+ path.join(projectRoot, 'src', 'lib', 'types.ts')
702
+ ];
703
+
704
+ for (const typePath of typeFiles) {
705
+ if (!fs.existsSync(typePath)) continue;
706
+
707
+ try {
708
+ const content = fs.readFileSync(typePath, 'utf-8');
709
+
710
+ // Extract interface/type definitions
711
+ const interfaceRegex = /(?:export\s+)?interface\s+(\w+)\s*(?:extends\s+[\w,\s]+)?\s*\{([^}]+)\}/g;
712
+ const typeRegex = /(?:export\s+)?type\s+(\w+)\s*=\s*([^;]+);/g;
713
+
714
+ let match;
715
+
716
+ // Extract interfaces with their key properties
717
+ while ((match = interfaceRegex.exec(content)) !== null && typeHints.length < maxTypes) {
718
+ const name = match[1];
719
+ const body = match[2];
720
+
721
+ // Extract key properties (first 5 lines or so)
722
+ const props = body
723
+ .split('\n')
724
+ .map(line => line.trim())
725
+ .filter(line => line && !line.startsWith('//'))
726
+ .slice(0, 5)
727
+ .map(line => ' ' + line);
728
+
729
+ if (props.length > 0) {
730
+ typeHints.push(`${name}: { ${props.join(' ').replace(/\s+/g, ' ').slice(0, 100)}${props.length > 5 ? ' ...' : ''} }`);
731
+ }
732
+ }
733
+
734
+ // Extract type aliases (simpler)
735
+ while ((match = typeRegex.exec(content)) !== null && typeHints.length < maxTypes) {
736
+ const name = match[1];
737
+ const value = match[2].trim().slice(0, 80);
738
+
739
+ // Only include simple types, not complex unions
740
+ if (!value.includes('\n') && value.length < 80) {
741
+ typeHints.push(`${name}: ${value}`);
742
+ }
743
+ }
744
+
745
+ } catch (err) {
746
+ if (process.env.DEBUG) {
747
+ console.warn(`Could not parse types from ${typePath}: ${err.message}`);
748
+ }
749
+ }
750
+ }
751
+
752
+ if (typeHints.length === 0) return null;
753
+
754
+ return `**Type Hints (condensed):**\n${typeHints.map(h => `- ${h}`).join('\n')}`;
755
+ }
756
+
757
+ module.exports = {
758
+ INSTRUCTION_RICHNESS,
759
+ COMPLEXITY_TO_RICHNESS,
760
+ getInstructionRichness,
761
+ getVerbosityGuidance,
762
+ // Context loaders
763
+ loadProjectContext,
764
+ loadPatterns,
765
+ loadRelevantTypes,
766
+ loadRelevantTypesWithLSP, // LSP-enhanced version
767
+ loadRelatedCode,
768
+ loadSimilarExamples,
769
+ // Type hints
770
+ generateTypeHints,
771
+ // LSP helpers
772
+ extractIdentifiersForLSP
773
+ };
774
+
775
+ // ============================================================
776
+ // CLI for testing
777
+ // ============================================================
778
+
779
+ if (require.main === module) {
780
+ const args = process.argv.slice(2);
781
+
782
+ if (args.length === 0) {
783
+ console.log(`
784
+ Usage: node flow-instruction-richness.js <complexity-level>
785
+
786
+ Complexity levels: small, medium, large, xl
787
+
788
+ Examples:
789
+ node flow-instruction-richness.js small
790
+ node flow-instruction-richness.js large
791
+ `);
792
+ process.exit(0);
793
+ }
794
+
795
+ const level = args[0];
796
+ const richness = getInstructionRichness(level);
797
+
798
+ console.log('\n═══════════════════════════════════════════════════════════');
799
+ console.log(' INSTRUCTION RICHNESS CONFIG (Local LLM is FREE!)');
800
+ console.log('═══════════════════════════════════════════════════════════\n');
801
+
802
+ console.log(`Complexity Level: ${level.toUpperCase()}`);
803
+ console.log(`Richness Level: ${richness.level.toUpperCase()}`);
804
+ console.log(`\n${richness.description}\n`);
805
+
806
+ console.log('───────────────────────────────────────────────────────────');
807
+ console.log(' CONTEXT TO INCLUDE');
808
+ console.log('───────────────────────────────────────────────────────────\n');
809
+
810
+ console.log(`Template Verbosity: ${richness.templateVerbosity}`);
811
+ console.log('');
812
+ console.log(`Include Project Context: ${richness.includeProjectContext ? '✅ Yes' : '❌ No'}`);
813
+ console.log(`Include Type Definitions: ${richness.includeTypeDefinitions ? '✅ Yes' : '❌ No'}`);
814
+ console.log(`Include Related Code: ${richness.includeRelatedCode ? '✅ Yes' : '❌ No'}`);
815
+ console.log(`Include Examples: ${richness.includeExamples ? '✅ Yes' : '❌ No'}`);
816
+ console.log(`Include Patterns: ${richness.includePatterns ? '✅ Yes' : '❌ No'}`);
817
+ console.log(`Include Full File Contents: ${richness.includeFullFileContents ? '✅ Yes' : '❌ No'}`);
818
+
819
+ console.log('\n───────────────────────────────────────────────────────────');
820
+ console.log(' GUIDANCE');
821
+ console.log('───────────────────────────────────────────────────────────');
822
+ console.log(getVerbosityGuidance(richness.templateVerbosity));
823
+
824
+ console.log('\n💡 Remember: Local LLM tokens are FREE! Include MORE context');
825
+ console.log(' when in doubt. Failed executions cost more than extra context.\n');
826
+ console.log('═══════════════════════════════════════════════════════════\n');
827
+ }