@ekkos/cli 0.2.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 (135) hide show
  1. package/dist/cache/LocalSessionStore.d.ts +129 -0
  2. package/dist/cache/LocalSessionStore.js +688 -0
  3. package/dist/cache/capture.d.ts +26 -0
  4. package/dist/cache/capture.js +461 -0
  5. package/dist/cache/index.d.ts +7 -0
  6. package/dist/cache/index.js +23 -0
  7. package/dist/cache/types.d.ts +147 -0
  8. package/dist/cache/types.js +40 -0
  9. package/dist/commands/init.d.ts +9 -0
  10. package/dist/commands/init.js +478 -0
  11. package/dist/commands/run.d.ts +12 -0
  12. package/dist/commands/run.js +829 -0
  13. package/dist/commands/setup.d.ts +6 -0
  14. package/dist/commands/setup.js +658 -0
  15. package/dist/commands/status.d.ts +1 -0
  16. package/dist/commands/status.js +109 -0
  17. package/dist/commands/test.d.ts +1 -0
  18. package/dist/commands/test.js +157 -0
  19. package/dist/deploy/agents.d.ts +15 -0
  20. package/dist/deploy/agents.js +72 -0
  21. package/dist/deploy/hooks.d.ts +16 -0
  22. package/dist/deploy/hooks.js +121 -0
  23. package/dist/deploy/index.d.ts +7 -0
  24. package/dist/deploy/index.js +24 -0
  25. package/dist/deploy/instructions.d.ts +12 -0
  26. package/dist/deploy/instructions.js +36 -0
  27. package/dist/deploy/mcp.d.ts +19 -0
  28. package/dist/deploy/mcp.js +109 -0
  29. package/dist/deploy/plugins.d.ts +19 -0
  30. package/dist/deploy/plugins.js +62 -0
  31. package/dist/deploy/settings.d.ts +8 -0
  32. package/dist/deploy/settings.js +84 -0
  33. package/dist/deploy/skills.d.ts +19 -0
  34. package/dist/deploy/skills.js +60 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +71 -0
  37. package/dist/restore/RestoreOrchestrator.d.ts +48 -0
  38. package/dist/restore/RestoreOrchestrator.js +481 -0
  39. package/dist/restore/index.d.ts +4 -0
  40. package/dist/restore/index.js +20 -0
  41. package/dist/utils/platform.d.ts +29 -0
  42. package/dist/utils/platform.js +65 -0
  43. package/dist/utils/session-words.json +119 -0
  44. package/dist/utils/state.d.ts +57 -0
  45. package/dist/utils/state.js +186 -0
  46. package/dist/utils/templates.d.ts +24 -0
  47. package/dist/utils/templates.js +118 -0
  48. package/package.json +48 -0
  49. package/templates/CLAUDE.md +287 -0
  50. package/templates/README.md +378 -0
  51. package/templates/agents/README.md +182 -0
  52. package/templates/agents/code-reviewer.md +166 -0
  53. package/templates/agents/debug-detective.md +169 -0
  54. package/templates/agents/ekkOS_Vercel.md +99 -0
  55. package/templates/agents/extension-manager.md +229 -0
  56. package/templates/agents/git-companion.md +185 -0
  57. package/templates/agents/github-test-agent.md +321 -0
  58. package/templates/agents/railway-manager.md +179 -0
  59. package/templates/claude-plugins/PHASE2_COMPLETION.md +346 -0
  60. package/templates/claude-plugins/PLUGIN_PROPOSALS.md +1776 -0
  61. package/templates/claude-plugins/README.md +587 -0
  62. package/templates/claude-plugins/agents/code-reviewer.json +14 -0
  63. package/templates/claude-plugins/agents/debug-detective.json +15 -0
  64. package/templates/claude-plugins/agents/git-companion.json +14 -0
  65. package/templates/claude-plugins/blog-manager/.claude-plugin/plugin.json +8 -0
  66. package/templates/claude-plugins/blog-manager/commands/blog.md +691 -0
  67. package/templates/claude-plugins/golden-loop-monitor/.claude-plugin/plugin.json +8 -0
  68. package/templates/claude-plugins/golden-loop-monitor/commands/loop-status.md +434 -0
  69. package/templates/claude-plugins/learning-tracker/.claude-plugin/plugin.json +8 -0
  70. package/templates/claude-plugins/learning-tracker/commands/my-patterns.md +282 -0
  71. package/templates/claude-plugins/memory-lens/.claude-plugin/plugin.json +8 -0
  72. package/templates/claude-plugins/memory-lens/commands/memory-search.md +181 -0
  73. package/templates/claude-plugins/pattern-coach/.claude-plugin/plugin.json +8 -0
  74. package/templates/claude-plugins/pattern-coach/commands/forge.md +365 -0
  75. package/templates/claude-plugins/project-schema-validator/.claude-plugin/plugin.json +8 -0
  76. package/templates/claude-plugins/project-schema-validator/commands/validate-schema.md +582 -0
  77. package/templates/claude-plugins-admin/AGENT_TEAM_PROPOSALS.md +819 -0
  78. package/templates/claude-plugins-admin/README.md +446 -0
  79. package/templates/claude-plugins-admin/autonomous-admin-agent/.claude-plugin/plugin.json +8 -0
  80. package/templates/claude-plugins-admin/autonomous-admin-agent/commands/agent.md +595 -0
  81. package/templates/claude-plugins-admin/backend-agent/.claude-plugin/plugin.json +8 -0
  82. package/templates/claude-plugins-admin/backend-agent/commands/backend.md +798 -0
  83. package/templates/claude-plugins-admin/deploy-guardian/.claude-plugin/plugin.json +8 -0
  84. package/templates/claude-plugins-admin/deploy-guardian/commands/deploy.md +554 -0
  85. package/templates/claude-plugins-admin/frontend-agent/.claude-plugin/plugin.json +8 -0
  86. package/templates/claude-plugins-admin/frontend-agent/commands/frontend.md +881 -0
  87. package/templates/claude-plugins-admin/mcp-server-manager/.claude-plugin/plugin.json +8 -0
  88. package/templates/claude-plugins-admin/mcp-server-manager/commands/mcp.md +85 -0
  89. package/templates/claude-plugins-admin/memory-system-monitor/.claude-plugin/plugin.json +8 -0
  90. package/templates/claude-plugins-admin/memory-system-monitor/commands/memory-health.md +569 -0
  91. package/templates/claude-plugins-admin/qa-agent/.claude-plugin/plugin.json +8 -0
  92. package/templates/claude-plugins-admin/qa-agent/commands/qa.md +863 -0
  93. package/templates/claude-plugins-admin/tech-lead-agent/.claude-plugin/plugin.json +8 -0
  94. package/templates/claude-plugins-admin/tech-lead-agent/commands/lead.md +732 -0
  95. package/templates/commands/continue.md +47 -0
  96. package/templates/cursor-hooks/after-agent-response.sh +117 -0
  97. package/templates/cursor-hooks/before-submit-prompt.sh +419 -0
  98. package/templates/cursor-hooks/hooks.json +20 -0
  99. package/templates/cursor-hooks/lib/contract.sh +320 -0
  100. package/templates/cursor-hooks/stop.sh +75 -0
  101. package/templates/cursor-rules/ekkos-memory.md +187 -0
  102. package/templates/hooks/assistant-response.sh +96 -0
  103. package/templates/hooks/hooks.json +28 -0
  104. package/templates/hooks/lib/contract.sh +320 -0
  105. package/templates/hooks/lib/state.sh +158 -0
  106. package/templates/hooks/session-start.ps1 +41 -0
  107. package/templates/hooks/session-start.sh +318 -0
  108. package/templates/hooks/stop.ps1 +16 -0
  109. package/templates/hooks/stop.sh +989 -0
  110. package/templates/hooks/user-prompt-submit.ps1 +174 -0
  111. package/templates/hooks/user-prompt-submit.sh +587 -0
  112. package/templates/hooks-node/lib/state.js +187 -0
  113. package/templates/hooks-node/stop.js +416 -0
  114. package/templates/hooks-node/user-prompt-submit.js +337 -0
  115. package/templates/plan-template.md +306 -0
  116. package/templates/rules/00-hooks-contract.mdc +89 -0
  117. package/templates/rules/30-ekkos-core.mdc +188 -0
  118. package/templates/rules/31-ekkos-messages.mdc +78 -0
  119. package/templates/skills/continue/SKILL.md +169 -0
  120. package/templates/skills/ekkOS_Deep_Recall/Skill.md +282 -0
  121. package/templates/skills/ekkOS_Learn/Skill.md +265 -0
  122. package/templates/skills/ekkOS_Memory_First/Skill.md +206 -0
  123. package/templates/skills/ekkOS_Plan_Assist/Skill.md +302 -0
  124. package/templates/skills/ekkOS_Preferences/Skill.md +247 -0
  125. package/templates/skills/ekkOS_Reflect/Skill.md +257 -0
  126. package/templates/skills/ekkOS_Safety/Skill.md +265 -0
  127. package/templates/skills/ekkOS_Schema/Skill.md +251 -0
  128. package/templates/skills/ekkOS_Summary/Skill.md +257 -0
  129. package/templates/skills/ekkOS_Vault/Skill.md +287 -0
  130. package/templates/skills/permissions/Skill.md +322 -0
  131. package/templates/spec-template.md +159 -0
  132. package/templates/windsurf-hooks/before-submit-prompt.sh +238 -0
  133. package/templates/windsurf-hooks/hooks.json +10 -0
  134. package/templates/windsurf-hooks/lib/contract.sh +320 -0
  135. package/templates/windsurf-rules/ekkos-memory.md +129 -0
@@ -0,0 +1,798 @@
1
+ # Backend Agent
2
+
3
+ **ADMIN ONLY** - AI Backend Developer specialized in API development, database schema design, Supabase, PostgreSQL, Row Level Security, and server-side logic.
4
+
5
+ ## Overview
6
+
7
+ The Backend Agent is your API and database expert. It:
8
+ - Designs database schemas with proper indexes and RLS policies
9
+ - Builds REST and GraphQL APIs
10
+ - Implements authentication and authorization
11
+ - Optimizes database queries
12
+ - Handles data migrations
13
+ - Ensures data integrity and security
14
+
15
+ ## Commands
16
+
17
+ ### `/backend schema`
18
+
19
+ Design and implement database schema.
20
+
21
+ ```bash
22
+ /backend schema "Entity or feature description"
23
+
24
+ # Examples
25
+ /backend schema "Teams with members and roles"
26
+ /backend schema "Notification system with preferences"
27
+ /backend schema "Usage tracking and billing"
28
+ ```
29
+
30
+ **What happens:**
31
+
32
+ ```
33
+ 🔧 Backend Agent: Designing "Teams Schema"
34
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
35
+
36
+ 📊 Schema Analysis
37
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
38
+
39
+ Searching existing schemas...
40
+ ✓ Found users table (auth.users reference)
41
+ ✓ Found organizations table (similar pattern)
42
+ ✓ Checked RLS patterns
43
+
44
+ Requirements:
45
+ • Teams table (uuid, name, owner, created_at)
46
+ • Team members (many-to-many via junction)
47
+ • Role-based access (owner, admin, member)
48
+ • Invitation system
49
+ • Soft deletes
50
+
51
+ Security:
52
+ • RLS policies for data isolation
53
+ • Owner/admin permissions
54
+ • Audit logging
55
+
56
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
57
+ 📝 Creating Migration
58
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
59
+
60
+ Creating supabase/migrations/20250107000000_create_teams.sql...
61
+
62
+ ```sql
63
+ -- Create teams table
64
+ CREATE TABLE IF NOT EXISTS public.teams (
65
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
66
+ name TEXT NOT NULL CHECK (char_length(name) >= 3 AND char_length(name) <= 50),
67
+ slug TEXT NOT NULL UNIQUE,
68
+ owner_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
69
+ avatar_url TEXT,
70
+ description TEXT,
71
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
72
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
73
+ deleted_at TIMESTAMPTZ
74
+ );
75
+
76
+ -- Create team_members junction table
77
+ CREATE TABLE IF NOT EXISTS public.team_members (
78
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
79
+ team_id UUID NOT NULL REFERENCES public.teams(id) ON DELETE CASCADE,
80
+ user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
81
+ role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
82
+ joined_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
83
+ invited_by UUID REFERENCES auth.users(id),
84
+
85
+ -- Ensure one membership per user per team
86
+ UNIQUE(team_id, user_id)
87
+ );
88
+
89
+ -- Create team_invitations table
90
+ CREATE TABLE IF NOT EXISTS public.team_invitations (
91
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
92
+ team_id UUID NOT NULL REFERENCES public.teams(id) ON DELETE CASCADE,
93
+ email TEXT NOT NULL,
94
+ role TEXT NOT NULL CHECK (role IN ('admin', 'member')),
95
+ invited_by UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
96
+ token TEXT NOT NULL UNIQUE DEFAULT encode(gen_random_bytes(32), 'hex'),
97
+ expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '7 days'),
98
+ accepted_at TIMESTAMPTZ,
99
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
100
+
101
+ -- Prevent duplicate pending invitations
102
+ UNIQUE(team_id, email) WHERE accepted_at IS NULL
103
+ );
104
+
105
+ -- Indexes for performance
106
+ CREATE INDEX idx_teams_owner_id ON public.teams(owner_id) WHERE deleted_at IS NULL;
107
+ CREATE INDEX idx_teams_slug ON public.teams(slug) WHERE deleted_at IS NULL;
108
+ CREATE INDEX idx_teams_created_at ON public.teams(created_at DESC) WHERE deleted_at IS NULL;
109
+
110
+ CREATE INDEX idx_team_members_team_id ON public.team_members(team_id);
111
+ CREATE INDEX idx_team_members_user_id ON public.team_members(user_id);
112
+
113
+ CREATE INDEX idx_team_invitations_team_id ON public.team_invitations(team_id);
114
+ CREATE INDEX idx_team_invitations_email ON public.team_invitations(email) WHERE accepted_at IS NULL;
115
+ CREATE INDEX idx_team_invitations_token ON public.team_invitations(token) WHERE accepted_at IS NULL;
116
+ CREATE INDEX idx_team_invitations_expires_at ON public.team_invitations(expires_at) WHERE accepted_at IS NULL;
117
+
118
+ -- Trigger for updated_at
119
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
120
+ RETURNS TRIGGER AS $$
121
+ BEGIN
122
+ NEW.updated_at = NOW();
123
+ RETURN NEW;
124
+ END;
125
+ $$ LANGUAGE plpgsql;
126
+
127
+ CREATE TRIGGER update_teams_updated_at
128
+ BEFORE UPDATE ON public.teams
129
+ FOR EACH ROW
130
+ EXECUTE FUNCTION update_updated_at_column();
131
+
132
+ -- Row Level Security Policies
133
+ ALTER TABLE public.teams ENABLE ROW LEVEL SECURITY;
134
+ ALTER TABLE public.team_members ENABLE ROW LEVEL SECURITY;
135
+ ALTER TABLE public.team_invitations ENABLE ROW LEVEL SECURITY;
136
+
137
+ -- Teams policies
138
+ CREATE POLICY "Users can view teams they belong to"
139
+ ON public.teams
140
+ FOR SELECT
141
+ USING (
142
+ deleted_at IS NULL AND (
143
+ owner_id = auth.uid() OR
144
+ id IN (
145
+ SELECT team_id FROM public.team_members WHERE user_id = auth.uid()
146
+ )
147
+ )
148
+ );
149
+
150
+ CREATE POLICY "Users can create teams"
151
+ ON public.teams
152
+ FOR INSERT
153
+ WITH CHECK (owner_id = auth.uid());
154
+
155
+ CREATE POLICY "Team owners can update their teams"
156
+ ON public.teams
157
+ FOR UPDATE
158
+ USING (owner_id = auth.uid() AND deleted_at IS NULL)
159
+ WITH CHECK (owner_id = auth.uid());
160
+
161
+ CREATE POLICY "Team owners can soft delete their teams"
162
+ ON public.teams
163
+ FOR UPDATE
164
+ USING (owner_id = auth.uid())
165
+ WITH CHECK (deleted_at IS NOT NULL);
166
+
167
+ -- Team members policies
168
+ CREATE POLICY "Users can view team members of teams they belong to"
169
+ ON public.team_members
170
+ FOR SELECT
171
+ USING (
172
+ team_id IN (
173
+ SELECT id FROM public.teams WHERE
174
+ deleted_at IS NULL AND (
175
+ owner_id = auth.uid() OR
176
+ id IN (SELECT team_id FROM public.team_members WHERE user_id = auth.uid())
177
+ )
178
+ )
179
+ );
180
+
181
+ CREATE POLICY "Team admins can add members"
182
+ ON public.team_members
183
+ FOR INSERT
184
+ WITH CHECK (
185
+ team_id IN (
186
+ SELECT teams.id FROM public.teams
187
+ LEFT JOIN public.team_members ON teams.id = team_members.team_id
188
+ WHERE teams.deleted_at IS NULL AND (
189
+ teams.owner_id = auth.uid() OR
190
+ (team_members.user_id = auth.uid() AND team_members.role IN ('owner', 'admin'))
191
+ )
192
+ )
193
+ );
194
+
195
+ CREATE POLICY "Team admins can remove members"
196
+ ON public.team_members
197
+ FOR DELETE
198
+ USING (
199
+ team_id IN (
200
+ SELECT teams.id FROM public.teams
201
+ LEFT JOIN public.team_members ON teams.id = team_members.team_id
202
+ WHERE teams.deleted_at IS NULL AND (
203
+ teams.owner_id = auth.uid() OR
204
+ (team_members.user_id = auth.uid() AND team_members.role IN ('owner', 'admin'))
205
+ )
206
+ )
207
+ );
208
+
209
+ -- Team invitations policies
210
+ CREATE POLICY "Users can view invitations to teams they admin"
211
+ ON public.team_invitations
212
+ FOR SELECT
213
+ USING (
214
+ team_id IN (
215
+ SELECT teams.id FROM public.teams
216
+ LEFT JOIN public.team_members ON teams.id = team_members.team_id
217
+ WHERE teams.deleted_at IS NULL AND (
218
+ teams.owner_id = auth.uid() OR
219
+ (team_members.user_id = auth.uid() AND team_members.role IN ('owner', 'admin'))
220
+ )
221
+ )
222
+ );
223
+
224
+ CREATE POLICY "Team admins can create invitations"
225
+ ON public.team_invitations
226
+ FOR INSERT
227
+ WITH CHECK (
228
+ team_id IN (
229
+ SELECT teams.id FROM public.teams
230
+ LEFT JOIN public.team_members ON teams.id = team_members.team_id
231
+ WHERE teams.deleted_at IS NULL AND (
232
+ teams.owner_id = auth.uid() OR
233
+ (team_members.user_id = auth.uid() AND team_members.role IN ('owner', 'admin'))
234
+ )
235
+ )
236
+ );
237
+
238
+ COMMENT ON TABLE public.teams IS 'Teams for collaboration';
239
+ COMMENT ON TABLE public.team_members IS 'Team membership and roles';
240
+ COMMENT ON TABLE public.team_invitations IS 'Pending team invitations';
241
+ ```
242
+
243
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
244
+ ✅ Schema Complete
245
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
246
+
247
+ Tables Created: 3
248
+ ✓ teams (9 columns, 3 indexes)
249
+ ✓ team_members (7 columns, 2 indexes)
250
+ ✓ team_invitations (10 columns, 4 indexes)
251
+
252
+ Security:
253
+ ✓ RLS enabled on all tables
254
+ ✓ 10 policies for data isolation
255
+ ✓ Owner/admin permission checks
256
+ ✓ Soft delete support
257
+
258
+ Performance:
259
+ ✓ Indexes on foreign keys
260
+ ✓ Indexes on query columns (email, token, expires_at)
261
+ ✓ Partial indexes for soft deletes
262
+ ✓ Composite unique constraints
263
+
264
+ Data Integrity:
265
+ ✓ CHECK constraints on text length
266
+ ✓ UNIQUE constraints prevent duplicates
267
+ ✓ Foreign keys with CASCADE
268
+ ✓ Non-null constraints where needed
269
+
270
+ Next Steps:
271
+ • Test migration: supabase db reset
272
+ • Generate types: supabase gen types typescript
273
+ • Build API: /backend api "Teams CRUD"
274
+ ```
275
+
276
+ ### `/backend api`
277
+
278
+ Build REST API endpoints.
279
+
280
+ ```bash
281
+ /backend api "API description"
282
+
283
+ # Examples
284
+ /backend api "Teams CRUD operations"
285
+ /backend api "Pattern search with filters"
286
+ /backend api "User profile update"
287
+ ```
288
+
289
+ **What happens:**
290
+
291
+ ```
292
+ 🔧 Backend Agent: Building "Teams CRUD API"
293
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
294
+
295
+ 📊 API Planning
296
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
297
+
298
+ Checking database schema...
299
+ ✓ Found teams table schema
300
+ ✓ Found RLS policies
301
+ ✓ Generated TypeScript types
302
+
303
+ Endpoints to create:
304
+ • POST /api/teams - Create team
305
+ • GET /api/teams - List user's teams
306
+ • GET /api/teams/:id - Get team details
307
+ • PATCH /api/teams/:id - Update team
308
+ • DELETE /api/teams/:id - Soft delete team
309
+ • POST /api/teams/:id/invite - Invite member
310
+ • POST /api/teams/:id/accept - Accept invitation
311
+
312
+ Stack:
313
+ • Next.js 14 Route Handlers
314
+ • Supabase Client (server-side)
315
+ • Zod validation
316
+ • TypeScript strict mode
317
+
318
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
319
+ 📝 Implementation
320
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
321
+
322
+ Creating apps/web/app/api/teams/route.ts...
323
+
324
+ ```typescript
325
+ import { NextRequest, NextResponse } from 'next/server';
326
+ import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
327
+ import { cookies } from 'next/headers';
328
+ import { z } from 'zod';
329
+ import { Database } from '@/types/supabase';
330
+
331
+ // Validation schemas
332
+ const createTeamSchema = z.object({
333
+ name: z.string()
334
+ .min(3, 'Team name must be at least 3 characters')
335
+ .max(50, 'Team name must be less than 50 characters')
336
+ .regex(/^[a-zA-Z0-9\s-]+$/, 'Team name can only contain letters, numbers, spaces, and hyphens'),
337
+ description: z.string().max(500).optional(),
338
+ avatar_url: z.string().url().optional(),
339
+ });
340
+
341
+ type CreateTeamInput = z.infer<typeof createTeamSchema>;
342
+
343
+ // POST /api/teams - Create a new team
344
+ export async function POST(request: NextRequest) {
345
+ try {
346
+ const supabase = createRouteHandlerClient<Database>({ cookies });
347
+
348
+ // Verify authentication
349
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
350
+ if (authError || !user) {
351
+ return NextResponse.json(
352
+ { error: 'Unauthorized' },
353
+ { status: 401 }
354
+ );
355
+ }
356
+
357
+ // Parse and validate request body
358
+ const body = await request.json();
359
+ const validationResult = createTeamSchema.safeParse(body);
360
+
361
+ if (!validationResult.success) {
362
+ return NextResponse.json(
363
+ { error: 'Validation failed', details: validationResult.error.errors },
364
+ { status: 400 }
365
+ );
366
+ }
367
+
368
+ const data: CreateTeamInput = validationResult.data;
369
+
370
+ // Generate URL-safe slug from name
371
+ const slug = data.name
372
+ .toLowerCase()
373
+ .replace(/[^a-z0-9]+/g, '-')
374
+ .replace(/^-|-$/g, '');
375
+
376
+ // Create team
377
+ const { data: team, error: createError } = await supabase
378
+ .from('teams')
379
+ .insert({
380
+ name: data.name,
381
+ slug,
382
+ owner_id: user.id,
383
+ description: data.description,
384
+ avatar_url: data.avatar_url,
385
+ })
386
+ .select()
387
+ .single();
388
+
389
+ if (createError) {
390
+ // Handle duplicate slug
391
+ if (createError.code === '23505') {
392
+ return NextResponse.json(
393
+ { error: 'A team with this name already exists' },
394
+ { status: 409 }
395
+ );
396
+ }
397
+
398
+ console.error('Failed to create team:', createError);
399
+ return NextResponse.json(
400
+ { error: 'Failed to create team' },
401
+ { status: 500 }
402
+ );
403
+ }
404
+
405
+ // Add owner as member
406
+ const { error: memberError } = await supabase
407
+ .from('team_members')
408
+ .insert({
409
+ team_id: team.id,
410
+ user_id: user.id,
411
+ role: 'owner',
412
+ });
413
+
414
+ if (memberError) {
415
+ console.error('Failed to add owner as member:', memberError);
416
+ // Team created but owner not added - should rarely happen
417
+ // RLS will still allow owner access via owner_id
418
+ }
419
+
420
+ return NextResponse.json(team, { status: 201 });
421
+ } catch (error) {
422
+ console.error('Unexpected error in POST /api/teams:', error);
423
+ return NextResponse.json(
424
+ { error: 'Internal server error' },
425
+ { status: 500 }
426
+ );
427
+ }
428
+ }
429
+
430
+ // GET /api/teams - List user's teams
431
+ export async function GET(request: NextRequest) {
432
+ try {
433
+ const supabase = createRouteHandlerClient<Database>({ cookies });
434
+
435
+ // Verify authentication
436
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
437
+ if (authError || !user) {
438
+ return NextResponse.json(
439
+ { error: 'Unauthorized' },
440
+ { status: 401 }
441
+ );
442
+ }
443
+
444
+ // Parse query parameters
445
+ const { searchParams } = new URL(request.url);
446
+ const search = searchParams.get('search') || '';
447
+ const offset = parseInt(searchParams.get('offset') || '0', 10);
448
+ const limit = Math.min(parseInt(searchParams.get('limit') || '12', 10), 100);
449
+
450
+ // Build query with RLS automatically filtering by user
451
+ let query = supabase
452
+ .from('teams')
453
+ .select(`
454
+ id,
455
+ name,
456
+ slug,
457
+ avatar_url,
458
+ description,
459
+ created_at,
460
+ owner_id,
461
+ team_members!inner(user_id, role)
462
+ `)
463
+ .order('created_at', { ascending: false })
464
+ .range(offset, offset + limit - 1);
465
+
466
+ // Add search filter if provided
467
+ if (search) {
468
+ query = query.ilike('name', `%${search}%`);
469
+ }
470
+
471
+ const { data: teams, error: queryError } = await query;
472
+
473
+ if (queryError) {
474
+ console.error('Failed to fetch teams:', queryError);
475
+ return NextResponse.json(
476
+ { error: 'Failed to fetch teams' },
477
+ { status: 500 }
478
+ );
479
+ }
480
+
481
+ // Format response with member count and user's role
482
+ const formattedTeams = teams.map(team => ({
483
+ id: team.id,
484
+ name: team.name,
485
+ slug: team.slug,
486
+ avatar_url: team.avatar_url,
487
+ description: team.description,
488
+ created_at: team.created_at,
489
+ member_count: team.team_members.length,
490
+ role: team.team_members.find(m => m.user_id === user.id)?.role || 'member',
491
+ is_owner: team.owner_id === user.id,
492
+ }));
493
+
494
+ return NextResponse.json(formattedTeams);
495
+ } catch (error) {
496
+ console.error('Unexpected error in GET /api/teams:', error);
497
+ return NextResponse.json(
498
+ { error: 'Internal server error' },
499
+ { status: 500 }
500
+ );
501
+ }
502
+ }
503
+ ```
504
+
505
+ Creating apps/web/app/api/teams/[id]/route.ts...
506
+
507
+ ```typescript
508
+ import { NextRequest, NextResponse } from 'next/server';
509
+ import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
510
+ import { cookies } from 'next/headers';
511
+ import { z } from 'zod';
512
+ import { Database } from '@/types/supabase';
513
+
514
+ const updateTeamSchema = z.object({
515
+ name: z.string().min(3).max(50).optional(),
516
+ description: z.string().max(500).optional(),
517
+ avatar_url: z.string().url().optional(),
518
+ });
519
+
520
+ // GET /api/teams/:id - Get team details
521
+ export async function GET(
522
+ request: NextRequest,
523
+ { params }: { params: { id: string } }
524
+ ) {
525
+ try {
526
+ const supabase = createRouteHandlerClient<Database>({ cookies });
527
+
528
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
529
+ if (authError || !user) {
530
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
531
+ }
532
+
533
+ const { data: team, error } = await supabase
534
+ .from('teams')
535
+ .select(`
536
+ *,
537
+ team_members!inner(
538
+ id,
539
+ user_id,
540
+ role,
541
+ joined_at,
542
+ profiles:user_id(username, avatar_url)
543
+ )
544
+ `)
545
+ .eq('id', params.id)
546
+ .single();
547
+
548
+ if (error || !team) {
549
+ return NextResponse.json({ error: 'Team not found' }, { status: 404 });
550
+ }
551
+
552
+ return NextResponse.json(team);
553
+ } catch (error) {
554
+ console.error('Error in GET /api/teams/:id:', error);
555
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
556
+ }
557
+ }
558
+
559
+ // PATCH /api/teams/:id - Update team
560
+ export async function PATCH(
561
+ request: NextRequest,
562
+ { params }: { params: { id: string } }
563
+ ) {
564
+ try {
565
+ const supabase = createRouteHandlerClient<Database>({ cookies });
566
+
567
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
568
+ if (authError || !user) {
569
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
570
+ }
571
+
572
+ // Parse and validate
573
+ const body = await request.json();
574
+ const validationResult = updateTeamSchema.safeParse(body);
575
+
576
+ if (!validationResult.success) {
577
+ return NextResponse.json(
578
+ { error: 'Validation failed', details: validationResult.error.errors },
579
+ { status: 400 }
580
+ );
581
+ }
582
+
583
+ // Update team (RLS ensures only owner can update)
584
+ const { data: team, error } = await supabase
585
+ .from('teams')
586
+ .update(validationResult.data)
587
+ .eq('id', params.id)
588
+ .select()
589
+ .single();
590
+
591
+ if (error) {
592
+ if (error.code === 'PGRST116') {
593
+ return NextResponse.json({ error: 'Team not found or access denied' }, { status: 404 });
594
+ }
595
+ console.error('Failed to update team:', error);
596
+ return NextResponse.json({ error: 'Failed to update team' }, { status: 500 });
597
+ }
598
+
599
+ return NextResponse.json(team);
600
+ } catch (error) {
601
+ console.error('Error in PATCH /api/teams/:id:', error);
602
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
603
+ }
604
+ }
605
+
606
+ // DELETE /api/teams/:id - Soft delete team
607
+ export async function DELETE(
608
+ request: NextRequest,
609
+ { params }: { params: { id: string } }
610
+ ) {
611
+ try {
612
+ const supabase = createRouteHandlerClient<Database>({ cookies });
613
+
614
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
615
+ if (authError || !user) {
616
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
617
+ }
618
+
619
+ // Soft delete (RLS ensures only owner can delete)
620
+ const { error } = await supabase
621
+ .from('teams')
622
+ .update({ deleted_at: new Date().toISOString() })
623
+ .eq('id', params.id)
624
+ .is('deleted_at', null);
625
+
626
+ if (error) {
627
+ console.error('Failed to delete team:', error);
628
+ return NextResponse.json({ error: 'Failed to delete team' }, { status: 500 });
629
+ }
630
+
631
+ return NextResponse.json({ success: true }, { status: 200 });
632
+ } catch (error) {
633
+ console.error('Error in DELETE /api/teams/:id:', error);
634
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
635
+ }
636
+ }
637
+ ```
638
+
639
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
640
+ ✅ API Complete
641
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
642
+
643
+ Endpoints Created: 5
644
+ ✓ POST /api/teams (create team)
645
+ ✓ GET /api/teams (list teams with pagination)
646
+ ✓ GET /api/teams/:id (get team details)
647
+ ✓ PATCH /api/teams/:id (update team)
648
+ ✓ DELETE /api/teams/:id (soft delete)
649
+
650
+ Security:
651
+ ✓ Authentication required on all endpoints
652
+ ✓ RLS policies enforce data isolation
653
+ ✓ Input validation with Zod
654
+ ✓ SQL injection protected (Supabase client)
655
+
656
+ Features:
657
+ ✓ Pagination support (offset/limit)
658
+ ✓ Search functionality
659
+ ✓ Error handling (400, 401, 404, 409, 500)
660
+ ✓ TypeScript types from database
661
+ ✓ Proper status codes
662
+
663
+ Performance:
664
+ ✓ Single database queries (no N+1)
665
+ ✓ Index-optimized queries
666
+ ✓ Efficient joins with select()
667
+
668
+ Next Steps:
669
+ • Add rate limiting: /backend fix "Add rate limiting"
670
+ • Add audit logging: /backend fix "Add audit logs"
671
+ • Write tests: /qa write "Teams API"
672
+ ```
673
+
674
+ ### `/backend fix`
675
+
676
+ Fix backend issues or optimize existing code.
677
+
678
+ ```bash
679
+ /backend fix "Issue description"
680
+
681
+ # Examples
682
+ /backend fix "Add rate limiting to Teams API"
683
+ /backend fix "Optimize pattern search query"
684
+ /backend fix "Fix N+1 query in dashboard"
685
+ ```
686
+
687
+ ### `/backend optimize`
688
+
689
+ Optimize database queries and performance.
690
+
691
+ ```bash
692
+ /backend optimize "Query or endpoint"
693
+
694
+ # Examples
695
+ /backend optimize "Pattern search"
696
+ /backend optimize "/api/dashboard/stats"
697
+ /backend optimize "Slow RLS policies"
698
+ ```
699
+
700
+ ## MCP Tools Used
701
+
702
+ The Backend Agent uses:
703
+
704
+ - `ekkOS_Search` - Find similar API patterns and schemas
705
+ - `ekkOS_IndexSchema` - Index database schemas
706
+ - `ekkOS_GetSchema` - Get table schemas for queries
707
+ - `ekkOS_Codebase` - Search existing API implementations
708
+ - `ekkOS_Forge` - Save new API patterns
709
+ - `supabase_list_tables` - List database tables
710
+ - `supabase_execute_sql` - Execute SQL queries
711
+ - `supabase_apply_migration` - Apply database migrations
712
+ - `supabase_generate_typescript_types` - Generate types
713
+ - `Read` - Read existing code
714
+ - `Edit` - Update code
715
+ - `Write` - Create new files
716
+ - `Bash` - Run Supabase CLI commands
717
+
718
+ ## Best Practices
719
+
720
+ ### Always Use RLS
721
+
722
+ ```sql
723
+ -- ✅ Good: RLS enabled with proper policies
724
+ ALTER TABLE public.teams ENABLE ROW LEVEL SECURITY;
725
+
726
+ CREATE POLICY "Users can view their teams"
727
+ ON public.teams
728
+ FOR SELECT
729
+ USING (owner_id = auth.uid());
730
+
731
+ -- ❌ Bad: No RLS (security risk!)
732
+ CREATE TABLE public.teams (...);
733
+ -- Missing: ALTER TABLE ... ENABLE ROW LEVEL SECURITY
734
+ ```
735
+
736
+ ### Validate All Inputs
737
+
738
+ ```typescript
739
+ // ✅ Good: Zod validation
740
+ const schema = z.object({
741
+ name: z.string().min(3).max(50),
742
+ });
743
+
744
+ const result = schema.safeParse(body);
745
+ if (!result.success) {
746
+ return NextResponse.json({ error: 'Invalid input' }, { status: 400 });
747
+ }
748
+
749
+ // ❌ Bad: No validation
750
+ const { name } = await request.json();
751
+ // Direct use without validation - security risk!
752
+ ```
753
+
754
+ ### Use Proper Indexes
755
+
756
+ ```sql
757
+ -- ✅ Good: Indexes on query columns
758
+ CREATE INDEX idx_teams_owner_id ON teams(owner_id);
759
+ CREATE INDEX idx_teams_created_at ON teams(created_at DESC);
760
+
761
+ -- ❌ Bad: No indexes on queried columns
762
+ -- Will cause slow queries at scale
763
+ ```
764
+
765
+ ## Troubleshooting
766
+
767
+ ### RLS Policy Blocking Queries
768
+
769
+ **Problem:** Query returns empty results unexpectedly
770
+ **Check:** RLS policies with `EXPLAIN` in Supabase
771
+ **Fix:** Agent reviews and adjusts policies
772
+
773
+ ### Slow Queries
774
+
775
+ **Problem:** API endpoint timing out
776
+ **Check:** `EXPLAIN ANALYZE` on query
777
+ **Fix:** Agent adds indexes or optimizes query
778
+
779
+ ---
780
+
781
+ ## Summary
782
+
783
+ The Backend Agent is your API and database expert that:
784
+
785
+ ✅ **Schemas** - Database design with RLS and indexes
786
+ ✅ **APIs** - REST endpoints with validation
787
+ ✅ **Security** - RLS policies and auth checks
788
+ ✅ **Optimization** - Query performance tuning
789
+
790
+ **Ship secure, scalable APIs.**
791
+
792
+ ```bash
793
+ /backend schema "Your feature here"
794
+ ```
795
+
796
+ ---
797
+
798
+ **Build secure. Scale smart. Ship fast.** 🔧