janito 2.1.0__py3-none-any.whl → 2.2.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 (37) hide show
  1. janito/__init__.py +1 -1
  2. janito/cli/chat_mode/shell/commands/__init__.py +0 -2
  3. janito/cli/chat_mode/shell/input_history.py +2 -2
  4. janito/cli/chat_mode/toolbar.py +2 -1
  5. janito/cli/config.py +1 -2
  6. janito/cli/core/runner.py +148 -141
  7. janito/cli/main_cli.py +8 -0
  8. janito/drivers/azure_openai/driver.py +21 -2
  9. janito/llm/provider.py +9 -0
  10. janito/provider_registry.py +158 -152
  11. janito/providers/azure_openai/provider.py +20 -0
  12. janito/providers/deepseek/model_info.py +2 -2
  13. janito/providers/deepseek/provider.py +1 -1
  14. janito/providers/openai/model_info.py +0 -11
  15. janito/providers/openai/provider.py +1 -1
  16. janito/providers/registry.py +26 -26
  17. janito/tools/__init__.py +6 -2
  18. janito/tools/adapters/local/__init__.py +62 -57
  19. janito/tools/adapters/local/adapter.py +5 -1
  20. janito/tools/adapters/local/ask_user.py +2 -15
  21. janito/tools/adapters/local/find_files.py +0 -2
  22. janito/tools/adapters/local/get_file_outline/core.py +68 -102
  23. janito/tools/adapters/local/get_file_outline/java_outline.py +40 -0
  24. janito/tools/adapters/local/open_html_in_browser.py +24 -29
  25. janito/tools/adapters/local/open_url.py +3 -2
  26. janito/tools/adapters/local/python_code_run.py +1 -0
  27. janito/tools/adapters/local/python_command_run.py +1 -0
  28. janito/tools/adapters/local/python_file_run.py +1 -0
  29. janito/tools/adapters/local/remove_file.py +5 -5
  30. janito/version.py +1 -1
  31. {janito-2.1.0.dist-info → janito-2.2.0.dist-info}/METADATA +21 -2
  32. {janito-2.1.0.dist-info → janito-2.2.0.dist-info}/RECORD +36 -35
  33. janito-2.2.0.dist-info/licenses/LICENSE +21 -0
  34. janito/cli/chat_mode/shell/commands/last.py +0 -137
  35. {janito-2.1.0.dist-info → janito-2.2.0.dist-info}/WHEEL +0 -0
  36. {janito-2.1.0.dist-info → janito-2.2.0.dist-info}/entry_points.txt +0 -0
  37. {janito-2.1.0.dist-info → janito-2.2.0.dist-info}/top_level.txt +0 -0
@@ -1,152 +1,158 @@
1
- """
2
- ProviderRegistry: Handles provider listing and selection logic for janito CLI.
3
- """
4
-
5
- from rich.table import Table
6
- from janito.cli.console import shared_console
7
- from janito.providers.registry import LLMProviderRegistry
8
- from janito.providers.provider_static_info import STATIC_PROVIDER_METADATA
9
- from janito.llm.auth import LLMAuthManager
10
- import sys
11
- from janito.exceptions import MissingProviderSelectionException
12
-
13
-
14
- class ProviderRegistry:
15
- def list_providers(self):
16
- """List all supported LLM providers as a table using rich, showing if auth is configured and supported model names."""
17
- providers = self._get_provider_names()
18
- table = self._create_table()
19
- rows = self._get_all_provider_rows(providers)
20
- self._add_rows_to_table(table, rows)
21
- self._print_table(table)
22
-
23
- def _get_provider_names(self):
24
- return list(STATIC_PROVIDER_METADATA.keys())
25
-
26
- def _create_table(self):
27
- table = Table(title="Supported LLM Providers")
28
- table.add_column("Provider", style="cyan")
29
- table.add_column("Maintainer", style="yellow", justify="center")
30
- table.add_column("Model Names", style="magenta")
31
- return table
32
-
33
- def _get_all_provider_rows(self, providers):
34
- rows = []
35
- for p in providers:
36
- info = self._get_provider_info(p)
37
- # info is (provider_name, maintainer, model_names, skip)
38
- if len(info) == 4 and info[3]:
39
- continue # skip providers flagged as not implemented
40
- rows.append(info[:3])
41
- rows.sort(key=self._maintainer_sort_key)
42
- return rows
43
-
44
- def _add_rows_to_table(self, table, rows):
45
- for idx, (p, maintainer, model_names) in enumerate(rows):
46
- table.add_row(p, maintainer, model_names)
47
- if idx != len(rows) - 1:
48
- table.add_section()
49
-
50
- def _print_table(self, table):
51
- shared_console.print(table)
52
-
53
- def _get_provider_info(self, provider_name):
54
- static_info = STATIC_PROVIDER_METADATA.get(provider_name, {})
55
- maintainer_val = static_info.get("maintainer", "-")
56
- maintainer = (
57
- "[red]🚨 Needs maintainer[/red]"
58
- if maintainer_val == "Needs maintainer"
59
- else f"👤 {maintainer_val}"
60
- )
61
- model_names = "-"
62
- unavailable_reason = None
63
- skip = False
64
- try:
65
- provider_class = LLMProviderRegistry.get(provider_name)
66
- creds = LLMAuthManager().get_credentials(provider_name)
67
- provider_instance = None
68
- instantiation_failed = False
69
- try:
70
- provider_instance = provider_class()
71
- except NotImplementedError:
72
- skip = True
73
- unavailable_reason = "Not implemented"
74
- model_names = f"[red]❌ Not implemented[/red]"
75
- except Exception as e:
76
- instantiation_failed = True
77
- unavailable_reason = (
78
- f"Unavailable (import error or missing dependency): {str(e)}"
79
- )
80
- model_names = f"[red]❌ {unavailable_reason}[/red]"
81
- if not instantiation_failed and provider_instance is not None:
82
- available, unavailable_reason = self._get_availability(
83
- provider_instance
84
- )
85
- if (
86
- not available
87
- and unavailable_reason
88
- and "not implemented" in str(unavailable_reason).lower()
89
- ):
90
- skip = True
91
- if available:
92
- model_names = self._get_model_names(provider_name)
93
- else:
94
- model_names = f"[red]❌ {unavailable_reason}[/red]"
95
- except Exception as import_error:
96
- model_names = f"[red]❌ Unavailable (cannot import provider module): {str(import_error)}[/red]"
97
- return (provider_name, maintainer, model_names, skip)
98
-
99
- def _get_availability(self, provider_instance):
100
- try:
101
- available = getattr(provider_instance, "available", True)
102
- unavailable_reason = getattr(provider_instance, "unavailable_reason", None)
103
- except Exception as e:
104
- available = False
105
- unavailable_reason = f"Error reading runtime availability: {str(e)}"
106
- return available, unavailable_reason
107
-
108
- def _get_model_names(self, provider_name):
109
- provider_to_specs = {
110
- "openai": "janito.providers.openai.model_info",
111
- "azure_openai": "janito.providers.azure_openai.model_info",
112
- "google": "janito.providers.google.model_info",
113
- "mistralai": "janito.providers.mistralai.model_info",
114
- "deepseek": "janito.providers.deepseek.model_info",
115
- }
116
- if provider_name in provider_to_specs:
117
- try:
118
- mod = __import__(
119
- provider_to_specs[provider_name], fromlist=["MODEL_SPECS"]
120
- )
121
- return ", ".join(mod.MODEL_SPECS.keys())
122
- except Exception:
123
- return "(Error)"
124
- return "-"
125
-
126
- def _maintainer_sort_key(self, row):
127
- maint = row[1]
128
- is_needs_maint = "Needs maintainer" in maint
129
- return (is_needs_maint, row[2] != "✅ Auth")
130
-
131
- def get_provider(self, provider_name):
132
- """Return the provider class for the given provider name."""
133
- from janito.providers.registry import LLMProviderRegistry
134
-
135
- if not provider_name:
136
- raise ValueError("Provider name must be specified.")
137
- return LLMProviderRegistry.get(provider_name)
138
-
139
- def get_instance(self, provider_name, config=None):
140
- """Return an instance of the provider for the given provider name, optionally passing a config object."""
141
- provider_class = self.get_provider(provider_name)
142
- if provider_class is None:
143
- raise ValueError(f"No provider class found for '{provider_name}'")
144
- if config is not None:
145
- return provider_class(config=config)
146
- return provider_class()
147
-
148
-
149
- # For backward compatibility
150
- def list_providers():
151
- """Legacy function for listing providers, now uses ProviderRegistry class."""
152
- ProviderRegistry().list_providers()
1
+ """
2
+ ProviderRegistry: Handles provider listing and selection logic for janito CLI.
3
+ """
4
+
5
+ from rich.table import Table
6
+ from janito.cli.console import shared_console
7
+ from janito.providers.registry import LLMProviderRegistry
8
+ from janito.providers.provider_static_info import STATIC_PROVIDER_METADATA
9
+ from janito.llm.auth import LLMAuthManager
10
+ import sys
11
+ from janito.exceptions import MissingProviderSelectionException
12
+
13
+
14
+ class ProviderRegistry:
15
+ def list_providers(self):
16
+ """List all supported LLM providers as a table using rich, showing if auth is configured and supported model names."""
17
+ providers = self._get_provider_names()
18
+ table = self._create_table()
19
+ rows = self._get_all_provider_rows(providers)
20
+ self._add_rows_to_table(table, rows)
21
+ self._print_table(table)
22
+
23
+ def _get_provider_names(self):
24
+ return list(STATIC_PROVIDER_METADATA.keys())
25
+
26
+ def _create_table(self):
27
+ table = Table(title="Supported LLM Providers")
28
+ table.add_column("Provider", style="cyan")
29
+ table.add_column("Maintainer", style="yellow", justify="center")
30
+ table.add_column("Model Names", style="magenta")
31
+ return table
32
+
33
+ def _get_all_provider_rows(self, providers):
34
+ rows = []
35
+ for p in providers:
36
+ info = self._get_provider_info(p)
37
+ # info is (provider_name, maintainer, model_names, skip)
38
+ if len(info) == 4 and info[3]:
39
+ continue # skip providers flagged as not implemented
40
+ rows.append(info[:3])
41
+ rows.sort(key=self._maintainer_sort_key)
42
+ return rows
43
+
44
+ def _add_rows_to_table(self, table, rows):
45
+ for idx, (p, maintainer, model_names) in enumerate(rows):
46
+ table.add_row(p, maintainer, model_names)
47
+ if idx != len(rows) - 1:
48
+ table.add_section()
49
+
50
+ def _print_table(self, table):
51
+ shared_console.print(table)
52
+
53
+ def _get_provider_info(self, provider_name):
54
+ static_info = STATIC_PROVIDER_METADATA.get(provider_name, {})
55
+ maintainer_val = static_info.get("maintainer", "-")
56
+ maintainer = (
57
+ "[red]🚨 Needs maintainer[/red]"
58
+ if maintainer_val == "Needs maintainer"
59
+ else f"👤 {maintainer_val}"
60
+ )
61
+ model_names = "-"
62
+ unavailable_reason = None
63
+ skip = False
64
+ try:
65
+ provider_class = LLMProviderRegistry.get(provider_name)
66
+ creds = LLMAuthManager().get_credentials(provider_name)
67
+ provider_instance = None
68
+ instantiation_failed = False
69
+ try:
70
+ provider_instance = provider_class()
71
+ except NotImplementedError:
72
+ skip = True
73
+ unavailable_reason = "Not implemented"
74
+ model_names = f"[red]❌ Not implemented[/red]"
75
+ except Exception as e:
76
+ instantiation_failed = True
77
+ unavailable_reason = (
78
+ f"Unavailable (import error or missing dependency): {str(e)}"
79
+ )
80
+ model_names = f"[red]❌ {unavailable_reason}[/red]"
81
+ if not instantiation_failed and provider_instance is not None:
82
+ available, unavailable_reason = self._get_availability(
83
+ provider_instance
84
+ )
85
+ if (
86
+ not available
87
+ and unavailable_reason
88
+ and "not implemented" in str(unavailable_reason).lower()
89
+ ):
90
+ skip = True
91
+ if available:
92
+ model_names = self._get_model_names(provider_name)
93
+ else:
94
+ model_names = f"[red]❌ {unavailable_reason}[/red]"
95
+ except Exception as import_error:
96
+ model_names = f"[red]❌ Unavailable (cannot import provider module): {str(import_error)}[/red]"
97
+ return (provider_name, maintainer, model_names, skip)
98
+
99
+ def _get_availability(self, provider_instance):
100
+ try:
101
+ available = getattr(provider_instance, "available", True)
102
+ unavailable_reason = getattr(provider_instance, "unavailable_reason", None)
103
+ except Exception as e:
104
+ available = False
105
+ unavailable_reason = f"Error reading runtime availability: {str(e)}"
106
+ return available, unavailable_reason
107
+
108
+ def _get_model_names(self, provider_name):
109
+ provider_to_specs = {
110
+ "openai": "janito.providers.openai.model_info",
111
+ "azure_openai": "janito.providers.azure_openai.model_info",
112
+ "google": "janito.providers.google.model_info",
113
+ "mistralai": "janito.providers.mistralai.model_info",
114
+ "deepseek": "janito.providers.deepseek.model_info",
115
+ }
116
+ if provider_name in provider_to_specs:
117
+ try:
118
+ mod = __import__(
119
+ provider_to_specs[provider_name], fromlist=["MODEL_SPECS"]
120
+ )
121
+ return ", ".join(mod.MODEL_SPECS.keys())
122
+ except Exception:
123
+ return "(Error)"
124
+ return "-"
125
+
126
+ def _maintainer_sort_key(self, row):
127
+ maint = row[1]
128
+ is_needs_maint = "Needs maintainer" in maint
129
+ return (is_needs_maint, row[2] != "✅ Auth")
130
+
131
+ def get_provider(self, provider_name):
132
+ """Return the provider class for the given provider name. Returns None if not found."""
133
+ from janito.providers.registry import LLMProviderRegistry
134
+
135
+ if not provider_name:
136
+ print("Error: Provider name must be specified.")
137
+ return None
138
+ provider_class = LLMProviderRegistry.get(provider_name)
139
+ if provider_class is None:
140
+ available = ', '.join(LLMProviderRegistry.list_providers())
141
+ print(f"Error: Provider '{provider_name}' is not recognized. Available providers: {available}.")
142
+ return None
143
+ return provider_class
144
+
145
+ def get_instance(self, provider_name, config=None):
146
+ """Return an instance of the provider for the given provider name, optionally passing a config object. Returns None if not found."""
147
+ provider_class = self.get_provider(provider_name)
148
+ if provider_class is None:
149
+ return None
150
+ if config is not None:
151
+ return provider_class(config=config)
152
+ return provider_class()
153
+
154
+
155
+ # For backward compatibility
156
+ def list_providers():
157
+ """Legacy function for listing providers, now uses ProviderRegistry class."""
158
+ ProviderRegistry().list_providers()
@@ -55,6 +55,21 @@ class AzureOpenAIProvider(LLMProvider):
55
55
  def unavailable_reason(self):
56
56
  return unavailable_reason
57
57
 
58
+ def is_model_available(self, model_name):
59
+ """
60
+ Returns True for any model name, since Azure deployments are user-defined and not enumerable in advance.
61
+ """
62
+ return True
63
+
64
+ def create_driver(self):
65
+ """
66
+ Creates and returns a new AzureOpenAIModelDriver instance with the provider's configuration and tools adapter.
67
+ """
68
+ driver = AzureOpenAIModelDriver(tools_adapter=self._tools_adapter)
69
+ driver.config = self._driver_config
70
+ # NOTE: The caller is responsible for calling driver.start() if background processing is needed.
71
+ return driver
72
+
58
73
  def create_agent(self, tools_adapter=None, agent_name: str = None, **kwargs):
59
74
  from janito.llm.agent import LLMAgent
60
75
  from janito.drivers.azure_openai.driver import AzureOpenAIModelDriver
@@ -63,6 +78,11 @@ class AzureOpenAIProvider(LLMProvider):
63
78
  driver = AzureOpenAIModelDriver(tools_adapter=tools_adapter)
64
79
  return LLMAgent(self, tools_adapter, agent_name=agent_name, **kwargs)
65
80
 
81
+ @property
82
+ def driver_config(self):
83
+ """Public, read-only access to the provider's LLMDriverConfig object."""
84
+ return self._driver_config
85
+
66
86
  def execute_tool(self, tool_name: str, event_bus, *args, **kwargs):
67
87
  # Use direct execution via adapter:
68
88
  self._tools_adapter.event_bus = event_bus
@@ -6,8 +6,8 @@ MODEL_SPECS = {
6
6
  "family": "deepseek",
7
7
  "default": True,
8
8
  },
9
- "deepseek-coder": {
10
- "description": "DeepSeek Coder Model (OpenAI-compatible)",
9
+ "deepseek-reasoner": {
10
+ "description": "DeepSeek Reasoner Model (OpenAI-compatible)",
11
11
  "context_window": 8192,
12
12
  "max_tokens": 4096,
13
13
  "family": "deepseek",
@@ -16,7 +16,7 @@ class DeepseekProvider(LLMProvider):
16
16
  name = "deepseek"
17
17
  maintainer = "Needs maintainer"
18
18
  MODEL_SPECS = MODEL_SPECS
19
- DEFAULT_MODEL = "deepseek-chat" # Options: deepseek-chat, deepseek-coder
19
+ DEFAULT_MODEL = "deepseek-chat" # Options: deepseek-chat, deepseek-reasoner
20
20
 
21
21
  def __init__(
22
22
  self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
@@ -111,17 +111,6 @@ MODEL_SPECS = {
111
111
  open="openai",
112
112
  driver="OpenAIModelDriver",
113
113
  ),
114
- "o4-mini-high": LLMModelInfo(
115
- name="o4-mini-high",
116
- context=200000,
117
- max_input=100000,
118
- max_cot="N/A",
119
- max_response=100000,
120
- thinking_supported=True,
121
- default_temp=0.2,
122
- open="openai",
123
- driver="OpenAIModelDriver",
124
- ),
125
114
  # duplicated gpt-4-turbo with minimal properties for distinction
126
115
  "gpt-4-turbo-alt": LLMModelInfo(
127
116
  name="gpt-4-turbo",
@@ -17,7 +17,7 @@ class OpenAIProvider(LLMProvider):
17
17
  maintainer = "João Pinto <lamego.pinto@gmail.com>"
18
18
  MODEL_SPECS = MODEL_SPECS
19
19
  DEFAULT_MODEL = (
20
- "gpt-4.1" # Options: gpt-4.1, gpt-4o, o3-mini, o4-mini, o4-mini-high
20
+ "gpt-4.1" # Options: gpt-4.1, gpt-4o, o3-mini, o4-mini,
21
21
  )
22
22
 
23
23
  def __init__(
@@ -1,26 +1,26 @@
1
- from typing import Type, Dict
2
- from janito.llm.provider import LLMProvider
3
-
4
-
5
- class LLMProviderRegistry:
6
- """
7
- Registry for LLM provider classes.
8
- """
9
-
10
- _providers: Dict[str, Type[LLMProvider]] = {}
11
-
12
- @classmethod
13
- def register(cls, name: str, provider_cls: Type[LLMProvider]):
14
- if name in cls._providers:
15
- raise ValueError(f"Provider '{name}' is already registered.")
16
- cls._providers[name] = provider_cls
17
-
18
- @classmethod
19
- def get(cls, name: str) -> Type[LLMProvider]:
20
- if name not in cls._providers:
21
- raise KeyError(f"Provider '{name}' is not registered.")
22
- return cls._providers[name]
23
-
24
- @classmethod
25
- def list_providers(cls):
26
- return list(cls._providers.keys())
1
+ from typing import Type, Dict
2
+ from janito.llm.provider import LLMProvider
3
+
4
+
5
+ class LLMProviderRegistry:
6
+ """
7
+ Registry for LLM provider classes.
8
+ """
9
+
10
+ _providers: Dict[str, Type[LLMProvider]] = {}
11
+
12
+ @classmethod
13
+ def register(cls, name: str, provider_cls: Type[LLMProvider]):
14
+ if name in cls._providers:
15
+ raise ValueError(f"Provider '{name}' is already registered.")
16
+ cls._providers[name] = provider_cls
17
+
18
+ @classmethod
19
+ def get(cls, name: str) -> Type[LLMProvider]:
20
+ if name not in cls._providers:
21
+ return None
22
+ return cls._providers[name]
23
+
24
+ @classmethod
25
+ def list_providers(cls):
26
+ return list(cls._providers.keys())
janito/tools/__init__.py CHANGED
@@ -4,9 +4,13 @@ from janito.tools.adapters.local import (
4
4
  )
5
5
 
6
6
 
7
- def get_local_tools_adapter():
8
-
7
+ def get_local_tools_adapter(workdir=None):
9
8
  # Use set_verbose_tools on the returned adapter to set verbosity as needed
9
+ if workdir is not None:
10
+ import os
11
+ if not os.path.exists(workdir):
12
+ os.makedirs(workdir, exist_ok=True)
13
+ return LocalToolsAdapter(workdir=workdir)
10
14
  return _internal_local_tools_adapter
11
15
 
12
16
 
@@ -1,57 +1,62 @@
1
- from .adapter import LocalToolsAdapter
2
-
3
- from .ask_user import AskUserTool
4
- from .copy_file import CopyFileTool
5
- from .create_directory import CreateDirectoryTool
6
- from .create_file import CreateFileTool
7
- from .fetch_url import FetchUrlTool
8
- from .find_files import FindFilesTool
9
- from .view_file import ViewFileTool
10
- from .move_file import MoveFileTool
11
- from .open_url import OpenUrlTool
12
- from .open_html_in_browser import OpenHtmlInBrowserTool
13
- from .python_code_run import PythonCodeRunTool
14
- from .python_command_run import PythonCommandRunTool
15
- from .python_file_run import PythonFileRunTool
16
- from .remove_directory import RemoveDirectoryTool
17
- from .remove_file import RemoveFileTool
18
- from .replace_text_in_file import ReplaceTextInFileTool
19
- from .run_bash_command import RunBashCommandTool
20
- from .run_powershell_command import RunPowershellCommandTool
21
- from .get_file_outline.core import GetFileOutlineTool
22
- from .get_file_outline.search_outline import SearchOutlineTool
23
- from .search_text.core import SearchTextTool
24
- from .validate_file_syntax.core import ValidateFileSyntaxTool
25
-
26
- # Singleton tools adapter with all standard tools registered
27
- local_tools_adapter = LocalToolsAdapter()
28
-
29
- # Register tools
30
- for tool_class in [
31
- AskUserTool,
32
- CopyFileTool,
33
- CreateDirectoryTool,
34
- CreateFileTool,
35
- FetchUrlTool,
36
- FindFilesTool,
37
- ViewFileTool,
38
- MoveFileTool,
39
- OpenUrlTool,
40
- OpenHtmlInBrowserTool,
41
- PythonCodeRunTool,
42
- PythonCommandRunTool,
43
- PythonFileRunTool,
44
- RemoveDirectoryTool,
45
- RemoveFileTool,
46
- ReplaceTextInFileTool,
47
- RunBashCommandTool,
48
- RunPowershellCommandTool,
49
- GetFileOutlineTool,
50
- SearchOutlineTool,
51
- SearchTextTool,
52
- ValidateFileSyntaxTool,
53
- ]:
54
- local_tools_adapter.register_tool(tool_class)
55
-
56
- # DEBUG: Print registered tools at startup
57
-
1
+ from .adapter import LocalToolsAdapter
2
+
3
+ from .ask_user import AskUserTool
4
+ from .copy_file import CopyFileTool
5
+ from .create_directory import CreateDirectoryTool
6
+ from .create_file import CreateFileTool
7
+ from .fetch_url import FetchUrlTool
8
+ from .find_files import FindFilesTool
9
+ from .view_file import ViewFileTool
10
+ from .move_file import MoveFileTool
11
+ from .open_url import OpenUrlTool
12
+ from .open_html_in_browser import OpenHtmlInBrowserTool
13
+ from .python_code_run import PythonCodeRunTool
14
+ from .python_command_run import PythonCommandRunTool
15
+ from .python_file_run import PythonFileRunTool
16
+ from .remove_directory import RemoveDirectoryTool
17
+ from .remove_file import RemoveFileTool
18
+ from .replace_text_in_file import ReplaceTextInFileTool
19
+ from .run_bash_command import RunBashCommandTool
20
+ from .run_powershell_command import RunPowershellCommandTool
21
+ from .get_file_outline.core import GetFileOutlineTool
22
+ from .get_file_outline.search_outline import SearchOutlineTool
23
+ from .search_text.core import SearchTextTool
24
+ from .validate_file_syntax.core import ValidateFileSyntaxTool
25
+
26
+ # Singleton tools adapter with all standard tools registered
27
+ import os
28
+ local_tools_adapter = LocalToolsAdapter(workdir=os.getcwd())
29
+
30
+ def get_local_tools_adapter(workdir=None):
31
+ import os
32
+ return LocalToolsAdapter(workdir=workdir or os.getcwd())
33
+
34
+ # Register tools
35
+ for tool_class in [
36
+ AskUserTool,
37
+ CopyFileTool,
38
+ CreateDirectoryTool,
39
+ CreateFileTool,
40
+ FetchUrlTool,
41
+ FindFilesTool,
42
+ ViewFileTool,
43
+ MoveFileTool,
44
+ OpenUrlTool,
45
+ OpenHtmlInBrowserTool,
46
+ PythonCodeRunTool,
47
+ PythonCommandRunTool,
48
+ PythonFileRunTool,
49
+ RemoveDirectoryTool,
50
+ RemoveFileTool,
51
+ ReplaceTextInFileTool,
52
+ RunBashCommandTool,
53
+ RunPowershellCommandTool,
54
+ GetFileOutlineTool,
55
+ SearchOutlineTool,
56
+ SearchTextTool,
57
+ ValidateFileSyntaxTool,
58
+ ]:
59
+ local_tools_adapter.register_tool(tool_class)
60
+
61
+ # DEBUG: Print registered tools at startup
62
+
@@ -15,9 +15,13 @@ class LocalToolsAdapter(ToolsAdapter):
15
15
  Handles registration, lookup, enabling/disabling, listing, and now, tool execution (merged from executor).
16
16
  """
17
17
 
18
- def __init__(self, tools=None, event_bus=None, allowed_tools=None):
18
+ def __init__(self, tools=None, event_bus=None, allowed_tools=None, workdir=None):
19
19
  super().__init__(tools=tools, event_bus=event_bus, allowed_tools=allowed_tools)
20
20
  self._tools: Dict[str, Dict[str, Any]] = {}
21
+ self.workdir = workdir
22
+ if self.workdir:
23
+ import os
24
+ os.chdir(self.workdir)
21
25
  if tools:
22
26
  for tool in tools:
23
27
  self.register_tool(tool)
@@ -43,26 +43,13 @@ class AskUserTool(ToolBase):
43
43
  def _(event):
44
44
  pass
45
45
 
46
- # F12 instruction rotation
47
- _f12_instructions = [
48
- tr("proceed"),
49
- tr("go ahead"),
50
- tr("continue"),
51
- tr("next"),
52
- tr("okay"),
53
- ]
54
- _f12_index = {"value": 0}
55
-
56
46
  @bindings.add("f12")
57
47
  def _(event):
58
- """When F12 is pressed, rotate through a set of short instructions."""
59
48
  buf = event.app.current_buffer
60
- idx = _f12_index["value"]
61
- buf.text = _f12_instructions[idx]
49
+ buf.text = "Do It"
62
50
  buf.validate_and_handle()
63
- _f12_index["value"] = (idx + 1) % len(_f12_instructions)
64
51
 
65
- # Use shared CLI styles
52
+ # Use shared CLI styles
66
53
 
67
54
  # prompt_style contains the prompt area and input background
68
55
  # toolbar_style contains the bottom-toolbar styling
@@ -21,12 +21,10 @@ class FindFilesTool(ToolBase):
21
21
  - If the pattern ends with '/' or '\', only matching directory names (with trailing slash) are returned, not the files within those directories. For example, pattern '*/' will return only directories at the specified depth.
22
22
  max_depth (int, optional): Maximum directory depth to search. If None, unlimited recursion. If 0, only the top-level directory. If 1, only the root directory (matches 'find . -maxdepth 1').
23
23
  include_gitignored (bool, optional): If True, includes files/directories ignored by .gitignore. Defaults to False.
24
- max_results (int, optional): Maximum number of results to return. 0 means no limit (default).
25
24
  Returns:
26
25
  str: Newline-separated list of matching file paths. Example:
27
26
  "/path/to/file1.py\n/path/to/file2.py"
28
27
  "Warning: Empty file pattern provided. Operation skipped."
29
- If max_results is reached, appends a note to the output.
30
28
  """
31
29
 
32
30
  tool_name = "find_files"