janito 0.4.0__py3-none-any.whl → 0.6.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 (104) hide show
  1. janito/__init__.py +1 -1
  2. janito/__main__.py +102 -326
  3. janito/agents/__init__.py +16 -0
  4. janito/agents/agent.py +21 -0
  5. janito/{claude.py → agents/claudeai.py} +13 -17
  6. janito/agents/openai.py +53 -0
  7. janito/agents/test.py +34 -0
  8. janito/change/__init__.py +32 -0
  9. janito/change/__main__.py +0 -0
  10. janito/change/analysis/__init__.py +23 -0
  11. janito/change/analysis/__main__.py +7 -0
  12. janito/change/analysis/analyze.py +61 -0
  13. janito/change/analysis/formatting.py +78 -0
  14. janito/change/analysis/options.py +81 -0
  15. janito/change/analysis/prompts.py +98 -0
  16. janito/change/analysis/view/__init__.py +9 -0
  17. janito/change/analysis/view/terminal.py +171 -0
  18. janito/change/applier/__init__.py +5 -0
  19. janito/change/applier/file.py +58 -0
  20. janito/change/applier/main.py +156 -0
  21. janito/change/applier/text.py +245 -0
  22. janito/change/applier/workspace_dir.py +58 -0
  23. janito/change/core.py +131 -0
  24. janito/change/history.py +44 -0
  25. janito/change/operations.py +7 -0
  26. janito/change/parser.py +289 -0
  27. janito/change/play.py +54 -0
  28. janito/change/preview.py +82 -0
  29. janito/change/prompts.py +126 -0
  30. janito/change/test.py +0 -0
  31. janito/change/validator.py +251 -0
  32. janito/change/viewer/__init__.py +11 -0
  33. janito/change/viewer/content.py +66 -0
  34. janito/change/viewer/diff.py +43 -0
  35. janito/change/viewer/pager.py +56 -0
  36. janito/change/viewer/panels.py +555 -0
  37. janito/change/viewer/styling.py +103 -0
  38. janito/change/viewer/themes.py +55 -0
  39. janito/clear_statement_parser/clear_statement_format.txt +328 -0
  40. janito/clear_statement_parser/examples.txt +326 -0
  41. janito/clear_statement_parser/models.py +104 -0
  42. janito/clear_statement_parser/parser.py +496 -0
  43. janito/cli/__init__.py +2 -0
  44. janito/cli/base.py +30 -0
  45. janito/cli/commands.py +45 -0
  46. janito/cli/functions.py +111 -0
  47. janito/cli/handlers/ask.py +22 -0
  48. janito/cli/handlers/demo.py +22 -0
  49. janito/cli/handlers/request.py +24 -0
  50. janito/cli/handlers/scan.py +9 -0
  51. janito/cli/history.py +61 -0
  52. janito/cli/registry.py +26 -0
  53. janito/common.py +41 -10
  54. janito/config.py +71 -6
  55. janito/demo/__init__.py +4 -0
  56. janito/demo/data.py +13 -0
  57. janito/demo/mock_data.py +20 -0
  58. janito/demo/operations.py +45 -0
  59. janito/demo/runner.py +59 -0
  60. janito/demo/scenarios.py +32 -0
  61. janito/prompts.py +1 -65
  62. janito/qa.py +8 -5
  63. janito/review.py +13 -0
  64. janito/search_replace/README.md +146 -0
  65. janito/search_replace/__init__.py +6 -0
  66. janito/search_replace/__main__.py +21 -0
  67. janito/search_replace/core.py +119 -0
  68. janito/search_replace/parser.py +52 -0
  69. janito/search_replace/play.py +61 -0
  70. janito/search_replace/replacer.py +36 -0
  71. janito/search_replace/searcher.py +299 -0
  72. janito/shell/__init__.py +39 -0
  73. janito/shell/bus.py +31 -0
  74. janito/shell/commands.py +195 -0
  75. janito/shell/handlers.py +122 -0
  76. janito/shell/history.py +20 -0
  77. janito/shell/processor.py +52 -0
  78. janito/tui/__init__.py +21 -0
  79. janito/tui/base.py +22 -0
  80. janito/tui/flows/__init__.py +5 -0
  81. janito/tui/flows/changes.py +65 -0
  82. janito/tui/flows/content.py +128 -0
  83. janito/tui/flows/selection.py +117 -0
  84. janito/tui/screens/__init__.py +3 -0
  85. janito/tui/screens/app.py +1 -0
  86. janito/workspace/__init__.py +7 -0
  87. janito/workspace/analysis.py +121 -0
  88. janito/workspace/manager.py +48 -0
  89. janito/workspace/scan.py +232 -0
  90. janito-0.6.0.dist-info/METADATA +185 -0
  91. janito-0.6.0.dist-info/RECORD +95 -0
  92. {janito-0.4.0.dist-info → janito-0.6.0.dist-info}/WHEEL +1 -1
  93. janito/analysis.py +0 -281
  94. janito/changeapplier.py +0 -436
  95. janito/changeviewer.py +0 -350
  96. janito/console.py +0 -330
  97. janito/contentchange.py +0 -84
  98. janito/contextparser.py +0 -113
  99. janito/fileparser.py +0 -125
  100. janito/scan.py +0 -137
  101. janito-0.4.0.dist-info/METADATA +0 -164
  102. janito-0.4.0.dist-info/RECORD +0 -21
  103. {janito-0.4.0.dist-info → janito-0.6.0.dist-info}/entry_points.txt +0 -0
  104. {janito-0.4.0.dist-info → janito-0.6.0.dist-info}/licenses/LICENSE +0 -0
janito/analysis.py DELETED
@@ -1,281 +0,0 @@
1
- """Analysis display module for Janito.
2
-
3
- This module handles the formatting and display of analysis results, option selection,
4
- and related functionality for the Janito application.
5
- """
6
-
7
- from typing import Optional, Dict, List, Tuple
8
- from pathlib import Path
9
- from rich.console import Console
10
- from rich.markdown import Markdown
11
- from rich.panel import Panel
12
- from rich.text import Text
13
- from rich import box
14
- from rich.columns import Columns
15
- from rich.rule import Rule
16
- from rich.prompt import Prompt
17
- from janito.claude import ClaudeAPIAgent
18
- from janito.scan import collect_files_content
19
- from janito.common import progress_send_message
20
- from janito.config import config
21
- from dataclasses import dataclass
22
- import re
23
-
24
- MIN_PANEL_WIDTH = 40 # Minimum width for each panel
25
-
26
- def get_history_file_type(filepath: Path) -> str:
27
- """Determine the type of saved file based on its name"""
28
- name = filepath.name.lower()
29
- if 'changes' in name:
30
- return 'changes'
31
- elif 'selected' in name:
32
- return 'selected'
33
- elif 'analysis' in name:
34
- return 'analysis'
35
- elif 'response' in name:
36
- return 'response'
37
- return 'unknown'
38
-
39
- @dataclass
40
- class AnalysisOption:
41
- letter: str
42
- summary: str
43
- affected_files: List[str]
44
- description_items: List[str] # Changed from description to description_items
45
-
46
- CHANGE_ANALYSIS_PROMPT = """
47
- Current files:
48
- <files>
49
- {files_content}
50
- </files>
51
-
52
- Considering the above current files content, provide options for the requested change in the following format:
53
-
54
- A. Keyword summary of the change
55
- -----------------
56
- Description:
57
- - Detailed description of the change
58
-
59
- Affected files:
60
- - file1.py
61
- - file2.py (new)
62
- -----------------
63
- END_OF_OPTIONS (mandatory marker)
64
-
65
- RULES:
66
- - do NOT provide the content of the files
67
- - do NOT offer to implement the changes
68
-
69
- Request:
70
- {request}
71
- """
72
-
73
-
74
-
75
-
76
- def prompt_user(message: str, choices: List[str] = None) -> str:
77
- """Display a prominent user prompt with optional choices"""
78
- console = Console()
79
- console.print()
80
- console.print(Rule(" User Input Required ", style="bold cyan"))
81
-
82
- if choices:
83
- choice_text = f"[cyan]Options: {', '.join(choices)}[/cyan]"
84
- console.print(Panel(choice_text, box=box.ROUNDED))
85
-
86
- return Prompt.ask(f"[bold cyan]> {message}[/bold cyan]")
87
-
88
- def validate_option_letter(letter: str, options: dict) -> bool:
89
- """Validate if the given letter is a valid option or 'M' for modify"""
90
- return letter.upper() in options or letter.upper() == 'M'
91
-
92
- def get_option_selection() -> str:
93
- """Get user input for option selection with modify option"""
94
- console = Console()
95
- console.print("\n[cyan]Enter option letter or 'M' to modify request[/cyan]")
96
- while True:
97
- letter = prompt_user("Select option").strip().upper()
98
- if letter == 'M' or (letter.isalpha() and len(letter) == 1):
99
- return letter
100
- console.print("[red]Please enter a valid letter or 'M'[/red]")
101
-
102
- def _display_options(options: Dict[str, AnalysisOption]) -> None:
103
- """Display available options with left-aligned content and horizontally centered panels."""
104
- console = Console()
105
-
106
- # Display centered title using Rule
107
- console.print()
108
- console.print(Rule(" Available Options ", style="bold cyan", align="center"))
109
- console.print()
110
-
111
- # Calculate optimal width based on terminal
112
- term_width = console.width or 100
113
- panel_width = max(MIN_PANEL_WIDTH, (term_width // 2) - 10) # Width for two columns
114
-
115
- # Create panels for each option
116
- panels = []
117
- for letter, option in options.items():
118
- content = Text()
119
-
120
- # Display description as bullet points
121
- content.append("Description:\n", style="bold cyan")
122
- for item in option.description_items:
123
- content.append(f"• {item}\n", style="white")
124
- content.append("\n")
125
-
126
- # Display affected files
127
- if option.affected_files:
128
- content.append("Affected files:\n", style="bold cyan")
129
- for file in option.affected_files:
130
- content.append(f"• {file}\n", style="yellow")
131
-
132
- # Create panel with consistent styling
133
- panel = Panel(
134
- content,
135
- box=box.ROUNDED,
136
- border_style="cyan",
137
- title=f"Option {letter}: {option.summary}",
138
- title_align="center",
139
- padding=(1, 2),
140
- width=panel_width
141
- )
142
- panels.append(panel)
143
-
144
- # Display panels in columns with center alignment
145
- if panels:
146
- # Group panels into pairs for two columns
147
- for i in range(0, len(panels), 2):
148
- pair = panels[i:i+2]
149
- columns = Columns(
150
- pair,
151
- align="center",
152
- expand=True,
153
- equal=True,
154
- padding=(0, 2)
155
- )
156
- console.print(columns)
157
- console.print() # Add spacing between rows
158
-
159
- def _display_markdown(content: str) -> None:
160
- """Display content in markdown format."""
161
- console = Console()
162
- md = Markdown(content)
163
- console.print(md)
164
-
165
- def _display_raw_history(claude: ClaudeAPIAgent) -> None:
166
- """Display raw message history from Claude agent."""
167
- console = Console()
168
- console.print("\n=== Message History ===")
169
- for role, content in claude.messages_history:
170
- console.print(f"\n[bold cyan]{role.upper()}:[/bold cyan]")
171
- console.print(content)
172
- console.print("\n=== End Message History ===\n")
173
-
174
-
175
- def format_analysis(analysis: str, raw: bool = False, claude: Optional[ClaudeAPIAgent] = None, workdir: Optional[Path] = None) -> None:
176
- """Format and display the analysis output with enhanced capabilities."""
177
- console = Console()
178
-
179
- if raw and claude:
180
- _display_raw_history(claude)
181
- else:
182
- options = parse_analysis_options(analysis)
183
- if options:
184
- _display_options(options)
185
- else:
186
- console.print("\n[yellow]Warning: No valid options found in response. Displaying as markdown.[/yellow]\n")
187
- _display_markdown(analysis)
188
-
189
- def get_history_path(workdir: Path) -> Path:
190
- """Create and return the history directory path"""
191
- history_dir = workdir / '.janito' / 'history'
192
- history_dir.mkdir(parents=True, exist_ok=True)
193
- return history_dir
194
-
195
- def get_timestamp() -> str:
196
- """Get current UTC timestamp in YMD_HMS format with leading zeros"""
197
- from datetime import datetime, timezone
198
- return datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')
199
-
200
- def save_to_file(content: str, prefix: str, workdir: Path) -> Path:
201
- """Save content to a timestamped file in history directory"""
202
- history_dir = get_history_path(workdir)
203
- timestamp = get_timestamp()
204
- filename = f"{timestamp}_{prefix}.txt"
205
- file_path = history_dir / filename
206
- file_path.write_text(content)
207
- return file_path
208
-
209
-
210
-
211
- def parse_analysis_options(response: str) -> dict[str, AnalysisOption]:
212
- """Parse options from the response text using a line-based approach."""
213
- options = {}
214
-
215
- # Extract content up to END_OF_OPTIONS
216
- if 'END_OF_OPTIONS' in response:
217
- response = response.split('END_OF_OPTIONS')[0]
218
-
219
- lines = response.splitlines()
220
- current_option = None
221
- current_section = None
222
-
223
- for line in lines:
224
- line = line.strip()
225
- if not line:
226
- continue
227
-
228
- # Check for new option starting with letter
229
- if len(line) >= 2 and line[0].isalpha() and line[1] == '.' and line[0].isupper():
230
- if current_option:
231
- options[current_option.letter] = current_option
232
-
233
- letter = line[0]
234
- summary = line[2:].strip()
235
- current_option = AnalysisOption(
236
- letter=letter,
237
- summary=summary,
238
- affected_files=[],
239
- description_items=[]
240
- )
241
- current_section = None
242
- continue
243
-
244
- # Skip separator lines
245
- if line.startswith('---'):
246
- continue
247
-
248
- # Check for section headers
249
- if line.startswith('Description:'):
250
- current_section = 'description'
251
- continue
252
- elif line.startswith('Affected files:'):
253
- current_section = 'files'
254
- continue
255
-
256
- # Process content based on current section
257
- if current_option and current_section and line:
258
- if current_section == 'description':
259
- # Strip bullet points and whitespace
260
- item = line.lstrip(' -•').strip()
261
- if item:
262
- current_option.description_items.append(item)
263
- elif current_section == 'files':
264
- # Strip bullet points and (modified)/(new) annotations
265
- file_path = line.lstrip(' -')
266
- file_path = re.sub(r'\s*\([^)]+\)\s*$', '', file_path)
267
- if file_path:
268
- current_option.affected_files.append(file_path)
269
-
270
- # Add the last option if exists
271
- if current_option:
272
- options[current_option.letter] = current_option
273
-
274
- return options
275
-
276
- def build_request_analysis_prompt(files_content: str, request: str) -> str:
277
- """Build prompt for information requests"""
278
- return CHANGE_ANALYSIS_PROMPT.format(
279
- files_content=files_content,
280
- request=request
281
- )