Skip to content

Tree Generator (codebrief.tools.tree_generator)

Documentation for the tree generator module.

tree_generator

Directory Tree Generation Utilities.

This module provides functions to generate a textual or Rich-enhanced representation of a directory's structure. It supports excluding specified files and directories and can output the tree to the console or a file.

Core functionalities: - Recursive traversal of directories. - Filtering of items based on .llmignore files, CLI arguments, core system exclusions, and a default set of common development artifacts. - Generation of plain text tree for file output. - Generation of a Rich Tree object for styled console output.

Functions

generate_and_output_tree(root_dir, output_file_path=None, ignore_list=None, config_global_excludes=None)

Generate and output tree structure.

Parameters:

NameTypeDescriptionDefault
root_dirPath

Root directory to generate tree for.

required
output_file_pathOptional[Path]

Optional path to save the tree. If None, returns string for console.

None
ignore_listOptional[list[str]]

List of patterns to ignore.

None
config_global_excludesOptional[list[str]]

Global exclusion patterns from config.

None

Returns:

TypeDescription
Optional[str]

String representation of the tree if no output file specified, None otherwise.

Source code in src/codebrief/tools/tree_generator.py
def generate_and_output_tree(
    root_dir: Path,
    output_file_path: Optional[Path] = None,
    ignore_list: Optional[list[str]] = None,
    config_global_excludes: Optional[list[str]] = None,  # <--- NEW PARAMETER
) -> Optional[str]:
    """Generate and output tree structure.

    Args:
        root_dir: Root directory to generate tree for.
        output_file_path: Optional path to save the tree. If None, returns string for console.
        ignore_list: List of patterns to ignore.
        config_global_excludes: Global exclusion patterns from config.

    Returns:
        String representation of the tree if no output file specified, None otherwise.
    """
    if not root_dir.is_dir():
        console.print(
            f"[bold red]Error: Root directory '{root_dir}' not found or is not a directory.[/bold red]"
        )
        raise typer.Exit(code=1)

    llmignore_spec = ignore_handler.load_ignore_patterns(root_dir)
    # Simplified console message logic for brevity, can be restored from your version
    if (
        llmignore_spec
        and output_file_path
        and (root_dir / ignore_handler.LLMIGNORE_FILENAME).exists()
    ):
        console.print(
            f"[dim]Using .llmignore patterns from '{root_dir / ignore_handler.LLMIGNORE_FILENAME}'[/dim]"
        )
    elif not llmignore_spec and output_file_path:
        console.print(
            "[dim]No .llmignore file, or it's empty. Using fallback exclusions if applicable.[/dim]"
        )

    effective_cli_ignores = list(ignore_list) if ignore_list else []
    if output_file_path:
        abs_output_file = output_file_path.resolve()
        if (
            abs_output_file.is_relative_to(root_dir.resolve())
            and abs_output_file.name not in effective_cli_ignores
        ):
            effective_cli_ignores.append(abs_output_file.name)
            # Re-add this console print if desired
            # console.print(f"[dim]Output file '{abs_output_file.name}' will be dynamically ignored for this run.[/dim]")

    current_tool_specific_exclusions = DEFAULT_EXCLUDED_ITEMS_TOOL_SPECIFIC.copy()

    # Create Rich tree for both file output and console display
    rich_tree_root_label = f"📁 [link file://{root_dir.resolve()}]{root_dir.name}"
    rich_tree_root = RichTree(
        rich_tree_root_label,
        guide_style="bold bright_blue",
    )
    _add_nodes_to_rich_tree_recursive(
        rich_tree_node=rich_tree_root,
        current_path_obj=root_dir,
        root_dir_for_ignores=root_dir,
        llmignore_spec=llmignore_spec,
        cli_ignores=effective_cli_ignores,
        config_global_excludes=config_global_excludes,
        tool_specific_fallback_exclusions=current_tool_specific_exclusions,
    )

    if output_file_path:
        # Render Rich tree to string for file output
        from io import StringIO

        from rich.console import Console as RichConsole

        string_buffer = StringIO()
        file_console = RichConsole(file=string_buffer, width=120, legacy_windows=False)
        file_console.print(rich_tree_root)
        rich_output = string_buffer.getvalue()

        try:
            output_file_path.parent.mkdir(parents=True, exist_ok=True)
            with output_file_path.open(mode="w", encoding="utf-8") as f:
                f.write(rich_output)
            console.print(
                f"Directory tree saved to [cyan]{output_file_path.resolve()}[/cyan]"
            )
        except OSError as e:
            console.print(
                f"[bold red]Error writing to output file '{output_file_path}': {e}[/bold red]"
            )
            raise typer.Exit(code=1) from e
        return None
    else:
        # Return the string representation for console output or clipboard
        from io import StringIO

        from rich.console import Console as RichConsole

        string_buffer = StringIO()
        console_for_capture = RichConsole(
            file=string_buffer, width=120, legacy_windows=False
        )
        console_for_capture.print(rich_tree_root)
        return string_buffer.getvalue()

Modules