fix cli
This commit is contained in:
314
python_backup/src/aigpt/docs/wiki_generator.py
Normal file
314
python_backup/src/aigpt/docs/wiki_generator.py
Normal file
@@ -0,0 +1,314 @@
|
||||
"""Wiki generation utilities for ai.wiki management."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
from .config import DocsConfig, get_ai_root
|
||||
from .utils import find_project_directories
|
||||
from .git_utils import pull_wiki_repository, push_wiki_repository
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
class WikiGenerator:
|
||||
"""Generates wiki content from project documentation."""
|
||||
|
||||
def __init__(self, config: DocsConfig, ai_root: Path):
|
||||
self.config = config
|
||||
self.ai_root = ai_root
|
||||
self.wiki_root = ai_root / "ai.wiki" if (ai_root / "ai.wiki").exists() else None
|
||||
|
||||
def extract_project_summary(self, project_md_path: Path) -> Dict[str, str]:
|
||||
"""Extract key information from claude/projects/${repo}.md file."""
|
||||
if not project_md_path.exists():
|
||||
return {"title": "No documentation", "summary": "Project documentation not found", "status": "Unknown"}
|
||||
|
||||
try:
|
||||
content = project_md_path.read_text(encoding="utf-8")
|
||||
|
||||
# Extract title (first # heading)
|
||||
title_match = re.search(r'^# (.+)$', content, re.MULTILINE)
|
||||
title = title_match.group(1) if title_match else "Unknown Project"
|
||||
|
||||
# Extract project overview/summary (look for specific patterns)
|
||||
summary = self._extract_summary_section(content)
|
||||
|
||||
# Extract status information
|
||||
status = self._extract_status_info(content)
|
||||
|
||||
# Extract key features/goals
|
||||
features = self._extract_features(content)
|
||||
|
||||
return {
|
||||
"title": title,
|
||||
"summary": summary,
|
||||
"status": status,
|
||||
"features": features,
|
||||
"last_updated": self._get_last_updated_info(content)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[yellow]Warning: Failed to parse {project_md_path}: {e}[/yellow]")
|
||||
return {"title": "Parse Error", "summary": str(e), "status": "Error"}
|
||||
|
||||
def _extract_summary_section(self, content: str) -> str:
|
||||
"""Extract summary or overview section."""
|
||||
# Look for common summary patterns
|
||||
patterns = [
|
||||
r'## 概要\s*\n(.*?)(?=\n##|\n#|\Z)',
|
||||
r'## Overview\s*\n(.*?)(?=\n##|\n#|\Z)',
|
||||
r'## プロジェクト概要\s*\n(.*?)(?=\n##|\n#|\Z)',
|
||||
r'\*\*目的\*\*: (.+?)(?=\n|$)',
|
||||
r'\*\*中核概念\*\*:\s*\n(.*?)(?=\n##|\n#|\Z)',
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, content, re.DOTALL | re.MULTILINE)
|
||||
if match:
|
||||
summary = match.group(1).strip()
|
||||
# Clean up and truncate
|
||||
summary = re.sub(r'\n+', ' ', summary)
|
||||
summary = re.sub(r'\s+', ' ', summary)
|
||||
return summary[:300] + "..." if len(summary) > 300 else summary
|
||||
|
||||
# Fallback: first paragraph after title
|
||||
lines = content.split('\n')
|
||||
summary_lines = []
|
||||
found_content = False
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
if found_content and summary_lines:
|
||||
break
|
||||
continue
|
||||
if line.startswith('#'):
|
||||
found_content = True
|
||||
continue
|
||||
if found_content and not line.startswith('*') and not line.startswith('-'):
|
||||
summary_lines.append(line)
|
||||
if len(' '.join(summary_lines)) > 200:
|
||||
break
|
||||
|
||||
return ' '.join(summary_lines)[:300] + "..." if summary_lines else "No summary available"
|
||||
|
||||
def _extract_status_info(self, content: str) -> str:
|
||||
"""Extract status information."""
|
||||
# Look for status patterns
|
||||
patterns = [
|
||||
r'\*\*状況\*\*: (.+?)(?=\n|$)',
|
||||
r'\*\*Status\*\*: (.+?)(?=\n|$)',
|
||||
r'\*\*現在の状況\*\*: (.+?)(?=\n|$)',
|
||||
r'- \*\*状況\*\*: (.+?)(?=\n|$)',
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, content)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
|
||||
return "No status information"
|
||||
|
||||
def _extract_features(self, content: str) -> List[str]:
|
||||
"""Extract key features or bullet points."""
|
||||
features = []
|
||||
|
||||
# Look for bullet point lists
|
||||
lines = content.split('\n')
|
||||
in_list = False
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if line.startswith('- ') or line.startswith('* '):
|
||||
feature = line[2:].strip()
|
||||
if len(feature) > 10 and not feature.startswith('**'): # Skip metadata
|
||||
features.append(feature)
|
||||
in_list = True
|
||||
if len(features) >= 5: # Limit to 5 features
|
||||
break
|
||||
elif in_list and not line:
|
||||
break
|
||||
|
||||
return features
|
||||
|
||||
def _get_last_updated_info(self, content: str) -> str:
|
||||
"""Extract last updated information."""
|
||||
patterns = [
|
||||
r'生成日時: (.+?)(?=\n|$)',
|
||||
r'最終更新: (.+?)(?=\n|$)',
|
||||
r'Last updated: (.+?)(?=\n|$)',
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, content)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
|
||||
return "Unknown"
|
||||
|
||||
def generate_project_wiki_page(self, project_name: str, project_info: Dict[str, str]) -> str:
|
||||
"""Generate wiki page for a single project."""
|
||||
config_info = self.config.get_project_info(project_name)
|
||||
|
||||
content = f"""# {project_name}
|
||||
|
||||
## 概要
|
||||
{project_info['summary']}
|
||||
|
||||
## プロジェクト情報
|
||||
- **タイプ**: {config_info.type if config_info else 'Unknown'}
|
||||
- **説明**: {config_info.text if config_info else 'No description'}
|
||||
- **ステータス**: {config_info.status if config_info else project_info.get('status', 'Unknown')}
|
||||
- **ブランチ**: {config_info.branch if config_info else 'main'}
|
||||
- **最終更新**: {project_info.get('last_updated', 'Unknown')}
|
||||
|
||||
## 主な機能・特徴
|
||||
"""
|
||||
|
||||
features = project_info.get('features', [])
|
||||
if features:
|
||||
for feature in features:
|
||||
content += f"- {feature}\n"
|
||||
else:
|
||||
content += "- 情報なし\n"
|
||||
|
||||
content += f"""
|
||||
## リンク
|
||||
- **Repository**: https://git.syui.ai/ai/{project_name}
|
||||
- **Project Documentation**: [claude/projects/{project_name}.md](https://git.syui.ai/ai/ai/src/branch/main/claude/projects/{project_name}.md)
|
||||
- **Generated Documentation**: [{project_name}/claude.md](https://git.syui.ai/ai/{project_name}/src/branch/main/claude.md)
|
||||
|
||||
---
|
||||
*このページは claude/projects/{project_name}.md から自動生成されました*
|
||||
"""
|
||||
|
||||
return content
|
||||
|
||||
def generate_wiki_home_page(self, project_summaries: Dict[str, Dict[str, str]]) -> str:
|
||||
"""Generate the main Home.md page with all project summaries."""
|
||||
content = """# AI Ecosystem Wiki
|
||||
|
||||
AI生態系プロジェクトの概要とドキュメント集約ページです。
|
||||
|
||||
## プロジェクト一覧
|
||||
|
||||
"""
|
||||
|
||||
# Group projects by type
|
||||
project_groups = {}
|
||||
for project_name, info in project_summaries.items():
|
||||
config_info = self.config.get_project_info(project_name)
|
||||
project_type = config_info.type if config_info else 'other'
|
||||
if isinstance(project_type, list):
|
||||
project_type = project_type[0] # Use first type
|
||||
|
||||
if project_type not in project_groups:
|
||||
project_groups[project_type] = []
|
||||
project_groups[project_type].append((project_name, info))
|
||||
|
||||
# Generate sections by type
|
||||
type_names = {
|
||||
'ai': '🧠 AI・知能システム',
|
||||
'gpt': '🤖 自律・対話システム',
|
||||
'os': '💻 システム・基盤',
|
||||
'card': '🎮 ゲーム・エンターテイメント',
|
||||
'shell': '⚡ ツール・ユーティリティ',
|
||||
'other': '📦 その他'
|
||||
}
|
||||
|
||||
for project_type, projects in project_groups.items():
|
||||
type_display = type_names.get(project_type, f'📁 {project_type}')
|
||||
content += f"### {type_display}\n\n"
|
||||
|
||||
for project_name, info in projects:
|
||||
content += f"#### [{project_name}](auto/{project_name}.md)\n"
|
||||
content += f"{info['summary'][:150]}{'...' if len(info['summary']) > 150 else ''}\n\n"
|
||||
|
||||
# Add quick status
|
||||
config_info = self.config.get_project_info(project_name)
|
||||
if config_info:
|
||||
content += f"**Status**: {config_info.status} \n"
|
||||
content += f"**Links**: [Repo](https://git.syui.ai/ai/{project_name}) | [Docs](https://git.syui.ai/ai/{project_name}/src/branch/main/claude.md)\n\n"
|
||||
|
||||
content += """
|
||||
---
|
||||
|
||||
## ディレクトリ構成
|
||||
|
||||
- `auto/` - 自動生成されたプロジェクト概要
|
||||
- `claude/` - Claude Code作業記録
|
||||
- `manual/` - 手動作成ドキュメント
|
||||
|
||||
---
|
||||
|
||||
*このページは ai.json と claude/projects/ から自動生成されました*
|
||||
*最終更新: {last_updated}*
|
||||
""".format(last_updated=self._get_current_timestamp())
|
||||
|
||||
return content
|
||||
|
||||
def _get_current_timestamp(self) -> str:
|
||||
"""Get current timestamp."""
|
||||
from datetime import datetime
|
||||
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def update_wiki_auto_directory(self, auto_pull: bool = True) -> Tuple[bool, List[str]]:
|
||||
"""Update the auto/ directory with project summaries."""
|
||||
if not self.wiki_root:
|
||||
return False, ["ai.wiki directory not found"]
|
||||
|
||||
# Pull latest changes from wiki repository first
|
||||
if auto_pull:
|
||||
success, message = pull_wiki_repository(self.wiki_root)
|
||||
if not success:
|
||||
console.print(f"[yellow]⚠️ Wiki pull failed: {message}[/yellow]")
|
||||
console.print("[dim]Continuing with local wiki update...[/dim]")
|
||||
else:
|
||||
console.print(f"[green]✅ Wiki repository updated[/green]")
|
||||
|
||||
auto_dir = self.wiki_root / "auto"
|
||||
auto_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Get claude/projects directory
|
||||
claude_projects_dir = self.ai_root / "claude" / "projects"
|
||||
if not claude_projects_dir.exists():
|
||||
return False, [f"claude/projects directory not found: {claude_projects_dir}"]
|
||||
|
||||
project_summaries = {}
|
||||
updated_files = []
|
||||
|
||||
console.print("[blue]📋 Extracting project summaries from claude/projects/...[/blue]")
|
||||
|
||||
# Process all projects from ai.json
|
||||
for project_name in self.config.list_projects():
|
||||
project_md_path = claude_projects_dir / f"{project_name}.md"
|
||||
|
||||
# Extract summary from claude/projects/${project}.md
|
||||
project_info = self.extract_project_summary(project_md_path)
|
||||
project_summaries[project_name] = project_info
|
||||
|
||||
# Generate individual project wiki page
|
||||
wiki_content = self.generate_project_wiki_page(project_name, project_info)
|
||||
wiki_file_path = auto_dir / f"{project_name}.md"
|
||||
|
||||
try:
|
||||
wiki_file_path.write_text(wiki_content, encoding="utf-8")
|
||||
updated_files.append(f"auto/{project_name}.md")
|
||||
console.print(f"[green]✓ Generated auto/{project_name}.md[/green]")
|
||||
except Exception as e:
|
||||
console.print(f"[red]✗ Failed to write auto/{project_name}.md: {e}[/red]")
|
||||
|
||||
# Generate Home.md
|
||||
try:
|
||||
home_content = self.generate_wiki_home_page(project_summaries)
|
||||
home_path = self.wiki_root / "Home.md"
|
||||
home_path.write_text(home_content, encoding="utf-8")
|
||||
updated_files.append("Home.md")
|
||||
console.print(f"[green]✓ Generated Home.md[/green]")
|
||||
except Exception as e:
|
||||
console.print(f"[red]✗ Failed to write Home.md: {e}[/red]")
|
||||
|
||||
return True, updated_files
|
Reference in New Issue
Block a user