Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:46:50 +08:00
commit a3a73d67d7
67 changed files with 19703 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
#!/usr/bin/env python3
"""
Changelog Generator
Generates semantic changelog from git history and Oracle sessions.
Usage:
python generate_changelog.py
python generate_changelog.py --since v1.0.0
python generate_changelog.py --format markdown|json
"""
import os
import sys
import json
import argparse
import subprocess
from pathlib import Path
from datetime import datetime
from collections import defaultdict
def get_git_commits(since=None):
"""Get git commits since a tag or date."""
cmd = ['git', 'log', '--pretty=format:%H|%s|%an|%ad', '--date=short']
if since:
cmd.append(f'{since}..HEAD')
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
commits = []
for line in result.stdout.strip().split('\n'):
if line:
hash, subject, author, date = line.split('|')
commits.append({
'hash': hash[:7],
'subject': subject,
'author': author,
'date': date
})
return commits
except subprocess.CalledProcessError:
return []
def categorize_commit(subject):
"""Categorize commit by type."""
subject_lower = subject.lower()
# Check for conventional commits
if subject.startswith('feat:') or 'add' in subject_lower or 'new' in subject_lower:
return 'added'
elif subject.startswith('fix:') or 'fix' in subject_lower:
return 'fixed'
elif subject.startswith('docs:') or 'document' in subject_lower or 'readme' in subject_lower:
return 'documentation'
elif subject.startswith('refactor:') or 'refactor' in subject_lower:
return 'changed'
elif 'deprecat' in subject_lower:
return 'deprecated'
elif 'remov' in subject_lower or 'delet' in subject_lower:
return 'removed'
elif 'perf' in subject_lower or 'optim' in subject_lower:
return 'performance'
elif 'security' in subject_lower:
return 'security'
else:
return 'changed'
def generate_markdown_changelog(changes_by_category, version='Unreleased'):
"""Generate markdown changelog."""
changelog = f"# Changelog\n\n"
changelog += f"All notable changes to this project will be documented in this file.\n\n"
changelog += f"## [{version}] - {datetime.now().strftime('%Y-%m-%d')}\n\n"
category_order = ['added', 'changed', 'deprecated', 'removed', 'fixed', 'security', 'performance', 'documentation']
category_titles = {
'added': 'Added',
'changed': 'Changed',
'deprecated': 'Deprecated',
'removed': 'Removed',
'fixed': 'Fixed',
'security': 'Security',
'performance': 'Performance',
'documentation': 'Documentation'
}
for category in category_order:
if category in changes_by_category and changes_by_category[category]:
changelog += f"### {category_titles[category]}\n\n"
for change in changes_by_category[category]:
changelog += f"- {change['subject']} ({change['hash']})\n"
changelog += "\n"
return changelog
def main():
parser = argparse.ArgumentParser(
description='Generate changelog from git history'
)
parser.add_argument(
'--since',
type=str,
help='Generate changelog since this tag or commit'
)
parser.add_argument(
'--version',
type=str,
default='Unreleased',
help='Version for this changelog'
)
parser.add_argument(
'--format',
choices=['markdown', 'json'],
default='markdown',
help='Output format'
)
parser.add_argument(
'--output',
type=str,
default='CHANGELOG.md',
help='Output file'
)
args = parser.parse_args()
print(f"[NOTE] Generating changelog...")
# Get commits
commits = get_git_commits(args.since)
if not commits:
print(" [WARNING] No commits found")
return
print(f" [INFO] Found {len(commits)} commits")
# Categorize commits
changes_by_category = defaultdict(list)
for commit in commits:
category = categorize_commit(commit['subject'])
changes_by_category[category].append(commit)
# Generate changelog
if args.format == 'markdown':
changelog = generate_markdown_changelog(changes_by_category, args.version)
with open(args.output, 'w') as f:
f.write(changelog)
print(f" [OK] Generated {args.output}")
# Print summary
print(f"\n Summary:")
for category, changes in changes_by_category.items():
print(f" {category.capitalize()}: {len(changes)}")
elif args.format == 'json':
output = {
'version': args.version,
'date': datetime.now().strftime('%Y-%m-%d'),
'changes': dict(changes_by_category)
}
output_file = Path(args.output).with_suffix('.json')
with open(output_file, 'w') as f:
json.dump(output, f, indent=2)
print(f" [OK] Generated {output_file}")
print("\n[OK] Changelog generated successfully!")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Documentation generator - creates documentation from code and knowledge."""
import sys
import json
from pathlib import Path
from datetime import datetime
def generate_readme(project_path):
"""Generate README from project structure."""
package_json = project_path / 'package.json'
name = project_path.name
description = "Project description"
if package_json.exists():
with open(package_json) as f:
data = json.load(f)
name = data.get('name', name)
description = data.get('description', description)
readme = f"""# {name}
{description}
## Installation
\`\`\`bash
npm install
\`\`\`
## Usage
\`\`\`typescript
// Add usage examples here
\`\`\`
## Documentation
- [API Documentation](./docs/api/)
- [Architecture Decision Records](./docs/adr/)
- [Contributing Guidelines](./CONTRIBUTING.md)
## License
See LICENSE file.
---
*Generated by Documentation Wizard on {datetime.now().strftime('%Y-%m-%d')}*
"""
output = project_path / 'README.md'
with open(output, 'w') as f:
f.write(readme)
print(f"[OK] Generated: {output}")
def main():
project = Path(sys.argv[1] if len(sys.argv) > 1 else '.').resolve()
print(f"[NOTE] Generating documentation for: {project}\n")
generate_readme(project)
print("\n[OK] Documentation generated!")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,210 @@
#!/usr/bin/env python3
"""
Documentation Synchronization Script
Syncs documentation from Oracle knowledge, Summoner MCDs, and Style Master guides.
Usage:
python sync_docs.py --source oracle
python sync_docs.py --source summoner
python sync_docs.py --source style-master
python sync_docs.py --source all
"""
import os
import sys
import json
import argparse
from pathlib import Path
from datetime import datetime
def find_oracle_root():
"""Find .oracle directory."""
current = Path.cwd()
while current != current.parent:
oracle_path = current / '.oracle'
if oracle_path.exists():
return oracle_path
current = current.parent
return None
def sync_from_oracle(oracle_path, output_dir):
"""Sync documentation from Oracle knowledge base."""
print(" Syncing from Oracle knowledge base...")
knowledge_dir = oracle_path / 'knowledge'
if not knowledge_dir.exists():
print(" [WARNING] No Oracle knowledge found")
return
patterns_file = knowledge_dir / 'patterns.json'
gotchas_file = knowledge_dir / 'gotchas.json'
sections = []
# Load patterns
if patterns_file.exists():
with open(patterns_file, 'r') as f:
patterns = json.load(f)
if patterns:
sections.append("## Architecture Patterns\n")
sections.append("*From Oracle knowledge base*\n\n")
for pattern in patterns[:10]: # Top 10
title = pattern.get('title', 'Untitled')
content = pattern.get('content', '')
sections.append(f"### {title}\n\n{content}\n\n")
# Load gotchas
if gotchas_file.exists():
with open(gotchas_file, 'r') as f:
gotchas = json.load(f)
if gotchas:
sections.append("## Known Issues & Gotchas\n")
sections.append("*From Oracle knowledge base*\n\n")
for gotcha in gotchas[:10]:
title = gotcha.get('title', 'Untitled')
content = gotcha.get('content', '')
priority = gotcha.get('priority', 'medium')
emoji = {'critical': '', 'high': '', 'medium': '', 'low': ''}.get(priority, '')
sections.append(f"### {emoji} {title}\n\n{content}\n\n")
if sections:
# Write to ARCHITECTURE.md
output_file = output_dir / 'ARCHITECTURE.md'
with open(output_file, 'w') as f:
f.write(f"# Architecture Documentation\n\n")
f.write(f"*Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M')}*\n\n")
f.write(''.join(sections))
print(f" [OK] Created {output_file}")
print(f" [NOTE] Synced {len(patterns if patterns_file.exists() else [])} patterns, {len(gotchas if gotchas_file.exists() else [])} gotchas")
else:
print(" [WARNING] No patterns or gotchas to sync")
def sync_from_style_master(project_path, output_dir):
"""Sync from Style Master style guide."""
print(" Syncing from Style Master...")
style_guide = project_path / 'STYLEGUIDE.md'
if style_guide.exists():
# Copy style guide to docs
output_file = output_dir / 'STYLEGUIDE.md'
with open(style_guide, 'r') as f:
content = f.read()
with open(output_file, 'w') as f:
f.write(content)
print(f" [OK] Synced {output_file}")
else:
print(" [WARNING] No STYLEGUIDE.md found")
def sync_from_summoner(project_path, output_dir):
"""Sync from Summoner Mission Control Documents."""
print(" Syncing from Summoner MCDs...")
# Look for mission-*.md files
mcds = list(project_path.glob('mission-*.md'))
if not mcds:
print(" [WARNING] No Summoner MCDs found")
return
adr_dir = output_dir / 'adr'
adr_dir.mkdir(exist_ok=True)
for mcd in mcds:
print(f" Processing {mcd.name}")
with open(mcd, 'r') as f:
content = f.read()
# Extract decisions from MCD
if '## Decisions' in content:
decisions_section = content.split('## Decisions')[1].split('\n\n')[0]
# Create ADR
adr_num = len(list(adr_dir.glob('*.md'))) + 1
adr_file = adr_dir / f'{adr_num:03d}-from-{mcd.stem}.md'
adr_content = f"""# ADR-{adr_num:03d}: Decisions from {mcd.stem}
Date: {datetime.now().strftime('%Y-%m-%d')}
Status: Accepted
Source: Summoner MCD {mcd.name}
## Context
From Mission Control Document: {mcd.name}
## Decisions
{decisions_section}
## Links
- Source MCD: {mcd.name}
"""
with open(adr_file, 'w') as f:
f.write(adr_content)
print(f" [OK] Created ADR: {adr_file.name}")
def main():
parser = argparse.ArgumentParser(
description='Sync documentation from various sources'
)
parser.add_argument(
'--source',
choices=['oracle', 'summoner', 'style-master', 'all'],
default='all',
help='Source to sync from'
)
parser.add_argument(
'--output',
type=str,
default='docs',
help='Output directory (default: docs/)'
)
args = parser.parse_args()
project_path = Path.cwd()
output_dir = project_path / args.output
output_dir.mkdir(exist_ok=True)
print(f" Syncing documentation to {output_dir}\n")
if args.source in ['oracle', 'all']:
oracle_path = find_oracle_root()
if oracle_path:
sync_from_oracle(oracle_path, output_dir)
else:
print("[WARNING] Oracle not initialized for this project")
print()
if args.source in ['style-master', 'all']:
sync_from_style_master(project_path, output_dir)
print()
if args.source in ['summoner', 'all']:
sync_from_summoner(project_path, output_dir)
print()
print("[OK] Documentation sync complete!")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Documentation validator - checks for stale docs and issues."""
import sys
from pathlib import Path
import re
def validate_docs(project_path):
"""Validate documentation."""
issues = []
# Check README exists
readme = project_path / 'README.md'
if not readme.exists():
issues.append("[ERROR] README.md missing")
else:
print("[OK] README.md found")
# Check for broken internal links
content = readme.read_text()
links = re.findall(r'\[([^\]]+)\]\(([^)]+)\)', content)
for text, link in links:
if not link.startswith('http'):
link_path = project_path / link.lstrip('./')
if not link_path.exists():
issues.append(f"[ERROR] Broken link in README: {link}")
# Check for CONTRIBUTING.md
if not (project_path / 'CONTRIBUTING.md').exists():
issues.append("[WARNING] CONTRIBUTING.md missing (recommended)")
return issues
def main():
project = Path(sys.argv[1] if len(sys.argv) > 1 else '.').resolve()
print(f"[SEARCH] Validating documentation for: {project}\n")
issues = validate_docs(project)
if issues:
print("\nIssues found:")
for issue in issues:
print(f" {issue}")
else:
print("\n[OK] All documentation checks passed!")
if __name__ == '__main__':
main()