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.
Files changed (81) hide show
  1. package/README.md +275 -60
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +3999 -0
  4. package/install.bat +9 -0
  5. package/package.json +71 -115
  6. package/src/channels/discord.ts +5 -0
  7. package/src/channels/slack.ts +5 -0
  8. package/src/channels/sms.ts +4 -0
  9. package/src/channels/telegram.ts +5 -0
  10. package/src/channels/whatsapp.ts +7 -0
  11. package/src/commands/check.ts +365 -0
  12. package/src/commands/code.ts +684 -0
  13. package/src/commands/connect.ts +12 -0
  14. package/src/commands/deploy.ts +414 -0
  15. package/src/commands/fix.ts +20 -0
  16. package/src/commands/gateway.ts +604 -0
  17. package/src/commands/generate.ts +178 -0
  18. package/src/commands/init.ts +574 -0
  19. package/src/commands/learn.ts +36 -0
  20. package/src/commands/security.ts +102 -0
  21. package/src/commands/setup.ts +448 -0
  22. package/src/commands/status.ts +56 -0
  23. package/src/index.ts +268 -0
  24. package/src/patterns/loader.ts +337 -0
  25. package/src/services/github.ts +61 -0
  26. package/src/services/supabase.ts +147 -0
  27. package/src/services/vercel.ts +61 -0
  28. package/src/utils/claude-md.ts +287 -0
  29. package/src/utils/config.ts +282 -0
  30. package/src/utils/updates.ts +27 -0
  31. package/tsconfig.json +17 -10
  32. package/.vscodeignore +0 -18
  33. package/LICENSE +0 -21
  34. package/codebakers-1.0.0.vsix +0 -0
  35. package/codebakers-1.0.10.vsix +0 -0
  36. package/codebakers-1.0.11.vsix +0 -0
  37. package/codebakers-1.0.12.vsix +0 -0
  38. package/codebakers-1.0.13.vsix +0 -0
  39. package/codebakers-1.0.14.vsix +0 -0
  40. package/codebakers-1.0.15.vsix +0 -0
  41. package/codebakers-1.0.16.vsix +0 -0
  42. package/codebakers-1.0.17.vsix +0 -0
  43. package/codebakers-1.0.18.vsix +0 -0
  44. package/codebakers-1.0.19.vsix +0 -0
  45. package/codebakers-1.0.20.vsix +0 -0
  46. package/codebakers-1.0.21.vsix +0 -0
  47. package/codebakers-1.0.22.vsix +0 -0
  48. package/codebakers-1.0.23.vsix +0 -0
  49. package/codebakers-1.0.24.vsix +0 -0
  50. package/codebakers-1.0.25.vsix +0 -0
  51. package/codebakers-1.0.26.vsix +0 -0
  52. package/codebakers-1.0.27.vsix +0 -0
  53. package/codebakers-1.0.28.vsix +0 -0
  54. package/codebakers-1.0.29.vsix +0 -0
  55. package/codebakers-1.0.30.vsix +0 -0
  56. package/codebakers-1.0.31.vsix +0 -0
  57. package/codebakers-1.0.32.vsix +0 -0
  58. package/codebakers-1.0.35.vsix +0 -0
  59. package/codebakers-1.0.36.vsix +0 -0
  60. package/codebakers-1.0.37.vsix +0 -0
  61. package/codebakers-1.0.38.vsix +0 -0
  62. package/codebakers-1.0.39.vsix +0 -0
  63. package/codebakers-1.0.40.vsix +0 -0
  64. package/codebakers-1.0.41.vsix +0 -0
  65. package/codebakers-1.0.42.vsix +0 -0
  66. package/codebakers-1.0.43.vsix +0 -0
  67. package/codebakers-1.0.44.vsix +0 -0
  68. package/codebakers-1.0.45.vsix +0 -0
  69. package/dist/extension.js +0 -1394
  70. package/esbuild.js +0 -63
  71. package/media/icon.png +0 -0
  72. package/media/icon.svg +0 -7
  73. package/nul +0 -1
  74. package/preview.html +0 -547
  75. package/src/ChatPanelProvider.ts +0 -1815
  76. package/src/ChatViewProvider.ts +0 -749
  77. package/src/CodeBakersClient.ts +0 -1146
  78. package/src/CodeValidator.ts +0 -645
  79. package/src/FileOperations.ts +0 -410
  80. package/src/ProjectContext.ts +0 -526
  81. package/src/extension.ts +0 -332
package/src/index.ts ADDED
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import * as p from '@clack/prompts';
5
+ import chalk from 'chalk';
6
+ import boxen from 'boxen';
7
+ import gradient from 'gradient-string';
8
+ import { Config } from './utils/config.js';
9
+ import { checkForUpdates } from './utils/updates.js';
10
+ import { setupCommand } from './commands/setup.js';
11
+ import { initCommand } from './commands/init.js';
12
+ import { codeCommand } from './commands/code.js';
13
+ import { checkCommand } from './commands/check.js';
14
+ import { deployCommand } from './commands/deploy.js';
15
+ import { connectCommand } from './commands/connect.js';
16
+ import { statusCommand } from './commands/status.js';
17
+ import { gatewayCommand } from './commands/gateway.js';
18
+ import { learnCommand } from './commands/learn.js';
19
+ import { securityCommand } from './commands/security.js';
20
+ import { generateCommand } from './commands/generate.js';
21
+ import { fixCommand } from './commands/fix.js';
22
+
23
+ const VERSION = '1.0.0';
24
+
25
+ // ASCII art logo
26
+ const logo = `
27
+ ██████╗ ██████╗ ██████╗ ███████╗██████╗ █████╗ ██╗ ██╗███████╗██████╗ ███████╗
28
+ ██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗██╔══██╗██║ ██╔╝██╔════╝██╔══██╗██╔════╝
29
+ ██║ ██║ ██║██║ ██║█████╗ ██████╔╝███████║█████╔╝ █████╗ ██████╔╝███████╗
30
+ ██║ ██║ ██║██║ ██║██╔══╝ ██╔══██╗██╔══██║██╔═██╗ ██╔══╝ ██╔══██╗╚════██║
31
+ ╚██████╗╚██████╔╝██████╔╝███████╗██████╔╝██║ ██║██║ ██╗███████╗██║ ██║███████║
32
+ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝
33
+ `;
34
+
35
+ // Main interactive menu
36
+ async function showMainMenu(): Promise<void> {
37
+ const config = new Config();
38
+ const isSetup = config.isConfigured();
39
+
40
+ // Show logo
41
+ console.log(gradient.pastel.multiline(logo));
42
+ console.log(chalk.dim(` v${VERSION} — AI dev team that follows the rules\n`));
43
+
44
+ // If not setup, run setup wizard
45
+ if (!isSetup) {
46
+ console.log(boxen(
47
+ chalk.yellow('Welcome to CodeBakers! Let\'s get you set up.'),
48
+ { padding: 1, borderColor: 'yellow', borderStyle: 'round' }
49
+ ));
50
+ await setupCommand();
51
+ return;
52
+ }
53
+
54
+ // Check if in a project directory
55
+ const inProject = config.isInProject();
56
+
57
+ // Main menu
58
+ const action = await p.select({
59
+ message: 'What do you want to do?',
60
+ options: inProject ? [
61
+ { value: 'code', label: '💬 Code with AI', hint: 'build features, fix bugs' },
62
+ { value: 'check', label: '🔍 Check code quality', hint: 'run pattern enforcement' },
63
+ { value: 'deploy', label: '🚀 Deploy', hint: 'deploy to Vercel' },
64
+ { value: 'fix', label: '🔧 Fix errors', hint: 'auto-fix build/deploy errors' },
65
+ { value: 'generate', label: '⚡ Generate', hint: 'scaffold components, pages' },
66
+ { value: 'status', label: '📊 Project status', hint: 'view project health' },
67
+ { value: 'separator1', label: '─────────────────────────' },
68
+ { value: 'connect', label: '🔗 Connect service', hint: 'add integrations' },
69
+ { value: 'gateway', label: '📱 Channel gateway', hint: 'WhatsApp, Telegram, etc.' },
70
+ { value: 'learn', label: '🧠 Learning settings', hint: 'view what I\'ve learned' },
71
+ { value: 'security', label: '🔒 Security audit', hint: 'check for vulnerabilities' },
72
+ { value: 'separator2', label: '─────────────────────────' },
73
+ { value: 'new', label: '🆕 Create new project' },
74
+ { value: 'settings', label: '⚙️ Settings' },
75
+ { value: 'help', label: '❓ Help' },
76
+ ] : [
77
+ { value: 'new', label: '🆕 Create new project' },
78
+ { value: 'open', label: '📂 Open existing project' },
79
+ { value: 'status', label: '📊 View all projects' },
80
+ { value: 'separator1', label: '─────────────────────────' },
81
+ { value: 'connect', label: '🔗 Connect service' },
82
+ { value: 'gateway', label: '📱 Channel gateway', hint: 'WhatsApp, Telegram, etc.' },
83
+ { value: 'learn', label: '🧠 Learning settings' },
84
+ { value: 'settings', label: '⚙️ Settings' },
85
+ { value: 'help', label: '❓ Help' },
86
+ ]
87
+ });
88
+
89
+ if (p.isCancel(action)) {
90
+ p.cancel('Goodbye!');
91
+ process.exit(0);
92
+ }
93
+
94
+ // Handle actions
95
+ switch (action) {
96
+ case 'code':
97
+ await codeCommand();
98
+ break;
99
+ case 'check':
100
+ await checkCommand();
101
+ break;
102
+ case 'deploy':
103
+ await deployCommand();
104
+ break;
105
+ case 'fix':
106
+ await fixCommand();
107
+ break;
108
+ case 'generate':
109
+ await generateCommand();
110
+ break;
111
+ case 'status':
112
+ await statusCommand();
113
+ break;
114
+ case 'connect':
115
+ await connectCommand();
116
+ break;
117
+ case 'gateway':
118
+ await gatewayCommand();
119
+ break;
120
+ case 'learn':
121
+ await learnCommand();
122
+ break;
123
+ case 'security':
124
+ await securityCommand();
125
+ break;
126
+ case 'new':
127
+ await initCommand();
128
+ break;
129
+ case 'settings':
130
+ await setupCommand();
131
+ break;
132
+ case 'help':
133
+ showHelp();
134
+ break;
135
+ default:
136
+ // Separators, do nothing
137
+ await showMainMenu();
138
+ }
139
+ }
140
+
141
+ function showHelp(): void {
142
+ console.log(boxen(`
143
+ ${chalk.bold('CodeBakers CLI')} — AI dev team that follows the rules
144
+
145
+ ${chalk.bold('Commands:')}
146
+ ${chalk.cyan('codebakers')} Interactive menu (or just run with no args)
147
+ ${chalk.cyan('codebakers init')} Create a new project
148
+ ${chalk.cyan('codebakers code')} Start AI coding session
149
+ ${chalk.cyan('codebakers check')} Run pattern enforcement
150
+ ${chalk.cyan('codebakers deploy')} Deploy to production
151
+ ${chalk.cyan('codebakers fix')} Auto-fix errors
152
+ ${chalk.cyan('codebakers generate')} Generate components/pages
153
+ ${chalk.cyan('codebakers connect')} Connect external services
154
+ ${chalk.cyan('codebakers gateway')} Manage messaging channels
155
+ ${chalk.cyan('codebakers status')} View project status
156
+ ${chalk.cyan('codebakers security')} Run security audit
157
+ ${chalk.cyan('codebakers learn')} View/manage learning
158
+
159
+ ${chalk.bold('Help at any time:')}
160
+ Press ${chalk.yellow('?')} during any command to get contextual help
161
+
162
+ ${chalk.bold('Documentation:')}
163
+ ${chalk.dim('https://codebakers.dev/docs')}
164
+ `, { padding: 1, borderColor: 'cyan', borderStyle: 'round' }));
165
+ }
166
+
167
+ // CLI setup with Commander
168
+ const program = new Command();
169
+
170
+ program
171
+ .name('codebakers')
172
+ .description('AI dev team that follows the rules')
173
+ .version(VERSION)
174
+ .action(showMainMenu);
175
+
176
+ program
177
+ .command('setup')
178
+ .description('Configure CodeBakers (first-time setup)')
179
+ .action(setupCommand);
180
+
181
+ program
182
+ .command('init')
183
+ .description('Create a new project')
184
+ .option('-n, --name <name>', 'Project name')
185
+ .option('-t, --template <template>', 'Template (nextjs, voice-agent, saas)')
186
+ .option('--no-git', 'Skip GitHub repo creation')
187
+ .option('--no-vercel', 'Skip Vercel deployment')
188
+ .option('--no-supabase', 'Skip Supabase setup')
189
+ .action(initCommand);
190
+
191
+ program
192
+ .command('code [prompt]')
193
+ .description('Start AI coding session')
194
+ .option('-w, --watch', 'Watch mode - continuous assistance')
195
+ .action(codeCommand);
196
+
197
+ program
198
+ .command('check')
199
+ .description('Run CodeBakers pattern enforcement')
200
+ .option('-f, --fix', 'Auto-fix violations')
201
+ .option('--watch', 'Watch mode')
202
+ .action(checkCommand);
203
+
204
+ program
205
+ .command('deploy')
206
+ .description('Deploy to production')
207
+ .option('-p, --preview', 'Deploy to preview only')
208
+ .option('--no-check', 'Skip pattern check')
209
+ .action(deployCommand);
210
+
211
+ program
212
+ .command('fix')
213
+ .description('Auto-fix build and deployment errors')
214
+ .option('--auto', 'Fix without asking')
215
+ .action(fixCommand);
216
+
217
+ program
218
+ .command('generate <type>')
219
+ .alias('g')
220
+ .description('Generate components, pages, API routes')
221
+ .option('-n, --name <name>', 'Name of the generated item')
222
+ .action(generateCommand);
223
+
224
+ program
225
+ .command('connect [service]')
226
+ .description('Connect external services (Stripe, VAPI, etc.)')
227
+ .action(connectCommand);
228
+
229
+ program
230
+ .command('gateway')
231
+ .description('Manage messaging channel gateway')
232
+ .option('--start', 'Start the gateway')
233
+ .option('--stop', 'Stop the gateway')
234
+ .option('--status', 'Show gateway status')
235
+ .action(gatewayCommand);
236
+
237
+ program
238
+ .command('status')
239
+ .description('View project status and health')
240
+ .option('-a, --all', 'Show all projects')
241
+ .action(statusCommand);
242
+
243
+ program
244
+ .command('security')
245
+ .description('Run security audit')
246
+ .option('--fix', 'Auto-fix security issues')
247
+ .action(securityCommand);
248
+
249
+ program
250
+ .command('learn')
251
+ .description('View and manage learning settings')
252
+ .option('--forget <item>', 'Forget a learned preference')
253
+ .option('--reset', 'Reset all learning')
254
+ .option('--export', 'Export learned patterns')
255
+ .action(learnCommand);
256
+
257
+ // Parse args or show menu
258
+ const args = process.argv.slice(2);
259
+
260
+ if (args.length === 0) {
261
+ // Check for updates in background
262
+ checkForUpdates().catch(() => {});
263
+
264
+ // Show interactive menu
265
+ showMainMenu().catch(console.error);
266
+ } else {
267
+ program.parse();
268
+ }
@@ -0,0 +1,337 @@
1
+ import * as fs from 'fs-extra';
2
+ import * as path from 'path';
3
+ import { Config } from '../utils/config.js';
4
+
5
+ // Embedded core patterns (extracted from your CLAUDE.md)
6
+ const CORE_PATTERNS = `
7
+ # CODEBAKERS CORE PATTERNS
8
+
9
+ ## ABSOLUTE PROHIBITIONS
10
+
11
+ These will NEVER appear in your code:
12
+
13
+ \`\`\`typescript
14
+ // ❌ BANNED - NON-FUNCTIONAL CODE
15
+ onClick={handleClick} // where handleClick doesn't exist
16
+ onSubmit={handleSubmit} // where handleSubmit doesn't exist
17
+ href="/some-page" // where the page doesn't exist
18
+
19
+ // ❌ BANNED - INCOMPLETE CODE
20
+ TODO: // No TODOs ever
21
+ FIXME: // No FIXMEs ever
22
+ // ... // No placeholder comments
23
+ throw new Error('Not implemented')
24
+
25
+ // ❌ BANNED - DEBUG CODE
26
+ console.log('test') // No debug logs
27
+ debugger; // No debugger statements
28
+
29
+ // ❌ BANNED - TYPE SAFETY VIOLATIONS
30
+ any // No 'any' types
31
+ @ts-ignore // No ignoring TypeScript
32
+ as any // No casting to any
33
+
34
+ // ❌ BANNED - SECURITY VIOLATIONS
35
+ eval() // No eval ever
36
+ innerHTML = // No direct innerHTML
37
+ dangerouslySetInnerHTML // Without DOMPurify
38
+ \`\`\`
39
+
40
+ ## MANDATORY PATTERNS
41
+
42
+ ### Every Button MUST Have:
43
+
44
+ \`\`\`typescript
45
+ <Button
46
+ onClick={handleAction}
47
+ disabled={isLoading || isDisabled}
48
+ aria-label="Descriptive action name"
49
+ >
50
+ {isLoading ? (
51
+ <>
52
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
53
+ Processing...
54
+ </>
55
+ ) : (
56
+ 'Action Name'
57
+ )}
58
+ </Button>
59
+
60
+ // The handler MUST exist and be complete:
61
+ const handleAction = async () => {
62
+ setIsLoading(true);
63
+ try {
64
+ await performAction();
65
+ toast.success('Action completed successfully');
66
+ } catch (error) {
67
+ const message = error instanceof Error ? error.message : 'Action failed';
68
+ toast.error(message);
69
+ } finally {
70
+ setIsLoading(false);
71
+ }
72
+ };
73
+ \`\`\`
74
+
75
+ ### Every Form MUST Have:
76
+
77
+ 1. React Hook Form with Zod validation
78
+ 2. Loading state during submission
79
+ 3. Error messages for each field
80
+ 4. Success/error toast notifications
81
+ 5. Proper disabled state while submitting
82
+
83
+ ### Every Async Operation MUST Have:
84
+
85
+ \`\`\`typescript
86
+ const [data, setData] = useState<DataType | null>(null);
87
+ const [isLoading, setIsLoading] = useState(false);
88
+ const [error, setError] = useState<string | null>(null);
89
+
90
+ const fetchData = async () => {
91
+ setIsLoading(true);
92
+ setError(null);
93
+
94
+ try {
95
+ const response = await fetch('/api/data');
96
+ if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
97
+ const result = await response.json();
98
+ setData(result.data);
99
+ } catch (err) {
100
+ const message = err instanceof Error ? err.message : 'Failed to fetch';
101
+ setError(message);
102
+ toast.error(message);
103
+ } finally {
104
+ setIsLoading(false);
105
+ }
106
+ };
107
+ \`\`\`
108
+
109
+ ### Every List MUST Have:
110
+
111
+ 1. Loading state with spinner/skeleton
112
+ 2. Error state with retry button
113
+ 3. Empty state with helpful message
114
+ 4. Success state with data
115
+
116
+ ## FILE STRUCTURE
117
+
118
+ \`\`\`
119
+ src/
120
+ ├── app/ # Next.js App Router
121
+ │ ├── (auth)/ # Routes requiring authentication
122
+ │ ├── (public)/ # Public routes
123
+ │ ├── (marketing)/ # Marketing pages
124
+ │ ├── api/ # API routes
125
+ │ └── layout.tsx # Root layout
126
+ ├── components/
127
+ │ ├── ui/ # shadcn/ui components
128
+ │ ├── forms/ # Form components
129
+ │ ├── layouts/ # Layout components
130
+ │ └── [feature]/ # Feature-specific components
131
+ ├── lib/
132
+ │ ├── supabase/ # Supabase clients
133
+ │ ├── utils.ts # Utility functions
134
+ │ └── validations.ts # Zod schemas
135
+ ├── hooks/ # Custom React hooks
136
+ ├── types/ # TypeScript types
137
+ ├── stores/ # Zustand stores
138
+ └── services/ # Business logic
139
+ \`\`\`
140
+
141
+ ## API ROUTE PATTERN
142
+
143
+ \`\`\`typescript
144
+ import { NextRequest, NextResponse } from 'next/server';
145
+ import { z } from 'zod';
146
+ import { createServerClient } from '@/lib/supabase/server';
147
+
148
+ const requestSchema = z.object({
149
+ // Define request body schema
150
+ });
151
+
152
+ export async function POST(request: NextRequest) {
153
+ try {
154
+ // 1. Auth check
155
+ const supabase = createServerClient();
156
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
157
+
158
+ if (authError || !user) {
159
+ return NextResponse.json(
160
+ { error: 'Unauthorized' },
161
+ { status: 401 }
162
+ );
163
+ }
164
+
165
+ // 2. Validate input
166
+ const body = await request.json();
167
+ const result = requestSchema.safeParse(body);
168
+
169
+ if (!result.success) {
170
+ return NextResponse.json(
171
+ { error: 'Validation failed', details: result.error.flatten() },
172
+ { status: 400 }
173
+ );
174
+ }
175
+
176
+ // 3. Business logic
177
+ const data = result.data;
178
+ // ... do something
179
+
180
+ // 4. Return success
181
+ return NextResponse.json({ data }, { status: 201 });
182
+
183
+ } catch (error) {
184
+ console.error('[API] Error:', error);
185
+ return NextResponse.json(
186
+ { error: 'Internal server error' },
187
+ { status: 500 }
188
+ );
189
+ }
190
+ }
191
+ \`\`\`
192
+
193
+ ## SUPABASE RLS PATTERN
194
+
195
+ Every table MUST have Row Level Security enabled:
196
+
197
+ \`\`\`sql
198
+ -- Enable RLS
199
+ ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
200
+
201
+ -- User can only see their own data
202
+ CREATE POLICY "Users can view own data"
203
+ ON your_table FOR SELECT
204
+ TO authenticated
205
+ USING (user_id = auth.uid());
206
+
207
+ -- User can only insert their own data
208
+ CREATE POLICY "Users can insert own data"
209
+ ON your_table FOR INSERT
210
+ TO authenticated
211
+ WITH CHECK (user_id = auth.uid());
212
+
213
+ -- User can only update their own data
214
+ CREATE POLICY "Users can update own data"
215
+ ON your_table FOR UPDATE
216
+ TO authenticated
217
+ USING (user_id = auth.uid())
218
+ WITH CHECK (user_id = auth.uid());
219
+
220
+ -- User can only delete their own data
221
+ CREATE POLICY "Users can delete own data"
222
+ ON your_table FOR DELETE
223
+ TO authenticated
224
+ USING (user_id = auth.uid());
225
+ \`\`\`
226
+
227
+ ## STATE MANAGEMENT
228
+
229
+ Use Zustand for global state:
230
+
231
+ \`\`\`typescript
232
+ import { create } from 'zustand';
233
+
234
+ interface AppState {
235
+ user: User | null;
236
+ isLoading: boolean;
237
+ setUser: (user: User | null) => void;
238
+ reset: () => void;
239
+ }
240
+
241
+ export const useAppStore = create<AppState>((set) => ({
242
+ user: null,
243
+ isLoading: false,
244
+ setUser: (user) => set({ user }),
245
+ reset: () => set({ user: null, isLoading: false }),
246
+ }));
247
+ \`\`\`
248
+
249
+ ## ERROR HANDLING
250
+
251
+ Use typed error classes:
252
+
253
+ \`\`\`typescript
254
+ export class AppError extends Error {
255
+ constructor(
256
+ message: string,
257
+ public code: string,
258
+ public statusCode: number = 500
259
+ ) {
260
+ super(message);
261
+ }
262
+ }
263
+
264
+ export class AuthenticationError extends AppError {
265
+ constructor(message = 'Authentication required') {
266
+ super(message, 'AUTH_ERROR', 401);
267
+ }
268
+ }
269
+
270
+ export class ValidationError extends AppError {
271
+ constructor(message = 'Validation failed', public details?: Record<string, string[]>) {
272
+ super(message, 'VALIDATION_ERROR', 400);
273
+ }
274
+ }
275
+ \`\`\`
276
+ `;
277
+
278
+ export async function loadPatterns(config: Config): Promise<string> {
279
+ const projectDir = process.cwd();
280
+ const patterns: string[] = [CORE_PATTERNS];
281
+
282
+ // Load project-specific patterns if they exist
283
+ const projectPatternDir = path.join(projectDir, '.codebakers', 'patterns');
284
+ if (await fs.pathExists(projectPatternDir)) {
285
+ const files = await fs.readdir(projectPatternDir);
286
+ for (const file of files) {
287
+ if (file.endsWith('.md')) {
288
+ const content = await fs.readFile(path.join(projectPatternDir, file), 'utf-8');
289
+ patterns.push(content);
290
+ }
291
+ }
292
+ }
293
+
294
+ // Load global patterns
295
+ const globalPatternDir = config.getPatternsDir();
296
+ if (await fs.pathExists(globalPatternDir)) {
297
+ const files = await fs.readdir(globalPatternDir);
298
+ for (const file of files.slice(0, 10)) { // Limit to avoid context overflow
299
+ if (file.endsWith('.md')) {
300
+ const content = await fs.readFile(path.join(globalPatternDir, file), 'utf-8');
301
+ // Truncate if too long
302
+ patterns.push(content.slice(0, 10000));
303
+ }
304
+ }
305
+ }
306
+
307
+ return patterns.join('\n\n---\n\n');
308
+ }
309
+
310
+ export async function validateAgainstPatterns(
311
+ code: string,
312
+ patterns: string
313
+ ): Promise<{ valid: boolean; violations: string[] }> {
314
+ const violations: string[] = [];
315
+
316
+ // Check for banned patterns
317
+ if (code.includes(': any') && !code.includes('// @allow-any')) {
318
+ violations.push('Use of "any" type detected');
319
+ }
320
+ if (code.includes('@ts-ignore')) {
321
+ violations.push('@ts-ignore detected');
322
+ }
323
+ if (/console\.log\(/.test(code)) {
324
+ violations.push('console.log detected');
325
+ }
326
+ if (/TODO:|FIXME:/.test(code)) {
327
+ violations.push('TODO/FIXME comment detected');
328
+ }
329
+ if (/eval\s*\(/.test(code)) {
330
+ violations.push('eval() detected - security risk');
331
+ }
332
+
333
+ return {
334
+ valid: violations.length === 0,
335
+ violations,
336
+ };
337
+ }
@@ -0,0 +1,61 @@
1
+ import { Octokit } from '@octokit/rest';
2
+ import { Config } from '../utils/config.js';
3
+
4
+ export class GitHubService {
5
+ private octokit: Octokit;
6
+ private config: Config;
7
+
8
+ constructor(config: Config) {
9
+ this.config = config;
10
+ const creds = config.getCredentials('github');
11
+
12
+ if (!creds?.token) {
13
+ throw new Error('GitHub not configured. Run `codebakers setup`.');
14
+ }
15
+
16
+ this.octokit = new Octokit({ auth: creds.token });
17
+ }
18
+
19
+ async createRepo(name: string, options: { private?: boolean } = {}): Promise<{
20
+ id: number;
21
+ name: string;
22
+ full_name: string;
23
+ html_url: string;
24
+ clone_url: string;
25
+ }> {
26
+ const response = await this.octokit.repos.createForAuthenticatedUser({
27
+ name,
28
+ private: options.private ?? true,
29
+ auto_init: false,
30
+ });
31
+
32
+ return {
33
+ id: response.data.id,
34
+ name: response.data.name,
35
+ full_name: response.data.full_name,
36
+ html_url: response.data.html_url,
37
+ clone_url: response.data.clone_url,
38
+ };
39
+ }
40
+
41
+ async getUser(): Promise<{ login: string; name: string | null }> {
42
+ const response = await this.octokit.users.getAuthenticated();
43
+ return {
44
+ login: response.data.login,
45
+ name: response.data.name,
46
+ };
47
+ }
48
+
49
+ async listRepos(): Promise<Array<{ name: string; full_name: string; private: boolean }>> {
50
+ const response = await this.octokit.repos.listForAuthenticatedUser({
51
+ sort: 'updated',
52
+ per_page: 100,
53
+ });
54
+
55
+ return response.data.map(repo => ({
56
+ name: repo.name,
57
+ full_name: repo.full_name,
58
+ private: repo.private,
59
+ }));
60
+ }
61
+ }