Skip to main content
Glama

Fork Parity MCP

by moikas-code
database.jsβ€’11.6 kB
import Database from 'better-sqlite3'; import { join } from 'path'; import { existsSync, mkdirSync } from 'fs'; class ForkParityDatabase { constructor(dbPath = null) { // Default to .fork-parity directory in project root if (!dbPath) { const projectRoot = process.cwd(); const parityDir = join(projectRoot, '.fork-parity'); if (!existsSync(parityDir)) { mkdirSync(parityDir, { recursive: true }); } dbPath = join(parityDir, 'parity.db'); } this.db = new Database(dbPath); this.initializeSchema(); } initializeSchema() { // Repository configuration this.db.exec(` CREATE TABLE IF NOT EXISTS repositories ( id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT UNIQUE NOT NULL, upstream_url TEXT, upstream_branch TEXT DEFAULT 'main', fork_branch TEXT DEFAULT 'main', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Commit tracking with rich metadata this.db.exec(` CREATE TABLE IF NOT EXISTS commits ( id INTEGER PRIMARY KEY AUTOINCREMENT, repository_id INTEGER NOT NULL, hash TEXT NOT NULL, author TEXT, author_email TEXT, commit_date DATETIME, message TEXT, files_changed TEXT, -- JSON array of changed files insertions INTEGER DEFAULT 0, deletions INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (repository_id) REFERENCES repositories (id), UNIQUE(repository_id, hash) ) `); // Triage results from auto-analysis this.db.exec(` CREATE TABLE IF NOT EXISTS triage_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, commit_id INTEGER NOT NULL, priority TEXT CHECK(priority IN ('critical', 'high', 'medium', 'low')) NOT NULL, category TEXT CHECK(category IN ('security', 'bugfix', 'feature', 'refactor', 'docs', 'test', 'chore')) NOT NULL, impact_areas TEXT, -- JSON array of impact areas conflict_risk REAL CHECK(conflict_risk >= 0 AND conflict_risk <= 1), effort_estimate TEXT CHECK(effort_estimate IN ('trivial', 'small', 'medium', 'large', 'xl')), reasoning TEXT, confidence REAL CHECK(confidence >= 0 AND confidence <= 1), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (commit_id) REFERENCES commits (id), UNIQUE(commit_id) ) `); // Status tracking with decision history this.db.exec(` CREATE TABLE IF NOT EXISTS commit_status ( id INTEGER PRIMARY KEY AUTOINCREMENT, commit_id INTEGER NOT NULL, status TEXT CHECK(status IN ('pending', 'reviewed', 'integrated', 'skipped', 'conflict', 'deferred')) NOT NULL, decision_reasoning TEXT, reviewer TEXT, review_date DATETIME, adaptation_notes TEXT, integration_effort_actual TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (commit_id) REFERENCES commits (id) ) `); // Integration history for tracking what was actually done this.db.exec(` CREATE TABLE IF NOT EXISTS integrations ( id INTEGER PRIMARY KEY AUTOINCREMENT, commit_id INTEGER NOT NULL, integration_type TEXT CHECK(integration_type IN ('direct', 'adapted', 'cherry-pick', 'manual')) NOT NULL, target_branch TEXT, integration_commit_hash TEXT, conflicts_resolved TEXT, -- JSON array of conflict details adaptation_changes TEXT, -- Description of changes made integration_date DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (commit_id) REFERENCES commits (id) ) `); // Metrics and analytics this.db.exec(` CREATE TABLE IF NOT EXISTS metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, repository_id INTEGER NOT NULL, metric_type TEXT NOT NULL, metric_value REAL, metric_data TEXT, -- JSON for complex metrics period_start DATETIME, period_end DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (repository_id) REFERENCES repositories (id) ) `); // Create indexes for performance this.db.exec(` CREATE INDEX IF NOT EXISTS idx_commits_repo_hash ON commits(repository_id, hash); CREATE INDEX IF NOT EXISTS idx_commits_date ON commits(commit_date); CREATE INDEX IF NOT EXISTS idx_triage_priority ON triage_results(priority); CREATE INDEX IF NOT EXISTS idx_status_status ON commit_status(status); CREATE INDEX IF NOT EXISTS idx_metrics_type_date ON metrics(metric_type, created_at); `); // Create triggers for updated_at timestamps this.db.exec(` CREATE TRIGGER IF NOT EXISTS update_repositories_timestamp AFTER UPDATE ON repositories BEGIN UPDATE repositories SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; `); this.db.exec(` CREATE TRIGGER IF NOT EXISTS update_commit_status_timestamp AFTER UPDATE ON commit_status BEGIN UPDATE commit_status SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; `); } // Repository management addRepository(path, upstreamUrl, upstreamBranch = 'main', forkBranch = 'main') { const stmt = this.db.prepare(` INSERT OR REPLACE INTO repositories (path, upstream_url, upstream_branch, fork_branch) VALUES (?, ?, ?, ?) `); return stmt.run(path, upstreamUrl, upstreamBranch, forkBranch); } getRepository(path) { const stmt = this.db.prepare('SELECT * FROM repositories WHERE path = ?'); return stmt.get(path); } // Commit management addCommit(repositoryId, commitData) { const stmt = this.db.prepare(` INSERT OR REPLACE INTO commits (repository_id, hash, author, author_email, commit_date, message, files_changed, insertions, deletions) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `); return stmt.run( repositoryId, commitData.hash, commitData.author, commitData.authorEmail, commitData.commitDate, commitData.message, JSON.stringify(commitData.filesChanged || []), commitData.insertions || 0, commitData.deletions || 0 ); } getCommit(repositoryId, hash) { const stmt = this.db.prepare(` SELECT c.*, tr.*, cs.status, cs.decision_reasoning, cs.reviewer, cs.review_date FROM commits c LEFT JOIN triage_results tr ON c.id = tr.commit_id LEFT JOIN commit_status cs ON c.id = cs.commit_id WHERE c.repository_id = ? AND c.hash = ? `); return stmt.get(repositoryId, hash); } // Triage management addTriageResult(commitId, triageData) { const stmt = this.db.prepare(` INSERT OR REPLACE INTO triage_results (commit_id, priority, category, impact_areas, conflict_risk, effort_estimate, reasoning, confidence) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); return stmt.run( commitId, triageData.priority, triageData.category, JSON.stringify(triageData.impactAreas || []), triageData.conflictRisk, triageData.effortEstimate, triageData.reasoning, triageData.confidence ); } // Status management updateCommitStatus(commitId, status, metadata = {}) { const stmt = this.db.prepare(` INSERT OR REPLACE INTO commit_status (commit_id, status, decision_reasoning, reviewer, review_date, adaptation_notes, integration_effort_actual) VALUES (?, ?, ?, ?, ?, ?, ?) `); return stmt.run( commitId, status, metadata.decisionReasoning, metadata.reviewer, metadata.reviewDate || new Date().toISOString(), metadata.adaptationNotes, metadata.integrationEffortActual ); } // Analytics and reporting getParityDashboard(repositoryId, options = {}) { const { since, priority, status } = options; let whereClause = 'WHERE c.repository_id = ?'; let params = [repositoryId]; if (since) { whereClause += ' AND c.commit_date >= ?'; params.push(since); } if (priority) { whereClause += ' AND tr.priority = ?'; params.push(priority); } if (status) { whereClause += ' AND cs.status = ?'; params.push(status); } // Get summary statistics const summaryStmt = this.db.prepare(` SELECT COUNT(*) as total_commits, COUNT(CASE WHEN cs.status = 'integrated' THEN 1 END) as integrated_count, COUNT(CASE WHEN cs.status = 'skipped' THEN 1 END) as skipped_count, COUNT(CASE WHEN cs.status = 'pending' THEN 1 END) as pending_count, COUNT(CASE WHEN tr.priority = 'critical' THEN 1 END) as critical_count, COUNT(CASE WHEN tr.priority = 'high' THEN 1 END) as high_count, AVG(tr.conflict_risk) as avg_conflict_risk FROM commits c LEFT JOIN triage_results tr ON c.id = tr.commit_id LEFT JOIN commit_status cs ON c.id = cs.commit_id ${whereClause} `); const summary = summaryStmt.get(...params); // Get actionable items (high priority, pending status) const actionableStmt = this.db.prepare(` SELECT c.hash, c.message, c.author, c.commit_date, tr.priority, tr.category, tr.reasoning FROM commits c JOIN triage_results tr ON c.id = tr.commit_id LEFT JOIN commit_status cs ON c.id = cs.commit_id WHERE c.repository_id = ? AND tr.priority IN ('critical', 'high') AND (cs.status IS NULL OR cs.status = 'pending') ORDER BY CASE tr.priority WHEN 'critical' THEN 1 WHEN 'high' THEN 2 ELSE 3 END, c.commit_date DESC LIMIT 20 `); const actionableItems = actionableStmt.all(repositoryId); return { summary, actionableItems, generatedAt: new Date().toISOString() }; } getCommitsByStatus(repositoryId, status, limit = 50) { const stmt = this.db.prepare(` SELECT c.*, tr.priority, tr.category, cs.status, cs.decision_reasoning FROM commits c LEFT JOIN triage_results tr ON c.id = tr.commit_id LEFT JOIN commit_status cs ON c.id = cs.commit_id WHERE c.repository_id = ? AND cs.status = ? ORDER BY c.commit_date DESC LIMIT ? `); return stmt.all(repositoryId, status, limit); } // Batch operations batchUpdateStatus(commitIds, status, metadata = {}) { const stmt = this.db.prepare(` INSERT OR REPLACE INTO commit_status (commit_id, status, decision_reasoning, reviewer, review_date) VALUES (?, ?, ?, ?, ?) `); const transaction = this.db.transaction((ids) => { for (const commitId of ids) { stmt.run( commitId, status, metadata.decisionReasoning, metadata.reviewer, metadata.reviewDate || new Date().toISOString() ); } }); return transaction(commitIds); } // Database maintenance vacuum() { this.db.exec('VACUUM'); } backup(backupPath) { return this.db.backup(backupPath); } close() { this.db.close(); } // Helper method to get commit ID getCommitId(repositoryId, hash) { const stmt = this.db.prepare('SELECT id FROM commits WHERE repository_id = ? AND hash = ?'); const result = stmt.get(repositoryId, hash); return result?.id; } } export default ForkParityDatabase;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/moikas-code/fork-parity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server