Skip to main content
Glama
noteFormatter.ts6.47 kB
/** * Service for formatting session data into markdown notes */ import type { SessionNote, CommandEntry, FileChange, CodeSnippet } from '../types/session.js'; /** * Format a session note as markdown */ export function formatNoteAsMarkdown(note: SessionNote): string { const lines: string[] = []; // Header lines.push(`# ${note.projectName || 'Claude Code Session'}`); if (note.topic) { lines.push(`## ${note.topic}`); } lines.push(''); // Metadata lines.push('---'); lines.push(`**Date**: ${new Date(note.timestamp).toLocaleString()}`); if (note.workingDirectory) { lines.push(`**Working Directory**: \`${note.workingDirectory}\``); } if (note.tags && note.tags.length > 0) { lines.push(`**Tags**: ${note.tags.map(t => `#${t}`).join(', ')}`); } if (note.analysis) { if (note.analysis.pattern) { lines.push(`**Pattern**: ${formatPattern(note.analysis.pattern)}`); } if (note.analysis.complexity) { const fileCountText = note.analysis.fileCount ? ` (${note.analysis.fileCount} file${note.analysis.fileCount !== 1 ? 's' : ''})` : ''; lines.push(`**Complexity**: ${formatComplexity(note.analysis.complexity)}${fileCountText}`); } } lines.push('---'); lines.push(''); // Summary lines.push('## Summary'); lines.push(''); lines.push(note.summary); lines.push(''); // Key Changes (concise bullets from file descriptions) if (note.fileChanges && note.fileChanges.length > 0) { const changes = note.fileChanges .filter(f => f.description && f.description.trim().length > 0) .map(f => f.description!.trim()) .slice(0, 3); if (changes.length > 0) { lines.push('## Key Changes'); lines.push(''); changes.forEach(change => lines.push(`- ${change}`)); lines.push(''); } else { // Fallback: basic file count summary const created = note.fileChanges.filter(f => f.type === 'created').length; const modified = note.fileChanges.filter(f => f.type === 'modified').length; const deleted = note.fileChanges.filter(f => f.type === 'deleted').length; if (created + modified + deleted > 0) { lines.push('## Key Changes'); lines.push(''); if (created > 0) lines.push(`- Created ${created} file${created > 1 ? 's' : ''}`); if (modified > 0) lines.push(`- Modified ${modified} file${modified > 1 ? 's' : ''}`); if (deleted > 0) lines.push(`- Deleted ${deleted} file${deleted > 1 ? 's' : ''}`); lines.push(''); } } } // Commands executed if (note.commands && note.commands.length > 0) { lines.push('## Commands Executed'); lines.push(''); for (const cmd of note.commands) { lines.push(`### ${cmd.description || 'Command'}`); lines.push('```bash'); lines.push(cmd.command); lines.push('```'); if (cmd.output) { lines.push(''); lines.push('<details>'); lines.push('<summary>Output</summary>'); lines.push(''); lines.push('```'); lines.push(cmd.output); lines.push('```'); lines.push('</details>'); } lines.push(''); } } // File changes if (note.fileChanges && note.fileChanges.length > 0) { lines.push('## File Changes'); lines.push(''); const created = note.fileChanges.filter(f => f.type === 'created'); const modified = note.fileChanges.filter(f => f.type === 'modified'); const deleted = note.fileChanges.filter(f => f.type === 'deleted'); if (created.length > 0) { lines.push('### Created Files'); for (const file of created) { lines.push(`- \`${file.path}\`${file.description ? ` - ${file.description}` : ''}`); } lines.push(''); } if (modified.length > 0) { lines.push('### Modified Files'); for (const file of modified) { lines.push(`- \`${file.path}\`${file.description ? ` - ${file.description}` : ''}`); if (file.diff) { lines.push(' <details>'); lines.push(' <summary>Changes</summary>'); lines.push(' '); lines.push(' ```diff'); lines.push(file.diff); lines.push(' ```'); lines.push(' </details>'); } } lines.push(''); } if (deleted.length > 0) { lines.push('### Deleted Files'); for (const file of deleted) { lines.push(`- \`${file.path}\`${file.description ? ` - ${file.description}` : ''}`); } lines.push(''); } } // Code snippets if (note.codeSnippets && note.codeSnippets.length > 0) { lines.push('## Code Snippets'); lines.push(''); for (const snippet of note.codeSnippets) { if (snippet.description) { lines.push(`### ${snippet.description}`); } if (snippet.filePath) { lines.push(`*From: \`${snippet.filePath}\`*`); lines.push(''); } lines.push('```' + snippet.language); lines.push(snippet.code); lines.push('```'); lines.push(''); } } return lines.join('\n'); } /** * Generate a filename for the note based on project and timestamp */ export function generateFilename(note: SessionNote): string { const date = new Date(note.timestamp); const dateStr = date.toISOString().split('T')[0]; // YYYY-MM-DD const timeStr = date.toTimeString().split(' ')[0].replace(/:/g, '-'); // HH-MM-SS const projectSlug = (note.projectName || 'session') .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); const topicSlug = note.topic ? '-' + note.topic.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : ''; return `${dateStr}_${timeStr}_${projectSlug}${topicSlug}.md`; } /** * Format pattern for display */ function formatPattern(pattern: string): string { const patterns: Record<string, string> = { 'new-feature': '🆕 New Feature', 'bug-fix': '🐛 Bug Fix', 'refactoring': '♻️ Refactoring', 'documentation': '📝 Documentation', 'configuration': '⚙️ Configuration', 'testing': '✅ Testing', 'mixed': '🔀 Mixed', }; return patterns[pattern] || pattern; } /** * Format complexity for display */ function formatComplexity(complexity: string): string { const complexities: Record<string, string> = { 'simple': '🟢 Simple', 'moderate': '🟡 Moderate', 'complex': '🔴 Complex', }; return complexities[complexity] || complexity; }

Latest Blog Posts

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/VoCoufi/second-brain-mcp'

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