Files
ClaudeProjectManager-main/readme_generator.py
Claude Project Manager ec92da8a64 Initial commit
2025-07-07 22:11:38 +02:00

295 Zeilen
10 KiB
Python

"""
README Generator Module
Automatically generates and updates README files for projects
"""
import os
import json
from datetime import datetime
from typing import Dict, List, Set
import mimetypes
class ReadmeGenerator:
def __init__(self):
self.language_extensions = {
'.py': 'Python',
'.js': 'JavaScript',
'.ts': 'TypeScript',
'.jsx': 'React',
'.tsx': 'React TypeScript',
'.java': 'Java',
'.cpp': 'C++',
'.c': 'C',
'.cs': 'C#',
'.php': 'PHP',
'.rb': 'Ruby',
'.go': 'Go',
'.rs': 'Rust',
'.swift': 'Swift',
'.kt': 'Kotlin',
'.dart': 'Dart',
'.r': 'R',
'.scala': 'Scala',
'.lua': 'Lua',
'.pl': 'Perl',
'.sh': 'Shell',
'.bat': 'Batch',
'.ps1': 'PowerShell',
}
self.framework_indicators = {
'React': ['package.json', 'react'],
'Angular': ['angular.json', '@angular'],
'Vue': ['vue.config.js', 'vue'],
'Django': ['manage.py', 'django'],
'Flask': ['app.py', 'flask'],
'Express': ['express', 'node_modules'],
'Spring': ['pom.xml', 'spring'],
'.NET': ['.csproj', 'dotnet'],
'Laravel': ['artisan', 'laravel'],
'Rails': ['Gemfile', 'rails'],
}
def analyze_project(self, project_path: str) -> Dict:
"""Analyze project structure and content"""
analysis = {
'path': project_path,
'name': os.path.basename(project_path),
'languages': set(),
'frameworks': set(),
'file_count': 0,
'total_size': 0,
'structure': {},
'key_files': [],
'last_modified': None,
}
# Walk through directory
for root, dirs, files in os.walk(project_path):
# Skip hidden and system directories
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__', 'venv', 'env']]
rel_path = os.path.relpath(root, project_path)
level = rel_path.count(os.sep)
# Limit depth for structure
if level < 3:
current_dict = analysis['structure']
if rel_path != '.':
for part in rel_path.split(os.sep):
if part not in current_dict:
current_dict[part] = {}
current_dict = current_dict[part]
# Add files to structure
for file in files[:10]: # Limit files shown
if not file.startswith('.'):
current_dict[file] = 'file'
# Analyze files
for file in files:
if file.startswith('.'):
continue
file_path = os.path.join(root, file)
analysis['file_count'] += 1
try:
# Get file size
size = os.path.getsize(file_path)
analysis['total_size'] += size
# Get modification time
mtime = os.path.getmtime(file_path)
if not analysis['last_modified'] or mtime > analysis['last_modified']:
analysis['last_modified'] = mtime
# Detect language
ext = os.path.splitext(file)[1].lower()
if ext in self.language_extensions:
analysis['languages'].add(self.language_extensions[ext])
# Key files
if file in ['README.md', 'package.json', 'requirements.txt', 'Dockerfile',
'.gitignore', 'Makefile', 'setup.py', 'pom.xml']:
analysis['key_files'].append(file)
except:
pass
# Detect frameworks
for framework, indicators in self.framework_indicators.items():
for indicator in indicators:
if any(indicator in str(f) for f in analysis['key_files']):
analysis['frameworks'].add(framework)
# Convert sets to lists for JSON serialization
analysis['languages'] = list(analysis['languages'])
analysis['frameworks'] = list(analysis['frameworks'])
return analysis
def generate_readme(self, project_path: str, project_name: str = None) -> str:
"""Generate README content for a project"""
analysis = self.analyze_project(project_path)
if project_name:
analysis['name'] = project_name
# Format file size
size_mb = analysis['total_size'] / (1024 * 1024)
size_str = f"{size_mb:.1f} MB" if size_mb > 1 else f"{analysis['total_size'] / 1024:.1f} KB"
# Format last modified
if analysis['last_modified']:
last_mod = datetime.fromtimestamp(analysis['last_modified']).strftime('%Y-%m-%d %H:%M')
else:
last_mod = "Unknown"
# Build README content
content = f"""# {analysis['name']}
*This README was automatically generated by Claude Project Manager*
## Project Overview
- **Path**: `{analysis['path']}`
- **Files**: {analysis['file_count']} files
- **Size**: {size_str}
- **Last Modified**: {last_mod}
## Technology Stack
"""
if analysis['languages']:
content += "### Languages\n"
for lang in sorted(analysis['languages']):
content += f"- {lang}\n"
content += "\n"
if analysis['frameworks']:
content += "### Frameworks & Libraries\n"
for framework in sorted(analysis['frameworks']):
content += f"- {framework}\n"
content += "\n"
# Add structure
content += "## Project Structure\n\n```\n"
content += self._format_structure(analysis['structure'])
content += "```\n\n"
# Add key files section
if analysis['key_files']:
content += "## Key Files\n\n"
for file in analysis['key_files']:
content += f"- `{file}`\n"
content += "\n"
# Add Claude usage section
content += """## Claude Integration
This project is managed with Claude Project Manager. To work with this project:
1. Open Claude Project Manager
2. Click on this project's tile
3. Claude will open in the project directory
## Notes
*Add your project-specific notes here*
---
## Development Log
"""
# Add timestamp
content += f"- README generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
return content
def _format_structure(self, structure: Dict, prefix: str = "", is_last: bool = True) -> str:
"""Format directory structure for display"""
output = ""
items = list(structure.items())
for i, (name, value) in enumerate(items):
is_last_item = i == len(items) - 1
# Add tree characters
if prefix:
output += prefix
output += "└── " if is_last_item else "├── "
output += name
if isinstance(value, dict) and value: # It's a directory with contents
output += "/\n"
extension = " " if is_last_item else ""
output += self._format_structure(value, prefix + extension, is_last_item)
else:
output += "\n"
return output
def update_readme(self, readme_path: str, new_content: str):
"""Update README file, preserving user notes"""
if os.path.exists(readme_path):
try:
with open(readme_path, 'r', encoding='utf-8') as f:
old_content = f.read()
# Try to preserve user notes section
notes_marker = "## Notes"
if notes_marker in old_content:
notes_start = old_content.find(notes_marker)
notes_end = old_content.find("\n---", notes_start)
if notes_end > notes_start:
user_notes = old_content[notes_start:notes_end]
# Replace empty notes section with user's notes
new_content = new_content.replace(
"## Notes\n\n*Add your project-specific notes here*\n",
user_notes
)
# Append to development log
if "## Development Log" in old_content:
log_start = old_content.find("## Development Log")
old_log = old_content[log_start:]
# Remove the new log header and just append entries
new_log_entry = f"- README updated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
new_content = new_content[:new_content.find("## Development Log")] + old_log + new_log_entry
except:
pass
# Write the README
with open(readme_path, 'w', encoding='utf-8') as f:
f.write(new_content)
def generate_and_save_readme(self, project_path: str, project_name: str = None) -> str:
"""Generate and save README for a project"""
readme_path = os.path.join(project_path, "CLAUDE_PROJECT_README.md")
content = self.generate_readme(project_path, project_name)
self.update_readme(readme_path, content)
return readme_path
# Test the module
if __name__ == "__main__":
generator = ReadmeGenerator()
# Test with current directory
test_path = os.path.dirname(os.path.abspath(__file__))
analysis = generator.analyze_project(test_path)
print("Project Analysis:")
print(f"Languages: {analysis['languages']}")
print(f"Files: {analysis['file_count']}")
print(f"Key files: {analysis['key_files']}")
# Generate README
readme = generator.generate_readme(test_path, "Claude Project Manager")
print("\nGenerated README Preview:")
print(readme[:500] + "...")