@turquoisehealth/pit-viper 2.179.1-dev.0 → 2.181.0

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 (33) hide show
  1. package/claude-plugin/.claude-plugin/plugin.json +8 -2
  2. package/claude-plugin/.mcp.json +8 -0
  3. package/claude-plugin/CLAUDE.md +71 -250
  4. package/claude-plugin/README.md +123 -69
  5. package/claude-plugin/skills/pit-viper/SKILL.md +180 -0
  6. package/claude-plugin/skills/pit-viper/references/design-language.md +80 -0
  7. package/claude-plugin/skills/pit-viper/references/design-rules.md +254 -0
  8. package/claude-plugin/skills/pit-viper/references/html-patterns.md +451 -0
  9. package/claude-plugin/skills/pit-viper/references/layout-patterns.md +359 -0
  10. package/claude-plugin/skills/pit-viper/references/patterns-core.md +97 -0
  11. package/claude-plugin/skills/pit-viper/references/theme-guide.md +149 -0
  12. package/claude-plugin/skills/pit-viper/references/vue-guidelines.md +513 -0
  13. package/package.json +3 -3
  14. package/pv-components/dist/stats/vue/base/stats.html +1 -1
  15. package/pv-components/dist/stats/vue/visualizations/stats.html +1 -1
  16. package/pv-components/dist/stats/web/pv-menu-stats.html +1 -1
  17. package/pv-components/dist/stats/web/pv-multi-select-button-stats.html +1 -1
  18. package/pv-components/dist/stats/web/pv-query-builder-input-stats.html +1 -1
  19. package/pv-components/dist/stats/web/pv-select-button-stats.html +1 -1
  20. package/pv-components/dist/vue/base/pv-components-base.mjs +2 -2
  21. package/pv-components/dist/vue/base/pv-components-base.mjs.map +1 -1
  22. package/pv-components/dist/vue/visualizations/components/tables/PvDataTable/filters/filterHelpers.d.ts +1 -1
  23. package/pv-components/dist/vue/visualizations/pv-components-visualizations.mjs +5 -5
  24. package/pv-components/dist/vue/visualizations/pv-components-visualizations.mjs.map +1 -1
  25. package/pv-components/dist/web/components/pv-menu/pv-menu.js +2 -2
  26. package/pv-components/dist/web/components/pv-multi-select-button/pv-multi-select-button.js +2 -2
  27. package/pv-components/dist/web/components/pv-query-builder-input/pv-query-builder-input.js +2 -2
  28. package/pv-components/dist/web/components/pv-select-button/pv-select-button.js +2 -2
  29. package/pv-components/dist/web/pv-components.iife.js +1 -1
  30. package/pv-components/dist/web/pv-components.iife.js.map +1 -1
  31. package/claude-plugin/skills/audit/SKILL.md +0 -125
  32. package/claude-plugin/skills/create/SKILL.md +0 -389
  33. package/claude-plugin/skills/resolve/SKILL.md +0 -142
@@ -0,0 +1,513 @@
1
+ # Vue Guidelines for Pit Viper
2
+
3
+ Use this reference when generating Vue SFC code (Engineer Mode).
4
+
5
+ ## Adding Pit Viper to a Vue Project
6
+
7
+ ### Step 1: Install the package
8
+
9
+ ```bash
10
+ npm install @turquoisehealth/pit-viper
11
+ # or
12
+ pnpm add @turquoisehealth/pit-viper
13
+ ```
14
+
15
+ ### Step 2: Import CSS in your app entry point
16
+
17
+ ```typescript
18
+ // main.ts or App.vue
19
+ import '@turquoisehealth/pit-viper/pit-viper-v2.css'
20
+ // OR for consumer/marketing sites:
21
+ // import '@turquoisehealth/pit-viper/pit-viper-consumer.css'
22
+ ```
23
+
24
+ ### Step 3: Import and use components
25
+
26
+ ```typescript
27
+ import { PvButton, PvInput } from '@turquoisehealth/pit-viper/components'
28
+ ```
29
+
30
+ For complete setup instructions and sandboxes, see the [Storybook Introduction](https://pit-viper-storybook.netlify.app/?path=/docs/introduction--documentation).
31
+
32
+ ## File Structure
33
+
34
+ Components use Vue 3 Composition API with TypeScript. Block order is strict:
35
+
36
+ 1. `<script lang="ts">` — Only for generic type definitions (when needed)
37
+ 2. `<script setup lang="ts">` — Component logic
38
+ 3. `<template>` — Markup
39
+ 4. `<style scoped>` — Only when absolutely necessary (prefer pv-* classes)
40
+
41
+ ## Import Paths
42
+
43
+ Never use barrel exports. Import directly from component paths:
44
+
45
+ ```typescript
46
+ // Base components
47
+ import { PvButton, PvInput, PvModal } from '@turquoisehealth/pit-viper/components'
48
+
49
+ // Visualization components (tables, charts)
50
+ import { PvDataTable, PvChart } from '@turquoisehealth/pit-viper/components/visualizations'
51
+
52
+ // Layout components (must import directly)
53
+ import PvSidePanel from '@turquoisehealth/pit-viper/components/layout/PvSidePanel/PvSidePanel.vue'
54
+ ```
55
+
56
+ ## Props Pattern
57
+
58
+ Define props as interfaces. Use `withDefaults` for default values. Props should be alphabetically ordered.
59
+
60
+ ```typescript
61
+ export interface MyComponentProps {
62
+ disabled?: boolean;
63
+ label: string;
64
+ options: Option[];
65
+ placeholder?: string;
66
+ }
67
+
68
+ const props = withDefaults(defineProps<MyComponentProps>(), {
69
+ disabled: false,
70
+ options: () => [],
71
+ placeholder: 'Select...',
72
+ });
73
+ ```
74
+
75
+ ## Emits Pattern
76
+
77
+ Define emits with kebab-case event names and typed payloads:
78
+
79
+ ```typescript
80
+ const emit = defineEmits<{
81
+ (e: 'value-change', value: string): void;
82
+ (e: 'item-select', item: Option): void;
83
+ }>();
84
+ ```
85
+
86
+ ## Generics Pattern
87
+
88
+ When using generics, declare interfaces in a separate `<script>` block:
89
+
90
+ ```vue
91
+ <script lang="ts">
92
+ export interface ListProps<T> {
93
+ items: T[];
94
+ selected: T | null;
95
+ }
96
+ </script>
97
+
98
+ <script setup lang="ts" generic="T">
99
+ const props = defineProps<ListProps<T>>();
100
+ </script>
101
+ ```
102
+
103
+ ## v-model Patterns
104
+
105
+ ### PvInput
106
+ ```vue
107
+ <PvInput v-model="searchQuery" placeholder="Search..." />
108
+ ```
109
+
110
+ ### PvSelectButton
111
+ ```vue
112
+ <PvSelectButton
113
+ v-model="selected"
114
+ :options="options"
115
+ label="Choose option"
116
+ placeholder="Select..."
117
+ />
118
+ ```
119
+
120
+ ### PvModal
121
+ ```vue
122
+ <PvModal v-model="isOpen" header="Confirm Action">
123
+ <template #body>
124
+ <p>Are you sure?</p>
125
+ </template>
126
+ <template #footer>
127
+ <PvButton variant="ghost" @click="isOpen = false">Cancel</PvButton>
128
+ <PvButton @click="confirm">Confirm</PvButton>
129
+ </template>
130
+ </PvModal>
131
+ ```
132
+
133
+ ### PvTabs
134
+ ```vue
135
+ <PvTabs v-model="activeTab" :tabs="[
136
+ { label: 'Overview', value: 'overview' },
137
+ { label: 'Settings', value: 'settings' },
138
+ ]" />
139
+ ```
140
+
141
+ ## Naming Conventions
142
+
143
+ Use PascalCase for component names AND HTML attributes (not kebab-case):
144
+
145
+ ```vue
146
+ <!-- Correct -->
147
+ <PvButton leftIcon="plus" isLoading />
148
+ <PvSelectButton optionsVariant="checkbox" />
149
+
150
+ <!-- Incorrect -->
151
+ <pv-button left-icon="plus" is-loading />
152
+ ```
153
+
154
+ ## Common Mistakes
155
+
156
+ | Mistake | Correct Approach |
157
+ |---------|------------------|
158
+ | Using kebab-case for props | Use PascalCase: `leftIcon` not `left-icon` |
159
+ | Importing from barrel exports | Import directly from component path |
160
+ | Adding `<style scoped>` blocks | Use pv-* utility classes instead |
161
+ | Inline prop types in defineProps | Define interfaces in types.ts |
162
+ | Using raw `<button>`, `<input>` | Use PvButton, PvInput components |
163
+ | Guessing icon names | Verify at https://pitviper.turquoise.health/visual-style/icons/ |
164
+
165
+ ## Styling
166
+
167
+ Use only Pit Viper utility classes. No custom CSS.
168
+
169
+ ```vue
170
+ <!-- Correct -->
171
+ <div class="pv-flex pv-stack-16">
172
+ <h1 class="pv-heading-1">Title</h1>
173
+ <p class="pv-text-body-md pv-text-subdued">Description</p>
174
+ </div>
175
+
176
+ <!-- Incorrect -->
177
+ <div style="display: flex; margin-bottom: 16px;">
178
+ <h1 style="font-size: 20px;">Title</h1>
179
+ </div>
180
+ ```
181
+
182
+ ## Tables & Charts
183
+
184
+ ### PvDataTable
185
+
186
+ Use for all tabular data. Never use raw `<table>` elements or custom table markup.
187
+
188
+ ```typescript
189
+ import { PvDataTable } from '@turquoisehealth/pit-viper/components/visualizations'
190
+
191
+ const colDefs = [
192
+ { field: 'name', headerName: 'Name', flex: 1 },
193
+ { field: 'email', headerName: 'Email', flex: 1 },
194
+ { field: 'role', headerName: 'Role', width: 120 },
195
+ ]
196
+
197
+ const rowData = ref([
198
+ { name: 'Alice', email: 'alice@example.com', role: 'Admin' },
199
+ { name: 'Bob', email: 'bob@example.com', role: 'Editor' },
200
+ ])
201
+ ```
202
+
203
+ ```vue
204
+ <PvDataTable
205
+ :rowData="rowData"
206
+ :colDefs="colDefs"
207
+ :loading="isLoading"
208
+ style="height: 400px;"
209
+ />
210
+ ```
211
+
212
+ ### PvDataTableWithChart
213
+
214
+ Use when you need a table with an integrated chart visualization.
215
+
216
+ ```typescript
217
+ import { PvDataTableWithChart } from '@turquoisehealth/pit-viper/components/visualizations'
218
+ ```
219
+
220
+ ### PvChart
221
+
222
+ Use for standalone charts. Never use D3, Chart.js, ApexCharts, or other charting libraries.
223
+
224
+ ```typescript
225
+ import { PvChart } from '@turquoisehealth/pit-viper/components/visualizations'
226
+
227
+ const chartOptions = {
228
+ data: [
229
+ { month: 'Jan', revenue: 50000 },
230
+ { month: 'Feb', revenue: 62000 },
231
+ { month: 'Mar', revenue: 58000 },
232
+ ],
233
+ series: [
234
+ {
235
+ type: 'bar',
236
+ xKey: 'month',
237
+ yKey: 'revenue',
238
+ yName: 'Revenue',
239
+ },
240
+ ],
241
+ }
242
+ ```
243
+
244
+ ```vue
245
+ <PvChart :options="chartOptions" style="height: 300px;" />
246
+ ```
247
+
248
+ ### Table & Chart Mistakes
249
+
250
+ | Mistake | Correct Approach |
251
+ |---------|------------------|
252
+ | Using raw `<table>` elements | Use PvDataTable component |
253
+ | Importing D3, Chart.js, ApexCharts | Use PvChart component |
254
+ | Custom table CSS/styling | PvDataTable has built-in Pit Viper theme |
255
+ | Building custom sorting/filtering | PvDataTable has AG Grid features built-in |
256
+
257
+ ## Web Component Compatibility
258
+
259
+ If the component may be used as a web component:
260
+
261
+ - All non-primitive props (not string/number/boolean) must be optional
262
+ - Boolean props default to `false`
263
+ - Use `::slotted()` in styles for Shadow DOM
264
+ - No scoped or conditional slots (`v-if="$slots.name"`)
265
+ - Complex props must be reactive for post-initialization assignment
266
+
267
+ ---
268
+
269
+ ## Anti-Patterns
270
+
271
+ ### Custom Classes on Components
272
+
273
+ Don't invent classes. Use pv-* utilities or inline styles for one-offs.
274
+
275
+ ```vue
276
+ <!-- BAD: Custom classes -->
277
+ <template>
278
+ <div class="settings-container">
279
+ <PvCard class="settings-card">
280
+ <PvButton class="submit-btn" label="Save" />
281
+ </PvCard>
282
+ </div>
283
+ </template>
284
+
285
+ <style scoped>
286
+ .settings-container { padding: 24px; }
287
+ .settings-card { max-width: 500px; }
288
+ .submit-btn { margin-top: 16px; }
289
+ </style>
290
+
291
+ <!-- GOOD: Pit Viper classes + inline for one-offs -->
292
+ <template>
293
+ <div class="pv-inset-square-24">
294
+ <PvCard style="max-width: 500px;">
295
+ <div class="pv-stack-16">
296
+ <PvButton label="Save" />
297
+ </div>
298
+ </PvCard>
299
+ </div>
300
+ </template>
301
+ ```
302
+
303
+ ### Custom CSS / SCSS
304
+
305
+ Never add `<style>` blocks with custom styling. This defeats the design system.
306
+
307
+ ```vue
308
+ <!-- BAD: Custom SCSS -->
309
+ <template>
310
+ <div class="error-label">
311
+ <PvIcon name="info-circle" />
312
+ <span class="error-label--text">{{ message }}</span>
313
+ </div>
314
+ </template>
315
+
316
+ <style lang="scss" scoped>
317
+ .error-label {
318
+ color: $red-base;
319
+ display: flex;
320
+ gap: 8px;
321
+
322
+ &--text {
323
+ @include text-medium;
324
+ }
325
+ }
326
+ </style>
327
+
328
+ <!-- GOOD: Pit Viper classes only -->
329
+ <template>
330
+ <div class="pv-flex pv-text-error" style="--flex-gap: 0.5rem;">
331
+ <PvIcon name="info-circle" />
332
+ <span class="pv-text-body-md">{{ message }}</span>
333
+ </div>
334
+ </template>
335
+ ```
336
+
337
+ ### Raw Tables with Custom Styling
338
+
339
+ Never build custom tables. Use PvDataTable.
340
+
341
+ ```vue
342
+ <!-- BAD: Raw table with custom styling -->
343
+ <template>
344
+ <table class="my-table">
345
+ <thead>
346
+ <tr>
347
+ <th>Name</th>
348
+ <th>Email</th>
349
+ </tr>
350
+ </thead>
351
+ <tbody>
352
+ <tr v-for="user in users" :key="user.id">
353
+ <td>{{ user.name }}</td>
354
+ <td>{{ user.email }}</td>
355
+ </tr>
356
+ </tbody>
357
+ </table>
358
+ </template>
359
+
360
+ <style scoped>
361
+ .my-table { width: 100%; border-collapse: collapse; }
362
+ .my-table th { background: #f5f5f5; padding: 8px; }
363
+ .my-table td { border-bottom: 1px solid #eee; padding: 8px; }
364
+ </style>
365
+
366
+ <!-- GOOD: PvDataTable -->
367
+ <script setup lang="ts">
368
+ import { PvDataTable } from "@turquoisehealth/pit-viper/components/visualizations";
369
+
370
+ const colDefs = [
371
+ { field: "name", headerName: "Name", flex: 1 },
372
+ { field: "email", headerName: "Email", flex: 1 },
373
+ ];
374
+ </script>
375
+
376
+ <template>
377
+ <PvDataTable :rowData="users" :colDefs="colDefs" style="height: 400px;" />
378
+ </template>
379
+ ```
380
+
381
+ ---
382
+
383
+ ## Good Patterns
384
+
385
+ ### Modal with Proper Structure
386
+
387
+ ```vue
388
+ <script setup lang="ts">
389
+ import { PvButton, PvModal } from "@turquoisehealth/pit-viper/components";
390
+ import { ref } from "vue";
391
+
392
+ const showModal = defineModel<boolean>({ required: true });
393
+
394
+ const emit = defineEmits<{
395
+ (e: "confirm"): void;
396
+ }>();
397
+
398
+ const close = () => {
399
+ showModal.value = false;
400
+ };
401
+ </script>
402
+
403
+ <template>
404
+ <PvModal
405
+ v-model="showModal"
406
+ header="Delete Report?"
407
+ @close="close"
408
+ style="--flow-size: 16px; width: 298px"
409
+ >
410
+ <template #body>
411
+ <p class="pv-text-body-sm pv-text-subdued">
412
+ This action cannot be undone.
413
+ </p>
414
+ </template>
415
+ <template #footer>
416
+ <div class="pv-flex" style="--flex-justify: flex-end;">
417
+ <PvButton variant="ghost" @click="close" label="Cancel" size="lg" />
418
+ <PvButton variant="destructive" @click="emit('confirm')" label="Delete" size="lg" />
419
+ </div>
420
+ </template>
421
+ </PvModal>
422
+ </template>
423
+ ```
424
+
425
+ **What makes this good:**
426
+ - Uses `label` attribute on buttons
427
+ - Proper modal structure with `#body` and `#footer` slots
428
+ - Semantic `variant="destructive"` for delete action
429
+ - Inline style for one-off width constraint
430
+ - CSS custom property `--flow-size` instead of custom class
431
+
432
+ ### Form Actions with Feedback
433
+
434
+ ```vue
435
+ <script setup lang="ts">
436
+ import { PvButton, PvToast } from "@turquoisehealth/pit-viper/components";
437
+ import { ref } from "vue";
438
+
439
+ const submitting = ref(false);
440
+ const submitError = ref<string | null>(null);
441
+ const submitSuccess = ref(false);
442
+ </script>
443
+
444
+ <template>
445
+ <div class="pv-flex-vertical pv-full-width">
446
+ <div class="pv-space-between pv-inset-block-24 pv-full-width">
447
+ <PvButton size="xl" label="Cancel" variant="secondary" @click="cancel" />
448
+ <PvButton
449
+ size="xl"
450
+ label="Save"
451
+ @click="submit"
452
+ :disabled="submitting"
453
+ :loading="submitting"
454
+ />
455
+ </div>
456
+ <PvToast
457
+ v-if="submitSuccess"
458
+ icon="check-circle"
459
+ label="Saved successfully!"
460
+ variant="info"
461
+ />
462
+ <PvToast
463
+ v-if="submitError"
464
+ icon="alert-circle"
465
+ :label="submitError"
466
+ variant="error"
467
+ />
468
+ </div>
469
+ </template>
470
+ ```
471
+
472
+ **What makes this good:**
473
+ - Uses `pv-space-between` for button layout
474
+ - Uses `pv-inset-block-24` for vertical padding
475
+ - Uses `:loading` prop instead of custom spinner
476
+ - Uses `PvToast` for feedback instead of custom alerts
477
+
478
+ ### Card with Content Sections
479
+
480
+ ```vue
481
+ <template>
482
+ <PvCard style="max-width: 500px;">
483
+ <template #header>
484
+ <h2 class="pv-heading-3">User Settings</h2>
485
+ </template>
486
+
487
+ <div class="pv-flex-vertical" style="--flex-gap: 1rem;">
488
+ <div>
489
+ <label class="pv-text-title-sm pv-stack-4">Name</label>
490
+ <PvInput v-model="name" placeholder="Full name" />
491
+ </div>
492
+ <div>
493
+ <label class="pv-text-title-sm pv-stack-4">Email</label>
494
+ <PvInput v-model="email" type="email" />
495
+ </div>
496
+ </div>
497
+
498
+ <template #footer>
499
+ <div class="pv-flex" style="--flex-justify: flex-end; --flex-gap: 0.5rem;">
500
+ <PvButton variant="ghost" label="Cancel" @click="cancel" />
501
+ <PvButton label="Save" @click="save" />
502
+ </div>
503
+ </template>
504
+ </PvCard>
505
+ </template>
506
+ ```
507
+
508
+ **What makes this good:**
509
+ - Uses proper card slots (`#header`, default, `#footer`)
510
+ - Uses `--flex-gap` CSS custom property instead of custom class
511
+ - Uses `--flex-justify` for alignment
512
+ - Inline style for max-width constraint (one-off)
513
+ - Primary action on the right
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turquoisehealth/pit-viper",
3
- "version": "2.179.1-dev.0",
3
+ "version": "2.181.0",
4
4
  "description": "Turquoise Health's design system.",
5
5
  "main": "README.md",
6
6
  "publishConfig": {
@@ -138,8 +138,8 @@
138
138
  "build:eleventy": "eleventy",
139
139
  "build:pagefind": "pagefind --site _site",
140
140
  "build:pv-components": "pnpm --filter pv-components run build",
141
- "build:mcp": "pnpm --filter pit-viper-mcp run build",
142
- "build:mcp-index": "pnpm --filter pit-viper-mcp run build:index",
141
+ "build:llm": "pnpm --filter pit-viper-llm run build",
142
+ "build:llm-index": "pnpm --filter pit-viper-llm run build:index",
143
143
  "start": "npm-run-all build:sass --parallel watch:*",
144
144
  "build": "npm-run-all build:*",
145
145
  "test": "pnpm --filter pv-components run test && pnpm --filter pv-storybook run test:e2e",