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
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
|
+
}
|