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/changeviewer.py DELETED
@@ -1,350 +0,0 @@
1
- from pathlib import Path
2
- from rich.console import Console
3
- from rich.text import Text
4
- from rich.panel import Panel
5
- from rich.table import Table
6
- from rich.rule import Rule # Add this import
7
- from typing import List, Optional, Dict
8
- from rich import box
9
- from janito.fileparser import FileChange
10
- from janito.analysis import AnalysisOption # Add this import
11
- from rich.columns import Columns # Add this import at the top with other imports
12
-
13
- MIN_PANEL_WIDTH = 40 # Minimum width for each panel
14
-
15
-
16
- def format_sequence_preview(lines: List[str]) -> Text:
17
- """Format a sequence of prefixed lines into rich text with colors"""
18
- text = Text()
19
- last_was_empty = False
20
-
21
- for line in lines:
22
- if not line:
23
- # Preserve empty lines but don't duplicate them
24
- if not last_was_empty:
25
- text.append("\n")
26
- last_was_empty = True
27
- continue
28
-
29
- last_was_empty = False
30
- prefix = line[0] if line[0] in ('=', '>', '<') else ' '
31
- content = line[1:] if line[0] in ('=', '>', '<') else line
32
-
33
- if prefix == '=':
34
- text.append(f" {content}\n", style="dim")
35
- elif prefix == '>':
36
- text.append(f"+{content}\n", style="green")
37
- elif prefix == '<':
38
- text.append(f"-{content}\n", style="red")
39
- else:
40
- text.append(f" {content}\n", style="yellow dim")
41
-
42
- return text
43
-
44
-
45
-
46
-
47
-
48
-
49
- def show_changes_legend(console: Console) -> None:
50
- """Display a legend explaining the colors and symbols used in change previews in a horizontal layout"""
51
- # Create a list of colored text objects
52
- legend_items = [
53
- Text("Unchanged", style="#98C379"),
54
- Text(" • ", style="dim"),
55
- Text("Removed", style="#E06C75"),
56
- Text(" • ", style="dim"),
57
- Text("Relocated", style="#61AFEF"),
58
- Text(" • ", style="dim"),
59
- Text("New", style="#C678DD")
60
- ]
61
-
62
- # Combine all items into a single text object
63
- legend_text = Text()
64
- for item in legend_items:
65
- legend_text.append_text(item)
66
-
67
- # Create a simple panel with the horizontal legend
68
- legend_panel = Panel(
69
- legend_text,
70
- title="Changes Legend",
71
- title_align="left",
72
- border_style="white",
73
- box=box.ROUNDED,
74
- padding=(0, 1)
75
- )
76
-
77
- # Center the legend panel horizontally
78
- console.print(Columns([legend_panel], align="center"))
79
- console.print() # Add extra line for spacing
80
-
81
-
82
- def show_change_preview(console: Console, filepath: Path, change: FileChange) -> None:
83
- """Display a preview of changes for a single file with side-by-side comparison"""
84
- # Show changes legend first
85
- show_changes_legend(console)
86
-
87
- # Create main file panel content
88
- main_content = []
89
-
90
- # Handle new file preview
91
- if change.is_new_file:
92
- new_file_panel = Panel(
93
- Text(change.content),
94
- title="New File Content",
95
- title_align="left",
96
- border_style="green",
97
- box=box.ROUNDED
98
- )
99
- main_content.append(new_file_panel)
100
-
101
- # Create and display main file panel
102
- file_panel = Panel(
103
- Columns(main_content),
104
- title=str(filepath),
105
- title_align="left",
106
- border_style="white",
107
- box=box.ROUNDED )
108
- return
109
-
110
-
111
-
112
- # For modifications, create side-by-side comparison for each change
113
- for i, (search, replace, description) in enumerate(change.search_blocks, 1):
114
- # Show change header with description
115
- header = f"Change {i}"
116
- if description:
117
- header += f": {description}"
118
-
119
- if replace is None:
120
- # For deletions, show single panel with content to be deleted
121
- change_panel = Panel(
122
- Text(search, style="red"),
123
- title=f"Content to Delete{' - ' + description if description else ''}",
124
- title_align="left",
125
- border_style="#E06C75", # Brighter red
126
- box=box.ROUNDED
127
- )
128
- main_content.append(change_panel)
129
- else:
130
- # For replacements, show side-by-side panels
131
-
132
-
133
- # Find common content between search and replace
134
- search_lines = search.splitlines()
135
- replace_lines = replace.splitlines()
136
-
137
- # Find common lines from top
138
- common_top = []
139
- for s, r in zip(search_lines, replace_lines):
140
- if s == r:
141
- common_top.append(s)
142
- else:
143
- break
144
-
145
- # Find common lines from bottom
146
- search_remaining = search_lines[len(common_top):]
147
- replace_remaining = replace_lines[len(common_top):]
148
-
149
- common_bottom = []
150
- for s, r in zip(reversed(search_remaining), reversed(replace_remaining)):
151
- if s == r:
152
- common_bottom.insert(0, s)
153
- else:
154
- break
155
-
156
- # Get the unique middle sections
157
- search_middle = search_remaining[:-len(common_bottom)] if common_bottom else search_remaining
158
- replace_middle = replace_remaining[:-len(common_bottom)] if common_bottom else replace_remaining
159
-
160
-
161
-
162
-
163
- # Format content with highlighting using consistent colors and line numbers
164
-
165
-
166
- def format_content(lines: List[str], is_search: bool) -> Text:
167
- text = Text()
168
-
169
- COLORS = {
170
- 'unchanged': '#98C379', # Brighter green for unchanged lines
171
- 'removed': '#E06C75', # Clearer red for removed lines
172
- 'added': '#61AFEF', # Bright blue for added lines
173
- 'new': '#C678DD', # Purple for completely new lines
174
- 'relocated': '#61AFEF' # Use same blue for relocated lines
175
- }
176
-
177
- # Create sets of lines for comparison
178
- search_set = set(search_lines)
179
- replace_set = set(replace_lines)
180
- common_lines = search_set & replace_set
181
- new_lines = replace_set - search_set
182
- relocated_lines = common_lines - set(common_top) - set(common_bottom)
183
-
184
- def add_line(line: str, style: str, prefix: str = " "):
185
- # Special handling for icons
186
- if style == COLORS['relocated']:
187
- prefix = "⇄"
188
- elif style == COLORS['removed'] and prefix == "-":
189
- prefix = "✕"
190
- elif style == COLORS['new'] or (style == COLORS['added'] and prefix == "+"):
191
- prefix = "✚"
192
- text.append(prefix, style=style)
193
- text.append(f" {line}\n", style=style)
194
-
195
- # Format common top section
196
- for line in common_top:
197
- add_line(line, COLORS['unchanged'], "=")
198
-
199
- # Format changed middle section
200
- for line in (search_middle if is_search else replace_middle):
201
- if line in relocated_lines:
202
- add_line(line, COLORS['relocated'], "⇄")
203
- elif not is_search and line in new_lines:
204
- add_line(line, COLORS['new'], "+")
205
- else:
206
- style = COLORS['removed'] if is_search else COLORS['added']
207
- prefix = "✕" if is_search else "+"
208
- add_line(line, style, prefix)
209
-
210
- # Format common bottom section
211
- for line in common_bottom:
212
- add_line(line, COLORS['unchanged'], "=")
213
-
214
- return text
215
-
216
-
217
-
218
- # Create panels for old and new content without width constraints
219
- old_panel = Panel(
220
- format_content(search_lines, True),
221
- title="Current Content",
222
- title_align="left",
223
- border_style="#E06C75",
224
- box=box.ROUNDED
225
- )
226
-
227
- new_panel = Panel(
228
- format_content(replace_lines, False),
229
- title="New Content",
230
- title_align="left",
231
- border_style="#61AFEF",
232
- box=box.ROUNDED
233
- )
234
-
235
- # Add change panels to main content with auto-fitting columns
236
- change_columns = Columns([old_panel, new_panel], equal=True, align="center")
237
- change_panel = Panel(
238
- change_columns,
239
- title=header,
240
- title_align="left",
241
- border_style="cyan",
242
- box=box.ROUNDED
243
- )
244
- main_content.append(change_panel)
245
-
246
- # Create and display main file panel
247
- file_panel = Panel(
248
- Columns(main_content, align="center"),
249
- title=f"Modifying {filepath}",
250
- title_align="left",
251
- border_style="white",
252
- box=box.ROUNDED
253
- )
254
- console.print(file_panel)
255
- console.print()
256
-
257
- # Remove or comment out the unused unified panel code since we're using direct column display
258
-
259
- def preview_all_changes(console: Console, changes: Dict[Path, FileChange]) -> None:
260
- """Show preview for all file changes"""
261
- console.print("\n[bold blue]Change Preview[/bold blue]")
262
-
263
- for filepath, change in changes.items():
264
- show_change_preview(console, filepath, change)
265
-
266
-
267
- def _display_options(options: Dict[str, AnalysisOption]) -> None:
268
- """Display available options in a centered, responsive layout with consistent spacing."""
269
- console = Console()
270
-
271
- # Display centered header with decorative rule
272
- console.print()
273
- console.print(Rule(" Available Options ", style="bold cyan", align="center"))
274
- console.print()
275
-
276
- # Safety check for empty options
277
- if not options:
278
- console.print(Panel("[yellow]No options available[/yellow]", border_style="yellow"))
279
- return
280
-
281
- # Calculate optimal layout dimensions based on terminal width
282
- terminal_width = console.width or 100
283
- panel_padding = (1, 2) # Consistent padding for all panels
284
- available_width = terminal_width - 4 # Account for margins
285
-
286
- # Determine optimal panel width and number of columns
287
- min_panels_per_row = 1
288
- max_panels_per_row = 3
289
- optimal_panel_width = min(
290
- available_width // max_panels_per_row,
291
- available_width // min_panels_per_row
292
- )
293
-
294
- if optimal_panel_width < MIN_PANEL_WIDTH:
295
- optimal_panel_width = MIN_PANEL_WIDTH
296
-
297
- # Create panels with consistent styling and spacing
298
- panels = []
299
- for letter, option in options.items():
300
- # Build content with consistent formatting
301
- content = Text()
302
-
303
- # Add description section
304
- content.append("Description:\n", style="bold cyan")
305
- for item in option.description_items:
306
- content.append(f"• {item}\n", style="white")
307
- content.append("\n")
308
-
309
- # Add affected files section if present
310
- if option.affected_files:
311
- content.append("Affected files:\n", style="bold cyan")
312
- for file in option.affected_files:
313
- content.append(f"• {file}\n", style="yellow")
314
-
315
- # Create panel with consistent styling
316
- panel = Panel(
317
- content,
318
- box=box.ROUNDED,
319
- border_style="cyan",
320
- title=f"Option {letter}: {option.summary}",
321
- title_align="center",
322
- padding=panel_padding,
323
- width=optimal_panel_width
324
- )
325
- panels.append(panel)
326
-
327
- # Calculate optimal number of columns based on available width
328
- num_columns = max(1, min(
329
- len(panels), # Don't exceed number of panels
330
- available_width // optimal_panel_width, # Width-based limit
331
- max_panels_per_row # Maximum columns limit
332
- ))
333
-
334
- # Create a centered container panel for all options
335
- container = Panel(
336
- Columns(
337
- panels,
338
- num_columns=num_columns,
339
- equal=True,
340
- align="center",
341
- padding=(0, 2) # Consistent spacing between columns
342
- ),
343
- box=box.SIMPLE,
344
- padding=(1, 4), # Add padding around the columns for better centering
345
- width=min(terminal_width - 4, num_columns * optimal_panel_width + (num_columns - 1) * 4)
346
- )
347
-
348
- # Display the centered container
349
- console.print(Columns([container], align="center"))
350
- console.print()
janito/console.py DELETED
@@ -1,330 +0,0 @@
1
- from prompt_toolkit import PromptSession
2
- from prompt_toolkit.history import FileHistory
3
- from prompt_toolkit.completion import WordCompleter, PathCompleter
4
- from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
5
- from prompt_toolkit.formatted_text import HTML
6
- from pathlib import Path
7
- from rich.console import Console
8
- from janito.claude import ClaudeAPIAgent
9
- from janito.prompts import SYSTEM_PROMPT
10
- from janito.analysis import build_request_analysis_prompt
11
- from janito.scan import collect_files_content
12
- from janito.__main__ import handle_option_selection
13
- from rich.panel import Panel
14
- from rich.align import Align
15
- from janito.common import progress_send_message
16
- from rich.table import Table
17
- from rich.layout import Layout
18
- from rich.live import Live
19
- from typing import List, Optional
20
- import shutil
21
-
22
- def create_completer(workdir: Path) -> WordCompleter:
23
- """Create command completer with common commands and paths"""
24
- commands = [
25
- 'ask', 'request', 'help', 'exit', 'quit',
26
- '--raw', '--verbose', '--debug', '--test'
27
- ]
28
- return WordCompleter(commands, ignore_case=True)
29
-
30
- def format_prompt(workdir: Path) -> HTML:
31
- """Format the prompt with current directory"""
32
- cwd = workdir.name
33
- return HTML(f'<ansigreen>janito</ansigreen> <ansiblue>{cwd}</ansiblue>> ')
34
-
35
- def display_help() -> None:
36
- """Display available commands, options and their descriptions"""
37
- console = Console()
38
-
39
- layout = Layout()
40
- layout.split_column(
41
- Layout(name="header"),
42
- Layout(name="commands"),
43
- Layout(name="options"),
44
- Layout(name="examples")
45
- )
46
-
47
- # Header
48
- header_table = Table(box=None, show_header=False)
49
- header_table.add_row("[bold cyan]Janito Console Help[/bold cyan]")
50
- header_table.add_row("[dim]Your AI-powered software development buddy[/dim]")
51
-
52
- # Commands table
53
- commands_table = Table(title="Available Commands", box=None)
54
- commands_table.add_column("Command", style="cyan", width=20)
55
- commands_table.add_column("Description", style="white")
56
-
57
-
58
- commands_table.add_row(
59
- "/ask <text> (/a)",
60
- "Ask a question about the codebase without making changes"
61
- )
62
- commands_table.add_row(
63
- "<text> or /request <text> (/r)",
64
- "Request code modifications or improvements"
65
- )
66
- commands_table.add_row(
67
- "/help (/h)",
68
- "Display this help message"
69
- )
70
- commands_table.add_row(
71
- "/quit or /exit (/q)",
72
- "Exit the console session"
73
- )
74
-
75
- # Options table
76
- options_table = Table(title="Common Options", box=None)
77
- options_table.add_column("Option", style="cyan", width=20)
78
- options_table.add_column("Description", style="white")
79
-
80
- options_table.add_row(
81
- "--raw",
82
- "Display raw response without formatting"
83
- )
84
- options_table.add_row(
85
- "--verbose",
86
- "Show additional information during execution"
87
- )
88
- options_table.add_row(
89
- "--debug",
90
- "Display detailed debug information"
91
- )
92
- options_table.add_row(
93
- "--test <cmd>",
94
- "Run specified test command before applying changes"
95
- )
96
-
97
- # Examples panel
98
- examples = Panel(
99
- "\n".join([
100
- "[dim]Basic Commands:[/dim]",
101
- " ask how does the error handling work?",
102
- " request add input validation to user functions",
103
- "",
104
- "[dim]Using Options:[/dim]",
105
- " request update tests --verbose",
106
- " ask explain auth flow --raw",
107
- " request optimize code --test 'pytest'",
108
- "",
109
- "[dim]Complex Examples:[/dim]",
110
- " request refactor login function --verbose --test 'python -m unittest'",
111
- " ask code structure --raw --debug"
112
- ]),
113
- title="Examples",
114
- border_style="blue"
115
- )
116
-
117
- # Update layout
118
- layout["header"].update(header_table)
119
- layout["commands"].update(commands_table)
120
- layout["options"].update(options_table)
121
- layout["examples"].update(examples)
122
-
123
- console.print(layout)
124
-
125
-
126
-
127
- def process_command(command: str, args: str, workdir: Path, include: List[Path], claude: ClaudeAPIAgent) -> None:
128
- """Process console commands using CLI functions for consistent behavior"""
129
- console = Console()
130
-
131
- # Parse command options
132
- raw = False
133
- verbose = False
134
- debug = False
135
- test_cmd = None
136
-
137
- # Extract options from args
138
- words = args.split()
139
- filtered_args = []
140
- i = 0
141
- while i < len(words):
142
- if words[i] == '--raw':
143
- raw = True
144
- elif words[i] == '--verbose':
145
- verbose = True
146
- elif words[i] == '--debug':
147
- debug = True
148
- elif words[i] == '--test' and i + 1 < len(words):
149
- test_cmd = words[i + 1]
150
- i += 1
151
- else:
152
- filtered_args.append(words[i])
153
- i += 1
154
-
155
- args = ' '.join(filtered_args)
156
-
157
- # Update config with command options
158
- from janito.config import config
159
- config.set_debug(debug)
160
- config.set_verbose(verbose)
161
- config.set_test_cmd(test_cmd)
162
-
163
- # Remove leading slash if present
164
- command = command.lstrip('/')
165
-
166
- # Handle command aliases
167
- command_aliases = {
168
- 'h': 'help',
169
- 'a': 'ask',
170
- 'r': 'request',
171
- 'q': 'quit',
172
- 'exit': 'quit'
173
- }
174
- command = command_aliases.get(command, command)
175
-
176
- if command == "help":
177
- display_help()
178
- return
179
-
180
- if command == "quit":
181
- raise EOFError()
182
-
183
- if command == "ask":
184
- if not args:
185
- console.print(Panel(
186
- "[red]Ask command requires a question[/red]",
187
- title="Error",
188
- border_style="red"
189
- ))
190
- return
191
-
192
- # Use CLI question processing function
193
- from janito.__main__ import process_question
194
- process_question(args, workdir, include, raw, claude)
195
- return
196
-
197
- if command == "request":
198
- if not args:
199
- console.print(Panel(
200
- "[red]Request command requires a description[/red]",
201
- title="Error",
202
- border_style="red"
203
- ))
204
- return
205
-
206
- paths_to_scan = [workdir] if workdir else []
207
- if include:
208
- paths_to_scan.extend(include)
209
- files_content = collect_files_content(paths_to_scan, workdir)
210
-
211
- # Use CLI request processing functions
212
- initial_prompt = build_request_analysis_prompt(files_content, args)
213
- initial_response = progress_send_message(claude, initial_prompt)
214
-
215
- from janito.__main__ import save_to_file
216
- save_to_file(initial_response, 'analysis', workdir)
217
-
218
- from janito.analysis import format_analysis
219
- format_analysis(initial_response, raw, claude)
220
- handle_option_selection(claude, initial_response, args, raw, workdir, include)
221
- return
222
-
223
- console.print(Panel(
224
- f"[red]Unknown command: /{command}[/red]\nType '/help' for available commands",
225
- title="Error",
226
- border_style="red"
227
- ))
228
-
229
- def start_console_session(workdir: Path, include: Optional[List[Path]] = None) -> None:
230
- """Start an enhanced interactive console session"""
231
- console = Console()
232
- claude = ClaudeAPIAgent(system_prompt=SYSTEM_PROMPT)
233
-
234
- # Setup history with persistence
235
- history_file = workdir / '.janito' / 'console_history'
236
- history_file.parent.mkdir(parents=True, exist_ok=True)
237
-
238
- # Create session with history and completions
239
- session = PromptSession(
240
- history=FileHistory(str(history_file)),
241
- completer=create_completer(workdir),
242
- auto_suggest=AutoSuggestFromHistory(),
243
- complete_while_typing=True
244
- )
245
-
246
- # Get version and terminal info
247
- from importlib.metadata import version
248
- try:
249
- ver = version("janito")
250
- except:
251
- ver = "dev"
252
-
253
- term_width = shutil.get_terminal_size().columns
254
-
255
-
256
-
257
- # Create welcome message with consistent colors and enhanced information
258
- COLORS = {
259
- 'primary': '#729FCF', # Soft blue for primary elements
260
- 'secondary': '#8AE234', # Bright green for actions/success
261
- 'accent': '#AD7FA8', # Purple for accents
262
- 'muted': '#7F9F7F', # Muted green for less important text
263
- }
264
-
265
- welcome_text = (
266
- f"[bold {COLORS['primary']}]Welcome to Janito v{ver}[/bold {COLORS['primary']}]\n"
267
- f"[{COLORS['muted']}]Your AI-Powered Software Development Buddy[/{COLORS['muted']}]\n\n"
268
- f"[{COLORS['accent']}]Keyboard Shortcuts:[/{COLORS['accent']}]\n"
269
- "• ↑↓ : Navigate command history\n"
270
- "• Tab : Complete commands and paths\n"
271
- "• Ctrl+D : Exit console\n"
272
- "• Ctrl+C : Cancel current operation\n\n"
273
- f"[{COLORS['accent']}]Available Commands:[/{COLORS['accent']}]\n"
274
- "• /ask (or /a) : Ask questions about code\n"
275
- "• /request (or /r) : Request code changes\n"
276
- "• /help (or /h) : Show detailed help\n"
277
- "• /quit (or /q) : Exit console\n\n"
278
- f"[{COLORS['accent']}]Quick Tips:[/{COLORS['accent']}]\n"
279
- "• Start typing and press Tab for suggestions\n"
280
- "• Use --test to run tests before changes\n"
281
- "• Add --verbose for detailed output\n"
282
- "• Type a request directly without /request\n\n"
283
- f"[{COLORS['secondary']}]Current Version:[/{COLORS['secondary']}] v{ver}\n"
284
- f"[{COLORS['muted']}]Working Directory:[/{COLORS['muted']}] {workdir.absolute()}"
285
- )
286
-
287
- welcome_panel = Panel(
288
- welcome_text,
289
- width=min(80, term_width - 4),
290
- border_style="blue",
291
- title="Janito Console",
292
- subtitle="Press Tab for completions"
293
- )
294
-
295
- console.print("\n")
296
- console.print(welcome_panel)
297
- console.print("\n[cyan]How can I help you with your code today?[/cyan]\n")
298
-
299
- while True:
300
- try:
301
- # Get input with formatted prompt
302
- user_input = session.prompt(
303
- lambda: format_prompt(workdir),
304
- complete_while_typing=True
305
- ).strip()
306
-
307
- if not user_input:
308
- continue
309
-
310
- if user_input.lower() in ('exit', 'quit'):
311
- console.print("\n[cyan]Goodbye! Have a great day![/cyan]\n")
312
- break
313
-
314
- # Split input into command and args
315
- parts = user_input.split(maxsplit=1)
316
- if parts[0].startswith('/'): # Handle /command format
317
- command = parts[0][1:] # Remove the / prefix
318
- else:
319
- command = "request" # Default to request if no command specified
320
-
321
- args = parts[1] if len(parts) > 1 else ""
322
-
323
- # Process command with separated args
324
- process_command(command, args, workdir, include, claude)
325
-
326
- except KeyboardInterrupt:
327
- continue
328
- except EOFError:
329
- console.print("\n[cyan]Goodbye! Have a great day![/cyan]\n")
330
- break