mgcheck 0.1.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.
@@ -0,0 +1,323 @@
1
+ // src/installer/index.ts
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
3
+ import { resolve, join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import * as readline from "readline/promises";
6
+ import { stdin as input, stdout as output } from "process";
7
+ import os from "os";
8
+ import chalk from "chalk";
9
+ function detectClients() {
10
+ const geminiDir = join(os.homedir(), ".gemini");
11
+ const antigravity = existsSync(geminiDir);
12
+ const localCursor = existsSync(join(process.cwd(), ".cursor"));
13
+ let systemCursor = false;
14
+ if (process.platform === "win32") {
15
+ systemCursor = existsSync(join(process.env.APPDATA || "", "Cursor")) || existsSync(join(process.env.LOCALAPPDATA || "", "Programs", "cursor"));
16
+ } else if (process.platform === "darwin") {
17
+ systemCursor = existsSync("/Applications/Cursor.app") || existsSync(join(os.homedir(), "Applications", "Cursor.app"));
18
+ } else {
19
+ systemCursor = existsSync(join(os.homedir(), ".config", "Cursor"));
20
+ }
21
+ const cursor = localCursor || systemCursor;
22
+ let claudeDir = "";
23
+ if (process.platform === "win32") {
24
+ claudeDir = join(process.env.APPDATA || "", "Claude");
25
+ } else if (process.platform === "darwin") {
26
+ claudeDir = join(os.homedir(), "Library", "Application Support", "Claude");
27
+ } else {
28
+ claudeDir = join(os.homedir(), ".config", "Claude");
29
+ }
30
+ const claude = existsSync(claudeDir);
31
+ return {
32
+ antigravity,
33
+ cursor,
34
+ claude,
35
+ hasAny: antigravity || cursor || claude
36
+ };
37
+ }
38
+ function setupMcp(options = {}) {
39
+ const targetClient = options.client || "all";
40
+ const currentDir = dirname(fileURLToPath(import.meta.url));
41
+ const serverPath = resolve(currentDir, "./mcp/server.js");
42
+ const fallbackPath = resolve(currentDir, "../dist/mcp/server.js");
43
+ const finalServerPath = existsSync(serverPath) ? serverPath : fallbackPath;
44
+ const results = [];
45
+ if (targetClient === "all" || targetClient === "antigravity") {
46
+ try {
47
+ const geminiConfigDir = join(os.homedir(), ".gemini", "config");
48
+ const mcpConfigFile = join(geminiConfigDir, "mcp_config.json");
49
+ if (!existsSync(geminiConfigDir)) {
50
+ mkdirSync(geminiConfigDir, { recursive: true });
51
+ }
52
+ let config = { mcpServers: {} };
53
+ if (existsSync(mcpConfigFile)) {
54
+ try {
55
+ config = JSON.parse(readFileSync(mcpConfigFile, "utf-8"));
56
+ if (!config.mcpServers) config.mcpServers = {};
57
+ } catch {
58
+ config = { mcpServers: {} };
59
+ }
60
+ }
61
+ config.mcpServers["migration-guardian"] = {
62
+ command: "node",
63
+ args: [finalServerPath]
64
+ };
65
+ writeFileSync(mcpConfigFile, JSON.stringify(config, null, 2), "utf-8");
66
+ results.push(chalk.green(" \u2705 Google Antigravity") + chalk.dim(` (${mcpConfigFile})`));
67
+ } catch (err) {
68
+ results.push(chalk.yellow(" \u26A0\uFE0F Google Antigravity: ") + err.message);
69
+ }
70
+ }
71
+ if (targetClient === "all" || targetClient === "cursor") {
72
+ try {
73
+ const cursorDir = join(process.cwd(), ".cursor");
74
+ const cursorFile = join(cursorDir, "mcp.json");
75
+ if (!existsSync(cursorDir)) {
76
+ mkdirSync(cursorDir, { recursive: true });
77
+ }
78
+ let config = { mcpServers: {} };
79
+ if (existsSync(cursorFile)) {
80
+ try {
81
+ config = JSON.parse(readFileSync(cursorFile, "utf-8"));
82
+ if (!config.mcpServers) config.mcpServers = {};
83
+ } catch {
84
+ config = { mcpServers: {} };
85
+ }
86
+ }
87
+ config.mcpServers["migration-guardian"] = {
88
+ command: "node",
89
+ args: [finalServerPath]
90
+ };
91
+ writeFileSync(cursorFile, JSON.stringify(config, null, 2), "utf-8");
92
+ results.push(chalk.green(" \u2705 Cursor IDE") + chalk.dim(` (${cursorFile})`));
93
+ } catch (err) {
94
+ results.push(chalk.yellow(" \u26A0\uFE0F Cursor IDE: ") + err.message);
95
+ }
96
+ }
97
+ if (targetClient === "all" || targetClient === "claude") {
98
+ try {
99
+ let claudeDir = "";
100
+ if (process.platform === "win32") {
101
+ claudeDir = join(process.env.APPDATA || "", "Claude");
102
+ } else if (process.platform === "darwin") {
103
+ claudeDir = join(os.homedir(), "Library", "Application Support", "Claude");
104
+ } else {
105
+ claudeDir = join(os.homedir(), ".config", "Claude");
106
+ }
107
+ const claudeConfigFile = join(claudeDir, "claude_desktop_config.json");
108
+ if (existsSync(claudeDir)) {
109
+ let config = { mcpServers: {} };
110
+ if (existsSync(claudeConfigFile)) {
111
+ try {
112
+ config = JSON.parse(readFileSync(claudeConfigFile, "utf-8"));
113
+ if (!config.mcpServers) config.mcpServers = {};
114
+ } catch {
115
+ config = { mcpServers: {} };
116
+ }
117
+ }
118
+ config.mcpServers["migration-guardian"] = {
119
+ command: "node",
120
+ args: [finalServerPath]
121
+ };
122
+ writeFileSync(claudeConfigFile, JSON.stringify(config, null, 2), "utf-8");
123
+ } else if (targetClient === "claude") {
124
+ results.push(chalk.dim(" \u2139\uFE0F Claude Desktop not installed (skipped)"));
125
+ }
126
+ } catch (err) {
127
+ results.push(chalk.yellow(" \u26A0\uFE0F Claude Desktop: ") + err.message);
128
+ }
129
+ }
130
+ const ruleResults = injectAgentRules(process.cwd());
131
+ results.push(...ruleResults);
132
+ return results;
133
+ }
134
+ var GUARDIAN_RULE_BANNER = "<!-- MIGRATION GUARDIAN SAFETY RULES -->";
135
+ var GUARDIAN_RULE_CONTENT = `${GUARDIAN_RULE_BANNER}
136
+ ## Database Migration Safety (Enforced by Migration Guardian)
137
+
138
+ When creating, modifying, or executing database migrations (SQL, Prisma, Drizzle, TypeORM, Knex, etc.):
139
+ 1. **NEVER execute raw DDL or database migrations directly** on local, staging, or production databases without prior safety checks.
140
+ 2. **ALWAYS use Migration Guardian (\`mgcheck\`)**:
141
+ - If MCP tools are available, invoke \\\`check_migration\\\` (or \\\`analyze_migration\\\` for static analysis).
142
+ - Alternatively, run \\\`npx mgcheck analyze <path>\\\` or \\\`npx mgcheck run <path> --provider pglite\\\` in terminal.
143
+ 3. **Follow Zero-Downtime Migration Standards**:
144
+ - \\\`MG001\\\`: Always use \\\`CREATE INDEX CONCURRENTLY\\\` instead of blocking indexes.
145
+ - \\\`MG002\\\`: Never use \\\`ALTER COLUMN TYPE\\\` directly (use expand/contract pattern).
146
+ - \\\`MG003\\\`: Always provide a \\\`DEFAULT\\\` value when adding \\\`NOT NULL\\\` columns.
147
+ - \\\`MG004\\\`: Add constraints with \\\`NOT VALID\\\`, then validate them in a subsequent step.
148
+ - \\\`MG005\\\` / \\\`MG006\\\`: Never drop columns or tables without explicit user confirmation.
149
+ - \\\`MG009\\\`: Always set \\\`SET lock_timeout = '5s';\\\` before risky DDL.
150
+ - \\\`MG010\\\`: Provide a rollback / down migration file whenever applicable.
151
+ `;
152
+ function appendOrWriteRule(filePath, name) {
153
+ try {
154
+ const parentDir = dirname(filePath);
155
+ if (!existsSync(parentDir)) {
156
+ mkdirSync(parentDir, { recursive: true });
157
+ }
158
+ if (existsSync(filePath)) {
159
+ const existing = readFileSync(filePath, "utf-8");
160
+ if (existing.includes(GUARDIAN_RULE_BANNER) || existing.includes("Migration Guardian")) {
161
+ return null;
162
+ }
163
+ writeFileSync(filePath, `${existing.trimEnd()}
164
+
165
+ ${GUARDIAN_RULE_CONTENT}`, "utf-8");
166
+ return chalk.green(` \u2705 Updated ${name}`) + chalk.dim(` (${filePath})`);
167
+ } else {
168
+ writeFileSync(filePath, GUARDIAN_RULE_CONTENT, "utf-8");
169
+ return chalk.green(` \u2705 Created ${name}`) + chalk.dim(` (${filePath})`);
170
+ }
171
+ } catch (err) {
172
+ return chalk.yellow(` \u26A0\uFE0F ${name}: `) + err.message;
173
+ }
174
+ }
175
+ function injectAgentRules(workspaceDir = process.cwd()) {
176
+ const results = [];
177
+ const cursorRuleFile = join(workspaceDir, ".cursor", "rules", "migration-guardian.mdc");
178
+ const resCursorMdc = appendOrWriteRule(cursorRuleFile, "Cursor Rule (.cursor/rules)");
179
+ if (resCursorMdc) results.push(resCursorMdc);
180
+ const cursorRulesLegacy = join(workspaceDir, ".cursorrules");
181
+ const resCursorLegacy = appendOrWriteRule(cursorRulesLegacy, "Cursor Rules (.cursorrules)");
182
+ if (resCursorLegacy) results.push(resCursorLegacy);
183
+ const claudeMd = join(workspaceDir, "CLAUDE.md");
184
+ const resClaude = appendOrWriteRule(claudeMd, "Claude Rules (CLAUDE.md)");
185
+ if (resClaude) results.push(resClaude);
186
+ const agentsMd = join(workspaceDir, "AGENTS.md");
187
+ const resAgents = appendOrWriteRule(agentsMd, "Agent Rules (AGENTS.md)");
188
+ if (resAgents) results.push(resAgents);
189
+ const windsurfRules = join(workspaceDir, ".windsurfrules");
190
+ const resWindsurf = appendOrWriteRule(windsurfRules, "Windsurf Rules (.windsurfrules)");
191
+ if (resWindsurf) results.push(resWindsurf);
192
+ const copilotInstructions = join(workspaceDir, ".github", "copilot-instructions.md");
193
+ const resCopilot = appendOrWriteRule(copilotInstructions, "GitHub Copilot Instructions");
194
+ if (resCopilot) results.push(resCopilot);
195
+ return results;
196
+ }
197
+ function verifyConnections() {
198
+ const lines = [];
199
+ const antigravityConfig = join(os.homedir(), ".gemini", "config", "mcp_config.json");
200
+ if (existsSync(antigravityConfig)) {
201
+ try {
202
+ const cfg = JSON.parse(readFileSync(antigravityConfig, "utf-8"));
203
+ if (cfg?.mcpServers?.["migration-guardian"]) {
204
+ lines.push(chalk.green(" \u2705 Connected to Google Antigravity"));
205
+ }
206
+ } catch {
207
+ }
208
+ }
209
+ const cursorConfig = join(process.cwd(), ".cursor", "mcp.json");
210
+ if (existsSync(cursorConfig)) {
211
+ try {
212
+ const cfg = JSON.parse(readFileSync(cursorConfig, "utf-8"));
213
+ if (cfg?.mcpServers?.["migration-guardian"]) {
214
+ lines.push(chalk.green(" \u2705 Connected to Cursor IDE"));
215
+ }
216
+ } catch {
217
+ }
218
+ }
219
+ let claudeConfig = "";
220
+ if (process.platform === "win32") {
221
+ claudeConfig = join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
222
+ } else if (process.platform === "darwin") {
223
+ claudeConfig = join(os.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
224
+ } else {
225
+ claudeConfig = join(os.homedir(), ".config", "Claude", "claude_desktop_config.json");
226
+ }
227
+ if (existsSync(claudeConfig)) {
228
+ try {
229
+ const cfg = JSON.parse(readFileSync(claudeConfig, "utf-8"));
230
+ if (cfg?.mcpServers?.["migration-guardian"]) {
231
+ lines.push(chalk.green(" \u2705 Connected to Claude Desktop"));
232
+ }
233
+ } catch {
234
+ }
235
+ }
236
+ return lines;
237
+ }
238
+ async function runInteractiveWizard(options = {}) {
239
+ console.log("");
240
+ console.log(chalk.bold.cyan(" \u{1F6E1}\uFE0F Migration Guardian") + chalk.dim(" (mgcheck) \u2014 Database Migration Safety"));
241
+ console.log(chalk.dim(" Catch unsafe database migrations before they hit production.\n"));
242
+ const detected = detectClients();
243
+ const detectedList = [];
244
+ if (detected.antigravity) detectedList.push("Google Antigravity");
245
+ if (detected.cursor) detectedList.push("Cursor IDE");
246
+ if (detected.claude) detectedList.push("Claude Desktop");
247
+ if (detectedList.length > 0) {
248
+ console.log(chalk.bold(" Detected AI Clients on your system:"));
249
+ for (const client of detectedList) {
250
+ console.log(` ${chalk.cyan("\u2022")} ${chalk.white(client)}`);
251
+ }
252
+ console.log("");
253
+ } else {
254
+ console.log(chalk.dim(" Configuring MCP integration for your AI editors (Antigravity, Cursor, Claude)...\n"));
255
+ }
256
+ let shouldProceed = options.yes ?? false;
257
+ if (!shouldProceed) {
258
+ const promptText = detectedList.length > 0 ? ` Connect Migration Guardian to ${detectedList.join(" & ")}? ${chalk.dim("[Y/n]")} ` : ` Connect Migration Guardian to your AI agents? ${chalk.dim("[Y/n]")} `;
259
+ const rl = readline.createInterface({ input, output });
260
+ try {
261
+ const answer = await rl.question(promptText);
262
+ const trimmed = answer.trim().toLowerCase();
263
+ shouldProceed = trimmed === "" || trimmed === "y" || trimmed === "yes";
264
+ } finally {
265
+ rl.close();
266
+ }
267
+ }
268
+ if (!shouldProceed) {
269
+ console.log(chalk.dim("\n Setup skipped."));
270
+ console.log(chalk.white(" You can connect anytime by running: ") + chalk.cyan("npx mgcheck setup"));
271
+ console.log(chalk.white(" Or run checks manually with: ") + chalk.cyan("npx mgcheck run <migration.sql>\n"));
272
+ return;
273
+ }
274
+ console.log(chalk.dim("\n Configuring MCP integrations...\n"));
275
+ const results = setupMcp(options);
276
+ for (const res of results) {
277
+ console.log(res);
278
+ }
279
+ console.log("");
280
+ console.log(chalk.bold(" \u{1F517} Connection Status"));
281
+ const verified = verifyConnections();
282
+ if (verified.length === 0) {
283
+ console.log(chalk.yellow(" \u26A0 No active connections found. Run ") + chalk.cyan("mgcheck setup") + chalk.yellow(" to retry."));
284
+ } else {
285
+ for (const v of verified) {
286
+ console.log(v);
287
+ }
288
+ }
289
+ console.log(chalk.bold.green("\n \u{1F389} Migration Guardian is ready!"));
290
+ console.log(chalk.white(" Your AI assistant will now automatically check migrations before executing.\n"));
291
+ console.log(chalk.dim(" Tools available to your AI:"));
292
+ console.log(chalk.dim(" \u2022 check_migration \u2014 shadow database execution + 10 AST safety rules"));
293
+ console.log(chalk.dim(" \u2022 analyze_migration \u2014 fast static SQL linting\n"));
294
+ console.log(chalk.dim(" Manual CLI Commands:"));
295
+ console.log(chalk.dim(" \u2022 mgcheck run <path> \u2014 analyze + shadow DB execution"));
296
+ console.log(chalk.dim(" \u2022 mgcheck analyze <path> \u2014 static analysis only\n"));
297
+ console.log(chalk.bold.cyan(" \u{1F441}\uFE0F Watch Mode \u2014 Listening for AI migration activity..."));
298
+ console.log(chalk.dim(" Press Ctrl+C to stop.\n"));
299
+ console.log(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
300
+ console.log("");
301
+ const { watchActivityLog, formatActivityEntry, LOG_FILE } = await import("./activity-log-ETNHCZ7B.js");
302
+ writeFileSync(LOG_FILE, "", "utf-8");
303
+ const stopWatching = watchActivityLog((entry) => {
304
+ console.log(formatActivityEntry(entry));
305
+ console.log("");
306
+ });
307
+ const keepAlive = setInterval(() => {
308
+ }, 1e3 * 60 * 60);
309
+ process.on("SIGINT", () => {
310
+ clearInterval(keepAlive);
311
+ stopWatching();
312
+ console.log(chalk.dim("\n Migration Guardian stopped. Goodbye! \u{1F44B}\n"));
313
+ process.exit(0);
314
+ });
315
+ }
316
+ export {
317
+ detectClients,
318
+ injectAgentRules,
319
+ runInteractiveWizard,
320
+ setupMcp,
321
+ verifyConnections
322
+ };
323
+ //# sourceMappingURL=installer-KGWDJ6OR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/installer/index.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { resolve, join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport * as readline from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport os from 'node:os';\nimport chalk from 'chalk';\n\nexport interface SetupOptions {\n client?: 'antigravity' | 'claude' | 'cursor' | 'all';\n yes?: boolean;\n}\n\nexport interface DetectResult {\n antigravity: boolean;\n cursor: boolean;\n claude: boolean;\n hasAny: boolean;\n}\n\n/**\n * Detect which AI editors/clients are installed on the user's system or workspace.\n */\nexport function detectClients(): DetectResult {\n // 1. Antigravity check\n const geminiDir = join(os.homedir(), '.gemini');\n const antigravity = existsSync(geminiDir);\n\n // 2. Cursor check (local .cursor or system Cursor install)\n const localCursor = existsSync(join(process.cwd(), '.cursor'));\n let systemCursor = false;\n if (process.platform === 'win32') {\n systemCursor = existsSync(join(process.env.APPDATA || '', 'Cursor')) ||\n existsSync(join(process.env.LOCALAPPDATA || '', 'Programs', 'cursor'));\n } else if (process.platform === 'darwin') {\n systemCursor = existsSync('/Applications/Cursor.app') ||\n existsSync(join(os.homedir(), 'Applications', 'Cursor.app'));\n } else {\n systemCursor = existsSync(join(os.homedir(), '.config', 'Cursor'));\n }\n const cursor = localCursor || systemCursor;\n\n // 3. Claude Desktop check\n let claudeDir = '';\n if (process.platform === 'win32') {\n claudeDir = join(process.env.APPDATA || '', 'Claude');\n } else if (process.platform === 'darwin') {\n claudeDir = join(os.homedir(), 'Library', 'Application Support', 'Claude');\n } else {\n claudeDir = join(os.homedir(), '.config', 'Claude');\n }\n const claude = existsSync(claudeDir);\n\n return {\n antigravity,\n cursor,\n claude,\n hasAny: antigravity || cursor || claude,\n };\n}\n\n/**\n * Apply the MCP server configuration to the specified clients.\n */\nexport function setupMcp(options: SetupOptions = {}): string[] {\n const targetClient = options.client || 'all';\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const serverPath = resolve(currentDir, './mcp/server.js');\n const fallbackPath = resolve(currentDir, '../dist/mcp/server.js');\n const finalServerPath = existsSync(serverPath) ? serverPath : fallbackPath;\n const results: string[] = [];\n\n // 1. Antigravity Configuration (~/.gemini/config/mcp_config.json)\n if (targetClient === 'all' || targetClient === 'antigravity') {\n try {\n const geminiConfigDir = join(os.homedir(), '.gemini', 'config');\n const mcpConfigFile = join(geminiConfigDir, 'mcp_config.json');\n\n if (!existsSync(geminiConfigDir)) {\n mkdirSync(geminiConfigDir, { recursive: true });\n }\n\n let config: any = { mcpServers: {} };\n if (existsSync(mcpConfigFile)) {\n try {\n config = JSON.parse(readFileSync(mcpConfigFile, 'utf-8'));\n if (!config.mcpServers) config.mcpServers = {};\n } catch {\n config = { mcpServers: {} };\n }\n }\n\n config.mcpServers['migration-guardian'] = {\n command: 'node',\n args: [finalServerPath],\n };\n\n writeFileSync(mcpConfigFile, JSON.stringify(config, null, 2), 'utf-8');\n results.push(chalk.green(' ✅ Google Antigravity') + chalk.dim(` (${mcpConfigFile})`));\n } catch (err) {\n results.push(chalk.yellow(' ⚠️ Google Antigravity: ') + (err as Error).message);\n }\n }\n\n // 2. Cursor Configuration (.cursor/mcp.json in workspace)\n if (targetClient === 'all' || targetClient === 'cursor') {\n try {\n const cursorDir = join(process.cwd(), '.cursor');\n const cursorFile = join(cursorDir, 'mcp.json');\n\n if (!existsSync(cursorDir)) {\n mkdirSync(cursorDir, { recursive: true });\n }\n\n let config: any = { mcpServers: {} };\n if (existsSync(cursorFile)) {\n try {\n config = JSON.parse(readFileSync(cursorFile, 'utf-8'));\n if (!config.mcpServers) config.mcpServers = {};\n } catch {\n config = { mcpServers: {} };\n }\n }\n\n config.mcpServers['migration-guardian'] = {\n command: 'node',\n args: [finalServerPath],\n };\n\n writeFileSync(cursorFile, JSON.stringify(config, null, 2), 'utf-8');\n results.push(chalk.green(' ✅ Cursor IDE') + chalk.dim(` (${cursorFile})`));\n } catch (err) {\n results.push(chalk.yellow(' ⚠️ Cursor IDE: ') + (err as Error).message);\n }\n }\n\n // 3. Claude Desktop Configuration\n if (targetClient === 'all' || targetClient === 'claude') {\n try {\n let claudeDir = '';\n if (process.platform === 'win32') {\n claudeDir = join(process.env.APPDATA || '', 'Claude');\n } else if (process.platform === 'darwin') {\n claudeDir = join(os.homedir(), 'Library', 'Application Support', 'Claude');\n } else {\n claudeDir = join(os.homedir(), '.config', 'Claude');\n }\n\n const claudeConfigFile = join(claudeDir, 'claude_desktop_config.json');\n\n if (existsSync(claudeDir)) {\n let config: any = { mcpServers: {} };\n if (existsSync(claudeConfigFile)) {\n try {\n config = JSON.parse(readFileSync(claudeConfigFile, 'utf-8'));\n if (!config.mcpServers) config.mcpServers = {};\n } catch {\n config = { mcpServers: {} };\n }\n }\n\n config.mcpServers['migration-guardian'] = {\n command: 'node',\n args: [finalServerPath],\n };\n\n writeFileSync(claudeConfigFile, JSON.stringify(config, null, 2), 'utf-8');\n } else if (targetClient === 'claude') {\n results.push(chalk.dim(' ℹ️ Claude Desktop not installed (skipped)'));\n }\n } catch (err) {\n results.push(chalk.yellow(' ⚠️ Claude Desktop: ') + (err as Error).message);\n }\n }\n\n // 4. Inject Agent Rule Files into current workspace\n const ruleResults = injectAgentRules(process.cwd());\n results.push(...ruleResults);\n\n return results;\n}\n\nconst GUARDIAN_RULE_BANNER = '<!-- MIGRATION GUARDIAN SAFETY RULES -->';\nconst GUARDIAN_RULE_CONTENT = `${GUARDIAN_RULE_BANNER}\n## Database Migration Safety (Enforced by Migration Guardian)\n\nWhen creating, modifying, or executing database migrations (SQL, Prisma, Drizzle, TypeORM, Knex, etc.):\n1. **NEVER execute raw DDL or database migrations directly** on local, staging, or production databases without prior safety checks.\n2. **ALWAYS use Migration Guardian (\\`mgcheck\\`)**:\n - If MCP tools are available, invoke \\\\\\`check_migration\\\\\\` (or \\\\\\`analyze_migration\\\\\\` for static analysis).\n - Alternatively, run \\\\\\`npx mgcheck analyze <path>\\\\\\` or \\\\\\`npx mgcheck run <path> --provider pglite\\\\\\` in terminal.\n3. **Follow Zero-Downtime Migration Standards**:\n - \\\\\\`MG001\\\\\\`: Always use \\\\\\`CREATE INDEX CONCURRENTLY\\\\\\` instead of blocking indexes.\n - \\\\\\`MG002\\\\\\`: Never use \\\\\\`ALTER COLUMN TYPE\\\\\\` directly (use expand/contract pattern).\n - \\\\\\`MG003\\\\\\`: Always provide a \\\\\\`DEFAULT\\\\\\` value when adding \\\\\\`NOT NULL\\\\\\` columns.\n - \\\\\\`MG004\\\\\\`: Add constraints with \\\\\\`NOT VALID\\\\\\`, then validate them in a subsequent step.\n - \\\\\\`MG005\\\\\\` / \\\\\\`MG006\\\\\\`: Never drop columns or tables without explicit user confirmation.\n - \\\\\\`MG009\\\\\\`: Always set \\\\\\`SET lock_timeout = '5s';\\\\\\` before risky DDL.\n - \\\\\\`MG010\\\\\\`: Provide a rollback / down migration file whenever applicable.\n`;\n\n/**\n * Append or create AI agent rule files (.cursorrules, CLAUDE.md, AGENTS.md, etc.)\n */\nfunction appendOrWriteRule(filePath: string, name: string): string | null {\n try {\n const parentDir = dirname(filePath);\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true });\n }\n\n if (existsSync(filePath)) {\n const existing = readFileSync(filePath, 'utf-8');\n if (existing.includes(GUARDIAN_RULE_BANNER) || existing.includes('Migration Guardian')) {\n return null; // Already present\n }\n writeFileSync(filePath, `${existing.trimEnd()}\\n\\n${GUARDIAN_RULE_CONTENT}`, 'utf-8');\n return chalk.green(` ✅ Updated ${name}`) + chalk.dim(` (${filePath})`);\n } else {\n writeFileSync(filePath, GUARDIAN_RULE_CONTENT, 'utf-8');\n return chalk.green(` ✅ Created ${name}`) + chalk.dim(` (${filePath})`);\n }\n } catch (err) {\n return chalk.yellow(` ⚠️ ${name}: `) + (err as Error).message;\n }\n}\n\n/**\n * Injects Migration Guardian safety rules into AI rule files across popular AI IDEs and agents.\n */\nexport function injectAgentRules(workspaceDir: string = process.cwd()): string[] {\n const results: string[] = [];\n\n // 1. Cursor IDE Rules (.cursor/rules/migration-guardian.mdc and .cursorrules)\n const cursorRuleFile = join(workspaceDir, '.cursor', 'rules', 'migration-guardian.mdc');\n const resCursorMdc = appendOrWriteRule(cursorRuleFile, 'Cursor Rule (.cursor/rules)');\n if (resCursorMdc) results.push(resCursorMdc);\n\n const cursorRulesLegacy = join(workspaceDir, '.cursorrules');\n const resCursorLegacy = appendOrWriteRule(cursorRulesLegacy, 'Cursor Rules (.cursorrules)');\n if (resCursorLegacy) results.push(resCursorLegacy);\n\n // 2. Claude Code / Claude Desktop Rules (CLAUDE.md)\n const claudeMd = join(workspaceDir, 'CLAUDE.md');\n const resClaude = appendOrWriteRule(claudeMd, 'Claude Rules (CLAUDE.md)');\n if (resClaude) results.push(resClaude);\n\n // 3. Antigravity & General Agent Rules (AGENTS.md)\n const agentsMd = join(workspaceDir, 'AGENTS.md');\n const resAgents = appendOrWriteRule(agentsMd, 'Agent Rules (AGENTS.md)');\n if (resAgents) results.push(resAgents);\n\n // 4. Windsurf Rules (.windsurfrules)\n const windsurfRules = join(workspaceDir, '.windsurfrules');\n const resWindsurf = appendOrWriteRule(windsurfRules, 'Windsurf Rules (.windsurfrules)');\n if (resWindsurf) results.push(resWindsurf);\n\n // 5. GitHub Copilot Instructions (.github/copilot-instructions.md)\n const copilotInstructions = join(workspaceDir, '.github', 'copilot-instructions.md');\n const resCopilot = appendOrWriteRule(copilotInstructions, 'GitHub Copilot Instructions');\n if (resCopilot) results.push(resCopilot);\n\n return results;\n}\n\n/**\n * Verify that migration-guardian is present in each AI client's MCP config.\n * Returns human-readable status lines.\n */\nexport function verifyConnections(): string[] {\n const lines: string[] = [];\n\n // 1. Antigravity\n const antigravityConfig = join(os.homedir(), '.gemini', 'config', 'mcp_config.json');\n if (existsSync(antigravityConfig)) {\n try {\n const cfg = JSON.parse(readFileSync(antigravityConfig, 'utf-8'));\n if (cfg?.mcpServers?.['migration-guardian']) {\n lines.push(chalk.green(' ✅ Connected to Google Antigravity'));\n }\n } catch { /* skip */ }\n }\n\n // 2. Cursor\n const cursorConfig = join(process.cwd(), '.cursor', 'mcp.json');\n if (existsSync(cursorConfig)) {\n try {\n const cfg = JSON.parse(readFileSync(cursorConfig, 'utf-8'));\n if (cfg?.mcpServers?.['migration-guardian']) {\n lines.push(chalk.green(' ✅ Connected to Cursor IDE'));\n }\n } catch { /* skip */ }\n }\n\n // 3. Claude Desktop\n let claudeConfig = '';\n if (process.platform === 'win32') {\n claudeConfig = join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');\n } else if (process.platform === 'darwin') {\n claudeConfig = join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');\n } else {\n claudeConfig = join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');\n }\n if (existsSync(claudeConfig)) {\n try {\n const cfg = JSON.parse(readFileSync(claudeConfig, 'utf-8'));\n if (cfg?.mcpServers?.['migration-guardian']) {\n lines.push(chalk.green(' ✅ Connected to Claude Desktop'));\n }\n } catch { /* skip */ }\n }\n\n return lines;\n}\n\n/**\n * Interactive Wizard: Auto-detects clients, prompts the user, and connects.\n */\nexport async function runInteractiveWizard(options: SetupOptions = {}) {\n console.log('');\n console.log(chalk.bold.cyan(' 🛡️ Migration Guardian') + chalk.dim(' (mgcheck) — Database Migration Safety'));\n console.log(chalk.dim(' Catch unsafe database migrations before they hit production.\\n'));\n\n const detected = detectClients();\n const detectedList: string[] = [];\n\n if (detected.antigravity) detectedList.push('Google Antigravity');\n if (detected.cursor) detectedList.push('Cursor IDE');\n if (detected.claude) detectedList.push('Claude Desktop');\n\n if (detectedList.length > 0) {\n console.log(chalk.bold(' Detected AI Clients on your system:'));\n for (const client of detectedList) {\n console.log(` ${chalk.cyan('•')} ${chalk.white(client)}`);\n }\n console.log('');\n } else {\n console.log(chalk.dim(' Configuring MCP integration for your AI editors (Antigravity, Cursor, Claude)...\\n'));\n }\n\n // Check if --yes was passed to bypass the prompt\n let shouldProceed = options.yes ?? false;\n\n if (!shouldProceed) {\n const promptText = detectedList.length > 0\n ? ` Connect Migration Guardian to ${detectedList.join(' & ')}? ${chalk.dim('[Y/n]')} `\n : ` Connect Migration Guardian to your AI agents? ${chalk.dim('[Y/n]')} `;\n\n const rl = readline.createInterface({ input, output });\n\n try {\n const answer = await rl.question(promptText);\n const trimmed = answer.trim().toLowerCase();\n shouldProceed = trimmed === '' || trimmed === 'y' || trimmed === 'yes';\n } finally {\n rl.close();\n }\n }\n\n if (!shouldProceed) {\n console.log(chalk.dim('\\n Setup skipped.'));\n console.log(chalk.white(' You can connect anytime by running: ') + chalk.cyan('npx mgcheck setup'));\n console.log(chalk.white(' Or run checks manually with: ') + chalk.cyan('npx mgcheck run <migration.sql>\\n'));\n return;\n }\n\n console.log(chalk.dim('\\n Configuring MCP integrations...\\n'));\n const results = setupMcp(options);\n\n for (const res of results) {\n console.log(res);\n }\n\n // ── Connection Verification ──────────────────────────────────────\n console.log('');\n console.log(chalk.bold(' 🔗 Connection Status'));\n\n const verified = verifyConnections();\n if (verified.length === 0) {\n console.log(chalk.yellow(' ⚠ No active connections found. Run ') + chalk.cyan('mgcheck setup') + chalk.yellow(' to retry.'));\n } else {\n for (const v of verified) {\n console.log(v);\n }\n }\n\n console.log(chalk.bold.green('\\n 🎉 Migration Guardian is ready!'));\n console.log(chalk.white(' Your AI assistant will now automatically check migrations before executing.\\n'));\n console.log(chalk.dim(' Tools available to your AI:'));\n console.log(chalk.dim(' • check_migration — shadow database execution + 10 AST safety rules'));\n console.log(chalk.dim(' • analyze_migration — fast static SQL linting\\n'));\n console.log(chalk.dim(' Manual CLI Commands:'));\n console.log(chalk.dim(' • mgcheck run <path> — analyze + shadow DB execution'));\n console.log(chalk.dim(' • mgcheck analyze <path> — static analysis only\\n'));\n\n // ── Enter Watch Mode ──────────────────────────────────────────────\n console.log(chalk.bold.cyan(' 👁️ Watch Mode — Listening for AI migration activity...'));\n console.log(chalk.dim(' Press Ctrl+C to stop.\\n'));\n console.log(chalk.dim(' ─────────────────────────────────────────────────────────'));\n console.log('');\n\n const { watchActivityLog, formatActivityEntry, LOG_FILE } = await import('../core/activity-log.js');\n\n // Clear old log entries so they don't replay on startup\n writeFileSync(LOG_FILE, '', 'utf-8');\n\n const stopWatching = watchActivityLog((entry) => {\n console.log(formatActivityEntry(entry));\n console.log('');\n });\n\n // Keep the process alive with an active interval timer\n const keepAlive = setInterval(() => {}, 1000 * 60 * 60);\n\n // Handle graceful shutdown on Ctrl+C\n process.on('SIGINT', () => {\n clearInterval(keepAlive);\n stopWatching();\n console.log(chalk.dim('\\n Migration Guardian stopped. Goodbye! 👋\\n'));\n process.exit(0);\n });\n}\n\n"],"mappings":"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","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,158 @@
1
+ import {
2
+ loadConfig,
3
+ runCheck
4
+ } from "../chunk-7JBFSZBD.js";
5
+ import {
6
+ logActivity
7
+ } from "../chunk-3G3R2NM3.js";
8
+
9
+ // src/mcp/server.ts
10
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
+
13
+ // src/mcp/tools.ts
14
+ import { z } from "zod";
15
+ import { writeFileSync, mkdtempSync } from "fs";
16
+ import { join } from "path";
17
+ import { tmpdir } from "os";
18
+ function runMcpTools(server) {
19
+ server.tool(
20
+ "check_migration",
21
+ "Analyze a database migration for safety issues and execute it against a disposable shadow PostgreSQL database. Returns a detailed report with violations, explanations, and suggested fixes.",
22
+ {
23
+ migration_sql: z.string().describe("The SQL content of the migration to check"),
24
+ base_schema_sql: z.string().optional().describe(
25
+ "Optional base schema SQL to apply before the migration (e.g., CREATE TABLE statements that the migration depends on)"
26
+ ),
27
+ confirm_destructive: z.boolean().default(false).describe(
28
+ "Set to true to allow destructive operations (DROP TABLE, DROP COLUMN). Default: false"
29
+ ),
30
+ provider: z.enum(["docker", "pglite"]).default("pglite").describe(
31
+ 'Shadow database provider. "pglite" requires no Docker. "docker" gives full Postgres fidelity.'
32
+ )
33
+ },
34
+ async ({ migration_sql, base_schema_sql, confirm_destructive, provider }) => {
35
+ try {
36
+ const tmpDir = mkdtempSync(join(tmpdir(), "mgcheck-"));
37
+ const tmpFile = join(tmpDir, "migration.sql");
38
+ writeFileSync(tmpFile, migration_sql, "utf-8");
39
+ const config = loadConfig({
40
+ shadowDb: {
41
+ provider,
42
+ dockerImage: "postgres:16-alpine",
43
+ seedFile: base_schema_sql ? (() => {
44
+ const seedFile = join(tmpDir, "seed.sql");
45
+ writeFileSync(seedFile, base_schema_sql, "utf-8");
46
+ return seedFile;
47
+ })() : void 0
48
+ },
49
+ confirmDestructive: confirm_destructive,
50
+ output: { format: "json", verbose: false }
51
+ });
52
+ const report = await runCheck(tmpFile, config, "run");
53
+ logActivityFromReport("check_migration", report);
54
+ return {
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: JSON.stringify(report, null, 2)
59
+ }
60
+ ]
61
+ };
62
+ } catch (err) {
63
+ return {
64
+ content: [
65
+ {
66
+ type: "text",
67
+ text: JSON.stringify(
68
+ { error: err.message },
69
+ null,
70
+ 2
71
+ )
72
+ }
73
+ ],
74
+ isError: true
75
+ };
76
+ }
77
+ }
78
+ );
79
+ server.tool(
80
+ "analyze_migration",
81
+ "Static analysis only \u2014 check migration SQL against 10 safety rules without executing. No Docker or database required.",
82
+ {
83
+ migration_sql: z.string().describe("The SQL content of the migration to analyze"),
84
+ confirm_destructive: z.boolean().default(false).describe("Set to true to allow destructive operations")
85
+ },
86
+ async ({ migration_sql, confirm_destructive }) => {
87
+ try {
88
+ const tmpDir = mkdtempSync(join(tmpdir(), "mgcheck-"));
89
+ const tmpFile = join(tmpDir, "migration.sql");
90
+ writeFileSync(tmpFile, migration_sql, "utf-8");
91
+ const config = loadConfig({
92
+ confirmDestructive: confirm_destructive,
93
+ output: { format: "json", verbose: false }
94
+ });
95
+ const report = await runCheck(tmpFile, config, "analyze");
96
+ logActivityFromReport("analyze_migration", report);
97
+ return {
98
+ content: [
99
+ {
100
+ type: "text",
101
+ text: JSON.stringify(report, null, 2)
102
+ }
103
+ ]
104
+ };
105
+ } catch (err) {
106
+ return {
107
+ content: [
108
+ {
109
+ type: "text",
110
+ text: JSON.stringify(
111
+ { error: err.message },
112
+ null,
113
+ 2
114
+ )
115
+ }
116
+ ],
117
+ isError: true
118
+ };
119
+ }
120
+ }
121
+ );
122
+ }
123
+ function logActivityFromReport(tool, report) {
124
+ try {
125
+ const errors = report.violations.filter((v) => v.severity === "error").length;
126
+ const warnings = report.violations.filter((v) => v.severity === "warning").length;
127
+ const stmts = (report.statements || []).map(
128
+ (s) => s.raw.replace(/\s+/g, " ").trim()
129
+ );
130
+ logActivity({
131
+ timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }),
132
+ tool,
133
+ summary: stmts.length > 0 ? stmts[0] : "(no statements)",
134
+ violations: report.violations.length,
135
+ errors,
136
+ warnings,
137
+ passed: report.passed,
138
+ statements: stmts
139
+ });
140
+ } catch {
141
+ }
142
+ }
143
+
144
+ // src/mcp/server.ts
145
+ async function main() {
146
+ const server = new McpServer({
147
+ name: "mgcheck",
148
+ version: "0.1.0"
149
+ });
150
+ runMcpTools(server);
151
+ const transport = new StdioServerTransport();
152
+ await server.connect(transport);
153
+ }
154
+ main().catch((err) => {
155
+ console.error("MCP server error:", err);
156
+ process.exit(1);
157
+ });
158
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { runMcpTools } from './tools.js';\n\n/**\n * MCP Server for Migration Guardian.\n *\n * Exposes the core engine as MCP tools that AI coding agents\n * (Claude Code, Cursor, etc.) can call to verify migrations inline.\n *\n * Start with: npx mgcheck mcp\n * Or: node dist/mcp/server.js\n */\nasync function main() {\n const server = new McpServer({\n name: 'mgcheck',\n version: '0.1.0',\n });\n\n // Register tools\n runMcpTools(server);\n\n // Connect via stdio\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n console.error('MCP server error:', err);\n process.exit(1);\n});\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { writeFileSync, mkdtempSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { runCheck } from '../core/engine.js';\nimport { loadConfig } from '../core/config.js';\nimport { logActivity } from '../core/activity-log.js';\nimport type { MigrationReport } from '../core/types.js';\n\n/**\n * Register MCP tools on the server.\n */\nexport function runMcpTools(server: McpServer) {\n // Tool 1: check_migration — full pipeline (analyze + execute)\n server.tool(\n 'check_migration',\n 'Analyze a database migration for safety issues and execute it against a disposable shadow PostgreSQL database. Returns a detailed report with violations, explanations, and suggested fixes.',\n {\n migration_sql: z\n .string()\n .describe('The SQL content of the migration to check'),\n base_schema_sql: z\n .string()\n .optional()\n .describe(\n 'Optional base schema SQL to apply before the migration (e.g., CREATE TABLE statements that the migration depends on)'\n ),\n confirm_destructive: z\n .boolean()\n .default(false)\n .describe(\n 'Set to true to allow destructive operations (DROP TABLE, DROP COLUMN). Default: false'\n ),\n provider: z\n .enum(['docker', 'pglite'])\n .default('pglite')\n .describe(\n 'Shadow database provider. \"pglite\" requires no Docker. \"docker\" gives full Postgres fidelity.'\n ),\n },\n async ({ migration_sql, base_schema_sql, confirm_destructive, provider }) => {\n try {\n // Write SQL to a temp file for the engine\n const tmpDir = mkdtempSync(join(tmpdir(), 'mgcheck-'));\n const tmpFile = join(tmpDir, 'migration.sql');\n writeFileSync(tmpFile, migration_sql, 'utf-8');\n\n // If base schema provided, write it too\n const config = loadConfig({\n shadowDb: {\n provider,\n dockerImage: 'postgres:16-alpine',\n seedFile: base_schema_sql ? (() => {\n const seedFile = join(tmpDir, 'seed.sql');\n writeFileSync(seedFile, base_schema_sql, 'utf-8');\n return seedFile;\n })() : undefined,\n },\n confirmDestructive: confirm_destructive,\n output: { format: 'json', verbose: false },\n });\n\n const report = await runCheck(tmpFile, config, 'run');\n\n logActivityFromReport('check_migration', report);\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(report, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(\n { error: (err as Error).message },\n null,\n 2\n ),\n },\n ],\n isError: true,\n };\n }\n }\n );\n\n // Tool 2: analyze_migration — static analysis only (no Docker needed)\n server.tool(\n 'analyze_migration',\n 'Static analysis only — check migration SQL against 10 safety rules without executing. No Docker or database required.',\n {\n migration_sql: z\n .string()\n .describe('The SQL content of the migration to analyze'),\n confirm_destructive: z\n .boolean()\n .default(false)\n .describe('Set to true to allow destructive operations'),\n },\n async ({ migration_sql, confirm_destructive }) => {\n try {\n const tmpDir = mkdtempSync(join(tmpdir(), 'mgcheck-'));\n const tmpFile = join(tmpDir, 'migration.sql');\n writeFileSync(tmpFile, migration_sql, 'utf-8');\n\n const config = loadConfig({\n confirmDestructive: confirm_destructive,\n output: { format: 'json', verbose: false },\n });\n\n const report = await runCheck(tmpFile, config, 'analyze');\n\n logActivityFromReport('analyze_migration', report);\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(report, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(\n { error: (err as Error).message },\n null,\n 2\n ),\n },\n ],\n isError: true,\n };\n }\n }\n );\n}\n\n/**\n * Extract info from a report and write to the shared activity log.\n */\nfunction logActivityFromReport(tool: string, report: MigrationReport): void {\n try {\n const errors = report.violations.filter((v) => v.severity === 'error').length;\n const warnings = report.violations.filter((v) => v.severity === 'warning').length;\n const stmts = (report.statements || []).map((s) =>\n s.raw.replace(/\\s+/g, ' ').trim()\n );\n\n logActivity({\n timestamp: new Date().toLocaleTimeString('en-US', { hour12: false }),\n tool,\n summary: stmts.length > 0 ? stmts[0] : '(no statements)',\n violations: report.violations.length,\n errors,\n warnings,\n passed: report.passed,\n statements: stmts,\n });\n } catch {\n // Never let logging break the MCP response\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "mgcheck",
3
+ "version": "0.1.0",
4
+ "description": "Migration Guardian — Catch unsafe database migrations before they hit production",
5
+ "type": "module",
6
+ "bin": {
7
+ "mgcheck": "dist/cli.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./mcp": {
17
+ "import": "./dist/mcp/server.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "dev": "tsx src/cli.ts",
27
+ "build": "tsup",
28
+ "test": "vitest run",
29
+ "test:unit": "vitest run --project unit",
30
+ "test:integration": "vitest run --project integration",
31
+ "test:watch": "vitest",
32
+ "lint": "tsc --noEmit",
33
+ "prepublishOnly": "npm run build"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "keywords": [
39
+ "migration",
40
+ "database",
41
+ "postgresql",
42
+ "postgres",
43
+ "safety",
44
+ "linter",
45
+ "prisma",
46
+ "drizzle",
47
+ "sql",
48
+ "ci",
49
+ "mcp",
50
+ "shadow-database",
51
+ "migration-check"
52
+ ],
53
+ "author": "",
54
+ "license": "MIT",
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "git+https://github.com/yourusername/mgcheck.git"
58
+ },
59
+ "homepage": "https://github.com/yourusername/mgcheck#readme",
60
+ "bugs": {
61
+ "url": "https://github.com/yourusername/mgcheck/issues"
62
+ },
63
+ "dependencies": {
64
+ "@electric-sql/pglite": "^0.2.0",
65
+ "@modelcontextprotocol/sdk": "^1.0.0",
66
+ "chalk": "^5.3.0",
67
+ "commander": "^12.1.0",
68
+ "dockerode": "^4.0.0",
69
+ "ora": "^8.0.0",
70
+ "pg": "^8.12.0",
71
+ "pgsql-parser": "^13.16.0",
72
+ "zod": "^3.23.0"
73
+ },
74
+ "devDependencies": {
75
+ "@types/dockerode": "^3.3.0",
76
+ "@types/node": "^20.14.0",
77
+ "@types/pg": "^8.11.0",
78
+ "tsup": "^8.1.0",
79
+ "tsx": "^4.15.0",
80
+ "typescript": "^5.5.0",
81
+ "vitest": "^1.6.0"
82
+ }
83
+ }