@brad-frost-web/eddie-brain 0.32.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 (239) hide show
  1. package/README.md +109 -0
  2. package/dist/analyze/drift-detector.d.ts +30 -0
  3. package/dist/analyze/drift-detector.d.ts.map +1 -0
  4. package/dist/analyze/drift-detector.js +310 -0
  5. package/dist/analyze/drift-detector.js.map +1 -0
  6. package/dist/analyze/health-scorer.d.ts +71 -0
  7. package/dist/analyze/health-scorer.d.ts.map +1 -0
  8. package/dist/analyze/health-scorer.js +420 -0
  9. package/dist/analyze/health-scorer.js.map +1 -0
  10. package/dist/analyze/index.d.ts +11 -0
  11. package/dist/analyze/index.d.ts.map +1 -0
  12. package/dist/analyze/index.js +11 -0
  13. package/dist/analyze/index.js.map +1 -0
  14. package/dist/analyze/naming-validator.d.ts +99 -0
  15. package/dist/analyze/naming-validator.d.ts.map +1 -0
  16. package/dist/analyze/naming-validator.js +430 -0
  17. package/dist/analyze/naming-validator.js.map +1 -0
  18. package/dist/analyze/slot-contract-validator.d.ts +68 -0
  19. package/dist/analyze/slot-contract-validator.d.ts.map +1 -0
  20. package/dist/analyze/slot-contract-validator.js +232 -0
  21. package/dist/analyze/slot-contract-validator.js.map +1 -0
  22. package/dist/analyze/token-validator.d.ts +62 -0
  23. package/dist/analyze/token-validator.d.ts.map +1 -0
  24. package/dist/analyze/token-validator.js +348 -0
  25. package/dist/analyze/token-validator.js.map +1 -0
  26. package/dist/cli/brain.d.ts +12 -0
  27. package/dist/cli/brain.d.ts.map +1 -0
  28. package/dist/cli/brain.js +641 -0
  29. package/dist/cli/brain.js.map +1 -0
  30. package/dist/cli/formatters/json.d.ts +15 -0
  31. package/dist/cli/formatters/json.d.ts.map +1 -0
  32. package/dist/cli/formatters/json.js +18 -0
  33. package/dist/cli/formatters/json.js.map +1 -0
  34. package/dist/cli/formatters/terminal.d.ts +19 -0
  35. package/dist/cli/formatters/terminal.d.ts.map +1 -0
  36. package/dist/cli/formatters/terminal.js +125 -0
  37. package/dist/cli/formatters/terminal.js.map +1 -0
  38. package/dist/cli/index.d.ts +7 -0
  39. package/dist/cli/index.d.ts.map +1 -0
  40. package/dist/cli/index.js +7 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/data/governance-rules.json +94 -0
  43. package/dist/governance/audit-log.d.ts +17 -0
  44. package/dist/governance/audit-log.d.ts.map +1 -0
  45. package/dist/governance/audit-log.js +44 -0
  46. package/dist/governance/audit-log.js.map +1 -0
  47. package/dist/governance/index.d.ts +8 -0
  48. package/dist/governance/index.d.ts.map +1 -0
  49. package/dist/governance/index.js +8 -0
  50. package/dist/governance/index.js.map +1 -0
  51. package/dist/governance/permissions.d.ts +26 -0
  52. package/dist/governance/permissions.d.ts.map +1 -0
  53. package/dist/governance/permissions.js +75 -0
  54. package/dist/governance/permissions.js.map +1 -0
  55. package/dist/governance/rules-engine.d.ts +24 -0
  56. package/dist/governance/rules-engine.d.ts.map +1 -0
  57. package/dist/governance/rules-engine.js +111 -0
  58. package/dist/governance/rules-engine.js.map +1 -0
  59. package/dist/governance/trust-manager.d.ts +34 -0
  60. package/dist/governance/trust-manager.d.ts.map +1 -0
  61. package/dist/governance/trust-manager.js +148 -0
  62. package/dist/governance/trust-manager.js.map +1 -0
  63. package/dist/index.d.ts +23 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +28 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/knowledge-graph/component-index.d.ts +320 -0
  68. package/dist/knowledge-graph/component-index.d.ts.map +1 -0
  69. package/dist/knowledge-graph/component-index.js +1033 -0
  70. package/dist/knowledge-graph/component-index.js.map +1 -0
  71. package/dist/knowledge-graph/index.d.ts +134 -0
  72. package/dist/knowledge-graph/index.d.ts.map +1 -0
  73. package/dist/knowledge-graph/index.js +249 -0
  74. package/dist/knowledge-graph/index.js.map +1 -0
  75. package/dist/knowledge-graph/learning-history.d.ts +77 -0
  76. package/dist/knowledge-graph/learning-history.d.ts.map +1 -0
  77. package/dist/knowledge-graph/learning-history.js +187 -0
  78. package/dist/knowledge-graph/learning-history.js.map +1 -0
  79. package/dist/knowledge-graph/relationship-map.d.ts +55 -0
  80. package/dist/knowledge-graph/relationship-map.d.ts.map +1 -0
  81. package/dist/knowledge-graph/relationship-map.js +238 -0
  82. package/dist/knowledge-graph/relationship-map.js.map +1 -0
  83. package/dist/knowledge-graph/token-taxonomy.d.ts +127 -0
  84. package/dist/knowledge-graph/token-taxonomy.d.ts.map +1 -0
  85. package/dist/knowledge-graph/token-taxonomy.js +357 -0
  86. package/dist/knowledge-graph/token-taxonomy.js.map +1 -0
  87. package/dist/loop/fix-agent.d.ts +55 -0
  88. package/dist/loop/fix-agent.d.ts.map +1 -0
  89. package/dist/loop/fix-agent.js +344 -0
  90. package/dist/loop/fix-agent.js.map +1 -0
  91. package/dist/loop/index.d.ts +8 -0
  92. package/dist/loop/index.d.ts.map +1 -0
  93. package/dist/loop/index.js +8 -0
  94. package/dist/loop/index.js.map +1 -0
  95. package/dist/loop/issue-fetcher.d.ts +51 -0
  96. package/dist/loop/issue-fetcher.d.ts.map +1 -0
  97. package/dist/loop/issue-fetcher.js +188 -0
  98. package/dist/loop/issue-fetcher.js.map +1 -0
  99. package/dist/loop/observer.d.ts +42 -0
  100. package/dist/loop/observer.d.ts.map +1 -0
  101. package/dist/loop/observer.js +220 -0
  102. package/dist/loop/observer.js.map +1 -0
  103. package/dist/loop/pacer.d.ts +44 -0
  104. package/dist/loop/pacer.d.ts.map +1 -0
  105. package/dist/loop/pacer.js +90 -0
  106. package/dist/loop/pacer.js.map +1 -0
  107. package/dist/loop/reporter.d.ts +9 -0
  108. package/dist/loop/reporter.d.ts.map +1 -0
  109. package/dist/loop/reporter.js +119 -0
  110. package/dist/loop/reporter.js.map +1 -0
  111. package/dist/loop/runner.d.ts +57 -0
  112. package/dist/loop/runner.d.ts.map +1 -0
  113. package/dist/loop/runner.js +390 -0
  114. package/dist/loop/runner.js.map +1 -0
  115. package/dist/loop/types.d.ts +151 -0
  116. package/dist/loop/types.d.ts.map +1 -0
  117. package/dist/loop/types.js +22 -0
  118. package/dist/loop/types.js.map +1 -0
  119. package/dist/mcp/index.d.ts +7 -0
  120. package/dist/mcp/index.d.ts.map +1 -0
  121. package/dist/mcp/index.js +7 -0
  122. package/dist/mcp/index.js.map +1 -0
  123. package/dist/mcp/server.d.ts +12 -0
  124. package/dist/mcp/server.d.ts.map +1 -0
  125. package/dist/mcp/server.js +618 -0
  126. package/dist/mcp/server.js.map +1 -0
  127. package/dist/pipeline/agent-runner.d.ts +34 -0
  128. package/dist/pipeline/agent-runner.d.ts.map +1 -0
  129. package/dist/pipeline/agent-runner.js +323 -0
  130. package/dist/pipeline/agent-runner.js.map +1 -0
  131. package/dist/pipeline/agents/accessibility-auditor.d.ts +10 -0
  132. package/dist/pipeline/agents/accessibility-auditor.d.ts.map +1 -0
  133. package/dist/pipeline/agents/accessibility-auditor.js +69 -0
  134. package/dist/pipeline/agents/accessibility-auditor.js.map +1 -0
  135. package/dist/pipeline/agents/code-reviewer.d.ts +10 -0
  136. package/dist/pipeline/agents/code-reviewer.d.ts.map +1 -0
  137. package/dist/pipeline/agents/code-reviewer.js +75 -0
  138. package/dist/pipeline/agents/code-reviewer.js.map +1 -0
  139. package/dist/pipeline/agents/code-writer.d.ts +10 -0
  140. package/dist/pipeline/agents/code-writer.d.ts.map +1 -0
  141. package/dist/pipeline/agents/code-writer.js +103 -0
  142. package/dist/pipeline/agents/code-writer.js.map +1 -0
  143. package/dist/pipeline/agents/component-architect.d.ts +13 -0
  144. package/dist/pipeline/agents/component-architect.d.ts.map +1 -0
  145. package/dist/pipeline/agents/component-architect.js +81 -0
  146. package/dist/pipeline/agents/component-architect.js.map +1 -0
  147. package/dist/pipeline/agents/index.d.ts +16 -0
  148. package/dist/pipeline/agents/index.d.ts.map +1 -0
  149. package/dist/pipeline/agents/index.js +24 -0
  150. package/dist/pipeline/agents/index.js.map +1 -0
  151. package/dist/pipeline/agents/library-researcher.d.ts +12 -0
  152. package/dist/pipeline/agents/library-researcher.d.ts.map +1 -0
  153. package/dist/pipeline/agents/library-researcher.js +85 -0
  154. package/dist/pipeline/agents/library-researcher.js.map +1 -0
  155. package/dist/pipeline/agents/quality-gate.d.ts +9 -0
  156. package/dist/pipeline/agents/quality-gate.d.ts.map +1 -0
  157. package/dist/pipeline/agents/quality-gate.js +71 -0
  158. package/dist/pipeline/agents/quality-gate.js.map +1 -0
  159. package/dist/pipeline/agents/spec-analyst.d.ts +10 -0
  160. package/dist/pipeline/agents/spec-analyst.d.ts.map +1 -0
  161. package/dist/pipeline/agents/spec-analyst.js +72 -0
  162. package/dist/pipeline/agents/spec-analyst.js.map +1 -0
  163. package/dist/pipeline/agents/story-author.d.ts +9 -0
  164. package/dist/pipeline/agents/story-author.d.ts.map +1 -0
  165. package/dist/pipeline/agents/story-author.js +65 -0
  166. package/dist/pipeline/agents/story-author.js.map +1 -0
  167. package/dist/pipeline/artifact-store.d.ts +27 -0
  168. package/dist/pipeline/artifact-store.d.ts.map +1 -0
  169. package/dist/pipeline/artifact-store.js +77 -0
  170. package/dist/pipeline/artifact-store.js.map +1 -0
  171. package/dist/pipeline/conversational-gate.d.ts +26 -0
  172. package/dist/pipeline/conversational-gate.d.ts.map +1 -0
  173. package/dist/pipeline/conversational-gate.js +122 -0
  174. package/dist/pipeline/conversational-gate.js.map +1 -0
  175. package/dist/pipeline/index.d.ts +14 -0
  176. package/dist/pipeline/index.d.ts.map +1 -0
  177. package/dist/pipeline/index.js +17 -0
  178. package/dist/pipeline/index.js.map +1 -0
  179. package/dist/pipeline/iteration-tracker.d.ts +29 -0
  180. package/dist/pipeline/iteration-tracker.d.ts.map +1 -0
  181. package/dist/pipeline/iteration-tracker.js +102 -0
  182. package/dist/pipeline/iteration-tracker.js.map +1 -0
  183. package/dist/pipeline/learning-bridge.d.ts +37 -0
  184. package/dist/pipeline/learning-bridge.d.ts.map +1 -0
  185. package/dist/pipeline/learning-bridge.js +118 -0
  186. package/dist/pipeline/learning-bridge.js.map +1 -0
  187. package/dist/pipeline/orchestrator.d.ts +45 -0
  188. package/dist/pipeline/orchestrator.d.ts.map +1 -0
  189. package/dist/pipeline/orchestrator.js +473 -0
  190. package/dist/pipeline/orchestrator.js.map +1 -0
  191. package/dist/pipeline/templates/architecture.d.ts +27 -0
  192. package/dist/pipeline/templates/architecture.d.ts.map +1 -0
  193. package/dist/pipeline/templates/architecture.js +111 -0
  194. package/dist/pipeline/templates/architecture.js.map +1 -0
  195. package/dist/pipeline/templates/brief.d.ts +22 -0
  196. package/dist/pipeline/templates/brief.d.ts.map +1 -0
  197. package/dist/pipeline/templates/brief.js +121 -0
  198. package/dist/pipeline/templates/brief.js.map +1 -0
  199. package/dist/pipeline/templates/component-rules.d.ts +25 -0
  200. package/dist/pipeline/templates/component-rules.d.ts.map +1 -0
  201. package/dist/pipeline/templates/component-rules.js +93 -0
  202. package/dist/pipeline/templates/component-rules.js.map +1 -0
  203. package/dist/pipeline/templates/index.d.ts +9 -0
  204. package/dist/pipeline/templates/index.d.ts.map +1 -0
  205. package/dist/pipeline/templates/index.js +7 -0
  206. package/dist/pipeline/templates/index.js.map +1 -0
  207. package/dist/pipeline/tool-handler.d.ts +25 -0
  208. package/dist/pipeline/tool-handler.d.ts.map +1 -0
  209. package/dist/pipeline/tool-handler.js +392 -0
  210. package/dist/pipeline/tool-handler.js.map +1 -0
  211. package/dist/pipeline/types.d.ts +146 -0
  212. package/dist/pipeline/types.d.ts.map +1 -0
  213. package/dist/pipeline/types.js +27 -0
  214. package/dist/pipeline/types.js.map +1 -0
  215. package/dist/plan/action-types.d.ts +31 -0
  216. package/dist/plan/action-types.d.ts.map +1 -0
  217. package/dist/plan/action-types.js +83 -0
  218. package/dist/plan/action-types.js.map +1 -0
  219. package/dist/plan/decision-engine.d.ts +57 -0
  220. package/dist/plan/decision-engine.d.ts.map +1 -0
  221. package/dist/plan/decision-engine.js +162 -0
  222. package/dist/plan/decision-engine.js.map +1 -0
  223. package/dist/plan/index.d.ts +6 -0
  224. package/dist/plan/index.d.ts.map +1 -0
  225. package/dist/plan/index.js +6 -0
  226. package/dist/plan/index.js.map +1 -0
  227. package/dist/types.d.ts +351 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +26 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/utils/anthropic.d.ts +15 -0
  232. package/dist/utils/anthropic.d.ts.map +1 -0
  233. package/dist/utils/anthropic.js +40 -0
  234. package/dist/utils/anthropic.js.map +1 -0
  235. package/dist/utils/id.d.ts +8 -0
  236. package/dist/utils/id.d.ts.map +1 -0
  237. package/dist/utils/id.js +14 -0
  238. package/dist/utils/id.js.map +1 -0
  239. package/package.json +80 -0
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Naming Validator
3
+ *
4
+ * Validates BEM/BEMIT naming conventions in Eddie components:
5
+ * - CSS class prefixes (ed-c-, ed-l-, ed-u-, ed-r-, ed-p-, etc.)
6
+ * - BEM structure (block__element--modifier)
7
+ * - Detects unclassed HTML elements in Lit component templates
8
+ *
9
+ * Understands Lit-specific patterns:
10
+ * - Only scans inside html`...` tagged template literals
11
+ * - Recognizes dynamic class bindings (class="${expr}")
12
+ * - Skips JSDoc/comment blocks
13
+ * - Skips .stories.ts files for unclassed element checks (slot content is fine)
14
+ */
15
+ import { readFile } from 'fs/promises';
16
+ import glob from 'fast-glob';
17
+ export class NamingValidator {
18
+ /**
19
+ * Class prefix patterns
20
+ */
21
+ validPrefixes = [
22
+ /^ed-c-/, // Component
23
+ /^ed-l-/, // Layout
24
+ /^ed-u-/, // Utility
25
+ /^is-/, // State
26
+ /^has-/, // State (has)
27
+ /^ed-r-/, // Recipe
28
+ /^ed-p-/, // Page
29
+ ];
30
+ /**
31
+ * HTML elements that can exist without a class
32
+ * - SVG internals, slots, templates, scripts, style tags
33
+ * - Custom elements (contain a hyphen) always skip
34
+ */
35
+ unclassedAllowed = new Set([
36
+ 'svg', 'path', 'g', 'circle', 'rect', 'ellipse', 'line',
37
+ 'polyline', 'polygon', 'text', 'tspan', 'image', 'use',
38
+ 'defs', 'symbol', 'marker', 'linearGradient', 'radialGradient',
39
+ 'stop', 'clipPath', 'mask', 'pattern',
40
+ 'style', 'script', 'template', 'slot',
41
+ // Lit-specific
42
+ 'br', 'hr', 'img', 'input', 'meta', 'link', 'source', 'track', 'wbr',
43
+ ]);
44
+ /**
45
+ * Validate a single component file
46
+ */
47
+ async validateFile(filePath) {
48
+ try {
49
+ const content = await readFile(filePath, 'utf-8');
50
+ const issues = [];
51
+ const isTemplate = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
52
+ const isStyle = filePath.endsWith('.scss');
53
+ const isStories = filePath.includes('.stories.');
54
+ if (isTemplate) {
55
+ issues.push(...this.validateTemplate(filePath, content, isStories));
56
+ }
57
+ if (isStyle) {
58
+ issues.push(...this.validateStyleFile(filePath, content));
59
+ }
60
+ return issues;
61
+ }
62
+ catch (error) {
63
+ return [{
64
+ id: `naming-validate-error-${Date.now()}`,
65
+ category: 'naming',
66
+ severity: 'error',
67
+ message: `Failed to validate file: ${error instanceof Error ? error.message : String(error)}`,
68
+ file: filePath,
69
+ autoFixable: false,
70
+ }];
71
+ }
72
+ }
73
+ /**
74
+ * Validate all component files in a directory
75
+ */
76
+ async validateDirectory(dirPath) {
77
+ const componentFiles = await glob(['**/*.ts', '**/*.tsx', '**/*.scss'], {
78
+ cwd: dirPath,
79
+ absolute: true,
80
+ ignore: ['**/node_modules/**', '**/*.d.ts'],
81
+ });
82
+ const allIssues = [];
83
+ for (const file of componentFiles) {
84
+ const issues = await this.validateFile(file);
85
+ allIssues.push(...issues);
86
+ }
87
+ return allIssues;
88
+ }
89
+ /**
90
+ * Validate a single CSS class name string
91
+ */
92
+ validateClassName(className) {
93
+ const result = this.classNameValidation(className);
94
+ if (!result.valid) {
95
+ return {
96
+ id: `naming-class-${className}`,
97
+ category: 'naming',
98
+ severity: 'error',
99
+ message: `Invalid class name "${className}": ${result.issues.join('; ')}`,
100
+ actual: className,
101
+ expected: this.suggestClassName(className),
102
+ autoFixable: false,
103
+ file: 'unknown',
104
+ };
105
+ }
106
+ return null;
107
+ }
108
+ // ===========================================================================
109
+ // Template validation — Lit-aware
110
+ // ===========================================================================
111
+ /**
112
+ * Validate HTML elements inside Lit templates.
113
+ *
114
+ * Key behaviors:
115
+ * - Extracts only the content inside html`...` tagged template literals
116
+ * - Strips JSDoc/block comments before scanning
117
+ * - Recognizes dynamic class bindings: class="${...}" counts as having a class
118
+ * - Skips unclassed-element checks in .stories.ts (slot content is fine)
119
+ * - Skips self-closing void elements and custom elements (contain hyphen)
120
+ */
121
+ validateTemplate(filePath, content, isStories) {
122
+ const issues = [];
123
+ // Strip all block comments (/** ... */, /* ... */) to avoid scanning JSDoc
124
+ const stripped = this.stripBlockComments(content);
125
+ // Extract html`...` template regions with their starting line offsets
126
+ const templateRegions = this.extractLitTemplates(stripped);
127
+ if (templateRegions.length === 0) {
128
+ // No Lit templates found — nothing to validate for unclassed elements
129
+ // Still check any static class="" attributes in the whole file
130
+ return issues;
131
+ }
132
+ for (const region of templateRegions) {
133
+ // Check for unclassed elements (skip in stories files)
134
+ if (!isStories) {
135
+ issues.push(...this.checkUnclassedElements(filePath, region.content, region.startLine));
136
+ }
137
+ // Check static class names in templates
138
+ issues.push(...this.checkStaticClassNames(filePath, region.content, region.startLine));
139
+ }
140
+ return issues;
141
+ }
142
+ /**
143
+ * Strip block comments (/* ... * /) including JSDoc (/** ... * /)
144
+ * Preserves line count by replacing comment content with blank lines.
145
+ */
146
+ stripBlockComments(content) {
147
+ // Replace block comments with the same number of newlines to preserve line numbering
148
+ return content.replace(/\/\*[\s\S]*?\*\//g, (match) => {
149
+ const newlineCount = (match.match(/\n/g) || []).length;
150
+ return '\n'.repeat(newlineCount);
151
+ });
152
+ }
153
+ /**
154
+ * Extract content from html`...` tagged template literals.
155
+ * Returns regions with their content and the line offset where they start.
156
+ */
157
+ extractLitTemplates(content) {
158
+ const regions = [];
159
+ // Match html`...` with support for nested ${} expressions
160
+ // We use a state machine approach since regex can't handle nested backticks well
161
+ const lines = content.split('\n');
162
+ let inTemplate = false;
163
+ let depth = 0;
164
+ let templateLines = [];
165
+ let templateStartLine = 0;
166
+ for (let i = 0; i < lines.length; i++) {
167
+ const line = lines[i];
168
+ // Skip single-line comments
169
+ const trimmed = line.trim();
170
+ if (trimmed.startsWith('//'))
171
+ continue;
172
+ if (!inTemplate) {
173
+ // Look for html` start
174
+ const htmlStart = line.indexOf('html`');
175
+ if (htmlStart !== -1) {
176
+ inTemplate = true;
177
+ depth = 1;
178
+ templateStartLine = i;
179
+ // Get the part after html`
180
+ const afterStart = line.substring(htmlStart + 5);
181
+ // Count backticks to track depth (simplified — handles most cases)
182
+ templateLines = [afterStart];
183
+ // Check if template closes on same line
184
+ if (this.templateClosesOnLine(afterStart)) {
185
+ inTemplate = false;
186
+ regions.push({
187
+ content: templateLines.join('\n'),
188
+ startLine: templateStartLine,
189
+ });
190
+ templateLines = [];
191
+ }
192
+ }
193
+ }
194
+ else {
195
+ templateLines.push(line);
196
+ // Check if template closes on this line
197
+ // Look for a backtick that's not inside a ${} expression
198
+ if (this.templateClosesOnLine(line)) {
199
+ inTemplate = false;
200
+ regions.push({
201
+ content: templateLines.join('\n'),
202
+ startLine: templateStartLine,
203
+ });
204
+ templateLines = [];
205
+ }
206
+ }
207
+ }
208
+ return regions;
209
+ }
210
+ /**
211
+ * Simplified check: does a template close on this line?
212
+ * Looks for a ` that's not preceded by ${ context.
213
+ */
214
+ templateClosesOnLine(line) {
215
+ // Count unescaped backticks not inside ${}
216
+ let inExpr = 0;
217
+ for (let i = 0; i < line.length; i++) {
218
+ if (line[i] === '$' && line[i + 1] === '{') {
219
+ inExpr++;
220
+ i++; // skip {
221
+ }
222
+ else if (line[i] === '}' && inExpr > 0) {
223
+ inExpr--;
224
+ }
225
+ else if (line[i] === '`' && inExpr === 0) {
226
+ return true;
227
+ }
228
+ }
229
+ return false;
230
+ }
231
+ /**
232
+ * Check for HTML elements without classes in template content.
233
+ *
234
+ * Understands:
235
+ * - class="${expr}" — dynamic class, counts as having a class
236
+ * - class="literal" — static class
237
+ * - styleModifier="..." — Eddie's pattern for passing classes to children
238
+ * - Tags with a hyphen are custom elements (always skip)
239
+ */
240
+ checkUnclassedElements(filePath, templateContent, startLine) {
241
+ const issues = [];
242
+ const lines = templateContent.split('\n');
243
+ // Skip TextPassage (prose content is allowed unclassed) and test files
244
+ if (filePath.includes('text-passage') ||
245
+ filePath.includes('TextPassage') ||
246
+ filePath.includes('.test.') ||
247
+ filePath.includes('.spec.')) {
248
+ return issues;
249
+ }
250
+ for (let i = 0; i < lines.length; i++) {
251
+ const line = lines[i];
252
+ // Find opening HTML tags — include hyphens in tag name to capture custom elements
253
+ const tagMatches = line.matchAll(/<([a-zA-Z][a-zA-Z0-9-]*)\b([^>]*?)>/g);
254
+ for (const match of tagMatches) {
255
+ const tagName = match[1].toLowerCase();
256
+ const attributes = match[2];
257
+ // Skip: custom elements (have hyphen), void elements, allowed elements
258
+ if (tagName.includes('-'))
259
+ continue;
260
+ if (this.unclassedAllowed.has(tagName))
261
+ continue;
262
+ // Check if element has a class attribute (static or dynamic)
263
+ const hasClass = /class\s*=/.test(attributes) || // class="..." or class="${...}"
264
+ /styleModifier\s*=/.test(attributes) || // Eddie's styleModifier pattern
265
+ /\$\{.*class/.test(attributes); // ${classMap(...)} etc.
266
+ if (!hasClass) {
267
+ issues.push({
268
+ id: `naming-unclassed-${tagName}-${startLine + i}`,
269
+ category: 'naming',
270
+ severity: 'warning',
271
+ message: `Unclassed <${tagName}> element in Lit template. Consider adding a BEM class.`,
272
+ file: filePath,
273
+ line: startLine + i + 1,
274
+ actual: `<${tagName}>`,
275
+ expected: `<${tagName} class="ed-c-...">`,
276
+ autoFixable: false,
277
+ });
278
+ }
279
+ }
280
+ }
281
+ return issues;
282
+ }
283
+ /**
284
+ * Check static class name values in templates for Eddie naming compliance.
285
+ * Only validates literal class strings, not dynamic ${} expressions.
286
+ */
287
+ checkStaticClassNames(filePath, templateContent, startLine) {
288
+ const issues = [];
289
+ const lines = templateContent.split('\n');
290
+ for (let i = 0; i < lines.length; i++) {
291
+ const line = lines[i];
292
+ // Match static class="literal-value" (not class="${dynamic}")
293
+ const staticClassMatches = line.matchAll(/class\s*=\s*"([^"$]+)"/g);
294
+ for (const match of staticClassMatches) {
295
+ const classValue = match[1].trim();
296
+ if (!classValue)
297
+ continue;
298
+ const classNames = classValue.split(/\s+/);
299
+ for (const className of classNames) {
300
+ if (!className)
301
+ continue;
302
+ // Skip Lit expressions that leaked through
303
+ if (className.includes('${') || className.includes('}'))
304
+ continue;
305
+ const result = this.classNameValidation(className);
306
+ if (!result.valid) {
307
+ issues.push({
308
+ id: `naming-class-${className}-${startLine + i}`,
309
+ category: 'naming',
310
+ severity: 'error',
311
+ message: `Invalid class name "${className}" in template. ${result.issues.join('; ')}`,
312
+ file: filePath,
313
+ line: startLine + i + 1,
314
+ actual: className,
315
+ expected: this.suggestClassName(className),
316
+ autoFixable: true,
317
+ });
318
+ }
319
+ }
320
+ }
321
+ }
322
+ return issues;
323
+ }
324
+ // ===========================================================================
325
+ // SCSS validation
326
+ // ===========================================================================
327
+ /**
328
+ * Validate class names in SCSS file
329
+ */
330
+ validateStyleFile(filePath, content) {
331
+ const issues = [];
332
+ const lines = content.split('\n');
333
+ let inBlockComment = false;
334
+ lines.forEach((line, lineIndex) => {
335
+ const trimmed = line.trim();
336
+ // Track block comments
337
+ if (trimmed.includes('/*'))
338
+ inBlockComment = true;
339
+ if (trimmed.includes('*/')) {
340
+ inBlockComment = false;
341
+ return;
342
+ }
343
+ if (inBlockComment)
344
+ return;
345
+ // Skip single-line comments
346
+ if (trimmed.startsWith('//'))
347
+ return;
348
+ // Find class definitions (.className { or .className &)
349
+ const classDefMatches = line.matchAll(/\.([a-zA-Z][a-zA-Z0-9_-]*)\s*[{&,]/g);
350
+ for (const match of classDefMatches) {
351
+ const className = match[1];
352
+ // Skip Sass variables and interpolations
353
+ if (className.startsWith('$') || className.includes('#{'))
354
+ continue;
355
+ const result = this.classNameValidation(className);
356
+ if (!result.valid) {
357
+ issues.push({
358
+ id: `naming-scss-class-${className}-${lineIndex}`,
359
+ category: 'naming',
360
+ severity: 'error',
361
+ message: `Invalid class name ".${className}" in SCSS. ${result.issues.join('; ')}`,
362
+ file: filePath,
363
+ line: lineIndex + 1,
364
+ actual: className,
365
+ expected: this.suggestClassName(className),
366
+ autoFixable: false,
367
+ });
368
+ }
369
+ }
370
+ });
371
+ return issues;
372
+ }
373
+ // ===========================================================================
374
+ // Class name validation logic
375
+ // ===========================================================================
376
+ /**
377
+ * Validate a single class name string against Eddie's BEM/BEMIT conventions
378
+ */
379
+ classNameValidation(className) {
380
+ const issues = [];
381
+ if (!className) {
382
+ return { valid: false, issues: ['Class name is empty'] };
383
+ }
384
+ // Check for valid prefix
385
+ const hasValidPrefix = this.validPrefixes.some((pattern) => pattern.test(className));
386
+ if (!hasValidPrefix) {
387
+ issues.push('Does not start with valid prefix (ed-c-, ed-l-, ed-u-, ed-r-, ed-p-, is-, has-)');
388
+ }
389
+ // Extract the part after prefix for BEM validation
390
+ let bemPart = className;
391
+ for (const prefix of this.validPrefixes) {
392
+ const match = className.match(prefix);
393
+ if (match) {
394
+ bemPart = className.slice(match[0].length);
395
+ break;
396
+ }
397
+ }
398
+ // Validate BEM structure (block__element--modifier)
399
+ if (bemPart && !this.validateBemStructure(bemPart)) {
400
+ issues.push('Invalid BEM structure. Use block, block__element, or block--modifier format');
401
+ }
402
+ // Check for uppercase letters (BEM uses lowercase only)
403
+ if (/[A-Z]/.test(className)) {
404
+ issues.push('BEM classes must be lowercase');
405
+ }
406
+ return {
407
+ valid: issues.length === 0,
408
+ issues,
409
+ };
410
+ }
411
+ /**
412
+ * Validate BEM structure (block__element--modifier)
413
+ * Allows: block, block__element, block--modifier, block__element--modifier
414
+ * Parts can contain hyphens (single) for multi-word names.
415
+ */
416
+ validateBemStructure(bemPart) {
417
+ const bemRegex = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:__[a-z][a-z0-9]*(?:-[a-z0-9]+)*)?(?:--[a-z][a-z0-9]*(?:-[a-z0-9]+)*)?$/;
418
+ return bemRegex.test(bemPart);
419
+ }
420
+ /**
421
+ * Suggest a corrected class name
422
+ */
423
+ suggestClassName(className) {
424
+ if (!this.validPrefixes.some((p) => p.test(className))) {
425
+ return `ed-c-${className.toLowerCase()}`;
426
+ }
427
+ return className.toLowerCase().replace(/[^a-z0-9_-]/g, '');
428
+ }
429
+ }
430
+ //# sourceMappingURL=naming-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming-validator.js","sourceRoot":"","sources":["../../src/analyze/naming-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Slot Contract Validator
3
+ *
4
+ * Detects the silent-drop class of bug filed under #595, #627, #639: Eddie
5
+ * markup that uses a `slot="X"` attribute referencing a slot the component
6
+ * doesn't declare. The unknown slot reaches the shadow DOM with no matching
7
+ * `<slot name="X">`, the content is dropped, and nothing in the browser
8
+ * surfaces the failure.
9
+ *
10
+ * Scope:
11
+ * - `.html` files (consumer projects, boilerplates)
12
+ * - Lit `html\`...\`` template regions inside `.ts` / `.tsx` files
13
+ *
14
+ * Strategy:
15
+ * - Find every `<ed-...>` opening tag
16
+ * - Walk forward to its matching close tag (depth-tracked)
17
+ * - Inside that range, find every immediate-child element with a
18
+ * `slot="NAME"` attribute
19
+ * - Look up the parent component in the index; if NAME is not in the
20
+ * declared slot list, emit a finding
21
+ *
22
+ * The check is intentionally conservative: false positives are worse than
23
+ * false negatives because validators run in tight CI loops and noisy
24
+ * output gets ignored. Components missing from the index are skipped
25
+ * (rather than flagged as unknown), and the regex is forgiving about
26
+ * whitespace and quote style.
27
+ */
28
+ import type { ComponentEntry, HealthIssue } from '../types.js';
29
+ interface ComponentLookup {
30
+ getComponent(tagName: string): ComponentEntry | undefined;
31
+ }
32
+ export declare class SlotContractValidator {
33
+ private readonly index;
34
+ constructor(index: ComponentLookup);
35
+ /**
36
+ * Validate a single file for slot-contract violations.
37
+ * Returns an empty array for files we don't scan (e.g. .scss).
38
+ */
39
+ validateFile(filePath: string): Promise<HealthIssue[]>;
40
+ /**
41
+ * Scan one chunk of markup for slot-contract violations.
42
+ * `startLine` is the 0-based line offset of the chunk within the file —
43
+ * issue line numbers are reported relative to the file, not the chunk.
44
+ */
45
+ private scanRegion;
46
+ /**
47
+ * Look up the component, then scan the inner markup for any
48
+ * `slot="NAME"` attribute. Emit a finding for each NAME that the
49
+ * component doesn't declare.
50
+ */
51
+ private scanInner;
52
+ /**
53
+ * Walk forward from `startIdx` to find the matching `</tag>` for an
54
+ * already-opened `<tag …>`. Tracks open/close depth so nested same-named
55
+ * tags don't fool the matcher.
56
+ */
57
+ private findMatchingClose;
58
+ /**
59
+ * Extract Lit `html\`...\`` template regions from a TS/TSX file.
60
+ * Mirrors NamingValidator.extractLitTemplates, but stripped down — we
61
+ * don't need to handle every nesting edge case, just the common ones.
62
+ */
63
+ private extractLitTemplates;
64
+ private templateClosesOnLine;
65
+ private lineOf;
66
+ }
67
+ export {};
68
+ //# sourceMappingURL=slot-contract-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-contract-validator.d.ts","sourceRoot":"","sources":["../../src/analyze/slot-contract-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/D,UAAU,eAAe;IACvB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;CAC3D;AAED,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,eAAe;IAEnD;;;OAGG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgC5D;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA+BlB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IA6CjB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAsC3B,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,MAAM;CAOf"}