gin-skills 1.0.3 → 1.0.5

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.
@@ -689,7 +689,7 @@
689
689
  <div class="container">
690
690
  <div class="hero-badge fade-up">
691
691
  <span class="dot"></span>
692
- <!-- AUTO:BADGE -->v1.0.2 &mdash; 17 Skills &middot; 6 Agents<!-- /AUTO:BADGE -->
692
+ <!-- AUTO:BADGE -->v1.0.4 &mdash; 17 Skills &middot; 6 Agents<!-- /AUTO:BADGE -->
693
693
  </div>
694
694
  <h1 class="fade-up"><span>Supercharge</span> Claude Code<br>with One Command</h1>
695
695
  <p class="hero-sub fade-up">
@@ -1220,7 +1220,7 @@
1220
1220
  <footer class="footer">
1221
1221
  <div class="container">
1222
1222
  <div class="footer-content">
1223
- <div class="footer-brand">GinStudio Skills v1.0.2</div>
1223
+ <div class="footer-brand">GinStudio Skills v1.0.4</div>
1224
1224
  <div class="footer-links">
1225
1225
  <a href="https://www.npmjs.com/package/gin-skills" target="_blank" rel="noopener">npm</a>
1226
1226
  <a href="https://github.com/vuduynhiennn/ginskills" target="_blank" rel="noopener">GitHub</a>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gin-skills",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Install GinStudio skills and agents for Claude Code",
5
5
  "homepage": "https://skills.ginstudio.asia",
6
6
  "repository": {
@@ -7,6 +7,8 @@ echo "Animation Performance Scanner"
7
7
  echo "================================"
8
8
 
9
9
  # Animated.timing/spring/decay without useNativeDriver: true
10
+ # Note: useNativeDriver only supports opacity and transform. Properties like backgroundColor,
11
+ # width, height, margin, padding, borderRadius REQUIRE useNativeDriver: false — not a bug.
10
12
  echo ""
11
13
  echo "--- Animated Without useNativeDriver ---"
12
14
  grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
@@ -15,10 +17,18 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
15
17
  | while IFS= read -r line; do
16
18
  file=$(echo "$line" | cut -d: -f1)
17
19
  lineno=$(echo "$line" | cut -d: -f2)
20
+ # Look at broader context (20 lines) to check what property the animated value drives
18
21
  context=$(sed -n "${lineno},$((lineno + 8))p" "$file" 2>/dev/null)
22
+ broad_context=$(sed -n "1,$((lineno + 30))p" "$file" 2>/dev/null | tail -50)
19
23
  if ! echo "$context" | grep -q 'useNativeDriver:\s*true'; then
20
24
  if echo "$context" | grep -q 'useNativeDriver:\s*false'; then
21
- echo "$file:$lineno [ERROR] Animated.timing/spring with useNativeDriver: false Change to useNativeDriver: true; only opacity and transform are supported on native thread"
25
+ # Check if the animated value is used for non-native-driver-compatible properties
26
+ # (backgroundColor, width, height, margin*, padding*, border*, left, top, right, bottom, flex)
27
+ if echo "$broad_context" | grep -qE 'backgroundColor|width:\s*anim|height:\s*anim|marginTop|marginBottom|marginLeft|marginRight|paddingTop|paddingBottom|paddingLeft|paddingRight|borderRadius|borderWidth|left:\s*anim|top:\s*anim|right:\s*anim|bottom:\s*anim|interpolate.*backgroundColor|interpolate.*color'; then
28
+ echo "$file:$lineno — [INFO] Animated with useNativeDriver: false (justified: animates layout/color property) → Consider react-native-reanimated for smoother layout animations on UI thread"
29
+ else
30
+ echo "$file:$lineno — [ERROR] Animated.timing/spring with useNativeDriver: false → Change to useNativeDriver: true; only opacity and transform are supported on native thread"
31
+ fi
22
32
  else
23
33
  echo "$file:$lineno — [ERROR] Animated.timing/spring missing useNativeDriver → Add useNativeDriver: true to run animation on UI thread and avoid JS thread jank"
24
34
  fi
@@ -7,8 +7,19 @@ echo "Console & DevTools Scanner"
7
7
  echo "================================"
8
8
 
9
9
  # console.log without __DEV__ guard
10
+ # Escalates to ERROR when on gesture/animation hot paths (60fps concern)
11
+ # Projects using a custom logger (e.g. appLog) should prefer that over raw console.log
10
12
  echo ""
11
13
  echo "--- console.log Without __DEV__ Guard ---"
14
+
15
+ # Auto-detect custom logger: check if the project has an appLog/logger utility
16
+ CUSTOM_LOGGER=""
17
+ CUSTOM_LOGGER_IMPORT=""
18
+ if grep -rql --include="*.ts" --include="*.tsx" 'export const appLog' "$DIR" 2>/dev/null; then
19
+ CUSTOM_LOGGER="appLog"
20
+ CUSTOM_LOGGER_IMPORT='import { appLog } from "@/shared/utils/logger"'
21
+ fi
22
+
12
23
  grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
13
24
  'console\.log(' "$DIR" 2>/dev/null \
14
25
  | grep -v node_modules | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' \
@@ -25,7 +36,25 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
25
36
  file_context=$(sed -n "1,$((lineno - 1))p" "$file" 2>/dev/null)
26
37
  if ! echo "$context" | grep -q '__DEV__\|if.*DEV' && \
27
38
  ! echo "$file_context" | grep -q '= __DEV__'; then
28
- echo "$file:$lineno [WARN] console.log without __DEV__ guard Wrap with if (__DEV__) or use babel-plugin-transform-remove-console"
39
+ # Check if this console.log is on a gesture/animation hot path (runs every frame)
40
+ hot_path_context=$(sed -n "$((lineno > 20 ? lineno - 20 : 1)),${lineno}p" "$file" 2>/dev/null)
41
+ is_worklet=false
42
+ if echo "$hot_path_context" | grep -q '"worklet"\|'\''worklet'\'''; then
43
+ is_worklet=true
44
+ fi
45
+ if echo "$hot_path_context" | grep -qE 'onUpdate|onActive|Pan\.|onMove|onScroll|onGestureEvent|Gesture\.|worklet|runOnJS|requestAnimationFrame|\.addListener'; then
46
+ if [ "$is_worklet" = true ]; then
47
+ echo "$file:$lineno — [ERROR] console.log in Reanimated worklet (runs on UI thread every frame) → Wrap with if (__DEV__) console.log(...); appLog cannot run in worklets"
48
+ else
49
+ echo "$file:$lineno — [ERROR] console.log on gesture/animation hot path (runs every frame at 60fps) → Remove or replace with ${CUSTOM_LOGGER:-__DEV__ guard}; serialization on hot paths causes jank"
50
+ fi
51
+ else
52
+ if [ -n "$CUSTOM_LOGGER" ]; then
53
+ echo "$file:$lineno — [WARN] raw console.log instead of project logger → Replace with $CUSTOM_LOGGER.debug(...) which is dev-only by default"
54
+ else
55
+ echo "$file:$lineno — [WARN] console.log without __DEV__ guard → Wrap with if (__DEV__) or use babel-plugin-transform-remove-console"
56
+ fi
57
+ fi
29
58
  ISSUES=$((ISSUES + 1))
30
59
  fi
31
60
  done
@@ -47,7 +76,11 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
47
76
  file_context=$(sed -n "1,$((lineno - 1))p" "$file" 2>/dev/null)
48
77
  if ! echo "$context" | grep -q '__DEV__\|if.*DEV' && \
49
78
  ! echo "$file_context" | grep -q '= __DEV__'; then
50
- echo "$file:$lineno — [WARN] console.warn without __DEV__ guard → Wrap with if (__DEV__) or suppress in production logger"
79
+ if [ -n "$CUSTOM_LOGGER" ]; then
80
+ echo "$file:$lineno — [WARN] raw console.warn instead of project logger → Replace with $CUSTOM_LOGGER.warn(...) which is dev-only by default"
81
+ else
82
+ echo "$file:$lineno — [WARN] console.warn without __DEV__ guard → Wrap with if (__DEV__) or suppress in production logger"
83
+ fi
51
84
  ISSUES=$((ISSUES + 1))
52
85
  fi
53
86
  done
@@ -61,7 +94,11 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
61
94
  | grep -v '__DEV__\|Sentry\|crashlytics\|bugsnag' \
62
95
  | while IFS= read -r line; do
63
96
  file_loc=$(echo "$line" | cut -d: -f1,2)
64
- echo "$file_loc — [INFO] console.error in production code → Use a crash reporting SDK (Sentry/Crashlytics) instead of console.error"
97
+ if [ -n "$CUSTOM_LOGGER" ]; then
98
+ echo "$file_loc — [INFO] raw console.error → Replace with $CUSTOM_LOGGER.error(...) or use a crash reporting SDK (Sentry/Crashlytics)"
99
+ else
100
+ echo "$file_loc — [INFO] console.error in production code → Use a crash reporting SDK (Sentry/Crashlytics) instead of console.error"
101
+ fi
65
102
  ISSUES=$((ISSUES + 1))
66
103
  done
67
104
 
@@ -73,7 +110,11 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
73
110
  | grep -v node_modules | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' \
74
111
  | while IFS= read -r line; do
75
112
  file_loc=$(echo "$line" | cut -d: -f1,2)
76
- echo "$file_loc — [WARN] console.info/debug in production code → Remove or guard with if (__DEV__)"
113
+ if [ -n "$CUSTOM_LOGGER" ]; then
114
+ echo "$file_loc — [WARN] raw console.info/debug → Replace with $CUSTOM_LOGGER.debug(...) which is dev-only by default"
115
+ else
116
+ echo "$file_loc — [WARN] console.info/debug in production code → Remove or guard with if (__DEV__)"
117
+ fi
77
118
  ISSUES=$((ISSUES + 1))
78
119
  done
79
120
 
@@ -140,6 +181,7 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
140
181
  done
141
182
 
142
183
  # Production-critical logic inside __DEV__ blocks (anti-pattern)
184
+ # Skip __DEV__ blocks that only contain console.log (this is the correct pattern for worklet code)
143
185
  echo ""
144
186
  echo "--- Production Logic Inside __DEV__ Block ---"
145
187
  grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
@@ -149,6 +191,11 @@ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
149
191
  file=$(echo "$line" | cut -d: -f1)
150
192
  lineno=$(echo "$line" | cut -d: -f2)
151
193
  context=$(sed -n "${lineno},$((lineno + 10))p" "$file" 2>/dev/null)
194
+ # Skip if the __DEV__ block only wraps console.log/warn/error (that's the correct pattern)
195
+ match_line=$(sed -n "${lineno}p" "$file" 2>/dev/null)
196
+ if echo "$match_line" | grep -q 'console\.\(log\|warn\|error\|debug\|info\)'; then
197
+ continue
198
+ fi
152
199
  if echo "$context" | grep -q 'navigate\|dispatch\|setState\|fetch(\|api\.'; then
153
200
  echo "$file:$lineno — [WARN] Production-critical logic inside __DEV__ block → State changes/navigation inside __DEV__ blocks will not run in production; this may be a bug"
154
201
  ISSUES=$((ISSUES + 1))
@@ -151,6 +151,7 @@ grep -rln --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js"
151
151
  # subscribe, addEventListener, setInterval, AppState) catch these patterns more reliably.
152
152
 
153
153
  # AppState.addEventListener without remove
154
+ # Skip module-level singletons and class constructors — they intentionally persist for app lifetime
154
155
  echo ""
155
156
  echo "--- AppState.addEventListener Without Cleanup ---"
156
157
  grep -rln --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" 'AppState' "$DIR" 2>/dev/null \
@@ -158,8 +159,19 @@ grep -rln --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js"
158
159
  | while IFS= read -r file; do
159
160
  if grep -q 'AppState\.addEventListener\|AppState\.addListener' "$file" \
160
161
  && ! grep -q '\.remove()\|removeEventListener' "$file"; then
162
+ # Only flag files that use useEffect (React component/hook context)
163
+ # Module-level listeners and class constructor listeners are singletons — no cleanup needed
164
+ if ! grep -q 'useEffect' "$file"; then
165
+ continue
166
+ fi
161
167
  grep -n 'AppState\.addEventListener\|AppState\.addListener' "$file" | while IFS= read -r match; do
162
168
  lineno=$(echo "$match" | cut -d: -f1)
169
+ # Skip if the line is inside a class constructor or module-level scope
170
+ # Check if there's a class/constructor context above this line
171
+ preceding=$(sed -n "1,${lineno}p" "$file" 2>/dev/null)
172
+ if echo "$preceding" | grep -q 'class .*{' && echo "$preceding" | grep -q 'constructor('; then
173
+ continue
174
+ fi
163
175
  echo "$file:$lineno — [ERROR] AppState.addEventListener without cleanup → Store subscription and call subscription.remove() in useEffect cleanup"
164
176
  ISSUES=$((ISSUES + 1))
165
177
  done
@@ -231,6 +243,29 @@ grep -rln --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js"
231
243
  fi
232
244
  done
233
245
 
246
+ # AbortSignal.addEventListener("abort", ...) without { once: true }
247
+ # When composing AbortControllers (e.g. timeout + user cancel), each listener on the
248
+ # upstream signal accumulates if not cleaned up. { once: true } auto-removes after firing.
249
+ echo ""
250
+ echo "--- AbortSignal addEventListener Without { once: true } ---"
251
+ grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
252
+ '\.addEventListener.*abort' "$DIR" 2>/dev/null \
253
+ | grep -v node_modules | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' \
254
+ | while IFS= read -r line; do
255
+ file=$(echo "$line" | cut -d: -f1)
256
+ lineno=$(echo "$line" | cut -d: -f2)
257
+ match_text=$(echo "$line" | cut -d: -f3-)
258
+ # Check if { once: true } is present on the same line or within the next 2 lines
259
+ context=$(sed -n "${lineno},$((lineno + 2))p" "$file" 2>/dev/null)
260
+ if ! echo "$context" | grep -q 'once.*true\|{ once: true }'; then
261
+ # Check if there's a matching removeEventListener for cleanup
262
+ if ! grep -q 'removeEventListener.*abort' "$file"; then
263
+ echo "$file:$lineno — [WARN] addEventListener(\"abort\", fn) without { once: true } → Listeners accumulate on reused signals causing memory leak; add { once: true } as 3rd arg or call removeEventListener in cleanup"
264
+ ISSUES=$((ISSUES + 1))
265
+ fi
266
+ fi
267
+ done
268
+
234
269
  # Reanimated useAnimatedReaction without cancelAnimation cleanup
235
270
  echo ""
236
271
  echo "--- useAnimatedReaction Without cancelAnimation Cleanup ---"
@@ -30,12 +30,35 @@ grep -rn --include="*.tsx" --include="*.jsx" --include="*.ts" --include="*.js" '
30
30
  done
31
31
 
32
32
  # key={index} or key={i} or key={idx}
33
+ # Skip static/skeleton lists where index keys are safe (never reordered, no unique IDs)
33
34
  echo ""
34
35
  echo "--- Index as List Key ---"
35
36
  grep -rn --include="*.tsx" --include="*.jsx" 'key={index}\|key={i}\b\|key={idx}' "$DIR" 2>/dev/null \
36
37
  | grep -v node_modules \
37
38
  | while IFS= read -r line; do
38
- file_loc=$(echo "$line" | cut -d: -f1,2)
39
+ file=$(echo "$line" | cut -d: -f1)
40
+ lineno=$(echo "$line" | cut -d: -f2)
41
+ file_loc="$file:$lineno"
42
+ content=$(echo "$line" | cut -d: -f3-)
43
+
44
+ # Check surrounding context (5 lines before, 2 after) for skeleton/static patterns
45
+ context=$(sed -n "$((lineno > 5 ? lineno - 5 : 1)),$((lineno + 2))p" "$file" 2>/dev/null)
46
+
47
+ # Skip 1: Static skeleton arrays (Array.from, Array(N), new Array)
48
+ if echo "$context" | grep -qE 'Array\.from|Array\([0-9]|new Array|\.fill\(|Skeleton|skeleton'; then
49
+ continue
50
+ fi
51
+
52
+ # Skip 2: Files that are skeleton components (filename contains skeleton/loading)
53
+ if echo "$file" | grep -qiE 'skeleton|loading-skeleton|placeholder'; then
54
+ continue
55
+ fi
56
+
57
+ # Skip 3: Static enum/constant arrays that never reorder
58
+ if echo "$context" | grep -qE 'const\s+\[.*\]\s*=|\.entries\(\)|Object\.keys|Object\.values'; then
59
+ continue
60
+ fi
61
+
39
62
  echo "$file_loc — [ERROR] Using index as key causes O(n) re-renders → Use stable unique ID (item.id)"
40
63
  ISSUES=$((ISSUES + 1))
41
64
  done
@@ -44,13 +44,29 @@ grep -rln --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js"
44
44
  done
45
45
 
46
46
  # Zustand store subscribed without selector
47
+ # Skip commented-out code and store definition files (where the hook is created, not called)
47
48
  echo ""
48
49
  echo "--- Zustand Store Without Selector ---"
49
50
  grep -rn --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
50
51
  'useStore()\|use[A-Z][a-zA-Z]*Store()' "$DIR" 2>/dev/null \
51
52
  | grep -v node_modules | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' \
52
53
  | while IFS= read -r line; do
53
- file_loc=$(echo "$line" | cut -d: -f1,2)
54
+ file=$(echo "$line" | cut -d: -f1)
55
+ lineno=$(echo "$line" | cut -d: -f2)
56
+ content=$(echo "$line" | cut -d: -f3-)
57
+ file_loc="$file:$lineno"
58
+
59
+ # Skip commented-out lines (// or * at start, or inside block comments)
60
+ trimmed=$(echo "$content" | sed 's/^[[:space:]]*//')
61
+ if echo "$trimmed" | grep -qE '^\s*(//|/?\*|\*)'; then
62
+ continue
63
+ fi
64
+
65
+ # Skip store definition files (where create() defines the hook)
66
+ if grep -q 'create(' "$file" && grep -q 'zustand\|from.*create' "$file"; then
67
+ continue
68
+ fi
69
+
54
70
  echo "$file_loc — [ERROR] Zustand store subscribed without selector → Pass a selector: useStore(state => state.field) to avoid re-renders on unrelated state changes"
55
71
  ISSUES=$((ISSUES + 1))
56
72
  done