codetoprompt 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Yash Bhaskar
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,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: codetoprompt
3
+ Version: 0.1.0
4
+ Summary: Convert your codebase into a single LLM prompt
5
+ Author-email: Yash Bhaskar <yash9439@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yash9439/codetoprompt
8
+ Project-URL: Documentation, https://github.com/yash9439/codetoprompt#readme
9
+ Project-URL: Repository, https://github.com/yash9439/codetoprompt
10
+ Project-URL: Issues, https://github.com/yash9439/codetoprompt/issues
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Development Status :: 4 - Beta
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Software Development :: Version Control :: Git
21
+ Classifier: Topic :: Text Processing :: General
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pygit2>=1.12.0
26
+ Requires-Dist: tiktoken>=0.5.0
27
+ Requires-Dist: pyperclip>=1.8.2
28
+ Requires-Dist: jinja2>=3.1.0
29
+ Requires-Dist: pathspec>=0.11.0
30
+ Requires-Dist: rich>=13.0.0
31
+ Requires-Dist: typing-extensions>=4.0.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: black>=23.0.0; extra == "dev"
35
+ Requires-Dist: isort>=5.0.0; extra == "dev"
36
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
37
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # CodeToPrompt
41
+
42
+ Convert your codebase into a single LLM prompt!
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ # Install the Python package
48
+ pip install codetoprompt
49
+
50
+ # System Dependencies
51
+ # For clipboard support on Linux:
52
+ # - X11: Install xclip
53
+ # sudo apt-get install xclip # Debian/Ubuntu
54
+ # sudo dnf install xclip # Fedora
55
+ # sudo pacman -S xclip # Arch Linux
56
+ # - Wayland: Install wl-clipboard
57
+ # sudo apt-get install wl-clipboard # Debian/Ubuntu
58
+ # sudo dnf install wl-clipboard # Fedora
59
+ # sudo pacman -S wl-clipboard # Arch Linux
60
+ ```
61
+
62
+ ## Quick Start
63
+
64
+ ```bash
65
+ # Basic usage
66
+ codetoprompt /path/to/your/codebase
67
+
68
+ # Save to file
69
+ codetoprompt /path/to/your/codebase --output prompt.txt
70
+
71
+ # Include specific file patterns
72
+ codetoprompt /path/to/your/codebase --include "*.py,*.js"
73
+
74
+ # Exclude specific file patterns
75
+ codetoprompt /path/to/your/codebase --exclude "*.pyc,__pycache__"
76
+ ```
77
+
78
+ ## Features
79
+
80
+ - **Automatic Code Processing**: Convert codebases of any size into readable, formatted prompts
81
+ - **Smart Filtering**: Include/exclude files using glob patterns and respect `.gitignore` rules
82
+ - **Flexible Templating**: Customize prompts with Jinja2 templates for different use cases
83
+ - **Token Tracking**: Track token usage to stay within LLM context limits
84
+ - **Git Integration**: Include diffs, logs, and branch comparisons in your prompts
85
+ - **Developer Experience**: Automatic clipboard copy, line numbers, and file organization options
86
+
87
+ ## Python API
88
+
89
+ ```python
90
+ from codetoprompt import CodeToPrompt
91
+
92
+ # Initialize the processor
93
+ processor = CodeToPrompt(
94
+ root_dir="/path/to/your/codebase",
95
+ include_patterns=["*.py", "*.js"],
96
+ exclude_patterns=["*.pyc", "__pycache__"]
97
+ )
98
+
99
+ # Generate the prompt
100
+ prompt = processor.generate_prompt()
101
+
102
+ # Save to file
103
+ processor.save_to_file("prompt.txt")
104
+
105
+ # Get token count
106
+ token_count = processor.get_token_count()
107
+ ```
108
+
109
+ ## License
110
+
111
+ MIT License - see LICENSE file for details
@@ -0,0 +1,72 @@
1
+ # CodeToPrompt
2
+
3
+ Convert your codebase into a single LLM prompt!
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Install the Python package
9
+ pip install codetoprompt
10
+
11
+ # System Dependencies
12
+ # For clipboard support on Linux:
13
+ # - X11: Install xclip
14
+ # sudo apt-get install xclip # Debian/Ubuntu
15
+ # sudo dnf install xclip # Fedora
16
+ # sudo pacman -S xclip # Arch Linux
17
+ # - Wayland: Install wl-clipboard
18
+ # sudo apt-get install wl-clipboard # Debian/Ubuntu
19
+ # sudo dnf install wl-clipboard # Fedora
20
+ # sudo pacman -S wl-clipboard # Arch Linux
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # Basic usage
27
+ codetoprompt /path/to/your/codebase
28
+
29
+ # Save to file
30
+ codetoprompt /path/to/your/codebase --output prompt.txt
31
+
32
+ # Include specific file patterns
33
+ codetoprompt /path/to/your/codebase --include "*.py,*.js"
34
+
35
+ # Exclude specific file patterns
36
+ codetoprompt /path/to/your/codebase --exclude "*.pyc,__pycache__"
37
+ ```
38
+
39
+ ## Features
40
+
41
+ - **Automatic Code Processing**: Convert codebases of any size into readable, formatted prompts
42
+ - **Smart Filtering**: Include/exclude files using glob patterns and respect `.gitignore` rules
43
+ - **Flexible Templating**: Customize prompts with Jinja2 templates for different use cases
44
+ - **Token Tracking**: Track token usage to stay within LLM context limits
45
+ - **Git Integration**: Include diffs, logs, and branch comparisons in your prompts
46
+ - **Developer Experience**: Automatic clipboard copy, line numbers, and file organization options
47
+
48
+ ## Python API
49
+
50
+ ```python
51
+ from codetoprompt import CodeToPrompt
52
+
53
+ # Initialize the processor
54
+ processor = CodeToPrompt(
55
+ root_dir="/path/to/your/codebase",
56
+ include_patterns=["*.py", "*.js"],
57
+ exclude_patterns=["*.pyc", "__pycache__"]
58
+ )
59
+
60
+ # Generate the prompt
61
+ prompt = processor.generate_prompt()
62
+
63
+ # Save to file
64
+ processor.save_to_file("prompt.txt")
65
+
66
+ # Get token count
67
+ token_count = processor.get_token_count()
68
+ ```
69
+
70
+ ## License
71
+
72
+ MIT License - see LICENSE file for details
@@ -0,0 +1,4 @@
1
+ from .core import CodeToPrompt
2
+ from .version import __version__
3
+
4
+ __all__ = ['CodeToPrompt', '__version__']
@@ -0,0 +1,119 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
6
+ from rich import print as rprint
7
+ from .core import CodeToPrompt
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(description="Convert your codebase into a single LLM prompt")
11
+ parser.add_argument("root_dir", help="Root directory of the codebase")
12
+ parser.add_argument("--output", "-o", help="Output file path")
13
+ parser.add_argument("--include", "-i", help="Comma-separated list of file patterns to include")
14
+ parser.add_argument("--exclude", "-e", help="Comma-separated list of file patterns to exclude")
15
+ parser.add_argument("--no-gitignore", action="store_true", help="Don't respect .gitignore rules")
16
+ parser.add_argument("--no-line-numbers", action="store_true", help="Don't show line numbers")
17
+ parser.add_argument("--max-tokens", type=int, help="Maximum number of tokens in the prompt")
18
+ parser.add_argument("--no-copy", action="store_true", help="Don't copy prompt to clipboard")
19
+
20
+ args = parser.parse_args()
21
+ console = Console()
22
+
23
+ try:
24
+ # Validate root directory
25
+ root_dir = Path(args.root_dir)
26
+ if not root_dir.exists():
27
+ console.print(f"[red]Error: Directory '{args.root_dir}' does not exist[/red]")
28
+ return 1
29
+ if not root_dir.is_dir():
30
+ console.print(f"[red]Error: '{args.root_dir}' is not a directory[/red]")
31
+ return 1
32
+
33
+ # Process include/exclude patterns
34
+ include_patterns = args.include.split(",") if args.include else None
35
+ exclude_patterns = args.exclude.split(",") if args.exclude else None
36
+
37
+ # Print configuration
38
+ console.print(Panel.fit(
39
+ f"[bold]Configuration:[/bold]\n"
40
+ f"Root Directory: {root_dir}\n"
41
+ f"Include Patterns: {include_patterns or ['*']}\n"
42
+ f"Exclude Patterns: {exclude_patterns or []}\n"
43
+ f"Respect .gitignore: {not args.no_gitignore}\n"
44
+ f"Show Line Numbers: {not args.no_line_numbers}\n"
45
+ f"Max Tokens: {args.max_tokens or 'Unlimited'}\n"
46
+ f"Copy to Clipboard: {not args.no_copy}",
47
+ title="CodeToPrompt",
48
+ border_style="blue"
49
+ ))
50
+
51
+ # Initialize processor
52
+ processor = CodeToPrompt(
53
+ root_dir=str(root_dir),
54
+ include_patterns=include_patterns,
55
+ exclude_patterns=exclude_patterns,
56
+ respect_gitignore=not args.no_gitignore,
57
+ show_line_numbers=not args.no_line_numbers,
58
+ max_tokens=args.max_tokens
59
+ )
60
+
61
+ # Check clipboard requirements before starting progress bars
62
+ clipboard_success = False
63
+ if not args.no_copy:
64
+ clipboard_success = processor.copy_to_clipboard()
65
+
66
+ with Progress(
67
+ SpinnerColumn(),
68
+ TextColumn("[progress.description]{task.description}"),
69
+ BarColumn(),
70
+ TimeElapsedColumn(),
71
+ console=console,
72
+ ) as progress:
73
+ # Add all tasks upfront
74
+ git_task = progress.add_task("Getting git information...", total=None)
75
+ tree_task = progress.add_task("Building file tree...", total=None)
76
+ files_task = progress.add_task("Finding files...", total=None)
77
+ process_task = progress.add_task("Processing files...", total=None)
78
+ save_task = progress.add_task("Saving to file...", total=None) if args.output else None
79
+
80
+ # Generate prompt
81
+ prompt = processor.generate_prompt()
82
+
83
+ # Update progress for completed tasks
84
+ progress.update(git_task, completed=True)
85
+ progress.update(tree_task, completed=True)
86
+ progress.update(files_task, completed=True)
87
+ progress.update(process_task, completed=True)
88
+
89
+ # # Print the generated prompt to the console
90
+ # console.print("\n[bold]Generated Prompt:[/bold]\n")
91
+ # console.print(prompt)
92
+
93
+ # Save to file if specified
94
+ if args.output:
95
+ processor.save_to_file(args.output)
96
+ progress.update(save_task, completed=True)
97
+ console.print(f"[green]✓ Prompt saved to {args.output}[/green]")
98
+
99
+ # Print summary
100
+ console.print(Panel.fit(
101
+ f"[bold]Summary:[/bold]\n"
102
+ f"Total Tokens: {len(processor.tokenizer.encode(prompt)):,}\n"
103
+ f"Output File: {args.output or 'None'}\n"
104
+ f"Copied to Clipboard: {'Yes' if clipboard_success else 'No'}",
105
+ title="Processing Complete",
106
+ border_style="green"
107
+ ))
108
+
109
+ except KeyboardInterrupt:
110
+ console.print("\n[yellow]Operation cancelled by user[/yellow]")
111
+ return 1
112
+ except Exception as e:
113
+ console.print(f"[red]Error: {str(e)}[/red]")
114
+ return 1
115
+
116
+ return 0
117
+
118
+ if __name__ == "__main__":
119
+ exit(main())
@@ -0,0 +1,210 @@
1
+ import os
2
+ import pygit2
3
+ import tiktoken
4
+ import pyperclip
5
+ from pathlib import Path
6
+ from typing import List, Optional, Set
7
+ from pathspec import PathSpec
8
+ from pathspec.patterns import GitWildMatchPattern
9
+ from rich.console import Console
10
+ from rich.tree import Tree
11
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
12
+ from concurrent.futures import ThreadPoolExecutor
13
+ import threading
14
+ import platform
15
+ import subprocess
16
+
17
+ class CodeToPrompt:
18
+ def __init__(
19
+ self,
20
+ root_dir: str,
21
+ include_patterns: Optional[List[str]] = None,
22
+ exclude_patterns: Optional[List[str]] = None,
23
+ respect_gitignore: bool = True,
24
+ show_line_numbers: bool = True,
25
+ max_tokens: Optional[int] = None,
26
+ ):
27
+ self.root_dir = Path(root_dir).resolve()
28
+ self.include_patterns = include_patterns or ["*"]
29
+ self.exclude_patterns = exclude_patterns or []
30
+ self.respect_gitignore = respect_gitignore
31
+ self.show_line_numbers = show_line_numbers
32
+ self.max_tokens = max_tokens
33
+ self.console = Console()
34
+
35
+ # Initialize git repository if exists
36
+ try:
37
+ self.repo = pygit2.Repository(str(self.root_dir))
38
+ except pygit2.GitError:
39
+ self.repo = None
40
+
41
+ # Initialize tokenizer
42
+ self.tokenizer = tiktoken.get_encoding("cl100k_base")
43
+
44
+ # Initialize pathspec for gitignore
45
+ if self.respect_gitignore and self.repo:
46
+ gitignore_path = self.root_dir / ".gitignore"
47
+ if gitignore_path.exists():
48
+ with open(gitignore_path) as f:
49
+ self.gitignore = PathSpec.from_lines(GitWildMatchPattern, f)
50
+ else:
51
+ self.gitignore = None
52
+ else:
53
+ self.gitignore = None
54
+
55
+ def _check_clipboard_requirements(self) -> bool:
56
+ """Check if clipboard requirements are met."""
57
+ if platform.system() == "Linux":
58
+ try:
59
+ # Check if xclip is installed
60
+ subprocess.run(["which", "xclip"], check=True, capture_output=True)
61
+ return True
62
+ except subprocess.CalledProcessError:
63
+ self.console.print("\n[yellow]⚠️ Clipboard functionality is disabled because xclip is not installed.[/yellow]")
64
+ self.console.print("\n[yellow]To enable clipboard support, run this command:[/yellow]")
65
+ self.console.print("\n[bold cyan]sudo apt-get install xclip[/bold cyan]")
66
+ self.console.print("\n[yellow]After installing xclip, run codetoprompt again to use clipboard functionality.[/yellow]\n")
67
+ return False
68
+ return True
69
+
70
+ def _should_include_file(self, file_path: Path) -> bool:
71
+ """Check if a file should be included based on patterns and gitignore."""
72
+ rel_path = file_path.relative_to(self.root_dir)
73
+
74
+ # Check include patterns
75
+ if not any(rel_path.match(pattern) for pattern in self.include_patterns):
76
+ return False
77
+
78
+ # Check exclude patterns
79
+ if any(rel_path.match(pattern) for pattern in self.exclude_patterns):
80
+ return False
81
+
82
+ # Check gitignore
83
+ if self.gitignore and self.gitignore.match_file(str(rel_path)):
84
+ return False
85
+
86
+ return True
87
+
88
+ def _get_file_content(self, file_path: Path) -> str:
89
+ """Read and format file content with optional line numbers."""
90
+ try:
91
+ with open(file_path, 'r', encoding='utf-8') as f:
92
+ content = f.read()
93
+
94
+ if self.show_line_numbers:
95
+ lines = content.splitlines()
96
+ numbered_lines = [f"{i+1:4d} | {line}" for i, line in enumerate(lines)]
97
+ return "\n".join(numbered_lines)
98
+ return content
99
+ except Exception as e:
100
+ return f"Error reading file: {str(e)}"
101
+
102
+ def _get_git_info(self) -> str:
103
+ """Get git repository information."""
104
+ if not self.repo:
105
+ return ""
106
+
107
+ info = []
108
+ try:
109
+ head = self.repo.head
110
+ info.append(f"Current branch: {head.name}")
111
+ info.append(f"Latest commit: {head.target.hex}")
112
+ info.append(f"Author: {head.peel().author.name} <{head.peel().author.email}>")
113
+ info.append(f"Date: {head.peel().author.time}")
114
+ info.append(f"Message: {head.peel().message.strip()}")
115
+ except Exception as e:
116
+ info.append(f"Error getting git info: {str(e)}")
117
+
118
+ return "\n".join(info)
119
+
120
+ def _process_file(self, file_path: Path) -> tuple:
121
+ """Process a single file and return its content."""
122
+ rel_path = file_path.relative_to(self.root_dir)
123
+ content = self._get_file_content(file_path)
124
+ return (rel_path, content)
125
+
126
+ def generate_prompt(self, progress: Optional[Progress] = None) -> str:
127
+ """Generate the prompt from the codebase."""
128
+ prompt_parts = []
129
+
130
+ # Add git information if available
131
+ git_info = self._get_git_info()
132
+ if git_info:
133
+ prompt_parts.append("=== Git Repository Information ===")
134
+ prompt_parts.append(git_info)
135
+ prompt_parts.append("")
136
+
137
+ # Add file tree
138
+ prompt_parts.append("=== File Structure ===")
139
+ tree = Tree("📁 " + str(self.root_dir.name))
140
+ self._build_tree(self.root_dir, tree)
141
+ # Convert tree to string using rich's console
142
+ console = Console(width=100) # Set a reasonable width
143
+ with console.capture() as capture:
144
+ console.print(tree)
145
+ prompt_parts.append(capture.get())
146
+ prompt_parts.append("")
147
+
148
+ # Get all files
149
+ files = list(self._get_files())
150
+
151
+ # Process files in parallel
152
+ prompt_parts.append("=== File Contents ===")
153
+
154
+ with ThreadPoolExecutor() as executor:
155
+ for rel_path, content in executor.map(self._process_file, files):
156
+ prompt_parts.append(f"\n=== {rel_path} ===")
157
+ prompt_parts.append(content)
158
+
159
+ prompt = "\n".join(prompt_parts)
160
+
161
+ # Check token limit
162
+ if self.max_tokens:
163
+ tokens = self.tokenizer.encode(prompt)
164
+ if len(tokens) > self.max_tokens:
165
+ self.console.print(f"[yellow]Warning: Prompt exceeds {self.max_tokens} tokens[/yellow]")
166
+
167
+ return prompt
168
+
169
+ def _build_tree(self, path: Path, tree: Tree) -> None:
170
+ """Build a tree representation of the codebase."""
171
+ for item in sorted(path.iterdir()):
172
+ if item.is_dir():
173
+ if self._should_include_file(item):
174
+ branch = tree.add(f"📁 {item.name}")
175
+ self._build_tree(item, branch)
176
+ else:
177
+ if self._should_include_file(item):
178
+ tree.add(f"📄 {item.name}")
179
+
180
+ def _get_files(self) -> Set[Path]:
181
+ """Get all files that should be included in the prompt."""
182
+ files = set()
183
+ for pattern in self.include_patterns:
184
+ files.update(self.root_dir.glob(f"**/{pattern}"))
185
+ return {f for f in files if f.is_file() and self._should_include_file(f)}
186
+
187
+ def save_to_file(self, output_path: str) -> None:
188
+ """Save the generated prompt to a file."""
189
+ prompt = self.generate_prompt()
190
+ with open(output_path, 'w', encoding='utf-8') as f:
191
+ f.write(prompt)
192
+
193
+ def copy_to_clipboard(self) -> bool:
194
+ """Copy the generated prompt to clipboard. Returns True if successful."""
195
+ # Check clipboard requirements first, before any progress bars
196
+ if not self._check_clipboard_requirements():
197
+ return False
198
+
199
+ prompt = self.generate_prompt()
200
+ try:
201
+ pyperclip.copy(prompt)
202
+ return True
203
+ except Exception as e:
204
+ self.console.print(f"[red]Error copying to clipboard: {str(e)}[/red]")
205
+ return False
206
+
207
+ def get_token_count(self) -> int:
208
+ """Get the number of tokens in the generated prompt."""
209
+ prompt = self.generate_prompt()
210
+ return len(self.tokenizer.encode(prompt))
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: codetoprompt
3
+ Version: 0.1.0
4
+ Summary: Convert your codebase into a single LLM prompt
5
+ Author-email: Yash Bhaskar <yash9439@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yash9439/codetoprompt
8
+ Project-URL: Documentation, https://github.com/yash9439/codetoprompt#readme
9
+ Project-URL: Repository, https://github.com/yash9439/codetoprompt
10
+ Project-URL: Issues, https://github.com/yash9439/codetoprompt/issues
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Development Status :: 4 - Beta
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Software Development :: Version Control :: Git
21
+ Classifier: Topic :: Text Processing :: General
22
+ Requires-Python: >=3.8
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pygit2>=1.12.0
26
+ Requires-Dist: tiktoken>=0.5.0
27
+ Requires-Dist: pyperclip>=1.8.2
28
+ Requires-Dist: jinja2>=3.1.0
29
+ Requires-Dist: pathspec>=0.11.0
30
+ Requires-Dist: rich>=13.0.0
31
+ Requires-Dist: typing-extensions>=4.0.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: black>=23.0.0; extra == "dev"
35
+ Requires-Dist: isort>=5.0.0; extra == "dev"
36
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
37
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # CodeToPrompt
41
+
42
+ Convert your codebase into a single LLM prompt!
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ # Install the Python package
48
+ pip install codetoprompt
49
+
50
+ # System Dependencies
51
+ # For clipboard support on Linux:
52
+ # - X11: Install xclip
53
+ # sudo apt-get install xclip # Debian/Ubuntu
54
+ # sudo dnf install xclip # Fedora
55
+ # sudo pacman -S xclip # Arch Linux
56
+ # - Wayland: Install wl-clipboard
57
+ # sudo apt-get install wl-clipboard # Debian/Ubuntu
58
+ # sudo dnf install wl-clipboard # Fedora
59
+ # sudo pacman -S wl-clipboard # Arch Linux
60
+ ```
61
+
62
+ ## Quick Start
63
+
64
+ ```bash
65
+ # Basic usage
66
+ codetoprompt /path/to/your/codebase
67
+
68
+ # Save to file
69
+ codetoprompt /path/to/your/codebase --output prompt.txt
70
+
71
+ # Include specific file patterns
72
+ codetoprompt /path/to/your/codebase --include "*.py,*.js"
73
+
74
+ # Exclude specific file patterns
75
+ codetoprompt /path/to/your/codebase --exclude "*.pyc,__pycache__"
76
+ ```
77
+
78
+ ## Features
79
+
80
+ - **Automatic Code Processing**: Convert codebases of any size into readable, formatted prompts
81
+ - **Smart Filtering**: Include/exclude files using glob patterns and respect `.gitignore` rules
82
+ - **Flexible Templating**: Customize prompts with Jinja2 templates for different use cases
83
+ - **Token Tracking**: Track token usage to stay within LLM context limits
84
+ - **Git Integration**: Include diffs, logs, and branch comparisons in your prompts
85
+ - **Developer Experience**: Automatic clipboard copy, line numbers, and file organization options
86
+
87
+ ## Python API
88
+
89
+ ```python
90
+ from codetoprompt import CodeToPrompt
91
+
92
+ # Initialize the processor
93
+ processor = CodeToPrompt(
94
+ root_dir="/path/to/your/codebase",
95
+ include_patterns=["*.py", "*.js"],
96
+ exclude_patterns=["*.pyc", "__pycache__"]
97
+ )
98
+
99
+ # Generate the prompt
100
+ prompt = processor.generate_prompt()
101
+
102
+ # Save to file
103
+ processor.save_to_file("prompt.txt")
104
+
105
+ # Get token count
106
+ token_count = processor.get_token_count()
107
+ ```
108
+
109
+ ## License
110
+
111
+ MIT License - see LICENSE file for details
@@ -0,0 +1,16 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ codetoprompt/__init__.py
6
+ codetoprompt/cli.py
7
+ codetoprompt/core.py
8
+ codetoprompt/version.py
9
+ codetoprompt.egg-info/PKG-INFO
10
+ codetoprompt.egg-info/SOURCES.txt
11
+ codetoprompt.egg-info/dependency_links.txt
12
+ codetoprompt.egg-info/entry_points.txt
13
+ codetoprompt.egg-info/requires.txt
14
+ codetoprompt.egg-info/top_level.txt
15
+ tests/test_cli.py
16
+ tests/test_core.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ codetoprompt = codetoprompt.cli:main
@@ -0,0 +1,14 @@
1
+ pygit2>=1.12.0
2
+ tiktoken>=0.5.0
3
+ pyperclip>=1.8.2
4
+ jinja2>=3.1.0
5
+ pathspec>=0.11.0
6
+ rich>=13.0.0
7
+ typing-extensions>=4.0.0
8
+
9
+ [dev]
10
+ pytest>=7.0.0
11
+ black>=23.0.0
12
+ isort>=5.0.0
13
+ mypy>=1.0.0
14
+ flake8>=6.0.0
@@ -0,0 +1 @@
1
+ codetoprompt
@@ -0,0 +1,54 @@
1
+ [build-system]
2
+ requires = ["setuptools>=65.0.0", "wheel>=0.40.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "codetoprompt"
7
+ version = "0.1.0"
8
+ description = "Convert your codebase into a single LLM prompt"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Yash Bhaskar", email = "yash9439@gmail.com" }
14
+ ]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Operating System :: OS Independent",
22
+ "Development Status :: 4 - Beta",
23
+ "Intended Audience :: Developers",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ "Topic :: Software Development :: Version Control :: Git",
26
+ "Topic :: Text Processing :: General",
27
+ ]
28
+ dependencies = [
29
+ "pygit2>=1.12.0",
30
+ "tiktoken>=0.5.0",
31
+ "pyperclip>=1.8.2",
32
+ "jinja2>=3.1.0",
33
+ "pathspec>=0.11.0",
34
+ "rich>=13.0.0",
35
+ "typing-extensions>=4.0.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ dev = [
40
+ "pytest>=7.0.0",
41
+ "black>=23.0.0",
42
+ "isort>=5.0.0",
43
+ "mypy>=1.0.0",
44
+ "flake8>=6.0.0",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/yash9439/codetoprompt"
49
+ Documentation = "https://github.com/yash9439/codetoprompt#readme"
50
+ Repository = "https://github.com/yash9439/codetoprompt"
51
+ Issues = "https://github.com/yash9439/codetoprompt/issues"
52
+
53
+ [project.scripts]
54
+ codetoprompt = "codetoprompt.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,94 @@
1
+ """Tests for the CLI functionality of codetoprompt."""
2
+ import os
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+ from codetoprompt.cli import main
8
+
9
+ @pytest.fixture
10
+ def temp_dir():
11
+ """Create a temporary directory with some test files."""
12
+ with tempfile.TemporaryDirectory() as tmpdir:
13
+ # Create some test files
14
+ test_dir = Path(tmpdir)
15
+ (test_dir / "test1.py").write_text("print('hello')\n")
16
+ (test_dir / "test2.py").write_text("def test():\n pass\n")
17
+ (test_dir / "test3.txt").write_text("Some text\n")
18
+ yield test_dir
19
+
20
+ def test_cli_basic_usage(temp_dir, capsys):
21
+ """Test basic CLI usage."""
22
+ # Mock sys.argv
23
+ import sys
24
+ sys.argv = ["codetoprompt", str(temp_dir)]
25
+
26
+ # Run CLI
27
+ result = main()
28
+ assert result == 0
29
+
30
+ # Check output
31
+ captured = capsys.readouterr()
32
+ assert "Configuration" in captured.out
33
+ assert "Processing Complete" in captured.out
34
+ assert "Total Tokens" in captured.out
35
+
36
+ def test_cli_with_output(temp_dir, capsys):
37
+ """Test CLI with output file."""
38
+ output_file = temp_dir / "output.txt"
39
+
40
+ # Mock sys.argv
41
+ import sys
42
+ sys.argv = ["codetoprompt", str(temp_dir), "--output", str(output_file)]
43
+
44
+ # Run CLI
45
+ result = main()
46
+ assert result == 0
47
+
48
+ # Check output file
49
+ assert output_file.exists()
50
+ content = output_file.read_text()
51
+ assert "test1.py" in content
52
+ assert "test2.py" in content
53
+
54
+ def test_cli_with_patterns(temp_dir, capsys):
55
+ """Test CLI with include/exclude patterns."""
56
+ # Mock sys.argv
57
+ import sys
58
+ sys.argv = [
59
+ "codetoprompt",
60
+ str(temp_dir),
61
+ "--include", "*.py",
62
+ "--exclude", "test2.py"
63
+ ]
64
+
65
+ # Run CLI
66
+ result = main()
67
+ assert result == 0
68
+
69
+ # Check output
70
+ captured = capsys.readouterr()
71
+ output = captured.out
72
+ # Only check the 'Generated Prompt' section
73
+ if "Generated Prompt:" in output:
74
+ prompt_section = output.split("Generated Prompt:", 1)[1]
75
+ else:
76
+ prompt_section = output
77
+ assert "test1.py" in prompt_section
78
+ assert "test2.py" not in prompt_section
79
+ assert "test3.txt" not in prompt_section
80
+
81
+ def test_cli_invalid_directory(capsys):
82
+ """Test CLI with invalid directory."""
83
+ # Mock sys.argv
84
+ import sys
85
+ sys.argv = ["codetoprompt", "/nonexistent/directory"]
86
+
87
+ # Run CLI
88
+ result = main()
89
+ assert result == 1
90
+
91
+ # Check error message
92
+ captured = capsys.readouterr()
93
+ assert "Error" in captured.out
94
+ assert "does not exist" in captured.out
@@ -0,0 +1,63 @@
1
+ """Tests for the core functionality of codetoprompt."""
2
+ import os
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+ from codetoprompt import CodeToPrompt
8
+
9
+ @pytest.fixture
10
+ def temp_dir():
11
+ """Create a temporary directory with some test files."""
12
+ with tempfile.TemporaryDirectory() as tmpdir:
13
+ # Create some test files
14
+ test_dir = Path(tmpdir)
15
+ (test_dir / "test1.py").write_text("print('hello')\n")
16
+ (test_dir / "test2.py").write_text("def test():\n pass\n")
17
+ (test_dir / "test3.txt").write_text("Some text\n")
18
+ yield test_dir
19
+
20
+ def test_initialization(temp_dir):
21
+ """Test CodeToPrompt initialization."""
22
+ processor = CodeToPrompt(str(temp_dir))
23
+ assert processor.root_dir == temp_dir
24
+ assert processor.include_patterns == ["*"]
25
+ assert processor.exclude_patterns == []
26
+ assert processor.respect_gitignore is True
27
+ assert processor.show_line_numbers is True
28
+
29
+ def test_file_inclusion(temp_dir):
30
+ """Test file inclusion patterns."""
31
+ processor = CodeToPrompt(
32
+ str(temp_dir),
33
+ include_patterns=["*.py"],
34
+ exclude_patterns=["test2.py"]
35
+ )
36
+ files = list(processor._get_files())
37
+ assert len(files) == 1
38
+ assert files[0].name == "test1.py"
39
+
40
+ def test_generate_prompt(temp_dir):
41
+ """Test prompt generation."""
42
+ processor = CodeToPrompt(str(temp_dir))
43
+ prompt = processor.generate_prompt()
44
+ assert "test1.py" in prompt
45
+ assert "test2.py" in prompt
46
+ assert "test3.txt" in prompt
47
+ assert "print('hello')" in prompt
48
+
49
+ def test_token_count(temp_dir):
50
+ """Test token counting."""
51
+ processor = CodeToPrompt(str(temp_dir))
52
+ count = processor.get_token_count()
53
+ assert count > 0
54
+
55
+ def test_save_to_file(temp_dir):
56
+ """Test saving prompt to file."""
57
+ processor = CodeToPrompt(str(temp_dir))
58
+ output_file = temp_dir / "output.txt"
59
+ processor.save_to_file(str(output_file))
60
+ assert output_file.exists()
61
+ content = output_file.read_text()
62
+ assert "test1.py" in content
63
+ assert "test2.py" in content