@sofianedjerbi/knowledge-tree-mcp
Version:
MCP server for hierarchical project knowledge management
187 lines • 7.42 kB
JavaScript
/**
* Markdown export utilities for Knowledge Tree MCP
* Handles conversion of knowledge entries to Markdown format
*/
import { PRIORITY_LEVELS, PRIORITY_ORDER } from '../../constants/index.js';
/**
* Exports knowledge entries to Markdown format
* @param entries - Array of entries to export
* @param includeLinks - Whether to include relationship links
* @returns Markdown formatted string
*/
export function exportToMarkdown(entries, includeLinks) {
let md = '# Knowledge Tree Export\n\n';
md += `_Generated on ${new Date().toISOString()}_\n\n`;
md += `**Total Entries**: ${entries.length}\n\n`;
// Group by priority
const byPriority = entries.reduce((acc, { path, entry }) => {
if (!acc[entry.priority])
acc[entry.priority] = [];
acc[entry.priority].push({ path, entry });
return acc;
}, {});
// Export each priority group in order
for (const priority of PRIORITY_LEVELS) {
const group = byPriority[priority];
if (!group || group.length === 0)
continue;
md += `## ${priority} (${group.length})\n\n`;
for (const { path, entry } of group) {
md += `### 📄 ${entry.title || path}\n\n`;
if (entry.category || entry.tags) {
if (entry.category)
md += `**Category**: ${entry.category} `;
if (entry.tags && entry.tags.length > 0)
md += `**Tags**: ${entry.tags.join(', ')}`;
md += '\n\n';
}
md += `**Problem**: ${entry.problem}\n\n`;
if (entry.context) {
md += `**Context**: ${entry.context}\n\n`;
}
md += `**Solution**: ${entry.solution}\n\n`;
if (entry.examples && entry.examples.length > 0) {
md += '**Examples**:\n\n';
for (const example of entry.examples) {
if (example.title)
md += `_${example.title}_\n`;
if (example.description)
md += `${example.description}\n`;
if (example.code) {
const lang = example.language || '';
md += `\`\`\`${lang}\n${example.code}\n\`\`\`\n`;
}
md += '\n';
}
}
else if (entry.code) {
md += '**Code**:\n```\n' + entry.code + '\n```\n\n';
}
if (includeLinks && entry.related_to && entry.related_to.length > 0) {
md += '**Related**:\n';
for (const link of entry.related_to) {
md += `- ${link.relationship}: [${link.path}](#${link.path.replace(/[^\w-]/g, '-')})\n`;
if (link.description) {
md += ` - ${link.description}\n`;
}
}
md += '\n';
}
if (entry.author || entry.version || entry.created_at || entry.updated_at) {
md += '**Metadata**:\n';
if (entry.author)
md += `- Author: ${entry.author}\n`;
if (entry.version)
md += `- Version: ${entry.version}\n`;
if (entry.created_at)
md += `- Created: ${new Date(entry.created_at).toLocaleDateString()}\n`;
if (entry.updated_at)
md += `- Updated: ${new Date(entry.updated_at).toLocaleDateString()}\n`;
md += '\n';
}
md += `**Path**: \`${path}\`\n\n`;
md += '---\n\n';
}
}
return md;
}
/**
* Creates a markdown table of contents from entries
* @param entries - Array of entries
* @returns Markdown TOC string
*/
export function createMarkdownTOC(entries) {
let toc = '## Table of Contents\n\n';
// Group by category
const byCategory = entries.reduce((acc, { path, entry }) => {
const category = path.split('/').slice(0, -1).join('/') || 'root';
if (!acc[category])
acc[category] = [];
acc[category].push({ path, entry });
return acc;
}, {});
// Sort categories
const sortedCategories = Object.keys(byCategory).sort();
for (const category of sortedCategories) {
toc += `### ${category}\n`;
const categoryEntries = byCategory[category];
// Sort by priority then filename
categoryEntries.sort((a, b) => {
const priorityDiff = PRIORITY_ORDER[a.entry.priority] - PRIORITY_ORDER[b.entry.priority];
if (priorityDiff !== 0)
return priorityDiff;
return a.path.localeCompare(b.path);
});
for (const { path, entry } of categoryEntries) {
const filename = path.split('/').pop();
const title = entry.title || filename;
const anchor = path.replace(/[^\w-]/g, '-');
toc += `- [${title}](#${anchor}) - ${entry.priority}\n`;
}
toc += '\n';
}
return toc;
}
/**
* Formats a single knowledge entry as markdown
* @param entry - Knowledge entry
* @param path - Entry path
* @param includeLinks - Whether to include links
* @returns Markdown formatted entry
*/
export function formatEntryAsMarkdown(entry, path, includeLinks = true) {
let md = `## ${entry.title || path}\n\n`;
md += `**Priority**: ${entry.priority}\n\n`;
if (entry.category || entry.tags) {
if (entry.category)
md += `**Category**: ${entry.category}\n`;
if (entry.tags && entry.tags.length > 0)
md += `**Tags**: ${entry.tags.join(', ')}\n`;
md += '\n';
}
md += `**Problem**: ${entry.problem}\n\n`;
if (entry.context) {
md += `**Context**: ${entry.context}\n\n`;
}
md += `**Solution**: ${entry.solution}\n\n`;
if (entry.examples && entry.examples.length > 0) {
md += '**Examples**:\n\n';
for (const example of entry.examples) {
if (example.title)
md += `### ${example.title}\n`;
if (example.description)
md += `${example.description}\n\n`;
if (example.code) {
const lang = example.language || '';
md += `\`\`\`${lang}\n${example.code}\n\`\`\`\n\n`;
}
}
}
else if (entry.code) {
md += '**Code Example**:\n```\n' + entry.code + '\n```\n\n';
}
if (includeLinks && entry.related_to && entry.related_to.length > 0) {
md += '**Related Entries**:\n';
for (const link of entry.related_to) {
md += `- **${link.relationship}**: ${link.path}`;
if (link.description) {
md += ` - ${link.description}`;
}
md += '\n';
}
md += '\n';
}
if (entry.author || entry.version || entry.created_at || entry.updated_at) {
md += '**Metadata**:\n';
if (entry.author)
md += `- Author: ${entry.author}\n`;
if (entry.version)
md += `- Version: ${entry.version}\n`;
if (entry.created_at)
md += `- Created: ${new Date(entry.created_at).toLocaleDateString()}\n`;
if (entry.updated_at)
md += `- Updated: ${new Date(entry.updated_at).toLocaleDateString()}\n`;
}
return md;
}
//# sourceMappingURL=markdown.js.map