janito 0.3.0__py3-none-any.whl → 0.4.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.
- janito/__main__.py +158 -59
- janito/analysis.py +281 -0
- janito/changeapplier.py +436 -0
- janito/changeviewer.py +337 -51
- janito/claude.py +31 -46
- janito/common.py +23 -0
- janito/config.py +8 -3
- janito/console.py +300 -30
- janito/contentchange.py +8 -89
- janito/contextparser.py +113 -0
- janito/fileparser.py +125 -0
- janito/prompts.py +43 -74
- janito/qa.py +36 -5
- janito/scan.py +24 -9
- janito/version.py +23 -0
- {janito-0.3.0.dist-info → janito-0.4.0.dist-info}/METADATA +34 -8
- janito-0.4.0.dist-info/RECORD +21 -0
- janito-0.3.0.dist-info/RECORD +0 -15
- {janito-0.3.0.dist-info → janito-0.4.0.dist-info}/WHEEL +0 -0
- {janito-0.3.0.dist-info → janito-0.4.0.dist-info}/entry_points.txt +0 -0
- {janito-0.3.0.dist-info → janito-0.4.0.dist-info}/licenses/LICENSE +0 -0
janito/fileparser.py
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Dict, Tuple, List, Optional
|
3
|
+
from dataclasses import dataclass
|
4
|
+
import re
|
5
|
+
import ast
|
6
|
+
import sys # Add this import
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.panel import Panel # Add this import
|
9
|
+
from janito.config import config # Add this import
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class FileChange:
|
13
|
+
"""Represents a file change with search/replace, search/delete or create instructions"""
|
14
|
+
description: str
|
15
|
+
is_new_file: bool
|
16
|
+
content: str = "" # For new files
|
17
|
+
search_blocks: List[Tuple[str, Optional[str], Optional[str]]] = None # (search, replace, description)
|
18
|
+
|
19
|
+
def add_search_block(self, search: str, replace: Optional[str], description: Optional[str] = None) -> None:
|
20
|
+
"""Add a search/replace or search/delete block with optional description"""
|
21
|
+
if self.search_blocks is None:
|
22
|
+
self.search_blocks = []
|
23
|
+
self.search_blocks.append((search, replace, description))
|
24
|
+
|
25
|
+
def validate_python_syntax(content: str, filepath: Path) -> Tuple[bool, str]:
|
26
|
+
"""Validate Python syntax and return (is_valid, error_message)"""
|
27
|
+
try:
|
28
|
+
ast.parse(content)
|
29
|
+
console = Console()
|
30
|
+
console.print(f"[green]✓ Python syntax validation passed:[/green] {filepath.absolute()}")
|
31
|
+
return True, ""
|
32
|
+
except SyntaxError as e:
|
33
|
+
error_msg = f"Line {e.lineno}: {e.msg}"
|
34
|
+
console = Console()
|
35
|
+
console.print(f"[red]✗ Python syntax validation failed:[/red] {filepath.absolute()}")
|
36
|
+
console.print(f"[red] {error_msg}[/red]")
|
37
|
+
return False, error_msg
|
38
|
+
|
39
|
+
|
40
|
+
def parse_block_changes(response_text: str) -> Dict[Path, FileChange]:
|
41
|
+
"""Parse file changes from response blocks"""
|
42
|
+
changes = {}
|
43
|
+
console = Console()
|
44
|
+
# Match file blocks with UUID
|
45
|
+
file_pattern = r'## ([a-f0-9]{8}) file (.*?) (modify|create) "(.*?)" ##\n?(.*?)## \1 file end ##'
|
46
|
+
|
47
|
+
for match in re.finditer(file_pattern, response_text, re.DOTALL):
|
48
|
+
uuid, filepath, action, description, content = match.groups()
|
49
|
+
path = Path(filepath.strip())
|
50
|
+
|
51
|
+
if action == 'create':
|
52
|
+
changes[path] = FileChange(
|
53
|
+
description=description,
|
54
|
+
is_new_file=True,
|
55
|
+
content=content[1:] if content.startswith('\n') else content,
|
56
|
+
search_blocks=[]
|
57
|
+
)
|
58
|
+
continue
|
59
|
+
|
60
|
+
# For modifications, find all search/replace and search/delete blocks
|
61
|
+
search_blocks = []
|
62
|
+
block_patterns = [
|
63
|
+
# Match search/replace blocks with description - updated pattern
|
64
|
+
(r'## ' + re.escape(uuid) + r' search/replace "(.*?)" ##\n?(.*?)## ' +
|
65
|
+
re.escape(uuid) + r' replace with ##\n?(.*?)(?=## ' + re.escape(uuid) + r'|$)', False),
|
66
|
+
# Match search/delete blocks with description
|
67
|
+
(r'## ' + re.escape(uuid) + r' search/delete "(.*?)" ##\n?(.*?)(?=## ' + re.escape(uuid) + r'|$)', True)
|
68
|
+
]
|
69
|
+
|
70
|
+
if config.debug:
|
71
|
+
console.print("\n[blue]Updated regex patterns:[/blue]")
|
72
|
+
for pattern, is_delete in block_patterns:
|
73
|
+
console.print(Panel(pattern, title="Search/Replace Pattern" if not is_delete else "Search/Delete Pattern", border_style="blue"))
|
74
|
+
|
75
|
+
for pattern, is_delete in block_patterns:
|
76
|
+
if config.debug:
|
77
|
+
console.print(f"\n[blue]Looking for pattern:[/blue]")
|
78
|
+
console.print(Panel(pattern, title="Pattern", border_style="blue"))
|
79
|
+
console.print(f"\n[blue]In content:[/blue]")
|
80
|
+
console.print(Panel(content, title="Content", border_style="blue"))
|
81
|
+
|
82
|
+
for block_match in re.finditer(pattern, content, re.DOTALL):
|
83
|
+
if is_delete:
|
84
|
+
description, search = block_match.groups()
|
85
|
+
search = search.rstrip('\n') + '\n' # Ensure single trailing newline
|
86
|
+
replace = None
|
87
|
+
else:
|
88
|
+
description, search, replace = block_match.groups()
|
89
|
+
search = search.rstrip('\n') + '\n' # Ensure single trailing newline
|
90
|
+
replace = (replace.rstrip('\n') + '\n') if replace else None
|
91
|
+
|
92
|
+
# Abort parsing if replace content is empty
|
93
|
+
if not is_delete and (replace is None or replace.strip() == ''):
|
94
|
+
console.print(f"\n[red]Error: Empty replace content found![/red]")
|
95
|
+
console.print(f"[red]File:[/red] {filepath}")
|
96
|
+
console.print(f"[red]Description:[/red] {description}")
|
97
|
+
console.print("[yellow]Search block:[/yellow]")
|
98
|
+
console.print(Panel(search, title="Search Content", border_style="yellow"))
|
99
|
+
console.print("[red]Replace block is empty or contains only whitespace![/red]")
|
100
|
+
console.print("[red]Aborting due to empty replace content.[/red]")
|
101
|
+
sys.exit(1)
|
102
|
+
|
103
|
+
# Enhanced debug info
|
104
|
+
if config.debug or (not is_delete and (replace is None or replace.strip() == '')):
|
105
|
+
console.print(f"\n[yellow]Search/Replace block analysis:[/yellow]")
|
106
|
+
console.print(f"[yellow]File:[/yellow] {filepath}")
|
107
|
+
console.print(f"[yellow]Description:[/yellow] {description}")
|
108
|
+
console.print("[yellow]Search block:[/yellow]")
|
109
|
+
console.print(Panel(search, title="Search Content", border_style="yellow"))
|
110
|
+
console.print("[yellow]Replace block:[/yellow]")
|
111
|
+
console.print(Panel(replace if replace else "<empty>", title="Replace Content", border_style="yellow"))
|
112
|
+
console.print("\n[blue]Match groups:[/blue]")
|
113
|
+
for i, group in enumerate(block_match.groups()):
|
114
|
+
console.print(Panel(str(group), title=f"Group {i}", border_style="blue"))
|
115
|
+
|
116
|
+
search_blocks.append((search, replace, description))
|
117
|
+
|
118
|
+
# Add debug info if no blocks were found
|
119
|
+
if config.debug and not search_blocks:
|
120
|
+
console.print(f"\n[red]No search/replace blocks found for file:[/red] {filepath}")
|
121
|
+
console.print("[red]Check if the content format matches the expected patterns[/red]")
|
122
|
+
|
123
|
+
changes[path] = FileChange(description=description, is_new_file=False, search_blocks=search_blocks)
|
124
|
+
|
125
|
+
return changes
|
janito/prompts.py
CHANGED
@@ -1,25 +1,12 @@
|
|
1
1
|
import re
|
2
|
+
import uuid
|
3
|
+
from typing import List, Union
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from .analysis import parse_analysis_options, AnalysisOption
|
2
6
|
|
3
7
|
# Core system prompt focused on role and purpose
|
4
|
-
SYSTEM_PROMPT = """
|
5
|
-
"""
|
6
|
-
|
8
|
+
SYSTEM_PROMPT = """I am Janito, your friendly software development buddy. I help you with coding tasks while being clear and concise in my responses."""
|
7
9
|
|
8
|
-
CHANGE_ANALISYS_PROMPT = """
|
9
|
-
Current files:
|
10
|
-
<files>
|
11
|
-
{files_content}
|
12
|
-
</files>
|
13
|
-
|
14
|
-
Considering the current files content, provide a table of options for the requested change.
|
15
|
-
Always provide options using a header label "=== **Option 1** : ...", "=== **Option 2**: ...", etc.
|
16
|
-
Provide the header with a short description followed by the file changes on the next line
|
17
|
-
What files should be modified and what should they contain? (one line description)
|
18
|
-
Do not provide the content of any of the file suggested to be created or modified.
|
19
|
-
|
20
|
-
Request:
|
21
|
-
{request}
|
22
|
-
"""
|
23
10
|
|
24
11
|
SELECTED_OPTION_PROMPT = """
|
25
12
|
Original request: {request}
|
@@ -32,66 +19,48 @@ Current files:
|
|
32
19
|
{files_content}
|
33
20
|
</files>
|
34
21
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
22
|
+
RULES:
|
23
|
+
- When revmoing constants, ensure they are not used elsewhere
|
24
|
+
- When adding new features to python files, add the necessary imports
|
25
|
+
- Python imports should be inserted at the top of the file
|
26
|
+
|
27
|
+
Please provide the changes in this format:
|
28
|
+
|
29
|
+
## {uuid} file <filepath> modify "short file change description" ##
|
30
|
+
## {uuid} search/replace "short change description" ##
|
31
|
+
<search_content>
|
32
|
+
## {uuid} replace with ##
|
33
|
+
<replace_content>
|
34
|
+
## {uuid} file end ##
|
35
|
+
|
36
|
+
Or to delete content:
|
37
|
+
## {uuid} file <filepath> modify ##
|
38
|
+
## {uuid} search/delete "short change description" ##
|
39
|
+
<content_to_delete>
|
40
|
+
## {uuid} file end ##
|
41
|
+
|
42
|
+
For new files:
|
43
|
+
## {uuid} file <filepath> create "short description" ##
|
44
|
+
<full_file_content>
|
45
|
+
## {uuid} file end ##
|
46
|
+
|
47
|
+
RULES:
|
48
|
+
1. search_content MUST preserve the original identation/whitespace
|
43
49
|
"""
|
44
50
|
|
45
|
-
def build_selected_option_prompt(
|
46
|
-
"""Build prompt for selected option details
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
def build_selected_option_prompt(option_text: str, request: str, files_content: str = "") -> str:
|
52
|
+
"""Build prompt for selected option details
|
53
|
+
|
54
|
+
Args:
|
55
|
+
option_text: Formatted text describing the selected option
|
56
|
+
request: The original user request
|
57
|
+
files_content: Content of relevant files
|
58
|
+
"""
|
59
|
+
short_uuid = str(uuid.uuid4())[:8]
|
50
60
|
|
51
61
|
return SELECTED_OPTION_PROMPT.format(
|
52
|
-
option_text=
|
62
|
+
option_text=option_text,
|
53
63
|
request=request,
|
54
|
-
files_content=files_content
|
55
|
-
)
|
56
|
-
|
57
|
-
def parse_options(response: str) -> dict[int, str]:
|
58
|
-
"""Parse options from the response text, including any list items after the option label"""
|
59
|
-
options = {}
|
60
|
-
pattern = r"===\s*\*\*Option (\d+)\*\*\s*:\s*(.+?)(?====\s*\*\*Option|\Z)"
|
61
|
-
matches = re.finditer(pattern, response, re.DOTALL)
|
62
|
-
|
63
|
-
for match in matches:
|
64
|
-
option_num = int(match.group(1))
|
65
|
-
option_text = match.group(2).strip()
|
66
|
-
|
67
|
-
# Split into description and list items
|
68
|
-
lines = option_text.splitlines()
|
69
|
-
description = lines[0]
|
70
|
-
list_items = []
|
71
|
-
|
72
|
-
# Collect list items that follow
|
73
|
-
for line in lines[1:]:
|
74
|
-
line = line.strip()
|
75
|
-
if line.startswith(('- ', '* ', '• ')):
|
76
|
-
list_items.append(line)
|
77
|
-
elif not line:
|
78
|
-
continue
|
79
|
-
else:
|
80
|
-
break
|
81
|
-
|
82
|
-
# Combine description with list items if any exist
|
83
|
-
if list_items:
|
84
|
-
option_text = description + '\n' + '\n'.join(list_items)
|
85
|
-
|
86
|
-
options[option_num] = option_text
|
87
|
-
|
88
|
-
return options
|
89
|
-
|
90
|
-
|
91
|
-
def build_request_analisys_prompt(files_content: str, request: str) -> str:
|
92
|
-
"""Build prompt for information requests"""
|
93
|
-
|
94
|
-
return CHANGE_ANALISYS_PROMPT.format(
|
95
64
|
files_content=files_content,
|
96
|
-
|
65
|
+
uuid=short_uuid
|
97
66
|
)
|
janito/qa.py
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
from rich.console import Console
|
2
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
|
3
7
|
from janito.claude import ClaudeAPIAgent
|
8
|
+
from janito.common import progress_send_message
|
9
|
+
from typing import Dict, List
|
10
|
+
import re
|
4
11
|
|
5
12
|
QA_PROMPT = """Please provide a clear and concise answer to the following question about the codebase:
|
6
13
|
|
@@ -12,6 +19,7 @@ Current files:
|
|
12
19
|
</files>
|
13
20
|
|
14
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.
|
15
23
|
"""
|
16
24
|
|
17
25
|
def ask_question(question: str, files_content: str, claude: ClaudeAPIAgent) -> str:
|
@@ -20,13 +28,36 @@ def ask_question(question: str, files_content: str, claude: ClaudeAPIAgent) -> s
|
|
20
28
|
question=question,
|
21
29
|
files_content=files_content
|
22
30
|
)
|
23
|
-
return claude
|
31
|
+
return progress_send_message(claude, prompt)
|
32
|
+
|
24
33
|
|
25
34
|
def display_answer(answer: str, raw: bool = False) -> None:
|
26
|
-
"""Display the answer
|
35
|
+
"""Display the answer as markdown with consistent colors"""
|
27
36
|
console = Console()
|
37
|
+
|
38
|
+
# Define consistent colors
|
39
|
+
COLORS = {
|
40
|
+
'primary': '#729FCF', # Soft blue for primary elements
|
41
|
+
'secondary': '#8AE234', # Bright green for actions/success
|
42
|
+
'accent': '#AD7FA8', # Purple for accents
|
43
|
+
'muted': '#7F9F7F', # Muted green for less important text
|
44
|
+
}
|
45
|
+
|
28
46
|
if raw:
|
29
47
|
console.print(answer)
|
30
|
-
|
31
|
-
|
32
|
-
|
48
|
+
return
|
49
|
+
|
50
|
+
# Display markdown answer in a panel with consistent styling
|
51
|
+
answer_panel = Panel(
|
52
|
+
Markdown(answer),
|
53
|
+
title="[bold]Answer[/bold]",
|
54
|
+
title_align="center",
|
55
|
+
border_style=COLORS['primary'],
|
56
|
+
padding=(1, 2)
|
57
|
+
)
|
58
|
+
|
59
|
+
console.print("\n")
|
60
|
+
console.print(Rule(style=COLORS['accent']))
|
61
|
+
console.print(answer_panel)
|
62
|
+
console.print(Rule(style=COLORS['accent']))
|
63
|
+
console.print("\n")
|
janito/scan.py
CHANGED
@@ -19,7 +19,7 @@ def _scan_paths(paths: List[Path], workdir: Path = None) -> Tuple[List[str], Lis
|
|
19
19
|
# Load gitignore if it exists
|
20
20
|
gitignore_path = workdir / '.gitignore' if workdir else None
|
21
21
|
gitignore_spec = None
|
22
|
-
if gitignore_path and gitignore_path.exists():
|
22
|
+
if (gitignore_path and gitignore_path.exists()):
|
23
23
|
with open(gitignore_path) as f:
|
24
24
|
gitignore = f.read()
|
25
25
|
gitignore_spec = PathSpec.from_lines(GitWildMatchPattern, gitignore.splitlines())
|
@@ -100,21 +100,36 @@ def collect_files_content(paths: List[Path], workdir: Path = None) -> str:
|
|
100
100
|
|
101
101
|
return "\n".join(content_parts)
|
102
102
|
|
103
|
+
|
103
104
|
def preview_scan(paths: List[Path], workdir: Path = None) -> None:
|
104
105
|
"""Preview what files and directories would be scanned"""
|
105
106
|
console = Console()
|
106
107
|
_, file_items = _scan_paths(paths, workdir)
|
107
108
|
|
108
|
-
#
|
109
|
-
|
110
|
-
|
109
|
+
# Display working directory status
|
110
|
+
console.print("\n[bold blue]Analysis Paths:[/bold blue]")
|
111
|
+
console.print(f"[cyan]Working Directory:[/cyan] {workdir.absolute()}")
|
112
|
+
|
113
|
+
# Show if working directory is being scanned
|
114
|
+
is_workdir_scanned = any(p.resolve() == workdir.resolve() for p in paths)
|
115
|
+
if is_workdir_scanned:
|
116
|
+
console.print("[green]✓ Working directory will be scanned[/green]")
|
111
117
|
else:
|
112
|
-
console.print(
|
113
|
-
|
118
|
+
console.print("[yellow]! Working directory will not be scanned[/yellow]")
|
119
|
+
|
120
|
+
# Show included paths relative to working directory
|
121
|
+
if len(paths) > (1 if is_workdir_scanned else 0):
|
122
|
+
console.print("\n[cyan]Additional Included Paths:[/cyan]")
|
114
123
|
for path in paths:
|
115
|
-
|
116
|
-
|
117
|
-
|
124
|
+
if path.resolve() != workdir.resolve():
|
125
|
+
try:
|
126
|
+
rel_path = path.relative_to(workdir)
|
127
|
+
console.print(f" • ./{rel_path}")
|
128
|
+
except ValueError:
|
129
|
+
# Path is outside working directory
|
130
|
+
console.print(f" • {path.absolute()}")
|
131
|
+
|
132
|
+
console.print("\n[bold blue]Files that will be analyzed:[/bold blue]")
|
118
133
|
console.print(Columns(file_items, padding=(0, 4), expand=True))
|
119
134
|
|
120
135
|
def is_dir_empty(path: Path) -> bool:
|
janito/version.py
ADDED
@@ -0,0 +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")
|
23
|
+
return "unknown"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: janito
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: A CLI tool for software development tasks powered by AI
|
5
5
|
Project-URL: Homepage, https://github.com/joaompinto/janito
|
6
6
|
Project-URL: Repository, https://github.com/joaompinto/janito.git
|
@@ -18,6 +18,7 @@ Requires-Python: >=3.8
|
|
18
18
|
Requires-Dist: anthropic
|
19
19
|
Requires-Dist: pathspec
|
20
20
|
Requires-Dist: rich
|
21
|
+
Requires-Dist: tomli
|
21
22
|
Requires-Dist: typer
|
22
23
|
Description-Content-Type: text/markdown
|
23
24
|
|
@@ -25,7 +26,7 @@ Description-Content-Type: text/markdown
|
|
25
26
|
|
26
27
|
A CLI tool for software development tasks powered by AI.
|
27
28
|
|
28
|
-
Janito
|
29
|
+
Meet Janito, your friendly AI-powered software development buddy! Janito helps you with coding tasks like refactoring, documentation updates, and code optimization while being clear and concise in its responses.
|
29
30
|
|
30
31
|
## 📥 Installation
|
31
32
|
|
@@ -59,6 +60,15 @@ export ANTHROPIC_API_KEY='your-api-key-here'
|
|
59
60
|
|
60
61
|
You can also add this to your shell profile (~/.bashrc, ~/.zshrc, etc.) for persistence.
|
61
62
|
|
63
|
+
### ⚙️ Test Command Setup
|
64
|
+
You can configure a test command to run before applying changes:
|
65
|
+
|
66
|
+
```bash
|
67
|
+
export JANITO_TEST_CMD='your-test-command'
|
68
|
+
```
|
69
|
+
|
70
|
+
This command will be executed in the preview directory before applying changes to verify they don't break anything.
|
71
|
+
|
62
72
|
## 📖 Usage
|
63
73
|
|
64
74
|
Janito can be used in two modes: Command Line or Interactive Console.
|
@@ -70,17 +80,26 @@ janito REQUEST [OPTIONS]
|
|
70
80
|
```
|
71
81
|
|
72
82
|
#### Arguments
|
73
|
-
- `REQUEST`: The modification request
|
83
|
+
- `REQUEST`: The modification request to process (optional)
|
74
84
|
|
75
85
|
#### Options
|
86
|
+
##### General Options
|
87
|
+
- `--version`: Show version and exit
|
76
88
|
- `-w, --workdir PATH`: Working directory (defaults to current directory)
|
77
|
-
-
|
89
|
+
- `-i, --include PATH`: Additional paths to include in analysis (can be used multiple times)
|
90
|
+
|
91
|
+
##### Operation Modes
|
92
|
+
- `--ask TEXT`: Ask a question about the codebase instead of making changes
|
93
|
+
- `--scan`: Preview files that would be analyzed without making changes
|
78
94
|
- `--play PATH`: Replay a saved prompt file
|
79
|
-
|
80
|
-
|
95
|
+
|
96
|
+
##### Output Control
|
97
|
+
- `--raw`: Print raw response instead of markdown format
|
81
98
|
- `-v, --verbose`: Show verbose output
|
82
|
-
- `--
|
83
|
-
|
99
|
+
- `--debug`: Show debug information
|
100
|
+
|
101
|
+
##### Testing
|
102
|
+
- `-t, --test COMMAND`: Test command to run before applying changes (overrides JANITO_TEST_CMD)
|
84
103
|
|
85
104
|
### 🖥️ Interactive Console Mode
|
86
105
|
|
@@ -105,6 +124,12 @@ janito "update tests" -i ./tests -i ./lib
|
|
105
124
|
janito --ask "explain the authentication flow"
|
106
125
|
janito --scan # Preview files to be analyzed
|
107
126
|
|
127
|
+
# Test Command Examples
|
128
|
+
janito "update code" --test "pytest" # Run pytest before applying changes
|
129
|
+
janito "refactor module" -t "make test" # Run make test before applying
|
130
|
+
export JANITO_TEST_CMD="python -m unittest" # Set default test command
|
131
|
+
janito "optimize function" # Will use JANITO_TEST_CMD
|
132
|
+
|
108
133
|
# Console Mode
|
109
134
|
janito # Starts interactive session
|
110
135
|
```
|
@@ -120,6 +145,7 @@ janito # Starts interactive session
|
|
120
145
|
- 🐛 Debug and verbose output modes
|
121
146
|
- ❓ Question-answering about codebase
|
122
147
|
- 🔍 File scanning preview
|
148
|
+
- 🧪 Test command execution before applying changes
|
123
149
|
|
124
150
|
## 📚 History and Debugging
|
125
151
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
janito/__init__.py,sha256=CLeVFqpY9Ki3R3MgLAiTjNsJjsj1BD3_9CzP2kgCj-k,52
|
2
|
+
janito/__main__.py,sha256=DqbMlHNWCX-XwiZz1qEYP3qAADKtfmlDhhbTgpXB5_M,14158
|
3
|
+
janito/analysis.py,sha256=dAzMARAcDZ1LN6KC2-Xb0bIB0p5VsaT2lhurtPLLIKk,9368
|
4
|
+
janito/changeapplier.py,sha256=fDP87Hh3uY8qoSv3-SSnafLCRJHLPQZ5U8GIMkdVZ9Y,17697
|
5
|
+
janito/changeviewer.py,sha256=51J0pvGe1rfYpiYo0mhZMonsYJmyAN44mg1TfKRxdM4,12716
|
6
|
+
janito/claude.py,sha256=N7ZX6WCrLKcj9na3o2MMPqH8tlobHYtIG1HnhgMVUKw,2272
|
7
|
+
janito/common.py,sha256=1blM7rY55-mLPXD4L3xwS-RaJ2tBZx8xHhaLYQku3Rk,801
|
8
|
+
janito/config.py,sha256=ocg0lyab9ysgczKaqJTAtv0ZRa2VDMwclTJBgps7Vxw,1171
|
9
|
+
janito/console.py,sha256=E2mLPoBw7Gh1K1hPuCoRDBEFE-klIERt5KFgE3fHqUg,11100
|
10
|
+
janito/contentchange.py,sha256=2HkNhqA_ZIXDHfiv3xRcwfoxMzFUNMuFfbMtf-4jhRM,3282
|
11
|
+
janito/contextparser.py,sha256=iDX6nlqUQr-lj7lkEI4B6jHLci_Kxl-XWOaEiAQtVxA,4531
|
12
|
+
janito/fileparser.py,sha256=8nngFmR9s6NVv2YuMcNKO4udNh-ZcKN4_Jo11vKzLhI,6586
|
13
|
+
janito/prompts.py,sha256=aUDDx16L2ygPJZwrWn4FmrXt1KGNfLeYRcDSPD9BzIA,1879
|
14
|
+
janito/qa.py,sha256=qrAXbsGPCRSW378V88w8e19uU3hfbDnx6lsiRYhq324,1927
|
15
|
+
janito/scan.py,sha256=c_IKSuWx1_525RlIgPtP-tFoH7lManVGGNFMxVg1hMk,6014
|
16
|
+
janito/version.py,sha256=ylfPwGtdY8dEOFJ-DB9gKUQLggqRCvoLxhpnwjzCM94,739
|
17
|
+
janito-0.4.0.dist-info/METADATA,sha256=uUCven38ndkbyLrrLw3B42P7AGyijqTGICKRzLJkDDE,4705
|
18
|
+
janito-0.4.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
19
|
+
janito-0.4.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
|
20
|
+
janito-0.4.0.dist-info/licenses/LICENSE,sha256=xLIUXRPjtsgQml2zD1Pn4LpgiyZ49raw6jZDlO_gZdo,1062
|
21
|
+
janito-0.4.0.dist-info/RECORD,,
|
janito-0.3.0.dist-info/RECORD
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
janito/__init__.py,sha256=CLeVFqpY9Ki3R3MgLAiTjNsJjsj1BD3_9CzP2kgCj-k,52
|
2
|
-
janito/__main__.py,sha256=bGp3nWUZC5omneyE3hn78D_J5PkBfk19qKfMV1kIThI,9892
|
3
|
-
janito/changeviewer.py,sha256=C_CRdeD6dE4AIpOM_kryTn_HyD5XC-glaZO8n8zrQPE,2487
|
4
|
-
janito/claude.py,sha256=tj0lNNVE0CW0bBkbhVDFgBl0AFoMHicWaHnYuYOk3_E,2911
|
5
|
-
janito/config.py,sha256=YsS0bNVkjl7cIboP9nSDy0NXsJaVHYAIpPkc6bbErpo,967
|
6
|
-
janito/console.py,sha256=ieKZ7IRbvJO_KW8A3CU4FCoO4YFEjJQT8BN98YotAnM,2770
|
7
|
-
janito/contentchange.py,sha256=BxFmW8JtRjzX5lnfGfzo0JPRnGRw1RQEtqZCK1wVArw,6404
|
8
|
-
janito/prompts.py,sha256=XonVVbfIg3YY1Dpbkx9m0ZSRE4bgDP3MYHO3D-5FcIE,3080
|
9
|
-
janito/qa.py,sha256=F9bd18CBaZDpbJwwvwFL18gXPBA0cq8kRY0nA3_AKPY,916
|
10
|
-
janito/scan.py,sha256=5JV0crOepVUqCZ3LAUqCJL2yerLFvjZ6RYdOwIGHvX0,5450
|
11
|
-
janito-0.3.0.dist-info/METADATA,sha256=fq7zvCHx_PV8RND5SVfOMnM082BEptZQ2G2qWPOVisQ,3681
|
12
|
-
janito-0.3.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
13
|
-
janito-0.3.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
|
14
|
-
janito-0.3.0.dist-info/licenses/LICENSE,sha256=xLIUXRPjtsgQml2zD1Pn4LpgiyZ49raw6jZDlO_gZdo,1062
|
15
|
-
janito-0.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|