@musashishao/agent-kit 1.8.1 → 1.9.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 (92) hide show
  1. package/.agent/agents/ai-architect.md +39 -0
  2. package/.agent/agents/cloud-engineer.md +39 -0
  3. package/.agent/agents/game-asset-curator.md +317 -0
  4. package/.agent/agents/game-developer.md +190 -89
  5. package/.agent/agents/game-narrative-designer.md +310 -0
  6. package/.agent/agents/game-qa-agent.md +441 -0
  7. package/.agent/agents/marketing-specialist.md +41 -0
  8. package/.agent/agents/penetration-tester.md +15 -1
  9. package/.agent/rules/CODEX.md +26 -2
  10. package/.agent/rules/GEMINI.md +7 -5
  11. package/.agent/rules/REFERENCE.md +92 -2
  12. package/.agent/scripts/ak_cli.py +1 -1
  13. package/.agent/scripts/localize_workflows.py +54 -0
  14. package/.agent/scripts/memory_manager.py +24 -1
  15. package/.agent/skills/3d-web-experience/SKILL.md +386 -0
  16. package/.agent/skills/DEPENDENCIES.md +54 -0
  17. package/.agent/skills/ab-test-setup/SKILL.md +77 -0
  18. package/.agent/skills/active-directory-attacks/SKILL.md +59 -0
  19. package/.agent/skills/agent-evaluation/SKILL.md +430 -0
  20. package/.agent/skills/agent-memory-systems/SKILL.md +426 -0
  21. package/.agent/skills/agent-tool-builder/SKILL.md +139 -0
  22. package/.agent/skills/ai-agents-architect/SKILL.md +115 -0
  23. package/.agent/skills/ai-product/SKILL.md +86 -0
  24. package/.agent/skills/ai-wrapper-product/SKILL.md +90 -0
  25. package/.agent/skills/analytics-tracking/SKILL.md +88 -0
  26. package/.agent/skills/api-fuzzing-bug-bounty/SKILL.md +66 -0
  27. package/.agent/skills/app-store-optimization/SKILL.md +66 -0
  28. package/.agent/skills/autonomous-agent-patterns/SKILL.md +414 -0
  29. package/.agent/skills/aws-penetration-testing/SKILL.md +50 -0
  30. package/.agent/skills/aws-serverless/SKILL.md +327 -0
  31. package/.agent/skills/azure-functions/SKILL.md +340 -0
  32. package/.agent/skills/broken-authentication/SKILL.md +53 -0
  33. package/.agent/skills/browser-automation/SKILL.md +408 -0
  34. package/.agent/skills/browser-extension-builder/SKILL.md +422 -0
  35. package/.agent/skills/bullmq-specialist/SKILL.md +424 -0
  36. package/.agent/skills/bun-development/SKILL.md +386 -0
  37. package/.agent/skills/burp-suite-testing/SKILL.md +60 -0
  38. package/.agent/skills/clerk-auth/SKILL.md +432 -0
  39. package/.agent/skills/cloud-penetration-testing/SKILL.md +51 -0
  40. package/.agent/skills/copywriting/SKILL.md +66 -0
  41. package/.agent/skills/crewai/SKILL.md +470 -0
  42. package/.agent/skills/discord-bot-architect/SKILL.md +447 -0
  43. package/.agent/skills/email-sequence/SKILL.md +73 -0
  44. package/.agent/skills/ethical-hacking-methodology/SKILL.md +67 -0
  45. package/.agent/skills/firebase/SKILL.md +377 -0
  46. package/.agent/skills/game-development/godot-expert/SKILL.md +462 -0
  47. package/.agent/skills/game-development/npc-ai-integration/SKILL.md +110 -0
  48. package/.agent/skills/game-development/procedural-generation/SKILL.md +168 -0
  49. package/.agent/skills/game-development/unity-integration/SKILL.md +358 -0
  50. package/.agent/skills/game-development/webgpu-shading/SKILL.md +209 -0
  51. package/.agent/skills/gcp-cloud-run/SKILL.md +358 -0
  52. package/.agent/skills/graphql/SKILL.md +492 -0
  53. package/.agent/skills/idor-testing/SKILL.md +64 -0
  54. package/.agent/skills/inngest/SKILL.md +128 -0
  55. package/.agent/skills/langfuse/SKILL.md +415 -0
  56. package/.agent/skills/langgraph/SKILL.md +360 -0
  57. package/.agent/skills/launch-strategy/SKILL.md +68 -0
  58. package/.agent/skills/linux-privilege-escalation/SKILL.md +62 -0
  59. package/.agent/skills/llm-app-patterns/SKILL.md +367 -0
  60. package/.agent/skills/marketing-ideas/SKILL.md +66 -0
  61. package/.agent/skills/metasploit-framework/SKILL.md +60 -0
  62. package/.agent/skills/micro-saas-launcher/SKILL.md +93 -0
  63. package/.agent/skills/neon-postgres/SKILL.md +339 -0
  64. package/.agent/skills/paid-ads/SKILL.md +64 -0
  65. package/.agent/skills/supabase-integration/SKILL.md +411 -0
  66. package/.agent/workflows/ai-agent.md +36 -0
  67. package/.agent/workflows/autofix.md +1 -0
  68. package/.agent/workflows/brainstorm.md +1 -0
  69. package/.agent/workflows/context.md +1 -0
  70. package/.agent/workflows/create.md +1 -0
  71. package/.agent/workflows/dashboard.md +1 -0
  72. package/.agent/workflows/debug.md +1 -0
  73. package/.agent/workflows/deploy.md +1 -0
  74. package/.agent/workflows/enhance.md +1 -0
  75. package/.agent/workflows/game-prototype.md +154 -0
  76. package/.agent/workflows/marketing.md +37 -0
  77. package/.agent/workflows/next.md +1 -0
  78. package/.agent/workflows/orchestrate.md +1 -0
  79. package/.agent/workflows/pentest.md +37 -0
  80. package/.agent/workflows/plan.md +1 -0
  81. package/.agent/workflows/preview.md +2 -1
  82. package/.agent/workflows/quality.md +1 -0
  83. package/.agent/workflows/saas.md +36 -0
  84. package/.agent/workflows/spec.md +1 -0
  85. package/.agent/workflows/status.md +1 -0
  86. package/.agent/workflows/test.md +1 -0
  87. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  88. package/README.md +52 -24
  89. package/bin/cli.js +68 -3
  90. package/docs/CHANGELOG_AI_INFRA.md +30 -0
  91. package/docs/MIGRATION_GUIDE_V1.9.md +55 -0
  92. package/package.json +1 -1
@@ -0,0 +1,492 @@
1
+ ---
2
+ name: graphql
3
+ description: "GraphQL gives clients exactly the data they need. Covers schema design, resolvers, DataLoader for N+1 prevention, federation, and client integration with Apollo/urql."
4
+ version: "1.0.0"
5
+ source: "antigravity-awesome-skills (adapted)"
6
+ ---
7
+
8
+ # 🔮 GraphQL
9
+
10
+ You're a developer who has built GraphQL APIs at scale. You've seen the N+1 query problem bring down production servers. You know that GraphQL's power is also its danger.
11
+
12
+ **Key insight**: GraphQL is a contract. The schema is the API documentation. Design it carefully.
13
+
14
+ ---
15
+
16
+ ## When to Use This Skill
17
+
18
+ - Building APIs with complex data requirements
19
+ - Clients need flexible data fetching
20
+ - Multiple frontends (web, mobile) sharing API
21
+ - Microservices needing unified graph
22
+ - Real-time subscriptions
23
+
24
+ ---
25
+
26
+ ## Capabilities
27
+
28
+ - `graphql-schema-design`
29
+ - `graphql-resolvers`
30
+ - `graphql-federation`
31
+ - `graphql-subscriptions`
32
+ - `graphql-dataloader`
33
+ - `graphql-codegen`
34
+ - `apollo-server`
35
+ - `apollo-client`
36
+
37
+ ---
38
+
39
+ ## 1. Schema Design
40
+
41
+ ### Type-Safe Schema
42
+
43
+ ```graphql
44
+ # schema.graphql
45
+
46
+ # Custom scalars
47
+ scalar DateTime
48
+ scalar UUID
49
+
50
+ # Enums
51
+ enum OrderStatus {
52
+ PENDING
53
+ PROCESSING
54
+ SHIPPED
55
+ DELIVERED
56
+ CANCELLED
57
+ }
58
+
59
+ # Types with relations
60
+ type User {
61
+ id: UUID!
62
+ email: String!
63
+ name: String
64
+ avatar: String
65
+ createdAt: DateTime!
66
+ orders: [Order!]!
67
+ ordersCount: Int!
68
+ }
69
+
70
+ type Order {
71
+ id: UUID!
72
+ status: OrderStatus!
73
+ total: Float!
74
+ items: [OrderItem!]!
75
+ user: User!
76
+ createdAt: DateTime!
77
+ updatedAt: DateTime!
78
+ }
79
+
80
+ type OrderItem {
81
+ id: UUID!
82
+ quantity: Int!
83
+ unitPrice: Float!
84
+ product: Product!
85
+ }
86
+
87
+ type Product {
88
+ id: UUID!
89
+ name: String!
90
+ description: String
91
+ price: Float!
92
+ inventory: Int!
93
+ category: Category!
94
+ }
95
+
96
+ # Paginated response
97
+ type UserConnection {
98
+ edges: [UserEdge!]!
99
+ pageInfo: PageInfo!
100
+ totalCount: Int!
101
+ }
102
+
103
+ type UserEdge {
104
+ node: User!
105
+ cursor: String!
106
+ }
107
+
108
+ type PageInfo {
109
+ hasNextPage: Boolean!
110
+ hasPreviousPage: Boolean!
111
+ startCursor: String
112
+ endCursor: String
113
+ }
114
+
115
+ # Queries
116
+ type Query {
117
+ user(id: UUID!): User
118
+ users(first: Int, after: String): UserConnection!
119
+ order(id: UUID!): Order
120
+ products(category: String, limit: Int = 20): [Product!]!
121
+ }
122
+
123
+ # Mutations
124
+ type Mutation {
125
+ createUser(input: CreateUserInput!): User!
126
+ updateUser(id: UUID!, input: UpdateUserInput!): User!
127
+ createOrder(input: CreateOrderInput!): Order!
128
+ cancelOrder(id: UUID!): Order!
129
+ }
130
+
131
+ # Input types
132
+ input CreateUserInput {
133
+ email: String!
134
+ name: String
135
+ password: String!
136
+ }
137
+
138
+ input UpdateUserInput {
139
+ name: String
140
+ avatar: String
141
+ }
142
+
143
+ input CreateOrderInput {
144
+ items: [OrderItemInput!]!
145
+ }
146
+
147
+ input OrderItemInput {
148
+ productId: UUID!
149
+ quantity: Int!
150
+ }
151
+
152
+ # Subscriptions
153
+ type Subscription {
154
+ orderStatusChanged(orderId: UUID!): Order!
155
+ }
156
+ ```
157
+
158
+ ---
159
+
160
+ ## 2. Resolvers with DataLoader
161
+
162
+ ### DataLoader for N+1 Prevention
163
+
164
+ ```typescript
165
+ // dataloaders.ts
166
+ import DataLoader from 'dataloader';
167
+ import { prisma } from './prisma';
168
+
169
+ export function createLoaders() {
170
+ return {
171
+ user: new DataLoader<string, User>(async (ids) => {
172
+ const users = await prisma.user.findMany({
173
+ where: { id: { in: [...ids] } }
174
+ });
175
+
176
+ // Map results to match input order
177
+ const userMap = new Map(users.map(u => [u.id, u]));
178
+ return ids.map(id => userMap.get(id) || null);
179
+ }),
180
+
181
+ product: new DataLoader<string, Product>(async (ids) => {
182
+ const products = await prisma.product.findMany({
183
+ where: { id: { in: [...ids] } }
184
+ });
185
+ const productMap = new Map(products.map(p => [p.id, p]));
186
+ return ids.map(id => productMap.get(id) || null);
187
+ }),
188
+
189
+ // Batch load related items
190
+ orderItems: new DataLoader<string, OrderItem[]>(async (orderIds) => {
191
+ const items = await prisma.orderItem.findMany({
192
+ where: { orderId: { in: [...orderIds] } }
193
+ });
194
+
195
+ // Group by orderId
196
+ const itemsByOrder = new Map<string, OrderItem[]>();
197
+ items.forEach(item => {
198
+ const existing = itemsByOrder.get(item.orderId) || [];
199
+ existing.push(item);
200
+ itemsByOrder.set(item.orderId, existing);
201
+ });
202
+
203
+ return orderIds.map(id => itemsByOrder.get(id) || []);
204
+ }),
205
+ };
206
+ }
207
+
208
+ export type Loaders = ReturnType<typeof createLoaders>;
209
+ ```
210
+
211
+ ### Resolvers
212
+
213
+ ```typescript
214
+ // resolvers.ts
215
+ import { GraphQLContext } from './context';
216
+
217
+ export const resolvers = {
218
+ Query: {
219
+ user: async (_, { id }, ctx: GraphQLContext) => {
220
+ return ctx.loaders.user.load(id);
221
+ },
222
+
223
+ users: async (_, { first = 20, after }, ctx: GraphQLContext) => {
224
+ const cursor = after ? { id: after } : undefined;
225
+
226
+ const users = await ctx.prisma.user.findMany({
227
+ take: first + 1, // Fetch one extra for hasNextPage
228
+ cursor,
229
+ skip: cursor ? 1 : 0,
230
+ orderBy: { createdAt: 'desc' }
231
+ });
232
+
233
+ const hasNextPage = users.length > first;
234
+ const edges = users.slice(0, first).map(user => ({
235
+ node: user,
236
+ cursor: user.id
237
+ }));
238
+
239
+ return {
240
+ edges,
241
+ pageInfo: {
242
+ hasNextPage,
243
+ hasPreviousPage: !!after,
244
+ startCursor: edges[0]?.cursor,
245
+ endCursor: edges[edges.length - 1]?.cursor
246
+ },
247
+ totalCount: await ctx.prisma.user.count()
248
+ };
249
+ },
250
+ },
251
+
252
+ Mutation: {
253
+ createUser: async (_, { input }, ctx: GraphQLContext) => {
254
+ return ctx.prisma.user.create({
255
+ data: {
256
+ email: input.email,
257
+ name: input.name,
258
+ passwordHash: await hash(input.password)
259
+ }
260
+ });
261
+ },
262
+
263
+ createOrder: async (_, { input }, ctx: GraphQLContext) => {
264
+ // Verify auth
265
+ if (!ctx.user) {
266
+ throw new Error('Unauthorized');
267
+ }
268
+
269
+ return ctx.prisma.$transaction(async (tx) => {
270
+ // Calculate total
271
+ const products = await tx.product.findMany({
272
+ where: { id: { in: input.items.map(i => i.productId) } }
273
+ });
274
+
275
+ const total = input.items.reduce((sum, item) => {
276
+ const product = products.find(p => p.id === item.productId);
277
+ return sum + (product?.price ?? 0) * item.quantity;
278
+ }, 0);
279
+
280
+ // Create order
281
+ return tx.order.create({
282
+ data: {
283
+ userId: ctx.user.id,
284
+ status: 'PENDING',
285
+ total,
286
+ items: {
287
+ create: input.items.map(item => ({
288
+ productId: item.productId,
289
+ quantity: item.quantity,
290
+ unitPrice: products.find(p => p.id === item.productId)?.price ?? 0
291
+ }))
292
+ }
293
+ }
294
+ });
295
+ });
296
+ }
297
+ },
298
+
299
+ // Field resolvers
300
+ User: {
301
+ orders: (user, _, ctx: GraphQLContext) => {
302
+ return ctx.prisma.order.findMany({
303
+ where: { userId: user.id }
304
+ });
305
+ },
306
+ ordersCount: (user, _, ctx: GraphQLContext) => {
307
+ return ctx.prisma.order.count({
308
+ where: { userId: user.id }
309
+ });
310
+ }
311
+ },
312
+
313
+ Order: {
314
+ user: (order, _, ctx: GraphQLContext) => {
315
+ return ctx.loaders.user.load(order.userId);
316
+ },
317
+ items: (order, _, ctx: GraphQLContext) => {
318
+ return ctx.loaders.orderItems.load(order.id);
319
+ }
320
+ },
321
+
322
+ OrderItem: {
323
+ product: (item, _, ctx: GraphQLContext) => {
324
+ return ctx.loaders.product.load(item.productId);
325
+ }
326
+ }
327
+ };
328
+ ```
329
+
330
+ ---
331
+
332
+ ## 3. Apollo Server Setup
333
+
334
+ ```typescript
335
+ // server.ts
336
+ import { ApolloServer } from '@apollo/server';
337
+ import { expressMiddleware } from '@apollo/server/express4';
338
+ import { createLoaders } from './dataloaders';
339
+ import { prisma } from './prisma';
340
+ import { typeDefs } from './schema';
341
+ import { resolvers } from './resolvers';
342
+
343
+ const server = new ApolloServer({
344
+ typeDefs,
345
+ resolvers,
346
+ plugins: [
347
+ // Disable introspection in production
348
+ ...(process.env.NODE_ENV === 'production'
349
+ ? [{ async serverWillStart() { return { async drainServer() {} }; } }]
350
+ : []),
351
+ ],
352
+ validationRules: [
353
+ // Limit query depth
354
+ depthLimit(10),
355
+ // Limit query complexity
356
+ createComplexityLimitRule(1000),
357
+ ],
358
+ });
359
+
360
+ await server.start();
361
+
362
+ app.use(
363
+ '/graphql',
364
+ expressMiddleware(server, {
365
+ context: async ({ req }) => {
366
+ const token = req.headers.authorization?.replace('Bearer ', '');
367
+ const user = token ? await verifyToken(token) : null;
368
+
369
+ return {
370
+ user,
371
+ prisma,
372
+ loaders: createLoaders(), // Fresh loaders per request
373
+ };
374
+ },
375
+ })
376
+ );
377
+ ```
378
+
379
+ ---
380
+
381
+ ## 4. Apollo Client Setup
382
+
383
+ ```typescript
384
+ // lib/apollo-client.ts
385
+ import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
386
+
387
+ const httpLink = createHttpLink({
388
+ uri: process.env.NEXT_PUBLIC_GRAPHQL_URL,
389
+ });
390
+
391
+ export const apolloClient = new ApolloClient({
392
+ link: httpLink,
393
+ cache: new InMemoryCache({
394
+ typePolicies: {
395
+ Query: {
396
+ fields: {
397
+ users: {
398
+ // Cursor-based pagination
399
+ keyArgs: false,
400
+ merge(existing, incoming, { args }) {
401
+ if (!args?.after) {
402
+ return incoming;
403
+ }
404
+ return {
405
+ ...incoming,
406
+ edges: [...(existing?.edges ?? []), ...incoming.edges],
407
+ };
408
+ },
409
+ },
410
+ },
411
+ },
412
+ },
413
+ }),
414
+ });
415
+ ```
416
+
417
+ ---
418
+
419
+ ## 5. Anti-Patterns
420
+
421
+ ### ❌ No DataLoader
422
+
423
+ ```typescript
424
+ // WRONG: N+1 queries
425
+ const Order = {
426
+ items: async (order) => {
427
+ return prisma.orderItem.findMany({ where: { orderId: order.id } });
428
+ // 100 orders = 100 queries!
429
+ }
430
+ };
431
+
432
+ // CORRECT: Use DataLoader
433
+ const Order = {
434
+ items: (order, _, ctx) => ctx.loaders.orderItems.load(order.id)
435
+ // 100 orders = 1 batched query!
436
+ };
437
+ ```
438
+
439
+ ### ❌ No Query Depth Limiting
440
+
441
+ ```graphql
442
+ # WRONG: Allow unlimited depth
443
+ query DeeplyNested {
444
+ user(id: "1") {
445
+ orders {
446
+ items {
447
+ product {
448
+ category {
449
+ products {
450
+ # ... infinite nesting
451
+ }
452
+ }
453
+ }
454
+ }
455
+ }
456
+ }
457
+ }
458
+ ```
459
+
460
+ ### ❌ Introspection in Production
461
+
462
+ ```typescript
463
+ // WRONG: Schema exposed in production
464
+ const server = new ApolloServer({ typeDefs, resolvers });
465
+
466
+ // CORRECT: Disable introspection
467
+ const server = new ApolloServer({
468
+ typeDefs,
469
+ resolvers,
470
+ introspection: process.env.NODE_ENV !== 'production',
471
+ });
472
+ ```
473
+
474
+ ---
475
+
476
+ ## 6. Sharp Edges
477
+
478
+ | Issue | Severity | Solution |
479
+ |-------|----------|----------|
480
+ | N+1 queries | Critical | USE DATALOADER |
481
+ | Deep nesting DoS | Critical | LIMIT DEPTH AND COMPLEXITY |
482
+ | Introspection exposed | High | DISABLE IN PRODUCTION |
483
+ | Auth only in directives | High | AUTHORIZE IN RESOLVERS |
484
+ | Non-null cascading nulls | Medium | DESIGN NULLABILITY |
485
+
486
+ ---
487
+
488
+ ## Related Skills
489
+
490
+ - `api-patterns` - REST comparison
491
+ - `database-design` - Schema design
492
+ - `prisma-expert` - ORM integration
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: idor-testing
3
+ description: "Specialized skill for finding Insecure Direct Object Reference (IDOR) vulnerabilities. Covers parameter tampering, mass assignment, and BOLA (API)."
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # 🔑 IDOR Testing
8
+
9
+ You are a Bug Bounty hunter who specializes in IDORs—the most common and impactful web vulnerability. You understand that IDOR is a logic flaw that scanners almost always miss.
10
+
11
+ ---
12
+
13
+ ## What is IDOR?
14
+ When an application uses user-supplied input to access objects directly without an authorization check.
15
+ - **Example**: `/api/v1/orders/1001` -> works. Change to `/api/v1/orders/1002` -> access another user's order.
16
+
17
+ ---
18
+
19
+ ## Advanced IDOR Patterns
20
+
21
+ ### 1. Parameter Pollution
22
+ Standard IDOR: `?id=123`
23
+ Bypass: `?id=123&id=456` (Server might process the second one while checking the first).
24
+
25
+ ### 2. ID Type Conversion
26
+ `GET /user/123` -> Forbidden.
27
+ Try: `GET /user/admin` or `GET /user/me`.
28
+
29
+ ### 3. Path Traversal in API
30
+ `GET /api/users/123/profile` -> Forbidden.
31
+ Try: `GET /api/users/999/../123/profile`.
32
+
33
+ ### 4. Wrapped IDOR (JSON)
34
+ ```json
35
+ // Original
36
+ { "user_id": 123 }
37
+
38
+ // Attempt
39
+ { "user_id": [123, 456] }
40
+ { "user_id": { "id": 456 } }
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Broken Object Level Authorization (BOLA)
46
+ The API version of IDOR.
47
+ - **Technique**: Use a GUID instead of an Integer.
48
+ - **Bypass**: Where do you get the GUIDs? Try public profile pages, search results, or leak them from another API endpoint (e.g., `GET /api/v1/search?q=...`).
49
+
50
+ ---
51
+
52
+ ## Testing Workflow (The "Two User" Method)
53
+ 1. Login as **User A** and **User B**.
54
+ 2. Intercept User A's request to "Update Profile".
55
+ 3. Replace User A's ID/Token with User B's ID/Token.
56
+ 4. If User B's profile is updated while using User A's session -> **CRITICAL IDOR**.
57
+
58
+ ---
59
+
60
+ ## Related Skills
61
+
62
+ - `burp-suite-testing` - The primary tool for IDOR
63
+ - `api-fuzzing-bug-bounty` - Scaling IDOR testing
64
+ - `broken-authentication` - Related auth flaws
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: inngest
3
+ description: "Expertise in building reliable, event-driven applications with Inngest. Covers durable functions, background jobs, and multi-step workflows."
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # ⚡ Inngest
8
+
9
+ You are an expert in Inngest—the durable execution engine for TypeScript. You know how to build background jobs, delayed functions, and complex multi-step workflows without managing queues or cron infrastructure.
10
+
11
+ ---
12
+
13
+ ## When to Use This Skill
14
+
15
+ - Building background jobs in Next.js/Edge environments
16
+ - Implementing multi-step AI pipelines (e.g., Generate -> Review -> Notify)
17
+ - Setting up durable cron jobs
18
+ - Handling high-concurrency event processing
19
+ - Orchestrating complex user onboarding flows
20
+
21
+ ---
22
+
23
+ ## Capabilities
24
+
25
+ - `durable-functions`
26
+ - `event-driven-architecture`
27
+ - `step-functions-in-typescript`
28
+ - `background-job-management`
29
+ - `inngest-middleware`
30
+ - `fan-out-fan-in-patterns`
31
+
32
+ ---
33
+
34
+ ## 1. Defining Functions
35
+
36
+ Inngest functions are event-driven and durable by default.
37
+
38
+ ```typescript
39
+ import { Inngest } from "inngest";
40
+
41
+ const inngest = new Inngest({ id: "my-app" });
42
+
43
+ export const helloWorld = inngest.createFunction(
44
+ { id: "hello-world" },
45
+ { event: "test/hello.world" },
46
+ async ({ event, step }) => {
47
+ await step.run("log-hello", () => {
48
+ console.log("Hello", event.data.name);
49
+ });
50
+
51
+ const result = await step.run("calculate", () => 2 + 2);
52
+
53
+ return { result };
54
+ }
55
+ );
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 2. Multi-Step AI Workflow
61
+
62
+ A common pattern for AI agents where steps might time out or need retries.
63
+
64
+ ```typescript
65
+ export const processArticle = inngest.createFunction(
66
+ { id: "process-article", retries: 3 },
67
+ { event: "article/submitted" },
68
+ async ({ event, step }) => {
69
+ // 1. Generate Summary
70
+ const summary = await step.run("generate-summary", async () => {
71
+ return await llm.summarize(event.data.text);
72
+ });
73
+
74
+ // 2. Wait for a specific duration or external signal
75
+ await step.sleep("wait-for-review", "1h");
76
+
77
+ // 3. Notify user
78
+ await step.run("notify-user", async () => {
79
+ await resend.emails.send({
80
+ to: event.data.email,
81
+ subject: "Your summary is ready",
82
+ text: summary
83
+ });
84
+ });
85
+ }
86
+ );
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 3. Fan-Out Pattern (Batch Processing)
92
+
93
+ ```typescript
94
+ export const handleMigration = inngest.createFunction(
95
+ { id: "master-migration" },
96
+ { event: "db/migrate-all" },
97
+ async ({ step }) => {
98
+ const users = await step.run("fetch-users", () => db.users.findMany());
99
+
100
+ // Fan-out: create a separate function execution for each user
101
+ const events = users.map(user => ({
102
+ name: "db/user.migrate",
103
+ data: { userId: user.id }
104
+ }));
105
+
106
+ await step.sendEvent("send-migration-events", events);
107
+ }
108
+ );
109
+ ```
110
+
111
+ ---
112
+
113
+ ## 4. Advanced Features
114
+
115
+ | Feature | Description |
116
+ |---------|-------------|
117
+ | **Concurrency** | Limit how many instances of a function run at once. |
118
+ | **Idempotency** | Ensure functions only process the same event once via `idempotencyKey`. |
119
+ | **Cancellation** | Stop a running workflow if a specific event is received (e.g., `order/cancelled`). |
120
+ | **Throttling** | Limit events over a duration per user. |
121
+
122
+ ---
123
+
124
+ ## Related Skills
125
+
126
+ - `api-patterns` - For event design
127
+ - `nodejs-best-practices` - General backend
128
+ - `bullmq-specialist` - Alternative for complex Redis-based queues