@dianzhong/create-harness-app 0.1.2 → 0.1.3

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 (38) hide show
  1. package/package.json +1 -1
  2. package/templates/harness/full/.claude/agents/code-reviewer.md +1 -1
  3. package/templates/harness/full/.claude/agents/harness-reviewer.md +0 -2
  4. package/templates/harness/full/.claude/rules/skills-mcp.md +2 -3
  5. package/templates/harness/full/.claude/settings.json +1 -1
  6. package/templates/harness/full/docs/ai-harness.md +4 -6
  7. package/templates/harness/full/docs/harness-quick-reference.md +1 -1
  8. package/templates/harness/full/docs/review-checklist.md +1 -1
  9. package/templates/harness/full/scripts/verify-skills.mjs +6 -61
  10. package/templates/harness/full/scripts/verify-skills.test.mjs +1 -11
  11. package/templates/harness/full/.agents/skills/find-skills/SKILL.md +0 -143
  12. package/templates/harness/full/.agents/skills/vue-best-practices/LICENSE.md +0 -21
  13. package/templates/harness/full/.agents/skills/vue-best-practices/SKILL.md +0 -155
  14. package/templates/harness/full/.agents/skills/vue-best-practices/SYNC.md +0 -5
  15. package/templates/harness/full/.agents/skills/vue-best-practices/references/animation-class-based-technique.md +0 -258
  16. package/templates/harness/full/.agents/skills/vue-best-practices/references/animation-state-driven-technique.md +0 -287
  17. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-async.md +0 -99
  18. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-data-flow.md +0 -313
  19. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-fallthrough-attrs.md +0 -179
  20. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-keep-alive.md +0 -139
  21. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-slots.md +0 -226
  22. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-suspense.md +0 -231
  23. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-teleport.md +0 -110
  24. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-transition-group.md +0 -131
  25. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-transition.md +0 -135
  26. package/templates/harness/full/.agents/skills/vue-best-practices/references/composables.md +0 -303
  27. package/templates/harness/full/.agents/skills/vue-best-practices/references/directives.md +0 -168
  28. package/templates/harness/full/.agents/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +0 -177
  29. package/templates/harness/full/.agents/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +0 -185
  30. package/templates/harness/full/.agents/skills/vue-best-practices/references/perf-virtualize-large-lists.md +0 -182
  31. package/templates/harness/full/.agents/skills/vue-best-practices/references/plugins.md +0 -178
  32. package/templates/harness/full/.agents/skills/vue-best-practices/references/reactivity.md +0 -371
  33. package/templates/harness/full/.agents/skills/vue-best-practices/references/render-functions.md +0 -227
  34. package/templates/harness/full/.agents/skills/vue-best-practices/references/sfc.md +0 -355
  35. package/templates/harness/full/.agents/skills/vue-best-practices/references/state-management.md +0 -138
  36. package/templates/harness/full/.agents/skills/vue-best-practices/references/updated-hook-performance.md +0 -193
  37. package/templates/harness/full/AGENTS.md +0 -3
  38. package/templates/harness/full/GEMINI.md +0 -3
@@ -1,258 +0,0 @@
1
- ---
2
- title: Use Class-based Animations for Non-Enter/Leave Effects
3
- impact: LOW
4
- impactDescription: Class-based animations are simpler and more performant for elements that remain in the DOM
5
- type: best-practice
6
- tags: [vue3, animation, css, class-binding, state]
7
- ---
8
-
9
- # Use Class-based Animations for Non-Enter/Leave Effects
10
-
11
- **Impact: LOW** - For animations on elements that are not entering or leaving the DOM, use CSS class-based animations triggered by Vue's reactive state. This is simpler than `<Transition>` and more appropriate for feedback animations like shake, pulse, or highlight effects.
12
-
13
- ## Task List
14
-
15
- - Use class-based animations for elements staying in the DOM
16
- - Use `<Transition>` only for enter/leave animations
17
- - Combine CSS animations with Vue's class bindings (`:class`)
18
- - Consider using `setTimeout` to auto-remove animation classes
19
-
20
- **When to Use Class-based Animations:**
21
-
22
- - User feedback (shake on error, pulse on success)
23
- - Attention-grabbing effects (highlight changes)
24
- - Hover/focus states that need more than CSS transitions
25
- - Any animation where the element stays mounted
26
-
27
- **When to Use Transition Component:**
28
-
29
- - Elements entering/leaving the DOM (v-if/v-show)
30
- - Route transitions
31
- - List item additions/removals
32
-
33
- ## Basic Pattern
34
-
35
- ```vue
36
- <script setup>
37
- import { ref } from 'vue'
38
-
39
- const showError = ref(false)
40
-
41
- function submitForm() {
42
- if (!isValid()) {
43
- // Trigger shake animation
44
- showError.value = true
45
-
46
- // Auto-remove class after animation completes
47
- setTimeout(() => {
48
- showError.value = false
49
- }, 820) // Match animation duration
50
- }
51
- }
52
- </script>
53
-
54
- <template>
55
- <div :class="{ shake: showError }">
56
- <button @click="submitForm">Submit</button>
57
- <span v-if="showError">This feature is disabled!</span>
58
- </div>
59
- </template>
60
-
61
- <style>
62
- .shake {
63
- animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
64
- transform: translate3d(0, 0, 0); /* Enable GPU acceleration */
65
- }
66
-
67
- @keyframes shake {
68
- 10%,
69
- 90% {
70
- transform: translate3d(-1px, 0, 0);
71
- }
72
- 20%,
73
- 80% {
74
- transform: translate3d(2px, 0, 0);
75
- }
76
- 30%,
77
- 50%,
78
- 70% {
79
- transform: translate3d(-4px, 0, 0);
80
- }
81
- 40%,
82
- 60% {
83
- transform: translate3d(4px, 0, 0);
84
- }
85
- }
86
- </style>
87
- ```
88
-
89
- ## Common Animation Patterns
90
-
91
- ### Pulse on Success
92
-
93
- ```vue
94
- <script setup>
95
- import { ref } from 'vue'
96
-
97
- const saved = ref(false)
98
-
99
- async function save() {
100
- await saveData()
101
- saved.value = true
102
- setTimeout(() => (saved.value = false), 1000)
103
- }
104
- </script>
105
-
106
- <template>
107
- <button :class="{ pulse: saved }" @click="save">
108
- {{ saved ? 'Saved!' : 'Save' }}
109
- </button>
110
- </template>
111
-
112
- <style>
113
- .pulse {
114
- animation: pulse 0.5s ease-in-out;
115
- }
116
-
117
- @keyframes pulse {
118
- 0%,
119
- 100% {
120
- transform: scale(1);
121
- }
122
- 50% {
123
- transform: scale(1.05);
124
- }
125
- }
126
- </style>
127
- ```
128
-
129
- ### Highlight on Change
130
-
131
- ```vue
132
- <script setup>
133
- import { ref, watch } from 'vue'
134
-
135
- const value = ref(0)
136
- const justUpdated = ref(false)
137
-
138
- watch(value, () => {
139
- justUpdated.value = true
140
- setTimeout(() => (justUpdated.value = false), 1000)
141
- })
142
- </script>
143
-
144
- <template>
145
- <div :class="{ highlight: justUpdated }">Value: {{ value }}</div>
146
- </template>
147
-
148
- <style>
149
- .highlight {
150
- animation: highlight 1s ease-out;
151
- }
152
-
153
- @keyframes highlight {
154
- 0% {
155
- background-color: yellow;
156
- }
157
- 100% {
158
- background-color: transparent;
159
- }
160
- }
161
- </style>
162
- ```
163
-
164
- ### Bounce Attention
165
-
166
- ```vue
167
- <script setup>
168
- import { ref } from 'vue'
169
-
170
- const needsAttention = ref(false)
171
-
172
- function notifyUser() {
173
- needsAttention.value = true
174
- // No setTimeout needed - using animationend event
175
- }
176
- </script>
177
-
178
- <template>
179
- <div :class="{ bounce: needsAttention }" @animationend="needsAttention = false">
180
- <BellIcon />
181
- </div>
182
- </template>
183
-
184
- <style>
185
- .bounce {
186
- animation: bounce 0.5s ease;
187
- }
188
-
189
- @keyframes bounce {
190
- 0%,
191
- 100% {
192
- transform: translateY(0);
193
- }
194
- 50% {
195
- transform: translateY(-10px);
196
- }
197
- }
198
- </style>
199
- ```
200
-
201
- ## Using animationend Event
202
-
203
- Instead of `setTimeout`, use the `animationend` event for cleaner code:
204
-
205
- ```vue
206
- <script setup>
207
- import { ref } from 'vue'
208
-
209
- const isAnimating = ref(false)
210
-
211
- function triggerAnimation() {
212
- isAnimating.value = true
213
- // Class is automatically removed when animation ends
214
- }
215
- </script>
216
-
217
- <template>
218
- <div :class="{ animate: isAnimating }" @animationend="isAnimating = false">Content</div>
219
- </template>
220
- ```
221
-
222
- ## Composable for Reusable Animations
223
-
224
- ```javascript
225
- // composables/useAnimation.js
226
- import { ref } from 'vue'
227
-
228
- export function useAnimation(duration = 500) {
229
- const isAnimating = ref(false)
230
-
231
- function trigger() {
232
- isAnimating.value = true
233
- setTimeout(() => {
234
- isAnimating.value = false
235
- }, duration)
236
- }
237
-
238
- return {
239
- isAnimating,
240
- trigger,
241
- }
242
- }
243
- ```
244
-
245
- ```vue
246
- <script setup>
247
- import { useAnimation } from '@/composables/useAnimation'
248
-
249
- const shake = useAnimation(820)
250
- const pulse = useAnimation(500)
251
- </script>
252
-
253
- <template>
254
- <button :class="{ shake: shake.isAnimating.value }" @click="shake.trigger()">Shake me</button>
255
-
256
- <button :class="{ pulse: pulse.isAnimating.value }" @click="pulse.trigger()">Pulse me</button>
257
- </template>
258
- ```
@@ -1,287 +0,0 @@
1
- ---
2
- title: State-driven Animations with CSS Transitions and Style Bindings
3
- impact: LOW
4
- impactDescription: Combining Vue's reactive style bindings with CSS transitions creates smooth, interactive animations
5
- type: best-practice
6
- tags: [vue3, animation, css, transition, style-binding, state, interactive]
7
- ---
8
-
9
- # State-driven Animations with CSS Transitions and Style Bindings
10
-
11
- **Impact: LOW** - For responsive, interactive animations that react to user input or state changes, combine Vue's dynamic style bindings with CSS transitions. This creates smooth animations that interpolate values in real-time based on state.
12
-
13
- ## Task List
14
-
15
- - Use `:style` binding for dynamic properties that change frequently
16
- - Add CSS `transition` property to smoothly animate between values
17
- - Consider using `transform` and `opacity` for GPU-accelerated animations
18
- - For complex value interpolation, use watchers with animation libraries
19
-
20
- ## Basic Pattern
21
-
22
- ```vue
23
- <script setup>
24
- import { ref } from 'vue'
25
-
26
- const hue = ref(0)
27
-
28
- function onMousemove(e) {
29
- // Map mouse X position to hue (0-360)
30
- const rect = e.currentTarget.getBoundingClientRect()
31
- hue.value = Math.round(((e.clientX - rect.left) / rect.width) * 360)
32
- }
33
- </script>
34
-
35
- <template>
36
- <div
37
- :style="{ backgroundColor: `hsl(${hue}, 80%, 50%)` }"
38
- class="interactive-area"
39
- @mousemove="onMousemove"
40
- >
41
- <p>Move your mouse across this div...</p>
42
- <p>Hue: {{ hue }}</p>
43
- </div>
44
- </template>
45
-
46
- <style>
47
- .interactive-area {
48
- transition: background-color 0.3s ease;
49
- height: 200px;
50
- display: flex;
51
- flex-direction: column;
52
- align-items: center;
53
- justify-content: center;
54
- }
55
- </style>
56
- ```
57
-
58
- ## Common Use Cases
59
-
60
- ### Following Mouse Position
61
-
62
- ```vue
63
- <script setup>
64
- import { ref } from 'vue'
65
-
66
- const x = ref(0)
67
- const y = ref(0)
68
-
69
- function onMousemove(e) {
70
- const rect = e.currentTarget.getBoundingClientRect()
71
- x.value = e.clientX - rect.left
72
- y.value = e.clientY - rect.top
73
- }
74
- </script>
75
-
76
- <template>
77
- <div class="container" @mousemove="onMousemove">
78
- <div
79
- class="follower"
80
- :style="{
81
- transform: `translate(${x}px, ${y}px)`,
82
- }"
83
- />
84
- </div>
85
- </template>
86
-
87
- <style>
88
- .container {
89
- position: relative;
90
- height: 300px;
91
- }
92
-
93
- .follower {
94
- position: absolute;
95
- width: 20px;
96
- height: 20px;
97
- background: blue;
98
- border-radius: 50%;
99
- /* Smooth following with transition */
100
- transition: transform 0.1s ease-out;
101
- /* Prevent the follower from triggering mousemove */
102
- pointer-events: none;
103
- }
104
- </style>
105
- ```
106
-
107
- ### Progress Animation
108
-
109
- ```vue
110
- <script setup>
111
- import { ref } from 'vue'
112
-
113
- const progress = ref(0)
114
- </script>
115
-
116
- <template>
117
- <div class="progress-container">
118
- <div class="progress-bar" :style="{ width: `${progress}%` }" />
119
- </div>
120
- <input v-model.number="progress" type="range" min="0" max="100" />
121
- </template>
122
-
123
- <style>
124
- .progress-container {
125
- height: 20px;
126
- background: #e0e0e0;
127
- border-radius: 10px;
128
- overflow: hidden;
129
- }
130
-
131
- .progress-bar {
132
- height: 100%;
133
- background: linear-gradient(90deg, #4caf50, #8bc34a);
134
- transition: width 0.3s ease;
135
- }
136
- </style>
137
- ```
138
-
139
- ### Scroll-based Animation
140
-
141
- ```vue
142
- <script setup>
143
- import { computed, onMounted, onUnmounted, ref } from 'vue'
144
-
145
- const scrollY = ref(0)
146
-
147
- const heroOpacity = computed(() => {
148
- return Math.max(0, 1 - scrollY.value / 300)
149
- })
150
-
151
- const scrollOffset = computed(() => {
152
- return scrollY.value * 0.5 // Parallax effect
153
- })
154
-
155
- function handleScroll() {
156
- scrollY.value = window.scrollY
157
- }
158
-
159
- onMounted(() => {
160
- window.addEventListener('scroll', handleScroll, { passive: true })
161
- })
162
-
163
- onUnmounted(() => {
164
- window.removeEventListener('scroll', handleScroll)
165
- })
166
- </script>
167
-
168
- <template>
169
- <div
170
- class="hero"
171
- :style="{
172
- opacity: heroOpacity,
173
- transform: `translateY(${scrollOffset}px)`,
174
- }"
175
- >
176
- <h1>Scroll Down</h1>
177
- </div>
178
- </template>
179
-
180
- <style>
181
- .hero {
182
- height: 100vh;
183
- display: flex;
184
- align-items: center;
185
- justify-content: center;
186
- /* Note: No transition for scroll-based animations - they should be instant */
187
- }
188
- </style>
189
- ```
190
-
191
- ### Color Theme Transition
192
-
193
- ```vue
194
- <script setup>
195
- import { computed, ref } from 'vue'
196
-
197
- const isDark = ref(false)
198
-
199
- const themeStyles = computed(() => ({
200
- '--bg-color': isDark.value ? '#1a1a1a' : '#ffffff',
201
- '--text-color': isDark.value ? '#ffffff' : '#1a1a1a',
202
- backgroundColor: 'var(--bg-color)',
203
- color: 'var(--text-color)',
204
- }))
205
-
206
- function toggleTheme() {
207
- isDark.value = !isDark.value
208
- }
209
- </script>
210
-
211
- <template>
212
- <div class="app" :style="themeStyles">
213
- <button @click="toggleTheme">Toggle Theme</button>
214
- <p>Current theme: {{ isDark ? 'Dark' : 'Light' }}</p>
215
- </div>
216
- </template>
217
-
218
- <style>
219
- .app {
220
- min-height: 100vh;
221
- transition:
222
- background-color 0.5s ease,
223
- color 0.5s ease;
224
- }
225
- </style>
226
- ```
227
-
228
- ## Advanced: Numerical Tweening with Watchers
229
-
230
- For smooth number animations (counters, stats), use watchers with animation libraries:
231
-
232
- ```vue
233
- <script setup>
234
- import gsap from 'gsap'
235
-
236
- import { computed, reactive, ref, watch } from 'vue'
237
-
238
- const targetNumber = ref(0)
239
- const tweened = reactive({ value: 0 })
240
-
241
- // Computed for display
242
- const displayNumber = computed(() => tweened.value)
243
-
244
- watch(targetNumber, (newValue) => {
245
- gsap.to(tweened, {
246
- duration: 0.5,
247
- value: Number(newValue) || 0,
248
- ease: 'power2.out',
249
- })
250
- })
251
- </script>
252
-
253
- <template>
254
- <div>
255
- <input v-model.number="targetNumber" type="number" />
256
- <p class="counter">
257
- {{ displayNumber.toFixed(0) }}
258
- </p>
259
- </div>
260
- </template>
261
- ```
262
-
263
- ## Performance Considerations
264
-
265
- ```vue
266
- <style>
267
- /* GOOD: GPU-accelerated properties */
268
- .element {
269
- transition:
270
- transform 0.3s ease,
271
- opacity 0.3s ease;
272
- }
273
-
274
- /* AVOID: Properties that trigger layout recalculation */
275
- .element {
276
- transition:
277
- width 0.3s ease,
278
- height 0.3s ease,
279
- margin 0.3s ease;
280
- }
281
-
282
- /* For high-frequency updates, consider will-change */
283
- .frequently-animated {
284
- will-change: transform;
285
- }
286
- </style>
287
- ```
@@ -1,99 +0,0 @@
1
- ---
2
- title: Async Component Best Practices
3
- impact: MEDIUM
4
- impactDescription: Poor async component strategy can delay interactivity in SSR apps and create loading UI flicker
5
- type: best-practice
6
- tags: [vue3, async-components, ssr, hydration, performance, ux]
7
- ---
8
-
9
- # Async Component Best Practices
10
-
11
- **Impact: MEDIUM** - Async components should reduce JavaScript cost without degrading perceived performance. Focus on hydration timing in SSR and stable loading UX.
12
-
13
- ## Task List
14
-
15
- - Use lazy hydration strategies for non-critical SSR component trees
16
- - Import only the hydration helpers you actually use
17
- - Keep `loadingComponent` delay near the default `200ms` unless real UX data suggests otherwise
18
- - Configure `delay` and `timeout` together for predictable loading behavior
19
-
20
- ## Use Lazy Hydration Strategies in SSR
21
-
22
- In Vue 3.5+, async components can delay hydration until idle time, visibility, media query match, or user interaction.
23
-
24
- **BAD:**
25
-
26
- ```vue
27
- <script setup lang="ts">
28
- import { defineAsyncComponent } from 'vue'
29
-
30
- const AsyncComments = defineAsyncComponent({
31
- loader: () => import('./Comments.vue'),
32
- })
33
- </script>
34
- ```
35
-
36
- **GOOD:**
37
-
38
- ```vue
39
- <script setup lang="ts">
40
- import { defineAsyncComponent, hydrateOnIdle, hydrateOnVisible } from 'vue'
41
-
42
- const AsyncComments = defineAsyncComponent({
43
- loader: () => import('./Comments.vue'),
44
- hydrate: hydrateOnVisible({ rootMargin: '100px' }),
45
- })
46
-
47
- const AsyncFooter = defineAsyncComponent({
48
- loader: () => import('./Footer.vue'),
49
- hydrate: hydrateOnIdle(5000),
50
- })
51
- </script>
52
- ```
53
-
54
- ## Prevent Loading Spinner Flicker
55
-
56
- Avoid showing loading UI immediately for components that usually resolve quickly.
57
-
58
- **BAD:**
59
-
60
- ```vue
61
- <script setup lang="ts">
62
- import { defineAsyncComponent } from 'vue'
63
-
64
- import LoadingSpinner from './LoadingSpinner.vue'
65
-
66
- const AsyncDashboard = defineAsyncComponent({
67
- loader: () => import('./Dashboard.vue'),
68
- loadingComponent: LoadingSpinner,
69
- delay: 0,
70
- })
71
- </script>
72
- ```
73
-
74
- **GOOD:**
75
-
76
- ```vue
77
- <script setup lang="ts">
78
- import { defineAsyncComponent } from 'vue'
79
-
80
- import ErrorDisplay from './ErrorDisplay.vue'
81
- import LoadingSpinner from './LoadingSpinner.vue'
82
-
83
- const AsyncDashboard = defineAsyncComponent({
84
- loader: () => import('./Dashboard.vue'),
85
- loadingComponent: LoadingSpinner,
86
- errorComponent: ErrorDisplay,
87
- delay: 200,
88
- timeout: 30000,
89
- })
90
- </script>
91
- ```
92
-
93
- ## Delay Guidelines
94
-
95
- | Scenario | Recommended Delay |
96
- | ----------------------------- | ----------------- |
97
- | Small component, fast network | `200ms` |
98
- | Known heavy component | `100ms` |
99
- | Background or non-critical UI | `300-500ms` |