@iamnishankhan/opencode-kit-grok 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.
- package/bin/cli.js +46 -0
- package/kit/.opencode/AGENTS.md +392 -0
- package/kit/.opencode/README.md +71 -0
- package/kit/.opencode/USAGE.md +405 -0
- package/kit/.opencode/agents/backend-specialist.md +278 -0
- package/kit/.opencode/agents/code-archaeologist.md +114 -0
- package/kit/.opencode/agents/database-architect.md +233 -0
- package/kit/.opencode/agents/debugger.md +234 -0
- package/kit/.opencode/agents/devops-engineer.md +252 -0
- package/kit/.opencode/agents/documentation-writer.md +111 -0
- package/kit/.opencode/agents/explorer-agent.md +83 -0
- package/kit/.opencode/agents/frontend-specialist.md +606 -0
- package/kit/.opencode/agents/game-developer.md +169 -0
- package/kit/.opencode/agents/mobile-developer.md +385 -0
- package/kit/.opencode/agents/orchestrator.md +213 -0
- package/kit/.opencode/agents/penetration-tester.md +197 -0
- package/kit/.opencode/agents/performance-optimizer.md +194 -0
- package/kit/.opencode/agents/product-manager.md +120 -0
- package/kit/.opencode/agents/product-owner.md +103 -0
- package/kit/.opencode/agents/project-planner.md +418 -0
- package/kit/.opencode/agents/qa-automation-engineer.md +113 -0
- package/kit/.opencode/agents/security-auditor.md +179 -0
- package/kit/.opencode/agents/seo-specialist.md +119 -0
- package/kit/.opencode/agents/test-engineer.md +169 -0
- package/kit/.opencode/commands/brainstorm.md +113 -0
- package/kit/.opencode/commands/coordinate.md +72 -0
- package/kit/.opencode/commands/create.md +63 -0
- package/kit/.opencode/commands/debug.md +102 -0
- package/kit/.opencode/commands/deploy.md +175 -0
- package/kit/.opencode/commands/enhance.md +62 -0
- package/kit/.opencode/commands/orchestrate.md +241 -0
- package/kit/.opencode/commands/plan.md +89 -0
- package/kit/.opencode/commands/preview.md +80 -0
- package/kit/.opencode/commands/remember.md +67 -0
- package/kit/.opencode/commands/status.md +86 -0
- package/kit/.opencode/commands/test.md +143 -0
- package/kit/.opencode/commands/verify.md +71 -0
- package/kit/.opencode/memory/MEMORY.md +6 -0
- package/kit/.opencode/memory/feedback-history.md +9 -0
- package/kit/.opencode/memory/project-conventions.md +16 -0
- package/kit/.opencode/memory/tech-decisions.md +10 -0
- package/kit/.opencode/memory/user-preferences.md +9 -0
- package/kit/.opencode/opencode.json +15 -0
- package/kit/.opencode/scripts/README.md +95 -0
- package/kit/.opencode/scripts/auto_preview.py +149 -0
- package/kit/.opencode/scripts/checklist.py +75 -0
- package/kit/.opencode/scripts/component_registry.py +238 -0
- package/kit/.opencode/scripts/dependency_graph.py +73 -0
- package/kit/.opencode/scripts/generate_manifest.py +44 -0
- package/kit/.opencode/scripts/session_manager.py +120 -0
- package/kit/.opencode/scripts/tests/test_toolkit.py +238 -0
- package/kit/.opencode/scripts/validate_kit.py +397 -0
- package/kit/.opencode/scripts/validation_runner.py +216 -0
- package/kit/.opencode/scripts/verify_all.py +85 -0
- package/kit/.opencode/skills/api-patterns/SKILL.md +83 -0
- package/kit/.opencode/skills/api-patterns/api-style.md +42 -0
- package/kit/.opencode/skills/api-patterns/auth.md +24 -0
- package/kit/.opencode/skills/api-patterns/documentation.md +26 -0
- package/kit/.opencode/skills/api-patterns/graphql.md +41 -0
- package/kit/.opencode/skills/api-patterns/rate-limiting.md +31 -0
- package/kit/.opencode/skills/api-patterns/response.md +37 -0
- package/kit/.opencode/skills/api-patterns/rest.md +40 -0
- package/kit/.opencode/skills/api-patterns/scripts/api_validator.py +211 -0
- package/kit/.opencode/skills/api-patterns/security-testing.md +122 -0
- package/kit/.opencode/skills/api-patterns/trpc.md +41 -0
- package/kit/.opencode/skills/api-patterns/versioning.md +22 -0
- package/kit/.opencode/skills/app-builder/SKILL.md +78 -0
- package/kit/.opencode/skills/app-builder/agent-coordination.md +71 -0
- package/kit/.opencode/skills/app-builder/feature-building.md +53 -0
- package/kit/.opencode/skills/app-builder/project-detection.md +45 -0
- package/kit/.opencode/skills/app-builder/scaffolding.md +110 -0
- package/kit/.opencode/skills/app-builder/tech-stack.md +41 -0
- package/kit/.opencode/skills/app-builder/templates/SKILL.md +39 -0
- package/kit/.opencode/skills/app-builder/templates/astro-static/TEMPLATE.md +78 -0
- package/kit/.opencode/skills/app-builder/templates/chrome-extension/TEMPLATE.md +96 -0
- package/kit/.opencode/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/kit/.opencode/skills/app-builder/templates/electron-desktop/TEMPLATE.md +97 -0
- package/kit/.opencode/skills/app-builder/templates/express-api/TEMPLATE.md +89 -0
- package/kit/.opencode/skills/app-builder/templates/flutter-app/TEMPLATE.md +93 -0
- package/kit/.opencode/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +97 -0
- package/kit/.opencode/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +126 -0
- package/kit/.opencode/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +125 -0
- package/kit/.opencode/skills/app-builder/templates/nextjs-static/TEMPLATE.md +174 -0
- package/kit/.opencode/skills/app-builder/templates/nuxt-app/TEMPLATE.md +127 -0
- package/kit/.opencode/skills/app-builder/templates/python-fastapi/TEMPLATE.md +94 -0
- package/kit/.opencode/skills/app-builder/templates/react-native-app/TEMPLATE.md +121 -0
- package/kit/.opencode/skills/architecture/SKILL.md +57 -0
- package/kit/.opencode/skills/architecture/context-discovery.md +43 -0
- package/kit/.opencode/skills/architecture/examples.md +94 -0
- package/kit/.opencode/skills/architecture/pattern-selection.md +68 -0
- package/kit/.opencode/skills/architecture/patterns-reference.md +50 -0
- package/kit/.opencode/skills/architecture/trade-off-analysis.md +77 -0
- package/kit/.opencode/skills/bash-linux/SKILL.md +201 -0
- package/kit/.opencode/skills/batch-operations/SKILL.md +105 -0
- package/kit/.opencode/skills/behavioral-modes/SKILL.md +253 -0
- package/kit/.opencode/skills/brainstorming/SKILL.md +178 -0
- package/kit/.opencode/skills/brainstorming/dynamic-questioning.md +350 -0
- package/kit/.opencode/skills/clean-code/SKILL.md +201 -0
- package/kit/.opencode/skills/code-review-checklist/SKILL.md +111 -0
- package/kit/.opencode/skills/code-review-graph/SKILL.md +305 -0
- package/kit/.opencode/skills/context-compression/SKILL.md +145 -0
- package/kit/.opencode/skills/coordinator-mode/SKILL.md +208 -0
- package/kit/.opencode/skills/database-design/SKILL.md +54 -0
- package/kit/.opencode/skills/database-design/database-selection.md +43 -0
- package/kit/.opencode/skills/database-design/indexing.md +39 -0
- package/kit/.opencode/skills/database-design/migrations.md +48 -0
- package/kit/.opencode/skills/database-design/optimization.md +36 -0
- package/kit/.opencode/skills/database-design/orm-selection.md +30 -0
- package/kit/.opencode/skills/database-design/schema-design.md +56 -0
- package/kit/.opencode/skills/database-design/scripts/schema_validator.py +172 -0
- package/kit/.opencode/skills/deployment-procedures/SKILL.md +243 -0
- package/kit/.opencode/skills/design-spec/SKILL.md +185 -0
- package/kit/.opencode/skills/design-spec/collection.md +106 -0
- package/kit/.opencode/skills/documentation-templates/SKILL.md +196 -0
- package/kit/.opencode/skills/frontend-architecture/SKILL.md +245 -0
- package/kit/.opencode/skills/frontend-design/SKILL.md +1222 -0
- package/kit/.opencode/skills/frontend-design/redesign.md +178 -0
- package/kit/.opencode/skills/frontend-design/scripts/accessibility_checker.py +145 -0
- package/kit/.opencode/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/kit/.opencode/skills/frontend-design/style-brutalist.md +92 -0
- package/kit/.opencode/skills/frontend-design/style-minimalist.md +85 -0
- package/kit/.opencode/skills/game-development/2d-games/SKILL.md +118 -0
- package/kit/.opencode/skills/game-development/3d-games/SKILL.md +134 -0
- package/kit/.opencode/skills/game-development/SKILL.md +169 -0
- package/kit/.opencode/skills/game-development/game-art/SKILL.md +184 -0
- package/kit/.opencode/skills/game-development/game-audio/SKILL.md +189 -0
- package/kit/.opencode/skills/game-development/game-design/SKILL.md +128 -0
- package/kit/.opencode/skills/game-development/mobile-games/SKILL.md +107 -0
- package/kit/.opencode/skills/game-development/multiplayer/SKILL.md +131 -0
- package/kit/.opencode/skills/game-development/pc-games/SKILL.md +143 -0
- package/kit/.opencode/skills/game-development/vr-ar/SKILL.md +122 -0
- package/kit/.opencode/skills/game-development/web-games/SKILL.md +149 -0
- package/kit/.opencode/skills/geo-fundamentals/SKILL.md +158 -0
- package/kit/.opencode/skills/geo-fundamentals/scripts/geo_checker.py +273 -0
- package/kit/.opencode/skills/i18n-localization/SKILL.md +156 -0
- package/kit/.opencode/skills/i18n-localization/scripts/i18n_checker.py +223 -0
- package/kit/.opencode/skills/intelligent-routing/SKILL.md +337 -0
- package/kit/.opencode/skills/lint-and-validate/SKILL.md +47 -0
- package/kit/.opencode/skills/lint-and-validate/scripts/lint_runner.py +184 -0
- package/kit/.opencode/skills/lint-and-validate/scripts/type_coverage.py +245 -0
- package/kit/.opencode/skills/mcp-builder/SKILL.md +205 -0
- package/kit/.opencode/skills/memory-system/SKILL.md +181 -0
- package/kit/.opencode/skills/mobile-design/SKILL.md +396 -0
- package/kit/.opencode/skills/mobile-design/decision-trees.md +516 -0
- package/kit/.opencode/skills/mobile-design/mobile-backend.md +491 -0
- package/kit/.opencode/skills/mobile-design/mobile-color-system.md +420 -0
- package/kit/.opencode/skills/mobile-design/mobile-debugging.md +122 -0
- package/kit/.opencode/skills/mobile-design/mobile-design-thinking.md +357 -0
- package/kit/.opencode/skills/mobile-design/mobile-navigation.md +458 -0
- package/kit/.opencode/skills/mobile-design/mobile-performance.md +767 -0
- package/kit/.opencode/skills/mobile-design/mobile-testing.md +356 -0
- package/kit/.opencode/skills/mobile-design/mobile-typography.md +433 -0
- package/kit/.opencode/skills/mobile-design/platform-android.md +666 -0
- package/kit/.opencode/skills/mobile-design/platform-ios.md +561 -0
- package/kit/.opencode/skills/mobile-design/scripts/mobile_audit.py +670 -0
- package/kit/.opencode/skills/mobile-design/touch-psychology.md +537 -0
- package/kit/.opencode/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +351 -0
- package/kit/.opencode/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -0
- package/kit/.opencode/skills/nextjs-react-expert/3-server-server-side-performance.md +490 -0
- package/kit/.opencode/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -0
- package/kit/.opencode/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -0
- package/kit/.opencode/skills/nextjs-react-expert/6-rendering-rendering-performance.md +432 -0
- package/kit/.opencode/skills/nextjs-react-expert/7-js-javascript-performance.md +684 -0
- package/kit/.opencode/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -0
- package/kit/.opencode/skills/nextjs-react-expert/9-cache-components.md +103 -0
- package/kit/.opencode/skills/nextjs-react-expert/SKILL.md +293 -0
- package/kit/.opencode/skills/nextjs-react-expert/scripts/convert_rules.py +222 -0
- package/kit/.opencode/skills/nextjs-react-expert/scripts/react_performance_checker.py +281 -0
- package/kit/.opencode/skills/nodejs-best-practices/SKILL.md +336 -0
- package/kit/.opencode/skills/parallel-agents/SKILL.md +192 -0
- package/kit/.opencode/skills/performance-profiling/SKILL.md +145 -0
- package/kit/.opencode/skills/performance-profiling/scripts/bundle_analyzer.py +120 -0
- package/kit/.opencode/skills/performance-profiling/scripts/lighthouse_audit.py +108 -0
- package/kit/.opencode/skills/plan-writing/SKILL.md +153 -0
- package/kit/.opencode/skills/powershell-windows/SKILL.md +169 -0
- package/kit/.opencode/skills/python-patterns/SKILL.md +443 -0
- package/kit/.opencode/skills/red-team-tactics/SKILL.md +201 -0
- package/kit/.opencode/skills/rust-pro/SKILL.md +177 -0
- package/kit/.opencode/skills/seo-fundamentals/SKILL.md +131 -0
- package/kit/.opencode/skills/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/kit/.opencode/skills/server-management/SKILL.md +163 -0
- package/kit/.opencode/skills/simplify-code/SKILL.md +131 -0
- package/kit/.opencode/skills/skillify/SKILL.md +115 -0
- package/kit/.opencode/skills/systematic-debugging/SKILL.md +111 -0
- package/kit/.opencode/skills/tailwind-patterns/SKILL.md +271 -0
- package/kit/.opencode/skills/tdd-workflow/SKILL.md +151 -0
- package/kit/.opencode/skills/testing-patterns/SKILL.md +180 -0
- package/kit/.opencode/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/kit/.opencode/skills/verify-changes/SKILL.md +127 -0
- package/kit/.opencode/skills/vulnerability-scanner/SKILL.md +278 -0
- package/kit/.opencode/skills/vulnerability-scanner/checklists.md +124 -0
- package/kit/.opencode/skills/vulnerability-scanner/scripts/dependency_analyzer.py +144 -0
- package/kit/.opencode/skills/vulnerability-scanner/scripts/security_scan.py +410 -0
- package/kit/.opencode/skills/web-design-guidelines/SKILL.md +56 -0
- package/kit/.opencode/skills/webapp-testing/SKILL.md +189 -0
- package/kit/.opencode/skills/webapp-testing/scripts/playwright_runner.py +123 -0
- package/lib/init.js +61 -0
- package/package.json +21 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Conversion Script: React Best Practices → .agents Format
|
|
4
|
+
Merges 59 individual rules into 8 grouped section files
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Tuple
|
|
11
|
+
|
|
12
|
+
# Section metadata from _sections.md
|
|
13
|
+
SECTIONS = {
|
|
14
|
+
'async': {
|
|
15
|
+
'number': 1,
|
|
16
|
+
'title': 'Eliminating Waterfalls',
|
|
17
|
+
'impact': 'CRITICAL',
|
|
18
|
+
'description': 'Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.'
|
|
19
|
+
},
|
|
20
|
+
'bundle': {
|
|
21
|
+
'number': 2,
|
|
22
|
+
'title': 'Bundle Size Optimization',
|
|
23
|
+
'impact': 'CRITICAL',
|
|
24
|
+
'description': 'Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.'
|
|
25
|
+
},
|
|
26
|
+
'server': {
|
|
27
|
+
'number': 3,
|
|
28
|
+
'title': 'Server-Side Performance',
|
|
29
|
+
'impact': 'HIGH',
|
|
30
|
+
'description': 'Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.'
|
|
31
|
+
},
|
|
32
|
+
'client': {
|
|
33
|
+
'number': 4,
|
|
34
|
+
'title': 'Client-Side Data Fetching',
|
|
35
|
+
'impact': 'MEDIUM-HIGH',
|
|
36
|
+
'description': 'Automatic deduplication and efficient data fetching patterns reduce redundant network requests.'
|
|
37
|
+
},
|
|
38
|
+
'rerender': {
|
|
39
|
+
'number': 5,
|
|
40
|
+
'title': 'Re-render Optimization',
|
|
41
|
+
'impact': 'MEDIUM',
|
|
42
|
+
'description': 'Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.'
|
|
43
|
+
},
|
|
44
|
+
'rendering': {
|
|
45
|
+
'number': 6,
|
|
46
|
+
'title': 'Rendering Performance',
|
|
47
|
+
'impact': 'MEDIUM',
|
|
48
|
+
'description': 'Optimizing the rendering process reduces the work the browser needs to do.'
|
|
49
|
+
},
|
|
50
|
+
'js': {
|
|
51
|
+
'number': 7,
|
|
52
|
+
'title': 'JavaScript Performance',
|
|
53
|
+
'impact': 'LOW-MEDIUM',
|
|
54
|
+
'description': 'Micro-optimizations for hot paths can add up to meaningful improvements.'
|
|
55
|
+
},
|
|
56
|
+
'advanced': {
|
|
57
|
+
'number': 8,
|
|
58
|
+
'title': 'Advanced Patterns',
|
|
59
|
+
'impact': 'VARIABLE',
|
|
60
|
+
'description': 'Advanced patterns for specific cases that require careful implementation.'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def parse_frontmatter(content: str) -> Tuple[Dict, str]:
|
|
66
|
+
"""Parse markdown frontmatter and body"""
|
|
67
|
+
if not content.startswith('---'):
|
|
68
|
+
return {}, content
|
|
69
|
+
|
|
70
|
+
parts = content.split('---', 2)
|
|
71
|
+
if len(parts) < 3:
|
|
72
|
+
return {}, content
|
|
73
|
+
|
|
74
|
+
# Parse YAML frontmatter manually (simple key: value)
|
|
75
|
+
frontmatter = {}
|
|
76
|
+
for line in parts[1].strip().split('\n'):
|
|
77
|
+
if ':' in line:
|
|
78
|
+
key, value = line.split(':', 1)
|
|
79
|
+
frontmatter[key.strip()] = value.strip()
|
|
80
|
+
|
|
81
|
+
body = parts[2].strip()
|
|
82
|
+
return frontmatter, body
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def parse_rule_file(filepath: Path) -> Dict:
|
|
86
|
+
"""Parse a single rule file"""
|
|
87
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
88
|
+
content = f.read()
|
|
89
|
+
|
|
90
|
+
frontmatter, body = parse_frontmatter(content)
|
|
91
|
+
|
|
92
|
+
# Extract prefix from filename
|
|
93
|
+
filename = filepath.stem
|
|
94
|
+
prefix = filename.split('-')[0]
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
'filename': filepath.name,
|
|
98
|
+
'prefix': prefix,
|
|
99
|
+
'title': frontmatter.get('title', filename),
|
|
100
|
+
'impact': frontmatter.get('impact', ''),
|
|
101
|
+
'impactDescription': frontmatter.get('impactDescription', ''),
|
|
102
|
+
'tags': frontmatter.get('tags', ''),
|
|
103
|
+
'body': body,
|
|
104
|
+
'frontmatter': frontmatter
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def group_rules_by_section(rules_dir: Path) -> Dict[str, List[Dict]]:
|
|
109
|
+
"""Group all rules by their section prefix"""
|
|
110
|
+
grouped = {prefix: [] for prefix in SECTIONS.keys()}
|
|
111
|
+
|
|
112
|
+
for rule_file in sorted(rules_dir.glob('*.md')):
|
|
113
|
+
# Skip special files
|
|
114
|
+
if rule_file.name.startswith('_'):
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
rule = parse_rule_file(rule_file)
|
|
118
|
+
prefix = rule['prefix']
|
|
119
|
+
|
|
120
|
+
if prefix in grouped:
|
|
121
|
+
grouped[prefix].append(rule)
|
|
122
|
+
else:
|
|
123
|
+
print(f"[WARNING] Unknown prefix '{prefix}' in file: {rule_file.name}")
|
|
124
|
+
|
|
125
|
+
return grouped
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def generate_section_file(section_prefix: str, rules: List[Dict], output_dir: Path):
|
|
129
|
+
"""Generate a merged section file"""
|
|
130
|
+
if not rules:
|
|
131
|
+
print(f"[WARNING] No rules found for section: {section_prefix}")
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
section_meta = SECTIONS[section_prefix]
|
|
135
|
+
section_num = section_meta['number']
|
|
136
|
+
section_title = section_meta['title']
|
|
137
|
+
impact = section_meta['impact']
|
|
138
|
+
description = section_meta['description']
|
|
139
|
+
|
|
140
|
+
# Sort rules by title
|
|
141
|
+
rules.sort(key=lambda r: r['title'])
|
|
142
|
+
|
|
143
|
+
# Build content
|
|
144
|
+
content = f"""# {section_num}. {section_title}
|
|
145
|
+
|
|
146
|
+
> **Impact:** {impact}
|
|
147
|
+
> **Focus:** {description}
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Overview
|
|
152
|
+
|
|
153
|
+
This section contains **{len(rules)} rules** focused on {section_title.lower()}.
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
# Add each rule
|
|
158
|
+
for i, rule in enumerate(rules, 1):
|
|
159
|
+
rule_id = f"{section_num}.{i}"
|
|
160
|
+
title = rule['title']
|
|
161
|
+
rule_impact = rule['impact']
|
|
162
|
+
tags = rule['tags']
|
|
163
|
+
body = rule['body']
|
|
164
|
+
|
|
165
|
+
content += f"""---
|
|
166
|
+
|
|
167
|
+
## Rule {rule_id}: {title}
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
if rule_impact:
|
|
172
|
+
content += f"**Impact:** {rule_impact} \n"
|
|
173
|
+
|
|
174
|
+
if tags:
|
|
175
|
+
content += f"**Tags:** {tags} \n"
|
|
176
|
+
|
|
177
|
+
content += f"\n{body}\n\n"
|
|
178
|
+
|
|
179
|
+
# Write file
|
|
180
|
+
output_file = output_dir / f"{section_num}-{section_prefix}-{section_title.lower().replace(' ', '-')}.md"
|
|
181
|
+
output_file.write_text(content, encoding='utf-8')
|
|
182
|
+
print(f"[OK] Generated: {output_file.name} ({len(rules)} rules)")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def main():
|
|
186
|
+
"""Main conversion function"""
|
|
187
|
+
# Paths
|
|
188
|
+
base_dir = Path(__file__).parent.parent.parent.parent.parent
|
|
189
|
+
rules_dir = base_dir / "others/agent-skills/skills/react-best-practices/rules"
|
|
190
|
+
output_dir = base_dir / ".opencode/skills/react-best-practices"
|
|
191
|
+
|
|
192
|
+
print(f"[*] Reading rules from: {rules_dir}")
|
|
193
|
+
print(f"[*] Output to: {output_dir}")
|
|
194
|
+
print()
|
|
195
|
+
|
|
196
|
+
# Check if rules directory exists
|
|
197
|
+
if not rules_dir.exists():
|
|
198
|
+
print(f"[ERROR] Rules directory not found: {rules_dir}")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# Group rules
|
|
202
|
+
print("[*] Grouping rules by section...")
|
|
203
|
+
grouped_rules = group_rules_by_section(rules_dir)
|
|
204
|
+
|
|
205
|
+
# Stats
|
|
206
|
+
total_rules = sum(len(rules) for rules in grouped_rules.values())
|
|
207
|
+
print(f"[*] Found {total_rules} total rules")
|
|
208
|
+
print()
|
|
209
|
+
|
|
210
|
+
# Generate section files
|
|
211
|
+
print("[*] Generating section files...")
|
|
212
|
+
for section_prefix in SECTIONS.keys():
|
|
213
|
+
rules = grouped_rules[section_prefix]
|
|
214
|
+
generate_section_file(section_prefix, rules, output_dir)
|
|
215
|
+
|
|
216
|
+
print()
|
|
217
|
+
print("[SUCCESS] Conversion complete!")
|
|
218
|
+
print(f"[*] Generated 8 section files from {total_rules} rules")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if __name__ == '__main__':
|
|
222
|
+
main()
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
React Performance Checker
|
|
4
|
+
Automated performance audit for React/Next.js projects
|
|
5
|
+
Based on Vercel Engineering best practices
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
SKIP_DIRS = {
|
|
13
|
+
".git", ".next", ".agents", "node_modules", "dist", "build",
|
|
14
|
+
"coverage", "out", ".turbo", ".cache", "vendor",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PerformanceChecker:
|
|
19
|
+
def __init__(self, project_path: str):
|
|
20
|
+
self.project_path = Path(project_path).resolve()
|
|
21
|
+
self.issues = []
|
|
22
|
+
self.warnings = []
|
|
23
|
+
self.passed = []
|
|
24
|
+
self.scan_roots = self._discover_scan_roots()
|
|
25
|
+
|
|
26
|
+
def _discover_scan_roots(self):
|
|
27
|
+
"""Prefer actual React source roots over generated or unrelated files."""
|
|
28
|
+
candidates = []
|
|
29
|
+
for base in (self.project_path, self.project_path / "web"):
|
|
30
|
+
package_json = base / "package.json"
|
|
31
|
+
if not package_json.exists():
|
|
32
|
+
continue
|
|
33
|
+
try:
|
|
34
|
+
package_text = package_json.read_text(encoding="utf-8", errors="replace")
|
|
35
|
+
except OSError:
|
|
36
|
+
continue
|
|
37
|
+
if not any(name in package_text for name in ('"next"', '"react"')):
|
|
38
|
+
continue
|
|
39
|
+
source = base / "src"
|
|
40
|
+
candidates.append(source if source.is_dir() else base)
|
|
41
|
+
return candidates or [self.project_path]
|
|
42
|
+
|
|
43
|
+
def _iter_files(self, extensions):
|
|
44
|
+
"""Yield project files matching the given extensions.
|
|
45
|
+
|
|
46
|
+
pathlib.rglob does NOT support brace expansion ('*.{ts,tsx}' matches
|
|
47
|
+
nothing), so we iterate per-extension explicitly.
|
|
48
|
+
"""
|
|
49
|
+
seen = set()
|
|
50
|
+
for root in self.scan_roots:
|
|
51
|
+
for ext in extensions:
|
|
52
|
+
for filepath in root.rglob(f"*.{ext}"):
|
|
53
|
+
if any(part in SKIP_DIRS for part in filepath.parts):
|
|
54
|
+
continue
|
|
55
|
+
resolved = filepath.resolve()
|
|
56
|
+
if resolved in seen:
|
|
57
|
+
continue
|
|
58
|
+
seen.add(resolved)
|
|
59
|
+
yield filepath
|
|
60
|
+
|
|
61
|
+
def check_waterfalls(self):
|
|
62
|
+
"""Check for sequential await patterns (Section 1)"""
|
|
63
|
+
print("\n[*] Checking for waterfalls (sequential awaits)...")
|
|
64
|
+
|
|
65
|
+
for filepath in self._iter_files(['ts', 'tsx', 'js', 'jsx']):
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
content = filepath.read_text(encoding='utf-8')
|
|
69
|
+
|
|
70
|
+
# Pattern: multiple awaits in sequence without Promise.all
|
|
71
|
+
sequential_awaits = re.findall(r'await\s+\w+.*?\n\s*await\s+\w+', content)
|
|
72
|
+
|
|
73
|
+
if sequential_awaits:
|
|
74
|
+
self.issues.append({
|
|
75
|
+
'file': str(filepath.relative_to(self.project_path)),
|
|
76
|
+
'type': 'CRITICAL',
|
|
77
|
+
'issue': 'Sequential awaits detected (waterfall)',
|
|
78
|
+
'fix': 'Use Promise.all() for parallel fetching',
|
|
79
|
+
'section': '1-async-eliminating-waterfalls.md'
|
|
80
|
+
})
|
|
81
|
+
except Exception as e:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
def check_barrel_imports(self):
|
|
85
|
+
"""Check for barrel imports (Section 2)"""
|
|
86
|
+
print("[*] Checking for barrel imports...")
|
|
87
|
+
|
|
88
|
+
for filepath in self._iter_files(['ts', 'tsx', 'js', 'jsx']):
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
content = filepath.read_text(encoding='utf-8')
|
|
92
|
+
|
|
93
|
+
# Only explicit /index imports are treated as barrels. Extensionless
|
|
94
|
+
# relative imports are normal in TypeScript and are not evidence of a barrel.
|
|
95
|
+
barrel_imports = re.findall(
|
|
96
|
+
r"import[^;\n]*from\s+['\"](?:@/|\.{1,2}/)[^'\"]*/index['\"]",
|
|
97
|
+
content,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if barrel_imports:
|
|
101
|
+
self.warnings.append({
|
|
102
|
+
'file': str(filepath.relative_to(self.project_path)),
|
|
103
|
+
'type': 'CRITICAL',
|
|
104
|
+
'issue': 'Potential barrel imports detected',
|
|
105
|
+
'fix': 'Import directly from specific files',
|
|
106
|
+
'section': '2-bundle-bundle-size-optimization.md'
|
|
107
|
+
})
|
|
108
|
+
except Exception as e:
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
def check_dynamic_imports(self):
|
|
112
|
+
"""Check if large components use dynamic imports (Section 2)"""
|
|
113
|
+
print("[*] Checking for missing dynamic imports...")
|
|
114
|
+
|
|
115
|
+
for filepath in self._iter_files(['ts', 'tsx']):
|
|
116
|
+
try:
|
|
117
|
+
content = filepath.read_text(encoding='utf-8')
|
|
118
|
+
|
|
119
|
+
# Check file size - if > 10KB, should probably use dynamic import
|
|
120
|
+
if len(content) > 10000:
|
|
121
|
+
# Check if it's imported statically somewhere
|
|
122
|
+
filename = filepath.stem
|
|
123
|
+
|
|
124
|
+
# Search for static imports of this component
|
|
125
|
+
for check_file in self._iter_files(['ts', 'tsx']):
|
|
126
|
+
if check_file == filepath:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
check_content = check_file.read_text(encoding='utf-8')
|
|
130
|
+
if f"import {filename}" in check_content or f"import {{ {filename}" in check_content:
|
|
131
|
+
if 'dynamic(' not in check_content:
|
|
132
|
+
self.warnings.append({
|
|
133
|
+
'file': str(check_file.relative_to(self.project_path)),
|
|
134
|
+
'type': 'CRITICAL',
|
|
135
|
+
'issue': f'Large component {filename} imported statically',
|
|
136
|
+
'fix': 'Use dynamic() for code splitting',
|
|
137
|
+
'section': '2-bundle-bundle-size-optimization.md'
|
|
138
|
+
})
|
|
139
|
+
break
|
|
140
|
+
except Exception as e:
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
def check_useEffect_fetching(self):
|
|
144
|
+
"""Check for data fetching in useEffect (Section 4)"""
|
|
145
|
+
print("[*] Checking for useEffect data fetching...")
|
|
146
|
+
|
|
147
|
+
for filepath in self._iter_files(['ts', 'tsx']):
|
|
148
|
+
try:
|
|
149
|
+
content = filepath.read_text(encoding='utf-8')
|
|
150
|
+
|
|
151
|
+
# Pattern: fetch or axios in useEffect
|
|
152
|
+
if 'useEffect' in content:
|
|
153
|
+
if re.search(r'useEffect.*?fetch\(', content, re.DOTALL):
|
|
154
|
+
self.warnings.append({
|
|
155
|
+
'file': str(filepath.relative_to(self.project_path)),
|
|
156
|
+
'type': 'MEDIUM-HIGH',
|
|
157
|
+
'issue': 'Data fetching in useEffect',
|
|
158
|
+
'fix': 'Consider using SWR or React Query for deduplication',
|
|
159
|
+
'section': '4-client-client-side-data-fetching.md'
|
|
160
|
+
})
|
|
161
|
+
except Exception as e:
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
def check_missing_memoization(self):
|
|
165
|
+
"""Check for missing React.memo, useMemo, useCallback (Section 5)"""
|
|
166
|
+
print("[*] Checking for missing memoization...")
|
|
167
|
+
|
|
168
|
+
for filepath in self._iter_files(['tsx']):
|
|
169
|
+
try:
|
|
170
|
+
content = filepath.read_text(encoding='utf-8')
|
|
171
|
+
|
|
172
|
+
# Check for component definitions without memo
|
|
173
|
+
components = re.findall(r'(?:export\s+)?(?:const|function)\s+([A-Z]\w+)', content)
|
|
174
|
+
|
|
175
|
+
if components and 'React.memo' not in content and 'memo(' not in content:
|
|
176
|
+
# Check if component receives props
|
|
177
|
+
if 'props:' in content or 'Props>' in content:
|
|
178
|
+
self.warnings.append({
|
|
179
|
+
'file': str(filepath.relative_to(self.project_path)),
|
|
180
|
+
'type': 'MEDIUM',
|
|
181
|
+
'issue': 'Component with props not memoized',
|
|
182
|
+
'fix': 'Consider using React.memo if props are stable',
|
|
183
|
+
'section': '5-rerender-re-render-optimization.md'
|
|
184
|
+
})
|
|
185
|
+
except Exception as e:
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
def check_image_optimization(self):
|
|
189
|
+
"""Check for unoptimized images (Section 6)"""
|
|
190
|
+
print("[*] Checking for image optimization...")
|
|
191
|
+
|
|
192
|
+
for filepath in self._iter_files(['ts', 'tsx', 'js', 'jsx']):
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
content = filepath.read_text(encoding='utf-8')
|
|
196
|
+
|
|
197
|
+
# Check for <img> tags instead of next/image
|
|
198
|
+
if '<img' in content and 'next/image' not in content:
|
|
199
|
+
self.warnings.append({
|
|
200
|
+
'file': str(filepath.relative_to(self.project_path)),
|
|
201
|
+
'type': 'MEDIUM',
|
|
202
|
+
'issue': 'Using <img> instead of next/image',
|
|
203
|
+
'fix': 'Use next/image for automatic optimization',
|
|
204
|
+
'section': '6-rendering-rendering-performance.md'
|
|
205
|
+
})
|
|
206
|
+
except Exception as e:
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
def generate_report(self):
|
|
210
|
+
"""Generate final report"""
|
|
211
|
+
print("\n" + "="*60)
|
|
212
|
+
print("REACT PERFORMANCE AUDIT REPORT")
|
|
213
|
+
print("="*60)
|
|
214
|
+
|
|
215
|
+
print(f"\n[CRITICAL ISSUES] ({len([i for i in self.issues if i['type'] == 'CRITICAL'])})")
|
|
216
|
+
for issue in self.issues:
|
|
217
|
+
if issue['type'] == 'CRITICAL':
|
|
218
|
+
print(f" - {issue['file']}")
|
|
219
|
+
print(f" Issue: {issue['issue']}")
|
|
220
|
+
print(f" Fix: {issue['fix']}")
|
|
221
|
+
print(f" Reference: {issue['section']}\n")
|
|
222
|
+
|
|
223
|
+
print(f"\n[WARNINGS] ({len(self.warnings)})")
|
|
224
|
+
for warning in self.warnings[:10]: # Show first 10
|
|
225
|
+
print(f" - {warning['file']}")
|
|
226
|
+
print(f" Issue: {warning['issue']}")
|
|
227
|
+
print(f" Fix: {warning['fix']}")
|
|
228
|
+
print(f" Reference: {warning['section']}\n")
|
|
229
|
+
|
|
230
|
+
if len(self.warnings) > 10:
|
|
231
|
+
print(f" ... and {len(self.warnings) - 10} more warnings")
|
|
232
|
+
|
|
233
|
+
print("\n" + "="*60)
|
|
234
|
+
print(f"SUMMARY:")
|
|
235
|
+
print(f" Critical Issues: {len([i for i in self.issues if i['type'] == 'CRITICAL'])}")
|
|
236
|
+
print(f" Warnings: {len(self.warnings)}")
|
|
237
|
+
print("="*60)
|
|
238
|
+
|
|
239
|
+
if len(self.issues) == 0 and len(self.warnings) == 0:
|
|
240
|
+
print("\n[SUCCESS] No major performance issues detected!")
|
|
241
|
+
else:
|
|
242
|
+
print("\n[ACTION REQUIRED] Review and fix issues above")
|
|
243
|
+
print("Priority: CRITICAL > HIGH > MEDIUM > LOW")
|
|
244
|
+
|
|
245
|
+
def run(self):
|
|
246
|
+
"""Run all checks"""
|
|
247
|
+
print("="*60)
|
|
248
|
+
print("React Performance Checker (Vercel Engineering)")
|
|
249
|
+
print("="*60)
|
|
250
|
+
print(f"Scanning: {self.project_path}")
|
|
251
|
+
|
|
252
|
+
self.check_waterfalls()
|
|
253
|
+
self.check_barrel_imports()
|
|
254
|
+
self.check_dynamic_imports()
|
|
255
|
+
self.check_useEffect_fetching()
|
|
256
|
+
self.check_missing_memoization()
|
|
257
|
+
self.check_image_optimization()
|
|
258
|
+
|
|
259
|
+
self.generate_report()
|
|
260
|
+
return len(self.issues) == 0
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def main():
|
|
264
|
+
import argparse
|
|
265
|
+
|
|
266
|
+
parser = argparse.ArgumentParser(description="Audit React/Next.js performance patterns")
|
|
267
|
+
parser.add_argument("project_path", nargs="?", default=".")
|
|
268
|
+
parser.add_argument("--fail-on-warnings", action="store_true", help="Treat advisory warnings as blocking")
|
|
269
|
+
args = parser.parse_args()
|
|
270
|
+
|
|
271
|
+
if not os.path.isdir(args.project_path):
|
|
272
|
+
parser.error(f"Path not found: {args.project_path}")
|
|
273
|
+
|
|
274
|
+
checker = PerformanceChecker(args.project_path)
|
|
275
|
+
critical_passed = checker.run()
|
|
276
|
+
passed = critical_passed and (not args.fail_on_warnings or not checker.warnings)
|
|
277
|
+
return 0 if passed else 1
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
if __name__ == '__main__':
|
|
281
|
+
raise SystemExit(main())
|