@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,278 @@
1
+ # Factory Pattern
2
+
3
+ > **Example:** [test-utils-index.ts](./examples/test-utils-index.ts)
4
+
5
+ Transaction-bound factories for creating test data with sensible defaults.
6
+
7
+ ## Core Pattern
8
+
9
+ ```typescript
10
+ import type { Transaction } from "kysely";
11
+ import type { DB } from "../db/db";
12
+
13
+ function createFactories(trx: Transaction<DB>) {
14
+ return {
15
+ async user(data: Partial<{
16
+ email: string;
17
+ name: string;
18
+ role: string;
19
+ }> = {}) {
20
+ const num = Math.floor(Math.random() * 10000);
21
+ return trx
22
+ .insertInto("user")
23
+ .values({
24
+ email: data.email ?? `test${num}@example.com`,
25
+ name: data.name ?? "Test User",
26
+ role: data.role ?? "user",
27
+ })
28
+ .returningAll()
29
+ .executeTakeFirstOrThrow();
30
+ },
31
+
32
+ async project(data: Partial<{
33
+ name: string;
34
+ ownerId: number;
35
+ status: string;
36
+ }> = {}) {
37
+ // Auto-create owner if not provided
38
+ let ownerId = data.ownerId;
39
+ if (!ownerId) {
40
+ const owner = await this.user();
41
+ ownerId = owner.id;
42
+ }
43
+
44
+ return trx
45
+ .insertInto("project")
46
+ .values({
47
+ name: data.name ?? `Test Project ${Date.now()}`,
48
+ owner_id: ownerId,
49
+ status: data.status ?? "active",
50
+ })
51
+ .returningAll()
52
+ .executeTakeFirstOrThrow();
53
+ },
54
+
55
+ async task(data: {
56
+ projectId: number; // Required - explicit dependency
57
+ title?: string;
58
+ status?: string;
59
+ assigneeId?: number | null;
60
+ }) {
61
+ return trx
62
+ .insertInto("task")
63
+ .values({
64
+ project_id: data.projectId,
65
+ title: data.title ?? "Test Task",
66
+ status: data.status ?? "pending",
67
+ assignee_id: data.assigneeId ?? null,
68
+ })
69
+ .returningAll()
70
+ .executeTakeFirstOrThrow();
71
+ },
72
+ };
73
+ }
74
+
75
+ export type Factories = ReturnType<typeof createFactories>;
76
+ ```
77
+
78
+ ## Design Principles
79
+
80
+ ### 1. Sensible Defaults
81
+
82
+ Every field has a default so you only specify what matters:
83
+
84
+ ```typescript
85
+ // Minimal - uses all defaults
86
+ const user = await factories.user();
87
+
88
+ // Override just what you need
89
+ const admin = await factories.user({ role: "admin" });
90
+ ```
91
+
92
+ ### 2. Required vs Optional Dependencies
93
+
94
+ **Optional dependencies** are auto-created:
95
+
96
+ ```typescript
97
+ async project(data: Partial<{...}> = {}) {
98
+ let ownerId = data.ownerId;
99
+ if (!ownerId) {
100
+ const owner = await this.user(); // Auto-create
101
+ ownerId = owner.id;
102
+ }
103
+ // ...
104
+ }
105
+
106
+ // Usage - owner created automatically
107
+ const project = await factories.project();
108
+
109
+ // Or specify one
110
+ const project = await factories.project({ ownerId: existingUser.id });
111
+ ```
112
+
113
+ **Required dependencies** must be passed:
114
+
115
+ ```typescript
116
+ async task(data: {
117
+ projectId: number; // Required
118
+ // ...
119
+ }) {
120
+ // ...
121
+ }
122
+
123
+ // Usage - must create project first
124
+ const project = await factories.project();
125
+ const task = await factories.task({ projectId: project.id });
126
+ ```
127
+
128
+ ### 3. Unique Values
129
+
130
+ Avoid collisions with random/unique values:
131
+
132
+ ```typescript
133
+ async user(data: Partial<{...}> = {}) {
134
+ const num = Math.floor(Math.random() * 10000);
135
+ return trx.insertInto("user").values({
136
+ email: data.email ?? `test${num}@example.com`,
137
+ // ...
138
+ });
139
+ }
140
+
141
+ async project(data: Partial<{...}> = {}) {
142
+ return trx.insertInto("project").values({
143
+ name: data.name ?? `Test Project ${Date.now()}`,
144
+ // ...
145
+ });
146
+ }
147
+ ```
148
+
149
+ ### 4. Use `this` for Composition
150
+
151
+ Factories can call each other:
152
+
153
+ ```typescript
154
+ async task(data: {...}) {
155
+ return trx.insertInto("task").values({...});
156
+ }
157
+
158
+ async taskWithAssignee(data: {...}) {
159
+ const assignee = await this.user({ role: "member" });
160
+ return this.task({ ...data, assigneeId: assignee.id });
161
+ }
162
+ ```
163
+
164
+ ## Complex Relationships
165
+
166
+ ### Many-to-Many
167
+
168
+ ```typescript
169
+ async projectMember(data: {
170
+ projectId: number;
171
+ userId: number;
172
+ role?: string;
173
+ }) {
174
+ return trx
175
+ .insertInto("project_member")
176
+ .values({
177
+ project_id: data.projectId,
178
+ user_id: data.userId,
179
+ role: data.role ?? "member",
180
+ })
181
+ .returningAll()
182
+ .executeTakeFirstOrThrow();
183
+ }
184
+
185
+ // Usage
186
+ const project = await factories.project();
187
+ const user = await factories.user();
188
+ await factories.projectMember({
189
+ projectId: project.id,
190
+ userId: user.id,
191
+ role: "admin",
192
+ });
193
+ ```
194
+
195
+ ### JSON Fields
196
+
197
+ ```typescript
198
+ async organization(data: Partial<{
199
+ name: string;
200
+ metadata: Record<string, any>;
201
+ contacts: { email?: string; phone?: string };
202
+ }> = {}) {
203
+ return trx
204
+ .insertInto("organization")
205
+ .values({
206
+ name: data.name ?? `Org ${Date.now()}`,
207
+ metadata: data.metadata ?? {},
208
+ contacts: data.contacts ?? {},
209
+ })
210
+ .returningAll()
211
+ .executeTakeFirstOrThrow();
212
+ }
213
+ ```
214
+
215
+ ### Self-Referential (Parent/Child)
216
+
217
+ ```typescript
218
+ async category(data: Partial<{
219
+ name: string;
220
+ parentId: number | null;
221
+ }> = {}) {
222
+ return trx
223
+ .insertInto("category")
224
+ .values({
225
+ name: data.name ?? "Test Category",
226
+ parent_id: data.parentId ?? null,
227
+ })
228
+ .returningAll()
229
+ .executeTakeFirstOrThrow();
230
+ }
231
+
232
+ // Usage - create hierarchy
233
+ const parent = await factories.category({ name: "Electronics" });
234
+ const child = await factories.category({
235
+ name: "Phones",
236
+ parentId: parent.id,
237
+ });
238
+ ```
239
+
240
+ ## Usage in Tests
241
+
242
+ ```typescript
243
+ describe("GET /api/projects/[id]/tasks", () => {
244
+ test("returns tasks for project", async ({ factories }) => {
245
+ const project = await factories.project();
246
+ const task1 = await factories.task({ projectId: project.id });
247
+ const task2 = await factories.task({ projectId: project.id });
248
+
249
+ const event = mockGet({ id: project.id });
250
+ const result = await handler(event);
251
+
252
+ expect(result).toHaveLength(2);
253
+ expect(result.map((t: any) => t.id)).toContain(task1.id);
254
+ });
255
+
256
+ test("excludes other project's tasks", async ({ factories }) => {
257
+ const myProject = await factories.project();
258
+ const otherProject = await factories.project();
259
+
260
+ await factories.task({ projectId: myProject.id });
261
+ await factories.task({ projectId: otherProject.id }); // Different project
262
+
263
+ const event = mockGet({ id: myProject.id });
264
+ const result = await handler(event);
265
+
266
+ expect(result).toHaveLength(1); // Only our project's task
267
+ });
268
+ });
269
+ ```
270
+
271
+ ## Factory Best Practices
272
+
273
+ 1. **Make required FKs explicit** - Force caller to think about relationships
274
+ 2. **Make optional FKs auto-created** - Reduce boilerplate for simple tests
275
+ 3. **Use random/unique values** - Avoid collisions even if tests somehow overlap
276
+ 4. **Return full entity** - Use `returningAll()` for flexibility
277
+ 5. **Minimal defaults** - Only set what's needed for valid inserts
278
+ 6. **Keep factories simple** - No business logic, just data creation