codebakers 2.5.4 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +54 -255
  2. package/dist/chunk-HOWR3YTF.js +146 -0
  3. package/dist/index.d.ts +0 -3
  4. package/dist/index.js +10505 -7997
  5. package/dist/terminal-6ZQVP6R7.js +10 -0
  6. package/package.json +26 -41
  7. package/AUDIT_REPORT.md +0 -138
  8. package/dist/advisors-RWRTSJRR.js +0 -7
  9. package/dist/chunk-ASIJIQYC.js +0 -320
  10. package/dist/chunk-D44U3IEA.js +0 -565
  11. package/dist/chunk-LANM5XQW.js +0 -326
  12. package/dist/prd-RYITSL6Q.js +0 -7
  13. package/install.bat +0 -9
  14. package/installers/CodeBakers-Install.bat +0 -207
  15. package/installers/CodeBakers-Install.command +0 -232
  16. package/installers/README.md +0 -157
  17. package/installers/mac/assets/README.txt +0 -31
  18. package/installers/mac/build-mac-installer.sh +0 -240
  19. package/installers/windows/CodeBakers.iss +0 -256
  20. package/installers/windows/assets/README.txt +0 -16
  21. package/installers/windows/scripts/post-install.bat +0 -15
  22. package/src/channels/discord.ts +0 -5
  23. package/src/channels/slack.ts +0 -5
  24. package/src/channels/sms.ts +0 -4
  25. package/src/channels/telegram.ts +0 -5
  26. package/src/channels/whatsapp.ts +0 -7
  27. package/src/commands/advisors.ts +0 -699
  28. package/src/commands/build.ts +0 -1025
  29. package/src/commands/check.ts +0 -365
  30. package/src/commands/code.ts +0 -806
  31. package/src/commands/connect.ts +0 -12
  32. package/src/commands/deploy.ts +0 -448
  33. package/src/commands/design.ts +0 -298
  34. package/src/commands/fix.ts +0 -20
  35. package/src/commands/gateway.ts +0 -604
  36. package/src/commands/generate.ts +0 -178
  37. package/src/commands/init.ts +0 -634
  38. package/src/commands/integrate.ts +0 -884
  39. package/src/commands/learn.ts +0 -36
  40. package/src/commands/migrate.ts +0 -419
  41. package/src/commands/prd-maker.ts +0 -588
  42. package/src/commands/prd.ts +0 -419
  43. package/src/commands/security.ts +0 -102
  44. package/src/commands/setup.ts +0 -600
  45. package/src/commands/status.ts +0 -56
  46. package/src/commands/website.ts +0 -741
  47. package/src/index.ts +0 -627
  48. package/src/patterns/loader.ts +0 -337
  49. package/src/services/github.ts +0 -61
  50. package/src/services/supabase.ts +0 -147
  51. package/src/services/vercel.ts +0 -61
  52. package/src/utils/claude-md.ts +0 -287
  53. package/src/utils/config.ts +0 -375
  54. package/src/utils/display.ts +0 -338
  55. package/src/utils/files.ts +0 -418
  56. package/src/utils/nlp.ts +0 -312
  57. package/src/utils/ui.ts +0 -441
  58. package/src/utils/updates.ts +0 -8
  59. package/src/utils/voice.ts +0 -323
  60. package/tsconfig.json +0 -26
@@ -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
- }
@@ -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
- }