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,600 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Active Pattern Enforcement
5
+ *
6
+ * Ensures that learned patterns from decisions.md, app-map.md, and skills
7
+ * are actively injected into prompts and enforced during code generation.
8
+ *
9
+ * Key Features:
10
+ * - Extracts relevant patterns based on task context
11
+ * - Injects patterns prominently into prompts
12
+ * - Validates output against patterns
13
+ * - Requires citation of patterns in generated code
14
+ *
15
+ * Usage:
16
+ * const { injectPatterns, validateAgainstPatterns } = require('./flow-pattern-enforcer');
17
+ * const enrichedPrompt = injectPatterns(prompt, task, projectRoot);
18
+ *
19
+ * Part of v1.8.0 - Active Learning Enforcement
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const { getProjectRoot, getConfig } = require('./flow-utils');
25
+
26
+ // ============================================================
27
+ // Configuration
28
+ // ============================================================
29
+
30
+ const PROJECT_ROOT = getProjectRoot();
31
+ const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
32
+ const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
33
+
34
+ // ============================================================
35
+ // Pattern Extraction
36
+ // ============================================================
37
+
38
+ /**
39
+ * Load all patterns from decisions.md
40
+ */
41
+ function loadDecisionPatterns(projectRoot = PROJECT_ROOT) {
42
+ const decisionsPath = path.join(projectRoot, '.workflow', 'state', 'decisions.md');
43
+
44
+ if (!fs.existsSync(decisionsPath)) {
45
+ return [];
46
+ }
47
+
48
+ const content = fs.readFileSync(decisionsPath, 'utf-8');
49
+ const patterns = [];
50
+
51
+ // Extract each section as a pattern category
52
+ const sections = content.match(/## ([^\n]+)\n([\s\S]*?)(?=\n## |$)/g) || [];
53
+
54
+ for (const section of sections) {
55
+ const match = section.match(/## ([^\n]+)\n([\s\S]*)/);
56
+ if (match) {
57
+ const category = match[1].trim();
58
+ const rules = match[2].trim();
59
+
60
+ // Extract individual rules (lines starting with - or *)
61
+ const ruleLines = rules.match(/^[\s]*[-*]\s+.+$/gm) || [];
62
+
63
+ patterns.push({
64
+ category,
65
+ rules: ruleLines.map(r => r.replace(/^[\s]*[-*]\s+/, '').trim()),
66
+ raw: rules
67
+ });
68
+ }
69
+ }
70
+
71
+ return patterns;
72
+ }
73
+
74
+ /**
75
+ * Load components from app-map.md
76
+ */
77
+ function loadAppMapComponents(projectRoot = PROJECT_ROOT) {
78
+ const appMapPath = path.join(projectRoot, '.workflow', 'state', 'app-map.md');
79
+
80
+ if (!fs.existsSync(appMapPath)) {
81
+ return [];
82
+ }
83
+
84
+ const content = fs.readFileSync(appMapPath, 'utf-8');
85
+ const components = [];
86
+
87
+ // Extract table rows (| Component | Variants | ... |)
88
+ const tableRows = content.match(/^\|[^|]+\|[^|]+\|.+\|$/gm) || [];
89
+
90
+ for (const row of tableRows) {
91
+ if (row.includes('---') || row.toLowerCase().includes('component')) continue;
92
+
93
+ const cells = row.split('|').map(c => c.trim()).filter(Boolean);
94
+ if (cells.length >= 2) {
95
+ components.push({
96
+ name: cells[0],
97
+ variants: cells[1] ? cells[1].split(',').map(v => v.trim()) : [],
98
+ description: cells[2] || '',
99
+ path: cells[3] || ''
100
+ });
101
+ }
102
+ }
103
+
104
+ return components;
105
+ }
106
+
107
+ /**
108
+ * Load skill patterns for a given file type
109
+ */
110
+ function loadSkillPatterns(projectRoot, fileExtension, taskDescription = '') {
111
+ const skillsDir = path.join(projectRoot, '.claude', 'skills');
112
+ if (!fs.existsSync(skillsDir)) return null;
113
+
114
+ // Map file extensions to skills
115
+ const extensionToSkill = {
116
+ '.module.ts': 'nestjs',
117
+ '.controller.ts': 'nestjs',
118
+ '.service.ts': 'nestjs',
119
+ '.tsx': 'react',
120
+ '.jsx': 'react',
121
+ '.vue': 'vue',
122
+ '.py': 'python',
123
+ '.rs': 'rust',
124
+ '.go': 'go'
125
+ };
126
+
127
+ // Find matching skill
128
+ let skillName = null;
129
+ for (const [ext, skill] of Object.entries(extensionToSkill)) {
130
+ if (fileExtension.endsWith(ext)) {
131
+ skillName = skill;
132
+ break;
133
+ }
134
+ }
135
+
136
+ // Also check task description for framework mentions
137
+ if (!skillName && taskDescription) {
138
+ const frameworks = ['nestjs', 'react', 'vue', 'angular', 'express', 'fastapi', 'django'];
139
+ for (const fw of frameworks) {
140
+ if (taskDescription.toLowerCase().includes(fw)) {
141
+ skillName = fw;
142
+ break;
143
+ }
144
+ }
145
+ }
146
+
147
+ if (!skillName) return null;
148
+
149
+ const skillDir = path.join(skillsDir, skillName);
150
+ if (!fs.existsSync(skillDir)) return null;
151
+
152
+ const patterns = { skillName, patterns: null, antiPatterns: null };
153
+
154
+ // Load patterns
155
+ const patternsPath = path.join(skillDir, 'knowledge', 'patterns.md');
156
+ if (fs.existsSync(patternsPath)) {
157
+ patterns.patterns = fs.readFileSync(patternsPath, 'utf-8');
158
+ }
159
+
160
+ // Load anti-patterns
161
+ const antiPatternsPath = path.join(skillDir, 'knowledge', 'anti-patterns.md');
162
+ if (fs.existsSync(antiPatternsPath)) {
163
+ patterns.antiPatterns = fs.readFileSync(antiPatternsPath, 'utf-8');
164
+ }
165
+
166
+ return patterns;
167
+ }
168
+
169
+ /**
170
+ * Extract patterns relevant to a specific task
171
+ */
172
+ function extractRelevantPatterns(task, projectRoot = PROJECT_ROOT) {
173
+ const relevant = {
174
+ decisions: [],
175
+ components: [],
176
+ skill: null,
177
+ keywords: []
178
+ };
179
+
180
+ // Extract keywords from task
181
+ const taskText = `${task.description || ''} ${task.file || ''} ${task.action || ''}`.toLowerCase();
182
+ relevant.keywords = taskText
183
+ .replace(/[^a-z0-9\s]/g, ' ')
184
+ .split(/\s+/)
185
+ .filter(w => w.length > 2);
186
+
187
+ // Load all patterns
188
+ const decisionPatterns = loadDecisionPatterns(projectRoot);
189
+ const appMapComponents = loadAppMapComponents(projectRoot);
190
+
191
+ // Filter decision patterns by relevance
192
+ for (const pattern of decisionPatterns) {
193
+ const categoryLower = pattern.category.toLowerCase();
194
+ const rulesLower = pattern.rules.join(' ').toLowerCase();
195
+
196
+ // Check for keyword matches
197
+ const isRelevant = relevant.keywords.some(kw =>
198
+ categoryLower.includes(kw) || rulesLower.includes(kw)
199
+ );
200
+
201
+ // Always include certain categories
202
+ const alwaysInclude = ['naming', 'file', 'import', 'general', 'coding'];
203
+ const shouldAlwaysInclude = alwaysInclude.some(ai => categoryLower.includes(ai));
204
+
205
+ if (isRelevant || shouldAlwaysInclude) {
206
+ relevant.decisions.push(pattern);
207
+ }
208
+ }
209
+
210
+ // Filter components by relevance
211
+ for (const component of appMapComponents) {
212
+ const componentLower = `${component.name} ${component.variants.join(' ')} ${component.description}`.toLowerCase();
213
+
214
+ if (relevant.keywords.some(kw => componentLower.includes(kw))) {
215
+ relevant.components.push(component);
216
+ }
217
+ }
218
+
219
+ // Load skill patterns if applicable
220
+ if (task.file) {
221
+ relevant.skill = loadSkillPatterns(projectRoot, task.file, task.description);
222
+ }
223
+
224
+ return relevant;
225
+ }
226
+
227
+ // ============================================================
228
+ // Pattern Injection
229
+ // ============================================================
230
+
231
+ /**
232
+ * Format patterns for prompt injection
233
+ */
234
+ function formatPatternsForPrompt(relevantPatterns, config = {}) {
235
+ const { requireCitation = false } = config;
236
+ let output = '';
237
+
238
+ // Header
239
+ output += `\n## ⚠️ MANDATORY PATTERNS - MUST FOLLOW ⚠️\n\n`;
240
+ output += `The following patterns are REQUIRED. Violations will be rejected.\n\n`;
241
+
242
+ // Decision patterns
243
+ if (relevantPatterns.decisions.length > 0) {
244
+ output += `### Project Rules (from decisions.md)\n\n`;
245
+
246
+ for (const pattern of relevantPatterns.decisions) {
247
+ output += `**${pattern.category}**\n`;
248
+ for (const rule of pattern.rules.slice(0, 5)) { // Limit rules per category
249
+ output += `- ${rule}\n`;
250
+ }
251
+ output += '\n';
252
+ }
253
+ }
254
+
255
+ // Existing components
256
+ if (relevantPatterns.components.length > 0) {
257
+ output += `### Existing Components (from app-map.md) - REUSE THESE\n\n`;
258
+ output += `| Component | Variants | Path |\n`;
259
+ output += `|-----------|----------|------|\n`;
260
+
261
+ for (const comp of relevantPatterns.components.slice(0, 10)) {
262
+ output += `| ${comp.name} | ${comp.variants.join(', ')} | ${comp.path} |\n`;
263
+ }
264
+ output += '\n';
265
+ }
266
+
267
+ // Skill patterns
268
+ if (relevantPatterns.skill) {
269
+ output += `### ${relevantPatterns.skill.skillName} Patterns\n\n`;
270
+
271
+ if (relevantPatterns.skill.patterns) {
272
+ output += `**DO:**\n${relevantPatterns.skill.patterns.slice(0, 1000)}\n\n`;
273
+ }
274
+
275
+ if (relevantPatterns.skill.antiPatterns) {
276
+ output += `**DON'T:**\n${relevantPatterns.skill.antiPatterns.slice(0, 500)}\n\n`;
277
+ }
278
+ }
279
+
280
+ // Citation requirement
281
+ if (requireCitation) {
282
+ output += `### Citation Requirement\n\n`;
283
+ output += `You MUST include a comment citing which pattern you're following:\n`;
284
+ output += `\`\`\`typescript\n`;
285
+ output += `// Following: "Use kebab-case for files" (decisions.md)\n`;
286
+ output += `// Reusing: Button component (app-map.md)\n`;
287
+ output += `\`\`\`\n\n`;
288
+ }
289
+
290
+ return output;
291
+ }
292
+
293
+ /**
294
+ * Inject patterns into a prompt
295
+ */
296
+ function injectPatterns(prompt, task, projectRoot = PROJECT_ROOT) {
297
+ const config = getConfig();
298
+ const enforcement = config.enforcement || {};
299
+
300
+ const relevantPatterns = extractRelevantPatterns(task, projectRoot);
301
+
302
+ // Skip if no patterns found
303
+ if (relevantPatterns.decisions.length === 0 &&
304
+ relevantPatterns.components.length === 0 &&
305
+ !relevantPatterns.skill) {
306
+ return prompt;
307
+ }
308
+
309
+ const patternSection = formatPatternsForPrompt(relevantPatterns, {
310
+ requireCitation: enforcement.requirePatternCitation || false
311
+ });
312
+
313
+ // Inject patterns at the beginning of the prompt (high visibility)
314
+ return patternSection + '\n---\n\n' + prompt;
315
+ }
316
+
317
+ // ============================================================
318
+ // Pattern Validation
319
+ // ============================================================
320
+
321
+ /**
322
+ * Validation rules based on pattern categories
323
+ */
324
+ const VALIDATION_RULES = {
325
+ 'naming': [
326
+ { pattern: /PascalCase/i, check: (code) => /[A-Z][a-z]+[A-Z]/.test(code), inverse: false },
327
+ { pattern: /kebab-case/i, check: (code, files) => files?.every(f => /^[a-z0-9-]+\.[a-z]+$/.test(path.basename(f))), inverse: false },
328
+ { pattern: /camelCase/i, check: (code) => /[a-z]+[A-Z][a-z]+/.test(code), inverse: false }
329
+ ],
330
+ 'import': [
331
+ { pattern: /absolute.*@\//i, check: (code) => code.includes('@/'), inverse: false },
332
+ { pattern: /relative.*\.\.\//i, check: (code) => !code.includes('../'), inverse: true }
333
+ ]
334
+ };
335
+
336
+ /**
337
+ * Validate code against extracted patterns
338
+ */
339
+ function validateAgainstPatterns(code, patterns, files = []) {
340
+ const violations = [];
341
+ const passes = [];
342
+
343
+ for (const pattern of patterns.decisions) {
344
+ const categoryLower = pattern.category.toLowerCase();
345
+
346
+ for (const rule of pattern.rules) {
347
+ const ruleLower = rule.toLowerCase();
348
+
349
+ // Check naming conventions
350
+ if (categoryLower.includes('naming') || ruleLower.includes('naming')) {
351
+ if (ruleLower.includes('kebab-case') && files.length > 0) {
352
+ const nonKebab = files.filter(f => !/^[a-z0-9-]+\.[a-z]+$/.test(path.basename(f)));
353
+ if (nonKebab.length > 0) {
354
+ violations.push({
355
+ rule: rule,
356
+ category: pattern.category,
357
+ message: `Files not in kebab-case: ${nonKebab.join(', ')}`
358
+ });
359
+ } else {
360
+ passes.push({ rule: rule, category: pattern.category });
361
+ }
362
+ }
363
+ }
364
+
365
+ // Check import patterns
366
+ if (categoryLower.includes('import') || ruleLower.includes('import')) {
367
+ if (ruleLower.includes('absolute') && ruleLower.includes('@/')) {
368
+ if (!code.includes('@/') && code.includes('../')) {
369
+ violations.push({
370
+ rule: rule,
371
+ category: pattern.category,
372
+ message: 'Using relative imports instead of absolute @/ imports'
373
+ });
374
+ } else if (code.includes('@/')) {
375
+ passes.push({ rule: rule, category: pattern.category });
376
+ }
377
+ }
378
+ }
379
+
380
+ // Check forbidden patterns
381
+ if (ruleLower.includes('never') || ruleLower.includes('don\'t') || ruleLower.includes('avoid')) {
382
+ // Extract what to avoid
383
+ const avoidMatch = ruleLower.match(/(?:never|don't|avoid)\s+(?:use\s+)?(.+?)(?:\.|$)/);
384
+ if (avoidMatch) {
385
+ const forbidden = avoidMatch[1].trim();
386
+ if (code.toLowerCase().includes(forbidden)) {
387
+ violations.push({
388
+ rule: rule,
389
+ category: pattern.category,
390
+ message: `Code contains forbidden pattern: "${forbidden}"`
391
+ });
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+
398
+ // Check component reuse
399
+ if (patterns.components.length > 0) {
400
+ const createdNew = code.match(/(?:function|const|class)\s+([A-Z][a-zA-Z]+)/g) || [];
401
+
402
+ for (const created of createdNew) {
403
+ const name = created.replace(/(?:function|const|class)\s+/, '');
404
+ const existing = patterns.components.find(c =>
405
+ c.name.toLowerCase() === name.toLowerCase()
406
+ );
407
+
408
+ if (existing) {
409
+ violations.push({
410
+ rule: `Reuse existing component: ${existing.name}`,
411
+ category: 'Component Reuse',
412
+ message: `Created new "${name}" but "${existing.name}" already exists at ${existing.path}`
413
+ });
414
+ }
415
+ }
416
+ }
417
+
418
+ return {
419
+ valid: violations.length === 0,
420
+ violations,
421
+ passes,
422
+ summary: violations.length === 0
423
+ ? `✓ All ${passes.length} pattern checks passed`
424
+ : `✗ ${violations.length} violations, ${passes.length} passes`
425
+ };
426
+ }
427
+
428
+ /**
429
+ * Check if code includes required citations
430
+ */
431
+ function validateCitations(code, patterns) {
432
+ const citations = code.match(/\/\/\s*(?:Following|Reusing|Pattern):\s*.+/gi) || [];
433
+
434
+ return {
435
+ hasCitations: citations.length > 0,
436
+ citations: citations,
437
+ message: citations.length > 0
438
+ ? `Found ${citations.length} pattern citations`
439
+ : 'No pattern citations found (required when enforcement.requirePatternCitation is true)'
440
+ };
441
+ }
442
+
443
+ // ============================================================
444
+ // Session Context Loading
445
+ // ============================================================
446
+
447
+ /**
448
+ * Generate session start summary showing loaded patterns
449
+ */
450
+ function generateSessionSummary(projectRoot = PROJECT_ROOT) {
451
+ const decisions = loadDecisionPatterns(projectRoot);
452
+ const components = loadAppMapComponents(projectRoot);
453
+ const config = getConfig();
454
+
455
+ let summary = '\n';
456
+ summary += '┌─────────────────────────────────────────────────────────────┐\n';
457
+ summary += '│ 📋 PROJECT CONTEXT LOADED │\n';
458
+ summary += '├─────────────────────────────────────────────────────────────┤\n';
459
+
460
+ // Decisions summary
461
+ const ruleCount = decisions.reduce((acc, d) => acc + d.rules.length, 0);
462
+ summary += `│ decisions.md: ${ruleCount} rules in ${decisions.length} categories\n`;
463
+
464
+ for (const d of decisions.slice(0, 3)) {
465
+ summary += `│ • ${d.category}: ${d.rules.length} rules\n`;
466
+ }
467
+ if (decisions.length > 3) {
468
+ summary += `│ • ... and ${decisions.length - 3} more categories\n`;
469
+ }
470
+
471
+ // Components summary
472
+ summary += `│\n│ app-map.md: ${components.length} components registered\n`;
473
+
474
+ for (const c of components.slice(0, 3)) {
475
+ summary += `│ • ${c.name} (${c.variants.length} variants)\n`;
476
+ }
477
+ if (components.length > 3) {
478
+ summary += `│ • ... and ${components.length - 3} more components\n`;
479
+ }
480
+
481
+ // Skills summary
482
+ const skillsDir = path.join(projectRoot, '.claude', 'skills');
483
+ if (fs.existsSync(skillsDir)) {
484
+ const skills = fs.readdirSync(skillsDir).filter(d =>
485
+ fs.statSync(path.join(skillsDir, d)).isDirectory() && !d.startsWith('_')
486
+ );
487
+ if (skills.length > 0) {
488
+ summary += `│\n│ .claude/skills/: ${skills.join(', ')}\n`;
489
+ }
490
+ }
491
+
492
+ summary += '│\n│ ⚠️ THESE RULES ARE MANDATORY FOR ALL WORK │\n';
493
+ summary += '└─────────────────────────────────────────────────────────────┘\n';
494
+
495
+ return summary;
496
+ }
497
+
498
+ // ============================================================
499
+ // Exports
500
+ // ============================================================
501
+
502
+ module.exports = {
503
+ // Pattern loading
504
+ loadDecisionPatterns,
505
+ loadAppMapComponents,
506
+ loadSkillPatterns,
507
+ extractRelevantPatterns,
508
+
509
+ // Pattern injection
510
+ formatPatternsForPrompt,
511
+ injectPatterns,
512
+
513
+ // Validation
514
+ validateAgainstPatterns,
515
+ validateCitations,
516
+
517
+ // Session helpers
518
+ generateSessionSummary
519
+ };
520
+
521
+ // ============================================================
522
+ // CLI
523
+ // ============================================================
524
+
525
+ if (require.main === module) {
526
+ const args = process.argv.slice(2);
527
+ const command = args[0];
528
+
529
+ switch (command) {
530
+ case 'summary': {
531
+ console.log(generateSessionSummary());
532
+ break;
533
+ }
534
+
535
+ case 'patterns': {
536
+ const patterns = loadDecisionPatterns();
537
+ console.log('\nDecision Patterns:\n');
538
+ for (const p of patterns) {
539
+ console.log(`## ${p.category}`);
540
+ for (const r of p.rules) {
541
+ console.log(` - ${r}`);
542
+ }
543
+ console.log('');
544
+ }
545
+ break;
546
+ }
547
+
548
+ case 'components': {
549
+ const components = loadAppMapComponents();
550
+ console.log('\nRegistered Components:\n');
551
+ for (const c of components) {
552
+ console.log(` ${c.name}: ${c.variants.join(', ') || 'no variants'}`);
553
+ }
554
+ break;
555
+ }
556
+
557
+ case 'validate': {
558
+ const filePath = args[1];
559
+ if (!filePath) {
560
+ console.error('Usage: flow-pattern-enforcer validate <file>');
561
+ process.exit(1);
562
+ }
563
+
564
+ const code = fs.readFileSync(filePath, 'utf-8');
565
+ const patterns = extractRelevantPatterns({ file: filePath, description: '' });
566
+ const result = validateAgainstPatterns(code, patterns, [filePath]);
567
+
568
+ console.log('\nValidation Result:\n');
569
+ console.log(result.summary);
570
+
571
+ if (result.violations.length > 0) {
572
+ console.log('\nViolations:');
573
+ for (const v of result.violations) {
574
+ console.log(` ✗ [${v.category}] ${v.rule}`);
575
+ console.log(` ${v.message}`);
576
+ }
577
+ }
578
+ break;
579
+ }
580
+
581
+ default: {
582
+ console.log(`
583
+ Wogi Flow - Pattern Enforcer
584
+
585
+ Usage:
586
+ node flow-pattern-enforcer.js <command>
587
+
588
+ Commands:
589
+ summary Show session context summary
590
+ patterns List all decision patterns
591
+ components List registered components
592
+ validate <file> Validate a file against patterns
593
+
594
+ Examples:
595
+ node flow-pattern-enforcer.js summary
596
+ node flow-pattern-enforcer.js validate src/components/Button.tsx
597
+ `);
598
+ }
599
+ }
600
+ }