janito 1.9.0__py3-none-any.whl → 1.11.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 (106) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/api_exceptions.py +4 -0
  3. janito/agent/config.py +1 -1
  4. janito/agent/config_defaults.py +2 -26
  5. janito/agent/conversation.py +163 -122
  6. janito/agent/conversation_api.py +246 -168
  7. janito/agent/conversation_ui.py +1 -1
  8. janito/agent/{conversation_history.py → llm_conversation_history.py} +30 -1
  9. janito/agent/openai_client.py +38 -23
  10. janito/agent/openai_schema_generator.py +162 -129
  11. janito/agent/platform_discovery.py +134 -77
  12. janito/agent/profile_manager.py +5 -5
  13. janito/agent/rich_message_handler.py +80 -31
  14. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +20 -4
  15. janito/agent/test_openai_schema_generator.py +93 -0
  16. janito/agent/tool_base.py +7 -2
  17. janito/agent/tool_executor.py +54 -49
  18. janito/agent/tool_registry.py +5 -2
  19. janito/agent/tool_use_tracker.py +26 -5
  20. janito/agent/tools/__init__.py +8 -3
  21. janito/agent/tools/create_directory.py +3 -1
  22. janito/agent/tools/create_file.py +7 -1
  23. janito/agent/tools/fetch_url.py +40 -3
  24. janito/agent/tools/find_files.py +29 -14
  25. janito/agent/tools/get_file_outline/core.py +7 -8
  26. janito/agent/tools/get_file_outline/python_outline.py +139 -95
  27. janito/agent/tools/get_file_outline/search_outline.py +3 -1
  28. janito/agent/tools/get_lines.py +98 -64
  29. janito/agent/tools/move_file.py +59 -31
  30. janito/agent/tools/open_url.py +31 -0
  31. janito/agent/tools/present_choices.py +3 -1
  32. janito/agent/tools/python_command_runner.py +149 -0
  33. janito/agent/tools/python_file_runner.py +147 -0
  34. janito/agent/tools/python_stdin_runner.py +153 -0
  35. janito/agent/tools/remove_directory.py +3 -1
  36. janito/agent/tools/remove_file.py +5 -1
  37. janito/agent/tools/replace_file.py +12 -2
  38. janito/agent/tools/replace_text_in_file.py +195 -149
  39. janito/agent/tools/run_bash_command.py +30 -69
  40. janito/agent/tools/run_powershell_command.py +138 -105
  41. janito/agent/tools/search_text/__init__.py +1 -0
  42. janito/agent/tools/search_text/core.py +176 -0
  43. janito/agent/tools/search_text/match_lines.py +58 -0
  44. janito/agent/tools/search_text/pattern_utils.py +65 -0
  45. janito/agent/tools/search_text/traverse_directory.py +127 -0
  46. janito/agent/tools/validate_file_syntax/core.py +43 -30
  47. janito/agent/tools/validate_file_syntax/html_validator.py +21 -5
  48. janito/agent/tools/validate_file_syntax/markdown_validator.py +77 -34
  49. janito/agent/tools_utils/action_type.py +7 -0
  50. janito/agent/tools_utils/dir_walk_utils.py +3 -2
  51. janito/agent/tools_utils/formatting.py +47 -21
  52. janito/agent/tools_utils/gitignore_utils.py +89 -40
  53. janito/agent/tools_utils/test_gitignore_utils.py +46 -0
  54. janito/agent/tools_utils/utils.py +7 -1
  55. janito/cli/_print_config.py +63 -61
  56. janito/cli/arg_parser.py +13 -12
  57. janito/cli/cli_main.py +137 -147
  58. janito/cli/config_commands.py +112 -109
  59. janito/cli/main.py +152 -174
  60. janito/cli/one_shot.py +40 -26
  61. janito/i18n/__init__.py +1 -1
  62. janito/rich_utils.py +46 -8
  63. janito/shell/commands/__init__.py +2 -4
  64. janito/shell/commands/conversation_restart.py +3 -1
  65. janito/shell/commands/edit.py +3 -0
  66. janito/shell/commands/history_view.py +3 -3
  67. janito/shell/commands/lang.py +3 -0
  68. janito/shell/commands/livelogs.py +5 -3
  69. janito/shell/commands/prompt.py +6 -0
  70. janito/shell/commands/session.py +3 -0
  71. janito/shell/commands/session_control.py +3 -0
  72. janito/shell/commands/termweb_log.py +8 -0
  73. janito/shell/commands/tools.py +3 -0
  74. janito/shell/commands/track.py +36 -0
  75. janito/shell/commands/utility.py +13 -18
  76. janito/shell/commands/verbose.py +3 -4
  77. janito/shell/input_history.py +62 -0
  78. janito/shell/main.py +160 -181
  79. janito/shell/session/config.py +83 -75
  80. janito/shell/session/manager.py +0 -21
  81. janito/shell/ui/interactive.py +97 -75
  82. janito/termweb/static/editor.css +32 -33
  83. janito/termweb/static/editor.css.bak +140 -22
  84. janito/termweb/static/editor.html +12 -7
  85. janito/termweb/static/editor.html.bak +16 -11
  86. janito/termweb/static/editor.js +94 -40
  87. janito/termweb/static/editor.js.bak +97 -65
  88. janito/termweb/static/index.html +1 -2
  89. janito/termweb/static/index.html.bak +1 -1
  90. janito/termweb/static/termweb.css +1 -22
  91. janito/termweb/static/termweb.css.bak +6 -4
  92. janito/termweb/static/termweb.js +0 -6
  93. janito/termweb/static/termweb.js.bak +1 -2
  94. janito/tests/test_rich_utils.py +44 -0
  95. janito/web/app.py +0 -75
  96. {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/METADATA +61 -42
  97. janito-1.11.0.dist-info/RECORD +163 -0
  98. {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/WHEEL +1 -1
  99. janito/agent/providers.py +0 -77
  100. janito/agent/tools/run_python_command.py +0 -161
  101. janito/agent/tools/search_text.py +0 -204
  102. janito/shell/commands/sum.py +0 -49
  103. janito-1.9.0.dist-info/RECORD +0 -151
  104. {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/entry_points.txt +0 -0
  105. {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/licenses/LICENSE +0 -0
  106. {janito-1.9.0.dist-info → janito-1.11.0.dist-info}/top_level.txt +0 -0
janito/cli/cli_main.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import sys
2
+ from janito.agent.llm_conversation_history import LLMConversationHistory
2
3
  import socket
3
4
  from janito.agent.profile_manager import AgentProfileManager
4
5
  from janito.agent.runtime_config import unified_config, runtime_config
@@ -17,19 +18,11 @@ def is_port_free(port):
17
18
  return s.connect_ex(("localhost", port)) != 0
18
19
 
19
20
 
20
- def run_cli(args):
21
- if args.version:
22
- print(f"janito version {__version__}")
23
- sys.exit(0)
24
-
25
- # Set vanilla mode if -V/--vanilla is passed
21
+ def normalize_args(args):
26
22
  if getattr(args, "vanilla", False):
27
23
  runtime_config.set("vanilla_mode", True)
28
-
29
- # Set no_tools_tracking if --ntt is passed
30
24
  if getattr(args, "ntt", False):
31
25
  runtime_config.set("no_tools_tracking", True)
32
- # Normalize all verbose flags into runtime_config
33
26
  for flag in [
34
27
  "verbose_http",
35
28
  "verbose_http_raw",
@@ -37,58 +30,49 @@ def run_cli(args):
37
30
  "verbose_reason",
38
31
  "verbose_tools",
39
32
  "verbose_events",
40
- "verbose_stream",
33
+ "verbose_messages",
41
34
  ]:
42
35
  if hasattr(args, flag):
43
36
  runtime_config.set(flag, getattr(args, flag, False))
44
-
45
- role = args.role or unified_config.get("role", "software engineer")
46
- if args.role:
47
- runtime_config.set("role", args.role)
37
+ if getattr(args, "trust_tools", False):
38
+ runtime_config.set("trust_tools", True)
48
39
  if getattr(args, "model", None):
49
40
  runtime_config.set("model", args.model)
50
41
  if getattr(args, "max_tools", None) is not None:
51
42
  runtime_config.set("max_tools", args.max_tools)
52
- if getattr(args, "trust_tools", False):
53
- runtime_config.set("trust_tools", True)
54
- if not getattr(args, "prompt", None):
55
- interaction_mode = "chat"
56
- else:
57
- interaction_mode = "prompt"
58
- profile = "base"
59
- # PATCH: Pass lang from args or runtime_config to AgentProfileManager
60
- lang = getattr(args, "lang", None) or runtime_config.get("lang", "en")
61
- profile_manager = AgentProfileManager(
43
+ if getattr(args, "verbose_reason", False):
44
+ runtime_config.set("verbose_reason", True)
45
+ if getattr(args, "max_tokens", None) is not None:
46
+ runtime_config.set("max_tokens", args.max_tokens)
47
+
48
+
49
+ def setup_profile_manager(args, role, interaction_mode, profile, lang):
50
+ return AgentProfileManager(
62
51
  api_key=get_api_key(),
63
52
  model=unified_config.get("model"),
64
53
  role=role,
65
54
  profile_name=profile,
66
55
  interaction_mode=interaction_mode,
67
56
  verbose_tools=args.verbose_tools,
68
- base_url=unified_config.get("base_url", "https://openrouter.ai/api/v1"),
57
+ base_url=unified_config.get("base_url", ""),
69
58
  azure_openai_api_version=unified_config.get(
70
59
  "azure_openai_api_version", "2023-05-15"
71
60
  ),
72
61
  use_azure_openai=unified_config.get("use_azure_openai", False),
73
62
  lang=lang,
74
63
  )
75
- profile_manager.refresh_prompt()
76
- if getattr(args, "show_system", False):
77
- print(profile_manager.render_prompt())
78
- sys.exit(0)
79
- if args.max_tokens is not None:
80
- runtime_config.set("max_tokens", args.max_tokens)
81
- if getattr(args, "verbose_reason", False):
82
- runtime_config.set("verbose_reason", True)
83
64
 
84
- # --- termweb integration ---
65
+
66
+ def handle_termweb(args, interaction_mode):
85
67
  termweb_proc = None
86
68
  selected_port = None
69
+ termweb_stdout_path = None
70
+ termweb_stderr_path = None
87
71
  if (
88
72
  not getattr(args, "no_termweb", False)
89
73
  and interaction_mode == "chat"
90
74
  and not runtime_config.get("vanilla_mode", False)
91
- and not getattr(args, "input_arg", None) # Prevent termweb in one-shot mode
75
+ and not getattr(args, "input_arg", None)
92
76
  ):
93
77
  default_port = 8088
94
78
  max_port = 8100
@@ -122,54 +106,131 @@ def run_cli(args):
122
106
  termweb_proc, started, termweb_stdout_path, termweb_stderr_path = start_termweb(
123
107
  selected_port
124
108
  )
125
- # Store last running port in .janito/config.json if started
126
109
  if started:
127
110
  from janito.agent.config import local_config
128
111
 
129
112
  local_config.set("termweb_last_running_port", selected_port)
130
113
  local_config.save()
114
+ return termweb_proc, termweb_stdout_path, termweb_stderr_path
115
+
116
+
117
+ def handle_continue_session(args):
118
+ continue_session = False
119
+ session_id = None
120
+ if getattr(args, "input_arg", None) or getattr(args, "continue_session", False):
121
+ _cont = getattr(args, "continue_session", False)
122
+ if _cont:
123
+ continue_session = True
124
+ session_id = getattr(args, "input_arg", None)
125
+ if session_id is None:
126
+ import os
127
+ import glob
128
+
129
+ chat_hist_dir = (
130
+ os.path.join(os.path.expanduser("~"), ".janito", "chat_history")
131
+ if not os.path.isabs(".janito")
132
+ else os.path.join(".janito", "chat_history")
133
+ )
134
+ if not os.path.exists(chat_hist_dir):
135
+ session_id = None
136
+ else:
137
+ files = glob.glob(os.path.join(chat_hist_dir, "*.json"))
138
+ if files:
139
+ latest = max(files, key=os.path.getmtime)
140
+ session_id = os.path.splitext(os.path.basename(latest))[0]
141
+ else:
142
+ session_id = None
143
+ else:
144
+ continue_session = False
145
+ session_id = None
146
+ return continue_session, session_id
147
+
148
+
149
+ def handle_prompt_mode(args, profile_manager):
150
+ prompt = getattr(args, "input_arg", None)
151
+ from rich.console import Console
152
+ from janito.agent.rich_message_handler import RichMessageHandler
153
+
154
+ console = Console()
155
+ message_handler = RichMessageHandler()
156
+ messages = []
157
+ system_prompt_override = runtime_config.get("system_prompt_template")
158
+ if system_prompt_override:
159
+ if not runtime_config.get("vanilla_mode", False) or getattr(
160
+ args, "system", None
161
+ ):
162
+ messages.append({"role": "system", "content": system_prompt_override})
163
+ elif profile_manager.system_prompt_template and not runtime_config.get(
164
+ "vanilla_mode", False
165
+ ):
166
+ messages.append(
167
+ {"role": "system", "content": profile_manager.system_prompt_template}
168
+ )
169
+ messages.append({"role": "user", "content": prompt})
170
+ import time
171
+
172
+ info_start_time = None
173
+ if getattr(args, "info", False):
174
+ info_start_time = time.time()
175
+ try:
176
+ max_rounds = 100
177
+ result = profile_manager.agent.chat(
178
+ LLMConversationHistory(messages),
179
+ message_handler=message_handler,
180
+ spinner=True,
181
+ max_rounds=max_rounds,
182
+ tool_user=getattr(args, "tool_user", False),
183
+ )
184
+ if (
185
+ getattr(args, "info", False)
186
+ and info_start_time is not None
187
+ and result is not None
188
+ ):
189
+ usage_info = result.get("usage")
190
+ total_tokens = usage_info.get("total_tokens") if usage_info else None
191
+ prompt_tokens = usage_info.get("prompt_tokens") if usage_info else None
192
+ completion_tokens = (
193
+ usage_info.get("completion_tokens") if usage_info else None
194
+ )
195
+ elapsed = time.time() - info_start_time
196
+ console.print(
197
+ f"[bold green]Total tokens:[/] [yellow]{total_tokens}[/yellow] [bold green]| Input:[/] [cyan]{prompt_tokens}[/cyan] [bold green]| Output:[/] [magenta]{completion_tokens}[/magenta] [bold green]| Elapsed:[/] [yellow]{elapsed:.2f}s[/yellow]",
198
+ style="dim",
199
+ )
200
+ except MaxRoundsExceededError:
201
+ console.print("[red]Max conversation rounds exceeded.[/red]")
202
+ except ProviderError as e:
203
+ console.print(f"[red]Provider error:[/red] {e}")
204
+ except EmptyResponseError as e:
205
+ console.print(f"[red]Error:[/red] {e}")
206
+
131
207
 
132
- # --- End termweb integration ---
208
+ def run_cli(args):
209
+ if args.version:
210
+ print(f"janito version {__version__}")
211
+ sys.exit(0)
212
+ normalize_args(args)
213
+ role = args.role or unified_config.get("role", "software engineer")
214
+ if args.role:
215
+ runtime_config.set("role", args.role)
216
+ interaction_mode = "chat" if not getattr(args, "prompt", None) else "prompt"
217
+ profile = "base"
218
+ lang = getattr(args, "lang", None) or runtime_config.get("lang", "en")
219
+ profile_manager = setup_profile_manager(args, role, interaction_mode, profile, lang)
220
+ profile_manager.refresh_prompt()
221
+ if getattr(args, "show_system", False):
222
+ print(profile_manager.render_prompt())
223
+ sys.exit(0)
224
+ termweb_proc, termweb_stdout_path, termweb_stderr_path = handle_termweb(
225
+ args, interaction_mode
226
+ )
133
227
  try:
134
- livereload_stdout_path = None
135
- livereload_stderr_path = None
136
- continue_session = False
137
- session_id = None
228
+ continue_session, session_id = handle_continue_session(args)
138
229
  if getattr(args, "input_arg", None):
139
230
  from janito.cli.one_shot import run_oneshot_mode
140
231
 
141
232
  run_oneshot_mode(args, profile_manager, runtime_config)
142
233
  return
143
- if not getattr(args, "input_arg", None) or getattr(
144
- args, "continue_session", False
145
- ):
146
- # Determine continue_session and session_id
147
- _cont = getattr(args, "continue_session", False)
148
- if _cont:
149
- continue_session = True
150
- session_id = getattr(args, "input_arg", None)
151
- if session_id is None:
152
- # Find the most recent session id from .janito/chat_history/*.json
153
- import os
154
- import glob
155
-
156
- chat_hist_dir = (
157
- os.path.join(os.path.expanduser("~"), ".janito", "chat_history")
158
- if not os.path.isabs(".janito")
159
- else os.path.join(".janito", "chat_history")
160
- )
161
- if not os.path.exists(chat_hist_dir):
162
- session_id = None
163
- else:
164
- files = glob.glob(os.path.join(chat_hist_dir, "*.json"))
165
- if files:
166
- latest = max(files, key=os.path.getmtime)
167
- session_id = os.path.splitext(os.path.basename(latest))[0]
168
- else:
169
- session_id = None
170
- else:
171
- continue_session = False
172
- session_id = None
173
234
  import time
174
235
 
175
236
  info_start_time = None
@@ -179,18 +240,10 @@ def run_cli(args):
179
240
  profile_manager,
180
241
  continue_session=continue_session,
181
242
  session_id=session_id,
182
- termweb_stdout_path=(
183
- termweb_stdout_path if "termweb_stdout_path" in locals() else None
184
- ),
185
- termweb_stderr_path=(
186
- termweb_stderr_path if "termweb_stderr_path" in locals() else None
187
- ),
188
- livereload_stdout_path=(
189
- livereload_stdout_path if "livereload_stdout_path" in locals() else None
190
- ),
191
- livereload_stderr_path=(
192
- livereload_stderr_path if "livereload_stderr_path" in locals() else None
193
- ),
243
+ termweb_stdout_path=termweb_stdout_path,
244
+ termweb_stderr_path=termweb_stderr_path,
245
+ livereload_stdout_path=None,
246
+ livereload_stderr_path=None,
194
247
  )
195
248
  if (
196
249
  getattr(args, "info", False)
@@ -200,75 +253,12 @@ def run_cli(args):
200
253
  elapsed = time.time() - info_start_time
201
254
  from rich.console import Console
202
255
 
203
- console = Console()
204
256
  total_tokens = usage_info.get("total_tokens")
257
+ console = Console()
205
258
  console.print(
206
259
  f"[bold green]Total tokens used:[/] [yellow]{total_tokens}[/yellow] [bold green]| Elapsed time:[/] [yellow]{elapsed:.2f}s[/yellow]"
207
260
  )
208
261
  sys.exit(0)
209
- # --- Prompt mode ---
210
- prompt = getattr(args, "input_arg", None)
211
- from rich.console import Console
212
- from janito.agent.rich_message_handler import RichMessageHandler
213
-
214
- console = Console()
215
- message_handler = RichMessageHandler()
216
- messages = []
217
- system_prompt_override = runtime_config.get("system_prompt_template")
218
- if system_prompt_override:
219
- # Só adiciona system prompt se NÃO for vanilla, ou se foi explicitamente passado via --system
220
- if not runtime_config.get("vanilla_mode", False) or getattr(
221
- args, "system", None
222
- ):
223
- messages.append({"role": "system", "content": system_prompt_override})
224
- elif profile_manager.system_prompt_template and not runtime_config.get(
225
- "vanilla_mode", False
226
- ):
227
- messages.append(
228
- {"role": "system", "content": profile_manager.system_prompt_template}
229
- )
230
- messages.append({"role": "user", "content": prompt})
231
- import time
232
-
233
- info_start_time = None
234
- if getattr(args, "info", False):
235
- info_start_time = time.time()
236
- try:
237
- max_rounds = 100
238
- from janito.agent.conversation_history import ConversationHistory
239
-
240
- result = profile_manager.agent.chat(
241
- ConversationHistory(messages),
242
- message_handler=message_handler,
243
- spinner=True,
244
- max_rounds=max_rounds,
245
- stream=getattr(args, "stream", False),
246
- )
247
- if (
248
- getattr(args, "info", False)
249
- and info_start_time is not None
250
- and result is not None
251
- ):
252
- usage_info = result.get("usage")
253
- total_tokens = usage_info.get("total_tokens") if usage_info else None
254
- prompt_tokens = usage_info.get("prompt_tokens") if usage_info else None
255
- completion_tokens = (
256
- usage_info.get("completion_tokens") if usage_info else None
257
- )
258
- elapsed = time.time() - info_start_time
259
- from rich.console import Console
260
-
261
- console = Console()
262
- console.print(
263
- f"[bold green]Total tokens:[/] [yellow]{total_tokens}[/yellow] [bold green]| Input:[/] [cyan]{prompt_tokens}[/cyan] [bold green]| Output:[/] [magenta]{completion_tokens}[/magenta] [bold green]| Elapsed:[/] [yellow]{elapsed:.2f}s[/yellow]",
264
- style="dim",
265
- )
266
- except MaxRoundsExceededError:
267
- console.print("[red]Max conversation rounds exceeded.[/red]")
268
- except ProviderError as e:
269
- console.print(f"[red]Provider error:[/red] {e}")
270
- except EmptyResponseError as e:
271
- console.print(f"[red]Error:[/red] {e}")
272
262
  except KeyboardInterrupt:
273
263
  from rich.console import Console
274
264
 
@@ -4,12 +4,11 @@ from janito.agent.runtime_config import unified_config, runtime_config
4
4
  from janito.agent.config_defaults import CONFIG_DEFAULTS
5
5
  from rich import print
6
6
  from ._utils import home_shorten
7
+ import os
8
+ from pathlib import Path
7
9
 
8
10
 
9
- def handle_config_commands(args):
10
- """Handle --set-local-config, --set-global-config, --show-config. Exit if any are used."""
11
- did_something = False
12
-
11
+ def handle_run_config(args):
13
12
  if args.run_config:
14
13
  for run_item in args.run_config:
15
14
  try:
@@ -24,6 +23,11 @@ def handle_config_commands(args):
24
23
  )
25
24
  sys.exit(1)
26
25
  runtime_config.set(key, val.strip())
26
+ return True
27
+ return False
28
+
29
+
30
+ def handle_set_local_config(args):
27
31
  if args.set_local_config:
28
32
  try:
29
33
  key, val = args.set_local_config.split("=", 1)
@@ -40,8 +44,11 @@ def handle_config_commands(args):
40
44
  local_config.save()
41
45
  runtime_config.set(key, val.strip())
42
46
  print(f"Local config updated: {key} = {val.strip()}")
43
- did_something = True
47
+ return True
48
+ return False
49
+
44
50
 
51
+ def handle_set_global_config(args):
45
52
  if args.set_global_config:
46
53
  try:
47
54
  key, val = args.set_global_config.split("=", 1)
@@ -65,129 +72,111 @@ def handle_config_commands(args):
65
72
  del global_config._data[key]
66
73
  runtime_config.set("template", template_dict)
67
74
  print(f"Global config updated: template.{subkey} = {val.strip()}")
68
- did_something = True
75
+ return True
69
76
  else:
70
77
  global_config.set(key, val.strip())
71
78
  global_config.save()
72
79
  runtime_config.set(key, val.strip())
73
80
  print(f"Global config updated: {key} = {val.strip()}")
74
- did_something = True
81
+ return True
82
+ return False
75
83
 
84
+
85
+ def handle_set_api_key(args):
76
86
  if args.set_api_key:
77
- # Merge: load full config, update api_key, save all
78
87
  existing = dict(global_config.all())
79
88
  existing["api_key"] = args.set_api_key.strip()
80
89
  global_config._data = existing
81
90
  global_config.save()
82
91
  runtime_config.set("api_key", args.set_api_key.strip())
83
92
  print("Global API key saved.")
84
- did_something = True
93
+ return True
94
+ return False
95
+
85
96
 
97
+ def handle_show_config(args):
86
98
  if args.show_config:
87
- local_items = {}
88
- global_items = {}
89
-
90
- # Collect and group keys
91
- local_keys = set(local_config.all().keys())
92
- global_keys = set(global_config.all().keys())
93
- if not (local_keys or global_keys):
94
- print("No configuration found.")
95
- else:
96
- # Imports previously inside block to avoid circular import at module level
97
- # Handle template as nested dict
98
- for key in sorted(local_keys):
99
- if key == "template":
100
- template_dict = local_config.get("template", {})
101
- if template_dict:
102
- local_items["template"] = f"({len(template_dict)} keys set)"
103
- for tkey, tval in template_dict.items():
104
- local_items[f" template.{tkey}"] = tval
105
- continue
106
- if key.startswith("template."):
107
- # Skip legacy flat keys
108
- continue
109
- if key == "api_key":
110
- value = local_config.get("api_key")
111
- value = (
112
- value[:4] + "..." + value[-4:]
113
- if value and len(value) > 8
114
- else ("***" if value else None)
115
- )
116
- else:
117
- value = unified_config.get(key)
118
- local_items[key] = value
119
- for key in sorted(global_keys - local_keys):
120
- if key == "template":
121
- template_dict = global_config.get("template", {})
122
- if template_dict:
123
- global_items["template"] = f"({len(template_dict)} keys set)"
124
- for tkey, tval in template_dict.items():
125
- global_items[f" template.{tkey}"] = tval
126
- continue
127
- if key.startswith("template."):
128
- continue
129
- if key == "api_key":
130
- value = global_config.get("api_key")
131
- value = (
132
- value[:4] + "..." + value[-4:]
133
- if value and len(value) > 8
134
- else ("***" if value else None)
135
- )
136
- else:
137
- value = unified_config.get(key)
138
- global_items[key] = value
139
-
140
- # Mask API key
141
- for cfg in (local_items, global_items):
142
- if "api_key" in cfg and cfg["api_key"]:
143
- val = cfg["api_key"]
144
- cfg["api_key"] = (
145
- val[:4] + "..." + val[-4:] if len(val) > 8 else "***"
146
- )
147
-
148
- # Print local config
149
- from ._print_config import print_config_items
150
-
151
- print_config_items(
152
- local_items, color_label="[cyan]🏠 Local Configuration[/cyan]"
153
- )
99
+ local_items = _collect_config_items(local_config, unified_config, True)
100
+ global_items = _collect_config_items(
101
+ global_config, unified_config, False, set(local_items.keys())
102
+ )
103
+ _mask_api_keys(local_items)
104
+ _mask_api_keys(global_items)
105
+ _print_config_items(local_items, global_items)
106
+ _print_default_items(local_items, global_items)
107
+ return True
108
+ return False
154
109
 
155
- # Print global config
156
- print_config_items(
157
- global_items, color_label="[yellow]🌐 Global Configuration[/yellow]"
110
+
111
+ def _collect_config_items(config, unified_config, is_local, exclude_keys=None):
112
+ items = {}
113
+ keys = set(config.all().keys())
114
+ if exclude_keys:
115
+ keys = keys - set(exclude_keys)
116
+ for key in sorted(keys):
117
+ if key == "template":
118
+ template_dict = config.get("template", {})
119
+ if template_dict:
120
+ items["template"] = f"({len(template_dict)} keys set)"
121
+ for tkey, tval in template_dict.items():
122
+ items[f" template.{tkey}"] = tval
123
+ continue
124
+ if key.startswith("template."):
125
+ continue
126
+ if key == "api_key":
127
+ value = config.get("api_key")
128
+ value = (
129
+ value[:4] + "..." + value[-4:]
130
+ if value and len(value) > 8
131
+ else ("***" if value else None)
158
132
  )
133
+ else:
134
+ value = unified_config.get(key)
135
+ items[key] = value
136
+ return items
137
+
138
+
139
+ def _mask_api_keys(cfg):
140
+ if "api_key" in cfg and cfg["api_key"]:
141
+ val = cfg["api_key"]
142
+ cfg["api_key"] = val[:4] + "..." + val[-4:] if len(val) > 8 else "***"
159
143
 
160
- # Show defaults for unset keys
161
- shown_keys = set(local_items.keys()) | set(global_items.keys())
162
- default_items = {
163
- k: v
164
- for k, v in CONFIG_DEFAULTS.items()
165
- if k not in shown_keys and k != "api_key"
166
- }
167
- if default_items:
168
- print("[green]🟢 Defaults (not set in config files)[/green]")
169
- for key, value in default_items.items():
170
- # Special case for system_prompt: show template file if None
171
- if key == "system_prompt" and value is None:
172
- from pathlib import Path
173
-
174
- template_path = (
175
- Path(__file__).parent
176
- / "agent"
177
- / "templates"
178
- / "system_prompt_template_default.j2"
179
- )
180
- print(
181
- f"{key} = (default template path: {home_shorten(str(template_path))})"
182
- )
183
- else:
184
- print(f"{key} = {value}")
185
- print()
186
- did_something = True
187
-
188
- import os
189
- from pathlib import Path
190
144
 
145
+ def _print_config_items(local_items, global_items):
146
+ from ._print_config import print_config_items
147
+
148
+ print_config_items(local_items, color_label="[cyan]🏠 Local Configuration[/cyan]")
149
+ print_config_items(
150
+ global_items, color_label="[yellow]🌐 Global Configuration[/yellow]"
151
+ )
152
+
153
+
154
+ def _print_default_items(local_items, global_items):
155
+ shown_keys = set(local_items.keys()) | set(global_items.keys())
156
+ default_items = {
157
+ k: v
158
+ for k, v in CONFIG_DEFAULTS.items()
159
+ if k not in shown_keys and k != "api_key"
160
+ }
161
+ if default_items:
162
+ print("[green]🟢 Defaults (not set in config files)[/green]")
163
+ for key, value in default_items.items():
164
+ if key == "system_prompt" and value is None:
165
+ template_path = (
166
+ Path(__file__).parent
167
+ / "agent"
168
+ / "templates"
169
+ / "system_prompt_template_default.j2"
170
+ )
171
+ print(
172
+ f"{key} = (default template path: {home_shorten(str(template_path))})"
173
+ )
174
+ else:
175
+ print(f"{key} = {value}")
176
+ print()
177
+
178
+
179
+ def handle_config_reset_local(args):
191
180
  if getattr(args, "config_reset_local", False):
192
181
  local_path = Path(".janito/config.json")
193
182
  if local_path.exists():
@@ -196,6 +185,9 @@ def handle_config_commands(args):
196
185
  else:
197
186
  print(f"Local config file does not exist: {local_path}")
198
187
  sys.exit(0)
188
+
189
+
190
+ def handle_config_reset_global(args):
199
191
  if getattr(args, "config_reset_global", False):
200
192
  global_path = Path.home() / ".janito/config.json"
201
193
  if global_path.exists():
@@ -204,5 +196,16 @@ def handle_config_commands(args):
204
196
  else:
205
197
  print(f"Global config file does not exist: {global_path}")
206
198
  sys.exit(0)
199
+
200
+
201
+ def handle_config_commands(args):
202
+ did_something = False
203
+ did_something |= handle_run_config(args)
204
+ did_something |= handle_set_local_config(args)
205
+ did_something |= handle_set_global_config(args)
206
+ did_something |= handle_set_api_key(args)
207
+ did_something |= handle_show_config(args)
208
+ handle_config_reset_local(args)
209
+ handle_config_reset_global(args)
207
210
  if did_something:
208
211
  sys.exit(0)