janito 0.7.0__py3-none-any.whl → 0.9.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 (114) hide show
  1. janito/__init__.py +5 -2
  2. janito/__main__.py +151 -142
  3. janito/callbacks.py +130 -0
  4. janito/cli.py +202 -0
  5. janito/config.py +57 -96
  6. janito/data/instructions.txt +6 -0
  7. janito/test_file.py +4 -0
  8. janito/token_report.py +73 -0
  9. janito/tools/__init__.py +10 -0
  10. janito/tools/decorators.py +84 -0
  11. janito/tools/delete_file.py +44 -0
  12. janito/tools/find_files.py +154 -0
  13. janito/tools/search_text.py +197 -0
  14. janito/tools/str_replace_editor/__init__.py +6 -0
  15. janito/tools/str_replace_editor/editor.py +43 -0
  16. janito/tools/str_replace_editor/handlers.py +338 -0
  17. janito/tools/str_replace_editor/utils.py +88 -0
  18. {janito-0.7.0.dist-info/licenses → janito-0.9.0.dist-info}/LICENSE +20 -20
  19. janito-0.9.0.dist-info/METADATA +9 -0
  20. janito-0.9.0.dist-info/RECORD +23 -0
  21. {janito-0.7.0.dist-info → janito-0.9.0.dist-info}/WHEEL +2 -1
  22. janito-0.9.0.dist-info/entry_points.txt +2 -0
  23. janito-0.9.0.dist-info/top_level.txt +1 -0
  24. janito/agents/__init__.py +0 -22
  25. janito/agents/agent.py +0 -28
  26. janito/agents/claudeai.py +0 -45
  27. janito/agents/openai.py +0 -57
  28. janito/agents/test.py +0 -34
  29. janito/change/__init__.py +0 -32
  30. janito/change/__main__.py +0 -0
  31. janito/change/analysis/__init__.py +0 -23
  32. janito/change/analysis/__main__.py +0 -7
  33. janito/change/analysis/analyze.py +0 -62
  34. janito/change/analysis/formatting.py +0 -78
  35. janito/change/analysis/options.py +0 -81
  36. janito/change/analysis/prompts.py +0 -90
  37. janito/change/analysis/view/__init__.py +0 -9
  38. janito/change/analysis/view/terminal.py +0 -181
  39. janito/change/applier/__init__.py +0 -5
  40. janito/change/applier/file.py +0 -58
  41. janito/change/applier/main.py +0 -156
  42. janito/change/applier/text.py +0 -247
  43. janito/change/applier/workspace_dir.py +0 -58
  44. janito/change/core.py +0 -124
  45. janito/change/history.py +0 -44
  46. janito/change/operations.py +0 -7
  47. janito/change/parser.py +0 -287
  48. janito/change/play.py +0 -54
  49. janito/change/preview.py +0 -82
  50. janito/change/prompts.py +0 -121
  51. janito/change/test.py +0 -0
  52. janito/change/validator.py +0 -269
  53. janito/change/viewer/__init__.py +0 -11
  54. janito/change/viewer/content.py +0 -66
  55. janito/change/viewer/diff.py +0 -43
  56. janito/change/viewer/panels.py +0 -533
  57. janito/change/viewer/styling.py +0 -114
  58. janito/change/viewer/themes.py +0 -55
  59. janito/clear_statement_parser/clear_statement_format.txt +0 -328
  60. janito/clear_statement_parser/examples.txt +0 -326
  61. janito/clear_statement_parser/models.py +0 -104
  62. janito/clear_statement_parser/parser.py +0 -496
  63. janito/cli/__init__.py +0 -2
  64. janito/cli/base.py +0 -30
  65. janito/cli/commands.py +0 -88
  66. janito/cli/functions.py +0 -111
  67. janito/cli/history.py +0 -61
  68. janito/cli/registry.py +0 -26
  69. janito/common.py +0 -80
  70. janito/demo/__init__.py +0 -4
  71. janito/demo/data.py +0 -13
  72. janito/demo/mock_data.py +0 -20
  73. janito/demo/operations.py +0 -45
  74. janito/demo/runner.py +0 -59
  75. janito/demo/scenarios.py +0 -32
  76. janito/prompt.py +0 -36
  77. janito/qa.py +0 -57
  78. janito/review.py +0 -13
  79. janito/search_replace/README.md +0 -192
  80. janito/search_replace/__init__.py +0 -7
  81. janito/search_replace/__main__.py +0 -21
  82. janito/search_replace/core.py +0 -120
  83. janito/search_replace/logger.py +0 -35
  84. janito/search_replace/parser.py +0 -52
  85. janito/search_replace/play.py +0 -61
  86. janito/search_replace/replacer.py +0 -36
  87. janito/search_replace/searcher.py +0 -411
  88. janito/search_replace/strategy_result.py +0 -10
  89. janito/shell/__init__.py +0 -38
  90. janito/shell/bus.py +0 -31
  91. janito/shell/commands.py +0 -136
  92. janito/shell/history.py +0 -20
  93. janito/shell/processor.py +0 -32
  94. janito/shell/prompt.py +0 -48
  95. janito/shell/registry.py +0 -60
  96. janito/tui/__init__.py +0 -21
  97. janito/tui/base.py +0 -22
  98. janito/tui/flows/__init__.py +0 -5
  99. janito/tui/flows/changes.py +0 -65
  100. janito/tui/flows/content.py +0 -128
  101. janito/tui/flows/selection.py +0 -117
  102. janito/tui/screens/__init__.py +0 -3
  103. janito/tui/screens/app.py +0 -1
  104. janito/version.py +0 -23
  105. janito/workspace/__init__.py +0 -6
  106. janito/workspace/analysis.py +0 -121
  107. janito/workspace/show.py +0 -141
  108. janito/workspace/stats.py +0 -43
  109. janito/workspace/types.py +0 -98
  110. janito/workspace/workset.py +0 -108
  111. janito/workspace/workspace.py +0 -114
  112. janito-0.7.0.dist-info/METADATA +0 -167
  113. janito-0.7.0.dist-info/RECORD +0 -96
  114. janito-0.7.0.dist-info/entry_points.txt +0 -2
@@ -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) [year] [copyright holder]
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 this 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.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.2
2
+ Name: janito
3
+ Version: 0.9.0
4
+ Summary: Janito CLI tool
5
+ Requires-Python: >=3.8
6
+ License-File: LICENSE
7
+ Requires-Dist: typer>=0.9.0
8
+ Requires-Dist: rich>=13.0.0
9
+ Requires-Dist: claudine>=0.1.0
@@ -0,0 +1,23 @@
1
+ janito/__init__.py,sha256=-e15UAOQqb5aSP4nRNSSpPOQy8y_9nRDbxmNmCSNC_k,52
2
+ janito/__main__.py,sha256=gskP0c2f1Zu9VwZ9V5QjdGf20wginiuGWa33qeZVDyY,6867
3
+ janito/callbacks.py,sha256=_kmoRR2lDPQzNLfWPPibilbna4W-abj6hMO1VFICmwY,5288
4
+ janito/cli.py,sha256=Vbg8W79d-LEB2b4cc58a2HE-8jmZrTvaNoEg2J2543k,9381
5
+ janito/config.py,sha256=SYaMg3sqWroTaByfxGASleMLxux3s6b-fYZnT-ggqFw,2097
6
+ janito/test_file.py,sha256=c6GWGdTYG3z-Y5XBao9Tmhmq3G-v0L37OfwLgBo8zIU,126
7
+ janito/token_report.py,sha256=qLCAPce90Pgh_Q5qssA7irRB1C9e9pOfBC01Wi-ZUug,4006
8
+ janito/data/instructions.txt,sha256=WkPubK1wPnLG2PpsPikEf7lWQrRW8t1C5p65PV-1qC8,311
9
+ janito/tools/__init__.py,sha256=izLbyETR5piuFjQZ6ZY6zRgS7Tlx0yXk_wzhPn_CVYc,279
10
+ janito/tools/decorators.py,sha256=VzUHsoxtxmsd5ic1KAW42eCOT56gjjSzWbEZTcv0RZs,2617
11
+ janito/tools/delete_file.py,sha256=5JgSFtiF8bpfo0Z15ifj_RFHEHkl9cto1BES9TxIBIA,1245
12
+ janito/tools/find_files.py,sha256=bN97u3VbFBA78ssXCaEo_cFloni5PE1UW6cSDP9kvjw,5993
13
+ janito/tools/search_text.py,sha256=nABJJM_vEnMpVPfuLd_tIlVwCfXHTfo1e-K31a8IyJE,7674
14
+ janito/tools/str_replace_editor/__init__.py,sha256=kYmscmQgft3Jzt3oCNz7k2FiRbJvku6OFDDC3Q_zoAA,144
15
+ janito/tools/str_replace_editor/editor.py,sha256=XGrBADTlKwlcXat38T5th_KOPrspb9CBCP0g9KRuqmg,1345
16
+ janito/tools/str_replace_editor/handlers.py,sha256=-7HJinfiJP2s-XHHVAS6TtrNwoNtTH8IJHxuLlYH2pA,12423
17
+ janito/tools/str_replace_editor/utils.py,sha256=WOkos4bZ5Pe9U_UES6bS_QNISob0GvGN8TQVaRi6RbM,2670
18
+ janito-0.9.0.dist-info/LICENSE,sha256=6-H8LXExbBIAuT4cyiE-Qy8Bad1K4pagQRVTWr6wkhk,1096
19
+ janito-0.9.0.dist-info/METADATA,sha256=t8R4TKZwyPRMaS2BbJMhHPwIenKxaPX-Ro7QN7988Rc,216
20
+ janito-0.9.0.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
21
+ janito-0.9.0.dist-info/entry_points.txt,sha256=JMbF_1jg-xQddidpAYkzjOKdw70fy_ymJfcmerY2wIY,47
22
+ janito-0.9.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
23
+ janito-0.9.0.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ janito = janito.__main__:app
@@ -0,0 +1 @@
1
+ janito
janito/agents/__init__.py DELETED
@@ -1,22 +0,0 @@
1
- import os
2
-
3
- SYSTEM_PROMPT = """I am Janito, your friendly software development buddy. I help you with coding tasks while being clear and concise in my responses."""
4
-
5
- ai_backend = os.getenv('AI_BACKEND', 'claudeai').lower()
6
-
7
- if ai_backend == 'openai':
8
- import warnings
9
- warnings.warn(
10
- "Using deprecated OpenAI backend. Please switch to Claude AI backend by removing AI_BACKEND=openai "
11
- "from your environment variables.",
12
- DeprecationWarning,
13
- stacklevel=2
14
- )
15
- from .openai import OpenAIAgent 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(SYSTEM_PROMPT)
janito/agents/agent.py DELETED
@@ -1,28 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from threading import Event
3
- from typing import Optional, List, Tuple
4
-
5
- class Agent(ABC):
6
- """Abstract base class for AI agents"""
7
- def __init__(self, api_key: Optional[str] = None, system_prompt: str = None):
8
- self.api_key = api_key
9
- self.system_message = system_prompt
10
- self.last_prompt = None
11
- self.last_full_message = None
12
- self.last_response = None
13
- self.messages_history: List[Tuple[str, str]] = []
14
- if system_prompt:
15
- self.messages_history.append(("system", system_prompt))
16
-
17
- @abstractmethod
18
- def send_message(self, message: str, system: str) -> str:
19
- """Send message to the AI agent
20
-
21
- Args:
22
- message: The message to send
23
- stop_event: Optional event to signal cancellation
24
-
25
- Returns:
26
- The response from the AI agent
27
- """
28
- pass
janito/agents/claudeai.py DELETED
@@ -1,45 +0,0 @@
1
- import anthropic
2
- import os
3
- from typing import Optional
4
- from threading import Event
5
- from .agent import Agent
6
-
7
- class ClaudeAIAgent(Agent):
8
- """Handles interaction with Claude API, including message handling"""
9
- DEFAULT_MODEL = "claude-3-5-sonnet-20241022"
10
-
11
- def __init__(self, system_prompt: str = None):
12
- self.api_key = os.getenv('ANTHROPIC_API_KEY')
13
- super().__init__(self.api_key, system_prompt)
14
- if not system_prompt:
15
- raise ValueError("system_prompt is required")
16
-
17
- if not self.api_key:
18
- raise ValueError("ANTHROPIC_API_KEY environment variable is required")
19
- self.client = anthropic.Client(api_key=self.api_key)
20
- self.model = os.getenv('CLAUDE_MODEL', self.DEFAULT_MODEL)
21
- self.system_message = system_prompt
22
- self.last_prompt = None
23
- self.last_full_message = None
24
- self.last_response = None
25
-
26
-
27
- def send_message(self, message: str, system_message: str = None) -> str:
28
- """Send message to Claude API and return response"""
29
- self.messages_history.append(("user", message))
30
- # Store the full message
31
- self.last_full_message = message
32
-
33
- response = self.client.messages.create(
34
- model=self.model, # Use discovered model
35
- system=system_message or self.system_message,
36
- max_tokens=8192,
37
- messages=[
38
- {"role": "user", "content": message}
39
- ],
40
- temperature=0,
41
- )
42
-
43
-
44
- # Always return the response, let caller handle cancellation
45
- return response
janito/agents/openai.py DELETED
@@ -1,57 +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
- """[DEPRECATED] Handles interaction with OpenAI API, including message handling.
9
-
10
- This backend is no longer actively maintained. Please use the Claude AI backend instead.
11
- The code is kept for backward compatibility but may be removed in future versions.
12
- """
13
- DEFAULT_MODEL = "o1-mini-2024-09-12"
14
-
15
- def __init__(self, api_key: Optional[str] = None, system_prompt: str = None):
16
- super().__init__(api_key, system_prompt)
17
- if not system_prompt:
18
- raise ValueError("system_prompt is required")
19
- self.api_key = api_key or os.getenv('OPENAI_API_KEY')
20
- if not self.api_key:
21
- raise ValueError("OPENAI_API_KEY environment variable is required")
22
- openai.api_key = self.api_key
23
- openai.organization = os.getenv("OPENAI_ORG")
24
- self.client = openai.Client() # initialized client
25
- self.model = os.getenv('OPENAI_MODEL', "o1-mini-2024-09-12") # reverted to original default model
26
-
27
- def send_message(self, message: str, stop_event: Event = None) -> str:
28
- """Send message to OpenAI API and return response"""
29
- self.messages_history.append(("user", message))
30
- self.last_full_message = message
31
-
32
- try:
33
- if stop_event and stop_event.is_set():
34
- return ""
35
-
36
- #messages = [{"role": "system", "content": self.system_message}]
37
- messages = [{"role": "user", "content": message}]
38
-
39
- response = self.client.chat.completions.create(
40
- model=self.model,
41
- messages=messages,
42
- max_completion_tokens=4000,
43
- temperature=1,
44
- )
45
-
46
- response_text = response.choices[0].message.content
47
-
48
- if not (stop_event and stop_event.is_set()):
49
- self.last_response = response_text
50
- self.messages_history.append(("assistant", response_text))
51
-
52
- return response_text
53
-
54
- except KeyboardInterrupt:
55
- if stop_event:
56
- stop_event.set()
57
- 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
- ]
@@ -1,7 +0,0 @@
1
- """Main entry point for the analysis module."""
2
-
3
- from .analyze import analyze_request
4
- from janito.config import config
5
- from janito.workspace import collect_files_content
6
- from pathlib import Path
7
-
@@ -1,62 +0,0 @@
1
- """Core analysis functionality."""
2
-
3
- from typing import Optional
4
- from janito.agents import agent
5
- from janito.common import progress_send_message
6
- from janito.config import config
7
- from janito.workspace.workset import Workset
8
- from .view import format_analysis
9
- from .options import AnalysisOption, parse_analysis_options
10
- from .prompts import (
11
- build_request_analysis_prompt,
12
- get_option_selection,
13
- validate_option_letter
14
- )
15
-
16
- def analyze_request(
17
- request: str,
18
- pre_select: str = ""
19
- ) -> Optional[AnalysisOption]:
20
- """
21
- Analyze changes and get user selection.
22
-
23
- Args:
24
- request: User's change request
25
- files_content_xml: Optional content of files to analyze
26
- pre_select: Optional pre-selected option letter
27
-
28
- Returns:
29
- Selected AnalysisOption or None if modified
30
- """
31
- workset = Workset() # Create workset instance
32
-
33
- # Build and send prompt using workset content directly
34
- prompt = build_request_analysis_prompt(request)
35
- response = progress_send_message(prompt)
36
-
37
- # Parse and handle options
38
- options = parse_analysis_options(response)
39
- if not options:
40
- return None
41
-
42
- if pre_select:
43
- return options.get(pre_select.upper())
44
-
45
- if config.tui:
46
- from janito.tui import TuiApp
47
- app = TuiApp(options=options)
48
- app.run()
49
- return app.selected_option
50
-
51
- # Display formatted analysis in terminal mode
52
- format_analysis(response, config.raw)
53
-
54
- # Get user selection
55
- while True:
56
- selection = get_option_selection()
57
-
58
- if selection == 'M':
59
- return None
60
-
61
- if validate_option_letter(selection, options):
62
- return options[selection.upper()]
@@ -1,78 +0,0 @@
1
- """Centralized formatting utilities for analysis display."""
2
-
3
- from typing import Dict, List, Text
4
- from rich.text import Text
5
- from rich.columns import Columns
6
- from rich.padding import Padding
7
- from pathlib import Path
8
-
9
- # Layout constants
10
- COLUMN_SPACING = 6 # Increased spacing between columns
11
- MIN_PANEL_WIDTH = 45 # Wider minimum width for better readability
12
- SECTION_PADDING = (2, 0) # More vertical padding
13
-
14
- # Color scheme constants
15
- STATUS_COLORS = {
16
- 'new': 'bright_green',
17
- 'modified': 'yellow',
18
- 'removed': 'red',
19
- 'default': 'white'
20
- }
21
-
22
- STRUCTURAL_COLORS = {
23
- 'directory': 'dim',
24
- 'separator': 'blue dim',
25
- 'repeat': 'bright_magenta bold'
26
- }
27
-
28
- def create_header(text: str, style: str = "bold cyan") -> Text:
29
- """Create formatted header with separator."""
30
- content = Text()
31
- content.append(text, style=style)
32
- content.append("\n")
33
- content.append("═" * len(text), style="cyan")
34
- return content
35
-
36
- def create_section_header(text: str, width: int = 20) -> Text:
37
- """Create centered section header with separator."""
38
- content = Text()
39
- padding = (width - len(text)) // 2
40
- content.append(" " * padding + text, style="bold cyan")
41
- content.append("\n")
42
- content.append("─" * width, style="cyan")
43
- return content
44
-
45
- def format_file_path(path: str, status: str, max_dir_length: int = 0, is_repeated: bool = False) -> Text:
46
- """Format file path with status indicators and consistent alignment.
47
-
48
- Args:
49
- path: File path to format
50
- status: File status (Modified, New, Removed)
51
- max_dir_length: Maximum directory name length for padding
52
- is_repeated: Whether this directory was seen before
53
- """
54
- content = Text()
55
- style = STATUS_COLORS.get(status.lower(), STATUS_COLORS['default'])
56
-
57
- parts = Path(path).parts
58
- parent_dir = str(Path(path).parent)
59
-
60
- if parent_dir != '.':
61
- # Add 4 spaces for consistent base padding
62
- base_padding = 4
63
- dir_padding = max_dir_length + base_padding
64
-
65
- if is_repeated:
66
- # Add arrow with consistent spacing
67
- content.append(" " * base_padding)
68
- content.append("↑ ", style="magenta")
69
- content.append(" " * (dir_padding - base_padding - 2))
70
- else:
71
- # Left-align directory name with consistent padding
72
- content.append(" " * base_padding)
73
- content.append(parent_dir, style="dim")
74
- content.append(" " * (dir_padding - len(parent_dir) - base_padding))
75
-
76
- # Add filename with consistent spacing
77
- content.append(parts[-1], style=style)
78
- return content
@@ -1,81 +0,0 @@
1
- from dataclasses import dataclass, field
2
- from typing import Dict, List
3
- from pathlib import Path
4
-
5
- @dataclass
6
- class AnalysisOption:
7
- """Represents an analysis option with letter identifier and details"""
8
- letter: str
9
- summary: str
10
- description_items: List[str] = field(default_factory=list)
11
- affected_files: List[str] = field(default_factory=list)
12
-
13
- def format_option_text(self) -> str:
14
- """Format option details as text for change core"""
15
- text = f"Option {self.letter} - {self.summary}\n"
16
- text += "=" * len(f"Option {self.letter} - {self.summary}") + "\n\n"
17
-
18
- if self.description_items:
19
- text += "Description:\n"
20
- for item in self.description_items:
21
- text += f"- {item}\n"
22
- text += "\n"
23
-
24
- if self.affected_files:
25
- text += "Affected files:\n"
26
- for file in self.affected_files:
27
- text += f"- {file}\n"
28
-
29
- return text
30
-
31
- def is_new_directory(self, file_path: str) -> bool:
32
- """Check if file path represents the first occurrence of a directory"""
33
- parent = str(Path(file_path).parent)
34
- return parent != '.' and not any(
35
- parent in self.get_clean_path(file)
36
- for file in self.affected_files
37
- if self.get_clean_path(file) != file_path
38
- )
39
-
40
- def get_clean_path(self, file_path: str) -> str:
41
- """Remove status markers from file path"""
42
- return file_path.split(' (')[0].strip()
43
-
44
- def parse_analysis_options(content: str) -> Dict[str, AnalysisOption]:
45
- """Parse analysis options from formatted text file"""
46
- options = {}
47
- current_option = None
48
- current_section = None
49
-
50
- for line in content.splitlines():
51
- line = line.strip()
52
-
53
- # Skip empty lines and section separators
54
- if not line or line.startswith('---') or line == 'END_OF_OPTIONS':
55
- continue
56
-
57
- # New option starts with a letter and period
58
- if line[0].isalpha() and line[1:3] == '. ':
59
- letter, summary = line.split('. ', 1)
60
- current_option = AnalysisOption(letter=letter.upper(), summary=summary)
61
- options[letter.upper()] = current_option
62
- current_section = None
63
- continue
64
-
65
- # Section headers
66
- if line.lower() == 'description:':
67
- current_section = 'description'
68
- continue
69
- elif line.lower() == 'affected files:':
70
- current_section = 'files'
71
- continue
72
-
73
- # Add items to current section
74
- if current_option and line.startswith('- '):
75
- content = line[2:].strip()
76
- if current_section == 'description':
77
- current_option.description_items.append(content)
78
- elif current_section == 'files':
79
- current_option.affected_files.append(content)
80
-
81
- return options
@@ -1,90 +0,0 @@
1
- """User prompts and input handling for analysis."""
2
-
3
- from typing import List, Dict
4
- from rich.console import Console
5
- from rich.panel import Panel
6
- from rich.rule import Rule
7
- from rich.prompt import Prompt
8
- from rich import box
9
-
10
-
11
- from .options import AnalysisOption
12
-
13
- # Keep only prompt-related functionality
14
- CHANGE_ANALYSIS_PROMPT = """
15
-
16
-
17
- Considering the above workset content, provide 3 sections, each identified by a keyword and representing an option.
18
- Each option should include a concise description and a list of affected files.
19
- 1st option should be basic style change, 2nd organized style, 3rd exntensible style.
20
- Do not use style as keyword, instead focus on the changes summary.
21
-
22
- Use the following format:
23
-
24
- A. Keyword summary of the change
25
- -----------------
26
- Description:
27
- - Concise description of the change
28
-
29
- Affected files:
30
- - path/file1.py (new)
31
- - path/file2.py (modified)
32
- - path/file3.py (removed)
33
-
34
- END_OF_OPTIONS (mandatory marker)
35
-
36
- RULES:
37
- - do NOT provide the content of the files
38
- - do NOT offer to implement the changes
39
- - description items should be 80 chars or less
40
-
41
- Request:
42
- {request}
43
- """
44
-
45
- def prompt_user(message: str, choices: List[str] = None) -> str:
46
- """Display a prominent user prompt with optional choices"""
47
- console = Console()
48
- term_width = console.width or 80
49
- console.print()
50
- console.print(Rule(" User Input Required ", style="bold cyan", align="center"))
51
-
52
- if choices:
53
- choice_text = f"[cyan]Options: {', '.join(choices)}[/cyan]"
54
- console.print(Panel(choice_text, box=box.ROUNDED, justify="center"))
55
-
56
- # Center the prompt with padding
57
- padding = (term_width - len(message)) // 2
58
- padded_message = " " * padding + message
59
- return Prompt.ask(f"[bold cyan]{padded_message}[/bold cyan]")
60
-
61
- def validate_option_letter(letter: str, options: Dict[str, AnalysisOption]) -> bool:
62
- """Validate if the given letter is a valid option or 'M' for modify"""
63
- if letter.upper() == 'M':
64
- return True
65
- return letter.upper() in options
66
-
67
- def get_option_selection() -> str:
68
- """Get user input for option selection with modify option"""
69
- console = Console()
70
- term_width = console.width or 80
71
- message = "Enter option letter or 'M' to modify request"
72
- padding = (term_width - len(message)) // 2
73
- padded_message = " " * padding + message
74
-
75
- console.print(f"\n[cyan]{padded_message}[/cyan]")
76
- while True:
77
- letter = prompt_user("Select option").strip().upper()
78
- if letter == 'M' or (letter.isalpha() and len(letter) == 1):
79
- return letter
80
-
81
- error_msg = "Please enter a valid letter or 'M'"
82
- error_padding = (term_width - len(error_msg)) // 2
83
- padded_error = " " * error_padding + error_msg
84
- console.print(f"[red]{padded_error}[/red]")
85
-
86
- def build_request_analysis_prompt(request: str) -> str:
87
- """Build prompt for information requests"""
88
- return CHANGE_ANALYSIS_PROMPT.format(
89
- request=request
90
- )