folderTree-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
folderTree/__init__.py ADDED
File without changes
folderTree/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .cli.main import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()
File without changes
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from folderTree.models import Folder, Project
6
+ from folderTree.utils.constants import EXTENSION_TO_LANGUAGE
7
+
8
+
9
+ @dataclass(slots=True)
10
+ class LanguageStatistics:
11
+ """
12
+ Stores language statistics for a project.
13
+ """
14
+
15
+ language_counts: dict[str, int] = field(default_factory=dict)
16
+
17
+
18
+ class LanguageAnalyzer:
19
+ """
20
+ Analyzes programming languages used in a project.
21
+ """
22
+
23
+ def analyze(self, project: Project) -> LanguageStatistics:
24
+ """
25
+ Analyze the project and return language statistics.
26
+ """
27
+
28
+ statistics = LanguageStatistics()
29
+
30
+ self._walk_folder(
31
+ project.root_folder,
32
+ statistics,
33
+ )
34
+
35
+ return statistics
36
+
37
+ def _walk_folder(
38
+ self,
39
+ folder: Folder,
40
+ statistics: LanguageStatistics,
41
+ ) -> None:
42
+ """
43
+ Recursively walk through folders.
44
+ """
45
+ # Process files in the current folder
46
+ for file in folder.files:
47
+
48
+ language = EXTENSION_TO_LANGUAGE.get(
49
+ file.extension.lower(),
50
+ "Unknown",
51
+ )
52
+
53
+ file.language = language
54
+
55
+ statistics.language_counts[language] = (
56
+ statistics.language_counts.get(language, 0) + 1
57
+ )
58
+
59
+ # Recursively process subfolders
60
+ for subfolder in folder.folders:
61
+
62
+ self._walk_folder(
63
+ subfolder,
64
+ statistics,
65
+ )
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from folderTree.models import Folder, Project
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class Statistics:
10
+ """
11
+ Stores statistics about a scanned project.
12
+ """
13
+
14
+ total_files: int = 0
15
+ total_folders: int = 0
16
+ total_size: int = 0
17
+
18
+ extension_counts: dict[str, int] = field(default_factory=dict)
19
+
20
+
21
+ class StatisticsAnalyzer:
22
+ """
23
+ Analyzes a Project and generates statistics.
24
+ """
25
+
26
+ def analyze(self, project: Project) -> Statistics:
27
+ """
28
+ Analyze a project and return statistics.
29
+ """
30
+
31
+ statistics = Statistics()
32
+
33
+ self._walk_folder(
34
+ project.root_folder,
35
+ statistics,
36
+ )
37
+
38
+ return statistics
39
+
40
+ def _walk_folder(
41
+ self,
42
+ folder: Folder,
43
+ statistics: Statistics,
44
+ ) -> None:
45
+ """
46
+ Recursively walk through folders and collect statistics.
47
+ """
48
+
49
+ # Count the current folder
50
+ statistics.total_folders += 1
51
+
52
+ # Process files in the current folder
53
+ for file in folder.files:
54
+
55
+ statistics.total_files += 1
56
+
57
+ statistics.total_size += file.size
58
+
59
+ extension = file.extension.lower()
60
+
61
+ statistics.extension_counts[extension] = (
62
+ statistics.extension_counts.get(extension, 0) + 1
63
+ )
64
+
65
+ # Recursively process subfolders
66
+ for subfolder in folder.folders:
67
+ self._walk_folder(
68
+ subfolder,
69
+ statistics,
70
+ )
File without changes
folderTree/cli/main.py ADDED
@@ -0,0 +1,66 @@
1
+ from pathlib import Path
2
+
3
+ from folderTree.exporters.markdown import MarkdownExporter
4
+ from folderTree.exporters.txt import TXTExporter
5
+ from folderTree.generators.documentation import DocumentationGenerator
6
+ from folderTree.scanner.scanner import ProjectScanner
7
+ from folderTree.cli.parser import create_parser
8
+
9
+
10
+ def main() -> None:
11
+ """
12
+ Entry point for the FolderTree CLI.
13
+ """
14
+
15
+ parser = create_parser()
16
+
17
+ args = parser.parse_args()
18
+
19
+ if args.command == "scan":
20
+ scan(Path(args.path), args.output)
21
+
22
+
23
+ def scan(
24
+ project_path: Path,
25
+ output: str | None,
26
+ ) -> None:
27
+ """
28
+ Scan a project and generate documentation.
29
+ """
30
+ scanner = ProjectScanner()
31
+
32
+ project = scanner.scan(project_path)
33
+
34
+ generator = DocumentationGenerator()
35
+
36
+ documentation = generator.generate(project)
37
+
38
+ if output is None:
39
+ print(documentation)
40
+ return
41
+
42
+ output_path = Path(output)
43
+
44
+ suffix = output_path.suffix.lower()
45
+
46
+ if suffix == ".md":
47
+ exporter = MarkdownExporter()
48
+
49
+ elif suffix == ".txt":
50
+ exporter = TXTExporter()
51
+
52
+ else:
53
+ raise ValueError(
54
+ f"Unsupported output format: {suffix}"
55
+ )
56
+
57
+ exporter.export(
58
+ documentation,
59
+ output_path,
60
+ )
61
+
62
+ print("✓ Documentation generated successfully.")
63
+ print(f"Output: {output_path}")
64
+
65
+ if __name__ == "__main__":
66
+ main()
@@ -0,0 +1,35 @@
1
+ from argparse import ArgumentParser
2
+
3
+
4
+ def create_parser() -> ArgumentParser:
5
+ """
6
+ Create and configure the FolderTree CLI parser.
7
+ """
8
+
9
+ parser = ArgumentParser(
10
+ prog="folderTree",
11
+ description="Generate project documentation.",
12
+ )
13
+
14
+ subparsers = parser.add_subparsers(
15
+ dest="command",
16
+ required=True,
17
+ )
18
+
19
+ scan_parser = subparsers.add_parser(
20
+ "scan",
21
+ help="Scan a project directory.",
22
+ )
23
+
24
+ scan_parser.add_argument(
25
+ "path",
26
+ help="Path to the project directory.",
27
+ )
28
+
29
+ scan_parser.add_argument(
30
+ "-o",
31
+ "--output",
32
+ help="Output file (.md or .txt).",
33
+ )
34
+
35
+ return parser
folderTree/config.py ADDED
@@ -0,0 +1,17 @@
1
+ from dataclasses import dataclass, field
2
+
3
+ from folderTree.utils.constants import DEFAULT_IGNORES
4
+
5
+
6
+ @dataclass(slots=True)
7
+ class Config:
8
+ """
9
+ Runtime configuration for FolderTree.
10
+ """
11
+
12
+ max_depth: int | None = None
13
+ show_hidden: bool = False
14
+ follow_symlinks: bool = False
15
+ ignore_patterns: set[str] = field(
16
+ default_factory=lambda: set(DEFAULT_IGNORES)
17
+ )
File without changes
@@ -0,0 +1,27 @@
1
+ from pathlib import Path
2
+
3
+
4
+ class MarkdownExporter:
5
+ """
6
+ Exports documentation as a Markdown file.
7
+ """
8
+
9
+ def export(
10
+ self,
11
+ content: str,
12
+ output_path: Path,
13
+ ) -> Path:
14
+ """
15
+ Write Markdown content to a file.
16
+ """
17
+ output_path.parent.mkdir(
18
+ parents=True,
19
+ exist_ok=True,
20
+ )
21
+
22
+ output_path.write_text(
23
+ content,
24
+ encoding="utf-8",
25
+ )
26
+
27
+ return output_path
@@ -0,0 +1,28 @@
1
+ from pathlib import Path
2
+
3
+
4
+ class TXTExporter:
5
+ """
6
+ Exports text content to a .txt file.
7
+ """
8
+
9
+ def export(
10
+ self,
11
+ content: str,
12
+ output_path: Path,
13
+ ) -> None:
14
+ """
15
+ Write text content to the given file.
16
+ """
17
+
18
+ # Create parent directories if they don't exist
19
+ output_path.parent.mkdir(
20
+ parents=True,
21
+ exist_ok=True,
22
+ )
23
+
24
+ # Write the content to the file
25
+ output_path.write_text(
26
+ content,
27
+ encoding="utf-8",
28
+ )
File without changes
@@ -0,0 +1,116 @@
1
+ from datetime import datetime
2
+
3
+ from folderTree.utils.formatter import Formatter
4
+ from folderTree.analyzers.languages import LanguageAnalyzer
5
+ from folderTree.analyzers.statistics import StatisticsAnalyzer
6
+ from folderTree.generators.tree import TreeGenerator
7
+ from folderTree.models import Project
8
+
9
+
10
+ class DocumentationGenerator:
11
+ """
12
+ Generates Markdown documentation for a project.
13
+ """
14
+
15
+ def __init__(self):
16
+ self.tree_generator = TreeGenerator()
17
+ self.statistics_analyzer = StatisticsAnalyzer()
18
+ self.language_analyzer = LanguageAnalyzer()
19
+
20
+ def generate(
21
+ self,
22
+ project: Project,
23
+ ) -> str:
24
+ """
25
+ Generate complete project documentation.
26
+ """
27
+ return "\n\n".join(
28
+ [
29
+ "# Project Documentation",
30
+ "",
31
+ self._project_information(project),
32
+ self._statistics_section(project),
33
+ self._languages_section(project),
34
+ self._tree_section(project),
35
+ ]
36
+ )
37
+
38
+ def _project_information(
39
+ self,
40
+ project: Project,
41
+ ) -> str:
42
+ """
43
+ Generate the project information section.
44
+ """
45
+
46
+ metadata = project.root_folder.metadata
47
+
48
+ return (
49
+ "## Project Information\n\n"
50
+ f"- **Project Name:** {project.name}\n"
51
+ f"- **Project Location:** {project.root_path.resolve()}\n"
52
+ f"- **Project Created:** "
53
+ f"{Formatter.format_datetime(metadata.created_at)}\n"
54
+ f"- **Last Modified:** "
55
+ f"{Formatter.format_datetime(metadata.modified_at)}\n"
56
+ f"- **Documentation Generated:** "
57
+ f"{Formatter.format_datetime(datetime.now())}\n"
58
+ )
59
+
60
+ def _statistics_section(
61
+ self,
62
+ project: Project,
63
+ ) -> str:
64
+ """
65
+ Generate the statistics section.
66
+ """
67
+
68
+ statistics = self.statistics_analyzer.analyze(project)
69
+
70
+ return (
71
+ "## Statistics\n\n"
72
+ f"- **Total Files:** {statistics.total_files}\n"
73
+ f"- **Total Folders:** {statistics.total_folders}\n"
74
+ f"- **Total Size:** {Formatter.format_bytes(statistics.total_size)}\n"
75
+ )
76
+
77
+ def _languages_section(
78
+ self,
79
+ project: Project,
80
+ ) -> str:
81
+ """
82
+ Generate the languages section.
83
+ """
84
+
85
+ statistics = self.language_analyzer.analyze(project)
86
+
87
+ lines = [
88
+ "## Languages",
89
+ "",
90
+ ]
91
+
92
+ for language, count in sorted(
93
+ statistics.language_counts.items()
94
+ ):
95
+ lines.append(
96
+ f"- **{language}:** {count}"
97
+ )
98
+
99
+ return "\n".join(lines)
100
+
101
+ def _tree_section(
102
+ self,
103
+ project: Project,
104
+ ) -> str:
105
+ """
106
+ Generate the folder tree section.
107
+ """
108
+
109
+ tree = self.tree_generator.generate(project)
110
+
111
+ return (
112
+ "## Folder Structure\n\n"
113
+ "```text\n"
114
+ f"{tree}\n"
115
+ "```\n"
116
+ )
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ from folderTree.models import Project, Folder
4
+
5
+
6
+ class TreeGenerator:
7
+ """
8
+ Generates a tree representation from a Project model.
9
+ """
10
+
11
+ def generate(self, project: Project) -> str:
12
+ """
13
+ Generate a tree string from a project.
14
+ """
15
+
16
+ lines: list[str] = []
17
+
18
+ lines.append(f"{project.name}/")
19
+
20
+ self._build_tree(
21
+ folder=project.root_folder,
22
+ prefix="",
23
+ lines=lines,
24
+ )
25
+
26
+ return "\n".join(lines)
27
+
28
+ def _build_tree(
29
+ self,
30
+ folder: Folder,
31
+ prefix: str,
32
+ lines: list[str],
33
+ ) -> None:
34
+ """
35
+ Recursively build tree lines from a folder.
36
+ """
37
+
38
+ entries = sorted(
39
+ [
40
+ *folder.folders,
41
+ *folder.files
42
+ ],
43
+ key = lambda entry: (
44
+ not isinstance(entry, Folder),
45
+ entry.name.lower(),
46
+ )
47
+ )
48
+
49
+ for index, entry in enumerate(entries):
50
+
51
+ is_last = index == len(entries) - 1
52
+
53
+ if is_last:
54
+ connector = "└── "
55
+ next_prefix = prefix + " "
56
+ else:
57
+ connector = "├── "
58
+ next_prefix = prefix + "│ "
59
+
60
+ if isinstance(entry, Folder):
61
+
62
+ lines.append(
63
+ f"{prefix}{connector}{entry.name}/"
64
+ )
65
+
66
+ self._build_tree(
67
+ folder=entry,
68
+ prefix=next_prefix,
69
+ lines=lines,
70
+ )
71
+
72
+ else:
73
+
74
+ lines.append(
75
+ f"{prefix}{connector}{entry.name}"
76
+ )
@@ -0,0 +1,11 @@
1
+ from .file import File
2
+ from .folder import Folder
3
+ from .project import Project
4
+ from .metadata import Metadata
5
+
6
+ __all__ = [
7
+ "File",
8
+ "Folder",
9
+ "Project",
10
+ "Metadata",
11
+ ]
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
5
+ from pathlib import Path
6
+
7
+ from .metadata import Metadata
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class File:
12
+ """
13
+ Represents a file in the scanned project.
14
+ """
15
+
16
+ name: str
17
+ path: Path
18
+ extension: str
19
+ metadata: Metadata
20
+
21
+ language: str | None = None
22
+
23
+ @property
24
+ def size(self) -> int:
25
+ """
26
+ Returns the file size in bytes.
27
+ """
28
+ return self.metadata.size
29
+
30
+ @property
31
+ def created_at(self) -> datetime:
32
+ """
33
+ Returns the file creation timestamp.
34
+ """
35
+ return self.metadata.created_at
36
+
37
+ @property
38
+ def modified_at(self) -> datetime:
39
+ """
40
+ Returns the file's last modification timestamp.
41
+ """
42
+ return self.metadata.modified_at
43
+
44
+ @property
45
+ def size_kb(self) -> float:
46
+ """
47
+ Returns the file size in kilobytes.
48
+ """
49
+ return self.size / 1024
50
+
51
+ @property
52
+ def size_mb(self) -> float:
53
+ """
54
+ Returns the file size in megabytes.
55
+ """
56
+ return self.size / (1024 * 1024)
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from pathlib import Path
5
+
6
+ from .file import File
7
+ from .metadata import Metadata
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class Folder:
12
+ """
13
+ Represents a folder in the scanned project.
14
+ """
15
+
16
+ name: str
17
+ path: Path
18
+ metadata: Metadata
19
+
20
+ files: list[File] = field(default_factory=list)
21
+ folders: list["Folder"] = field(default_factory=list)
22
+
23
+ @property
24
+ def file_count(self) -> int:
25
+ """
26
+ Returns the number of files directly inside this folder.
27
+ """
28
+ return len(self.files)
29
+
30
+ @property
31
+ def folder_count(self) -> int:
32
+ """
33
+ Returns the number of immediate subfolders.
34
+ """
35
+ return len(self.folders)
36
+
37
+ @property
38
+ def total_size(self) -> int:
39
+ """
40
+ Returns the total size of this folder recursively.
41
+ """
42
+ size = sum(file.size for file in self.files)
43
+
44
+ for folder in self.folders:
45
+ size += folder.total_size
46
+
47
+ return size
@@ -0,0 +1,13 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+
4
+
5
+ @dataclass(slots=True)
6
+ class Metadata:
7
+ """
8
+ Filesystem metadata associated with a file or folder.
9
+ """
10
+
11
+ size: int
12
+ created_at: datetime
13
+ modified_at: datetime
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+
5
+ from .folder import Folder
6
+
7
+
8
+ @dataclass(slots=True)
9
+ class Project:
10
+ """
11
+ Represents the scanned project.
12
+ """
13
+
14
+ name: str
15
+ root_path: Path
16
+ root_folder: Folder
17
+
18
+ scanned_at: datetime = field(default_factory=datetime.now)
File without changes
@@ -0,0 +1,37 @@
1
+ from pathlib import Path
2
+ from fnmatch import fnmatch
3
+
4
+ from folderTree.config import Config
5
+
6
+
7
+ class IgnoreEngine:
8
+ """
9
+ Handles all ignore logic for files and directories.
10
+ """
11
+
12
+ def __init__(self, config: Config):
13
+ self.config = config
14
+
15
+ def should_ignore(self, path: Path) -> bool:
16
+ if not self.config.show_hidden and self.is_hidden(path):
17
+ return True
18
+
19
+ if self.matches_ignore_pattern(path):
20
+ return True
21
+
22
+ return False
23
+
24
+
25
+ def matches_ignore_pattern(self, path: Path) -> bool:
26
+ for pattern in self.config.ignore_patterns:
27
+ if fnmatch(path.name, pattern):
28
+ return True
29
+ return False
30
+
31
+
32
+ @staticmethod
33
+ def is_hidden(path: Path) -> bool:
34
+ """
35
+ Return True if the path is hidden.
36
+ """
37
+ return path.name.startswith(".")
@@ -0,0 +1,20 @@
1
+ from datetime import datetime
2
+ from pathlib import Path
3
+
4
+ from folderTree.models import Metadata
5
+
6
+
7
+ class MetadataCollector:
8
+ """
9
+ Collects metadata from filesystem paths.
10
+ """
11
+
12
+ @staticmethod
13
+ def collect(path: Path) -> Metadata:
14
+ stats = path.stat()
15
+
16
+ return Metadata(
17
+ size=stats.st_size,
18
+ created_at=datetime.fromtimestamp(stats.st_ctime),
19
+ modified_at=datetime.fromtimestamp(stats.st_mtime),
20
+ )
@@ -0,0 +1,111 @@
1
+ from pathlib import Path
2
+
3
+ from folderTree.config import Config
4
+ from folderTree.models import File, Folder, Project
5
+ from folderTree.scanner.ignore import IgnoreEngine
6
+ from folderTree.scanner.metadata_collector import MetadataCollector
7
+ from folderTree.utils.helpers import validate_path, get_extension
8
+
9
+
10
+ class ProjectScanner:
11
+ """
12
+ Scans a directory and builds a Project model.
13
+ """
14
+
15
+ def __init__(self, config: Config | None = None):
16
+ self.config = config or Config()
17
+ self.ignore_engine = IgnoreEngine(self.config)
18
+ self.metadata_collector = MetadataCollector()
19
+
20
+ def scan(self, path: str | Path) -> Project:
21
+ """
22
+ Scan the given project directory.
23
+ """
24
+ root_path = validate_path(path)
25
+
26
+ if not root_path.is_dir():
27
+ raise NotADirectoryError(f"{root_path} is not a directory.")
28
+
29
+ root_folder = self._scan_folder(root_path)
30
+
31
+ return Project(
32
+ name=root_path.name,
33
+ root_path=root_path,
34
+ root_folder=root_folder,
35
+ )
36
+
37
+ def _scan_folder(self, path: Path, depth: int = 0) -> Folder:
38
+ """
39
+ Recursively scan a folder and return a Folder model.
40
+ """
41
+
42
+ # Stop recursion if max depth is reached
43
+ if (
44
+ self.config.max_depth is not None
45
+ and depth > self.config.max_depth
46
+ ):
47
+ metadata = self.metadata_collector.collect(path)
48
+
49
+ return Folder(
50
+ name=path.name,
51
+ path=path,
52
+ metadata=metadata,
53
+ )
54
+
55
+ metadata = self.metadata_collector.collect(path)
56
+
57
+ folder = Folder(
58
+ name=path.name,
59
+ path=path,
60
+ metadata=metadata,
61
+ )
62
+
63
+ try:
64
+ entries = sorted(
65
+ path.iterdir(),
66
+ key=lambda entry: (entry.is_file(), entry.name.lower())
67
+ )
68
+ except (PermissionError, FileNotFoundError, OSError):
69
+ return folder
70
+
71
+ for entry in entries:
72
+
73
+ if self.ignore_engine.should_ignore(entry):
74
+ continue
75
+
76
+ if entry.is_dir():
77
+
78
+ try:
79
+ subfolder = self._scan_folder(
80
+ entry,
81
+ depth + 1,
82
+ )
83
+
84
+ folder.folders.append(subfolder)
85
+ except (PermissionError, FileNotFoundError, OSError):
86
+ continue
87
+
88
+ elif entry.is_file():
89
+
90
+ try:
91
+ file = self._create_file(entry)
92
+
93
+ folder.files.append(file)
94
+ except (PermissionError, FileNotFoundError, OSError):
95
+ continue
96
+
97
+ return folder
98
+
99
+ def _create_file(self, path: Path) -> File:
100
+ """
101
+ Create a File model from a filesystem path.
102
+ """
103
+ metadata = self.metadata_collector.collect(path)
104
+
105
+ return File(
106
+ name = path.name,
107
+ path = path,
108
+ extension = get_extension(path),
109
+ metadata = metadata,
110
+ language = None
111
+ )
File without changes
@@ -0,0 +1,68 @@
1
+ """
2
+ Application constants.
3
+ """
4
+
5
+ APP_NAME = "FolderTree"
6
+ VERSION = "0.1.0"
7
+
8
+ TREE_BRANCH = "├── "
9
+ TREE_LAST_BRANCH = "└── "
10
+ TREE_PIPE = "│ "
11
+ TREE_SPACE = " "
12
+
13
+ DEFAULT_IGNORES = {
14
+ ".git",
15
+ ".github",
16
+ ".venv",
17
+ "venv",
18
+ "env",
19
+ "test_env",
20
+ "test_venv",
21
+ "build",
22
+ "dist",
23
+ "*.egg-info",
24
+ "__pycache__",
25
+ ".pytest_cache",
26
+ ".mypy_cache",
27
+ ".idea",
28
+ ".vscode",
29
+ "node_modules",
30
+ ".DS_Store",
31
+ }
32
+
33
+ EXTENSION_TO_LANGUAGE = {
34
+ ".py": "Python",
35
+ ".js": "JavaScript",
36
+ ".ts": "TypeScript",
37
+ ".java": "Java",
38
+ ".c": "C",
39
+ ".cpp": "C++",
40
+ ".cs": "C#",
41
+ ".go": "Go",
42
+ ".rs": "Rust",
43
+ ".php": "PHP",
44
+ ".rb": "Ruby",
45
+
46
+ ".html": "HTML",
47
+ ".css": "CSS",
48
+ ".scss": "SCSS",
49
+
50
+ ".json": "JSON",
51
+ ".xml": "XML",
52
+ ".yaml": "YAML",
53
+ ".yml": "YAML",
54
+ ".toml": "TOML",
55
+ ".ini": "INI",
56
+
57
+ ".md": "Markdown",
58
+ ".txt": "Text",
59
+
60
+ ".sh": "Shell",
61
+ ".bat": "Batch",
62
+ ".ps1": "PowerShell",
63
+
64
+ ".sql": "SQL",
65
+
66
+ ".dockerfile": "Docker",
67
+ ".env": "Environment",
68
+ }
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+
5
+
6
+ class Formatter:
7
+ """
8
+ Utility class for formatting values for output.
9
+ """
10
+
11
+ @staticmethod
12
+ def format_bytes(size: int) -> str:
13
+ """
14
+ Format bytes into a human-readable string.
15
+ """
16
+
17
+ units = ["bytes", "KB", "MB", "GB", "TB"]
18
+
19
+ value = float(size)
20
+
21
+ for unit in units:
22
+
23
+ if value < 1024 or unit == units[-1]:
24
+ if unit == "bytes":
25
+ return f"{int(value)} {unit}"
26
+
27
+ return f"{value:.2f} {unit}"
28
+
29
+ value /= 1024
30
+
31
+ @staticmethod
32
+ def format_datetime(value: datetime) -> str:
33
+ """
34
+ Format a datetime object.
35
+ """
36
+ return value.strftime("%Y-%m-%d %H:%M:%S")
37
+
38
+ @staticmethod
39
+ def heading(
40
+ text: str,
41
+ level: int = 1,
42
+ ) -> str:
43
+ """
44
+ Return a Markdown heading.
45
+ """
46
+ return f'{"#" * level} {text}'
47
+
48
+ @staticmethod
49
+ def bullet(text: str) -> str:
50
+ """
51
+ Return a Markdown bullet.
52
+ """
53
+ return f"- {text}"
@@ -0,0 +1,41 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def validate_path(path: str | Path) -> Path:
5
+ """
6
+ Validate that a path exists and return it as a resolved Path object.
7
+ """
8
+ path = Path(path).expanduser().resolve()
9
+
10
+ if not path.exists():
11
+ raise FileNotFoundError(f"Path does not exist: {path}")
12
+
13
+ return path
14
+
15
+
16
+ def is_hidden(path: Path) -> bool:
17
+ """
18
+ Return True if the file or folder is hidden.
19
+ """
20
+ return path.name.startswith(".")
21
+
22
+
23
+ def get_extension(path: Path) -> str:
24
+ """
25
+ Return the file extension in lowercase.
26
+ """
27
+ return path.suffix.lower()
28
+
29
+
30
+ def format_size(size: int) -> str:
31
+ """
32
+ Convert bytes into a human-readable string.
33
+ """
34
+ units = ["B", "KB", "MB", "GB", "TB"]
35
+
36
+ value = float(size)
37
+
38
+ for unit in units:
39
+ if value < 1024 or unit == units[-1]:
40
+ return f"{value:.2f} {unit}"
41
+ value /= 1024
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: folderTree-cli
3
+ Version: 0.1.0
4
+ Summary: Generate beautiful folder tree structures and project documentation.
5
+ Author: Kaushal Kumar Jha
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Nikk-hub-code/folderTree
8
+ Project-URL: Repository, https://github.com/Nikk-hub-code/folderTree
9
+ Project-URL: Issues, https://github.com/Nikk-hub-code/folderTree/issues
10
+ Keywords: folder-tree,tree,documentation,cli,filesystem,project,directory,python
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # FolderTree
27
+
28
+ A lightweight command-line tool that automatically analyzes a project directory and generates clean, well-structured project documentation.
29
+
30
+ FolderTree scans your project, collects metadata, analyzes project statistics, detects programming languages, generates a folder tree, and exports documentation in Markdown or TXT format.
31
+
32
+ ---
33
+
34
+ ## Features
35
+
36
+ - 📁 Scan any project directory
37
+ - 🌳 Generate a beautiful folder tree
38
+ - 📊 Analyze project statistics
39
+ - 💻 Detect programming languages
40
+ - 📝 Generate project documentation
41
+ - 📄 Export documentation as Markdown or TXT
42
+ - ⚡ Fast and simple CLI interface
43
+
44
+ ---
45
+
46
+ ## Requirements
47
+
48
+ - Python **3.11** or later
49
+
50
+ ---
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ pip install folderTree
56
+ ```
57
+
58
+ After installation, the `folderTree` command will be available globally.
59
+
60
+ ---
61
+
62
+ ## Usage
63
+
64
+ ### Show available commands
65
+
66
+ ```bash
67
+ folderTree --help
68
+ ```
69
+
70
+ ---
71
+
72
+ ### Generate documentation in the terminal
73
+
74
+ ```bash
75
+ folderTree scan .
76
+ ```
77
+
78
+ or
79
+
80
+ ```bash
81
+ folderTree scan path/to/project
82
+ ```
83
+
84
+ Example
85
+
86
+ ```bash
87
+ folderTree scan sample_projects/python_project
88
+ ```
89
+
90
+ ---
91
+
92
+ ### Export as Markdown
93
+
94
+ ```bash
95
+ folderTree scan . -o PROJECT_DOCUMENTATION.md
96
+ ```
97
+
98
+ ---
99
+
100
+ ### Export as TXT
101
+
102
+ ```bash
103
+ folderTree scan . -o PROJECT_DOCUMENTATION.txt
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Example Output
109
+
110
+ ```text
111
+ # Project Documentation
112
+
113
+ ## Project Information
114
+
115
+ - Project Name: folderTree
116
+ - Project Location: D:\Projects\folderTree
117
+ - Project Created: 2026-07-10
118
+ - Documentation Generated: 2026-07-19
119
+
120
+ ## Statistics
121
+
122
+ - Total Files: 59
123
+ - Total Folders: 22
124
+ - Total Size: 41.94 KB
125
+
126
+ ## Languages
127
+
128
+ - Python: 47
129
+ - Markdown: 4
130
+ - TOML: 1
131
+ - HTML: 1
132
+ - CSS: 1
133
+ - JavaScript: 1
134
+
135
+ ## Folder Structure
136
+
137
+ folderTree/
138
+ ├── src/
139
+ │ └── folderTree/
140
+ ├── tests/
141
+ ├── README.md
142
+ ├── LICENSE
143
+ └── pyproject.toml
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Project Structure
149
+
150
+ ```text
151
+ folderTree/
152
+
153
+ ├── src/
154
+ │ └── folderTree/
155
+ │ ├── analyzers/
156
+ │ ├── cli/
157
+ │ ├── exporters/
158
+ │ ├── generators/
159
+ │ ├── models/
160
+ │ ├── scanner/
161
+ │ └── utils/
162
+
163
+ ├── tests/
164
+ ├── docs/
165
+ ├── README.md
166
+ ├── LICENSE
167
+ ├── CHANGELOG.md
168
+ └── pyproject.toml
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Development
174
+
175
+ Clone the repository:
176
+
177
+ ```bash
178
+ git clone https://github.com/Nikk-hub-code/folderTree.git
179
+ ```
180
+
181
+ Move into the project directory:
182
+
183
+ ```bash
184
+ cd folderTree
185
+ ```
186
+
187
+ Install in editable mode:
188
+
189
+ ```bash
190
+ pip install -e .
191
+ ```
192
+
193
+ Run the test suite:
194
+
195
+ ```bash
196
+ pytest
197
+ ```
198
+
199
+ ---
200
+
201
+ ## License
202
+
203
+ This project is licensed under the MIT License.
@@ -0,0 +1,34 @@
1
+ folderTree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ folderTree/__main__.py,sha256=ikrOjMyTd655yw1MLY0-tV-iu0UCYEZd_pOEov1-7kY,70
3
+ folderTree/config.py,sha256=X5h0kBsje5-inszSfPhPChtdSfxi3lRMecBWfIVoqUg,405
4
+ folderTree/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ folderTree/analyzers/languages.py,sha256=sZChTsWu4m50InhzcOxG0v52XDjGktz8jrYkxlTqEZw,1585
6
+ folderTree/analyzers/statistics.py,sha256=RULiHRDGbf-xu6pCk7YCIaudjAk_DjMPkizGguVHbHQ,1638
7
+ folderTree/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ folderTree/cli/main.py,sha256=i5AJ6oAijhAoeTVwA04RjoIFnwFuVOoJTXq9ZhlFzQQ,1450
9
+ folderTree/cli/parser.py,sha256=ZMOBmLGMgIA0SxSvWoOG_XHfWSc0TxUUF7j603wl8kY,729
10
+ folderTree/exporters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ folderTree/exporters/markdown.py,sha256=Zh57kfsYryxQuB9laIrR16ucb6T-7bWkaiF_exImOQA,518
12
+ folderTree/exporters/txt.py,sha256=f31wDP-_KY31IRme_9t2tFwuSUfqiu_f9-SZ4_QPWfc,582
13
+ folderTree/generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ folderTree/generators/documentation.py,sha256=fgFvyf44dCpZCimupNV0Wp3LQjMl7h7V9-Zgu0VfyrI,3189
15
+ folderTree/generators/tree.py,sha256=hbrU-pPepuen-1yF6clKqfUK2mqNVg7FVdWT08Npdt8,1811
16
+ folderTree/models/__init__.py,sha256=9929wrH7bIXGkuzTf7F2HqEAS_6Cn9lnCFidrbm6Sgk,191
17
+ folderTree/models/file.py,sha256=a2NMhDEhcNoOTX0pkVsltByXLGp3JN_n4ryoI7LGPUw,1195
18
+ folderTree/models/folder.py,sha256=4JT--NG3a5DeSl6XFg01IyOLX3IgXREMQ1sf3tbTf9g,1076
19
+ folderTree/models/metadata.py,sha256=DS7V_C2CYV5Fl6_eI8_P3mh50yxiLNEUjpSAcgqatO0,256
20
+ folderTree/models/project.py,sha256=MfzWgS_AyDyblHIbD24DsRYU2iQhMUgtMnwhPJFrT88,355
21
+ folderTree/scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ folderTree/scanner/ignore.py,sha256=qVNlyXZmynRjs6GzKAi7KAAfs0W-ZhIzftN8UNsLBLg,894
23
+ folderTree/scanner/metadata_collector.py,sha256=ZBh9BWlNwgflBYFjz-Zvfvjxeu3BaAeUE4ayC5AjAfE,484
24
+ folderTree/scanner/scanner.py,sha256=TXG9tfalbcS7RyTPHMkOABItzZlofiu6GV_ZLKY1qCs,3228
25
+ folderTree/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ folderTree/utils/constants.py,sha256=EWAvqw7c2F929bBJZG5jZlOj7Z-g8Dhan2mW-PrDseI,1146
27
+ folderTree/utils/formatter.py,sha256=6t_YVrLSWyv6m5dkiEDZgAo1kxyNzVG7bdYhPOLLLlk,1162
28
+ folderTree/utils/helpers.py,sha256=z1oBQqV4pfjjxdYctyXdqj80zcCl_sHR1oixpKSYjZI,924
29
+ foldertree_cli-0.1.0.dist-info/licenses/LICENSE,sha256=gr-BnW0GXsyXE2Q_nY9FgKNGVb0FKDeUhoKWzs-tPBQ,1093
30
+ foldertree_cli-0.1.0.dist-info/METADATA,sha256=mwnw0RWV6zJb2v_Z3yk1WMuMnyvl5I6PIQFgGpR91Ws,3795
31
+ foldertree_cli-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
32
+ foldertree_cli-0.1.0.dist-info/entry_points.txt,sha256=bCL98tMdRfekt2s0Bao5RNRMMDHn3EZt0S6yFbf0urU,56
33
+ foldertree_cli-0.1.0.dist-info/top_level.txt,sha256=smlF8L6ttPFbjZLugMNV5vHTfJhPRVJrSyk5XqxytAo,11
34
+ foldertree_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ folderTree = folderTree.cli.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaushal Kumar Jha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ folderTree