swadhinbiswas feat: OpenCodeHub v1.1.0 - Enterprise Features & Storage Adapters · 2/15/2026
719a1cb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { simpleGit } from "simple-git";
import { logger } from "@/lib/logger";
import { getDatabase, schema } from "@/db";
import path from "path";
const REPOS_BASE_PATH = process.env.REPOS_PATH || path.join(process.cwd(), "data", "repos");
export async function cleanupAllRepos() {
const db = getDatabase();
const repos = await db.query.repositories.findMany();
for (const repo of repos) {
if (repo.diskPath) {
const repoPath = path.join(REPOS_BASE_PATH, repo.diskPath);
await cleanupSpeculativeBranches(repoPath);
}
}
}
/**
* Cleanup stale speculative branches
* Deletes branches starting with 'mq-spec-' that are older than X hours
*/
export async function cleanupSpeculativeBranches(repoPath: string, maxAgeHours: number = 24) {
const git = simpleGit(repoPath);
try {
await git.fetch("origin", "--prune");
const branches = await git.branchLocal();
const now = Date.now();
const maxAgeMs = maxAgeHours * 60 * 60 * 1000;
let deletedCount = 0;
for (const branch of branches.all) {
if (branch.startsWith("mq-spec-")) {
// Parse timestamp from branch name: mq-spec-{timestamp}-{prNumbers}
const parts = branch.split("-");
if (parts.length >= 3) {
const timestamp = parseInt(parts[2], 10);
if (!isNaN(timestamp) && (now - timestamp > maxAgeMs)) {
try {
await git.deleteLocalBranch(branch, true); // Force delete
logger.info({ branch }, "Deleted stale speculative branch");
deletedCount++;
} catch (e) {
logger.error({ branch, error: e }, "Failed to delete stale branch");
}
}
}
}
}
if (deletedCount > 0) {
logger.info({ deletedCount }, "Speculative branch cleanup completed");
}
} catch (error) {
logger.error({ error, repoPath }, "Failed to run speculative branch cleanup");
}
}