@dianzhong/create-harness-app 0.1.1 → 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 (39) hide show
  1. package/README.md +68 -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,227 +0,0 @@
1
- ---
2
- title: Render Function Patterns and Performance
3
- impact: MEDIUM
4
- impactDescription: Render functions require explicit patterns for lists, events, v-model, and performance to stay correct and maintainable
5
- type: best-practice
6
- tags: [vue3, render-function, h, v-model, directives, performance, jsx]
7
- ---
8
-
9
- # Render Function Patterns and Performance
10
-
11
- **Impact: MEDIUM** - Render functions are powerful but opt out of template compiler optimizations. Use them intentionally and apply the key patterns below to keep output correct and performant.
12
-
13
- ## Task List
14
-
15
- - Prefer templates; use render functions only when templates cannot express the logic
16
- - Always add stable keys when rendering lists with `h()`/JSX
17
- - Use `withModifiers` / `withKeys` for event modifiers
18
- - Implement `v-model` via `modelValue` + `onUpdate:modelValue`
19
- - Apply custom directives with `withDirectives`
20
- - Use functional components for stateless presentational UI
21
-
22
- ## Prefer templates over render functions
23
-
24
- **BAD:**
25
-
26
- ```vue
27
- <script setup>
28
- import { h, ref } from 'vue'
29
-
30
- const count = ref(0)
31
- const render = () => h('div', `Count: ${count.value}`)
32
- </script>
33
- ```
34
-
35
- **GOOD:**
36
-
37
- ```vue
38
- <script setup>
39
- import { ref } from 'vue'
40
-
41
- const count = ref(0)
42
- </script>
43
-
44
- <template>
45
- <div>Count: {{ count }}</div>
46
- </template>
47
- ```
48
-
49
- ## Always add keys for list rendering
50
-
51
- **BAD:**
52
-
53
- ```javascript
54
- import { h, ref } from 'vue'
55
-
56
- export default {
57
- setup() {
58
- const items = ref([{ id: 1, name: 'Apple' }])
59
-
60
- return () =>
61
- h(
62
- 'ul',
63
- items.value.map((item) => h('li', item.name)),
64
- )
65
- },
66
- }
67
- ```
68
-
69
- **GOOD:**
70
-
71
- ```javascript
72
- import { h, ref } from 'vue'
73
-
74
- export default {
75
- setup() {
76
- const items = ref([{ id: 1, name: 'Apple' }])
77
-
78
- return () =>
79
- h(
80
- 'ul',
81
- items.value.map((item) => h('li', { key: item.id }, item.name)),
82
- )
83
- },
84
- }
85
- ```
86
-
87
- ## Use `withModifiers` / `withKeys` for event modifiers
88
-
89
- **BAD:**
90
-
91
- ```javascript
92
- import { h } from 'vue'
93
-
94
- export default {
95
- setup() {
96
- const handleClick = (e) => {
97
- e.stopPropagation()
98
- e.preventDefault()
99
- }
100
-
101
- return () => h('button', { onClick: handleClick }, 'Click')
102
- },
103
- }
104
- ```
105
-
106
- **GOOD:**
107
-
108
- ```javascript
109
- import { h, withKeys, withModifiers } from 'vue'
110
-
111
- export default {
112
- setup() {
113
- const handleClick = () => {}
114
- const handleEnter = () => {}
115
-
116
- return () =>
117
- h('div', [
118
- h(
119
- 'button',
120
- {
121
- onClick: withModifiers(handleClick, ['stop', 'prevent']),
122
- },
123
- 'Click',
124
- ),
125
- h('input', {
126
- onKeyup: withKeys(handleEnter, ['enter']),
127
- }),
128
- ])
129
- },
130
- }
131
- ```
132
-
133
- ## Implement `v-model` explicitly
134
-
135
- **BAD:**
136
-
137
- ```javascript
138
- import { h, ref } from 'vue'
139
-
140
- import CustomInput from './CustomInput.vue'
141
-
142
- export default {
143
- setup() {
144
- const text = ref('')
145
- return () => h(CustomInput, { modelValue: text.value })
146
- },
147
- }
148
- ```
149
-
150
- **GOOD:**
151
-
152
- ```javascript
153
- import { h, ref } from 'vue'
154
-
155
- import CustomInput from './CustomInput.vue'
156
-
157
- export default {
158
- setup() {
159
- const text = ref('')
160
- return () =>
161
- h(CustomInput, {
162
- modelValue: text.value,
163
- 'onUpdate:modelValue': (value) => {
164
- text.value = value
165
- },
166
- })
167
- },
168
- }
169
- ```
170
-
171
- ## Use `withDirectives` for custom directives
172
-
173
- **BAD:**
174
-
175
- ```javascript
176
- import { h } from 'vue'
177
-
178
- const vFocus = { mounted: (el) => el.focus() }
179
-
180
- export default {
181
- setup() {
182
- return () => h('input', { 'v-focus': true })
183
- },
184
- }
185
- ```
186
-
187
- **GOOD:**
188
-
189
- ```javascript
190
- import { h, withDirectives } from 'vue'
191
-
192
- const vFocus = { mounted: (el) => el.focus() }
193
-
194
- export default {
195
- setup() {
196
- return () => withDirectives(h('input'), [[vFocus]])
197
- },
198
- }
199
- ```
200
-
201
- ## Prefer functional components for stateless UI
202
-
203
- **BAD:**
204
-
205
- ```javascript
206
- import { h } from 'vue'
207
-
208
- export default {
209
- setup() {
210
- return () => h('span', { class: 'badge' }, 'New')
211
- },
212
- }
213
- ```
214
-
215
- **GOOD:**
216
-
217
- ```javascript
218
- import { h } from 'vue'
219
-
220
- function Badge(props, { slots }) {
221
- return h('span', { class: 'badge' }, slots.default?.())
222
- }
223
-
224
- Badge.props = ['variant']
225
-
226
- export default Badge
227
- ```
@@ -1,355 +0,0 @@
1
- ---
2
- title: Single-File Component Structure, Styling, and Template Patterns
3
- impact: MEDIUM
4
- impactDescription: Consistent SFC structure and styling choices improve maintainability, tooling support, and render performance
5
- type: best-practice
6
- tags:
7
- [
8
- vue3,
9
- sfc,
10
- scoped-css,
11
- styles,
12
- build-tools,
13
- performance,
14
- template,
15
- v-html,
16
- v-for,
17
- computed,
18
- v-if,
19
- v-show,
20
- ]
21
- ---
22
-
23
- # Single-File Component Structure, Styling, and Template Patterns
24
-
25
- **Impact: MEDIUM** - Using SFCs with consistent structure and performant styling keeps components easier to maintain and avoids unnecessary render overhead.
26
-
27
- ## Task List
28
-
29
- - Use `.vue` SFCs instead of separate `.js`/`.ts` and `.css` files for components
30
- - Colocate template, script, and styles in the same SFC by default
31
- - Use PascalCase for component names in templates and filenames
32
- - Prefer component-scoped styles
33
- - Prefer class selectors (not element selectors) in scoped CSS for performance
34
- - Access DOM / component refs with `useTemplateRef()` in Vue 3.5+
35
- - Use camelCase keys in `:style` bindings for consistency and IDE support
36
- - Use `v-for` and `v-if` correctly
37
- - Never use `v-html` with untrusted/user-provided content
38
- - Choose `v-if` vs `v-show` based on toggle frequency and initial render cost
39
-
40
- ## Colocate template, script, and styles
41
-
42
- **BAD:**
43
-
44
- ```
45
- components/
46
- ├── UserCard.vue
47
- ├── UserCard.js
48
- └── UserCard.css
49
- ```
50
-
51
- **GOOD:**
52
-
53
- ```vue
54
- <!-- components/UserCard.vue -->
55
- <script setup>
56
- import { computed } from 'vue'
57
-
58
- const props = defineProps({
59
- user: { type: Object, required: true },
60
- })
61
-
62
- const displayName = computed(() => `${props.user.firstName} ${props.user.lastName}`)
63
- </script>
64
-
65
- <template>
66
- <div class="user-card">
67
- <h3 class="name">
68
- {{ displayName }}
69
- </h3>
70
- </div>
71
- </template>
72
-
73
- <style scoped>
74
- .user-card {
75
- padding: 1rem;
76
- }
77
-
78
- .name {
79
- margin: 0;
80
- }
81
- </style>
82
- ```
83
-
84
- ## Use PascalCase for component names
85
-
86
- **BAD:**
87
-
88
- ```vue
89
- <script setup>
90
- import userProfile from './user-profile.vue'
91
- </script>
92
-
93
- <template>
94
- <user-profile :user="currentUser" />
95
- </template>
96
- ```
97
-
98
- **GOOD:**
99
-
100
- ```vue
101
- <script setup>
102
- import UserProfile from './UserProfile.vue'
103
- </script>
104
-
105
- <template>
106
- <UserProfile :user="currentUser" />
107
- </template>
108
- ```
109
-
110
- ## Best practices for `<style>` block in SFCs
111
-
112
- ### Prefer component-scoped styles
113
-
114
- - Use `<style scoped>` for styles that belong to a component.
115
- - Keep **global CSS** in a dedicated file (e.g. `src/assets/main.css`) for resets, typography, tokens, etc.
116
- - Use `:deep()` sparingly (edge cases only).
117
-
118
- **BAD:**
119
-
120
- ```vue
121
- <style>
122
- /* ❌ leaks everywhere */
123
- button {
124
- border-radius: 999px;
125
- }
126
- </style>
127
- ```
128
-
129
- **GOOD:**
130
-
131
- ```vue
132
- <style scoped>
133
- .button {
134
- border-radius: 999px;
135
- }
136
- </style>
137
- ```
138
-
139
- **GOOD:**
140
-
141
- ```css
142
- /* src/assets/main.css */
143
- /* ✅ resets, tokens, typography, app-wide rules */
144
- :root {
145
- --radius: 999px;
146
- }
147
- ```
148
-
149
- ### Use class selectors in scoped CSS
150
-
151
- **BAD:**
152
-
153
- ```vue
154
- <template>
155
- <article>
156
- <h1>{{ title }}</h1>
157
- <p>{{ subtitle }}</p>
158
- </article>
159
- </template>
160
-
161
- <style scoped>
162
- article {
163
- max-width: 800px;
164
- }
165
- h1 {
166
- font-size: 2rem;
167
- }
168
- p {
169
- line-height: 1.6;
170
- }
171
- </style>
172
- ```
173
-
174
- **GOOD:**
175
-
176
- ```vue
177
- <template>
178
- <article class="article">
179
- <h1 class="article-title">
180
- {{ title }}
181
- </h1>
182
- <p class="article-subtitle">
183
- {{ subtitle }}
184
- </p>
185
- </article>
186
- </template>
187
-
188
- <style scoped>
189
- .article {
190
- max-width: 800px;
191
- }
192
- .article-title {
193
- font-size: 2rem;
194
- }
195
- .article-subtitle {
196
- line-height: 1.6;
197
- }
198
- </style>
199
- ```
200
-
201
- ## Access DOM / component refs with `useTemplateRef()`
202
-
203
- For Vue 3.5+: use `useTemplateRef()` to access template refs.
204
-
205
- ```vue
206
- <script setup lang="ts">
207
- import { onMounted, useTemplateRef } from 'vue'
208
-
209
- const inputRef = useTemplateRef<HTMLInputElement>('input')
210
-
211
- onMounted(() => {
212
- inputRef.value?.focus()
213
- })
214
- </script>
215
-
216
- <template>
217
- <input ref="input" />
218
- </template>
219
- ```
220
-
221
- ## Use camelCase in `:style` bindings
222
-
223
- **BAD:**
224
-
225
- ```vue
226
- <template>
227
- <div :style="{ 'font-size': `${fontSize}px`, 'background-color': bg }">Content</div>
228
- </template>
229
- ```
230
-
231
- **GOOD:**
232
-
233
- ```vue
234
- <template>
235
- <div :style="{ fontSize: `${fontSize}px`, backgroundColor: bg }">Content</div>
236
- </template>
237
- ```
238
-
239
- ## Use `v-for` and `v-if` correctly
240
-
241
- ### Always provide a stable `:key`
242
-
243
- - Prefer primitive keys (`string | number`).
244
- - Avoid using objects as keys.
245
-
246
- **GOOD:**
247
-
248
- ```vue
249
- <li v-for="item in items" :key="item.id">
250
- <input v-model="item.text" />
251
- </li>
252
- ```
253
-
254
- ### Avoid `v-if` and `v-for` on the same element
255
-
256
- It leads to unclear intent and unnecessary work.
257
- ([Reference](https://vuejs.org/guide/essentials/list.html#v-for-with-v-if))
258
-
259
- **To filter items**
260
- **BAD:**
261
-
262
- ```vue
263
- <li v-for="user in users" v-if="user.active" :key="user.id">
264
- {{ user.name }}
265
- </li>
266
- ```
267
-
268
- **GOOD:**
269
-
270
- ```vue
271
- <script setup lang="ts">
272
- import { computed } from 'vue'
273
-
274
- const activeUsers = computed(() => users.value.filter((u) => u.active))
275
- </script>
276
-
277
- <template>
278
- <li v-for="user in activeUsers" :key="user.id">
279
- {{ user.name }}
280
- </li>
281
- </template>
282
- ```
283
-
284
- **To conditionally show/hide the entire list**
285
- **GOOD:**
286
-
287
- ```vue
288
- <ul v-if="shouldShowUsers">
289
- <li v-for="user in users" :key="user.id">
290
- {{ user.name }}
291
- </li>
292
- </ul>
293
- ```
294
-
295
- ## Never render untrusted HTML with `v-html`
296
-
297
- **BAD:**
298
-
299
- ```vue
300
- <template>
301
- <!-- DANGEROUS: untrusted input can inject scripts -->
302
- <article v-html="userProvidedContent" />
303
- </template>
304
- ```
305
-
306
- **GOOD:**
307
-
308
- ```vue
309
- <script setup>
310
- import DOMPurify from 'dompurify'
311
-
312
- import { computed } from 'vue'
313
-
314
- const props = defineProps<{
315
- trustedHtml?: string
316
- plainText: string
317
- }>()
318
-
319
- const safeHtml = computed(() => DOMPurify.sanitize(props.trustedHtml ?? ''))
320
- </script>
321
-
322
- <template>
323
- <!-- Preferred: escaped interpolation -->
324
- <p>{{ props.plainText }}</p>
325
-
326
- <!-- Only for trusted/sanitized HTML -->
327
- <article v-html="safeHtml" />
328
- </template>
329
- ```
330
-
331
- ## Choose `v-if` vs `v-show` by toggle behavior
332
-
333
- **BAD:**
334
-
335
- ```vue
336
- <template>
337
- <!-- Frequent toggles with v-if cause repeated mount/unmount -->
338
- <ComplexPanel v-if="isPanelOpen" />
339
-
340
- <!-- Rarely shown content with v-show pays initial render cost -->
341
- <AdminPanel v-show="isAdmin" />
342
- </template>
343
- ```
344
-
345
- **GOOD:**
346
-
347
- ```vue
348
- <template>
349
- <!-- Frequent toggles: keep in DOM, toggle display -->
350
- <ComplexPanel v-show="isPanelOpen" />
351
-
352
- <!-- Rare condition: lazy render only when true -->
353
- <AdminPanel v-if="isAdmin" />
354
- </template>
355
- ```
@@ -1,138 +0,0 @@
1
- ---
2
- title: State Management Strategy
3
- impact: HIGH
4
- impactDescription: Choosing the wrong store pattern can cause SSR request leaks, brittle mutation flows, and poor scaling
5
- type: best-practice
6
- tags: [vue3, state-management, pinia, composables, ssr, vueuse]
7
- ---
8
-
9
- # State Management Strategy
10
-
11
- **Impact: HIGH** - Use the lightest state solution that fits your app architecture. SPA-only apps can use lightweight global composables, while SSR/Nuxt apps should default to Pinia for request-safe isolation and predictable tooling.
12
-
13
- ## Task List
14
-
15
- - Keep state local first, then promote to shared/global only when needed
16
- - Use singleton composables only in non-SSR applications
17
- - Expose global state as readonly and mutate through explicit actions
18
- - Prefer Pinia for SSR/Nuxt, large apps, and advanced debugging/plugin needs
19
- - Avoid exporting mutable module-level reactive state directly
20
-
21
- ## Choose the Lightest Store Approach
22
-
23
- - **Feature composable:** Default for reusable logic with local/feature-level state.
24
- - **Singleton composable or VueUse `createGlobalState`:** Small non-SSR apps needing shared app state.
25
- - **Pinia:** SSR/Nuxt apps, medium-to-large apps, and cases requiring DevTools, plugins, or action tracing.
26
-
27
- ## Avoid Exporting Mutable Module State
28
-
29
- **BAD:**
30
-
31
- ```ts
32
- // store/cart.ts
33
- import { reactive } from 'vue'
34
-
35
- export const cart = reactive({
36
- items: [] as Array<{ id: string; qty: number }>,
37
- })
38
- ```
39
-
40
- **GOOD:**
41
-
42
- ```ts
43
- // composables/useCartStore.ts
44
- import { reactive, readonly } from 'vue'
45
-
46
- let _store: ReturnType<typeof createCartStore> | null = null
47
-
48
- function createCartStore() {
49
- const state = reactive({
50
- items: [] as Array<{ id: string; qty: number }>,
51
- })
52
-
53
- function addItem(id: string, qty = 1) {
54
- const existing = state.items.find((item) => item.id === id)
55
- if (existing) {
56
- existing.qty += qty
57
- return
58
- }
59
- state.items.push({ id, qty })
60
- }
61
-
62
- return {
63
- state: readonly(state),
64
- addItem,
65
- }
66
- }
67
-
68
- export function useCartStore() {
69
- if (!_store) _store = createCartStore()
70
- return _store
71
- }
72
- ```
73
-
74
- ## Do Not Use Runtime Singletons in SSR
75
-
76
- Module singletons live for the runtime lifetime. In SSR this can leak state between requests.
77
-
78
- **BAD:**
79
-
80
- ```ts
81
- // shared singleton reused across requests
82
- const cartStore = useCartStore()
83
-
84
- export function useServerCart() {
85
- return cartStore
86
- }
87
- ```
88
-
89
- **GOOD:**
90
-
91
- > `pinia` dependency required.
92
-
93
- ```ts
94
- // stores/cart.ts
95
- import { defineStore } from 'pinia'
96
-
97
- export const useCartStore = defineStore('cart', {
98
- state: () => ({
99
- items: [] as Array<{ id: string; qty: number }>,
100
- }),
101
- actions: {
102
- addItem(id: string, qty = 1) {
103
- const existing = this.items.find((item) => item.id === id)
104
- if (existing) {
105
- existing.qty += qty
106
- return
107
- }
108
- this.items.push({ id, qty })
109
- },
110
- },
111
- })
112
- ```
113
-
114
- ## Use `createGlobalState` for Small SPA Global State
115
-
116
- > `@vueuse/core` dependency required.
117
-
118
- If the app is non-SSR and already uses VueUse, `createGlobalState` removes singleton boilerplate.
119
-
120
- ```ts
121
- import { createGlobalState } from '@vueuse/core'
122
- import { computed, ref } from 'vue'
123
-
124
- export const useAuthState = createGlobalState(() => {
125
- const token = ref<string | null>(null)
126
- const isAuthenticated = computed(() => token.value !== null)
127
-
128
- function setToken(next: string | null) {
129
- token.value = next
130
- }
131
-
132
- return {
133
- token,
134
- isAuthenticated,
135
- setToken,
136
- }
137
- })
138
- ```