@codebakers/cli 1.1.4 → 1.1.6
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/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +218 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +772 -0
- package/dist/commands/install-hook.d.ts +12 -0
- package/dist/commands/install-hook.js +193 -0
- package/dist/commands/install.d.ts +1 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +54 -0
- package/dist/commands/mcp-config.d.ts +6 -0
- package/dist/commands/mcp-config.js +209 -0
- package/dist/commands/serve.d.ts +1 -0
- package/dist/commands/serve.js +26 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +92 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +49 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +50 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +33 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +71 -1026
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +544 -0
- package/package.json +16 -38
- package/src/commands/doctor.ts +231 -0
- package/src/commands/init.ts +827 -0
- package/src/commands/install-hook.ts +207 -0
- package/src/commands/install.ts +94 -0
- package/src/commands/login.ts +56 -0
- package/src/commands/mcp-config.ts +235 -0
- package/src/commands/serve.ts +23 -0
- package/src/commands/setup.ts +104 -0
- package/src/commands/status.ts +48 -0
- package/src/commands/uninstall.ts +49 -0
- package/src/config.ts +34 -0
- package/src/index.ts +87 -0
- package/src/mcp/server.ts +617 -0
- package/tsconfig.json +16 -0
- package/README.md +0 -89
- package/dist/chunk-7CKLRE2H.js +0 -36
- package/dist/config-R2H6JKGW.js +0 -16
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.runServer = runServer;
|
|
38
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
39
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
40
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
41
|
+
const config_js_1 = require("../config.js");
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
// Pattern cache to avoid repeated API calls
|
|
45
|
+
const patternCache = new Map();
|
|
46
|
+
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
47
|
+
class CodeBakersServer {
|
|
48
|
+
server;
|
|
49
|
+
apiKey;
|
|
50
|
+
apiUrl;
|
|
51
|
+
constructor() {
|
|
52
|
+
this.apiKey = (0, config_js_1.getApiKey)();
|
|
53
|
+
this.apiUrl = (0, config_js_1.getApiUrl)();
|
|
54
|
+
this.server = new index_js_1.Server({
|
|
55
|
+
name: 'codebakers',
|
|
56
|
+
version: '1.0.0',
|
|
57
|
+
}, {
|
|
58
|
+
capabilities: {
|
|
59
|
+
tools: {},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
this.setupHandlers();
|
|
63
|
+
}
|
|
64
|
+
gatherProjectContext() {
|
|
65
|
+
const cwd = process.cwd();
|
|
66
|
+
const context = {
|
|
67
|
+
projectName: 'Unknown',
|
|
68
|
+
dependencies: [],
|
|
69
|
+
devDependencies: [],
|
|
70
|
+
folderStructure: [],
|
|
71
|
+
hasAuth: false,
|
|
72
|
+
hasDatabase: false,
|
|
73
|
+
hasPayments: false,
|
|
74
|
+
uiLibrary: null,
|
|
75
|
+
schemaPath: null,
|
|
76
|
+
componentsPath: null,
|
|
77
|
+
existingComponents: [],
|
|
78
|
+
existingServices: [],
|
|
79
|
+
existingApiRoutes: [],
|
|
80
|
+
codebakersState: null,
|
|
81
|
+
};
|
|
82
|
+
// Read package.json
|
|
83
|
+
try {
|
|
84
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
85
|
+
if (fs.existsSync(pkgPath)) {
|
|
86
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
87
|
+
context.projectName = pkg.name || 'Unknown';
|
|
88
|
+
context.dependencies = Object.keys(pkg.dependencies || {});
|
|
89
|
+
context.devDependencies = Object.keys(pkg.devDependencies || {});
|
|
90
|
+
// Detect libraries
|
|
91
|
+
const allDeps = [...context.dependencies, ...context.devDependencies];
|
|
92
|
+
context.hasAuth = allDeps.some(d => d.includes('supabase') || d.includes('next-auth') || d.includes('clerk'));
|
|
93
|
+
context.hasDatabase = allDeps.some(d => d.includes('drizzle') || d.includes('prisma') || d.includes('postgres'));
|
|
94
|
+
context.hasPayments = allDeps.some(d => d.includes('stripe') || d.includes('paypal'));
|
|
95
|
+
if (allDeps.includes('@radix-ui/react-dialog') || allDeps.some(d => d.includes('shadcn'))) {
|
|
96
|
+
context.uiLibrary = 'shadcn/ui';
|
|
97
|
+
}
|
|
98
|
+
else if (allDeps.includes('@chakra-ui/react')) {
|
|
99
|
+
context.uiLibrary = 'Chakra UI';
|
|
100
|
+
}
|
|
101
|
+
else if (allDeps.includes('@mui/material')) {
|
|
102
|
+
context.uiLibrary = 'Material UI';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Ignore package.json errors
|
|
108
|
+
}
|
|
109
|
+
// Read .codebakers.json state
|
|
110
|
+
try {
|
|
111
|
+
const statePath = path.join(cwd, '.codebakers.json');
|
|
112
|
+
if (fs.existsSync(statePath)) {
|
|
113
|
+
context.codebakersState = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Ignore state file errors
|
|
118
|
+
}
|
|
119
|
+
// Scan folder structure
|
|
120
|
+
const scanDir = (dir, prefix = '') => {
|
|
121
|
+
const results = [];
|
|
122
|
+
try {
|
|
123
|
+
if (!fs.existsSync(dir))
|
|
124
|
+
return results;
|
|
125
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules')
|
|
128
|
+
continue;
|
|
129
|
+
const fullPath = path.join(prefix, entry.name);
|
|
130
|
+
if (entry.isDirectory()) {
|
|
131
|
+
results.push(fullPath + '/');
|
|
132
|
+
// Only go 2 levels deep
|
|
133
|
+
if (prefix.split('/').length < 2) {
|
|
134
|
+
results.push(...scanDir(path.join(dir, entry.name), fullPath));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Ignore scan errors
|
|
141
|
+
}
|
|
142
|
+
return results;
|
|
143
|
+
};
|
|
144
|
+
context.folderStructure = scanDir(cwd);
|
|
145
|
+
// Find schema path
|
|
146
|
+
const schemaPaths = [
|
|
147
|
+
'src/db/schema.ts',
|
|
148
|
+
'src/lib/db/schema.ts',
|
|
149
|
+
'db/schema.ts',
|
|
150
|
+
'prisma/schema.prisma',
|
|
151
|
+
'drizzle/schema.ts',
|
|
152
|
+
];
|
|
153
|
+
for (const schemaPath of schemaPaths) {
|
|
154
|
+
if (fs.existsSync(path.join(cwd, schemaPath))) {
|
|
155
|
+
context.schemaPath = schemaPath;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Find components path and list components
|
|
160
|
+
const componentPaths = ['src/components', 'components', 'app/components'];
|
|
161
|
+
for (const compPath of componentPaths) {
|
|
162
|
+
const fullPath = path.join(cwd, compPath);
|
|
163
|
+
if (fs.existsSync(fullPath)) {
|
|
164
|
+
context.componentsPath = compPath;
|
|
165
|
+
try {
|
|
166
|
+
const scanComponents = (dir, prefix = '') => {
|
|
167
|
+
const comps = [];
|
|
168
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
if (entry.name.startsWith('.'))
|
|
171
|
+
continue;
|
|
172
|
+
if (entry.isDirectory()) {
|
|
173
|
+
comps.push(...scanComponents(path.join(dir, entry.name), entry.name + '/'));
|
|
174
|
+
}
|
|
175
|
+
else if (entry.name.endsWith('.tsx') || entry.name.endsWith('.jsx')) {
|
|
176
|
+
comps.push(prefix + entry.name.replace(/\.(tsx|jsx)$/, ''));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return comps;
|
|
180
|
+
};
|
|
181
|
+
context.existingComponents = scanComponents(fullPath).slice(0, 50); // Limit to 50
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Ignore component scan errors
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Find services
|
|
190
|
+
const servicePaths = ['src/services', 'src/lib/services', 'services'];
|
|
191
|
+
for (const servPath of servicePaths) {
|
|
192
|
+
const fullPath = path.join(cwd, servPath);
|
|
193
|
+
if (fs.existsSync(fullPath)) {
|
|
194
|
+
try {
|
|
195
|
+
const entries = fs.readdirSync(fullPath);
|
|
196
|
+
context.existingServices = entries
|
|
197
|
+
.filter(e => e.endsWith('.ts') || e.endsWith('.js'))
|
|
198
|
+
.map(e => e.replace(/\.(ts|js)$/, ''))
|
|
199
|
+
.slice(0, 20);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Ignore service scan errors
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Find API routes
|
|
208
|
+
const apiPaths = ['src/app/api', 'app/api', 'pages/api'];
|
|
209
|
+
for (const apiPath of apiPaths) {
|
|
210
|
+
const fullPath = path.join(cwd, apiPath);
|
|
211
|
+
if (fs.existsSync(fullPath)) {
|
|
212
|
+
try {
|
|
213
|
+
const scanApiRoutes = (dir, prefix = '') => {
|
|
214
|
+
const routes = [];
|
|
215
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
216
|
+
for (const entry of entries) {
|
|
217
|
+
if (entry.name.startsWith('.'))
|
|
218
|
+
continue;
|
|
219
|
+
if (entry.isDirectory()) {
|
|
220
|
+
routes.push(...scanApiRoutes(path.join(dir, entry.name), prefix + '/' + entry.name));
|
|
221
|
+
}
|
|
222
|
+
else if (entry.name === 'route.ts' || entry.name === 'route.js') {
|
|
223
|
+
routes.push(prefix || '/');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return routes;
|
|
227
|
+
};
|
|
228
|
+
context.existingApiRoutes = scanApiRoutes(fullPath).slice(0, 30);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Ignore API route scan errors
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return context;
|
|
237
|
+
}
|
|
238
|
+
formatContextForPrompt(context) {
|
|
239
|
+
const lines = [];
|
|
240
|
+
lines.push(`Project: ${context.projectName}`);
|
|
241
|
+
if (context.uiLibrary) {
|
|
242
|
+
lines.push(`UI Library: ${context.uiLibrary}`);
|
|
243
|
+
}
|
|
244
|
+
if (context.schemaPath) {
|
|
245
|
+
lines.push(`Database Schema: ${context.schemaPath}`);
|
|
246
|
+
}
|
|
247
|
+
if (context.componentsPath && context.existingComponents.length > 0) {
|
|
248
|
+
lines.push(`Components Path: ${context.componentsPath}`);
|
|
249
|
+
lines.push(`Existing Components: ${context.existingComponents.slice(0, 20).join(', ')}`);
|
|
250
|
+
}
|
|
251
|
+
if (context.existingServices.length > 0) {
|
|
252
|
+
lines.push(`Existing Services: ${context.existingServices.join(', ')}`);
|
|
253
|
+
}
|
|
254
|
+
if (context.existingApiRoutes.length > 0) {
|
|
255
|
+
lines.push(`Existing API Routes: ${context.existingApiRoutes.join(', ')}`);
|
|
256
|
+
}
|
|
257
|
+
const features = [];
|
|
258
|
+
if (context.hasAuth)
|
|
259
|
+
features.push('auth');
|
|
260
|
+
if (context.hasDatabase)
|
|
261
|
+
features.push('database');
|
|
262
|
+
if (context.hasPayments)
|
|
263
|
+
features.push('payments');
|
|
264
|
+
if (features.length > 0) {
|
|
265
|
+
lines.push(`Has: ${features.join(', ')}`);
|
|
266
|
+
}
|
|
267
|
+
const relevantDeps = context.dependencies.filter(d => ['next', 'react', 'drizzle-orm', 'stripe', '@supabase/supabase-js', 'zod', 'react-hook-form', 'tailwindcss'].some(rd => d.includes(rd)));
|
|
268
|
+
if (relevantDeps.length > 0) {
|
|
269
|
+
lines.push(`Key Dependencies: ${relevantDeps.join(', ')}`);
|
|
270
|
+
}
|
|
271
|
+
return lines.join('\n');
|
|
272
|
+
}
|
|
273
|
+
setupHandlers() {
|
|
274
|
+
// List available tools
|
|
275
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
276
|
+
tools: [
|
|
277
|
+
{
|
|
278
|
+
name: 'optimize_and_build',
|
|
279
|
+
description: 'ALWAYS USE THIS FIRST for any coding request. Takes a simple user request, uses AI to analyze intent and detect relevant patterns, optimizes it into a production-ready prompt, and returns everything needed to build the feature correctly. No keyword matching - AI understands what you actually want to build.',
|
|
280
|
+
inputSchema: {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {
|
|
283
|
+
request: {
|
|
284
|
+
type: 'string',
|
|
285
|
+
description: 'The user\'s original request (e.g., "add login", "create checkout", "zoom animation on image")',
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
required: ['request'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'get_pattern',
|
|
293
|
+
description: 'Fetch a single CodeBakers pattern module by name. Use optimize_and_build instead for automatic pattern detection.',
|
|
294
|
+
inputSchema: {
|
|
295
|
+
type: 'object',
|
|
296
|
+
properties: {
|
|
297
|
+
pattern: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
description: 'Pattern name (e.g., "00-core", "01-database", "02-auth", "03-api", "04-frontend")',
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
required: ['pattern'],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: 'list_patterns',
|
|
307
|
+
description: 'List all available CodeBakers pattern modules.',
|
|
308
|
+
inputSchema: {
|
|
309
|
+
type: 'object',
|
|
310
|
+
properties: {},
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: 'get_patterns',
|
|
315
|
+
description: 'Fetch multiple CodeBakers patterns at once. Use optimize_and_build instead for automatic pattern detection.',
|
|
316
|
+
inputSchema: {
|
|
317
|
+
type: 'object',
|
|
318
|
+
properties: {
|
|
319
|
+
patterns: {
|
|
320
|
+
type: 'array',
|
|
321
|
+
items: { type: 'string' },
|
|
322
|
+
description: 'Array of pattern names to fetch (max 5)',
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
required: ['patterns'],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
}));
|
|
330
|
+
// Handle tool calls
|
|
331
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
332
|
+
if (!this.apiKey) {
|
|
333
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not logged in. Run `codebakers login` first.');
|
|
334
|
+
}
|
|
335
|
+
const { name, arguments: args } = request.params;
|
|
336
|
+
switch (name) {
|
|
337
|
+
case 'optimize_and_build':
|
|
338
|
+
return this.handleOptimizeAndBuild(args);
|
|
339
|
+
case 'get_pattern':
|
|
340
|
+
return this.handleGetPattern(args);
|
|
341
|
+
case 'list_patterns':
|
|
342
|
+
return this.handleListPatterns();
|
|
343
|
+
case 'get_patterns':
|
|
344
|
+
return this.handleGetPatterns(args);
|
|
345
|
+
default:
|
|
346
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async handleOptimizeAndBuild(args) {
|
|
351
|
+
const { request: userRequest } = args;
|
|
352
|
+
// Step 1: Gather project context
|
|
353
|
+
const context = this.gatherProjectContext();
|
|
354
|
+
const contextSummary = this.formatContextForPrompt(context);
|
|
355
|
+
// Step 2: Call API to optimize the prompt with context
|
|
356
|
+
// The API uses AI to analyze intent and detect patterns (no keyword matching)
|
|
357
|
+
const optimizeResponse = await fetch(`${this.apiUrl}/api/optimize-prompt`, {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'application/json',
|
|
361
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
362
|
+
},
|
|
363
|
+
body: JSON.stringify({
|
|
364
|
+
prompt: userRequest,
|
|
365
|
+
context: {
|
|
366
|
+
summary: contextSummary,
|
|
367
|
+
projectName: context.projectName,
|
|
368
|
+
uiLibrary: context.uiLibrary,
|
|
369
|
+
schemaPath: context.schemaPath,
|
|
370
|
+
componentsPath: context.componentsPath,
|
|
371
|
+
existingComponents: context.existingComponents.slice(0, 30),
|
|
372
|
+
existingServices: context.existingServices,
|
|
373
|
+
existingApiRoutes: context.existingApiRoutes,
|
|
374
|
+
hasAuth: context.hasAuth,
|
|
375
|
+
hasDatabase: context.hasDatabase,
|
|
376
|
+
hasPayments: context.hasPayments,
|
|
377
|
+
dependencies: context.dependencies.slice(0, 30),
|
|
378
|
+
},
|
|
379
|
+
}),
|
|
380
|
+
});
|
|
381
|
+
// Default values if API fails
|
|
382
|
+
let optimizedPrompt = userRequest;
|
|
383
|
+
let detectedFeature = 'Feature';
|
|
384
|
+
let patterns = ['00-core', '04-frontend']; // Default fallback
|
|
385
|
+
if (optimizeResponse.ok) {
|
|
386
|
+
const optimizeData = await optimizeResponse.json();
|
|
387
|
+
optimizedPrompt = optimizeData.optimizedPrompt || userRequest;
|
|
388
|
+
detectedFeature = optimizeData.featureName || 'Feature';
|
|
389
|
+
// Use AI-detected patterns from the API (no local keyword matching)
|
|
390
|
+
patterns = optimizeData.patterns || ['00-core', '04-frontend'];
|
|
391
|
+
}
|
|
392
|
+
// Step 3: Fetch all relevant patterns (as detected by AI)
|
|
393
|
+
const patternResult = await this.fetchPatterns(patterns);
|
|
394
|
+
// Step 4: Build the response showing the optimization with context
|
|
395
|
+
const patternContent = Object.entries(patternResult.patterns || {})
|
|
396
|
+
.map(([name, text]) => `## ${name}\n\n${text}`)
|
|
397
|
+
.join('\n\n---\n\n');
|
|
398
|
+
const response = `# 🪄 Prompt Optimizer (AI-Powered Intent Analysis)
|
|
399
|
+
|
|
400
|
+
## Your Request
|
|
401
|
+
"${userRequest}"
|
|
402
|
+
|
|
403
|
+
## Project Context Detected
|
|
404
|
+
${contextSummary}
|
|
405
|
+
|
|
406
|
+
## AI Analysis
|
|
407
|
+
- **Detected Intent:** ${detectedFeature}
|
|
408
|
+
- **Relevant Patterns:** ${patterns.join(', ')}
|
|
409
|
+
|
|
410
|
+
## Optimized Prompt (Production-Ready)
|
|
411
|
+
${optimizedPrompt}
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
# Pattern Documentation
|
|
416
|
+
|
|
417
|
+
${patternContent}
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
**IMPORTANT:** Use the optimized prompt above as your guide. It is tailored to THIS project's structure, existing components, and conventions. The AI analyzed your intent (not just keywords) to select the right patterns. The prompt includes:
|
|
422
|
+
- References to existing components and services you should reuse
|
|
423
|
+
- The correct file paths for this project
|
|
424
|
+
- Production requirements (error handling, loading states, validation, tests)
|
|
425
|
+
|
|
426
|
+
Show the user what their simple request was expanded into, then proceed with the implementation following the patterns above.`;
|
|
427
|
+
return {
|
|
428
|
+
content: [
|
|
429
|
+
{
|
|
430
|
+
type: 'text',
|
|
431
|
+
text: response,
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async handleGetPattern(args) {
|
|
437
|
+
const { pattern } = args;
|
|
438
|
+
// Check cache first
|
|
439
|
+
const cached = patternCache.get(pattern);
|
|
440
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
441
|
+
return {
|
|
442
|
+
content: [
|
|
443
|
+
{
|
|
444
|
+
type: 'text',
|
|
445
|
+
text: cached.content,
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
// Fetch from API
|
|
451
|
+
const result = await this.fetchPatterns([pattern]);
|
|
452
|
+
if (result.patterns && result.patterns[pattern]) {
|
|
453
|
+
const content = result.patterns[pattern];
|
|
454
|
+
// Cache the result
|
|
455
|
+
patternCache.set(pattern, { content, timestamp: Date.now() });
|
|
456
|
+
return {
|
|
457
|
+
content: [
|
|
458
|
+
{
|
|
459
|
+
type: 'text',
|
|
460
|
+
text: content,
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, `Pattern "${pattern}" not found. Use list_patterns to see available patterns.`);
|
|
466
|
+
}
|
|
467
|
+
async handleListPatterns() {
|
|
468
|
+
const response = await fetch(`${this.apiUrl}/api/patterns`, {
|
|
469
|
+
method: 'GET',
|
|
470
|
+
headers: {
|
|
471
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
if (!response.ok) {
|
|
475
|
+
const error = await response.json().catch(() => ({}));
|
|
476
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, error.error || 'Failed to fetch patterns');
|
|
477
|
+
}
|
|
478
|
+
const data = await response.json();
|
|
479
|
+
const patternList = data.patterns
|
|
480
|
+
.map((p) => `- ${p.name}`)
|
|
481
|
+
.join('\n');
|
|
482
|
+
return {
|
|
483
|
+
content: [
|
|
484
|
+
{
|
|
485
|
+
type: 'text',
|
|
486
|
+
text: `Available CodeBakers Patterns (${data.total} total):\n\n${patternList}\n\nTip: Use optimize_and_build for automatic AI-powered pattern detection.`,
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
async handleGetPatterns(args) {
|
|
492
|
+
const { patterns } = args;
|
|
493
|
+
if (patterns.length > 5) {
|
|
494
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Maximum 5 patterns per request');
|
|
495
|
+
}
|
|
496
|
+
const result = await this.fetchPatterns(patterns);
|
|
497
|
+
const content = Object.entries(result.patterns || {})
|
|
498
|
+
.map(([name, text]) => `## ${name}\n\n${text}`)
|
|
499
|
+
.join('\n\n---\n\n');
|
|
500
|
+
return {
|
|
501
|
+
content: [
|
|
502
|
+
{
|
|
503
|
+
type: 'text',
|
|
504
|
+
text: content ||
|
|
505
|
+
'No patterns found. Use list_patterns to see available patterns.',
|
|
506
|
+
},
|
|
507
|
+
],
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async fetchPatterns(patterns) {
|
|
511
|
+
const response = await fetch(`${this.apiUrl}/api/patterns`, {
|
|
512
|
+
method: 'POST',
|
|
513
|
+
headers: {
|
|
514
|
+
'Content-Type': 'application/json',
|
|
515
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
516
|
+
},
|
|
517
|
+
body: JSON.stringify({ patterns }),
|
|
518
|
+
});
|
|
519
|
+
if (!response.ok) {
|
|
520
|
+
const error = await response.json().catch(() => ({}));
|
|
521
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, error.error || 'Failed to fetch patterns');
|
|
522
|
+
}
|
|
523
|
+
return response.json();
|
|
524
|
+
}
|
|
525
|
+
async run() {
|
|
526
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
527
|
+
await this.server.connect(transport);
|
|
528
|
+
// Log to stderr so it doesn't interfere with stdio protocol
|
|
529
|
+
console.error('CodeBakers MCP server running on stdio');
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
// Export function for programmatic usage
|
|
533
|
+
async function runServer() {
|
|
534
|
+
const server = new CodeBakersServer();
|
|
535
|
+
await server.run();
|
|
536
|
+
}
|
|
537
|
+
// Run directly if executed as main module
|
|
538
|
+
const isMain = process.argv[1]?.includes('server');
|
|
539
|
+
if (isMain) {
|
|
540
|
+
runServer().catch((error) => {
|
|
541
|
+
console.error('Failed to start MCP server:', error);
|
|
542
|
+
process.exit(1);
|
|
543
|
+
});
|
|
544
|
+
}
|
package/package.json
CHANGED
|
@@ -1,61 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codebakers/cli",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "CodeBakers CLI -
|
|
3
|
+
"version": "1.1.6",
|
|
4
|
+
"description": "CodeBakers CLI - Production patterns for AI-assisted development",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"codebakers": "dist/index.js"
|
|
7
|
+
"codebakers": "./dist/index.js",
|
|
8
|
+
"codebakers-mcp": "./dist/mcp/server.js"
|
|
8
9
|
},
|
|
9
|
-
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "
|
|
12
|
-
"dev": "
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"test:watch": "vitest",
|
|
16
|
-
"test:coverage": "vitest run --coverage",
|
|
17
|
-
"test:unit": "vitest run tests/unit",
|
|
18
|
-
"test:integration": "vitest run tests/integration"
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"mcp": "tsx src/mcp/server.ts"
|
|
19
15
|
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
],
|
|
23
16
|
"keywords": [
|
|
24
17
|
"codebakers",
|
|
25
18
|
"ai",
|
|
26
|
-
"prompts",
|
|
27
19
|
"cursor",
|
|
28
20
|
"claude",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"mcp",
|
|
32
|
-
"cli"
|
|
21
|
+
"patterns",
|
|
22
|
+
"mcp"
|
|
33
23
|
],
|
|
34
24
|
"author": "CodeBakers",
|
|
35
25
|
"license": "MIT",
|
|
36
|
-
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "https://github.com/codebakers/cli"
|
|
39
|
-
},
|
|
40
|
-
"homepage": "https://codebakers.ai",
|
|
41
26
|
"dependencies": {
|
|
42
27
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
43
28
|
"chalk": "^5.3.0",
|
|
44
29
|
"commander": "^12.1.0",
|
|
45
|
-
"conf": "^
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"ora": "^8.0.1"
|
|
30
|
+
"conf": "^13.0.1",
|
|
31
|
+
"ora": "^8.1.1",
|
|
32
|
+
"openai": "^4.77.0"
|
|
49
33
|
},
|
|
50
34
|
"devDependencies": {
|
|
51
|
-
"@types/
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"tsup": "^8.0.2",
|
|
55
|
-
"typescript": "^5.3.3",
|
|
56
|
-
"vitest": "^1.2.0"
|
|
57
|
-
},
|
|
58
|
-
"engines": {
|
|
59
|
-
"node": ">=18"
|
|
35
|
+
"@types/node": "^20.10.0",
|
|
36
|
+
"tsx": "^4.7.0",
|
|
37
|
+
"typescript": "^5.3.0"
|
|
60
38
|
}
|
|
61
39
|
}
|