@prajwolkc/stk 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/mcp/server.js +351 -0
  2. package/package.json +3 -3
@@ -1008,6 +1008,357 @@ server.tool("stk_cost", "Track costs across your stack: Stripe fees, Vercel usag
1008
1008
  content: [{ type: "text", text: JSON.stringify({ costs }, null, 2) }],
1009
1009
  };
1010
1010
  });
1011
+ // ──────────────────────────────────────────
1012
+ // Brain: Supabase Knowledge Base Client
1013
+ // ──────────────────────────────────────────
1014
+ function getBrainClient() {
1015
+ const url = process.env.SUPABASE_URL;
1016
+ const key = process.env.SUPABASE_SERVICE_KEY;
1017
+ if (!url || !key)
1018
+ return null;
1019
+ return {
1020
+ async query(table, params = {}) {
1021
+ const searchParams = new URLSearchParams(params);
1022
+ const res = await fetch(`${url}/rest/v1/${table}?${searchParams}`, {
1023
+ headers: {
1024
+ apikey: key,
1025
+ Authorization: `Bearer ${key}`,
1026
+ "Content-Type": "application/json",
1027
+ Prefer: "count=exact",
1028
+ },
1029
+ });
1030
+ const count = res.headers.get("content-range")?.split("/")[1] ?? null;
1031
+ const data = await res.json();
1032
+ return { data, count, ok: res.ok };
1033
+ },
1034
+ async insert(table, row) {
1035
+ const res = await fetch(`${url}/rest/v1/${table}`, {
1036
+ method: "POST",
1037
+ headers: {
1038
+ apikey: key,
1039
+ Authorization: `Bearer ${key}`,
1040
+ "Content-Type": "application/json",
1041
+ Prefer: "return=representation",
1042
+ },
1043
+ body: JSON.stringify(row),
1044
+ });
1045
+ const data = await res.json();
1046
+ return { data, ok: res.ok };
1047
+ },
1048
+ };
1049
+ }
1050
+ // ──────────────────────────────────────────
1051
+ // Tool: stk_brain_search
1052
+ // ──────────────────────────────────────────
1053
+ server.tool("stk_brain_search", "Search the knowledge base for SaaS patterns, best practices, and architecture examples from top open-source projects (LangChain, Ollama, Transformers, llama.cpp, vLLM, AutoGen, OpenAI Cookbook). Use this when you need to know how successful projects solve specific problems.", {
1054
+ query: z.string().describe("What to search for (e.g., 'authentication', 'real-time updates', 'payment integration')"),
1055
+ category: z.string().optional().describe("Filter: architecture, auth, payments, database, api, deployment, testing, performance, security, ml, realtime, general"),
1056
+ }, async ({ query, category }) => {
1057
+ const brain = getBrainClient();
1058
+ if (!brain)
1059
+ return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
1060
+ // Try ilike search on content and title
1061
+ const words = query.split(" ").filter(w => w.length > 2);
1062
+ const searchWord = words[0] ?? query;
1063
+ const params = {
1064
+ or: `(title.ilike.%${searchWord}%,content.ilike.%${searchWord}%)`,
1065
+ limit: "10",
1066
+ order: "source",
1067
+ };
1068
+ if (category)
1069
+ params["category"] = `eq.${category}`;
1070
+ const { data, ok } = await brain.query("knowledge", params);
1071
+ if (!ok)
1072
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Query failed", data }) }] };
1073
+ return {
1074
+ content: [{
1075
+ type: "text",
1076
+ text: JSON.stringify({
1077
+ query,
1078
+ results: (data ?? []).map((r) => ({ source: r.source, category: r.category, title: r.title, content: r.content, tags: r.tags })),
1079
+ total: data?.length ?? 0,
1080
+ }, null, 2),
1081
+ }],
1082
+ };
1083
+ });
1084
+ // ──────────────────────────────────────────
1085
+ // Tool: stk_brain_patterns
1086
+ // ──────────────────────────────────────────
1087
+ server.tool("stk_brain_patterns", "Get best practice patterns for a specific feature. Returns how top projects implement auth, payments, real-time, caching, APIs, etc.", {
1088
+ feature: z.string().describe("The feature or pattern (e.g., 'authentication', 'webhooks', 'caching', 'model serving', 'fine-tuning')"),
1089
+ }, async ({ feature }) => {
1090
+ const brain = getBrainClient();
1091
+ if (!brain)
1092
+ return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
1093
+ const { data, ok } = await brain.query("knowledge", {
1094
+ or: `(title.ilike.%${feature}%,content.ilike.%${feature}%)`,
1095
+ limit: "15",
1096
+ order: "source",
1097
+ });
1098
+ if (!ok)
1099
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Query failed" }) }] };
1100
+ // Group by source
1101
+ const grouped = {};
1102
+ for (const item of data ?? []) {
1103
+ if (!grouped[item.source])
1104
+ grouped[item.source] = [];
1105
+ grouped[item.source].push({ title: item.title, category: item.category, content: item.content });
1106
+ }
1107
+ return {
1108
+ content: [{
1109
+ type: "text",
1110
+ text: JSON.stringify({
1111
+ feature,
1112
+ patterns: grouped,
1113
+ totalSources: Object.keys(grouped).length,
1114
+ totalPatterns: data?.length ?? 0,
1115
+ }, null, 2),
1116
+ }],
1117
+ };
1118
+ });
1119
+ // ──────────────────────────────────────────
1120
+ // Tool: stk_brain_stack
1121
+ // ──────────────────────────────────────────
1122
+ server.tool("stk_brain_stack", "Get recommendations specific to YOUR stack (Supabase + Vercel + Stripe + Node.js). Filters knowledge for patterns matching your technology choices.", {
1123
+ question: z.string().describe("What you want to build or solve (e.g., 'add user auth', 'implement webhooks', 'optimize queries')"),
1124
+ }, async ({ question }) => {
1125
+ const brain = getBrainClient();
1126
+ if (!brain)
1127
+ return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
1128
+ const words = question.split(" ").filter(w => w.length > 3);
1129
+ const searchWord = words[0] ?? question;
1130
+ const { data, ok } = await brain.query("knowledge", {
1131
+ or: `(content.ilike.%${searchWord}%,title.ilike.%${searchWord}%)`,
1132
+ limit: "10",
1133
+ });
1134
+ if (!ok)
1135
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Query failed" }) }] };
1136
+ return {
1137
+ content: [{
1138
+ type: "text",
1139
+ text: JSON.stringify({
1140
+ question,
1141
+ stack: ["Supabase", "Vercel", "Stripe", "Node.js/Express"],
1142
+ recommendations: (data ?? []).map((r) => ({ source: r.source, title: r.title, content: r.content, relevance: r.category })),
1143
+ total: data?.length ?? 0,
1144
+ }, null, 2),
1145
+ }],
1146
+ };
1147
+ });
1148
+ // ──────────────────────────────────────────
1149
+ // Tool: stk_brain_learn
1150
+ // ──────────────────────────────────────────
1151
+ server.tool("stk_brain_learn", "Save new knowledge to the brain. Use this to remember patterns, solutions, or learnings for future reference across all projects.", {
1152
+ title: z.string().describe("Short title"),
1153
+ content: z.string().describe("The knowledge — pattern, solution, or best practice"),
1154
+ source: z.string().optional().describe("Where this came from (e.g., 'project:worldchat', 'github:vercel/next.js')"),
1155
+ category: z.string().optional().describe("Category: architecture, auth, payments, database, api, deployment, testing, performance, security, ml, general"),
1156
+ tags: z.array(z.string()).optional().describe("Tags for searchability"),
1157
+ }, async ({ title, content, source, category, tags }) => {
1158
+ const brain = getBrainClient();
1159
+ if (!brain)
1160
+ return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
1161
+ const { data, ok } = await brain.insert("knowledge", {
1162
+ title,
1163
+ content,
1164
+ source: source ?? "manual",
1165
+ category: category ?? "general",
1166
+ tags: tags ?? [],
1167
+ created_at: new Date().toISOString(),
1168
+ });
1169
+ if (!ok)
1170
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Insert failed", data }) }] };
1171
+ return {
1172
+ content: [{
1173
+ type: "text",
1174
+ text: JSON.stringify({ learned: true, title, message: "Knowledge saved. I can recall this in future conversations." }, null, 2),
1175
+ }],
1176
+ };
1177
+ });
1178
+ // ──────────────────────────────────────────
1179
+ // Tool: stk_brain_stats
1180
+ // ──────────────────────────────────────────
1181
+ server.tool("stk_brain_stats", "Check what the brain knows — total knowledge entries, categories, sources, and coverage.", {}, async () => {
1182
+ const brain = getBrainClient();
1183
+ if (!brain)
1184
+ return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
1185
+ const { data, count } = await brain.query("knowledge", { select: "category,source", limit: "1000" });
1186
+ const categories = {};
1187
+ const sources = {};
1188
+ for (const row of data ?? []) {
1189
+ categories[row.category] = (categories[row.category] || 0) + 1;
1190
+ sources[row.source] = (sources[row.source] || 0) + 1;
1191
+ }
1192
+ return {
1193
+ content: [{
1194
+ type: "text",
1195
+ text: JSON.stringify({
1196
+ totalKnowledge: count ?? data?.length ?? 0,
1197
+ categories,
1198
+ sources,
1199
+ topSources: Object.entries(sources)
1200
+ .sort(([, a], [, b]) => b - a)
1201
+ .slice(0, 10)
1202
+ .map(([name, count]) => ({ name, count })),
1203
+ }, null, 2),
1204
+ }],
1205
+ };
1206
+ });
1207
+ // ──────────────────────────────────────────
1208
+ // Tool: stk_brain_claudemd
1209
+ // ──────────────────────────────────────────
1210
+ server.tool("stk_brain_claudemd", "Auto-generate a CLAUDE.md file for the current project. Analyzes the tech stack, project structure, services, and brain knowledge to create comprehensive project instructions for Claude Code.", {
1211
+ projectName: z.string().optional().describe("Project name (auto-detected from stk.config.json if omitted)"),
1212
+ write: z.boolean().optional().default(false).describe("Actually write the CLAUDE.md file to disk. If false, just returns the content for review."),
1213
+ }, async ({ projectName, write }) => {
1214
+ const config = loadConfig();
1215
+ const name = projectName ?? config.name ?? "my-project";
1216
+ const enabled = enabledServices(config);
1217
+ // Detect project info
1218
+ let gitBranch = "";
1219
+ let gitRemote = "";
1220
+ let packageJson = {};
1221
+ try {
1222
+ gitBranch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
1223
+ }
1224
+ catch { }
1225
+ try {
1226
+ gitRemote = execSync("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
1227
+ }
1228
+ catch { }
1229
+ try {
1230
+ const { readFileSync } = await import("fs");
1231
+ packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
1232
+ }
1233
+ catch { }
1234
+ // Detect tech stack
1235
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
1236
+ const stack = [];
1237
+ if (deps?.["next"])
1238
+ stack.push("Next.js");
1239
+ if (deps?.["react"])
1240
+ stack.push("React");
1241
+ if (deps?.["vue"])
1242
+ stack.push("Vue");
1243
+ if (deps?.["express"])
1244
+ stack.push("Express.js");
1245
+ if (deps?.["fastify"])
1246
+ stack.push("Fastify");
1247
+ if (deps?.["@supabase/supabase-js"])
1248
+ stack.push("Supabase");
1249
+ if (deps?.["stripe"])
1250
+ stack.push("Stripe");
1251
+ if (deps?.["prisma"] || deps?.["@prisma/client"])
1252
+ stack.push("Prisma");
1253
+ if (deps?.["mongoose"])
1254
+ stack.push("Mongoose/MongoDB");
1255
+ if (deps?.["redis"] || deps?.["ioredis"])
1256
+ stack.push("Redis");
1257
+ if (deps?.["tailwindcss"])
1258
+ stack.push("Tailwind CSS");
1259
+ if (deps?.["typescript"])
1260
+ stack.push("TypeScript");
1261
+ if (deps?.["zod"])
1262
+ stack.push("Zod");
1263
+ if (stack.length === 0 && Object.keys(deps ?? {}).length > 0) {
1264
+ stack.push("Node.js");
1265
+ }
1266
+ // Detect file structure
1267
+ let fileStructure = "";
1268
+ try {
1269
+ fileStructure = execSync("find . -maxdepth 3 -type f -name '*.ts' -o -name '*.js' -o -name '*.tsx' -o -name '*.jsx' -o -name '*.json' -o -name '*.css' -o -name '*.html' | grep -v node_modules | grep -v dist | grep -v .git | sort | head -40", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
1270
+ }
1271
+ catch { }
1272
+ // Build CLAUDE.md
1273
+ const lines = [];
1274
+ lines.push(`# ${name}\n`);
1275
+ // Project overview
1276
+ lines.push(`## Overview\n`);
1277
+ if (packageJson.description)
1278
+ lines.push(`${packageJson.description}\n`);
1279
+ if (stack.length > 0)
1280
+ lines.push(`**Tech Stack:** ${stack.join(", ")}\n`);
1281
+ if (gitRemote)
1282
+ lines.push(`**Repo:** ${gitRemote}`);
1283
+ if (gitBranch)
1284
+ lines.push(`**Main Branch:** ${gitBranch}`);
1285
+ lines.push("");
1286
+ // Services
1287
+ if (enabled.length > 0) {
1288
+ lines.push(`## Services\n`);
1289
+ for (const svc of enabled) {
1290
+ const svcName = svc.charAt(0).toUpperCase() + svc.slice(1);
1291
+ lines.push(`- **${svcName}** — configured and monitored via stk`);
1292
+ }
1293
+ lines.push("");
1294
+ }
1295
+ // Project structure
1296
+ if (fileStructure) {
1297
+ lines.push(`## Project Structure\n`);
1298
+ lines.push("```");
1299
+ lines.push(fileStructure);
1300
+ lines.push("```\n");
1301
+ }
1302
+ // Development commands
1303
+ lines.push(`## Development\n`);
1304
+ if (packageJson.scripts) {
1305
+ lines.push("```bash");
1306
+ if (packageJson.scripts.dev)
1307
+ lines.push(`npm run dev # Start dev server`);
1308
+ if (packageJson.scripts.build)
1309
+ lines.push(`npm run build # Build for production`);
1310
+ if (packageJson.scripts.test)
1311
+ lines.push(`npm test # Run tests`);
1312
+ if (packageJson.scripts.start)
1313
+ lines.push(`npm start # Start production server`);
1314
+ lines.push("```\n");
1315
+ }
1316
+ // stk tools available
1317
+ lines.push(`## stk Tools Available\n`);
1318
+ lines.push(`This project uses stk for infrastructure monitoring. Available commands:\n`);
1319
+ lines.push(`- \`stk_health\` — Check service health`);
1320
+ lines.push(`- \`stk_status\` — Full project overview`);
1321
+ lines.push(`- \`stk_logs\` — Production logs`);
1322
+ lines.push(`- \`stk_db\` — Query database`);
1323
+ lines.push(`- \`stk_alerts\` — Check for problems`);
1324
+ lines.push(`- \`stk_deploy\` — Deploy to production`);
1325
+ lines.push(`- \`stk_brain_search\` — Search knowledge base`);
1326
+ lines.push(`- \`stk_brain_learn\` — Save patterns for future use`);
1327
+ lines.push("");
1328
+ // Conventions
1329
+ lines.push(`## Conventions\n`);
1330
+ if (deps?.["typescript"]) {
1331
+ lines.push(`- TypeScript strict mode enabled`);
1332
+ }
1333
+ lines.push(`- Use existing patterns in the codebase before introducing new ones`);
1334
+ lines.push(`- Check stk_health before debugging production issues`);
1335
+ lines.push(`- Use stk_brain_search to find how other projects solve similar problems`);
1336
+ lines.push(`- Save useful patterns with stk_brain_learn for future reference`);
1337
+ lines.push("");
1338
+ // Environment
1339
+ lines.push(`## Environment Variables\n`);
1340
+ lines.push(`Environment variables are managed via \`.env\` files. See \`.env.example\` for required variables.`);
1341
+ if (enabled.includes("vercel"))
1342
+ lines.push(`Use \`stk_env_sync\` to compare local vs remote env vars.`);
1343
+ lines.push("");
1344
+ const content = lines.join("\n");
1345
+ if (write) {
1346
+ const { writeFileSync } = await import("fs");
1347
+ writeFileSync("CLAUDE.md", content);
1348
+ return {
1349
+ content: [{
1350
+ type: "text",
1351
+ text: JSON.stringify({ written: true, path: "CLAUDE.md", lines: content.split("\n").length }, null, 2),
1352
+ }],
1353
+ };
1354
+ }
1355
+ return {
1356
+ content: [{
1357
+ type: "text",
1358
+ text: JSON.stringify({ preview: true, content, note: "Call again with write: true to save to disk" }, null, 2),
1359
+ }],
1360
+ };
1361
+ });
1011
1362
  // Helper
1012
1363
  function detectGitHubRepo() {
1013
1364
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@prajwolkc/stk",
3
- "version": "0.3.0",
4
- "description": "One CLI to deploy, monitor, and debug your entire stack. Health checks, deploy watching, env sync, logs, and GitHub issues — all from one command.",
3
+ "version": "0.4.1",
4
+ "description": "One CLI to deploy, monitor, debug, and learn about your entire stack. Infrastructure monitoring, knowledge base brain, deploy watching, and GitHub issues — all from one command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "prajwolkc",
@@ -35,7 +35,7 @@
35
35
  "LICENSE"
36
36
  ],
37
37
  "engines": {
38
- "node": ">=18"
38
+ "node": ">=20"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc",