@fluencypassdevs/cycle 0.5.1 → 0.6.1

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.
package/README.md CHANGED
@@ -26,7 +26,24 @@ import { Button, Badge, CycleIcon } from "@fluencypassdevs/cycle"
26
26
  npx cycle init
27
27
  ```
28
28
 
29
- Adiciona as regras do Cycle Design System ao seu CLAUDE.md, .cursorrules e outros spec-kits automaticamente. Preserva regras existentes.
29
+ Esse comando:
30
+ - Adiciona regras do Cycle ao seu `CLAUDE.md`, `.cursorrules` e outros spec-kits (preserva regras existentes)
31
+ - Cria `.mcp.json` para Claude Code — habilita 6 tools do Cycle (buscar componentes, tokens, regras)
32
+
33
+ ### MCP Server (Claude Code)
34
+
35
+ O `init` configura automaticamente o MCP server. O Claude Code passa a ter acesso a:
36
+
37
+ | Tool | Funcao |
38
+ |------|--------|
39
+ | `cycle_search_component` | Busca componentes por nome/keyword |
40
+ | `cycle_get_component` | Detalhes completos (props, exemplos) |
41
+ | `cycle_get_icon_usage` | Regras de uso de icones |
42
+ | `cycle_get_setup` | Instrucoes de setup |
43
+ | `cycle_get_tokens` | Tokens semanticos disponiveis |
44
+ | `cycle_get_rules` | Regras obrigatorias para IA |
45
+
46
+ Isso garante que a IA **consulte** os componentes existentes antes de gerar codigo, em vez de depender apenas de instrucoes estaticas.
30
47
 
31
48
  ## Documentacao
32
49
 
package/bin/cli.mjs ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cycle Design System — CLI Router
5
+ *
6
+ * Routes subcommands:
7
+ * npx cycle init → configure AI rules + MCP
8
+ * npx cycle mcp → start MCP server (stdio)
9
+ */
10
+
11
+ const command = process.argv[2]
12
+
13
+ if (command === "mcp") {
14
+ await import("./mcp.mjs")
15
+ } else if (command === "init" || !command) {
16
+ await import("./init.mjs")
17
+ } else {
18
+ console.log("")
19
+ console.log(" Cycle Design System CLI")
20
+ console.log(" ───────────────────────")
21
+ console.log("")
22
+ console.log(" Commands:")
23
+ console.log(" npx cycle init Configure AI rules + MCP server")
24
+ console.log(" npx cycle mcp Start MCP server (stdio)")
25
+ console.log("")
26
+ process.exit(1)
27
+ }
package/bin/init.mjs CHANGED
@@ -88,8 +88,43 @@ if (fs.existsSync(agentsMd)) {
88
88
  mergeIntoFile(agentsMd, "AGENTS.md")
89
89
  }
90
90
 
91
+ // 6. .mcp.json (Claude Code MCP Server)
92
+ const mcpConfigPath = path.join(cwd, ".mcp.json")
93
+ const mcpConfig = {
94
+ mcpServers: {
95
+ "cycle-design-system": {
96
+ type: "stdio",
97
+ command: "npx",
98
+ args: ["cycle", "mcp"],
99
+ },
100
+ },
101
+ }
102
+
103
+ if (!fs.existsSync(mcpConfigPath)) {
104
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n")
105
+ console.log(" ✓ .mcp.json criado (MCP server do Cycle para Claude Code)")
106
+ } else {
107
+ try {
108
+ const existing = JSON.parse(fs.readFileSync(mcpConfigPath, "utf-8"))
109
+ if (!existing.mcpServers?.["cycle-design-system"]) {
110
+ existing.mcpServers = existing.mcpServers || {}
111
+ existing.mcpServers["cycle-design-system"] = mcpConfig.mcpServers["cycle-design-system"]
112
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(existing, null, 2) + "\n")
113
+ console.log(" ✓ .mcp.json atualizado (MCP server do Cycle adicionado)")
114
+ } else {
115
+ existing.mcpServers["cycle-design-system"] = mcpConfig.mcpServers["cycle-design-system"]
116
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(existing, null, 2) + "\n")
117
+ console.log(" ✓ .mcp.json atualizado (MCP server do Cycle atualizado)")
118
+ }
119
+ } catch {
120
+ console.log(" ⚠ .mcp.json existe mas nao e JSON valido — MCP nao configurado")
121
+ }
122
+ }
123
+
91
124
  console.log("")
92
125
  console.log(" Pronto! As regras do Cycle Design System foram configuradas.")
126
+ console.log(" MCP server configurado — Claude Code vai consultar os componentes automaticamente.")
127
+ console.log("")
93
128
  console.log(" Para atualizar apos upgrade do pacote, rode novamente:")
94
- console.log(" npx @fluencypassdevs/cycle init")
129
+ console.log(" npx cycle init")
95
130
  console.log("")
package/bin/mcp.mjs ADDED
@@ -0,0 +1,933 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cycle Design System — MCP Server
5
+ *
6
+ * Model Context Protocol server that exposes the Cycle Design System
7
+ * component library to AI assistants. Zero external dependencies.
8
+ *
9
+ * Usage (Claude Code .mcp.json):
10
+ * { "mcpServers": { "cycle-design-system": { "type": "stdio", "command": "npx", "args": ["cycle", "mcp"] } } }
11
+ */
12
+
13
+ import { createInterface } from "readline"
14
+
15
+ // ─── Component Registry ────────────────────────────────────────────────────────
16
+
17
+ const COMPONENTS = [
18
+ {
19
+ name: "Button",
20
+ import: `import { Button } from "@fluencypassdevs/cycle"`,
21
+ description: "Botao com 6 variants (default, destructive, outline, secondary, ghost, link), 8 sizes (xs, sm, default, lg, icon-xs, icon-sm, icon-default, icon-lg). Suporte a temas via .theme-*.",
22
+ props: [
23
+ { name: "variant", type: '"default" | "destructive" | "outline" | "secondary" | "ghost" | "link"', default: '"default"' },
24
+ { name: "size", type: '"xs" | "sm" | "default" | "lg" | "icon-xs" | "icon-sm" | "icon-default" | "icon-lg"', default: '"default"' },
25
+ { name: "asChild", type: "boolean", default: "false" },
26
+ ],
27
+ example: `<Button variant="default" size="sm">Salvar</Button>
28
+ <Button variant="outline" size="lg">Cancelar</Button>
29
+ <Button variant="ghost" size="icon-sm"><CycleIcon icon={X} size="sm" decorative /></Button>`,
30
+ keywords: ["botao", "button", "cta", "acao", "submit", "click"],
31
+ },
32
+ {
33
+ name: "Input",
34
+ import: `import { Input } from "@fluencypassdevs/cycle"`,
35
+ description: "Campo de entrada com 2 variantes (outline, filled), 3 sizes (sm, default, lg). Estados: default, focused, disabled, error (aria-invalid). Suporte a temas via .theme-*.",
36
+ props: [
37
+ { name: "variant", type: '"outline" | "filled"', default: '"outline"' },
38
+ { name: "inputSize", type: '"sm" | "default" | "lg"', default: '"default"' },
39
+ ],
40
+ example: `<Input placeholder="Email" />
41
+ <Input variant="filled" inputSize="lg" />
42
+ <Input aria-invalid="true" />`,
43
+ keywords: ["input", "campo", "texto", "form", "formulario", "email", "senha"],
44
+ },
45
+ {
46
+ name: "Textarea",
47
+ import: `import { Textarea } from "@fluencypassdevs/cycle"`,
48
+ description: "Campo multi-linha. 2 variantes (outline, filled), 3 sizes (sm=60px, default=80px, lg=120px). Mesmos tokens do Input. Resize-y.",
49
+ props: [
50
+ { name: "variant", type: '"outline" | "filled"', default: '"outline"' },
51
+ { name: "textareaSize", type: '"sm" | "default" | "lg"', default: '"default"' },
52
+ ],
53
+ example: `<Textarea placeholder="Escreva aqui..." />
54
+ <Textarea variant="filled" textareaSize="lg" />`,
55
+ keywords: ["textarea", "texto", "multi-linha", "comentario", "mensagem", "descricao"],
56
+ },
57
+ {
58
+ name: "Label",
59
+ import: `import { Label } from "@fluencypassdevs/cycle"`,
60
+ description: "Rotulo acessivel para campos de formulario. Baseado no Radix UI Label.",
61
+ props: [
62
+ { name: "htmlFor", type: "string", default: "-" },
63
+ ],
64
+ example: `<Label htmlFor="email">Email</Label>
65
+ <Input id="email" />`,
66
+ keywords: ["label", "rotulo", "formulario", "campo", "acessibilidade"],
67
+ },
68
+ {
69
+ name: "Badge",
70
+ import: `import { Badge } from "@fluencypassdevs/cycle"`,
71
+ description: "Badge com 10 variants (default, secondary, destructive, outline, ghost, link, muted, success, progress, progress-completed), 3 sizes (sm, default, lg). Suporte a temas via .theme-*.",
72
+ props: [
73
+ { name: "variant", type: '"default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | "muted" | "success" | "progress" | "progress-completed"', default: '"default"' },
74
+ { name: "size", type: '"sm" | "default" | "lg"', default: '"default"' },
75
+ ],
76
+ example: `<Badge>Novo</Badge>
77
+ <Badge variant="success" size="sm">Completo</Badge>
78
+ <Badge variant="progress">Em progresso</Badge>`,
79
+ keywords: ["badge", "tag", "etiqueta", "status", "label", "chip"],
80
+ },
81
+ {
82
+ name: "Accordion",
83
+ import: `import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@fluencypassdevs/cycle"`,
84
+ description: "Secoes colapsaveis com animacao. Suporta single e multiple.",
85
+ props: [
86
+ { name: "type", type: '"single" | "multiple"', default: '"single"' },
87
+ { name: "collapsible", type: "boolean", default: "false" },
88
+ ],
89
+ example: `<Accordion type="single" collapsible>
90
+ <AccordionItem value="item-1">
91
+ <AccordionTrigger>Secao 1</AccordionTrigger>
92
+ <AccordionContent>Conteudo aqui</AccordionContent>
93
+ </AccordionItem>
94
+ </Accordion>`,
95
+ keywords: ["accordion", "colapsavel", "expandir", "secao", "faq", "pergunta"],
96
+ },
97
+ {
98
+ name: "Tabs",
99
+ import: `import { Tabs, TabsList, TabsTrigger, TabsContent } from "@fluencypassdevs/cycle"`,
100
+ description: "Componente de abas com 2 variants (default, line), orientacao horizontal e vertical.",
101
+ props: [
102
+ { name: "variant", type: '"default" | "line"', default: '"default"' },
103
+ { name: "orientation", type: '"horizontal" | "vertical"', default: '"horizontal"' },
104
+ ],
105
+ example: `<Tabs defaultValue="tab1">
106
+ <TabsList>
107
+ <TabsTrigger value="tab1">Aba 1</TabsTrigger>
108
+ <TabsTrigger value="tab2">Aba 2</TabsTrigger>
109
+ </TabsList>
110
+ <TabsContent value="tab1">Conteudo 1</TabsContent>
111
+ <TabsContent value="tab2">Conteudo 2</TabsContent>
112
+ </Tabs>`,
113
+ keywords: ["tabs", "abas", "navegacao", "tab", "painel"],
114
+ },
115
+ {
116
+ name: "Sheet",
117
+ import: `import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetClose } from "@fluencypassdevs/cycle"`,
118
+ description: "Painel deslizante (overlay lateral ou bottom). Sides: top, right, bottom, left.",
119
+ props: [
120
+ { name: "side", type: '"top" | "right" | "bottom" | "left"', default: '"right"' },
121
+ ],
122
+ example: `<Sheet>
123
+ <SheetTrigger asChild><Button>Abrir</Button></SheetTrigger>
124
+ <SheetContent side="right">
125
+ <SheetHeader><SheetTitle>Titulo</SheetTitle></SheetHeader>
126
+ Conteudo
127
+ </SheetContent>
128
+ </Sheet>`,
129
+ keywords: ["sheet", "drawer", "painel", "lateral", "overlay", "menu", "sidebar"],
130
+ },
131
+ {
132
+ name: "ScrollArea",
133
+ import: `import { ScrollArea, ScrollBar } from "@fluencypassdevs/cycle"`,
134
+ description: "Area de scroll customizada com scrollbar estilizado. Vertical e horizontal.",
135
+ props: [],
136
+ example: `<ScrollArea className="h-72 w-48">
137
+ <div>Conteudo longo...</div>
138
+ </ScrollArea>`,
139
+ keywords: ["scroll", "scrollbar", "rolagem", "area"],
140
+ },
141
+ {
142
+ name: "Checkbox",
143
+ import: `import { Checkbox } from "@fluencypassdevs/cycle"`,
144
+ description: "Checkbox com 3 sizes (sm, default, lg), 2 variants (default, circular). Prop theme aplica cor no estado checked.",
145
+ props: [
146
+ { name: "size", type: '"sm" | "default" | "lg"', default: '"default"' },
147
+ { name: "variant", type: '"default" | "circular"', default: '"default"' },
148
+ { name: "theme", type: "string", default: "-" },
149
+ ],
150
+ example: `<Checkbox />
151
+ <Checkbox size="lg" variant="circular" />`,
152
+ keywords: ["checkbox", "check", "marcar", "selecionar", "formulario", "toggle"],
153
+ },
154
+ {
155
+ name: "RadioGroup",
156
+ import: `import { RadioGroup, RadioGroupItem } from "@fluencypassdevs/cycle"`,
157
+ description: "Radio button com 3 sizes (sm, default, lg). Prop theme aplica cor no estado checked.",
158
+ props: [
159
+ { name: "size", type: '"sm" | "default" | "lg"', default: '"default"' },
160
+ { name: "theme", type: "string", default: "-" },
161
+ ],
162
+ example: `<RadioGroup defaultValue="a">
163
+ <RadioGroupItem value="a" id="a" />
164
+ <Label htmlFor="a">Opcao A</Label>
165
+ <RadioGroupItem value="b" id="b" />
166
+ <Label htmlFor="b">Opcao B</Label>
167
+ </RadioGroup>`,
168
+ keywords: ["radio", "opcao", "selecionar", "formulario", "escolher"],
169
+ },
170
+ {
171
+ name: "Switch",
172
+ import: `import { Switch } from "@fluencypassdevs/cycle"`,
173
+ description: "Toggle on/off. 3 sizes (sm, default, lg). Prop theme aplica cor no estado checked.",
174
+ props: [
175
+ { name: "size", type: '"sm" | "default" | "lg"', default: '"default"' },
176
+ { name: "theme", type: "string", default: "-" },
177
+ ],
178
+ example: `<Switch />
179
+ <Switch size="lg" />`,
180
+ keywords: ["switch", "toggle", "ligar", "desligar", "on", "off"],
181
+ },
182
+ {
183
+ name: "Slider",
184
+ import: `import { Slider } from "@fluencypassdevs/cycle"`,
185
+ description: "Controle deslizante com 3 sizes (sm, default, lg), suporte a range (2 thumbs). Suporta .theme-*.",
186
+ props: [
187
+ { name: "size", type: '"sm" | "default" | "lg"', default: '"default"' },
188
+ { name: "defaultValue", type: "number[]", default: "[50]" },
189
+ { name: "min", type: "number", default: "0" },
190
+ { name: "max", type: "number", default: "100" },
191
+ { name: "step", type: "number", default: "1" },
192
+ ],
193
+ example: `<Slider defaultValue={[50]} max={100} step={1} />
194
+ <Slider defaultValue={[25, 75]} /> {/* range */}`,
195
+ keywords: ["slider", "deslizante", "range", "volume", "controle"],
196
+ },
197
+ {
198
+ name: "Toggle",
199
+ import: `import { Toggle } from "@fluencypassdevs/cycle"`,
200
+ description: "Botao toggle (on/off). 2 variants (default, outline), 8 sizes. Prop fillOnPress para preencher icones SVG no estado on.",
201
+ props: [
202
+ { name: "variant", type: '"default" | "outline"', default: '"default"' },
203
+ { name: "size", type: '"xs" | "sm" | "default" | "lg" | "icon-xs" | "icon-sm" | "icon-default" | "icon-lg"', default: '"default"' },
204
+ { name: "fillOnPress", type: "boolean", default: "false" },
205
+ ],
206
+ example: `<Toggle><CycleIcon icon={Bold} size="sm" decorative /> Bold</Toggle>
207
+ <Toggle size="icon-sm" fillOnPress><CycleIcon icon={Heart} size="sm" decorative /></Toggle>`,
208
+ keywords: ["toggle", "botao", "pressionar", "favorito", "bold", "italico"],
209
+ },
210
+ {
211
+ name: "Progress",
212
+ import: `import { Progress } from "@fluencypassdevs/cycle"`,
213
+ description: "Barra de progresso. 4 sizes (xs, sm, default, lg), 4 variants (default, secondary, destructive, muted). Prop theme aplica cor apenas no indicator.",
214
+ props: [
215
+ { name: "value", type: "number", default: "0" },
216
+ { name: "size", type: '"xs" | "sm" | "default" | "lg"', default: '"default"' },
217
+ { name: "variant", type: '"default" | "secondary" | "destructive" | "muted"', default: '"default"' },
218
+ { name: "theme", type: "string", default: "-" },
219
+ ],
220
+ example: `<Progress value={60} />
221
+ <Progress value={80} size="lg" theme="brand" />`,
222
+ keywords: ["progress", "progresso", "barra", "loading", "porcentagem", "completar"],
223
+ },
224
+ {
225
+ name: "ProgressStage",
226
+ import: `import { ProgressStage } from "@fluencypassdevs/cycle"`,
227
+ description: "Progresso segmentado em pills (2-10 stages). Prop theme aplica cor apenas nas pills preenchidas.",
228
+ props: [
229
+ { name: "current", type: "number", default: "0" },
230
+ { name: "total", type: "number (2-10)", default: "5" },
231
+ { name: "theme", type: "string", default: "-" },
232
+ ],
233
+ example: `<ProgressStage current={3} total={5} />
234
+ <ProgressStage current={2} total={4} theme="brand" />`,
235
+ keywords: ["progress", "stage", "etapa", "passo", "step", "wizard", "segmentado"],
236
+ },
237
+ {
238
+ name: "ProgressDot",
239
+ import: `import { ProgressDot } from "@fluencypassdevs/cycle"`,
240
+ description: "Progresso em dots circulares (2-10 stages). 4 sizes (xs=4px, sm=6px, default=8px, lg=12px). Prop theme aplica cor nos dots preenchidos.",
241
+ props: [
242
+ { name: "current", type: "number", default: "0" },
243
+ { name: "total", type: "number (2-10)", default: "5" },
244
+ { name: "size", type: '"xs" | "sm" | "default" | "lg"', default: '"default"' },
245
+ { name: "theme", type: "string", default: "-" },
246
+ ],
247
+ example: `<ProgressDot current={2} total={5} />
248
+ <ProgressDot current={1} total={3} size="lg" theme="brand" />`,
249
+ keywords: ["progress", "dot", "ponto", "indicador", "carousel", "slide", "paginacao"],
250
+ },
251
+ {
252
+ name: "FileCard",
253
+ import: `import { FileCard } from "@fluencypassdevs/cycle"`,
254
+ description: "Botao de download com icone, titulo, tipo e tamanho. 3 sizes (sm, md, lg).",
255
+ props: [
256
+ { name: "title", type: "string", default: "-" },
257
+ { name: "fileType", type: "string", default: "-" },
258
+ { name: "fileSize", type: "string", default: "-" },
259
+ { name: "size", type: '"sm" | "md" | "lg"', default: '"md"' },
260
+ { name: "icon", type: "LucideIcon", default: "FileDown" },
261
+ { name: "onClick", type: "() => void", default: "-" },
262
+ ],
263
+ example: `<FileCard title="Material da aula" fileType="PDF" fileSize="2.4 MB" onClick={() => download()} />`,
264
+ keywords: ["file", "arquivo", "download", "card", "pdf", "documento", "anexo"],
265
+ },
266
+ {
267
+ name: "Avatar",
268
+ import: `import { Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup } from "@fluencypassdevs/cycle"`,
269
+ description: "Avatar com imagem, fallback de iniciais, badge de status e group. 3 sizes (sm=24px, default=32px, lg=40px).",
270
+ props: [
271
+ { name: "size", type: '"sm" | "default" | "lg"', default: '"default"' },
272
+ { name: "theme", type: "string", default: "-" },
273
+ ],
274
+ example: `<Avatar size="lg">
275
+ <AvatarImage src="/foto.jpg" alt="Nome" />
276
+ <AvatarFallback>FP</AvatarFallback>
277
+ <AvatarBadge status="online" />
278
+ </Avatar>
279
+
280
+ <AvatarGroup>
281
+ <Avatar><AvatarFallback>A</AvatarFallback></Avatar>
282
+ <Avatar><AvatarFallback>B</AvatarFallback></Avatar>
283
+ </AvatarGroup>`,
284
+ keywords: ["avatar", "foto", "imagem", "usuario", "perfil", "iniciais", "group"],
285
+ },
286
+ {
287
+ name: "ChatBubble",
288
+ import: `import { ChatBubble } from "@fluencypassdevs/cycle"`,
289
+ description: "Bolha de mensagem. 3 variantes: default (aluno), instructor (professor com destaque primary), system (mensagem central italico).",
290
+ props: [
291
+ { name: "variant", type: '"default" | "instructor" | "system"', default: '"default"' },
292
+ { name: "message", type: "string", default: "-" },
293
+ { name: "sender", type: "string", default: "-" },
294
+ { name: "avatarSrc", type: "string", default: "-" },
295
+ { name: "timestamp", type: "string", default: "-" },
296
+ ],
297
+ example: `<ChatBubble variant="default" sender="Aluno" message="Oi, professor!" timestamp="14:30" />
298
+ <ChatBubble variant="instructor" sender="Professor" message="Oi! Vamos comecar." />`,
299
+ keywords: ["chat", "mensagem", "bolha", "conversa", "bubble", "message"],
300
+ },
301
+ {
302
+ name: "ChatPanel",
303
+ import: `import { ChatPanel } from "@fluencypassdevs/cycle"`,
304
+ description: "Painel de chat completo com lista de ChatBubbles, scroll automatico, e campo de input para envio.",
305
+ props: [
306
+ { name: "messages", type: "ChatMessage[]", default: "[]" },
307
+ { name: "onSend", type: "(msg: string) => void", default: "-" },
308
+ { name: "currentUser", type: "string", default: "-" },
309
+ ],
310
+ example: `<ChatPanel messages={messages} onSend={handleSend} currentUser="aluno@email.com" />`,
311
+ keywords: ["chat", "painel", "conversa", "mensagens", "live", "tempo real"],
312
+ },
313
+ {
314
+ name: "LikeDislike",
315
+ import: `import { LikeDislike } from "@fluencypassdevs/cycle"`,
316
+ description: "Botoes de like/dislike mutuamente exclusivos. 4 sizes (xs, sm, default, lg). Animacao burst no like. Feedback opcional no dislike via Textarea.",
317
+ props: [
318
+ { name: "size", type: '"xs" | "sm" | "default" | "lg"', default: '"default"' },
319
+ { name: "value", type: '"like" | "dislike" | null', default: "null" },
320
+ { name: "onValueChange", type: "(val) => void", default: "-" },
321
+ { name: "showFeedback", type: "boolean", default: "false" },
322
+ ],
323
+ example: `<LikeDislike onValueChange={(val) => console.log(val)} />
324
+ <LikeDislike showFeedback onFeedbackSubmit={(text) => save(text)} />`,
325
+ keywords: ["like", "dislike", "feedback", "avaliacao", "curtir", "positivo", "negativo"],
326
+ },
327
+ {
328
+ name: "LiveWaiting",
329
+ import: `import { LiveWaiting } from "@fluencypassdevs/cycle"`,
330
+ description: "Tela de espera para lives com countdown em tempo real, badge, info do professor (Avatar) e topico.",
331
+ props: [
332
+ { name: "scheduledAt", type: "Date", default: "-" },
333
+ { name: "teacherName", type: "string", default: "-" },
334
+ { name: "teacherAvatar", type: "string", default: "-" },
335
+ { name: "topic", type: "string", default: "-" },
336
+ ],
337
+ example: `<LiveWaiting scheduledAt={new Date("2026-03-25T19:00")} teacherName="Prof. Ana" topic="Speaking Practice" />`,
338
+ keywords: ["live", "espera", "waiting", "countdown", "aula", "evento", "timer"],
339
+ },
340
+ {
341
+ name: "AudioPlayer",
342
+ import: `import { AudioPlayer } from "@fluencypassdevs/cycle"`,
343
+ description: "Player de audio (Vidstack). 2 variantes (default, card). Suporta MP3, OGG, HLS. showSpeed=true por padrao.",
344
+ props: [
345
+ { name: "src", type: "string", default: "-" },
346
+ { name: "variant", type: '"default" | "card"', default: '"default"' },
347
+ { name: "title", type: "string", default: "-" },
348
+ { name: "description", type: "string", default: "-" },
349
+ { name: "showSpeed", type: "boolean", default: "true" },
350
+ ],
351
+ example: `<AudioPlayer src="/audio.mp3" />
352
+ <AudioPlayer variant="card" src="/audio.mp3" title="Listening Practice" description="Episode 1" />`,
353
+ keywords: ["audio", "player", "musica", "podcast", "som", "mp3", "ouvir", "listening"],
354
+ },
355
+ {
356
+ name: "VideoPlayer",
357
+ import: `import { VideoPlayer } from "@fluencypassdevs/cycle"`,
358
+ description: "Player de video (Vidstack). Suporta MP4, WebM, HLS. Chapters, buffering spinner, live indicator, tooltips.",
359
+ props: [
360
+ { name: "src", type: "string", default: "-" },
361
+ { name: "title", type: "string", default: "-" },
362
+ { name: "chapters", type: "string (VTT URL)", default: "-" },
363
+ { name: "showBuffering", type: "boolean", default: "true" },
364
+ ],
365
+ example: `<VideoPlayer src="/video.mp4" title="Aula 1" />
366
+ <VideoPlayer src="https://stream.example/live.m3u8" title="Live" />`,
367
+ keywords: ["video", "player", "aula", "assistir", "mp4", "stream", "hls", "live"],
368
+ },
369
+ {
370
+ name: "Sonner (Toast)",
371
+ import: `import { Toaster, cycleToast } from "@fluencypassdevs/cycle"`,
372
+ description: "Notificacoes temporarias (toast). cycleToast.positive/critical/warning/info aplica temas Cycle. Requer <Toaster /> no layout.",
373
+ props: [],
374
+ example: `// No layout raiz:
375
+ <Toaster />
376
+
377
+ // Para disparar:
378
+ cycleToast.positive("Salvo com sucesso!")
379
+ cycleToast.critical("Erro ao salvar")
380
+ cycleToast.warning("Atencao: limite proximo")
381
+ cycleToast.info("Nova mensagem recebida")`,
382
+ keywords: ["toast", "notificacao", "snackbar", "alerta", "mensagem", "sonner", "feedback"],
383
+ },
384
+ {
385
+ name: "Alert",
386
+ import: `import { Alert, AlertTitle, AlertDescription, AlertAction, AlertClose } from "@fluencypassdevs/cycle"`,
387
+ description: "Alerta informativo com icone, acao e close. 6 variantes: default, destructive, positive, warning, critical, info.",
388
+ props: [
389
+ { name: "variant", type: '"default" | "destructive" | "positive" | "warning" | "critical" | "info"', default: '"default"' },
390
+ ],
391
+ example: `<Alert variant="positive">
392
+ <AlertTitle>Sucesso</AlertTitle>
393
+ <AlertDescription>Dados salvos com sucesso.</AlertDescription>
394
+ <AlertAction onClick={() => undo()}>Desfazer</AlertAction>
395
+ <AlertClose />
396
+ </Alert>`,
397
+ keywords: ["alert", "alerta", "aviso", "notificacao", "info", "warning", "erro", "sucesso"],
398
+ },
399
+ {
400
+ name: "AlertDialog",
401
+ import: `import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogAction, AlertDialogCancel } from "@fluencypassdevs/cycle"`,
402
+ description: "Dialog de confirmacao modal. 2 sizes (default, sm). Media/icone, titulo, descricao, botoes de acao e cancelar.",
403
+ props: [
404
+ { name: "size", type: '"default" | "sm"', default: '"default"' },
405
+ ],
406
+ example: `<AlertDialog>
407
+ <AlertDialogTrigger asChild><Button variant="destructive">Excluir</Button></AlertDialogTrigger>
408
+ <AlertDialogContent>
409
+ <AlertDialogHeader>
410
+ <AlertDialogTitle>Tem certeza?</AlertDialogTitle>
411
+ <AlertDialogDescription>Esta acao nao pode ser desfeita.</AlertDialogDescription>
412
+ </AlertDialogHeader>
413
+ <AlertDialogFooter>
414
+ <AlertDialogCancel>Cancelar</AlertDialogCancel>
415
+ <AlertDialogAction>Confirmar</AlertDialogAction>
416
+ </AlertDialogFooter>
417
+ </AlertDialogContent>
418
+ </AlertDialog>`,
419
+ keywords: ["dialog", "confirmacao", "modal", "excluir", "deletar", "confirmar", "alert"],
420
+ },
421
+ {
422
+ name: "Dialog",
423
+ import: `import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose } from "@fluencypassdevs/cycle"`,
424
+ description: "Modal dialog com overlay, close button, header/footer, titulo/descricao. Controlado ou via trigger.",
425
+ props: [
426
+ { name: "showCloseButton", type: "boolean", default: "true" },
427
+ ],
428
+ example: `<Dialog>
429
+ <DialogTrigger asChild><Button>Abrir</Button></DialogTrigger>
430
+ <DialogContent>
431
+ <DialogHeader>
432
+ <DialogTitle>Titulo</DialogTitle>
433
+ <DialogDescription>Descricao do modal.</DialogDescription>
434
+ </DialogHeader>
435
+ Conteudo
436
+ <DialogFooter>
437
+ <Button>Salvar</Button>
438
+ </DialogFooter>
439
+ </DialogContent>
440
+ </Dialog>`,
441
+ keywords: ["dialog", "modal", "popup", "janela", "overlay", "formulario"],
442
+ },
443
+ {
444
+ name: "Select",
445
+ import: `import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator } from "@fluencypassdevs/cycle"`,
446
+ description: "Dropdown de selecao. 2 sizes (sm, default). Suporta grupos, labels, separadores e scroll.",
447
+ props: [
448
+ { name: "size", type: '"sm" | "default"', default: '"default"' },
449
+ ],
450
+ example: `<Select>
451
+ <SelectTrigger><SelectValue placeholder="Selecione..." /></SelectTrigger>
452
+ <SelectContent>
453
+ <SelectItem value="a">Opcao A</SelectItem>
454
+ <SelectItem value="b">Opcao B</SelectItem>
455
+ </SelectContent>
456
+ </Select>`,
457
+ keywords: ["select", "dropdown", "selecao", "opcao", "escolher", "lista", "combo"],
458
+ },
459
+ {
460
+ name: "Popover",
461
+ import: `import { Popover, PopoverTrigger, PopoverContent } from "@fluencypassdevs/cycle"`,
462
+ description: "Conteudo flutuante ancorado a um trigger. Header, titulo e descricao.",
463
+ props: [],
464
+ example: `<Popover>
465
+ <PopoverTrigger asChild><Button variant="outline">Info</Button></PopoverTrigger>
466
+ <PopoverContent>Conteudo do popover</PopoverContent>
467
+ </Popover>`,
468
+ keywords: ["popover", "flutuante", "tooltip", "info", "detalhes", "popup"],
469
+ },
470
+ {
471
+ name: "Tooltip",
472
+ import: `import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "@fluencypassdevs/cycle"`,
473
+ description: "Dica de texto ao hover/focus. Requer TooltipProvider no layout raiz.",
474
+ props: [],
475
+ example: `// No layout raiz:
476
+ <TooltipProvider>...</TooltipProvider>
477
+
478
+ // Uso:
479
+ <Tooltip>
480
+ <TooltipTrigger asChild><Button variant="ghost">?</Button></TooltipTrigger>
481
+ <TooltipContent>Texto de ajuda</TooltipContent>
482
+ </Tooltip>`,
483
+ keywords: ["tooltip", "dica", "ajuda", "hover", "hint", "info"],
484
+ },
485
+ {
486
+ name: "Resizable",
487
+ import: `import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@fluencypassdevs/cycle"`,
488
+ description: "Paineis redimensionaveis com handle de arraste. Horizontal e vertical.",
489
+ props: [
490
+ { name: "direction", type: '"horizontal" | "vertical"', default: '"horizontal"' },
491
+ ],
492
+ example: `<ResizablePanelGroup direction="horizontal">
493
+ <ResizablePanel>Painel 1</ResizablePanel>
494
+ <ResizableHandle />
495
+ <ResizablePanel>Painel 2</ResizablePanel>
496
+ </ResizablePanelGroup>`,
497
+ keywords: ["resizable", "redimensionar", "painel", "split", "dividir", "drag"],
498
+ },
499
+ {
500
+ name: "Empty",
501
+ import: `import { Empty, EmptyIcon, EmptyTitle, EmptyDescription, EmptyContent } from "@fluencypassdevs/cycle"`,
502
+ description: "Estado vazio com icone/media, titulo, descricao e area para acoes.",
503
+ props: [],
504
+ example: `<Empty>
505
+ <EmptyIcon><CycleIcon icon={Inbox} size="lg" decorative /></EmptyIcon>
506
+ <EmptyTitle>Nenhum resultado</EmptyTitle>
507
+ <EmptyDescription>Tente ajustar os filtros.</EmptyDescription>
508
+ <EmptyContent><Button>Limpar filtros</Button></EmptyContent>
509
+ </Empty>`,
510
+ keywords: ["empty", "vazio", "nenhum", "resultado", "placeholder", "estado"],
511
+ },
512
+ {
513
+ name: "Skeleton",
514
+ import: `import { Skeleton } from "@fluencypassdevs/cycle"`,
515
+ description: "Placeholder animado (pulse) para conteudo em carregamento.",
516
+ props: [],
517
+ example: `<Skeleton className="h-4 w-[250px]" />
518
+ <Skeleton className="h-12 w-12 rounded-full" />`,
519
+ keywords: ["skeleton", "loading", "carregando", "placeholder", "shimmer", "pulse"],
520
+ },
521
+ {
522
+ name: "FluencypassLogo",
523
+ import: `import { FluencypassLogo, FluencypassIcon } from "@fluencypassdevs/cycle"`,
524
+ description: "Logo da Fluencypass (icone coral + texto). 5 sizes (xs, sm, md, lg, xl). FluencypassIcon para o icone sozinho.",
525
+ props: [
526
+ { name: "size", type: '"xs" | "sm" | "md" | "lg" | "xl"', default: '"md"' },
527
+ ],
528
+ example: `<FluencypassLogo size="md" />
529
+ <FluencypassIcon className="h-7 w-auto" />`,
530
+ keywords: ["logo", "fluencypass", "marca", "brand", "icone"],
531
+ },
532
+ {
533
+ name: "ProductLogo",
534
+ import: `import { ProductLogo, ClassLogo, PrivateTalkLogo, GroupTalkLogo } from "@fluencypassdevs/cycle"`,
535
+ description: "Logo de produto (icone Lucide + label). 5 sizes. Pre-built: ClassLogo, PrivateTalkLogo, GroupTalkLogo.",
536
+ props: [
537
+ { name: "icon", type: "LucideIcon", default: "-" },
538
+ { name: "label", type: "string", default: "-" },
539
+ { name: "size", type: '"xs" | "sm" | "md" | "lg" | "xl"', default: '"md"' },
540
+ ],
541
+ example: `<ClassLogo size="md" />
542
+ <PrivateTalkLogo size="sm" />
543
+ <GroupTalkLogo size="lg" />
544
+ <ProductLogo icon={Rocket} label="Impulse" size="md" className="theme-impulse" />`,
545
+ keywords: ["logo", "produto", "class", "private", "group", "talk", "marca"],
546
+ },
547
+ {
548
+ name: "CycleIcon",
549
+ import: `import { CycleIcon } from "@fluencypassdevs/cycle"
550
+ import { Home, Search, Settings } from "@fluencypassdevs/cycle/icons/lucide"`,
551
+ description: "Wrapper para icones Lucide com size/stroke tokens. OBRIGATORIO: icones Lucide SEMPRE de @fluencypassdevs/cycle/icons/lucide, NUNCA de lucide-react.",
552
+ props: [
553
+ { name: "icon", type: "LucideIcon", default: "-" },
554
+ { name: "size", type: '"xs" | "sm" | "md" | "lg" | "xl"', default: '"sm"' },
555
+ { name: "decorative", type: "boolean", default: "false" },
556
+ { name: "aria-label", type: "string", default: "-" },
557
+ ],
558
+ example: `import { CycleIcon } from "@fluencypassdevs/cycle"
559
+ import { Home, Search } from "@fluencypassdevs/cycle/icons/lucide"
560
+
561
+ <CycleIcon icon={Home} size="sm" decorative />
562
+ <CycleIcon icon={Search} size="md" aria-label="Buscar" />`,
563
+ keywords: ["icone", "icon", "lucide", "svg", "grafico"],
564
+ },
565
+ ]
566
+
567
+ // ─── Design Tokens ─────────────────────────────────────────────────────────────
568
+
569
+ const TOKENS = {
570
+ colors: {
571
+ backgrounds: ["bg-background", "bg-card", "bg-popover", "bg-primary", "bg-secondary", "bg-muted", "bg-accent", "bg-destructive"],
572
+ foregrounds: ["text-foreground", "text-card-foreground", "text-popover-foreground", "text-primary-foreground", "text-secondary-foreground", "text-muted-foreground", "text-accent-foreground", "text-destructive-foreground"],
573
+ borders: ["border-border", "border-input"],
574
+ semantic: ["bg-positive", "bg-warning", "bg-critical", "bg-info"],
575
+ },
576
+ radius: ["rounded-sm", "rounded-md", "rounded-lg", "rounded-xl"],
577
+ typography: {
578
+ body: ["body-xs (11px)", "body-sm (12px)", "body-md (14px)", "body-lg (16px)"],
579
+ heading: ["heading-xs (14px/600)", "heading-sm (16px/600)", "heading-md (18px/600)", "heading-lg (20px/600)", "heading-xl (24px/600)", "heading-2xl (30px/700)", "heading-3xl (36px/700)", "heading-4xl (48px/700)"],
580
+ },
581
+ themes: [".theme-brand", ".theme-class", ".theme-private-talk", ".theme-group-talk"],
582
+ }
583
+
584
+ // ─── Tool Definitions ──────────────────────────────────────────────────────────
585
+
586
+ const TOOLS = [
587
+ {
588
+ name: "cycle_search_component",
589
+ description: "Search for a Cycle Design System component by name or keyword. Use this BEFORE creating any UI to check if a component already exists. Returns matching components with imports and descriptions.",
590
+ inputSchema: {
591
+ type: "object",
592
+ properties: {
593
+ query: {
594
+ type: "string",
595
+ description: "Component name or keyword to search (e.g. 'button', 'progress', 'chat', 'download', 'modal')",
596
+ },
597
+ },
598
+ required: ["query"],
599
+ },
600
+ },
601
+ {
602
+ name: "cycle_get_component",
603
+ description: "Get full details of a specific Cycle Design System component including all props, variants, sizes, and usage examples.",
604
+ inputSchema: {
605
+ type: "object",
606
+ properties: {
607
+ name: {
608
+ type: "string",
609
+ description: "Exact component name (e.g. 'Button', 'Progress', 'AudioPlayer')",
610
+ },
611
+ },
612
+ required: ["name"],
613
+ },
614
+ },
615
+ {
616
+ name: "cycle_get_icon_usage",
617
+ description: "Get the correct way to use icons in projects using the Cycle Design System. IMPORTANT: lucide-react must NEVER be imported directly.",
618
+ inputSchema: {
619
+ type: "object",
620
+ properties: {},
621
+ },
622
+ },
623
+ {
624
+ name: "cycle_get_setup",
625
+ description: "Get project setup instructions for using the Cycle Design System (npm install, CSS config, @source directive).",
626
+ inputSchema: {
627
+ type: "object",
628
+ properties: {},
629
+ },
630
+ },
631
+ {
632
+ name: "cycle_get_tokens",
633
+ description: "Get available design tokens (colors, typography, radius, themes). IMPORTANT: Never use hardcoded colors like bg-blue-500 or hex values.",
634
+ inputSchema: {
635
+ type: "object",
636
+ properties: {
637
+ category: {
638
+ type: "string",
639
+ enum: ["colors", "typography", "radius", "themes", "all"],
640
+ description: "Token category to retrieve",
641
+ },
642
+ },
643
+ },
644
+ },
645
+ {
646
+ name: "cycle_get_rules",
647
+ description: "Get the mandatory rules for AI assistants using the Cycle Design System. Call this at the start of any UI implementation task.",
648
+ inputSchema: {
649
+ type: "object",
650
+ properties: {},
651
+ },
652
+ },
653
+ ]
654
+
655
+ // ─── Tool Handlers ─────────────────────────────────────────────────────────────
656
+
657
+ function handleToolCall(name, args) {
658
+ switch (name) {
659
+ case "cycle_search_component": {
660
+ const query = (args.query || "").toLowerCase()
661
+ const results = COMPONENTS.filter((c) => {
662
+ const searchable = `${c.name} ${c.description} ${c.keywords.join(" ")}`.toLowerCase()
663
+ return query.split(/\s+/).every((word) => searchable.includes(word))
664
+ })
665
+ if (results.length === 0) {
666
+ return `No components found for "${args.query}". This component does NOT exist in the Cycle Design System. Do NOT create it from scratch — signal the gap to the developer and use existing primitives (Button, Input, Badge, etc.) as building blocks if needed.`
667
+ }
668
+ const list = results
669
+ .map((c) => `### ${c.name}\n${c.import}\n${c.description}`)
670
+ .join("\n\n")
671
+ return `Found ${results.length} component(s) matching "${args.query}":\n\n${list}\n\nUse cycle_get_component for full details with props and examples.`
672
+ }
673
+
674
+ case "cycle_get_component": {
675
+ const comp = COMPONENTS.find(
676
+ (c) => c.name.toLowerCase() === (args.name || "").toLowerCase()
677
+ )
678
+ if (!comp) {
679
+ return `Component "${args.name}" not found. Use cycle_search_component to search by keyword.`
680
+ }
681
+ const propsTable = comp.props.length > 0
682
+ ? "Props:\n" + comp.props.map((p) => ` - ${p.name}: ${p.type} (default: ${p.default})`).join("\n")
683
+ : "No configurable props."
684
+ return `# ${comp.name}\n\n${comp.import}\n\n${comp.description}\n\n${propsTable}\n\nExample:\n\`\`\`tsx\n${comp.example}\n\`\`\``
685
+ }
686
+
687
+ case "cycle_get_icon_usage":
688
+ return `# Icons in Cycle Design System
689
+
690
+ ## REGRA OBRIGATORIA
691
+ **NUNCA importar de "lucide-react" diretamente. O pacote lucide-react e uma dependencia INTERNA do Cycle.**
692
+
693
+ ## Como usar icones Lucide (UNICO caminho correto):
694
+
695
+ \`\`\`tsx
696
+ import { CycleIcon } from "@fluencypassdevs/cycle"
697
+ import { Home, Search, Settings } from "@fluencypassdevs/cycle/icons/lucide"
698
+
699
+ // Icone decorativo (sem significado semantico):
700
+ <CycleIcon icon={Home} size="sm" decorative />
701
+
702
+ // Icone com significado (acessivel):
703
+ <CycleIcon icon={Search} size="sm" aria-label="Buscar" />
704
+ \`\`\`
705
+
706
+ ## Icones custom Cycle (EdTech) — SEM wrapper CycleIcon:
707
+
708
+ \`\`\`tsx
709
+ import { Flashcard, Quiz, Course, Streak, Live } from "@fluencypassdevs/cycle"
710
+
711
+ <Flashcard size="sm" decorative />
712
+ \`\`\`
713
+
714
+ ## PROIBIDO:
715
+ \`\`\`tsx
716
+ // ❌ NUNCA fazer isso:
717
+ import { Home } from "lucide-react" // PROIBIDO
718
+ \`\`\`
719
+
720
+ ## Sizes disponiveis:
721
+ - xs: 16px (strokeWidth 1.2)
722
+ - sm: 24px (strokeWidth 1.5)
723
+ - md: 32px (strokeWidth 1.8)
724
+ - lg: 40px (strokeWidth 2.1)
725
+ - xl: 48px (strokeWidth 2.4)`
726
+
727
+ case "cycle_get_setup":
728
+ return `# Setup do Cycle Design System
729
+
730
+ ## 1. Instalar o pacote:
731
+ \`\`\`bash
732
+ npm install @fluencypassdevs/cycle
733
+ \`\`\`
734
+
735
+ ## 2. Configurar CSS (globals.css):
736
+ \`\`\`css
737
+ @import "tailwindcss";
738
+ @source "../../node_modules/@fluencypassdevs/cycle/dist";
739
+ @import "@fluencypassdevs/cycle/styles.css";
740
+ \`\`\`
741
+
742
+ > **CRITICO**: O \`@source\` e OBRIGATORIO. Sem ele, o Tailwind v4 nao escaneia node_modules e remove (purge) TODAS as classes usadas pelos componentes do Cycle. O path do @source deve ser relativo ao seu globals.css ate a pasta dist do pacote.
743
+
744
+ ## 3. Importar componentes:
745
+ \`\`\`tsx
746
+ import { Button, Input, Badge } from "@fluencypassdevs/cycle"
747
+ \`\`\`
748
+
749
+ ## 4. Importar icones (OBRIGATORIO via Cycle):
750
+ \`\`\`tsx
751
+ import { CycleIcon } from "@fluencypassdevs/cycle"
752
+ import { Home } from "@fluencypassdevs/cycle/icons/lucide"
753
+ // NUNCA: import { Home } from "lucide-react" ❌
754
+ \`\`\`
755
+
756
+ ## 5. Utilidades:
757
+ \`\`\`tsx
758
+ import { cn } from "@fluencypassdevs/cycle/lib/utils"
759
+ \`\`\`
760
+
761
+ ## 6. Configurar regras para IA (opcional):
762
+ \`\`\`bash
763
+ npx cycle init
764
+ \`\`\``
765
+
766
+ case "cycle_get_tokens": {
767
+ const cat = args.category || "all"
768
+ if (cat === "all") {
769
+ return `# Design Tokens do Cycle\n\n## Colors\nBackgrounds: ${TOKENS.colors.backgrounds.join(", ")}\nForegrounds: ${TOKENS.colors.foregrounds.join(", ")}\nBorders: ${TOKENS.colors.borders.join(", ")}\n\n## Typography\nBody: ${TOKENS.typography.body.join(", ")}\nHeading: ${TOKENS.typography.heading.join(", ")}\n\n## Radius\n${TOKENS.radius.join(", ")}\n\n## Themes\n${TOKENS.themes.join(", ")}\n\n**NUNCA usar cores hardcoded** (bg-blue-500, #hex). Sempre usar semantic tokens.`
770
+ }
771
+ const data = TOKENS[cat]
772
+ if (!data) return `Unknown category "${cat}". Available: colors, typography, radius, themes, all.`
773
+ return `# ${cat} tokens\n\n${JSON.stringify(data, null, 2)}\n\n**NUNCA usar valores hardcoded.** Sempre usar estes semantic tokens.`
774
+ }
775
+
776
+ case "cycle_get_rules":
777
+ return `# Regras OBRIGATORIAS para IA — Cycle Design System
778
+
779
+ ## Regra Principal
780
+ **NUNCA copiar, clonar ou recriar componentes que ja existem na biblioteca.**
781
+ Sempre importar de \`@fluencypassdevs/cycle\`.
782
+
783
+ ## Regras de Componentes
784
+ 1. SEMPRE importar de \`@fluencypassdevs/cycle\` — NUNCA criar componentes se ja existem na biblioteca
785
+ 2. NUNCA copiar codigo-fonte de componentes para dentro do projeto
786
+ 3. Se um componente NAO existe na biblioteca, SINALIZE ao dev — nao invente um substituto
787
+ 4. Composites do projeto ficam em \`src/components/composites/\` e usam primitives da biblioteca
788
+ 5. Nunca criar um componente custom se o Cycle ja resolve
789
+
790
+ ## Regras de Icones (CRITICO)
791
+ 6. NUNCA importar de \`lucide-react\` — icones Lucide SEMPRE de \`@fluencypassdevs/cycle/icons/lucide\`
792
+ 7. Renderizar icones Lucide via \`<CycleIcon icon={...} />\`
793
+ 8. Icones custom Cycle (EdTech) direto de \`@fluencypassdevs/cycle\` sem wrapper
794
+
795
+ ## Regras de Estilo
796
+ 9. NUNCA usar cores hardcoded (bg-blue-500, #hex) — usar semantic tokens (bg-primary, text-muted-foreground)
797
+ 10. Usar \`cn()\` de \`@fluencypassdevs/cycle/lib/utils\` para merge de classes
798
+ 11. Dark mode via classe \`.dark\` — tokens light/dark ja estao definidos
799
+ 12. Usar variaveis de radius (rounded-sm, rounded-md, rounded-lg, rounded-xl)
800
+
801
+ ## Regras de Codigo
802
+ 13. TypeScript strict — tipar todas as props, nunca usar \`any\`
803
+ 14. Imports com alias \`@/\` para codigo do projeto — componentes do DS sempre de \`@fluencypassdevs/cycle\`
804
+
805
+ ## Fluxo ao receber um design/screenshot
806
+ 1. Identificar as areas da tela
807
+ 2. Para cada area, usar \`cycle_search_component\` para verificar se existe um componente
808
+ 3. Apresentar ao dev a lista de componentes ANTES de escrever codigo
809
+ 4. Aguardar aprovacao do dev
810
+ 5. Montar a pagina compondo os componentes importados
811
+ 6. Criar apenas o que NAO existe no Design System`
812
+
813
+ default:
814
+ return `Unknown tool: ${name}`
815
+ }
816
+ }
817
+
818
+ // ─── JSON-RPC 2.0 over stdio ──────────────────────────────────────────────────
819
+
820
+ const SERVER_INFO = {
821
+ name: "cycle-design-system",
822
+ version: "0.6.0",
823
+ }
824
+
825
+ function makeResponse(id, result) {
826
+ return JSON.stringify({ jsonrpc: "2.0", id, result })
827
+ }
828
+
829
+ function makeError(id, code, message) {
830
+ return JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } })
831
+ }
832
+
833
+ function handleMessage(msg) {
834
+ const { id, method, params } = msg
835
+
836
+ switch (method) {
837
+ case "initialize":
838
+ return makeResponse(id, {
839
+ protocolVersion: "2024-11-05",
840
+ capabilities: { tools: {} },
841
+ serverInfo: SERVER_INFO,
842
+ })
843
+
844
+ case "notifications/initialized":
845
+ return null // no response for notifications
846
+
847
+ case "tools/list":
848
+ return makeResponse(id, { tools: TOOLS })
849
+
850
+ case "tools/call": {
851
+ const toolName = params?.name
852
+ const toolArgs = params?.arguments || {}
853
+ try {
854
+ const result = handleToolCall(toolName, toolArgs)
855
+ return makeResponse(id, {
856
+ content: [{ type: "text", text: result }],
857
+ })
858
+ } catch (err) {
859
+ return makeResponse(id, {
860
+ content: [{ type: "text", text: `Error: ${err.message}` }],
861
+ isError: true,
862
+ })
863
+ }
864
+ }
865
+
866
+ case "ping":
867
+ return makeResponse(id, {})
868
+
869
+ default:
870
+ if (id != null) {
871
+ return makeError(id, -32601, `Method not found: ${method}`)
872
+ }
873
+ return null // ignore unknown notifications
874
+ }
875
+ }
876
+
877
+ // ─── Main ──────────────────────────────────────────────────────────────────────
878
+
879
+ const rl = createInterface({ input: process.stdin, terminal: false })
880
+
881
+ let buffer = ""
882
+
883
+ process.stdin.on("data", (chunk) => {
884
+ buffer += chunk.toString()
885
+
886
+ // Try to parse complete JSON-RPC messages
887
+ while (buffer.length > 0) {
888
+ // Look for Content-Length header (LSP-style framing)
889
+ const headerMatch = buffer.match(/^Content-Length:\s*(\d+)\r?\n\r?\n/)
890
+ if (headerMatch) {
891
+ const contentLength = parseInt(headerMatch[1], 10)
892
+ const headerLength = headerMatch[0].length
893
+ if (buffer.length >= headerLength + contentLength) {
894
+ const body = buffer.slice(headerLength, headerLength + contentLength)
895
+ buffer = buffer.slice(headerLength + contentLength)
896
+ try {
897
+ const msg = JSON.parse(body)
898
+ const response = handleMessage(msg)
899
+ if (response) {
900
+ const responseBytes = Buffer.byteLength(response, "utf-8")
901
+ process.stdout.write(`Content-Length: ${responseBytes}\r\n\r\n${response}`)
902
+ }
903
+ } catch {
904
+ // skip malformed messages
905
+ }
906
+ continue
907
+ }
908
+ }
909
+
910
+ // Try plain newline-delimited JSON as fallback
911
+ const newlineIdx = buffer.indexOf("\n")
912
+ if (newlineIdx !== -1) {
913
+ const line = buffer.slice(0, newlineIdx).trim()
914
+ buffer = buffer.slice(newlineIdx + 1)
915
+ if (line.startsWith("{")) {
916
+ try {
917
+ const msg = JSON.parse(line)
918
+ const response = handleMessage(msg)
919
+ if (response) {
920
+ process.stdout.write(response + "\n")
921
+ }
922
+ } catch {
923
+ // skip
924
+ }
925
+ }
926
+ continue
927
+ }
928
+
929
+ break // need more data
930
+ }
931
+ })
932
+
933
+ rl.on("close", () => process.exit(0))
@@ -120,6 +120,21 @@ Todos importados de `@fluencypassdevs/cycle`:
120
120
  | ProductLogo, ClassLogo, PrivateTalkLogo, GroupTalkLogo | Logos dos produtos |
121
121
  | CycleIcon | Wrapper para icones Lucide |
122
122
 
123
+ ## MCP Server (Claude Code)
124
+
125
+ Se o projeto tem `.mcp.json` configurado, o Claude Code tera acesso a tools do Cycle:
126
+
127
+ - `cycle_search_component` — buscar componente por nome/keyword
128
+ - `cycle_get_component` — detalhes completos com props e exemplos
129
+ - `cycle_get_icon_usage` — regras de uso de icones
130
+ - `cycle_get_setup` — instrucoes de setup
131
+ - `cycle_get_tokens` — tokens de design disponiveis
132
+ - `cycle_get_rules` — regras obrigatorias
133
+
134
+ **USE estas tools antes de criar qualquer UI.** Elas sao a fonte de verdade.
135
+
136
+ Para configurar: `npx cycle init`
137
+
123
138
  ## Documentacao
124
139
 
125
140
  - **Componentes**: https://cycle-design.fluencypass.com/docs/components
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluencypassdevs/cycle",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "description": "Cycle Design System — UI component library by Fluencypass",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -51,7 +51,7 @@
51
51
  "./tailwind-theme.css": "./dist/styles/tokens.css"
52
52
  },
53
53
  "bin": {
54
- "cycle": "./bin/init.mjs"
54
+ "cycle": "./bin/cli.mjs"
55
55
  },
56
56
  "files": [
57
57
  "dist",