codebakers 2.5.3 → 3.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 +54 -255
- package/dist/chunk-HOWR3YTF.js +146 -0
- package/dist/index.d.ts +0 -3
- package/dist/index.js +10489 -7994
- package/dist/terminal-6ZQVP6R7.js +10 -0
- package/package.json +26 -41
- package/AUDIT_REPORT.md +0 -138
- package/dist/advisors-RWRTSJRR.js +0 -7
- package/dist/chunk-ASIJIQYC.js +0 -320
- package/dist/chunk-D44U3IEA.js +0 -565
- package/dist/chunk-LANM5XQW.js +0 -326
- package/dist/prd-RYITSL6Q.js +0 -7
- package/install.bat +0 -9
- package/installers/CodeBakers-Install.bat +0 -207
- package/installers/CodeBakers-Install.command +0 -232
- package/installers/README.md +0 -157
- package/installers/mac/assets/README.txt +0 -31
- package/installers/mac/build-mac-installer.sh +0 -240
- package/installers/windows/CodeBakers.iss +0 -256
- package/installers/windows/assets/README.txt +0 -16
- package/installers/windows/scripts/post-install.bat +0 -15
- package/src/channels/discord.ts +0 -5
- package/src/channels/slack.ts +0 -5
- package/src/channels/sms.ts +0 -4
- package/src/channels/telegram.ts +0 -5
- package/src/channels/whatsapp.ts +0 -7
- package/src/commands/advisors.ts +0 -699
- package/src/commands/build.ts +0 -1025
- package/src/commands/check.ts +0 -365
- package/src/commands/code.ts +0 -806
- package/src/commands/connect.ts +0 -12
- package/src/commands/deploy.ts +0 -448
- package/src/commands/design.ts +0 -298
- package/src/commands/fix.ts +0 -20
- package/src/commands/gateway.ts +0 -604
- package/src/commands/generate.ts +0 -178
- package/src/commands/init.ts +0 -634
- package/src/commands/integrate.ts +0 -884
- package/src/commands/learn.ts +0 -36
- package/src/commands/migrate.ts +0 -419
- package/src/commands/prd-maker.ts +0 -588
- package/src/commands/prd.ts +0 -419
- package/src/commands/security.ts +0 -102
- package/src/commands/setup.ts +0 -600
- package/src/commands/status.ts +0 -56
- package/src/commands/website.ts +0 -741
- package/src/index.ts +0 -627
- package/src/patterns/loader.ts +0 -337
- package/src/services/github.ts +0 -61
- package/src/services/supabase.ts +0 -147
- package/src/services/vercel.ts +0 -61
- package/src/utils/claude-md.ts +0 -287
- package/src/utils/config.ts +0 -375
- package/src/utils/display.ts +0 -338
- package/src/utils/files.ts +0 -418
- package/src/utils/nlp.ts +0 -312
- package/src/utils/ui.ts +0 -441
- package/src/utils/updates.ts +0 -8
- package/src/utils/voice.ts +0 -323
- package/tsconfig.json +0 -26
package/src/utils/claude-md.ts
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
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
|
-
}
|
package/src/utils/config.ts
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import Conf from 'conf';
|
|
2
|
-
import fsExtra from 'fs-extra';
|
|
3
|
-
import { existsSync, readFileSync, appendFileSync } from 'fs';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import os from 'os';
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
|
|
8
|
-
// Schema for config validation
|
|
9
|
-
const ConfigSchema = z.object({
|
|
10
|
-
version: z.string().default('1.0.0'),
|
|
11
|
-
credentials: z.object({
|
|
12
|
-
github: z.object({
|
|
13
|
-
token: z.string().optional(),
|
|
14
|
-
username: z.string().optional(),
|
|
15
|
-
}).optional(),
|
|
16
|
-
vercel: z.object({
|
|
17
|
-
token: z.string().optional(),
|
|
18
|
-
teamId: z.string().optional(),
|
|
19
|
-
}).optional(),
|
|
20
|
-
supabase: z.object({
|
|
21
|
-
accessToken: z.string().optional(),
|
|
22
|
-
orgId: z.string().optional(),
|
|
23
|
-
}).optional(),
|
|
24
|
-
anthropic: z.object({
|
|
25
|
-
apiKey: z.string().optional(),
|
|
26
|
-
}).optional(),
|
|
27
|
-
openai: z.object({
|
|
28
|
-
apiKey: z.string().optional(),
|
|
29
|
-
}).optional(),
|
|
30
|
-
stripe: z.object({
|
|
31
|
-
secretKey: z.string().optional(),
|
|
32
|
-
publishableKey: z.string().optional(),
|
|
33
|
-
webhookSecret: z.string().optional(),
|
|
34
|
-
}).optional(),
|
|
35
|
-
twilio: z.object({
|
|
36
|
-
accountSid: z.string().optional(),
|
|
37
|
-
authToken: z.string().optional(),
|
|
38
|
-
phoneNumber: z.string().optional(),
|
|
39
|
-
}).optional(),
|
|
40
|
-
vapi: z.object({
|
|
41
|
-
apiKey: z.string().optional(),
|
|
42
|
-
}).optional(),
|
|
43
|
-
resend: z.object({
|
|
44
|
-
apiKey: z.string().optional(),
|
|
45
|
-
}).optional(),
|
|
46
|
-
elevenLabs: z.object({
|
|
47
|
-
apiKey: z.string().optional(),
|
|
48
|
-
}).optional(),
|
|
49
|
-
microsoft: z.object({
|
|
50
|
-
clientId: z.string().optional(),
|
|
51
|
-
clientSecret: z.string().optional(),
|
|
52
|
-
tenantId: z.string().optional(),
|
|
53
|
-
}).optional(),
|
|
54
|
-
google: z.object({
|
|
55
|
-
clientId: z.string().optional(),
|
|
56
|
-
clientSecret: z.string().optional(),
|
|
57
|
-
}).optional(),
|
|
58
|
-
}).default({}),
|
|
59
|
-
preferences: z.object({
|
|
60
|
-
defaultFramework: z.string().optional(),
|
|
61
|
-
defaultUI: z.string().optional(),
|
|
62
|
-
defaultPackages: z.array(z.string()).optional(),
|
|
63
|
-
deployToPreviewFirst: z.boolean().default(true),
|
|
64
|
-
}).default({}),
|
|
65
|
-
learning: z.object({
|
|
66
|
-
enabled: z.boolean().default(true),
|
|
67
|
-
shortcuts: z.record(z.string()).default({}),
|
|
68
|
-
preferences: z.record(z.any()).default({}),
|
|
69
|
-
rejections: z.array(z.string()).default([]),
|
|
70
|
-
workflows: z.record(z.array(z.string())).default({}),
|
|
71
|
-
}).default({}),
|
|
72
|
-
projects: z.array(z.object({
|
|
73
|
-
name: z.string(),
|
|
74
|
-
path: z.string(),
|
|
75
|
-
github: z.string().optional(),
|
|
76
|
-
vercel: z.string().optional(),
|
|
77
|
-
supabase: z.string().optional(),
|
|
78
|
-
createdAt: z.string(),
|
|
79
|
-
})).default([]),
|
|
80
|
-
channels: z.object({
|
|
81
|
-
whatsapp: z.object({
|
|
82
|
-
enabled: z.boolean().default(false),
|
|
83
|
-
phoneNumber: z.string().optional(),
|
|
84
|
-
}).optional(),
|
|
85
|
-
telegram: z.object({
|
|
86
|
-
enabled: z.boolean().default(false),
|
|
87
|
-
botToken: z.string().optional(),
|
|
88
|
-
}).optional(),
|
|
89
|
-
discord: z.object({
|
|
90
|
-
enabled: z.boolean().default(false),
|
|
91
|
-
botToken: z.string().optional(),
|
|
92
|
-
}).optional(),
|
|
93
|
-
slack: z.object({
|
|
94
|
-
enabled: z.boolean().default(false),
|
|
95
|
-
botToken: z.string().optional(),
|
|
96
|
-
}).optional(),
|
|
97
|
-
}).default({}),
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
type ConfigType = z.infer<typeof ConfigSchema>;
|
|
101
|
-
|
|
102
|
-
export class Config {
|
|
103
|
-
private conf: Conf<ConfigType>;
|
|
104
|
-
private configDir: string;
|
|
105
|
-
|
|
106
|
-
constructor() {
|
|
107
|
-
this.configDir = path.join(os.homedir(), '.codebakers');
|
|
108
|
-
|
|
109
|
-
// Ensure config directory exists
|
|
110
|
-
fsExtra.ensureDirSync(this.configDir);
|
|
111
|
-
fsExtra.ensureDirSync(path.join(this.configDir, 'patterns'));
|
|
112
|
-
fsExtra.ensureDirSync(path.join(this.configDir, 'templates'));
|
|
113
|
-
fsExtra.ensureDirSync(path.join(this.configDir, 'learning'));
|
|
114
|
-
|
|
115
|
-
this.conf = new Conf<ConfigType>({
|
|
116
|
-
projectName: 'codebakers',
|
|
117
|
-
cwd: this.configDir,
|
|
118
|
-
schema: {
|
|
119
|
-
version: { type: 'string', default: '1.0.0' },
|
|
120
|
-
credentials: { type: 'object', default: {} },
|
|
121
|
-
preferences: { type: 'object', default: {} },
|
|
122
|
-
learning: { type: 'object', default: { enabled: true, shortcuts: {}, preferences: {}, rejections: [], workflows: {} } },
|
|
123
|
-
projects: { type: 'array', default: [] },
|
|
124
|
-
channels: { type: 'object', default: {} },
|
|
125
|
-
} as any,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Check if initial setup is complete
|
|
130
|
-
isConfigured(): boolean {
|
|
131
|
-
const creds = this.conf.get('credentials') || {};
|
|
132
|
-
return !!(
|
|
133
|
-
creds.github?.token &&
|
|
134
|
-
creds.vercel?.token &&
|
|
135
|
-
creds.supabase?.accessToken &&
|
|
136
|
-
creds.anthropic?.apiKey
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Check if current directory is a CodeBakers project
|
|
141
|
-
isInProject(): boolean {
|
|
142
|
-
const cwd = process.cwd();
|
|
143
|
-
const codebakersDir = path.join(cwd, '.codebakers');
|
|
144
|
-
const claudeFile = path.join(cwd, 'CLAUDE.md');
|
|
145
|
-
|
|
146
|
-
// Already a CodeBakers project
|
|
147
|
-
if (existsSync(codebakersDir) || existsSync(claudeFile)) {
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Auto-detect existing projects by common files
|
|
152
|
-
const projectIndicators = [
|
|
153
|
-
'package.json',
|
|
154
|
-
'next.config.js',
|
|
155
|
-
'next.config.mjs',
|
|
156
|
-
'next.config.ts',
|
|
157
|
-
'vite.config.ts',
|
|
158
|
-
'vite.config.js',
|
|
159
|
-
'remix.config.js',
|
|
160
|
-
'astro.config.mjs',
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
const hasProjectFile = projectIndicators.some(file =>
|
|
164
|
-
existsSync(path.join(cwd, file))
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
// If it's a project but no .codebakers, auto-initialize
|
|
168
|
-
if (hasProjectFile) {
|
|
169
|
-
this.autoInitExisting(cwd);
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Auto-initialize CodeBakers in an existing project
|
|
177
|
-
private autoInitExisting(cwd: string): void {
|
|
178
|
-
try {
|
|
179
|
-
// Detect framework
|
|
180
|
-
let framework = 'unknown';
|
|
181
|
-
let ui = 'unknown';
|
|
182
|
-
|
|
183
|
-
if (existsSync(path.join(cwd, 'next.config.js')) ||
|
|
184
|
-
existsSync(path.join(cwd, 'next.config.mjs')) ||
|
|
185
|
-
existsSync(path.join(cwd, 'next.config.ts'))) {
|
|
186
|
-
framework = 'nextjs';
|
|
187
|
-
} else if (existsSync(path.join(cwd, 'vite.config.ts')) ||
|
|
188
|
-
existsSync(path.join(cwd, 'vite.config.js'))) {
|
|
189
|
-
framework = 'vite';
|
|
190
|
-
} else if (existsSync(path.join(cwd, 'remix.config.js'))) {
|
|
191
|
-
framework = 'remix';
|
|
192
|
-
} else if (existsSync(path.join(cwd, 'astro.config.mjs'))) {
|
|
193
|
-
framework = 'astro';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Check for UI libraries in package.json
|
|
197
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
198
|
-
if (existsSync(pkgPath)) {
|
|
199
|
-
const pkg = fsExtra.readJsonSync(pkgPath);
|
|
200
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
201
|
-
|
|
202
|
-
if (deps['@shadcn/ui'] || deps['class-variance-authority']) {
|
|
203
|
-
ui = 'shadcn';
|
|
204
|
-
} else if (deps['@chakra-ui/react']) {
|
|
205
|
-
ui = 'chakra';
|
|
206
|
-
} else if (deps['@mantine/core']) {
|
|
207
|
-
ui = 'mantine';
|
|
208
|
-
} else if (deps['@mui/material']) {
|
|
209
|
-
ui = 'mui';
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Create .codebakers config
|
|
214
|
-
const configDir = path.join(cwd, '.codebakers');
|
|
215
|
-
fsExtra.ensureDirSync(configDir);
|
|
216
|
-
|
|
217
|
-
const config = {
|
|
218
|
-
name: path.basename(cwd),
|
|
219
|
-
framework,
|
|
220
|
-
ui,
|
|
221
|
-
createdAt: new Date().toISOString(),
|
|
222
|
-
autoDetected: true,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
fsExtra.writeJsonSync(path.join(configDir, 'config.json'), config, { spaces: 2 });
|
|
226
|
-
|
|
227
|
-
// Add to .gitignore if exists
|
|
228
|
-
const gitignorePath = path.join(cwd, '.gitignore');
|
|
229
|
-
if (existsSync(gitignorePath)) {
|
|
230
|
-
const gitignore = readFileSync(gitignorePath, 'utf-8');
|
|
231
|
-
if (!gitignore.includes('.codebakers')) {
|
|
232
|
-
appendFileSync(gitignorePath, '\n# CodeBakers\n.codebakers/\n');
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
} catch {
|
|
236
|
-
// Silent fail - don't block if auto-init fails
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Get current project config
|
|
241
|
-
getProjectConfig(): Record<string, unknown> | null {
|
|
242
|
-
const cwd = process.cwd();
|
|
243
|
-
const configPath = path.join(cwd, '.codebakers', 'config.json');
|
|
244
|
-
if (existsSync(configPath)) {
|
|
245
|
-
return fsExtra.readJsonSync(configPath);
|
|
246
|
-
}
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Credentials
|
|
251
|
-
getCredentials(service: string): Record<string, string | undefined> | undefined {
|
|
252
|
-
const creds = this.conf.get('credentials') || {};
|
|
253
|
-
return creds[service as keyof typeof creds];
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
setCredentials(service: string, credentials: Record<string, string>): void {
|
|
257
|
-
const current = this.conf.get('credentials') || {};
|
|
258
|
-
this.conf.set('credentials', {
|
|
259
|
-
...current,
|
|
260
|
-
[service]: { ...current[service as keyof typeof current], ...credentials },
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Preferences
|
|
265
|
-
getPreference<T>(key: string): T | undefined {
|
|
266
|
-
const prefs = this.conf.get('preferences') || {};
|
|
267
|
-
return prefs[key as keyof typeof prefs] as T;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
setPreference(key: string, value: unknown): void {
|
|
271
|
-
const current = this.conf.get('preferences') || {};
|
|
272
|
-
this.conf.set('preferences', { ...current, [key]: value });
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Learning
|
|
276
|
-
getLearning(): ConfigType['learning'] {
|
|
277
|
-
return this.conf.get('learning') || { enabled: true, shortcuts: {}, preferences: {}, rejections: [], workflows: {} };
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
addShortcut(shortcut: string, expansion: string): void {
|
|
281
|
-
const learning = this.getLearning();
|
|
282
|
-
learning.shortcuts[shortcut] = expansion;
|
|
283
|
-
this.conf.set('learning', learning);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
addRejection(item: string): void {
|
|
287
|
-
const learning = this.getLearning();
|
|
288
|
-
if (!learning.rejections.includes(item)) {
|
|
289
|
-
learning.rejections.push(item);
|
|
290
|
-
this.conf.set('learning', learning);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
addWorkflow(name: string, steps: string[]): void {
|
|
295
|
-
const learning = this.getLearning();
|
|
296
|
-
learning.workflows[name] = steps;
|
|
297
|
-
this.conf.set('learning', learning);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
learnPreference(key: string, value: unknown): void {
|
|
301
|
-
const learning = this.getLearning();
|
|
302
|
-
learning.preferences[key] = value;
|
|
303
|
-
this.conf.set('learning', learning);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
forgetPreference(key: string): void {
|
|
307
|
-
const learning = this.getLearning();
|
|
308
|
-
delete learning.preferences[key];
|
|
309
|
-
this.conf.set('learning', learning);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
resetLearning(): void {
|
|
313
|
-
this.conf.set('learning', { enabled: true, shortcuts: {}, preferences: {}, rejections: [], workflows: {} });
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Projects
|
|
317
|
-
getProjects(): ConfigType['projects'] {
|
|
318
|
-
return this.conf.get('projects') || [];
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
addProject(project: ConfigType['projects'][0]): void {
|
|
322
|
-
const projects = this.getProjects();
|
|
323
|
-
projects.push(project);
|
|
324
|
-
this.conf.set('projects', projects);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Channels
|
|
328
|
-
getChannelConfig(channel: string): Record<string, unknown> | undefined {
|
|
329
|
-
const channels = this.conf.get('channels') || {};
|
|
330
|
-
return channels[channel as keyof typeof channels];
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
setChannelConfig(channel: string, config: Record<string, unknown>): void {
|
|
334
|
-
const current = this.conf.get('channels') || {};
|
|
335
|
-
this.conf.set('channels', { ...current, [channel]: config });
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Config directory access
|
|
339
|
-
getConfigDir(): string {
|
|
340
|
-
return this.configDir;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
getPatternsDir(): string {
|
|
344
|
-
return path.join(this.configDir, 'patterns');
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
getTemplatesDir(): string {
|
|
348
|
-
return path.join(this.configDir, 'templates');
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
getLearningDir(): string {
|
|
352
|
-
return path.join(this.configDir, 'learning');
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Export/Import
|
|
356
|
-
exportConfig(): ConfigType {
|
|
357
|
-
return {
|
|
358
|
-
version: this.conf.get('version') || '1.0.0',
|
|
359
|
-
credentials: {}, // Don't export credentials
|
|
360
|
-
preferences: this.conf.get('preferences') || {},
|
|
361
|
-
learning: this.getLearning(),
|
|
362
|
-
projects: this.getProjects(),
|
|
363
|
-
channels: {}, // Don't export channel tokens
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
importConfig(config: Partial<ConfigType>): void {
|
|
368
|
-
if (config.preferences) {
|
|
369
|
-
this.conf.set('preferences', config.preferences);
|
|
370
|
-
}
|
|
371
|
-
if (config.learning) {
|
|
372
|
-
this.conf.set('learning', config.learning);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|