@gallopsystems/agent-skills 1.0.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 (52) hide show
  1. package/README.md +137 -0
  2. package/package.json +26 -0
  3. package/plugins/doctl/.claude-plugin/plugin.json +8 -0
  4. package/plugins/doctl/skills/doctl/SKILL.md +93 -0
  5. package/plugins/kysely-postgres/.claude-plugin/plugin.json +8 -0
  6. package/plugins/kysely-postgres/skills/kysely-postgres/SKILL.md +1101 -0
  7. package/plugins/kysely-postgres/skills/kysely-postgres/references/aggregations.ts +167 -0
  8. package/plugins/kysely-postgres/skills/kysely-postgres/references/ctes.ts +165 -0
  9. package/plugins/kysely-postgres/skills/kysely-postgres/references/expressions.ts +272 -0
  10. package/plugins/kysely-postgres/skills/kysely-postgres/references/joins.ts +206 -0
  11. package/plugins/kysely-postgres/skills/kysely-postgres/references/json-arrays.ts +398 -0
  12. package/plugins/kysely-postgres/skills/kysely-postgres/references/mutations.ts +199 -0
  13. package/plugins/kysely-postgres/skills/kysely-postgres/references/orderby-pagination.ts +117 -0
  14. package/plugins/kysely-postgres/skills/kysely-postgres/references/relations.ts +176 -0
  15. package/plugins/kysely-postgres/skills/kysely-postgres/references/select-where.ts +146 -0
  16. package/plugins/linear/.claude-plugin/plugin.json +8 -0
  17. package/plugins/linear/skills/linear/SKILL.md +1040 -0
  18. package/plugins/linear/skills/linear/bin/linear.mjs +1228 -0
  19. package/plugins/linear/skills/linear/tech-stack.md +273 -0
  20. package/plugins/nitro-testing/.claude-plugin/plugin.json +8 -0
  21. package/plugins/nitro-testing/skills/nitro-testing/SKILL.md +497 -0
  22. package/plugins/nitro-testing/skills/nitro-testing/async-testing.md +270 -0
  23. package/plugins/nitro-testing/skills/nitro-testing/ci-setup.md +226 -0
  24. package/plugins/nitro-testing/skills/nitro-testing/examples/global-setup.ts +90 -0
  25. package/plugins/nitro-testing/skills/nitro-testing/examples/handler.test.ts +167 -0
  26. package/plugins/nitro-testing/skills/nitro-testing/examples/setup.ts +29 -0
  27. package/plugins/nitro-testing/skills/nitro-testing/examples/test-utils-index.ts +297 -0
  28. package/plugins/nitro-testing/skills/nitro-testing/examples/vitest.config.ts +42 -0
  29. package/plugins/nitro-testing/skills/nitro-testing/factories.md +278 -0
  30. package/plugins/nitro-testing/skills/nitro-testing/frontend-testing.md +512 -0
  31. package/plugins/nitro-testing/skills/nitro-testing/test-utils.md +262 -0
  32. package/plugins/nitro-testing/skills/nitro-testing/transaction-rollback.md +183 -0
  33. package/plugins/nitro-testing/skills/nitro-testing/vitest-config.md +236 -0
  34. package/plugins/nuxt-nitro-api/.claude-plugin/plugin.json +8 -0
  35. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/SKILL.md +260 -0
  36. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/auth-patterns.md +228 -0
  37. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/composables-utils.md +174 -0
  38. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/deep-linking.md +190 -0
  39. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-middleware.ts +32 -0
  40. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/auth-utils.ts +51 -0
  41. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/deep-link-page.vue +61 -0
  42. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/service-util.ts +63 -0
  43. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/sse-endpoint.ts +59 -0
  44. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/examples/validation-endpoint.ts +38 -0
  45. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/fetch-patterns.md +178 -0
  46. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/nitro-tasks.md +243 -0
  47. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/page-structure.md +162 -0
  48. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/server-services.md +238 -0
  49. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/sse.md +221 -0
  50. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/ssr-client.md +166 -0
  51. package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/validation.md +131 -0
  52. package/scripts/link-skills.mjs +252 -0
@@ -0,0 +1,497 @@
1
+ ---
2
+ name: nitro-testing
3
+ description: Test Nuxt 3 / Nitro applications - both API handlers (real PostgreSQL, transaction rollback) and frontend components (@nuxt/test-utils, mountSuspended).
4
+ ---
5
+
6
+ # Nuxt / Nitro Testing Patterns
7
+
8
+ Test Nuxt 3 applications end-to-end: API handlers with real PostgreSQL using transaction rollback isolation, and frontend components with @nuxt/test-utils.
9
+
10
+ ## When to Use This Skill
11
+
12
+ Use this skill when:
13
+ - Testing Nuxt 3 / Nitro API handlers
14
+ - Testing Vue components, pages, or composables in Nuxt
15
+ - Using Kysely or another query builder with PostgreSQL
16
+ - Need real database testing (not mocks)
17
+ - Want fast, isolated tests without truncation
18
+
19
+ ## Reference Files
20
+
21
+ **Backend (API Handlers):**
22
+ - [transaction-rollback.md](./transaction-rollback.md) - Core isolation pattern with Vitest fixtures
23
+ - [test-utils.md](./test-utils.md) - Mock events, stubs, and assertion helpers
24
+ - [factories.md](./factories.md) - Transaction-bound factory pattern
25
+ - [vitest-config.md](./vitest-config.md) - Vitest configuration for Nitro
26
+ - [ci-setup.md](./ci-setup.md) - GitHub Actions with PostgreSQL service
27
+ - [async-testing.md](./async-testing.md) - Testing background tasks and automations
28
+
29
+ **Frontend (Components/Pages):**
30
+ - [frontend-testing.md](./frontend-testing.md) - Component testing with @nuxt/test-utils
31
+
32
+ ## Example Files
33
+
34
+ - [test-utils-index.ts](./examples/test-utils-index.ts) - Complete test utilities module
35
+ - [global-setup.ts](./examples/global-setup.ts) - Database reset and migration
36
+ - [setup.ts](./examples/setup.ts) - Per-file setup with stubs
37
+ - [handler.test.ts](./examples/handler.test.ts) - Example API handler test
38
+ - [vitest.config.ts](./examples/vitest.config.ts) - Vitest configuration
39
+
40
+ ## Core Concept: Transaction Rollback
41
+
42
+ Instead of truncating tables between tests, each test runs inside a database transaction that rolls back at the end:
43
+
44
+ ```typescript
45
+ // Each test gets isolated factories and db access
46
+ test("creates user", async ({ factories, db }) => {
47
+ const user = await factories.user({ email: "test@example.com" });
48
+
49
+ // Test your handler
50
+ const event = mockPost({}, { name: "New Item" });
51
+ const result = await handler(event);
52
+
53
+ // Verify in database
54
+ const saved = await db.selectFrom("item").selectAll().execute();
55
+ expect(saved).toHaveLength(1);
56
+ });
57
+ // Transaction auto-rolls back - no cleanup needed
58
+ ```
59
+
60
+ Benefits:
61
+ - **Fast**: No DELETE/TRUNCATE between tests
62
+ - **Isolated**: Tests can't affect each other
63
+ - **Real SQL**: Catches actual database issues
64
+ - **Simple**: No manual cleanup
65
+
66
+ ## Quick Setup
67
+
68
+ ### 1. Install Dependencies
69
+
70
+ ```bash
71
+ yarn add -D vitest @vitest/coverage-v8
72
+ ```
73
+
74
+ ### 2. Create Test Utils Structure
75
+
76
+ ```
77
+ server/
78
+ test-utils/
79
+ index.ts # Factories, fixtures, mock helpers
80
+ global-setup.ts # Runs once: reset DB, run migrations
81
+ setup.ts # Runs per-file: stub auto-imports
82
+ ```
83
+
84
+ ### 3. Configure Vitest
85
+
86
+ ```typescript
87
+ // vitest.config.ts
88
+ import { defineConfig } from "vitest/config";
89
+ import path from "path";
90
+
91
+ export default defineConfig({
92
+ test: {
93
+ globals: true,
94
+ environment: "node",
95
+ globalSetup: ["./server/test-utils/global-setup.ts"],
96
+ setupFiles: ["./server/test-utils/setup.ts"],
97
+ },
98
+ resolve: {
99
+ alias: {
100
+ "~": path.resolve(__dirname),
101
+ },
102
+ },
103
+ });
104
+ ```
105
+
106
+ ### 4. Write Tests
107
+
108
+ ```typescript
109
+ // server/api/users/index.post.test.ts
110
+ import { describe, test, expect, mockPost, expectHttpError } from "~/server/test-utils";
111
+ import handler from "./index.post";
112
+
113
+ describe("POST /api/users", () => {
114
+ test("creates user with valid data", async ({ factories: _, db }) => {
115
+ const event = mockPost({}, {
116
+ email: "new@example.com",
117
+ name: "New User"
118
+ });
119
+ const result = await handler(event);
120
+
121
+ expect(result.id).toBeDefined();
122
+ expect(result.email).toBe("new@example.com");
123
+
124
+ // Verify persisted
125
+ const saved = await db
126
+ .selectFrom("user")
127
+ .where("id", "=", result.id)
128
+ .selectAll()
129
+ .executeTakeFirst();
130
+ expect(saved?.name).toBe("New User");
131
+ });
132
+
133
+ test("throws 400 for missing email", async ({ factories: _ }) => {
134
+ const event = mockPost({}, { name: "No Email" });
135
+ await expectHttpError(handler(event), { statusCode: 400 });
136
+ });
137
+ });
138
+ ```
139
+
140
+ ## Key Patterns
141
+
142
+ ### Mock Event Helpers
143
+
144
+ ```typescript
145
+ // GET with route params and query
146
+ const event = mockGet({ id: 123 }, { include: "details" });
147
+
148
+ // POST with body
149
+ const event = mockPost({}, { name: "Test", status: "active" });
150
+
151
+ // PATCH with route params and body
152
+ const event = mockPatch({ id: 123 }, { status: "completed" });
153
+
154
+ // DELETE with route params
155
+ const event = mockDelete({ id: 123 });
156
+ ```
157
+
158
+ ### Factory Pattern
159
+
160
+ ```typescript
161
+ test("lists user's projects", async ({ factories }) => {
162
+ // Factories are transaction-bound - auto-rolled back
163
+ const user = await factories.user();
164
+ const project1 = await factories.project({ ownerId: user.id });
165
+ const project2 = await factories.project({ ownerId: user.id });
166
+
167
+ const event = mockGet({ userId: user.id });
168
+ const result = await handler(event);
169
+
170
+ expect(result).toHaveLength(2);
171
+ });
172
+ ```
173
+
174
+ ### Testing with Related Data
175
+
176
+ ```typescript
177
+ test("returns task with job details", async ({ factories }) => {
178
+ // Factories auto-create dependencies
179
+ const job = await factories.job(); // Creates project automatically
180
+ const task = await factories.task({ jobId: job.id });
181
+
182
+ const event = mockGet({ id: task.id });
183
+ const result = await handler(event);
184
+
185
+ expect(result.job.id).toBe(job.id);
186
+ expect(result.job.project).toBeDefined();
187
+ });
188
+ ```
189
+
190
+ ### Testing Error Cases
191
+
192
+ ```typescript
193
+ test("returns 404 for non-existent resource", async ({ factories: _ }) => {
194
+ const event = mockGet({ id: 999999 });
195
+ await expectHttpError(handler(event), {
196
+ statusCode: 404,
197
+ message: "Not found",
198
+ });
199
+ });
200
+
201
+ test("returns 400 for invalid input", async ({ factories: _ }) => {
202
+ const event = mockPost({}, { invalidField: true });
203
+ await expectHttpError(handler(event), { statusCode: 400 });
204
+ });
205
+ ```
206
+
207
+ ## Auto-Import Stubs
208
+
209
+ The setup file stubs Nuxt/Nitro auto-imports:
210
+
211
+ | Stub | Purpose |
212
+ |------|---------|
213
+ | `defineEventHandler` | Unwraps to return handler directly |
214
+ | `getUserSession` | Returns test user (configurable) |
215
+ | `useDatabase` | Returns test transaction |
216
+ | `createError` | Creates H3-style errors |
217
+ | `getValidatedQuery` | Validates mock query params |
218
+ | `readValidatedBody` | Validates mock body |
219
+ | `getRouterParam` | Returns mock route params |
220
+
221
+ ## Key Gotchas
222
+
223
+ 1. **Always destructure `factories`** - Even if unused, it sets up the transaction:
224
+ ```typescript
225
+ test("...", async ({ factories: _ }) => { ... });
226
+ ```
227
+
228
+ 2. **Don't use top-level db imports** - Use the `db` fixture instead:
229
+ ```typescript
230
+ // ❌ Wrong - uses real db, not transaction
231
+ import { db } from "../utils/db";
232
+
233
+ // ✅ Right - uses test transaction
234
+ test("...", async ({ db }) => { ... });
235
+ ```
236
+
237
+ 3. **Nested transactions work** - Code that calls `db.transaction()` works because we patch the prototype
238
+
239
+ 4. **Test file location** - Co-locate with handlers: `handler.ts` → `handler.test.ts`
240
+
241
+ 5. **Separate test database** - Always use a dedicated test DB (`myapp-test`, not `myapp`)
242
+
243
+ 6. **CI needs PostgreSQL service** - See [ci-setup.md](./ci-setup.md) for GitHub Actions config
244
+
245
+ ---
246
+
247
+ ## Frontend Testing
248
+
249
+ Test Vue components and pages in Nuxt using `@nuxt/test-utils` with `mountSuspended`.
250
+
251
+ ### Setup
252
+
253
+ **Dependencies:**
254
+ ```bash
255
+ yarn add -D @nuxt/test-utils @vue/test-utils happy-dom
256
+ ```
257
+
258
+ **Vitest Config** - Use environment variable to separate frontend/backend:
259
+
260
+ ```typescript
261
+ // vitest.config.ts
262
+ import { defineConfig } from "vitest/config";
263
+ import { defineVitestConfig } from "@nuxt/test-utils/config";
264
+
265
+ const isNuxtEnv = process.env.VITEST_ENV === "nuxt";
266
+
267
+ export default isNuxtEnv
268
+ ? defineVitestConfig({
269
+ test: {
270
+ environment: "nuxt",
271
+ globals: true,
272
+ include: [
273
+ "components/**/*.test.ts",
274
+ "pages/**/*.test.ts",
275
+ "utils/**/*.test.ts",
276
+ ],
277
+ },
278
+ })
279
+ : defineConfig({
280
+ // ... backend config
281
+ });
282
+ ```
283
+
284
+ **Package.json scripts:**
285
+ ```json
286
+ {
287
+ "scripts": {
288
+ "test": "vitest",
289
+ "test:frontend": "VITEST_ENV=nuxt vitest",
290
+ "test:frontend:run": "VITEST_ENV=nuxt vitest run"
291
+ }
292
+ }
293
+ ```
294
+
295
+ **Nuxt test config** (`nuxt.config.test.ts`):
296
+ ```typescript
297
+ export default defineNuxtConfig({
298
+ modules: ["@primevue/nuxt-module"], // Include UI libraries
299
+ ssr: false, // Disable SSR for simpler component testing
300
+ });
301
+ ```
302
+
303
+ ### File Organization
304
+
305
+ Co-locate tests with source files:
306
+ ```
307
+ components/
308
+ ProjectCard.vue
309
+ ProjectCard.test.ts
310
+ pages/
311
+ projects/
312
+ index.vue
313
+ index.test.ts
314
+ utils/
315
+ dates.ts
316
+ dates.test.ts
317
+ ```
318
+
319
+ ### Component Testing Pattern
320
+
321
+ Use `mountSuspended` for async-safe component mounting:
322
+
323
+ ```typescript
324
+ import { describe, it, expect } from "vitest";
325
+ import { mountSuspended } from "@nuxt/test-utils/runtime";
326
+ import ProjectCard from "./ProjectCard.vue";
327
+
328
+ describe("ProjectCard", () => {
329
+ it("renders project name", async () => {
330
+ const wrapper = await mountSuspended(ProjectCard, {
331
+ props: {
332
+ project: { id: 1, name: "Test Project", status: "active" },
333
+ },
334
+ });
335
+
336
+ expect(wrapper.text()).toContain("Test Project");
337
+ });
338
+
339
+ it("shows active badge when active", async () => {
340
+ const wrapper = await mountSuspended(ProjectCard, {
341
+ props: {
342
+ project: { id: 1, name: "Test", status: "active" },
343
+ },
344
+ });
345
+
346
+ expect(wrapper.html()).toMatch(/active/i);
347
+ });
348
+ });
349
+ ```
350
+
351
+ ### Mocking Composables
352
+
353
+ Use `mockNuxtImport` for Nuxt composables:
354
+
355
+ ```typescript
356
+ import { mockNuxtImport } from "@nuxt/test-utils/runtime";
357
+
358
+ mockNuxtImport("useAddress", () => {
359
+ return () => ({
360
+ getDisplayAddress: (project: any) =>
361
+ project.address || "No address",
362
+ });
363
+ });
364
+
365
+ mockNuxtImport("useUserSession", () => {
366
+ return () => ({
367
+ user: { id: 1, name: "Test User" },
368
+ loggedIn: true,
369
+ });
370
+ });
371
+ ```
372
+
373
+ ### Mocking API Endpoints
374
+
375
+ Use `registerEndpoint` to mock API calls:
376
+
377
+ ```typescript
378
+ import { registerEndpoint } from "@nuxt/test-utils/runtime";
379
+
380
+ registerEndpoint("/api/projects", {
381
+ method: "GET",
382
+ handler: () => [
383
+ { id: 1, name: "Project A", status: "active" },
384
+ { id: 2, name: "Project B", status: "completed" },
385
+ ],
386
+ });
387
+
388
+ registerEndpoint("/api/projects/:id", {
389
+ method: "GET",
390
+ handler: (event) => ({
391
+ id: parseInt(event.context.params.id),
392
+ name: "Project Detail",
393
+ }),
394
+ });
395
+ ```
396
+
397
+ ### Testing Pages with Routes
398
+
399
+ ```typescript
400
+ import { describe, it, expect } from "vitest";
401
+ import { mountSuspended, registerEndpoint } from "@nuxt/test-utils/runtime";
402
+ import TaskPage from "./[id].vue";
403
+
404
+ describe("Task Detail Page", () => {
405
+ it("renders task details", async () => {
406
+ registerEndpoint("/api/tasks/123", {
407
+ method: "GET",
408
+ handler: () => ({ id: 123, name: "Fix bug", status: "open" }),
409
+ });
410
+
411
+ const wrapper = await mountSuspended(TaskPage, {
412
+ route: {
413
+ params: { id: "123" },
414
+ },
415
+ });
416
+
417
+ expect(wrapper.text()).toContain("Fix bug");
418
+ });
419
+ });
420
+ ```
421
+
422
+ ### Stubbing UI Library Components
423
+
424
+ For PrimeVue or other UI libraries, stub complex components:
425
+
426
+ ```typescript
427
+ import { mountSuspended } from "@nuxt/test-utils/runtime";
428
+ import ToastService from "primevue/toastservice";
429
+ import ConfirmationService from "primevue/confirmationservice";
430
+
431
+ const wrapper = await mountSuspended(MyComponent, {
432
+ global: {
433
+ plugins: [ToastService, ConfirmationService],
434
+ stubs: {
435
+ DataTable: true,
436
+ Column: true,
437
+ Dialog: true,
438
+ },
439
+ },
440
+ });
441
+ ```
442
+
443
+ ### Testing Utility Functions
444
+
445
+ For pure utilities, standard Vitest patterns work:
446
+
447
+ ```typescript
448
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
449
+ import { formatDate, parseDate } from "./dates";
450
+
451
+ describe("date utilities", () => {
452
+ const originalTZ = process.env.TZ;
453
+
454
+ afterEach(() => {
455
+ process.env.TZ = originalTZ;
456
+ });
457
+
458
+ it("formats date correctly in EST", () => {
459
+ process.env.TZ = "America/New_York";
460
+ expect(formatDate("2025-01-15")).toBe("Jan 15, 2025");
461
+ });
462
+
463
+ it("formats date correctly in PST", () => {
464
+ process.env.TZ = "America/Los_Angeles";
465
+ expect(formatDate("2025-01-15")).toBe("Jan 15, 2025");
466
+ });
467
+ });
468
+ ```
469
+
470
+ ### Async Handling
471
+
472
+ ```typescript
473
+ import { nextTick } from "vue";
474
+
475
+ it("updates after user interaction", async () => {
476
+ const wrapper = await mountSuspended(Counter);
477
+
478
+ await wrapper.find("button").trigger("click");
479
+ await nextTick();
480
+
481
+ expect(wrapper.text()).toContain("Count: 1");
482
+ });
483
+ ```
484
+
485
+ ### Frontend Testing Gotchas
486
+
487
+ 1. **Use `mountSuspended`** - Not regular `mount`. Handles async setup and Nuxt context.
488
+
489
+ 2. **Mock composables before mounting** - `mockNuxtImport` must be called before `mountSuspended`.
490
+
491
+ 3. **Register endpoints before mounting** - API mocks must exist before the component fetches.
492
+
493
+ 4. **Use `wrapper.text()` for content** - More reliable than searching for specific elements.
494
+
495
+ 5. **Await everything** - `mountSuspended`, `trigger()`, `nextTick()` are all async.
496
+
497
+ 6. **Separate test configs** - Use `VITEST_ENV` to run frontend and backend tests with different environments.