@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,199 @@
1
+ /**
2
+ * Mutation Patterns
3
+ * INSERT, UPDATE, DELETE, UPSERT
4
+ */
5
+ import { db } from "./db";
6
+ import { sql } from "kysely";
7
+
8
+ // ============================================
9
+ // INSERT
10
+ // ============================================
11
+
12
+ // Basic insert with RETURNING
13
+ const newCategory = await db
14
+ .insertInto("category")
15
+ .values({ name: "New Category", sort_order: 99 })
16
+ .returning(["id", "name"])
17
+ .executeTakeFirst();
18
+
19
+ // Insert multiple rows
20
+ const newCategories = await db
21
+ .insertInto("category")
22
+ .values([
23
+ { name: "Category A", sort_order: 100 },
24
+ { name: "Category B", sort_order: 101 },
25
+ ])
26
+ .returning(["id", "name"])
27
+ .execute();
28
+
29
+ // ============================================
30
+ // UPSERT (ON CONFLICT)
31
+ // ============================================
32
+
33
+ // Insert or update if conflict
34
+ const upsertedProduct = await db
35
+ .insertInto("product")
36
+ .values({
37
+ name: "Upsert Product",
38
+ sku: "UPSERT-001",
39
+ price: "99.99",
40
+ stock_quantity: 10,
41
+ })
42
+ .onConflict((oc) =>
43
+ oc.column("sku").doUpdateSet((eb) => ({
44
+ // Update these columns on conflict - type-safe!
45
+ stock_quantity: eb("product.stock_quantity", "+", eb.ref("excluded.stock_quantity")),
46
+ }))
47
+ )
48
+ .returning(["id", "sku", "stock_quantity"])
49
+ .executeTakeFirst();
50
+
51
+ // OnConflict variations:
52
+ // - oc.column("col") - single column constraint
53
+ // - oc.columns(["col1", "col2"]) - composite constraint
54
+ // - oc.constraint("constraint_name") - named constraint
55
+ // - oc.doNothing() - ignore conflicts
56
+ // - oc.doUpdateSet({...}) - update on conflict
57
+
58
+ // Upsert with doNothing
59
+ await db
60
+ .insertInto("user")
61
+ .values({ email: "exists@example.com", first_name: "Test", last_name: "User" })
62
+ .onConflict((oc) => oc.column("email").doNothing())
63
+ .execute();
64
+
65
+ // ============================================
66
+ // UPDATE
67
+ // ============================================
68
+
69
+ // Basic update
70
+ const updatedCategory = await db
71
+ .updateTable("category")
72
+ .set({ sort_order: 999 })
73
+ .where("name", "=", "New Category")
74
+ .returning(["id", "name", "sort_order"])
75
+ .executeTakeFirst();
76
+
77
+ // Update with expression (increment)
78
+ const updatedStock = await db
79
+ .updateTable("product")
80
+ .set((eb) => ({
81
+ stock_quantity: eb("stock_quantity", "+", 5),
82
+ }))
83
+ .where("sku", "=", "UPSERT-001")
84
+ .returning(["id", "sku", "stock_quantity"])
85
+ .executeTakeFirst();
86
+
87
+ // Update multiple columns with expressions
88
+ await db
89
+ .updateTable("product")
90
+ .set((eb) => ({
91
+ stock_quantity: eb("stock_quantity", "-", 1),
92
+ price: eb("price", "*", eb.lit(1.1)), // 10% increase
93
+ }))
94
+ .where("id", "=", 1)
95
+ .execute();
96
+
97
+ // ============================================
98
+ // DELETE
99
+ // ============================================
100
+
101
+ // Delete with RETURNING
102
+ const deleted = await db
103
+ .deleteFrom("category")
104
+ .where("name", "like", "Category%")
105
+ .returning(["id", "name"])
106
+ .execute();
107
+
108
+ // Delete with subquery
109
+ await db
110
+ .deleteFrom("order_item")
111
+ .where(
112
+ "order_id",
113
+ "in",
114
+ db.selectFrom("order").select("id").where("status", "=", "cancelled")
115
+ )
116
+ .execute();
117
+
118
+ // ============================================
119
+ // INSERT FROM SELECT
120
+ // ============================================
121
+
122
+ // Insert rows from another query
123
+ const auditLogs = await db
124
+ .insertInto("inventory_log")
125
+ .columns(["product_id", "change_quantity", "reason"])
126
+ .expression(
127
+ db
128
+ .selectFrom("product")
129
+ .select([
130
+ "id as product_id",
131
+ sql`0`.as("change_quantity"),
132
+ sql`'Audit check'`.as("reason"),
133
+ ])
134
+ .where("is_active", "=", true)
135
+ .limit(10)
136
+ )
137
+ .returning(["id", "product_id", "reason"])
138
+ .execute();
139
+
140
+ // ============================================
141
+ // UNION
142
+ // ============================================
143
+
144
+ // Combine results from multiple queries
145
+ const contacts = await db
146
+ .selectFrom("user")
147
+ .select(["email as contact", sql`'email'`.as("type")])
148
+ .where("role", "=", "admin")
149
+ .union(
150
+ db
151
+ .selectFrom("user")
152
+ .select(["email as contact", sql`'email'`.as("type")])
153
+ .where("role", "=", "manager")
154
+ )
155
+ .execute();
156
+
157
+ // unionAll - includes duplicates
158
+ // intersect - rows in both queries
159
+ // except - rows in first but not second
160
+
161
+ // ============================================
162
+ // KEY PATTERNS SUMMARY
163
+ // ============================================
164
+
165
+ /*
166
+ 1. INSERT:
167
+ .insertInto("table")
168
+ .values({...}) or .values([...])
169
+ .returning([...])
170
+ .execute() or .executeTakeFirst()
171
+
172
+ 2. UPSERT (ON CONFLICT) - type-safe with eb callback:
173
+ .onConflict((oc) => oc.column("col").doUpdateSet((eb) => ({
174
+ col: eb("table.col", "+", eb.ref("excluded.col")),
175
+ })))
176
+ - eb.ref("excluded.col") for inserted values
177
+ - eb("table.col", ...) for existing values (type-safe!)
178
+
179
+ 3. UPDATE:
180
+ .updateTable("table")
181
+ .set({...}) or .set((eb) => ({...}))
182
+ .where(...)
183
+ - Use eb(col, op, value) for expressions
184
+
185
+ 4. DELETE:
186
+ .deleteFrom("table")
187
+ .where(...)
188
+ .returning([...]) - optional
189
+
190
+ 5. INSERT FROM SELECT:
191
+ .insertInto("table")
192
+ .columns([...])
193
+ .expression(db.selectFrom(...))
194
+
195
+ 6. RETURNING:
196
+ - Available on INSERT, UPDATE, DELETE
197
+ - Returns affected rows
198
+ - Use with executeTakeFirst() for single row
199
+ */
@@ -0,0 +1,117 @@
1
+ /**
2
+ * ORDER BY and Pagination Patterns
3
+ * Sorting, NULLS handling, DISTINCT, pagination
4
+ */
5
+ import { db } from "./db";
6
+
7
+ // ============================================
8
+ // ORDER BY
9
+ // ============================================
10
+
11
+ // Simple ORDER BY
12
+ const productsByPrice = await db
13
+ .selectFrom("product")
14
+ .select(["name", "price"])
15
+ .orderBy("price", "desc")
16
+ .execute();
17
+
18
+ // Multiple ORDER BY - chain calls
19
+ // NOT array syntax - that's deprecated!
20
+ const sortedProducts = await db
21
+ .selectFrom("product")
22
+ .select(["name", "price", "stock_quantity"])
23
+ .orderBy("is_active", "desc") // Primary sort
24
+ .orderBy("price", "asc") // Secondary sort
25
+ .execute();
26
+
27
+ // ============================================
28
+ // NULLS FIRST / NULLS LAST
29
+ // ============================================
30
+
31
+ // Use order builder callback (ob) - NOT sql``!
32
+ const productsNullsLast = await db
33
+ .selectFrom("product")
34
+ .select(["name", "category_id"])
35
+ .orderBy("category_id", (ob) => ob.asc().nullsLast())
36
+ .execute();
37
+
38
+ const productsNullsFirst = await db
39
+ .selectFrom("product")
40
+ .select(["name", "category_id"])
41
+ .orderBy("category_id", (ob) => ob.desc().nullsFirst())
42
+ .execute();
43
+
44
+ // ============================================
45
+ // PAGINATION
46
+ // ============================================
47
+
48
+ // LIMIT and OFFSET
49
+ const page2 = await db
50
+ .selectFrom("product")
51
+ .selectAll()
52
+ .orderBy("name")
53
+ .limit(10) // Items per page
54
+ .offset(10) // Skip first page (page 1)
55
+ .execute();
56
+
57
+ // Pagination helper pattern
58
+ async function getPaginatedProducts(page: number, pageSize: number) {
59
+ return db
60
+ .selectFrom("product")
61
+ .selectAll()
62
+ .orderBy("created_at", "desc")
63
+ .limit(pageSize)
64
+ .offset((page - 1) * pageSize)
65
+ .execute();
66
+ }
67
+
68
+ // ============================================
69
+ // DISTINCT
70
+ // ============================================
71
+
72
+ // DISTINCT - unique values
73
+ const uniqueStatuses = await db
74
+ .selectFrom("order")
75
+ .select("status")
76
+ .distinct()
77
+ .execute();
78
+
79
+ // DISTINCT ON (PostgreSQL only)
80
+ // Get first row for each unique value of specified column(s)
81
+ const latestOrderPerUser = await db
82
+ .selectFrom("order")
83
+ .distinctOn("user_id")
84
+ .select(["user_id", "id", "status", "created_at"])
85
+ .orderBy("user_id")
86
+ .orderBy("created_at", "desc") // Most recent first
87
+ .execute();
88
+ // Returns one row per user - their latest order
89
+
90
+ // ============================================
91
+ // KEY PATTERNS SUMMARY
92
+ // ============================================
93
+
94
+ /*
95
+ 1. ORDER BY syntax:
96
+ .orderBy("column", "asc") // ascending
97
+ .orderBy("column", "desc") // descending
98
+
99
+ 2. Multiple columns - CHAIN, don't use array:
100
+ .orderBy("col1", "desc")
101
+ .orderBy("col2", "asc")
102
+
103
+ 3. NULLS handling - use order builder callback:
104
+ .orderBy("col", (ob) => ob.asc().nullsLast())
105
+ .orderBy("col", (ob) => ob.desc().nullsFirst())
106
+
107
+ AVOID sql`` for this - deprecated pattern!
108
+
109
+ 4. Pagination:
110
+ .limit(pageSize)
111
+ .offset((page - 1) * pageSize)
112
+
113
+ 5. DISTINCT ON (PostgreSQL):
114
+ - Gets first row per unique value
115
+ - ORDER BY must start with DISTINCT ON column(s)
116
+ - Then order by what determines "first"
117
+ */
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Relations Patterns
3
+ * jsonArrayFrom and jsonObjectFrom for nested data
4
+ */
5
+ import { db } from "./db";
6
+ import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/postgres";
7
+
8
+ // ============================================
9
+ // IMPORTANT: Kysely is NOT an ORM
10
+ // ============================================
11
+
12
+ // Kysely doesn't have built-in relations like Prisma or TypeORM.
13
+ // Instead, use PostgreSQL JSON functions to fetch related data
14
+ // in a single query.
15
+
16
+ // ============================================
17
+ // jsonArrayFrom - One-to-Many
18
+ // ============================================
19
+
20
+ // User with their orders (one user -> many orders)
21
+ const usersWithOrders = await db
22
+ .selectFrom("user")
23
+ .select((eb) => [
24
+ "user.id",
25
+ "user.email",
26
+ "user.first_name",
27
+ // Correlated subquery returns JSON array
28
+ jsonArrayFrom(
29
+ eb
30
+ .selectFrom("order")
31
+ .select(["order.id", "order.status", "order.total_amount"])
32
+ .whereRef("order.user_id", "=", "user.id") // Correlation!
33
+ .orderBy("order.created_at", "desc")
34
+ ).as("orders"),
35
+ ])
36
+ .where("user.is_active", "=", true)
37
+ .execute();
38
+
39
+ // Result type: { id, email, first_name, orders: Array<{id, status, total_amount}> }
40
+
41
+ // ============================================
42
+ // jsonObjectFrom - Many-to-One
43
+ // ============================================
44
+
45
+ // Product with its category (many products -> one category)
46
+ const productsWithCategory = await db
47
+ .selectFrom("product")
48
+ .select((eb) => [
49
+ "product.id",
50
+ "product.name",
51
+ "product.price",
52
+ // Correlated subquery returns single JSON object
53
+ jsonObjectFrom(
54
+ eb
55
+ .selectFrom("category")
56
+ .select(["category.id", "category.name"])
57
+ .whereRef("category.id", "=", "product.category_id") // Correlation!
58
+ ).as("category"),
59
+ ])
60
+ .execute();
61
+
62
+ // Result type: { id, name, price, category: {id, name} | null }
63
+
64
+ // ============================================
65
+ // COMBINED NESTED RELATIONS
66
+ // ============================================
67
+
68
+ // Deep nesting: User -> Orders -> Products
69
+ const userOrderDetails = await db
70
+ .selectFrom("user")
71
+ .select((eb) => [
72
+ "user.id",
73
+ "user.email",
74
+ jsonArrayFrom(
75
+ eb
76
+ .selectFrom("order")
77
+ .innerJoin("order_item", "order_item.order_id", "order.id")
78
+ .innerJoin("product", "product.id", "order_item.product_id")
79
+ .select([
80
+ "order.id as orderId",
81
+ "order.status",
82
+ "product.name as productName",
83
+ "order_item.quantity",
84
+ ])
85
+ .whereRef("order.user_id", "=", "user.id")
86
+ ).as("orderDetails"),
87
+ ])
88
+ .where("user.email", "=", "alice@example.com")
89
+ .executeTakeFirst();
90
+
91
+ // ============================================
92
+ // MULTIPLE RELATIONS
93
+ // ============================================
94
+
95
+ // Order with user (many-to-one) AND items (one-to-many)
96
+ const ordersComplete = await db
97
+ .selectFrom("order")
98
+ .select((eb) => [
99
+ "order.id",
100
+ "order.status",
101
+ "order.total_amount",
102
+ // Many-to-one: user
103
+ jsonObjectFrom(
104
+ eb
105
+ .selectFrom("user")
106
+ .select(["user.id", "user.email", "user.first_name"])
107
+ .whereRef("user.id", "=", "order.user_id")
108
+ ).as("user"),
109
+ // One-to-many: items
110
+ jsonArrayFrom(
111
+ eb
112
+ .selectFrom("order_item")
113
+ .innerJoin("product", "product.id", "order_item.product_id")
114
+ .select([
115
+ "order_item.quantity",
116
+ "order_item.unit_price",
117
+ "product.name as productName",
118
+ ])
119
+ .whereRef("order_item.order_id", "=", "order.id")
120
+ ).as("items"),
121
+ ])
122
+ .execute();
123
+
124
+ // ============================================
125
+ // WITH FILTERING AND ORDERING
126
+ // ============================================
127
+
128
+ // Only include active items, ordered by price
129
+ const productWithActiveReviews = await db
130
+ .selectFrom("product")
131
+ .select((eb) => [
132
+ "product.id",
133
+ "product.name",
134
+ jsonArrayFrom(
135
+ eb
136
+ .selectFrom("review")
137
+ .select(["review.id", "review.rating", "review.title"])
138
+ .whereRef("review.product_id", "=", "product.id")
139
+ .where("review.rating", ">=", 4) // Filter inside relation!
140
+ .orderBy("review.created_at", "desc")
141
+ .limit(5) // Limit inside relation!
142
+ ).as("topReviews"),
143
+ ])
144
+ .execute();
145
+
146
+ // ============================================
147
+ // KEY PATTERNS SUMMARY
148
+ // ============================================
149
+
150
+ /*
151
+ 1. jsonArrayFrom - One-to-Many:
152
+ - Returns JSON array of related rows
153
+ - Use whereRef() to correlate with outer query
154
+ - Can include joins, filters, ordering inside
155
+
156
+ 2. jsonObjectFrom - Many-to-One:
157
+ - Returns single JSON object or null
158
+ - Use whereRef() to correlate with outer query
159
+ - Returns null if no match (like LEFT JOIN)
160
+
161
+ 3. Key differences from ORMs:
162
+ - No automatic eager/lazy loading
163
+ - You explicitly define what to fetch
164
+ - Single query, no N+1 problem
165
+ - Full control over the SQL
166
+
167
+ 4. whereRef() is critical:
168
+ - Links subquery to outer query
169
+ - First arg: inner table column
170
+ - Third arg: outer table column (eb.ref())
171
+
172
+ 5. Performance benefits:
173
+ - Single database round trip
174
+ - PostgreSQL optimizes the JSON aggregation
175
+ - No N+1 queries ever
176
+ */
@@ -0,0 +1,146 @@
1
+ /**
2
+ * SELECT and WHERE Patterns
3
+ * Basic query patterns for fetching and filtering data
4
+ */
5
+ import { db } from "./db";
6
+ import { sql } from "kysely";
7
+
8
+ // ============================================
9
+ // SELECT PATTERNS
10
+ // ============================================
11
+
12
+ // Select all columns
13
+ const allUsers = await db.selectFrom("user").selectAll().execute();
14
+
15
+ // Select specific columns
16
+ const userNames = await db
17
+ .selectFrom("user")
18
+ .select(["id", "email", "first_name", "last_name"])
19
+ .execute();
20
+
21
+ // Column aliases with eb.ref().as()
22
+ const aliasedUsers = await db
23
+ .selectFrom("user")
24
+ .select((eb) => [
25
+ eb.ref("first_name").as("firstName"),
26
+ eb.ref("last_name").as("lastName"),
27
+ eb.ref("email").as("emailAddress"),
28
+ ])
29
+ .execute();
30
+
31
+ // executeTakeFirst - returns T | undefined (for 0 or 1 row)
32
+ const maybeUser = await db
33
+ .selectFrom("user")
34
+ .selectAll()
35
+ .where("email", "=", "alice@example.com")
36
+ .executeTakeFirst();
37
+
38
+ // executeTakeFirstOrThrow - throws if no row found
39
+ const definiteUser = await db
40
+ .selectFrom("user")
41
+ .selectAll()
42
+ .where("email", "=", "alice@example.com")
43
+ .executeTakeFirstOrThrow();
44
+
45
+ // ============================================
46
+ // WHERE CLAUSES
47
+ // ============================================
48
+
49
+ // Equality
50
+ const admins = await db
51
+ .selectFrom("user")
52
+ .selectAll()
53
+ .where("role", "=", "admin")
54
+ .execute();
55
+
56
+ // Comparison operators (<, >, <=, >=, !=)
57
+ const expensiveProducts = await db
58
+ .selectFrom("product")
59
+ .select(["name", "price"])
60
+ .where("price", ">", "100")
61
+ .execute();
62
+
63
+ // IN clause - array of values
64
+ const pendingOrShipped = await db
65
+ .selectFrom("order")
66
+ .selectAll()
67
+ .where("status", "in", ["pending", "shipped"])
68
+ .execute();
69
+
70
+ // LIKE pattern matching
71
+ const bookProducts = await db
72
+ .selectFrom("product")
73
+ .select(["name", "sku"])
74
+ .where("name", "like", "%Book%")
75
+ .execute();
76
+
77
+ // IS NULL / IS NOT NULL
78
+ const uncategorized = await db
79
+ .selectFrom("product")
80
+ .select(["name", "category_id"])
81
+ .where("category_id", "is", null)
82
+ .execute();
83
+
84
+ // ============================================
85
+ // COMBINING CONDITIONS
86
+ // ============================================
87
+
88
+ // Multiple WHERE = AND (chained)
89
+ // Chaining .where() creates AND conditions
90
+ const activeAffordable = await db
91
+ .selectFrom("product")
92
+ .select(["name", "price", "is_active"])
93
+ .where("is_active", "=", true)
94
+ .where("price", "<", "100")
95
+ .execute();
96
+
97
+ // OR conditions using eb.or()
98
+ const adminOrManager = await db
99
+ .selectFrom("user")
100
+ .selectAll()
101
+ .where((eb) =>
102
+ eb.or([
103
+ eb("role", "=", "admin"),
104
+ eb("role", "=", "manager"),
105
+ ])
106
+ )
107
+ .execute();
108
+
109
+ // Complex AND/OR with eb.and() and eb.or()
110
+ const complexFilter = await db
111
+ .selectFrom("product")
112
+ .select(["name", "price", "stock_quantity"])
113
+ .where((eb) =>
114
+ eb.and([
115
+ eb("is_active", "=", true),
116
+ eb.or([
117
+ eb("price", "<", "50"),
118
+ eb("stock_quantity", ">", 100),
119
+ ]),
120
+ ])
121
+ )
122
+ .execute();
123
+
124
+ // ============================================
125
+ // KEY PATTERNS SUMMARY
126
+ // ============================================
127
+
128
+ /*
129
+ 1. selectAll() vs select([...])
130
+ - selectAll() gets all columns
131
+ - select([...]) for specific columns - better performance
132
+
133
+ 2. executeTakeFirst() vs execute()
134
+ - execute() returns array
135
+ - executeTakeFirst() returns single row or undefined
136
+ - executeTakeFirstOrThrow() throws if not found
137
+
138
+ 3. WHERE chaining = AND
139
+ - .where(...).where(...) creates AND
140
+ - Use eb.or([...]) for OR
141
+ - Use eb.and([...]) for explicit AND
142
+
143
+ 4. eb() inside where callbacks
144
+ - eb("column", "=", value) creates comparison
145
+ - Returns Expression<SqlBool> for composability
146
+ */
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "linear",
3
+ "description": "Create, triage, and manage Linear issues at Gallop Systems following team conventions: cycle placement, issue templates, project / milestone hierarchy, project refresh, and cycle rebalance. Includes a bash GraphQL CLI for the operations the Linear MCP server doesn't expose.",
4
+ "version": "1.0.0",
5
+ "author": {
6
+ "name": "Gallop Systems"
7
+ }
8
+ }