agy-superpowers 5.1.2 → 5.1.4
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/LICENSE +1 -1
- package/README.md +198 -175
- package/package.json +1 -1
- package/template/agent/.shared/mobile-uiux-promax/data/accessibility.csv +25 -0
- package/template/agent/.shared/mobile-uiux-promax/data/animation.csv +22 -0
- package/template/agent/.shared/mobile-uiux-promax/data/components.csv +21 -0
- package/template/agent/.shared/mobile-uiux-promax/data/gestures.csv +26 -0
- package/template/agent/.shared/mobile-uiux-promax/data/layout.csv +21 -0
- package/template/agent/.shared/mobile-uiux-promax/data/navigation.csv +27 -0
- package/template/agent/.shared/mobile-uiux-promax/data/onboarding.csv +17 -0
- package/template/agent/.shared/mobile-uiux-promax/data/platform.csv +22 -0
- package/template/agent/.shared/mobile-uiux-promax/data/stacks/flutter.csv +19 -0
- package/template/agent/.shared/mobile-uiux-promax/data/stacks/jetpack-compose.csv +18 -0
- package/template/agent/.shared/mobile-uiux-promax/data/stacks/react-native.csv +20 -0
- package/template/agent/.shared/mobile-uiux-promax/data/stacks/swiftui.csv +18 -0
- package/template/agent/.shared/mobile-uiux-promax/data/ux-laws.csv +16 -0
- package/template/agent/.shared/mobile-uiux-promax/scripts/mobile-search.py +157 -0
- package/template/agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
- package/template/agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
- package/template/agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
- package/template/agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
- package/template/agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/template/agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/template/agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
- package/template/agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
- package/template/agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/template/agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
- package/template/agent/.shared/ui-ux-pro-max/scripts/core.py +236 -0
- package/template/agent/.shared/ui-ux-pro-max/scripts/search.py +61 -0
- package/template/agent/.tests/TESTS.md +119 -0
- package/template/agent/.tests/mobile-uiux-promax/test_search.py +266 -0
- package/template/agent/.tests/run_tests.py +86 -0
- package/template/agent/patches/skills-patches.md +24 -0
- package/template/agent/rules/git-policy.md +25 -0
- package/template/agent/skills/brainstorming/SKILL.md +57 -0
- package/template/agent/skills/finishing-a-development-branch/SKILL.md +18 -6
- package/template/agent/skills/frontend-design/SKILL.md +147 -0
- package/template/agent/skills/frontend-design/reference/color-and-contrast.md +117 -0
- package/template/agent/skills/frontend-design/reference/interaction-design.md +159 -0
- package/template/agent/skills/frontend-design/reference/motion-design.md +150 -0
- package/template/agent/skills/frontend-design/reference/responsive-design.md +161 -0
- package/template/agent/skills/frontend-design/reference/spatial-design.md +122 -0
- package/template/agent/skills/frontend-design/reference/typography.md +124 -0
- package/template/agent/skills/frontend-design/reference/ux-writing.md +127 -0
- package/template/agent/skills/mobile-uiux-promax/SKILL.md +139 -0
- package/template/agent/skills/using-git-worktrees/SKILL.md +3 -1
- package/template/agent/skills/verification-before-completion/SKILL.md +11 -0
- package/template/agent/workflows/mobile-uiux-promax.md +137 -0
- package/template/agent/workflows/ui-ux-pro-max.md +231 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Comprehensive test suite for mobile-search.py
|
|
4
|
+
Tests all 9 domains, 4 stacks, edge cases, and data integrity.
|
|
5
|
+
"""
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import csv
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
# Project root: 3 levels up (.agent/.tests/<skill>/test_*.py → project root)
|
|
12
|
+
BASE = str(Path(__file__).resolve().parents[3])
|
|
13
|
+
SCRIPT = f"{BASE}/.agent/.shared/mobile-uiux-promax/scripts/mobile-search.py"
|
|
14
|
+
DATA_DIR = f"{BASE}/.agent/.shared/mobile-uiux-promax/data"
|
|
15
|
+
|
|
16
|
+
passed = []
|
|
17
|
+
failed = []
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run(query, flag, target, n=3):
|
|
21
|
+
return subprocess.run(
|
|
22
|
+
["python3", SCRIPT, query, flag, target, "-n", str(n)],
|
|
23
|
+
capture_output=True, text=True, cwd=BASE
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def check(label, output, expected_kw, returncode=0):
|
|
28
|
+
ok_code = (output.returncode == returncode)
|
|
29
|
+
all_output = output.stdout + output.stderr
|
|
30
|
+
ok_kw = expected_kw.lower() in all_output.lower() if expected_kw else True
|
|
31
|
+
ok = ok_code and ok_kw
|
|
32
|
+
if ok:
|
|
33
|
+
passed.append(label)
|
|
34
|
+
print(f" ✅ {label}")
|
|
35
|
+
else:
|
|
36
|
+
failed.append(label)
|
|
37
|
+
reasons = []
|
|
38
|
+
if not ok_code:
|
|
39
|
+
reasons.append(f"returncode={output.returncode} (expected {returncode})")
|
|
40
|
+
if not ok_kw:
|
|
41
|
+
reasons.append(f"keyword '{expected_kw}' not found")
|
|
42
|
+
print(f" ❌ {label}")
|
|
43
|
+
for r in reasons:
|
|
44
|
+
print(f" → {r}")
|
|
45
|
+
print(f" → stdout[:300]: {output.stdout[:300]!r}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ═══════════════════════════════════════════════════════════
|
|
49
|
+
# GROUP 1: Navigation domain
|
|
50
|
+
# ═══════════════════════════════════════════════════════════
|
|
51
|
+
print("\n📍 GROUP 1: Navigation domain")
|
|
52
|
+
check("tab bar primary sections", run("bottom tab bar primary sections", "--domain", "navigation"), "Tab Bar")
|
|
53
|
+
check("hamburger discoverability", run("hamburger hidden navigation discoverability", "--domain", "navigation"), "discoverability")
|
|
54
|
+
check("thumb zone ergonomics", run("thumb zone one handed ergonomics bottom", "--domain", "navigation"), "Thumb")
|
|
55
|
+
check("drawer sidebar menu", run("drawer sidebar menu hamburger secondary", "--domain", "navigation"), "Drawer")
|
|
56
|
+
check("modal full screen overlay", run("modal full screen immersive camera auth", "--domain", "navigation"), "Modal")
|
|
57
|
+
check("bottom sheet partial", run("bottom sheet action partial overlay", "--domain", "navigation"), "Bottom Sheet")
|
|
58
|
+
check("deep link universal", run("deep link universal link routing url", "--domain", "navigation"), "Deep Link")
|
|
59
|
+
check("back navigation ios swipe", run("back navigation ios swipe gesture return", "--domain", "navigation"), "Back")
|
|
60
|
+
|
|
61
|
+
# ═══════════════════════════════════════════════════════════
|
|
62
|
+
# GROUP 2: Gestures domain
|
|
63
|
+
# ═══════════════════════════════════════════════════════════
|
|
64
|
+
print("\n📍 GROUP 2: Gestures domain")
|
|
65
|
+
check("swipe back ios edge", run("swipe back dismiss ios edge left", "--domain", "gestures"), "Swipe")
|
|
66
|
+
check("pinch zoom two finger", run("pinch zoom scale two finger gesture", "--domain", "gestures"), "Pinch")
|
|
67
|
+
check("long press context menu", run("long press hold context menu action", "--domain", "gestures"), "Long")
|
|
68
|
+
check("pull to refresh scroll", run("pull to refresh scroll top reload", "--domain", "gestures"), "Pull")
|
|
69
|
+
check("double tap zoom toggle", run("double tap zoom toggle like", "--domain", "gestures"), "Double")
|
|
70
|
+
|
|
71
|
+
# ═══════════════════════════════════════════════════════════
|
|
72
|
+
# GROUP 3: Components domain
|
|
73
|
+
# ═══════════════════════════════════════════════════════════
|
|
74
|
+
print("\n📍 GROUP 3: Components domain")
|
|
75
|
+
check("bottom sheet component", run("bottom sheet modal partial screen", "--domain", "components"), "Bottom Sheet")
|
|
76
|
+
check("FAB floating action button", run("floating action button primary create", "--domain", "components"), "FAB")
|
|
77
|
+
check("skeleton loading shimmer", run("skeleton loading placeholder shimmer", "--domain", "components"), "Skeleton")
|
|
78
|
+
check("snackbar toast feedback", run("snackbar toast feedback notification", "--domain", "components"), "Snackbar")
|
|
79
|
+
check("chip filter tag", run("chip filter tag selection category", "--domain", "components"), "Chip")
|
|
80
|
+
|
|
81
|
+
# ═══════════════════════════════════════════════════════════
|
|
82
|
+
# GROUP 4: Layout domain
|
|
83
|
+
# ═══════════════════════════════════════════════════════════
|
|
84
|
+
print("\n📍 GROUP 4: Layout domain")
|
|
85
|
+
check("safe area notch inset", run("safe area notch inset dynamic island", "--domain", "layout"), "Safe Area")
|
|
86
|
+
check("touch target 44pt 48dp", run("touch target minimum size 44pt 48dp", "--domain", "layout"), "44")
|
|
87
|
+
check("thumb zone bottom reach", run("thumb zone reachability bottom reach", "--domain", "layout"), "Thumb")
|
|
88
|
+
check("keyboard avoidance input", run("keyboard avoidance input scroll TextField", "--domain", "layout"), "Keyboard")
|
|
89
|
+
check("spacing design system 8pt", run("spacing 8pt grid system padding margin", "--domain", "layout"), "8")
|
|
90
|
+
|
|
91
|
+
# ═══════════════════════════════════════════════════════════
|
|
92
|
+
# GROUP 5: Platform domain
|
|
93
|
+
# ═══════════════════════════════════════════════════════════
|
|
94
|
+
print("\n📍 GROUP 5: Platform domain")
|
|
95
|
+
check("dark mode system appearance", run("dark mode night system appearance adaptive", "--domain", "platform"), "Dark Mode")
|
|
96
|
+
check("haptic feedback vibration", run("haptic feedback vibration taptic engine", "--domain", "platform"), "Haptic")
|
|
97
|
+
check("typography font system", run("typography font ios android system native", "--domain", "platform"), "Typography")
|
|
98
|
+
check("navigation ios android diff", run("navigation back button ios android differ", "--domain", "platform"), "Navigation")
|
|
99
|
+
check("status bar appearance", run("status bar color tint appearance", "--domain", "platform"), "Status Bar")
|
|
100
|
+
|
|
101
|
+
# ═══════════════════════════════════════════════════════════
|
|
102
|
+
# GROUP 6: Onboarding domain
|
|
103
|
+
# ═══════════════════════════════════════════════════════════
|
|
104
|
+
print("\n📍 GROUP 6: Onboarding domain")
|
|
105
|
+
check("permission priming camera", run("permission priming rationale camera notification", "--domain", "onboarding"), "Permission")
|
|
106
|
+
check("paywall timing subscription", run("paywall timing subscription trial premium", "--domain", "onboarding"), "Paywall")
|
|
107
|
+
check("sign in apple social", run("sign in with apple social login auth", "--domain", "onboarding"), "Sign in with Apple")
|
|
108
|
+
check("value proposition screen", run("value proposition benefit feature showcase", "--domain", "onboarding"), "Value")
|
|
109
|
+
check("progress step indicator", run("onboarding step indicator progress multi", "--domain", "onboarding"), "Step")
|
|
110
|
+
|
|
111
|
+
# ═══════════════════════════════════════════════════════════
|
|
112
|
+
# GROUP 7: Animation domain
|
|
113
|
+
# ═══════════════════════════════════════════════════════════
|
|
114
|
+
print("\n📍 GROUP 7: Animation domain")
|
|
115
|
+
check("spring animation modal", run("spring animation modal sheet natural", "--domain", "animation"), "Spring")
|
|
116
|
+
check("skeleton shimmer loading", run("skeleton shimmer loading placeholder animation", "--domain", "animation"), "Shimmer")
|
|
117
|
+
check("reduce motion accessibility", run("reduce motion accessibility preference disable", "--domain", "animation"), "Reduce")
|
|
118
|
+
check("page transition navigation", run("page transition navigation push slide", "--domain", "animation"), "Transition")
|
|
119
|
+
check("haptic sync animation", run("haptic feedback sync animation success", "--domain", "animation"), "Haptic")
|
|
120
|
+
|
|
121
|
+
# ═══════════════════════════════════════════════════════════
|
|
122
|
+
# GROUP 8: Accessibility domain
|
|
123
|
+
# ═══════════════════════════════════════════════════════════
|
|
124
|
+
print("\n📍 GROUP 8: Accessibility domain")
|
|
125
|
+
check("touch target 44pt minimum", run("touch target size minimum 44pt", "--domain", "accessibility"), "Touch Target")
|
|
126
|
+
check("screen reader label name", run("screen reader voiceover label accessible name", "--domain", "accessibility"), "Screen Reader")
|
|
127
|
+
check("view-tap asymmetry small", run("view tap asymmetry visible untappable small dot", "--domain", "accessibility"), "Asymmetry")
|
|
128
|
+
check("color contrast ratio 4.5:1", run("color contrast ratio 4.5 text background", "--domain", "accessibility"), "Contrast")
|
|
129
|
+
check("dynamic type font scaling", run("dynamic type font scaling text size large", "--domain", "accessibility"), "Dynamic Type")
|
|
130
|
+
check("NNGroup 1cm physical touch", run("NNGroup physical 1cm touch target research", "--domain", "accessibility"), "NNGroup")
|
|
131
|
+
check("coach mark single tip", run("coach mark single tip one at a time overlay", "--domain", "accessibility"), "Coach Mark")
|
|
132
|
+
|
|
133
|
+
# ═══════════════════════════════════════════════════════════
|
|
134
|
+
# GROUP 9: UX Laws domain
|
|
135
|
+
# ═══════════════════════════════════════════════════════════
|
|
136
|
+
print("\n📍 GROUP 9: UX Laws domain")
|
|
137
|
+
check("Fitts touch target size", run("touch target size placement distance primary", "--domain", "ux-laws"), "Fitts")
|
|
138
|
+
check("Hick choices decision time", run("choices menu options decision time complexity", "--domain", "ux-laws"), "Hick")
|
|
139
|
+
check("Jakob mental model familiar", run("mental model convention familiar platform", "--domain", "ux-laws"), "Jakob")
|
|
140
|
+
check("Goal-Gradient progress bar", run("progress steps completion motivation reward", "--domain", "ux-laws"), "Goal-Gradient")
|
|
141
|
+
check("Peak-End memorable delight", run("memorable experience delight success moment", "--domain", "ux-laws"), "Peak")
|
|
142
|
+
check("Doherty 400ms threshold", run("400ms response time loading feedback threshold", "--domain", "ux-laws"), "Doherty")
|
|
143
|
+
check("Miller 7 items memory", run("7 chunks working memory cognitive load limit", "--domain", "ux-laws"), "Miller")
|
|
144
|
+
check("Zeigarnik streak incomplete", run("streak incomplete task engagement retention", "--domain", "ux-laws"), "Zeigarnik")
|
|
145
|
+
check("Von Restorff standout color", run("standout highlight color accent primary call", "--domain", "ux-laws"), "Von Restorff")
|
|
146
|
+
check("Serial Position first last", run("first last item serial position memory recall", "--domain", "ux-laws"), "Serial Position")
|
|
147
|
+
check("Proximity grouping related", run("proximity grouping related elements spacing", "--domain", "ux-laws"), "Proximity")
|
|
148
|
+
check("Common Region card container",run("card container region visual grouping border", "--domain", "ux-laws"), "Common Region")
|
|
149
|
+
check("Paradox coach mark tutorial", run("tutorial coach mark skip learn by doing", "--domain", "ux-laws"), "Paradox")
|
|
150
|
+
|
|
151
|
+
# ═══════════════════════════════════════════════════════════
|
|
152
|
+
# GROUP 10: Stack — React Native
|
|
153
|
+
# ═══════════════════════════════════════════════════════════
|
|
154
|
+
print("\n📍 GROUP 10: Stack — React Native")
|
|
155
|
+
check("FlatList performance", run("FlatList list performance rendering key", "--stack", "react-native"), "FlatList")
|
|
156
|
+
check("Reanimated animations", run("Reanimated animation gesture performant", "--stack", "react-native"), "Reanimated")
|
|
157
|
+
check("accessibilityLabel VoiceOver",run("accessibilityLabel accessible name VoiceOver", "--stack", "react-native"), "accessibilityLabel")
|
|
158
|
+
check("Metro bundler fast refresh", run("Metro bundler fast refresh hot reload", "--stack", "react-native"), "Metro")
|
|
159
|
+
|
|
160
|
+
# ═══════════════════════════════════════════════════════════
|
|
161
|
+
# GROUP 11: Stack — Flutter
|
|
162
|
+
# ═══════════════════════════════════════════════════════════
|
|
163
|
+
print("\n📍 GROUP 11: Stack — Flutter")
|
|
164
|
+
check("GoRouter navigation", run("GoRouter routing navigation deep link", "--stack", "flutter"), "GoRouter")
|
|
165
|
+
check("ListView.builder lazy", run("ListView builder lazy performance list", "--stack", "flutter"), "ListView")
|
|
166
|
+
check("Riverpod state management", run("Riverpod state management provider", "--stack", "flutter"), "Riverpod")
|
|
167
|
+
check("MediaQuery safe area", run("MediaQuery safe area padding inset", "--stack", "flutter"), "MediaQuery")
|
|
168
|
+
|
|
169
|
+
# ═══════════════════════════════════════════════════════════
|
|
170
|
+
# GROUP 12: Stack — SwiftUI
|
|
171
|
+
# ═══════════════════════════════════════════════════════════
|
|
172
|
+
print("\n📍 GROUP 12: Stack — SwiftUI")
|
|
173
|
+
check("NavigationStack push view", run("NavigationStack navigation push screen iOS", "--stack", "swiftui"), "NavigationStack")
|
|
174
|
+
check("@StateObject ViewModel", run("StateObject ViewModel lifecycle init", "--stack", "swiftui"), "StateObject")
|
|
175
|
+
check("reduce motion isReduceMotion",run("reduce motion isReduceMotionEnabled animation", "--stack", "swiftui"), "reduceMotion")
|
|
176
|
+
check("task modifier async load", run("task async await data loading onAppear", "--stack", "swiftui"), "task")
|
|
177
|
+
|
|
178
|
+
# ═══════════════════════════════════════════════════════════
|
|
179
|
+
# GROUP 13: Stack — Jetpack Compose
|
|
180
|
+
# ═══════════════════════════════════════════════════════════
|
|
181
|
+
print("\n📍 GROUP 13: Stack — Jetpack Compose")
|
|
182
|
+
check("LazyColumn list performance", run("LazyColumn list performance lazy scroll", "--stack", "jetpack-compose"), "LazyColumn")
|
|
183
|
+
check("sealed UiState data class", run("sealed class UiState loading error success", "--stack", "jetpack-compose"), "sealed")
|
|
184
|
+
check("WindowInsets edge-to-edge", run("WindowInsets edge to edge insets system bar", "--stack", "jetpack-compose"), "WindowInsets")
|
|
185
|
+
check("remember derivedStateOf", run("remember derivedStateOf state performance recomposition", "--stack", "jetpack-compose"), "derivedStateOf")
|
|
186
|
+
|
|
187
|
+
# ═══════════════════════════════════════════════════════════
|
|
188
|
+
# GROUP 14: Edge Cases
|
|
189
|
+
# ═══════════════════════════════════════════════════════════
|
|
190
|
+
print("\n📍 GROUP 14: Edge Cases")
|
|
191
|
+
|
|
192
|
+
# -n flag returns correct count
|
|
193
|
+
r = run("navigation tab bottom bar", "--domain", "navigation", n=5)
|
|
194
|
+
count = r.stdout.count("### Result")
|
|
195
|
+
ok = count == 5
|
|
196
|
+
(passed if ok else failed).append("navigation: -n 5 returns 5 results")
|
|
197
|
+
print(f" {'✅' if ok else '❌'} -n 5 flag returns 5 results (got {count})")
|
|
198
|
+
|
|
199
|
+
# invalid domain → non-zero exit
|
|
200
|
+
r_bad = subprocess.run(["python3", SCRIPT, "test", "--domain", "foobar"],
|
|
201
|
+
capture_output=True, text=True, cwd=BASE)
|
|
202
|
+
check("invalid domain → error exit", r_bad, None, returncode=2)
|
|
203
|
+
|
|
204
|
+
# invalid stack → non-zero exit
|
|
205
|
+
r_bad2 = subprocess.run(["python3", SCRIPT, "test", "--stack", "xamarin"],
|
|
206
|
+
capture_output=True, text=True, cwd=BASE)
|
|
207
|
+
check("invalid stack → error exit", r_bad2, None, returncode=2)
|
|
208
|
+
|
|
209
|
+
# no flag → non-zero exit
|
|
210
|
+
r_bad3 = subprocess.run(["python3", SCRIPT, "hello"],
|
|
211
|
+
capture_output=True, text=True, cwd=BASE)
|
|
212
|
+
check("no --domain/--stack → error", r_bad3, None, returncode=2)
|
|
213
|
+
|
|
214
|
+
# zero results for nonsense query → "No results" message
|
|
215
|
+
r_zero = run("xyzxyzxyz_gibberish_asdfqwer", "--domain", "navigation")
|
|
216
|
+
check("gibberish query → no crash", r_zero, "results", returncode=0)
|
|
217
|
+
|
|
218
|
+
# ═══════════════════════════════════════════════════════════
|
|
219
|
+
# GROUP 15: Data Integrity
|
|
220
|
+
# ═══════════════════════════════════════════════════════════
|
|
221
|
+
print("\n📍 GROUP 15: Data Integrity (row counts)")
|
|
222
|
+
|
|
223
|
+
expected_min_rows = {
|
|
224
|
+
"navigation.csv": 20,
|
|
225
|
+
"gestures.csv": 20,
|
|
226
|
+
"components.csv": 15,
|
|
227
|
+
"layout.csv": 15,
|
|
228
|
+
"platform.csv": 15,
|
|
229
|
+
"onboarding.csv": 12,
|
|
230
|
+
"animation.csv": 15,
|
|
231
|
+
"accessibility.csv": 20,
|
|
232
|
+
"ux-laws.csv": 12,
|
|
233
|
+
"stacks/react-native.csv": 15,
|
|
234
|
+
"stacks/flutter.csv": 15,
|
|
235
|
+
"stacks/swiftui.csv": 14,
|
|
236
|
+
"stacks/jetpack-compose.csv": 14,
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for fname, min_rows in expected_min_rows.items():
|
|
240
|
+
fpath = Path(DATA_DIR) / fname
|
|
241
|
+
if not fpath.exists():
|
|
242
|
+
failed.append(f"file exists: {fname}")
|
|
243
|
+
print(f" ❌ file exists: {fname} → FILE MISSING")
|
|
244
|
+
continue
|
|
245
|
+
with open(fpath, encoding="utf-8") as f:
|
|
246
|
+
rows = list(csv.reader(f))
|
|
247
|
+
data_rows = len(rows) - 1 # minus header
|
|
248
|
+
ok = data_rows >= min_rows
|
|
249
|
+
(passed if ok else failed).append(f"row count: {fname}")
|
|
250
|
+
print(f" {'✅' if ok else '❌'} {fname}: {data_rows} rows (min {min_rows})")
|
|
251
|
+
|
|
252
|
+
# ═══════════════════════════════════════════════════════════
|
|
253
|
+
# FINAL REPORT
|
|
254
|
+
# ═══════════════════════════════════════════════════════════
|
|
255
|
+
total = len(passed) + len(failed)
|
|
256
|
+
pct = int(100 * len(passed) / total) if total else 0
|
|
257
|
+
print(f"\n{'═'*55}")
|
|
258
|
+
print(f" TOTAL TESTS : {total}")
|
|
259
|
+
print(f" PASSED : {len(passed)} ({pct}%)")
|
|
260
|
+
print(f" FAILED : {len(failed)}")
|
|
261
|
+
if failed:
|
|
262
|
+
print(f"\n ❌ FAILED TESTS:")
|
|
263
|
+
for f in failed:
|
|
264
|
+
print(f" - {f}")
|
|
265
|
+
print(f"{'═'*55}")
|
|
266
|
+
sys.exit(0 if not failed else 1)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
.agent/.tests/run_tests.py
|
|
4
|
+
Auto-discover and run all test_*.py files for a given skill.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
python3 .agent/.tests/run_tests.py mobile-uiux-promax
|
|
8
|
+
python3 .agent/.tests/run_tests.py --all
|
|
9
|
+
"""
|
|
10
|
+
import sys
|
|
11
|
+
import subprocess
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
ROOT = Path(__file__).resolve().parent # .agent/.tests/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run_suite(skill_name: str) -> tuple[int, int]:
|
|
18
|
+
"""Run all test_*.py in .agent/.tests/<skill_name>/. Returns (passed_files, failed_files)."""
|
|
19
|
+
suite_dir = ROOT / skill_name
|
|
20
|
+
if not suite_dir.exists():
|
|
21
|
+
print(f" ⚠️ No test suite found for '{skill_name}' at {suite_dir}")
|
|
22
|
+
return 0, 0
|
|
23
|
+
|
|
24
|
+
test_files = sorted(suite_dir.glob("test_*.py"))
|
|
25
|
+
if not test_files:
|
|
26
|
+
print(f" ⚠️ No test_*.py files found in {suite_dir}")
|
|
27
|
+
return 0, 0
|
|
28
|
+
|
|
29
|
+
passed_files = 0
|
|
30
|
+
failed_files = 0
|
|
31
|
+
|
|
32
|
+
for tf in test_files:
|
|
33
|
+
print(f" → {tf.name}")
|
|
34
|
+
result = subprocess.run(
|
|
35
|
+
["python3", str(tf)],
|
|
36
|
+
capture_output=False, # let output stream directly (verbose)
|
|
37
|
+
cwd=str(ROOT.parent.parent), # project root
|
|
38
|
+
)
|
|
39
|
+
if result.returncode == 0:
|
|
40
|
+
passed_files += 1
|
|
41
|
+
else:
|
|
42
|
+
failed_files += 1
|
|
43
|
+
|
|
44
|
+
return passed_files, failed_files
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
args = sys.argv[1:]
|
|
49
|
+
|
|
50
|
+
if not args:
|
|
51
|
+
print("Usage: python3 run_tests.py <skill-name> OR --all")
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
if args[0] == "--all":
|
|
55
|
+
skills = [d.name for d in ROOT.iterdir() if d.is_dir() and not d.name.startswith(".")]
|
|
56
|
+
if not skills:
|
|
57
|
+
print("No test suites found.")
|
|
58
|
+
sys.exit(0)
|
|
59
|
+
else:
|
|
60
|
+
skills = [args[0]]
|
|
61
|
+
|
|
62
|
+
total_passed = total_failed = 0
|
|
63
|
+
|
|
64
|
+
for skill in skills:
|
|
65
|
+
print(f"\n🧪 Running tests for: {skill}")
|
|
66
|
+
p, f = run_suite(skill)
|
|
67
|
+
total_passed += p
|
|
68
|
+
total_failed += f
|
|
69
|
+
|
|
70
|
+
total = total_passed + total_failed
|
|
71
|
+
if total == 0:
|
|
72
|
+
print("\nNo test files executed.")
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
75
|
+
print(f"\n{'═' * 55}")
|
|
76
|
+
if total_failed == 0:
|
|
77
|
+
print(f" ✅ ALL SUITES PASSED ({total_passed}/{total} files)")
|
|
78
|
+
else:
|
|
79
|
+
print(f" ❌ {total_failed} SUITE(S) FAILED ({total_passed}/{total} files passed)")
|
|
80
|
+
print(f"{'═' * 55}")
|
|
81
|
+
|
|
82
|
+
sys.exit(0 if total_failed == 0 else 1)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
main()
|
|
@@ -92,3 +92,27 @@ Update any diagram labels that mention "commits" to reflect this conditionality
|
|
|
92
92
|
**Intent:** Make the "Commit your work" step in the implementer's job list conditional:
|
|
93
93
|
- Change "4. Commit your work" to "4. Commit your work (if auto_commit is enabled)"
|
|
94
94
|
- Add sub-steps: read `.agent/config.yml`, if `auto_commit: true` (or not set): `git add` + `git commit`, if `auto_commit: false`: skip commit and staging entirely, print skip message.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Patch: git-policy rule — finishing-a-development-branch
|
|
99
|
+
|
|
100
|
+
**File:** `finishing-a-development-branch/SKILL.md`
|
|
101
|
+
**Intent:** Before each git write operation in Execute Choice (Option 1: merge + branch delete,
|
|
102
|
+
Option 2: push, Option 4: branch delete), add an explicit `auto_commit` check:
|
|
103
|
+
- Read `.agent/config.yml` before the git operation
|
|
104
|
+
- If `auto_commit: false`: skip the operation, print "Skipping git operation (auto_commit: false)."
|
|
105
|
+
- If `auto_commit: true` (or absent): proceed normally.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Patch: git-policy rule — using-git-worktrees
|
|
110
|
+
|
|
111
|
+
**File:** `using-git-worktrees/SKILL.md`
|
|
112
|
+
**Intent:** In the "If NOT ignored" safety verification block, make the git commit step conditional:
|
|
113
|
+
- Step 1 (add to .gitignore): always runs — file edit is not a git write operation
|
|
114
|
+
- Step 2 (commit): check `auto_commit` in `.agent/config.yml`
|
|
115
|
+
- If `false`: skip commit, print "Skipping git operation (auto_commit: false)."
|
|
116
|
+
- If `true` (or absent): `git add .gitignore && git commit -m "chore: ignore worktree directory"`
|
|
117
|
+
- Step 3 (proceed with worktree creation): always runs
|
|
118
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Git Policy
|
|
2
|
+
|
|
3
|
+
<HARD-GATE>
|
|
4
|
+
Before running ANY git write operation — git add, git commit, git push,
|
|
5
|
+
git pull, git merge, git tag, git branch -d, git branch -D, git worktree remove,
|
|
6
|
+
git rebase, git cherry-pick, git reset --hard —
|
|
7
|
+
you MUST read `.agent/config.yml` and check the `auto_commit` setting.
|
|
8
|
+
|
|
9
|
+
If `auto_commit: false`:
|
|
10
|
+
- DO NOT run the operation
|
|
11
|
+
- Print exactly: "Skipping git operation (auto_commit: false)."
|
|
12
|
+
- Continue with the rest of the task (non-git steps still execute)
|
|
13
|
+
|
|
14
|
+
If `auto_commit: true` (or key is absent): proceed normally.
|
|
15
|
+
</HARD-GATE>
|
|
16
|
+
|
|
17
|
+
This applies everywhere — inside skills, workflows, and any ad-hoc actions.
|
|
18
|
+
No exceptions.
|
|
19
|
+
|
|
20
|
+
## Always Allowed (read-only)
|
|
21
|
+
|
|
22
|
+
These operations are never blocked:
|
|
23
|
+
- `git status`, `git log`, `git diff`, `git show`
|
|
24
|
+
- `git worktree add`, `git worktree list`
|
|
25
|
+
- `git checkout <branch>` (navigation only)
|
|
@@ -25,6 +25,7 @@ You MUST create a task for each of these items and complete them in order:
|
|
|
25
25
|
2. **Offer visual companion** (if topic will involve visual questions) — this is its own message, not combined with a clarifying question. See the Visual Companion section below.
|
|
26
26
|
3. **Ask clarifying questions** — one at a time, understand purpose/constraints/success criteria
|
|
27
27
|
4. **Propose 2-3 approaches** — with trade-offs and your recommendation
|
|
28
|
+
- *UI/visual tasks only:* Before proposing, run `search.py` to gather style, typography, color, and UX data so proposals are grounded in real design patterns. See the [UI/UX Intelligence](#uiux-intelligence) section below.
|
|
28
29
|
5. **Present design** — in sections scaled to their complexity, get user approval after each section
|
|
29
30
|
6. **Write design doc** — save to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` and commit
|
|
30
31
|
7. **Spec review loop** — dispatch spec-document-reviewer subagent with precisely crafted review context (never your session history); fix issues and re-dispatch until approved (max 3 iterations, then surface to human)
|
|
@@ -86,6 +87,62 @@ digraph brainstorming {
|
|
|
86
87
|
- Present options conversationally with your recommendation and reasoning
|
|
87
88
|
- Lead with your recommended option and explain why
|
|
88
89
|
|
|
90
|
+
## UI/UX Intelligence
|
|
91
|
+
|
|
92
|
+
**When the task involves any visual/UI work** (landing pages, dashboards, components, screens, design systems), run the UI/UX Pro Max search tool **before** presenting the design. This grounds your proposals in a curated knowledge base rather than generic defaults.
|
|
93
|
+
|
|
94
|
+
**How to trigger:**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python3 .agent/.shared/ui-ux-pro-max/scripts/search.py "<keyword>" --domain <domain>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Recommended search sequence** (run in parallel where possible):
|
|
101
|
+
|
|
102
|
+
| # | Domain | What to search | Example |
|
|
103
|
+
|---|--------|----------------|---------|
|
|
104
|
+
| 1 | `product` | Product type + industry | `"SaaS dashboard"`, `"beauty landing page"` |
|
|
105
|
+
| 2 | `style` | Desired visual style | `"glassmorphism dark"`, `"minimal clean"` |
|
|
106
|
+
| 3 | `typography` | Mood/personality | `"elegant modern"`, `"playful friendly"` |
|
|
107
|
+
| 4 | `color` | Industry or product type | `"fintech"`, `"healthcare"`, `"beauty spa"` |
|
|
108
|
+
| 5 | `landing` | Page structure type | `"hero-centric social-proof"` |
|
|
109
|
+
| 6 | `ux` | `"animation"`, `"accessibility"`, `"z-index"` | Always check these three |
|
|
110
|
+
| 7 | `stack` | Target framework | `--stack react-native`, `--stack flutter` |
|
|
111
|
+
|
|
112
|
+
**When to use each domain:**
|
|
113
|
+
- Always run `product` + `style` + `ux` as a baseline for any UI task
|
|
114
|
+
- Add `typography` + `color` when presenting a full design system
|
|
115
|
+
- Add `landing` only for marketing/landing pages
|
|
116
|
+
- Add `chart` only for analytics dashboards
|
|
117
|
+
- Use `--stack` flag to get implementation-specific patterns (e.g., `--stack react-native` for mobile)
|
|
118
|
+
|
|
119
|
+
**Synthesize results before presenting design:** Do not dump raw search output to the user. Summarize what you found and explain how it informs your design choices.
|
|
120
|
+
|
|
121
|
+
> **Skip this step** for purely backend, data-model, or logic-only tasks where no UI is involved.
|
|
122
|
+
|
|
123
|
+
### Mobile UI/UX Intelligence (Extended Layer)
|
|
124
|
+
|
|
125
|
+
**When the request is for a mobile app** — detect keywords like: `iOS`, `Android`, `React Native`, `Flutter`, `SwiftUI`, `Jetpack Compose`, `mobile`, `app screen`, `native app` — run the **mobile-uiux-promax workflow** IN ADDITION to the web tool above.
|
|
126
|
+
|
|
127
|
+
Read the workflow file first: `.agent/workflows/mobile-uiux-promax.md`
|
|
128
|
+
|
|
129
|
+
The mobile workflow adds 3 more steps after the web style layer:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Step 2: Mobile behavior
|
|
133
|
+
python3 .agent/.shared/mobile-uiux-promax/scripts/mobile-search.py "<nav pattern>" --domain navigation
|
|
134
|
+
python3 .agent/.shared/mobile-uiux-promax/scripts/mobile-search.py "<component>" --domain components
|
|
135
|
+
python3 .agent/.shared/mobile-uiux-promax/scripts/mobile-search.py "<platform topic>" --domain platform
|
|
136
|
+
python3 .agent/.shared/mobile-uiux-promax/scripts/mobile-search.py "<animation>" --domain animation
|
|
137
|
+
|
|
138
|
+
# Step 3: Stack guidelines (react-native / flutter / swiftui / jetpack-compose)
|
|
139
|
+
python3 .agent/.shared/mobile-uiux-promax/scripts/mobile-search.py "<topic>" --stack react-native
|
|
140
|
+
|
|
141
|
+
# Step 4: Synthesize style + mobile behavior + stack patterns → present design
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Read and apply the mobile-uiux-promax skill: `.agent/skills/mobile-uiux-promax/SKILL.md`
|
|
145
|
+
|
|
89
146
|
**Presenting the design:**
|
|
90
147
|
|
|
91
148
|
- Once you believe you understand what you're building, present the design
|
|
@@ -67,20 +67,24 @@ Which option?
|
|
|
67
67
|
|
|
68
68
|
#### Option 1: Merge Locally
|
|
69
69
|
|
|
70
|
+
**Before git write steps:** Check `.agent/config.yml` for `auto_commit`.
|
|
71
|
+
- If `auto_commit: false`: skip all git write operations below, print "Skipping git operation (auto_commit: false)."
|
|
72
|
+
- If `auto_commit: true` (or absent): proceed normally.
|
|
73
|
+
|
|
70
74
|
```bash
|
|
71
75
|
# Switch to base branch
|
|
72
76
|
git checkout <base-branch>
|
|
73
77
|
|
|
74
|
-
# Pull latest
|
|
78
|
+
# Pull latest (blocked if auto_commit: false)
|
|
75
79
|
git pull
|
|
76
80
|
|
|
77
|
-
# Merge feature branch
|
|
81
|
+
# Merge feature branch (blocked if auto_commit: false)
|
|
78
82
|
git merge <feature-branch>
|
|
79
83
|
|
|
80
84
|
# Verify tests on merged result
|
|
81
85
|
<test command>
|
|
82
86
|
|
|
83
|
-
# If tests pass
|
|
87
|
+
# If tests pass (blocked if auto_commit: false)
|
|
84
88
|
git branch -d <feature-branch>
|
|
85
89
|
```
|
|
86
90
|
|
|
@@ -88,11 +92,15 @@ Then: Cleanup worktree (Step 5)
|
|
|
88
92
|
|
|
89
93
|
#### Option 2: Push and Create PR
|
|
90
94
|
|
|
95
|
+
**Before git write steps:** Check `.agent/config.yml` for `auto_commit`.
|
|
96
|
+
- If `auto_commit: false`: skip all git write operations below, print "Skipping git operation (auto_commit: false)."
|
|
97
|
+
- If `auto_commit: true` (or absent): proceed normally.
|
|
98
|
+
|
|
91
99
|
```bash
|
|
92
|
-
# Push branch
|
|
100
|
+
# Push branch (blocked if auto_commit: false)
|
|
93
101
|
git push -u origin <feature-branch>
|
|
94
102
|
|
|
95
|
-
# Create PR
|
|
103
|
+
# Create PR (blocked if auto_commit: false)
|
|
96
104
|
gh pr create --title "<title>" --body "$(cat <<'EOF'
|
|
97
105
|
## Summary
|
|
98
106
|
<2-3 bullets of what changed>
|
|
@@ -125,10 +133,14 @@ Type 'discard' to confirm.
|
|
|
125
133
|
|
|
126
134
|
Wait for exact confirmation.
|
|
127
135
|
|
|
136
|
+
**Before git write steps:** Check `.agent/config.yml` for `auto_commit`.
|
|
137
|
+
- If `auto_commit: false`: skip all git write operations below, print "Skipping git operation (auto_commit: false)."
|
|
138
|
+
- If `auto_commit: true` (or absent): proceed normally.
|
|
139
|
+
|
|
128
140
|
If confirmed:
|
|
129
141
|
```bash
|
|
130
142
|
git checkout <base-branch>
|
|
131
|
-
git branch -D <feature-branch>
|
|
143
|
+
git branch -D <feature-branch> # blocked if auto_commit: false
|
|
132
144
|
```
|
|
133
145
|
|
|
134
146
|
Then: Cleanup worktree (Step 5)
|