Skip to main content
Glama
generator.py15.9 kB
""" HiveMind MCP Server - Generator Generate hivemind.md and flowchart.mmd from structure and AI context. """ import os from pathlib import Path from typing import Dict, List, Optional, Any from datetime import datetime import aiofiles from config import HIVEMIND_FILENAME, FLOWCHART_FILENAME, get_complexity_label from utils import ( sanitize_filename, create_anchor, format_list, format_code_block, get_relative_path, truncate_string, ) async def generate_hivemind( structure: Dict[str, Any], ai_context: Optional[Dict[str, Any]] = None ) -> str: """ Generate hivemind.md content from structure and AI context. Args: structure: Parsed structure dictionary from parser.analyze_directory() ai_context: Optional AI-provided context dictionary Returns: Formatted markdown string """ ai_context = ai_context or {} sections = [] # Header dir_name = structure.get('name', 'Unknown') sections.append(f"# {dir_name}") sections.append("") # ============================================================ # AI Context Sections (above the line) # ============================================================ # 🎯 What This Does if ai_context.get('context'): sections.append("## What This Does") sections.append(ai_context['context']) sections.append("") # 👤 User Requirements if ai_context.get('user_requirements'): sections.append("## User Requirements") sections.append(ai_context['user_requirements']) sections.append("") # ⚠️ Important Notes if ai_context.get('warnings'): sections.append("## Important Notes") sections.append(ai_context['warnings']) sections.append("") # 🔮 Next Steps if ai_context.get('next_steps'): sections.append("## Next Steps") sections.append(ai_context['next_steps']) sections.append("") # 💡 How It Works if ai_context.get('how_it_works'): sections.append("## How It Works") sections.append(ai_context['how_it_works']) sections.append("") # ============================================================ # Separator # ============================================================ sections.append("---") sections.append("") # ============================================================ # Dry Logic Sections (below the line) # ============================================================ # 📁 Files at This Level files = structure.get('files', []) if files: sections.append("## Files at This Level") for f in files: name = f.get('name', 'unknown') lines = f.get('lines', 0) sections.append(f"- `{name}` ({lines} lines)") sections.append("") # ⚙️ Functions Defined functions = structure.get('all_functions', []) if functions: sections.append("## Functions Defined") sections.append("") for func in functions: name = func.get('name', 'unknown') params = func.get('params', '()') return_type = func.get('return_type', 'void') # Function signature sections.append(f"### `{name}{params}: {return_type}`") # Details file_name = Path(func.get('file_path', '')).name if func.get('file_path') else 'unknown' start = func.get('start_line', 0) end = func.get('end_line', 0) sections.append(f"- **Location**: {file_name}:{start}-{end}") sections.append(f"- **Async**: {'Yes' if func.get('async') else 'No'}") sections.append(f"- **Exported**: {'Yes' if func.get('exported') else 'No'}") sections.append(f"- **Complexity**: {func.get('complexity_label', 'Low')}") if func.get('docstring'): sections.append(f"- **Description**: {truncate_string(func['docstring'], 100)}") sections.append("") # 📦 Dependencies imports = structure.get('all_imports', []) if imports: sections.append("## Dependencies") sections.append("") external = [] internal = [] for imp in imports: module = imp.get('module', '') names = ', '.join(imp.get('imports', [])) if module.startswith('.') or module.startswith('@/') or module.startswith('~/'): internal.append(f"- `{module}` - {names or 'namespace'}") else: external.append(f"- `{module}` - {names or 'namespace'}") if external: sections.append("### External") sections.extend(external) sections.append("") if internal: sections.append("### Internal") sections.extend(internal) sections.append("") # 📤 Exports exports = structure.get('all_exports', []) if exports: sections.append("## Exports") sections.append("") export_lines = [] for exp in exports: name = exp.get('name', '') exp_type = exp.get('type', 'named') is_default = exp.get('is_default', False) if is_default: export_lines.append(f"export default {name}") else: export_lines.append(f"export {exp_type} {name}") if export_lines: sections.append(format_code_block('\n'.join(export_lines), 'typescript')) sections.append("") # Classes classes = structure.get('all_classes', []) if classes: sections.append("## Classes") sections.append("") for cls in classes: name = cls.get('name', 'Unknown') extends = cls.get('extends') sections.append(f"### `{name}`" + (f" extends `{extends}`" if extends else "")) sections.append(f"- **Lines**: {cls.get('start_line', 0)}-{cls.get('end_line', 0)}") sections.append(f"- **Exported**: {'Yes' if cls.get('exported') else 'No'}") methods = cls.get('methods', []) if methods: sections.append(f"- **Methods**: {len(methods)}") for m in methods[:5]: # Limit to 5 methods shown sections.append(f" - `{m.get('name', '')}()`") if len(methods) > 5: sections.append(f" - _...and {len(methods) - 5} more_") sections.append("") # Types (TypeScript) types = structure.get('all_types', []) if types: sections.append("## Types & Interfaces") sections.append("") for t in types: kind = t.get('kind', 'type') name = t.get('name', 'Unknown') sections.append(f"- `{kind} {name}` (line {t.get('start_line', 0)})") sections.append("") # 🔗 Connections connections = structure.get('connections', {}) uses = connections.get('uses', []) used_by = connections.get('used_by', []) if uses or used_by: sections.append("## Connections") sections.append("") if used_by: sections.append("### Used By (Downstream)") for dep in used_by: anchor = create_anchor(Path(dep)) sections.append(f"- `{anchor}`") sections.append("") if uses: sections.append("### Uses (Upstream)") for dep in uses: anchor = create_anchor(Path(dep)) sections.append(f"- `{anchor}`") sections.append("") # Navigation sections.append("## Navigation") sections.append("") current_path = Path(structure.get('path', '')) parent = structure.get('parent') children = structure.get('children', []) # Parent link if parent: parent_path = Path(parent) rel_path = get_relative_path(current_path, parent_path) anchor = create_anchor(parent_path) sections.append(f"- **Parent**: [{parent_path.name}]({rel_path}/hivemind.md) `{anchor}`") # Children links if children: sections.append("- **Children**:") for child in children: child_path = Path(child) rel_path = get_relative_path(current_path, child_path) anchor = create_anchor(child_path) sections.append(f" - [{child_path.name}]({rel_path}/hivemind.md) `{anchor}`") # Current anchor current_anchor = create_anchor(current_path) sections.append(f"- **Anchor**: `{current_anchor}`") sections.append("") # 📊 Metrics metrics = structure.get('metrics', {}) sections.append("## Metrics") sections.append("") sections.append(f"- **Total Lines**: {metrics.get('total_lines', 0)}") sections.append(f"- **Total Files**: {metrics.get('total_files', 0)}") sections.append(f"- **Functions**: {metrics.get('total_functions', 0)}") sections.append(f"- **Classes**: {metrics.get('total_classes', 0)}") sections.append(f"- **Types**: {metrics.get('total_types', 0)}") sections.append(f"- **Avg Complexity**: {metrics.get('complexity_label', 'Low')} ({metrics.get('avg_complexity', 1.0)})") sections.append(f"- **Last Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M')}") sections.append("") return '\n'.join(sections) async def generate_flowchart(structure: Dict[str, Any]) -> str: """ Generate Mermaid flowchart from structure. Args: structure: Parsed structure dictionary Returns: Mermaid diagram string """ lines = [] lines.append("graph TD") lines.append("") # Current directory as central node current_name = structure.get('name', 'current') current_id = sanitize_filename(current_name) lines.append(f" %% Current directory as central node") lines.append(f" {current_id}[{current_name}]") lines.append("") # Parent link parent = structure.get('parent') if parent: parent_name = Path(parent).name parent_id = sanitize_filename(f"parent_{parent_name}") lines.append(f" %% Parent link") lines.append(f" {parent_id}[{parent_name}] --> {current_id}") lines.append("") # Children links children = structure.get('children', []) if children: lines.append(f" %% Children") for child in children: child_name = Path(child).name child_id = sanitize_filename(f"child_{child_name}") lines.append(f" {current_id} --> {child_id}[{child_name}]") lines.append("") # Upstream dependencies (things this imports) connections = structure.get('connections', {}) uses = connections.get('uses', []) if uses: lines.append(f" %% Upstream dependencies (things this imports)") for i, dep in enumerate(uses[:5]): # Limit to 5 dep_name = Path(dep).name dep_id = sanitize_filename(f"dep_{dep_name}_{i}") lines.append(f" {dep_id}[{dep_name}] --> {current_id}") lines.append("") # Downstream usage (things that import this) used_by = connections.get('used_by', []) if used_by: lines.append(f" %% Downstream usage (things that import this)") for i, dep in enumerate(used_by[:5]): # Limit to 5 dep_name = Path(dep).name dep_id = sanitize_filename(f"user_{dep_name}_{i}") lines.append(f" {current_id} --> {dep_id}[{dep_name}]") lines.append("") # Styling lines.append(" %% Styling for visual clarity") lines.append(" classDef currentNode fill:#4f46e5,stroke:#333,stroke-width:3px,color:#fff") lines.append(" classDef parentNode fill:#64748b,stroke:#333,stroke-width:2px,color:#fff") lines.append(" classDef childNode fill:#10b981,stroke:#333,stroke-width:2px,color:#fff") lines.append(" classDef upstreamNode fill:#f59e0b,stroke:#333,stroke-width:2px,color:#fff") lines.append(" classDef downstreamNode fill:#06b6d4,stroke:#333,stroke-width:2px,color:#fff") lines.append("") # Apply styles lines.append(f" class {current_id} currentNode") if parent: parent_name = Path(parent).name parent_id = sanitize_filename(f"parent_{parent_name}") lines.append(f" class {parent_id} parentNode") if children: child_ids = [sanitize_filename(f"child_{Path(c).name}") for c in children] lines.append(f" class {','.join(child_ids)} childNode") if uses: dep_ids = [sanitize_filename(f"dep_{Path(d).name}_{i}") for i, d in enumerate(uses[:5])] lines.append(f" class {','.join(dep_ids)} upstreamNode") if used_by: user_ids = [sanitize_filename(f"user_{Path(d).name}_{i}") for i, d in enumerate(used_by[:5])] lines.append(f" class {','.join(user_ids)} downstreamNode") return '\n'.join(lines) async def write_files( dir_path: Path, hivemind_content: str, flowchart_content: str, ) -> Dict[str, Any]: """ Write hivemind.md and flowchart.mmd to directory. Args: dir_path: Directory to write files to hivemind_content: Content for hivemind.md flowchart_content: Content for flowchart.mmd Returns: Dictionary with write status and file paths """ result = { 'success': True, 'files_written': [], 'errors': [], } # Ensure directory exists dir_path = Path(dir_path) try: dir_path.mkdir(parents=True, exist_ok=True) except Exception as e: result['success'] = False result['errors'].append(f"Failed to create directory: {e}") return result # Write hivemind.md hivemind_path = dir_path / HIVEMIND_FILENAME try: async with aiofiles.open(hivemind_path, 'w', encoding='utf-8') as f: await f.write(hivemind_content) result['files_written'].append(str(hivemind_path)) except Exception as e: result['success'] = False result['errors'].append(f"Failed to write {HIVEMIND_FILENAME}: {e}") # Write flowchart.mmd flowchart_path = dir_path / FLOWCHART_FILENAME try: async with aiofiles.open(flowchart_path, 'w', encoding='utf-8') as f: await f.write(flowchart_content) result['files_written'].append(str(flowchart_path)) except Exception as e: result['success'] = False result['errors'].append(f"Failed to write {FLOWCHART_FILENAME}: {e}") return result async def generate_and_write( structure: Dict[str, Any], ai_context: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """ Generate documentation and write to directory. Convenience function that combines generation and writing. Args: structure: Parsed structure dictionary ai_context: Optional AI context Returns: Result dictionary with status and details """ dir_path = Path(structure.get('path', '.')) # Generate content hivemind_content = await generate_hivemind(structure, ai_context) flowchart_content = await generate_flowchart(structure) # Write files result = await write_files(dir_path, hivemind_content, flowchart_content) # Add summary result['directory'] = str(dir_path) result['hivemind_lines'] = len(hivemind_content.split('\n')) result['flowchart_lines'] = len(flowchart_content.split('\n')) return result

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/Jahanzaib-Kaleem/hive-mind-mcp'

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