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.
- janito/__init__.py +1 -1
- janito/__main__.py +0 -1
- janito/agent/config.py +11 -10
- janito/agent/config_defaults.py +3 -2
- janito/agent/conversation.py +93 -119
- janito/agent/conversation_api.py +98 -0
- janito/agent/conversation_exceptions.py +12 -0
- janito/agent/conversation_tool_calls.py +22 -0
- janito/agent/conversation_ui.py +17 -0
- janito/agent/message_handler.py +8 -9
- janito/agent/{agent.py → openai_client.py} +48 -16
- janito/agent/openai_schema_generator.py +53 -37
- janito/agent/profile_manager.py +172 -0
- janito/agent/queued_message_handler.py +13 -14
- janito/agent/rich_live.py +32 -0
- janito/agent/rich_message_handler.py +64 -0
- janito/agent/runtime_config.py +6 -1
- janito/agent/{tools/tool_base.py → tool_base.py} +15 -8
- janito/agent/tool_registry.py +118 -132
- janito/agent/tools/__init__.py +41 -2
- janito/agent/tools/ask_user.py +43 -33
- janito/agent/tools/create_directory.py +18 -16
- janito/agent/tools/create_file.py +31 -36
- janito/agent/tools/fetch_url.py +23 -19
- janito/agent/tools/find_files.py +40 -36
- janito/agent/tools/get_file_outline.py +100 -22
- janito/agent/tools/get_lines.py +40 -32
- janito/agent/tools/gitignore_utils.py +9 -6
- janito/agent/tools/move_file.py +22 -13
- janito/agent/tools/py_compile_file.py +40 -0
- janito/agent/tools/remove_directory.py +34 -24
- janito/agent/tools/remove_file.py +22 -20
- janito/agent/tools/replace_file.py +51 -0
- janito/agent/tools/replace_text_in_file.py +69 -42
- janito/agent/tools/rich_live.py +9 -2
- janito/agent/tools/run_bash_command.py +155 -107
- janito/agent/tools/run_python_command.py +139 -0
- janito/agent/tools/search_files.py +51 -34
- janito/agent/tools/tools_utils.py +4 -2
- janito/agent/tools/utils.py +6 -2
- janito/cli/_print_config.py +42 -16
- janito/cli/_utils.py +1 -0
- janito/cli/arg_parser.py +182 -29
- janito/cli/config_commands.py +54 -22
- janito/cli/logging_setup.py +9 -3
- janito/cli/main.py +11 -10
- janito/cli/runner/__init__.py +2 -0
- janito/cli/runner/cli_main.py +148 -0
- janito/cli/runner/config.py +33 -0
- janito/cli/runner/formatting.py +12 -0
- janito/cli/runner/scan.py +44 -0
- janito/cli_chat_shell/__init__.py +0 -1
- janito/cli_chat_shell/chat_loop.py +71 -92
- janito/cli_chat_shell/chat_state.py +38 -0
- janito/cli_chat_shell/chat_ui.py +43 -0
- janito/cli_chat_shell/commands/__init__.py +45 -0
- janito/cli_chat_shell/commands/config.py +22 -0
- janito/cli_chat_shell/commands/history_reset.py +29 -0
- janito/cli_chat_shell/commands/session.py +48 -0
- janito/cli_chat_shell/commands/session_control.py +12 -0
- janito/cli_chat_shell/commands/system.py +73 -0
- janito/cli_chat_shell/commands/utility.py +29 -0
- janito/cli_chat_shell/config_shell.py +39 -10
- janito/cli_chat_shell/load_prompt.py +5 -2
- janito/cli_chat_shell/session_manager.py +24 -27
- janito/cli_chat_shell/ui.py +75 -40
- janito/rich_utils.py +15 -2
- janito/web/__main__.py +10 -2
- janito/web/app.py +88 -52
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/METADATA +76 -11
- janito-1.6.0.dist-info/RECORD +81 -0
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/WHEEL +1 -1
- janito/agent/rich_tool_handler.py +0 -43
- janito/agent/templates/system_instructions.j2 +0 -38
- janito/agent/tool_auto_imports.py +0 -5
- janito/agent/tools/append_text_to_file.py +0 -41
- janito/agent/tools/py_compile.py +0 -39
- janito/agent/tools/python_exec.py +0 -83
- janito/cli/runner.py +0 -137
- janito/cli_chat_shell/commands.py +0 -204
- janito/render_prompt.py +0 -13
- janito-1.5.2.dist-info/RECORD +0 -66
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/entry_points.txt +0 -0
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/licenses/LICENSE +0 -0
- {janito-1.5.2.dist-info → janito-1.6.0.dist-info}/top_level.txt +0 -0
@@ -1,54 +1,71 @@
|
|
1
|
-
from janito.agent.
|
1
|
+
from janito.agent.tool_base import ToolBase
|
2
2
|
from janito.agent.tool_registry import register_tool
|
3
|
+
from janito.agent.tools.tools_utils import pluralize
|
3
4
|
|
4
5
|
import os
|
5
6
|
from janito.agent.tools.gitignore_utils import filter_ignored
|
6
7
|
|
8
|
+
|
7
9
|
@register_tool(name="search_files")
|
8
10
|
class SearchFilesTool(ToolBase):
|
9
|
-
"""
|
10
|
-
|
11
|
-
"""
|
12
|
-
Search for a text pattern in all files within one or more directories and return matching lines.
|
11
|
+
"""
|
12
|
+
Search for a text pattern in all files within a directory and return matching lines. Respects .gitignore.
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
Args:
|
15
|
+
directories (list[str]): List of directories to search in.
|
16
|
+
pattern (str): Plain text substring to search for in files. (Not a regular expression or glob pattern.)
|
17
|
+
recursive (bool): Whether to search recursively in subdirectories. Defaults to True.
|
18
|
+
max_depth (int, optional): Maximum directory depth to search (0 = only top-level). If None, unlimited. Defaults to None.
|
19
|
+
Returns:
|
20
|
+
str: Matching lines from files as a newline-separated string, each formatted as 'filepath:lineno: line'. Example:
|
21
|
+
- "/path/to/file.py:10: def my_function():"
|
22
|
+
- "Warning: Empty search pattern provided. Operation skipped."
|
23
|
+
"""
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
def call(
|
26
|
+
self,
|
27
|
+
directories: list[str],
|
28
|
+
pattern: str,
|
29
|
+
recursive: bool = True,
|
30
|
+
max_depth: int = None,
|
31
|
+
) -> str:
|
24
32
|
if not pattern:
|
25
|
-
self.report_warning(
|
33
|
+
self.report_warning(
|
34
|
+
"⚠️ Warning: Empty search pattern provided. Operation skipped."
|
35
|
+
)
|
26
36
|
return "Warning: Empty search pattern provided. Operation skipped."
|
27
|
-
|
37
|
+
output = []
|
28
38
|
for directory in directories:
|
29
|
-
|
30
|
-
|
39
|
+
info_str = f"🔎 Searching for text '{pattern}' in '{directory}'"
|
40
|
+
if recursive is False:
|
41
|
+
info_str += f" (recursive={recursive})"
|
42
|
+
self.report_info(info_str)
|
43
|
+
if recursive:
|
44
|
+
walker = os.walk(directory)
|
45
|
+
else:
|
46
|
+
# Only the top directory, not recursive
|
47
|
+
dirs, files = filter_ignored(
|
48
|
+
directory, *os.walk(directory).__next__()[1:]
|
49
|
+
)
|
50
|
+
walker = [(directory, dirs, files)]
|
51
|
+
for root, dirs, files in walker:
|
52
|
+
# Calculate depth
|
53
|
+
rel_path = os.path.relpath(root, directory)
|
54
|
+
depth = 0 if rel_path == "." else rel_path.count(os.sep) + 1
|
55
|
+
if max_depth is not None and depth > max_depth:
|
56
|
+
dirs[:] = []
|
57
|
+
continue
|
58
|
+
if not recursive and depth > 0:
|
59
|
+
break
|
31
60
|
dirs, files = filter_ignored(root, dirs, files)
|
32
61
|
for filename in files:
|
33
62
|
path = os.path.join(root, filename)
|
34
63
|
try:
|
35
|
-
with open(path,
|
64
|
+
with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
36
65
|
for lineno, line in enumerate(f, 1):
|
37
66
|
if pattern in line:
|
38
|
-
|
39
|
-
if len(matches) >= max_results:
|
40
|
-
break
|
67
|
+
output.append(f"{path}:{lineno}: {line.strip()}")
|
41
68
|
except Exception:
|
42
69
|
continue
|
43
|
-
|
44
|
-
|
45
|
-
if len(matches) >= max_results:
|
46
|
-
warning = "\n⚠️ Warning: Maximum result limit reached. Some matches may not be shown."
|
47
|
-
suffix = " (Max Reached)"
|
48
|
-
else:
|
49
|
-
suffix = ""
|
50
|
-
self.report_success(f" ✅ {len(matches)} {pluralize('line', len(matches))}{suffix}")
|
51
|
-
return '\n'.join(matches) + warning
|
52
|
-
|
53
|
-
|
54
|
-
from janito.agent.tools.tools_utils import pluralize
|
70
|
+
self.report_success(f" ✅ {len(output)} {pluralize('line', len(output))} found")
|
71
|
+
return "\n".join(output)
|
@@ -1,11 +1,13 @@
|
|
1
1
|
def display_path(path):
|
2
2
|
import os
|
3
|
+
|
3
4
|
if os.path.isabs(path):
|
4
5
|
return path
|
5
6
|
return os.path.relpath(path)
|
6
7
|
|
8
|
+
|
7
9
|
def pluralize(word: str, count: int) -> str:
|
8
10
|
"""Return the pluralized form of word if count != 1, unless word already ends with 's'."""
|
9
|
-
if count == 1 or word.endswith(
|
11
|
+
if count == 1 or word.endswith("s"):
|
10
12
|
return word
|
11
|
-
return word +
|
13
|
+
return word + "s"
|
janito/agent/tools/utils.py
CHANGED
@@ -20,11 +20,15 @@ def display_path(original_path: str, expanded_path: str) -> str:
|
|
20
20
|
- Else, show the expanded path.
|
21
21
|
"""
|
22
22
|
# Detect relative path (POSIX or Windows)
|
23
|
-
if not (
|
23
|
+
if not (
|
24
|
+
original_path.startswith("/")
|
25
|
+
or original_path.startswith("~")
|
26
|
+
or (os.name == "nt" and len(original_path) > 1 and original_path[1] == ":")
|
27
|
+
):
|
24
28
|
return original_path
|
25
29
|
home = os.path.expanduser("~")
|
26
30
|
if original_path.startswith("~"):
|
27
31
|
return original_path
|
28
32
|
if expanded_path.startswith(home):
|
29
|
-
return "~" + expanded_path[len(home):]
|
33
|
+
return "~" + expanded_path[len(home) :]
|
30
34
|
return expanded_path
|
janito/cli/_print_config.py
CHANGED
@@ -2,6 +2,7 @@ import os
|
|
2
2
|
from janito.rich_utils import print_info, print_warning, print_magenta
|
3
3
|
from ._utils import home_shorten
|
4
4
|
|
5
|
+
|
5
6
|
def print_config_items(items, color_label=None):
|
6
7
|
if not items:
|
7
8
|
return
|
@@ -9,7 +10,7 @@ def print_config_items(items, color_label=None):
|
|
9
10
|
print_info(color_label)
|
10
11
|
home = os.path.expanduser("~")
|
11
12
|
for key, value in items.items():
|
12
|
-
if key == "
|
13
|
+
if key == "system_prompt_template" and isinstance(value, str):
|
13
14
|
if value.startswith(home):
|
14
15
|
print(f"{key} = {home_shorten(value)}")
|
15
16
|
else:
|
@@ -18,51 +19,76 @@ def print_config_items(items, color_label=None):
|
|
18
19
|
print_info(f"{key} = {value}")
|
19
20
|
print_info("")
|
20
21
|
|
21
|
-
|
22
|
+
|
23
|
+
def print_full_config(
|
24
|
+
local_config, global_config, unified_config, config_defaults, console=None
|
25
|
+
):
|
22
26
|
"""
|
23
27
|
Print local, global, and default config values in a unified way.
|
24
|
-
Handles masking API keys and showing the template file for
|
28
|
+
Handles masking API keys and showing the template file for system_prompt_template if not set.
|
25
29
|
"""
|
26
30
|
local_items = {}
|
27
31
|
global_items = {}
|
28
32
|
local_keys = set(local_config.all().keys())
|
29
33
|
global_keys = set(global_config.all().keys())
|
30
|
-
all_keys = set(config_defaults.keys()) | global_keys | local_keys
|
31
|
-
out = print_info if console is None else console.print
|
32
34
|
if not (local_keys or global_keys):
|
33
35
|
print_warning("No configuration found.")
|
34
36
|
else:
|
35
37
|
for key in sorted(local_keys):
|
36
38
|
if key == "api_key":
|
37
39
|
value = local_config.get("api_key")
|
38
|
-
value =
|
40
|
+
value = (
|
41
|
+
value[:4] + "..." + value[-4:]
|
42
|
+
if value and len(value) > 8
|
43
|
+
else ("***" if value else None)
|
44
|
+
)
|
39
45
|
else:
|
40
46
|
value = unified_config.get(key)
|
41
47
|
local_items[key] = value
|
42
48
|
for key in sorted(global_keys - local_keys):
|
43
49
|
if key == "api_key":
|
44
50
|
value = global_config.get("api_key")
|
45
|
-
value =
|
51
|
+
value = (
|
52
|
+
value[:4] + "..." + value[-4:]
|
53
|
+
if value and len(value) > 8
|
54
|
+
else ("***" if value else None)
|
55
|
+
)
|
46
56
|
else:
|
47
57
|
value = unified_config.get(key)
|
48
58
|
global_items[key] = value
|
49
59
|
# Mask API key
|
50
60
|
for cfg in (local_items, global_items):
|
51
|
-
if
|
52
|
-
val = cfg[
|
53
|
-
cfg[
|
54
|
-
print_config_items(
|
55
|
-
|
61
|
+
if "api_key" in cfg and cfg["api_key"]:
|
62
|
+
val = cfg["api_key"]
|
63
|
+
cfg["api_key"] = val[:4] + "..." + val[-4:] if len(val) > 8 else "***"
|
64
|
+
print_config_items(
|
65
|
+
local_items, color_label="[cyan]🏠 Local Configuration[/cyan]"
|
66
|
+
)
|
67
|
+
print_config_items(
|
68
|
+
global_items, color_label="[yellow]🌐 Global Configuration[/yellow]"
|
69
|
+
)
|
56
70
|
# Show defaults for unset keys
|
57
71
|
shown_keys = set(local_items.keys()) | set(global_items.keys())
|
58
|
-
default_items = {
|
72
|
+
default_items = {
|
73
|
+
k: v
|
74
|
+
for k, v in config_defaults.items()
|
75
|
+
if k not in shown_keys and k != "api_key"
|
76
|
+
}
|
59
77
|
if default_items:
|
60
78
|
print_magenta("[green]🟢 Defaults (not set in config files)[/green]")
|
61
79
|
from pathlib import Path
|
62
|
-
|
80
|
+
|
81
|
+
template_path = (
|
82
|
+
Path(__file__).parent
|
83
|
+
/ "agent"
|
84
|
+
/ "templates"
|
85
|
+
/ "system_prompt_template.j2"
|
86
|
+
)
|
63
87
|
for key, value in default_items.items():
|
64
|
-
if key == "
|
65
|
-
print_info(
|
88
|
+
if key == "system_prompt_template" and value is None:
|
89
|
+
print_info(
|
90
|
+
f"{key} = (default template path: {home_shorten(str(template_path))})"
|
91
|
+
)
|
66
92
|
else:
|
67
93
|
print_info(f"{key} = {value}")
|
68
94
|
print_info("")
|
janito/cli/_utils.py
CHANGED
janito/cli/arg_parser.py
CHANGED
@@ -2,38 +2,191 @@ import argparse
|
|
2
2
|
|
3
3
|
|
4
4
|
def create_parser():
|
5
|
-
parser = argparse.ArgumentParser(
|
6
|
-
|
5
|
+
parser = argparse.ArgumentParser(
|
6
|
+
description="OpenRouter API call using OpenAI Python SDK"
|
7
|
+
)
|
8
|
+
parser.add_argument(
|
9
|
+
"prompt", type=str, nargs="?", help="Prompt to send to the model"
|
10
|
+
)
|
7
11
|
|
8
|
-
parser.add_argument(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
parser.add_argument(
|
13
|
+
"--max-tokens",
|
14
|
+
type=int,
|
15
|
+
default=None,
|
16
|
+
help="Maximum tokens for model response (overrides config, default: 200000)",
|
17
|
+
)
|
18
|
+
parser.add_argument(
|
19
|
+
"--max-tools",
|
20
|
+
type=int,
|
21
|
+
default=None,
|
22
|
+
help="Maximum number of tool calls allowed within a chat session (default: unlimited)",
|
23
|
+
)
|
24
|
+
parser.add_argument(
|
25
|
+
"--model",
|
26
|
+
type=str,
|
27
|
+
default=None,
|
28
|
+
help="Model name to use for this session (overrides config, does not persist)",
|
29
|
+
)
|
30
|
+
parser.add_argument(
|
31
|
+
"--max-rounds",
|
32
|
+
type=int,
|
33
|
+
default=None,
|
34
|
+
help="Maximum number of agent rounds per prompt (overrides config, default: 50)",
|
35
|
+
)
|
12
36
|
|
13
37
|
# Mutually exclusive group for system prompt options
|
14
38
|
group = parser.add_mutually_exclusive_group()
|
15
|
-
group.add_argument(
|
16
|
-
|
39
|
+
group.add_argument(
|
40
|
+
"-s",
|
41
|
+
"--system",
|
42
|
+
type=str,
|
43
|
+
default=None,
|
44
|
+
help="Optional system prompt as a raw string.",
|
45
|
+
)
|
46
|
+
group.add_argument(
|
47
|
+
"--system-file",
|
48
|
+
type=str,
|
49
|
+
default=None,
|
50
|
+
help="Path to a plain text file to use as the system prompt (no template rendering, takes precedence over --system-prompt)",
|
51
|
+
)
|
17
52
|
|
18
|
-
parser.add_argument(
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
parser.add_argument(
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
parser.add_argument(
|
33
|
-
|
34
|
-
|
35
|
-
parser.add_argument(
|
36
|
-
|
37
|
-
|
38
|
-
|
53
|
+
parser.add_argument(
|
54
|
+
"-r",
|
55
|
+
"--role",
|
56
|
+
type=str,
|
57
|
+
default=None,
|
58
|
+
help="Role description for the default system prompt",
|
59
|
+
)
|
60
|
+
parser.add_argument(
|
61
|
+
"-t",
|
62
|
+
"--temperature",
|
63
|
+
type=float,
|
64
|
+
default=None,
|
65
|
+
help="Sampling temperature (e.g., 0.0 - 2.0)",
|
66
|
+
)
|
67
|
+
parser.add_argument(
|
68
|
+
"--verbose-http", action="store_true", help="Enable verbose HTTP logging"
|
69
|
+
)
|
70
|
+
parser.add_argument(
|
71
|
+
"--verbose-http-raw",
|
72
|
+
action="store_true",
|
73
|
+
help="Enable raw HTTP wire-level logging",
|
74
|
+
)
|
75
|
+
parser.add_argument(
|
76
|
+
"--verbose-response",
|
77
|
+
action="store_true",
|
78
|
+
help="Pretty print the full response object",
|
79
|
+
)
|
80
|
+
parser.add_argument(
|
81
|
+
"--show-system",
|
82
|
+
action="store_true",
|
83
|
+
help="Show model, parameters, system prompt, and tool definitions, then exit",
|
84
|
+
)
|
85
|
+
parser.add_argument(
|
86
|
+
"--verbose-tools",
|
87
|
+
action="store_true",
|
88
|
+
help="Print tool call parameters and results",
|
89
|
+
)
|
90
|
+
parser.add_argument(
|
91
|
+
"-n",
|
92
|
+
"--no-tools",
|
93
|
+
action="store_true",
|
94
|
+
default=False,
|
95
|
+
help="Disable tool use (default: enabled)",
|
96
|
+
)
|
97
|
+
parser.add_argument(
|
98
|
+
"--set-local-config",
|
99
|
+
type=str,
|
100
|
+
default=None,
|
101
|
+
help='Set a local config key-value pair, format "key=val"',
|
102
|
+
)
|
103
|
+
parser.add_argument(
|
104
|
+
"--set-global-config",
|
105
|
+
type=str,
|
106
|
+
default=None,
|
107
|
+
help='Set a global config key-value pair, format "key=val"',
|
108
|
+
)
|
109
|
+
parser.add_argument(
|
110
|
+
"--run-config",
|
111
|
+
type=str,
|
112
|
+
action="append",
|
113
|
+
default=None,
|
114
|
+
help='Set a runtime (in-memory only) config key-value pair, format "key=val". Can be repeated.',
|
115
|
+
)
|
116
|
+
parser.add_argument(
|
117
|
+
"--show-config",
|
118
|
+
action="store_true",
|
119
|
+
help="Show effective configuration and exit",
|
120
|
+
)
|
121
|
+
parser.add_argument(
|
122
|
+
"--set-api-key",
|
123
|
+
type=str,
|
124
|
+
default=None,
|
125
|
+
help="Set and save the API key globally",
|
126
|
+
)
|
127
|
+
parser.add_argument(
|
128
|
+
"--version", action="store_true", help="Show program's version number and exit"
|
129
|
+
)
|
130
|
+
parser.add_argument(
|
131
|
+
"--help-config",
|
132
|
+
action="store_true",
|
133
|
+
help="Show all configuration options and exit",
|
134
|
+
)
|
135
|
+
parser.add_argument(
|
136
|
+
"--continue-session",
|
137
|
+
action="store_true",
|
138
|
+
help="Continue from the last saved conversation",
|
139
|
+
)
|
140
|
+
parser.add_argument(
|
141
|
+
"--web", action="store_true", help="Launch the Janito web server instead of CLI"
|
142
|
+
)
|
143
|
+
parser.add_argument(
|
144
|
+
"--config-reset-local",
|
145
|
+
action="store_true",
|
146
|
+
help="Remove the local config file (~/.janito/config.json)",
|
147
|
+
)
|
148
|
+
parser.add_argument(
|
149
|
+
"--config-reset-global",
|
150
|
+
action="store_true",
|
151
|
+
help="Remove the global config file (~/.janito/config.json)",
|
152
|
+
)
|
153
|
+
parser.add_argument(
|
154
|
+
"--verbose-events",
|
155
|
+
action="store_true",
|
156
|
+
help="Print all agent events before dispatching to the message handler (for debugging)",
|
157
|
+
)
|
158
|
+
parser.add_argument(
|
159
|
+
"-V",
|
160
|
+
"--vanilla",
|
161
|
+
action="store_true",
|
162
|
+
default=False,
|
163
|
+
help="Vanilla mode: disables tools, system prompt, and temperature (unless -t is set)",
|
164
|
+
)
|
165
|
+
parser.add_argument(
|
166
|
+
"-T",
|
167
|
+
"--trust-tools",
|
168
|
+
action="store_true",
|
169
|
+
help="Suppress all tool output (trusted tools mode: only shows output file locations)",
|
170
|
+
)
|
171
|
+
parser.add_argument(
|
172
|
+
"--style",
|
173
|
+
type=str,
|
174
|
+
default=None,
|
175
|
+
help="Interaction style for system prompt template (e.g., default, technical)",
|
176
|
+
)
|
177
|
+
parser.add_argument(
|
178
|
+
"--stream",
|
179
|
+
action="store_true",
|
180
|
+
help="Enable OpenAI streaming mode (yields tokens as they arrive)",
|
181
|
+
)
|
182
|
+
parser.add_argument(
|
183
|
+
"--verbose-stream",
|
184
|
+
action="store_true",
|
185
|
+
help="Print raw chunks as they are fetched from OpenAI (for debugging)",
|
186
|
+
)
|
187
|
+
parser.add_argument(
|
188
|
+
"--scan",
|
189
|
+
action="store_true",
|
190
|
+
help="Scan the project to auto-detect relevant tech/skills and save to .janito/tech.txt (no chat)",
|
191
|
+
)
|
39
192
|
return parser
|
janito/cli/config_commands.py
CHANGED
@@ -19,7 +19,9 @@ def handle_config_commands(args):
|
|
19
19
|
sys.exit(1)
|
20
20
|
key = key.strip()
|
21
21
|
if key not in CONFIG_OPTIONS:
|
22
|
-
print(
|
22
|
+
print(
|
23
|
+
f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
24
|
+
)
|
23
25
|
sys.exit(1)
|
24
26
|
runtime_config.set(key, val.strip())
|
25
27
|
if args.set_local_config:
|
@@ -30,7 +32,9 @@ def handle_config_commands(args):
|
|
30
32
|
sys.exit(1)
|
31
33
|
key = key.strip()
|
32
34
|
if key not in CONFIG_OPTIONS:
|
33
|
-
print(
|
35
|
+
print(
|
36
|
+
f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
37
|
+
)
|
34
38
|
sys.exit(1)
|
35
39
|
local_config.set(key, val.strip())
|
36
40
|
local_config.save()
|
@@ -46,18 +50,20 @@ def handle_config_commands(args):
|
|
46
50
|
sys.exit(1)
|
47
51
|
key = key.strip()
|
48
52
|
if key not in CONFIG_OPTIONS and not key.startswith("template."):
|
49
|
-
print(
|
53
|
+
print(
|
54
|
+
f"Invalid config key: '{key}'. Supported keys are: {', '.join(CONFIG_OPTIONS.keys())}"
|
55
|
+
)
|
50
56
|
sys.exit(1)
|
51
|
-
if key.startswith(
|
52
|
-
subkey = key[len(
|
53
|
-
template_dict = global_config.get(
|
57
|
+
if key.startswith("template."):
|
58
|
+
subkey = key[len("template.") :]
|
59
|
+
template_dict = global_config.get("template", {})
|
54
60
|
template_dict[subkey] = val.strip()
|
55
|
-
global_config.set(
|
61
|
+
global_config.set("template", template_dict)
|
56
62
|
global_config.save()
|
57
63
|
# Remove legacy flat key if present
|
58
64
|
if key in global_config._data:
|
59
65
|
del global_config._data[key]
|
60
|
-
runtime_config.set(
|
66
|
+
runtime_config.set("template", template_dict)
|
61
67
|
print(f"Global config updated: template.{subkey} = {val.strip()}")
|
62
68
|
did_something = True
|
63
69
|
else:
|
@@ -84,7 +90,6 @@ def handle_config_commands(args):
|
|
84
90
|
# Collect and group keys
|
85
91
|
local_keys = set(local_config.all().keys())
|
86
92
|
global_keys = set(global_config.all().keys())
|
87
|
-
all_keys = set(CONFIG_DEFAULTS.keys()) | global_keys | local_keys
|
88
93
|
if not (local_keys or global_keys):
|
89
94
|
print("No configuration found.")
|
90
95
|
else:
|
@@ -103,7 +108,11 @@ def handle_config_commands(args):
|
|
103
108
|
continue
|
104
109
|
if key == "api_key":
|
105
110
|
value = local_config.get("api_key")
|
106
|
-
value =
|
111
|
+
value = (
|
112
|
+
value[:4] + "..." + value[-4:]
|
113
|
+
if value and len(value) > 8
|
114
|
+
else ("***" if value else None)
|
115
|
+
)
|
107
116
|
else:
|
108
117
|
value = unified_config.get(key)
|
109
118
|
local_items[key] = value
|
@@ -119,36 +128,58 @@ def handle_config_commands(args):
|
|
119
128
|
continue
|
120
129
|
if key == "api_key":
|
121
130
|
value = global_config.get("api_key")
|
122
|
-
value =
|
131
|
+
value = (
|
132
|
+
value[:4] + "..." + value[-4:]
|
133
|
+
if value and len(value) > 8
|
134
|
+
else ("***" if value else None)
|
135
|
+
)
|
123
136
|
else:
|
124
137
|
value = unified_config.get(key)
|
125
138
|
global_items[key] = value
|
126
139
|
|
127
140
|
# Mask API key
|
128
141
|
for cfg in (local_items, global_items):
|
129
|
-
if
|
130
|
-
val = cfg[
|
131
|
-
cfg[
|
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
|
+
)
|
132
147
|
|
133
148
|
# Print local config
|
134
149
|
from ._print_config import print_config_items
|
135
|
-
print_config_items(local_items, color_label="[cyan]🏠 Local Configuration[/cyan]")
|
136
150
|
|
137
|
-
|
138
|
-
|
151
|
+
print_config_items(
|
152
|
+
local_items, color_label="[cyan]🏠 Local Configuration[/cyan]"
|
153
|
+
)
|
139
154
|
|
155
|
+
# Print global config
|
156
|
+
print_config_items(
|
157
|
+
global_items, color_label="[yellow]🌐 Global Configuration[/yellow]"
|
158
|
+
)
|
140
159
|
|
141
160
|
# Show defaults for unset keys
|
142
161
|
shown_keys = set(local_items.keys()) | set(global_items.keys())
|
143
|
-
default_items = {
|
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
|
+
}
|
144
167
|
if default_items:
|
145
168
|
print("[green]🟢 Defaults (not set in config files)[/green]")
|
146
169
|
for key, value in default_items.items():
|
147
170
|
# Special case for system_prompt: show template file if None
|
148
171
|
if key == "system_prompt" and value is None:
|
149
172
|
from pathlib import Path
|
150
|
-
|
151
|
-
|
173
|
+
|
174
|
+
template_path = (
|
175
|
+
Path(__file__).parent
|
176
|
+
/ "agent"
|
177
|
+
/ "templates"
|
178
|
+
/ "system_prompt_template.j2"
|
179
|
+
)
|
180
|
+
print(
|
181
|
+
f"{key} = (default template path: {home_shorten(str(template_path))})"
|
182
|
+
)
|
152
183
|
else:
|
153
184
|
print(f"{key} = {value}")
|
154
185
|
print()
|
@@ -156,8 +187,9 @@ def handle_config_commands(args):
|
|
156
187
|
|
157
188
|
import os
|
158
189
|
from pathlib import Path
|
190
|
+
|
159
191
|
if getattr(args, "config_reset_local", False):
|
160
|
-
local_path = Path(
|
192
|
+
local_path = Path(".janito/config.json")
|
161
193
|
if local_path.exists():
|
162
194
|
os.remove(local_path)
|
163
195
|
print(f"Removed local config file: {local_path}")
|
@@ -165,7 +197,7 @@ def handle_config_commands(args):
|
|
165
197
|
print(f"Local config file does not exist: {local_path}")
|
166
198
|
sys.exit(0)
|
167
199
|
if getattr(args, "config_reset_global", False):
|
168
|
-
global_path = Path.home() /
|
200
|
+
global_path = Path.home() / ".janito/config.json"
|
169
201
|
if global_path.exists():
|
170
202
|
os.remove(global_path)
|
171
203
|
print(f"Removed global config file: {global_path}")
|