gitmem-mcp 1.5.1 → 1.6.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.
@@ -0,0 +1,606 @@
1
+ /**
2
+ * GitMem Pro Activation Wizard
3
+ *
4
+ * Usage: npx gitmem-mcp activate [license-key]
5
+ *
6
+ * Credential resolution (priority order):
7
+ * 1. Environment variables (SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, OPENROUTER_API_KEY)
8
+ * 2. Existing values in .gitmem/config.json (re-activation)
9
+ * 3. Interactive prompt (TTY only)
10
+ *
11
+ * Steps:
12
+ * 1. Accept key as argument or prompt for it
13
+ * 2. Validate key against our endpoint (register device)
14
+ * 3. Resolve Supabase URL + service role key (env → config → prompt)
15
+ * 4. Test Supabase connection (verify tables exist)
16
+ * 5. Resolve OpenRouter API key (env → config → prompt)
17
+ * 6. Write everything to ~/.gitmem/config.json
18
+ */
19
+ import * as fs from "fs";
20
+ import * as path from "path";
21
+ import * as readline from "readline";
22
+ import { fileURLToPath } from "url";
23
+ import { getGitmemDir, getInstallId } from "../services/gitmem-dir.js";
24
+ import { validateLicense, clearLicenseCache } from "../services/license.js";
25
+ import { hasLocalData, hasPreMigrationData, migrateLocalToSupabase, reimportFromBackups, archiveLocalData } from "./migrate-local.js";
26
+ function createReadline() {
27
+ return readline.createInterface({
28
+ input: process.stdin,
29
+ output: process.stdout,
30
+ });
31
+ }
32
+ function ask(rl, question) {
33
+ return new Promise((resolve) => {
34
+ rl.question(question, (answer) => resolve(answer.trim()));
35
+ });
36
+ }
37
+ /**
38
+ * Resolve a credential value using the priority chain:
39
+ * 1. Environment variable
40
+ * 2. Existing config value
41
+ * 3. Interactive prompt (if TTY available)
42
+ *
43
+ * Returns the resolved value or empty string.
44
+ */
45
+ async function resolveCredential(opts) {
46
+ // 1. Environment variable
47
+ const envValue = process.env[opts.envVar];
48
+ if (envValue) {
49
+ return { value: envValue, source: "env" };
50
+ }
51
+ // 2. Existing config value
52
+ if (opts.configValue) {
53
+ return { value: opts.configValue, source: "config" };
54
+ }
55
+ // 3. Interactive prompt
56
+ if (opts.rl) {
57
+ const prompt = opts.existingHint
58
+ ? ` ${opts.promptLabel} [${opts.existingHint}]: `
59
+ : ` ${opts.promptLabel}: `;
60
+ const input = await ask(opts.rl, prompt);
61
+ if (input) {
62
+ return { value: input, source: "prompt" };
63
+ }
64
+ }
65
+ return { value: "", source: "none" };
66
+ }
67
+ /**
68
+ * Test basic Supabase connectivity via REST API
69
+ */
70
+ async function testSupabaseConnection(url, key) {
71
+ try {
72
+ const response = await fetch(`${url}/rest/v1/`, {
73
+ method: "GET",
74
+ headers: {
75
+ apikey: key,
76
+ Authorization: `Bearer ${key}`,
77
+ },
78
+ });
79
+ return response.ok || response.status === 200;
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ /**
86
+ * Check if gitmem tables exist in the user's Supabase
87
+ * Returns list of missing tables (empty = all present)
88
+ */
89
+ async function checkSchemaExists(url, key) {
90
+ const requiredTables = ["gitmem_learnings", "gitmem_sessions", "gitmem_decisions", "gitmem_scar_usage"];
91
+ const missing = [];
92
+ for (const table of requiredTables) {
93
+ try {
94
+ const response = await fetch(`${url}/rest/v1/${table}?select=id&limit=0`, {
95
+ method: "GET",
96
+ headers: {
97
+ apikey: key,
98
+ Authorization: `Bearer ${key}`,
99
+ },
100
+ });
101
+ if (!response.ok) {
102
+ missing.push(table);
103
+ }
104
+ }
105
+ catch {
106
+ missing.push(table);
107
+ }
108
+ }
109
+ return missing;
110
+ }
111
+ /**
112
+ * Get the Supabase access token for Management API
113
+ * Priority: SUPABASE_ACCESS_TOKEN env var → ~/.supabase/access-token file
114
+ */
115
+ function getSupabaseAccessToken() {
116
+ const envToken = process.env.SUPABASE_ACCESS_TOKEN;
117
+ if (envToken)
118
+ return envToken;
119
+ try {
120
+ const tokenPath = path.join(process.env.HOME || process.env.USERPROFILE || "", ".supabase", "access-token");
121
+ if (fs.existsSync(tokenPath)) {
122
+ return fs.readFileSync(tokenPath, "utf-8").trim();
123
+ }
124
+ }
125
+ catch {
126
+ // No stored token
127
+ }
128
+ return null;
129
+ }
130
+ /**
131
+ * Extract project ref from Supabase URL
132
+ * e.g., "https://abcdef.supabase.co" → "abcdef"
133
+ */
134
+ function extractProjectRef(url) {
135
+ const match = url.match(/https?:\/\/([^.]+)\.supabase\.co/);
136
+ return match ? match[1] : null;
137
+ }
138
+ /**
139
+ * Load the setup SQL from the schema file bundled with the package
140
+ * Strips the license management section (not for user's Supabase)
141
+ */
142
+ function loadSetupSql() {
143
+ try {
144
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
145
+ const sqlPath = path.join(__dirname, "..", "..", "schema", "setup.sql");
146
+ if (!fs.existsSync(sqlPath))
147
+ return null;
148
+ let sql = fs.readFileSync(sqlPath, "utf-8");
149
+ // Strip license management tables (they belong on our infra, not user's)
150
+ const licenseIdx = sql.indexOf("-- License Management Tables");
151
+ if (licenseIdx > 0) {
152
+ sql = sql.substring(0, licenseIdx);
153
+ }
154
+ return sql;
155
+ }
156
+ catch {
157
+ return null;
158
+ }
159
+ }
160
+ /**
161
+ * Apply schema SQL via direct Postgres connection (pg client)
162
+ * Uses DATABASE_URL connection string from env/config/prompt
163
+ */
164
+ async function applySchemaViaPg(databaseUrl, sql) {
165
+ try {
166
+ const pg = await import("pg");
167
+ const client = new pg.default.Client({
168
+ connectionString: databaseUrl,
169
+ ssl: { rejectUnauthorized: false },
170
+ connectionTimeoutMillis: 15_000,
171
+ statement_timeout: 30_000,
172
+ });
173
+ await client.connect();
174
+ await client.query(sql);
175
+ // Reload PostgREST schema cache
176
+ await client.query("NOTIFY pgrst, 'reload schema'");
177
+ await client.end();
178
+ return { success: true };
179
+ }
180
+ catch (err) {
181
+ const message = err instanceof Error ? err.message : "Unknown error";
182
+ return { success: false, error: message };
183
+ }
184
+ }
185
+ /**
186
+ * Apply schema SQL to user's Supabase via Management API
187
+ */
188
+ async function applySchema(projectRef, accessToken, sql) {
189
+ try {
190
+ const response = await fetch(`https://api.supabase.com/v1/projects/${projectRef}/database/query`, {
191
+ method: "POST",
192
+ headers: {
193
+ "Content-Type": "application/json",
194
+ Authorization: `Bearer ${accessToken}`,
195
+ },
196
+ body: JSON.stringify({ query: sql }),
197
+ signal: AbortSignal.timeout(30_000),
198
+ });
199
+ if (!response.ok) {
200
+ const text = await response.text();
201
+ return { success: false, error: `HTTP ${response.status}: ${text.slice(0, 200)}` };
202
+ }
203
+ return { success: true };
204
+ }
205
+ catch (err) {
206
+ const message = err instanceof Error ? err.message : "Unknown error";
207
+ return { success: false, error: message };
208
+ }
209
+ }
210
+ export async function main(args) {
211
+ console.log("");
212
+ console.log("GitMem Pro Activation");
213
+ console.log("─────────────────────");
214
+ console.log("");
215
+ const gitmemDir = getGitmemDir();
216
+ const configPath = path.join(gitmemDir, "config.json");
217
+ // Load existing config
218
+ let config = {};
219
+ try {
220
+ if (fs.existsSync(configPath)) {
221
+ config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
222
+ }
223
+ }
224
+ catch {
225
+ // Start fresh
226
+ }
227
+ // Ensure install_id exists
228
+ let installId = config.install_id || getInstallId();
229
+ if (!installId) {
230
+ const { randomUUID } = await import("crypto");
231
+ installId = randomUUID();
232
+ config.install_id = installId;
233
+ }
234
+ // Step 1: Get license key (arg → env → prompt)
235
+ let apiKey = args[0] || process.env.GITMEM_API_KEY || config.api_key || "";
236
+ if (!apiKey) {
237
+ if (!process.stdin.isTTY) {
238
+ console.error("Error: License key required. Usage: npx gitmem-mcp activate <key>");
239
+ console.error(" Or set GITMEM_API_KEY environment variable.");
240
+ process.exit(1);
241
+ }
242
+ const rl = createReadline();
243
+ apiKey = await ask(rl, "License key: ");
244
+ rl.close();
245
+ if (!apiKey) {
246
+ console.error("Error: License key is required.");
247
+ process.exit(1);
248
+ }
249
+ }
250
+ // Validate key format
251
+ if (!apiKey.startsWith("gitmem_pro_") && !apiKey.startsWith("gitmem_dev_")) {
252
+ console.error("Error: Invalid key format. Keys start with 'gitmem_pro_' or 'gitmem_dev_'.");
253
+ process.exit(1);
254
+ }
255
+ // Step 2: Validate key against endpoint
256
+ console.log("Validating license key...");
257
+ const result = await validateLicense(apiKey, installId);
258
+ if (!result.valid) {
259
+ console.error(`\nError: ${result.message}`);
260
+ process.exit(1);
261
+ }
262
+ console.log(`✓ Key validated (${result.tier} tier)`);
263
+ console.log("");
264
+ // Create readline only if TTY is available
265
+ const rl = process.stdin.isTTY ? createReadline() : null;
266
+ // Step 3: Resolve Supabase credentials (env → config → prompt)
267
+ const existingUrl = config.supabase_url;
268
+ const existingKey = config.supabase_key;
269
+ if (rl) {
270
+ console.log("Supabase Setup");
271
+ console.log(" (Create a free project at https://database.new)");
272
+ if (existingUrl) {
273
+ console.log(` Current: ${existingUrl}`);
274
+ }
275
+ console.log("");
276
+ }
277
+ const supabaseUrlResult = await resolveCredential({
278
+ envVar: "SUPABASE_URL",
279
+ configValue: existingUrl,
280
+ promptLabel: "Project URL",
281
+ required: true,
282
+ rl,
283
+ });
284
+ const supabaseUrl = supabaseUrlResult.value;
285
+ // Re-activation safety: warn if changing URL interactively
286
+ if (rl && existingUrl && supabaseUrl !== existingUrl && supabaseUrlResult.source === "prompt") {
287
+ console.log("");
288
+ console.log(" ⚠ WARNING: You are changing your Supabase URL.");
289
+ console.log(` Old: ${existingUrl}`);
290
+ console.log(` New: ${supabaseUrl}`);
291
+ console.log(" Your existing data in the old project will NOT be migrated.");
292
+ console.log("");
293
+ const confirm = await ask(rl, " Continue with new URL? (y/N): ");
294
+ if (confirm.toLowerCase() !== "y" && confirm.toLowerCase() !== "yes") {
295
+ console.log(" Keeping existing URL.");
296
+ // Fall back handled below by using existingUrl
297
+ }
298
+ }
299
+ // Resolve service role key — if same URL, offer to keep existing
300
+ const supabaseKeyResult = await resolveCredential({
301
+ envVar: "SUPABASE_SERVICE_ROLE_KEY",
302
+ configValue: (supabaseUrl === existingUrl) ? existingKey : undefined,
303
+ promptLabel: "Service Role Key",
304
+ required: true,
305
+ rl,
306
+ existingHint: (existingKey && supabaseUrl === existingUrl) ? "keep existing" : undefined,
307
+ });
308
+ const supabaseKey = supabaseKeyResult.value;
309
+ // Step 4: Test connection if we have credentials
310
+ let missingTables = [];
311
+ let connectionFailed = false;
312
+ if (supabaseUrl && supabaseKey) {
313
+ if (supabaseUrlResult.source !== "config" || supabaseKeyResult.source !== "config") {
314
+ // Only test if credentials are new (not just re-read from config)
315
+ console.log(" Testing connection...");
316
+ const connected = await testSupabaseConnection(supabaseUrl, supabaseKey);
317
+ if (!connected) {
318
+ connectionFailed = true;
319
+ if (rl) {
320
+ // Interactive: hard failure — user can re-enter
321
+ console.error(" ✗ Could not connect to Supabase. Check your URL and key.");
322
+ rl.close();
323
+ process.exit(1);
324
+ }
325
+ else {
326
+ // Non-interactive: warn but save credentials anyway
327
+ console.log(" ⚠ Could not connect to Supabase (credentials saved anyway).");
328
+ console.log(" Verify your SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are correct.");
329
+ }
330
+ }
331
+ else {
332
+ console.log(" ✓ Connected to Supabase");
333
+ }
334
+ }
335
+ if (!connectionFailed) {
336
+ missingTables = await checkSchemaExists(supabaseUrl, supabaseKey);
337
+ if (missingTables.length > 0) {
338
+ console.log(" Setting up schema...");
339
+ const projectRef = extractProjectRef(supabaseUrl);
340
+ const accessToken = getSupabaseAccessToken();
341
+ const setupSql = loadSetupSql();
342
+ if (projectRef && accessToken && setupSql) {
343
+ // Auto-apply schema via Management API
344
+ const result = await applySchema(projectRef, accessToken, setupSql);
345
+ if (result.success) {
346
+ // Reload PostgREST schema cache so new tables are visible via REST API
347
+ await applySchema(projectRef, accessToken, "NOTIFY pgrst, 'reload schema'");
348
+ // Brief wait for PostgREST to pick up the notification
349
+ await new Promise((r) => setTimeout(r, 2000));
350
+ // Verify tables now exist
351
+ const stillMissing = await checkSchemaExists(supabaseUrl, supabaseKey);
352
+ if (stillMissing.length === 0) {
353
+ console.log(" ✓ Schema applied automatically");
354
+ missingTables = [];
355
+ }
356
+ else {
357
+ console.log(" ⚠ Schema applied but some tables still missing: " + stillMissing.join(", "));
358
+ console.log(" PostgREST may need a moment to reload. Try: npx gitmem-mcp check");
359
+ missingTables = stillMissing;
360
+ }
361
+ }
362
+ else {
363
+ console.log(" ⚠ Auto-schema failed: " + result.error);
364
+ console.log(" Apply manually:");
365
+ console.log("");
366
+ console.log(" npx gitmem-mcp setup | pbcopy (macOS — copies SQL to clipboard)");
367
+ console.log(" npx gitmem-mcp setup (prints SQL to paste manually)");
368
+ console.log("");
369
+ console.log(" Then: Supabase Dashboard → SQL Editor → New query → Paste → Run");
370
+ }
371
+ }
372
+ else if (!setupSql) {
373
+ console.log(" ⚠ Could not load schema SQL file");
374
+ }
375
+ else {
376
+ // No access token — try DATABASE_URL as fallback
377
+ const dbUrlResult = await resolveCredential({
378
+ envVar: "DATABASE_URL",
379
+ configValue: config.database_url,
380
+ promptLabel: "Database URL (from Supabase Connect panel)",
381
+ required: false,
382
+ rl,
383
+ });
384
+ if (dbUrlResult.value) {
385
+ console.log(" Applying schema via direct connection...");
386
+ const pgResult = await applySchemaViaPg(dbUrlResult.value, setupSql);
387
+ if (pgResult.success) {
388
+ // Save DATABASE_URL to config for future use
389
+ config.database_url = dbUrlResult.value;
390
+ // Wait for PostgREST cache reload
391
+ await new Promise((r) => setTimeout(r, 2000));
392
+ const stillMissing = await checkSchemaExists(supabaseUrl, supabaseKey);
393
+ if (stillMissing.length === 0) {
394
+ console.log(" ✓ Schema applied via direct connection");
395
+ missingTables = [];
396
+ }
397
+ else {
398
+ console.log(" ⚠ Schema applied but PostgREST cache may be stale");
399
+ console.log(" Try: npx gitmem-mcp check");
400
+ missingTables = stillMissing;
401
+ }
402
+ }
403
+ else {
404
+ console.log(" ⚠ Direct connection failed: " + pgResult.error);
405
+ console.log(" Apply manually:");
406
+ console.log(" npx gitmem-mcp setup | pbcopy (macOS)");
407
+ console.log(" Then: Supabase Dashboard → SQL Editor → Paste → Run");
408
+ }
409
+ }
410
+ else {
411
+ // No access token AND no DATABASE_URL
412
+ console.log(" ⚠ Missing tables: " + missingTables.join(", "));
413
+ console.log("");
414
+ console.log(" To apply automatically, provide one of:");
415
+ console.log(" - DATABASE_URL (from Supabase Dashboard → Connect)");
416
+ console.log(" - SUPABASE_ACCESS_TOKEN (from: npx supabase login)");
417
+ console.log(" Then re-run: npx gitmem-mcp activate");
418
+ console.log("");
419
+ console.log(" Or apply manually:");
420
+ console.log(" npx gitmem-mcp setup | pbcopy (macOS)");
421
+ console.log(" Then: Supabase Dashboard → SQL Editor → Paste → Run");
422
+ }
423
+ }
424
+ }
425
+ else {
426
+ console.log(" ✓ Schema verified (all tables present)");
427
+ }
428
+ }
429
+ console.log("");
430
+ }
431
+ else if (!supabaseUrl || !supabaseKey) {
432
+ console.log(" ⚠ Supabase credentials not provided.");
433
+ console.log(" Pro features require Supabase. Set via:");
434
+ console.log(" - Environment: SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY");
435
+ console.log(" - Config: edit .gitmem/config.json (supabase_url, supabase_key)");
436
+ console.log(" - Re-run: npx gitmem-mcp activate (interactive)");
437
+ console.log("");
438
+ }
439
+ // Step 5: Resolve OpenRouter key (env → config → prompt)
440
+ if (rl) {
441
+ console.log("OpenRouter Setup");
442
+ console.log(" (Get a key at https://openrouter.ai/keys)");
443
+ const existingOpenRouter = config.openrouter_key;
444
+ if (existingOpenRouter) {
445
+ console.log(` Current: ${existingOpenRouter.substring(0, 12)}...`);
446
+ }
447
+ console.log("");
448
+ }
449
+ const openrouterResult = await resolveCredential({
450
+ envVar: "OPENROUTER_API_KEY",
451
+ configValue: config.openrouter_key,
452
+ promptLabel: "API Key",
453
+ required: false,
454
+ rl,
455
+ existingHint: config.openrouter_key ? "keep existing" : undefined,
456
+ });
457
+ const openrouterKey = openrouterResult.value;
458
+ if (openrouterKey) {
459
+ if (openrouterResult.source === "env") {
460
+ console.log(" ✓ OpenRouter configured (from env)");
461
+ }
462
+ else if (openrouterResult.source === "config") {
463
+ // Silent — already configured
464
+ }
465
+ else {
466
+ console.log(" ✓ OpenRouter configured");
467
+ }
468
+ }
469
+ else if (rl) {
470
+ console.log(" ⚠ Skipped (semantic search will not work without embeddings)");
471
+ }
472
+ if (rl)
473
+ rl.close();
474
+ // Step 6: Write config (preserves existing fields like project, install_id, feedback_enabled)
475
+ config.api_key = apiKey;
476
+ if (supabaseUrl)
477
+ config.supabase_url = supabaseUrl;
478
+ if (supabaseKey)
479
+ config.supabase_key = supabaseKey;
480
+ if (openrouterKey)
481
+ config.openrouter_key = openrouterKey;
482
+ if (!fs.existsSync(gitmemDir)) {
483
+ fs.mkdirSync(gitmemDir, { recursive: true });
484
+ }
485
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
486
+ // Clear any stale license cache
487
+ clearLicenseCache();
488
+ // Ensure .gitmem/ is in .gitignore (prevents credential exposure)
489
+ try {
490
+ const projectRoot = process.cwd();
491
+ const gitignorePath = path.join(projectRoot, ".gitignore");
492
+ if (fs.existsSync(path.join(projectRoot, ".git"))) {
493
+ let gitignore = "";
494
+ if (fs.existsSync(gitignorePath)) {
495
+ gitignore = fs.readFileSync(gitignorePath, "utf-8");
496
+ }
497
+ if (!gitignore.split("\n").some(line => line.trim() === ".gitmem/" || line.trim() === ".gitmem")) {
498
+ const separator = gitignore.length > 0 && !gitignore.endsWith("\n") ? "\n" : "";
499
+ fs.appendFileSync(gitignorePath, `${separator}\n# GitMem local data (contains credentials)\n.gitmem/\n`);
500
+ console.log(" ✓ Added .gitmem/ to .gitignore");
501
+ }
502
+ }
503
+ }
504
+ catch {
505
+ // Non-fatal — warn but don't block activation
506
+ console.log(" ⚠ Could not update .gitignore — manually add .gitmem/ to prevent credential exposure");
507
+ }
508
+ // Step 7: Migrate local data to Supabase (free → pro upgrade)
509
+ // Handles three scenarios:
510
+ // A) Fresh upgrade: .json files exist → migrate and archive
511
+ // B) Re-activation after failed migration: .pre-migration backups exist → reimport
512
+ // C) Already migrated: neither exists → skip
513
+ if (supabaseUrl && supabaseKey && missingTables.length === 0) {
514
+ const hasLive = hasLocalData(gitmemDir);
515
+ const hasBackups = hasPreMigrationData(gitmemDir);
516
+ if (hasLive || hasBackups) {
517
+ if (hasLive) {
518
+ console.log("Migrating Local Data");
519
+ console.log(" Found existing local data from free tier...");
520
+ }
521
+ else {
522
+ console.log("Re-importing From Backups");
523
+ console.log(" Found .pre-migration backup files from a previous upgrade...");
524
+ }
525
+ console.log("");
526
+ const migrationResult = hasLive
527
+ ? await migrateLocalToSupabase({
528
+ supabaseUrl,
529
+ supabaseKey,
530
+ gitmemDir,
531
+ onProgress: (msg) => console.log(msg),
532
+ })
533
+ : await reimportFromBackups({
534
+ supabaseUrl,
535
+ supabaseKey,
536
+ gitmemDir,
537
+ onProgress: (msg) => console.log(msg),
538
+ });
539
+ // Report results
540
+ const collections = Object.keys(migrationResult.migrated);
541
+ let totalMigrated = 0;
542
+ let totalSkipped = 0;
543
+ let totalErrors = 0;
544
+ for (const col of collections) {
545
+ const m = migrationResult.migrated[col];
546
+ const s = migrationResult.skipped[col];
547
+ const e = migrationResult.errors[col]?.length || 0;
548
+ totalMigrated += m;
549
+ totalSkipped += s;
550
+ totalErrors += e;
551
+ if (m > 0 || s > 0) {
552
+ console.log(` ${m > 0 ? "✓" : "⚠"} ${col}: ${m} migrated${s > 0 ? `, ${s} failed` : ""}`);
553
+ }
554
+ }
555
+ // Show ALL errors
556
+ if (totalErrors > 0) {
557
+ console.log("");
558
+ for (const col of collections) {
559
+ for (const err of migrationResult.errors[col] || []) {
560
+ console.log(` ⚠ ${col}: ${err}`);
561
+ }
562
+ }
563
+ }
564
+ if (totalMigrated > 0) {
565
+ console.log("");
566
+ console.log(` ✓ Migrated ${totalMigrated} records to Supabase`);
567
+ if (hasLive) {
568
+ // Archive local files so they aren't re-read
569
+ const archived = archiveLocalData(gitmemDir);
570
+ if (archived.length > 0) {
571
+ console.log(` ✓ Local files archived (${archived.join(", ")}.json → .pre-migration)`);
572
+ }
573
+ }
574
+ }
575
+ else if (migrationResult.hasLocalData) {
576
+ console.log(" ⚠ Migration encountered errors. Local data preserved.");
577
+ console.log(" Check .gitmem/migration.log for details.");
578
+ console.log(" Fix issues and re-run: npx gitmem-mcp activate");
579
+ }
580
+ console.log("");
581
+ }
582
+ }
583
+ // Summary
584
+ console.log("");
585
+ console.log("─────────────────────");
586
+ const sources = [];
587
+ if (supabaseUrl)
588
+ sources.push(`Supabase (${supabaseUrlResult.source})`);
589
+ if (openrouterKey)
590
+ sources.push(`OpenRouter (${openrouterResult.source})`);
591
+ if (!supabaseUrl) {
592
+ console.log("License key activated. Supabase credentials still needed for Pro features.");
593
+ }
594
+ else if (missingTables.length > 0) {
595
+ console.log("Pro tier activated! Run schema setup, then restart your editor.");
596
+ }
597
+ else {
598
+ console.log("Pro tier activated! Restart your editor to apply.");
599
+ }
600
+ if (sources.length > 0) {
601
+ console.log(` Credentials: ${sources.join(", ")}`);
602
+ }
603
+ console.log(` Config: ${configPath}`);
604
+ console.log("");
605
+ }
606
+ //# sourceMappingURL=activate.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * GitMem Pro Deactivation
3
+ *
4
+ * 1. Calls gitmem_deactivate_device RPC to remove this device server-side
5
+ * 2. Removes api_key, supabase_url, supabase_key, openrouter_key from config.json
6
+ * 3. Deletes license-cache.json
7
+ * Does NOT remove .gitmem/ directory or local data.
8
+ */
9
+ export declare function main(_args: string[]): Promise<void>;
10
+ //# sourceMappingURL=deactivate.d.ts.map