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.
- codetoprompt-0.1.0/LICENSE +21 -0
- codetoprompt-0.1.0/MANIFEST.in +3 -0
- codetoprompt-0.1.0/PKG-INFO +111 -0
- codetoprompt-0.1.0/README.md +72 -0
- codetoprompt-0.1.0/codetoprompt/__init__.py +4 -0
- codetoprompt-0.1.0/codetoprompt/cli.py +119 -0
- codetoprompt-0.1.0/codetoprompt/core.py +210 -0
- codetoprompt-0.1.0/codetoprompt/version.py +1 -0
- codetoprompt-0.1.0/codetoprompt.egg-info/PKG-INFO +111 -0
- codetoprompt-0.1.0/codetoprompt.egg-info/SOURCES.txt +16 -0
- codetoprompt-0.1.0/codetoprompt.egg-info/dependency_links.txt +1 -0
- codetoprompt-0.1.0/codetoprompt.egg-info/entry_points.txt +2 -0
- codetoprompt-0.1.0/codetoprompt.egg-info/requires.txt +14 -0
- codetoprompt-0.1.0/codetoprompt.egg-info/top_level.txt +1 -0
- codetoprompt-0.1.0/pyproject.toml +54 -0
- codetoprompt-0.1.0/setup.cfg +4 -0
- codetoprompt-0.1.0/tests/test_cli.py +94 -0
- codetoprompt-0.1.0/tests/test_core.py +63 -0
|
@@ -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,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,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 @@
|
|
|
1
|
+
|
|
@@ -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,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
|