@plazmodium/odin 0.3.2-beta → 0.3.4-beta

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 (73) hide show
  1. package/README.md +82 -11
  2. package/builtin/ODIN.md +1045 -0
  3. package/builtin/agent-definitions/README.md +170 -0
  4. package/builtin/agent-definitions/_shared-context.md +377 -0
  5. package/builtin/agent-definitions/architect.md +627 -0
  6. package/builtin/agent-definitions/builder.md +716 -0
  7. package/builtin/agent-definitions/discovery.md +293 -0
  8. package/builtin/agent-definitions/documenter.md +238 -0
  9. package/builtin/agent-definitions/guardian.md +1049 -0
  10. package/builtin/agent-definitions/integrator.md +363 -0
  11. package/builtin/agent-definitions/planning.md +236 -0
  12. package/builtin/agent-definitions/product.md +405 -0
  13. package/builtin/agent-definitions/release.md +430 -0
  14. package/builtin/agent-definitions/reviewer.md +447 -0
  15. package/builtin/agent-definitions/watcher.md +402 -0
  16. package/builtin/skills/api/graphql/SKILL.md +548 -0
  17. package/builtin/skills/api/grpc/SKILL.md +554 -0
  18. package/builtin/skills/api/rest-api/SKILL.md +469 -0
  19. package/builtin/skills/api/trpc/SKILL.md +503 -0
  20. package/builtin/skills/architecture/clean-architecture/SKILL.md +141 -0
  21. package/builtin/skills/architecture/domain-driven-design/SKILL.md +129 -0
  22. package/builtin/skills/architecture/event-driven/SKILL.md +145 -0
  23. package/builtin/skills/architecture/microservices/SKILL.md +143 -0
  24. package/builtin/skills/architecture/tla-precheck/SKILL.md +171 -0
  25. package/builtin/skills/backend/golang-gin/SKILL.md +141 -0
  26. package/builtin/skills/backend/nodejs-express/SKILL.md +277 -0
  27. package/builtin/skills/backend/nodejs-fastify/SKILL.md +152 -0
  28. package/builtin/skills/backend/python-django/SKILL.md +128 -0
  29. package/builtin/skills/backend/python-fastapi/SKILL.md +140 -0
  30. package/builtin/skills/database/mongodb/SKILL.md +132 -0
  31. package/builtin/skills/database/postgresql/SKILL.md +120 -0
  32. package/builtin/skills/database/prisma-orm/SKILL.md +366 -0
  33. package/builtin/skills/database/redis/SKILL.md +140 -0
  34. package/builtin/skills/database/supabase/SKILL.md +416 -0
  35. package/builtin/skills/devops/aws/SKILL.md +382 -0
  36. package/builtin/skills/devops/docker/SKILL.md +359 -0
  37. package/builtin/skills/devops/github-actions/SKILL.md +435 -0
  38. package/builtin/skills/devops/kubernetes/SKILL.md +459 -0
  39. package/builtin/skills/devops/terraform/SKILL.md +453 -0
  40. package/builtin/skills/frontend/alpine-dev/SKILL.md +27 -0
  41. package/builtin/skills/frontend/angular-dev/SKILL.md +28 -0
  42. package/builtin/skills/frontend/astro-dev/SKILL.md +28 -0
  43. package/builtin/skills/frontend/htmx-dev/SKILL.md +28 -0
  44. package/builtin/skills/frontend/nextjs-dev/SKILL.md +470 -0
  45. package/builtin/skills/frontend/react-patterns/SKILL.md +166 -0
  46. package/builtin/skills/frontend/svelte-dev/SKILL.md +28 -0
  47. package/builtin/skills/frontend/tailwindcss/SKILL.md +131 -0
  48. package/builtin/skills/frontend/vuejs-dev/SKILL.md +28 -0
  49. package/builtin/skills/generic-dev/SKILL.md +307 -0
  50. package/builtin/skills/testing/cypress/SKILL.md +372 -0
  51. package/builtin/skills/testing/jest/SKILL.md +176 -0
  52. package/builtin/skills/testing/playwright/SKILL.md +341 -0
  53. package/builtin/skills/testing/unit-tests-eval-sdd/SKILL.md +73 -0
  54. package/builtin/skills/testing/unit-tests-sdd/SKILL.md +83 -0
  55. package/builtin/skills/testing/vitest/SKILL.md +249 -0
  56. package/dist/adapters/skills/filesystem.d.ts.map +1 -1
  57. package/dist/adapters/skills/filesystem.js +2 -18
  58. package/dist/adapters/skills/filesystem.js.map +1 -1
  59. package/dist/builtin-assets.d.ts +8 -0
  60. package/dist/builtin-assets.d.ts.map +1 -0
  61. package/dist/builtin-assets.js +90 -0
  62. package/dist/builtin-assets.js.map +1 -0
  63. package/dist/init.js +69 -11
  64. package/dist/init.js.map +1 -1
  65. package/dist/schemas.d.ts +1 -1
  66. package/dist/server.js +1 -1
  67. package/dist/server.js.map +1 -1
  68. package/dist/tools/prepare-phase-context.d.ts.map +1 -1
  69. package/dist/tools/prepare-phase-context.js +5 -0
  70. package/dist/tools/prepare-phase-context.js.map +1 -1
  71. package/dist/types.d.ts +3 -0
  72. package/dist/types.d.ts.map +1 -1
  73. package/package.json +5 -3
@@ -0,0 +1,548 @@
1
+ ---
2
+ name: graphql
3
+ description: GraphQL API design and implementation expertise. Covers schema design, resolvers, queries, mutations, subscriptions, and client integration with Apollo or urql.
4
+ category: api
5
+ compatible_with:
6
+ - nodejs-express
7
+ - nodejs-fastify
8
+ - nextjs-dev
9
+ - react-patterns
10
+ ---
11
+
12
+ # GraphQL API Design
13
+
14
+ ## Instructions
15
+
16
+ 1. **Assess the API need**: Flexible queries, real-time data, or complex relationships.
17
+ 2. **Follow GraphQL conventions**:
18
+ - Schema-first or code-first approach
19
+ - Proper type definitions
20
+ - N+1 query prevention with DataLoader
21
+ - Input validation
22
+ 3. **Provide complete examples**: Include schema, resolvers, and client queries.
23
+ 4. **Guide on best practices**: Pagination, error handling, authentication.
24
+
25
+ ## Schema Design
26
+
27
+ ### Basic Types
28
+
29
+ ```graphql
30
+ type User {
31
+ id: ID!
32
+ email: String!
33
+ name: String!
34
+ avatar: String
35
+ role: Role!
36
+ posts: [Post!]!
37
+ createdAt: DateTime!
38
+ updatedAt: DateTime!
39
+ }
40
+
41
+ type Post {
42
+ id: ID!
43
+ title: String!
44
+ content: String!
45
+ published: Boolean!
46
+ author: User!
47
+ comments: [Comment!]!
48
+ tags: [Tag!]!
49
+ createdAt: DateTime!
50
+ }
51
+
52
+ enum Role {
53
+ USER
54
+ ADMIN
55
+ MODERATOR
56
+ }
57
+
58
+ scalar DateTime
59
+ ```
60
+
61
+ ### Input Types
62
+
63
+ ```graphql
64
+ input CreateUserInput {
65
+ email: String!
66
+ name: String!
67
+ password: String!
68
+ role: Role = USER
69
+ }
70
+
71
+ input UpdateUserInput {
72
+ email: String
73
+ name: String
74
+ avatar: String
75
+ }
76
+
77
+ input PostFilters {
78
+ published: Boolean
79
+ authorId: ID
80
+ tags: [String!]
81
+ search: String
82
+ }
83
+ ```
84
+
85
+ ### Queries and Mutations
86
+
87
+ ```graphql
88
+ type Query {
89
+ # Single resource
90
+ user(id: ID!): User
91
+ post(id: ID!): Post
92
+
93
+ # Collections with pagination
94
+ users(
95
+ first: Int
96
+ after: String
97
+ filter: UserFilter
98
+ ): UserConnection!
99
+
100
+ posts(
101
+ first: Int
102
+ after: String
103
+ filter: PostFilters
104
+ orderBy: PostOrderBy
105
+ ): PostConnection!
106
+
107
+ # Current user
108
+ me: User
109
+ }
110
+
111
+ type Mutation {
112
+ # User mutations
113
+ createUser(input: CreateUserInput!): CreateUserPayload!
114
+ updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
115
+ deleteUser(id: ID!): DeleteUserPayload!
116
+
117
+ # Auth mutations
118
+ login(email: String!, password: String!): AuthPayload!
119
+ logout: Boolean!
120
+
121
+ # Post mutations
122
+ createPost(input: CreatePostInput!): Post!
123
+ updatePost(id: ID!, input: UpdatePostInput!): Post!
124
+ publishPost(id: ID!): Post!
125
+ deletePost(id: ID!): Boolean!
126
+ }
127
+
128
+ type Subscription {
129
+ postCreated: Post!
130
+ commentAdded(postId: ID!): Comment!
131
+ }
132
+ ```
133
+
134
+ ### Relay-style Pagination
135
+
136
+ ```graphql
137
+ type UserConnection {
138
+ edges: [UserEdge!]!
139
+ pageInfo: PageInfo!
140
+ totalCount: Int!
141
+ }
142
+
143
+ type UserEdge {
144
+ cursor: String!
145
+ node: User!
146
+ }
147
+
148
+ type PageInfo {
149
+ hasNextPage: Boolean!
150
+ hasPreviousPage: Boolean!
151
+ startCursor: String
152
+ endCursor: String
153
+ }
154
+ ```
155
+
156
+ ### Payload Types (for mutations)
157
+
158
+ ```graphql
159
+ type CreateUserPayload {
160
+ user: User
161
+ errors: [Error!]
162
+ }
163
+
164
+ type Error {
165
+ field: String
166
+ message: String!
167
+ code: String!
168
+ }
169
+ ```
170
+
171
+ ## Resolvers
172
+
173
+ ### Basic Resolvers (Node.js)
174
+
175
+ ```typescript
176
+ import { Resolvers } from './generated/graphql';
177
+
178
+ const resolvers: Resolvers = {
179
+ Query: {
180
+ user: async (_, { id }, { dataSources }) => {
181
+ return dataSources.users.findById(id);
182
+ },
183
+
184
+ users: async (_, { first = 20, after, filter }, { dataSources }) => {
185
+ return dataSources.users.findMany({ first, after, filter });
186
+ },
187
+
188
+ me: async (_, __, { currentUser }) => {
189
+ return currentUser;
190
+ },
191
+ },
192
+
193
+ Mutation: {
194
+ createUser: async (_, { input }, { dataSources }) => {
195
+ try {
196
+ const user = await dataSources.users.create(input);
197
+ return { user, errors: null };
198
+ } catch (error) {
199
+ return {
200
+ user: null,
201
+ errors: [{ message: error.message, code: 'CREATE_FAILED' }]
202
+ };
203
+ }
204
+ },
205
+
206
+ updateUser: async (_, { id, input }, { dataSources, currentUser }) => {
207
+ if (currentUser.id !== id && currentUser.role !== 'ADMIN') {
208
+ throw new ForbiddenError('Not authorized');
209
+ }
210
+ return dataSources.users.update(id, input);
211
+ },
212
+ },
213
+
214
+ // Field resolvers
215
+ User: {
216
+ posts: async (user, _, { dataSources }) => {
217
+ return dataSources.posts.findByAuthorId(user.id);
218
+ },
219
+ },
220
+
221
+ Post: {
222
+ author: async (post, _, { dataSources }) => {
223
+ return dataSources.users.findById(post.authorId);
224
+ },
225
+ },
226
+ };
227
+ ```
228
+
229
+ ### DataLoader (N+1 Prevention)
230
+
231
+ ```typescript
232
+ import DataLoader from 'dataloader';
233
+
234
+ // Create loaders
235
+ const createLoaders = (db) => ({
236
+ userLoader: new DataLoader(async (ids: string[]) => {
237
+ const users = await db.users.findMany({
238
+ where: { id: { in: ids } }
239
+ });
240
+ // Return in same order as requested ids
241
+ return ids.map(id => users.find(u => u.id === id));
242
+ }),
243
+
244
+ postsByAuthorLoader: new DataLoader(async (authorIds: string[]) => {
245
+ const posts = await db.posts.findMany({
246
+ where: { authorId: { in: authorIds } }
247
+ });
248
+ // Group by author
249
+ return authorIds.map(authorId =>
250
+ posts.filter(p => p.authorId === authorId)
251
+ );
252
+ }),
253
+ });
254
+
255
+ // Use in resolvers
256
+ const resolvers = {
257
+ Post: {
258
+ author: (post, _, { loaders }) => {
259
+ return loaders.userLoader.load(post.authorId);
260
+ },
261
+ },
262
+ User: {
263
+ posts: (user, _, { loaders }) => {
264
+ return loaders.postsByAuthorLoader.load(user.id);
265
+ },
266
+ },
267
+ };
268
+ ```
269
+
270
+ ## Server Setup
271
+
272
+ ### Apollo Server
273
+
274
+ ```typescript
275
+ import { ApolloServer } from '@apollo/server';
276
+ import { expressMiddleware } from '@apollo/server/express4';
277
+ import { makeExecutableSchema } from '@graphql-tools/schema';
278
+
279
+ const schema = makeExecutableSchema({
280
+ typeDefs,
281
+ resolvers,
282
+ });
283
+
284
+ const server = new ApolloServer({
285
+ schema,
286
+ plugins: [
287
+ ApolloServerPluginDrainHttpServer({ httpServer }),
288
+ ],
289
+ });
290
+
291
+ await server.start();
292
+
293
+ app.use(
294
+ '/graphql',
295
+ cors(),
296
+ express.json(),
297
+ expressMiddleware(server, {
298
+ context: async ({ req }) => {
299
+ const token = req.headers.authorization?.replace('Bearer ', '');
300
+ const currentUser = token ? await verifyToken(token) : null;
301
+
302
+ return {
303
+ currentUser,
304
+ dataSources: createDataSources(db),
305
+ loaders: createLoaders(db),
306
+ };
307
+ },
308
+ })
309
+ );
310
+ ```
311
+
312
+ ## Client Queries
313
+
314
+ ### Apollo Client
315
+
316
+ ```typescript
317
+ import { gql, useQuery, useMutation } from '@apollo/client';
318
+
319
+ // Query
320
+ const GET_USERS = gql`
321
+ query GetUsers($first: Int, $after: String) {
322
+ users(first: $first, after: $after) {
323
+ edges {
324
+ cursor
325
+ node {
326
+ id
327
+ name
328
+ email
329
+ }
330
+ }
331
+ pageInfo {
332
+ hasNextPage
333
+ endCursor
334
+ }
335
+ }
336
+ }
337
+ `;
338
+
339
+ function UserList() {
340
+ const { data, loading, error, fetchMore } = useQuery(GET_USERS, {
341
+ variables: { first: 20 },
342
+ });
343
+
344
+ if (loading) return <Loading />;
345
+ if (error) return <Error error={error} />;
346
+
347
+ return (
348
+ <>
349
+ {data.users.edges.map(({ node }) => (
350
+ <UserCard key={node.id} user={node} />
351
+ ))}
352
+ {data.users.pageInfo.hasNextPage && (
353
+ <button onClick={() => fetchMore({
354
+ variables: { after: data.users.pageInfo.endCursor }
355
+ })}>
356
+ Load More
357
+ </button>
358
+ )}
359
+ </>
360
+ );
361
+ }
362
+
363
+ // Mutation
364
+ const CREATE_USER = gql`
365
+ mutation CreateUser($input: CreateUserInput!) {
366
+ createUser(input: $input) {
367
+ user {
368
+ id
369
+ name
370
+ email
371
+ }
372
+ errors {
373
+ field
374
+ message
375
+ }
376
+ }
377
+ }
378
+ `;
379
+
380
+ function CreateUserForm() {
381
+ const [createUser, { loading }] = useMutation(CREATE_USER, {
382
+ update(cache, { data }) {
383
+ if (data.createUser.user) {
384
+ cache.modify({
385
+ fields: {
386
+ users(existing = { edges: [] }) {
387
+ const newEdge = {
388
+ __typename: 'UserEdge',
389
+ cursor: data.createUser.user.id,
390
+ node: data.createUser.user,
391
+ };
392
+ return {
393
+ ...existing,
394
+ edges: [newEdge, ...existing.edges],
395
+ };
396
+ },
397
+ },
398
+ });
399
+ }
400
+ },
401
+ });
402
+
403
+ const handleSubmit = async (values) => {
404
+ const { data } = await createUser({ variables: { input: values } });
405
+ if (data.createUser.errors) {
406
+ // Handle errors
407
+ }
408
+ };
409
+ }
410
+ ```
411
+
412
+ ### Fragments
413
+
414
+ ```graphql
415
+ fragment UserFields on User {
416
+ id
417
+ name
418
+ email
419
+ avatar
420
+ }
421
+
422
+ fragment PostWithAuthor on Post {
423
+ id
424
+ title
425
+ content
426
+ author {
427
+ ...UserFields
428
+ }
429
+ }
430
+
431
+ query GetPost($id: ID!) {
432
+ post(id: $id) {
433
+ ...PostWithAuthor
434
+ comments {
435
+ id
436
+ content
437
+ author {
438
+ ...UserFields
439
+ }
440
+ }
441
+ }
442
+ }
443
+ ```
444
+
445
+ ## Subscriptions
446
+
447
+ ### Server
448
+
449
+ ```typescript
450
+ import { createServer } from 'http';
451
+ import { WebSocketServer } from 'ws';
452
+ import { useServer } from 'graphql-ws/lib/use/ws';
453
+
454
+ const httpServer = createServer(app);
455
+ const wsServer = new WebSocketServer({
456
+ server: httpServer,
457
+ path: '/graphql',
458
+ });
459
+
460
+ useServer(
461
+ {
462
+ schema,
463
+ context: async (ctx) => {
464
+ const token = ctx.connectionParams?.authToken;
465
+ return { currentUser: await verifyToken(token) };
466
+ },
467
+ },
468
+ wsServer
469
+ );
470
+
471
+ // Resolver
472
+ const resolvers = {
473
+ Subscription: {
474
+ postCreated: {
475
+ subscribe: () => pubsub.asyncIterator(['POST_CREATED']),
476
+ },
477
+ commentAdded: {
478
+ subscribe: (_, { postId }) => {
479
+ return pubsub.asyncIterator([`COMMENT_ADDED_${postId}`]);
480
+ },
481
+ },
482
+ },
483
+ Mutation: {
484
+ createPost: async (_, { input }, { dataSources, pubsub }) => {
485
+ const post = await dataSources.posts.create(input);
486
+ pubsub.publish('POST_CREATED', { postCreated: post });
487
+ return post;
488
+ },
489
+ },
490
+ };
491
+ ```
492
+
493
+ ### Client
494
+
495
+ ```typescript
496
+ const POST_SUBSCRIPTION = gql`
497
+ subscription OnPostCreated {
498
+ postCreated {
499
+ id
500
+ title
501
+ author {
502
+ name
503
+ }
504
+ }
505
+ }
506
+ `;
507
+
508
+ function PostFeed() {
509
+ const { data, subscribeToMore } = useQuery(GET_POSTS);
510
+
511
+ useEffect(() => {
512
+ return subscribeToMore({
513
+ document: POST_SUBSCRIPTION,
514
+ updateQuery: (prev, { subscriptionData }) => {
515
+ if (!subscriptionData.data) return prev;
516
+ const newPost = subscriptionData.data.postCreated;
517
+ return {
518
+ ...prev,
519
+ posts: {
520
+ ...prev.posts,
521
+ edges: [
522
+ { node: newPost, cursor: newPost.id },
523
+ ...prev.posts.edges,
524
+ ],
525
+ },
526
+ };
527
+ },
528
+ });
529
+ }, [subscribeToMore]);
530
+ }
531
+ ```
532
+
533
+ ## Best Practices
534
+
535
+ - **Use DataLoader** - Prevent N+1 queries
536
+ - **Implement pagination** - Relay cursor-based for large datasets
537
+ - **Validate inputs** - Use custom scalars or directives
538
+ - **Handle errors gracefully** - Return errors in payload, not exceptions
539
+ - **Depth limiting** - Prevent deeply nested queries
540
+ - **Query complexity** - Limit expensive queries
541
+ - **Persisted queries** - For production security
542
+ - **Schema stitching/federation** - For microservices
543
+
544
+ ## References
545
+
546
+ - GraphQL Specification: https://spec.graphql.org/
547
+ - Apollo Documentation: https://www.apollographql.com/docs/
548
+ - GraphQL Best Practices: https://graphql.org/learn/best-practices/