Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-07 22:11:38 +02:00
Commit ec92da8a64
73 geänderte Dateien mit 16367 neuen und 0 gelöschten Zeilen

295
readme_generator.py Normale Datei
Datei anzeigen

@ -0,0 +1,295 @@
"""
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] + "...")