janito 2.3.0__py3-none-any.whl → 2.3.1__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 +6 -6
- janito/cli/chat_mode/shell/autocomplete.py +21 -21
- janito/cli/chat_mode/shell/commands/clear.py +12 -12
- janito/cli/chat_mode/shell/commands/multi.py +51 -51
- janito/cli/chat_mode/shell/input_history.py +62 -62
- janito/cli/cli_commands/list_models.py +35 -35
- janito/cli/cli_commands/list_providers.py +9 -9
- janito/cli/cli_commands/list_tools.py +53 -53
- janito/cli/cli_commands/model_selection.py +50 -50
- janito/cli/cli_commands/model_utils.py +95 -95
- janito/cli/cli_commands/set_api_key.py +19 -19
- janito/cli/cli_commands/show_config.py +51 -51
- janito/cli/cli_commands/show_system_prompt.py +62 -62
- janito/cli/core/__init__.py +4 -4
- janito/cli/core/event_logger.py +59 -59
- janito/cli/core/getters.py +33 -33
- janito/cli/core/unsetters.py +54 -54
- janito/cli/single_shot_mode/__init__.py +6 -6
- janito/config.py +5 -5
- janito/config_manager.py +112 -112
- janito/drivers/anthropic/driver.py +113 -113
- janito/formatting_token.py +54 -54
- janito/i18n/__init__.py +35 -35
- janito/i18n/messages.py +23 -23
- janito/i18n/pt.py +47 -47
- janito/llm/__init__.py +5 -5
- janito/llm/agent.py +443 -443
- janito/llm/auth.py +63 -63
- janito/llm/driver_config_builder.py +34 -34
- janito/llm/driver_input.py +12 -12
- janito/llm/message_parts.py +60 -60
- janito/llm/model.py +38 -38
- janito/llm/provider.py +196 -196
- janito/provider_registry.py +176 -176
- janito/providers/anthropic/model_info.py +22 -22
- janito/providers/anthropic/provider.py +2 -0
- janito/providers/azure_openai/model_info.py +16 -16
- janito/providers/azure_openai/provider.py +3 -0
- janito/providers/deepseek/__init__.py +1 -1
- janito/providers/deepseek/model_info.py +16 -16
- janito/providers/deepseek/provider.py +94 -91
- janito/providers/google/provider.py +3 -0
- janito/providers/mistralai/provider.py +3 -0
- janito/providers/openai/provider.py +4 -0
- janito/tools/adapters/__init__.py +1 -1
- janito/tools/adapters/local/ask_user.py +102 -102
- janito/tools/adapters/local/copy_file.py +84 -84
- janito/tools/adapters/local/create_directory.py +69 -69
- janito/tools/adapters/local/create_file.py +82 -82
- janito/tools/adapters/local/fetch_url.py +97 -97
- janito/tools/adapters/local/find_files.py +138 -138
- janito/tools/adapters/local/get_file_outline/__init__.py +1 -1
- janito/tools/adapters/local/get_file_outline/core.py +117 -117
- janito/tools/adapters/local/get_file_outline/java_outline.py +40 -40
- janito/tools/adapters/local/get_file_outline/markdown_outline.py +14 -14
- janito/tools/adapters/local/get_file_outline/python_outline.py +303 -303
- janito/tools/adapters/local/get_file_outline/python_outline_v2.py +156 -156
- janito/tools/adapters/local/get_file_outline/search_outline.py +33 -33
- janito/tools/adapters/local/python_code_run.py +166 -166
- janito/tools/adapters/local/python_command_run.py +164 -164
- janito/tools/adapters/local/python_file_run.py +163 -163
- janito/tools/adapters/local/run_bash_command.py +176 -176
- janito/tools/adapters/local/run_powershell_command.py +219 -219
- janito/tools/adapters/local/search_text/__init__.py +1 -1
- janito/tools/adapters/local/search_text/core.py +201 -201
- janito/tools/adapters/local/search_text/pattern_utils.py +73 -73
- janito/tools/adapters/local/search_text/traverse_directory.py +145 -145
- janito/tools/adapters/local/validate_file_syntax/__init__.py +1 -1
- janito/tools/adapters/local/validate_file_syntax/core.py +106 -106
- janito/tools/adapters/local/validate_file_syntax/css_validator.py +35 -35
- janito/tools/adapters/local/validate_file_syntax/html_validator.py +93 -93
- janito/tools/adapters/local/validate_file_syntax/js_validator.py +27 -27
- janito/tools/adapters/local/validate_file_syntax/json_validator.py +6 -6
- janito/tools/adapters/local/validate_file_syntax/markdown_validator.py +109 -109
- janito/tools/adapters/local/validate_file_syntax/ps1_validator.py +32 -32
- janito/tools/adapters/local/validate_file_syntax/python_validator.py +5 -5
- janito/tools/adapters/local/validate_file_syntax/xml_validator.py +11 -11
- janito/tools/adapters/local/validate_file_syntax/yaml_validator.py +6 -6
- janito/tools/adapters/local/view_file.py +167 -167
- janito/tools/inspect_registry.py +17 -17
- janito/tools/tool_base.py +105 -105
- janito/tools/tool_events.py +58 -58
- janito/tools/tool_run_exception.py +12 -12
- janito/tools/tool_use_tracker.py +81 -81
- janito/tools/tool_utils.py +45 -45
- janito/tools/tools_schema.py +104 -104
- janito/version.py +4 -4
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/METADATA +390 -388
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/RECORD +93 -93
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/WHEEL +0 -0
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/entry_points.txt +0 -0
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/licenses/LICENSE +0 -0
- {janito-2.3.0.dist-info → janito-2.3.1.dist-info}/top_level.txt +0 -0
@@ -1,145 +1,145 @@
|
|
1
|
-
import os
|
2
|
-
from janito.gitignore_utils import GitignoreFilter
|
3
|
-
from .match_lines import match_line, should_limit, read_file_lines
|
4
|
-
|
5
|
-
|
6
|
-
def walk_directory(search_path, max_depth):
|
7
|
-
if max_depth == 1:
|
8
|
-
walk_result = next(os.walk(search_path), None)
|
9
|
-
if walk_result is None:
|
10
|
-
return [(search_path, [], [])]
|
11
|
-
else:
|
12
|
-
return [walk_result]
|
13
|
-
else:
|
14
|
-
return os.walk(search_path)
|
15
|
-
|
16
|
-
|
17
|
-
def filter_dirs(dirs, root, gitignore_filter):
|
18
|
-
# Always exclude directories named .git, regardless of gitignore
|
19
|
-
return [
|
20
|
-
d
|
21
|
-
for d in dirs
|
22
|
-
if d != ".git" and not gitignore_filter.is_ignored(os.path.join(root, d))
|
23
|
-
]
|
24
|
-
|
25
|
-
|
26
|
-
def process_file_count_only(
|
27
|
-
file_path,
|
28
|
-
per_file_counts,
|
29
|
-
pattern,
|
30
|
-
regex,
|
31
|
-
use_regex,
|
32
|
-
case_sensitive,
|
33
|
-
max_results,
|
34
|
-
total_results,
|
35
|
-
):
|
36
|
-
match_count, file_limit_reached, _ = read_file_lines(
|
37
|
-
file_path,
|
38
|
-
pattern,
|
39
|
-
regex,
|
40
|
-
use_regex,
|
41
|
-
case_sensitive,
|
42
|
-
True,
|
43
|
-
max_results,
|
44
|
-
total_results + sum(count for _, count in per_file_counts),
|
45
|
-
)
|
46
|
-
if match_count > 0:
|
47
|
-
per_file_counts.append((file_path, match_count))
|
48
|
-
return file_limit_reached
|
49
|
-
|
50
|
-
|
51
|
-
def process_file_collect(
|
52
|
-
file_path,
|
53
|
-
dir_output,
|
54
|
-
per_file_counts,
|
55
|
-
pattern,
|
56
|
-
regex,
|
57
|
-
use_regex,
|
58
|
-
case_sensitive,
|
59
|
-
max_results,
|
60
|
-
total_results,
|
61
|
-
):
|
62
|
-
actual_match_count, file_limit_reached, file_lines_output = read_file_lines(
|
63
|
-
file_path,
|
64
|
-
pattern,
|
65
|
-
regex,
|
66
|
-
use_regex,
|
67
|
-
case_sensitive,
|
68
|
-
False,
|
69
|
-
max_results,
|
70
|
-
total_results + len(dir_output),
|
71
|
-
)
|
72
|
-
dir_output.extend(file_lines_output)
|
73
|
-
if actual_match_count > 0:
|
74
|
-
per_file_counts.append((file_path, actual_match_count))
|
75
|
-
return file_limit_reached
|
76
|
-
|
77
|
-
|
78
|
-
def should_limit_depth(root, search_path, max_depth, dirs):
|
79
|
-
if max_depth > 0:
|
80
|
-
rel_root = os.path.relpath(root, search_path)
|
81
|
-
if rel_root != ".":
|
82
|
-
depth = rel_root.count(os.sep) + 1
|
83
|
-
if depth >= max_depth:
|
84
|
-
del dirs[:]
|
85
|
-
|
86
|
-
|
87
|
-
def traverse_directory(
|
88
|
-
search_path,
|
89
|
-
pattern,
|
90
|
-
regex,
|
91
|
-
use_regex,
|
92
|
-
case_sensitive,
|
93
|
-
max_depth,
|
94
|
-
max_results,
|
95
|
-
total_results,
|
96
|
-
count_only,
|
97
|
-
):
|
98
|
-
dir_output = []
|
99
|
-
dir_limit_reached = False
|
100
|
-
per_file_counts = []
|
101
|
-
walker = walk_directory(search_path, max_depth)
|
102
|
-
gitignore_filter = GitignoreFilter(search_path)
|
103
|
-
|
104
|
-
for root, dirs, files in walker:
|
105
|
-
dirs[:] = filter_dirs(dirs, root, gitignore_filter)
|
106
|
-
for file in files:
|
107
|
-
file_path = os.path.join(root, file)
|
108
|
-
if gitignore_filter.is_ignored(file_path):
|
109
|
-
continue
|
110
|
-
if count_only:
|
111
|
-
file_limit_reached = process_file_count_only(
|
112
|
-
file_path,
|
113
|
-
per_file_counts,
|
114
|
-
pattern,
|
115
|
-
regex,
|
116
|
-
use_regex,
|
117
|
-
case_sensitive,
|
118
|
-
max_results,
|
119
|
-
total_results,
|
120
|
-
)
|
121
|
-
if file_limit_reached:
|
122
|
-
dir_limit_reached = True
|
123
|
-
break
|
124
|
-
else:
|
125
|
-
file_limit_reached = process_file_collect(
|
126
|
-
file_path,
|
127
|
-
dir_output,
|
128
|
-
per_file_counts,
|
129
|
-
pattern,
|
130
|
-
regex,
|
131
|
-
use_regex,
|
132
|
-
case_sensitive,
|
133
|
-
max_results,
|
134
|
-
total_results,
|
135
|
-
)
|
136
|
-
if file_limit_reached:
|
137
|
-
dir_limit_reached = True
|
138
|
-
break
|
139
|
-
if dir_limit_reached:
|
140
|
-
break
|
141
|
-
should_limit_depth(root, search_path, max_depth, dirs)
|
142
|
-
if count_only:
|
143
|
-
return per_file_counts, dir_limit_reached, []
|
144
|
-
else:
|
145
|
-
return dir_output, dir_limit_reached, per_file_counts
|
1
|
+
import os
|
2
|
+
from janito.gitignore_utils import GitignoreFilter
|
3
|
+
from .match_lines import match_line, should_limit, read_file_lines
|
4
|
+
|
5
|
+
|
6
|
+
def walk_directory(search_path, max_depth):
|
7
|
+
if max_depth == 1:
|
8
|
+
walk_result = next(os.walk(search_path), None)
|
9
|
+
if walk_result is None:
|
10
|
+
return [(search_path, [], [])]
|
11
|
+
else:
|
12
|
+
return [walk_result]
|
13
|
+
else:
|
14
|
+
return os.walk(search_path)
|
15
|
+
|
16
|
+
|
17
|
+
def filter_dirs(dirs, root, gitignore_filter):
|
18
|
+
# Always exclude directories named .git, regardless of gitignore
|
19
|
+
return [
|
20
|
+
d
|
21
|
+
for d in dirs
|
22
|
+
if d != ".git" and not gitignore_filter.is_ignored(os.path.join(root, d))
|
23
|
+
]
|
24
|
+
|
25
|
+
|
26
|
+
def process_file_count_only(
|
27
|
+
file_path,
|
28
|
+
per_file_counts,
|
29
|
+
pattern,
|
30
|
+
regex,
|
31
|
+
use_regex,
|
32
|
+
case_sensitive,
|
33
|
+
max_results,
|
34
|
+
total_results,
|
35
|
+
):
|
36
|
+
match_count, file_limit_reached, _ = read_file_lines(
|
37
|
+
file_path,
|
38
|
+
pattern,
|
39
|
+
regex,
|
40
|
+
use_regex,
|
41
|
+
case_sensitive,
|
42
|
+
True,
|
43
|
+
max_results,
|
44
|
+
total_results + sum(count for _, count in per_file_counts),
|
45
|
+
)
|
46
|
+
if match_count > 0:
|
47
|
+
per_file_counts.append((file_path, match_count))
|
48
|
+
return file_limit_reached
|
49
|
+
|
50
|
+
|
51
|
+
def process_file_collect(
|
52
|
+
file_path,
|
53
|
+
dir_output,
|
54
|
+
per_file_counts,
|
55
|
+
pattern,
|
56
|
+
regex,
|
57
|
+
use_regex,
|
58
|
+
case_sensitive,
|
59
|
+
max_results,
|
60
|
+
total_results,
|
61
|
+
):
|
62
|
+
actual_match_count, file_limit_reached, file_lines_output = read_file_lines(
|
63
|
+
file_path,
|
64
|
+
pattern,
|
65
|
+
regex,
|
66
|
+
use_regex,
|
67
|
+
case_sensitive,
|
68
|
+
False,
|
69
|
+
max_results,
|
70
|
+
total_results + len(dir_output),
|
71
|
+
)
|
72
|
+
dir_output.extend(file_lines_output)
|
73
|
+
if actual_match_count > 0:
|
74
|
+
per_file_counts.append((file_path, actual_match_count))
|
75
|
+
return file_limit_reached
|
76
|
+
|
77
|
+
|
78
|
+
def should_limit_depth(root, search_path, max_depth, dirs):
|
79
|
+
if max_depth > 0:
|
80
|
+
rel_root = os.path.relpath(root, search_path)
|
81
|
+
if rel_root != ".":
|
82
|
+
depth = rel_root.count(os.sep) + 1
|
83
|
+
if depth >= max_depth:
|
84
|
+
del dirs[:]
|
85
|
+
|
86
|
+
|
87
|
+
def traverse_directory(
|
88
|
+
search_path,
|
89
|
+
pattern,
|
90
|
+
regex,
|
91
|
+
use_regex,
|
92
|
+
case_sensitive,
|
93
|
+
max_depth,
|
94
|
+
max_results,
|
95
|
+
total_results,
|
96
|
+
count_only,
|
97
|
+
):
|
98
|
+
dir_output = []
|
99
|
+
dir_limit_reached = False
|
100
|
+
per_file_counts = []
|
101
|
+
walker = walk_directory(search_path, max_depth)
|
102
|
+
gitignore_filter = GitignoreFilter(search_path)
|
103
|
+
|
104
|
+
for root, dirs, files in walker:
|
105
|
+
dirs[:] = filter_dirs(dirs, root, gitignore_filter)
|
106
|
+
for file in files:
|
107
|
+
file_path = os.path.join(root, file)
|
108
|
+
if gitignore_filter.is_ignored(file_path):
|
109
|
+
continue
|
110
|
+
if count_only:
|
111
|
+
file_limit_reached = process_file_count_only(
|
112
|
+
file_path,
|
113
|
+
per_file_counts,
|
114
|
+
pattern,
|
115
|
+
regex,
|
116
|
+
use_regex,
|
117
|
+
case_sensitive,
|
118
|
+
max_results,
|
119
|
+
total_results,
|
120
|
+
)
|
121
|
+
if file_limit_reached:
|
122
|
+
dir_limit_reached = True
|
123
|
+
break
|
124
|
+
else:
|
125
|
+
file_limit_reached = process_file_collect(
|
126
|
+
file_path,
|
127
|
+
dir_output,
|
128
|
+
per_file_counts,
|
129
|
+
pattern,
|
130
|
+
regex,
|
131
|
+
use_regex,
|
132
|
+
case_sensitive,
|
133
|
+
max_results,
|
134
|
+
total_results,
|
135
|
+
)
|
136
|
+
if file_limit_reached:
|
137
|
+
dir_limit_reached = True
|
138
|
+
break
|
139
|
+
if dir_limit_reached:
|
140
|
+
break
|
141
|
+
should_limit_depth(root, search_path, max_depth, dirs)
|
142
|
+
if count_only:
|
143
|
+
return per_file_counts, dir_limit_reached, []
|
144
|
+
else:
|
145
|
+
return dir_output, dir_limit_reached, per_file_counts
|
@@ -1 +1 @@
|
|
1
|
-
# Validation syntax package
|
1
|
+
# Validation syntax package
|
@@ -1,106 +1,106 @@
|
|
1
|
-
import os
|
2
|
-
from janito.i18n import tr
|
3
|
-
from janito.tools.tool_base import ToolBase
|
4
|
-
from janito.report_events import ReportAction
|
5
|
-
from janito.tools.adapters.local.adapter import register_local_tool
|
6
|
-
from janito.tools.tool_utils import display_path
|
7
|
-
from janito.tools.adapters.local.adapter import register_local_tool as register_tool
|
8
|
-
|
9
|
-
from .python_validator import validate_python
|
10
|
-
from .json_validator import validate_json
|
11
|
-
from .yaml_validator import validate_yaml
|
12
|
-
from .ps1_validator import validate_ps1
|
13
|
-
from .xml_validator import validate_xml
|
14
|
-
from .html_validator import validate_html
|
15
|
-
from .markdown_validator import validate_markdown
|
16
|
-
from .js_validator import validate_js
|
17
|
-
from .css_validator import validate_css
|
18
|
-
|
19
|
-
|
20
|
-
def _get_validator(ext):
|
21
|
-
"""Return the appropriate validator function for the file extension."""
|
22
|
-
mapping = {
|
23
|
-
".py": validate_python,
|
24
|
-
".pyw": validate_python,
|
25
|
-
".json": validate_json,
|
26
|
-
".yml": validate_yaml,
|
27
|
-
".yaml": validate_yaml,
|
28
|
-
".ps1": validate_ps1,
|
29
|
-
".xml": validate_xml,
|
30
|
-
".html": validate_html,
|
31
|
-
".htm": validate_html,
|
32
|
-
".md": validate_markdown,
|
33
|
-
".js": validate_js,
|
34
|
-
".css": validate_css,
|
35
|
-
}
|
36
|
-
return mapping.get(ext)
|
37
|
-
|
38
|
-
|
39
|
-
def _handle_validation_error(e, report_warning):
|
40
|
-
msg = tr("⚠️ Warning: Syntax error: {error}", error=e)
|
41
|
-
if report_warning:
|
42
|
-
report_warning(msg)
|
43
|
-
return msg
|
44
|
-
|
45
|
-
|
46
|
-
def validate_file_syntax(
|
47
|
-
file_path: str, report_info=None, report_warning=None, report_success=None
|
48
|
-
) -> str:
|
49
|
-
ext = os.path.splitext(file_path)[1].lower()
|
50
|
-
validator = _get_validator(ext)
|
51
|
-
try:
|
52
|
-
if validator:
|
53
|
-
return validator(file_path)
|
54
|
-
else:
|
55
|
-
msg = tr("⚠️ Warning: Unsupported file extension: {ext}", ext=ext)
|
56
|
-
if report_warning:
|
57
|
-
report_warning(msg)
|
58
|
-
return msg
|
59
|
-
except Exception as e:
|
60
|
-
return _handle_validation_error(e, report_warning)
|
61
|
-
|
62
|
-
|
63
|
-
class ValidateFileSyntaxTool(ToolBase):
|
64
|
-
"""
|
65
|
-
Validate a file for syntax issues.
|
66
|
-
|
67
|
-
Supported types:
|
68
|
-
- Python (.py, .pyw)
|
69
|
-
- JSON (.json)
|
70
|
-
- YAML (.yml, .yaml)
|
71
|
-
- PowerShell (.ps1)
|
72
|
-
- XML (.xml)
|
73
|
-
- HTML (.html, .htm) [lxml]
|
74
|
-
- Markdown (.md)
|
75
|
-
- JavaScript (.js)
|
76
|
-
|
77
|
-
Args:
|
78
|
-
file_path (str): Path to the file to validate.
|
79
|
-
Returns:
|
80
|
-
str: Validation status message. Example:
|
81
|
-
- "✅ Syntax OK"
|
82
|
-
- "⚠️ Warning: Syntax error: <error message>"
|
83
|
-
- "⚠️ Warning: Unsupported file extension: <ext>"
|
84
|
-
"""
|
85
|
-
|
86
|
-
tool_name = "validate_file_syntax"
|
87
|
-
|
88
|
-
def run(self, file_path: str) -> str:
|
89
|
-
disp_path = display_path(file_path)
|
90
|
-
self.report_action(
|
91
|
-
tr(
|
92
|
-
"🔎 Validate syntax for file '{disp_path}' ...",
|
93
|
-
disp_path=disp_path,
|
94
|
-
),
|
95
|
-
ReportAction.READ,
|
96
|
-
)
|
97
|
-
result = validate_file_syntax(
|
98
|
-
file_path,
|
99
|
-
report_info=self.report_info,
|
100
|
-
report_warning=self.report_warning,
|
101
|
-
report_success=self.report_success,
|
102
|
-
)
|
103
|
-
if result.startswith("✅"):
|
104
|
-
self.report_success(result, ReportAction.READ)
|
105
|
-
|
106
|
-
return result
|
1
|
+
import os
|
2
|
+
from janito.i18n import tr
|
3
|
+
from janito.tools.tool_base import ToolBase
|
4
|
+
from janito.report_events import ReportAction
|
5
|
+
from janito.tools.adapters.local.adapter import register_local_tool
|
6
|
+
from janito.tools.tool_utils import display_path
|
7
|
+
from janito.tools.adapters.local.adapter import register_local_tool as register_tool
|
8
|
+
|
9
|
+
from .python_validator import validate_python
|
10
|
+
from .json_validator import validate_json
|
11
|
+
from .yaml_validator import validate_yaml
|
12
|
+
from .ps1_validator import validate_ps1
|
13
|
+
from .xml_validator import validate_xml
|
14
|
+
from .html_validator import validate_html
|
15
|
+
from .markdown_validator import validate_markdown
|
16
|
+
from .js_validator import validate_js
|
17
|
+
from .css_validator import validate_css
|
18
|
+
|
19
|
+
|
20
|
+
def _get_validator(ext):
|
21
|
+
"""Return the appropriate validator function for the file extension."""
|
22
|
+
mapping = {
|
23
|
+
".py": validate_python,
|
24
|
+
".pyw": validate_python,
|
25
|
+
".json": validate_json,
|
26
|
+
".yml": validate_yaml,
|
27
|
+
".yaml": validate_yaml,
|
28
|
+
".ps1": validate_ps1,
|
29
|
+
".xml": validate_xml,
|
30
|
+
".html": validate_html,
|
31
|
+
".htm": validate_html,
|
32
|
+
".md": validate_markdown,
|
33
|
+
".js": validate_js,
|
34
|
+
".css": validate_css,
|
35
|
+
}
|
36
|
+
return mapping.get(ext)
|
37
|
+
|
38
|
+
|
39
|
+
def _handle_validation_error(e, report_warning):
|
40
|
+
msg = tr("⚠️ Warning: Syntax error: {error}", error=e)
|
41
|
+
if report_warning:
|
42
|
+
report_warning(msg)
|
43
|
+
return msg
|
44
|
+
|
45
|
+
|
46
|
+
def validate_file_syntax(
|
47
|
+
file_path: str, report_info=None, report_warning=None, report_success=None
|
48
|
+
) -> str:
|
49
|
+
ext = os.path.splitext(file_path)[1].lower()
|
50
|
+
validator = _get_validator(ext)
|
51
|
+
try:
|
52
|
+
if validator:
|
53
|
+
return validator(file_path)
|
54
|
+
else:
|
55
|
+
msg = tr("⚠️ Warning: Unsupported file extension: {ext}", ext=ext)
|
56
|
+
if report_warning:
|
57
|
+
report_warning(msg)
|
58
|
+
return msg
|
59
|
+
except Exception as e:
|
60
|
+
return _handle_validation_error(e, report_warning)
|
61
|
+
|
62
|
+
|
63
|
+
class ValidateFileSyntaxTool(ToolBase):
|
64
|
+
"""
|
65
|
+
Validate a file for syntax issues.
|
66
|
+
|
67
|
+
Supported types:
|
68
|
+
- Python (.py, .pyw)
|
69
|
+
- JSON (.json)
|
70
|
+
- YAML (.yml, .yaml)
|
71
|
+
- PowerShell (.ps1)
|
72
|
+
- XML (.xml)
|
73
|
+
- HTML (.html, .htm) [lxml]
|
74
|
+
- Markdown (.md)
|
75
|
+
- JavaScript (.js)
|
76
|
+
|
77
|
+
Args:
|
78
|
+
file_path (str): Path to the file to validate.
|
79
|
+
Returns:
|
80
|
+
str: Validation status message. Example:
|
81
|
+
- "✅ Syntax OK"
|
82
|
+
- "⚠️ Warning: Syntax error: <error message>"
|
83
|
+
- "⚠️ Warning: Unsupported file extension: <ext>"
|
84
|
+
"""
|
85
|
+
|
86
|
+
tool_name = "validate_file_syntax"
|
87
|
+
|
88
|
+
def run(self, file_path: str) -> str:
|
89
|
+
disp_path = display_path(file_path)
|
90
|
+
self.report_action(
|
91
|
+
tr(
|
92
|
+
"🔎 Validate syntax for file '{disp_path}' ...",
|
93
|
+
disp_path=disp_path,
|
94
|
+
),
|
95
|
+
ReportAction.READ,
|
96
|
+
)
|
97
|
+
result = validate_file_syntax(
|
98
|
+
file_path,
|
99
|
+
report_info=self.report_info,
|
100
|
+
report_warning=self.report_warning,
|
101
|
+
report_success=self.report_success,
|
102
|
+
)
|
103
|
+
if result.startswith("✅"):
|
104
|
+
self.report_success(result, ReportAction.READ)
|
105
|
+
|
106
|
+
return result
|
@@ -1,35 +1,35 @@
|
|
1
|
-
from janito.i18n import tr
|
2
|
-
import re
|
3
|
-
|
4
|
-
|
5
|
-
def validate_css(file_path: str) -> str:
|
6
|
-
with open(file_path, "r", encoding="utf-8") as f:
|
7
|
-
content = f.read()
|
8
|
-
errors = []
|
9
|
-
# Check for unmatched curly braces
|
10
|
-
if content.count("{") != content.count("}"):
|
11
|
-
errors.append("Unmatched curly braces { }")
|
12
|
-
# Check for unclosed comments
|
13
|
-
if content.count("/*") != content.count("*/"):
|
14
|
-
errors.append("Unclosed comment (/* ... */)")
|
15
|
-
# Check for invalid property declarations (very basic)
|
16
|
-
for i, line in enumerate(content.splitlines(), 1):
|
17
|
-
# Ignore empty lines and comments
|
18
|
-
if not line.strip() or line.strip().startswith("/*"):
|
19
|
-
continue
|
20
|
-
# Match property: value; (allow whitespace)
|
21
|
-
if ":" in line and not re.search(r":.*;", line):
|
22
|
-
errors.append(
|
23
|
-
f"Line {i}: Missing semicolon after property value | {line.strip()}"
|
24
|
-
)
|
25
|
-
# Match lines with property but missing colon
|
26
|
-
if ";" in line and ":" not in line:
|
27
|
-
errors.append(
|
28
|
-
f"Line {i}: Missing colon in property declaration | {line.strip()}"
|
29
|
-
)
|
30
|
-
if errors:
|
31
|
-
msg = tr(
|
32
|
-
"⚠️ Warning: CSS syntax issues found:\n{errors}", errors="\n".join(errors)
|
33
|
-
)
|
34
|
-
return msg
|
35
|
-
return "✅ OK"
|
1
|
+
from janito.i18n import tr
|
2
|
+
import re
|
3
|
+
|
4
|
+
|
5
|
+
def validate_css(file_path: str) -> str:
|
6
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
7
|
+
content = f.read()
|
8
|
+
errors = []
|
9
|
+
# Check for unmatched curly braces
|
10
|
+
if content.count("{") != content.count("}"):
|
11
|
+
errors.append("Unmatched curly braces { }")
|
12
|
+
# Check for unclosed comments
|
13
|
+
if content.count("/*") != content.count("*/"):
|
14
|
+
errors.append("Unclosed comment (/* ... */)")
|
15
|
+
# Check for invalid property declarations (very basic)
|
16
|
+
for i, line in enumerate(content.splitlines(), 1):
|
17
|
+
# Ignore empty lines and comments
|
18
|
+
if not line.strip() or line.strip().startswith("/*"):
|
19
|
+
continue
|
20
|
+
# Match property: value; (allow whitespace)
|
21
|
+
if ":" in line and not re.search(r":.*;", line):
|
22
|
+
errors.append(
|
23
|
+
f"Line {i}: Missing semicolon after property value | {line.strip()}"
|
24
|
+
)
|
25
|
+
# Match lines with property but missing colon
|
26
|
+
if ";" in line and ":" not in line:
|
27
|
+
errors.append(
|
28
|
+
f"Line {i}: Missing colon in property declaration | {line.strip()}"
|
29
|
+
)
|
30
|
+
if errors:
|
31
|
+
msg = tr(
|
32
|
+
"⚠️ Warning: CSS syntax issues found:\n{errors}", errors="\n".join(errors)
|
33
|
+
)
|
34
|
+
return msg
|
35
|
+
return "✅ OK"
|