janito 0.6.0__tar.gz → 0.8.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.
- janito-0.8.0/.gitignore +42 -0
- {janito-0.6.0 → janito-0.8.0}/LICENSE +20 -20
- janito-0.8.0/PKG-INFO +106 -0
- janito-0.8.0/README.md +82 -0
- janito-0.8.0/janito/__main__.py +128 -0
- janito-0.8.0/janito/agents/__init__.py +22 -0
- janito-0.8.0/janito/agents/agent.py +25 -0
- janito-0.8.0/janito/agents/claudeai.py +41 -0
- janito-0.8.0/janito/agents/deepseekai.py +47 -0
- janito-0.8.0/janito/change/applied_blocks.py +34 -0
- janito-0.8.0/janito/change/applier.py +167 -0
- janito-0.8.0/janito/change/edit_blocks.py +148 -0
- janito-0.8.0/janito/change/finder.py +72 -0
- janito-0.8.0/janito/change/request.py +144 -0
- janito-0.8.0/janito/change/validator.py +87 -0
- janito-0.8.0/janito/change/view/content.py +63 -0
- {janito-0.6.0/janito/change/viewer → janito-0.8.0/janito/change/view}/diff.py +44 -43
- janito-0.8.0/janito/change/view/panels.py +201 -0
- janito-0.8.0/janito/change/view/sections.py +69 -0
- janito-0.8.0/janito/change/view/styling.py +140 -0
- janito-0.8.0/janito/change/view/summary.py +37 -0
- {janito-0.6.0/janito/change/viewer → janito-0.8.0/janito/change/view}/themes.py +62 -55
- janito-0.8.0/janito/change/view/viewer.py +59 -0
- {janito-0.6.0 → janito-0.8.0}/janito/cli/__init__.py +1 -1
- janito-0.8.0/janito/cli/commands.py +68 -0
- {janito-0.6.0 → janito-0.8.0}/janito/cli/functions.py +66 -111
- janito-0.8.0/janito/common.py +133 -0
- {janito-0.6.0 → janito-0.8.0}/janito/config.py +99 -101
- janito-0.8.0/janito/data/change_prompt.txt +81 -0
- janito-0.8.0/janito/data/system_prompt.txt +3 -0
- janito-0.8.0/janito/qa.py +56 -0
- {janito-0.6.0 → janito-0.8.0}/janito/version.py +22 -22
- janito-0.8.0/janito/workspace/__init__.py +8 -0
- {janito-0.6.0 → janito-0.8.0}/janito/workspace/analysis.py +120 -120
- janito-0.8.0/janito/workspace/models.py +97 -0
- janito-0.8.0/janito/workspace/show.py +115 -0
- janito-0.8.0/janito/workspace/stats.py +42 -0
- janito-0.8.0/janito/workspace/workset.py +135 -0
- janito-0.8.0/janito/workspace/workspace.py +335 -0
- {janito-0.6.0 → janito-0.8.0}/pyproject.toml +48 -41
- {janito-0.6.0 → janito-0.8.0}/tools/release.sh +86 -86
- janito-0.6.0/.gitignore +0 -88
- janito-0.6.0/PKG-INFO +0 -185
- janito-0.6.0/README.md +0 -160
- janito-0.6.0/janito/__init__.py +0 -2
- janito-0.6.0/janito/__main__.py +0 -135
- janito-0.6.0/janito/agents/__init__.py +0 -16
- janito-0.6.0/janito/agents/agent.py +0 -21
- janito-0.6.0/janito/agents/claudeai.py +0 -55
- janito-0.6.0/janito/agents/openai.py +0 -53
- janito-0.6.0/janito/agents/test.py +0 -34
- janito-0.6.0/janito/change/__init__.py +0 -32
- janito-0.6.0/janito/change/__main__.py +0 -0
- janito-0.6.0/janito/change/analysis/__init__.py +0 -23
- janito-0.6.0/janito/change/analysis/__main__.py +0 -7
- janito-0.6.0/janito/change/analysis/analyze.py +0 -61
- janito-0.6.0/janito/change/analysis/formatting.py +0 -78
- janito-0.6.0/janito/change/analysis/options.py +0 -81
- janito-0.6.0/janito/change/analysis/prompts.py +0 -98
- janito-0.6.0/janito/change/analysis/view/__init__.py +0 -9
- janito-0.6.0/janito/change/analysis/view/terminal.py +0 -171
- janito-0.6.0/janito/change/applier/__init__.py +0 -5
- janito-0.6.0/janito/change/applier/file.py +0 -58
- janito-0.6.0/janito/change/applier/main.py +0 -156
- janito-0.6.0/janito/change/applier/text.py +0 -245
- janito-0.6.0/janito/change/applier/workspace_dir.py +0 -58
- janito-0.6.0/janito/change/core.py +0 -131
- janito-0.6.0/janito/change/history.py +0 -44
- janito-0.6.0/janito/change/operations.py +0 -7
- janito-0.6.0/janito/change/parser.py +0 -289
- janito-0.6.0/janito/change/play.py +0 -54
- janito-0.6.0/janito/change/preview.py +0 -82
- janito-0.6.0/janito/change/prompts.py +0 -126
- janito-0.6.0/janito/change/test.py +0 -0
- janito-0.6.0/janito/change/validator.py +0 -251
- janito-0.6.0/janito/change/viewer/__init__.py +0 -11
- janito-0.6.0/janito/change/viewer/content.py +0 -66
- janito-0.6.0/janito/change/viewer/pager.py +0 -56
- janito-0.6.0/janito/change/viewer/panels.py +0 -555
- janito-0.6.0/janito/change/viewer/styling.py +0 -103
- janito-0.6.0/janito/clear_statement_parser/clear_statement_format.txt +0 -328
- janito-0.6.0/janito/clear_statement_parser/examples.txt +0 -326
- janito-0.6.0/janito/clear_statement_parser/models.py +0 -104
- janito-0.6.0/janito/clear_statement_parser/parser.py +0 -496
- janito-0.6.0/janito/cli/base.py +0 -30
- janito-0.6.0/janito/cli/commands.py +0 -45
- janito-0.6.0/janito/cli/handlers/ask.py +0 -22
- janito-0.6.0/janito/cli/handlers/demo.py +0 -22
- janito-0.6.0/janito/cli/handlers/request.py +0 -24
- janito-0.6.0/janito/cli/handlers/scan.py +0 -9
- janito-0.6.0/janito/cli/history.py +0 -61
- janito-0.6.0/janito/cli/registry.py +0 -26
- janito-0.6.0/janito/common.py +0 -54
- janito-0.6.0/janito/demo/__init__.py +0 -4
- janito-0.6.0/janito/demo/data.py +0 -13
- janito-0.6.0/janito/demo/mock_data.py +0 -20
- janito-0.6.0/janito/demo/operations.py +0 -45
- janito-0.6.0/janito/demo/runner.py +0 -59
- janito-0.6.0/janito/demo/scenarios.py +0 -32
- janito-0.6.0/janito/prompts.py +0 -2
- janito-0.6.0/janito/qa.py +0 -66
- janito-0.6.0/janito/review.py +0 -13
- janito-0.6.0/janito/search_replace/README.md +0 -146
- janito-0.6.0/janito/search_replace/__init__.py +0 -6
- janito-0.6.0/janito/search_replace/__main__.py +0 -21
- janito-0.6.0/janito/search_replace/core.py +0 -119
- janito-0.6.0/janito/search_replace/parser.py +0 -52
- janito-0.6.0/janito/search_replace/play.py +0 -61
- janito-0.6.0/janito/search_replace/replacer.py +0 -36
- janito-0.6.0/janito/search_replace/searcher.py +0 -299
- janito-0.6.0/janito/shell/__init__.py +0 -39
- janito-0.6.0/janito/shell/bus.py +0 -31
- janito-0.6.0/janito/shell/commands.py +0 -195
- janito-0.6.0/janito/shell/handlers.py +0 -122
- janito-0.6.0/janito/shell/history.py +0 -20
- janito-0.6.0/janito/shell/processor.py +0 -52
- janito-0.6.0/janito/tui/__init__.py +0 -21
- janito-0.6.0/janito/tui/base.py +0 -22
- janito-0.6.0/janito/tui/flows/__init__.py +0 -5
- janito-0.6.0/janito/tui/flows/changes.py +0 -65
- janito-0.6.0/janito/tui/flows/content.py +0 -128
- janito-0.6.0/janito/tui/flows/selection.py +0 -117
- janito-0.6.0/janito/tui/screens/__init__.py +0 -3
- janito-0.6.0/janito/tui/screens/app.py +0 -1
- janito-0.6.0/janito/workspace/__init__.py +0 -7
- janito-0.6.0/janito/workspace/manager.py +0 -48
- janito-0.6.0/janito/workspace/scan.py +0 -232
- janito-0.6.0/setup.py +0 -4
- janito-0.6.0/tests/test_python_adjustments.py +0 -271
janito-0.8.0/.gitignore
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Python
|
2
|
+
__pycache__/
|
3
|
+
*.py[cod]
|
4
|
+
*$py.class
|
5
|
+
*.so
|
6
|
+
.Python
|
7
|
+
build/
|
8
|
+
develop-eggs/
|
9
|
+
dist/
|
10
|
+
downloads/
|
11
|
+
eggs/
|
12
|
+
.eggs/
|
13
|
+
lib/
|
14
|
+
lib64/
|
15
|
+
parts/
|
16
|
+
sdist/
|
17
|
+
var/
|
18
|
+
wheels/
|
19
|
+
*.egg-info/
|
20
|
+
.installed.cfg
|
21
|
+
*.egg
|
22
|
+
|
23
|
+
# Virtual Environment
|
24
|
+
venv/
|
25
|
+
env/
|
26
|
+
ENV/
|
27
|
+
.env
|
28
|
+
.venv
|
29
|
+
|
30
|
+
# IDE
|
31
|
+
.idea/
|
32
|
+
.vscode/
|
33
|
+
*.swp
|
34
|
+
*.swo
|
35
|
+
.DS_Store
|
36
|
+
|
37
|
+
# Testing
|
38
|
+
.coverage
|
39
|
+
htmlcov/
|
40
|
+
.tox/
|
41
|
+
.pytest_cache/
|
42
|
+
.janito_last_response.txt
|
@@ -1,21 +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
|
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
21
|
SOFTWARE.
|
janito-0.8.0/PKG-INFO
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: janito
|
3
|
+
Version: 0.8.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: tomli
|
22
|
+
Requires-Dist: typer
|
23
|
+
Description-Content-Type: text/markdown
|
24
|
+
|
25
|
+
# 🤖 Janito
|
26
|
+
|
27
|
+
[](https://badge.fury.io/py/janito)
|
28
|
+
[](https://pypi.org/project/janito/)
|
29
|
+
[](https://opensource.org/licenses/MIT)
|
30
|
+
|
31
|
+
Janito is an AI-powered CLI tool designed to help developers manage and modify their codebase with ease. It leverages advanced AI models to understand and transform your code intelligently.
|
32
|
+
|
33
|
+
## ✨ Features
|
34
|
+
|
35
|
+
### 🔄 Code Modifications
|
36
|
+
- **Smart Code Changes**: Automated code modifications with AI understanding
|
37
|
+
- **Context-Aware**: Considers your entire codebase for accurate changes
|
38
|
+
- **Preview & Validate**: Review changes before applying them
|
39
|
+
|
40
|
+
### 💡 Code Analysis
|
41
|
+
- **Intelligent Queries**: Ask questions about your codebase
|
42
|
+
- **Deep Understanding**: Get detailed explanations about code functionality
|
43
|
+
- **Context-Rich Responses**: Answers based on your actual code
|
44
|
+
|
45
|
+
### ⚙️ Easy Configuration
|
46
|
+
- **Multiple AI Backends**: Support for Claude and DeepSeek AI
|
47
|
+
- **Flexible Setup**: Simple environment variable configuration
|
48
|
+
- **Workspace Control**: Fine-grained control over scanned files
|
49
|
+
|
50
|
+
## 🚀 Installation
|
51
|
+
|
52
|
+
### Prerequisites
|
53
|
+
- Python 3.8 or higher
|
54
|
+
- pip package manager
|
55
|
+
|
56
|
+
### Install from PyPI
|
57
|
+
```bash
|
58
|
+
pip install janito
|
59
|
+
```
|
60
|
+
|
61
|
+
## 🔧 Configuration
|
62
|
+
|
63
|
+
Set up your preferred AI backend using environment variables:
|
64
|
+
|
65
|
+
### For Claude AI
|
66
|
+
```bash
|
67
|
+
export ANTHROPIC_API_KEY=your_api_key
|
68
|
+
export AI_BACKEND=claudeai # Optional, detected from API key
|
69
|
+
```
|
70
|
+
|
71
|
+
### For DeepSeek AI
|
72
|
+
```bash
|
73
|
+
export DEEPSEEK_API_KEY=your_api_key
|
74
|
+
export AI_BACKEND=deepseekai # Optional, detected from API key
|
75
|
+
```
|
76
|
+
|
77
|
+
## 📖 Usage
|
78
|
+
|
79
|
+
### Basic Commands
|
80
|
+
|
81
|
+
1. **Ask Questions**
|
82
|
+
```bash
|
83
|
+
janito --ask "How does the error handling work in this codebase?"
|
84
|
+
```
|
85
|
+
|
86
|
+
2. **Request Changes**
|
87
|
+
```bash
|
88
|
+
janito "Add error handling to the process_data function"
|
89
|
+
```
|
90
|
+
|
91
|
+
3. **Preview Files**
|
92
|
+
```bash
|
93
|
+
janito --scan
|
94
|
+
```
|
95
|
+
|
96
|
+
### Advanced Options
|
97
|
+
|
98
|
+
- **Workspace Directory**: `-w, --workspace_dir PATH`
|
99
|
+
- **Include Paths**: `-i, --include PATH`
|
100
|
+
- **Recursive Scan**: `-r, --recursive PATH`
|
101
|
+
- **Debug Mode**: `--debug`
|
102
|
+
- **Verbose Output**: `--verbose`
|
103
|
+
|
104
|
+
## 📝 License
|
105
|
+
|
106
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
janito-0.8.0/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# 🤖 Janito
|
2
|
+
|
3
|
+
[](https://badge.fury.io/py/janito)
|
4
|
+
[](https://pypi.org/project/janito/)
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
6
|
+
|
7
|
+
Janito is an AI-powered CLI tool designed to help developers manage and modify their codebase with ease. It leverages advanced AI models to understand and transform your code intelligently.
|
8
|
+
|
9
|
+
## ✨ Features
|
10
|
+
|
11
|
+
### 🔄 Code Modifications
|
12
|
+
- **Smart Code Changes**: Automated code modifications with AI understanding
|
13
|
+
- **Context-Aware**: Considers your entire codebase for accurate changes
|
14
|
+
- **Preview & Validate**: Review changes before applying them
|
15
|
+
|
16
|
+
### 💡 Code Analysis
|
17
|
+
- **Intelligent Queries**: Ask questions about your codebase
|
18
|
+
- **Deep Understanding**: Get detailed explanations about code functionality
|
19
|
+
- **Context-Rich Responses**: Answers based on your actual code
|
20
|
+
|
21
|
+
### ⚙️ Easy Configuration
|
22
|
+
- **Multiple AI Backends**: Support for Claude and DeepSeek AI
|
23
|
+
- **Flexible Setup**: Simple environment variable configuration
|
24
|
+
- **Workspace Control**: Fine-grained control over scanned files
|
25
|
+
|
26
|
+
## 🚀 Installation
|
27
|
+
|
28
|
+
### Prerequisites
|
29
|
+
- Python 3.8 or higher
|
30
|
+
- pip package manager
|
31
|
+
|
32
|
+
### Install from PyPI
|
33
|
+
```bash
|
34
|
+
pip install janito
|
35
|
+
```
|
36
|
+
|
37
|
+
## 🔧 Configuration
|
38
|
+
|
39
|
+
Set up your preferred AI backend using environment variables:
|
40
|
+
|
41
|
+
### For Claude AI
|
42
|
+
```bash
|
43
|
+
export ANTHROPIC_API_KEY=your_api_key
|
44
|
+
export AI_BACKEND=claudeai # Optional, detected from API key
|
45
|
+
```
|
46
|
+
|
47
|
+
### For DeepSeek AI
|
48
|
+
```bash
|
49
|
+
export DEEPSEEK_API_KEY=your_api_key
|
50
|
+
export AI_BACKEND=deepseekai # Optional, detected from API key
|
51
|
+
```
|
52
|
+
|
53
|
+
## 📖 Usage
|
54
|
+
|
55
|
+
### Basic Commands
|
56
|
+
|
57
|
+
1. **Ask Questions**
|
58
|
+
```bash
|
59
|
+
janito --ask "How does the error handling work in this codebase?"
|
60
|
+
```
|
61
|
+
|
62
|
+
2. **Request Changes**
|
63
|
+
```bash
|
64
|
+
janito "Add error handling to the process_data function"
|
65
|
+
```
|
66
|
+
|
67
|
+
3. **Preview Files**
|
68
|
+
```bash
|
69
|
+
janito --scan
|
70
|
+
```
|
71
|
+
|
72
|
+
### Advanced Options
|
73
|
+
|
74
|
+
- **Workspace Directory**: `-w, --workspace_dir PATH`
|
75
|
+
- **Include Paths**: `-i, --include PATH`
|
76
|
+
- **Recursive Scan**: `-r, --recursive PATH`
|
77
|
+
- **Debug Mode**: `--debug`
|
78
|
+
- **Verbose Output**: `--verbose`
|
79
|
+
|
80
|
+
## 📝 License
|
81
|
+
|
82
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
@@ -0,0 +1,128 @@
|
|
1
|
+
import typer
|
2
|
+
from typing import Optional, List, Set
|
3
|
+
from pathlib import Path
|
4
|
+
from rich.text import Text
|
5
|
+
from rich import print as rich_print
|
6
|
+
from rich.console import Console
|
7
|
+
from .version import get_version
|
8
|
+
|
9
|
+
from janito.config import config
|
10
|
+
from janito.workspace import workset
|
11
|
+
from janito.workspace.models import ScanType # Add this import
|
12
|
+
from .cli.commands import (
|
13
|
+
handle_request, handle_ask,
|
14
|
+
handle_scan
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
app = typer.Typer(pretty_exceptions_enable=False)
|
19
|
+
|
20
|
+
# Initialize console for CLI output
|
21
|
+
console = Console()
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def typer_main(
|
26
|
+
user_request: Optional[str] = typer.Argument(None, help="User request"),
|
27
|
+
workspace_dir: Optional[Path] = typer.Option(None, "-w", "--workspace_dir", help="Working directory", file_okay=False, dir_okay=True),
|
28
|
+
debug: bool = typer.Option(False, "--debug", help="Show debug information"),
|
29
|
+
verbose: bool = typer.Option(False, "--verbose", help="Show verbose output"),
|
30
|
+
include: Optional[List[Path]] = typer.Option(None, "-i", "--include", help="Additional paths to include"),
|
31
|
+
ask: bool = typer.Option(False, "--ask", help="Treat the request as a question about the codebase"),
|
32
|
+
play: Optional[Path] = typer.Option(None, "--play", help="Replay a saved prompt file"),
|
33
|
+
replay: bool = typer.Option(False, "--replay", help="Trigger the replay response flow"),
|
34
|
+
scan: bool = typer.Option(False, "--scan", help="Preview files that would be analyzed"),
|
35
|
+
version: bool = typer.Option(False, "--version", help="Show version information"),
|
36
|
+
test_cmd: Optional[str] = typer.Option(None, "--test", help="Command to run tests after changes"),
|
37
|
+
auto_apply: bool = typer.Option(False, "--auto-apply", help="Apply changes without confirmation"),
|
38
|
+
recursive: Optional[List[Path]] = typer.Option(None, "-r", "--recursive", help="Paths to scan recursively (directories only)"),
|
39
|
+
skip_work: bool = typer.Option(False, "-s", "--skip-work", help="Skip scanning workspace_dir when using include paths"),
|
40
|
+
):
|
41
|
+
"""Janito - AI-powered code modification assistant"""
|
42
|
+
if version:
|
43
|
+
console.print(f"Janito version {get_version()}")
|
44
|
+
return
|
45
|
+
|
46
|
+
# Check if workspace directory exists and handle creation
|
47
|
+
if workspace_dir and not workspace_dir.exists():
|
48
|
+
create = typer.confirm(f"\nWorkspace directory '{workspace_dir}' does not exist. Create it?")
|
49
|
+
if create:
|
50
|
+
try:
|
51
|
+
workspace_dir.mkdir(parents=True)
|
52
|
+
console.print(f"[green]Created workspace directory: {workspace_dir}[/green]")
|
53
|
+
except Exception as e:
|
54
|
+
error_text = Text(f"\nError: Failed to create workspace directory: {e}", style="red")
|
55
|
+
rich_print(error_text)
|
56
|
+
raise typer.Exit(1)
|
57
|
+
else:
|
58
|
+
error_text = Text("\nError: Workspace directory does not exist and was not created", style="red")
|
59
|
+
rich_print(error_text)
|
60
|
+
raise typer.Exit(1)
|
61
|
+
|
62
|
+
# Configure workspace
|
63
|
+
config.set_workspace_dir(workspace_dir)
|
64
|
+
config.set_debug(debug)
|
65
|
+
config.set_verbose(verbose)
|
66
|
+
config.set_auto_apply(auto_apply)
|
67
|
+
|
68
|
+
# Configure workset with scan paths
|
69
|
+
if include:
|
70
|
+
if config.debug:
|
71
|
+
Console(stderr=True).print("[cyan]Debug: Processing include paths...[/cyan]")
|
72
|
+
for path in include:
|
73
|
+
full_path = config.workspace_dir / path
|
74
|
+
if not full_path.resolve().is_relative_to(config.workspace_dir):
|
75
|
+
error_text = Text(f"\nError: Path must be within workspace: {path}", style="red")
|
76
|
+
rich_print(error_text)
|
77
|
+
raise typer.Exit(1)
|
78
|
+
workset.add_scan_path(path, ScanType.PLAIN)
|
79
|
+
|
80
|
+
if recursive:
|
81
|
+
if config.debug:
|
82
|
+
Console(stderr=True).print("[cyan]Debug: Processing recursive paths...[/cyan]")
|
83
|
+
for path in recursive:
|
84
|
+
full_path = config.workspace_dir / path
|
85
|
+
if not path.is_dir():
|
86
|
+
error_text = Text(f"\nError: Recursive path must be a directory: {path} ", style="red")
|
87
|
+
rich_print(error_text)
|
88
|
+
raise typer.Exit(1)
|
89
|
+
if not full_path.resolve().is_relative_to(config.workspace_dir):
|
90
|
+
error_text = Text(f"\nError: Path must be within workspace: {path}", style="red")
|
91
|
+
rich_print(error_text)
|
92
|
+
raise typer.Exit(1)
|
93
|
+
workset.add_scan_path(path, ScanType.RECURSIVE)
|
94
|
+
|
95
|
+
# Validate skip_work usage
|
96
|
+
if skip_work:
|
97
|
+
# Check if any include or recursive paths are provided
|
98
|
+
if not include and not recursive:
|
99
|
+
error_text = Text("\nError: --skip-work requires at least one include path (-i or -r)", style="red")
|
100
|
+
rich_print(error_text)
|
101
|
+
raise typer.Exit(1)
|
102
|
+
# Remove root path from workset when skip_work is enabled
|
103
|
+
workset._scan_paths = [p for p in workset._scan_paths if p.path != Path(".")]
|
104
|
+
|
105
|
+
if test_cmd:
|
106
|
+
config.set_test_cmd(test_cmd)
|
107
|
+
|
108
|
+
# Refresh workset content before handling commands
|
109
|
+
workset.refresh()
|
110
|
+
|
111
|
+
if ask:
|
112
|
+
if not user_request:
|
113
|
+
error_text = Text("\nError: No question provided. Please provide a question as the main argument when using --ask", style="red")
|
114
|
+
rich_print(error_text)
|
115
|
+
raise typer.Exit(1)
|
116
|
+
handle_ask(user_request)
|
117
|
+
elif play:
|
118
|
+
handle_play(play)
|
119
|
+
elif scan:
|
120
|
+
handle_scan()
|
121
|
+
else:
|
122
|
+
handle_request(user_request, replay=replay)
|
123
|
+
|
124
|
+
def main():
|
125
|
+
typer.run(typer_main)
|
126
|
+
|
127
|
+
if __name__ == "__main__":
|
128
|
+
main()
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
# Try to determine backend from available API keys if not explicitly set
|
4
|
+
ai_backend = os.getenv('AI_BACKEND', '').lower()
|
5
|
+
|
6
|
+
if not ai_backend:
|
7
|
+
if os.getenv('ANTHROPIC_API_KEY'):
|
8
|
+
ai_backend = 'claudeai'
|
9
|
+
elif os.getenv('DEEPSEEK_API_KEY'):
|
10
|
+
ai_backend = 'deepseekai'
|
11
|
+
else:
|
12
|
+
raise ValueError("No AI backend API keys found. Please set either ANTHROPIC_API_KEY or DEEPSEEK_API_KEY")
|
13
|
+
|
14
|
+
if ai_backend == "deepseekai":
|
15
|
+
from .deepseekai import DeepSeekAIAgent as AIAgent
|
16
|
+
elif ai_backend == 'claudeai':
|
17
|
+
from .claudeai import ClaudeAIAgent as AIAgent
|
18
|
+
else:
|
19
|
+
raise ValueError(f"Unsupported AI_BACKEND: {ai_backend}")
|
20
|
+
|
21
|
+
# Create a singleton instance
|
22
|
+
agent = AIAgent()
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
class Agent(ABC):
|
5
|
+
"""Abstract base class for AI agents"""
|
6
|
+
friendly_name = "Unknown"
|
7
|
+
|
8
|
+
def __init__(self, api_key: Optional[str] = None):
|
9
|
+
self.api_key = api_key
|
10
|
+
self.last_prompt = None
|
11
|
+
self.last_full_message = None
|
12
|
+
self.last_response = None
|
13
|
+
|
14
|
+
@abstractmethod
|
15
|
+
def send_message(self, message: str, system: str) -> str:
|
16
|
+
"""Send message to the AI agent
|
17
|
+
|
18
|
+
Args:
|
19
|
+
message: The message to send
|
20
|
+
stop_event: Optional event to signal cancellation
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
The response from the AI agent
|
24
|
+
"""
|
25
|
+
pass
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import anthropic
|
2
|
+
import os
|
3
|
+
|
4
|
+
from .agent import Agent
|
5
|
+
|
6
|
+
class ClaudeAIAgent(Agent):
|
7
|
+
"""Handles interaction with Claude API, including message handling"""
|
8
|
+
DEFAULT_MODEL = "claude-3-5-sonnet-20241022"
|
9
|
+
friendly_name = "Claude"
|
10
|
+
|
11
|
+
def __init__(self):
|
12
|
+
self.api_key = os.getenv('ANTHROPIC_API_KEY')
|
13
|
+
super().__init__(self.api_key)
|
14
|
+
|
15
|
+
if not self.api_key:
|
16
|
+
raise ValueError("ANTHROPIC_API_KEY environment variable is required")
|
17
|
+
self.client = anthropic.Client(api_key=self.api_key)
|
18
|
+
self.model = os.getenv('CLAUDE_MODEL', self.DEFAULT_MODEL)
|
19
|
+
self.last_prompt = None
|
20
|
+
self.last_full_message = None
|
21
|
+
self.last_response = None
|
22
|
+
|
23
|
+
|
24
|
+
def send_message(self, system_message: str, message: str) -> str:
|
25
|
+
"""Send message to Claude API and return response"""
|
26
|
+
# Store the full message
|
27
|
+
self.last_full_message = message
|
28
|
+
|
29
|
+
response = self.client.messages.create(
|
30
|
+
model=self.model, # Use discovered model
|
31
|
+
system=system_message or self.system_message,
|
32
|
+
max_tokens=8192,
|
33
|
+
messages=[
|
34
|
+
{"role": "user", "content": message}
|
35
|
+
],
|
36
|
+
temperature=0,
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
# Always return the response, let caller handle cancellation
|
41
|
+
return response
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from openai import OpenAI
|
2
|
+
import os
|
3
|
+
from typing import Optional
|
4
|
+
from threading import Event
|
5
|
+
from .agent import Agent
|
6
|
+
|
7
|
+
class DeepSeekAIAgent(Agent):
|
8
|
+
""" DeepSeek AI Agent """
|
9
|
+
DEFAULT_MODEL = "deepseek-chat"
|
10
|
+
friendly_name = "DeepSeek"
|
11
|
+
api_key = None
|
12
|
+
|
13
|
+
def __init__(self, system_prompt: str = None):
|
14
|
+
self.api_key = os.getenv('DEEPSEEK_API_KEY')
|
15
|
+
super().__init__(self.api_key, system_prompt)
|
16
|
+
if not system_prompt:
|
17
|
+
raise ValueError("system_prompt is required")
|
18
|
+
if not self.api_key:
|
19
|
+
raise ValueError("DEEPSEEK_API_KEY environment variable is required")
|
20
|
+
self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
|
21
|
+
self.model = self.DEFAULT_MODEL
|
22
|
+
self.system_message = system_prompt
|
23
|
+
|
24
|
+
def send_message(self, message: str, system_message: str = None) -> str:
|
25
|
+
"""Send message to OpenAI API and return response"""
|
26
|
+
self.last_full_message = message
|
27
|
+
|
28
|
+
try:
|
29
|
+
messages = [
|
30
|
+
{ "role": "system", "content": system_message},
|
31
|
+
{ "role": "user", "content": message}
|
32
|
+
]
|
33
|
+
|
34
|
+
response = self.client.chat.completions.create(
|
35
|
+
model=self.model,
|
36
|
+
messages=messages,
|
37
|
+
max_completion_tokens=4096,
|
38
|
+
temperature=0,
|
39
|
+
)
|
40
|
+
|
41
|
+
response_text = response.choices[0].message.content
|
42
|
+
self.last_response = response_text
|
43
|
+
|
44
|
+
return response
|
45
|
+
|
46
|
+
except KeyboardInterrupt:
|
47
|
+
return ""
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import List, Optional
|
4
|
+
from .edit_blocks import EditType
|
5
|
+
|
6
|
+
@dataclass
|
7
|
+
class AppliedBlock:
|
8
|
+
filename: Path
|
9
|
+
edit_type: EditType
|
10
|
+
reason: str
|
11
|
+
original_content: List[str]
|
12
|
+
modified_content: List[str]
|
13
|
+
range_start: int
|
14
|
+
range_end: int
|
15
|
+
block_marker: Optional[str] = None
|
16
|
+
error_message: Optional[str] = None
|
17
|
+
has_error: bool = False
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class AppliedBlocks:
|
21
|
+
blocks: List[AppliedBlock]
|
22
|
+
|
23
|
+
def get_changes_summary(self):
|
24
|
+
"""Get summary info for all applied blocks"""
|
25
|
+
return [{
|
26
|
+
'file': block.filename,
|
27
|
+
'type': block.edit_type.name,
|
28
|
+
'reason': block.reason,
|
29
|
+
'lines_original': len(block.original_content),
|
30
|
+
'lines_modified': len(block.modified_content),
|
31
|
+
'range_start': block.range_start,
|
32
|
+
'range_end': block.range_end,
|
33
|
+
'block_marker': block.block_marker
|
34
|
+
} for block in self.blocks]
|