"""Module documentation generation for wiki."""
import time
from pathlib import Path
from typing import TYPE_CHECKING
from local_deepwiki.core.vectorstore import VectorStore
from local_deepwiki.models import IndexStatus, WikiPage
from local_deepwiki.providers.base import LLMProvider
if TYPE_CHECKING:
from local_deepwiki.generators.wiki_status import WikiStatusManager
async def generate_module_docs(
index_status: IndexStatus,
vector_store: VectorStore,
llm: LLMProvider,
system_prompt: str,
status_manager: "WikiStatusManager",
full_rebuild: bool = False,
) -> tuple[list[WikiPage], int, int]:
"""Generate documentation for each module/directory.
Args:
index_status: Index status with file information.
vector_store: Vector store with indexed code.
llm: LLM provider for generation.
system_prompt: System prompt for LLM.
status_manager: Wiki status manager for incremental updates.
full_rebuild: If True, regenerate all pages.
Returns:
Tuple of (pages list, generated count, skipped count).
"""
pages = []
pages_generated = 0
pages_skipped = 0
# Group files by top-level directory
directories: dict[str, list[str]] = {}
for file_info in index_status.files:
parts = Path(file_info.path).parts
if len(parts) > 1:
dir_name = parts[0]
else:
dir_name = "root"
directories.setdefault(dir_name, []).append(file_info.path)
# Generate a page for each significant directory
for dir_name, files in directories.items():
if len(files) < 2:
continue
page_path = f"modules/{dir_name}.md"
# Check if page needs regeneration (module pages depend on all files in that module)
if not full_rebuild and not status_manager.needs_regeneration(page_path, files):
existing_page = await status_manager.load_existing_page(page_path)
if existing_page is not None:
pages.append(existing_page)
status_manager.record_page_status(existing_page, files)
pages_skipped += 1
continue
# Get chunks for this directory
search_results = await vector_store.search(
f"module {dir_name}",
limit=15,
)
# Filter to chunks from this directory
relevant_chunks = [r for r in search_results if r.chunk.file_path.startswith(dir_name)]
if not relevant_chunks:
continue
context = "\n\n".join(
[
f"File: {r.chunk.file_path}\nType: {r.chunk.chunk_type.value}\nName: {r.chunk.name}\n{r.chunk.content[:400]}"
for r in relevant_chunks[:10]
]
)
prompt = f"""Generate documentation for the '{dir_name}' module based ONLY on the code provided.
Files in module: {', '.join(files[:10])}{'...' if len(files) > 10 else ''}
Code context:
{context}
Generate documentation that includes:
1. **Module Purpose** - Explain what this module does based on the code shown
2. **Key Classes and Functions** - Describe each class/function visible in the code above. Write class names as plain text for cross-linking.
3. **How Components Interact** - Explain how the components shown work together
4. **Usage Examples** - Show how to use the components (use code blocks)
5. **Dependencies** - What other modules this depends on (based on imports shown)
CRITICAL CONSTRAINTS:
- ONLY describe classes and functions that appear in the code context above
- Do NOT invent additional components not shown
- Do NOT fabricate usage patterns or APIs not visible in the code
- Write class names as plain text (e.g., "The CodeParser class") for cross-linking
Format as markdown."""
content = await llm.generate(prompt, system_prompt=system_prompt)
page = WikiPage(
path=page_path,
title=f"Module: {dir_name}",
content=content,
generated_at=time.time(),
)
pages.append(page)
status_manager.record_page_status(page, files)
pages_generated += 1
# Create modules index (always regenerate since it depends on module pages)
if pages:
modules_index = WikiPage(
path="modules/index.md",
title="Modules",
content=_generate_modules_index(pages),
generated_at=time.time(),
)
pages.insert(0, modules_index)
# Index depends on all files in all modules
all_module_files = [f for files in directories.values() for f in files]
status_manager.record_page_status(modules_index, all_module_files)
return pages, pages_generated, pages_skipped
def _generate_modules_index(module_pages: list[WikiPage]) -> str:
"""Generate index page for modules.
Args:
module_pages: List of module wiki pages.
Returns:
Markdown content for modules index.
"""
lines = ["# Modules\n", "This section contains documentation for each module.\n"]
for page in module_pages:
if page.path != "modules/index.md":
name = Path(page.path).stem
lines.append(f"- [{page.title}]({name}.md)")
return "\n".join(lines)