agileflow 2.90.6 → 2.91.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 (75) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/codebase-indexer.js +810 -0
  4. package/lib/validate-names.js +3 -3
  5. package/package.json +4 -1
  6. package/scripts/obtain-context.js +238 -0
  7. package/scripts/precompact-context.sh +13 -1
  8. package/scripts/query-codebase.js +430 -0
  9. package/scripts/tui/blessed/data/watcher.js +175 -0
  10. package/scripts/tui/blessed/index.js +244 -0
  11. package/scripts/tui/blessed/panels/output.js +95 -0
  12. package/scripts/tui/blessed/panels/sessions.js +143 -0
  13. package/scripts/tui/blessed/panels/trace.js +91 -0
  14. package/scripts/tui/blessed/ui/help.js +77 -0
  15. package/scripts/tui/blessed/ui/screen.js +52 -0
  16. package/scripts/tui/blessed/ui/statusbar.js +51 -0
  17. package/scripts/tui/blessed/ui/tabbar.js +99 -0
  18. package/scripts/tui/index.js +38 -32
  19. package/scripts/tui/simple-tui.js +8 -5
  20. package/scripts/validators/README.md +143 -0
  21. package/scripts/validators/component-validator.js +212 -0
  22. package/scripts/validators/json-schema-validator.js +179 -0
  23. package/scripts/validators/markdown-validator.js +153 -0
  24. package/scripts/validators/migration-validator.js +117 -0
  25. package/scripts/validators/security-validator.js +276 -0
  26. package/scripts/validators/story-format-validator.js +176 -0
  27. package/scripts/validators/test-result-validator.js +99 -0
  28. package/scripts/validators/workflow-validator.js +240 -0
  29. package/src/core/agents/accessibility.md +6 -0
  30. package/src/core/agents/adr-writer.md +6 -0
  31. package/src/core/agents/analytics.md +6 -0
  32. package/src/core/agents/api.md +6 -0
  33. package/src/core/agents/ci.md +6 -0
  34. package/src/core/agents/codebase-query.md +237 -0
  35. package/src/core/agents/compliance.md +6 -0
  36. package/src/core/agents/configuration-damage-control.md +6 -0
  37. package/src/core/agents/configuration-visual-e2e.md +6 -0
  38. package/src/core/agents/database.md +10 -0
  39. package/src/core/agents/datamigration.md +6 -0
  40. package/src/core/agents/design.md +6 -0
  41. package/src/core/agents/devops.md +6 -0
  42. package/src/core/agents/documentation.md +6 -0
  43. package/src/core/agents/epic-planner.md +6 -0
  44. package/src/core/agents/integrations.md +6 -0
  45. package/src/core/agents/mentor.md +6 -0
  46. package/src/core/agents/mobile.md +6 -0
  47. package/src/core/agents/monitoring.md +6 -0
  48. package/src/core/agents/multi-expert.md +6 -0
  49. package/src/core/agents/performance.md +6 -0
  50. package/src/core/agents/product.md +6 -0
  51. package/src/core/agents/qa.md +6 -0
  52. package/src/core/agents/readme-updater.md +6 -0
  53. package/src/core/agents/refactor.md +6 -0
  54. package/src/core/agents/research.md +6 -0
  55. package/src/core/agents/security.md +6 -0
  56. package/src/core/agents/testing.md +10 -0
  57. package/src/core/agents/ui.md +6 -0
  58. package/src/core/commands/audit.md +401 -0
  59. package/src/core/commands/board.md +1 -0
  60. package/src/core/commands/epic.md +92 -1
  61. package/src/core/commands/help.md +1 -0
  62. package/src/core/commands/metrics.md +1 -0
  63. package/src/core/commands/research/analyze.md +1 -0
  64. package/src/core/commands/research/ask.md +2 -0
  65. package/src/core/commands/research/import.md +1 -0
  66. package/src/core/commands/research/list.md +2 -0
  67. package/src/core/commands/research/synthesize.md +584 -0
  68. package/src/core/commands/research/view.md +2 -0
  69. package/src/core/commands/status.md +126 -1
  70. package/src/core/commands/story/list.md +9 -9
  71. package/src/core/commands/story/view.md +1 -0
  72. package/src/core/experts/codebase-query/expertise.yaml +190 -0
  73. package/src/core/experts/codebase-query/question.md +73 -0
  74. package/src/core/experts/codebase-query/self-improve.md +105 -0
  75. package/tools/cli/commands/tui.js +40 -271
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ const blessed = require('blessed');
4
+
5
+ /**
6
+ * Create the status bar at the bottom with always-visible key hints
7
+ * nano-style: users can always see what keys are available
8
+ */
9
+ module.exports = function createStatusBar(screen, state) {
10
+ const statusBar = blessed.box({
11
+ parent: screen,
12
+ bottom: 0,
13
+ left: 0,
14
+ width: '100%',
15
+ height: 1,
16
+ tags: true,
17
+ style: {
18
+ fg: 'white',
19
+ bg: 'blue'
20
+ }
21
+ });
22
+
23
+ // Always-visible key hints (nano-style for user-friendliness)
24
+ const hints = [
25
+ '{bold}1-3{/bold}:Tab',
26
+ '{bold}Tab{/bold}:Next',
27
+ '{bold}j/k{/bold}:Nav',
28
+ '{bold}r{/bold}:Refresh',
29
+ '{bold}?{/bold}:Help',
30
+ '{bold}q{/bold}:Quit'
31
+ ];
32
+
33
+ const hintText = ' ' + hints.join(' ');
34
+ statusBar.setContent(hintText);
35
+
36
+ return {
37
+ element: statusBar,
38
+ setStatus(text) {
39
+ // Show custom status with key hints
40
+ const shortHints = [
41
+ '{bold}r{/bold}:Refresh',
42
+ '{bold}?{/bold}:Help',
43
+ '{bold}q{/bold}:Quit'
44
+ ];
45
+ statusBar.setContent(` ${text} | ${shortHints.join(' ')}`);
46
+ },
47
+ resetHints() {
48
+ statusBar.setContent(hintText);
49
+ }
50
+ };
51
+ };
@@ -0,0 +1,99 @@
1
+ 'use strict';
2
+
3
+ const blessed = require('blessed');
4
+
5
+ /**
6
+ * Create a proper styled tab bar with visual distinction
7
+ */
8
+ module.exports = function createTabBar(screen, state) {
9
+ // Header bar background
10
+ const header = blessed.box({
11
+ parent: screen,
12
+ top: 0,
13
+ left: 0,
14
+ width: '100%',
15
+ height: 3,
16
+ style: {
17
+ bg: 'black'
18
+ }
19
+ });
20
+
21
+ // Logo/title
22
+ blessed.box({
23
+ parent: header,
24
+ top: 0,
25
+ left: 0,
26
+ width: 20,
27
+ height: 3,
28
+ content: '{bold}{#e8683a-fg}▄▀▄ AgileFlow{/}',
29
+ tags: true,
30
+ style: {
31
+ bg: 'black'
32
+ }
33
+ });
34
+
35
+ // Tab container
36
+ const tabContainer = blessed.box({
37
+ parent: header,
38
+ top: 0,
39
+ left: 20,
40
+ width: '100%-20',
41
+ height: 3,
42
+ style: {
43
+ bg: 'black'
44
+ }
45
+ });
46
+
47
+ // Create styled tabs
48
+ const tabs = state.tabs.map((name, i) => {
49
+ const tab = blessed.box({
50
+ parent: tabContainer,
51
+ top: 1,
52
+ left: i * 18,
53
+ width: 16,
54
+ height: 1,
55
+ content: `[${i + 1}] ${name}`,
56
+ tags: true,
57
+ style: {
58
+ fg: 'white',
59
+ bg: 'black'
60
+ }
61
+ });
62
+ return tab;
63
+ });
64
+
65
+ // Version info on right
66
+ blessed.box({
67
+ parent: header,
68
+ top: 1,
69
+ right: 1,
70
+ width: 12,
71
+ height: 1,
72
+ content: '{gray-fg}v2.90.7{/}',
73
+ tags: true,
74
+ style: {
75
+ bg: 'black'
76
+ }
77
+ });
78
+
79
+ return {
80
+ element: header,
81
+ setTab(index) {
82
+ tabs.forEach((tab, i) => {
83
+ if (i === index) {
84
+ // Active tab - cyan background, black text, with brackets
85
+ tab.style.fg = 'black';
86
+ tab.style.bg = 'cyan';
87
+ tab.style.bold = true;
88
+ tab.setContent(`▶ ${state.tabs[i]} ◀`);
89
+ } else {
90
+ // Inactive tabs
91
+ tab.style.fg = 'gray';
92
+ tab.style.bg = 'black';
93
+ tab.style.bold = false;
94
+ tab.setContent(`[${i + 1}] ${state.tabs[i]}`);
95
+ }
96
+ });
97
+ }
98
+ };
99
+ };
@@ -4,52 +4,58 @@
4
4
  /**
5
5
  * AgileFlow TUI - Terminal User Interface
6
6
  *
7
- * Real-time visualization for session monitoring, multi-agent orchestration,
8
- * and interactive loop control.
7
+ * Full-screen, flicker-free dashboard for session monitoring, multi-agent
8
+ * orchestration, and interactive workflow control.
9
9
  *
10
10
  * Usage:
11
11
  * node scripts/tui/index.js
12
12
  * npx agileflow tui
13
+ * npx agileflow tui --fallback (use simple ANSI version)
13
14
  *
14
15
  * Key bindings:
15
- * q - Quit TUI
16
- * s - Start loop on current story
17
- * p - Pause active loop
18
- * r - Resume paused loop
19
- * t - Toggle trace panel
20
- * 1-9 - Switch session focus
16
+ * 1-3 Switch tabs (Sessions, Output, Trace)
17
+ * Tab Next tab
18
+ * j/k Navigate list items
19
+ * r Refresh data
20
+ * ?/h Toggle help overlay
21
+ * q Quit TUI
21
22
  */
22
23
 
23
- // Ink-based TUI is disabled due to React version conflicts in monorepo
24
- // The simple-tui provides responsive layouts and works reliably
25
- const useInk = false;
24
+ // Check for --fallback flag
25
+ const useFallback = process.argv.includes('--fallback') || process.argv.includes('--simple');
26
26
 
27
27
  /**
28
28
  * Main entry point
29
29
  */
30
30
  async function main() {
31
- if (useInk) {
32
- // Use the React/Ink-based Dashboard for modern terminals
33
- const React = require('react');
34
- const { render } = require('ink');
35
- const { Dashboard } = require('./Dashboard');
36
-
37
- // Handle actions from the dashboard
38
- const handleAction = action => {
39
- // TODO: Implement loop control actions
40
- // console.log('Action:', action);
41
- };
42
-
43
- // Render the dashboard
44
- const { waitUntilExit } = render(React.createElement(Dashboard, { onAction: handleAction }));
45
-
46
- // Wait for exit
47
- await waitUntilExit();
31
+ if (useFallback) {
32
+ // Use simple TUI (pure Node.js ANSI codes, no dependencies)
33
+ try {
34
+ const { main: simpleTuiMain } = require('./simple-tui');
35
+ simpleTuiMain();
36
+ } catch (err) {
37
+ console.error('Simple TUI Error:', err.message);
38
+ process.exit(1);
39
+ }
48
40
  } else {
49
- // Fallback to simple TUI (pure Node.js, no React)
50
- console.log('React/Ink not available, using simple TUI...');
51
- const { main: simpleTuiMain } = require('./simple-tui');
52
- simpleTuiMain();
41
+ // Use blessed TUI (professional full-screen interface)
42
+ try {
43
+ const { main: blessedMain } = require('./blessed');
44
+ blessedMain();
45
+ } catch (err) {
46
+ // If blessed fails (missing deps, terminal issues), fall back to simple
47
+ console.error('Blessed TUI failed to load:', err.message);
48
+ console.error('Falling back to simple TUI...\n');
49
+
50
+ try {
51
+ const { main: simpleTuiMain } = require('./simple-tui');
52
+ simpleTuiMain();
53
+ } catch (fallbackErr) {
54
+ console.error('Fallback TUI also failed:', fallbackErr.message);
55
+ console.error('\nTry running with: npx agileflow status');
56
+ process.exit(1);
57
+ }
58
+ }
53
59
  }
54
60
  }
55
61
 
@@ -55,6 +55,7 @@ const ANSI = {
55
55
  showCursor: '\x1b[?25h',
56
56
  saveCursor: '\x1b[s',
57
57
  restoreCursor: '\x1b[u',
58
+ clearLine: '\x1b[K', // Clear from cursor to end of line
58
59
  };
59
60
 
60
61
  // Get project root
@@ -213,7 +214,8 @@ class SimpleTUI {
213
214
  this.render();
214
215
  });
215
216
 
216
- // Initial render
217
+ // Clear screen once at start, then render
218
+ process.stdout.write(ANSI.clear + ANSI.home);
217
219
  this.render();
218
220
 
219
221
  // Update loop
@@ -290,8 +292,8 @@ class SimpleTUI {
290
292
  const height = process.stdout.rows || 24;
291
293
  const output = [];
292
294
 
293
- // Clear screen
294
- output.push(ANSI.clear + ANSI.home);
295
+ // Move to home position (don't clear - prevents bouncing)
296
+ output.push(ANSI.home);
295
297
 
296
298
  // Determine layout mode based on terminal width
297
299
  const isWide = width >= 100;
@@ -317,8 +319,9 @@ class SimpleTUI {
317
319
  this.renderStacked(output, width, height, sessions, loopStatus, agentEvents, isNarrow);
318
320
  }
319
321
 
320
- // Output everything
321
- process.stdout.write(output.join('\n'));
322
+ // Output everything with line clearing to prevent artifacts
323
+ const outputWithClear = output.map(line => line + ANSI.clearLine);
324
+ process.stdout.write(outputWithClear.join('\n'));
322
325
  }
323
326
 
324
327
  renderSideBySide(output, width, height, sessions, loopStatus, agentEvents) {
@@ -0,0 +1,143 @@
1
+ # Validators
2
+
3
+ Specialized self-validation scripts for AgileFlow agents.
4
+
5
+ ## Overview
6
+
7
+ Validators are Node.js scripts that run via hooks to verify agent output. They enable **closed-loop prompts** where validation is guaranteed, not optional.
8
+
9
+ **Research**: See [ADR-0009](../../../docs/03-decisions/adr-0009-specialized-self-validating-agents.md)
10
+
11
+ ---
12
+
13
+ ## Exit Codes
14
+
15
+ | Code | Meaning | Behavior |
16
+ |------|---------|----------|
17
+ | `0` | Success | Proceed normally |
18
+ | `2` | Error (blocking) | Stderr sent to Claude for self-correction |
19
+ | Other | Warning | Log but continue |
20
+
21
+ **Exit code 2 is special**: Claude receives stderr output and automatically attempts to fix the issue.
22
+
23
+ ---
24
+
25
+ ## Input Format
26
+
27
+ Validators receive JSON via stdin with tool context:
28
+
29
+ ```json
30
+ {
31
+ "tool_name": "Write",
32
+ "tool_input": {
33
+ "file_path": "/path/to/file.json",
34
+ "content": "..."
35
+ },
36
+ "result": "File written successfully"
37
+ }
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Validator Template
43
+
44
+ ```javascript
45
+ #!/usr/bin/env node
46
+ const fs = require('fs');
47
+
48
+ let input = '';
49
+ process.stdin.on('data', chunk => input += chunk);
50
+ process.stdin.on('end', () => {
51
+ try {
52
+ const context = JSON.parse(input);
53
+ const filePath = context.tool_input?.file_path;
54
+
55
+ if (!filePath) {
56
+ console.log('No file path in context, skipping');
57
+ process.exit(0);
58
+ }
59
+
60
+ // Your validation logic here
61
+ const issues = validate(filePath);
62
+
63
+ if (issues.length > 0) {
64
+ // Exit 2 = Claude will try to fix these
65
+ console.error(`Resolve these issues in ${filePath}:`);
66
+ issues.forEach(i => console.error(` - ${i}`));
67
+ process.exit(2);
68
+ }
69
+
70
+ console.log(`Validation passed: ${filePath}`);
71
+ process.exit(0);
72
+ } catch (e) {
73
+ // Exit 1 = warning, don't block
74
+ console.error(`Validator error: ${e.message}`);
75
+ process.exit(1);
76
+ }
77
+ });
78
+
79
+ function validate(filePath) {
80
+ const issues = [];
81
+ // Add your checks here
82
+ return issues;
83
+ }
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Hook Configuration
89
+
90
+ Add hooks to agent/command frontmatter:
91
+
92
+ ### PostToolUse (after each tool call)
93
+
94
+ ```yaml
95
+ hooks:
96
+ PostToolUse:
97
+ - matcher: "Write"
98
+ hooks:
99
+ - type: command
100
+ command: "node .agileflow/hooks/validators/your-validator.js"
101
+ ```
102
+
103
+ ### Stop (when agent finishes)
104
+
105
+ ```yaml
106
+ hooks:
107
+ Stop:
108
+ - hooks:
109
+ - type: command
110
+ command: "node .agileflow/hooks/validators/final-validator.js"
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Best Practices
116
+
117
+ 1. **Keep validators fast** (< 100ms) - they run on every matching tool call
118
+ 2. **Use specific matchers** - `"Write"` not `"Write|Edit|Read"`
119
+ 3. **Return helpful errors** - Claude uses stderr to fix issues
120
+ 4. **Test standalone first** - `echo '{"tool_input":{"file_path":"test.json"}}' | node validator.js`
121
+ 5. **Log success too** - helps debugging hook chains
122
+
123
+ ---
124
+
125
+ ## Available Validators
126
+
127
+ | Validator | Purpose | Matcher |
128
+ |-----------|---------|---------|
129
+ | `json-schema-validator.js` | Validate JSON structure | Write (*.json) |
130
+ | `markdown-validator.js` | Validate markdown format | Write (*.md) |
131
+ | `story-format-validator.js` | Validate story structure | Write (status.json) |
132
+
133
+ ---
134
+
135
+ ## Testing Validators
136
+
137
+ ```bash
138
+ # Test with sample input
139
+ echo '{"tool_name":"Write","tool_input":{"file_path":"test.json","content":"{}"}}' | node json-schema-validator.js
140
+
141
+ # Check exit code
142
+ echo $?
143
+ ```
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Component Validator
4
+ *
5
+ * Validates React/Vue/Svelte component files for common issues.
6
+ *
7
+ * Exit codes:
8
+ * 0 = Success
9
+ * 2 = Error (Claude will attempt to fix)
10
+ * 1 = Warning (logged but not blocking)
11
+ *
12
+ * Usage in agent hooks:
13
+ * hooks:
14
+ * PostToolUse:
15
+ * - matcher: "Write"
16
+ * hooks:
17
+ * - type: command
18
+ * command: "node .agileflow/hooks/validators/component-validator.js"
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ let input = '';
25
+ process.stdin.on('data', chunk => input += chunk);
26
+ process.stdin.on('end', () => {
27
+ try {
28
+ const context = JSON.parse(input);
29
+ const filePath = context.tool_input?.file_path;
30
+
31
+ // Only validate component files
32
+ if (!filePath || !isComponentFile(filePath)) {
33
+ process.exit(0);
34
+ }
35
+
36
+ // Skip if file doesn't exist
37
+ if (!fs.existsSync(filePath)) {
38
+ console.log(`File not found: ${filePath} (skipping validation)`);
39
+ process.exit(0);
40
+ }
41
+
42
+ const issues = validateComponent(filePath);
43
+
44
+ if (issues.length > 0) {
45
+ console.error(`Fix these component issues in ${filePath}:`);
46
+ issues.forEach(i => console.error(` - ${i}`));
47
+ process.exit(2); // Claude will fix
48
+ }
49
+
50
+ console.log(`Component validation passed: ${filePath}`);
51
+ process.exit(0);
52
+ } catch (e) {
53
+ console.error(`Validator error: ${e.message}`);
54
+ process.exit(1);
55
+ }
56
+ });
57
+
58
+ function isComponentFile(filePath) {
59
+ const ext = path.extname(filePath).toLowerCase();
60
+ const componentExtensions = ['.jsx', '.tsx', '.vue', '.svelte'];
61
+
62
+ // Also check for .js/.ts files in component directories
63
+ if (['.js', '.ts'].includes(ext)) {
64
+ const normalizedPath = filePath.toLowerCase();
65
+ return normalizedPath.includes('/components/') ||
66
+ normalizedPath.includes('/pages/') ||
67
+ normalizedPath.includes('/views/');
68
+ }
69
+
70
+ return componentExtensions.includes(ext);
71
+ }
72
+
73
+ function validateComponent(filePath) {
74
+ const issues = [];
75
+
76
+ try {
77
+ const content = fs.readFileSync(filePath, 'utf8');
78
+ const ext = path.extname(filePath).toLowerCase();
79
+ const fileName = path.basename(filePath, ext);
80
+
81
+ // Check for empty component
82
+ if (!content.trim()) {
83
+ issues.push('Component file is empty');
84
+ return issues;
85
+ }
86
+
87
+ // React/JSX/TSX validation
88
+ if (['.jsx', '.tsx'].includes(ext) || content.includes('React')) {
89
+ issues.push(...validateReactComponent(content, fileName));
90
+ }
91
+
92
+ // Vue validation
93
+ if (ext === '.vue') {
94
+ issues.push(...validateVueComponent(content));
95
+ }
96
+
97
+ // Svelte validation
98
+ if (ext === '.svelte') {
99
+ issues.push(...validateSvelteComponent(content));
100
+ }
101
+
102
+ // General accessibility checks
103
+ issues.push(...validateAccessibility(content));
104
+
105
+ } catch (e) {
106
+ issues.push(`Read error: ${e.message}`);
107
+ }
108
+
109
+ return issues;
110
+ }
111
+
112
+ function validateReactComponent(content, fileName) {
113
+ const issues = [];
114
+
115
+ // Check for component export
116
+ if (!content.includes('export default') && !content.includes('export function') && !content.includes('export const')) {
117
+ issues.push('Component should have an export (export default, export function, or export const)');
118
+ }
119
+
120
+ // Check for React import in JSX files
121
+ if (content.includes('React.') && !content.includes("from 'react'") && !content.includes('from "react"')) {
122
+ issues.push('Using React. prefix but React is not imported');
123
+ }
124
+
125
+ // Check for proper function/component naming (should match filename for default exports)
126
+ const pascalCaseRegex = /^[A-Z][a-zA-Z0-9]*$/;
127
+ if (!pascalCaseRegex.test(fileName) && !fileName.startsWith('use')) {
128
+ // Only warn, don't block - could be a utility file
129
+ console.log(`Note: Component filename "${fileName}" should be PascalCase`);
130
+ }
131
+
132
+ // Check for inline styles (prefer CSS modules or styled-components)
133
+ const inlineStyleCount = (content.match(/style=\{\{/g) || []).length;
134
+ if (inlineStyleCount > 5) {
135
+ issues.push(`Too many inline styles (${inlineStyleCount}) - consider using CSS modules or styled-components`);
136
+ }
137
+
138
+ // Check for console.log in production code
139
+ if (content.includes('console.log') && !content.includes('// debug') && !content.includes('// DEBUG')) {
140
+ console.log('Note: console.log found - ensure it\'s removed before production');
141
+ }
142
+
143
+ // Check for missing key prop in map
144
+ if (content.includes('.map(') && content.includes('<') && !content.includes('key=')) {
145
+ issues.push('Array .map() rendering elements should include key prop');
146
+ }
147
+
148
+ return issues;
149
+ }
150
+
151
+ function validateVueComponent(content) {
152
+ const issues = [];
153
+
154
+ // Check for required template section
155
+ if (!content.includes('<template>') && !content.includes('<template ')) {
156
+ issues.push('Vue component must have a <template> section');
157
+ }
158
+
159
+ // Check for script section
160
+ if (!content.includes('<script') && !content.includes('<script>')) {
161
+ // Not strictly required but recommended
162
+ console.log('Note: Vue component has no <script> section');
163
+ }
164
+
165
+ // Check for scoped styles (recommended)
166
+ if (content.includes('<style>') && !content.includes('<style scoped') && !content.includes('scoped>')) {
167
+ console.log('Note: Consider using scoped styles to prevent CSS leaks');
168
+ }
169
+
170
+ return issues;
171
+ }
172
+
173
+ function validateSvelteComponent(content) {
174
+ const issues = [];
175
+
176
+ // Check for script section
177
+ if (!content.includes('<script') && !content.includes('<script>')) {
178
+ console.log('Note: Svelte component has no <script> section');
179
+ }
180
+
181
+ return issues;
182
+ }
183
+
184
+ function validateAccessibility(content) {
185
+ const issues = [];
186
+
187
+ // Check for images without alt
188
+ const imgWithoutAlt = content.match(/<img[^>]*(?!alt=)[^>]*>/gi) || [];
189
+ const imgWithEmptyAlt = content.match(/<img[^>]*alt=["']["'][^>]*>/gi) || [];
190
+
191
+ if (imgWithoutAlt.length > 0) {
192
+ issues.push(`Found ${imgWithoutAlt.length} <img> tag(s) without alt attribute`);
193
+ }
194
+
195
+ // Check for button without type
196
+ if (content.includes('<button') && !content.includes('type=')) {
197
+ console.log('Note: <button> elements should have explicit type attribute');
198
+ }
199
+
200
+ // Check for click handlers on non-interactive elements
201
+ const clickOnDiv = content.match(/onClick[^>]*>[^<]*<\/div>/gi) || [];
202
+ if (clickOnDiv.length > 0) {
203
+ issues.push('onClick on <div> detected - use <button> for interactive elements (accessibility)');
204
+ }
205
+
206
+ // Check for form inputs without labels
207
+ if (content.includes('<input') && !content.includes('<label') && !content.includes('aria-label')) {
208
+ console.log('Note: <input> elements should have associated <label> or aria-label');
209
+ }
210
+
211
+ return issues;
212
+ }