janito 2.21.0__py3-none-any.whl → 2.24.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 (55) hide show
  1. janito/agent/setup_agent.py +48 -4
  2. janito/agent/templates/profiles/system_prompt_template_Developer_with_Python_Tools.txt.j2 +59 -11
  3. janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +53 -7
  4. janito/agent/templates/profiles/system_prompt_template_market_analyst.txt.j2 +110 -0
  5. janito/agent/templates/profiles/system_prompt_template_model_conversation_without_tools_or_context.txt.j2 +53 -1
  6. janito/cli/chat_mode/session.py +8 -1
  7. janito/cli/chat_mode/session_profile_select.py +20 -3
  8. janito/cli/chat_mode/shell/commands/__init__.py +2 -0
  9. janito/cli/chat_mode/shell/commands/security/__init__.py +1 -0
  10. janito/cli/chat_mode/shell/commands/security/allowed_sites.py +94 -0
  11. janito/cli/chat_mode/shell/commands/security_command.py +51 -0
  12. janito/cli/cli_commands/list_plugins.py +45 -0
  13. janito/cli/cli_commands/list_profiles.py +29 -1
  14. janito/cli/cli_commands/show_system_prompt.py +24 -10
  15. janito/cli/core/getters.py +4 -0
  16. janito/cli/core/runner.py +7 -2
  17. janito/cli/core/setters.py +10 -1
  18. janito/cli/main_cli.py +25 -3
  19. janito/cli/single_shot_mode/handler.py +3 -1
  20. janito/config_manager.py +10 -0
  21. janito/plugins/__init__.py +17 -0
  22. janito/plugins/base.py +93 -0
  23. janito/plugins/discovery.py +160 -0
  24. janito/plugins/manager.py +185 -0
  25. janito/providers/ibm/model_info.py +9 -0
  26. janito/tools/adapters/local/__init__.py +2 -0
  27. janito/tools/adapters/local/adapter.py +55 -0
  28. janito/tools/adapters/local/ask_user.py +2 -0
  29. janito/tools/adapters/local/fetch_url.py +184 -11
  30. janito/tools/adapters/local/find_files.py +2 -0
  31. janito/tools/adapters/local/get_file_outline/core.py +2 -0
  32. janito/tools/adapters/local/get_file_outline/search_outline.py +2 -0
  33. janito/tools/adapters/local/open_html_in_browser.py +2 -0
  34. janito/tools/adapters/local/open_url.py +2 -0
  35. janito/tools/adapters/local/python_code_run.py +15 -10
  36. janito/tools/adapters/local/python_command_run.py +14 -9
  37. janito/tools/adapters/local/python_file_run.py +15 -10
  38. janito/tools/adapters/local/read_chart.py +252 -0
  39. janito/tools/adapters/local/read_files.py +2 -0
  40. janito/tools/adapters/local/replace_text_in_file.py +1 -1
  41. janito/tools/adapters/local/run_bash_command.py +18 -12
  42. janito/tools/adapters/local/run_powershell_command.py +15 -9
  43. janito/tools/adapters/local/search_text/core.py +2 -0
  44. janito/tools/adapters/local/validate_file_syntax/core.py +6 -0
  45. janito/tools/adapters/local/validate_file_syntax/jinja2_validator.py +47 -0
  46. janito/tools/adapters/local/view_file.py +2 -0
  47. janito/tools/loop_protection.py +115 -0
  48. janito/tools/loop_protection_decorator.py +110 -0
  49. janito/tools/url_whitelist.py +121 -0
  50. {janito-2.21.0.dist-info → janito-2.24.0.dist-info}/METADATA +1 -1
  51. {janito-2.21.0.dist-info → janito-2.24.0.dist-info}/RECORD +55 -41
  52. {janito-2.21.0.dist-info → janito-2.24.0.dist-info}/WHEEL +0 -0
  53. {janito-2.21.0.dist-info → janito-2.24.0.dist-info}/entry_points.txt +0 -0
  54. {janito-2.21.0.dist-info → janito-2.24.0.dist-info}/licenses/LICENSE +0 -0
  55. {janito-2.21.0.dist-info → janito-2.24.0.dist-info}/top_level.txt +0 -0
@@ -52,10 +52,40 @@ def _load_template_content(profile, templates_dir):
52
52
  with open(user_template_path, "r", encoding="utf-8") as file:
53
53
  return file.read(), user_template_path
54
54
 
55
- # If nothing matched, raise an informative error
56
- raise FileNotFoundError(
57
- 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}."
58
- )
55
+ # If nothing matched, list available profiles and raise an informative error
56
+ from janito.cli.cli_commands.list_profiles import _gather_default_profiles, _gather_user_profiles
57
+
58
+ default_profiles = _gather_default_profiles()
59
+ user_profiles = _gather_user_profiles()
60
+
61
+ available_profiles = []
62
+ if default_profiles:
63
+ available_profiles.extend([(p, "default") for p in default_profiles])
64
+ if user_profiles:
65
+ available_profiles.extend([(p, "user") for p in user_profiles])
66
+
67
+ # Normalize the input profile for better matching suggestions
68
+ normalized_input = re.sub(r"\s+", " ", profile.strip().lower())
69
+
70
+ if available_profiles:
71
+ profile_list = "\n".join([f" - {name} ({source})" for name, source in available_profiles])
72
+
73
+ # Find close matches
74
+ close_matches = []
75
+ for name, source in available_profiles:
76
+ normalized_name = name.lower()
77
+ if normalized_input in normalized_name or normalized_name in normalized_input:
78
+ close_matches.append(name)
79
+
80
+ suggestion = ""
81
+ if close_matches:
82
+ suggestion = f"\nDid you mean: {', '.join(close_matches)}?"
83
+
84
+ error_msg = f"[janito] Could not find profile '{profile}'. Available profiles:\n{profile_list}{suggestion}"
85
+ else:
86
+ error_msg = f"[janito] Could not find profile '{profile}'. No profiles available."
87
+
88
+ raise FileNotFoundError(error_msg)
59
89
  # Replace spaces in profile name with underscores for filename resolution
60
90
  sanitized_profile = re.sub(r"\\s+", "_", profile.strip()) if profile else profile
61
91
  """
@@ -114,6 +144,20 @@ def _prepare_template_context(role, profile, allowed_permissions):
114
144
  context["platform"] = pd.get_platform_name()
115
145
  context["python_version"] = pd.get_python_version()
116
146
  context["shell_info"] = pd.detect_shell()
147
+
148
+ # Add allowed sites for market analyst profile
149
+ if profile == "market-analyst":
150
+ from janito.tools.url_whitelist import get_url_whitelist_manager
151
+ whitelist_manager = get_url_whitelist_manager()
152
+ allowed_sites = whitelist_manager.get_allowed_sites()
153
+ context["allowed_sites"] = allowed_sites
154
+
155
+ # Add market data sources documentation
156
+ if not allowed_sites:
157
+ context["allowed_sites_info"] = "No whitelist restrictions - all sites allowed"
158
+ else:
159
+ context["allowed_sites_info"] = f"Restricted to: {', '.join(allowed_sites)}"
160
+
117
161
  return context
118
162
 
119
163
 
@@ -1,22 +1,70 @@
1
- {# General role setup
1
+ {# Profile Metadata #}
2
+ {% if name %}Name: {{ name }}{% endif %}
3
+ {% if version %}Version: {{ version }}{% endif %}
4
+ {% if description %}Description: {{ description }}{% endif %}
5
+ {% if author %}Author: {{ author }}{% endif %}
6
+ {% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
7
+
8
+ {# General role setup
2
9
  ex. "Search in code" -> Python Developer -> find(*.py) | Java Developer -> find(*.java)
3
10
  #}
4
- You are: {{ role }}
11
+ You are: {{ role | default('developer') }}
5
12
 
6
- {# Improves tool selection and platform specific constrains, eg, path format, C:\ vs /path #}
7
- {% if allowed_permissions and 'x' in allowed_permissions %}
8
- You will be using the following environment:
9
- Platform: {{ platform }}
10
- Python version: {{ python_version }}
11
- Shell/Environment: {{ shell_info }}
13
+ {% if capabilities %}
14
+ ## Capabilities
15
+ {% for capability in capabilities %}
16
+ - {{ capability }}
17
+ {% endfor %}
18
+ {% else %}
19
+ ## Capabilities
20
+ - Software development and engineering
21
+ - Code analysis and debugging
22
+ - File system operations
23
+ - Python development and tooling
24
+ - Cross-platform development
12
25
  {% endif %}
13
26
 
27
+ {% if constraints %}
28
+ ## Constraints
29
+ {% for constraint in constraints %}
30
+ - {{ constraint }}
31
+ {% endfor %}
32
+ {% endif %}
14
33
 
15
- {% if allowed_permissions and 'r' in allowed_permissions %}
16
- Before answering map the questions to artifacts found in the current directory - the current project.
34
+ {% if environment or platform or python_version or shell_info %}
35
+ ## Environment
36
+ {% if platform %}Platform: {{ platform }}{% endif %}
37
+ {% if python_version %}Python version: {{ python_version }}{% endif %}
38
+ {% if shell_info %}Shell/Environment: {{ shell_info }}{% endif %}
39
+ {% endif %}
40
+
41
+ {% if examples %}
42
+ ## Examples
43
+ {% for example in examples %}
44
+ ### {{ example.title }}
45
+ {{ example.description }}
46
+ ```
47
+ {{ example.code }}
48
+ ```
49
+ {% endfor %}
50
+ {% endif %}
51
+
52
+ {% if custom_instructions %}
53
+ ## Custom Instructions
54
+ {{ custom_instructions }}
55
+ {% endif %}
56
+
57
+ {% if available_tools %}
58
+ ## Available Tools
59
+ {% for tool in available_tools %}
60
+ - **{{ tool.name }}**: {{ tool.description }}
61
+ {% if tool.parameters %}
62
+ Parameters: {{ tool.parameters | join(', ') }}
63
+ {% endif %}
64
+ {% endfor %}
17
65
  {% endif %}
18
66
 
19
- Respond according to the following guidelines:
67
+ ## Guidelines
20
68
  {% if allowed_permissions %}
21
69
  - Before using the namespace tools provide a short reason
22
70
  {% endif %}
@@ -1,14 +1,60 @@
1
- You are: software developer
1
+ {# Profile Metadata #}
2
+ {% if name %}Name: {{ name }}{% endif %}
3
+ {% if version %}Version: {{ version }}{% endif %}
4
+ {% if description %}Description: {{ description }}{% endif %}
5
+ {% if author %}Author: {{ author }}{% endif %}
6
+ {% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
2
7
 
3
- {# Improves tool selection and platform specific constrains, eg, path format, C:\ vs /path #}
4
- You will be using the following environment:
5
- Platform: Windows
6
- Shell/Environment: PowerShell
8
+ You are: {{ role | default('software developer') }}
7
9
 
10
+ {% if capabilities %}
11
+ ## Capabilities
12
+ {% for capability in capabilities %}
13
+ - {{ capability }}
14
+ {% endfor %}
15
+ {% endif %}
16
+
17
+ {% if constraints %}
18
+ ## Constraints
19
+ {% for constraint in constraints %}
20
+ - {{ constraint }}
21
+ {% endfor %}
22
+ {% endif %}
8
23
 
9
- Before answering map the questions to artifacts found in the current directory - the current project.
24
+ {% if environment or platform or shell_info %}
25
+ ## Environment
26
+ {% if platform %}Platform: {{ platform }}{% endif %}
27
+ {% if python_version %}Python version: {{ python_version }}{% endif %}
28
+ {% if shell_info %}Shell/Environment: {{ shell_info }}{% endif %}
29
+ {% endif %}
30
+
31
+ {% if examples %}
32
+ ## Examples
33
+ {% for example in examples %}
34
+ ### {{ example.title }}
35
+ {{ example.description }}
36
+ ```
37
+ {{ example.code }}
38
+ ```
39
+ {% endfor %}
40
+ {% endif %}
41
+
42
+ {% if custom_instructions %}
43
+ ## Custom Instructions
44
+ {{ custom_instructions }}
45
+ {% endif %}
46
+
47
+ {% if available_tools %}
48
+ ## Available Tools
49
+ {% for tool in available_tools %}
50
+ - **{{ tool.name }}**: {{ tool.description }}
51
+ {% if tool.parameters %}
52
+ Parameters: {{ tool.parameters | join(', ') }}
53
+ {% endif %}
54
+ {% endfor %}
55
+ {% endif %}
10
56
 
11
- Respond according to the following guidelines:
57
+ ## Guidelines
12
58
  {% if allowed_permissions %}
13
59
  - Before using the namespace tools provide a short reason
14
60
  {% endif %}
@@ -0,0 +1,110 @@
1
+ {# Profile Metadata #}
2
+ {% if name %}Name: {{ name }}{% endif %}
3
+ {% if version %}Version: {{ version }}{% endif %}
4
+ {% if description %}Description: {{ description }}{% endif %}
5
+ {% if author %}Author: {{ author }}{% endif %}
6
+ {% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
7
+
8
+ You are: {{ role | default('market analyst specializing in financial markets, business intelligence, and economic research') }}
9
+
10
+ {% if capabilities %}
11
+ ## Capabilities
12
+ {% for capability in capabilities %}
13
+ - {{ capability }}
14
+ {% endfor %}
15
+ {% else %}
16
+ ## Capabilities
17
+ - Financial analysis and market insights
18
+ - Stock recommendations and trading strategies
19
+ - Technical and fundamental analysis
20
+ - Risk management and position sizing
21
+ - Economic research and business intelligence
22
+ {% endif %}
23
+
24
+ {% if constraints %}
25
+ ## Constraints
26
+ {% for constraint in constraints %}
27
+ - {{ constraint }}
28
+ {% endfor %}
29
+ {% else %}
30
+ ## Constraints
31
+ - Only use allowed sites configured via security settings
32
+ - Never attempt to access unauthorized sources
33
+ - Provide actionable insights with specific entry/exit strategies
34
+ - Include risk management in all recommendations
35
+ {% endif %}
36
+
37
+ {% if data_sources %}
38
+ ## Data Sources
39
+ {% for source in data_sources %}
40
+ - {{ source }}
41
+ {% endfor %}
42
+ {% else %}
43
+ ## Data Sources
44
+ {% if allowed_sites %}
45
+ ✅ **Configured Sources:** {{ ', '.join(allowed_sites) }}
46
+ {% else %}
47
+ ⚠️ **No whitelist configured** - All sites allowed (use with caution)
48
+ {% endif %}
49
+
50
+ **Recommended Market Data Sources:**
51
+ - **Tier 1 (Government):** sec.gov, fred.stlouisfed.org
52
+ - **Tier 2 (Public):** tradingview.com, investing.com, finance.yahoo.com
53
+ - **Tier 3 (APIs):** alphavantage.co, financialmodelingprep.com, twelvedata.com
54
+ {% endif %}
55
+
56
+ {% if environment or platform or shell_info %}
57
+ ## Environment
58
+ {% if platform %}Platform: {{ platform }}{% endif %}
59
+ {% if python_version %}Python version: {{ python_version }}{% endif %}
60
+ {% if shell_info %}Shell/Environment: {{ shell_info }}{% endif %}
61
+ {% endif %}
62
+
63
+ {% if examples %}
64
+ ## Examples
65
+ {% for example in examples %}
66
+ ### {{ example.title }}
67
+ {{ example.description }}
68
+ ```
69
+ {{ example.code }}
70
+ ```
71
+ {% endfor %}
72
+ {% else %}
73
+ ## Analysis Framework
74
+
75
+ When analyzing data:
76
+
77
+ • Identify market trends and patterns with specific technical indicators (RSI, MACD, moving averages, support/resistance levels)
78
+ • Provide quantitative analysis with clear metrics, valuation ratios (P/E, P/S, EV/EBITDA), and financial ratios
79
+ • Consider economic indicators, market sentiment, and sector rotation patterns
80
+ • Offer actionable insights for trading and investment decisions with specific entry/exit strategies
81
+ • Use appropriate financial terminology and frameworks (DCF, comparable company analysis, technical analysis)
82
+ • Reference relevant market data, benchmarks, and industry standards
83
+ • Provide specific stock recommendations with risk management strategies and position sizing guidance
84
+ • Analyze options flow, institutional positioning, and sentiment indicators
85
+
86
+ When asked about specific stocks or market movements, provide detailed analysis including:
87
+ - Technical indicators and chart patterns
88
+ - Fundamental valuation metrics
89
+ - Analyst estimate revisions and sentiment shifts
90
+ - Options market activity and flow
91
+ - Risk/reward profiles for trades
92
+ - Specific price targets and stop-loss levels
93
+ {% endif %}
94
+
95
+ {% if custom_instructions %}
96
+ ## Custom Instructions
97
+ {{ custom_instructions }}
98
+ {% endif %}
99
+
100
+ {% if available_tools %}
101
+ ## Available Tools
102
+ {% for tool in available_tools %}
103
+ - **{{ tool.name }}**: {{ tool.description }}
104
+ {% if tool.parameters %}
105
+ Parameters: {{ tool.parameters | join(', ') }}
106
+ {% endif %}
107
+ {% endfor %}
108
+ {% endif %}
109
+
110
+ ## Guidelines
@@ -1 +1,53 @@
1
- You are a helpful assistant.
1
+ {# Profile Metadata #}
2
+ {% if name %}Name: {{ name }}{% endif %}
3
+ {% if version %}Version: {{ version }}{% endif %}
4
+ {% if description %}Description: {{ description }}{% endif %}
5
+ {% if author %}Author: {{ author }}{% endif %}
6
+ {% if tags %}Tags: {{ tags | join(', ') }}{% endif %}
7
+
8
+ You are: {{ role | default('helpful assistant') }}
9
+
10
+ {% if capabilities %}
11
+ ## Capabilities
12
+ {% for capability in capabilities %}
13
+ - {{ capability }}
14
+ {% endfor %}
15
+ {% endif %}
16
+
17
+ {% if constraints %}
18
+ ## Constraints
19
+ {% for constraint in constraints %}
20
+ - {{ constraint }}
21
+ {% endfor %}
22
+ {% endif %}
23
+
24
+ {% if examples %}
25
+ ## Examples
26
+ {% for example in examples %}
27
+ ### {{ example.title }}
28
+ {{ example.description }}
29
+ ```
30
+ {{ example.code }}
31
+ ```
32
+ {% endfor %}
33
+ {% endif %}
34
+
35
+ {% if custom_instructions %}
36
+ ## Custom Instructions
37
+ {{ custom_instructions }}
38
+ {% endif %}
39
+
40
+ {% if available_tools %}
41
+ ## Available Tools
42
+ {% for tool in available_tools %}
43
+ - **{{ tool.name }}**: {{ tool.description }}
44
+ {% if tool.parameters %}
45
+ Parameters: {{ tool.parameters | join(', ') }}
46
+ {% endif %}
47
+ {% endfor %}
48
+ {% endif %}
49
+
50
+ ## Guidelines
51
+ - Provide helpful, accurate, and concise responses
52
+ - Ask clarifying questions when needed
53
+ - Maintain a friendly and professional tone
@@ -94,6 +94,7 @@ class ChatSession:
94
94
  profile_system_prompt,
95
95
  conversation_history,
96
96
  )
97
+ self.profile = profile # Store profile name for welcome message
97
98
  self.shell_state = ChatShellState(self.mem_history, conversation_history)
98
99
  self.shell_state.agent = self.agent
99
100
  # Set no_tools_mode if present
@@ -116,6 +117,7 @@ class ChatSession:
116
117
  profile = getattr(args, "profile", None) if args is not None else None
117
118
  role_arg = getattr(args, "role", None) if args is not None else None
118
119
  python_profile = getattr(args, "python", False) if args is not None else False
120
+ market_profile = getattr(args, "market", False) if args is not None else False
119
121
  profile_system_prompt = None
120
122
  no_tools_mode = False
121
123
 
@@ -123,7 +125,11 @@ class ChatSession:
123
125
  if python_profile and profile is None and role_arg is None:
124
126
  profile = "Developer with Python Tools"
125
127
 
126
- if profile is None and role_arg is None and not python_profile:
128
+ # Handle --market flag
129
+ if market_profile and profile is None and role_arg is None:
130
+ profile = "Market Analyst"
131
+
132
+ if profile is None and role_arg is None and not python_profile and not market_profile:
127
133
  try:
128
134
  from janito.cli.chat_mode.session_profile_select import select_profile
129
135
 
@@ -202,6 +208,7 @@ class ChatSession:
202
208
  from janito import __version__
203
209
 
204
210
  self.console.print(f"[bold green]Janito Chat Mode v{__version__}[/bold green]")
211
+ self.console.print(f"[dim]Profile: {self.profile}[/dim]")
205
212
  self.console.print(
206
213
  "[green]/help for commands /exit or Ctrl+C to quit[/green]"
207
214
  )
@@ -111,6 +111,7 @@ def select_profile():
111
111
  choices = [
112
112
  "Developer with Python Tools",
113
113
  "Developer",
114
+ "Market Analyst",
114
115
  "Custom system prompt...",
115
116
  "Raw Model Session (no tools, no context)",
116
117
  ]
@@ -146,9 +147,9 @@ def select_profile():
146
147
  from jinja2 import Template
147
148
  from janito.agent.setup_agent import _prepare_template_context
148
149
 
149
- template_path = Path(
150
- "./janito/agent/templates/profiles/system_prompt_template_Developer.txt.j2"
151
- )
150
+ # Get the absolute path relative to the current script location
151
+ current_dir = Path(__file__).parent
152
+ template_path = current_dir / "../../agent/templates/profiles/system_prompt_template_developer.txt.j2"
152
153
  with open(template_path, "r", encoding="utf-8") as f:
153
154
  template_content = f.read()
154
155
 
@@ -156,4 +157,20 @@ def select_profile():
156
157
  context = _prepare_template_context("developer", "Developer", None)
157
158
  prompt = template.render(**context)
158
159
  return {"profile": "Developer", "profile_system_prompt": prompt}
160
+ elif answer == "Market Analyst":
161
+ # Return the content of the built-in Market Analyst profile prompt
162
+ from pathlib import Path
163
+ from jinja2 import Template
164
+ from janito.agent.setup_agent import _prepare_template_context
165
+
166
+ # Get the absolute path relative to the current script location
167
+ current_dir = Path(__file__).parent
168
+ template_path = current_dir / "../../agent/templates/profiles/system_prompt_template_market_analyst.txt.j2"
169
+ with open(template_path, "r", encoding="utf-8") as f:
170
+ template_content = f.read()
171
+
172
+ template = Template(template_content)
173
+ context = _prepare_template_context("market_analyst", "Market Analyst", None)
174
+ prompt = template.render(**context)
175
+ return {"profile": "Market Analyst", "profile_system_prompt": prompt}
159
176
  return answer
@@ -9,6 +9,7 @@ from .role import RoleCommandShellHandler
9
9
  from .session import HistoryShellHandler
10
10
  from .tools import ToolsShellHandler
11
11
  from .help import HelpShellHandler
12
+ from .security_command import SecurityCommand
12
13
  from janito.cli.console import shared_console
13
14
 
14
15
  COMMAND_HANDLERS = {
@@ -41,6 +42,7 @@ COMMAND_HANDLERS = {
41
42
  "/model": ModelShellHandler,
42
43
  "/multi": MultiShellHandler,
43
44
  "/help": HelpShellHandler,
45
+ "/security": SecurityCommand,
44
46
  }
45
47
 
46
48
 
@@ -0,0 +1 @@
1
+ """Security management commands for chat mode."""
@@ -0,0 +1,94 @@
1
+ """Security commands for managing allowed sites."""
2
+
3
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler as BaseCommand
4
+ from janito.tools.url_whitelist import get_url_whitelist_manager
5
+
6
+
7
+ class SecurityAllowedSitesCommand(BaseCommand):
8
+ """Manage allowed sites for fetch_url tool."""
9
+
10
+ def get_name(self) -> str:
11
+ return "allowed-sites"
12
+
13
+ def get_description(self) -> str:
14
+ return "Manage allowed sites for the fetch_url tool"
15
+
16
+ def get_usage(self):
17
+ return self.get_description() + """
18
+ Usage: /security allowed-sites [command] [site]
19
+
20
+ Commands:
21
+ list List all allowed sites
22
+ add <site> Add a site to the whitelist
23
+ remove <site> Remove a site from the whitelist
24
+ clear Clear all allowed sites (allow all)
25
+
26
+ Examples:
27
+ /security allowed-sites list
28
+ /security allowed-sites add tradingview.com
29
+ /security allowed-sites remove yahoo.com
30
+ /security allowed-sites clear
31
+ """
32
+ return """
33
+ Usage: /security allowed-sites [command] [site]
34
+
35
+ Commands:
36
+ list List all allowed sites
37
+ add <site> Add a site to the whitelist
38
+ remove <site> Remove a site from the whitelist
39
+ clear Clear all allowed sites (allow all)
40
+
41
+ Examples:
42
+ /security allowed-sites list
43
+ /security allowed-sites add tradingview.com
44
+ /security allowed-sites remove yahoo.com
45
+ /security allowed-sites clear
46
+ """
47
+
48
+ def run(self):
49
+ """Execute the allowed-sites command."""
50
+ args = self.after_cmd_line.strip().split()
51
+
52
+ if not args:
53
+ print(self.get_usage())
54
+ return
55
+
56
+ command = args[0].lower()
57
+ whitelist_manager = get_url_whitelist_manager()
58
+
59
+ if command == "list":
60
+ sites = whitelist_manager.get_allowed_sites()
61
+ if sites:
62
+ print("Allowed sites:")
63
+ for site in sites:
64
+ print(f" • {site}")
65
+ else:
66
+ print("No sites are whitelisted (all sites are allowed)")
67
+
68
+ elif command == "add":
69
+ if len(args) < 2:
70
+ print("Error: Please specify a site to add")
71
+ return
72
+ site = args[1]
73
+ if whitelist_manager.add_allowed_site(site):
74
+ print(f"✅ Added '{site}' to allowed sites")
75
+ else:
76
+ print(f"ℹ️ '{site}' is already in allowed sites")
77
+
78
+ elif command == "remove":
79
+ if len(args) < 2:
80
+ print("Error: Please specify a site to remove")
81
+ return
82
+ site = args[1]
83
+ if whitelist_manager.remove_allowed_site(site):
84
+ print(f"✅ Removed '{site}' from allowed sites")
85
+ else:
86
+ print(f"ℹ️ '{site}' was not in allowed sites")
87
+
88
+ elif command == "clear":
89
+ whitelist_manager.clear_whitelist()
90
+ print("✅ Cleared all allowed sites (all sites are now allowed)")
91
+
92
+ else:
93
+ print(f"Error: Unknown command '{command}'")
94
+ print(self.get_usage())
@@ -0,0 +1,51 @@
1
+ """Security command group for chat mode."""
2
+
3
+ from janito.cli.chat_mode.shell.commands.base import ShellCmdHandler as BaseCommand
4
+ from janito.cli.chat_mode.shell.commands.security.allowed_sites import SecurityAllowedSitesCommand
5
+
6
+
7
+ class SecurityCommand(BaseCommand):
8
+ """Security management command group."""
9
+
10
+ def get_name(self) -> str:
11
+ return "security"
12
+
13
+ def get_description(self) -> str:
14
+ return "Security management commands"
15
+
16
+ def get_usage(self):
17
+ return """
18
+ Usage: /security <subcommand> [args...]
19
+
20
+ Subcommands:
21
+ allowed-sites Manage allowed sites for fetch_url tool
22
+
23
+ Examples:
24
+ /security allowed-sites list
25
+ /security allowed-sites add tradingview.com
26
+ /security allowed-sites remove yahoo.com
27
+ """
28
+
29
+ def __init__(self, after_cmd_line=None, shell_state=None):
30
+ super().__init__(after_cmd_line=after_cmd_line, shell_state=shell_state)
31
+ self.subcommands = {
32
+ "allowed-sites": SecurityAllowedSitesCommand(after_cmd_line=after_cmd_line, shell_state=shell_state)
33
+ }
34
+
35
+ def run(self):
36
+ """Execute the security command."""
37
+ args = self.after_cmd_line.strip().split()
38
+
39
+ if not args:
40
+ print(self.get_usage())
41
+ return
42
+
43
+ subcommand = args[0].lower()
44
+ if subcommand in self.subcommands:
45
+ # Pass the remaining args to the subcommand
46
+ remaining_args = " ".join(args[1:]) if len(args) > 1 else ""
47
+ self.subcommands[subcommand].after_cmd_line = remaining_args
48
+ self.subcommands[subcommand].run()
49
+ else:
50
+ print(f"Error: Unknown security subcommand '{subcommand}'")
51
+ print(self.get_usage())
@@ -0,0 +1,45 @@
1
+ """
2
+ CLI command to list available and loaded plugins.
3
+ """
4
+
5
+ import argparse
6
+ from typing import List, Dict, Any
7
+ from janito.plugins.discovery import list_available_plugins, discover_plugins
8
+ import os
9
+ from janito.plugins.manager import PluginManager
10
+
11
+
12
+ def handle_list_plugins(args: argparse.Namespace) -> None:
13
+ """List plugins command handler."""
14
+
15
+ if getattr(args, 'list_plugins_available', False):
16
+ # List available plugins
17
+ available = list_available_plugins()
18
+ if available:
19
+ print("Available plugins:")
20
+ for plugin in available:
21
+ print(f" - {plugin}")
22
+ else:
23
+ print("No plugins found in search paths")
24
+ print("Search paths:")
25
+ print(f" - {os.getcwd()}/plugins")
26
+ print(f" - {os.path.expanduser('~')}/.janito/plugins")
27
+ else:
28
+ # List loaded plugins
29
+ manager = PluginManager()
30
+ loaded = manager.list_plugins()
31
+
32
+ if loaded:
33
+ print("Loaded plugins:")
34
+ for plugin_name in loaded:
35
+ metadata = manager.get_plugin_metadata(plugin_name)
36
+ if metadata:
37
+ print(f" - {metadata.name} v{metadata.version}")
38
+ print(f" {metadata.description}")
39
+ if metadata.author:
40
+ print(f" Author: {metadata.author}")
41
+ print()
42
+ else:
43
+ print("No plugins loaded")
44
+
45
+