@nextsparkjs/ai-workflow 0.1.0-beta.100

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,383 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Extract Selectors Script
4
+
5
+ Scans React/TypeScript component files for data-cy attributes and generates
6
+ a report of selectors found. Useful for auditing selector coverage and
7
+ identifying missing selectors.
8
+
9
+ Usage:
10
+ python extract-selectors.py --component PATH [--output-format FORMAT]
11
+
12
+ Options:
13
+ --component PATH Component file or directory to scan
14
+ --output-format FORMAT Output format: json, md, or table (default: table)
15
+ --recursive Scan directories recursively
16
+ --report-missing Include analysis of potentially missing selectors
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ import re
22
+ import json
23
+ import argparse
24
+ from pathlib import Path
25
+ from typing import List, Dict, Set, Optional
26
+ from collections import defaultdict
27
+
28
+
29
+ # Patterns for finding data-cy attributes
30
+ DATA_CY_PATTERNS = [
31
+ r'data-cy=["\']([^"\']+)["\']', # data-cy="selector"
32
+ r'data-cy=\{[`"\']([^`"\']+)[`"\']\}', # data-cy={`selector`}
33
+ r'"data-cy":\s*["\']([^"\']+)["\']', # "data-cy": "selector"
34
+ r'data-cy=\{.*?\}', # Dynamic selectors (will flag for review)
35
+ ]
36
+
37
+ # Patterns for potentially interactive elements missing selectors
38
+ INTERACTIVE_PATTERNS = [
39
+ (r'<button\b(?![^>]*data-cy)', 'button'),
40
+ (r'<input\b(?![^>]*data-cy)', 'input'),
41
+ (r'<select\b(?![^>]*data-cy)', 'select'),
42
+ (r'<textarea\b(?![^>]*data-cy)', 'textarea'),
43
+ (r'<form\b(?![^>]*data-cy)', 'form'),
44
+ (r'onClick=\{(?![^}]*data-cy)', 'onClick handler'),
45
+ (r'onSubmit=\{(?![^}]*data-cy)', 'onSubmit handler'),
46
+ (r'<Link\b(?![^>]*data-cy)', 'Link component'),
47
+ (r'<a\b(?![^>]*data-cy)(?=[^>]*href)', 'anchor with href'),
48
+ ]
49
+
50
+
51
+ def extract_selectors(content: str) -> List[Dict]:
52
+ """Extract all data-cy selectors from file content."""
53
+ selectors = []
54
+ lines = content.split('\n')
55
+
56
+ for line_num, line in enumerate(lines, 1):
57
+ for pattern in DATA_CY_PATTERNS[:3]: # Skip dynamic pattern for extraction
58
+ matches = re.findall(pattern, line)
59
+ for match in matches:
60
+ selectors.append({
61
+ 'selector': match,
62
+ 'line': line_num,
63
+ 'context': line.strip()[:100],
64
+ 'is_dynamic': False
65
+ })
66
+
67
+ # Check for dynamic selectors
68
+ if re.search(DATA_CY_PATTERNS[3], line):
69
+ if not any(re.search(p, line) for p in DATA_CY_PATTERNS[:3]):
70
+ selectors.append({
71
+ 'selector': '(dynamic)',
72
+ 'line': line_num,
73
+ 'context': line.strip()[:100],
74
+ 'is_dynamic': True
75
+ })
76
+
77
+ return selectors
78
+
79
+
80
+ def find_missing_selectors(content: str) -> List[Dict]:
81
+ """Find potentially interactive elements missing data-cy selectors."""
82
+ missing = []
83
+ lines = content.split('\n')
84
+
85
+ for line_num, line in enumerate(lines, 1):
86
+ for pattern, element_type in INTERACTIVE_PATTERNS:
87
+ if re.search(pattern, line, re.IGNORECASE):
88
+ # Skip if line has data-cy elsewhere (might be on parent)
89
+ if 'data-cy' not in line:
90
+ missing.append({
91
+ 'element': element_type,
92
+ 'line': line_num,
93
+ 'context': line.strip()[:100]
94
+ })
95
+
96
+ return missing
97
+
98
+
99
+ def analyze_selector_patterns(selectors: List[Dict]) -> Dict:
100
+ """Analyze selector naming patterns."""
101
+ patterns = defaultdict(int)
102
+ prefixes = defaultdict(int)
103
+
104
+ for sel in selectors:
105
+ if sel['is_dynamic']:
106
+ patterns['dynamic'] += 1
107
+ continue
108
+
109
+ selector = sel['selector']
110
+
111
+ # Count prefixes
112
+ parts = selector.split('.')
113
+ if len(parts) > 1:
114
+ prefixes[parts[0]] += 1
115
+
116
+ # Count patterns
117
+ if '-' in selector:
118
+ patterns['kebab-case'] += 1
119
+ if '.' in selector:
120
+ patterns['dot-notation'] += 1
121
+ if selector.startswith(('btn-', 'button-')):
122
+ patterns['button-prefix'] += 1
123
+ if selector.startswith(('form-', 'input-')):
124
+ patterns['form-prefix'] += 1
125
+
126
+ return {
127
+ 'patterns': dict(patterns),
128
+ 'prefixes': dict(prefixes)
129
+ }
130
+
131
+
132
+ def scan_file(file_path: Path, report_missing: bool = False) -> Dict:
133
+ """Scan a single file for selectors."""
134
+ try:
135
+ content = file_path.read_text(encoding='utf-8')
136
+ except Exception as e:
137
+ return {'error': str(e), 'file': str(file_path)}
138
+
139
+ selectors = extract_selectors(content)
140
+ result = {
141
+ 'file': str(file_path),
142
+ 'selectors': selectors,
143
+ 'count': len(selectors),
144
+ 'dynamic_count': sum(1 for s in selectors if s['is_dynamic'])
145
+ }
146
+
147
+ if report_missing:
148
+ missing = find_missing_selectors(content)
149
+ result['missing'] = missing
150
+ result['missing_count'] = len(missing)
151
+
152
+ return result
153
+
154
+
155
+ def scan_directory(dir_path: Path, recursive: bool = True, report_missing: bool = False) -> List[Dict]:
156
+ """Scan a directory for selectors in all TypeScript/React files."""
157
+ results = []
158
+ pattern = '**/*.tsx' if recursive else '*.tsx'
159
+
160
+ for file_path in dir_path.glob(pattern):
161
+ result = scan_file(file_path, report_missing)
162
+ if result.get('count', 0) > 0 or result.get('missing_count', 0) > 0:
163
+ results.append(result)
164
+
165
+ # Also scan .ts files for utility functions
166
+ ts_pattern = '**/*.ts' if recursive else '*.ts'
167
+ for file_path in dir_path.glob(ts_pattern):
168
+ if not file_path.name.endswith('.d.ts'):
169
+ result = scan_file(file_path, report_missing)
170
+ if result.get('count', 0) > 0:
171
+ results.append(result)
172
+
173
+ return results
174
+
175
+
176
+ def format_table(results: List[Dict]) -> str:
177
+ """Format results as ASCII table."""
178
+ lines = []
179
+ lines.append("=" * 80)
180
+ lines.append("SELECTOR EXTRACTION REPORT")
181
+ lines.append("=" * 80)
182
+
183
+ total_selectors = 0
184
+ total_dynamic = 0
185
+ total_missing = 0
186
+ all_selectors = []
187
+
188
+ for result in results:
189
+ if 'error' in result:
190
+ lines.append(f"\nERROR: {result['file']}: {result['error']}")
191
+ continue
192
+
193
+ lines.append(f"\n{'-' * 80}")
194
+ lines.append(f"FILE: {result['file']}")
195
+ lines.append(f"Selectors: {result['count']} | Dynamic: {result['dynamic_count']}")
196
+
197
+ total_selectors += result['count']
198
+ total_dynamic += result['dynamic_count']
199
+
200
+ if result['selectors']:
201
+ lines.append("\n SELECTORS FOUND:")
202
+ for sel in result['selectors']:
203
+ marker = "[DYN]" if sel['is_dynamic'] else " "
204
+ lines.append(f" {marker} Line {sel['line']:4d}: {sel['selector']}")
205
+ all_selectors.append(sel['selector'])
206
+
207
+ if result.get('missing'):
208
+ total_missing += result['missing_count']
209
+ lines.append(f"\n POTENTIALLY MISSING ({result['missing_count']}):")
210
+ for m in result['missing'][:10]: # Limit to 10 per file
211
+ lines.append(f" Line {m['line']:4d}: {m['element']} - {m['context'][:60]}...")
212
+ if result['missing_count'] > 10:
213
+ lines.append(f" ... and {result['missing_count'] - 10} more")
214
+
215
+ # Summary
216
+ lines.append("\n" + "=" * 80)
217
+ lines.append("SUMMARY")
218
+ lines.append("=" * 80)
219
+ lines.append(f"Files scanned: {len(results)}")
220
+ lines.append(f"Total selectors: {total_selectors}")
221
+ lines.append(f"Dynamic selectors: {total_dynamic}")
222
+ if total_missing > 0:
223
+ lines.append(f"Potentially missing: {total_missing}")
224
+
225
+ # Unique selectors
226
+ unique = set(s for s in all_selectors if s != '(dynamic)')
227
+ lines.append(f"Unique selectors: {len(unique)}")
228
+
229
+ # Pattern analysis
230
+ if all_selectors:
231
+ analysis = analyze_selector_patterns([{'selector': s, 'is_dynamic': s == '(dynamic)'} for s in all_selectors])
232
+ if analysis['prefixes']:
233
+ lines.append("\nSelector prefixes:")
234
+ for prefix, count in sorted(analysis['prefixes'].items(), key=lambda x: -x[1])[:5]:
235
+ lines.append(f" {prefix}: {count}")
236
+
237
+ lines.append("=" * 80)
238
+
239
+ return '\n'.join(lines)
240
+
241
+
242
+ def format_json(results: List[Dict]) -> str:
243
+ """Format results as JSON."""
244
+ output = {
245
+ 'results': results,
246
+ 'summary': {
247
+ 'files_scanned': len(results),
248
+ 'total_selectors': sum(r.get('count', 0) for r in results),
249
+ 'dynamic_selectors': sum(r.get('dynamic_count', 0) for r in results),
250
+ 'missing_selectors': sum(r.get('missing_count', 0) for r in results),
251
+ }
252
+ }
253
+
254
+ # Collect all unique selectors
255
+ all_selectors = []
256
+ for r in results:
257
+ for s in r.get('selectors', []):
258
+ if not s['is_dynamic']:
259
+ all_selectors.append(s['selector'])
260
+
261
+ output['summary']['unique_selectors'] = list(set(all_selectors))
262
+
263
+ return json.dumps(output, indent=2)
264
+
265
+
266
+ def format_markdown(results: List[Dict]) -> str:
267
+ """Format results as Markdown."""
268
+ lines = []
269
+ lines.append("# Selector Extraction Report\n")
270
+
271
+ # Summary table
272
+ total_selectors = sum(r.get('count', 0) for r in results)
273
+ total_dynamic = sum(r.get('dynamic_count', 0) for r in results)
274
+ total_missing = sum(r.get('missing_count', 0) for r in results)
275
+
276
+ lines.append("## Summary\n")
277
+ lines.append("| Metric | Count |")
278
+ lines.append("|--------|-------|")
279
+ lines.append(f"| Files scanned | {len(results)} |")
280
+ lines.append(f"| Total selectors | {total_selectors} |")
281
+ lines.append(f"| Dynamic selectors | {total_dynamic} |")
282
+ if total_missing > 0:
283
+ lines.append(f"| Potentially missing | {total_missing} |")
284
+ lines.append("")
285
+
286
+ # Unique selectors list
287
+ all_selectors = set()
288
+ for r in results:
289
+ for s in r.get('selectors', []):
290
+ if not s['is_dynamic']:
291
+ all_selectors.add(s['selector'])
292
+
293
+ if all_selectors:
294
+ lines.append("## Unique Selectors\n")
295
+ lines.append("```")
296
+ for sel in sorted(all_selectors):
297
+ lines.append(sel)
298
+ lines.append("```\n")
299
+
300
+ # Per-file details
301
+ lines.append("## File Details\n")
302
+ for result in results:
303
+ if 'error' in result:
304
+ lines.append(f"### {result['file']}\n")
305
+ lines.append(f"**Error:** {result['error']}\n")
306
+ continue
307
+
308
+ lines.append(f"### {result['file']}\n")
309
+ lines.append(f"- **Selectors:** {result['count']}")
310
+ lines.append(f"- **Dynamic:** {result['dynamic_count']}")
311
+ if result.get('missing_count'):
312
+ lines.append(f"- **Missing:** {result['missing_count']}")
313
+ lines.append("")
314
+
315
+ if result['selectors']:
316
+ lines.append("| Line | Selector | Dynamic |")
317
+ lines.append("|------|----------|---------|")
318
+ for sel in result['selectors']:
319
+ dyn = "Yes" if sel['is_dynamic'] else "No"
320
+ lines.append(f"| {sel['line']} | `{sel['selector']}` | {dyn} |")
321
+ lines.append("")
322
+
323
+ return '\n'.join(lines)
324
+
325
+
326
+ def main():
327
+ parser = argparse.ArgumentParser(description='Extract data-cy selectors from components')
328
+ parser.add_argument('--component', required=True, help='Component file or directory path')
329
+ parser.add_argument('--output-format', default='table', choices=['json', 'md', 'table'],
330
+ help='Output format (default: table)')
331
+ parser.add_argument('--recursive', action='store_true', default=True,
332
+ help='Scan directories recursively')
333
+ parser.add_argument('--report-missing', action='store_true',
334
+ help='Report potentially missing selectors')
335
+ parser.add_argument('--output', default=None, help='Output file path')
336
+
337
+ args = parser.parse_args()
338
+
339
+ path = Path(args.component)
340
+
341
+ if not path.exists():
342
+ print(f"ERROR: Path does not exist: {path}")
343
+ return 1
344
+
345
+ print(f"\n{'=' * 60}")
346
+ print("EXTRACTING SELECTORS")
347
+ print(f"{'=' * 60}")
348
+ print(f"Path: {path}")
349
+ print(f"Format: {args.output_format}")
350
+ print(f"Report missing: {args.report_missing}")
351
+ print(f"{'=' * 60}\n")
352
+
353
+ # Scan files
354
+ if path.is_file():
355
+ results = [scan_file(path, args.report_missing)]
356
+ else:
357
+ results = scan_directory(path, args.recursive, args.report_missing)
358
+
359
+ if not results:
360
+ print("No files found with selectors.")
361
+ return 0
362
+
363
+ # Format output
364
+ if args.output_format == 'json':
365
+ output = format_json(results)
366
+ elif args.output_format == 'md':
367
+ output = format_markdown(results)
368
+ else:
369
+ output = format_table(results)
370
+
371
+ # Write or print
372
+ if args.output:
373
+ output_path = Path(args.output)
374
+ output_path.write_text(output, encoding='utf-8')
375
+ print(f"Report written to: {output_path}")
376
+ else:
377
+ print(output)
378
+
379
+ return 0
380
+
381
+
382
+ if __name__ == '__main__':
383
+ sys.exit(main())