@codebakers/cli 1.4.5 → 1.5.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/dist/commands/install-hook.js +7 -5
- package/dist/commands/mcp-config.js +7 -5
- package/dist/commands/provision.d.ts +51 -0
- package/dist/commands/provision.js +309 -0
- package/dist/commands/scaffold.js +30 -0
- package/dist/commands/setup.js +14 -6
- package/dist/config.d.ts +11 -0
- package/dist/config.js +26 -0
- package/dist/index.js +1 -1
- package/dist/mcp/server.js +48 -0
- package/package.json +1 -1
- package/src/commands/install-hook.ts +7 -5
- package/src/commands/mcp-config.ts +7 -5
- package/src/commands/provision.ts +380 -0
- package/src/commands/scaffold.ts +36 -0
- package/src/commands/setup.ts +17 -6
- package/src/config.ts +36 -0
- package/src/index.ts +1 -1
- package/src/mcp/server.ts +53 -0
|
@@ -100,9 +100,11 @@ async function installHook() {
|
|
|
100
100
|
console.log(chalk_1.default.gray(' ✓ Pre-flight checks before writing code'));
|
|
101
101
|
console.log(chalk_1.default.gray(' ✓ Self-review reminders after code changes'));
|
|
102
102
|
console.log(chalk_1.default.gray(' ✓ Pattern-based development from .claude/ folder\n'));
|
|
103
|
-
console.log(chalk_1.default.yellow(' ⚠️
|
|
104
|
-
console.log(chalk_1.default.
|
|
105
|
-
console.log(chalk_1.default.gray('
|
|
103
|
+
console.log(chalk_1.default.yellow.bold(' ⚠️ RESTART REQUIRED:\n'));
|
|
104
|
+
console.log(chalk_1.default.gray(' 1. Type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' to close this terminal'));
|
|
105
|
+
console.log(chalk_1.default.gray(' 2. Open a NEW terminal window'));
|
|
106
|
+
console.log(chalk_1.default.gray(' 3. Navigate to your project and run ') + chalk_1.default.cyan('claude\n'));
|
|
107
|
+
console.log(chalk_1.default.white(' To verify it\'s working, ask: ') + chalk_1.default.green('"Are you using CodeBakers?"\n'));
|
|
106
108
|
}
|
|
107
109
|
catch (error) {
|
|
108
110
|
spinner.fail('Hook installation failed');
|
|
@@ -141,8 +143,8 @@ async function uninstallHook() {
|
|
|
141
143
|
}
|
|
142
144
|
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2));
|
|
143
145
|
spinner.succeed('Hook removed successfully!');
|
|
144
|
-
console.log(chalk_1.default.yellow('\n ⚠️
|
|
145
|
-
console.log(chalk_1.default.
|
|
146
|
+
console.log(chalk_1.default.yellow('\n ⚠️ RESTART REQUIRED:\n'));
|
|
147
|
+
console.log(chalk_1.default.gray(' Type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' and open a new terminal to apply changes.\n'));
|
|
146
148
|
}
|
|
147
149
|
catch (error) {
|
|
148
150
|
spinner.fail('Hook removal failed');
|
|
@@ -132,9 +132,11 @@ function installGlobalConfig() {
|
|
|
132
132
|
console.log(chalk_1.default.green(' MCP configuration installed!\n'));
|
|
133
133
|
console.log(chalk_1.default.gray(' Config written to:'));
|
|
134
134
|
console.log(chalk_1.default.cyan(` ${configPath}\n`));
|
|
135
|
-
console.log(chalk_1.default.yellow(' ⚠️
|
|
136
|
-
console.log(chalk_1.default.
|
|
137
|
-
console.log(chalk_1.default.gray('
|
|
135
|
+
console.log(chalk_1.default.yellow.bold(' ⚠️ RESTART REQUIRED:\n'));
|
|
136
|
+
console.log(chalk_1.default.gray(' 1. Type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' to close this terminal'));
|
|
137
|
+
console.log(chalk_1.default.gray(' 2. Open a NEW terminal window'));
|
|
138
|
+
console.log(chalk_1.default.gray(' 3. Navigate to your project and run ') + chalk_1.default.cyan('claude\n'));
|
|
139
|
+
console.log(chalk_1.default.white(' To verify it\'s working, ask: ') + chalk_1.default.green('"Are you using CodeBakers?"\n'));
|
|
138
140
|
}
|
|
139
141
|
catch (error) {
|
|
140
142
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -194,8 +196,8 @@ async function mcpUninstall() {
|
|
|
194
196
|
delete config.mcpServers.codebakers;
|
|
195
197
|
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
196
198
|
console.log(chalk_1.default.green(' Removed from global config.\n'));
|
|
197
|
-
console.log(chalk_1.default.yellow(' ⚠️
|
|
198
|
-
console.log(chalk_1.default.
|
|
199
|
+
console.log(chalk_1.default.yellow(' ⚠️ RESTART REQUIRED:\n'));
|
|
200
|
+
console.log(chalk_1.default.gray(' Type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' and open a new terminal to apply changes.\n'));
|
|
199
201
|
}
|
|
200
202
|
else {
|
|
201
203
|
console.log(chalk_1.default.gray(' CodeBakers not found in global config.\n'));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface ProvisionResult {
|
|
2
|
+
github?: {
|
|
3
|
+
repoUrl: string;
|
|
4
|
+
cloneUrl: string;
|
|
5
|
+
};
|
|
6
|
+
supabase?: {
|
|
7
|
+
projectId: string;
|
|
8
|
+
projectUrl: string;
|
|
9
|
+
apiUrl: string;
|
|
10
|
+
anonKey: string;
|
|
11
|
+
};
|
|
12
|
+
vercel?: {
|
|
13
|
+
projectId: string;
|
|
14
|
+
projectUrl: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a GitHub repository
|
|
19
|
+
*/
|
|
20
|
+
export declare function createGitHubRepo(projectName: string, description?: string): Promise<{
|
|
21
|
+
repoUrl: string;
|
|
22
|
+
cloneUrl: string;
|
|
23
|
+
} | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Create a Supabase project
|
|
26
|
+
*/
|
|
27
|
+
export declare function createSupabaseProject(projectName: string, organizationId?: string): Promise<{
|
|
28
|
+
projectId: string;
|
|
29
|
+
projectUrl: string;
|
|
30
|
+
apiUrl: string;
|
|
31
|
+
anonKey: string;
|
|
32
|
+
} | null>;
|
|
33
|
+
/**
|
|
34
|
+
* Create a Vercel project
|
|
35
|
+
*/
|
|
36
|
+
export declare function createVercelProject(projectName: string, gitRepoUrl?: string): Promise<{
|
|
37
|
+
projectId: string;
|
|
38
|
+
projectUrl: string;
|
|
39
|
+
} | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Full provisioning flow - create all services
|
|
42
|
+
*/
|
|
43
|
+
export declare function provisionAll(projectName: string, description?: string): Promise<ProvisionResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Check which services have keys configured
|
|
46
|
+
*/
|
|
47
|
+
export declare function getConfiguredServices(): {
|
|
48
|
+
github: boolean;
|
|
49
|
+
supabase: boolean;
|
|
50
|
+
vercel: boolean;
|
|
51
|
+
};
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createGitHubRepo = createGitHubRepo;
|
|
7
|
+
exports.createSupabaseProject = createSupabaseProject;
|
|
8
|
+
exports.createVercelProject = createVercelProject;
|
|
9
|
+
exports.provisionAll = provisionAll;
|
|
10
|
+
exports.getConfiguredServices = getConfiguredServices;
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const ora_1 = __importDefault(require("ora"));
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
14
|
+
const readline_1 = require("readline");
|
|
15
|
+
const config_js_1 = require("../config.js");
|
|
16
|
+
function prompt(question) {
|
|
17
|
+
const rl = (0, readline_1.createInterface)({
|
|
18
|
+
input: process.stdin,
|
|
19
|
+
output: process.stdout,
|
|
20
|
+
});
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
rl.question(question, (answer) => {
|
|
23
|
+
rl.close();
|
|
24
|
+
resolve(answer.trim());
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a service key is configured, prompt if not
|
|
30
|
+
*/
|
|
31
|
+
async function ensureServiceKey(service, instructions) {
|
|
32
|
+
let key = (0, config_js_1.getServiceKey)(service);
|
|
33
|
+
if (!key) {
|
|
34
|
+
console.log(chalk_1.default.yellow(`\n No ${service} API key configured.\n`));
|
|
35
|
+
console.log(chalk_1.default.gray(instructions));
|
|
36
|
+
const inputKey = await prompt(`\n ${service} API key (or press Enter to skip): `);
|
|
37
|
+
if (inputKey) {
|
|
38
|
+
(0, config_js_1.setServiceKey)(service, inputKey);
|
|
39
|
+
key = inputKey;
|
|
40
|
+
console.log(chalk_1.default.green(` ✓ ${service} key saved\n`));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.log(chalk_1.default.gray(` Skipping ${service} provisioning.\n`));
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return key;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a GitHub repository
|
|
51
|
+
*/
|
|
52
|
+
async function createGitHubRepo(projectName, description = '') {
|
|
53
|
+
// Check if gh CLI is available
|
|
54
|
+
try {
|
|
55
|
+
(0, child_process_1.execSync)('gh --version', { stdio: 'pipe' });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
console.log(chalk_1.default.yellow(' GitHub CLI (gh) not found. Install it from: https://cli.github.com\n'));
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
// Check if authenticated
|
|
62
|
+
try {
|
|
63
|
+
(0, child_process_1.execSync)('gh auth status', { stdio: 'pipe' });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
console.log(chalk_1.default.yellow(' Not logged into GitHub CLI.\n'));
|
|
67
|
+
console.log(chalk_1.default.gray(' Run: gh auth login\n'));
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const spinner = (0, ora_1.default)('Creating GitHub repository...').start();
|
|
71
|
+
try {
|
|
72
|
+
// Create the repo
|
|
73
|
+
const result = (0, child_process_1.execSync)(`gh repo create ${projectName} --public --description "${description}" --source . --remote origin --push`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
74
|
+
// Get the repo URL
|
|
75
|
+
const repoUrl = (0, child_process_1.execSync)('gh repo view --json url -q .url', { encoding: 'utf-8' }).trim();
|
|
76
|
+
const cloneUrl = `${repoUrl}.git`;
|
|
77
|
+
spinner.succeed('GitHub repository created!');
|
|
78
|
+
console.log(chalk_1.default.gray(` ${repoUrl}\n`));
|
|
79
|
+
return { repoUrl, cloneUrl };
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
spinner.fail('Failed to create GitHub repository');
|
|
83
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
+
if (message.includes('already exists')) {
|
|
85
|
+
console.log(chalk_1.default.yellow(' Repository already exists. Skipping.\n'));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.log(chalk_1.default.red(` Error: ${message}\n`));
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create a Supabase project
|
|
95
|
+
*/
|
|
96
|
+
async function createSupabaseProject(projectName, organizationId) {
|
|
97
|
+
const accessToken = await ensureServiceKey('supabase', ' Get your access token from: https://supabase.com/dashboard/account/tokens');
|
|
98
|
+
if (!accessToken)
|
|
99
|
+
return null;
|
|
100
|
+
const spinner = (0, ora_1.default)('Creating Supabase project...').start();
|
|
101
|
+
try {
|
|
102
|
+
// Get organization if not provided
|
|
103
|
+
if (!organizationId) {
|
|
104
|
+
const orgsResponse = await fetch('https://api.supabase.com/v1/organizations', {
|
|
105
|
+
headers: {
|
|
106
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
if (!orgsResponse.ok) {
|
|
110
|
+
throw new Error('Failed to fetch organizations. Check your access token.');
|
|
111
|
+
}
|
|
112
|
+
const orgs = await orgsResponse.json();
|
|
113
|
+
if (orgs.length === 0) {
|
|
114
|
+
throw new Error('No organizations found. Create one at supabase.com first.');
|
|
115
|
+
}
|
|
116
|
+
organizationId = orgs[0].id;
|
|
117
|
+
}
|
|
118
|
+
// Generate a random password for the database
|
|
119
|
+
const dbPassword = Math.random().toString(36).slice(-16) +
|
|
120
|
+
Math.random().toString(36).slice(-16).toUpperCase() +
|
|
121
|
+
'!1';
|
|
122
|
+
// Create the project
|
|
123
|
+
const createResponse = await fetch('https://api.supabase.com/v1/projects', {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: {
|
|
126
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
127
|
+
'Content-Type': 'application/json',
|
|
128
|
+
},
|
|
129
|
+
body: JSON.stringify({
|
|
130
|
+
name: projectName,
|
|
131
|
+
organization_id: organizationId,
|
|
132
|
+
region: 'us-east-1',
|
|
133
|
+
plan: 'free',
|
|
134
|
+
db_pass: dbPassword,
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
if (!createResponse.ok) {
|
|
138
|
+
const error = await createResponse.json();
|
|
139
|
+
throw new Error(error.message || 'Failed to create project');
|
|
140
|
+
}
|
|
141
|
+
const project = await createResponse.json();
|
|
142
|
+
spinner.succeed('Supabase project created!');
|
|
143
|
+
// Wait for project to be ready (it takes a moment)
|
|
144
|
+
const waitSpinner = (0, ora_1.default)('Waiting for project to be ready...').start();
|
|
145
|
+
let projectReady = false;
|
|
146
|
+
let attempts = 0;
|
|
147
|
+
let projectDetails = {};
|
|
148
|
+
while (!projectReady && attempts < 30) {
|
|
149
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
150
|
+
const statusResponse = await fetch(`https://api.supabase.com/v1/projects/${project.id}`, {
|
|
151
|
+
headers: {
|
|
152
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
if (statusResponse.ok) {
|
|
156
|
+
projectDetails = await statusResponse.json();
|
|
157
|
+
if (projectDetails.api_url) {
|
|
158
|
+
projectReady = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
attempts++;
|
|
162
|
+
}
|
|
163
|
+
if (!projectReady) {
|
|
164
|
+
waitSpinner.warn('Project created but may not be fully ready yet.');
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
waitSpinner.succeed('Project ready!');
|
|
168
|
+
}
|
|
169
|
+
const projectUrl = `https://supabase.com/dashboard/project/${project.id}`;
|
|
170
|
+
console.log(chalk_1.default.gray(` ${projectUrl}\n`));
|
|
171
|
+
// Get the anon key
|
|
172
|
+
const keysResponse = await fetch(`https://api.supabase.com/v1/projects/${project.id}/api-keys`, {
|
|
173
|
+
headers: {
|
|
174
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
let anonKey = '';
|
|
178
|
+
if (keysResponse.ok) {
|
|
179
|
+
const keys = await keysResponse.json();
|
|
180
|
+
const anonKeyObj = keys.find((k) => k.name === 'anon');
|
|
181
|
+
anonKey = anonKeyObj?.api_key || '';
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
projectId: project.id,
|
|
185
|
+
projectUrl,
|
|
186
|
+
apiUrl: projectDetails.api_url || `https://${project.id}.supabase.co`,
|
|
187
|
+
anonKey,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
spinner.fail('Failed to create Supabase project');
|
|
192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
193
|
+
console.log(chalk_1.default.red(` Error: ${message}\n`));
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Create a Vercel project
|
|
199
|
+
*/
|
|
200
|
+
async function createVercelProject(projectName, gitRepoUrl) {
|
|
201
|
+
const accessToken = await ensureServiceKey('vercel', ' Get your token from: https://vercel.com/account/tokens');
|
|
202
|
+
if (!accessToken)
|
|
203
|
+
return null;
|
|
204
|
+
const spinner = (0, ora_1.default)('Creating Vercel project...').start();
|
|
205
|
+
try {
|
|
206
|
+
// Create the project
|
|
207
|
+
const createResponse = await fetch('https://api.vercel.com/v10/projects', {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
headers: {
|
|
210
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
211
|
+
'Content-Type': 'application/json',
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify({
|
|
214
|
+
name: projectName,
|
|
215
|
+
framework: 'nextjs',
|
|
216
|
+
...(gitRepoUrl && {
|
|
217
|
+
gitRepository: {
|
|
218
|
+
type: 'github',
|
|
219
|
+
repo: gitRepoUrl.replace('https://github.com/', ''),
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
}),
|
|
223
|
+
});
|
|
224
|
+
if (!createResponse.ok) {
|
|
225
|
+
const error = await createResponse.json();
|
|
226
|
+
throw new Error(error.error?.message || 'Failed to create project');
|
|
227
|
+
}
|
|
228
|
+
const project = await createResponse.json();
|
|
229
|
+
spinner.succeed('Vercel project created!');
|
|
230
|
+
const projectUrl = `https://vercel.com/${project.accountId}/${project.name}`;
|
|
231
|
+
console.log(chalk_1.default.gray(` ${projectUrl}\n`));
|
|
232
|
+
return {
|
|
233
|
+
projectId: project.id,
|
|
234
|
+
projectUrl,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
spinner.fail('Failed to create Vercel project');
|
|
239
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
240
|
+
if (message.includes('already exists')) {
|
|
241
|
+
console.log(chalk_1.default.yellow(' Project already exists. Skipping.\n'));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.log(chalk_1.default.red(` Error: ${message}\n`));
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Full provisioning flow - create all services
|
|
251
|
+
*/
|
|
252
|
+
async function provisionAll(projectName, description = '') {
|
|
253
|
+
console.log(chalk_1.default.blue('\n ══════════════════════════════════════════════════════════'));
|
|
254
|
+
console.log(chalk_1.default.white.bold(' 🚀 Auto-Provisioning Services\n'));
|
|
255
|
+
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
256
|
+
const result = {};
|
|
257
|
+
// 1. GitHub
|
|
258
|
+
console.log(chalk_1.default.white(' Step 1: GitHub Repository\n'));
|
|
259
|
+
const github = await createGitHubRepo(projectName, description);
|
|
260
|
+
if (github) {
|
|
261
|
+
result.github = github;
|
|
262
|
+
}
|
|
263
|
+
// 2. Supabase
|
|
264
|
+
console.log(chalk_1.default.white(' Step 2: Supabase Project\n'));
|
|
265
|
+
const supabase = await createSupabaseProject(projectName);
|
|
266
|
+
if (supabase) {
|
|
267
|
+
result.supabase = supabase;
|
|
268
|
+
}
|
|
269
|
+
// 3. Vercel
|
|
270
|
+
console.log(chalk_1.default.white(' Step 3: Vercel Project\n'));
|
|
271
|
+
const vercel = await createVercelProject(projectName, result.github?.repoUrl);
|
|
272
|
+
if (vercel) {
|
|
273
|
+
result.vercel = vercel;
|
|
274
|
+
}
|
|
275
|
+
// Summary
|
|
276
|
+
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════'));
|
|
277
|
+
console.log(chalk_1.default.white.bold(' 📋 Provisioning Summary\n'));
|
|
278
|
+
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
279
|
+
if (result.github) {
|
|
280
|
+
console.log(chalk_1.default.green(' ✅ GitHub: ') + chalk_1.default.gray(result.github.repoUrl));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
console.log(chalk_1.default.yellow(' ⏭️ GitHub: Skipped'));
|
|
284
|
+
}
|
|
285
|
+
if (result.supabase) {
|
|
286
|
+
console.log(chalk_1.default.green(' ✅ Supabase: ') + chalk_1.default.gray(result.supabase.projectUrl));
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
console.log(chalk_1.default.yellow(' ⏭️ Supabase: Skipped'));
|
|
290
|
+
}
|
|
291
|
+
if (result.vercel) {
|
|
292
|
+
console.log(chalk_1.default.green(' ✅ Vercel: ') + chalk_1.default.gray(result.vercel.projectUrl));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
console.log(chalk_1.default.yellow(' ⏭️ Vercel: Skipped'));
|
|
296
|
+
}
|
|
297
|
+
console.log('');
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Check which services have keys configured
|
|
302
|
+
*/
|
|
303
|
+
function getConfiguredServices() {
|
|
304
|
+
return {
|
|
305
|
+
github: false, // GitHub uses gh CLI auth, not stored key
|
|
306
|
+
supabase: !!(0, config_js_1.getServiceKey)('supabase'),
|
|
307
|
+
vercel: !!(0, config_js_1.getServiceKey)('vercel'),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
@@ -45,6 +45,7 @@ const path_1 = require("path");
|
|
|
45
45
|
const child_process_1 = require("child_process");
|
|
46
46
|
const templates = __importStar(require("../templates/nextjs-supabase.js"));
|
|
47
47
|
const config_js_1 = require("../config.js");
|
|
48
|
+
const provision_js_1 = require("./provision.js");
|
|
48
49
|
// Cursor IDE configuration templates
|
|
49
50
|
const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
|
|
50
51
|
# Zero-friction AI assistance - everything is automatic
|
|
@@ -507,6 +508,35 @@ async function scaffold() {
|
|
|
507
508
|
else {
|
|
508
509
|
console.log(chalk_1.default.yellow(' ⚠️ Not logged in - run `codebakers setup` first to get patterns\n'));
|
|
509
510
|
}
|
|
511
|
+
// Ask about auto-provisioning
|
|
512
|
+
console.log(chalk_1.default.white('\n Would you like to auto-provision your infrastructure?\n'));
|
|
513
|
+
console.log(chalk_1.default.gray(' This can create GitHub repo, Supabase database, and Vercel project automatically.'));
|
|
514
|
+
console.log(chalk_1.default.gray(' You\'ll need API keys for each service (one-time setup).\n'));
|
|
515
|
+
const wantProvision = await confirm(' Auto-provision services?');
|
|
516
|
+
let provisionResult = {};
|
|
517
|
+
if (wantProvision) {
|
|
518
|
+
// Initialize git first if not already
|
|
519
|
+
try {
|
|
520
|
+
(0, child_process_1.execSync)('git init', { cwd, stdio: 'pipe' });
|
|
521
|
+
(0, child_process_1.execSync)('git add .', { cwd, stdio: 'pipe' });
|
|
522
|
+
(0, child_process_1.execSync)('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
// Git might already be initialized or have issues
|
|
526
|
+
}
|
|
527
|
+
provisionResult = await (0, provision_js_1.provisionAll)(projectName, `${projectName} - Built with CodeBakers`);
|
|
528
|
+
// Update .env.local with Supabase credentials if available
|
|
529
|
+
if (provisionResult.supabase) {
|
|
530
|
+
const envPath = (0, path_1.join)(cwd, '.env.local');
|
|
531
|
+
let envContent = (0, fs_1.existsSync)(envPath) ? (0, fs_1.readFileSync)(envPath, 'utf-8') : '';
|
|
532
|
+
// Replace placeholder values with actual credentials
|
|
533
|
+
envContent = envContent
|
|
534
|
+
.replace('your-project-id.supabase.co', provisionResult.supabase.apiUrl.replace('https://', ''))
|
|
535
|
+
.replace('your-anon-key', provisionResult.supabase.anonKey || 'your-anon-key');
|
|
536
|
+
(0, fs_1.writeFileSync)(envPath, envContent);
|
|
537
|
+
console.log(chalk_1.default.green(' ✅ Updated .env.local with Supabase credentials!\n'));
|
|
538
|
+
}
|
|
539
|
+
}
|
|
510
540
|
// Success message
|
|
511
541
|
console.log(chalk_1.default.green(`
|
|
512
542
|
╔═══════════════════════════════════════════════════════════╗
|
package/dist/commands/setup.js
CHANGED
|
@@ -103,11 +103,19 @@ function showFinalInstructions() {
|
|
|
103
103
|
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════'));
|
|
104
104
|
console.log(chalk_1.default.white.bold('\n 🎉 Setup Complete!\n'));
|
|
105
105
|
console.log(chalk_1.default.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
106
|
-
console.log(chalk_1.default.
|
|
107
|
-
console.log(chalk_1.default.
|
|
108
|
-
console.log(chalk_1.default.
|
|
109
|
-
console.log(chalk_1.default.
|
|
110
|
-
console.log(chalk_1.default.
|
|
111
|
-
console.log(chalk_1.default.gray('
|
|
106
|
+
console.log(chalk_1.default.yellow.bold(' ⚠️ RESTART REQUIRED - Follow these steps:\n'));
|
|
107
|
+
console.log(chalk_1.default.white(' For Claude Code:\n'));
|
|
108
|
+
console.log(chalk_1.default.gray(' 1. Close this terminal completely (type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(')'));
|
|
109
|
+
console.log(chalk_1.default.gray(' 2. Open a NEW terminal window'));
|
|
110
|
+
console.log(chalk_1.default.gray(' 3. Navigate to your project folder'));
|
|
111
|
+
console.log(chalk_1.default.gray(' 4. Run ') + chalk_1.default.cyan('claude') + chalk_1.default.gray(' to start Claude Code\n'));
|
|
112
|
+
console.log(chalk_1.default.white(' For Cursor:\n'));
|
|
113
|
+
console.log(chalk_1.default.gray(' 1. Close Cursor completely (Cmd+Q / Alt+F4)'));
|
|
114
|
+
console.log(chalk_1.default.gray(' 2. Reopen Cursor'));
|
|
115
|
+
console.log(chalk_1.default.gray(' 3. Open Composer (Cmd+I / Ctrl+I)\n'));
|
|
116
|
+
console.log(chalk_1.default.blue(' ──────────────────────────────────────────────────────────\n'));
|
|
117
|
+
console.log(chalk_1.default.white(' ✅ To verify CodeBakers is working, ask the AI:\n'));
|
|
118
|
+
console.log(chalk_1.default.green(' "Are you using CodeBakers?"\n'));
|
|
119
|
+
console.log(chalk_1.default.gray(' The AI should respond with its CodeBakers status.'));
|
|
112
120
|
console.log(chalk_1.default.gray(' Need help? https://codebakers.ai/docs\n'));
|
|
113
121
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export type ExperienceLevel = 'beginner' | 'intermediate' | 'advanced';
|
|
2
|
+
interface ServiceKeys {
|
|
3
|
+
github: string | null;
|
|
4
|
+
supabase: string | null;
|
|
5
|
+
vercel: string | null;
|
|
6
|
+
}
|
|
2
7
|
export declare function getApiKey(): string | null;
|
|
3
8
|
export declare function setApiKey(key: string): void;
|
|
4
9
|
export declare function clearApiKey(): void;
|
|
@@ -6,3 +11,9 @@ export declare function getApiUrl(): string;
|
|
|
6
11
|
export declare function setApiUrl(url: string): void;
|
|
7
12
|
export declare function getExperienceLevel(): ExperienceLevel;
|
|
8
13
|
export declare function setExperienceLevel(level: ExperienceLevel): void;
|
|
14
|
+
export type ServiceName = 'github' | 'supabase' | 'vercel';
|
|
15
|
+
export declare function getServiceKey(service: ServiceName): string | null;
|
|
16
|
+
export declare function setServiceKey(service: ServiceName, key: string): void;
|
|
17
|
+
export declare function clearServiceKey(service: ServiceName): void;
|
|
18
|
+
export declare function getAllServiceKeys(): ServiceKeys;
|
|
19
|
+
export {};
|
package/dist/config.js
CHANGED
|
@@ -10,6 +10,10 @@ exports.getApiUrl = getApiUrl;
|
|
|
10
10
|
exports.setApiUrl = setApiUrl;
|
|
11
11
|
exports.getExperienceLevel = getExperienceLevel;
|
|
12
12
|
exports.setExperienceLevel = setExperienceLevel;
|
|
13
|
+
exports.getServiceKey = getServiceKey;
|
|
14
|
+
exports.setServiceKey = setServiceKey;
|
|
15
|
+
exports.clearServiceKey = clearServiceKey;
|
|
16
|
+
exports.getAllServiceKeys = getAllServiceKeys;
|
|
13
17
|
const conf_1 = __importDefault(require("conf"));
|
|
14
18
|
const config = new conf_1.default({
|
|
15
19
|
projectName: 'codebakers',
|
|
@@ -17,6 +21,11 @@ const config = new conf_1.default({
|
|
|
17
21
|
apiKey: null,
|
|
18
22
|
apiUrl: 'https://codebakers.ai',
|
|
19
23
|
experienceLevel: 'intermediate',
|
|
24
|
+
serviceKeys: {
|
|
25
|
+
github: null,
|
|
26
|
+
supabase: null,
|
|
27
|
+
vercel: null,
|
|
28
|
+
},
|
|
20
29
|
},
|
|
21
30
|
});
|
|
22
31
|
function getApiKey() {
|
|
@@ -40,3 +49,20 @@ function getExperienceLevel() {
|
|
|
40
49
|
function setExperienceLevel(level) {
|
|
41
50
|
config.set('experienceLevel', level);
|
|
42
51
|
}
|
|
52
|
+
function getServiceKey(service) {
|
|
53
|
+
const keys = config.get('serviceKeys');
|
|
54
|
+
return keys[service];
|
|
55
|
+
}
|
|
56
|
+
function setServiceKey(service, key) {
|
|
57
|
+
const keys = config.get('serviceKeys');
|
|
58
|
+
keys[service] = key;
|
|
59
|
+
config.set('serviceKeys', keys);
|
|
60
|
+
}
|
|
61
|
+
function clearServiceKey(service) {
|
|
62
|
+
const keys = config.get('serviceKeys');
|
|
63
|
+
keys[service] = null;
|
|
64
|
+
config.set('serviceKeys', keys);
|
|
65
|
+
}
|
|
66
|
+
function getAllServiceKeys() {
|
|
67
|
+
return config.get('serviceKeys');
|
|
68
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -53,7 +53,7 @@ const program = new commander_1.Command();
|
|
|
53
53
|
program
|
|
54
54
|
.name('codebakers')
|
|
55
55
|
.description('CodeBakers CLI - Production patterns for AI-assisted development')
|
|
56
|
-
.version('1.
|
|
56
|
+
.version('1.5.0');
|
|
57
57
|
// Primary command - one-time setup
|
|
58
58
|
program
|
|
59
59
|
.command('setup')
|
package/dist/mcp/server.js
CHANGED
|
@@ -413,6 +413,14 @@ class CodeBakersServer {
|
|
|
413
413
|
properties: {},
|
|
414
414
|
},
|
|
415
415
|
},
|
|
416
|
+
{
|
|
417
|
+
name: 'get_status',
|
|
418
|
+
description: 'Check if CodeBakers is active and get current status. Use this when user asks "are you using CodeBakers?" or wants to verify the integration is working. Returns version, connection status, and available features.',
|
|
419
|
+
inputSchema: {
|
|
420
|
+
type: 'object',
|
|
421
|
+
properties: {},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
416
424
|
],
|
|
417
425
|
}));
|
|
418
426
|
// Handle tool calls
|
|
@@ -442,6 +450,8 @@ class CodeBakersServer {
|
|
|
442
450
|
return this.handleSetExperienceLevel(args);
|
|
443
451
|
case 'get_experience_level':
|
|
444
452
|
return this.handleGetExperienceLevel();
|
|
453
|
+
case 'get_status':
|
|
454
|
+
return this.handleGetStatus();
|
|
445
455
|
default:
|
|
446
456
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
447
457
|
}
|
|
@@ -1113,6 +1123,44 @@ phase: development
|
|
|
1113
1123
|
}],
|
|
1114
1124
|
};
|
|
1115
1125
|
}
|
|
1126
|
+
handleGetStatus() {
|
|
1127
|
+
const level = (0, config_js_1.getExperienceLevel)();
|
|
1128
|
+
const context = this.gatherProjectContext();
|
|
1129
|
+
const statusText = `# ✅ CodeBakers is Active!
|
|
1130
|
+
|
|
1131
|
+
## Connection Status
|
|
1132
|
+
- **MCP Server:** Running
|
|
1133
|
+
- **API Connected:** Yes
|
|
1134
|
+
- **Version:** 1.5.0
|
|
1135
|
+
|
|
1136
|
+
## Current Settings
|
|
1137
|
+
- **Experience Level:** ${level.charAt(0).toUpperCase() + level.slice(1)}
|
|
1138
|
+
- **Project:** ${context.projectName}
|
|
1139
|
+
|
|
1140
|
+
## Available Features
|
|
1141
|
+
- 🔧 **optimize_and_build** - AI-powered prompt optimization
|
|
1142
|
+
- 📦 **get_pattern** - Fetch production patterns
|
|
1143
|
+
- 🔍 **search_patterns** - Search for specific guidance
|
|
1144
|
+
- 🏗️ **scaffold_project** - Create new projects
|
|
1145
|
+
- ⚙️ **init_project** - Add patterns to existing projects
|
|
1146
|
+
|
|
1147
|
+
## How to Use
|
|
1148
|
+
Just describe what you want to build! I'll automatically:
|
|
1149
|
+
1. Analyze your request
|
|
1150
|
+
2. Find the right patterns
|
|
1151
|
+
3. Apply production best practices
|
|
1152
|
+
|
|
1153
|
+
**Example:** "Build a login page with email/password"
|
|
1154
|
+
|
|
1155
|
+
---
|
|
1156
|
+
*CodeBakers is providing AI-assisted development patterns for this project.*`;
|
|
1157
|
+
return {
|
|
1158
|
+
content: [{
|
|
1159
|
+
type: 'text',
|
|
1160
|
+
text: statusText,
|
|
1161
|
+
}],
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1116
1164
|
async run() {
|
|
1117
1165
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
1118
1166
|
await this.server.connect(transport);
|
package/package.json
CHANGED
|
@@ -106,9 +106,11 @@ export async function installHook(): Promise<void> {
|
|
|
106
106
|
console.log(chalk.gray(' ✓ Self-review reminders after code changes'));
|
|
107
107
|
console.log(chalk.gray(' ✓ Pattern-based development from .claude/ folder\n'));
|
|
108
108
|
|
|
109
|
-
console.log(chalk.yellow(' ⚠️
|
|
110
|
-
console.log(chalk.
|
|
111
|
-
console.log(chalk.gray('
|
|
109
|
+
console.log(chalk.yellow.bold(' ⚠️ RESTART REQUIRED:\n'));
|
|
110
|
+
console.log(chalk.gray(' 1. Type ') + chalk.cyan('exit') + chalk.gray(' to close this terminal'));
|
|
111
|
+
console.log(chalk.gray(' 2. Open a NEW terminal window'));
|
|
112
|
+
console.log(chalk.gray(' 3. Navigate to your project and run ') + chalk.cyan('claude\n'));
|
|
113
|
+
console.log(chalk.white(' To verify it\'s working, ask: ') + chalk.green('"Are you using CodeBakers?"\n'));
|
|
112
114
|
} catch (error) {
|
|
113
115
|
spinner.fail('Hook installation failed');
|
|
114
116
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -156,8 +158,8 @@ export async function uninstallHook(): Promise<void> {
|
|
|
156
158
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
157
159
|
|
|
158
160
|
spinner.succeed('Hook removed successfully!');
|
|
159
|
-
console.log(chalk.yellow('\n ⚠️
|
|
160
|
-
console.log(chalk.
|
|
161
|
+
console.log(chalk.yellow('\n ⚠️ RESTART REQUIRED:\n'));
|
|
162
|
+
console.log(chalk.gray(' Type ') + chalk.cyan('exit') + chalk.gray(' and open a new terminal to apply changes.\n'));
|
|
161
163
|
} catch (error) {
|
|
162
164
|
spinner.fail('Hook removal failed');
|
|
163
165
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -158,9 +158,11 @@ function installGlobalConfig(): void {
|
|
|
158
158
|
console.log(chalk.green(' MCP configuration installed!\n'));
|
|
159
159
|
console.log(chalk.gray(' Config written to:'));
|
|
160
160
|
console.log(chalk.cyan(` ${configPath}\n`));
|
|
161
|
-
console.log(chalk.yellow(' ⚠️
|
|
162
|
-
console.log(chalk.
|
|
163
|
-
console.log(chalk.gray('
|
|
161
|
+
console.log(chalk.yellow.bold(' ⚠️ RESTART REQUIRED:\n'));
|
|
162
|
+
console.log(chalk.gray(' 1. Type ') + chalk.cyan('exit') + chalk.gray(' to close this terminal'));
|
|
163
|
+
console.log(chalk.gray(' 2. Open a NEW terminal window'));
|
|
164
|
+
console.log(chalk.gray(' 3. Navigate to your project and run ') + chalk.cyan('claude\n'));
|
|
165
|
+
console.log(chalk.white(' To verify it\'s working, ask: ') + chalk.green('"Are you using CodeBakers?"\n'));
|
|
164
166
|
} catch (error) {
|
|
165
167
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
166
168
|
console.log(chalk.red(`\n Error: ${message}\n`));
|
|
@@ -222,8 +224,8 @@ export async function mcpUninstall(): Promise<void> {
|
|
|
222
224
|
delete config.mcpServers.codebakers;
|
|
223
225
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
224
226
|
console.log(chalk.green(' Removed from global config.\n'));
|
|
225
|
-
console.log(chalk.yellow(' ⚠️
|
|
226
|
-
console.log(chalk.
|
|
227
|
+
console.log(chalk.yellow(' ⚠️ RESTART REQUIRED:\n'));
|
|
228
|
+
console.log(chalk.gray(' Type ') + chalk.cyan('exit') + chalk.gray(' and open a new terminal to apply changes.\n'));
|
|
227
229
|
} else {
|
|
228
230
|
console.log(chalk.gray(' CodeBakers not found in global config.\n'));
|
|
229
231
|
console.log(chalk.gray(' To remove from Claude Code, run:\n'));
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { execSync, exec } from 'child_process';
|
|
4
|
+
import { createInterface } from 'readline';
|
|
5
|
+
import { getServiceKey, setServiceKey, type ServiceName } from '../config.js';
|
|
6
|
+
|
|
7
|
+
function prompt(question: string): Promise<string> {
|
|
8
|
+
const rl = createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
rl.question(question, (answer) => {
|
|
15
|
+
rl.close();
|
|
16
|
+
resolve(answer.trim());
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ProvisionResult {
|
|
22
|
+
github?: {
|
|
23
|
+
repoUrl: string;
|
|
24
|
+
cloneUrl: string;
|
|
25
|
+
};
|
|
26
|
+
supabase?: {
|
|
27
|
+
projectId: string;
|
|
28
|
+
projectUrl: string;
|
|
29
|
+
apiUrl: string;
|
|
30
|
+
anonKey: string;
|
|
31
|
+
};
|
|
32
|
+
vercel?: {
|
|
33
|
+
projectId: string;
|
|
34
|
+
projectUrl: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if a service key is configured, prompt if not
|
|
40
|
+
*/
|
|
41
|
+
async function ensureServiceKey(service: ServiceName, instructions: string): Promise<string | null> {
|
|
42
|
+
let key = getServiceKey(service);
|
|
43
|
+
|
|
44
|
+
if (!key) {
|
|
45
|
+
console.log(chalk.yellow(`\n No ${service} API key configured.\n`));
|
|
46
|
+
console.log(chalk.gray(instructions));
|
|
47
|
+
|
|
48
|
+
const inputKey = await prompt(`\n ${service} API key (or press Enter to skip): `);
|
|
49
|
+
|
|
50
|
+
if (inputKey) {
|
|
51
|
+
setServiceKey(service, inputKey);
|
|
52
|
+
key = inputKey;
|
|
53
|
+
console.log(chalk.green(` ✓ ${service} key saved\n`));
|
|
54
|
+
} else {
|
|
55
|
+
console.log(chalk.gray(` Skipping ${service} provisioning.\n`));
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return key;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a GitHub repository
|
|
65
|
+
*/
|
|
66
|
+
export async function createGitHubRepo(
|
|
67
|
+
projectName: string,
|
|
68
|
+
description: string = ''
|
|
69
|
+
): Promise<{ repoUrl: string; cloneUrl: string } | null> {
|
|
70
|
+
// Check if gh CLI is available
|
|
71
|
+
try {
|
|
72
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
73
|
+
} catch {
|
|
74
|
+
console.log(chalk.yellow(' GitHub CLI (gh) not found. Install it from: https://cli.github.com\n'));
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if authenticated
|
|
79
|
+
try {
|
|
80
|
+
execSync('gh auth status', { stdio: 'pipe' });
|
|
81
|
+
} catch {
|
|
82
|
+
console.log(chalk.yellow(' Not logged into GitHub CLI.\n'));
|
|
83
|
+
console.log(chalk.gray(' Run: gh auth login\n'));
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const spinner = ora('Creating GitHub repository...').start();
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
// Create the repo
|
|
91
|
+
const result = execSync(
|
|
92
|
+
`gh repo create ${projectName} --public --description "${description}" --source . --remote origin --push`,
|
|
93
|
+
{ encoding: 'utf-8', stdio: 'pipe' }
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Get the repo URL
|
|
97
|
+
const repoUrl = execSync('gh repo view --json url -q .url', { encoding: 'utf-8' }).trim();
|
|
98
|
+
const cloneUrl = `${repoUrl}.git`;
|
|
99
|
+
|
|
100
|
+
spinner.succeed('GitHub repository created!');
|
|
101
|
+
console.log(chalk.gray(` ${repoUrl}\n`));
|
|
102
|
+
|
|
103
|
+
return { repoUrl, cloneUrl };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
spinner.fail('Failed to create GitHub repository');
|
|
106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
+
|
|
108
|
+
if (message.includes('already exists')) {
|
|
109
|
+
console.log(chalk.yellow(' Repository already exists. Skipping.\n'));
|
|
110
|
+
} else {
|
|
111
|
+
console.log(chalk.red(` Error: ${message}\n`));
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a Supabase project
|
|
119
|
+
*/
|
|
120
|
+
export async function createSupabaseProject(
|
|
121
|
+
projectName: string,
|
|
122
|
+
organizationId?: string
|
|
123
|
+
): Promise<{ projectId: string; projectUrl: string; apiUrl: string; anonKey: string } | null> {
|
|
124
|
+
const accessToken = await ensureServiceKey('supabase',
|
|
125
|
+
' Get your access token from: https://supabase.com/dashboard/account/tokens'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (!accessToken) return null;
|
|
129
|
+
|
|
130
|
+
const spinner = ora('Creating Supabase project...').start();
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// Get organization if not provided
|
|
134
|
+
if (!organizationId) {
|
|
135
|
+
const orgsResponse = await fetch('https://api.supabase.com/v1/organizations', {
|
|
136
|
+
headers: {
|
|
137
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!orgsResponse.ok) {
|
|
142
|
+
throw new Error('Failed to fetch organizations. Check your access token.');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const orgs = await orgsResponse.json();
|
|
146
|
+
if (orgs.length === 0) {
|
|
147
|
+
throw new Error('No organizations found. Create one at supabase.com first.');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
organizationId = orgs[0].id;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Generate a random password for the database
|
|
154
|
+
const dbPassword = Math.random().toString(36).slice(-16) +
|
|
155
|
+
Math.random().toString(36).slice(-16).toUpperCase() +
|
|
156
|
+
'!1';
|
|
157
|
+
|
|
158
|
+
// Create the project
|
|
159
|
+
const createResponse = await fetch('https://api.supabase.com/v1/projects', {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: {
|
|
162
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
163
|
+
'Content-Type': 'application/json',
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify({
|
|
166
|
+
name: projectName,
|
|
167
|
+
organization_id: organizationId,
|
|
168
|
+
region: 'us-east-1',
|
|
169
|
+
plan: 'free',
|
|
170
|
+
db_pass: dbPassword,
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (!createResponse.ok) {
|
|
175
|
+
const error = await createResponse.json();
|
|
176
|
+
throw new Error(error.message || 'Failed to create project');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const project = await createResponse.json();
|
|
180
|
+
|
|
181
|
+
spinner.succeed('Supabase project created!');
|
|
182
|
+
|
|
183
|
+
// Wait for project to be ready (it takes a moment)
|
|
184
|
+
const waitSpinner = ora('Waiting for project to be ready...').start();
|
|
185
|
+
|
|
186
|
+
let projectReady = false;
|
|
187
|
+
let attempts = 0;
|
|
188
|
+
let projectDetails: { api_url?: string; anon_key?: string } = {};
|
|
189
|
+
|
|
190
|
+
while (!projectReady && attempts < 30) {
|
|
191
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
192
|
+
|
|
193
|
+
const statusResponse = await fetch(`https://api.supabase.com/v1/projects/${project.id}`, {
|
|
194
|
+
headers: {
|
|
195
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (statusResponse.ok) {
|
|
200
|
+
projectDetails = await statusResponse.json();
|
|
201
|
+
if (projectDetails.api_url) {
|
|
202
|
+
projectReady = true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
attempts++;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!projectReady) {
|
|
209
|
+
waitSpinner.warn('Project created but may not be fully ready yet.');
|
|
210
|
+
} else {
|
|
211
|
+
waitSpinner.succeed('Project ready!');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const projectUrl = `https://supabase.com/dashboard/project/${project.id}`;
|
|
215
|
+
console.log(chalk.gray(` ${projectUrl}\n`));
|
|
216
|
+
|
|
217
|
+
// Get the anon key
|
|
218
|
+
const keysResponse = await fetch(`https://api.supabase.com/v1/projects/${project.id}/api-keys`, {
|
|
219
|
+
headers: {
|
|
220
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
let anonKey = '';
|
|
225
|
+
if (keysResponse.ok) {
|
|
226
|
+
const keys = await keysResponse.json();
|
|
227
|
+
const anonKeyObj = keys.find((k: { name: string }) => k.name === 'anon');
|
|
228
|
+
anonKey = anonKeyObj?.api_key || '';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
projectId: project.id,
|
|
233
|
+
projectUrl,
|
|
234
|
+
apiUrl: projectDetails.api_url || `https://${project.id}.supabase.co`,
|
|
235
|
+
anonKey,
|
|
236
|
+
};
|
|
237
|
+
} catch (error) {
|
|
238
|
+
spinner.fail('Failed to create Supabase project');
|
|
239
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
240
|
+
console.log(chalk.red(` Error: ${message}\n`));
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Create a Vercel project
|
|
247
|
+
*/
|
|
248
|
+
export async function createVercelProject(
|
|
249
|
+
projectName: string,
|
|
250
|
+
gitRepoUrl?: string
|
|
251
|
+
): Promise<{ projectId: string; projectUrl: string } | null> {
|
|
252
|
+
const accessToken = await ensureServiceKey('vercel',
|
|
253
|
+
' Get your token from: https://vercel.com/account/tokens'
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
if (!accessToken) return null;
|
|
257
|
+
|
|
258
|
+
const spinner = ora('Creating Vercel project...').start();
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
// Create the project
|
|
262
|
+
const createResponse = await fetch('https://api.vercel.com/v10/projects', {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {
|
|
265
|
+
'Authorization': `Bearer ${accessToken}`,
|
|
266
|
+
'Content-Type': 'application/json',
|
|
267
|
+
},
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
name: projectName,
|
|
270
|
+
framework: 'nextjs',
|
|
271
|
+
...(gitRepoUrl && {
|
|
272
|
+
gitRepository: {
|
|
273
|
+
type: 'github',
|
|
274
|
+
repo: gitRepoUrl.replace('https://github.com/', ''),
|
|
275
|
+
},
|
|
276
|
+
}),
|
|
277
|
+
}),
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (!createResponse.ok) {
|
|
281
|
+
const error = await createResponse.json();
|
|
282
|
+
throw new Error(error.error?.message || 'Failed to create project');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const project = await createResponse.json();
|
|
286
|
+
|
|
287
|
+
spinner.succeed('Vercel project created!');
|
|
288
|
+
|
|
289
|
+
const projectUrl = `https://vercel.com/${project.accountId}/${project.name}`;
|
|
290
|
+
console.log(chalk.gray(` ${projectUrl}\n`));
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
projectId: project.id,
|
|
294
|
+
projectUrl,
|
|
295
|
+
};
|
|
296
|
+
} catch (error) {
|
|
297
|
+
spinner.fail('Failed to create Vercel project');
|
|
298
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
299
|
+
|
|
300
|
+
if (message.includes('already exists')) {
|
|
301
|
+
console.log(chalk.yellow(' Project already exists. Skipping.\n'));
|
|
302
|
+
} else {
|
|
303
|
+
console.log(chalk.red(` Error: ${message}\n`));
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Full provisioning flow - create all services
|
|
311
|
+
*/
|
|
312
|
+
export async function provisionAll(
|
|
313
|
+
projectName: string,
|
|
314
|
+
description: string = ''
|
|
315
|
+
): Promise<ProvisionResult> {
|
|
316
|
+
console.log(chalk.blue('\n ══════════════════════════════════════════════════════════'));
|
|
317
|
+
console.log(chalk.white.bold(' 🚀 Auto-Provisioning Services\n'));
|
|
318
|
+
console.log(chalk.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
319
|
+
|
|
320
|
+
const result: ProvisionResult = {};
|
|
321
|
+
|
|
322
|
+
// 1. GitHub
|
|
323
|
+
console.log(chalk.white(' Step 1: GitHub Repository\n'));
|
|
324
|
+
const github = await createGitHubRepo(projectName, description);
|
|
325
|
+
if (github) {
|
|
326
|
+
result.github = github;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 2. Supabase
|
|
330
|
+
console.log(chalk.white(' Step 2: Supabase Project\n'));
|
|
331
|
+
const supabase = await createSupabaseProject(projectName);
|
|
332
|
+
if (supabase) {
|
|
333
|
+
result.supabase = supabase;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 3. Vercel
|
|
337
|
+
console.log(chalk.white(' Step 3: Vercel Project\n'));
|
|
338
|
+
const vercel = await createVercelProject(projectName, result.github?.repoUrl);
|
|
339
|
+
if (vercel) {
|
|
340
|
+
result.vercel = vercel;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Summary
|
|
344
|
+
console.log(chalk.blue(' ══════════════════════════════════════════════════════════'));
|
|
345
|
+
console.log(chalk.white.bold(' 📋 Provisioning Summary\n'));
|
|
346
|
+
console.log(chalk.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
347
|
+
|
|
348
|
+
if (result.github) {
|
|
349
|
+
console.log(chalk.green(' ✅ GitHub: ') + chalk.gray(result.github.repoUrl));
|
|
350
|
+
} else {
|
|
351
|
+
console.log(chalk.yellow(' ⏭️ GitHub: Skipped'));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (result.supabase) {
|
|
355
|
+
console.log(chalk.green(' ✅ Supabase: ') + chalk.gray(result.supabase.projectUrl));
|
|
356
|
+
} else {
|
|
357
|
+
console.log(chalk.yellow(' ⏭️ Supabase: Skipped'));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (result.vercel) {
|
|
361
|
+
console.log(chalk.green(' ✅ Vercel: ') + chalk.gray(result.vercel.projectUrl));
|
|
362
|
+
} else {
|
|
363
|
+
console.log(chalk.yellow(' ⏭️ Vercel: Skipped'));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
console.log('');
|
|
367
|
+
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Check which services have keys configured
|
|
373
|
+
*/
|
|
374
|
+
export function getConfiguredServices(): { github: boolean; supabase: boolean; vercel: boolean } {
|
|
375
|
+
return {
|
|
376
|
+
github: false, // GitHub uses gh CLI auth, not stored key
|
|
377
|
+
supabase: !!getServiceKey('supabase'),
|
|
378
|
+
vercel: !!getServiceKey('vercel'),
|
|
379
|
+
};
|
|
380
|
+
}
|
package/src/commands/scaffold.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from 'path';
|
|
|
6
6
|
import { execSync } from 'child_process';
|
|
7
7
|
import * as templates from '../templates/nextjs-supabase.js';
|
|
8
8
|
import { getApiKey, getApiUrl } from '../config.js';
|
|
9
|
+
import { provisionAll, type ProvisionResult } from './provision.js';
|
|
9
10
|
|
|
10
11
|
// Cursor IDE configuration templates
|
|
11
12
|
const CURSORRULES_TEMPLATE = `# CODEBAKERS CURSOR RULES
|
|
@@ -522,6 +523,41 @@ export async function scaffold(): Promise<void> {
|
|
|
522
523
|
console.log(chalk.yellow(' ⚠️ Not logged in - run `codebakers setup` first to get patterns\n'));
|
|
523
524
|
}
|
|
524
525
|
|
|
526
|
+
// Ask about auto-provisioning
|
|
527
|
+
console.log(chalk.white('\n Would you like to auto-provision your infrastructure?\n'));
|
|
528
|
+
console.log(chalk.gray(' This can create GitHub repo, Supabase database, and Vercel project automatically.'));
|
|
529
|
+
console.log(chalk.gray(' You\'ll need API keys for each service (one-time setup).\n'));
|
|
530
|
+
|
|
531
|
+
const wantProvision = await confirm(' Auto-provision services?');
|
|
532
|
+
let provisionResult: ProvisionResult = {};
|
|
533
|
+
|
|
534
|
+
if (wantProvision) {
|
|
535
|
+
// Initialize git first if not already
|
|
536
|
+
try {
|
|
537
|
+
execSync('git init', { cwd, stdio: 'pipe' });
|
|
538
|
+
execSync('git add .', { cwd, stdio: 'pipe' });
|
|
539
|
+
execSync('git commit -m "Initial commit from CodeBakers scaffold"', { cwd, stdio: 'pipe' });
|
|
540
|
+
} catch {
|
|
541
|
+
// Git might already be initialized or have issues
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
provisionResult = await provisionAll(projectName, `${projectName} - Built with CodeBakers`);
|
|
545
|
+
|
|
546
|
+
// Update .env.local with Supabase credentials if available
|
|
547
|
+
if (provisionResult.supabase) {
|
|
548
|
+
const envPath = join(cwd, '.env.local');
|
|
549
|
+
let envContent = existsSync(envPath) ? readFileSync(envPath, 'utf-8') : '';
|
|
550
|
+
|
|
551
|
+
// Replace placeholder values with actual credentials
|
|
552
|
+
envContent = envContent
|
|
553
|
+
.replace('your-project-id.supabase.co', provisionResult.supabase.apiUrl.replace('https://', ''))
|
|
554
|
+
.replace('your-anon-key', provisionResult.supabase.anonKey || 'your-anon-key');
|
|
555
|
+
|
|
556
|
+
writeFileSync(envPath, envContent);
|
|
557
|
+
console.log(chalk.green(' ✅ Updated .env.local with Supabase credentials!\n'));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
525
561
|
// Success message
|
|
526
562
|
console.log(chalk.green(`
|
|
527
563
|
╔═══════════════════════════════════════════════════════════╗
|
package/src/commands/setup.ts
CHANGED
|
@@ -113,13 +113,24 @@ function showFinalInstructions(): void {
|
|
|
113
113
|
console.log(chalk.white.bold('\n 🎉 Setup Complete!\n'));
|
|
114
114
|
console.log(chalk.blue(' ══════════════════════════════════════════════════════════\n'));
|
|
115
115
|
|
|
116
|
-
console.log(chalk.
|
|
117
|
-
console.log(chalk.cyan(' • Claude Code') + chalk.gray(' - Open any project folder'));
|
|
118
|
-
console.log(chalk.cyan(' • Cursor') + chalk.gray(' - Open Composer (Cmd+I / Ctrl+I)\n'));
|
|
116
|
+
console.log(chalk.yellow.bold(' ⚠️ RESTART REQUIRED - Follow these steps:\n'));
|
|
119
117
|
|
|
120
|
-
console.log(chalk.white('
|
|
121
|
-
console.log(chalk.
|
|
118
|
+
console.log(chalk.white(' For Claude Code:\n'));
|
|
119
|
+
console.log(chalk.gray(' 1. Close this terminal completely (type ') + chalk.cyan('exit') + chalk.gray(')'));
|
|
120
|
+
console.log(chalk.gray(' 2. Open a NEW terminal window'));
|
|
121
|
+
console.log(chalk.gray(' 3. Navigate to your project folder'));
|
|
122
|
+
console.log(chalk.gray(' 4. Run ') + chalk.cyan('claude') + chalk.gray(' to start Claude Code\n'));
|
|
122
123
|
|
|
123
|
-
console.log(chalk.
|
|
124
|
+
console.log(chalk.white(' For Cursor:\n'));
|
|
125
|
+
console.log(chalk.gray(' 1. Close Cursor completely (Cmd+Q / Alt+F4)'));
|
|
126
|
+
console.log(chalk.gray(' 2. Reopen Cursor'));
|
|
127
|
+
console.log(chalk.gray(' 3. Open Composer (Cmd+I / Ctrl+I)\n'));
|
|
128
|
+
|
|
129
|
+
console.log(chalk.blue(' ──────────────────────────────────────────────────────────\n'));
|
|
130
|
+
|
|
131
|
+
console.log(chalk.white(' ✅ To verify CodeBakers is working, ask the AI:\n'));
|
|
132
|
+
console.log(chalk.green(' "Are you using CodeBakers?"\n'));
|
|
133
|
+
|
|
134
|
+
console.log(chalk.gray(' The AI should respond with its CodeBakers status.'));
|
|
124
135
|
console.log(chalk.gray(' Need help? https://codebakers.ai/docs\n'));
|
|
125
136
|
}
|
package/src/config.ts
CHANGED
|
@@ -2,10 +2,17 @@ import Conf from 'conf';
|
|
|
2
2
|
|
|
3
3
|
export type ExperienceLevel = 'beginner' | 'intermediate' | 'advanced';
|
|
4
4
|
|
|
5
|
+
interface ServiceKeys {
|
|
6
|
+
github: string | null;
|
|
7
|
+
supabase: string | null;
|
|
8
|
+
vercel: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
interface ConfigSchema {
|
|
6
12
|
apiKey: string | null;
|
|
7
13
|
apiUrl: string;
|
|
8
14
|
experienceLevel: ExperienceLevel;
|
|
15
|
+
serviceKeys: ServiceKeys;
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
const config = new Conf<ConfigSchema>({
|
|
@@ -14,6 +21,11 @@ const config = new Conf<ConfigSchema>({
|
|
|
14
21
|
apiKey: null,
|
|
15
22
|
apiUrl: 'https://codebakers.ai',
|
|
16
23
|
experienceLevel: 'intermediate',
|
|
24
|
+
serviceKeys: {
|
|
25
|
+
github: null,
|
|
26
|
+
supabase: null,
|
|
27
|
+
vercel: null,
|
|
28
|
+
},
|
|
17
29
|
},
|
|
18
30
|
});
|
|
19
31
|
|
|
@@ -44,3 +56,27 @@ export function getExperienceLevel(): ExperienceLevel {
|
|
|
44
56
|
export function setExperienceLevel(level: ExperienceLevel): void {
|
|
45
57
|
config.set('experienceLevel', level);
|
|
46
58
|
}
|
|
59
|
+
|
|
60
|
+
// Service API Keys
|
|
61
|
+
export type ServiceName = 'github' | 'supabase' | 'vercel';
|
|
62
|
+
|
|
63
|
+
export function getServiceKey(service: ServiceName): string | null {
|
|
64
|
+
const keys = config.get('serviceKeys');
|
|
65
|
+
return keys[service];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function setServiceKey(service: ServiceName, key: string): void {
|
|
69
|
+
const keys = config.get('serviceKeys');
|
|
70
|
+
keys[service] = key;
|
|
71
|
+
config.set('serviceKeys', keys);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function clearServiceKey(service: ServiceName): void {
|
|
75
|
+
const keys = config.get('serviceKeys');
|
|
76
|
+
keys[service] = null;
|
|
77
|
+
config.set('serviceKeys', keys);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getAllServiceKeys(): ServiceKeys {
|
|
81
|
+
return config.get('serviceKeys');
|
|
82
|
+
}
|
package/src/index.ts
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -437,6 +437,15 @@ class CodeBakersServer {
|
|
|
437
437
|
properties: {},
|
|
438
438
|
},
|
|
439
439
|
},
|
|
440
|
+
{
|
|
441
|
+
name: 'get_status',
|
|
442
|
+
description:
|
|
443
|
+
'Check if CodeBakers is active and get current status. Use this when user asks "are you using CodeBakers?" or wants to verify the integration is working. Returns version, connection status, and available features.',
|
|
444
|
+
inputSchema: {
|
|
445
|
+
type: 'object' as const,
|
|
446
|
+
properties: {},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
440
449
|
],
|
|
441
450
|
}));
|
|
442
451
|
|
|
@@ -482,6 +491,9 @@ class CodeBakersServer {
|
|
|
482
491
|
case 'get_experience_level':
|
|
483
492
|
return this.handleGetExperienceLevel();
|
|
484
493
|
|
|
494
|
+
case 'get_status':
|
|
495
|
+
return this.handleGetStatus();
|
|
496
|
+
|
|
485
497
|
default:
|
|
486
498
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
487
499
|
}
|
|
@@ -1264,6 +1276,47 @@ phase: development
|
|
|
1264
1276
|
};
|
|
1265
1277
|
}
|
|
1266
1278
|
|
|
1279
|
+
private handleGetStatus() {
|
|
1280
|
+
const level = getExperienceLevel();
|
|
1281
|
+
const context = this.gatherProjectContext();
|
|
1282
|
+
|
|
1283
|
+
const statusText = `# ✅ CodeBakers is Active!
|
|
1284
|
+
|
|
1285
|
+
## Connection Status
|
|
1286
|
+
- **MCP Server:** Running
|
|
1287
|
+
- **API Connected:** Yes
|
|
1288
|
+
- **Version:** 1.5.0
|
|
1289
|
+
|
|
1290
|
+
## Current Settings
|
|
1291
|
+
- **Experience Level:** ${level.charAt(0).toUpperCase() + level.slice(1)}
|
|
1292
|
+
- **Project:** ${context.projectName}
|
|
1293
|
+
|
|
1294
|
+
## Available Features
|
|
1295
|
+
- 🔧 **optimize_and_build** - AI-powered prompt optimization
|
|
1296
|
+
- 📦 **get_pattern** - Fetch production patterns
|
|
1297
|
+
- 🔍 **search_patterns** - Search for specific guidance
|
|
1298
|
+
- 🏗️ **scaffold_project** - Create new projects
|
|
1299
|
+
- ⚙️ **init_project** - Add patterns to existing projects
|
|
1300
|
+
|
|
1301
|
+
## How to Use
|
|
1302
|
+
Just describe what you want to build! I'll automatically:
|
|
1303
|
+
1. Analyze your request
|
|
1304
|
+
2. Find the right patterns
|
|
1305
|
+
3. Apply production best practices
|
|
1306
|
+
|
|
1307
|
+
**Example:** "Build a login page with email/password"
|
|
1308
|
+
|
|
1309
|
+
---
|
|
1310
|
+
*CodeBakers is providing AI-assisted development patterns for this project.*`;
|
|
1311
|
+
|
|
1312
|
+
return {
|
|
1313
|
+
content: [{
|
|
1314
|
+
type: 'text' as const,
|
|
1315
|
+
text: statusText,
|
|
1316
|
+
}],
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1267
1320
|
async run(): Promise<void> {
|
|
1268
1321
|
const transport = new StdioServerTransport();
|
|
1269
1322
|
await this.server.connect(transport);
|