codebakers 1.0.45 โ 2.0.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.
- package/README.md +275 -60
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3999 -0
- package/install.bat +9 -0
- package/package.json +71 -115
- package/src/channels/discord.ts +5 -0
- package/src/channels/slack.ts +5 -0
- package/src/channels/sms.ts +4 -0
- package/src/channels/telegram.ts +5 -0
- package/src/channels/whatsapp.ts +7 -0
- package/src/commands/check.ts +365 -0
- package/src/commands/code.ts +684 -0
- package/src/commands/connect.ts +12 -0
- package/src/commands/deploy.ts +414 -0
- package/src/commands/fix.ts +20 -0
- package/src/commands/gateway.ts +604 -0
- package/src/commands/generate.ts +178 -0
- package/src/commands/init.ts +574 -0
- package/src/commands/learn.ts +36 -0
- package/src/commands/security.ts +102 -0
- package/src/commands/setup.ts +448 -0
- package/src/commands/status.ts +56 -0
- package/src/index.ts +268 -0
- package/src/patterns/loader.ts +337 -0
- package/src/services/github.ts +61 -0
- package/src/services/supabase.ts +147 -0
- package/src/services/vercel.ts +61 -0
- package/src/utils/claude-md.ts +287 -0
- package/src/utils/config.ts +282 -0
- package/src/utils/updates.ts +27 -0
- package/tsconfig.json +17 -10
- package/.vscodeignore +0 -18
- package/LICENSE +0 -21
- package/codebakers-1.0.0.vsix +0 -0
- package/codebakers-1.0.10.vsix +0 -0
- package/codebakers-1.0.11.vsix +0 -0
- package/codebakers-1.0.12.vsix +0 -0
- package/codebakers-1.0.13.vsix +0 -0
- package/codebakers-1.0.14.vsix +0 -0
- package/codebakers-1.0.15.vsix +0 -0
- package/codebakers-1.0.16.vsix +0 -0
- package/codebakers-1.0.17.vsix +0 -0
- package/codebakers-1.0.18.vsix +0 -0
- package/codebakers-1.0.19.vsix +0 -0
- package/codebakers-1.0.20.vsix +0 -0
- package/codebakers-1.0.21.vsix +0 -0
- package/codebakers-1.0.22.vsix +0 -0
- package/codebakers-1.0.23.vsix +0 -0
- package/codebakers-1.0.24.vsix +0 -0
- package/codebakers-1.0.25.vsix +0 -0
- package/codebakers-1.0.26.vsix +0 -0
- package/codebakers-1.0.27.vsix +0 -0
- package/codebakers-1.0.28.vsix +0 -0
- package/codebakers-1.0.29.vsix +0 -0
- package/codebakers-1.0.30.vsix +0 -0
- package/codebakers-1.0.31.vsix +0 -0
- package/codebakers-1.0.32.vsix +0 -0
- package/codebakers-1.0.35.vsix +0 -0
- package/codebakers-1.0.36.vsix +0 -0
- package/codebakers-1.0.37.vsix +0 -0
- package/codebakers-1.0.38.vsix +0 -0
- package/codebakers-1.0.39.vsix +0 -0
- package/codebakers-1.0.40.vsix +0 -0
- package/codebakers-1.0.41.vsix +0 -0
- package/codebakers-1.0.42.vsix +0 -0
- package/codebakers-1.0.43.vsix +0 -0
- package/codebakers-1.0.44.vsix +0 -0
- package/codebakers-1.0.45.vsix +0 -0
- package/dist/extension.js +0 -1394
- package/esbuild.js +0 -63
- package/media/icon.png +0 -0
- package/media/icon.svg +0 -7
- package/nul +0 -1
- package/preview.html +0 -547
- package/src/ChatPanelProvider.ts +0 -1815
- package/src/ChatViewProvider.ts +0 -749
- package/src/CodeBakersClient.ts +0 -1146
- package/src/CodeValidator.ts +0 -645
- package/src/FileOperations.ts +0 -410
- package/src/ProjectContext.ts +0 -526
- package/src/extension.ts +0 -332
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
|
|
3
|
+
export class SupabaseService {
|
|
4
|
+
private config: Config;
|
|
5
|
+
private accessToken: string;
|
|
6
|
+
|
|
7
|
+
constructor(config: Config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
const creds = config.getCredentials('supabase');
|
|
10
|
+
|
|
11
|
+
if (!creds?.accessToken) {
|
|
12
|
+
throw new Error('Supabase not configured. Run `codebakers setup`.');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
this.accessToken = creds.accessToken;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async createProject(name: string): Promise<{
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
api_url: string;
|
|
22
|
+
anon_key: string;
|
|
23
|
+
}> {
|
|
24
|
+
// Generate a random password for the database
|
|
25
|
+
const dbPassword = this.generatePassword();
|
|
26
|
+
|
|
27
|
+
const response = await fetch('https://api.supabase.com/v1/projects', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: `Bearer ${this.accessToken}`,
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
name,
|
|
35
|
+
organization_id: await this.getDefaultOrgId(),
|
|
36
|
+
region: 'us-west-1',
|
|
37
|
+
plan: 'free',
|
|
38
|
+
db_pass: dbPassword,
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const error = await response.json();
|
|
44
|
+
throw new Error(error.message || 'Failed to create Supabase project');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
|
|
49
|
+
// Wait for project to be ready
|
|
50
|
+
await this.waitForProject(data.id);
|
|
51
|
+
|
|
52
|
+
// Get API keys
|
|
53
|
+
const keys = await this.getApiKeys(data.id);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
id: data.id,
|
|
57
|
+
name: data.name,
|
|
58
|
+
api_url: `https://${data.id}.supabase.co`,
|
|
59
|
+
anon_key: keys.anon_key,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async getDefaultOrgId(): Promise<string> {
|
|
64
|
+
const response = await fetch('https://api.supabase.com/v1/organizations', {
|
|
65
|
+
headers: { Authorization: `Bearer ${this.accessToken}` },
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error('Failed to get organizations');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const orgs = await response.json();
|
|
73
|
+
if (orgs.length === 0) {
|
|
74
|
+
throw new Error('No Supabase organization found');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return orgs[0].id;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async waitForProject(projectId: string, maxWait = 120000): Promise<void> {
|
|
81
|
+
const startTime = Date.now();
|
|
82
|
+
|
|
83
|
+
while (Date.now() - startTime < maxWait) {
|
|
84
|
+
const response = await fetch(
|
|
85
|
+
`https://api.supabase.com/v1/projects/${projectId}`,
|
|
86
|
+
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (response.ok) {
|
|
90
|
+
const project = await response.json();
|
|
91
|
+
if (project.status === 'ACTIVE_HEALTHY') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
throw new Error('Timeout waiting for Supabase project');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private async getApiKeys(projectId: string): Promise<{ anon_key: string; service_role_key: string }> {
|
|
103
|
+
const response = await fetch(
|
|
104
|
+
`https://api.supabase.com/v1/projects/${projectId}/api-keys`,
|
|
105
|
+
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
throw new Error('Failed to get API keys');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const keys = await response.json();
|
|
113
|
+
const anonKey = keys.find((k: any) => k.name === 'anon');
|
|
114
|
+
const serviceKey = keys.find((k: any) => k.name === 'service_role');
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
anon_key: anonKey?.api_key || '',
|
|
118
|
+
service_role_key: serviceKey?.api_key || '',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private generatePassword(): string {
|
|
123
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%';
|
|
124
|
+
let password = '';
|
|
125
|
+
for (let i = 0; i < 24; i++) {
|
|
126
|
+
password += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
127
|
+
}
|
|
128
|
+
return password;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async listProjects(): Promise<Array<{ id: string; name: string; region: string }>> {
|
|
132
|
+
const response = await fetch('https://api.supabase.com/v1/projects', {
|
|
133
|
+
headers: { Authorization: `Bearer ${this.accessToken}` },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error('Failed to list projects');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const projects = await response.json();
|
|
141
|
+
return projects.map((p: any) => ({
|
|
142
|
+
id: p.id,
|
|
143
|
+
name: p.name,
|
|
144
|
+
region: p.region,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import { Config } from '../utils/config.js';
|
|
3
|
+
|
|
4
|
+
export class VercelService {
|
|
5
|
+
private config: Config;
|
|
6
|
+
private token: string;
|
|
7
|
+
|
|
8
|
+
constructor(config: Config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
const creds = config.getCredentials('vercel');
|
|
11
|
+
|
|
12
|
+
if (!creds?.token) {
|
|
13
|
+
throw new Error('Vercel not configured. Run `codebakers setup`.');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.token = creds.token;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async createProject(name: string): Promise<{ id: string; name: string }> {
|
|
20
|
+
const response = await fetch('https://api.vercel.com/v9/projects', {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${this.token}`,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({ name, framework: 'nextjs' }),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const error = await response.json();
|
|
31
|
+
throw new Error(error.error?.message || 'Failed to create project');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
return { id: data.id, name: data.name };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async deploy(projectPath: string, production = false): Promise<{ url: string }> {
|
|
39
|
+
const args = ['vercel', '--yes'];
|
|
40
|
+
if (production) args.push('--prod');
|
|
41
|
+
|
|
42
|
+
const result = await execa('npx', args, {
|
|
43
|
+
cwd: projectPath,
|
|
44
|
+
env: { ...process.env, VERCEL_TOKEN: this.token },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const urlMatch = result.stdout.match(/https:\/\/[^\s]+\.vercel\.app/);
|
|
48
|
+
return { url: urlMatch ? urlMatch[0] : 'https://vercel.app' };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async addDomain(projectName: string, domain: string): Promise<void> {
|
|
52
|
+
await fetch(`https://api.vercel.com/v10/projects/${projectName}/domains`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Bearer ${this.token}`,
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify({ name: domain }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
interface ProjectConfig {
|
|
2
|
+
name: string;
|
|
3
|
+
type: unknown;
|
|
4
|
+
framework: string;
|
|
5
|
+
ui: unknown;
|
|
6
|
+
packages: unknown;
|
|
7
|
+
services: unknown;
|
|
8
|
+
domain?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function generateClaudeMd(config: ProjectConfig): string {
|
|
12
|
+
return `# ${config.name.toUpperCase()} - CLAUDE.md
|
|
13
|
+
|
|
14
|
+
Generated by CodeBakers CLI
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# CORE DEVELOPMENT STANDARDS
|
|
19
|
+
|
|
20
|
+
## ๐ง MANDATORY THINKING PROTOCOL
|
|
21
|
+
|
|
22
|
+
**BEFORE WRITING ANY CODE**, complete this mental checklist:
|
|
23
|
+
|
|
24
|
+
\`\`\`
|
|
25
|
+
โข What is the user actually asking for?
|
|
26
|
+
โข What are ALL the components needed (UI, API, DB, types)?
|
|
27
|
+
โข What are the edge cases?
|
|
28
|
+
โข What error handling is required?
|
|
29
|
+
โข What loading/empty states are needed?
|
|
30
|
+
โข How does this integrate with existing code?
|
|
31
|
+
โข What security implications exist?
|
|
32
|
+
โข What could break in production?
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
**NEVER skip this step.**
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ๐ซ ABSOLUTE PROHIBITIONS
|
|
40
|
+
|
|
41
|
+
These will NEVER appear in your code under ANY circumstances:
|
|
42
|
+
|
|
43
|
+
\`\`\`typescript
|
|
44
|
+
// โ BANNED FOREVER - NON-FUNCTIONAL CODE
|
|
45
|
+
onClick={handleClick} // where handleClick doesn't exist
|
|
46
|
+
onSubmit={handleSubmit} // where handleSubmit doesn't exist
|
|
47
|
+
href="/some-page" // where the page doesn't exist
|
|
48
|
+
|
|
49
|
+
// โ BANNED FOREVER - INCOMPLETE CODE
|
|
50
|
+
TODO: // No TODOs ever
|
|
51
|
+
FIXME: // No FIXMEs ever
|
|
52
|
+
// ... // No placeholder comments
|
|
53
|
+
throw new Error('Not implemented')
|
|
54
|
+
|
|
55
|
+
// โ BANNED FOREVER - DEBUG CODE
|
|
56
|
+
console.log('test') // No debug logs in final code
|
|
57
|
+
debugger; // No debugger statements
|
|
58
|
+
|
|
59
|
+
// โ BANNED FOREVER - TYPE SAFETY VIOLATIONS
|
|
60
|
+
any // No 'any' types
|
|
61
|
+
@ts-ignore // No ignoring TypeScript
|
|
62
|
+
as any // No casting to any
|
|
63
|
+
|
|
64
|
+
// โ BANNED FOREVER - SECURITY VIOLATIONS
|
|
65
|
+
eval() // No eval ever
|
|
66
|
+
innerHTML = // No direct innerHTML
|
|
67
|
+
dangerouslySetInnerHTML // Without DOMPurify
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## โ
MANDATORY PATTERNS
|
|
73
|
+
|
|
74
|
+
### Every Button MUST Have:
|
|
75
|
+
|
|
76
|
+
\`\`\`typescript
|
|
77
|
+
// โ
CORRECT - Fully functional button
|
|
78
|
+
<Button
|
|
79
|
+
onClick={handleAction}
|
|
80
|
+
disabled={isLoading || isDisabled}
|
|
81
|
+
aria-label="Descriptive action name"
|
|
82
|
+
aria-busy={isLoading}
|
|
83
|
+
>
|
|
84
|
+
{isLoading ? (
|
|
85
|
+
<>
|
|
86
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
87
|
+
Processing...
|
|
88
|
+
</>
|
|
89
|
+
) : (
|
|
90
|
+
'Action Name'
|
|
91
|
+
)}
|
|
92
|
+
</Button>
|
|
93
|
+
|
|
94
|
+
// The handler MUST exist and be complete:
|
|
95
|
+
const handleAction = async () => {
|
|
96
|
+
setIsLoading(true);
|
|
97
|
+
try {
|
|
98
|
+
await performAction();
|
|
99
|
+
toast.success('Action completed successfully');
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const message = error instanceof Error ? error.message : 'Action failed';
|
|
102
|
+
toast.error(message);
|
|
103
|
+
} finally {
|
|
104
|
+
setIsLoading(false);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
### Every Form MUST Have:
|
|
110
|
+
|
|
111
|
+
\`\`\`typescript
|
|
112
|
+
'use client';
|
|
113
|
+
|
|
114
|
+
import { useState } from 'react';
|
|
115
|
+
import { z } from 'zod';
|
|
116
|
+
import { useForm } from 'react-hook-form';
|
|
117
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
118
|
+
import { toast } from 'sonner';
|
|
119
|
+
|
|
120
|
+
const formSchema = z.object({
|
|
121
|
+
email: z.string().email('Please enter a valid email'),
|
|
122
|
+
name: z.string().min(1, 'Name is required').max(100),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
type FormData = z.infer<typeof formSchema>;
|
|
126
|
+
|
|
127
|
+
export function ContactForm({ onSuccess }: { onSuccess?: () => void }) {
|
|
128
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
129
|
+
|
|
130
|
+
const form = useForm<FormData>({
|
|
131
|
+
resolver: zodResolver(formSchema),
|
|
132
|
+
defaultValues: { email: '', name: '' },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const onSubmit = async (data: FormData) => {
|
|
136
|
+
setIsSubmitting(true);
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch('/api/contact', {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify(data),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
const error = await response.json();
|
|
146
|
+
throw new Error(error.message || 'Failed to send');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
toast.success('Message sent!');
|
|
150
|
+
form.reset();
|
|
151
|
+
onSuccess?.();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
const message = error instanceof Error ? error.message : 'Something went wrong';
|
|
154
|
+
toast.error(message);
|
|
155
|
+
} finally {
|
|
156
|
+
setIsSubmitting(false);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<Form {...form}>
|
|
162
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
|
163
|
+
{/* Form fields with FormField, FormLabel, FormControl, FormMessage */}
|
|
164
|
+
<Button type="submit" disabled={isSubmitting}>
|
|
165
|
+
{isSubmitting ? 'Sending...' : 'Send Message'}
|
|
166
|
+
</Button>
|
|
167
|
+
</form>
|
|
168
|
+
</Form>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
\`\`\`
|
|
172
|
+
|
|
173
|
+
### Every List MUST Have:
|
|
174
|
+
|
|
175
|
+
\`\`\`typescript
|
|
176
|
+
interface ItemListProps {
|
|
177
|
+
items: Item[];
|
|
178
|
+
isLoading: boolean;
|
|
179
|
+
error: string | null;
|
|
180
|
+
onRetry?: () => void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function ItemList({ items, isLoading, error, onRetry }: ItemListProps) {
|
|
184
|
+
// Loading state
|
|
185
|
+
if (isLoading) {
|
|
186
|
+
return (
|
|
187
|
+
<div className="flex items-center justify-center p-8">
|
|
188
|
+
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Error state
|
|
194
|
+
if (error) {
|
|
195
|
+
return (
|
|
196
|
+
<div className="flex flex-col items-center gap-4 p-8 text-center">
|
|
197
|
+
<AlertCircle className="h-12 w-12 text-destructive" />
|
|
198
|
+
<p className="text-destructive">{error}</p>
|
|
199
|
+
{onRetry && <Button variant="outline" onClick={onRetry}>Try Again</Button>}
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Empty state
|
|
205
|
+
if (items.length === 0) {
|
|
206
|
+
return (
|
|
207
|
+
<div className="flex flex-col items-center gap-4 p-8 text-center">
|
|
208
|
+
<Package className="h-12 w-12 text-muted-foreground" />
|
|
209
|
+
<div>
|
|
210
|
+
<h3 className="font-medium">No items found</h3>
|
|
211
|
+
<p className="text-sm text-muted-foreground">Get started by creating your first item.</p>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Success state with data
|
|
218
|
+
return (
|
|
219
|
+
<ul className="divide-y divide-border">
|
|
220
|
+
{items.map((item) => (
|
|
221
|
+
<li key={item.id} className="p-4">
|
|
222
|
+
{item.name}
|
|
223
|
+
</li>
|
|
224
|
+
))}
|
|
225
|
+
</ul>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
\`\`\`
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## ๐ PROJECT STRUCTURE
|
|
233
|
+
|
|
234
|
+
\`\`\`
|
|
235
|
+
${config.name}/
|
|
236
|
+
โโโ src/
|
|
237
|
+
โ โโโ app/ # Next.js App Router
|
|
238
|
+
โ โ โโโ (auth)/ # Routes requiring authentication
|
|
239
|
+
โ โ โโโ (public)/ # Public routes
|
|
240
|
+
โ โ โโโ api/ # API routes
|
|
241
|
+
โ โ โโโ layout.tsx # Root layout
|
|
242
|
+
โ โโโ components/
|
|
243
|
+
โ โ โโโ ui/ # shadcn/ui components
|
|
244
|
+
โ โ โโโ forms/ # Form components
|
|
245
|
+
โ โ โโโ [feature]/ # Feature-specific
|
|
246
|
+
โ โโโ lib/
|
|
247
|
+
โ โ โโโ supabase/ # Supabase clients
|
|
248
|
+
โ โ โโโ utils.ts # Utilities
|
|
249
|
+
โ โ โโโ validations.ts # Zod schemas
|
|
250
|
+
โ โโโ hooks/ # Custom hooks
|
|
251
|
+
โ โโโ types/ # TypeScript types
|
|
252
|
+
โ โโโ stores/ # Zustand stores
|
|
253
|
+
โ โโโ services/ # Business logic
|
|
254
|
+
โโโ tests/ # Playwright tests
|
|
255
|
+
โโโ .codebakers/ # CodeBakers config
|
|
256
|
+
โโโ CLAUDE.md # This file
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## ๐ SECURITY REQUIREMENTS
|
|
262
|
+
|
|
263
|
+
1. **Never expose secrets** - Use environment variables
|
|
264
|
+
2. **Validate all inputs** - Use Zod on both client and server
|
|
265
|
+
3. **Enable RLS** - Every Supabase table must have Row Level Security
|
|
266
|
+
4. **Sanitize user content** - Use DOMPurify for any user HTML
|
|
267
|
+
5. **Use HTTPS** - Never use HTTP in production
|
|
268
|
+
6. **Rate limit APIs** - Protect public endpoints
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## ๐งช TESTING REQUIREMENTS
|
|
273
|
+
|
|
274
|
+
After building ANY feature, automatically test it:
|
|
275
|
+
|
|
276
|
+
1. Run \`codebakers check\` to verify patterns
|
|
277
|
+
2. Check TypeScript: \`npx tsc --noEmit\`
|
|
278
|
+
3. Run tests: \`npm test\`
|
|
279
|
+
4. Fix any failures before saying "done"
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
**END OF CLAUDE.md**
|
|
284
|
+
|
|
285
|
+
Generated by CodeBakers CLI v1.0.0
|
|
286
|
+
`;
|
|
287
|
+
}
|