Skip to content

Commit b46d580

Browse files
Claudeclaude
authored andcommitted
fix: resolve v1.6.0 Pro tier embedding and migration bugs
Bug 1 — Embeddings not generated on Pro/Supabase: - activate saves OpenRouter key to local config but embedding.ts only checked env vars, never the persisted config - Fix: embedding.ts, variant-generation.ts, transcript-chunker.ts now resolve key via getProConfig() fallback (same as supabase-client.ts) Bug 2 — Migration lossy (23 learnings to 3 survived): - Migration sent ALL local JSON fields to PostgREST; unknown columns caused 400 rejections. Error reporting capped at 3, hiding failures. - Fix: Added KNOWN_COLUMNS whitelist per table — only valid fields sent - Fix: Removed 3-error cap — all failures now visible to user - Fix: Added .gitmem/migration.log with per-record outcomes - Fix: activate detects .pre-migration backups and re-imports them (user just runs activate again — no new command needed) Credential exposure fix: - activate now auto-adds .gitmem/ to project .gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1f500fa commit b46d580

7 files changed

Lines changed: 1109 additions & 539 deletions

File tree

src/commands/activate.ts

Lines changed: 90 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import * as readline from "readline";
2323
import { fileURLToPath } from "url";
2424
import { getGitmemDir, getInstallId } from "../services/gitmem-dir.js";
2525
import { validateLicense, clearLicenseCache } from "../services/license.js";
26-
import { hasLocalData, migrateLocalToSupabase, archiveLocalData } from "./migrate-local.js";
26+
import { hasLocalData, hasPreMigrationData, migrateLocalToSupabase, reimportFromBackups, archiveLocalData } from "./migrate-local.js";
2727

2828
function createReadline(): readline.Interface {
2929
return readline.createInterface({
@@ -536,62 +536,106 @@ export async function main(args: string[]): Promise<void> {
536536
// Clear any stale license cache
537537
clearLicenseCache();
538538

539-
// Step 7: Migrate local data to Supabase (free → pro upgrade)
540-
if (supabaseUrl && supabaseKey && missingTables.length === 0 && hasLocalData(gitmemDir)) {
541-
console.log("Migrating Local Data");
542-
console.log(" Found existing local data from free tier...");
543-
console.log("");
544-
545-
const migrationResult = await migrateLocalToSupabase({
546-
supabaseUrl,
547-
supabaseKey,
548-
gitmemDir,
549-
onProgress: (msg) => console.log(msg),
550-
});
551-
552-
// Report results
553-
const collections = Object.keys(migrationResult.migrated);
554-
let totalMigrated = 0;
555-
let totalSkipped = 0;
556-
let totalErrors = 0;
557-
558-
for (const col of collections) {
559-
const m = migrationResult.migrated[col];
560-
const s = migrationResult.skipped[col];
561-
const e = migrationResult.errors[col]?.length || 0;
562-
totalMigrated += m;
563-
totalSkipped += s;
564-
totalErrors += e;
565-
if (m > 0) {
566-
console.log(` ✓ ${col}: ${m} records migrated${s > 0 ? ` (${s} skipped)` : ""}`);
539+
// Ensure .gitmem/ is in .gitignore (prevents credential exposure)
540+
try {
541+
const projectRoot = process.cwd();
542+
const gitignorePath = path.join(projectRoot, ".gitignore");
543+
if (fs.existsSync(path.join(projectRoot, ".git"))) {
544+
let gitignore = "";
545+
if (fs.existsSync(gitignorePath)) {
546+
gitignore = fs.readFileSync(gitignorePath, "utf-8");
547+
}
548+
if (!gitignore.split("\n").some(line => line.trim() === ".gitmem/" || line.trim() === ".gitmem")) {
549+
const separator = gitignore.length > 0 && !gitignore.endsWith("\n") ? "\n" : "";
550+
fs.appendFileSync(gitignorePath, `${separator}\n# GitMem local data (contains credentials)\n.gitmem/\n`);
551+
console.log(" ✓ Added .gitmem/ to .gitignore");
567552
}
568553
}
554+
} catch {
555+
// Non-fatal — warn but don't block activation
556+
console.log(" ⚠ Could not update .gitignore — manually add .gitmem/ to prevent credential exposure");
557+
}
569558

570-
// Show errors if any
571-
if (totalErrors > 0) {
559+
// Step 7: Migrate local data to Supabase (free → pro upgrade)
560+
// Handles three scenarios:
561+
// A) Fresh upgrade: .json files exist → migrate and archive
562+
// B) Re-activation after failed migration: .pre-migration backups exist → reimport
563+
// C) Already migrated: neither exists → skip
564+
if (supabaseUrl && supabaseKey && missingTables.length === 0) {
565+
const hasLive = hasLocalData(gitmemDir);
566+
const hasBackups = hasPreMigrationData(gitmemDir);
567+
568+
if (hasLive || hasBackups) {
569+
if (hasLive) {
570+
console.log("Migrating Local Data");
571+
console.log(" Found existing local data from free tier...");
572+
} else {
573+
console.log("Re-importing From Backups");
574+
console.log(" Found .pre-migration backup files from a previous upgrade...");
575+
}
572576
console.log("");
577+
578+
const migrationResult = hasLive
579+
? await migrateLocalToSupabase({
580+
supabaseUrl,
581+
supabaseKey,
582+
gitmemDir,
583+
onProgress: (msg) => console.log(msg),
584+
})
585+
: await reimportFromBackups({
586+
supabaseUrl,
587+
supabaseKey,
588+
gitmemDir,
589+
onProgress: (msg) => console.log(msg),
590+
});
591+
592+
// Report results
593+
const collections = Object.keys(migrationResult.migrated);
594+
let totalMigrated = 0;
595+
let totalSkipped = 0;
596+
let totalErrors = 0;
597+
573598
for (const col of collections) {
574-
for (const err of migrationResult.errors[col] || []) {
575-
console.log(` ⚠ ${col}: ${err}`);
599+
const m = migrationResult.migrated[col];
600+
const s = migrationResult.skipped[col];
601+
const e = migrationResult.errors[col]?.length || 0;
602+
totalMigrated += m;
603+
totalSkipped += s;
604+
totalErrors += e;
605+
if (m > 0 || s > 0) {
606+
console.log(` ${m > 0 ? "✓" : "⚠"} ${col}: ${m} migrated${s > 0 ? `, ${s} failed` : ""}`);
576607
}
577608
}
578-
}
579609

580-
if (totalMigrated > 0) {
581-
console.log("");
582-
console.log(` ✓ Migrated ${totalMigrated} records to Supabase`);
610+
// Show ALL errors
611+
if (totalErrors > 0) {
612+
console.log("");
613+
for (const col of collections) {
614+
for (const err of migrationResult.errors[col] || []) {
615+
console.log(` ⚠ ${col}: ${err}`);
616+
}
617+
}
618+
}
583619

584-
// Archive local files so they aren't re-read
585-
const archived = archiveLocalData(gitmemDir);
586-
if (archived.length > 0) {
587-
console.log(` ✓ Local files archived (${archived.join(", ")}.json → .pre-migration)`);
620+
if (totalMigrated > 0) {
621+
console.log("");
622+
console.log(` ✓ Migrated ${totalMigrated} records to Supabase`);
623+
624+
if (hasLive) {
625+
// Archive local files so they aren't re-read
626+
const archived = archiveLocalData(gitmemDir);
627+
if (archived.length > 0) {
628+
console.log(` ✓ Local files archived (${archived.join(", ")}.json → .pre-migration)`);
629+
}
630+
}
631+
} else if (migrationResult.hasLocalData) {
632+
console.log(" ⚠ Migration encountered errors. Local data preserved.");
633+
console.log(" Check .gitmem/migration.log for details.");
634+
console.log(" Fix issues and re-run: npx gitmem-mcp activate");
588635
}
589-
} else if (migrationResult.hasLocalData) {
590-
console.log(" ⚠ Migration encountered errors. Local data preserved.");
591-
console.log(" Re-run activate after resolving issues.");
592-
}
593636

594-
console.log("");
637+
console.log("");
638+
}
595639
}
596640

597641
// Summary

0 commit comments

Comments
 (0)