Skip to main content
Glama
hybrid-pipeline.ts5.14 kB
import { execFile } from "node:child_process"; import { promisify } from "node:util"; import * as path from "node:path"; import * as fs from "node:fs/promises"; import { runCommand } from "../utils/runCommand.js"; const execp = promisify(execFile); export interface PreprocessOptions { pattern?: string; output?: string; stats?: boolean; cwd: string; } export interface BuildPdfOptions { config?: string; skipPreprocess?: boolean; cwd: string; } export interface TestMapfileOptions { file: string; cwd: string; } export async function preprocessCommand(options: PreprocessOptions) { const { pattern = "articles/**/*.re", output = ".out", stats = true, cwd } = options; // Skip preprocessing for now since review-macro-shims is not available // In production, this would call the actual preprocessor console.log("[preprocess] Skipping JS preprocessing (using standard Re:VIEW)"); return { success: true, output: "Preprocessing skipped - using standard Re:VIEW", stats: stats ? { filesProcessed: 0, macrosExpanded: 0, warnings: [] } : null }; } export async function buildPdfHybridCommand(options: BuildPdfOptions) { const { config = "config.yml", skipPreprocess = false, cwd } = options; const results: any[] = []; if (!skipPreprocess) { console.log("[build-pdf-hybrid] Running preprocessor..."); const preprocessResult = await preprocessCommand({ cwd }); results.push({ step: "preprocess", result: preprocessResult }); if (!preprocessResult.success) { return { success: false, error: "Preprocessing failed", results }; } } console.log("[build-pdf-hybrid] Building PDF with review-pdfmaker..."); try { const pdfResult = await runCommand("review-pdfmaker", ["-c", config], { cwd, useBundle: true }); results.push({ step: "pdf-build", result: { success: true, output: pdfResult.stdout }}); const pdfFiles = await findGeneratedPdf(cwd); return { success: true, results, artifacts: pdfFiles }; } catch (error: any) { results.push({ step: "pdf-build", result: { success: false, error: error.message, stderr: error.stderr }}); return { success: false, error: "PDF build failed", results }; } } export async function checkRubyExtensionsCommand(options: { cwd: string }) { const { cwd } = options; try { const result = await runCommand("ruby", [ "-r", "./review-ext.rb", "-e", "puts 'Extensions loaded: ' + $LOADED_FEATURES.grep(/review/).join(', ')" ], { cwd, env: { ...process.env, DEBUG: "1" } }); const extensionsLoaded = result.stdout.includes("Extensions loaded"); const loadedFiles = result.stdout.match(/Extensions loaded: (.+)/)?.[1] || ""; return { success: extensionsLoaded, loadedExtensions: loadedFiles.split(", ").filter(Boolean), output: result.stdout }; } catch (error: any) { return { success: false, error: error.message, stderr: error.stderr }; } } export async function testMapfileCommand(options: TestMapfileOptions) { const { file, cwd } = options; const testFile = path.join(cwd, "test-mapfile.re"); const testContent = `//list[test]{ #@mapfile(${file}) #@end //} `; try { await fs.writeFile(testFile, testContent, "utf-8"); const result = await preprocessCommand({ pattern: "test-mapfile.re", output: ".out", stats: true, cwd }); await fs.unlink(testFile).catch(() => {}); if (result.success) { const outputFile = path.join(cwd, ".out", "test-mapfile.re"); const processedContent = await fs.readFile(outputFile, "utf-8").catch(() => ""); return { success: true, processedContent, stats: result.stats }; } return result; } catch (error: any) { await fs.unlink(testFile).catch(() => {}); return { success: false, error: error.message }; } } function parsePreprocessStats(output: string): any { const stats: any = { filesProcessed: 0, macrosExpanded: 0, warnings: [] }; const lines = output.split(/\r?\n/); for (const line of lines) { if (line.includes("Files processed:")) { stats.filesProcessed = parseInt(line.match(/\d+/)?.[0] || "0"); } if (line.includes("Macros expanded:")) { stats.macrosExpanded = parseInt(line.match(/\d+/)?.[0] || "0"); } if (line.includes("Warning:")) { stats.warnings.push(line); } } return stats; } async function findGeneratedPdf(cwd: string): Promise<string[]> { try { const files = await fs.readdir(cwd); const pdfFiles = files.filter(f => f.endsWith(".pdf")); return pdfFiles.map(f => path.join(cwd, f)); } catch { return []; } } export const hybridCommands = { preprocess: preprocessCommand, buildPdfHybrid: buildPdfHybridCommand, checkRubyExtensions: checkRubyExtensionsCommand, testMapfile: testMapfileCommand };

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/dsgarage/ReviewMCP'

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