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
janito/config.py CHANGED
@@ -1,102 +1,100 @@
1
- from typing import Optional, List
2
- import os
3
- from pathlib import Path
4
-
5
- class ConfigManager:
6
- _instance = None
7
-
8
- def __init__(self):
9
- self.debug = False
10
- self.verbose = False
11
- self.debug_line = None
12
- self.test_cmd = os.getenv('JANITO_TEST_CMD')
13
- self.workspace_dir = Path.cwd()
14
- self.raw = False
15
- self.include: List[Path] = []
16
- self.recursive: List[Path] = []
17
- self.auto_apply: bool = False
18
- self.tui: bool = False
19
- self.skipwork: bool = False
20
-
21
- @classmethod
22
- def get_instance(cls) -> "ConfigManager":
23
- if cls._instance is None:
24
- cls._instance = cls()
25
- return cls._instance
26
-
27
- def set_debug(self, enabled: bool) -> None:
28
- self.debug = enabled
29
-
30
- def set_verbose(self, enabled: bool) -> None:
31
- self.verbose = enabled
32
-
33
- def set_debug_line(self, line: Optional[int]) -> None:
34
- self.debug_line = line
35
-
36
- def should_debug_line(self, line: int) -> bool:
37
- """Return True if we should show debug for this line number"""
38
- return self.debug and (self.debug_line is None or self.debug_line == line)
39
-
40
- def set_test_cmd(self, cmd: Optional[str]) -> None:
41
- """Set the test command, overriding environment variable"""
42
- self.test_cmd = cmd if cmd is not None else os.getenv('JANITO_TEST_CMD')
43
-
44
- def set_workspace_dir(self, path: Optional[Path]) -> None:
45
- """Set the workspace directory"""
46
- self.workspace_dir = path if path is not None else Path.cwd()
47
-
48
- def set_raw(self, enabled: bool) -> None:
49
- """Set raw output mode"""
50
- self.raw = enabled
51
-
52
- def set_include(self, paths: Optional[List[Path]]) -> None:
53
- """
54
- Set additional paths to include.
55
-
56
- Args:
57
- paths: List of paths to include
58
-
59
- Raises:
60
- ValueError: If duplicate paths are provided
61
- """
62
- if paths is None:
63
- self.include = []
64
- return
65
-
66
- # Convert paths to absolute and resolve symlinks
67
- resolved_paths = [p.absolute().resolve() for p in paths]
68
-
69
- # Check for duplicates
70
- seen_paths = set()
71
- unique_paths = []
72
-
73
- for path in resolved_paths:
74
- if path in seen_paths:
75
- raise ValueError(f"Duplicate path provided: {path}")
76
- seen_paths.add(path)
77
- unique_paths.append(path)
78
-
79
- self.include = unique_paths
80
-
81
- def set_auto_apply(self, enabled: bool) -> None:
82
- """Set auto apply mode"""
83
- self.auto_apply = enabled
84
-
85
- def set_tui(self, enabled: bool) -> None:
86
- """Set TUI mode"""
87
- self.tui = enabled
88
-
89
- def set_recursive(self, paths: Optional[List[Path]]) -> None:
90
- """Set paths to scan recursively
91
-
92
- Args:
93
- paths: List of directory paths to scan recursively, or None to disable recursive scanning
94
- """
95
- self.recursive = paths
96
-
97
- def set_skipwork(self, enabled: bool) -> None:
98
- """Set skipwork flag to skip scanning workspace_dir"""
99
- self.skipwork = enabled
100
-
101
- # Create a singleton instance
1
+ from typing import Optional
2
+ import os
3
+ from pathlib import Path
4
+
5
+ class ConfigManager:
6
+ """Singleton configuration manager for the application."""
7
+
8
+ _instance = None
9
+
10
+ def __init__(self):
11
+ """Initialize configuration with default values."""
12
+ self.debug = False
13
+ self.verbose = False
14
+ self.test_cmd = os.getenv('JANITO_TEST_CMD')
15
+ self.workspace_dir = Path.cwd()
16
+ self.raw = False
17
+ self.auto_apply: bool = False
18
+ self.skip_work: bool = False
19
+
20
+ @classmethod
21
+ def get_instance(cls) -> "ConfigManager":
22
+ """Return the singleton instance of ConfigManager.
23
+
24
+ Returns:
25
+ ConfigManager: The singleton instance
26
+ """
27
+ if cls._instance is None:
28
+ cls._instance = cls()
29
+ return cls._instance
30
+
31
+ def set_debug(self, enabled: bool) -> None:
32
+ """Set debug mode.
33
+
34
+ Args:
35
+ enabled: True to enable debug mode, False to disable
36
+ """
37
+ self.debug = enabled
38
+
39
+ def set_verbose(self, enabled: bool) -> None:
40
+ """Set verbose output mode.
41
+
42
+ Args:
43
+ enabled: True to enable verbose output, False to disable
44
+ """
45
+ self.verbose = enabled
46
+
47
+ def set_debug_line(self, line: Optional[int]) -> None:
48
+ """Set specific line number for debug output.
49
+
50
+ Args:
51
+ line: Line number to debug, or None for all lines
52
+ """
53
+ self.debug_line = line
54
+
55
+ def should_debug_line(self, line: int) -> bool:
56
+ """Return True if we should show debug for this line number"""
57
+ return self.debug and (self.debug_line is None or self.debug_line == line)
58
+
59
+ def set_test_cmd(self, cmd: Optional[str]) -> None:
60
+ """Set the test command, overriding environment variable"""
61
+ self.test_cmd = cmd if cmd is not None else os.getenv('JANITO_TEST_CMD')
62
+
63
+ def set_workspace_dir(self, path: Optional[Path]) -> None:
64
+ """Set the workspace directory"""
65
+ self.workspace_dir = path if path is not None else Path.cwd()
66
+
67
+ def set_raw(self, enabled: bool) -> None:
68
+ """Set raw output mode.
69
+
70
+ Args:
71
+ enabled: True to enable raw output mode, False to disable
72
+ """
73
+ self.raw = enabled
74
+
75
+ def set_auto_apply(self, enabled: bool) -> None:
76
+ """Set auto apply mode for changes.
77
+
78
+ Args:
79
+ enabled: True to enable auto apply mode, False to disable
80
+ """
81
+ self.auto_apply = enabled
82
+
83
+ def set_tui(self, enabled: bool) -> None:
84
+ """Set Text User Interface mode.
85
+
86
+ Args:
87
+ enabled: True to enable TUI mode, False to disable
88
+ """
89
+ self.tui = enabled
90
+
91
+ def set_skip_work(self, enabled: bool) -> None:
92
+ """Set whether to skip scanning the workspace directory.
93
+
94
+ Args:
95
+ enabled: True to skip workspace directory, False to include it
96
+ """
97
+ self.skip_work = enabled
98
+
99
+ # Create a singleton instance
102
100
  config = ConfigManager.get_instance()
@@ -0,0 +1,81 @@
1
+ Provide the instructions for the change request:
2
+ {request}
3
+
4
+ This is the workset:
5
+
6
+ {workset}
7
+
8
+ Before providing the actual changes, please provide an action plan in markdown format. The action plan should include the following sections:
9
+
10
+ Execution Planning:
11
+ 1. Brief introduction refering the files and sections that will be affected.
12
+
13
+ Implementation:
14
+ - Both the original code and the modified code should be provided in plain text format with:
15
+ - original indentation as found in the original file
16
+ - original or modified indentation as might be required for the changes (eg, adding a try block will shift the indentation of the code inside the block)
17
+ - When the user asks for edits to their code, the assistant will provide one or edit blocks, a block can contain multiple changes to the same file.
18
+ - When new imports are required, they should be placed at the top of the existing files, even if that requires an extra edit block.
19
+ - Merge edit blocks that would affect the same function/method into a single edit block
20
+
21
+ Edit <path/to/file> "<reason for the change>"
22
+ <<<< original
23
+ {{ Assistant writes original lines found in the workset }}
24
+ >>>> modified
25
+ {{ Assistant writes the lines to replace the original }}
26
+ ==== # marks end of file edit block
27
+
28
+ # For removing large pieces of code, use the "clean" operation
29
+ Clean <path/to/file> "{{ Assistant writes reason }}"
30
+ <<<< starting
31
+ {{ Assistant writes the first lines to match where cleaning starts }}
32
+ >>>> ending
33
+ {{ Assistant writes the last lines to match where cleaning ends }}
34
+ # The cleaning operation removes all content between and including the starting and ending markers
35
+ # Be careful to provide minimal but sufficient lines to identify the correct content
36
+ ====
37
+
38
+ Create <path/to/file> "{{ Assistant writes reason }}"
39
+ >>>> modified
40
+ {{ Assistant writes the full content of the file }}
41
+ ====
42
+
43
+ Delete <path/to/file> "{{ Assistant writes reason }}"
44
+ ==== # for deletes there is no need for markers, just the end marker
45
+
46
+ Avoid common pitfalls:
47
+
48
+ - Adding a try block without the entire affected content
49
+ Edit file
50
+ <<<< original
51
+ def _apply_and_collect_change(self, edit: CodeChange) -> AppliedBlock:
52
+ """Apply a single edit and collect its change information."""
53
+ do_thing()
54
+
55
+ >>>> modified
56
+ def _apply_and_collect_change(self, edit: CodeChange) -> AppliedBlock:
57
+ """Apply a single edit and collect its change information."""
58
+ try:
59
+ do_thing()
60
+ ====
61
+ This would cause a syntax error, because do_thing is very likely followed by other code which would be kept outside the try block. The correct way to add a try block is to include all the code that should be inside the try block.
62
+
63
+ Correct would be:
64
+ Edit file
65
+ <<<< original
66
+ def _apply_and_collect_change(self, edit: CodeChange) -> AppliedBlock:
67
+ """Apply a single edit and collect its change information."""
68
+ do_thing()
69
+ do_other_thing()
70
+ >>>> modified
71
+ def _apply_and_collect_change(self, edit: CodeChange) -> AppliedBlock:
72
+ """Apply a single edit and collect its change information."""
73
+ try:
74
+ do_thing()
75
+ do_other_thing()
76
+ except Exception as e:
77
+ handle_exception(e)
78
+ =====
79
+
80
+ Final Summary:
81
+ Provide a one line summary of the expected result of the changes.
@@ -0,0 +1,3 @@
1
+ The assistant is an intelligent programmer, powered by Claude 3.5 Sonnet.
2
+ It is happy to help answer any questions that the user has (usually about coding).
3
+
janito/qa.py CHANGED
@@ -1,66 +1,56 @@
1
- from rich.console import Console
2
- from rich.markdown import Markdown
3
- from rich.panel import Panel
4
- from rich.syntax import Syntax
5
- from rich.table import Table
6
- from rich.rule import Rule
7
- from janito.agents import AIAgent
8
- from janito.common import progress_send_message
9
- from janito.workspace import workspace
10
-
11
-
12
- QA_PROMPT = """Please provide a clear and concise answer to the following question about the codebase:
13
-
14
- Question: {question}
15
-
16
- Current files:
17
- <files>
18
- {files_content}
19
- </files>
20
-
21
- Focus on providing factual information and explanations. Do not suggest code changes.
22
- Format your response using markdown with appropriate headers and code blocks.
23
- """
24
-
25
- def ask_question(question: str, files_content: str) -> str:
26
- """Process a question about the codebase and return the answer"""
27
- # Analyze workspace content if needed
28
- workspace.analyze()
29
-
30
- prompt = QA_PROMPT.format(
31
- question=question,
32
- files_content=files_content
33
- )
34
- return progress_send_message(prompt)
35
-
36
-
37
- def display_answer(answer: str, raw: bool = False) -> None:
38
- """Display the answer as markdown with consistent colors"""
39
- console = Console()
40
-
41
- # Define consistent colors
42
- COLORS = {
43
- 'primary': '#729FCF', # Soft blue for primary elements
44
- 'secondary': '#8AE234', # Bright green for actions/success
45
- 'accent': '#AD7FA8', # Purple for accents
46
- 'muted': '#7F9F7F', # Muted green for less important text
47
- }
48
-
49
- if raw:
50
- console.print(answer)
51
- return
52
-
53
- # Display markdown answer in a panel with consistent styling
54
- answer_panel = Panel(
55
- Markdown(answer),
56
- title="[bold]Answer[/bold]",
57
- title_align="center",
58
- border_style=COLORS['primary'],
59
- padding=(1, 2)
60
- )
61
-
62
- console.print("\n")
63
- console.print(Rule(style=COLORS['accent']))
64
- console.print(answer_panel)
65
- console.print(Rule(style=COLORS['accent']))
66
- console.print("\n")
1
+ from rich.console import Console
2
+ from rich.markdown import Markdown
3
+ from rich.panel import Panel
4
+ from janito.common import progress_send_message
5
+ from janito.workspace import workset
6
+ from pathlib import Path
7
+
8
+ QA_PROMPT = """Please provide a clear and concise answer to the following question about the workset provided later.
9
+
10
+ Question: {question}
11
+
12
+ Focus on providing factual information and explanations. Do not suggest code changes.
13
+ Format your response using markdown with appropriate headers and code blocks.
14
+
15
+ workset content:
16
+ {workset}
17
+ """
18
+
19
+ def ask_question(question: str, file_filter: list[Path] = None) -> str:
20
+ """Process a question about the codebase and return the answer
21
+
22
+ Args:
23
+ question: The question to ask about the codebase
24
+ file_filter: list of paths to files to include in the workset
25
+
26
+ Returns:
27
+ str: The answer from the AI agent, or an error message if interrupted
28
+ """
29
+ workset.refresh()
30
+
31
+ prompt = QA_PROMPT.format(
32
+ question=question,
33
+ workset=workset.content
34
+ )
35
+ answer = progress_send_message(prompt)
36
+
37
+ if answer is None:
38
+ return "Sorry, the response was interrupted. Please try asking your question again."
39
+
40
+ return answer
41
+
42
+
43
+ def display_answer(answer: str, raw: bool = False) -> None:
44
+ """Display the answer as markdown"""
45
+ if answer is None:
46
+ Console().print("\n[red]Error: No answer received - the response was interrupted[/red]\n")
47
+ return
48
+
49
+ console = Console()
50
+
51
+ if raw:
52
+ console.print(answer)
53
+ return
54
+
55
+ # Display markdown answer directly
56
+ console.print(Markdown(answer))
janito/version.py CHANGED
@@ -1,23 +1,23 @@
1
- """Version management module for Janito."""
2
- from pathlib import Path
3
- from typing import Optional
4
- import tomli
5
- from importlib.metadata import version as pkg_version
6
-
7
- def get_version() -> str:
8
- """
9
- Get Janito version from package metadata or pyproject.toml.
10
-
11
- Returns:
12
- str: The version string
13
- """
14
- try:
15
- return pkg_version("janito")
16
- except Exception:
17
- # Fallback to pyproject.toml
18
- pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
19
- if pyproject_path.exists():
20
- with open(pyproject_path, "rb") as f:
21
- pyproject_data = tomli.load(f)
22
- return pyproject_data.get("project", {}).get("version", "unknown")
1
+ """Version management module for Janito."""
2
+ from pathlib import Path
3
+ from typing import Optional
4
+ import tomli
5
+ from importlib.metadata import version as pkg_version
6
+
7
+ def get_version() -> str:
8
+ """
9
+ Get Janito version from package metadata or pyproject.toml.
10
+
11
+ Returns:
12
+ str: The version string
13
+ """
14
+ try:
15
+ return pkg_version("janito")
16
+ except Exception:
17
+ # Fallback to pyproject.toml
18
+ pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
19
+ if pyproject_path.exists():
20
+ with open(pyproject_path, "rb") as f:
21
+ pyproject_data = tomli.load(f)
22
+ return pyproject_data.get("project", {}).get("version", "unknown")
23
23
  return "unknown"
@@ -1,7 +1,8 @@
1
- from .manager import WorkspaceManager
2
- from .scan import preview_scan, collect_files_content, is_dir_empty
3
-
4
- # Create singleton instance
5
- workspace = WorkspaceManager.get_instance()
6
-
7
- __all__ = ['workspace', 'preview_scan', 'collect_files_content', 'is_dir_empty']
1
+ from .workset import Workset
2
+ from .workspace import Workspace
3
+
4
+ # Create and export singleton instance
5
+ workset = Workset()
6
+ workspace = Workspace()
7
+
8
+ __all__ = ['workset', 'workspace']