janito 0.3.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,88 @@
1
+ # Python
2
+
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+ *.so
7
+ .Python
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+
24
+ # Virtual Environment
25
+ venv/
26
+ env/
27
+ ENV/
28
+
29
+ # IDE
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+ *.swo
34
+
35
+ # Testing
36
+ .coverage
37
+ .pytest_cache/
38
+ htmlcov/
39
+
40
+ # Documentation
41
+ docs/_build/
42
+ site/
43
+
44
+ # Misc
45
+ .DS_Store
46
+ # Janito
47
+ .janito/history/
48
+
49
+ # Poetry
50
+ poetry.lock
51
+ .poetry/
52
+
53
+ # Pipenv
54
+ Pipfile.lock
55
+ .venv
56
+
57
+ # Tox
58
+ .tox/
59
+ .coverage.*
60
+ nosetests.xml
61
+ coverage.xml
62
+ *.cover
63
+
64
+ # Additional IDE
65
+ *.sublime-workspace
66
+ *.sublime-project
67
+ .settings/
68
+ .project
69
+ .pydevproject
70
+ .spyproject/
71
+ .ropeproject/
72
+
73
+ # Jupyter
74
+ .ipynb_checkpoints
75
+ *.ipynb
76
+
77
+ # mypy
78
+ .mypy_cache/
79
+ .dmypy.json
80
+ dmypy.json
81
+
82
+ # Environments
83
+ .env
84
+ .venv
85
+ env.bak/
86
+ venv.bak/
87
+
88
+ .janito/
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.3.0] - 2024-01-24
6
+
7
+ ### Added
8
+ - Initial changelog
9
+ - Version tracking
10
+
11
+ ### Changed
12
+ - Updated version from 0.1.0 to 0.3.0
13
+
14
+ ## [0.1.0] - Initial Release
15
+
16
+ ### Added
17
+ - Initial release of Janito CLI
18
+ - AI-powered code analysis and modifications
19
+ - Interactive console mode
20
+ - Support for multiple file types
21
+ - Syntax validation for Python files
22
+ - Interactive change preview and confirmation
23
+ - History tracking of changes
24
+ - Debug and verbose output modes
25
+ - Question-answering about codebase
26
+ - File scanning preview
janito-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 João Pinto
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 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.
janito-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.3
2
+ Name: janito
3
+ Version: 0.3.0
4
+ Summary: A CLI tool for software development tasks powered by AI
5
+ Project-URL: Homepage, https://github.com/joaompinto/janito
6
+ Project-URL: Repository, https://github.com/joaompinto/janito.git
7
+ Author-email: João Pinto <lamego.pinto@gmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Topic :: Software Development
17
+ Requires-Python: >=3.8
18
+ Requires-Dist: anthropic
19
+ Requires-Dist: pathspec
20
+ Requires-Dist: rich
21
+ Requires-Dist: typer
22
+ Description-Content-Type: text/markdown
23
+
24
+ # 🤖 Janito CLI
25
+
26
+ A CLI tool for software development tasks powered by AI.
27
+
28
+ Janito is an AI-powered assistant that helps automate common software development tasks like refactoring, documentation updates, and code optimization.
29
+
30
+ ## 📥 Installation
31
+
32
+ ```bash
33
+ # Install from PyPI
34
+ pip install janito
35
+
36
+ # Install from source
37
+ git clone https://github.com/joaompinto/janito.git
38
+ cd janito
39
+ pip install -e .
40
+ ```
41
+
42
+ ## ⚡ Requirements
43
+
44
+ - Python 3.8+
45
+ - Anthropic API key
46
+ - Required packages (automatically installed):
47
+ - typer
48
+ - pathspec
49
+ - rich
50
+
51
+ ## ⚙️ Configuration
52
+
53
+ ### 🔑 API Key Setup
54
+ Janito requires an Anthropic API key to function. Set it as an environment variable:
55
+
56
+ ```bash
57
+ export ANTHROPIC_API_KEY='your-api-key-here'
58
+ ```
59
+
60
+ You can also add this to your shell profile (~/.bashrc, ~/.zshrc, etc.) for persistence.
61
+
62
+ ## 📖 Usage
63
+
64
+ Janito can be used in two modes: Command Line or Interactive Console.
65
+
66
+ ### 💻 Command Line Mode
67
+
68
+ ```bash
69
+ janito REQUEST [OPTIONS]
70
+ ```
71
+
72
+ #### Arguments
73
+ - `REQUEST`: The modification request
74
+
75
+ #### Options
76
+ - `-w, --workdir PATH`: Working directory (defaults to current directory)
77
+ - `--raw`: Print raw response instead of markdown format
78
+ - `--play PATH`: Replay a saved prompt file
79
+ - `-i, --include PATH`: Additional paths to include in analysis
80
+ - `--debug`: Show debug information
81
+ - `-v, --verbose`: Show verbose output
82
+ - `--ask`: Ask a question about the codebase
83
+ - `--scan`: Preview files that would be analyzed
84
+
85
+ ### 🖥️ Interactive Console Mode
86
+
87
+ Start the interactive console by running `janito` without arguments:
88
+
89
+ ```bash
90
+ janito
91
+ ```
92
+
93
+ In console mode, you can:
94
+ - Enter requests directly
95
+ - Navigate history with up/down arrows
96
+ - Use special commands starting with /
97
+
98
+ ### 📝 Examples
99
+
100
+ ```bash
101
+ # Command Line Mode Examples
102
+ janito "create docstrings for all functions"
103
+ janito "add error handling" -w ./myproject
104
+ janito "update tests" -i ./tests -i ./lib
105
+ janito --ask "explain the authentication flow"
106
+ janito --scan # Preview files to be analyzed
107
+
108
+ # Console Mode
109
+ janito # Starts interactive session
110
+ ```
111
+
112
+ ## ✨ Features
113
+
114
+ - 🤖 AI-powered code analysis and modifications
115
+ - 💻 Interactive console mode for continuous interaction
116
+ - 📁 Support for multiple file types
117
+ - ✅ Syntax validation for Python files
118
+ - 👀 Interactive change preview and confirmation
119
+ - 📜 History tracking of all changes
120
+ - 🐛 Debug and verbose output modes
121
+ - ❓ Question-answering about codebase
122
+ - 🔍 File scanning preview
123
+
124
+ ## 📚 History and Debugging
125
+
126
+ Changes are automatically saved in `.janito/history/` with timestamps:
127
+ - `*_analysis.txt`: Initial analysis
128
+ - `*_selected.txt`: Selected implementation
129
+ - `*_changes.txt`: Actual changes
130
+
131
+ Enable debug mode for detailed logging:
132
+ ```bash
133
+ janito "request" --debug
134
+ ```
135
+
136
+ ## 📄 License
137
+
138
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
janito-0.3.0/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # 🤖 Janito CLI
2
+
3
+ A CLI tool for software development tasks powered by AI.
4
+
5
+ Janito is an AI-powered assistant that helps automate common software development tasks like refactoring, documentation updates, and code optimization.
6
+
7
+ ## 📥 Installation
8
+
9
+ ```bash
10
+ # Install from PyPI
11
+ pip install janito
12
+
13
+ # Install from source
14
+ git clone https://github.com/joaompinto/janito.git
15
+ cd janito
16
+ pip install -e .
17
+ ```
18
+
19
+ ## ⚡ Requirements
20
+
21
+ - Python 3.8+
22
+ - Anthropic API key
23
+ - Required packages (automatically installed):
24
+ - typer
25
+ - pathspec
26
+ - rich
27
+
28
+ ## ⚙️ Configuration
29
+
30
+ ### 🔑 API Key Setup
31
+ Janito requires an Anthropic API key to function. Set it as an environment variable:
32
+
33
+ ```bash
34
+ export ANTHROPIC_API_KEY='your-api-key-here'
35
+ ```
36
+
37
+ You can also add this to your shell profile (~/.bashrc, ~/.zshrc, etc.) for persistence.
38
+
39
+ ## 📖 Usage
40
+
41
+ Janito can be used in two modes: Command Line or Interactive Console.
42
+
43
+ ### 💻 Command Line Mode
44
+
45
+ ```bash
46
+ janito REQUEST [OPTIONS]
47
+ ```
48
+
49
+ #### Arguments
50
+ - `REQUEST`: The modification request
51
+
52
+ #### Options
53
+ - `-w, --workdir PATH`: Working directory (defaults to current directory)
54
+ - `--raw`: Print raw response instead of markdown format
55
+ - `--play PATH`: Replay a saved prompt file
56
+ - `-i, --include PATH`: Additional paths to include in analysis
57
+ - `--debug`: Show debug information
58
+ - `-v, --verbose`: Show verbose output
59
+ - `--ask`: Ask a question about the codebase
60
+ - `--scan`: Preview files that would be analyzed
61
+
62
+ ### 🖥️ Interactive Console Mode
63
+
64
+ Start the interactive console by running `janito` without arguments:
65
+
66
+ ```bash
67
+ janito
68
+ ```
69
+
70
+ In console mode, you can:
71
+ - Enter requests directly
72
+ - Navigate history with up/down arrows
73
+ - Use special commands starting with /
74
+
75
+ ### 📝 Examples
76
+
77
+ ```bash
78
+ # Command Line Mode Examples
79
+ janito "create docstrings for all functions"
80
+ janito "add error handling" -w ./myproject
81
+ janito "update tests" -i ./tests -i ./lib
82
+ janito --ask "explain the authentication flow"
83
+ janito --scan # Preview files to be analyzed
84
+
85
+ # Console Mode
86
+ janito # Starts interactive session
87
+ ```
88
+
89
+ ## ✨ Features
90
+
91
+ - 🤖 AI-powered code analysis and modifications
92
+ - 💻 Interactive console mode for continuous interaction
93
+ - 📁 Support for multiple file types
94
+ - ✅ Syntax validation for Python files
95
+ - 👀 Interactive change preview and confirmation
96
+ - 📜 History tracking of all changes
97
+ - 🐛 Debug and verbose output modes
98
+ - ❓ Question-answering about codebase
99
+ - 🔍 File scanning preview
100
+
101
+ ## 📚 History and Debugging
102
+
103
+ Changes are automatically saved in `.janito/history/` with timestamps:
104
+ - `*_analysis.txt`: Initial analysis
105
+ - `*_selected.txt`: Selected implementation
106
+ - `*_changes.txt`: Actual changes
107
+
108
+ Enable debug mode for detailed logging:
109
+ ```bash
110
+ janito "request" --debug
111
+ ```
112
+
113
+ ## 📄 License
114
+
115
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,2 @@
1
+
2
+ # Empty file to make the directory a Python package
@@ -0,0 +1,260 @@
1
+ import typer
2
+ from typing import Optional, Dict, Any, List
3
+ from pathlib import Path
4
+ from janito.claude import ClaudeAPIAgent
5
+ import shutil
6
+ from janito.prompts import (
7
+ build_request_analisys_prompt,
8
+ build_selected_option_prompt,
9
+ SYSTEM_PROMPT,
10
+ parse_options
11
+ )
12
+ from rich.console import Console
13
+ from rich.markdown import Markdown
14
+ import re
15
+ import tempfile
16
+ import json
17
+ from rich.syntax import Syntax
18
+ from janito.contentchange import (
19
+ handle_changes_file,
20
+ get_file_type,
21
+ parse_block_changes,
22
+ preview_and_apply_changes,
23
+ format_parsed_changes,
24
+ )
25
+ from rich.table import Table
26
+ from rich.columns import Columns
27
+ from rich.panel import Panel
28
+ from rich.text import Text
29
+ from rich.rule import Rule
30
+ from rich import box
31
+ from datetime import datetime, timezone
32
+ from itertools import chain
33
+ from janito.scan import collect_files_content, is_dir_empty, preview_scan
34
+ from janito.qa import ask_question, display_answer
35
+ from rich.prompt import Prompt, Confirm
36
+ from janito.config import config
37
+ from importlib.metadata import version
38
+
39
+ def get_version() -> str:
40
+ try:
41
+ return version("janito")
42
+ except:
43
+ return "dev"
44
+
45
+ def format_analysis(analysis: str, raw: bool = False, claude: Optional[ClaudeAPIAgent] = None) -> None:
46
+ """Format and display the analysis output"""
47
+ console = Console()
48
+ if raw and claude:
49
+ console.print("\n=== Message History ===")
50
+ for role, content in claude.messages_history:
51
+ console.print(f"\n[bold cyan]{role.upper()}:[/bold cyan]")
52
+ console.print(content)
53
+ console.print("\n=== End Message History ===\n")
54
+ else:
55
+ md = Markdown(analysis)
56
+ console.print(md)
57
+
58
+ def prompt_user(message: str, choices: List[str] = None) -> str:
59
+ """Display a prominent user prompt with optional choices"""
60
+ console = Console()
61
+ console.print()
62
+ console.print(Rule(" User Input Required ", style="bold cyan"))
63
+
64
+ if choices:
65
+ choice_text = f"[cyan]Options: {', '.join(choices)}[/cyan]"
66
+ console.print(Panel(choice_text, box=box.ROUNDED))
67
+
68
+ return Prompt.ask(f"[bold cyan]> {message}[/bold cyan]")
69
+
70
+ def get_option_selection() -> int:
71
+ """Get user input for option selection"""
72
+ while True:
73
+ try:
74
+ option = int(prompt_user("Select option number"))
75
+ return option
76
+ except ValueError:
77
+ console = Console()
78
+ console.print("[red]Please enter a valid number[/red]")
79
+
80
+ def get_history_path(workdir: Path) -> Path:
81
+ """Create and return the history directory path"""
82
+ history_dir = workdir / '.janito' / 'history'
83
+ history_dir.mkdir(parents=True, exist_ok=True)
84
+ return history_dir
85
+
86
+ def get_timestamp() -> str:
87
+ """Get current UTC timestamp in YMD_HMS format with leading zeros"""
88
+ return datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')
89
+
90
+ def save_prompt_to_file(prompt: str) -> Path:
91
+ """Save prompt to a named temporary file that won't be deleted"""
92
+ temp_file = tempfile.NamedTemporaryFile(prefix='selected_', suffix='.txt', delete=False)
93
+ temp_path = Path(temp_file.name)
94
+ temp_path.write_text(prompt)
95
+ return temp_path
96
+
97
+ def save_to_file(content: str, prefix: str, workdir: Path) -> Path:
98
+ """Save content to a timestamped file in history directory"""
99
+ history_dir = get_history_path(workdir)
100
+ timestamp = get_timestamp()
101
+ filename = f"{timestamp}_{prefix}.txt"
102
+ file_path = history_dir / filename
103
+ file_path.write_text(content)
104
+ return file_path
105
+
106
+ def handle_option_selection(claude: ClaudeAPIAgent, initial_response: str, request: str, raw: bool = False, workdir: Optional[Path] = None, include: Optional[List[Path]] = None) -> None:
107
+ """Handle option selection and implementation details"""
108
+ option = get_option_selection()
109
+ paths_to_scan = [workdir] if workdir else []
110
+ if include:
111
+ paths_to_scan.extend(include)
112
+ files_content = collect_files_content(paths_to_scan, workdir) if paths_to_scan else ""
113
+
114
+ selected_prompt = build_selected_option_prompt(option, request, initial_response, files_content)
115
+ prompt_file = save_to_file(selected_prompt, 'selected', workdir)
116
+ if config.verbose:
117
+ print(f"\nSelected prompt saved to: {prompt_file}")
118
+
119
+ selected_response = claude.send_message(selected_prompt)
120
+ changes_file = save_to_file(selected_response, 'changes', workdir)
121
+ if config.verbose:
122
+ print(f"\nChanges saved to: {changes_file}")
123
+
124
+ changes = parse_block_changes(selected_response)
125
+ preview_and_apply_changes(changes, workdir)
126
+
127
+ def replay_saved_file(filepath: Path, claude: ClaudeAPIAgent, workdir: Path, raw: bool = False) -> None:
128
+ """Process a saved prompt file and display the response"""
129
+ if not filepath.exists():
130
+ raise FileNotFoundError(f"File {filepath} not found")
131
+
132
+ file_type = get_file_type(filepath)
133
+ content = filepath.read_text()
134
+
135
+ if file_type == 'changes':
136
+ changes = parse_block_changes(content)
137
+ preview_and_apply_changes(changes, workdir)
138
+ elif file_type == 'analysis':
139
+ format_analysis(content, raw, claude)
140
+ handle_option_selection(claude, content, content, raw, workdir)
141
+ elif file_type == 'selected':
142
+ if raw:
143
+ console = Console()
144
+ console.print("\n=== Prompt Content ===")
145
+ console.print(content)
146
+ console.print("=== End Prompt Content ===\n")
147
+ response = claude.send_message(content)
148
+ changes_file = save_to_file(response, 'changes_', workdir)
149
+ print(f"\nChanges saved to: {changes_file}")
150
+
151
+ changes = parse_block_changes(response)
152
+ preview_and_apply_changes(preview_changes, workdir)
153
+ else:
154
+ response = claude.send_message(content)
155
+ format_analysis(response, raw)
156
+
157
+ def process_question(question: str, workdir: Path, include: List[Path], raw: bool, claude: ClaudeAPIAgent) -> None:
158
+ """Process a question about the codebase"""
159
+ paths_to_scan = [workdir] if workdir else []
160
+ if include:
161
+ paths_to_scan.extend(include)
162
+ files_content = collect_files_content(paths_to_scan, workdir)
163
+
164
+ answer = ask_question(question, files_content, claude)
165
+ display_answer(answer, raw)
166
+
167
+ def ensure_workdir(workdir: Path) -> Path:
168
+ """Ensure working directory exists, prompt for creation if it doesn't"""
169
+ if workdir.exists():
170
+ return workdir
171
+
172
+ console = Console()
173
+ console.print(f"\n[yellow]Directory does not exist:[/yellow] {workdir}")
174
+ if Confirm.ask("Create directory?"):
175
+ workdir.mkdir(parents=True)
176
+ console.print(f"[green]Created directory:[/green] {workdir}")
177
+ return workdir
178
+ raise typer.Exit(1)
179
+
180
+ def typer_main(
181
+ request: Optional[str] = typer.Argument(None, help="The modification request"),
182
+ ask: Optional[str] = typer.Option(None, "--ask", help="Ask a question about the codebase"),
183
+ workdir: Optional[Path] = typer.Option(None, "-w", "--workdir",
184
+ help="Working directory (defaults to current directory)",
185
+ file_okay=False, dir_okay=True),
186
+ raw: bool = typer.Option(False, "--raw", help="Print raw response instead of markdown format"),
187
+ play: Optional[Path] = typer.Option(None, "--play", help="Replay a saved prompt file"),
188
+ include: Optional[List[Path]] = typer.Option(None, "-i", "--include", help="Additional paths to include in analysis", exists=True),
189
+ debug: bool = typer.Option(False, "--debug", help="Show debug information"),
190
+ debug_line: Optional[int] = typer.Option(None, "--debug-line", help="Show debug information only for specific line number"),
191
+ verbose: bool = typer.Option(False, "-v", "--verbose", help="Show verbose output"),
192
+ scan: bool = typer.Option(False, "--scan", help="Preview files that would be analyzed"),
193
+ version: bool = typer.Option(False, "--version", help="Show version and exit"),
194
+ ) -> None:
195
+ """
196
+ Analyze files and provide modification instructions.
197
+ """
198
+ if version:
199
+ console = Console()
200
+ console.print(f"Janito v{get_version()}")
201
+ raise typer.Exit()
202
+
203
+ config.set_debug(debug)
204
+ config.set_verbose(verbose)
205
+ config.set_debug_line(debug_line)
206
+
207
+ claude = ClaudeAPIAgent(system_prompt=SYSTEM_PROMPT)
208
+
209
+ if not any([request, ask, play, scan]):
210
+ workdir = workdir or Path.cwd()
211
+ workdir = ensure_workdir(workdir)
212
+ from janito.console import start_console_session
213
+ start_console_session(workdir, include)
214
+ return
215
+
216
+ workdir = workdir or Path.cwd()
217
+ workdir = ensure_workdir(workdir)
218
+
219
+ if include:
220
+ include = [
221
+ path if path.is_absolute() else (workdir / path).resolve()
222
+ for path in include
223
+ ]
224
+
225
+ if ask:
226
+ process_question(ask, workdir, include, raw, claude)
227
+ return
228
+
229
+ if scan:
230
+ paths_to_scan = include if include else [workdir]
231
+ preview_scan(paths_to_scan, workdir)
232
+ return
233
+
234
+ if play:
235
+ replay_saved_file(play, claude, workdir, raw)
236
+ return
237
+
238
+ paths_to_scan = include if include else [workdir]
239
+
240
+ is_empty = is_dir_empty(workdir)
241
+ if is_empty and not include:
242
+ console = Console()
243
+ console.print("\n[bold blue]Empty directory - will create new files as needed[/bold blue]")
244
+ files_content = ""
245
+ else:
246
+ files_content = collect_files_content(paths_to_scan, workdir)
247
+
248
+ initial_prompt = build_request_analisys_prompt(files_content, request)
249
+ initial_response = claude.send_message(initial_prompt)
250
+ analysis_file = save_to_file(initial_response, 'analysis', workdir)
251
+
252
+ format_analysis(initial_response, raw, claude)
253
+
254
+ handle_option_selection(claude, initial_response, request, raw, workdir, include)
255
+
256
+ def main():
257
+ typer.run(typer_main)
258
+
259
+ if __name__ == "__main__":
260
+ main()