Skip to content

Commit

Permalink
MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
thompsonmj committed Apr 12, 2024
1 parent 73d05e4 commit cd1c752
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 1 deletion.
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,51 @@
# uncoil
# uncoil

`uncoil` is a command-line tool designed to explore and print contents of directories with options to selectively skip certain file extensions or directories into a flat format either to standard out or a text file.

It will produce tree visualization of directories and files and will reveal the contents of files not skipped by the user.

This can be useful for navigating large projects, quickly summarizing as well as thoroughly revealing the structure and contents of directories.

It may be useful for providing context of a codebase to a Large Language Model (LLM).

## Installation

`uncoil` can be installed using pip directly from the GitHub repository or by cloning the repo. Choose the method that best suits your needs.

### Direct Installation with pip

To install the latest version of `uncoil` directly from GitHub, run:

```bash
pip install git+https://github.com/thompsonmj/uncoil.git
```

## Usage
```bash
uncoil -d <directory> [-o <output_file>] [-x <extensions_to_skip,dirs_to_skip>]
```

### Options

- `-d <directory>`: **Required.** The directory you want to process.
- `-o <output_file>`: Optional. Output file to redirect the output into. If not provided, the output will be printed to the console.
- `-x <extensions_to_skip,dirs_to_skip>`: Optional. A comma-separated list of file extensions or subdirectories to skip.

### Examples

1. **Print the unfiltered structure of a directory to the console:**
```bash
uncoil -d directory
```
2. **Print the structure of a directory to the console, skipping certain extensions and subdirectories:**
```bash
uncoil -d directory -x .log,.tmp,.git
```
3. **Save the unfiltered structure of a directory to a file:**
```bash
uncoil -d directory -o output.txt
```
4. **Save the structure of a directory to a file, skipping certain extensions and subdirectories:**
```bash
uncoil -d directory -o output.txt -x .log,.tmp,.git
```
22 changes: 22 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "uncoil"
version = "1.0.0"
description = "A command-line tool to explore and print contents of directories with options to skip certain patterns."
authors = [{ name = "Matthew J. Thompson", email = "[email protected]" }]
license = {file = "LICENSE"}
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
"docopt-ng >= 0.9.0",
"rich >= 13.6.0"
]

[project.scripts]
uncoil = "uncoil.__main__:main"

[tool.hatch.version]
enabled = true
Empty file added src/uncoil/__init__.py
Empty file.
82 changes: 82 additions & 0 deletions src/uncoil/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
Usage:
uncoil -d <directory> [-o <output_file>] [-x <extensions_to_skip,dir_to_skip>]
Options:
-h --help Show this screen.
-d <directory> Directory to process.
-o <output_file> Output file to redirect output into.
-x <extensions_to_skip> Comma-separated list of file extensions or directories to skip.
"""

import os
import sys
from docopt import docopt
from rich.console import Console
from rich.tree import Tree

def matches_skip_pattern(file_path, skip_patterns):
lower_file = file_path.lower()
return any(skip.lower() in lower_file for skip in skip_patterns)

def unfurl_directory(directory, skip_list):
for root, dirs, files in os.walk(directory, topdown=True):
dirs[:] = [d for d in dirs if not matches_skip_pattern(os.path.join(root, d), skip_list)]
for file in files:
file_path = os.path.join(root, file)
if not matches_skip_pattern(file_path, skip_list):
yield file_path

def create_tree(directory, skip_list):
tree = Tree(f"Directory Structure: {directory}")
for root, dirs, files in os.walk(directory, topdown=True):
dirs[:] = [d for d in dirs if not matches_skip_pattern(os.path.join(root, d), skip_list)]
path = root.split(os.sep)
parent = tree
for part in path[1:]:
parent = parent.add(part)
for file in files:
file_path = os.path.join(root, file)
if not matches_skip_pattern(file_path, skip_list):
parent.add(file)
return tree

def print_file_contents(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
contents = file.read()
print(f"==> {file_path} <==")
print(contents)
print("\n") # Adds an extra newline for readability between files
except Exception as e:
print(f"Error reading {file_path}: {e}")

def main():
args = docopt(__doc__)
directory = args['-d']
output_file = args.get('-o')
extensions_to_skip = args.get('-x', '')

if extensions_to_skip:
extensions_to_skip = extensions_to_skip.split(',')
else:
extensions_to_skip = []

console = Console(file=open(output_file, 'w') if output_file else sys.stdout)
tree = create_tree(directory, extensions_to_skip)
console.print(tree)

files = unfurl_directory(directory, extensions_to_skip)

if output_file:
sys.stdout = open(output_file, 'a')

try:
for file_path in files:
print_file_contents(file_path)
finally:
if output_file:
sys.stdout.close()

if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Directory Structure: uncoil
Empty file added tests/__init__.py
Empty file.
Empty file added tests/test_main.py
Empty file.

0 comments on commit cd1c752

Please sign in to comment.