@dianzhong/create-harness-app 0.1.2 → 0.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.
Files changed (39) hide show
  1. package/dist/index.mjs +4 -0
  2. package/package.json +1 -1
  3. package/templates/harness/full/.claude/agents/code-reviewer.md +1 -1
  4. package/templates/harness/full/.claude/agents/harness-reviewer.md +0 -2
  5. package/templates/harness/full/.claude/rules/skills-mcp.md +2 -3
  6. package/templates/harness/full/.claude/settings.json +1 -1
  7. package/templates/harness/full/docs/ai-harness.md +4 -6
  8. package/templates/harness/full/docs/harness-quick-reference.md +1 -1
  9. package/templates/harness/full/docs/review-checklist.md +1 -1
  10. package/templates/harness/full/scripts/verify-skills.mjs +6 -61
  11. package/templates/harness/full/scripts/verify-skills.test.mjs +1 -11
  12. package/templates/harness/full/.agents/skills/find-skills/SKILL.md +0 -143
  13. package/templates/harness/full/.agents/skills/vue-best-practices/LICENSE.md +0 -21
  14. package/templates/harness/full/.agents/skills/vue-best-practices/SKILL.md +0 -155
  15. package/templates/harness/full/.agents/skills/vue-best-practices/SYNC.md +0 -5
  16. package/templates/harness/full/.agents/skills/vue-best-practices/references/animation-class-based-technique.md +0 -258
  17. package/templates/harness/full/.agents/skills/vue-best-practices/references/animation-state-driven-technique.md +0 -287
  18. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-async.md +0 -99
  19. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-data-flow.md +0 -313
  20. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-fallthrough-attrs.md +0 -179
  21. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-keep-alive.md +0 -139
  22. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-slots.md +0 -226
  23. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-suspense.md +0 -231
  24. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-teleport.md +0 -110
  25. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-transition-group.md +0 -131
  26. package/templates/harness/full/.agents/skills/vue-best-practices/references/component-transition.md +0 -135
  27. package/templates/harness/full/.agents/skills/vue-best-practices/references/composables.md +0 -303
  28. package/templates/harness/full/.agents/skills/vue-best-practices/references/directives.md +0 -168
  29. package/templates/harness/full/.agents/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +0 -177
  30. package/templates/harness/full/.agents/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +0 -185
  31. package/templates/harness/full/.agents/skills/vue-best-practices/references/perf-virtualize-large-lists.md +0 -182
  32. package/templates/harness/full/.agents/skills/vue-best-practices/references/plugins.md +0 -178
  33. package/templates/harness/full/.agents/skills/vue-best-practices/references/reactivity.md +0 -371
  34. package/templates/harness/full/.agents/skills/vue-best-practices/references/render-functions.md +0 -227
  35. package/templates/harness/full/.agents/skills/vue-best-practices/references/sfc.md +0 -355
  36. package/templates/harness/full/.agents/skills/vue-best-practices/references/state-management.md +0 -138
  37. package/templates/harness/full/.agents/skills/vue-best-practices/references/updated-hook-performance.md +0 -193
  38. package/templates/harness/full/AGENTS.md +0 -3
  39. 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` |