janito 0.6.0__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- janito/__main__.py +37 -30
- janito/agents/__init__.py +8 -2
- janito/agents/agent.py +10 -3
- janito/agents/claudeai.py +13 -23
- janito/agents/openai.py +5 -1
- janito/change/analysis/analyze.py +8 -7
- janito/change/analysis/prompts.py +4 -12
- janito/change/analysis/view/terminal.py +21 -11
- janito/change/applier/text.py +7 -5
- janito/change/core.py +22 -29
- janito/change/parser.py +0 -2
- janito/change/prompts.py +16 -21
- janito/change/validator.py +27 -9
- janito/change/viewer/content.py +1 -1
- janito/change/viewer/panels.py +93 -115
- janito/change/viewer/styling.py +15 -4
- janito/cli/commands.py +63 -20
- janito/common.py +44 -18
- janito/config.py +44 -44
- janito/prompt.py +36 -0
- janito/qa.py +5 -14
- janito/search_replace/README.md +63 -17
- janito/search_replace/__init__.py +2 -1
- janito/search_replace/core.py +15 -14
- janito/search_replace/logger.py +35 -0
- janito/search_replace/searcher.py +160 -48
- janito/search_replace/strategy_result.py +10 -0
- janito/shell/__init__.py +15 -16
- janito/shell/commands.py +38 -97
- janito/shell/processor.py +7 -27
- janito/shell/prompt.py +48 -0
- janito/shell/registry.py +60 -0
- janito/workspace/__init__.py +4 -5
- janito/workspace/analysis.py +2 -2
- janito/workspace/show.py +141 -0
- janito/workspace/stats.py +43 -0
- janito/workspace/types.py +98 -0
- janito/workspace/workset.py +108 -0
- janito/workspace/workspace.py +114 -0
- janito-0.7.0.dist-info/METADATA +167 -0
- {janito-0.6.0.dist-info → janito-0.7.0.dist-info}/RECORD +44 -43
- janito/change/viewer/pager.py +0 -56
- janito/cli/handlers/ask.py +0 -22
- janito/cli/handlers/demo.py +0 -22
- janito/cli/handlers/request.py +0 -24
- janito/cli/handlers/scan.py +0 -9
- janito/prompts.py +0 -2
- janito/shell/handlers.py +0 -122
- janito/workspace/manager.py +0 -48
- janito/workspace/scan.py +0 -232
- janito-0.6.0.dist-info/METADATA +0 -185
- {janito-0.6.0.dist-info → janito-0.7.0.dist-info}/WHEEL +0 -0
- {janito-0.6.0.dist-info → janito-0.7.0.dist-info}/entry_points.txt +0 -0
- {janito-0.6.0.dist-info → janito-0.7.0.dist-info}/licenses/LICENSE +0 -0
janito/workspace/scan.py
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
from collections import defaultdict
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import Dict, List, Set, Tuple
|
4
|
-
|
5
|
-
from pathspec import PathSpec
|
6
|
-
from pathspec.patterns import GitWildMatchPattern
|
7
|
-
from rich.columns import Columns
|
8
|
-
from rich.console import Console
|
9
|
-
from rich.panel import Panel
|
10
|
-
from rich.text import Text
|
11
|
-
from rich.rule import Rule
|
12
|
-
from rich.console import Group
|
13
|
-
from janito.config import config
|
14
|
-
|
15
|
-
|
16
|
-
SPECIAL_FILES = ["README.md", "__init__.py", "__main__.py"]
|
17
|
-
|
18
|
-
|
19
|
-
def _get_gitignore_spec() -> PathSpec:
|
20
|
-
"""Load gitignore patterns if available"""
|
21
|
-
gitignore_path = config.workspace_dir / '.gitignore' if config.workspace_dir else None
|
22
|
-
if gitignore_path and gitignore_path.exists():
|
23
|
-
with gitignore_path.open() as f:
|
24
|
-
lines = f.readlines()
|
25
|
-
return PathSpec.from_lines(GitWildMatchPattern, lines)
|
26
|
-
|
27
|
-
|
28
|
-
def _process_file(path: Path, relative_base: Path) -> Tuple[str, str, bool]:
|
29
|
-
"""Process a single file and return its XML content, display item and success status"""
|
30
|
-
relative_path = path.relative_to(relative_base)
|
31
|
-
try:
|
32
|
-
# Skip binary files
|
33
|
-
if path.read_bytes().find(b'\x00') != -1:
|
34
|
-
return "", "", False
|
35
|
-
|
36
|
-
file_content = path.read_text(encoding='utf-8')
|
37
|
-
xml_content = f"<file>\n<path>{relative_path}</path>\n<content>\n{file_content}\n</content>\n</file>"
|
38
|
-
display_item = f"[cyan]•[/cyan] {relative_path}"
|
39
|
-
return xml_content, display_item, True
|
40
|
-
except UnicodeDecodeError:
|
41
|
-
return "", str(relative_path), False
|
42
|
-
|
43
|
-
def _scan_paths(paths: List[Path] = None) -> Tuple[List[str], List[str], List[str], List[str]]:
|
44
|
-
"""Common scanning logic for both preview and content collection"""
|
45
|
-
content_parts = []
|
46
|
-
file_items = []
|
47
|
-
skipped_files = []
|
48
|
-
ignored_items = []
|
49
|
-
processed_files: Set[Path] = set()
|
50
|
-
console = Console()
|
51
|
-
gitignore_spec = _get_gitignore_spec()
|
52
|
-
|
53
|
-
def scan_path(path: Path, depth: int, is_recursive: bool) -> None:
|
54
|
-
if depth > 1 and not is_recursive:
|
55
|
-
return
|
56
|
-
|
57
|
-
path = path.resolve()
|
58
|
-
if '.janito' in path.parts or '.git' in path.parts or '.pytest_cache' in path.parts:
|
59
|
-
return
|
60
|
-
|
61
|
-
relative_base = config.workspace_dir
|
62
|
-
if path.is_dir():
|
63
|
-
relative_path = path.relative_to(relative_base)
|
64
|
-
content_parts.append(f'<directory><path>{relative_path}</path>not sent</directory>')
|
65
|
-
file_items.append(f"[blue]•[/blue] {relative_path}/")
|
66
|
-
|
67
|
-
# Process special files
|
68
|
-
special_found = []
|
69
|
-
for special_file in SPECIAL_FILES:
|
70
|
-
special_path = path / special_file
|
71
|
-
if special_path.exists() and special_path.resolve() not in processed_files:
|
72
|
-
special_found.append(special_file)
|
73
|
-
processed_files.add(special_path.resolve())
|
74
|
-
xml_content, _, success = _process_file(special_path, relative_base)
|
75
|
-
if success:
|
76
|
-
content_parts.append(xml_content)
|
77
|
-
else:
|
78
|
-
skipped_files.append(str(special_path.relative_to(relative_base)))
|
79
|
-
|
80
|
-
if special_found:
|
81
|
-
file_items[-1] = f"[blue]•[/blue] {relative_path}/ [cyan]({', '.join(special_found)})[/cyan]"
|
82
|
-
|
83
|
-
for item in path.iterdir():
|
84
|
-
# Skip ignored files/directories
|
85
|
-
if gitignore_spec and gitignore_spec.match_file(str(item.relative_to(config.workspace_dir))):
|
86
|
-
rel_path = item.relative_to(config.workspace_dir)
|
87
|
-
ignored_items.append(f"[dim red]•[/dim red] {rel_path}")
|
88
|
-
continue
|
89
|
-
scan_path(item, depth+1, is_recursive)
|
90
|
-
else:
|
91
|
-
if path.resolve() in processed_files:
|
92
|
-
return
|
93
|
-
|
94
|
-
processed_files.add(path.resolve())
|
95
|
-
xml_content, display_item, success = _process_file(path, relative_base)
|
96
|
-
if success:
|
97
|
-
content_parts.append(xml_content)
|
98
|
-
file_items.append(display_item)
|
99
|
-
else:
|
100
|
-
skipped_files.append(display_item)
|
101
|
-
if display_item:
|
102
|
-
console.print(f"[yellow]Warning: Skipping file due to encoding issues: {display_item}[/yellow]")
|
103
|
-
|
104
|
-
for path in paths:
|
105
|
-
is_recursive = Path(path) in config.recursive
|
106
|
-
scan_path(path, 0, is_recursive)
|
107
|
-
|
108
|
-
if skipped_files and config.verbose:
|
109
|
-
console.print("\n[yellow]Files skipped due to encoding issues:[/yellow]")
|
110
|
-
for file in skipped_files:
|
111
|
-
console.print(f" • {file}")
|
112
|
-
|
113
|
-
return content_parts, file_items, skipped_files, ignored_items
|
114
|
-
|
115
|
-
def collect_files_content(paths: List[Path] = None) -> str:
|
116
|
-
"""Collect content from all files in XML format"""
|
117
|
-
console = Console()
|
118
|
-
|
119
|
-
# If no paths specified and skipwork not set, use workspace_dir
|
120
|
-
if not paths and not config.skipwork:
|
121
|
-
paths = [config.workspace_dir]
|
122
|
-
# If paths specified and skipwork not set, include workspace_dir
|
123
|
-
elif paths and not config.skipwork:
|
124
|
-
paths = [config.workspace_dir] + paths
|
125
|
-
# If skipwork set, use only specified paths
|
126
|
-
elif not paths and config.skipwork:
|
127
|
-
console.print("[yellow]Warning: No paths to scan - skipwork enabled but no include paths specified[/yellow]")
|
128
|
-
return ""
|
129
|
-
|
130
|
-
content_parts, file_items, skipped_files, ignored_items = _scan_paths(paths)
|
131
|
-
|
132
|
-
if file_items and config.verbose:
|
133
|
-
console.print("\n[bold blue]Contents being analyzed:[/bold blue]")
|
134
|
-
console.print(Columns(file_items, padding=(0, 4), expand=True))
|
135
|
-
console.print("\n[bold green]Scan completed successfully[/bold green]")
|
136
|
-
|
137
|
-
return "\n".join(content_parts)
|
138
|
-
|
139
|
-
def preview_scan(paths: List[Path] = None) -> None:
|
140
|
-
"""Preview what files and directories would be scanned with structured output."""
|
141
|
-
console = Console()
|
142
|
-
_, file_items, skipped_files, ignored_items = _scan_paths(paths)
|
143
|
-
|
144
|
-
# Create sections list for structured output
|
145
|
-
sections = []
|
146
|
-
|
147
|
-
# Section 1: Paths Information
|
148
|
-
paths_section = []
|
149
|
-
is_workspace_dir_scanned = any(p.resolve() == config.workspace_dir.resolve() for p in paths)
|
150
|
-
|
151
|
-
# Show workspace_dir unless skipwork is set
|
152
|
-
if not config.skipwork:
|
153
|
-
paths_section.append(Panel(
|
154
|
-
f"📂 {config.workspace_dir.absolute()}",
|
155
|
-
title="[bold cyan]Working Directory[/bold cyan]",
|
156
|
-
border_style="cyan",
|
157
|
-
padding=(1, 2)
|
158
|
-
))
|
159
|
-
|
160
|
-
# Show included paths
|
161
|
-
if paths:
|
162
|
-
included_paths = []
|
163
|
-
for path in paths:
|
164
|
-
try:
|
165
|
-
rel_path = path.relative_to(config.workspace_dir)
|
166
|
-
is_recursive = path in config.recursive
|
167
|
-
included_paths.append(f"📁 ./{rel_path}" + ("/*" if is_recursive else "/"))
|
168
|
-
except ValueError:
|
169
|
-
included_paths.append(f"📁 {path.absolute()}")
|
170
|
-
|
171
|
-
paths_section.append(Panel(
|
172
|
-
Group(*[Text(p) for p in included_paths]),
|
173
|
-
title="[bold green]Included Paths[/bold green]",
|
174
|
-
border_style="green",
|
175
|
-
padding=(1, 2)
|
176
|
-
))
|
177
|
-
|
178
|
-
sections.extend(paths_section)
|
179
|
-
sections.append(Rule(style="blue"))
|
180
|
-
|
181
|
-
# Section 2: Files to be scanned
|
182
|
-
if file_items:
|
183
|
-
sections.append(Panel(
|
184
|
-
Columns(file_items, padding=(0, 2), expand=True),
|
185
|
-
title="[bold blue]Files to be Scanned[/bold blue]",
|
186
|
-
border_style="blue",
|
187
|
-
padding=(1, 2)
|
188
|
-
))
|
189
|
-
|
190
|
-
# Section 3: Ignored items
|
191
|
-
if ignored_items:
|
192
|
-
sections.append(Panel(
|
193
|
-
Columns(ignored_items, padding=(0, 2), expand=True),
|
194
|
-
title="[bold red]Ignored Items[/bold red]",
|
195
|
-
border_style="red",
|
196
|
-
padding=(1, 2)
|
197
|
-
))
|
198
|
-
|
199
|
-
# Section 4: Skipped files (only in verbose mode)
|
200
|
-
if skipped_files and config.verbose:
|
201
|
-
sections.append(Panel(
|
202
|
-
Columns([f"[yellow]•[/yellow] {f}" for f in skipped_files], padding=(0, 2), expand=True),
|
203
|
-
title="[bold yellow]Skipped Files[/bold yellow]",
|
204
|
-
border_style="yellow",
|
205
|
-
padding=(1, 2)
|
206
|
-
))
|
207
|
-
|
208
|
-
# Display all sections with separators
|
209
|
-
console.print("\n")
|
210
|
-
for section in sections:
|
211
|
-
console.print(section)
|
212
|
-
console.print("\n")
|
213
|
-
|
214
|
-
|
215
|
-
def is_dir_empty(path: Path) -> bool:
|
216
|
-
"""
|
217
|
-
Check if directory is empty (ignoring hidden files/directories).
|
218
|
-
|
219
|
-
Args:
|
220
|
-
path: Directory path to check
|
221
|
-
|
222
|
-
Returns:
|
223
|
-
True if directory has no visible files/directories, False otherwise
|
224
|
-
"""
|
225
|
-
if not path.is_dir():
|
226
|
-
return False
|
227
|
-
|
228
|
-
# List all non-hidden files and directories
|
229
|
-
visible_items = [item for item in path.iterdir()
|
230
|
-
if not item.name.startswith('.')]
|
231
|
-
|
232
|
-
return len(visible_items) == 0
|
janito-0.6.0.dist-info/METADATA
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: janito
|
3
|
-
Version: 0.6.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
|
-
License-File: LICENSE
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
11
|
-
Classifier: Environment :: Console
|
12
|
-
Classifier: Intended Audience :: Developers
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
17
|
-
Classifier: Topic :: Software Development
|
18
|
-
Requires-Python: >=3.8
|
19
|
-
Requires-Dist: anthropic
|
20
|
-
Requires-Dist: pathspec
|
21
|
-
Requires-Dist: rich
|
22
|
-
Requires-Dist: tomli
|
23
|
-
Requires-Dist: typer
|
24
|
-
Description-Content-Type: text/markdown
|
25
|
-
|
26
|
-
# Janito
|
27
|
-
|
28
|
-
[](https://badge.fury.io/py/janito)
|
29
|
-
[](https://opensource.org/licenses/MIT)
|
30
|
-
|
31
|
-
AI-powered CLI tool for code modifications and analysis. Janito helps you modify, analyze, and understand your codebase using natural language commands.
|
32
|
-
|
33
|
-
## Table of Contents
|
34
|
-
|
35
|
-
- [Features](#features)
|
36
|
-
- [Installation](#installation)
|
37
|
-
- [Prerequisites](#prerequisites)
|
38
|
-
- [Steps](#steps)
|
39
|
-
- [Usage](#usage)
|
40
|
-
- [Basic Commands](#basic-commands)
|
41
|
-
- [Common Use Cases](#common-use-cases)
|
42
|
-
- [Configuration](#configuration)
|
43
|
-
- [Environment Variables](#environment-variables)
|
44
|
-
- [Command Line Options](#command-line-options)
|
45
|
-
- [Troubleshooting](#troubleshooting)
|
46
|
-
- [Common Issues](#common-issues)
|
47
|
-
- [Error Messages](#error-messages)
|
48
|
-
- [Contributing](#contributing)
|
49
|
-
- [License](#license)
|
50
|
-
|
51
|
-
## ✨ Features
|
52
|
-
|
53
|
-
- Natural language code modifications
|
54
|
-
- Codebase analysis and question answering
|
55
|
-
- Smart search and replace with indentation awareness
|
56
|
-
- Git-aware operations
|
57
|
-
- Interactive shell mode
|
58
|
-
- Change preview and validation
|
59
|
-
- Automatic backup and restore
|
60
|
-
|
61
|
-
## 🚀 Installation
|
62
|
-
|
63
|
-
### Prerequisites
|
64
|
-
|
65
|
-
- Python 3.8 or higher
|
66
|
-
- pip package manager
|
67
|
-
- An Anthropic API key (default) or OpenAI API key
|
68
|
-
|
69
|
-
### Steps
|
70
|
-
|
71
|
-
1. Install using pip:
|
72
|
-
```bash
|
73
|
-
pip install janito
|
74
|
-
```
|
75
|
-
|
76
|
-
2. Configure your API key:
|
77
|
-
|
78
|
-
For Anthropic Claude (default):
|
79
|
-
```bash
|
80
|
-
export ANTHROPIC_API_KEY=your_api_key_here
|
81
|
-
```
|
82
|
-
|
83
|
-
For OpenAI:
|
84
|
-
```bash
|
85
|
-
export OPENAI_API_KEY=your_api_key_here
|
86
|
-
export AI_BACKEND=openai
|
87
|
-
```
|
88
|
-
|
89
|
-
## 💡 Usage
|
90
|
-
|
91
|
-
### Basic Commands
|
92
|
-
|
93
|
-
```bash
|
94
|
-
# Modify code
|
95
|
-
janito "add docstrings to this file"
|
96
|
-
|
97
|
-
# Ask questions about the codebase
|
98
|
-
janito --ask "explain the main function in this file"
|
99
|
-
|
100
|
-
# Preview files that would be analyzed
|
101
|
-
janito --scan
|
102
|
-
|
103
|
-
# Start interactive shell
|
104
|
-
janito
|
105
|
-
```
|
106
|
-
|
107
|
-
### Common Use Cases
|
108
|
-
|
109
|
-
1. Code Documentation:
|
110
|
-
```bash
|
111
|
-
janito "add type hints to all functions"
|
112
|
-
janito "improve docstrings with more details"
|
113
|
-
```
|
114
|
-
|
115
|
-
2. Code Analysis:
|
116
|
-
```bash
|
117
|
-
janito --ask "what are the main classes in this project?"
|
118
|
-
janito --ask "explain the error handling flow"
|
119
|
-
```
|
120
|
-
|
121
|
-
3. Code Refactoring:
|
122
|
-
```bash
|
123
|
-
janito "convert this class to use dataclasses"
|
124
|
-
janito "split this large function into smaller ones"
|
125
|
-
```
|
126
|
-
|
127
|
-
## ⚙️ Configuration
|
128
|
-
|
129
|
-
### Environment Variables
|
130
|
-
|
131
|
-
- `ANTHROPIC_API_KEY`: Anthropic API key
|
132
|
-
- `OPENAI_API_KEY`: OpenAI API key (if using OpenAI backend)
|
133
|
-
- `AI_BACKEND`: AI provider ('claudeai' or 'openai')
|
134
|
-
- `JANITO_TEST_CMD`: Default test command to run after changes
|
135
|
-
|
136
|
-
### Command Line Options
|
137
|
-
|
138
|
-
- `-w, --workspace_dir`: Set working directory
|
139
|
-
- `-i, --include`: Additional paths to include
|
140
|
-
- `--debug`: Show debug information
|
141
|
-
- `--verbose`: Show verbose output
|
142
|
-
- `--auto-apply`: Apply changes without confirmation
|
143
|
-
- `--history`: Display history of requests
|
144
|
-
|
145
|
-
## 🔧 Troubleshooting
|
146
|
-
|
147
|
-
### Common Issues
|
148
|
-
|
149
|
-
1. API Key Issues:
|
150
|
-
```bash
|
151
|
-
# Verify API key is set
|
152
|
-
echo $ANTHROPIC_API_KEY
|
153
|
-
|
154
|
-
# Temporarily set API key for single command
|
155
|
-
ANTHROPIC_API_KEY=your_key janito "your request"
|
156
|
-
```
|
157
|
-
|
158
|
-
2. Path Issues:
|
159
|
-
```bash
|
160
|
-
# Use absolute paths if having issues with relative paths
|
161
|
-
janito -w /full/path/to/project "your request"
|
162
|
-
|
163
|
-
# Specify additional paths explicitly
|
164
|
-
janito -i ./src -i ./tests "your request"
|
165
|
-
```
|
166
|
-
|
167
|
-
3. Debug Mode:
|
168
|
-
```bash
|
169
|
-
# Enable debug output for troubleshooting
|
170
|
-
janito --debug "your request"
|
171
|
-
```
|
172
|
-
|
173
|
-
### Error Messages
|
174
|
-
|
175
|
-
- "No command given": Provide a change request or command
|
176
|
-
- "No input provided": Check if using --input mode correctly
|
177
|
-
- "Duplicate path provided": Remove duplicate paths from includes
|
178
|
-
|
179
|
-
## 👥 Contributing
|
180
|
-
|
181
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
182
|
-
|
183
|
-
## 📄 License
|
184
|
-
|
185
|
-
MIT License - see [LICENSE](LICENSE)
|
File without changes
|
File without changes
|
File without changes
|