janito 3.12.1__py3-none-any.whl → 3.12.3__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.
janito/cli/main_cli.py CHANGED
@@ -1,519 +1,520 @@
1
- import argparse
2
- import sys
3
- import enum
4
- from janito.cli.core.setters import handle_api_key_set, handle_set
5
- from janito.cli.core.getters import handle_getter
6
- from janito.cli.core.runner import (
7
- prepare_llm_driver_config,
8
- handle_runner,
9
- get_prompt_mode,
10
- )
11
- from janito.cli.core.event_logger import (
12
- setup_event_logger_if_needed,
13
- inject_debug_event_bus_if_needed,
14
- )
15
-
16
-
17
- definition = [
18
- (
19
- ["-u", "--unrestricted"],
20
- {
21
- "action": "store_true",
22
- "help": "Unrestricted mode: disable path security and URL whitelist restrictions (DANGEROUS)",
23
- },
24
- ),
25
- (
26
- ["--multi"],
27
- {
28
- "action": "store_true",
29
- "help": "Start chat mode with multi-line input as default (no need for /multi command)",
30
- },
31
- ),
32
- (
33
- ["--profile"],
34
- {
35
- "metavar": "PROFILE",
36
- "help": "Select the profile name for the system prompt (e.g. 'developer').",
37
- "default": None,
38
- },
39
- ),
40
- (
41
- ["--developer"],
42
- {
43
- "action": "store_true",
44
- "help": "Start with the Python developer profile (equivalent to --profile 'Developer with Python Tools')",
45
- },
46
- ),
47
- (
48
- ["--market"],
49
- {
50
- "action": "store_true",
51
- "help": "Start with the Market Analyst profile (equivalent to --profile 'Market Analyst')",
52
- },
53
- ),
54
- (
55
- ["-W", "--workdir"],
56
- {
57
- "metavar": "WORKDIR",
58
- "help": "Working directory to chdir to before tool execution",
59
- "default": None,
60
- },
61
- ),
62
- (
63
- ["--verbose-api"],
64
- {
65
- "action": "store_true",
66
- "help": "Print API calls and responses of LLM driver APIs for debugging/tracing.",
67
- },
68
- ),
69
- (
70
- ["--verbose-tools"],
71
- {
72
- "action": "store_true",
73
- "help": "Print info messages for tool execution in tools adapter.",
74
- },
75
- ),
76
- (
77
- ["--verbose-agent"],
78
- {
79
- "action": "store_true",
80
- "help": "Print info messages for agent event and message part handling.",
81
- },
82
- ),
83
- (
84
- ["-z", "--zero"],
85
- {
86
- "action": "store_true",
87
- "help": "IDE zero mode: disables system prompt & all tools for raw LLM interaction",
88
- },
89
- ),
90
- (
91
- ["-x", "--exec"],
92
- {
93
- "action": "store_true",
94
- "help": "Enable execution/run tools (allows running code or shell tools from the CLI)",
95
- },
96
- ),
97
- (
98
- ["-r", "--read"],
99
- {
100
- "action": "store_true",
101
- "help": "Enable tools that require read permissions",
102
- },
103
- ),
104
- (
105
- ["-w", "--write"],
106
- {
107
- "action": "store_true",
108
- "help": "Enable tools that require write permissions",
109
- },
110
- ),
111
- (["--unset"], {"metavar": "KEY", "help": "Unset (remove) a config key"}),
112
- (["--version"], {"action": "version", "version": None}),
113
- (["--list-tools"], {"action": "store_true", "help": "List all registered tools"}),
114
- (["--show-config"], {"action": "store_true", "help": "Show the current config"}),
115
- (
116
- ["--list-config"],
117
- {"action": "store_true", "help": "List all configuration files"},
118
- ),
119
- (
120
- ["--list-profiles"],
121
- {"action": "store_true", "help": "List available system prompt profiles"},
122
- ),
123
- (
124
- ["--list-providers"],
125
- {"action": "store_true", "help": "List supported LLM providers"},
126
- ),
127
- (
128
- ["--ping"],
129
- {
130
- "action": "store_true",
131
- "help": "Ping/test connectivity for all providers (use with --list-providers)",
132
- },
133
- ),
134
- (
135
- ["--list-drivers"],
136
- {
137
- "action": "store_true",
138
- "help": "List available LLM drivers and their dependencies",
139
- },
140
- ),
141
- (
142
- ["--region-info"],
143
- {
144
- "action": "store_true",
145
- "help": "Show current region information and location",
146
- },
147
- ),
148
- (
149
- ["--list-providers-region"],
150
- {
151
- "action": "store_true",
152
- "help": "List all providers with their regional API information",
153
- },
154
- ),
155
- (
156
- ["-l", "--list-models"],
157
- {"action": "store_true", "help": "List all supported models"},
158
- ),
159
- (
160
- ["--set-api-key"],
161
- {
162
- "metavar": "API_KEY",
163
- "help": "Set API key for the provider (requires -p PROVIDER)",
164
- },
165
- ),
166
- (["--set"], {"metavar": "KEY=VALUE", "help": "Set a config key"}),
167
- (["-s", "--system"], {"metavar": "SYSTEM_PROMPT", "help": "Set a system prompt"}),
168
- (
169
- ["-S", "--show-system"],
170
- {
171
- "action": "store_true",
172
- "help": "Show the resolved system prompt for the main agent",
173
- },
174
- ),
175
- (["-p", "--provider"], {"metavar": "PROVIDER", "help": "Select the provider"}),
176
- (["-m", "--model"], {"metavar": "MODEL", "help": "Select the model"}),
177
- (
178
- ["-t", "--temperature"],
179
- {"type": float, "default": None, "help": "Set the temperature"},
180
- ),
181
- (
182
- ["-v", "--verbose"],
183
- {"action": "store_true", "help": "Print extra information before answering"},
184
- ),
185
- (
186
- ["-R", "--raw"],
187
- {
188
- "action": "store_true",
189
- "help": "Print the raw JSON response from the OpenAI API (if applicable)",
190
- },
191
- ),
192
- (
193
- ["--effort"],
194
- {
195
- "choices": ["low", "medium", "high", "none"],
196
- "default": None,
197
- "help": "Set the reasoning effort for models that support it (low, medium, high, none)",
198
- },
199
- ),
200
- (
201
- ["--emoji"],
202
- {
203
- "action": "store_true",
204
- "help": "Enable emoji usage in responses to make output more engaging and expressive",
205
- },
206
- ),
207
- (
208
- ["-i", "--interactive"],
209
- {
210
- "action": "store_true",
211
- "help": "Signal that this is an interactive chat session",
212
- },
213
- ),
214
- (["user_prompt"], {"nargs": argparse.REMAINDER, "help": "Prompt to submit"}),
215
- (
216
- ["-e", "--event-log"],
217
- {"action": "store_true", "help": "Enable event logging to the system bus"},
218
- ),
219
- (
220
- ["--event-debug"],
221
- {
222
- "action": "store_true",
223
- "help": "Print debug info on event subscribe/submit methods",
224
- },
225
- ),
226
- (
227
- ["-c", "--config"],
228
- {
229
- "metavar": "NAME",
230
- "help": "Use custom configuration file ~/.janito/configs/NAME.json instead of default config.json",
231
- },
232
- ),
233
- (
234
- ["--list-plugins"],
235
- {"action": "store_true", "help": "List all loaded plugins"},
236
- ),
237
- (
238
- ["--list-plugins-available"],
239
- {"action": "store_true", "help": "List all available plugins"},
240
- ),
241
- (
242
- ["--list-resources"],
243
- {
244
- "action": "store_true",
245
- "help": "List all resources (tools, commands, config) from loaded plugins",
246
- },
247
- ),
248
- ]
249
-
250
- MODIFIER_KEYS = [
251
- "provider",
252
- "model",
253
- "profile",
254
- "developer",
255
- "market",
256
- "system",
257
- "temperature",
258
- "verbose",
259
- "raw",
260
- "verbose_api",
261
- "verbose_tools",
262
- "exec",
263
- "read",
264
- "write",
265
- "emoji",
266
- "interactive",
267
- ]
268
- SETTER_KEYS = ["set", "set_provider", "set_api_key", "unset"]
269
- GETTER_KEYS = [
270
- "show_config",
271
- "list_providers",
272
- "list_profiles",
273
- "list_models",
274
- "list_tools",
275
- "list_config",
276
- "list_drivers",
277
- "region_info",
278
- "list_providers_region",
279
- "ping",
280
- ]
281
- GETTER_KEYS = [
282
- "show_config",
283
- "list_providers",
284
- "list_profiles",
285
- "list_models",
286
- "list_tools",
287
- "list_config",
288
- "list_drivers",
289
- "region_info",
290
- "list_providers_region",
291
- "ping",
292
- ]
293
-
294
-
295
- class RunMode(enum.Enum):
296
- GET = "get"
297
- SET = "set"
298
- RUN = "run"
299
-
300
-
301
- class JanitoCLI:
302
- def __init__(self):
303
- import janito.tools
304
-
305
- self.parser = argparse.ArgumentParser(
306
- description="Janito CLI - A tool for running LLM-powered workflows from the command line."
307
- "\n\nExample usage: janito -p moonshot -m kimi-k1-8k 'Your prompt here'\n\n"
308
- "Use -m or --model to set the model for the session.",
309
- )
310
- self._define_args()
311
- self.args = self.parser.parse_args()
312
- self._set_all_arg_defaults()
313
- # Support custom config file via -c/--config
314
- if getattr(self.args, "config", None):
315
- from janito import config as global_config
316
- from janito.config_manager import ConfigManager
317
- import sys
318
- import importlib
319
-
320
- config_name = self.args.config
321
- # Re-initialize the global config singleton
322
- new_config = ConfigManager(config_name=config_name)
323
- # Ensure the config path is updated when the singleton already existed
324
- from pathlib import Path
325
-
326
- new_config.config_path = (
327
- Path.home() / ".janito" / "configs" / f"{config_name}.json"
328
- )
329
- # Reload config from the selected file
330
- new_config._load_file_config()
331
- # Patch the global singleton reference
332
- import janito.config as config_module
333
-
334
- config_module.config = new_config
335
- sys.modules["janito.config"].config = new_config
336
- # Support reading prompt from stdin if no user_prompt is given
337
- import sys
338
-
339
- if not sys.stdin.isatty():
340
- stdin_input = sys.stdin.read().strip()
341
- if stdin_input:
342
- if self.args.user_prompt and len(self.args.user_prompt) > 0:
343
- # Prefix the prompt argument to the stdin input
344
- combined = " ".join(self.args.user_prompt) + " " + stdin_input
345
- self.args.user_prompt = [combined]
346
- else:
347
- self.args.user_prompt = [stdin_input]
348
- from janito.cli.rich_terminal_reporter import RichTerminalReporter
349
-
350
- self.rich_reporter = RichTerminalReporter(raw_mode=self.args.raw)
351
-
352
- def _define_args(self):
353
- for argnames, argkwargs in definition:
354
- # Patch version argument dynamically with real version
355
- if "--version" in argnames:
356
- from janito import __version__ as janito_version
357
-
358
- argkwargs["version"] = f"Janito {janito_version}"
359
- self.parser.add_argument(*argnames, **argkwargs)
360
-
361
- def _set_all_arg_defaults(self):
362
- # Gather all possible keys from definition, MODIFIER_KEYS, SETTER_KEYS, GETTER_KEYS
363
- all_keys = set()
364
- for argnames, argkwargs in definition:
365
- for name in argnames:
366
- key = name.lstrip("-").replace("-", "_")
367
- all_keys.add(key)
368
- all_keys.update(MODIFIER_KEYS)
369
- all_keys.update(SETTER_KEYS)
370
- all_keys.update(GETTER_KEYS)
371
- # Set defaults for all keys if not present
372
- for key in all_keys:
373
- if not hasattr(self.args, key):
374
- setattr(self.args, key, None)
375
-
376
- def collect_modifiers(self):
377
- modifiers = {
378
- k: getattr(self.args, k)
379
- for k in MODIFIER_KEYS
380
- if getattr(self.args, k, None) is not None
381
- }
382
-
383
- return modifiers
384
-
385
- def classify(self):
386
- if any(getattr(self.args, k, None) for k in SETTER_KEYS):
387
- return RunMode.SET
388
- if any(getattr(self.args, k, None) for k in GETTER_KEYS):
389
- return RunMode.GET
390
- return RunMode.RUN
391
-
392
- def run(self):
393
- # Handle --show-system/-S before anything else
394
- if getattr(self.args, "show_system", False):
395
- from janito.cli.cli_commands.show_system_prompt import (
396
- handle_show_system_prompt,
397
- )
398
-
399
- handle_show_system_prompt(self.args)
400
- return
401
- run_mode = self.classify()
402
- if run_mode == RunMode.SET:
403
- if self._run_set_mode():
404
- return
405
- # Special handling: provider is not required for list commands
406
- if run_mode == RunMode.GET and (
407
- self.args.list_providers
408
- or self.args.list_tools
409
- or self.args.list_profiles
410
- or self.args.show_config
411
- or self.args.list_config
412
- or self.args.list_drivers
413
- or self.args.list_plugins
414
- or self.args.list_plugins_available
415
- or self.args.list_resources
416
- or self.args.ping
417
- ):
418
- self._maybe_print_verbose_provider_model()
419
- handle_getter(self.args)
420
- return
421
- # Handle /rwx prefix for enabling all permissions
422
- if self.args.user_prompt and self.args.user_prompt[0] == "/rwx":
423
- self.args.read = True
424
- self.args.write = True
425
- self.args.exec = True
426
- # Remove the /rwx prefix from the prompt
427
- self.args.user_prompt = self.args.user_prompt[1:]
428
-
429
- # If running in single shot mode and --profile is not provided, default to 'developer' profile
430
- # Skip profile selection for list commands that don't need it
431
- # Also skip if interactive mode is enabled (forces chat mode)
432
- if get_prompt_mode(self.args) == "single_shot" and not getattr(
433
- self.args, "profile", None
434
- ):
435
- self.args.profile = "developer"
436
- provider = self._get_provider_or_default()
437
- if provider is None:
438
- print(
439
- "Error: No provider selected and no provider found in config. Please set a provider using '-p PROVIDER', '--set provider=name', or configure a provider."
440
- )
441
- sys.exit(1)
442
- modifiers = self.collect_modifiers()
443
- self._maybe_print_verbose_modifiers(modifiers)
444
- setup_event_logger_if_needed(self.args)
445
- inject_debug_event_bus_if_needed(self.args)
446
- provider, llm_driver_config, agent_role = prepare_llm_driver_config(
447
- self.args, modifiers
448
- )
449
- if provider is None or llm_driver_config is None:
450
- return
451
- self._maybe_print_verbose_llm_config(llm_driver_config, run_mode)
452
- if run_mode == RunMode.RUN:
453
- self._maybe_print_verbose_run_mode()
454
- # DEBUG: Print exec_enabled propagation at main_cli
455
-
456
- handle_runner(
457
- self.args,
458
- provider,
459
- llm_driver_config,
460
- agent_role,
461
- verbose_tools=self.args.verbose_tools,
462
- )
463
-
464
- def _run_set_mode(self):
465
- if handle_api_key_set(self.args):
466
- return True
467
- if handle_set(self.args):
468
- return True
469
- from janito.cli.core.unsetters import handle_unset
470
-
471
- if handle_unset(self.args):
472
- return True
473
- return False
474
-
475
- def _get_provider_or_default(self):
476
- provider = self.args.provider
477
- if provider is None:
478
- from janito.provider_config import get_config_provider
479
-
480
- provider = get_config_provider()
481
- return provider
482
-
483
- def _maybe_print_verbose_modifiers(self, modifiers):
484
- if self.args.verbose:
485
- from janito.cli.verbose_output import print_verbose_info
486
-
487
- print_verbose_info("Modifiers collected", modifiers, style="blue")
488
-
489
- def _maybe_print_verbose_provider_model(self):
490
- if self.args.verbose:
491
- from janito.cli.verbose_output import print_verbose_info
492
-
493
- print_verbose_info(
494
- "Validated provider/model",
495
- f"Provider: {self.args.provider} | Model: {self.args.model}",
496
- style="blue",
497
- )
498
-
499
- def _maybe_print_verbose_llm_config(self, llm_driver_config, run_mode):
500
- if self.args.verbose:
501
- from janito.cli.verbose_output import print_verbose_info
502
-
503
- print_verbose_info("LLMDriverConfig", llm_driver_config, style="cyan")
504
- print_verbose_info(
505
- "Dispatch branch", run_mode, style="cyan", align_content=True
506
- )
507
-
508
- def _maybe_print_verbose_run_mode(self):
509
- if self.args.verbose:
510
- from janito.cli.verbose_output import print_verbose_info
511
-
512
- print_verbose_info(
513
- "Run mode", get_prompt_mode(self.args), style="cyan", align_content=True
514
- )
515
-
516
-
517
- if __name__ == "__main__":
518
- cli = JanitoCLI()
519
- cli.run()
1
+ import argparse
2
+ import sys
3
+ import enum
4
+ from janito.cli.core.setters import handle_api_key_set, handle_set
5
+ from janito.cli.core.getters import handle_getter
6
+ from janito.cli.core.runner import (
7
+ prepare_llm_driver_config,
8
+ handle_runner,
9
+ get_prompt_mode,
10
+ )
11
+ from janito.cli.core.event_logger import (
12
+ setup_event_logger_if_needed,
13
+ inject_debug_event_bus_if_needed,
14
+ )
15
+
16
+
17
+ definition = [
18
+ (
19
+ ["-u", "--unrestricted"],
20
+ {
21
+ "action": "store_true",
22
+ "help": "Unrestricted mode: disable path security and URL whitelist restrictions (DANGEROUS)",
23
+ },
24
+ ),
25
+ (
26
+ ["--multi"],
27
+ {
28
+ "action": "store_true",
29
+ "help": "Start chat mode with multi-line input as default (no need for /multi command)",
30
+ },
31
+ ),
32
+ (
33
+ ["--profile"],
34
+ {
35
+ "metavar": "PROFILE",
36
+ "help": "Select the profile name for the system prompt (e.g. 'developer').",
37
+ "default": None,
38
+ },
39
+ ),
40
+ (
41
+ ["--developer"],
42
+ {
43
+ "action": "store_true",
44
+ "help": "Start with the Python developer profile (equivalent to --profile 'Developer')",
45
+ },
46
+ ),
47
+ (
48
+ ["--market"],
49
+ {
50
+ "action": "store_true",
51
+ "help": "Start with the Market Analyst profile (equivalent to --profile 'Market Analyst')",
52
+ },
53
+ ),
54
+ (
55
+ ["-W", "--workdir"],
56
+ {
57
+ "metavar": "WORKDIR",
58
+ "help": "Working directory to chdir to before tool execution",
59
+ "default": None,
60
+ },
61
+ ),
62
+ (
63
+ ["--verbose-api"],
64
+ {
65
+ "action": "store_true",
66
+ "help": "Print API calls and responses of LLM driver APIs for debugging/tracing.",
67
+ },
68
+ ),
69
+ (
70
+ ["--verbose-tools"],
71
+ {
72
+ "action": "store_true",
73
+ "help": "Print info messages for tool execution in tools adapter.",
74
+ },
75
+ ),
76
+ (
77
+ ["--verbose-agent"],
78
+ {
79
+ "action": "store_true",
80
+ "help": "Print info messages for agent event and message part handling.",
81
+ },
82
+ ),
83
+ (
84
+ ["-z", "--zero"],
85
+ {
86
+ "action": "store_true",
87
+ "help": "IDE zero mode: disables system prompt & all tools for raw LLM interaction",
88
+ },
89
+ ),
90
+ (
91
+ ["-x", "--exec"],
92
+ {
93
+ "action": "store_true",
94
+ "help": "Enable execution/run tools (allows running code or shell tools from the CLI)",
95
+ },
96
+ ),
97
+ (
98
+ ["-r", "--read"],
99
+ {
100
+ "action": "store_true",
101
+ "help": "Enable tools that require read permissions",
102
+ },
103
+ ),
104
+ (
105
+ ["-w", "--write"],
106
+ {
107
+ "action": "store_true",
108
+ "help": "Enable tools that require write permissions",
109
+ },
110
+ ),
111
+ (["--unset"], {"metavar": "KEY", "help": "Unset (remove) a config key"}),
112
+ (["--version"], {"action": "version", "version": None}),
113
+ (["--list-tools"], {"action": "store_true", "help": "List all registered tools"}),
114
+ (["--show-config"], {"action": "store_true", "help": "Show the current config"}),
115
+ (
116
+ ["--list-config"],
117
+ {"action": "store_true", "help": "List all configuration files"},
118
+ ),
119
+ (
120
+ ["--list-profiles"],
121
+ {"action": "store_true", "help": "List available system prompt profiles"},
122
+ ),
123
+ (
124
+ ["--list-providers"],
125
+ {"action": "store_true", "help": "List supported LLM providers"},
126
+ ),
127
+ (
128
+ ["--ping"],
129
+ {
130
+ "action": "store_true",
131
+ "help": "Ping/test connectivity for all providers (use with --list-providers)",
132
+ },
133
+ ),
134
+ (
135
+ ["--list-drivers"],
136
+ {
137
+ "action": "store_true",
138
+ "help": "List available LLM drivers and their dependencies",
139
+ },
140
+ ),
141
+ (
142
+ ["--region-info"],
143
+ {
144
+ "action": "store_true",
145
+ "help": "Show current region information and location",
146
+ },
147
+ ),
148
+ (
149
+ ["--list-providers-region"],
150
+ {
151
+ "action": "store_true",
152
+ "help": "List all providers with their regional API information",
153
+ },
154
+ ),
155
+ (
156
+ ["-l", "--list-models"],
157
+ {"action": "store_true", "help": "List all supported models"},
158
+ ),
159
+ (
160
+ ["--set-api-key"],
161
+ {
162
+ "metavar": "API_KEY",
163
+ "help": "Set API key for the provider (requires -p PROVIDER)",
164
+ },
165
+ ),
166
+ (["--set"], {"metavar": "KEY=VALUE", "help": "Set a config key"}),
167
+ (["-s", "--system"], {"metavar": "SYSTEM_PROMPT", "help": "Set a system prompt"}),
168
+ (
169
+ ["-S", "--show-system"],
170
+ {
171
+ "action": "store_true",
172
+ "help": "Show the resolved system prompt for the main agent",
173
+ },
174
+ ),
175
+ (["-p", "--provider"], {"metavar": "PROVIDER", "help": "Select the provider"}),
176
+ (["-m", "--model"], {"metavar": "MODEL", "help": "Select the model"}),
177
+ (
178
+ ["-t", "--temperature"],
179
+ {"type": float, "default": None, "help": "Set the temperature"},
180
+ ),
181
+ (
182
+ ["-v", "--verbose"],
183
+ {"action": "store_true", "help": "Print extra information before answering"},
184
+ ),
185
+ (
186
+ ["-R", "--raw"],
187
+ {
188
+ "action": "store_true",
189
+ "help": "Print the raw JSON response from the OpenAI API (if applicable)",
190
+ },
191
+ ),
192
+ (
193
+ ["--effort"],
194
+ {
195
+ "choices": ["low", "medium", "high", "none"],
196
+ "default": None,
197
+ "help": "Set the reasoning effort for models that support it (low, medium, high, none)",
198
+ },
199
+ ),
200
+ (
201
+ ["--emoji"],
202
+ {
203
+ "action": "store_true",
204
+ "help": "Enable emoji usage in responses to make output more engaging and expressive",
205
+ },
206
+ ),
207
+ (
208
+ ["-i", "--interactive"],
209
+ {
210
+ "action": "store_true",
211
+ "help": "Signal that this is an interactive chat session",
212
+ },
213
+ ),
214
+ (["user_prompt"], {"nargs": argparse.REMAINDER, "help": "Prompt to submit"}),
215
+ (
216
+ ["-e", "--event-log"],
217
+ {"action": "store_true", "help": "Enable event logging to the system bus"},
218
+ ),
219
+ (
220
+ ["--event-debug"],
221
+ {
222
+ "action": "store_true",
223
+ "help": "Print debug info on event subscribe/submit methods",
224
+ },
225
+ ),
226
+ (
227
+ ["-c", "--config"],
228
+ {
229
+ "metavar": "NAME",
230
+ "help": "Use custom configuration file ~/.janito/configs/NAME.json instead of default config.json",
231
+ },
232
+ ),
233
+ (
234
+ ["--list-plugins"],
235
+ {"action": "store_true", "help": "List all loaded plugins"},
236
+ ),
237
+ (
238
+ ["--list-plugins-available"],
239
+ {"action": "store_true", "help": "List all available plugins"},
240
+ ),
241
+ (
242
+ ["--list-resources"],
243
+ {
244
+ "action": "store_true",
245
+ "help": "List all resources (tools, commands, config) from loaded plugins",
246
+ },
247
+ ),
248
+ ]
249
+
250
+ MODIFIER_KEYS = [
251
+ "provider",
252
+ "model",
253
+ "profile",
254
+ "developer",
255
+ "market",
256
+ "system",
257
+ "temperature",
258
+ "verbose",
259
+ "raw",
260
+ "verbose_api",
261
+ "verbose_tools",
262
+ "exec",
263
+ "read",
264
+ "write",
265
+ "emoji",
266
+ "interactive",
267
+ ]
268
+ SETTER_KEYS = ["set", "set_provider", "set_api_key", "unset"]
269
+ GETTER_KEYS = [
270
+ "show_config",
271
+ "list_providers",
272
+ "list_profiles",
273
+ "list_models",
274
+ "list_tools",
275
+ "list_config",
276
+ "list_drivers",
277
+ "region_info",
278
+ "list_providers_region",
279
+ "ping",
280
+ ]
281
+ GETTER_KEYS = [
282
+ "show_config",
283
+ "list_providers",
284
+ "list_profiles",
285
+ "list_models",
286
+ "list_tools",
287
+ "list_config",
288
+ "list_drivers",
289
+ "region_info",
290
+ "list_providers_region",
291
+ "ping",
292
+ ]
293
+
294
+
295
+ class RunMode(enum.Enum):
296
+ GET = "get"
297
+ SET = "set"
298
+ RUN = "run"
299
+
300
+
301
+ class JanitoCLI:
302
+ def __init__(self):
303
+ import janito.tools
304
+
305
+ self.parser = argparse.ArgumentParser(
306
+ description="Janito CLI - A tool for running LLM-powered workflows from the command line."
307
+ "\n\nExample usage: janito -p moonshot -m kimi-k1-8k 'Your prompt here'\n\n"
308
+ "Use -m or --model to set the model for the session.",
309
+ )
310
+ self._define_args()
311
+ self.args = self.parser.parse_args()
312
+ self._set_all_arg_defaults()
313
+ # Support custom config file via -c/--config
314
+ if getattr(self.args, "config", None):
315
+ from janito import config as global_config
316
+ from janito.config_manager import ConfigManager
317
+ import sys
318
+ import importlib
319
+
320
+ config_name = self.args.config
321
+ # Re-initialize the global config singleton
322
+ new_config = ConfigManager(config_name=config_name)
323
+ # Ensure the config path is updated when the singleton already existed
324
+ from pathlib import Path
325
+
326
+ new_config.config_path = (
327
+ Path.home() / ".janito" / "configs" / f"{config_name}.json"
328
+ )
329
+ # Reload config from the selected file
330
+ new_config._load_file_config()
331
+ # Patch the global singleton reference
332
+ import janito.config as config_module
333
+
334
+ config_module.config = new_config
335
+ sys.modules["janito.config"].config = new_config
336
+ # Support reading prompt from stdin if no user_prompt is given
337
+ import sys
338
+
339
+ if not sys.stdin.isatty():
340
+ stdin_input = sys.stdin.read().strip()
341
+ if stdin_input:
342
+ if self.args.user_prompt and len(self.args.user_prompt) > 0:
343
+ # Prefix the prompt argument to the stdin input
344
+ combined = " ".join(self.args.user_prompt) + " " + stdin_input
345
+ self.args.user_prompt = [combined]
346
+ else:
347
+ self.args.user_prompt = [stdin_input]
348
+ from janito.cli.rich_terminal_reporter import RichTerminalReporter
349
+
350
+ self.rich_reporter = RichTerminalReporter(raw_mode=self.args.raw)
351
+
352
+ def _define_args(self):
353
+ for argnames, argkwargs in definition:
354
+ # Patch version argument dynamically with real version
355
+ if "--version" in argnames:
356
+ from janito import __version__ as janito_version
357
+
358
+ argkwargs["version"] = f"Janito {janito_version}"
359
+ self.parser.add_argument(*argnames, **argkwargs)
360
+
361
+ def _set_all_arg_defaults(self):
362
+ # Gather all possible keys from definition, MODIFIER_KEYS, SETTER_KEYS, GETTER_KEYS
363
+ all_keys = set()
364
+ for argnames, argkwargs in definition:
365
+ for name in argnames:
366
+ key = name.lstrip("-").replace("-", "_")
367
+ all_keys.add(key)
368
+ all_keys.update(MODIFIER_KEYS)
369
+ all_keys.update(SETTER_KEYS)
370
+ all_keys.update(GETTER_KEYS)
371
+ # Set defaults for all keys if not present
372
+ for key in all_keys:
373
+ if not hasattr(self.args, key):
374
+ setattr(self.args, key, None)
375
+
376
+ def collect_modifiers(self):
377
+ modifiers = {
378
+ k: getattr(self.args, k)
379
+ for k in MODIFIER_KEYS
380
+ if getattr(self.args, k, None) is not None
381
+ }
382
+
383
+ return modifiers
384
+
385
+ def classify(self):
386
+ if any(getattr(self.args, k, None) for k in SETTER_KEYS):
387
+ return RunMode.SET
388
+ if any(getattr(self.args, k, None) for k in GETTER_KEYS):
389
+ return RunMode.GET
390
+ return RunMode.RUN
391
+
392
+ def run(self):
393
+ # Handle --show-system/-S before anything else
394
+ if getattr(self.args, "show_system", False):
395
+ from janito.cli.cli_commands.show_system_prompt import (
396
+ handle_show_system_prompt,
397
+ )
398
+
399
+ handle_show_system_prompt(self.args)
400
+ return
401
+ run_mode = self.classify()
402
+ if run_mode == RunMode.SET:
403
+ if self._run_set_mode():
404
+ return
405
+ # Special handling: provider is not required for list commands
406
+ if run_mode == RunMode.GET and (
407
+ self.args.list_providers
408
+ or self.args.list_models
409
+ or self.args.list_tools
410
+ or self.args.list_profiles
411
+ or self.args.show_config
412
+ or self.args.list_config
413
+ or self.args.list_drivers
414
+ or self.args.list_plugins
415
+ or self.args.list_plugins_available
416
+ or self.args.list_resources
417
+ or self.args.ping
418
+ ):
419
+ self._maybe_print_verbose_provider_model()
420
+ handle_getter(self.args)
421
+ return
422
+ # Handle /rwx prefix for enabling all permissions
423
+ if self.args.user_prompt and self.args.user_prompt[0] == "/rwx":
424
+ self.args.read = True
425
+ self.args.write = True
426
+ self.args.exec = True
427
+ # Remove the /rwx prefix from the prompt
428
+ self.args.user_prompt = self.args.user_prompt[1:]
429
+
430
+ # If running in single shot mode and --profile is not provided, default to 'developer' profile
431
+ # Skip profile selection for list commands that don't need it
432
+ # Also skip if interactive mode is enabled (forces chat mode)
433
+ if get_prompt_mode(self.args) == "single_shot" and not getattr(
434
+ self.args, "profile", None
435
+ ):
436
+ self.args.profile = "developer"
437
+ provider = self._get_provider_or_default()
438
+ if provider is None:
439
+ print(
440
+ "Error: No provider selected and no provider found in config. Please set a provider using '-p PROVIDER', '--set provider=name', or configure a provider."
441
+ )
442
+ sys.exit(1)
443
+ modifiers = self.collect_modifiers()
444
+ self._maybe_print_verbose_modifiers(modifiers)
445
+ setup_event_logger_if_needed(self.args)
446
+ inject_debug_event_bus_if_needed(self.args)
447
+ provider, llm_driver_config, agent_role = prepare_llm_driver_config(
448
+ self.args, modifiers
449
+ )
450
+ if provider is None or llm_driver_config is None:
451
+ return
452
+ self._maybe_print_verbose_llm_config(llm_driver_config, run_mode)
453
+ if run_mode == RunMode.RUN:
454
+ self._maybe_print_verbose_run_mode()
455
+ # DEBUG: Print exec_enabled propagation at main_cli
456
+
457
+ handle_runner(
458
+ self.args,
459
+ provider,
460
+ llm_driver_config,
461
+ agent_role,
462
+ verbose_tools=self.args.verbose_tools,
463
+ )
464
+
465
+ def _run_set_mode(self):
466
+ if handle_api_key_set(self.args):
467
+ return True
468
+ if handle_set(self.args):
469
+ return True
470
+ from janito.cli.core.unsetters import handle_unset
471
+
472
+ if handle_unset(self.args):
473
+ return True
474
+ return False
475
+
476
+ def _get_provider_or_default(self):
477
+ provider = self.args.provider
478
+ if provider is None:
479
+ from janito.provider_config import get_config_provider
480
+
481
+ provider = get_config_provider()
482
+ return provider
483
+
484
+ def _maybe_print_verbose_modifiers(self, modifiers):
485
+ if self.args.verbose:
486
+ from janito.cli.verbose_output import print_verbose_info
487
+
488
+ print_verbose_info("Modifiers collected", modifiers, style="blue")
489
+
490
+ def _maybe_print_verbose_provider_model(self):
491
+ if self.args.verbose:
492
+ from janito.cli.verbose_output import print_verbose_info
493
+
494
+ print_verbose_info(
495
+ "Validated provider/model",
496
+ f"Provider: {self.args.provider} | Model: {self.args.model}",
497
+ style="blue",
498
+ )
499
+
500
+ def _maybe_print_verbose_llm_config(self, llm_driver_config, run_mode):
501
+ if self.args.verbose:
502
+ from janito.cli.verbose_output import print_verbose_info
503
+
504
+ print_verbose_info("LLMDriverConfig", llm_driver_config, style="cyan")
505
+ print_verbose_info(
506
+ "Dispatch branch", run_mode, style="cyan", align_content=True
507
+ )
508
+
509
+ def _maybe_print_verbose_run_mode(self):
510
+ if self.args.verbose:
511
+ from janito.cli.verbose_output import print_verbose_info
512
+
513
+ print_verbose_info(
514
+ "Run mode", get_prompt_mode(self.args), style="cyan", align_content=True
515
+ )
516
+
517
+
518
+ if __name__ == "__main__":
519
+ cli = JanitoCLI()
520
+ cli.run()