janito 2.29.0__py3-none-any.whl → 2.30.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/README.md +3 -3
- janito/agent/setup_agent.py +21 -35
- janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2 +6 -0
- janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +6 -0
- janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2 +7 -1
- janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2 +7 -1
- janito/cli/chat_mode/session.py +17 -51
- janito/cli/cli_commands/list_plugins.py +99 -75
- janito/cli/cli_commands/show_system_prompt.py +8 -3
- janito/cli/core/runner.py +2 -2
- janito/cli/main_cli.py +9 -15
- janito/llm/agent.py +6 -1
- janito/tools/adapters/local/ask_user.py +3 -1
- janito/tools/adapters/local/fetch_url.py +20 -28
- janito/tools/adapters/local/replace_text_in_file.py +9 -3
- janito/tools/adapters/local/search_text/core.py +1 -1
- janito/tools/loop_protection_decorator.py +12 -16
- janito/tools/tools_adapter.py +18 -4
- janito-2.30.0.dist-info/METADATA +83 -0
- {janito-2.29.0.dist-info → janito-2.30.0.dist-info}/RECORD +24 -25
- janito-2.30.0.dist-info/licenses/LICENSE +201 -0
- janito/cli/chat_mode/session_profile_select.py +0 -182
- janito-2.29.0.dist-info/METADATA +0 -431
- janito-2.29.0.dist-info/licenses/LICENSE +0 -21
- {janito-2.29.0.dist-info → janito-2.30.0.dist-info}/WHEEL +0 -0
- {janito-2.29.0.dist-info → janito-2.30.0.dist-info}/entry_points.txt +0 -0
- {janito-2.29.0.dist-info → janito-2.30.0.dist-info}/top_level.txt +0 -0
janito/README.md
CHANGED
@@ -116,11 +116,11 @@ Janito includes powerful built-in tools for:
|
|
116
116
|
- System commands
|
117
117
|
- And more...
|
118
118
|
|
119
|
-
### Profiles
|
119
|
+
### Profiles
|
120
120
|
Use predefined system prompts:
|
121
121
|
```bash
|
122
|
-
janito --
|
123
|
-
janito --
|
122
|
+
janito --developer "Create a REST API" # Same as --profile developer
|
123
|
+
janito --market "Analyze market trends" # Same as --profile market-analyst
|
124
124
|
```
|
125
125
|
|
126
126
|
### Environment Variables
|
janito/agent/setup_agent.py
CHANGED
@@ -25,10 +25,14 @@ def _load_template_content(profile, templates_dir):
|
|
25
25
|
|
26
26
|
Spaces in the profile name are converted to underscores to align with the file-naming convention (e.g. "Developer with Python Tools" ➜ "Developer_with_Python_Tools" (matches: system_prompt_template_Developer_with_Python_Tools.txt.j2)).
|
27
27
|
"""
|
28
|
-
|
29
|
-
sanitized_profile = re.sub(r"\s+", "_", profile.strip()) if profile else profile
|
30
|
-
|
28
|
+
sanitized_profile = re.sub(r"\s+", "_", profile.strip())
|
31
29
|
template_filename = f"system_prompt_template_{sanitized_profile}.txt.j2"
|
30
|
+
|
31
|
+
return _find_template_file(template_filename, templates_dir)
|
32
|
+
|
33
|
+
|
34
|
+
def _find_template_file(template_filename, templates_dir):
|
35
|
+
"""Find and load template file from various locations."""
|
32
36
|
template_path = templates_dir / template_filename
|
33
37
|
|
34
38
|
# 1) Check local templates directory
|
@@ -96,39 +100,9 @@ def _load_template_content(profile, templates_dir):
|
|
96
100
|
)
|
97
101
|
|
98
102
|
raise FileNotFoundError(error_msg)
|
99
|
-
# Replace spaces in profile name with underscores for filename resolution
|
100
|
-
sanitized_profile = re.sub(r"\\s+", "_", profile.strip()) if profile else profile
|
101
|
-
"""
|
102
|
-
Loads the template content for the given profile from the specified directory or package resources.
|
103
|
-
If the profile template is not found in the default locations, tries to load from the user profiles directory ~/.janito/profiles.
|
104
|
-
"""
|
105
|
-
|
106
|
-
# Sanitize profile name by replacing spaces with underscores to match filename conventions
|
107
|
-
sanitized_profile = re.sub(r"\\s+", "_", profile.strip())
|
108
|
-
template_filename = f"system_prompt_template_{sanitized_profile}.txt.j2"
|
109
|
-
template_path = templates_dir / template_filename
|
110
|
-
if template_path.exists():
|
111
|
-
with open(template_path, "r", encoding="utf-8") as file:
|
112
|
-
return file.read(), template_path
|
113
|
-
# Try package import fallback
|
114
|
-
try:
|
115
|
-
with importlib.resources.files("janito.agent.templates.profiles").joinpath(
|
116
|
-
template_filename
|
117
|
-
).open("r", encoding="utf-8") as file:
|
118
|
-
return file.read(), template_path
|
119
|
-
except (FileNotFoundError, ModuleNotFoundError, AttributeError):
|
120
|
-
# Try user profiles directory
|
121
|
-
user_profiles_dir = Path(os.path.expanduser("~/.janito/profiles"))
|
122
|
-
user_template_path = user_profiles_dir / profile
|
123
|
-
if user_template_path.exists():
|
124
|
-
with open(user_template_path, "r", encoding="utf-8") as file:
|
125
|
-
return file.read(), user_template_path
|
126
|
-
raise FileNotFoundError(
|
127
|
-
f"[janito] Could not find profile-specific template '{template_filename}' in {template_path} nor in janito.agent.templates.profiles package nor in user profiles directory {user_template_path}."
|
128
|
-
)
|
129
103
|
|
130
104
|
|
131
|
-
def _prepare_template_context(role, profile, allowed_permissions):
|
105
|
+
def _prepare_template_context(role, profile, allowed_permissions, args=None):
|
132
106
|
"""
|
133
107
|
Prepares the context dictionary for Jinja2 template rendering.
|
134
108
|
"""
|
@@ -148,6 +122,11 @@ def _prepare_template_context(role, profile, allowed_permissions):
|
|
148
122
|
perm_str += "x"
|
149
123
|
allowed_permissions = perm_str or None
|
150
124
|
context["allowed_permissions"] = allowed_permissions
|
125
|
+
|
126
|
+
# Add emoji flag for system prompt
|
127
|
+
context["emoji_enabled"] = (
|
128
|
+
getattr(args, "emoji", False) if "args" in locals() else False
|
129
|
+
)
|
151
130
|
# Inject platform info if execute permission is present
|
152
131
|
if allowed_permissions and "x" in allowed_permissions:
|
153
132
|
pd = PlatformDiscovery()
|
@@ -171,6 +150,11 @@ def _prepare_template_context(role, profile, allowed_permissions):
|
|
171
150
|
else:
|
172
151
|
context["allowed_sites_info"] = f"Restricted to: {', '.join(allowed_sites)}"
|
173
152
|
|
153
|
+
# Add emoji flag for system prompt
|
154
|
+
context["emoji_enabled"] = (
|
155
|
+
getattr(args, "emoji", False) if "args" in locals() else False
|
156
|
+
)
|
157
|
+
|
174
158
|
return context
|
175
159
|
|
176
160
|
|
@@ -267,7 +251,9 @@ def setup_agent(
|
|
267
251
|
template_content, template_path = _load_template_content(profile, templates_dir)
|
268
252
|
|
269
253
|
template = Template(template_content)
|
270
|
-
context = _prepare_template_context(
|
254
|
+
context = _prepare_template_context(
|
255
|
+
role, profile, allowed_permissions, locals().get("args")
|
256
|
+
)
|
271
257
|
|
272
258
|
# Debug output if requested
|
273
259
|
debug_flag = False
|
@@ -88,4 +88,10 @@ You are: {{ role | default('developer') }}
|
|
88
88
|
{# Trying to prevent surrogates generation, found this frequently in gpt4.1/windows #}
|
89
89
|
- While writing code, if you need an emoji or special Unicode character in a string, then insert the actual character (e.g., 📖) directly instead of using surrogate pairs or escape sequences.
|
90
90
|
{% endif %}
|
91
|
+
{% if emoji_enabled %}
|
92
|
+
## Emoji Usage
|
93
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 🎉
|
94
|
+
- Use appropriate emojis to enhance communication: ✅ for success, ⚠️ for warnings, 🔄 for progress, etc.
|
95
|
+
- Emojis should complement the message, not replace clear explanations
|
96
|
+
{% endif %}
|
91
97
|
|
@@ -78,4 +78,10 @@ You are: {{ role | default('software developer') }}
|
|
78
78
|
{# Trying to prevent surrogates generation, found this frequently in gpt4.1/windows #}
|
79
79
|
- While writing code, if you need an emoji or special Unicode character in a string, then insert the actual character (e.g., 📖) directly instead of using surrogate pairs or escape sequences.
|
80
80
|
{% endif %}
|
81
|
+
{% if emoji_enabled %}
|
82
|
+
## Emoji Usage
|
83
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 🎉
|
84
|
+
- Use appropriate emojis to enhance communication: ✅ for success, ⚠️ for warnings, 🔄 for progress, etc.
|
85
|
+
- Emojis should complement the message, not replace clear explanations
|
86
|
+
{% endif %}
|
81
87
|
|
@@ -107,4 +107,10 @@ When asked about specific stocks or market movements, provide detailed analysis
|
|
107
107
|
{% endfor %}
|
108
108
|
{% endif %}
|
109
109
|
|
110
|
-
## Guidelines
|
110
|
+
## Guidelines
|
111
|
+
{% if emoji_enabled %}
|
112
|
+
## Emoji Usage
|
113
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 📊
|
114
|
+
- Use appropriate emojis to enhance communication: 📈 for uptrends, 📉 for downtrends, 💰 for profits, ⚠️ for warnings, etc.
|
115
|
+
- Emojis should complement the message, not replace clear financial analysis
|
116
|
+
{% endif %}
|
@@ -50,4 +50,10 @@ You are: {{ role | default('helpful assistant') }}
|
|
50
50
|
## Guidelines
|
51
51
|
- Provide helpful, accurate, and concise responses
|
52
52
|
- Ask clarifying questions when needed
|
53
|
-
- Maintain a friendly and professional tone
|
53
|
+
- Maintain a friendly and professional tone
|
54
|
+
{% if emoji_enabled %}
|
55
|
+
## Emoji Usage
|
56
|
+
- Feel free to use emojis in your responses to make them more engaging and expressive 😊
|
57
|
+
- Use appropriate emojis to enhance communication: ✅ for confirmations, ❓ for questions, 💡 for ideas, etc.
|
58
|
+
- Emojis should complement the message, not replace clear communication
|
59
|
+
{% endif %}
|
janito/cli/chat_mode/session.py
CHANGED
@@ -119,9 +119,7 @@ class ChatSession:
|
|
119
119
|
profile_system_prompt = None
|
120
120
|
no_tools_mode = False
|
121
121
|
|
122
|
-
profile = self._determine_profile(
|
123
|
-
profile, role_arg, python_profile, market_profile
|
124
|
-
)
|
122
|
+
profile = self._determine_profile(profile, python_profile, market_profile)
|
125
123
|
|
126
124
|
if (
|
127
125
|
profile is None
|
@@ -136,31 +134,8 @@ class ChatSession:
|
|
136
134
|
if skip_profile_selection:
|
137
135
|
profile = "Developer with Python Tools" # Default for non-interactive commands
|
138
136
|
else:
|
139
|
-
try:
|
140
|
-
from janito.cli.chat_mode.session_profile_select import (
|
141
|
-
select_profile,
|
142
|
-
)
|
143
|
-
|
144
|
-
result = select_profile()
|
145
|
-
if isinstance(result, dict):
|
146
|
-
profile = result.get("profile")
|
147
|
-
profile_system_prompt = result.get("profile_system_prompt")
|
148
|
-
no_tools_mode = result.get("no_tools_mode", False)
|
149
|
-
elif isinstance(result, str) and result.startswith("role:"):
|
150
|
-
role = result[len("role:") :].strip()
|
151
|
-
profile = "Developer with Python Tools"
|
152
|
-
else:
|
153
|
-
profile = (
|
154
|
-
"Developer with Python Tools"
|
155
|
-
if result == "Developer"
|
156
|
-
else result
|
157
|
-
)
|
158
|
-
except ImportError:
|
159
|
-
profile = "Raw Model Session (no tools, no context)"
|
160
|
-
if role_arg is not None:
|
161
|
-
role = role_arg
|
162
|
-
if profile is None:
|
163
137
|
profile = "Developer with Python Tools"
|
138
|
+
|
164
139
|
return profile, role, profile_system_prompt, no_tools_mode
|
165
140
|
|
166
141
|
def _create_conversation_history(self):
|
@@ -308,25 +283,6 @@ class ChatSession:
|
|
308
283
|
)
|
309
284
|
)
|
310
285
|
|
311
|
-
for candidate in candidates:
|
312
|
-
try:
|
313
|
-
if not candidate:
|
314
|
-
continue
|
315
|
-
parsed = urlparse(str(candidate))
|
316
|
-
host = parsed.netloc or parsed.path
|
317
|
-
if host:
|
318
|
-
backend_hostname = host
|
319
|
-
break
|
320
|
-
except Exception:
|
321
|
-
backend_hostname = str(candidate)
|
322
|
-
break
|
323
|
-
|
324
|
-
self.console.print(
|
325
|
-
Rule(
|
326
|
-
f"[bold blue]Model: {model_name} ({provider_name}) | Backend: {backend_hostname}[/bold blue]"
|
327
|
-
)
|
328
|
-
)
|
329
|
-
|
330
286
|
self._prompt_handler.run_prompt(cmd_input)
|
331
287
|
end_time = time.time()
|
332
288
|
elapsed = end_time - start_time
|
@@ -356,18 +312,18 @@ class ChatSession:
|
|
356
312
|
def _extract_args(self, args):
|
357
313
|
"""Extract profile and role arguments from args."""
|
358
314
|
profile = getattr(args, "profile", None) if args is not None else None
|
359
|
-
role_arg =
|
315
|
+
role_arg = None
|
360
316
|
python_profile = (
|
361
317
|
getattr(args, "developer", False) if args is not None else False
|
362
318
|
)
|
363
319
|
market_profile = getattr(args, "market", False) if args is not None else False
|
364
320
|
return profile, role_arg, python_profile, market_profile
|
365
321
|
|
366
|
-
def _determine_profile(self, profile,
|
322
|
+
def _determine_profile(self, profile, python_profile, market_profile):
|
367
323
|
"""Determine the profile based on flags and arguments."""
|
368
|
-
if python_profile and profile is None
|
324
|
+
if python_profile and profile is None:
|
369
325
|
return "Developer with Python Tools"
|
370
|
-
if market_profile and profile is None
|
326
|
+
if market_profile and profile is None:
|
371
327
|
return "Market Analyst"
|
372
328
|
return profile
|
373
329
|
|
@@ -517,9 +473,19 @@ class ChatSession:
|
|
517
473
|
else:
|
518
474
|
duration_str = f"{session_duration/3600:.1f}h"
|
519
475
|
|
476
|
+
# Format tokens in k/m/t as appropriate
|
477
|
+
if total_tokens >= 1_000_000_000:
|
478
|
+
token_str = f"{total_tokens/1_000_000_000:.1f}t"
|
479
|
+
elif total_tokens >= 1_000_000:
|
480
|
+
token_str = f"{total_tokens/1_000_000:.1f}m"
|
481
|
+
elif total_tokens >= 1_000:
|
482
|
+
token_str = f"{total_tokens/1_000:.1f}k"
|
483
|
+
else:
|
484
|
+
token_str = f"{total_tokens}"
|
485
|
+
|
520
486
|
self.console.print(f"[bold yellow]Session completed![/bold yellow]")
|
521
487
|
self.console.print(
|
522
|
-
f"[dim]Session time: {duration_str} | Total tokens: {
|
488
|
+
f"[dim]Session time: {duration_str} | Total tokens: {token_str}[/dim]"
|
523
489
|
)
|
524
490
|
self.console.print("[bold yellow]Goodbye![/bold yellow]")
|
525
491
|
|
@@ -14,80 +14,104 @@ def handle_list_plugins(args: argparse.Namespace) -> None:
|
|
14
14
|
"""List plugins command handler."""
|
15
15
|
|
16
16
|
if getattr(args, "list_plugins_available", False):
|
17
|
-
|
18
|
-
available = list_available_plugins()
|
19
|
-
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
20
|
-
|
21
|
-
if available or builtin_plugins:
|
22
|
-
print("Available plugins:")
|
23
|
-
|
24
|
-
# Show builtin plugins first
|
25
|
-
if builtin_plugins:
|
26
|
-
print(" Builtin plugins:")
|
27
|
-
for plugin in builtin_plugins:
|
28
|
-
print(f" - {plugin} [BUILTIN]")
|
29
|
-
|
30
|
-
# Show other available plugins
|
31
|
-
other_plugins = [p for p in available if p not in builtin_plugins]
|
32
|
-
if other_plugins:
|
33
|
-
print(" External plugins:")
|
34
|
-
for plugin in other_plugins:
|
35
|
-
print(f" - {plugin}")
|
36
|
-
else:
|
37
|
-
print("No plugins found in search paths")
|
38
|
-
print("Search paths:")
|
39
|
-
print(f" - {os.getcwd()}/plugins")
|
40
|
-
print(f" - {os.path.expanduser('~')}/.janito/plugins")
|
17
|
+
_list_available_plugins()
|
41
18
|
elif getattr(args, "list_resources", False):
|
42
|
-
|
43
|
-
manager = PluginManager()
|
44
|
-
all_resources = manager.list_all_resources()
|
45
|
-
|
46
|
-
if all_resources:
|
47
|
-
print("Plugin Resources:")
|
48
|
-
for plugin_name, resources in all_resources.items():
|
49
|
-
metadata = manager.get_plugin_metadata(plugin_name)
|
50
|
-
print(
|
51
|
-
f"\n{plugin_name} v{metadata.version if metadata else 'unknown'}:"
|
52
|
-
)
|
53
|
-
|
54
|
-
# Group resources by type
|
55
|
-
tools = [r for r in resources if r["type"] == "tool"]
|
56
|
-
commands = [r for r in resources if r["type"] == "command"]
|
57
|
-
configs = [r for r in resources if r["type"] == "config"]
|
58
|
-
|
59
|
-
if tools:
|
60
|
-
print(" Tools:")
|
61
|
-
for tool in tools:
|
62
|
-
print(f" - {tool['name']}: {tool['description']}")
|
63
|
-
|
64
|
-
if commands:
|
65
|
-
print(" Commands:")
|
66
|
-
for cmd in commands:
|
67
|
-
print(f" - {cmd['name']}: {cmd['description']}")
|
68
|
-
|
69
|
-
if configs:
|
70
|
-
print(" Configuration:")
|
71
|
-
for config in configs:
|
72
|
-
print(f" - {config['name']}: {config['description']}")
|
73
|
-
else:
|
74
|
-
print("No plugins loaded")
|
19
|
+
_list_plugin_resources()
|
75
20
|
else:
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
21
|
+
_list_loaded_plugins()
|
22
|
+
|
23
|
+
|
24
|
+
def _list_available_plugins():
|
25
|
+
"""List available plugins."""
|
26
|
+
available = list_available_plugins()
|
27
|
+
builtin_plugins = BuiltinPluginRegistry.list_builtin_plugins()
|
28
|
+
|
29
|
+
if available or builtin_plugins:
|
30
|
+
print("Available plugins:")
|
31
|
+
_print_builtin_plugins(builtin_plugins)
|
32
|
+
_print_external_plugins(available, builtin_plugins)
|
33
|
+
else:
|
34
|
+
print("No plugins found in search paths")
|
35
|
+
print("Search paths:")
|
36
|
+
print(f" - {os.getcwd()}/plugins")
|
37
|
+
print(f" - {os.path.expanduser('~')}/.janito/plugins")
|
38
|
+
|
39
|
+
|
40
|
+
def _print_builtin_plugins(builtin_plugins):
|
41
|
+
"""Print builtin plugins."""
|
42
|
+
if builtin_plugins:
|
43
|
+
print(" Builtin plugins:")
|
44
|
+
for plugin in builtin_plugins:
|
45
|
+
print(f" - {plugin} [BUILTIN]")
|
46
|
+
|
47
|
+
|
48
|
+
def _print_external_plugins(available, builtin_plugins):
|
49
|
+
"""Print external plugins."""
|
50
|
+
other_plugins = [p for p in available if p not in builtin_plugins]
|
51
|
+
if other_plugins:
|
52
|
+
print(" External plugins:")
|
53
|
+
for plugin in other_plugins:
|
54
|
+
print(f" - {plugin}")
|
55
|
+
|
56
|
+
|
57
|
+
def _list_plugin_resources():
|
58
|
+
"""List all resources from loaded plugins."""
|
59
|
+
manager = PluginManager()
|
60
|
+
all_resources = manager.list_all_resources()
|
61
|
+
|
62
|
+
if all_resources:
|
63
|
+
print("Plugin Resources:")
|
64
|
+
for plugin_name, resources in all_resources.items():
|
65
|
+
metadata = manager.get_plugin_metadata(plugin_name)
|
66
|
+
print(f"\n{plugin_name} v{metadata.version if metadata else 'unknown'}:")
|
67
|
+
_print_resources_by_type(resources)
|
68
|
+
else:
|
69
|
+
print("No plugins loaded")
|
70
|
+
|
71
|
+
|
72
|
+
def _print_resources_by_type(resources):
|
73
|
+
"""Print resources grouped by type."""
|
74
|
+
tools = [r for r in resources if r["type"] == "tool"]
|
75
|
+
commands = [r for r in resources if r["type"] == "command"]
|
76
|
+
configs = [r for r in resources if r["type"] == "config"]
|
77
|
+
|
78
|
+
if tools:
|
79
|
+
print(" Tools:")
|
80
|
+
for tool in tools:
|
81
|
+
print(f" - {tool['name']}: {tool['description']}")
|
82
|
+
|
83
|
+
if commands:
|
84
|
+
print(" Commands:")
|
85
|
+
for cmd in commands:
|
86
|
+
print(f" - {cmd['name']}: {cmd['description']}")
|
87
|
+
|
88
|
+
if configs:
|
89
|
+
print(" Configuration:")
|
90
|
+
for config in configs:
|
91
|
+
print(f" - {config['name']}: {config['description']}")
|
92
|
+
|
93
|
+
|
94
|
+
def _list_loaded_plugins():
|
95
|
+
"""List loaded plugins."""
|
96
|
+
manager = PluginManager()
|
97
|
+
loaded = manager.list_plugins()
|
98
|
+
|
99
|
+
if loaded:
|
100
|
+
print("Loaded plugins:")
|
101
|
+
for plugin_name in loaded:
|
102
|
+
_print_plugin_details(manager, plugin_name)
|
103
|
+
else:
|
104
|
+
print("No plugins loaded")
|
105
|
+
|
106
|
+
|
107
|
+
def _print_plugin_details(manager, plugin_name):
|
108
|
+
"""Print details for a loaded plugin."""
|
109
|
+
metadata = manager.get_plugin_metadata(plugin_name)
|
110
|
+
is_builtin = BuiltinPluginRegistry.is_builtin(plugin_name)
|
111
|
+
if metadata:
|
112
|
+
builtin_tag = " [BUILTIN]" if is_builtin else ""
|
113
|
+
print(f" - {metadata.name} v{metadata.version}{builtin_tag}")
|
114
|
+
print(f" {metadata.description}")
|
115
|
+
if metadata.author:
|
116
|
+
print(f" Author: {metadata.author}")
|
117
|
+
print()
|
@@ -35,6 +35,7 @@ def _prepare_context(args, agent_role, allowed_permissions):
|
|
35
35
|
context["role"] = agent_role or "developer"
|
36
36
|
context["profile"] = getattr(args, "profile", None)
|
37
37
|
context["allowed_permissions"] = allowed_permissions
|
38
|
+
context["emoji_enabled"] = getattr(args, "emoji", False)
|
38
39
|
if allowed_permissions and "x" in allowed_permissions:
|
39
40
|
pd = PlatformDiscovery()
|
40
41
|
context["platform"] = pd.get_platform_name()
|
@@ -122,6 +123,10 @@ def handle_show_system_prompt(args):
|
|
122
123
|
if profile is None and getattr(args, "market", False):
|
123
124
|
profile = "Market Analyst"
|
124
125
|
|
126
|
+
# Handle --developer flag mapping to Developer With Python Tools profile
|
127
|
+
if profile is None and getattr(args, "developer", False):
|
128
|
+
profile = "Developer With Python Tools"
|
129
|
+
|
125
130
|
if not profile:
|
126
131
|
print(
|
127
132
|
"[janito] No profile specified. The main agent runs without a system prompt template.\n"
|
@@ -152,9 +157,9 @@ def handle_show_system_prompt(args):
|
|
152
157
|
system_prompt = template.render(**context)
|
153
158
|
system_prompt = re.sub(r"\n{3,}", "\n\n", system_prompt)
|
154
159
|
|
155
|
-
|
156
|
-
|
157
|
-
)
|
160
|
+
# Use the actual profile name for display, not the resolved value
|
161
|
+
display_profile = profile or "main"
|
162
|
+
print(f"\n--- System Prompt (resolved, profile: {display_profile}) ---\n")
|
158
163
|
print(system_prompt)
|
159
164
|
print("\n-------------------------------\n")
|
160
165
|
if agent_role:
|
janito/cli/core/runner.py
CHANGED
@@ -109,8 +109,8 @@ def prepare_llm_driver_config(args, modifiers):
|
|
109
109
|
llm_driver_config = LLMDriverConfig(**driver_config_data)
|
110
110
|
if getattr(llm_driver_config, "verbose_api", None):
|
111
111
|
pass
|
112
|
-
|
113
|
-
agent_role = modifiers.get("
|
112
|
+
|
113
|
+
agent_role = modifiers.get("profile") or "developer"
|
114
114
|
return provider, llm_driver_config, agent_role
|
115
115
|
|
116
116
|
|
janito/cli/main_cli.py
CHANGED
@@ -51,14 +51,6 @@ definition = [
|
|
51
51
|
"help": "Start with the Market Analyst profile (equivalent to --profile 'Market Analyst')",
|
52
52
|
},
|
53
53
|
),
|
54
|
-
(
|
55
|
-
["--role"],
|
56
|
-
{
|
57
|
-
"metavar": "ROLE",
|
58
|
-
"help": "Select the developer role name (overrides profile, e.g. 'python-expert').",
|
59
|
-
"default": None,
|
60
|
-
},
|
61
|
-
),
|
62
54
|
(
|
63
55
|
["-W", "--workdir"],
|
64
56
|
{
|
@@ -205,6 +197,13 @@ definition = [
|
|
205
197
|
"help": "Set the reasoning effort for models that support it (low, medium, high, none)",
|
206
198
|
},
|
207
199
|
),
|
200
|
+
(
|
201
|
+
["--emoji"],
|
202
|
+
{
|
203
|
+
"action": "store_true",
|
204
|
+
"help": "Enable emoji usage in responses to make output more engaging and expressive",
|
205
|
+
},
|
206
|
+
),
|
208
207
|
(["user_prompt"], {"nargs": argparse.REMAINDER, "help": "Prompt to submit"}),
|
209
208
|
(
|
210
209
|
["-e", "--event-log"],
|
@@ -244,7 +243,6 @@ definition = [
|
|
244
243
|
MODIFIER_KEYS = [
|
245
244
|
"provider",
|
246
245
|
"model",
|
247
|
-
"role",
|
248
246
|
"profile",
|
249
247
|
"developer",
|
250
248
|
"market",
|
@@ -257,6 +255,7 @@ MODIFIER_KEYS = [
|
|
257
255
|
"exec",
|
258
256
|
"read",
|
259
257
|
"write",
|
258
|
+
"emoji",
|
260
259
|
]
|
261
260
|
SETTER_KEYS = ["set", "set_provider", "set_api_key", "unset"]
|
262
261
|
GETTER_KEYS = [
|
@@ -372,9 +371,7 @@ class JanitoCLI:
|
|
372
371
|
for k in MODIFIER_KEYS
|
373
372
|
if getattr(self.args, k, None) is not None
|
374
373
|
}
|
375
|
-
|
376
|
-
if getattr(self.args, "role", None):
|
377
|
-
modifiers["role"] = getattr(self.args, "role")
|
374
|
+
|
378
375
|
return modifiers
|
379
376
|
|
380
377
|
def classify(self):
|
@@ -420,9 +417,6 @@ class JanitoCLI:
|
|
420
417
|
self.args.exec = True
|
421
418
|
# Remove the /rwx prefix from the prompt
|
422
419
|
self.args.user_prompt = self.args.user_prompt[1:]
|
423
|
-
elif self.args.user_prompt and self.args.user_prompt[0].startswith("/"):
|
424
|
-
# Skip LLM processing for other commands that start with /
|
425
|
-
return
|
426
420
|
|
427
421
|
# If running in single shot mode and --profile is not provided, default to 'developer' profile
|
428
422
|
# Skip profile selection for list commands that don't need it
|
janito/llm/agent.py
CHANGED
@@ -244,7 +244,12 @@ class LLMAgent:
|
|
244
244
|
f"[agent] [DEBUG] Tool call detected: {getattr(part, 'name', repr(part))} with arguments: {getattr(part, 'arguments', None)}"
|
245
245
|
)
|
246
246
|
tool_calls.append(part)
|
247
|
-
|
247
|
+
try:
|
248
|
+
result = self.tools_adapter.execute_function_call_message_part(part)
|
249
|
+
except Exception as e:
|
250
|
+
# Catch any exception during tool execution and return as string
|
251
|
+
# instead of letting it propagate to the user
|
252
|
+
result = str(e)
|
248
253
|
tool_results.append(result)
|
249
254
|
if tool_calls:
|
250
255
|
# Prepare tool_calls message for assistant
|
@@ -5,6 +5,7 @@ from janito.tools.loop_protection_decorator import protect_against_loops
|
|
5
5
|
from rich import print as rich_print
|
6
6
|
from janito.i18n import tr
|
7
7
|
from rich.panel import Panel
|
8
|
+
from rich.markdown import Markdown
|
8
9
|
from prompt_toolkit import PromptSession
|
9
10
|
from prompt_toolkit.key_binding import KeyBindings
|
10
11
|
from prompt_toolkit.enums import EditingMode
|
@@ -36,7 +37,7 @@ class AskUserTool(ToolBase):
|
|
36
37
|
def run(self, question: str) -> str:
|
37
38
|
|
38
39
|
print() # Print an empty line before the question panel
|
39
|
-
rich_print(Panel.fit(question, title=tr("Question"), style="cyan"))
|
40
|
+
rich_print(Panel.fit(Markdown(question), title=tr("Question"), style="cyan"))
|
40
41
|
|
41
42
|
bindings = KeyBindings()
|
42
43
|
mode = {"multiline": False}
|
@@ -107,4 +108,5 @@ class AskUserTool(ToolBase):
|
|
107
108
|
rich_print(
|
108
109
|
"[yellow]Warning: Some characters in your input were not valid UTF-8 and have been replaced.[/yellow]"
|
109
110
|
)
|
111
|
+
print("\a", end="", flush=True) # Print bell character
|
110
112
|
return sanitized
|