janito 0.6.0__py3-none-any.whl → 0.8.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.
Files changed (116) hide show
  1. janito/__main__.py +127 -134
  2. janito/agents/__init__.py +22 -16
  3. janito/agents/agent.py +24 -20
  4. janito/agents/claudeai.py +41 -55
  5. janito/agents/deepseekai.py +47 -0
  6. janito/change/applied_blocks.py +34 -0
  7. janito/change/applier.py +167 -0
  8. janito/change/edit_blocks.py +148 -0
  9. janito/change/finder.py +72 -0
  10. janito/change/request.py +144 -0
  11. janito/change/validator.py +87 -251
  12. janito/change/view/content.py +63 -0
  13. janito/change/{viewer → view}/diff.py +44 -43
  14. janito/change/view/panels.py +201 -0
  15. janito/change/view/sections.py +69 -0
  16. janito/change/view/styling.py +140 -0
  17. janito/change/view/summary.py +37 -0
  18. janito/change/{viewer → view}/themes.py +62 -55
  19. janito/change/view/viewer.py +59 -0
  20. janito/cli/__init__.py +1 -1
  21. janito/cli/commands.py +68 -45
  22. janito/cli/functions.py +66 -111
  23. janito/common.py +132 -53
  24. janito/config.py +99 -101
  25. janito/data/change_prompt.txt +81 -0
  26. janito/data/system_prompt.txt +3 -0
  27. janito/qa.py +56 -66
  28. janito/version.py +22 -22
  29. janito/workspace/__init__.py +8 -7
  30. janito/workspace/analysis.py +120 -120
  31. janito/workspace/models.py +97 -0
  32. janito/workspace/show.py +115 -0
  33. janito/workspace/stats.py +42 -0
  34. janito/workspace/workset.py +135 -0
  35. janito/workspace/workspace.py +335 -0
  36. janito-0.8.0.dist-info/METADATA +106 -0
  37. janito-0.8.0.dist-info/RECORD +40 -0
  38. {janito-0.6.0.dist-info → janito-0.8.0.dist-info}/licenses/LICENSE +20 -20
  39. janito/__init__.py +0 -2
  40. janito/agents/openai.py +0 -53
  41. janito/agents/test.py +0 -34
  42. janito/change/__init__.py +0 -32
  43. janito/change/__main__.py +0 -0
  44. janito/change/analysis/__init__.py +0 -23
  45. janito/change/analysis/__main__.py +0 -7
  46. janito/change/analysis/analyze.py +0 -61
  47. janito/change/analysis/formatting.py +0 -78
  48. janito/change/analysis/options.py +0 -81
  49. janito/change/analysis/prompts.py +0 -98
  50. janito/change/analysis/view/__init__.py +0 -9
  51. janito/change/analysis/view/terminal.py +0 -171
  52. janito/change/applier/__init__.py +0 -5
  53. janito/change/applier/file.py +0 -58
  54. janito/change/applier/main.py +0 -156
  55. janito/change/applier/text.py +0 -245
  56. janito/change/applier/workspace_dir.py +0 -58
  57. janito/change/core.py +0 -131
  58. janito/change/history.py +0 -44
  59. janito/change/operations.py +0 -7
  60. janito/change/parser.py +0 -289
  61. janito/change/play.py +0 -54
  62. janito/change/preview.py +0 -82
  63. janito/change/prompts.py +0 -126
  64. janito/change/test.py +0 -0
  65. janito/change/viewer/__init__.py +0 -11
  66. janito/change/viewer/content.py +0 -66
  67. janito/change/viewer/pager.py +0 -56
  68. janito/change/viewer/panels.py +0 -555
  69. janito/change/viewer/styling.py +0 -103
  70. janito/clear_statement_parser/clear_statement_format.txt +0 -328
  71. janito/clear_statement_parser/examples.txt +0 -326
  72. janito/clear_statement_parser/models.py +0 -104
  73. janito/clear_statement_parser/parser.py +0 -496
  74. janito/cli/base.py +0 -30
  75. janito/cli/handlers/ask.py +0 -22
  76. janito/cli/handlers/demo.py +0 -22
  77. janito/cli/handlers/request.py +0 -24
  78. janito/cli/handlers/scan.py +0 -9
  79. janito/cli/history.py +0 -61
  80. janito/cli/registry.py +0 -26
  81. janito/demo/__init__.py +0 -4
  82. janito/demo/data.py +0 -13
  83. janito/demo/mock_data.py +0 -20
  84. janito/demo/operations.py +0 -45
  85. janito/demo/runner.py +0 -59
  86. janito/demo/scenarios.py +0 -32
  87. janito/prompts.py +0 -2
  88. janito/review.py +0 -13
  89. janito/search_replace/README.md +0 -146
  90. janito/search_replace/__init__.py +0 -6
  91. janito/search_replace/__main__.py +0 -21
  92. janito/search_replace/core.py +0 -119
  93. janito/search_replace/parser.py +0 -52
  94. janito/search_replace/play.py +0 -61
  95. janito/search_replace/replacer.py +0 -36
  96. janito/search_replace/searcher.py +0 -299
  97. janito/shell/__init__.py +0 -39
  98. janito/shell/bus.py +0 -31
  99. janito/shell/commands.py +0 -195
  100. janito/shell/handlers.py +0 -122
  101. janito/shell/history.py +0 -20
  102. janito/shell/processor.py +0 -52
  103. janito/tui/__init__.py +0 -21
  104. janito/tui/base.py +0 -22
  105. janito/tui/flows/__init__.py +0 -5
  106. janito/tui/flows/changes.py +0 -65
  107. janito/tui/flows/content.py +0 -128
  108. janito/tui/flows/selection.py +0 -117
  109. janito/tui/screens/__init__.py +0 -3
  110. janito/tui/screens/app.py +0 -1
  111. janito/workspace/manager.py +0 -48
  112. janito/workspace/scan.py +0 -232
  113. janito-0.6.0.dist-info/METADATA +0 -185
  114. janito-0.6.0.dist-info/RECORD +0 -95
  115. {janito-0.6.0.dist-info → janito-0.8.0.dist-info}/WHEEL +0 -0
  116. {janito-0.6.0.dist-info → janito-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,335 @@
1
+ from pathlib import Path
2
+ from typing import List, Set, Dict, Optional, Tuple
3
+ import time
4
+ from rich.console import Console
5
+ from janito.config import config
6
+ from .models import WorksetContent, FileInfo, ScanPath # Add ScanPath import
7
+ import tempfile
8
+ import shutil
9
+ import pathspec
10
+
11
+ class PathNotRelativeError(Exception):
12
+ """Raised when a path is not relative."""
13
+ pass
14
+
15
+ class Workspace:
16
+ """Handles workspace scanning and content management."""
17
+
18
+ _instance = None
19
+
20
+ def __new__(cls):
21
+ if cls._instance is None:
22
+ cls._instance = super().__new__(cls)
23
+ cls._instance._initialized = False
24
+ return cls._instance
25
+
26
+ def __init__(self):
27
+ if not self._initialized:
28
+ self._content = WorksetContent()
29
+ self._root_dirs: Set[Path] = set()
30
+ self._initialized = True
31
+
32
+ def scan_files(self, paths: List[Path], recursive_paths: Set[Path]) -> None:
33
+ """Scan files from given paths and update content.
34
+
35
+ Args:
36
+ paths: List of paths to scan
37
+ recursive_paths: Set of paths to scan recursively
38
+ """
39
+ for path in paths:
40
+ if path.is_absolute():
41
+ raise PathNotRelativeError(f"Path must be relative: {path}")
42
+
43
+ scan_time = time.time()
44
+
45
+ if config.debug:
46
+ console = Console(stderr=True)
47
+ console.print(f"\n[cyan]Debug: Starting scan of {len(paths)} paths[/cyan]")
48
+
49
+ # Find root directories if scanning workspace root
50
+ if not config.skip_work and Path(".") in paths:
51
+ self._root_dirs = {
52
+ path.relative_to(config.workspace_dir)
53
+ for path in config.workspace_dir.iterdir()
54
+ if path.is_dir() and not path.name.startswith('.')
55
+ }
56
+ if config.debug:
57
+ Console(stderr=True).print(f"[cyan]Debug: Found root directories: {self._root_dirs}[/cyan]")
58
+
59
+ processed_files: Set[Path] = set()
60
+ for path in paths:
61
+ abs_path = config.workspace_dir / path
62
+ # Skip workspace root if skip_work is enabled
63
+ if config.skip_work and path == Path("."):
64
+ if config.debug:
65
+ Console(stderr=True).print("[cyan]Debug: Skipping workspace root due to skip_work[/cyan]")
66
+ continue
67
+ self._scan_path(abs_path, processed_files, scan_time, recursive_paths)
68
+
69
+ self._content.scan_completed = True
70
+ self._content.analyzed = False
71
+ self._content.scanned_paths = set(paths)
72
+
73
+ def _scan_path(self, path: Path, processed_files: Set[Path], scan_time: float,
74
+ recursive_paths: Set[Path]) -> None:
75
+ """Scan a single path and process its contents."""
76
+ if path in processed_files:
77
+ return
78
+
79
+ # Convert recursive_paths to absolute for comparison
80
+ abs_recursive_paths = {config.workspace_dir / p for p in recursive_paths}
81
+
82
+ path = path.resolve()
83
+ processed_files.add(path)
84
+
85
+ if path.is_dir():
86
+ try:
87
+ for item in path.iterdir():
88
+ if item.name.startswith(('.', '__pycache__')):
89
+ continue
90
+ if path in abs_recursive_paths:
91
+ self._scan_path(item, processed_files, scan_time, recursive_paths)
92
+ elif item.is_file():
93
+ self._scan_path(item, processed_files, scan_time, recursive_paths)
94
+ except PermissionError:
95
+ if config.debug:
96
+ Console(stderr=True).print(f"[red]Debug: Permission denied: {path}[/red]")
97
+ elif path.is_file():
98
+ self._process_file(path, scan_time)
99
+
100
+ def _process_file(self, path: Path, scan_time: float, force_update: bool = False) -> None:
101
+ """Process a single file and add it to the content."""
102
+ try:
103
+ # Check if file has supported extension or no extension
104
+ supported_extensions = {
105
+ '.py', '.md', '.txt', '.json', '.yaml', '.yml', '.toml',
106
+ '.html', '.htm', '.css', '.js'
107
+ }
108
+ if path.suffix.lower() in supported_extensions or not path.suffix:
109
+ content = path.read_text(encoding='utf-8')
110
+ rel_path = path.relative_to(config.workspace_dir)
111
+
112
+ # Check if file already exists in content
113
+ existing_files = [f for f in self._content.files if f.name == str(rel_path)]
114
+ if existing_files and not force_update:
115
+ if config.debug:
116
+ Console(stderr=True).print(f"[yellow]Debug: Skipping duplicate file: {rel_path}[/yellow]")
117
+ return
118
+ elif existing_files:
119
+ # Update existing file content
120
+ existing_files[0].content = content
121
+ existing_files[0].seconds_ago = int(scan_time - path.stat().st_mtime)
122
+ if config.debug:
123
+ Console(stderr=True).print(f"[cyan]Debug: Updated content: {rel_path}[/cyan]")
124
+ else:
125
+ # Add new file
126
+ seconds_ago = int(scan_time - path.stat().st_mtime)
127
+ file_info = FileInfo(
128
+ name=str(rel_path),
129
+ content=content,
130
+ seconds_ago=seconds_ago
131
+ )
132
+ self._content.add_file(file_info)
133
+ if config.debug:
134
+ Console(stderr=True).print(f"[cyan]Debug: Added file: {rel_path}[/cyan]")
135
+
136
+ except (UnicodeDecodeError, PermissionError) as e:
137
+ if config.debug:
138
+ Console(stderr=True).print(f"[red]Debug: Error reading file {path}: {str(e)}[/red]")
139
+
140
+ def create_file(self, path: Path, content: str) -> None:
141
+ """Create a new file in the workspace.
142
+
143
+ Args:
144
+ path: Relative path to the file to create
145
+ content: Content to write to the file
146
+
147
+ Raises:
148
+ PathNotRelativeError: If path is absolute
149
+ FileExistsError: If file already exists
150
+ OSError: If parent directory creation fails
151
+ """
152
+ if path.is_absolute():
153
+ raise PathNotRelativeError(f"Path must be relative: {path}")
154
+
155
+ abs_path = config.workspace_dir / path
156
+
157
+ if abs_path.exists():
158
+ raise FileExistsError(f"File already exists: {path}")
159
+
160
+ # Create parent directories if they don't exist
161
+ abs_path.parent.mkdir(parents=True, exist_ok=True)
162
+
163
+ # Write the file
164
+ abs_path.write_text(content, encoding='utf-8')
165
+
166
+ if config.debug:
167
+ Console(stderr=True).print(f"[green]Debug: Created file: {path}[/green]")
168
+
169
+ # Add to workspace content
170
+ scan_time = time.time()
171
+ self._process_file(abs_path, scan_time)
172
+
173
+ def clear(self) -> None:
174
+ """Clear all workspace content and settings."""
175
+ self._content = WorksetContent()
176
+
177
+ @property
178
+ def content(self) -> WorksetContent:
179
+ """Get the workspace content."""
180
+ return self._content
181
+
182
+ @property
183
+ def root_directories(self) -> Set[Path]:
184
+ """Get the directories found at the workspace root."""
185
+ return self._root_dirs
186
+
187
+ def modify_file(self, path: Path, content: str) -> None:
188
+ """Modify an existing file in the workspace.
189
+
190
+ Args:
191
+ path: Relative path to the file to modify
192
+ content: New content for the file
193
+
194
+ Raises:
195
+ PathNotRelativeError: If path is absolute
196
+ FileNotFoundError: If file doesn't exist
197
+ OSError: If write fails
198
+ """
199
+ if path.is_absolute():
200
+ raise PathNotRelativeError(f"Path must be relative: {path}")
201
+
202
+ abs_path = config.workspace_dir / path
203
+
204
+ if not abs_path.exists():
205
+ raise FileNotFoundError(f"File does not exist: {path}")
206
+
207
+ # Write the file
208
+ abs_path.write_text(content, encoding='utf-8')
209
+
210
+ if config.debug:
211
+ Console(stderr=True).print(f"[green]Debug: Modified file: {path}[/green]")
212
+
213
+ # Update workspace content
214
+ scan_time = time.time()
215
+ self._process_file(abs_path, scan_time, force_update=True)
216
+
217
+ def setup_preview_directory(self) -> None:
218
+ """Setup the preview directory with workspace contents.
219
+
220
+ Creates a copy of the current workspace contents in the preview directory.
221
+ Respects .gitignore patterns and excludes .git directory.
222
+ """
223
+ self._preview_dir = Path(tempfile.mkdtemp(prefix='janito_preview_'))
224
+
225
+ # Read .gitignore if it exists
226
+ gitignore_path = config.workspace_dir / '.gitignore'
227
+ if (gitignore_path.exists()):
228
+ gitignore = gitignore_path.read_text().splitlines()
229
+ # Always ignore .git directory
230
+ gitignore.append('.git')
231
+ spec = pathspec.PathSpec.from_lines('gitwildmatch', gitignore)
232
+ else:
233
+ # If no .gitignore exists, only ignore .git
234
+ spec = pathspec.PathSpec.from_lines('gitwildmatch', ['.git'])
235
+
236
+ # Copy workspace contents to preview directory
237
+ for item in config.workspace_dir.iterdir():
238
+ # Get relative path for gitignore matching
239
+ rel_path = item.relative_to(config.workspace_dir)
240
+
241
+ # Skip if matches gitignore patterns
242
+ if spec.match_file(str(rel_path)):
243
+ continue
244
+
245
+ # Skip hidden files/directories except .gitignore
246
+ if item.name.startswith('.') and item.name != '.gitignore':
247
+ continue
248
+
249
+ if item.is_dir():
250
+ # For directories, we need to filter contents based on gitignore
251
+ def copy_filtered(src, dst):
252
+ shutil.copytree(
253
+ src,
254
+ dst,
255
+ ignore=lambda d, files: [
256
+ f for f in files
257
+ if spec.match_file(str(Path(d).relative_to(config.workspace_dir) / f))
258
+ ]
259
+ )
260
+
261
+ copy_filtered(item, self._preview_dir / item.name)
262
+ else:
263
+ shutil.copy2(item, self._preview_dir / item.name)
264
+
265
+ return self._preview_dir
266
+
267
+
268
+ def preview_create_file(self, path: Path, content: str) -> None:
269
+ """Create a new file in the preview directory.
270
+
271
+ Args:
272
+ path: Relative path to the file to create
273
+ content: Content to write to the file
274
+ """
275
+ preview_path = self.get_preview_path(path)
276
+ preview_path.parent.mkdir(parents=True, exist_ok=True)
277
+ preview_path.write_text(content, encoding='utf-8')
278
+
279
+ def preview_modify_file(self, path: Path, content: str) -> None:
280
+ """Modify a file in the preview directory.
281
+
282
+ Args:
283
+ path: Relative path to the file to modify
284
+ content: New content for the file
285
+ """
286
+ preview_path = self.get_preview_path(path)
287
+ if not preview_path.exists():
288
+ raise FileNotFoundError(f"File does not exist in preview: {path}")
289
+ preview_path.write_text(content, encoding='utf-8')
290
+
291
+ def get_preview_path(self, path: Path) -> Path:
292
+ """Get the path to the preview directory."""
293
+ return self._preview_dir / path
294
+
295
+ def delete_file(self, path: Path) -> None:
296
+ """Delete a file from the workspace.
297
+
298
+ Args:
299
+ path: Relative path to the file to delete
300
+
301
+ Raises:
302
+ PathNotRelativeError: If path is absolute
303
+ FileNotFoundError: If file doesn't exist
304
+ """
305
+ if path.is_absolute():
306
+ raise PathNotRelativeError(f"Path must be relative: {path}")
307
+
308
+ abs_path = config.workspace_dir / path
309
+
310
+ if not abs_path.exists():
311
+ raise FileNotFoundError(f"File does not exist: {path}")
312
+
313
+ # Delete the file
314
+ abs_path.unlink()
315
+
316
+ if config.debug:
317
+ Console(stderr=True).print(f"[green]Debug: Deleted file: {path}[/green]")
318
+
319
+ def apply_changes(self, preview_dir: Path, created_files: List[Path], modified_files: Set[Path], deleted_files: Set[Path]):
320
+ """Apply changes from preview directory to workspace."""
321
+ for filename in created_files:
322
+ content = (preview_dir / filename).read_text(encoding='utf-8')
323
+ self.create_file(filename, content)
324
+ print("Created workspace file: ", filename)
325
+
326
+ for filename in modified_files:
327
+ content = (preview_dir / filename).read_text(encoding='utf-8')
328
+ self.modify_file(filename, content)
329
+ print("Modified workspace file: ", filename) # This will now include cleaned files
330
+
331
+ for filename in deleted_files:
332
+ self.delete_file(filename)
333
+ print("Deleted workspace file: ", filename)
334
+
335
+
@@ -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
+ [![PyPI version](https://badge.fury.io/py/janito.svg)](https://badge.fury.io/py/janito)
28
+ [![Python Versions](https://img.shields.io/pypi/pyversions/janito.svg)](https://pypi.org/project/janito/)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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.
@@ -0,0 +1,40 @@
1
+ janito/__main__.py,sha256=xYzdqkp-7L39F1aS7clcAFmdy_wwN-xFhrMSJJO3LZw,5640
2
+ janito/common.py,sha256=OiOVIP34cna6i6Kc0kTGKM8jx9pudvOuapmkkDl8bpA,5114
3
+ janito/config.py,sha256=3HNnlwLC6BxLHSMT-7PpG0rJekaJtZGLS8ZUmC-4hVg,3163
4
+ janito/qa.py,sha256=iv53YOCtoT0-gUDDStL_WArxh0fqcbq3meZHFtUXOH4,1689
5
+ janito/version.py,sha256=IEZ9-3cWXfCUmhFoP_LX02zvFfirWvYLDaRWJTHEh0s,761
6
+ janito/agents/__init__.py,sha256=R4nHZ31OhqanLaHlFFtaz2FMTsFhhW74QvmvGfkdtpo,728
7
+ janito/agents/agent.py,sha256=EZVtipg5WLbHR8JtwWMZaPWLtMvtlnEF8mY6kTvgg-Y,722
8
+ janito/agents/claudeai.py,sha256=Cl-9eSDtVNMEW32Lp8XMUHyCKnBqtRYd4jn67drz8w8,1384
9
+ janito/agents/deepseekai.py,sha256=viPI7xMdl_4tU9JbLtYpWUXZ-03M8rHQlqXN4aC0Xpo,1667
10
+ janito/change/applied_blocks.py,sha256=IIYM_CBHyjBbG4YfU_g5CAjfSafCQh4J_EyljyByLjM,1040
11
+ janito/change/applier.py,sha256=ByVfURYq6iFn5BQyPOfEbGvJfLzty6PgiuWZN9quE10,7266
12
+ janito/change/edit_blocks.py,sha256=i3Ka_zHJRjaV-iInAH0uJV0QAtV0mvzRFDqDmaR7oRw,5483
13
+ janito/change/finder.py,sha256=PhXA_3dNwDSgseEVdphIpo14n1zgbgqwGw4bD4EiRk4,2786
14
+ janito/change/request.py,sha256=W1-yuzO385rNPuUozlSh_g8nDToKuwZaRkk7Mxo0TKQ,5904
15
+ janito/change/validator.py,sha256=2E6kNKzWnu6QYuhYN9SAu4TNKuDJXf3KDQuG4AryTpg,3277
16
+ janito/change/view/content.py,sha256=Yl_llNnxmnfgaaPGod1Q1sW85Rievhm9d1ZQFpMVSTo,1594
17
+ janito/change/view/diff.py,sha256=32DPm4e-bt8qljvgV-5hGICrmxBiOCQLE0dhF63vJ9k,1859
18
+ janito/change/view/panels.py,sha256=-MzpzElAtyObtioloAVGnhJMwEWAHwvh9kjnrR1VF9w,6643
19
+ janito/change/view/sections.py,sha256=SrqWfmNHWbV4JvW_p7ynEoNvJUpiRg-IGVi4MLU1E-Q,2829
20
+ janito/change/view/styling.py,sha256=Mze3WCn_6OPhKI6RFtjiqZJFNP3f-W_OPAI4Qwd_zGw,5483
21
+ janito/change/view/summary.py,sha256=zCxyH_0TN4SPOikXYQEsKasBYX-8ELit62cupbTNCD4,1409
22
+ janito/change/view/themes.py,sha256=g1VURi51siZrVnN8eE8Dh3lfh4lvoLCDpOHfXZ-4iss,1774
23
+ janito/change/view/viewer.py,sha256=wAa_2mYS_ihZckpZ2VKFKf4TiXusnRAS6aNbEinqYWk,2165
24
+ janito/cli/__init__.py,sha256=2tXs98pDDXyU-QTLJ-QpUKpjJJvxObRDGIgRtesVQP4,78
25
+ janito/cli/commands.py,sha256=-PUmDBRdvOPgwmY9orbWAkUEyW5HgUyINotapG8I3L8,1826
26
+ janito/cli/functions.py,sha256=c3-HtIKzKpSebewFCWBhaR0BFCttHyvA2xeACSPUgRw,2543
27
+ janito/data/change_prompt.txt,sha256=iGPjxWvFBkxyCzny_9E6cSUg74c0gHenBNoga6YW7EM,3357
28
+ janito/data/system_prompt.txt,sha256=ySqjFj9bbJJC6xNaX9gOAEbLp08Jc9ckUEtu7bSQGkA,161
29
+ janito/workspace/__init__.py,sha256=KlU4uajYszb8qmvAhJDzOT4J9Uf04MoomrePP_wlBgI,188
30
+ janito/workspace/analysis.py,sha256=pRzYzBm6OqaQ8MTlbZeeDXQkRjEKFLfN-fz3EUw1sQQ,4204
31
+ janito/workspace/models.py,sha256=A4Hry2N1ZHMsZ60Vmi2ovR1oL7lEUGyiPWhcGxS5fSs,3283
32
+ janito/workspace/show.py,sha256=ReJ97X6DBLiIcfHpTK6TZ6RLZ5g_U-2y8BIDHHBJGWk,3885
33
+ janito/workspace/stats.py,sha256=0uOcyjoz_ZdtJK0thcZsvqULxYaZSVSsAtuK8k_57ps,1299
34
+ janito/workspace/workset.py,sha256=qDtxUwjIbg87VfSmHMXfVfQ28uIeWrpvCC24XtvVNbw,4302
35
+ janito/workspace/workspace.py,sha256=aXs37H-hfFQEGoAQJzQKB_wH0zCkxqVv2YiE8hPzjdQ,13565
36
+ janito-0.8.0.dist-info/METADATA,sha256=s1PMT0iTn91rMzCa11ANf-UH2DNmhEoY97l6x6KUz2U,3156
37
+ janito-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ janito-0.8.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
39
+ janito-0.8.0.dist-info/licenses/LICENSE,sha256=agBzvmwXtxr2qfxpwmoi8VabRb2AXflZeTZA3AjZeJE,1082
40
+ janito-0.8.0.dist-info/RECORD,,
@@ -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/__init__.py DELETED
@@ -1,2 +0,0 @@
1
- """Core package initialization for Janito."""
2
-
janito/agents/openai.py DELETED
@@ -1,53 +0,0 @@
1
- import openai # updated import
2
- import os
3
- from typing import Optional
4
- from threading import Event
5
- from .agent import Agent
6
-
7
- class OpenAIAgent(Agent):
8
- """Handles interaction with OpenAI API, including message handling"""
9
- DEFAULT_MODEL = "o1-mini-2024-09-12"
10
-
11
- def __init__(self, api_key: Optional[str] = None, system_prompt: str = None):
12
- super().__init__(api_key, system_prompt)
13
- if not system_prompt:
14
- raise ValueError("system_prompt is required")
15
- self.api_key = api_key or os.getenv('OPENAI_API_KEY')
16
- if not self.api_key:
17
- raise ValueError("OPENAI_API_KEY environment variable is required")
18
- openai.api_key = self.api_key
19
- openai.organization = os.getenv("OPENAI_ORG")
20
- self.client = openai.Client() # initialized client
21
- self.model = os.getenv('OPENAI_MODEL', "o1-mini-2024-09-12") # reverted to original default model
22
-
23
- def send_message(self, message: str, stop_event: Event = None) -> str:
24
- """Send message to OpenAI API and return response"""
25
- self.messages_history.append(("user", message))
26
- self.last_full_message = message
27
-
28
- try:
29
- if stop_event and stop_event.is_set():
30
- return ""
31
-
32
- #messages = [{"role": "system", "content": self.system_message}]
33
- messages = [{"role": "user", "content": message}]
34
-
35
- response = self.client.chat.completions.create(
36
- model=self.model,
37
- messages=messages,
38
- max_completion_tokens=4000,
39
- temperature=1,
40
- )
41
-
42
- response_text = response.choices[0].message.content
43
-
44
- if not (stop_event and stop_event.is_set()):
45
- self.last_response = response_text
46
- self.messages_history.append(("assistant", response_text))
47
-
48
- return response_text
49
-
50
- except KeyboardInterrupt:
51
- if stop_event:
52
- stop_event.set()
53
- return ""
janito/agents/test.py DELETED
@@ -1,34 +0,0 @@
1
- import unittest
2
- import os
3
- from unittest.mock import patch, MagicMock
4
- from .openai import OpenAIAgent
5
- from .claudeai import AIAgent
6
-
7
- class TestAIAgents(unittest.TestCase):
8
- def setUp(self):
9
- self.system_prompt = "You are a helpful assistant."
10
- self.test_message = "Hello, how are you?"
11
-
12
- def test_openai_agent_initialization(self):
13
- with patch.dict(os.environ, {'OPENAI_API_KEY': 'test_key'}):
14
- agent = OpenAIAgent(system_prompt=self.system_prompt)
15
-
16
- def test_claudeai_agent_initialization(self):
17
- with patch.dict(os.environ, {'ANTHROPIC_API_KEY': 'test_key'}):
18
- agent = AIAgent(system_prompt=self.system_prompt)
19
-
20
- def test_openai_agent_send_message(self):
21
- with patch('openai.OpenAI.chat.completions.create') as mock_create:
22
- mock_response = MagicMock()
23
- mock_response.choices[0].message.content = "I'm good, thank you!"
24
- mock_create.return_value = mock_response
25
- response = self.openai_agent.send_message(self.test_message)
26
- self.assertEqual(response, "I'm good, thank you!")
27
-
28
- def test_claudeai_agent_send_message(self):
29
- with patch('anthropic.Client.messages.create') as mock_create:
30
- mock_response = MagicMock()
31
- mock_response.content[0].text = "I'm Claude, how can I assist you?"
32
- mock_create.return_value = mock_response
33
- response = self.claudeai_agent.send_message(self.test_message)
34
- self.assertEqual(response, "I'm Claude, how can I assist you?")
janito/change/__init__.py DELETED
@@ -1,32 +0,0 @@
1
- """
2
- This package provides the following change flow steps:
3
-
4
- - Create a preview directory
5
- - Build change request prompt
6
- - Send change request to AI agent
7
- - Parse the response into changes
8
- - Save response to history file
9
- - Preview the changes (applying them to the preview directory)
10
- - Validate the changes
11
- - Run tests if specified
12
- - Show the change view (using janito.changeviewer)
13
- - Prompt the user to apply the changes to the working directory
14
- - Apply the changes
15
- """
16
-
17
- from typing import Tuple
18
- from pathlib import Path
19
- from ..agents import agent # Updated import to use singleton directly
20
- from .parser import build_change_request_prompt, parse_response
21
- from .preview import setup_workspace_dir_preview
22
- from .applier.main import ChangeApplier
23
-
24
- __all__ = [
25
- 'build_change_request_prompt',
26
- 'get_change_response',
27
- 'parse_response',
28
- 'setup_workspace_dir_preview',
29
- 'parse_change_response',
30
- 'save_change_response',
31
- 'ChangeApplier'
32
- ]
janito/change/__main__.py DELETED
File without changes
@@ -1,23 +0,0 @@
1
- """Analysis module for Janito.
2
-
3
- This module provides functionality for analyzing and displaying code changes.
4
- """
5
-
6
- from .options import AnalysisOption, parse_analysis_options
7
- from .view import format_analysis, prompt_user, get_option_selection
8
- from .prompts import (
9
- build_request_analysis_prompt,
10
- validate_option_letter
11
- )
12
- from .analyze import analyze_request
13
-
14
- __all__ = [
15
- 'AnalysisOption',
16
- 'parse_analysis_options',
17
- 'format_analysis',
18
- 'build_request_analysis_prompt',
19
- 'get_option_selection',
20
- 'prompt_user',
21
- 'validate_option_letter',
22
- 'analyze_request'
23
- ]