@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,881 @@
1
+ # Frontend Agent
2
+
3
+ **ADMIN ONLY** - AI Frontend Developer specialized in React, Next.js 14+, TypeScript, Tailwind CSS, and modern UI patterns. Builds components, pages, and handles client-side logic.
4
+
5
+ ## Overview
6
+
7
+ The Frontend Agent is your React expert. It:
8
+ - Builds UI components following design system
9
+ - Creates Next.js pages with proper routing
10
+ - Implements state management (React Query, Zustand)
11
+ - Ensures accessibility (WCAG 2.1 AA)
12
+ - Optimizes performance (code splitting, lazy loading)
13
+ - Follows ekkOS component patterns
14
+
15
+ ## Commands
16
+
17
+ ### `/frontend build`
18
+
19
+ Build a new component, page, or feature.
20
+
21
+ ```bash
22
+ /frontend build "Component/page description"
23
+
24
+ # Examples
25
+ /frontend build "Teams dashboard page"
26
+ /frontend build "Pattern card component"
27
+ /frontend build "User settings form with validation"
28
+ ```
29
+
30
+ **What happens:**
31
+
32
+ ```
33
+ 🎨 Frontend Agent: Building "Teams Dashboard Page"
34
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
35
+
36
+ 📊 Planning
37
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
38
+
39
+ Searching existing patterns...
40
+ ✓ Found 3 similar dashboard components
41
+ ✓ Found Teams API schema
42
+ ✓ Checked design system (components/ui/*)
43
+
44
+ Component Breakdown:
45
+ • Layout wrapper (app layout)
46
+ • Teams list with search/filter
47
+ • Team card component
48
+ • Create team modal
49
+ • Empty state
50
+
51
+ Stack:
52
+ • Next.js 14 App Router
53
+ • TypeScript (strict mode)
54
+ • Tailwind CSS + Shadcn/ui
55
+ • React Query for data fetching
56
+ • Zod for validation
57
+
58
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
59
+ 📝 Implementation
60
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
+
62
+ Creating app/teams/page.tsx...
63
+
64
+ ```typescript
65
+ import { Suspense } from 'react';
66
+ import { Metadata } from 'next';
67
+ import { TeamsHeader } from '@/components/teams/TeamsHeader';
68
+ import { TeamsList } from '@/components/teams/TeamsList';
69
+ import { CreateTeamButton } from '@/components/teams/CreateTeamButton';
70
+ import { PageContainer } from '@/components/layout/PageContainer';
71
+
72
+ export const metadata: Metadata = {
73
+ title: 'Teams | ekkOS',
74
+ description: 'Manage your teams and collaborate with others',
75
+ };
76
+
77
+ export default async function TeamsPage() {
78
+ return (
79
+ <PageContainer>
80
+ <TeamsHeader>
81
+ <CreateTeamButton />
82
+ </TeamsHeader>
83
+
84
+ <Suspense fallback={<TeamsListSkeleton />}>
85
+ <TeamsList />
86
+ </Suspense>
87
+ </PageContainer>
88
+ );
89
+ }
90
+ ```
91
+
92
+ Creating components/teams/TeamsList.tsx...
93
+
94
+ ```typescript
95
+ 'use client';
96
+
97
+ import { useQuery } from '@tanstack/react-query';
98
+ import { useState } from 'react';
99
+ import { TeamCard } from './TeamCard';
100
+ import { TeamsEmptyState } from './TeamsEmptyState';
101
+ import { SearchInput } from '@/components/ui/SearchInput';
102
+ import { Spinner } from '@/components/ui/Spinner';
103
+
104
+ interface Team {
105
+ id: string;
106
+ name: string;
107
+ memberCount: number;
108
+ createdAt: string;
109
+ role: 'owner' | 'admin' | 'member';
110
+ }
111
+
112
+ export function TeamsList() {
113
+ const [search, setSearch] = useState('');
114
+
115
+ const { data: teams, isLoading, error } = useQuery({
116
+ queryKey: ['teams', search],
117
+ queryFn: async () => {
118
+ const response = await fetch(
119
+ `/api/teams?search=${encodeURIComponent(search)}`
120
+ );
121
+ if (!response.ok) throw new Error('Failed to fetch teams');
122
+ return response.json() as Promise<Team[]>;
123
+ },
124
+ });
125
+
126
+ if (isLoading) {
127
+ return (
128
+ <div className="flex items-center justify-center py-12">
129
+ <Spinner size="lg" />
130
+ <span className="ml-3 text-sm text-gray-500">Loading teams...</span>
131
+ </div>
132
+ );
133
+ }
134
+
135
+ if (error) {
136
+ return (
137
+ <div className="rounded-lg border border-red-200 bg-red-50 p-6">
138
+ <h3 className="text-sm font-medium text-red-800">
139
+ Failed to load teams
140
+ </h3>
141
+ <p className="mt-1 text-sm text-red-700">
142
+ {error instanceof Error ? error.message : 'Unknown error'}
143
+ </p>
144
+ </div>
145
+ );
146
+ }
147
+
148
+ if (!teams || teams.length === 0) {
149
+ return <TeamsEmptyState onCreateTeam={() => {/* handled by modal */}} />;
150
+ }
151
+
152
+ return (
153
+ <div className="space-y-6">
154
+ <SearchInput
155
+ value={search}
156
+ onChange={setSearch}
157
+ placeholder="Search teams..."
158
+ className="max-w-md"
159
+ />
160
+
161
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
162
+ {teams.map((team) => (
163
+ <TeamCard key={team.id} team={team} />
164
+ ))}
165
+ </div>
166
+ </div>
167
+ );
168
+ }
169
+ ```
170
+
171
+ Creating components/teams/TeamCard.tsx...
172
+
173
+ ```typescript
174
+ 'use client';
175
+
176
+ import Link from 'next/link';
177
+ import { Users, Crown, Shield } from 'lucide-react';
178
+ import { Card, CardHeader, CardTitle, CardDescription } from '@/components/ui/Card';
179
+ import { Badge } from '@/components/ui/Badge';
180
+ import { formatDistanceToNow } from 'date-fns';
181
+
182
+ interface TeamCardProps {
183
+ team: {
184
+ id: string;
185
+ name: string;
186
+ memberCount: number;
187
+ createdAt: string;
188
+ role: 'owner' | 'admin' | 'member';
189
+ };
190
+ }
191
+
192
+ const roleIcons = {
193
+ owner: Crown,
194
+ admin: Shield,
195
+ member: Users,
196
+ };
197
+
198
+ const roleLabels = {
199
+ owner: 'Owner',
200
+ admin: 'Admin',
201
+ member: 'Member',
202
+ };
203
+
204
+ export function TeamCard({ team }: TeamCardProps) {
205
+ const RoleIcon = roleIcons[team.role];
206
+
207
+ return (
208
+ <Link href={`/teams/${team.id}`}>
209
+ <Card className="group hover:border-blue-300 hover:shadow-md transition-all">
210
+ <CardHeader>
211
+ <div className="flex items-start justify-between">
212
+ <CardTitle className="group-hover:text-blue-600 transition-colors">
213
+ {team.name}
214
+ </CardTitle>
215
+ <Badge variant={team.role === 'owner' ? 'default' : 'secondary'}>
216
+ <RoleIcon className="mr-1 h-3 w-3" />
217
+ {roleLabels[team.role]}
218
+ </Badge>
219
+ </div>
220
+
221
+ <CardDescription>
222
+ <div className="flex items-center gap-4 text-sm">
223
+ <span className="flex items-center gap-1">
224
+ <Users className="h-4 w-4" />
225
+ {team.memberCount} {team.memberCount === 1 ? 'member' : 'members'}
226
+ </span>
227
+ <span>
228
+ Created {formatDistanceToNow(new Date(team.createdAt), { addSuffix: true })}
229
+ </span>
230
+ </div>
231
+ </CardDescription>
232
+ </CardHeader>
233
+ </Card>
234
+ </Link>
235
+ );
236
+ }
237
+ ```
238
+
239
+ Creating components/teams/CreateTeamButton.tsx...
240
+
241
+ ```typescript
242
+ 'use client';
243
+
244
+ import { useState } from 'react';
245
+ import { Plus } from 'lucide-react';
246
+ import { Button } from '@/components/ui/Button';
247
+ import { CreateTeamModal } from './CreateTeamModal';
248
+
249
+ export function CreateTeamButton() {
250
+ const [isOpen, setIsOpen] = useState(false);
251
+
252
+ return (
253
+ <>
254
+ <Button onClick={() => setIsOpen(true)}>
255
+ <Plus className="mr-2 h-4 w-4" />
256
+ Create Team
257
+ </Button>
258
+
259
+ <CreateTeamModal
260
+ isOpen={isOpen}
261
+ onClose={() => setIsOpen(false)}
262
+ />
263
+ </>
264
+ );
265
+ }
266
+ ```
267
+
268
+ Creating components/teams/CreateTeamModal.tsx...
269
+
270
+ ```typescript
271
+ 'use client';
272
+
273
+ import { useState } from 'react';
274
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
275
+ import { useForm } from 'react-hook-form';
276
+ import { zodResolver } from '@hookform/resolvers/zod';
277
+ import { z } from 'zod';
278
+ import {
279
+ Dialog,
280
+ DialogContent,
281
+ DialogHeader,
282
+ DialogTitle,
283
+ DialogDescription,
284
+ DialogFooter,
285
+ } from '@/components/ui/Dialog';
286
+ import { Button } from '@/components/ui/Button';
287
+ import { Input } from '@/components/ui/Input';
288
+ import { Label } from '@/components/ui/Label';
289
+ import { toast } from 'sonner';
290
+
291
+ const createTeamSchema = z.object({
292
+ name: z.string()
293
+ .min(3, 'Team name must be at least 3 characters')
294
+ .max(50, 'Team name must be less than 50 characters')
295
+ .regex(/^[a-zA-Z0-9\s-]+$/, 'Team name can only contain letters, numbers, spaces, and hyphens'),
296
+ });
297
+
298
+ type CreateTeamForm = z.infer<typeof createTeamSchema>;
299
+
300
+ interface CreateTeamModalProps {
301
+ isOpen: boolean;
302
+ onClose: () => void;
303
+ }
304
+
305
+ export function CreateTeamModal({ isOpen, onClose }: CreateTeamModalProps) {
306
+ const queryClient = useQueryClient();
307
+
308
+ const {
309
+ register,
310
+ handleSubmit,
311
+ formState: { errors },
312
+ reset,
313
+ } = useForm<CreateTeamForm>({
314
+ resolver: zodResolver(createTeamSchema),
315
+ });
316
+
317
+ const createTeamMutation = useMutation({
318
+ mutationFn: async (data: CreateTeamForm) => {
319
+ const response = await fetch('/api/teams', {
320
+ method: 'POST',
321
+ headers: { 'Content-Type': 'application/json' },
322
+ body: JSON.stringify(data),
323
+ });
324
+
325
+ if (!response.ok) {
326
+ const error = await response.json();
327
+ throw new Error(error.message || 'Failed to create team');
328
+ }
329
+
330
+ return response.json();
331
+ },
332
+ onSuccess: () => {
333
+ queryClient.invalidateQueries({ queryKey: ['teams'] });
334
+ toast.success('Team created successfully');
335
+ reset();
336
+ onClose();
337
+ },
338
+ onError: (error: Error) => {
339
+ toast.error(error.message || 'Failed to create team');
340
+ },
341
+ });
342
+
343
+ const onSubmit = (data: CreateTeamForm) => {
344
+ createTeamMutation.mutate(data);
345
+ };
346
+
347
+ return (
348
+ <Dialog open={isOpen} onOpenChange={onClose}>
349
+ <DialogContent>
350
+ <DialogHeader>
351
+ <DialogTitle>Create New Team</DialogTitle>
352
+ <DialogDescription>
353
+ Create a team to collaborate with others on patterns and projects.
354
+ </DialogDescription>
355
+ </DialogHeader>
356
+
357
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
358
+ <div className="space-y-2">
359
+ <Label htmlFor="name">Team Name</Label>
360
+ <Input
361
+ id="name"
362
+ {...register('name')}
363
+ placeholder="Engineering Team"
364
+ autoFocus
365
+ />
366
+ {errors.name && (
367
+ <p className="text-sm text-red-600">{errors.name.message}</p>
368
+ )}
369
+ </div>
370
+
371
+ <DialogFooter>
372
+ <Button type="button" variant="outline" onClick={onClose}>
373
+ Cancel
374
+ </Button>
375
+ <Button type="submit" disabled={createTeamMutation.isPending}>
376
+ {createTeamMutation.isPending ? 'Creating...' : 'Create Team'}
377
+ </Button>
378
+ </DialogFooter>
379
+ </form>
380
+ </DialogContent>
381
+ </Dialog>
382
+ );
383
+ }
384
+ ```
385
+
386
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
387
+ ✅ Build Complete
388
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
389
+
390
+ Files Created: 6
391
+ ✓ app/teams/page.tsx (main page)
392
+ ✓ components/teams/TeamsList.tsx (list component)
393
+ ✓ components/teams/TeamCard.tsx (card component)
394
+ ✓ components/teams/CreateTeamButton.tsx (button)
395
+ ✓ components/teams/CreateTeamModal.tsx (modal)
396
+ ✓ components/teams/TeamsEmptyState.tsx (empty state)
397
+
398
+ Features Implemented:
399
+ ✅ Server-side rendering (Next.js 14)
400
+ ✅ Client-side data fetching (React Query)
401
+ ✅ Search functionality
402
+ ✅ Form validation (Zod)
403
+ ✅ Error handling
404
+ ✅ Loading states
405
+ ✅ Empty states
406
+ ✅ Responsive design (mobile-first)
407
+ ✅ Accessibility (ARIA labels, keyboard nav)
408
+ ✅ TypeScript strict mode
409
+
410
+ Performance:
411
+ ✅ Code splitting (client components)
412
+ ✅ Optimistic updates on mutations
413
+ ✅ Automatic query invalidation
414
+ ✅ Suspense boundaries
415
+
416
+ Next Steps:
417
+ • Add tests: /qa write "Teams UI"
418
+ • Review code: /lead review frontend
419
+ • Deploy to staging: /deploy execute apps/web --env staging
420
+ ```
421
+
422
+ ### `/frontend fix`
423
+
424
+ Fix bugs or issues in existing components.
425
+
426
+ ```bash
427
+ /frontend fix "Issue description"
428
+
429
+ # Examples
430
+ /frontend fix "Teams list not paginating properly"
431
+ /frontend fix "Modal not closing on mobile"
432
+ /frontend fix "Add optimistic updates to team creation"
433
+ ```
434
+
435
+ **What happens:**
436
+
437
+ ```
438
+ 🎨 Frontend Agent: Fixing "Teams list not paginating"
439
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
440
+
441
+ 📊 Analyzing Issue
442
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
443
+
444
+ Reading components/teams/TeamsList.tsx...
445
+
446
+ Issue Found:
447
+ • Using client-side filtering only
448
+ • No pagination implementation
449
+ • Will break at scale (100+ teams)
450
+
451
+ Root Cause:
452
+ • API returns all teams at once
453
+ • No cursor/offset pagination
454
+ • Frontend doesn't request pages
455
+
456
+ Fix Strategy:
457
+ 1. Update API to support pagination
458
+ 2. Add infinite scroll to UI
459
+ 3. Use React Query's useInfiniteQuery
460
+
461
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
462
+ 📝 Implementing Fix
463
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
464
+
465
+ Updating components/teams/TeamsList.tsx...
466
+
467
+ ```typescript
468
+ 'use client';
469
+
470
+ import { useInfiniteQuery } from '@tanstack/react-query';
471
+ import { useEffect, useRef } from 'react';
472
+ import { TeamCard } from './TeamCard';
473
+ import { Spinner } from '@/components/ui/Spinner';
474
+
475
+ const PAGE_SIZE = 12;
476
+
477
+ export function TeamsList() {
478
+ const observerTarget = useRef<HTMLDivElement>(null);
479
+
480
+ const {
481
+ data,
482
+ fetchNextPage,
483
+ hasNextPage,
484
+ isFetchingNextPage,
485
+ isLoading,
486
+ } = useInfiniteQuery({
487
+ queryKey: ['teams'],
488
+ queryFn: async ({ pageParam = 0 }) => {
489
+ const response = await fetch(
490
+ `/api/teams?offset=${pageParam}&limit=${PAGE_SIZE}`
491
+ );
492
+ if (!response.ok) throw new Error('Failed to fetch teams');
493
+ return response.json();
494
+ },
495
+ getNextPageParam: (lastPage, pages) => {
496
+ if (lastPage.length < PAGE_SIZE) return undefined;
497
+ return pages.length * PAGE_SIZE;
498
+ },
499
+ });
500
+
501
+ // Infinite scroll observer
502
+ useEffect(() => {
503
+ const observer = new IntersectionObserver(
504
+ (entries) => {
505
+ if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
506
+ fetchNextPage();
507
+ }
508
+ },
509
+ { threshold: 0.1 }
510
+ );
511
+
512
+ const target = observerTarget.current;
513
+ if (target) observer.observe(target);
514
+
515
+ return () => {
516
+ if (target) observer.unobserve(target);
517
+ };
518
+ }, [hasNextPage, isFetchingNextPage, fetchNextPage]);
519
+
520
+ const teams = data?.pages.flatMap((page) => page) ?? [];
521
+
522
+ if (isLoading) {
523
+ return <div>Loading...</div>;
524
+ }
525
+
526
+ return (
527
+ <div className="space-y-6">
528
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
529
+ {teams.map((team) => (
530
+ <TeamCard key={team.id} team={team} />
531
+ ))}
532
+ </div>
533
+
534
+ {/* Intersection observer target */}
535
+ <div ref={observerTarget} className="flex justify-center py-4">
536
+ {isFetchingNextPage && <Spinner />}
537
+ </div>
538
+
539
+ {!hasNextPage && teams.length > 0 && (
540
+ <p className="text-center text-sm text-gray-500">
541
+ All teams loaded
542
+ </p>
543
+ )}
544
+ </div>
545
+ );
546
+ }
547
+ ```
548
+
549
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
550
+ ✅ Fix Complete
551
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
552
+
553
+ Changes:
554
+ ✓ Replaced useQuery with useInfiniteQuery
555
+ ✓ Added Intersection Observer for infinite scroll
556
+ ✓ Proper cleanup in useEffect
557
+ ✓ Loading states for next pages
558
+ ✓ "All loaded" message
559
+
560
+ Performance:
561
+ ✓ Only loads 12 teams initially
562
+ ✓ Loads next page when scrolling near bottom
563
+ ✓ No unnecessary re-fetches
564
+
565
+ Note: Requires backend API update:
566
+ Backend Agent should add pagination support to /api/teams
567
+ Command: /backend fix "Add pagination to Teams API"
568
+
569
+ Testing:
570
+ ✓ Manual test: Works with 100+ teams
571
+ ✓ Network throttling: Smooth loading
572
+ ✓ Mobile: Smooth scroll on touch devices
573
+ ```
574
+
575
+ ### `/frontend optimize`
576
+
577
+ Optimize performance of existing components.
578
+
579
+ ```bash
580
+ /frontend optimize "Component name or path"
581
+
582
+ # Examples
583
+ /frontend optimize "Dashboard page"
584
+ /frontend optimize "Pattern search component"
585
+ /frontend optimize "app/teams/page.tsx"
586
+ ```
587
+
588
+ **What happens:**
589
+
590
+ ```
591
+ 🎨 Frontend Agent: Optimizing "Dashboard Page"
592
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
593
+
594
+ 📊 Performance Analysis
595
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
596
+
597
+ Analyzing app/dashboard/page.tsx...
598
+
599
+ Current Metrics:
600
+ • Initial Load: 2.1s (🔴 Slow)
601
+ • First Contentful Paint: 1.8s
602
+ • Time to Interactive: 2.3s
603
+ • Bundle Size: 245 KB (🟡 Large)
604
+
605
+ Issues Found:
606
+ 1. 🔴 All components loaded eagerly (no code splitting)
607
+ 2. 🟡 Large chart library (recharts) in main bundle
608
+ 3. 🟡 No memoization on expensive calculations
609
+ 4. 🟢 Images already optimized (next/image)
610
+
611
+ Optimization Plan:
612
+ 1. Lazy load chart components
613
+ 2. Memoize pattern stats calculation
614
+ 3. Defer non-critical data fetching
615
+ 4. Add loading skeletons
616
+
617
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
618
+ 📝 Implementing Optimizations
619
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
620
+
621
+ Updating app/dashboard/page.tsx...
622
+
623
+ ```typescript
624
+ import { Suspense, lazy } from 'react';
625
+ import { DashboardHeader } from '@/components/dashboard/DashboardHeader';
626
+ import { QuickStats } from '@/components/dashboard/QuickStats';
627
+ import { DashboardSkeleton } from '@/components/dashboard/DashboardSkeleton';
628
+
629
+ // Lazy load heavy components
630
+ const PatternChart = lazy(() => import('@/components/dashboard/PatternChart'));
631
+ const RecentActivity = lazy(() => import('@/components/dashboard/RecentActivity'));
632
+
633
+ export default function DashboardPage() {
634
+ return (
635
+ <div className="space-y-6">
636
+ <DashboardHeader />
637
+
638
+ {/* Load immediately (small, critical) */}
639
+ <QuickStats />
640
+
641
+ {/* Lazy load with suspense (large, non-critical) */}
642
+ <Suspense fallback={<DashboardSkeleton variant="chart" />}>
643
+ <PatternChart />
644
+ </Suspense>
645
+
646
+ <Suspense fallback={<DashboardSkeleton variant="list" />}>
647
+ <RecentActivity />
648
+ </Suspense>
649
+ </div>
650
+ );
651
+ }
652
+ ```
653
+
654
+ Updating components/dashboard/QuickStats.tsx...
655
+
656
+ ```typescript
657
+ 'use client';
658
+
659
+ import { useMemo } from 'react';
660
+ import { useQuery } from '@tanstack/react-query';
661
+ import { StatCard } from '@/components/ui/StatCard';
662
+
663
+ export function QuickStats() {
664
+ const { data: stats } = useQuery({
665
+ queryKey: ['dashboard-stats'],
666
+ queryFn: fetchDashboardStats,
667
+ staleTime: 5 * 60 * 1000, // Cache for 5 minutes
668
+ });
669
+
670
+ // Memoize expensive calculations
671
+ const metrics = useMemo(() => {
672
+ if (!stats) return null;
673
+
674
+ return {
675
+ totalPatterns: stats.patterns.length,
676
+ successRate: calculateSuccessRate(stats.patterns),
677
+ avgResponseTime: calculateAvgResponseTime(stats.patterns),
678
+ topTags: findTopTags(stats.patterns, 5),
679
+ };
680
+ }, [stats]);
681
+
682
+ if (!metrics) return <QuickStatsSkeleton />;
683
+
684
+ return (
685
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
686
+ <StatCard
687
+ title="Total Patterns"
688
+ value={metrics.totalPatterns}
689
+ trend="+12%"
690
+ />
691
+ <StatCard
692
+ title="Success Rate"
693
+ value={`${metrics.successRate}%`}
694
+ trend="+5%"
695
+ />
696
+ {/* ... more stats */}
697
+ </div>
698
+ );
699
+ }
700
+
701
+ // Memoize calculation functions
702
+ const calculateSuccessRate = (patterns: Pattern[]) => {
703
+ const successful = patterns.filter(p => p.outcome === 'success').length;
704
+ return Math.round((successful / patterns.length) * 100);
705
+ };
706
+ ```
707
+
708
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
709
+ ✅ Optimization Complete
710
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
711
+
712
+ Changes:
713
+ ✓ Lazy loaded PatternChart (-180 KB from initial bundle)
714
+ ✓ Lazy loaded RecentActivity (-45 KB from initial bundle)
715
+ ✓ Memoized expensive calculations
716
+ ✓ Added query staleTime caching
717
+ ✓ Added loading skeletons
718
+
719
+ Performance Improvements:
720
+ Initial Load: 2.1s → 0.9s (57% faster) ✅
721
+ FCP: 1.8s → 0.7s ✅
722
+ TTI: 2.3s → 1.1s ✅
723
+ Bundle Size: 245 KB → 20 KB initial (225 KB lazy loaded) ✅
724
+
725
+ Lighthouse Score:
726
+ Performance: 72 → 96 (+24 points) 🎉
727
+ Best Practices: 100 → 100
728
+ Accessibility: 95 → 95
729
+ SEO: 100 → 100
730
+
731
+ Next Steps:
732
+ • Test on slow 3G: Verify loading experience
733
+ • Monitor Core Web Vitals in production
734
+ • Consider adding service worker for offline support
735
+ ```
736
+
737
+ ### `/frontend review`
738
+
739
+ Review component code quality and suggest improvements.
740
+
741
+ ```bash
742
+ /frontend review "Component path"
743
+
744
+ # Examples
745
+ /frontend review "components/teams/TeamsList.tsx"
746
+ /frontend review "app/teams/**"
747
+ ```
748
+
749
+ ## MCP Tools Used
750
+
751
+ The Frontend Agent uses:
752
+
753
+ - `ekkOS_Search` - Find similar UI patterns and components
754
+ - `ekkOS_Codebase` - Search existing component implementations
755
+ - `ekkOS_IndexSchema` - Get TypeScript types for props
756
+ - `ekkOS_GetSchema` - Verify API response shapes
757
+ - `ekkOS_Forge` - Save new component patterns
758
+ - `Read` - Read existing component files
759
+ - `Edit` - Update components
760
+ - `Write` - Create new components
761
+ - `Glob` - Find related component files
762
+ - `Grep` - Search for component usage
763
+
764
+ ## Best Practices
765
+
766
+ ### Follow ekkOS Component Patterns
767
+
768
+ The agent follows established patterns:
769
+
770
+ ```typescript
771
+ // ✅ Good: Follows ekkOS patterns
772
+ 'use client';
773
+
774
+ import { useQuery } from '@tanstack/react-query';
775
+ import { Card } from '@/components/ui/Card';
776
+
777
+ export function TeamsList() {
778
+ const { data, isLoading, error } = useQuery({
779
+ queryKey: ['teams'],
780
+ queryFn: fetchTeams,
781
+ });
782
+
783
+ // Proper error handling
784
+ if (error) return <ErrorState error={error} />;
785
+
786
+ // Proper loading state
787
+ if (isLoading) return <Skeleton />;
788
+
789
+ return <div>{/* ... */}</div>;
790
+ }
791
+ ```
792
+
793
+ ```typescript
794
+ // ❌ Bad: Missing error handling
795
+ export function TeamsList() {
796
+ const { data } = useQuery({ queryKey: ['teams'], queryFn: fetchTeams });
797
+
798
+ return <div>{data.map(/* ... */)}</div>; // Will crash if data is undefined
799
+ }
800
+ ```
801
+
802
+ ### Use Design System Components
803
+
804
+ ```typescript
805
+ // ✅ Good: Uses Shadcn/ui components
806
+ import { Button } from '@/components/ui/Button';
807
+ import { Card } from '@/components/ui/Card';
808
+
809
+ // ❌ Bad: Custom styling inline
810
+ <button className="bg-blue-500 hover:bg-blue-600 px-4 py-2 rounded">
811
+ Click me
812
+ </button>
813
+ ```
814
+
815
+ ### Ensure Accessibility
816
+
817
+ ```typescript
818
+ // ✅ Good: Accessible
819
+ <button
820
+ onClick={handleClick}
821
+ aria-label="Close modal"
822
+ aria-pressed={isPressed}
823
+ >
824
+ <X className="h-4 w-4" />
825
+ <span className="sr-only">Close</span>
826
+ </button>
827
+
828
+ // ❌ Bad: Not accessible
829
+ <div onClick={handleClick}>
830
+ <X />
831
+ </div>
832
+ ```
833
+
834
+ ## Integration with Other Agents
835
+
836
+ Frontend Agent works closely with:
837
+
838
+ - **Backend Agent** - Consumes APIs, validates response shapes
839
+ - **QA Agent** - Receives component tests
840
+ - **Tech Lead** - Gets assignments and reviews
841
+
842
+ ## Troubleshooting
843
+
844
+ ### Component Not Rendering
845
+
846
+ **Problem:** Component shows blank or errors
847
+ **Check:** Browser console for React errors
848
+ **Fix:** Agent reviews component and fixes issues
849
+
850
+ ### Styling Not Applied
851
+
852
+ **Problem:** Tailwind classes not working
853
+ **Check:** Verify classes in tailwind.config.ts
854
+ **Fix:** Agent updates config or uses correct classes
855
+
856
+ ### State Not Updating
857
+
858
+ **Problem:** React Query data stale
859
+ **Check:** Query invalidation logic
860
+ **Fix:** Agent adds proper invalidation
861
+
862
+ ---
863
+
864
+ ## Summary
865
+
866
+ The Frontend Agent is your React expert that:
867
+
868
+ ✅ **Builds** - Components, pages, and features
869
+ ✅ **Fixes** - Bugs and UI issues
870
+ ✅ **Optimizes** - Performance and bundle size
871
+ ✅ **Reviews** - Code quality and patterns
872
+
873
+ **Ship beautiful UIs faster.**
874
+
875
+ ```bash
876
+ /frontend build "Your component here"
877
+ ```
878
+
879
+ ---
880
+
881
+ **Build fast. Look good. Work everywhere.** 🎨