janito 2.4.0__tar.gz → 2.5.1__tar.gz

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 (284) hide show
  1. janito-2.5.1/.vscode/settings.json +19 -0
  2. janito-2.5.1/CHANGELOG.md +103 -0
  3. {janito-2.4.0 → janito-2.5.1}/PKG-INFO +2 -15
  4. {janito-2.4.0 → janito-2.5.1}/README.md +1 -14
  5. janito-2.5.1/adding_mcp.txt +138 -0
  6. {janito-2.4.0 → janito-2.5.1}/janito/agent/setup_agent.py +4 -3
  7. janito-2.5.1/janito/agent/templates/profiles/system_prompt_template_assistant.txt.j2 +1 -0
  8. janito-2.5.1/janito/cli/chat_mode/script_runner.py +153 -0
  9. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/session.py +56 -36
  10. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/session_profile_select.py +1 -1
  11. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/__init__.py +6 -2
  12. janito-2.5.1/janito/cli/chat_mode/shell/commands/bang.py +36 -0
  13. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/conversation_restart.py +4 -28
  14. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/execute.py +1 -2
  15. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/help.py +1 -1
  16. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/prompt.py +1 -16
  17. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/read.py +1 -2
  18. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/role.py +2 -9
  19. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/write.py +1 -2
  20. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/input_history.py +1 -1
  21. janito-2.5.1/janito/cli/chat_mode/shell/session/manager.py +42 -0
  22. {janito-2.4.0 → janito-2.5.1}/janito/cli/core/runner.py +4 -4
  23. {janito-2.4.0 → janito-2.5.1}/janito/cli/main_cli.py +12 -5
  24. {janito-2.4.0 → janito-2.5.1}/janito/cli/prompt_setup.py +2 -2
  25. {janito-2.4.0 → janito-2.5.1}/janito/cli/rich_terminal_reporter.py +21 -6
  26. {janito-2.4.0 → janito-2.5.1}/janito/cli/single_shot_mode/handler.py +13 -7
  27. {janito-2.4.0 → janito-2.5.1}/janito/drivers/driver_registry.py +0 -2
  28. {janito-2.4.0 → janito-2.5.1}/janito/formatting_token.py +7 -6
  29. {janito-2.4.0 → janito-2.5.1}/janito/llm/agent.py +1 -1
  30. {janito-2.4.0 → janito-2.5.1}/janito/provider_registry.py +1 -1
  31. {janito-2.4.0 → janito-2.5.1}/janito/providers/__init__.py +0 -2
  32. {janito-2.4.0 → janito-2.5.1}/janito/providers/provider_static_info.py +0 -3
  33. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/core.py +1 -1
  34. {janito-2.4.0 → janito-2.5.1}/janito/tools/tool_base.py +0 -10
  35. {janito-2.4.0 → janito-2.5.1}/janito.egg-info/PKG-INFO +2 -15
  36. {janito-2.4.0 → janito-2.5.1}/janito.egg-info/SOURCES.txt +5 -4
  37. {janito-2.4.0 → janito-2.5.1}/pytest.ini +1 -0
  38. {janito-2.4.0 → janito-2.5.1}/tools/release.py +6 -0
  39. janito-2.4.0/CHANGELOG.md +0 -57
  40. janito-2.4.0/STRUCTURE.md +0 -14
  41. janito-2.4.0/TEST_STRATEGY.md +0 -0
  42. janito-2.4.0/benchmark/cli_basics.py +0 -4
  43. janito-2.4.0/janito/cli/chat_mode/shell/session/manager.py +0 -110
  44. janito-2.4.0/janito2.bak.zip +0 -0
  45. {janito-2.4.0 → janito-2.5.1}/.codespellrc +0 -0
  46. {janito-2.4.0 → janito-2.5.1}/.github/workflows/python-app.yml +0 -0
  47. {janito-2.4.0 → janito-2.5.1}/.gitignore +0 -0
  48. {janito-2.4.0 → janito-2.5.1}/.pre-commit-config.yaml +0 -0
  49. {janito-2.4.0 → janito-2.5.1}/.secrets.baseline +0 -0
  50. {janito-2.4.0 → janito-2.5.1}/LICENSE +0 -0
  51. {janito-2.4.0 → janito-2.5.1}/PROVIDERS.md +0 -0
  52. {janito-2.4.0 → janito-2.5.1}/README-dev.md +0 -0
  53. {janito-2.4.0 → janito-2.5.1}/docs/DIV.md +0 -0
  54. {janito-2.4.0 → janito-2.5.1}/docs/Interfaces.txt +0 -0
  55. {janito-2.4.0 → janito-2.5.1}/docs/TOOLBAR-STYLING.md +0 -0
  56. {janito-2.4.0 → janito-2.5.1}/docs/about/costs.md +0 -0
  57. {janito-2.4.0 → janito-2.5.1}/docs/about/vs-webchats.md +0 -0
  58. {janito-2.4.0 → janito-2.5.1}/docs/about/why.md +0 -0
  59. {janito-2.4.0 → janito-2.5.1}/docs/alternatives.md +0 -0
  60. {janito-2.4.0 → janito-2.5.1}/docs/code_intelligence/agentic-frameworks-comparison.md +0 -0
  61. {janito-2.4.0 → janito-2.5.1}/docs/code_intelligence/code-generation-challenges.md +0 -0
  62. {janito-2.4.0 → janito-2.5.1}/docs/code_intelligence/code-generation-observability.md +0 -0
  63. {janito-2.4.0 → janito-2.5.1}/docs/code_intelligence/our-approach.md +0 -0
  64. {janito-2.4.0 → janito-2.5.1}/docs/code_intelligence/why-string-replacement.md +0 -0
  65. {janito-2.4.0 → janito-2.5.1}/docs/concepts/analysis-style.md +0 -0
  66. {janito-2.4.0 → janito-2.5.1}/docs/concepts/index.md +0 -0
  67. {janito-2.4.0 → janito-2.5.1}/docs/concepts/language-model-clients.md +0 -0
  68. {janito-2.4.0 → janito-2.5.1}/docs/concepts/prompt-design-style.md +0 -0
  69. {janito-2.4.0 → janito-2.5.1}/docs/deepseek-setup.md +0 -0
  70. {janito-2.4.0 → janito-2.5.1}/docs/driver-flow.md +0 -0
  71. {janito-2.4.0 → janito-2.5.1}/docs/driver-request-cancellation.md +0 -0
  72. {janito-2.4.0 → janito-2.5.1}/docs/drivers/events.md +0 -0
  73. {janito-2.4.0 → janito-2.5.1}/docs/drivers.md +0 -0
  74. {janito-2.4.0 → janito-2.5.1}/docs/event-bus.md +0 -0
  75. {janito-2.4.0 → janito-2.5.1}/docs/guides/configuration.md +0 -0
  76. {janito-2.4.0 → janito-2.5.1}/docs/guides/developing.md +0 -0
  77. {janito-2.4.0 → janito-2.5.1}/docs/guides/installation.md +0 -0
  78. {janito-2.4.0 → janito-2.5.1}/docs/guides/profiles.md +0 -0
  79. {janito-2.4.0 → janito-2.5.1}/docs/guides/prompting/README.md +0 -0
  80. {janito-2.4.0 → janito-2.5.1}/docs/guides/single-shot-terminal.md +0 -0
  81. {janito-2.4.0 → janito-2.5.1}/docs/guides/terminal-shell.md +0 -0
  82. {janito-2.4.0 → janito-2.5.1}/docs/guides/tools-developer-guide.md +0 -0
  83. {janito-2.4.0 → janito-2.5.1}/docs/guides/using.md +0 -0
  84. {janito-2.4.0 → janito-2.5.1}/docs/guides/using_tools.md +0 -0
  85. {janito-2.4.0 → janito-2.5.1}/docs/imgs/code-generation-observability.png +0 -0
  86. {janito-2.4.0 → janito-2.5.1}/docs/imgs/code_generation_observability.png +0 -0
  87. {janito-2.4.0 → janito-2.5.1}/docs/imgs/happy-programmer.png +0 -0
  88. {janito-2.4.0 → janito-2.5.1}/docs/imgs/happy-programmer.svg +0 -0
  89. {janito-2.4.0 → janito-2.5.1}/docs/imgs/terminal-one-shot.png +0 -0
  90. {janito-2.4.0 → janito-2.5.1}/docs/imgs/terminal-shell.png +0 -0
  91. {janito-2.4.0 → janito-2.5.1}/docs/imgs/terminal_one_shot.png +0 -0
  92. {janito-2.4.0 → janito-2.5.1}/docs/imgs/terminal_shell.png +0 -0
  93. {janito-2.4.0 → janito-2.5.1}/docs/index.md +0 -0
  94. {janito-2.4.0 → janito-2.5.1}/docs/llm-drivers-required-config.md +0 -0
  95. {janito-2.4.0 → janito-2.5.1}/docs/llm-drivers.md +0 -0
  96. {janito-2.4.0 → janito-2.5.1}/docs/meta/developer-toolchain.md +0 -0
  97. {janito-2.4.0 → janito-2.5.1}/docs/meta/quality-checks.txt +0 -0
  98. {janito-2.4.0 → janito-2.5.1}/docs/reference/api.md +0 -0
  99. {janito-2.4.0 → janito-2.5.1}/docs/reference/azure-openai.md +0 -0
  100. {janito-2.4.0 → janito-2.5.1}/docs/reference/cli-options.md +0 -0
  101. {janito-2.4.0 → janito-2.5.1}/docs/reference/message-handler-model.md +0 -0
  102. {janito-2.4.0 → janito-2.5.1}/docs/reference/rich-message-handler.md +0 -0
  103. {janito-2.4.0 → janito-2.5.1}/docs/supported-providers-models.md +0 -0
  104. {janito-2.4.0 → janito-2.5.1}/docs/terms.md +0 -0
  105. {janito-2.4.0 → janito-2.5.1}/docs/tools-index.md +0 -0
  106. {janito-2.4.0 → janito-2.5.1}/docs/tools-natural-results.md +0 -0
  107. {janito-2.4.0 → janito-2.5.1}/docs/tools-precision.md +0 -0
  108. {janito-2.4.0 → janito-2.5.1}/janito/__init__.py +0 -0
  109. {janito-2.4.0 → janito-2.5.1}/janito/__main__.py +0 -0
  110. {janito-2.4.0 → janito-2.5.1}/janito/_version.py +0 -0
  111. {janito-2.4.0 → janito-2.5.1}/janito/agent/templates/profiles/system_prompt_template_developer.txt.j2 +0 -0
  112. {janito-2.4.0 → janito-2.5.1}/janito/cli/__init__.py +0 -0
  113. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/bindings.py +0 -0
  114. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/chat_entry.py +0 -0
  115. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/prompt_style.py +0 -0
  116. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/autocomplete.py +0 -0
  117. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/_priv_check.py +0 -0
  118. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/base.py +0 -0
  119. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/clear.py +0 -0
  120. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/history_view.py +0 -0
  121. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/lang.py +0 -0
  122. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/livelogs.py +0 -0
  123. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/model.py +0 -0
  124. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/multi.py +0 -0
  125. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/session.py +0 -0
  126. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/session_control.py +0 -0
  127. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/tools.py +0 -0
  128. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/utility.py +0 -0
  129. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands/verbose.py +0 -0
  130. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/commands.bak.zip +0 -0
  131. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/session/__init__.py +0 -0
  132. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/session/history.py +0 -0
  133. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/shell/session.bak.zip +0 -0
  134. {janito-2.4.0 → janito-2.5.1}/janito/cli/chat_mode/toolbar.py +0 -0
  135. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/list_models.py +0 -0
  136. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/list_providers.py +0 -0
  137. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/list_tools.py +0 -0
  138. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/model_selection.py +0 -0
  139. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/model_utils.py +0 -0
  140. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/set_api_key.py +0 -0
  141. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/show_config.py +0 -0
  142. {janito-2.4.0 → janito-2.5.1}/janito/cli/cli_commands/show_system_prompt.py +0 -0
  143. {janito-2.4.0 → janito-2.5.1}/janito/cli/config.py +0 -0
  144. {janito-2.4.0 → janito-2.5.1}/janito/cli/console.py +0 -0
  145. {janito-2.4.0 → janito-2.5.1}/janito/cli/core/__init__.py +0 -0
  146. {janito-2.4.0 → janito-2.5.1}/janito/cli/core/event_logger.py +0 -0
  147. {janito-2.4.0 → janito-2.5.1}/janito/cli/core/getters.py +0 -0
  148. {janito-2.4.0 → janito-2.5.1}/janito/cli/core/setters.py +0 -0
  149. {janito-2.4.0 → janito-2.5.1}/janito/cli/core/unsetters.py +0 -0
  150. {janito-2.4.0 → janito-2.5.1}/janito/cli/main.py +0 -0
  151. {janito-2.4.0 → janito-2.5.1}/janito/cli/prompt_core.py +0 -0
  152. {janito-2.4.0 → janito-2.5.1}/janito/cli/prompt_handler.py +0 -0
  153. {janito-2.4.0 → janito-2.5.1}/janito/cli/single_shot_mode/__init__.py +0 -0
  154. {janito-2.4.0 → janito-2.5.1}/janito/cli/utils.py +0 -0
  155. {janito-2.4.0 → janito-2.5.1}/janito/cli/verbose_output.py +0 -0
  156. {janito-2.4.0 → janito-2.5.1}/janito/config.py +0 -0
  157. {janito-2.4.0 → janito-2.5.1}/janito/config_manager.py +0 -0
  158. {janito-2.4.0 → janito-2.5.1}/janito/conversation_history.py +0 -0
  159. {janito-2.4.0 → janito-2.5.1}/janito/dir_walk_utils.py +0 -0
  160. {janito-2.4.0 → janito-2.5.1}/janito/driver_events.py +0 -0
  161. {janito-2.4.0 → janito-2.5.1}/janito/drivers/anthropic/driver.py +0 -0
  162. {janito-2.4.0 → janito-2.5.1}/janito/drivers/azure_openai/driver.py +0 -0
  163. {janito-2.4.0 → janito-2.5.1}/janito/drivers/dashscope.bak.zip +0 -0
  164. {janito-2.4.0 → janito-2.5.1}/janito/drivers/mistralai/driver.py +0 -0
  165. {janito-2.4.0 → janito-2.5.1}/janito/drivers/openai/README.md +0 -0
  166. {janito-2.4.0 → janito-2.5.1}/janito/drivers/openai/driver.py +0 -0
  167. {janito-2.4.0 → janito-2.5.1}/janito/drivers/openai_responses.bak.zip +0 -0
  168. {janito-2.4.0 → janito-2.5.1}/janito/event_bus/__init__.py +0 -0
  169. {janito-2.4.0 → janito-2.5.1}/janito/event_bus/bus.py +0 -0
  170. {janito-2.4.0 → janito-2.5.1}/janito/event_bus/event.py +0 -0
  171. {janito-2.4.0 → janito-2.5.1}/janito/event_bus/handler.py +0 -0
  172. {janito-2.4.0 → janito-2.5.1}/janito/event_bus/queue_bus.py +0 -0
  173. {janito-2.4.0 → janito-2.5.1}/janito/exceptions.py +0 -0
  174. {janito-2.4.0 → janito-2.5.1}/janito/formatting.py +0 -0
  175. {janito-2.4.0 → janito-2.5.1}/janito/gitignore_utils.py +0 -0
  176. {janito-2.4.0 → janito-2.5.1}/janito/i18n/__init__.py +0 -0
  177. {janito-2.4.0 → janito-2.5.1}/janito/i18n/messages.py +0 -0
  178. {janito-2.4.0 → janito-2.5.1}/janito/i18n/pt.py +0 -0
  179. {janito-2.4.0 → janito-2.5.1}/janito/llm/README.md +0 -0
  180. {janito-2.4.0 → janito-2.5.1}/janito/llm/__init__.py +0 -0
  181. {janito-2.4.0 → janito-2.5.1}/janito/llm/auth.py +0 -0
  182. {janito-2.4.0 → janito-2.5.1}/janito/llm/driver.py +0 -0
  183. {janito-2.4.0 → janito-2.5.1}/janito/llm/driver_config.py +0 -0
  184. {janito-2.4.0 → janito-2.5.1}/janito/llm/driver_config_builder.py +0 -0
  185. {janito-2.4.0 → janito-2.5.1}/janito/llm/driver_input.py +0 -0
  186. {janito-2.4.0 → janito-2.5.1}/janito/llm/message_parts.py +0 -0
  187. {janito-2.4.0 → janito-2.5.1}/janito/llm/model.py +0 -0
  188. {janito-2.4.0 → janito-2.5.1}/janito/llm/provider.py +0 -0
  189. {janito-2.4.0 → janito-2.5.1}/janito/perf_singleton.py +0 -0
  190. {janito-2.4.0 → janito-2.5.1}/janito/performance_collector.py +0 -0
  191. {janito-2.4.0 → janito-2.5.1}/janito/platform_discovery.py +0 -0
  192. {janito-2.4.0 → janito-2.5.1}/janito/provider_config.py +0 -0
  193. {janito-2.4.0 → janito-2.5.1}/janito/providers/anthropic/model_info.py +0 -0
  194. {janito-2.4.0 → janito-2.5.1}/janito/providers/anthropic/provider.py +0 -0
  195. {janito-2.4.0 → janito-2.5.1}/janito/providers/azure_openai/model_info.py +0 -0
  196. {janito-2.4.0 → janito-2.5.1}/janito/providers/azure_openai/provider.py +0 -0
  197. {janito-2.4.0 → janito-2.5.1}/janito/providers/dashscope.bak.zip +0 -0
  198. {janito-2.4.0 → janito-2.5.1}/janito/providers/deepseek/__init__.py +0 -0
  199. {janito-2.4.0 → janito-2.5.1}/janito/providers/deepseek/model_info.py +0 -0
  200. {janito-2.4.0 → janito-2.5.1}/janito/providers/deepseek/provider.py +0 -0
  201. {janito-2.4.0 → janito-2.5.1}/janito/providers/google/__init__.py +0 -0
  202. {janito-2.4.0 → janito-2.5.1}/janito/providers/google/model_info.py +0 -0
  203. {janito-2.4.0 → janito-2.5.1}/janito/providers/google/provider.py +0 -0
  204. {janito-2.4.0 → janito-2.5.1}/janito/providers/mistralai/model_info.py +0 -0
  205. {janito-2.4.0 → janito-2.5.1}/janito/providers/mistralai/provider.py +0 -0
  206. {janito-2.4.0 → janito-2.5.1}/janito/providers/openai/__init__.py +0 -0
  207. {janito-2.4.0 → janito-2.5.1}/janito/providers/openai/model_info.py +0 -0
  208. {janito-2.4.0 → janito-2.5.1}/janito/providers/openai/provider.py +0 -0
  209. {janito-2.4.0 → janito-2.5.1}/janito/providers/openai/schema_generator.py +0 -0
  210. {janito-2.4.0 → janito-2.5.1}/janito/providers/registry.py +0 -0
  211. {janito-2.4.0 → janito-2.5.1}/janito/report_events.py +0 -0
  212. {janito-2.4.0 → janito-2.5.1}/janito/shell.bak.zip +0 -0
  213. {janito-2.4.0 → janito-2.5.1}/janito/tools/DOCSTRING_STANDARD.txt +0 -0
  214. {janito-2.4.0 → janito-2.5.1}/janito/tools/README.md +0 -0
  215. {janito-2.4.0 → janito-2.5.1}/janito/tools/__init__.py +0 -0
  216. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/__init__.py +0 -0
  217. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/__init__.py +0 -0
  218. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/adapter.py +0 -0
  219. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/ask_user.py +0 -0
  220. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/copy_file.py +0 -0
  221. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/create_directory.py +0 -0
  222. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/create_file.py +0 -0
  223. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/delete_text_in_file.py +0 -0
  224. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/fetch_url.py +0 -0
  225. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/find_files.py +0 -0
  226. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/__init__.py +0 -0
  227. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/core.py +0 -0
  228. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/java_outline.py +0 -0
  229. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/markdown_outline.py +0 -0
  230. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/python_outline.py +0 -0
  231. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/python_outline_v2.py +0 -0
  232. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/get_file_outline/search_outline.py +0 -0
  233. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/move_file.py +0 -0
  234. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/open_html_in_browser.py +0 -0
  235. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/open_url.py +0 -0
  236. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/python_code_run.py +0 -0
  237. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/python_command_run.py +0 -0
  238. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/python_file_run.py +0 -0
  239. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/remove_directory.py +0 -0
  240. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/remove_file.py +0 -0
  241. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/replace_text_in_file.py +0 -0
  242. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/run_bash_command.py +0 -0
  243. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/run_powershell_command.py +0 -0
  244. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/search_text/__init__.py +0 -0
  245. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/search_text/core.py +0 -0
  246. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/search_text/match_lines.py +0 -0
  247. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/search_text/pattern_utils.py +0 -0
  248. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/search_text/traverse_directory.py +0 -0
  249. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/__init__.py +0 -0
  250. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/css_validator.py +0 -0
  251. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/html_validator.py +0 -0
  252. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/js_validator.py +0 -0
  253. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/json_validator.py +0 -0
  254. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +0 -0
  255. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +0 -0
  256. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/python_validator.py +0 -0
  257. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/xml_validator.py +0 -0
  258. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +0 -0
  259. {janito-2.4.0 → janito-2.5.1}/janito/tools/adapters/local/view_file.py +0 -0
  260. {janito-2.4.0 → janito-2.5.1}/janito/tools/inspect_registry.py +0 -0
  261. {janito-2.4.0 → janito-2.5.1}/janito/tools/outline_file.bak.zip +0 -0
  262. {janito-2.4.0 → janito-2.5.1}/janito/tools/permissions.py +0 -0
  263. {janito-2.4.0 → janito-2.5.1}/janito/tools/permissions_parse.py +0 -0
  264. {janito-2.4.0 → janito-2.5.1}/janito/tools/tool_events.py +0 -0
  265. {janito-2.4.0 → janito-2.5.1}/janito/tools/tool_run_exception.py +0 -0
  266. {janito-2.4.0 → janito-2.5.1}/janito/tools/tool_use_tracker.py +0 -0
  267. {janito-2.4.0 → janito-2.5.1}/janito/tools/tool_utils.py +0 -0
  268. {janito-2.4.0 → janito-2.5.1}/janito/tools/tools_adapter.py +0 -0
  269. {janito-2.4.0 → janito-2.5.1}/janito/tools/tools_schema.py +0 -0
  270. {janito-2.4.0 → janito-2.5.1}/janito/utils.py +0 -0
  271. {janito-2.4.0 → janito-2.5.1}/janito.egg-info/dependency_links.txt +0 -0
  272. {janito-2.4.0 → janito-2.5.1}/janito.egg-info/entry_points.txt +0 -0
  273. {janito-2.4.0 → janito-2.5.1}/janito.egg-info/requires.txt +0 -0
  274. {janito-2.4.0 → janito-2.5.1}/janito.egg-info/top_level.txt +0 -0
  275. {janito-2.4.0 → janito-2.5.1}/mkdocs.yml +0 -0
  276. {janito-2.4.0 → janito-2.5.1}/pyproject.toml +0 -0
  277. {janito-2.4.0 → janito-2.5.1}/requirements-dev.txt +0 -0
  278. {janito-2.4.0 → janito-2.5.1}/requirements.txt +0 -0
  279. {janito-2.4.0 → janito-2.5.1}/setup.cfg +0 -0
  280. {janito-2.4.0 → janito-2.5.1}/tests/adapters/local/get_file_outline/test_core_outline.py +0 -0
  281. {janito-2.4.0 → janito-2.5.1}/tests/test_cli_list_models.py +0 -0
  282. {janito-2.4.0 → janito-2.5.1}/tests/test_cli_list_providers.py +0 -0
  283. {janito-2.4.0 → janito-2.5.1}/tests/test_cli_version.py +0 -0
  284. {janito-2.4.0 → janito-2.5.1}/tox.ini +0 -0
@@ -0,0 +1,19 @@
1
+ {
2
+ "files.exclude": {
3
+ "**/__pycache__": true,
4
+ "**/build": true,
5
+ "**/dist": true,
6
+ "**/.eggs": true,
7
+ "**/*.egg-info": true,
8
+ "**/.tox": true,
9
+ "**/.nox": true,
10
+ "**/.hypothesis": true,
11
+ "**/.pytest_cache": true,
12
+ "**/.mypy_cache": true,
13
+ "**/.pyre": true,
14
+ "**/.ipynb_checkpoints": true,
15
+ "**/.ruff_cache": true,
16
+ "**/.janito": true,
17
+ "**/site": true
18
+ }
19
+ }
@@ -0,0 +1,103 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [2.5.0] - 2025-07-01
6
+ ### Added
7
+ - Show working directory in chat mode startup message.
8
+ - Bang (`!`) shell command handler for direct shell access from the chat interface.
9
+ - Elapsed time reporting to token usage summary and improved terminal output styling.
10
+ - CLI support for reading prompt from stdin and suppressing token usage summary in non-interactive mode.
11
+
12
+ ### Changed
13
+ - Import `os` in help command handler for future extensibility.
14
+ - Refactored `ChatSession._chat_loop` to reduce complexity by extracting command and prompt handling methods.
15
+ - Refactored profile selection, removed `ProfileShellHandler`, and improved terminal reporter output for STDOUT/STDERR.
16
+ - Refactored to remove `exec_enabled` argument from agent and CLI setup; now uses `args.exec` directly.
17
+ - Improved terminal output: added `delete_current_line` to `RichTerminalReporter` for cleaner UI.
18
+ - Refactored and cleaned up: removed redundant import of `handle_command`, removed backup, structure, and test strategy files, and added `.vscode/settings.json` for VSCode excludes.
19
+
20
+ ### Removed
21
+ - MistralAI provider and driver references and related files.
22
+ - Conversation history persistence and updated input history path.
23
+
24
+ ### Documentation
25
+ - Removed inline web file viewer documentation from `README.md` for clarity and focus on core options.
26
+
27
+ ## [2.4.0]
28
+
29
+ ### Changed
30
+ - Refactored tool permission management: migrated to a permission-based model (read/write/execute), updated CLI and docs, removed legacy execution toggling.
31
+ - Enhanced tool permissions: tools are now grouped by permission, config supports tool_permissions, ask_user is read-only, and permissions are applied at startup.
32
+ - Refined permission and tool output messages in shell commands; improved tool listing by permission class in tools.py.
33
+ - Refactored agent and prompt handler setup, improved model switching, and enhanced user interrupt handling. Includes new /model shell command and fixes for provider registry ASCII fallback.
34
+ - Refactored agent system prompt and permissions logic, switched to profile-based template selection, removed unused templates, and added --profile CLI support.
35
+ - Refactored chat mode startup messages and permission reset handling for improved clarity.
36
+ - Refactored ChatSession and ChatShellState: removed allow_execution logic and related assignments, use exec_enabled directly for execution control.
37
+ - Refactored tool system to use latest git tag for version detection in release script.
38
+ - Refined release script to recommend creating a new git tag if version exists on PyPI.
39
+ - Removed termweb: web file viewer and related CLI/editor features, updated docs and config accordingly.
40
+ - Removed temporary file x.txt.
41
+ - Restored tool permissions to CLI defaults on /restart; store and retrieve default tool permissions in AllowedPermissionsState. Runner now sets and saves default permissions for restoration. Updated conversation_restart to restore or fallback to all-off permissions.
42
+ - Updated disabled execution tools message for clarity.
43
+ - Docs and UX: clarified permissions (read/write/exec), added profiles doc links, and removed localhost references from UI/toolbar.
44
+
45
+ ### Added
46
+ - Agent/driver: drain driver's input queue before sending new messages in chat() to prevent stale DriverInput processing.
47
+
48
+ ### Fixed
49
+ - Ensure tools adapter is always available in provider classes, even if driver is missing. Prevents AttributeError in generic code paths relying on execute_tool().
50
+
51
+ ## [2.3.1] - 2025-06-25
52
+ ### Changed
53
+ - Bumped version to 2.3.1 in `version.py`, `pyproject.toml`, and `__init__.py`.
54
+
55
+ ## [2.3.0] - 2025-06-25
56
+ ### Added
57
+ - requirements-dev.txt with development dependencies (pytest, pre-commit, ruff, detect-secrets, codespell, black) for code quality and testing
58
+ - Java outline support to get_file_outline tool, including package-private methods
59
+ - create_driver method to AzureOpenAIProvider for driver instantiation
60
+ - CLI --version test and suppress pytest-asyncio deprecation warning
61
+ - New dependencies: prompt_toolkit, lxml, requests, bs4 to requirements.txt
62
+
63
+ ### Changed
64
+ - Improved error messages and documentation
65
+ - Refined error handling in open_html_in_browser.py and open_url.py
66
+ - Refactor remove_file tool: use ReportAction.DELETE for all file removal actions
67
+ - Remove redundant _prepare_api_kwargs override in AzureOpenAIModelDriver
68
+ - Refactor(azure_openai): use 'model' directly in API kwargs, remove deployment_name remapping
69
+ - Add public read-only driver_config property to AzureOpenAIProvider
70
+ - Add _prepare_api_kwargs to support deployment_name for Azure OpenAI API compatibility
71
+ - Update toolbar bindings: add CTRL-C for interrupt/exit, clarify F1 usage
72
+ - Update pyproject.toml optional-dependencies section for setuptools compatibility
73
+ - Remove references to max_results in FindFilesTool docstring
74
+ - Refactor: use .jsonl extension for input history files instead of .log
75
+ - Refactor get_file_outline core logic to remove duplication and add tests
76
+ - Test CLI: Ensure error on missing provider and validate supported models output for each provider
77
+ - Configure dynamic dependencies in pyproject.toml
78
+ - Define dependencies in requirements.txt: attrs, rich, pathspec, setuptools, pyyaml, jinja2
79
+ - Add workdir support to LocalToolsAdapter and CLI; improve Python tool adapters
80
+ - Friendly error message when the provider is not present from the available ones
81
+
82
+ ### Fixed
83
+ - Ensure error on missing provider and validate supported models output for each provider
84
+ - Update supported models table; remove o4-mini-high model from code and docs
85
+
86
+ ## [2.1.1] - 2024-06-23
87
+ ### Changed
88
+ - Bumped version to 2.1.1 in `version.py`, `pyproject.toml`, and `__init__.py`.
89
+ - docs: add DeepSeek setup guide, update navigation and references
90
+ - Add docs/deepseek-setup.md with setup instructions for DeepSeek provider
91
+ - Link DeepSeek setup in docs/index.md and mkdocs.yml navigation
92
+ - Fix model name: change 'deepseek-coder' to 'deepseek-reasoner' in DeepSeek provider and model_info
93
+ - Update DeepSeek provider docstrings and options to match supported models
94
+
95
+ ## [2.1.0] - 2024-06-09
96
+ ### Added
97
+
98
+ ### Changed
99
+ - Bumped version to 2.1.0 in `version.py`, `pyproject.toml`, and `__init__.py`.
100
+
101
+ ---
102
+
103
+ *Older changes may not be listed.*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 2.4.0
3
+ Version: 2.5.1
4
4
  Summary: A new Python package called janito.
5
5
  Author-email: João Pinto <lamego.pinto@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/janito-dev/janito
@@ -167,19 +167,6 @@ Janito has configuration options, like `--set api-key API_KEY` and `--set provid
167
167
 
168
168
  ### Advanced Options
169
169
 
170
- - **Enable Inline Web File Viewer for Clickable Links**
171
-
172
- By default, Janito can open referenced files in a browser-based viewer when you click on file links in supported terminals. To enable this feature for your session, use the `--web` flag:
173
-
174
- ```bash
175
- janito --web
176
- ```
177
-
178
- This starts the lightweight web file viewer () in the background, allowing you to inspect files referenced in responses directly in your browser. Combine with interactive mode or prompts as needed.
179
-
180
- > **Tip:** Use with the interactive shell for the best experience with clickable file links.
181
-
182
-
183
170
  - **Enable Execution Tools (Code/Shell Execution)**
184
171
 
185
172
  By default, **all tool privileges (read, write, execute)** are disabled for safety. This means Janito starts with no permissions to run tools that read, write, or execute code/shell commands unless you explicitly enable them.
@@ -232,7 +219,7 @@ janito -r -w -x "Run this code: print('Hello, world!')"
232
219
  ### Core CLI Options
233
220
  | Option | Description |
234
221
  |------------------------|-----------------------------------------------------------------------------|
235
- | `--web` | Enable the builtin lightweight web file viewer for clickable file links (). |
222
+
236
223
  | `--version` | Show program version |
237
224
  | `--list-tools` | List all registered tools |
238
225
  | `--list-providers` | List all supported LLM providers |
@@ -136,19 +136,6 @@ Janito has configuration options, like `--set api-key API_KEY` and `--set provid
136
136
 
137
137
  ### Advanced Options
138
138
 
139
- - **Enable Inline Web File Viewer for Clickable Links**
140
-
141
- By default, Janito can open referenced files in a browser-based viewer when you click on file links in supported terminals. To enable this feature for your session, use the `--web` flag:
142
-
143
- ```bash
144
- janito --web
145
- ```
146
-
147
- This starts the lightweight web file viewer () in the background, allowing you to inspect files referenced in responses directly in your browser. Combine with interactive mode or prompts as needed.
148
-
149
- > **Tip:** Use with the interactive shell for the best experience with clickable file links.
150
-
151
-
152
139
  - **Enable Execution Tools (Code/Shell Execution)**
153
140
 
154
141
  By default, **all tool privileges (read, write, execute)** are disabled for safety. This means Janito starts with no permissions to run tools that read, write, or execute code/shell commands unless you explicitly enable them.
@@ -201,7 +188,7 @@ janito -r -w -x "Run this code: print('Hello, world!')"
201
188
  ### Core CLI Options
202
189
  | Option | Description |
203
190
  |------------------------|-----------------------------------------------------------------------------|
204
- | `--web` | Enable the builtin lightweight web file viewer for clickable file links (). |
191
+
205
192
  | `--version` | Show program version |
206
193
  | `--list-tools` | List all registered tools |
207
194
  | `--list-providers` | List all supported LLM providers |
@@ -0,0 +1,138 @@
1
+ Below is a **minimal-yet-complete “router loop”** that lets any Chat Completions model talk to a remote **MCP server** exactly the same way the Agents SDK/Responses API does under the hood.
2
+ Feel free to copy-paste and adapt.
3
+
4
+ ---
5
+
6
+ ## 0 Dependencies & setup
7
+
8
+ ```bash
9
+ pip install openai httpx python-dotenv # ↩︎ async-friendly HTTP client
10
+ export OPENAI_API_KEY=sk-...
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 1 One-time discovery of all MCP tools
16
+
17
+ ```python
18
+ import httpx, json, os
19
+ from openai import OpenAI
20
+
21
+ client = OpenAI() # Chat Completions client
22
+ MCP_SERVERS = { # label → URL
23
+ "weather": "http://localhost:8000/v1/mcp",
24
+ }
25
+
26
+ tool_registry = {} # name → {id, server, schema}
27
+ for label, url in MCP_SERVERS.items():
28
+ r = httpx.post(url, json={"method": "list_tools", "id": 1})
29
+ for t in r.json()["result"]:
30
+ tool_registry[t["name"]] = {
31
+ "id": t["id"],
32
+ "server": url,
33
+ "schema": t, # already JSON-Schema compliant ✅
34
+ }
35
+
36
+ TOOLS_FOR_OPENAI = [v["schema"] for v in tool_registry.values()]
37
+ ```
38
+
39
+ The list-tools → JSON-Schema mapping is exactly what Chat Completions expects (via the `tools` parameter) ([help.openai.com][1]).
40
+
41
+ ---
42
+
43
+ ## 2 Chat → router loop
44
+
45
+ ```python
46
+ messages = [
47
+ {"role": "system",
48
+ "content": "You can call the tools provided to help the user."},
49
+ {"role": "user",
50
+ "content": "What’s the weather in Zurich right now?"}
51
+ ]
52
+
53
+ while True:
54
+ resp = client.chat.completions.create(
55
+ model = "gpt-4o-mini",
56
+ messages = messages,
57
+ tools = TOOLS_FOR_OPENAI,
58
+ tool_choice = "auto", # let the model decide :contentReference[oaicite:1]{index=1}
59
+ temperature = 0,
60
+ )
61
+
62
+ msg = resp.choices[0].message
63
+
64
+ # CASE A – assistant wants to call one or more tools
65
+ if msg.tool_calls:
66
+ messages.append(msg) # keep the call stub
67
+ for call in msg.tool_calls:
68
+ tool_name = call.function.name
69
+ args = json.loads(call.function.arguments)
70
+
71
+ m = tool_registry[tool_name]
72
+ result = httpx.post(
73
+ m["server"],
74
+ json={
75
+ "method": "call_tool",
76
+ "params": {"id": m["id"], "arguments": args},
77
+ "id": 42
78
+ }
79
+ ).json()["result"]
80
+
81
+ # Echo the result back so the model can finish the thought
82
+ messages.append({
83
+ "role": "tool",
84
+ "tool_call_id": call.id,
85
+ "content": json.dumps(result)
86
+ })
87
+ continue # ↩︎ model may want more tool calls.
88
+
89
+ # CASE B – we’re done (plain assistant message)
90
+ print(msg.content)
91
+ break
92
+ ```
93
+
94
+ **Key points**
95
+
96
+ | Why | What to remember |
97
+ | ------------------ | ---------------------------------------------------------------------------- |
98
+ | **Tool discovery** | Cache the JSON once; refresh on a schedule to save tokens. |
99
+ | **Routing** | Map *tool name* → (MCP URL, tool id). No need to expose ids to the model. |
100
+ | **Multiple calls** | The loop continues until the model returns a message *without* `tool_calls`. |
101
+ | **Safety** | Validate the tool name and arguments against `tool_registry` before firing. |
102
+
103
+ ---
104
+
105
+ ## 3 Streaming (optional)
106
+
107
+ Add `stream=True` to the `client.chat.completions.create` call and accumulate the chunks’ `delta` fields until you see `finish_reason == "tool_calls"` or `"stop"` ([cookbook.openai.com][2]).
108
+ Streamed tool calls arrive in exactly the same order; route each as soon as the JSON fragment closes to slash latency.
109
+
110
+ ---
111
+
112
+ ## 4 Handling more than one MCP server
113
+
114
+ Just keep extending `MCP_SERVERS` and the discovery loop; `tool_registry` remains a flat map, so collisions are impossible. The runtime cost is one HTTP request per server at startup.
115
+
116
+ ---
117
+
118
+ ## 5 Production checklist
119
+
120
+ | Risk | Mitigation |
121
+ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
122
+ | **Prompt-injection via tool output** | JSON-schema-validate **again** after the MCP call; escape any HTML. |
123
+ | **Runaway streams / DoS** | Timeouts on both Chat Completions and MCP HTTP calls; token/byte ceilings. |
124
+ | **Secrets in tool arguments** | Never log raw arguments; redact before shipping logs or traces. |
125
+ | **Token cost for huge schemas** | SHA-256 hash the last tool list; only resend when it changes. |
126
+ | **Model refuses to call a tool** | Use `tool_choice={"type":"function","function":{"name":"…"}}` or `"required"` to force a call ([learn.microsoft.com][3]). |
127
+
128
+ ---
129
+
130
+ ### Why not just switch to the Responses API?
131
+
132
+ Chat Completions works fine, but with Responses you get automatic **list\_tools/call\_tool** plumbing, background runs, retries, and built-in tracing for free. If you’re starting fresh, Responses is simpler; if you’re locked into Chat Completions (e.g., legacy infra, cost model), the loop above is all you need.
133
+
134
+ Happy hacking!
135
+
136
+ [1]: https://help.openai.com/en/articles/8555517-function-calling-in-the-openai-api "Function Calling in the OpenAI API | OpenAI Help Center"
137
+ [2]: https://cookbook.openai.com/examples/how_to_stream_completions "How to stream completions"
138
+ [3]: https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling "How to use function calling with Azure OpenAI in Azure AI Foundry Models - Azure OpenAI | Microsoft Learn"
@@ -20,7 +20,7 @@ def setup_agent(
20
20
  output_queue=None,
21
21
  verbose_tools=False,
22
22
  verbose_agent=False,
23
- exec_enabled=False,
23
+
24
24
  allowed_permissions=None,
25
25
  profile=None,
26
26
  profile_system_prompt=None,
@@ -156,6 +156,7 @@ def setup_agent(
156
156
  agent.template_vars["profile"] = profile
157
157
  # Store template path and context for dynamic prompt refresh
158
158
  agent.system_prompt_template = str(template_path)
159
+
159
160
  agent._template_vars = context.copy()
160
161
  agent._original_template_vars = context.copy()
161
162
  return agent
@@ -170,7 +171,7 @@ def create_configured_agent(
170
171
  verbose_agent=False,
171
172
  templates_dir=None,
172
173
  zero_mode=False,
173
- exec_enabled=False,
174
+
174
175
  allowed_permissions=None,
175
176
  profile=None,
176
177
  profile_system_prompt=None,
@@ -212,7 +213,7 @@ def create_configured_agent(
212
213
  output_queue=output_queue,
213
214
  verbose_tools=verbose_tools,
214
215
  verbose_agent=verbose_agent,
215
- exec_enabled=exec_enabled,
216
+
216
217
  allowed_permissions=allowed_permissions,
217
218
  profile=profile,
218
219
  profile_system_prompt=profile_system_prompt,
@@ -0,0 +1 @@
1
+ You are a helpful assistant.
@@ -0,0 +1,153 @@
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
@@ -54,7 +54,7 @@ class ChatSession:
54
54
  args=None,
55
55
  verbose_tools=False,
56
56
  verbose_agent=False,
57
- exec_enabled=False,
57
+
58
58
  allowed_permissions=None,
59
59
  ):
60
60
 
@@ -76,12 +76,9 @@ class ChatSession:
76
76
  from janito.cli.chat_mode.session_profile_select import select_profile
77
77
 
78
78
  result = select_profile()
79
- if (
80
- isinstance(result, dict)
81
- and result.get("profile") is None
82
- and result.get("profile_system_prompt")
83
- ):
84
- profile_system_prompt = result["profile_system_prompt"]
79
+ if isinstance(result, dict):
80
+ profile = result.get("profile")
81
+ profile_system_prompt = result.get("profile_system_prompt")
85
82
  elif isinstance(result, str) and result.startswith("role:"):
86
83
  role = result[len("role:") :].strip()
87
84
  profile = "developer"
@@ -105,7 +102,7 @@ class ChatSession:
105
102
  role=role,
106
103
  verbose_tools=verbose_tools,
107
104
  verbose_agent=verbose_agent,
108
- exec_enabled=exec_enabled,
105
+
109
106
  allowed_permissions=allowed_permissions,
110
107
  profile=profile,
111
108
  profile_system_prompt=profile_system_prompt,
@@ -163,6 +160,14 @@ class ChatSession:
163
160
  f"[bold green]Janito Chat Mode v{__version__}[/bold green]"
164
161
  )
165
162
  self.console.print("[green]/help for commands /exit or Ctrl+C to quit[/green]")
163
+ import os
164
+ cwd = os.getcwd()
165
+ home = os.path.expanduser('~')
166
+ if cwd.startswith(home):
167
+ cwd_display = '~' + cwd[len(home):]
168
+ else:
169
+ cwd_display = cwd
170
+ self.console.print(f"[green]Working Dir:[/green] {cwd_display}")
166
171
 
167
172
  # Inform user if no privileges are enabled
168
173
  from janito.cli.chat_mode.shell.commands._priv_check import user_has_any_privileges
@@ -185,40 +190,55 @@ class ChatSession:
185
190
  continue
186
191
  if self._handle_exit_conditions(cmd_input):
187
192
  break
188
- if cmd_input.startswith("/"):
189
- handle_command(cmd_input, shell_state=self.shell_state)
193
+ if self._handle_command_input(cmd_input):
190
194
  continue
191
195
  self.user_input_history.append(cmd_input)
192
- try:
193
- final_event = (
194
- self._prompt_handler.agent.last_event
195
- if hasattr(self._prompt_handler.agent, "last_event")
196
+ self._process_prompt(cmd_input)
197
+
198
+ def _handle_command_input(self, cmd_input):
199
+ if cmd_input.startswith("/"):
200
+ handle_command(cmd_input, shell_state=self.shell_state)
201
+ return True
202
+ if cmd_input.startswith("!"):
203
+ # Pass everything after ! to the bang handler
204
+ handle_command(f"! {cmd_input[1:]}", shell_state=self.shell_state)
205
+ return True
206
+ return False
207
+
208
+ def _process_prompt(self, cmd_input):
209
+ try:
210
+ import time
211
+ final_event = (
212
+ self._prompt_handler.agent.last_event
213
+ if hasattr(self._prompt_handler.agent, "last_event")
214
+ else None
215
+ )
216
+ start_time = time.time()
217
+ self._prompt_handler.run_prompt(cmd_input)
218
+ end_time = time.time()
219
+ elapsed = end_time - start_time
220
+ self.msg_count += 1
221
+ # After prompt, print the stat line using the shared core function
222
+ from janito.formatting_token import print_token_message_summary
223
+
224
+ usage = self.performance_collector.get_last_request_usage()
225
+ print_token_message_summary(self.console, self.msg_count, usage, elapsed=elapsed)
226
+ # Print exit reason if present in the final event
227
+ if final_event and hasattr(final_event, "metadata"):
228
+ exit_reason = (
229
+ final_event.metadata.get("exit_reason")
230
+ if hasattr(final_event, "metadata")
196
231
  else None
197
232
  )
198
- self._prompt_handler.run_prompt(cmd_input)
199
- self.msg_count += 1
200
- # After prompt, print the stat line using the shared core function
201
- from janito.formatting_token import print_token_message_summary
202
-
203
- usage = self.performance_collector.get_last_request_usage()
204
- print_token_message_summary(self.console, self.msg_count, usage)
205
- # Print exit reason if present in the final event
206
- if final_event and hasattr(final_event, "metadata"):
207
- exit_reason = (
208
- final_event.metadata.get("exit_reason")
209
- if hasattr(final_event, "metadata")
210
- else None
233
+ if exit_reason:
234
+ self.console.print(
235
+ f"[bold yellow]Exit reason: {exit_reason}[/bold yellow]"
211
236
  )
212
- if exit_reason:
213
- self.console.print(
214
- f"[bold yellow]Exit reason: {exit_reason}[/bold yellow]"
215
- )
216
-
217
- except Exception as exc:
218
- self.console.print(f"[red]Exception in agent: {exc}[/red]")
219
- import traceback
220
237
 
221
- self.console.print(traceback.format_exc())
238
+ except Exception as exc:
239
+ self.console.print(f"[red]Exception in agent: {exc}[/red]")
240
+ import traceback
241
+ self.console.print(traceback.format_exc())
222
242
 
223
243
  def _create_prompt_session(self):
224
244
  return PromptSession(
@@ -22,7 +22,7 @@ def select_profile():
22
22
  style=custom_style
23
23
  ).ask()
24
24
  if answer == "helpful assistant":
25
- return {"profile": None, "profile_system_prompt": "You are an helpful assistant"}
25
+ return {"profile": "assistant", "profile_system_prompt": None}
26
26
  if answer == "using role...":
27
27
  role_name = questionary.text("Enter the role name:").ask()
28
28
  return f"role:{role_name}"