tree-plus 1.0.79
A `tree` util enhanced with tokens, lines, and components.
Requires Python: >=3.8
Keywords: tree, util, cli

# Tree Plus

#### **Ever hit context limits prompting LLMs with code?**
**If so, you could benefit from `tree_plus`, a `tree` util enhanced with tokens, lines, and components.**

### Use `tree_plus` to ...:
- **Summarize huge codebases easily**
- **Autogenerate API documentation**
- **Create ideal LLM prompts**

[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-brightgreen)](https://choosealicense.com/licenses/)
[![Docs](https://img.shields.io/badge/docs-README-blue)](https://github.com/bionicles/tree_plus/blob/main/README.md)
[![Linux & MacOS](https://github.com/bionicles/tree_plus/actions/workflows/unix.yml/badge.svg)](https://github.com/bionicles/tree_plus/actions/workflows/unix.yml)
[![Microsoft](https://github.com/bionicles/tree_plus/actions/workflows/microsoft.yml/badge.svg)](https://github.com/bionicles/tree_plus/actions/workflows/microsoft.yml)

**Supported Python Versions:**

![Python 3.8](https://img.shields.io/badge/Python-3.8-blue)
![Python 3.9](https://img.shields.io/badge/Python-3.9-blue)
![Python 3.10](https://img.shields.io/badge/Python-3.10-blue)
![Python 3.11](https://img.shields.io/badge/Python-3.11-blue)
![Python 3.12](https://img.shields.io/badge/Python-3.12-blue)
![Python 3.12](https://img.shields.io/badge/Python-3.13-blue)

**Supported Operating Systems:**

![Ubuntu](https://img.shields.io/badge/Supports-Ubuntu-orange)
![MacOS](https://img.shields.io/badge/Supports-MacOS-orange)
![Windows](https://img.shields.io/badge/Supports-Windows-orange)

**Support Free, Open-Source Software:**

[![Support Tree Plus](https://img.shields.io/badge/Support%20Tree%20Plus-8A2BE2)](https://www.buymeacoffee.com/bionicles)


`pip install -U tree_plus`

## Usage
Here's how `tree_plus --help` looks (`-h` and `-H` both also work) 
<!-- t5-start -->
```sh
python -m tree_plus_cli -h
Usage: tree_plus_cli.py [OPTIONS] [PATHS]...

  A `tree` util enhanced with tokens, lines, and components.

  Wrap patterns in quotes: -i "*.py" / -g "*.rs"

  Example Invocations:

          Show tree_plus_src and tests simultaneously
              > tree_plus tree_plus_src tests

          Show files matching "*.*s" within tests/more_languages
              > tree_plus -g "*.*s" tests/more_languages

          Ignore Java files
              > tree_plus -i "*.java" tests

          Override DEFAULT_IGNORE: Only ignore .ini files.
              > tree_plus -o -i "*.ini" tests/dot_dot

          Syntax Highlight python files in src and tests
              > tree_plus -s tree_plus_src/*.py tests/*.py

          Concise Mode (No Parsing)
              > tree_plus -c

          URL + Tag Categories for a website
              > tree_plus example.com

          URL + Tag Categories for multiple websites with a link tree
              > tree_plus example.com example.org -l

          Hacker News Mode (3 articles, max depth 3)
              > tree_plus --yc

          Hacker News Mode (6 articles, max depth 6, warning, slow!)
              > tree_plus --yc -n 6 -m 6

          Use the Tiktoken gpt4o Model Tokenizer to tokenize Rust files
              > tree_plus -t -g '*.rs'

Options:
  -i, -I, --ignore TEXT        Patterns to ignore, in quotes: -i "*.java"
  -o, -O, --override           Override DEFAULT_IGNORE (includes ignored
                               content): -o -i "*.java"
  -g, -G, --glob TEXT          Patterns to find, in quotes: -g "*.rs"
  -v, -V, --version            Print the version and exit.
  -d, -D, --debug              Enables $DEBUG_TREE_PLUS.
  -s, -S, --syntax             DISABLE Syntax Highlighting.
  -c, -C, --concise            Omit module components. (False)
  --yc, --hn                   Include ycombinator (False)
  -n, -N, --number INTEGER     number of results (--yc mode only, default 3)
  -m, -M, --max-depth INTEGER  max number of steps (depth / level) from root
                               (--yc mode only, default 3)
  -l, -L, --links              include links (web mode only, default False)
  -t, --tiktoken               a shorthand for tiktoken with the 'gpt-4o'
                               tokenizer
  -T, --tokenizer-name TEXT    name of the tokenizer to use, for now only
                               'gpt-4o' works
  --timeout FLOAT              regex timeout in seconds (optional, default
                               0.7)
  -H, -h, --help               Show this message and exit.

  v(1.0.79) --- https://github.com/bionicles/tree_plus/blob/main/README.md

```
<!-- t5-end -->


## Example Output:
- [ ] Demonstrate Parsed Checkboxes
<!-- t1-start -->
```sh
python -m tree_plus_cli -s -i tests
📁 tree_plus (7 folders, 28 files) 
├── 📄 .env.test (4 tokens, 0 lines)
│   └── DEBUG_TREE_PLUS
├── 📁 .github (2 folders, 3 files) 
│   ├── 📄 dependabot.yml (128 tokens, 11 lines)
│   │   └── Unsupported YAML Category
│   └── 📁 workflows (1 folder, 2 files) 
│       ├── 📄 microsoft.yml (284 tokens, 40 lines)
│       │   ├── Microsoft
│       │   ├──   job: build
│       │   ├──     - Set up Python ${{ matrix.python-version }}
│       │   ├──     - Install tree_plus
│       │   ├──     - Create .env file
│       │   ├──     - Set PYTHONUTF8 for Windows
│       │   ├──     - Run generic tests
│       │   └──     - Run specific test
│       └── 📄 unix.yml (713 tokens, 92 lines)
│           ├── Linux & MacOS
│           ├──   job: test
│           ├──     - Set up Python ${{ matrix.python-version }}
│           ├──     - Install tree_plus
│           ├──     - Create .env file
│           ├──     - Run generic tests
│           ├──     - Run specific test
│           ├──   job: deploy
│           ├──     - Set up Python
│           ├──     - Install dependencies
│           ├──     - Increment Version
│           ├──     - Build
│           ├──     - Install
│           ├──     - Test
│           ├──     - Update README
│           ├──     - Build Again
│           ├──     - Check dists with twine
│           ├──     - Commit Updates
│           └──     - Publish to PyPI
├── 📄 .gitignore (219 tokens, 57 lines)
├── 📄 .mcp_server.pid (2 tokens, 1 line)
├── 📁 coverage (1 folder, 1 file) 
│   └── 📄 lcov.info (17,359 tokens, 2,180 lines)
├── 📄 LICENSE (2,744 tokens, 81 lines)
├── 📄 Makefile (801 tokens, 121 lines)
│   ├── SHELL := /bin/bash
│   ├── cli
│   ├── library-demo
│   ├── rewrite-demo
│   ├── coverage
│   ├── debug
│   ├── .PHONY: debug-command
│   ├── debug-command: test
│   ├── html-demo
│   ├── absurdly-huge-jsonl
│   ├── test: test-sequential test-tp-dotdot test-e2e test-cli test-programs test-deploy
│   ├── test-parallel
│   ├── test-sequential
│   ├── test-more-languages
│   ├── test-group
│   ├── test-units
│   ├── test-tp-dotdot
│   ├── test-e2e
│   ├── test-cli: cli
│   ├── test-programs
│   ├── test-deploy
│   ├── vulture: install_vulture
│   ├── install-vulture
│   ├── build: install-build-tool clean-dist
│   ├── install-wheel
│   ├── install-build-tool
│   ├── test-publish: install-twine
│   ├── install-twine
│   ├── publish: install-twine
│   ├── clean-dist
│   ├── t1
│   ├── t2
│   ├── t3
│   ├── t4
│   ├── t5
│   └── t6
├── 📄 nodemon.json (112 tokens, 24 lines)
├── 📄 pyproject.toml (366 tokens, 51 lines)
│   ├── name: tree_plus                                                                                                         
│   ├── version: N/A                                                                                                            
│   ├── description: A `tree` util enhanced with tokens, lines, and components.                                                 
│   ├── License :: OSI Approved :: Apache Software License                                                                      
│   ├── License :: OSI Approved :: MIT License                                                                                  
│   ├── dependencies:                                                                                                           
│   ├──     tiktoken                                                                                                            
│   ├──     PyYAML                                                                                                              
│   ├──     click                                                                                                               
│   ├──     rich                                                                                                                
│   ├──     tomli                                                                                                               
│   ├──     natsort>=7.1                                                                                                        
│   ├──     fake_useragent                                                                                                      
│   ├──     bs4                                                                                                                 
│   ├──     func_timeout                                                                                                        
│   └──     regex                                                                                                               
├── 📄 pytest.ini (20 tokens, 4 lines)
├── 📄 README.md (38,152 tokens, 3,705 lines)
│   ├── # Tree Plus                                                                                                             
│   ├── #### **Ever hit context limits prompting LLMs with code?**                                                              
│   ├── ### Use `tree_plus` to ...:                                                                                             
│   ├── ## Usage                                                                                                                
│   ├── ## Example Output:                                                                                                      
│   ├── - [ ] Demonstrate Parsed Checkboxes                                                                                     
│   ├── ## Start Quick!                                                                                                         
│   ├── ### Prerequisites                                                                                                       
│   ├── ### Install Tree Plus                                                                                                   
│   ├── #### PyPI                                                                                                               
│   ├── #### Local Hackable Install                                                                                             
│   ├── ### Alias Usage                                                                                                         
│   ├── ## Library Usage:                                                                                                       
│   ├── ## Moar Languages                                                                                                       
│   ├── ### Currently Tested Languages:                                                                                         
│   ├── ### Without the -c "Concise" Flag:                                                                                      
│   ├── ## Got Globs?                                                                                                           
│   ├── ## Languages Todo:                                                                                                      
│   ├── ## Oppose Unfair Business Practices                                                                                     
│   └── ## License                                                                                                              
├── 📄 tree_plus_cli.py (2,283 tokens, 331 lines)
│   ├── def move_cwd_to_end_of_sys_path()                                                                                       
│   └── def main(                                                                                                               
│           glob: Optional[Tuple[str, ...]],                                                                                    
│           paths: Optional[Union[str, Tuple[str, ...]]],                                                                       
│           ignore: Tuple[str, ...],                                                                                            
│           override: bool,                                                                                                     
│           debug: bool,                                                                                                        
│           version: bool,                                                                                                      
│           syntax: bool,                                                                                                       
│           concise: bool,                                                                                                      
│                                                                                                                               
│           yc: bool,                                                                                                           
│           number: int,                                                                                                        
│           max_depth: int,                                                                                                     
│           links: bool,                                                                                                        
│           tiktoken: bool,                                                                                                     
│           tokenizer_name: Optional[str],                                                                                      
│           timeout: Optional[float],                                                                                           
│       )                                                                                                                       
├── 📁 tree_plus_programs (1 folder, 4 files) 
│   ├── 📄 hello_tree_plus.py (545 tokens, 80 lines)
│   │   ├── class ItHappened                                                                                                    
│   │   ├── def main()                                                                                                          
│   │   └── def trees(                                                                                                          
│   │           or_: bool,                                                                                                      
│   │           it: int,                                                                                                        
│   │           didnt: str,                                                                                                     
│   │           happen: tuple,                                                                                                  
│   │       ) -> ItHappened                                                                                                     
│   ├── 📄 rewrite.py (4,017 tokens, 471 lines)
│   │   ├── TODO: research various kwargs for huggingface / torch performance                                                   
│   │   ├── class ModelName(Enum)                                                                                               
│   │   ├──     QWEN_2 = "Qwen/Qwen2-7B-Instruct"                                                                               
│   │   ├──     MISTRAL_3 = "mistralai/Mistral-7B-Instruct-v0.3"                                                                
│   │   ├── class ModelContext(Enum)                                                                                            
│   │   ├──     QWEN_2 = 131                                                                                                    
│   │   ├──     MISTRAL_3 = 32                                                                                                  
│   │   ├──             root: None,                                                                                             
│   │   ├── def rewrite_module(                                                                                                 
│   │   │       max_length: int,                                                                                                
│   │   │       fill_context: bool,                                                                                             
│   │   │       input_path: Path,                                                                                               
│   │   │       output_path: Optional[str],                                                                                     
│   │   │       suffix: Optional[str] = ".rs",                                                                                  
│   │   │       include_complete_file: bool = True,                                                                             
│   │   │   )                                                                                                                   
│   │   └── def main(                                                                                                           
│   │           fill_context: bool,                                                                                             
│   │           length: int,                                                                                                    
│   │           input_path: Path,                                                                                               
│   │           output_path: Optional[click.Path],                                                                              
│   │       )                                                                                                                   
│   ├── 📄 stub_tests.py (1,348 tokens, 180 lines)
│   │   ├── TODO: fix this path",                                                                                               
│   │   ├── TODO: fill in these stubs:",                                                                                        
│   │   ├── @lru_cache                                                                                                          
│   │   │   def remove_decorators(component: str) -> str                                                                        
│   │   ├── def make_import_path(path: Path) -> str                                                                             
│   │   ├── def stub_tests(                                                                                                     
│   │   │       input_path: str,                                                                                                
│   │   │       output_path: Optional[str],                                                                                     
│   │   │       rewrite_ok: bool,                                                                                               
│   │   │   ) -> Tuple[List[str], str, bool]                                                                                    
│   │   ├── def main(                                                                                                           
│   │   │       input_path: str,                                                                                                
│   │   │       output_path: Optional[str],                                                                                     
│   │   │       rewrite_ok: bool,                                                                                               
│   │   │   )                                                                                                                   
│   │   ├── class Vehicle                                                                                                       
│   │   └── class Car(Vehicle)                                                                                                  
│   └── 📄 test_stub_tests.py (79 tokens, 20 lines)
│       ├── TODO: fix this path                                                                                                 
│       ├── TODO: fill in these stubs:                                                                                          
│       ├── def test_remove_decorators()                                                                                        
│       ├── def test_make_import_path()                                                                                         
│       ├── def test_stub_tests()                                                                                               
│       ├── def test_main()                                                                                                     
│       ├── def test_class_vehicle()                                                                                            
│       └── def test_class_car()                                                                                                
└── 📁 tree_plus_src (2 folders, 10 files) 
    ├── 📄 count_tokens_lines.py (1,323 tokens, 209 lines)
    │   ├── TODO: show off how well we parse_todo!                                                                              
    │   ├── @dataclass(frozen=True)                                                                                             
    │   │   class TokenLineCount                                                                                                
    │   ├──     n_tokens: int                                                                                                   
    │   ├──     n_lines: int                                                                                                    
    │   ├── class TokenizerName(Enum)                                                                                           
    │   ├──     WC = "wc"                                                                                                       
    │   ├──     GPT_4O = "gpt-4o"                                                                                               
    │   ├── def count_tokens_lines(                                                                                             
    │   │       file_path: Union[str, Path],                                                                                    
    │   │       *,                                                                                                              
    │   │       tokenizer_name: TokenizerName = TokenizerName.WC,                                                               
    │   │   ) -> Optional[TokenLineCount]                                                                                       
    │   ├── def count_openai_tokens_lines_from_contents(                                                                        
    │   │       contents: Union[str, "Markdown"],                                                                               
    │   │       *,                                                                                                              
    │   │       tokenizer_name: TokenizerName = TokenizerName.GPT_4O,                                                           
    │   │   ) -> TokenLineCount                                                                                                 
    │   ├── def count_wc_tokens_lines_from_path(file_path: str) -> TokenLineCount                                               
    │   └── def add_tokens_lines(                                                                                               
    │           lhs_count: TokenLineCount, rhs_count: TokenLineCount                                                            
    │       ) -> TokenLineCount                                                                                                 
    ├── 📄 debug.py (186 tokens, 39 lines)
    │   ├── def disable_debug()                                                                                                 
    │   ├── @lru_cache                                                                                                          
    │   │   def debug_enabled()                                                                                                 
    │   ├── def debug_print(*args, **kwargs)                                                                                    
    │   ├── def enable_debug()                                                                                                  
    │   └── @contextmanager                                                                                                     
    │       def debug_disabled()                                                                                                
    ├── 📄 deploy.py (2,058 tokens, 230 lines)
    │   ├── def extract(path: Optional[str] = None) -> str                                                                      
    │   ├── def load(content: Optional[str] = None, path: Optional[str] = None)                                                 
    │   ├── def extract_version(source_path: Optional[str] = None) -> Tuple[int, int, int]                                      
    │   ├── def increment_version(                                                                                              
    │   │       source_path: Optional[str] = None,                                                                              
    │   │       sink_path: Optional[str] = None,                                                                                
    │   │   )                                                                                                                   
    │   ├── def run_command(command: Optional[str] = None, debug: bool = False)                                                 
    │   ├── def replace_readme_section(                                                                                         
    │   │       source_path: Optional[str] = None,                                                                              
    │   │       sink_path: Optional[str] = None,                                                                                
    │   │       marker: Optional[str] = None,                                                                                   
    │   │       command: Optional[str] = None,                                                                                  
    │   │   )                                                                                                                   
    │   ├── def update_readme(source_path: Optional[str] = None, sink_path: Optional[str] = None)                               
    │   └── def main()                                                                                                          
    ├── 📄 engine.py (12,042 tokens, 1,438 lines)
    │   ├── TODO: MOVE TIMEOUT_SECONDS TO ENV VAR & CLI INPUT                                                                   
    │   ├── NOTE: you can customize the color here, and we could make this functional                                           
    │   ├── TODO: clarify subtree types -- make this a DataFrame tbh                                                            
    │   ├── NOTE: sometimes we need wider trees (deploy)                                                                        
    │   ├── TODO: research & decide about globs as paths instead of as filters                                                  
    │   ├── NOTE: directly add input file_paths to the amortized glob matches                                                   
    │   ├── TODO: decide to apply glob patterns to glob paths (currently NO)                                                    
    │   ├── TODO: decide between glob and rglob in _from_glob                                                                   
    │   ├── TODO: decide if we need to re-amortize the globs in the glob seed                                                   
    │   ├── TODO: clarify ignore in glob seed context, skipping for now                                                         
    │   ├── NOTE: this is only to satisfy the type checker                                                                      
    │   ├── class Category(Enum)                                                                                                
    │   ├──     ROOT = 1                                                                                                        
    │   ├──     GLOB = 2                                                                                                        
    │   ├──     FOLDER = 3                                                                                                      
    │   ├──     FILE = 4                                                                                                        
    │   ├──     COMPONENT = 5                                                                                                   
    │   ├──     URL = 6                                                                                                         
    │   ├──     TAG = 7                                                                                                         
    │   ├── @dataclass                                                                                                          
    │   │   class TreePlus                                                                                                      
    │   ├──     category: Category                                                                                              
    │   ├──     name: Union[str, Panel, Text, Markdown, Table, Pretty]                                                          
    │   ├──     line_count: int                                                                                                 
    │   ├──     token_count: int                                                                                                
    │   ├──     subtrees: Union[                                                                                                
    │   ├──     hrefs: Optional[Dict[str, list]]                                                                                
    │   ├──     @property                                                                                                       
    │   │       def has_tree_plus_subtrees(self) -> Optional[bool]                                                              
    │   ├──     @property                                                                                                       
    │   │       def n_folders(self) -> int                                                                                      
    │   ├──     @property                                                                                                       
    │   │       def n_files(self) -> int                                                                                        
    │   ├──     @property                                                                                                       
    │   │       def n_lines(self) -> int                                                                                        
    │   ├──     @property                                                                                                       
    │   │       def n_tokens(self) -> int                                                                                       
    │   ├──     def is_root(self) -> bool                                                                                       
    │   ├──     def is_folder(self) -> bool                                                                                     
    │   ├──     def is_file(self) -> bool                                                                                       
    │   ├──     def is_glob(self) -> bool                                                                                       
    │   ├──     def is_component(self) -> bool                                                                                  
    │   ├──     def is_url(self) -> bool                                                                                        
    │   ├──     def into_rich_tree(self) -> Tree                                                                                
    │   ├──     def into_str(self) -> str                                                                                       
    │   ├──     def render(                                                                                                     
    │   │           self,                                                                                                       
    │   │           style: Optional[str] = None,                                                                                
    │   │           highlight: bool = HIGHLIGHT,                                                                                
    │   │           markup: bool = MARKUP,                                                                                      
    │   │           capturing: bool = False,                                                                                    
    │   │       )                                                                                                               
    │   ├──     def render_hrefs(self)                                                                                          
    │   ├──     def stats(self) -> str                                                                                          
    │   ├── def from_hrefs(                                                                                                     
    │   │       hrefs: dict,                                                                                                    
    │   │       root_panel_text,                                                                                                
    │   │       link_color: str = LINK_COLOR,                                                                                   
    │   │   ) -> Optional[TreePlus]                                                                                             
    │   ├── def stats_from_tree_plus(tree: TreePlus) -> str                                                                     
    │   ├── @lru_cache                                                                                                          
    │   │   def remove_trailing_space(x: str) -> str                                                                            
    │   ├── def tree_to_string(                                                                                                 
    │   │       tree: Tree,                                                                                                     
    │   │       markup: bool = MARKUP,                                                                                          
    │   │       highlight: bool = HIGHLIGHT,                                                                                    
    │   │       text_style: str = TEXT_COLOR,                                                                                   
    │   │   ) -> str                                                                                                            
    │   ├── def clean_string(input_str: str) -> str                                                                             
    │   ├── def safe_print(                                                                                                     
    │   │       tree: Union[Tree, Panel, str, list],                                                                            
    │   │       style: Optional[str] = None,                                                                                    
    │   │       highlight: bool = HIGHLIGHT,                                                                                    
    │   │       markup: bool = MARKUP,                                                                                          
    │   │       capturing: bool = False,                                                                                        
    │   │   )                                                                                                                   
    │   ├── def _make_rich_tree(