janito 2.6.1__py3-none-any.whl → 2.7.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 (33) hide show
  1. janito/__init__.py +7 -7
  2. janito/__main__.py +5 -5
  3. janito/_version.py +58 -58
  4. janito/agent/setup_agent.py +240 -240
  5. janito/agent/templates/profiles/{system_prompt_template_software developer.txt.j2 → system_prompt_template_plain_software_developer.txt.j2} +39 -39
  6. janito/cli/__init__.py +10 -10
  7. janito/cli/chat_mode/bindings.py +38 -38
  8. janito/cli/chat_mode/chat_entry.py +23 -23
  9. janito/cli/chat_mode/prompt_style.py +25 -25
  10. janito/cli/chat_mode/script_runner.py +154 -154
  11. janito/cli/chat_mode/session.py +1 -1
  12. janito/cli/chat_mode/session_profile_select.py +5 -5
  13. janito/cli/single_shot_mode/handler.py +95 -95
  14. janito/drivers/driver_registry.py +27 -27
  15. janito/drivers/openai/driver.py +435 -435
  16. janito/provider_registry.py +178 -178
  17. janito/providers/__init__.py +1 -0
  18. janito/providers/anthropic/model_info.py +41 -41
  19. janito/providers/anthropic/provider.py +80 -80
  20. janito/providers/moonshotai/__init__.py +1 -0
  21. janito/providers/moonshotai/model_info.py +15 -0
  22. janito/providers/moonshotai/provider.py +82 -0
  23. janito/providers/openai/model_info.py +1 -0
  24. janito/providers/provider_static_info.py +21 -18
  25. janito/tools/adapters/local/__init__.py +66 -66
  26. janito/tools/adapters/local/move_file.py +3 -3
  27. janito/tools/adapters/local/read_files.py +40 -40
  28. {janito-2.6.1.dist-info → janito-2.7.0.dist-info}/METADATA +419 -417
  29. {janito-2.6.1.dist-info → janito-2.7.0.dist-info}/RECORD +33 -30
  30. {janito-2.6.1.dist-info → janito-2.7.0.dist-info}/WHEEL +0 -0
  31. {janito-2.6.1.dist-info → janito-2.7.0.dist-info}/entry_points.txt +0 -0
  32. {janito-2.6.1.dist-info → janito-2.7.0.dist-info}/licenses/LICENSE +0 -0
  33. {janito-2.6.1.dist-info → janito-2.7.0.dist-info}/top_level.txt +0 -0
@@ -1,39 +1,39 @@
1
- You are: software developer
2
-
3
- {# Improves tool selection and platform specific constrains, eg, path format, C:\ vs /path #}
4
- {% if allowed_permissions and 'x' in allowed_permissions %}
5
- You will be using the following environment:
6
- Platform: {{ platform }}
7
- Shell/Environment: {{ shell_info }}
8
- {% endif %}
9
-
10
-
11
- {% if allowed_permissions and 'r' in allowed_permissions %}
12
- Before answering map the questions to artifacts found in the current directory - the current project.
13
- {% endif %}
14
-
15
- Respond according to the following guidelines:
16
- {% if allowed_permissions %}
17
- - Before using the namespace tools provide a short reason
18
- {% endif %}
19
- {% if allowed_permissions and 'r' in allowed_permissions %}
20
- {# Exploratory hint #}
21
- - Before answering to the user, explore the content related to the question
22
- {# Reduces chunking roundtip #}
23
- - When exploring full files content, provide empty range to read the entire files instead of chunked reads
24
- {% endif %}
25
- {% if allowed_permissions and 'w' in allowed_permissions %}
26
- {# Reduce unrequest code verbosity overhead #}
27
- - Use the namespace functions to deliver the code changes instead of showing the code.
28
- {# Drive edit mode, place holders critical as shown to be crucial to avoid corruption with code placeholders #}
29
- - Prefer making localized edits using string replacements. If the required change is extensive, replace the entire file instead, provide full content without placeholders.
30
- {# Without this, the LLM choses to create files from a literal interpretation of the purpose and intention #}
31
- - Before creating files search the code for the location related to the file purpose
32
- {# This will trigger a search for the old names/locations to be updates #}
33
- - After moving, removing or renaming functions or classes to different modules, update all imports, references, tests, and documentation to reflect the new locations, then verify functionality.
34
- {# Keeping docstrings update is key to have semanatic match between prompts and code #}
35
- - Once development or updates are finished, ensure that new or updated packages, modules, functions are properly documented.
36
- {# Trying to prevent surrogates generation, found this frequently in gpt4.1/windows #}
37
- - 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.
38
- {% endif %}
39
-
1
+ You are: software developer
2
+
3
+ {# Improves tool selection and platform specific constrains, eg, path format, C:\ vs /path #}
4
+ {% if allowed_permissions and 'x' in allowed_permissions %}
5
+ You will be using the following environment:
6
+ Platform: {{ platform }}
7
+ Shell/Environment: {{ shell_info }}
8
+ {% endif %}
9
+
10
+
11
+ {% if allowed_permissions and 'r' in allowed_permissions %}
12
+ Before answering map the questions to artifacts found in the current directory - the current project.
13
+ {% endif %}
14
+
15
+ Respond according to the following guidelines:
16
+ {% if allowed_permissions %}
17
+ - Before using the namespace tools provide a short reason
18
+ {% endif %}
19
+ {% if allowed_permissions and 'r' in allowed_permissions %}
20
+ {# Exploratory hint #}
21
+ - Before answering to the user, explore the content related to the question
22
+ {# Reduces chunking roundtip #}
23
+ - When exploring full files content, provide empty range to read the entire files instead of chunked reads
24
+ {% endif %}
25
+ {% if allowed_permissions and 'w' in allowed_permissions %}
26
+ {# Reduce unrequest code verbosity overhead #}
27
+ - Use the namespace functions to deliver the code changes instead of showing the code.
28
+ {# Drive edit mode, place holders critical as shown to be crucial to avoid corruption with code placeholders #}
29
+ - Prefer making localized edits using string replacements. If the required change is extensive, replace the entire file instead, provide full content without placeholders.
30
+ {# Without this, the LLM choses to create files from a literal interpretation of the purpose and intention #}
31
+ - Before creating files search the code for the location related to the file purpose
32
+ {# This will trigger a search for the old names/locations to be updates #}
33
+ - After moving, removing or renaming functions or classes to different modules, update all imports, references, tests, and documentation to reflect the new locations, then verify functionality.
34
+ {# Keeping docstrings update is key to have semanatic match between prompts and code #}
35
+ - Once development or updates are finished, ensure that new or updated packages, modules, functions are properly documented.
36
+ {# Trying to prevent surrogates generation, found this frequently in gpt4.1/windows #}
37
+ - 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.
38
+ {% endif %}
39
+
janito/cli/__init__.py CHANGED
@@ -1,10 +1,10 @@
1
- # janito.cli package
2
- from .utils import format_tokens, format_generation_time
3
-
4
- __all__ = [
5
- "setup_provider",
6
- "setup_agent",
7
- "format_tokens",
8
- "format_generation_time",
9
- ]
10
-
1
+ # janito.cli package
2
+ from .utils import format_tokens, format_generation_time
3
+
4
+ __all__ = [
5
+ "setup_provider",
6
+ "setup_agent",
7
+ "format_tokens",
8
+ "format_generation_time",
9
+ ]
10
+
@@ -1,38 +1,38 @@
1
- """
2
- Key bindings for Janito Chat CLI.
3
- """
4
-
5
- from prompt_toolkit.key_binding import KeyBindings
6
- from janito.tools.permissions import get_global_allowed_permissions
7
-
8
- class KeyBindingsFactory:
9
- @staticmethod
10
- def create():
11
- bindings = KeyBindings()
12
-
13
- @bindings.add("c-y")
14
- def _(event):
15
- buf = event.app.current_buffer
16
- buf.text = "Yes"
17
- buf.validate_and_handle()
18
-
19
- @bindings.add("c-n")
20
- def _(event):
21
- buf = event.app.current_buffer
22
- buf.text = "No"
23
- buf.validate_and_handle()
24
-
25
- @bindings.add("f2")
26
- def _(event):
27
- buf = event.app.current_buffer
28
- buf.text = "/restart"
29
- buf.validate_and_handle()
30
-
31
- @bindings.add("f12")
32
- def _(event):
33
- buf = event.app.current_buffer
34
- buf.text = "Do It"
35
- buf.validate_and_handle()
36
-
37
- return bindings
38
-
1
+ """
2
+ Key bindings for Janito Chat CLI.
3
+ """
4
+
5
+ from prompt_toolkit.key_binding import KeyBindings
6
+ from janito.tools.permissions import get_global_allowed_permissions
7
+
8
+ class KeyBindingsFactory:
9
+ @staticmethod
10
+ def create():
11
+ bindings = KeyBindings()
12
+
13
+ @bindings.add("c-y")
14
+ def _(event):
15
+ buf = event.app.current_buffer
16
+ buf.text = "Yes"
17
+ buf.validate_and_handle()
18
+
19
+ @bindings.add("c-n")
20
+ def _(event):
21
+ buf = event.app.current_buffer
22
+ buf.text = "No"
23
+ buf.validate_and_handle()
24
+
25
+ @bindings.add("f2")
26
+ def _(event):
27
+ buf = event.app.current_buffer
28
+ buf.text = "/restart"
29
+ buf.validate_and_handle()
30
+
31
+ @bindings.add("f12")
32
+ def _(event):
33
+ buf = event.app.current_buffer
34
+ buf.text = "Do It"
35
+ buf.validate_and_handle()
36
+
37
+ return bindings
38
+
@@ -1,23 +1,23 @@
1
- """
2
- Main entry point for the Janito Chat CLI.
3
- Handles the interactive chat loop and session startup.
4
- """
5
-
6
- from rich.console import Console
7
- from prompt_toolkit.formatted_text import HTML
8
- from janito.cli.chat_mode.session import ChatSession
9
-
10
-
11
- def main(args=None):
12
- console = Console()
13
- console.clear()
14
- from janito.version import __version__
15
-
16
-
17
- session = ChatSession(console, args=args)
18
- session.run()
19
-
20
-
21
- if __name__ == "__main__":
22
- main()
23
-
1
+ """
2
+ Main entry point for the Janito Chat CLI.
3
+ Handles the interactive chat loop and session startup.
4
+ """
5
+
6
+ from rich.console import Console
7
+ from prompt_toolkit.formatted_text import HTML
8
+ from janito.cli.chat_mode.session import ChatSession
9
+
10
+
11
+ def main(args=None):
12
+ console = Console()
13
+ console.clear()
14
+ from janito.version import __version__
15
+
16
+
17
+ session = ChatSession(console, args=args)
18
+ session.run()
19
+
20
+
21
+ if __name__ == "__main__":
22
+ main()
23
+
@@ -1,25 +1,25 @@
1
- from prompt_toolkit.styles import Style
2
-
3
- chat_shell_style = Style.from_dict(
4
- {
5
- "prompt": "bg:#2323af #ffffff bold",
6
- "": "bg:#005fdd #ffffff", # blue background for input area
7
- "bottom-toolbar": "fg:#2323af bg:yellow",
8
- "key-label": "bg:#ff9500 fg:#232323 bold",
9
- "provider": "fg:#117fbf",
10
- "model": "fg:#1f5fa9",
11
- "role": "fg:#e87c32 bold",
12
- "msg_count": "fg:#5454dd",
13
- "session_id": "fg:#704ab9",
14
- "tokens_total": "fg:#a022c7",
15
- "tokens_in": "fg:#00af5f",
16
- "tokens_out": "fg:#01814a",
17
- "max-tokens": "fg:#888888",
18
-
19
-
20
- "key-toggle-on": "bg:#ffd700 fg:#232323 bold",
21
- "key-toggle-off": "bg:#444444 fg:#ffffff bold",
22
- "cmd-label": "bg:#ff9500 fg:#232323 bold",
23
- }
24
- )
25
-
1
+ from prompt_toolkit.styles import Style
2
+
3
+ chat_shell_style = Style.from_dict(
4
+ {
5
+ "prompt": "bg:#2323af #ffffff bold",
6
+ "": "bg:#005fdd #ffffff", # blue background for input area
7
+ "bottom-toolbar": "fg:#2323af bg:yellow",
8
+ "key-label": "bg:#ff9500 fg:#232323 bold",
9
+ "provider": "fg:#117fbf",
10
+ "model": "fg:#1f5fa9",
11
+ "role": "fg:#e87c32 bold",
12
+ "msg_count": "fg:#5454dd",
13
+ "session_id": "fg:#704ab9",
14
+ "tokens_total": "fg:#a022c7",
15
+ "tokens_in": "fg:#00af5f",
16
+ "tokens_out": "fg:#01814a",
17
+ "max-tokens": "fg:#888888",
18
+
19
+
20
+ "key-toggle-on": "bg:#ffd700 fg:#232323 bold",
21
+ "key-toggle-off": "bg:#444444 fg:#ffffff bold",
22
+ "cmd-label": "bg:#ff9500 fg:#232323 bold",
23
+ }
24
+ )
25
+
@@ -1,154 +1,154 @@
1
- """
2
- Scripted runner for Janito chat mode.
3
-
4
- This utility allows you to execute the interactive ``ChatSession`` logic with
5
- an *in-memory* list of user inputs, making it much easier to write automated
6
- unit or integration tests for the chat CLI without resorting to fragile
7
- pseudo-terminal tricks.
8
-
9
- The runner monkey-patches the private ``_handle_input`` method so that the
10
- chat loop thinks it is receiving interactive input, while in reality the
11
- values come from the provided list. All output is captured through a
12
- ``rich.console.Console`` instance configured with ``record=True`` so the test
13
- can later inspect the rendered text.
14
-
15
- Typical usage
16
- -------------
17
- >>> from janito.cli.chat_mode.script_runner import ChatScriptRunner
18
- >>> inputs = ["Hello!", "/exit"]
19
- >>> runner = ChatScriptRunner(inputs)
20
- >>> transcript = runner.run()
21
- >>> assert "Hello!" in transcript
22
-
23
- The ``ChatScriptRunner`` purposefully replaces the internal call to the agent
24
- with a real agent call by default. If you want to use a stub, you must modify the runner implementation.
25
- """
26
- from __future__ import annotations
27
-
28
- from types import MethodType
29
- from typing import List, Optional
30
-
31
- from rich.console import Console
32
-
33
- from janito.cli.chat_mode.session import ChatSession
34
- from janito.provider_registry import ProviderRegistry
35
- from janito.llm.driver_config import LLMDriverConfig
36
-
37
- __all__ = ["ChatScriptRunner"]
38
-
39
-
40
- auth_warning = (
41
- "[yellow]ChatScriptRunner is executing in stubbed-agent mode; no calls to an "
42
- "external LLM provider will be made.[/yellow]"
43
- )
44
-
45
-
46
-
47
- class ChatScriptRunner:
48
- """Run a **ChatSession** non-interactively using a predefined set of inputs."""
49
-
50
- def __init__(
51
- self,
52
- inputs: List[str],
53
- *,
54
- console: Optional[Console] = None,
55
- provider: str = "openai",
56
- model: str = "gpt-4.1",
57
- use_real_agent: bool = True,
58
- **chat_session_kwargs,
59
- ) -> None:
60
- """Create the runner.
61
-
62
- Parameters
63
- ----------
64
- inputs:
65
- Ordered list of strings that will be fed to the chat loop.
66
- console:
67
- Optional *rich* console. If *None*, a new one is created with
68
- *record=True* so that output can later be retrieved through
69
- :py:meth:`rich.console.Console.export_text`.
70
- use_real_agent:
71
- chat_session_kwargs:
72
- Extra keyword arguments forwarded to :class:`janito.cli.chat_mode.session.ChatSession`.
73
- """
74
- self._input_queue = list(inputs)
75
- self.console = console or Console(record=True)
76
- self.provider = provider
77
- self.model = model
78
- self.use_real_agent = use_real_agent
79
- # Ensure we always pass a non-interactive *args* namespace so that the
80
- # normal ChatSession logic skips the Questionary profile prompt which
81
- # is incompatible with headless test runs.
82
- if "args" not in chat_session_kwargs or chat_session_kwargs["args"] is None:
83
- from types import SimpleNamespace
84
- chat_session_kwargs["args"] = SimpleNamespace(
85
- profile="developer",
86
- provider=self.provider,
87
- model=self.model,
88
- )
89
-
90
- # Create the ChatSession instance **after** we monkey-patch methods that rely on
91
- # prompt-toolkit so that no attempt is made to instantiate terminal UIs in
92
- # a headless environment like CI.
93
-
94
- # 1) Patch *ChatSession._create_prompt_session* to do nothing – the
95
- # interactive session object is irrelevant for scripted runs.
96
- from types import MethodType as _MT
97
- if "_original_create_prompt_session" not in ChatSession.__dict__:
98
- ChatSession._original_create_prompt_session = ChatSession._create_prompt_session # type: ignore[attr-defined]
99
- ChatSession._create_prompt_session = _MT(lambda _self: None, ChatSession) # type: ignore[method-assign]
100
-
101
- # Resolve provider instance now so that ChatSession uses a ready agent
102
- provider_instance = ProviderRegistry().get_instance(self.provider)
103
- if provider_instance is None:
104
- raise RuntimeError(f"Provider '{self.provider}' is not available on this system.")
105
- driver_config = LLMDriverConfig(model=self.model)
106
- chat_session_kwargs.setdefault("provider_instance", provider_instance)
107
- chat_session_kwargs.setdefault("llm_driver_config", driver_config)
108
-
109
- self.chat_session = ChatSession(console=self.console, **chat_session_kwargs)
110
-
111
-
112
- # Monkey-patch the *ChatSession._handle_input* method so that it pops
113
- # from our in-memory queue instead of reading from stdin.
114
- def _script_handle_input(this: ChatSession, _prompt_session_unused): # noqa: D401
115
- if not self._input_queue:
116
- # Signal normal shutdown
117
- this._handle_exit()
118
- return None
119
- return self._input_queue.pop(0)
120
-
121
- # Bind the method to the *chat_session* instance.
122
- self.chat_session._handle_input = MethodType( # type: ignore[assignment]
123
- _script_handle_input, self.chat_session
124
- )
125
-
126
- # ---------------------------------------------------------------------
127
- # Public helpers
128
- # ---------------------------------------------------------------------
129
- def run(self) -> str:
130
- """Execute the chat session and return the captured transcript."""
131
- self.chat_session.run()
132
- return self.console.export_text()
133
-
134
- # ---------------------------------------------------------------------
135
- # Helpers to introspect results
136
- # ---------------------------------------------------------------------
137
- def get_history(self):
138
- """Return the structured conversation history produced by the LLM."""
139
- try:
140
- return self.chat_session.shell_state.conversation_history.get_history()
141
- except Exception:
142
- return []
143
-
144
- def get_last_response(self) -> str | None:
145
- """Return the *assistant* content of the last message, if any."""
146
- history = self.get_history()
147
- for message in reversed(history):
148
- if message.get("role") == "assistant":
149
- return message.get("content")
150
- return None
151
-
152
- # Convenience alias so tests can simply call *runner()*
153
- __call__ = run
154
-
1
+ """
2
+ Scripted runner for Janito chat mode.
3
+
4
+ This utility allows you to execute the interactive ``ChatSession`` logic with
5
+ an *in-memory* list of user inputs, making it much easier to write automated
6
+ unit or integration tests for the chat CLI without resorting to fragile
7
+ pseudo-terminal tricks.
8
+
9
+ The runner monkey-patches the private ``_handle_input`` method so that the
10
+ chat loop thinks it is receiving interactive input, while in reality the
11
+ values come from the provided list. All output is captured through a
12
+ ``rich.console.Console`` instance configured with ``record=True`` so the test
13
+ can later inspect the rendered text.
14
+
15
+ Typical usage
16
+ -------------
17
+ >>> from janito.cli.chat_mode.script_runner import ChatScriptRunner
18
+ >>> inputs = ["Hello!", "/exit"]
19
+ >>> runner = ChatScriptRunner(inputs)
20
+ >>> transcript = runner.run()
21
+ >>> assert "Hello!" in transcript
22
+
23
+ The ``ChatScriptRunner`` purposefully replaces the internal call to the agent
24
+ with a real agent call by default. If you want to use a stub, you must modify the runner implementation.
25
+ """
26
+ from __future__ import annotations
27
+
28
+ from types import MethodType
29
+ from typing import List, Optional
30
+
31
+ from rich.console import Console
32
+
33
+ from janito.cli.chat_mode.session import ChatSession
34
+ from janito.provider_registry import ProviderRegistry
35
+ from janito.llm.driver_config import LLMDriverConfig
36
+
37
+ __all__ = ["ChatScriptRunner"]
38
+
39
+
40
+ auth_warning = (
41
+ "[yellow]ChatScriptRunner is executing in stubbed-agent mode; no calls to an "
42
+ "external LLM provider will be made.[/yellow]"
43
+ )
44
+
45
+
46
+
47
+ class ChatScriptRunner:
48
+ """Run a **ChatSession** non-interactively using a predefined set of inputs."""
49
+
50
+ def __init__(
51
+ self,
52
+ inputs: List[str],
53
+ *,
54
+ console: Optional[Console] = None,
55
+ provider: str = "openai",
56
+ model: str = "gpt-4.1",
57
+ use_real_agent: bool = True,
58
+ **chat_session_kwargs,
59
+ ) -> None:
60
+ """Create the runner.
61
+
62
+ Parameters
63
+ ----------
64
+ inputs:
65
+ Ordered list of strings that will be fed to the chat loop.
66
+ console:
67
+ Optional *rich* console. If *None*, a new one is created with
68
+ *record=True* so that output can later be retrieved through
69
+ :py:meth:`rich.console.Console.export_text`.
70
+ use_real_agent:
71
+ chat_session_kwargs:
72
+ Extra keyword arguments forwarded to :class:`janito.cli.chat_mode.session.ChatSession`.
73
+ """
74
+ self._input_queue = list(inputs)
75
+ self.console = console or Console(record=True)
76
+ self.provider = provider
77
+ self.model = model
78
+ self.use_real_agent = use_real_agent
79
+ # Ensure we always pass a non-interactive *args* namespace so that the
80
+ # normal ChatSession logic skips the Questionary profile prompt which
81
+ # is incompatible with headless test runs.
82
+ if "args" not in chat_session_kwargs or chat_session_kwargs["args"] is None:
83
+ from types import SimpleNamespace
84
+ chat_session_kwargs["args"] = SimpleNamespace(
85
+ profile="developer",
86
+ provider=self.provider,
87
+ model=self.model,
88
+ )
89
+
90
+ # Create the ChatSession instance **after** we monkey-patch methods that rely on
91
+ # prompt-toolkit so that no attempt is made to instantiate terminal UIs in
92
+ # a headless environment like CI.
93
+
94
+ # 1) Patch *ChatSession._create_prompt_session* to do nothing – the
95
+ # interactive session object is irrelevant for scripted runs.
96
+ from types import MethodType as _MT
97
+ if "_original_create_prompt_session" not in ChatSession.__dict__:
98
+ ChatSession._original_create_prompt_session = ChatSession._create_prompt_session # type: ignore[attr-defined]
99
+ ChatSession._create_prompt_session = _MT(lambda _self: None, ChatSession) # type: ignore[method-assign]
100
+
101
+ # Resolve provider instance now so that ChatSession uses a ready agent
102
+ provider_instance = ProviderRegistry().get_instance(self.provider)
103
+ if provider_instance is None:
104
+ raise RuntimeError(f"Provider '{self.provider}' is not available on this system.")
105
+ driver_config = LLMDriverConfig(model=self.model)
106
+ chat_session_kwargs.setdefault("provider_instance", provider_instance)
107
+ chat_session_kwargs.setdefault("llm_driver_config", driver_config)
108
+
109
+ self.chat_session = ChatSession(console=self.console, **chat_session_kwargs)
110
+
111
+
112
+ # Monkey-patch the *ChatSession._handle_input* method so that it pops
113
+ # from our in-memory queue instead of reading from stdin.
114
+ def _script_handle_input(this: ChatSession, _prompt_session_unused): # noqa: D401
115
+ if not self._input_queue:
116
+ # Signal normal shutdown
117
+ this._handle_exit()
118
+ return None
119
+ return self._input_queue.pop(0)
120
+
121
+ # Bind the method to the *chat_session* instance.
122
+ self.chat_session._handle_input = MethodType( # type: ignore[assignment]
123
+ _script_handle_input, self.chat_session
124
+ )
125
+
126
+ # ---------------------------------------------------------------------
127
+ # Public helpers
128
+ # ---------------------------------------------------------------------
129
+ def run(self) -> str:
130
+ """Execute the chat session and return the captured transcript."""
131
+ self.chat_session.run()
132
+ return self.console.export_text()
133
+
134
+ # ---------------------------------------------------------------------
135
+ # Helpers to introspect results
136
+ # ---------------------------------------------------------------------
137
+ def get_history(self):
138
+ """Return the structured conversation history produced by the LLM."""
139
+ try:
140
+ return self.chat_session.shell_state.conversation_history.get_history()
141
+ except Exception:
142
+ return []
143
+
144
+ def get_last_response(self) -> str | None:
145
+ """Return the *assistant* content of the last message, if any."""
146
+ history = self.get_history()
147
+ for message in reversed(history):
148
+ if message.get("role") == "assistant":
149
+ return message.get("content")
150
+ return None
151
+
152
+ # Convenience alias so tests can simply call *runner()*
153
+ __call__ = run
154
+
@@ -99,7 +99,7 @@ class ChatSession:
99
99
  profile = "developer"
100
100
  else:
101
101
  profile = (
102
- "developer" if result == "software developer" else result
102
+ "developer" if result == "plain_software_developer" else result
103
103
  )
104
104
  except ImportError:
105
105
  profile = "helpful assistant"
@@ -90,7 +90,7 @@ def select_profile():
90
90
  choices = [
91
91
  "helpful assistant",
92
92
  "developer",
93
- "software developer",
93
+ "plain_software_developer",
94
94
  "using role...",
95
95
  "full custom system prompt..."
96
96
  ]
@@ -118,11 +118,11 @@ def select_profile():
118
118
  elif answer in user_profiles:
119
119
  # Return the content of the user profile as a custom system prompt
120
120
  return {"profile": None, "profile_system_prompt": user_profiles[answer]}
121
- elif answer == "software developer":
122
- # Return the content of the built-in software developer profile prompt
123
- with open("./janito/agent/templates/profiles/system_prompt_template_software_developer.txt.j2", "r", encoding="utf-8") as f:
121
+ elif answer == "plain_software_developer":
122
+ # Return the content of the built-in plain_software_developer profile prompt
123
+ with open("./janito/agent/templates/profiles/system_prompt_template_plain_software_developer.txt.j2", "r", encoding="utf-8") as f:
124
124
  prompt = f.read().strip()
125
- return {"profile": "software developer", "profile_system_prompt": prompt}
125
+ return {"profile": "plain_software_developer", "profile_system_prompt": prompt}
126
126
  return answer
127
127
 
128
128
  choices = [