janito 0.15.0__py3-none-any.whl → 1.0.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/__init__.py +1 -5
- janito/__main__.py +3 -5
- janito/agent/__init__.py +1 -0
- janito/agent/agent.py +96 -0
- janito/agent/config.py +113 -0
- janito/agent/config_defaults.py +10 -0
- janito/agent/conversation.py +107 -0
- janito/agent/queued_tool_handler.py +16 -0
- janito/agent/runtime_config.py +30 -0
- janito/agent/tool_handler.py +124 -0
- janito/agent/tools/__init__.py +11 -0
- janito/agent/tools/ask_user.py +63 -0
- janito/agent/tools/bash_exec.py +58 -0
- janito/agent/tools/create_directory.py +19 -0
- janito/agent/tools/create_file.py +43 -0
- janito/agent/tools/fetch_url.py +48 -0
- janito/agent/tools/file_str_replace.py +48 -0
- janito/agent/tools/find_files.py +37 -0
- janito/agent/tools/gitignore_utils.py +40 -0
- janito/agent/tools/move_file.py +37 -0
- janito/agent/tools/remove_file.py +19 -0
- janito/agent/tools/rich_live.py +37 -0
- janito/agent/tools/rich_utils.py +31 -0
- janito/agent/tools/search_text.py +41 -0
- janito/agent/tools/view_file.py +34 -0
- janito/cli/__init__.py +0 -6
- janito/cli/_print_config.py +68 -0
- janito/cli/_utils.py +8 -0
- janito/cli/arg_parser.py +26 -0
- janito/cli/config_commands.py +131 -0
- janito/cli/logging_setup.py +27 -0
- janito/cli/main.py +39 -0
- janito/cli/runner.py +135 -0
- janito/cli_chat_shell/__init__.py +1 -0
- janito/cli_chat_shell/chat_loop.py +147 -0
- janito/cli_chat_shell/commands.py +202 -0
- janito/cli_chat_shell/config_shell.py +75 -0
- janito/cli_chat_shell/load_prompt.py +15 -0
- janito/cli_chat_shell/session_manager.py +60 -0
- janito/cli_chat_shell/ui.py +136 -0
- janito/render_prompt.py +12 -0
- janito/templates/system_instructions.j2 +36 -0
- janito/web/__init__.py +0 -0
- janito/web/__main__.py +17 -0
- janito/web/app.py +132 -0
- janito-1.0.0.dist-info/METADATA +144 -0
- janito-1.0.0.dist-info/RECORD +51 -0
- {janito-0.15.0.dist-info → janito-1.0.0.dist-info}/WHEEL +2 -1
- janito-1.0.0.dist-info/entry_points.txt +2 -0
- {janito-0.15.0.dist-info → janito-1.0.0.dist-info}/licenses/LICENSE +2 -2
- janito-1.0.0.dist-info/top_level.txt +1 -0
- janito/callbacks.py +0 -34
- janito/cli/agent/__init__.py +0 -7
- janito/cli/agent/conversation.py +0 -149
- janito/cli/agent/initialization.py +0 -168
- janito/cli/agent/query.py +0 -112
- janito/cli/agent.py +0 -12
- janito/cli/app.py +0 -178
- janito/cli/commands/__init__.py +0 -12
- janito/cli/commands/config.py +0 -30
- janito/cli/commands/history.py +0 -119
- janito/cli/commands/profile.py +0 -93
- janito/cli/commands/validation.py +0 -24
- janito/cli/commands/workspace.py +0 -31
- janito/cli/commands.py +0 -12
- janito/cli/output.py +0 -29
- janito/cli/utils.py +0 -22
- janito/config/README.md +0 -104
- janito/config/__init__.py +0 -16
- janito/config/cli/__init__.py +0 -28
- janito/config/cli/commands.py +0 -397
- janito/config/cli/validators.py +0 -77
- janito/config/core/__init__.py +0 -23
- janito/config/core/file_operations.py +0 -90
- janito/config/core/properties.py +0 -316
- janito/config/core/singleton.py +0 -282
- janito/config/profiles/__init__.py +0 -8
- janito/config/profiles/definitions.py +0 -38
- janito/config/profiles/manager.py +0 -80
- janito/data/instructions_template.txt +0 -34
- janito/token_report.py +0 -154
- janito/tools/__init__.py +0 -44
- janito/tools/bash/bash.py +0 -157
- janito/tools/bash/unix_persistent_bash.py +0 -215
- janito/tools/bash/win_persistent_bash.py +0 -341
- janito/tools/decorators.py +0 -90
- janito/tools/delete_file.py +0 -65
- janito/tools/fetch_webpage/__init__.py +0 -23
- janito/tools/fetch_webpage/core.py +0 -182
- janito/tools/find_files.py +0 -220
- janito/tools/move_file.py +0 -72
- janito/tools/prompt_user.py +0 -57
- janito/tools/replace_file.py +0 -63
- janito/tools/rich_console.py +0 -176
- janito/tools/search_text.py +0 -226
- janito/tools/str_replace_editor/__init__.py +0 -6
- janito/tools/str_replace_editor/editor.py +0 -55
- janito/tools/str_replace_editor/handlers/__init__.py +0 -16
- janito/tools/str_replace_editor/handlers/create.py +0 -60
- janito/tools/str_replace_editor/handlers/insert.py +0 -100
- janito/tools/str_replace_editor/handlers/str_replace.py +0 -94
- janito/tools/str_replace_editor/handlers/undo.py +0 -64
- janito/tools/str_replace_editor/handlers/view.py +0 -165
- janito/tools/str_replace_editor/utils.py +0 -33
- janito/tools/think.py +0 -37
- janito/tools/usage_tracker.py +0 -137
- janito-0.15.0.dist-info/METADATA +0 -481
- janito-0.15.0.dist-info/RECORD +0 -64
- janito-0.15.0.dist-info/entry_points.txt +0 -2
@@ -1,80 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Profile management functions for Janito configuration.
|
3
|
-
"""
|
4
|
-
from typing import Dict, Any
|
5
|
-
|
6
|
-
from .definitions import PROFILES
|
7
|
-
|
8
|
-
def get_available_profiles() -> Dict[str, Dict[str, Any]]:
|
9
|
-
"""
|
10
|
-
Get all available predefined profiles.
|
11
|
-
|
12
|
-
Returns:
|
13
|
-
Dictionary of profile names to profile settings
|
14
|
-
"""
|
15
|
-
return PROFILES
|
16
|
-
|
17
|
-
def get_profile(profile_name: str) -> Dict[str, Any]:
|
18
|
-
"""
|
19
|
-
Get a specific profile by name.
|
20
|
-
|
21
|
-
Args:
|
22
|
-
profile_name: Name of the profile to retrieve
|
23
|
-
|
24
|
-
Returns:
|
25
|
-
Dict containing the profile settings
|
26
|
-
|
27
|
-
Raises:
|
28
|
-
ValueError: If the profile name is not recognized
|
29
|
-
"""
|
30
|
-
profile_name = profile_name.lower()
|
31
|
-
if profile_name not in PROFILES:
|
32
|
-
valid_profiles = ", ".join(PROFILES.keys())
|
33
|
-
raise ValueError(f"Unknown profile: {profile_name}. Valid profiles are: {valid_profiles}")
|
34
|
-
|
35
|
-
return PROFILES[profile_name]
|
36
|
-
|
37
|
-
def create_custom_profile(name: str, temperature: float, description: str = None) -> Dict[str, Any]:
|
38
|
-
"""
|
39
|
-
Create a custom profile with the given parameters.
|
40
|
-
|
41
|
-
Args:
|
42
|
-
name: Name for the custom profile
|
43
|
-
temperature: Temperature value (0.0 to 1.0)
|
44
|
-
description: Optional description for the profile
|
45
|
-
|
46
|
-
Returns:
|
47
|
-
Dict containing the profile settings
|
48
|
-
|
49
|
-
Raises:
|
50
|
-
ValueError: If temperature is not between 0.0 and 1.0
|
51
|
-
"""
|
52
|
-
if temperature < 0.0 or temperature > 1.0:
|
53
|
-
raise ValueError("Temperature must be between 0.0 and 1.0")
|
54
|
-
|
55
|
-
# Determine top_p and top_k based on temperature
|
56
|
-
if temperature <= 0.3:
|
57
|
-
top_p = 0.85
|
58
|
-
top_k = 15
|
59
|
-
elif temperature <= 0.6:
|
60
|
-
top_p = 0.9
|
61
|
-
top_k = 40
|
62
|
-
else:
|
63
|
-
top_p = 0.95
|
64
|
-
top_k = 60
|
65
|
-
|
66
|
-
# Use provided description or generate a default one
|
67
|
-
if description is None:
|
68
|
-
if temperature <= 0.3:
|
69
|
-
description = "Custom precise profile"
|
70
|
-
elif temperature <= 0.6:
|
71
|
-
description = "Custom balanced profile"
|
72
|
-
else:
|
73
|
-
description = "Custom creative profile"
|
74
|
-
|
75
|
-
return {
|
76
|
-
"temperature": temperature,
|
77
|
-
"top_p": top_p,
|
78
|
-
"top_k": top_k,
|
79
|
-
"description": description
|
80
|
-
}
|
@@ -1,34 +0,0 @@
|
|
1
|
-
You are a {{ role }}, using the name Janito .
|
2
|
-
You will be assisting an user using a computer system on a {{ platform }} platform.
|
3
|
-
You can find more about the current project using the tools in the workspace directory.
|
4
|
-
If the question is related to the project, use the tools using the relative path, ./filename instead of /filename.
|
5
|
-
|
6
|
-
If creating or editing files with a large number of lines, organize them into smaller files.
|
7
|
-
If creating or editing files in an existing directory check surrounding files for the used patterns.
|
8
|
-
|
9
|
-
# Structure Discovery (docs/STRUCTURE.md from current directory)
|
10
|
-
Always start exploring the project by viewing for the file docs/STRUCTURE.md.
|
11
|
-
Do not track files or directories wich are in .gitignore in the structure.
|
12
|
-
At the end of responding to the user, update the structure file based on the files and directories you have interacted with,
|
13
|
-
be precise focusing on the most important files and directories, avoid adding extra information like architecture or design patterns.
|
14
|
-
|
15
|
-
# Tools
|
16
|
-
The bash tool does not support commands which will require user input.
|
17
|
-
Use the bash tool to get the current date or time when needed.
|
18
|
-
Prefer the str_replace_editor tool to view directories and file contents.
|
19
|
-
|
20
|
-
<IMPORTANT>
|
21
|
-
Call the user_prompt tool when:
|
22
|
-
- There are multiple options to apply a certain change
|
23
|
-
- The next operation risk is moderated or high
|
24
|
-
- The implementation plan is complex, requiring a review
|
25
|
-
Proceed according to the user answer.
|
26
|
-
</IMPORTANT>
|
27
|
-
|
28
|
-
When changing code in Python files, be mindful about the need to review the imports specially when new type hints are used (eg. Optional, Tuple, List, Dict, etc).
|
29
|
-
After performing changes to a project in interfaces which are exposed to the user, respond to the user with a short summary on how to verify the changes. eg. "run cmd xpto", prefer to provide a command to run instead of a description.
|
30
|
-
When displaying commands in instructions to the user, consider their platform.
|
31
|
-
When creating html pages which refer to images that should be manually placed by the user, instead of broken links provide a frame with a placeholder image.
|
32
|
-
|
33
|
-
If STRUCTURE.md was updated add it to the list of files to be committed.
|
34
|
-
After significant changes, run git commit with a message describing the changes made.
|
janito/token_report.py
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Module for generating token usage reports.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from rich.console import Console
|
6
|
-
|
7
|
-
def generate_token_report(agent, verbose=False, interrupted=False):
|
8
|
-
"""
|
9
|
-
Generate a token usage report.
|
10
|
-
|
11
|
-
Args:
|
12
|
-
agent: The Claude agent instance
|
13
|
-
verbose: Whether to show detailed token usage information
|
14
|
-
interrupted: Whether the request was interrupted
|
15
|
-
|
16
|
-
Returns:
|
17
|
-
None - prints the report to the console
|
18
|
-
"""
|
19
|
-
console = Console()
|
20
|
-
usage = agent.get_tokens()
|
21
|
-
cost = agent.get_token_cost()
|
22
|
-
|
23
|
-
text_usage = usage.text_usage
|
24
|
-
tools_usage = usage.tools_usage
|
25
|
-
|
26
|
-
if verbose:
|
27
|
-
total_usage = usage.total_usage
|
28
|
-
|
29
|
-
# Get costs from the cost object
|
30
|
-
text_input_cost = cost.input_cost
|
31
|
-
text_output_cost = cost.output_cost
|
32
|
-
text_cache_creation_cost = cost.cache_creation_cost
|
33
|
-
text_cache_read_cost = cost.cache_read_cost
|
34
|
-
|
35
|
-
tools_input_cost = cost.input_cost
|
36
|
-
tools_output_cost = cost.output_cost
|
37
|
-
tools_cache_creation_cost = cost.cache_creation_cost
|
38
|
-
tools_cache_read_cost = cost.cache_read_cost
|
39
|
-
|
40
|
-
# Format costs
|
41
|
-
def format_cost(cost):
|
42
|
-
return f"{cost * 100:.2f}¢ USD" if cost < 1.0 else f"${cost:.6f} USD"
|
43
|
-
|
44
|
-
console.print("\n[bold blue]📊 Detailed Token Usage:[/bold blue]")
|
45
|
-
console.print(f"📝 Text Input tokens: {text_usage.input_tokens}")
|
46
|
-
console.print(f"📤 Text Output tokens: {text_usage.output_tokens}")
|
47
|
-
console.print(f"💾 Text Cache Creation tokens: {text_usage.cache_creation_input_tokens}")
|
48
|
-
console.print(f"📖 Text Cache Read tokens: {text_usage.cache_read_input_tokens}")
|
49
|
-
console.print(f"📋 Text Total tokens: {text_usage.input_tokens + text_usage.output_tokens + text_usage.cache_creation_input_tokens + text_usage.cache_read_input_tokens}")
|
50
|
-
|
51
|
-
console.print(f"🔧 Tool Input tokens: {tools_usage.input_tokens}")
|
52
|
-
console.print(f"🔨 Tool Output tokens: {tools_usage.output_tokens}")
|
53
|
-
console.print(f"💾 Tool Cache Creation tokens: {tools_usage.cache_creation_input_tokens}")
|
54
|
-
console.print(f"📖 Tool Cache Read tokens: {tools_usage.cache_read_input_tokens}")
|
55
|
-
console.print(f"🧰 Tool Total tokens: {tools_usage.input_tokens + tools_usage.output_tokens + tools_usage.cache_creation_input_tokens + tools_usage.cache_read_input_tokens}")
|
56
|
-
|
57
|
-
console.print(f"🔢 Total tokens: {total_usage.input_tokens + total_usage.output_tokens + total_usage.cache_creation_input_tokens + total_usage.cache_read_input_tokens}")
|
58
|
-
|
59
|
-
console.print("\n[bold blue]💰 Pricing Information:[/bold blue]")
|
60
|
-
console.print(f"📝 Text Input cost: {format_cost(text_input_cost)}")
|
61
|
-
console.print(f"📤 Text Output cost: {format_cost(text_output_cost)}")
|
62
|
-
console.print(f"💾 Text Cache Creation cost: {format_cost(text_cache_creation_cost)}")
|
63
|
-
console.print(f"📖 Text Cache Read cost: {format_cost(text_cache_read_cost)}")
|
64
|
-
console.print(f"📋 Text Total cost: {format_cost(text_input_cost + text_output_cost + text_cache_creation_cost + text_cache_read_cost)}")
|
65
|
-
|
66
|
-
console.print(f"🔧 Tool Input cost: {format_cost(tools_input_cost)}")
|
67
|
-
console.print(f"🔨 Tool Output cost: {format_cost(tools_output_cost)}")
|
68
|
-
console.print(f"💾 Tool Cache Creation cost: {format_cost(tools_cache_creation_cost)}")
|
69
|
-
console.print(f"📖 Tool Cache Read cost: {format_cost(tools_cache_read_cost)}")
|
70
|
-
console.print(f"🧰 Tool Total cost: {format_cost(tools_input_cost + tools_output_cost + tools_cache_creation_cost + tools_cache_read_cost)}")
|
71
|
-
|
72
|
-
total_cost_text = f"💵 Total cost: {format_cost(text_input_cost + text_output_cost + text_cache_creation_cost + text_cache_read_cost + tools_input_cost + tools_output_cost + tools_cache_creation_cost + tools_cache_read_cost)}"
|
73
|
-
if interrupted:
|
74
|
-
total_cost_text += " (interrupted request not accounted)"
|
75
|
-
console.print(total_cost_text)
|
76
|
-
|
77
|
-
# Show cache delta if available
|
78
|
-
if hasattr(cost, 'cache_delta') and cost.cache_delta:
|
79
|
-
cache_delta = cost.cache_delta
|
80
|
-
console.print(f"\n[bold green]💰 Cache Savings:[/bold green] {format_cost(cache_delta)}")
|
81
|
-
|
82
|
-
# Calculate percentage savings
|
83
|
-
total_cost_without_cache = cost.total_cost + cache_delta
|
84
|
-
if total_cost_without_cache > 0:
|
85
|
-
savings_percentage = (cache_delta / total_cost_without_cache) * 100
|
86
|
-
console.print(f"[bold green]📊 Cache Savings Percentage:[/bold green] {savings_percentage:.2f}%")
|
87
|
-
console.print(f"[bold green]💸 Cost without cache:[/bold green] {format_cost(total_cost_without_cache)}")
|
88
|
-
console.print(f"[bold green]💲 Cost with cache:[/bold green] {format_cost(cost.total_cost)}")
|
89
|
-
|
90
|
-
# Per-tool breakdown
|
91
|
-
if usage.by_tool:
|
92
|
-
console.print("\n[bold blue]🔧 Per-Tool Breakdown:[/bold blue]")
|
93
|
-
try:
|
94
|
-
if hasattr(cost, 'by_tool') and cost.by_tool:
|
95
|
-
for tool_name, tool_usage in usage.by_tool.items():
|
96
|
-
tool_input_cost = cost.by_tool[tool_name].input_cost
|
97
|
-
tool_output_cost = cost.by_tool[tool_name].output_cost
|
98
|
-
tool_cache_creation_cost = cost.by_tool[tool_name].cache_creation_cost
|
99
|
-
tool_cache_read_cost = cost.by_tool[tool_name].cache_read_cost
|
100
|
-
tool_total_cost = tool_input_cost + tool_output_cost + tool_cache_creation_cost + tool_cache_read_cost
|
101
|
-
|
102
|
-
console.print(f" 🔧 Tool: {tool_name}")
|
103
|
-
console.print(f" 📥 Input tokens: {tool_usage.input_tokens}")
|
104
|
-
console.print(f" 📤 Output tokens: {tool_usage.output_tokens}")
|
105
|
-
console.print(f" 💾 Cache Creation tokens: {tool_usage.cache_creation_input_tokens}")
|
106
|
-
console.print(f" 📖 Cache Read tokens: {tool_usage.cache_read_input_tokens}")
|
107
|
-
console.print(f" 🔢 Total tokens: {tool_usage.input_tokens + tool_usage.output_tokens + tool_usage.cache_creation_input_tokens + tool_usage.cache_read_input_tokens}")
|
108
|
-
console.print(f" 💵 Total cost: {format_cost(tool_total_cost)}")
|
109
|
-
else:
|
110
|
-
# Calculate costs manually for each tool if cost.by_tool is not available
|
111
|
-
for tool_name, tool_usage in usage.by_tool.items():
|
112
|
-
# Estimate costs based on overall pricing
|
113
|
-
total_tokens = tool_usage.input_tokens + tool_usage.output_tokens + tool_usage.cache_creation_input_tokens + tool_usage.cache_read_input_tokens
|
114
|
-
estimated_cost = (total_tokens / (usage.total_usage.total_tokens + usage.total_usage.total_cache_tokens)) * cost.total_cost if usage.total_usage.total_tokens > 0 else 0
|
115
|
-
|
116
|
-
console.print(f" 🔧 Tool: {tool_name}")
|
117
|
-
console.print(f" 📥 Input tokens: {tool_usage.input_tokens}")
|
118
|
-
console.print(f" 📤 Output tokens: {tool_usage.output_tokens}")
|
119
|
-
console.print(f" 💾 Cache Creation tokens: {tool_usage.cache_creation_input_tokens}")
|
120
|
-
console.print(f" 📖 Cache Read tokens: {tool_usage.cache_read_input_tokens}")
|
121
|
-
console.print(f" 🔢 Total tokens: {tool_usage.input_tokens + tool_usage.output_tokens + tool_usage.cache_creation_input_tokens + tool_usage.cache_read_input_tokens}")
|
122
|
-
console.print(f" 💵 Total cost: {format_cost(estimated_cost)}")
|
123
|
-
except Exception as e:
|
124
|
-
console.print(f"❌ Error: {str(e)}")
|
125
|
-
else:
|
126
|
-
total_tokens = (text_usage.input_tokens + text_usage.output_tokens +
|
127
|
-
text_usage.cache_creation_input_tokens + text_usage.cache_read_input_tokens +
|
128
|
-
tools_usage.input_tokens + tools_usage.output_tokens +
|
129
|
-
tools_usage.cache_creation_input_tokens + tools_usage.cache_read_input_tokens)
|
130
|
-
|
131
|
-
# Format costs
|
132
|
-
def format_cost(cost):
|
133
|
-
return f"{cost * 100:.2f}¢ USD" if cost < 1.0 else f"${cost:.6f} USD"
|
134
|
-
|
135
|
-
# Prepare summary message
|
136
|
-
cost_text = f"Cost: {format_cost(cost.total_cost)}"
|
137
|
-
if interrupted:
|
138
|
-
cost_text += " (interrupted request not accounted)"
|
139
|
-
|
140
|
-
summary = f"Total tokens: {total_tokens} | {cost_text}"
|
141
|
-
|
142
|
-
# Add cache savings if available
|
143
|
-
if hasattr(cost, 'cache_delta') and cost.cache_delta != 0:
|
144
|
-
cache_delta = cost.cache_delta
|
145
|
-
total_cost_without_cache = cost.total_cost + cache_delta
|
146
|
-
savings_percentage = 0
|
147
|
-
if total_cost_without_cache > 0:
|
148
|
-
savings_percentage = (cache_delta / total_cost_without_cache) * 100
|
149
|
-
|
150
|
-
summary += f" | Cache savings: {format_cost(cache_delta)} ({savings_percentage:.1f}%)"
|
151
|
-
|
152
|
-
# Display with a rule
|
153
|
-
console.rule("[blue]Token Usage[/blue]")
|
154
|
-
console.print(f"[blue]{summary}[/blue]", justify="center")
|
janito/tools/__init__.py
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Janito tools package.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from .str_replace_editor import str_replace_editor
|
6
|
-
from .find_files import find_files
|
7
|
-
from .delete_file import delete_file
|
8
|
-
from .search_text import search_text
|
9
|
-
from .replace_file import replace_file
|
10
|
-
from .prompt_user import prompt_user
|
11
|
-
from .move_file import move_file
|
12
|
-
from janito.tools.fetch_webpage import fetch_webpage
|
13
|
-
from .think import think
|
14
|
-
from .usage_tracker import get_tracker, reset_tracker, print_usage_stats
|
15
|
-
from janito.config import get_config
|
16
|
-
|
17
|
-
__all__ = ["str_replace_editor", "find_files", "delete_file", "search_text", "replace_file",
|
18
|
-
"prompt_user", "move_file", "fetch_webpage", "think", "get_tools",
|
19
|
-
"get_tracker", "reset_tracker", "print_usage_stats"]
|
20
|
-
|
21
|
-
def get_tools():
|
22
|
-
"""
|
23
|
-
Get a list of all available tools.
|
24
|
-
|
25
|
-
Returns:
|
26
|
-
List of tool functions (excluding str_replace_editor which is passed separately)
|
27
|
-
If no_tools mode is enabled, returns an empty list
|
28
|
-
If ask_mode is enabled, only returns tools that don't perform changes
|
29
|
-
"""
|
30
|
-
# If no_tools mode is enabled, return an empty list
|
31
|
-
if get_config().no_tools:
|
32
|
-
return []
|
33
|
-
|
34
|
-
# Tools that only read or view but don't modify anything
|
35
|
-
read_only_tools = [find_files, search_text, prompt_user, fetch_webpage, think]
|
36
|
-
|
37
|
-
# Tools that modify the filesystem
|
38
|
-
write_tools = [delete_file, replace_file, move_file]
|
39
|
-
|
40
|
-
# If ask_mode is enabled, only return read-only tools
|
41
|
-
if get_config().ask_mode:
|
42
|
-
return read_only_tools
|
43
|
-
else:
|
44
|
-
return read_only_tools + write_tools
|
janito/tools/bash/bash.py
DELETED
@@ -1,157 +0,0 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
from typing import Tuple
|
3
|
-
import threading
|
4
|
-
import platform
|
5
|
-
import re
|
6
|
-
import queue
|
7
|
-
import signal
|
8
|
-
import time
|
9
|
-
from janito.config import get_config
|
10
|
-
from janito.tools.usage_tracker import get_tracker
|
11
|
-
from janito.tools.rich_console import console, print_info
|
12
|
-
|
13
|
-
# Import the appropriate implementation based on the platform
|
14
|
-
if platform.system() == "Windows":
|
15
|
-
from janito.tools.bash.win_persistent_bash import PersistentBash
|
16
|
-
else:
|
17
|
-
from janito.tools.bash.unix_persistent_bash import PersistentBash
|
18
|
-
|
19
|
-
# Global instance of PersistentBash to maintain state between calls
|
20
|
-
_bash_session = None
|
21
|
-
_session_lock = threading.RLock() # Use RLock to allow reentrant locking
|
22
|
-
_current_bash_thread = None
|
23
|
-
_command_interrupted = False
|
24
|
-
|
25
|
-
def _execute_bash_command(command, result_queue):
|
26
|
-
"""
|
27
|
-
Execute a bash command in a separate thread.
|
28
|
-
|
29
|
-
Args:
|
30
|
-
command: The bash command to execute
|
31
|
-
result_queue: Queue to store the result
|
32
|
-
"""
|
33
|
-
global _bash_session, _command_interrupted
|
34
|
-
|
35
|
-
try:
|
36
|
-
# Execute the command - output will be printed to console in real-time
|
37
|
-
output = _bash_session.execute(command)
|
38
|
-
|
39
|
-
# Put the result in the queue if the command wasn't interrupted
|
40
|
-
if not _command_interrupted:
|
41
|
-
result_queue.put((output, False))
|
42
|
-
except Exception as e:
|
43
|
-
# Handle any exceptions that might occur
|
44
|
-
error_message = f"Error executing bash command: {str(e)}"
|
45
|
-
console.print(error_message, style="red bold")
|
46
|
-
result_queue.put((error_message, True))
|
47
|
-
|
48
|
-
def _keyboard_interrupt_handler(signum, frame):
|
49
|
-
"""
|
50
|
-
Handle keyboard interrupt (Ctrl+C) by setting the interrupt flag.
|
51
|
-
"""
|
52
|
-
global _command_interrupted
|
53
|
-
_command_interrupted = True
|
54
|
-
console.print("\n[bold red]Command interrupted by user (Ctrl+C)[/bold red]")
|
55
|
-
|
56
|
-
# Restore the default signal handler
|
57
|
-
signal.signal(signal.SIGINT, original_sigint_handler)
|
58
|
-
|
59
|
-
def bash_tool(command: str, restart: Optional[bool] = False) -> Tuple[str, bool]:
|
60
|
-
"""
|
61
|
-
Execute a bash command using a persistent Bash session.
|
62
|
-
The appropriate implementation (Windows or Unix) is selected based on the detected platform.
|
63
|
-
When in ask mode, only read-only commands are allowed.
|
64
|
-
Output is printed to the console in real-time as it's received.
|
65
|
-
Command runs in a background thread, allowing Ctrl+C to interrupt just the command.
|
66
|
-
|
67
|
-
Args:
|
68
|
-
command: The bash command to execute
|
69
|
-
restart: Whether to restart the bash session
|
70
|
-
|
71
|
-
Returns:
|
72
|
-
A tuple containing (output message, is_error flag)
|
73
|
-
"""
|
74
|
-
# Import console for printing output in real-time
|
75
|
-
from janito.tools.rich_console import console, print_info
|
76
|
-
|
77
|
-
# Only print command if not in trust mode
|
78
|
-
if not get_config().trust_mode:
|
79
|
-
print_info(f"{command}", "Bash Run")
|
80
|
-
|
81
|
-
global _bash_session, _current_bash_thread, _command_interrupted, original_sigint_handler
|
82
|
-
_command_interrupted = False
|
83
|
-
|
84
|
-
# Check if in ask mode and if the command might modify files
|
85
|
-
if get_config().ask_mode:
|
86
|
-
# List of potentially modifying commands
|
87
|
-
modifying_patterns = [
|
88
|
-
r'\brm\b', r'\bmkdir\b', r'\btouch\b', r'\becho\b.*[>\|]', r'\bmv\b', r'\bcp\b',
|
89
|
-
r'\bchmod\b', r'\bchown\b', r'\bsed\b.*-i', r'\bawk\b.*[>\|]', r'\bcat\b.*[>\|]',
|
90
|
-
r'\bwrite\b', r'\binstall\b', r'\bapt\b', r'\byum\b', r'\bpip\b.*install',
|
91
|
-
r'\bnpm\b.*install', r'\bdocker\b', r'\bkubectl\b.*apply', r'\bgit\b.*commit',
|
92
|
-
r'\bgit\b.*push', r'\bgit\b.*merge', r'\bdd\b'
|
93
|
-
]
|
94
|
-
|
95
|
-
# Check if command matches any modifying pattern
|
96
|
-
for pattern in modifying_patterns:
|
97
|
-
if re.search(pattern, command, re.IGNORECASE):
|
98
|
-
return ("Cannot execute potentially modifying commands in ask mode. Use --ask option to disable modifications.", True)
|
99
|
-
|
100
|
-
with _session_lock:
|
101
|
-
# Initialize or restart the session if needed
|
102
|
-
if _bash_session is None or restart:
|
103
|
-
if _bash_session is not None:
|
104
|
-
_bash_session.close()
|
105
|
-
# Get GitBash path from config (None means auto-detect)
|
106
|
-
gitbash_path = get_config().gitbash_path
|
107
|
-
_bash_session = PersistentBash(bash_path=gitbash_path)
|
108
|
-
|
109
|
-
try:
|
110
|
-
# Create a queue to get the result from the thread
|
111
|
-
result_queue = queue.Queue()
|
112
|
-
|
113
|
-
# Save the original SIGINT handler
|
114
|
-
original_sigint_handler = signal.getsignal(signal.SIGINT)
|
115
|
-
|
116
|
-
# Set our custom SIGINT handler
|
117
|
-
signal.signal(signal.SIGINT, _keyboard_interrupt_handler)
|
118
|
-
|
119
|
-
# Create and start the thread
|
120
|
-
_current_bash_thread = threading.Thread(
|
121
|
-
target=_execute_bash_command,
|
122
|
-
args=(command, result_queue)
|
123
|
-
)
|
124
|
-
_current_bash_thread.daemon = True
|
125
|
-
_current_bash_thread.start()
|
126
|
-
|
127
|
-
# Wait for the thread to complete or for an interrupt
|
128
|
-
while _current_bash_thread.is_alive() and not _command_interrupted:
|
129
|
-
_current_bash_thread.join(0.1) # Check every 100ms
|
130
|
-
|
131
|
-
# If the command was interrupted, return a message
|
132
|
-
if _command_interrupted:
|
133
|
-
# Restore the original signal handler
|
134
|
-
signal.signal(signal.SIGINT, original_sigint_handler)
|
135
|
-
return ("Command was interrupted by Ctrl+C", True)
|
136
|
-
|
137
|
-
# Get the result from the queue
|
138
|
-
output, is_error = result_queue.get(timeout=1)
|
139
|
-
|
140
|
-
# Restore the original signal handler
|
141
|
-
signal.signal(signal.SIGINT, original_sigint_handler)
|
142
|
-
|
143
|
-
# Track bash command execution
|
144
|
-
get_tracker().increment('bash_commands')
|
145
|
-
|
146
|
-
# Return the output
|
147
|
-
return output, is_error
|
148
|
-
|
149
|
-
except Exception as e:
|
150
|
-
# Handle any exceptions that might occur
|
151
|
-
error_message = f"Error executing bash command: {str(e)}"
|
152
|
-
console.print(error_message, style="red bold")
|
153
|
-
|
154
|
-
# Restore the original signal handler
|
155
|
-
signal.signal(signal.SIGINT, original_sigint_handler)
|
156
|
-
|
157
|
-
return error_message, True
|