janito 1.5.2__py3-none-any.whl → 1.6.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 (85) hide show
  1. janito/__init__.py +1 -1
  2. janito/__main__.py +0 -1
  3. janito/agent/config.py +11 -10
  4. janito/agent/config_defaults.py +3 -2
  5. janito/agent/conversation.py +93 -119
  6. janito/agent/conversation_api.py +98 -0
  7. janito/agent/conversation_exceptions.py +12 -0
  8. janito/agent/conversation_tool_calls.py +22 -0
  9. janito/agent/conversation_ui.py +17 -0
  10. janito/agent/message_handler.py +8 -9
  11. janito/agent/{agent.py → openai_client.py} +48 -16
  12. janito/agent/openai_schema_generator.py +53 -37
  13. janito/agent/profile_manager.py +172 -0
  14. janito/agent/queued_message_handler.py +13 -14
  15. janito/agent/rich_live.py +32 -0
  16. janito/agent/rich_message_handler.py +64 -0
  17. janito/agent/runtime_config.py +6 -1
  18. janito/agent/{tools/tool_base.py → tool_base.py} +15 -8
  19. janito/agent/tool_registry.py +118 -132
  20. janito/agent/tools/__init__.py +41 -2
  21. janito/agent/tools/ask_user.py +43 -33
  22. janito/agent/tools/create_directory.py +18 -16
  23. janito/agent/tools/create_file.py +31 -36
  24. janito/agent/tools/fetch_url.py +23 -19
  25. janito/agent/tools/find_files.py +40 -36
  26. janito/agent/tools/get_file_outline.py +100 -22
  27. janito/agent/tools/get_lines.py +40 -32
  28. janito/agent/tools/gitignore_utils.py +9 -6
  29. janito/agent/tools/move_file.py +22 -13
  30. janito/agent/tools/py_compile_file.py +40 -0
  31. janito/agent/tools/remove_directory.py +34 -24
  32. janito/agent/tools/remove_file.py +22 -20
  33. janito/agent/tools/replace_file.py +51 -0
  34. janito/agent/tools/replace_text_in_file.py +69 -42
  35. janito/agent/tools/rich_live.py +9 -2
  36. janito/agent/tools/run_bash_command.py +155 -107
  37. janito/agent/tools/run_python_command.py +139 -0
  38. janito/agent/tools/search_files.py +51 -34
  39. janito/agent/tools/tools_utils.py +4 -2
  40. janito/agent/tools/utils.py +6 -2
  41. janito/cli/_print_config.py +42 -16
  42. janito/cli/_utils.py +1 -0
  43. janito/cli/arg_parser.py +182 -29
  44. janito/cli/config_commands.py +54 -22
  45. janito/cli/logging_setup.py +9 -3
  46. janito/cli/main.py +11 -10
  47. janito/cli/runner/__init__.py +2 -0
  48. janito/cli/runner/cli_main.py +148 -0
  49. janito/cli/runner/config.py +33 -0
  50. janito/cli/runner/formatting.py +12 -0
  51. janito/cli/runner/scan.py +44 -0
  52. janito/cli_chat_shell/__init__.py +0 -1
  53. janito/cli_chat_shell/chat_loop.py +71 -92
  54. janito/cli_chat_shell/chat_state.py +38 -0
  55. janito/cli_chat_shell/chat_ui.py +43 -0
  56. janito/cli_chat_shell/commands/__init__.py +45 -0
  57. janito/cli_chat_shell/commands/config.py +22 -0
  58. janito/cli_chat_shell/commands/history_reset.py +29 -0
  59. janito/cli_chat_shell/commands/session.py +48 -0
  60. janito/cli_chat_shell/commands/session_control.py +12 -0
  61. janito/cli_chat_shell/commands/system.py +73 -0
  62. janito/cli_chat_shell/commands/utility.py +29 -0
  63. janito/cli_chat_shell/config_shell.py +39 -10
  64. janito/cli_chat_shell/load_prompt.py +5 -2
  65. janito/cli_chat_shell/session_manager.py +24 -27
  66. janito/cli_chat_shell/ui.py +75 -40
  67. janito/rich_utils.py +15 -2
  68. janito/web/__main__.py +10 -2
  69. janito/web/app.py +88 -52
  70. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/METADATA +76 -11
  71. janito-1.6.0.dist-info/RECORD +81 -0
  72. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/WHEEL +1 -1
  73. janito/agent/rich_tool_handler.py +0 -43
  74. janito/agent/templates/system_instructions.j2 +0 -38
  75. janito/agent/tool_auto_imports.py +0 -5
  76. janito/agent/tools/append_text_to_file.py +0 -41
  77. janito/agent/tools/py_compile.py +0 -39
  78. janito/agent/tools/python_exec.py +0 -83
  79. janito/cli/runner.py +0 -137
  80. janito/cli_chat_shell/commands.py +0 -204
  81. janito/render_prompt.py +0 -13
  82. janito-1.5.2.dist-info/RECORD +0 -66
  83. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/entry_points.txt +0 -0
  84. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/licenses/LICENSE +0 -0
  85. {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/top_level.txt +0 -0
janito/rich_utils.py CHANGED
@@ -1,30 +1,43 @@
1
1
  """
2
2
  Utilities for working with the Rich library.
3
3
  """
4
+
4
5
  from rich.markdown import Markdown
5
6
  from rich.text import Text
6
7
  from rich.console import Console
7
8
 
9
+
8
10
  def print_markdown(console: Console, message: str):
9
11
  console.print(Markdown(message))
10
12
 
13
+
11
14
  def print_info(console: Console, message: str):
12
15
  console.print(message, style="cyan", end="")
13
16
 
17
+
14
18
  def print_success(console: Console, message: str):
15
19
  console.print(message, style="bold green", end="\n")
16
20
 
21
+
17
22
  def print_error(console: Console, message: str):
18
23
  console.print(message, style="bold red", end="\n")
19
24
 
25
+
20
26
  def print_warning(console: Console, message: str):
21
27
  console.print(message, style="bold yellow", end="\n")
22
28
 
29
+
23
30
  def print_magenta(console: Console, message: str):
24
31
  console.print(message, style="magenta", end="\n")
25
32
 
33
+
26
34
  def print_stdout(console: Console, message: str):
27
- console.print(Text(message, style="on #003300", no_wrap=True, overflow=None), end="")
35
+ console.print(
36
+ Text(message, style="on #003300", no_wrap=True, overflow=None), end=""
37
+ )
38
+
28
39
 
29
40
  def print_stderr(console: Console, message: str):
30
- console.print(Text(message, style="on #330000", no_wrap=True, overflow=None), end="")
41
+ console.print(
42
+ Text(message, style="on #330000", no_wrap=True, overflow=None), end=""
43
+ )
janito/web/__main__.py CHANGED
@@ -3,6 +3,14 @@ from . import app
3
3
 
4
4
 
5
5
  def main():
6
+ import os
7
+
8
+ # Ensure PYTHONUTF8 is set for consistent UTF-8 behavior
9
+ if os.environ.get("PYTHONUTF8") != "1":
10
+ os.environ["PYTHONUTF8"] = "1"
11
+ if os.name == "nt" and sys.argv[0]:
12
+ print("[info] Restarting web server with PYTHONUTF8=1 for UTF-8 support...")
13
+ os.execvpe(sys.executable, [sys.executable] + sys.argv, os.environ)
6
14
  port = 8000
7
15
  if len(sys.argv) > 1:
8
16
  try:
@@ -10,8 +18,8 @@ def main():
10
18
  except ValueError:
11
19
  print(f"Invalid port number: {sys.argv[1]}")
12
20
  sys.exit(1)
13
- app.app.run(host='0.0.0.0', port=port, debug=True)
21
+ app.app.run(host="0.0.0.0", port=port, debug=True)
14
22
 
15
23
 
16
- if __name__ == '__main__':
24
+ if __name__ == "__main__":
17
25
  main()
janito/web/app.py CHANGED
@@ -1,9 +1,16 @@
1
- from flask import Flask, request, Response, send_from_directory, jsonify, render_template
1
+ from flask import (
2
+ Flask,
3
+ request,
4
+ Response,
5
+ send_from_directory,
6
+ jsonify,
7
+ render_template,
8
+ )
2
9
  from queue import Queue
3
10
  import json
4
11
  from janito.agent.queued_message_handler import QueuedMessageHandler
5
- from janito.agent.agent import Agent
6
- from janito.render_prompt import render_system_prompt
12
+ from janito.agent.openai_client import Agent
13
+ from janito.agent.profile_manager import AgentProfileManager
7
14
  import os
8
15
  import threading
9
16
  import traceback
@@ -13,23 +20,34 @@ from janito.agent.runtime_config import unified_config
13
20
 
14
21
  # Render system prompt from config
15
22
  role = unified_config.get("role", "software engineer")
16
- system_prompt_override = unified_config.get("system_prompt")
17
- if system_prompt_override:
18
- system_prompt = system_prompt_override
23
+ system_prompt_template_override = unified_config.get("system_prompt_template")
24
+ if system_prompt_template_override:
25
+ system_prompt_template = system_prompt_template_override
19
26
  else:
20
- system_prompt = render_system_prompt(role)
27
+ profile_manager = AgentProfileManager(
28
+ api_key=unified_config.get("api_key"),
29
+ model=unified_config.get("model"),
30
+ role=role,
31
+ interaction_style=unified_config.get("interaction_style", "default"),
32
+ interaction_mode=unified_config.get("interaction_mode", "prompt"),
33
+ verbose_tools=unified_config.get("verbose_tools", False),
34
+ base_url=unified_config.get("base_url", None),
35
+ azure_openai_api_version=unified_config.get("azure_openai_api_version", None),
36
+ use_azure_openai=unified_config.get("use_azure_openai", False),
37
+ )
38
+ system_prompt_template = profile_manager.render_prompt()
21
39
 
22
40
  app = Flask(
23
41
  __name__,
24
- static_url_path='/static',
25
- static_folder=os.path.join(os.path.dirname(__file__), 'static')
42
+ static_url_path="/static",
43
+ static_folder=os.path.join(os.path.dirname(__file__), "static"),
26
44
  )
27
45
 
28
46
  # Secret key for session management
29
- app.secret_key = 'replace_with_a_secure_random_secret_key'
47
+ app.secret_key = "replace_with_a_secure_random_secret_key"
30
48
 
31
49
  # Path for persistent conversation storage
32
- conversation_file = os.path.expanduser('~/.janito/last_conversation_web.json')
50
+ conversation_file = os.path.expanduser("~/.janito/last_conversation_web.json")
33
51
 
34
52
  # Initially no conversation loaded
35
53
  conversation = None
@@ -45,14 +63,18 @@ message_handler = QueuedMessageHandler(stream_queue)
45
63
  agent = Agent(
46
64
  api_key=unified_config.get("api_key"),
47
65
  model=unified_config.get("model"),
48
- base_url=unified_config.get("base_url")
66
+ base_url=unified_config.get("base_url"),
49
67
  )
50
68
 
51
- @app.route('/get_config')
69
+
70
+ @app.route("/get_config")
52
71
  def get_config():
53
72
  # Expose full config for the web app: defaults, effective, runtime (mask api_key)
54
- from janito.agent.runtime_config import unified_config # Kept here: avoids circular import at module level
73
+ from janito.agent.runtime_config import (
74
+ unified_config,
75
+ ) # Kept here: avoids circular import at module level
55
76
  from janito.agent.config_defaults import CONFIG_DEFAULTS
77
+
56
78
  # Start with defaults
57
79
  config = dict(CONFIG_DEFAULTS)
58
80
  # Overlay effective config
@@ -61,19 +83,26 @@ def get_config():
61
83
  config.update(unified_config.runtime_cfg.all())
62
84
  api_key = config.get("api_key")
63
85
  if api_key:
64
- config["api_key"] = api_key[:4] + '...' + api_key[-4:] if len(api_key) > 8 else '***'
86
+ config["api_key"] = (
87
+ api_key[:4] + "..." + api_key[-4:] if len(api_key) > 8 else "***"
88
+ )
65
89
  return jsonify(config)
66
90
 
67
- @app.route('/set_config', methods=['POST'])
91
+
92
+ @app.route("/set_config", methods=["POST"])
68
93
  def set_config():
69
94
  from janito.agent.runtime_config import runtime_config
70
95
  from janito.agent.config import CONFIG_OPTIONS
71
96
  from janito.agent.config_defaults import CONFIG_DEFAULTS
97
+
72
98
  data = request.get_json()
73
- key = data.get('key')
74
- value = data.get('value')
99
+ key = data.get("key")
100
+ value = data.get("value")
75
101
  if key not in CONFIG_OPTIONS:
76
- return jsonify({'status': 'error', 'message': f'Invalid config key: {key}'}), 400
102
+ return (
103
+ jsonify({"status": "error", "message": f"Invalid config key: {key}"}),
104
+ 400,
105
+ )
77
106
  # Type coercion based on defaults
78
107
  default = CONFIG_DEFAULTS.get(key)
79
108
  if default is not None and value is not None:
@@ -86,48 +115,56 @@ def set_config():
86
115
  value = float(value)
87
116
  # else: leave as string or None
88
117
  except Exception as e:
89
- return jsonify({'status': 'error', 'message': f'Invalid value type for {key}: {e}'}), 400
118
+ return (
119
+ jsonify(
120
+ {"status": "error", "message": f"Invalid value type for {key}: {e}"}
121
+ ),
122
+ 400,
123
+ )
90
124
  runtime_config.set(key, value)
91
125
  # Mask api_key in response
92
126
  resp_value = value
93
- if key == 'api_key' and value:
94
- resp_value = value[:4] + '...' + value[-4:] if len(value) > 8 else '***'
95
- return jsonify({'status': 'ok', 'key': key, 'value': resp_value})
127
+ if key == "api_key" and value:
128
+ resp_value = value[:4] + "..." + value[-4:] if len(value) > 8 else "***"
129
+ return jsonify({"status": "ok", "key": key, "value": resp_value})
96
130
 
97
131
 
98
- @app.route('/favicon.ico')
132
+ @app.route("/favicon.ico")
99
133
  def favicon():
100
134
  return send_from_directory(
101
- os.path.join(app.root_path, 'static'),
102
- 'favicon.ico',
103
- mimetype='image/vnd.microsoft.icon'
135
+ os.path.join(app.root_path, "static"),
136
+ "favicon.ico",
137
+ mimetype="image/vnd.microsoft.icon",
104
138
  )
105
139
 
106
140
 
107
- @app.route('/')
141
+ @app.route("/")
108
142
  def index():
109
- return render_template('index.html')
143
+ return render_template("index.html")
144
+
110
145
 
111
- @app.route('/load_conversation')
146
+ @app.route("/load_conversation")
112
147
  def load_conversation():
113
148
  global conversation
114
149
  try:
115
- with open(conversation_file, 'r') as f:
150
+ with open(conversation_file, "r") as f:
116
151
  conversation = json.load(f)
117
152
  except (FileNotFoundError, json.JSONDecodeError):
118
153
  conversation = []
119
- return jsonify({'status': 'ok', 'conversation': conversation})
154
+ return jsonify({"status": "ok", "conversation": conversation})
155
+
120
156
 
121
- @app.route('/new_conversation', methods=['POST'])
157
+ @app.route("/new_conversation", methods=["POST"])
122
158
  def new_conversation():
123
159
  global conversation
124
160
  conversation = []
125
- return jsonify({'status': 'ok'})
161
+ return jsonify({"status": "ok"})
126
162
 
127
- @app.route('/execute_stream', methods=['POST'])
163
+
164
+ @app.route("/execute_stream", methods=["POST"])
128
165
  def execute_stream():
129
166
  data = request.get_json()
130
- user_input = data.get('input', '')
167
+ user_input = data.get("input", "")
131
168
 
132
169
  global conversation
133
170
  if conversation is None:
@@ -135,23 +172,22 @@ def execute_stream():
135
172
  conversation = []
136
173
 
137
174
  # Always start with the system prompt as the first message
138
- if not conversation or conversation[0]['role'] != 'system':
139
- conversation.insert(0, {"role": "system", "content": system_prompt})
175
+ if not conversation or conversation[0]["role"] != "system":
176
+ conversation.insert(0, {"role": "system", "content": system_prompt_template})
140
177
 
141
178
  # Append the new user message
142
179
  conversation.append({"role": "user", "content": user_input})
143
180
 
144
181
  def run_agent():
145
182
  try:
146
- response = agent.chat(
147
- conversation,
148
- message_handler=message_handler
149
- )
150
- if response and 'content' in response:
151
- conversation.append({"role": "assistant", "content": response['content']})
183
+ response = agent.chat(conversation, message_handler=message_handler)
184
+ if response and "content" in response:
185
+ conversation.append(
186
+ {"role": "assistant", "content": response["content"]}
187
+ )
152
188
  try:
153
189
  os.makedirs(os.path.dirname(conversation_file), exist_ok=True)
154
- with open(conversation_file, 'w') as f:
190
+ with open(conversation_file, "w") as f:
155
191
  json.dump(conversation, f, indent=2)
156
192
  except Exception as e:
157
193
  print(f"Error saving conversation: {e}")
@@ -168,7 +204,7 @@ def execute_stream():
168
204
  content = stream_queue.get()
169
205
  if content is None:
170
206
  break
171
- if isinstance(content, tuple) and content[0] == 'tool_progress':
207
+ if isinstance(content, tuple) and content[0] == "tool_progress":
172
208
  message = json.dumps({"type": "tool_progress", "data": content[1]})
173
209
  else:
174
210
  message = json.dumps(content)
@@ -177,11 +213,11 @@ def execute_stream():
177
213
 
178
214
  return Response(
179
215
  generate(),
180
- mimetype='text/event-stream',
216
+ mimetype="text/event-stream",
181
217
  headers={
182
- 'Cache-Control': 'no-cache',
183
- 'X-Accel-Buffering': 'no',
184
- 'Connection': 'keep-alive',
185
- 'Transfer-Encoding': 'chunked'
186
- }
218
+ "Cache-Control": "no-cache",
219
+ "X-Accel-Buffering": "no",
220
+ "Connection": "keep-alive",
221
+ "Transfer-Encoding": "chunked",
222
+ },
187
223
  )
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 1.5.2
4
- Summary: A Natural Programming Language Agent,
3
+ Version: 1.6.0
4
+ Summary: A Natural Language Programming Agent,
5
5
  Author-email: João Pinto <joao.pinto@gmail.com>
6
6
  License: MIT
7
7
  Project-URL: homepage, https://github.com/joaompinto/janito
@@ -23,12 +23,12 @@ Requires-Dist: requests
23
23
  Requires-Dist: rich
24
24
  Dynamic: license-file
25
25
 
26
- # 🚀 Janito: Natural Programming Language Agent
26
+ # 🚀 Janito: Natural Language Programming Agent
27
27
 
28
- **Current Version: 1.5.x**
29
- See [docs/CHANGELOG.md](docs/CHANGELOG.md) and [RELEASE_NOTES_1.5.md](./RELEASE_NOTES_1.5.md) for details on the latest release.
28
+ **Current Version: 1.6.0**
29
+ See [CHANGELOG.md](./CHANGELOG.md) and [RELEASE_NOTES_1.6.md](./RELEASE_NOTES_1.6.md) for details on the latest release.
30
30
 
31
- Janito is an AI-powered assistant for the command line and web that interprets natural language instructions to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
31
+ Janito is an AI-powered assistant for the command line and web that interprets natural language system_prompt_template to edit code, manage files, and analyze projects using patterns and tools designed by experienced software engineers. It prioritizes transparency, interactive clarification, and precise, reviewable changes.
32
32
 
33
33
  For a technical overview, see the [Architecture Guide](docs/ARCHITECTURE.md).
34
34
 
@@ -51,6 +51,7 @@ You can alter Janito's behavior in any interface using these flags:
51
51
 
52
52
  - `--system` / `--system-file`: Override or customize the system prompt for the session.
53
53
  - `--no-tools`: Disable all tool usage (Janito will only use the language model, no file/code/shell actions).
54
+ - `--trust-tools`/`-T`: Trusted tools mode (suppresses all tool output, only shows output file locations).
54
55
  - `--vanilla`: Disables tools, system prompt, and temperature settings for a pure LLM chat experience.
55
56
 
56
57
  These modifiers can be combined with any interface mode for tailored workflows.
@@ -92,11 +93,11 @@ janito --web
92
93
 
93
94
  ### Contributing & Developer Guide
94
95
 
95
- If you want to extend Janito or add new tools, see the [Developer Guide](docs/README_DEV.md) for instructions, tool registration requirements, and code style guidelines. For the full list of built-in tools and their usage, see the [Tools Reference](janito/agent/tools/README.md).
96
+ If you want to extend Janito or add new tools, see the [Developer Guide](docs/README_DEV.md) for system_prompt_template, tool registration requirements, and code style guidelines. For the full list of built-in tools and their usage, see the [Tools Reference](janito/agent/tools/README.md).
96
97
 
97
98
 
98
99
 
99
- For the full changelog, see [docs/CHANGELOG.md](docs/CHANGELOG.md).
100
+ For the full changelog, see [CHANGELOG.md](./CHANGELOG.md).
100
101
 
101
102
  ...
102
103
 
@@ -134,16 +135,16 @@ For details on using models hosted on Azure OpenAI, see [docs/AZURE_OPENAI.md](d
134
135
  Janito operates using a system prompt template that defines its behavior, communication style, and capabilities. By default, Janito assumes the role of a "software engineer"—this means its responses and actions are tailored to the expectations and best practices of professional software engineering.
135
136
 
136
137
  - **Role:** You can customize the agent's role (e.g., "data scientist", "DevOps engineer") using the `--role` flag or config. The default is `software engineer`.
137
- - **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_instructions.j2`). This template governs how the agent interprets instructions, interacts with files, and communicates with users.
138
+ - **System Prompt Template:** The system prompt is rendered from a Jinja2 template (see `janito/agent/templates/system_prompt_template.j2`). This template governs how the agent interprets system_prompt_template, interacts with files, and communicates with users.
138
139
  - **Customization & Precedence:** Advanced users can override the system prompt with the `--system` flag (raw string), or point to a custom file using `--system-file`. The precedence is: `--system-file` > `--system`/config > default template.
139
140
 
140
141
  The default template ensures the agent:
141
142
  - Prioritizes safe, reviewable, and minimal changes
142
- - Asks for clarification when instructions are ambiguous
143
+ - Asks for clarification when system_prompt_template are ambiguous
143
144
  - Provides concise plans before taking action
144
145
  - Documents any changes made
145
146
 
146
- For more details or to customize the prompt, see the template file at `janito/agent/templates/system_instructions.j2` and the architecture overview in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
147
+ For more details or to customize the prompt, see the template file at `janito/agent/templates/system_prompt_template.j2` and the architecture overview in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
147
148
 
148
149
  ---
149
150
 
@@ -174,3 +175,67 @@ Vanilla mode is ideal for:
174
175
  - Ensuring no agent-side intervention or context is added
175
176
 
176
177
  > Note: Vanilla mode is a runtime switch and does not change the Agent API or class signatures. It is controlled via CLI/config only.
178
+
179
+ ## 🧑‍💻 AgentProfileManager: Profile, Role, and Prompt Management
180
+
181
+ Janito now uses a dedicated `AgentProfileManager` class to manage user profiles, roles, interaction styles, and system prompt selection. This manager:
182
+ - Stores the current role (e.g., "software engineer") and interaction style (e.g., "default", "technical").
183
+ - Renders the system prompt from the appropriate template based on interaction style.
184
+ - Instantiates and manages the low-level LLM Agent, passing the correct prompt.
185
+ - Provides methods to update the role, interaction style, and refresh the prompt at runtime.
186
+
187
+ ### Multiple System Prompt Templates
188
+ - The system prompt template is now selected based on the interaction style (e.g., `default` or `technical`).
189
+ - Templates are located in `janito/agent/templates/` (see `system_prompt_template.j2` and `system_prompt_template_technical.j2`).
190
+ - You can switch interaction styles at runtime using the profile manager, enabling different agent behaviors for different user needs.
191
+
192
+ This separation ensures that the LLM Agent remains focused on language model interaction and tool execution, while all profile, role, and prompt logic is managed at a higher level.
193
+
194
+ See `janito/agent/profile_manager.py` for implementation details.
195
+
196
+ ### Agent Interaction Style
197
+
198
+ You can control the agent's behavior and prompt style globally or per-project using the `interaction_style` config key. Supported values:
199
+ - `default`: Concise, general-purpose agent (default)
200
+ - `technical`: Strict, workflow-oriented for technical/developer use
201
+
202
+ Set globally:
203
+ ```bash
204
+ janito --set-global-config interaction_style=technical
205
+ ```
206
+ Or per-project (in your project root):
207
+ ```bash
208
+ janito --set-local-config interaction_style=technical
209
+ ```
210
+
211
+ You can also override for a session with the CLI flag:
212
+ ```bash
213
+ janito --style technical
214
+ ```
215
+
216
+ See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) for full details.
217
+
218
+ ## 🧩 Combinatorial Style System
219
+
220
+ Janito now supports combinatorial styles for system prompts, allowing you to combine a main style (such as `default` or `technical`) with one or more feature extensions (such as `commit_all`).
221
+
222
+ - **Main style:** The base agent behavior and workflow (e.g., `default`, `technical`).
223
+ - **Feature extensions:** Optional features that override or extend the main style (e.g., `commit_all`).
224
+ - **Syntax:** Use a hyphen to combine, e.g., `technical-commit_all`.
225
+
226
+ **How it works:**
227
+ - The main style template is loaded first.
228
+ - Each feature extension template is layered on top, overriding or extending specific blocks in the main template.
229
+ - Feature templates must use `{% extends parent_template %}` for dynamic inheritance.
230
+
231
+ **Example usage:**
232
+ ```bash
233
+ janito --style technical-commit_all
234
+ ```
235
+
236
+ This will apply the `technical` style with the `commit_all` feature enabled in the agent's system prompt.
237
+
238
+ See `janito/render_prompt.py` and `janito/agent/templates/` for implementation details and to create your own feature extensions.
239
+
240
+ ---
241
+ _generated by janito.dev_
@@ -0,0 +1,81 @@
1
+ janito/__init__.py,sha256=PTA-UJ0gOgRLnjvcmghK_Up95KKEx6TmFCJ8J8l7GRI,23
2
+ janito/__main__.py,sha256=KKIoPBE9xPcb54PRYO2UOt0ti04iAwLeJlg8YY36vew,76
3
+ janito/rich_utils.py,sha256=gZ4uAYyAeSk_H1uCGYhYkFoel5Z1SDmNhp241pnniqg,1085
4
+ janito/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ janito/agent/config.py,sha256=gD2puBUjKbb8bv_KbGUBP7OeeW0JqEEvYQXmIjv0avE,4617
6
+ janito/agent/config_defaults.py,sha256=dL3_8mRzEd7c_CF6GQe_-LAN3vWZpOFpm2cgaitpkYo,482
7
+ janito/agent/config_utils.py,sha256=UmvR236wDrMc-aTy9LxVbop6YeoJaaPb1d2DBMlkSRg,254
8
+ janito/agent/content_handler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ janito/agent/conversation.py,sha256=fFdWmyxUBbqm3LWL1INVd1MKAQS7EbTPuGRBBAjWom8,4956
10
+ janito/agent/conversation_api.py,sha256=v85_BwcyNceeYZorDKnjpgpDUY5sfvTwQMoVjXxAdmE,3441
11
+ janito/agent/conversation_exceptions.py,sha256=F36wOPRx0yFaiQUuVqxktkRdNlnXVOUovAEdB40poNM,260
12
+ janito/agent/conversation_tool_calls.py,sha256=7RWy_TVxXZY8Cmafy2XtSF1sCLf9O3NIC7dw1PtVYqk,874
13
+ janito/agent/conversation_ui.py,sha256=y4f0IoJQoWGrFMB3yi7uIwXokuTjhFtJGK_R7zcTv3w,397
14
+ janito/agent/message_handler.py,sha256=Za4xUflbRvamlJP_4_YEStOiXuCYYjI1uQkHabTaUtA,754
15
+ janito/agent/openai_client.py,sha256=LM-94jjmARIuuASMCZeJ6N8_seobaQ_lbkeksTLZl24,4624
16
+ janito/agent/openai_schema_generator.py,sha256=hqe7VVIf5mPcnSn38_MOlI9HJtTayN-aAXJz9iwxp4s,4821
17
+ janito/agent/profile_manager.py,sha256=WSd8ZaOCF5MN4EvSIysl-w3TuTaExi8247BASoyta2E,6364
18
+ janito/agent/queued_message_handler.py,sha256=sWyadc4h1xkFn_kgO0Hp5lgzj_8J23i-tV31M1lNtV0,1422
19
+ janito/agent/rich_live.py,sha256=NKWU89hRakiJ-0N6FPg40bYREOxQizqicbDv9eUfsKs,927
20
+ janito/agent/rich_message_handler.py,sha256=JjoGLJThYd-nO5qLB2FsAyaHoTOS4lTEPP-N91nZPeA,2301
21
+ janito/agent/runtime_config.py,sha256=xSx0-RD-WVA9niSCEmEn2ZPLFbQfRhPwwGIa8tid_v8,901
22
+ janito/agent/tool_base.py,sha256=b7WJkoCUObFK-WjUXfUm874UmKujYQFAyC7FVkxDOMo,1901
23
+ janito/agent/tool_registry.py,sha256=Wa1mNzTBKsxQyFCc2hQgDDRrb-m6TI_XbBGebCzr-6U,4037
24
+ janito/agent/tool_registry_core.py,sha256=bF7rRn3ZMpU7JXBFhYQPg2XxczhN572Xc5pbdLBPkvE,148
25
+ janito/agent/tools/__init__.py,sha256=68YCMWtkepgjOBkptPNmQpv7Tgb5_58uOWxAMR8fjII,979
26
+ janito/agent/tools/ask_user.py,sha256=ZQGKI-cjK8CG6O3elEf5v6c5iybVzgfNiZienK6I2oQ,2912
27
+ janito/agent/tools/create_directory.py,sha256=vIKP0kYnU3Ypik9e_iNwuwU7rkb5sdUtHMj2q8NA37A,1303
28
+ janito/agent/tools/create_file.py,sha256=wkPpo42KS3958Jialjyt8_Lv0CXIRgOmUI5EJ7wNw3o,2230
29
+ janito/agent/tools/fetch_url.py,sha256=xsNdxfyDBj6nLYBsMiQoBr685-TUxRx3s3893F7E2ek,2027
30
+ janito/agent/tools/find_files.py,sha256=CMUAAC-3skw2OKbu_wN6SFBeiVx-xWgVrA5_u4a6sLc,2604
31
+ janito/agent/tools/get_file_outline.py,sha256=e9RNijfE5koaSB0mJe-L3j-fjG4I1YgTJeToDMTTFuY,5348
32
+ janito/agent/tools/get_lines.py,sha256=pCV9ut6-GTRj5rBCp8hijTAwh37D8hPXScOjuy1HWUQ,3926
33
+ janito/agent/tools/gitignore_utils.py,sha256=fDLyRZmCu6Hqhp1BEN8hs7oG0FtA-1SM9TGDz4g0lbw,1470
34
+ janito/agent/tools/move_file.py,sha256=6-90TNlL9JnhWMFCtoy59TRZq2xk6VrP8Mv-EpPDP80,2568
35
+ janito/agent/tools/py_compile_file.py,sha256=pfHDXVa75WhO5Io_rXF0DnIB8y2BypoAsS4wNCVANeE,1601
36
+ janito/agent/tools/remove_directory.py,sha256=oMme-7e6hZG5Z0Wxu6SpdWjop_yMMZlrBtwkoqfn2NY,2192
37
+ janito/agent/tools/remove_file.py,sha256=dq17r-vd61LGS6RLic77BlD6ZzuAjWBBUGtUsX5PCTk,1731
38
+ janito/agent/tools/replace_file.py,sha256=oex5dncfny0Lv9om3LVs1u77IOemKZ7Cjh4IRIz_paM,2381
39
+ janito/agent/tools/replace_text_in_file.py,sha256=DNafuM6CFTKVEimP6jb9IHglG3BGCezQDLRbqSJ6TfI,5288
40
+ janito/agent/tools/rich_live.py,sha256=KpAN-dF0h9LZnYvSK-UKR_sbDnnDw0jUxY3cj_EW_wQ,1215
41
+ janito/agent/tools/run_bash_command.py,sha256=yDyVVQspaV_bNqOoAyplwczOpwu62MhNMHsQGkV37s8,6878
42
+ janito/agent/tools/run_python_command.py,sha256=_NdpRMNeHIf4h0ARaYUyO7ncHEbLXICFk8uJVdwpCPU,6099
43
+ janito/agent/tools/search_files.py,sha256=iyED-9QW1zGEJWIlqHPTQYanMDZA8vK4qIQ03eO4ZkU,3240
44
+ janito/agent/tools/tools_utils.py,sha256=ud0S8uVe1I3hKQBdQMf_RcYEDt7baTGBGeDd5IU23-o,358
45
+ janito/agent/tools/utils.py,sha256=zsW-ROZJYYFeDAhwN0YHMAqsnjfBXPdvJJVAa_TmQ1g,1136
46
+ janito/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ janito/cli/_print_config.py,sha256=VRv_KSVaeMmqmF5cHQC0YfRqem3LgcsXoWRn6fyNJfQ,3566
48
+ janito/cli/_utils.py,sha256=tRAUMDWKczd81ZvKYkwpsHWSeLzQoVlOoQ-lOw9Iujw,291
49
+ janito/cli/arg_parser.py,sha256=AXzq6TIRjD7EBWb176CCszMz7LWuCtMu4c0a03XRQk8,5897
50
+ janito/cli/config_commands.py,sha256=CHIeqxs6rwAfvfaq_hZYrSdWtG0oQjsZBfMo95RUfLk,8531
51
+ janito/cli/logging_setup.py,sha256=u2FxjJo-klLsY6aHxBuDymDYnuw9bc--1aqqsVeW9jc,1213
52
+ janito/cli/main.py,sha256=2_fy_wPu7wAL-pxvdC65wxu6Wz4sFTTP5N6SLShT5Ak,1289
53
+ janito/cli/runner/__init__.py,sha256=LBu8S8JS73biePK_WwSIlRBwM1c5DX5DGklN7acIyO0,70
54
+ janito/cli/runner/cli_main.py,sha256=hKbvMmWZHm01z9qSUYTJ8bUUkE1o3YC55S7PYGN4Eqk,5446
55
+ janito/cli/runner/config.py,sha256=7uqzw7vIA_SCCJFACjE_pHtPb9l9IXsaZW-ra_0AdQ0,1705
56
+ janito/cli/runner/formatting.py,sha256=k0mtHoglqR8fKcebSK81iWTT_EL-gDl7eNfjlFZRY6g,287
57
+ janito/cli/runner/scan.py,sha256=QTA67URhdJtIE8jw7aA2NA0cal1d64G7v36NCH0E0w4,1760
58
+ janito/cli_chat_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ janito/cli_chat_shell/chat_loop.py,sha256=DD71TzcJublz3I3zRiylw6EPD2SuGNah2P81FZQmYVo,4450
60
+ janito/cli_chat_shell/chat_state.py,sha256=5wLSjZV0y5fHyU-Lkl4J4-G2GRY0n7gfcAof3515_Bo,1162
61
+ janito/cli_chat_shell/chat_ui.py,sha256=IbP2OVGkLycgcUBvdgbKyIUbCnMduhvoSMYYKlpWULc,1344
62
+ janito/cli_chat_shell/config_shell.py,sha256=sG04S_z27wI7nXKMaVDcwDpBCuo6Cvd4uhutUQesJKo,3807
63
+ janito/cli_chat_shell/load_prompt.py,sha256=8AyHaADBk5_berjt10fISG3Z9Bu5RnVUs2BzNEYOPIQ,633
64
+ janito/cli_chat_shell/session_manager.py,sha256=qzCDpAAXx_YZRVF4GwWPwbkxROwj_14jF52QjkjDGP4,2136
65
+ janito/cli_chat_shell/ui.py,sha256=5a5lwlEQs211R6Pr_WYt9HuMoQMjTkbamyoHZC5lsaI,6616
66
+ janito/cli_chat_shell/commands/__init__.py,sha256=D06FCOmKCFf02Li63KlkAtW753z5fSGJ_AylMJGvk9A,1378
67
+ janito/cli_chat_shell/commands/config.py,sha256=JTQwIuSBunxDwvGsU_Cu78GkQNYCtDFTPZ7HOxNOZCY,945
68
+ janito/cli_chat_shell/commands/history_reset.py,sha256=GBY8Cpcm2yE1C3DhiWWQwS7WFYPxoJ3LPmD0hCx8ut8,1017
69
+ janito/cli_chat_shell/commands/session.py,sha256=64H9UYB-LRSWzMar_C7iNM06MqrKmpRm_Dk9XXIMCiM,1739
70
+ janito/cli_chat_shell/commands/session_control.py,sha256=7G4XYfI22fHguJgIbIrbcQ_fIbcrvd1gBBXRuYUqtBU,333
71
+ janito/cli_chat_shell/commands/system.py,sha256=ixKp6JLagcs6zsXsmTvzSNY4TUFIVzRxhiZfLvI-OJU,2717
72
+ janito/cli_chat_shell/commands/utility.py,sha256=QUa35hppatRLY24DuXiEIn7gCIcNz4w4qbkRC157QOU,936
73
+ janito/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ janito/web/__main__.py,sha256=5Ck6okOZmxKYkQ-ir4mxXDH7XWMNR-9szgsm0UyQLE0,734
75
+ janito/web/app.py,sha256=O4rQSSqRG-N2tk3ho0CTMeybbrCd8pESFqpmiGASHVk,7453
76
+ janito-1.6.0.dist-info/licenses/LICENSE,sha256=sHBqv0bvtrb29H7WRR-Z603YHm9pLtJIo3nHU_9cmgE,1091
77
+ janito-1.6.0.dist-info/METADATA,sha256=JyLiizuc2poewVQtV2F2-H4rdut0FK7u5QvyaNiUhMU,10729
78
+ janito-1.6.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
79
+ janito-1.6.0.dist-info/entry_points.txt,sha256=wIo5zZxbmu4fC-ZMrsKD0T0vq7IqkOOLYhrqRGypkx4,48
80
+ janito-1.6.0.dist-info/top_level.txt,sha256=m0NaVCq0-ivxbazE2-ND0EA9Hmuijj_OGkmCbnBcCig,7
81
+ janito-1.6.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.1)
2
+ Generator: setuptools (79.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,43 +0,0 @@
1
- from rich.console import Console
2
- console = Console()
3
-
4
- class MessageHandler:
5
- """
6
- Unified message handler for all output (tool, agent, system) using Rich for styled output.
7
- """
8
- def __init__(self):
9
- self.console = console
10
-
11
- def handle_message(self, msg, msg_type=None):
12
- """
13
- Handles either a dict (with 'type' and 'message') or a plain string.
14
- If dict: uses type/message. If str: uses msg_type or defaults to 'info'.
15
- """
16
- from rich.markdown import Markdown
17
- if isinstance(msg, dict):
18
- msg_type = msg.get("type", "info")
19
- message = msg.get("message", "")
20
- if msg_type == "content":
21
- self.console.print(Markdown(message))
22
- elif msg_type == "info":
23
- self.console.print(message, style="cyan", end="")
24
- elif msg_type == "success":
25
- self.console.print(message, style="bold green", end="\n")
26
- elif msg_type == "error":
27
- self.console.print(message, style="bold red", end="\n")
28
- elif msg_type == "progress":
29
- self._handle_progress(message)
30
- elif msg_type == "warning":
31
- self.console.print(message, style="bold yellow", end="\n")
32
- elif msg_type == "stdout":
33
- from rich.text import Text
34
- self.console.print(Text(message, style="on #003300", no_wrap=True, overflow=None), end="")
35
- elif msg_type == "stderr":
36
- from rich.text import Text
37
- self.console.print(Text(message, style="on #330000", no_wrap=True, overflow=None), end="")
38
- else:
39
- # Ignore unsupported message types silently
40
- return
41
- else:
42
- # Print plain strings as markdown/markup
43
- self.console.print(Markdown(str(msg)))
@@ -1,38 +0,0 @@
1
- Your Agent Profile:
2
- - Role: {{ role }}
3
-
4
- You are an Agent for an analysis and development tool that operates on files and directories using text-based operations.
5
-
6
- <agent_profile>
7
- This Agent operates according to its Agent Profile, which includes system settings, role, and tools.
8
- </agent_profile>
9
-
10
- Provide a concise plan before calling any tool.
11
- Plan changes only after gathering all the necessary information.
12
- Always execute the plan immediately after presenting it, unless the user requests otherwise.
13
-
14
- <context>
15
- Always review `README_structure.txt` before conducting file-specific searches.
16
- Unless specified otherwise, look for the files that match the questions context.
17
- Explore files that might be relevant to the current task.
18
- </context>
19
-
20
- <analysis>
21
- In case of missing code or functions, look into the .bak files and check git diff/history for recent changes.
22
- </analysis>
23
-
24
- <editing>
25
- If in doubt during editing, use the `ask_user` function to get additional information; otherwise, proceed and inform the user of the decision made.
26
-
27
- When you need to make changes to a file, consider the following:
28
- - It is preferred to replace exact text occurrences over file overwriting.
29
- - When replacing files, review their current content before requesting the update.
30
- - When reorganizing, moving files, or functions, search for references in other files that might need to be updated accordingly.
31
- - After making changes to files, use available tools (such as syntax checkers, linters, or test runners) to validate the files and ensure correctness before proceeding.
32
- </editing>
33
-
34
- <finishing>
35
- - When asked to commit, check the git diff and summarize the changes in the commit message.
36
- - Review the README content if there are user-exposed or public API changes.
37
- - Update `README_structure.txt` considering discovered, created, or modified files.
38
- </finishing>
@@ -1,5 +0,0 @@
1
- # janito/agent/tool_auto_imports.py
2
- # This module imports all tool modules to ensure they are registered via their decorators.
3
- # It should be imported only where tool auto-registration is needed, to avoid circular import issues.
4
-
5
- from janito.agent.tools import search_files, run_bash_command, replace_text_in_file, remove_file, remove_directory, py_compile, python_exec, move_file, get_lines, get_file_outline, find_files, fetch_url, create_file, create_directory, ask_user, append_text_to_file