x2fromx 0.1.0__tar.gz

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.
x2fromx-0.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TakoUlzO / Erabytse x2fromx contributors
4
+
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
x2fromx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: x2fromx
3
+ Version: 0.1.0
4
+ Summary: CLI & library to convert project directories ↔ text trees. Ideal for AI scaffolding & codebase analysis.
5
+ Author-email: TonNom <ton.email@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/TonUser/x2fromx
8
+ Project-URL: Bug Tracker, https://github.com/TonUser/x2fromx/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # πŸŒ²β†”οΈπŸ“ x2fromx
18
+
19
+ [![PyPI version](https://badge.fury.io/py/x2fromx.svg)](https://pypi.org/project/x2fromx/)
20
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
+ [![Downloads](https://static.pepy.tech/badge/x2fromx)](https://pepy.tech/project/x2fromx)
23
+
24
+ > **Convert project directories ↔ text trees in one command.**
25
+ > Built for developers & AI enthusiasts who need to scaffold projects from LLM outputs or extract codebase structure for context analysis.
26
+
27
+ ---
28
+
29
+ ## πŸš€ Why `x2fromx`?
30
+ - πŸ€– **AI-Ready**: Paste a `tree.txt` into ChatGPT/Claude, get a modified architecture back, and rebuild it instantly.
31
+ - ⚑ **Zero Dependencies**: Pure Python standard library. Works everywhere, instantly.
32
+ - 🧹 **Smart Filtering**: Automatically ignores `.git`, `node_modules`, `__pycache__`, binaries, and images.
33
+ - 🌱 **Auto-Seeding**: Creates boilerplate content (`.py`, `.html`, `.js`, etc.) so your IDE doesn't complain.
34
+ - πŸ–₯️ **CLI & Library**: Use it in your terminal or import it directly into your Python scripts.
35
+
36
+ ---
37
+
38
+ ## πŸ“¦ Installation
39
+
40
+ ```bash
41
+ pip install x2fromx
42
+ ```
43
+
44
+ πŸ› οΈ CLI Usage
45
+ πŸ” Scan a directory β†’ generate a tree file
46
+
47
+ ```bash
48
+ x2fromx scan ./my_existing_project -o structure.txt --print
49
+ ```
50
+
51
+ πŸ—οΈ Build a project from a tree file
52
+ ```
53
+ x2fromx build structure.txt -n my_new_project --overwrite
54
+ ```
55
+
56
+ Available flags:
57
+
58
+ | Flag | Description |
59
+ | :--- | :--- |
60
+ | scan <dir> | Path to the folder to analyze |
61
+ | build <file> | Path to the .txt tree file |
62
+ | -o, --output | Output filename (default: project_structure.txt) |
63
+ | -n, --name | Root project name for build (default: new_project) |
64
+ | --print | Print the tree in the terminal after saving |
65
+ | --overwrite | Force overwrite if the target folder already exists |
66
+
67
+
68
+ πŸ€– AI Workflow (The Killer Feature)
69
+
70
+ 1. Extract context: x2fromx scan ./legacy_app -o context.txt
71
+ 2. Ask an LLM: "Here is my project structure. Refactor it to add a /tests folder, split routes.py into a router package, and add a Dockerfile. Return the full tree."
72
+ 3. Save the response: Paste the LLM's output into refactored.txt
73
+ 4. Scaffold instantly: x2fromx build refactored.txt -n app_v2
74
+ 5. Start coding: Your IDE opens a ready-to-use structure with placeholders.
75
+
76
+
77
+ 🐍 Python API
78
+
79
+ ```python
80
+ from x2fromx import DirectoryScanner, ProjectBuilder
81
+
82
+ # Scan
83
+ scanner = DirectoryScanner("./src", "tree.txt")
84
+ scanner.save()
85
+
86
+ # Build
87
+ builder = ProjectBuilder("tree.txt", "my_project")
88
+ count, root = builder.build(overwrite=True)
89
+ print(f"Created {count} items in {root}")
90
+
91
+ ```
92
+
93
+ πŸ“ Example Output
94
+ ```bash
95
+ <!-- TREEVIEW START -->
96
+ my_project/
97
+ β”‚
98
+ β”œβ”€β”€ πŸ“ src/
99
+ β”‚ β”œβ”€β”€ πŸ“ api/
100
+ β”‚ β”‚ β”œβ”€β”€ πŸ“„ routes.py # Endpoint API
101
+ β”‚ β”‚ └── πŸ“„ schemas.py
102
+ β”‚ └── πŸ“„ main.py # TODO: Implement logic
103
+ β”œβ”€β”€ πŸ“ tests/
104
+ β”‚ └── πŸ“„ test_api.py # Tests unitaires
105
+ β”œβ”€β”€ πŸ“„ README.md # Documentation
106
+ └── πŸ“„ requirements.txt # DΓ©pendances Python
107
+ ```
108
+ <!-- TREEVIEW END -->
109
+
110
+ 🀝 Contributing
111
+
112
+ Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
113
+ 1. Fork the repo
114
+ 2. Create your feature branch (git checkout -b feature/amazing-feature)
115
+ 3. Commit your changes (git commit -m 'Add amazing feature')
116
+ 4. Push to the branch (git push origin feature/amazing-feature)
117
+ 5. Open a Pull Request
118
+
119
+
120
+ πŸ“œ License
121
+
122
+ Distributed under the MIT License. See LICENSE for more information.
123
+
124
+ ---
125
+
126
+ ## πŸ’™ Support
127
+
128
+ If you use and value this tool, consider supporting its development:
129
+ [![Sponsor](https://img.shields.io/badge/sponsor-erabytse-181717?logo=github)](https://github.com/sponsors/takouzlo)
130
+
131
+
132
+ [![Python](https://img.shields.io/badge/Python-3.13-blue)](https://python.org)
133
+ [![Flask](https://img.shields.io/badge/Flask-Web_Framework-green)](https://flask.palletsprojects.com)
134
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://typescriptlang.org)
135
+ [![AI](https://img.shields.io/badge/AI-ML-orange)](https://pytorch.org)
136
+ [![Founder](https://img.shields.io/badge/Founder-erabytse-purple)](https://github.com/erabytse)
@@ -0,0 +1,120 @@
1
+ # πŸŒ²β†”οΈπŸ“ x2fromx
2
+
3
+ [![PyPI version](https://badge.fury.io/py/x2fromx.svg)](https://pypi.org/project/x2fromx/)
4
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Downloads](https://static.pepy.tech/badge/x2fromx)](https://pepy.tech/project/x2fromx)
7
+
8
+ > **Convert project directories ↔ text trees in one command.**
9
+ > Built for developers & AI enthusiasts who need to scaffold projects from LLM outputs or extract codebase structure for context analysis.
10
+
11
+ ---
12
+
13
+ ## πŸš€ Why `x2fromx`?
14
+ - πŸ€– **AI-Ready**: Paste a `tree.txt` into ChatGPT/Claude, get a modified architecture back, and rebuild it instantly.
15
+ - ⚑ **Zero Dependencies**: Pure Python standard library. Works everywhere, instantly.
16
+ - 🧹 **Smart Filtering**: Automatically ignores `.git`, `node_modules`, `__pycache__`, binaries, and images.
17
+ - 🌱 **Auto-Seeding**: Creates boilerplate content (`.py`, `.html`, `.js`, etc.) so your IDE doesn't complain.
18
+ - πŸ–₯️ **CLI & Library**: Use it in your terminal or import it directly into your Python scripts.
19
+
20
+ ---
21
+
22
+ ## πŸ“¦ Installation
23
+
24
+ ```bash
25
+ pip install x2fromx
26
+ ```
27
+
28
+ πŸ› οΈ CLI Usage
29
+ πŸ” Scan a directory β†’ generate a tree file
30
+
31
+ ```bash
32
+ x2fromx scan ./my_existing_project -o structure.txt --print
33
+ ```
34
+
35
+ πŸ—οΈ Build a project from a tree file
36
+ ```
37
+ x2fromx build structure.txt -n my_new_project --overwrite
38
+ ```
39
+
40
+ Available flags:
41
+
42
+ | Flag | Description |
43
+ | :--- | :--- |
44
+ | scan <dir> | Path to the folder to analyze |
45
+ | build <file> | Path to the .txt tree file |
46
+ | -o, --output | Output filename (default: project_structure.txt) |
47
+ | -n, --name | Root project name for build (default: new_project) |
48
+ | --print | Print the tree in the terminal after saving |
49
+ | --overwrite | Force overwrite if the target folder already exists |
50
+
51
+
52
+ πŸ€– AI Workflow (The Killer Feature)
53
+
54
+ 1. Extract context: x2fromx scan ./legacy_app -o context.txt
55
+ 2. Ask an LLM: "Here is my project structure. Refactor it to add a /tests folder, split routes.py into a router package, and add a Dockerfile. Return the full tree."
56
+ 3. Save the response: Paste the LLM's output into refactored.txt
57
+ 4. Scaffold instantly: x2fromx build refactored.txt -n app_v2
58
+ 5. Start coding: Your IDE opens a ready-to-use structure with placeholders.
59
+
60
+
61
+ 🐍 Python API
62
+
63
+ ```python
64
+ from x2fromx import DirectoryScanner, ProjectBuilder
65
+
66
+ # Scan
67
+ scanner = DirectoryScanner("./src", "tree.txt")
68
+ scanner.save()
69
+
70
+ # Build
71
+ builder = ProjectBuilder("tree.txt", "my_project")
72
+ count, root = builder.build(overwrite=True)
73
+ print(f"Created {count} items in {root}")
74
+
75
+ ```
76
+
77
+ πŸ“ Example Output
78
+ ```bash
79
+ <!-- TREEVIEW START -->
80
+ my_project/
81
+ β”‚
82
+ β”œβ”€β”€ πŸ“ src/
83
+ β”‚ β”œβ”€β”€ πŸ“ api/
84
+ β”‚ β”‚ β”œβ”€β”€ πŸ“„ routes.py # Endpoint API
85
+ β”‚ β”‚ └── πŸ“„ schemas.py
86
+ β”‚ └── πŸ“„ main.py # TODO: Implement logic
87
+ β”œβ”€β”€ πŸ“ tests/
88
+ β”‚ └── πŸ“„ test_api.py # Tests unitaires
89
+ β”œβ”€β”€ πŸ“„ README.md # Documentation
90
+ └── πŸ“„ requirements.txt # DΓ©pendances Python
91
+ ```
92
+ <!-- TREEVIEW END -->
93
+
94
+ 🀝 Contributing
95
+
96
+ Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
97
+ 1. Fork the repo
98
+ 2. Create your feature branch (git checkout -b feature/amazing-feature)
99
+ 3. Commit your changes (git commit -m 'Add amazing feature')
100
+ 4. Push to the branch (git push origin feature/amazing-feature)
101
+ 5. Open a Pull Request
102
+
103
+
104
+ πŸ“œ License
105
+
106
+ Distributed under the MIT License. See LICENSE for more information.
107
+
108
+ ---
109
+
110
+ ## πŸ’™ Support
111
+
112
+ If you use and value this tool, consider supporting its development:
113
+ [![Sponsor](https://img.shields.io/badge/sponsor-erabytse-181717?logo=github)](https://github.com/sponsors/takouzlo)
114
+
115
+
116
+ [![Python](https://img.shields.io/badge/Python-3.13-blue)](https://python.org)
117
+ [![Flask](https://img.shields.io/badge/Flask-Web_Framework-green)](https://flask.palletsprojects.com)
118
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://typescriptlang.org)
119
+ [![AI](https://img.shields.io/badge/AI-ML-orange)](https://pytorch.org)
120
+ [![Founder](https://img.shields.io/badge/Founder-erabytse-purple)](https://github.com/erabytse)
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "x2fromx"
7
+ version = "0.1.0"
8
+ description = "CLI & library to convert project directories ↔ text trees. Ideal for AI scaffolding & codebase analysis."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [{name = "TonNom", email = "ton.email@example.com"}]
12
+ classifiers = [
13
+ "Programming Language :: Python :: 3",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+ requires-python = ">=3.8"
18
+ dependencies = []
19
+
20
+ [project.scripts]
21
+ x2fromx = "x2fromx.cli:main"
22
+
23
+ [project.urls]
24
+ "Homepage" = "https://github.com/TonUser/x2fromx"
25
+ "Bug Tracker" = "https://github.com/TonUser/x2fromx/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """x2fromx - Convert project structures between directories and text trees."""
2
+ __version__ = "0.1.0"
3
+ from .scanner import DirectoryScanner
4
+ from .builder import ProjectBuilder
5
+
6
+ __all__ = ["DirectoryScanner", "ProjectBuilder"]
@@ -0,0 +1,74 @@
1
+ import os
2
+ import re
3
+ import shutil
4
+ from pathlib import Path
5
+
6
+ class ProjectBuilder:
7
+ def __init__(self, structure_file: str, root_name: str = None):
8
+ self.structure_file = Path(structure_file)
9
+ self.root_name = root_name
10
+ if not self.structure_file.exists():
11
+ raise FileNotFoundError(f"❌ Le fichier '{structure_file}' est introuvable.")
12
+
13
+ def parse_structure(self) -> list[tuple[str, bool]]:
14
+ paths = []
15
+ with open(self.structure_file, 'r', encoding='utf-8') as f:
16
+ lines = f.readlines()
17
+ path_stack = []
18
+ for line in lines:
19
+ raw_line = line.rstrip('\n')
20
+ if not raw_line.strip(): continue
21
+ clean_for_indent = re.sub(r'[β”‚β”œβ””β”€]', ' ', raw_line)
22
+ leading_spaces = len(clean_for_indent) - len(clean_for_indent.lstrip())
23
+ depth = leading_spaces // 4
24
+ cleaned = re.sub(r'.*?[β”œβ””][─]+\s*', '', raw_line)
25
+ cleaned = re.sub(r'^[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F700-\U0001F77F\U0001F780-\U0001F7FF\U0001F800-\U0001F8FF\U0001F900-\U0001F9FF\U0001FA00-\U0001FA6F\U0001FA70-\U0001FAFF\U0000FE00-\U0000FEFF]+', '', cleaned)
26
+ cleaned = cleaned.split('#')[0].strip()
27
+ if not cleaned: continue
28
+ is_dir = cleaned.endswith('/')
29
+ item_name = cleaned[:-1] if is_dir else cleaned
30
+ while path_stack and path_stack[-1][0] >= depth:
31
+ path_stack.pop()
32
+ current_parts = [p[1] for p in path_stack] + [item_name]
33
+ relative_path = os.path.join(*current_parts)
34
+ paths.append((relative_path, is_dir))
35
+ if is_dir:
36
+ path_stack.append((depth, item_name))
37
+ return paths
38
+
39
+ def build(self, overwrite: bool = False) -> tuple[int, Path]:
40
+ project_root = Path(self.root_name) if self.root_name else Path("new_project")
41
+ if project_root.exists():
42
+ if overwrite:
43
+ shutil.rmtree(project_root)
44
+ else:
45
+ raise FileExistsError(f"⚠️ Le dossier '{project_root}' existe déjà. Ajoute `--overwrite` pour forcer.")
46
+ project_root.mkdir(parents=True)
47
+ created_count = 0
48
+ paths = self.parse_structure()
49
+ for rel_path, is_dir in paths:
50
+ full_path = project_root / rel_path
51
+ if is_dir:
52
+ full_path.mkdir(parents=True, exist_ok=True)
53
+ (full_path / ".gitkeep").touch(exist_ok=True)
54
+ created_count += 1
55
+ else:
56
+ full_path.parent.mkdir(parents=True, exist_ok=True)
57
+ if not full_path.exists():
58
+ full_path.touch()
59
+ self._seed_content(full_path)
60
+ created_count += 1
61
+ return created_count, project_root
62
+
63
+ def _seed_content(self, file_path: Path):
64
+ ext = file_path.suffix.lower()
65
+ content = ""
66
+ if ext == '.py': content = "# TODO: Implement logic\n\ndef main():\n pass\n\nif __name__ == '__main__':\n main()\n"
67
+ elif ext == '.html': content = f"<!DOCTYPE html>\n<html>\n<head><title>{file_path.name}</title></head>\n<body></body>\n</html>"
68
+ elif ext == '.md': content = f"# {file_path.stem}\n\nDocumentation to be written.\n"
69
+ elif ext == '.js': content = f"// TODO: Implement JS logic for {file_path.name}\n"
70
+ elif ext == '.css': content = f"/* Styles for {file_path.name} */\n"
71
+ elif ext in ['.conf', '.sh', '.service']: content = f"# Configuration placeholder for {file_path.name}\n"
72
+ if content:
73
+ with open(file_path, 'w', encoding='utf-8') as f:
74
+ f.write(content)
@@ -0,0 +1,47 @@
1
+ import argparse
2
+ import sys
3
+ from .scanner import DirectoryScanner
4
+ from .builder import ProjectBuilder
5
+
6
+ def cmd_scan(args):
7
+ try:
8
+ scanner = DirectoryScanner(args.directory, args.output)
9
+ _, count = scanner.save()
10
+ print(f"βœ… {count} Γ©lΓ©ments trouvΓ©s. Arborescence sauvegardΓ©e dans '{args.output}'.")
11
+ if args.print_tree:
12
+ with open(args.output, 'r', encoding='utf-8') as f:
13
+ print("\n" + f.read())
14
+ except Exception as e:
15
+ print(f"❌ Erreur: {e}", file=sys.stderr)
16
+ sys.exit(1)
17
+
18
+ def cmd_build(args):
19
+ try:
20
+ builder = ProjectBuilder(args.structure_file, args.project_name)
21
+ count, root = builder.build(overwrite=args.overwrite)
22
+ print(f"βœ… {count} Γ©lΓ©ments créés dans '{root}'.")
23
+ except Exception as e:
24
+ print(f"❌ Erreur: {e}", file=sys.stderr)
25
+ sys.exit(1)
26
+
27
+ def main():
28
+ parser = argparse.ArgumentParser(prog="x2fromx", description="Outils pour convertir des arborescences de projets en texte et vice-versa.")
29
+ subparsers = parser.add_subparsers(dest="command", help="Commandes disponibles")
30
+
31
+ p_scan = subparsers.add_parser("scan", help="Scan un dossier et génère un fichier texte arborescent.")
32
+ p_scan.add_argument("directory", help="Chemin vers le dossier Γ  scanner.")
33
+ p_scan.add_argument("-o", "--output", default="project_structure.txt", help="Fichier de sortie (dΓ©faut: project_structure.txt)")
34
+ p_scan.add_argument("--print", dest="print_tree", action="store_true", help="Affiche l'arbre dans la console.")
35
+
36
+ p_build = subparsers.add_parser("build", help="CrΓ©e une arborescence Γ  partir d'un fichier texte.")
37
+ p_build.add_argument("structure_file", help="Fichier texte contenant l'arborescence.")
38
+ p_build.add_argument("-n", "--name", dest="project_name", default=None, help="Nom du dossier racine (dΓ©faut: new_project)")
39
+ p_build.add_argument("--overwrite", action="store_true", help="Γ‰crase le dossier s'il existe dΓ©jΓ .")
40
+
41
+ args = parser.parse_args()
42
+ if args.command == "scan": cmd_scan(args)
43
+ elif args.command == "build": cmd_build(args)
44
+ else: parser.print_help(); sys.exit(1)
45
+
46
+ if __name__ == "__main__":
47
+ main()
@@ -0,0 +1,89 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ class DirectoryScanner:
6
+ def __init__(self, root_path: str, output_file: str = "project_structure.txt"):
7
+ self.root_path = Path(root_path).resolve()
8
+ self.output_file = output_file
9
+ self.ignore_dirs = {
10
+ '.git', '__pycache__', 'node_modules', '.venv', 'venv',
11
+ 'env', '.idea', '.vscode', 'build', 'dist', '.pytest_cache',
12
+ 'migrations', 'static_collected'
13
+ }
14
+ self.ignore_extensions = {
15
+ '.pyc', '.pyo', '.so', '.dll', '.exe', '.log', '.db', '.sqlite',
16
+ '.jpg', '.jpeg', '.png', '.gif', '.ico', '.woff', '.woff2', '.ttf',
17
+ '.DS_Store', '.env'
18
+ }
19
+
20
+ def scan(self) -> list:
21
+ structure = []
22
+ if not self.root_path.exists():
23
+ raise FileNotFoundError(f"❌ Le dossier '{self.root_path}' n'existe pas.")
24
+
25
+ for dirpath, dirnames, filenames in os.walk(self.root_path):
26
+ dirnames[:] = [d for d in dirnames if d not in self.ignore_dirs and not d.startswith('.')]
27
+ dirnames.sort()
28
+ current_path = Path(dirpath)
29
+ relative_depth = len(current_path.relative_to(self.root_path).parts)
30
+
31
+ if current_path != self.root_path:
32
+ structure.append({'type': 'dir', 'name': current_path.name, 'depth': relative_depth, 'path': current_path})
33
+
34
+ valid_files = [f for f in filenames if not f.startswith('.') and Path(f).suffix.lower() not in self.ignore_extensions]
35
+ valid_files.sort()
36
+ for filename in valid_files:
37
+ file_path = current_path / filename
38
+ structure.append({'type': 'file', 'name': filename, 'depth': relative_depth + 1, 'path': file_path, 'ext': Path(filename).suffix.lower()})
39
+ return structure
40
+
41
+ def generate_tree_text(self, structure: list) -> str:
42
+ if not structure: return ""
43
+ lines = [f"{self.root_path.name}/", "β”‚"]
44
+ is_last_map = {}
45
+ for i, item in enumerate(structure):
46
+ next_item = structure[i+1] if i+1 < len(structure) else None
47
+ is_last_map[i] = next_item['depth'] <= item['depth'] if next_item else True
48
+
49
+ for i, item in enumerate(structure):
50
+ depth, is_last = item['depth'], is_last_map[i]
51
+ is_dir = item['type'] == 'dir'
52
+ prefix = ""
53
+ for d in range(1, depth):
54
+ parent_is_last = self._check_parent_status(structure, i, d)
55
+ prefix += " " if parent_is_last else "β”‚ "
56
+ branch = "└── " if is_last else "β”œβ”€β”€ "
57
+ icon = "πŸ“ " if is_dir else "πŸ“„ "
58
+ name = f"{item['name']}/" if is_dir else item['name']
59
+ comment = ""
60
+ if not is_dir:
61
+ ext = item.get('ext', '')
62
+ if name == "requirements.txt": comment = " # DΓ©pendances Python"
63
+ elif name == "wsgi.py": comment = " # Point d'entrΓ©e Apache"
64
+ elif name == "manager.py": comment = " # CΕ“ur logique"
65
+ elif name == "routes.py": comment = " # Routes Web"
66
+ elif ext == ".md": comment = " # Documentation"
67
+ elif ext == ".html": comment = " # Template UI"
68
+ elif ext == ".js": comment = " # Logique Frontend"
69
+ elif ext == ".css": comment = " # Styles"
70
+ elif ext == ".py":
71
+ if "test" in item['path'].parts: comment = " # Tests unitaires"
72
+ elif "api" in item['path'].parts: comment = " # Endpoint API"
73
+ lines.append(f"{prefix}{branch}{icon}{name}{comment}")
74
+ return "\n".join(lines)
75
+
76
+ def _check_parent_status(self, structure, current_index, target_depth):
77
+ for i in range(current_index - 1, -1, -1):
78
+ if structure[i]['depth'] == target_depth:
79
+ next_item = structure[i+1] if i+1 < len(structure) else None
80
+ return next_item['depth'] <= target_depth if next_item else True
81
+ return False
82
+
83
+ def save(self) -> tuple[str, int]:
84
+ structure = self.scan()
85
+ tree_content = self.generate_tree_text(structure)
86
+ full_content = f"# Structure gΓ©nΓ©rΓ©e automatiquement pour {self.root_path.name}\n# Date: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n{tree_content}\n"
87
+ with open(self.output_file, 'w', encoding='utf-8') as f:
88
+ f.write(full_content)
89
+ return full_content, len(structure)
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: x2fromx
3
+ Version: 0.1.0
4
+ Summary: CLI & library to convert project directories ↔ text trees. Ideal for AI scaffolding & codebase analysis.
5
+ Author-email: TonNom <ton.email@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/TonUser/x2fromx
8
+ Project-URL: Bug Tracker, https://github.com/TonUser/x2fromx/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # πŸŒ²β†”οΈπŸ“ x2fromx
18
+
19
+ [![PyPI version](https://badge.fury.io/py/x2fromx.svg)](https://pypi.org/project/x2fromx/)
20
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
+ [![Downloads](https://static.pepy.tech/badge/x2fromx)](https://pepy.tech/project/x2fromx)
23
+
24
+ > **Convert project directories ↔ text trees in one command.**
25
+ > Built for developers & AI enthusiasts who need to scaffold projects from LLM outputs or extract codebase structure for context analysis.
26
+
27
+ ---
28
+
29
+ ## πŸš€ Why `x2fromx`?
30
+ - πŸ€– **AI-Ready**: Paste a `tree.txt` into ChatGPT/Claude, get a modified architecture back, and rebuild it instantly.
31
+ - ⚑ **Zero Dependencies**: Pure Python standard library. Works everywhere, instantly.
32
+ - 🧹 **Smart Filtering**: Automatically ignores `.git`, `node_modules`, `__pycache__`, binaries, and images.
33
+ - 🌱 **Auto-Seeding**: Creates boilerplate content (`.py`, `.html`, `.js`, etc.) so your IDE doesn't complain.
34
+ - πŸ–₯️ **CLI & Library**: Use it in your terminal or import it directly into your Python scripts.
35
+
36
+ ---
37
+
38
+ ## πŸ“¦ Installation
39
+
40
+ ```bash
41
+ pip install x2fromx
42
+ ```
43
+
44
+ πŸ› οΈ CLI Usage
45
+ πŸ” Scan a directory β†’ generate a tree file
46
+
47
+ ```bash
48
+ x2fromx scan ./my_existing_project -o structure.txt --print
49
+ ```
50
+
51
+ πŸ—οΈ Build a project from a tree file
52
+ ```
53
+ x2fromx build structure.txt -n my_new_project --overwrite
54
+ ```
55
+
56
+ Available flags:
57
+
58
+ | Flag | Description |
59
+ | :--- | :--- |
60
+ | scan <dir> | Path to the folder to analyze |
61
+ | build <file> | Path to the .txt tree file |
62
+ | -o, --output | Output filename (default: project_structure.txt) |
63
+ | -n, --name | Root project name for build (default: new_project) |
64
+ | --print | Print the tree in the terminal after saving |
65
+ | --overwrite | Force overwrite if the target folder already exists |
66
+
67
+
68
+ πŸ€– AI Workflow (The Killer Feature)
69
+
70
+ 1. Extract context: x2fromx scan ./legacy_app -o context.txt
71
+ 2. Ask an LLM: "Here is my project structure. Refactor it to add a /tests folder, split routes.py into a router package, and add a Dockerfile. Return the full tree."
72
+ 3. Save the response: Paste the LLM's output into refactored.txt
73
+ 4. Scaffold instantly: x2fromx build refactored.txt -n app_v2
74
+ 5. Start coding: Your IDE opens a ready-to-use structure with placeholders.
75
+
76
+
77
+ 🐍 Python API
78
+
79
+ ```python
80
+ from x2fromx import DirectoryScanner, ProjectBuilder
81
+
82
+ # Scan
83
+ scanner = DirectoryScanner("./src", "tree.txt")
84
+ scanner.save()
85
+
86
+ # Build
87
+ builder = ProjectBuilder("tree.txt", "my_project")
88
+ count, root = builder.build(overwrite=True)
89
+ print(f"Created {count} items in {root}")
90
+
91
+ ```
92
+
93
+ πŸ“ Example Output
94
+ ```bash
95
+ <!-- TREEVIEW START -->
96
+ my_project/
97
+ β”‚
98
+ β”œβ”€β”€ πŸ“ src/
99
+ β”‚ β”œβ”€β”€ πŸ“ api/
100
+ β”‚ β”‚ β”œβ”€β”€ πŸ“„ routes.py # Endpoint API
101
+ β”‚ β”‚ └── πŸ“„ schemas.py
102
+ β”‚ └── πŸ“„ main.py # TODO: Implement logic
103
+ β”œβ”€β”€ πŸ“ tests/
104
+ β”‚ └── πŸ“„ test_api.py # Tests unitaires
105
+ β”œβ”€β”€ πŸ“„ README.md # Documentation
106
+ └── πŸ“„ requirements.txt # DΓ©pendances Python
107
+ ```
108
+ <!-- TREEVIEW END -->
109
+
110
+ 🀝 Contributing
111
+
112
+ Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
113
+ 1. Fork the repo
114
+ 2. Create your feature branch (git checkout -b feature/amazing-feature)
115
+ 3. Commit your changes (git commit -m 'Add amazing feature')
116
+ 4. Push to the branch (git push origin feature/amazing-feature)
117
+ 5. Open a Pull Request
118
+
119
+
120
+ πŸ“œ License
121
+
122
+ Distributed under the MIT License. See LICENSE for more information.
123
+
124
+ ---
125
+
126
+ ## πŸ’™ Support
127
+
128
+ If you use and value this tool, consider supporting its development:
129
+ [![Sponsor](https://img.shields.io/badge/sponsor-erabytse-181717?logo=github)](https://github.com/sponsors/takouzlo)
130
+
131
+
132
+ [![Python](https://img.shields.io/badge/Python-3.13-blue)](https://python.org)
133
+ [![Flask](https://img.shields.io/badge/Flask-Web_Framework-green)](https://flask.palletsprojects.com)
134
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://typescriptlang.org)
135
+ [![AI](https://img.shields.io/badge/AI-ML-orange)](https://pytorch.org)
136
+ [![Founder](https://img.shields.io/badge/Founder-erabytse-purple)](https://github.com/erabytse)
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/x2fromx/__init__.py
5
+ src/x2fromx/builder.py
6
+ src/x2fromx/cli.py
7
+ src/x2fromx/scanner.py
8
+ src/x2fromx.egg-info/PKG-INFO
9
+ src/x2fromx.egg-info/SOURCES.txt
10
+ src/x2fromx.egg-info/dependency_links.txt
11
+ src/x2fromx.egg-info/entry_points.txt
12
+ src/x2fromx.egg-info/top_level.txt
13
+ tests/test_x2fromx.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ x2fromx = x2fromx.cli:main
@@ -0,0 +1 @@
1
+ x2fromx
File without changes