Generate API documentation when code changes.Create .factory/hooks/generate-api-docs.sh:
#!/bin/bashset -einput=$(cat)tool_name=$(echo "$input" | jq -r '.tool_name')file_path=$(echo "$input" | jq -r '.tool_input.file_path // ""')# Only process code filesif ! echo "$file_path" | grep -qE '\.(ts|tsx|js|jsx|py|go)$'; then exit 0fi# Skip test filesif echo "$file_path" | grep -qE '\.(test|spec)\.(ts|tsx|js|jsx)$'; then exit 0ficwd=$(echo "$input" | jq -r '.cwd')cd "$cwd"echo "📚 Updating API documentation..."case "$file_path" in *.ts|*.tsx) # TypeScript - use typedoc if command -v typedoc &> /dev/null && [ -f "typedoc.json" ]; then echo "Generating TypeScript docs..." typedoc --out docs/api src/ 2>&1 || { echo "⚠️ Failed to generate docs" >&2 } echo "✓ API docs updated at docs/api" fi ;; *.py) # Python - use pdoc if command -v pdoc &> /dev/null; then module_name=$(echo "$file_path" | sed 's|^src/||; s|/|.|g; s|\.py$||') echo "Generating Python docs for $module_name..." pdoc --html --output-dir docs/api "$module_name" --force 2>&1 || { echo "⚠️ Failed to generate docs" >&2 } echo "✓ API docs updated" fi ;; *.go) # Go - use godoc if command -v godoc &> /dev/null; then echo "Generating Go docs..." # Go docs are typically served, not generated # But we can create markdown from godoc echo "✓ Go docs available via 'godoc -http=:6060'" fi ;;esacexit 0
Ensure code examples in docs match actual code:Create .factory/hooks/sync-doc-examples.py:
#!/usr/bin/env python3"""Sync code examples in markdown docs with actual source code."""import jsonimport sysimport reimport osdef extract_code_snippets(doc_file): """Extract code snippets from markdown file.""" with open(doc_file, 'r') as f: content = f.read() # Find code blocks with source file annotations # Format: ```typescript # // From: src/components/Button.tsx pattern = r'```(\w+)\n// From: (.*?)\n(.*?)\n```' snippets = re.findall(pattern, content, re.DOTALL) return snippetsdef verify_snippet_matches_source(language, source_file, snippet): """Check if snippet exists in source file.""" if not os.path.exists(source_file): return False, f"Source file not found: {source_file}" with open(source_file, 'r') as f: source_content = f.read() # Normalize whitespace for comparison normalized_snippet = ' '.join(snippet.split()) normalized_source = ' '.join(source_content.split()) if normalized_snippet in normalized_source: return True, "Snippet matches source" else: return False, "Snippet does not match source code"def main(): input_data = json.load(sys.stdin) file_path = input_data.get('tool_input', {}).get('file_path', '') # Check both code files and doc files if file_path.endswith(('.md', '.mdx')): # Doc file changed - verify all examples print(f"📖 Verifying code examples in {file_path}...") snippets = extract_code_snippets(file_path) issues = [] for lang, source, snippet in snippets: matches, message = verify_snippet_matches_source(lang, source, snippet) if not matches: issues.append(f"{source}: {message}") if issues: print("⚠️ Some code examples may be outdated:", file=sys.stderr) for issue in issues: print(f" - {issue}", file=sys.stderr) print("\nConsider updating the examples in the documentation.", file=sys.stderr) else: print("✓ All code examples are in sync") elif file_path.endswith(('.ts', '.tsx', '.js', '.jsx', '.py')): # Code file changed - check if it's referenced in docs print(f"Checking if {file_path} is referenced in documentation...") # Find docs that reference this file doc_files = [] for root, dirs, files in os.walk('docs'): for file in files: if file.endswith(('.md', '.mdx')): doc_path = os.path.join(root, file) with open(doc_path, 'r') as f: if file_path in f.read(): doc_files.append(doc_path) if doc_files: print(f"ℹ️ File is referenced in {len(doc_files)} documentation file(s):") for doc in doc_files: print(f" - {doc}") print("\nConsider updating these docs if the API changed.") sys.exit(0)if __name__ == '__main__': try: main() except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(0)
Automatically build changelog from git history:Create .factory/hooks/generate-changelog.sh:
#!/bin/bashset -einput=$(cat)hook_event=$(echo "$input" | jq -r '.hook_event_name')# Only run on Stop (after work is complete)if [ "$hook_event" != "Stop" ]; then exit 0ficwd=$(echo "$input" | jq -r '.cwd')cd "$cwd"# Check if there are new commits since last changelog updateif [ ! -f "CHANGELOG.md" ]; then exit 0fi# Get last version in changeloglast_version=$(grep -m1 "## \[" CHANGELOG.md | sed -E 's/.*\[([0-9.]+)\].*/\1/')if [ -z "$last_version" ]; then exit 0fi# Get commits since last version tagif git rev-parse "v$last_version" &>/dev/null; then new_commits=$(git log "v$last_version..HEAD" --oneline) if [ -n "$new_commits" ]; then echo "📝 New commits since v$last_version" echo "" echo "Consider updating CHANGELOG.md with:" echo "" # Group commits by type echo "### Features" git log "v$last_version..HEAD" --oneline | grep "^[a-f0-9]* feat" | sed 's/^[a-f0-9]* feat[:(]/- /' || true echo "" echo "### Bug Fixes" git log "v$last_version..HEAD" --oneline | grep "^[a-f0-9]* fix" | sed 's/^[a-f0-9]* fix[:(]/- /' || true echo "" fifiexit 0