automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
91 lines (90 loc) • 4.09 kB
JavaScript
;
/**
* Spell utilities for MCP server
* Extracted from server.ts per Amendment 10 (file size discipline)
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.listSpellsInDir = listSpellsInDir;
exports.readSpellContent = readSpellContent;
exports.normalizeSpellPath = normalizeSpellPath;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const server_helpers_js_1 = require("./server-helpers.js");
// Helper: List all spell files in a directory recursively
function listSpellsInDir(dir, basePath = '') {
const spells = [];
try {
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path_1.default.join(dir, entry.name);
const relativePath = basePath ? path_1.default.join(basePath, entry.name) : entry.name;
if (entry.isDirectory()) {
// Recurse into subdirectories
spells.push(...listSpellsInDir(fullPath, relativePath));
}
else if (entry.isFile() && entry.name.endsWith('.md')) {
// Extract spell name from frontmatter if possible
try {
const content = fs_1.default.readFileSync(fullPath, 'utf-8');
const frontmatterMatch = content.match(/^---\s*\nname:\s*(.+)\s*\n/);
const spellName = frontmatterMatch ? frontmatterMatch[1].trim() : entry.name.replace('.md', '');
spells.push({ path: relativePath, name: spellName });
}
catch {
spells.push({ path: relativePath, name: entry.name.replace('.md', '') });
}
}
}
}
catch (error) {
// Directory doesn't exist or can't be read
}
return spells;
}
// Helper: Read spell content and extract everything after frontmatter
function readSpellContent(spellPath) {
try {
const content = fs_1.default.readFileSync(spellPath, 'utf-8');
// Find the end of frontmatter (second ---)
const frontmatterEnd = content.indexOf('---', 3);
if (frontmatterEnd === -1) {
// No frontmatter, return entire content
return content;
}
// Return everything after the closing ---
return content.substring(frontmatterEnd + 3).trim();
}
catch (error) {
throw new Error(`Failed to read spell: ${error.message}`);
}
}
// Helper: Normalize spell path (strip leading .genie/, add directory if missing, add .md if missing)
function normalizeSpellPath(userPath) {
const workspaceRoot = (0, server_helpers_js_1.findWorkspaceRoot)();
// Strip leading .genie/ if present (prevents double prefix)
let normalized = userPath.replace(/^\.genie[\\/]/, '');
// If path contains no directory component and no scope prefix, search all scope directories
if (!normalized.includes('/') && !normalized.includes('\\')) {
// Try to find the spell in global, code, or create directories
const searchDirs = ['spells', 'code/spells', 'create/spells'];
for (const dir of searchDirs) {
const testPath = path_1.default.join(workspaceRoot, '.genie', dir, normalized.endsWith('.md') ? normalized : `${normalized}.md`);
if (fs_1.default.existsSync(testPath)) {
normalized = path_1.default.join(dir, normalized.endsWith('.md') ? normalized : `${normalized}.md`);
break;
}
}
// If not found in any directory, default to spells/ (will fail with clear error)
if (!normalized.includes('/') && !normalized.includes('\\')) {
normalized = path_1.default.join('spells', normalized);
}
}
// Add .md extension if missing
if (!normalized.endsWith('.md')) {
normalized += '.md';
}
return normalized;
}