ever-brain / packages /cli /src /services /materialize-service.ts
AlexKurian's picture
feat: complete Ever-Brain engine, MCP server, CLI evb mcp command, Dockerfile and release workflow
61b1da1
Raw History Blame Contribute Delete
3.3 kB
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { cacheService } from './cache-service.js';
export interface MaterializeResult {
skillId: string;
targetAgent: string;
destinationPath: string;
filesCopied: number;
}
export class MaterializeService {
materialize(
workspaceRoot: string,
skillId: string,
options?: {
target?: 'agents' | 'gemini' | 'cursor' | 'claude' | 'auto';
global?: boolean;
}
): MaterializeResult {
const targetAgent = options?.target && options.target !== 'auto' ? options.target : 'agents';
const isGlobal = !!options?.global;
// Locate skill in cache
const skillDirRel = `skills/${skillId}`;
const allCached = cacheService.listCachedFiles(workspaceRoot);
const skillFiles = allCached.filter(
(f) => f === `${skillDirRel}/SKILL.md` || f === `${skillDirRel}.md` || f.startsWith(`${skillDirRel}/`)
);
if (skillFiles.length === 0) {
throw new Error(`Skill '${skillId}' not found in local cache. Run 'evb sync' to update.`);
}
let destDir = '';
let primaryFile = '';
if (targetAgent === 'agents') {
const base = isGlobal ? path.join(os.homedir(), '.agents', 'skills') : path.join(workspaceRoot, '.agents', 'skills');
destDir = path.join(base, skillId);
primaryFile = path.join(destDir, 'SKILL.md');
} else if (targetAgent === 'gemini') {
const base = isGlobal ? path.join(os.homedir(), '.gemini', 'skills') : path.join(workspaceRoot, '.gemini', 'skills');
destDir = path.join(base, skillId);
primaryFile = path.join(destDir, 'SKILL.md');
} else if (targetAgent === 'cursor') {
const base = isGlobal ? path.join(os.homedir(), '.cursor', 'rules') : path.join(workspaceRoot, '.cursor', 'rules');
destDir = base;
primaryFile = path.join(destDir, `${skillId}.mdc`);
} else if (targetAgent === 'claude') {
const base = isGlobal ? path.join(os.homedir(), '.claude', 'skills') : path.join(workspaceRoot, '.claude', 'skills');
destDir = path.join(base, skillId);
primaryFile = path.join(destDir, 'SKILL.md');
}
fs.mkdirSync(destDir, { recursive: true });
let filesCopied = 0;
for (const file of skillFiles) {
const content = cacheService.readCachedFile(workspaceRoot, file);
if (content === null) continue;
if (targetAgent === 'cursor') {
// For Cursor, write primary mdc rule
if (file.endsWith('SKILL.md') || file === `${skillDirRel}.md`) {
fs.writeFileSync(primaryFile, content, 'utf8');
filesCopied++;
}
} else {
// For universal .agents, Gemini & Claude, copy directory structure
const subPath = file.startsWith(`${skillDirRel}/`)
? file.slice(`${skillDirRel}/`.length)
: 'SKILL.md';
const targetFilePath = path.join(destDir, subPath);
fs.mkdirSync(path.dirname(targetFilePath), { recursive: true });
fs.writeFileSync(targetFilePath, content, 'utf8');
filesCopied++;
}
}
return {
skillId,
targetAgent,
destinationPath: targetAgent === 'cursor' ? primaryFile : destDir,
filesCopied,
};
}
}
export const materializeService = new MaterializeService();