jarvis-ai-assistant 0.7.16__py3-none-any.whl → 1.0.2__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.16.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
jarvis/jarvis_code_agent/lint.py
CHANGED
|
@@ -8,324 +8,225 @@ Lint工具配置模块
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
|
-
from typing import Dict
|
|
11
|
+
from typing import Dict
|
|
12
|
+
from typing import List
|
|
13
|
+
from typing import Optional
|
|
14
|
+
from typing import Tuple
|
|
12
15
|
|
|
13
|
-
import yaml
|
|
16
|
+
import yaml
|
|
14
17
|
|
|
15
18
|
from jarvis.jarvis_utils.config import get_data_dir
|
|
16
19
|
|
|
17
|
-
#
|
|
18
|
-
|
|
20
|
+
# Lint工具命令模板映射(文件扩展名/文件名 -> 命令模板列表)
|
|
21
|
+
# 占位符说明:
|
|
22
|
+
# - {file_path}: 单个文件的完整路径
|
|
23
|
+
# - {file_name}: 文件名(不含路径)
|
|
24
|
+
# - {config}: 配置文件路径(可选)
|
|
25
|
+
LINT_COMMAND_TEMPLATES_BY_FILE: Dict[str, List[str]] = {
|
|
19
26
|
# C/C++
|
|
20
|
-
".c": ["clang-tidy"],
|
|
21
|
-
".cpp": ["clang-tidy"],
|
|
22
|
-
".cc": ["clang-tidy"],
|
|
23
|
-
".cxx": ["clang-tidy"],
|
|
24
|
-
".h": ["clang-tidy"],
|
|
25
|
-
".hpp": ["clang-tidy"],
|
|
26
|
-
".hxx": ["clang-tidy"],
|
|
27
|
-
".inl": ["clang-tidy"],
|
|
28
|
-
".ipp": ["clang-tidy"],
|
|
27
|
+
".c": ["clang-tidy {file_path}"],
|
|
28
|
+
".cpp": ["clang-tidy {file_path}"],
|
|
29
|
+
".cc": ["clang-tidy {file_path}"],
|
|
30
|
+
".cxx": ["clang-tidy {file_path}"],
|
|
31
|
+
".h": ["clang-tidy {file_path}"],
|
|
32
|
+
".hpp": ["clang-tidy {file_path}"],
|
|
33
|
+
".hxx": ["clang-tidy {file_path}"],
|
|
34
|
+
".inl": ["clang-tidy {file_path}"],
|
|
35
|
+
".ipp": ["clang-tidy {file_path}"],
|
|
29
36
|
# Go
|
|
30
|
-
".go": ["go vet"],
|
|
37
|
+
".go": ["go vet {file_path}"],
|
|
31
38
|
# Python
|
|
32
|
-
".py": [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
".
|
|
39
|
+
".py": [
|
|
40
|
+
"ruff check {file_path}",
|
|
41
|
+
"mypy {file_path}",
|
|
42
|
+
],
|
|
43
|
+
".pyw": [
|
|
44
|
+
"ruff check {file_path}",
|
|
45
|
+
"mypy {file_path}",
|
|
46
|
+
],
|
|
47
|
+
".pyi": [
|
|
48
|
+
"ruff check {file_path}",
|
|
49
|
+
"mypy {file_path}",
|
|
50
|
+
],
|
|
51
|
+
".pyx": [
|
|
52
|
+
"ruff check {file_path}",
|
|
53
|
+
"mypy {file_path}",
|
|
54
|
+
],
|
|
55
|
+
".pxd": [
|
|
56
|
+
"ruff check {file_path}",
|
|
57
|
+
"mypy {file_path}",
|
|
58
|
+
],
|
|
37
59
|
# Rust
|
|
38
|
-
".rs": ["cargo clippy"],
|
|
39
|
-
".rlib": ["cargo clippy"],
|
|
60
|
+
".rs": ["cargo clippy --message-format=short"],
|
|
61
|
+
".rlib": ["cargo clippy --message-format=short"],
|
|
40
62
|
# Java
|
|
41
|
-
".java": ["pmd"],
|
|
42
|
-
".class": ["pmd"],
|
|
43
|
-
".jar": ["pmd"],
|
|
63
|
+
".java": ["pmd check -d {file_path}"],
|
|
64
|
+
".class": ["pmd check -d {file_path}"],
|
|
65
|
+
".jar": ["pmd check -d {file_path}"],
|
|
44
66
|
# JavaScript/TypeScript
|
|
45
|
-
".js": ["eslint"],
|
|
46
|
-
".mjs": ["eslint"],
|
|
47
|
-
".cjs": ["eslint"],
|
|
48
|
-
".jsx": ["eslint"],
|
|
49
|
-
".ts": [
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
".js": ["eslint {file_path}"],
|
|
68
|
+
".mjs": ["eslint {file_path}"],
|
|
69
|
+
".cjs": ["eslint {file_path}"],
|
|
70
|
+
".jsx": ["eslint {file_path}"],
|
|
71
|
+
".ts": [
|
|
72
|
+
"eslint {file_path}",
|
|
73
|
+
"tsc --noEmit {file_path}",
|
|
74
|
+
],
|
|
75
|
+
".tsx": [
|
|
76
|
+
"eslint {file_path}",
|
|
77
|
+
"tsc --noEmit {file_path}",
|
|
78
|
+
],
|
|
79
|
+
".cts": [
|
|
80
|
+
"eslint {file_path}",
|
|
81
|
+
"tsc --noEmit {file_path}",
|
|
82
|
+
],
|
|
83
|
+
".mts": [
|
|
84
|
+
"eslint {file_path}",
|
|
85
|
+
"tsc --noEmit {file_path}",
|
|
86
|
+
],
|
|
53
87
|
# PHP
|
|
54
|
-
".php": ["phpstan"],
|
|
55
|
-
".phtml": ["phpstan"],
|
|
56
|
-
".php5": ["phpstan"],
|
|
57
|
-
".php7": ["phpstan"],
|
|
58
|
-
".phps": ["phpstan"],
|
|
88
|
+
".php": ["phpstan analyse {file_path}"],
|
|
89
|
+
".phtml": ["phpstan analyse {file_path}"],
|
|
90
|
+
".php5": ["phpstan analyse {file_path}"],
|
|
91
|
+
".php7": ["phpstan analyse {file_path}"],
|
|
92
|
+
".phps": ["phpstan analyse {file_path}"],
|
|
59
93
|
# Ruby
|
|
60
|
-
".rb": ["rubocop"],
|
|
61
|
-
".rake": ["rubocop"],
|
|
62
|
-
".gemspec": ["rubocop"],
|
|
94
|
+
".rb": ["rubocop {file_path}"],
|
|
95
|
+
".rake": ["rubocop {file_path}"],
|
|
96
|
+
".gemspec": ["rubocop {file_path}"],
|
|
63
97
|
# Swift
|
|
64
|
-
".swift": ["swiftlint"],
|
|
98
|
+
".swift": ["swiftlint lint {file_path}"],
|
|
65
99
|
# Kotlin
|
|
66
|
-
".kt": ["ktlint"],
|
|
67
|
-
".kts": ["ktlint"],
|
|
100
|
+
".kt": ["ktlint {file_path}"],
|
|
101
|
+
".kts": ["ktlint {file_path}"],
|
|
68
102
|
# C#
|
|
69
|
-
".cs": ["roslynator"],
|
|
70
|
-
".csx": ["roslynator"],
|
|
103
|
+
".cs": ["roslynator analyze {file_path}"],
|
|
104
|
+
".csx": ["roslynator analyze {file_path}"],
|
|
71
105
|
# SQL
|
|
72
|
-
".sql": ["sqlfluff"],
|
|
106
|
+
".sql": ["sqlfluff lint {file_path}"],
|
|
73
107
|
# Shell/Bash
|
|
74
|
-
".sh": ["shellcheck"],
|
|
75
|
-
".bash": ["shellcheck"],
|
|
108
|
+
".sh": ["shellcheck {file_path}"],
|
|
109
|
+
".bash": ["shellcheck {file_path}"],
|
|
76
110
|
# HTML/CSS
|
|
77
|
-
".html": ["htmlhint"],
|
|
78
|
-
".htm": ["htmlhint"],
|
|
79
|
-
".xhtml": ["htmlhint"],
|
|
80
|
-
".css": ["stylelint"],
|
|
81
|
-
".scss": ["stylelint"],
|
|
82
|
-
".sass": ["stylelint"],
|
|
83
|
-
".less": ["stylelint"],
|
|
111
|
+
".html": ["htmlhint {file_path}"],
|
|
112
|
+
".htm": ["htmlhint {file_path}"],
|
|
113
|
+
".xhtml": ["htmlhint {file_path}"],
|
|
114
|
+
".css": ["stylelint {file_path}"],
|
|
115
|
+
".scss": ["stylelint {file_path}"],
|
|
116
|
+
".sass": ["stylelint {file_path}"],
|
|
117
|
+
".less": ["stylelint {file_path}"],
|
|
84
118
|
# XML/JSON/YAML
|
|
85
|
-
".xml": ["xmllint"],
|
|
86
|
-
".xsd": ["xmllint"],
|
|
87
|
-
".dtd": ["xmllint"],
|
|
88
|
-
".tld": ["xmllint"],
|
|
89
|
-
".jsp": ["xmllint"],
|
|
90
|
-
".jspx": ["xmllint"],
|
|
91
|
-
".tag": ["xmllint"],
|
|
92
|
-
".tagx": ["xmllint"],
|
|
93
|
-
".json": ["jsonlint"],
|
|
94
|
-
".jsonl": ["jsonlint"],
|
|
95
|
-
".json5": ["jsonlint"],
|
|
96
|
-
".yaml": ["yamllint"],
|
|
97
|
-
".yml": ["yamllint"],
|
|
119
|
+
".xml": ["xmllint --noout {file_path}"],
|
|
120
|
+
".xsd": ["xmllint --noout {file_path}"],
|
|
121
|
+
".dtd": ["xmllint --noout {file_path}"],
|
|
122
|
+
".tld": ["xmllint --noout {file_path}"],
|
|
123
|
+
".jsp": ["xmllint --noout {file_path}"],
|
|
124
|
+
".jspx": ["xmllint --noout {file_path}"],
|
|
125
|
+
".tag": ["xmllint --noout {file_path}"],
|
|
126
|
+
".tagx": ["xmllint --noout {file_path}"],
|
|
127
|
+
".json": ["jsonlint {file_path}"],
|
|
128
|
+
".jsonl": ["jsonlint {file_path}"],
|
|
129
|
+
".json5": ["jsonlint {file_path}"],
|
|
130
|
+
".yaml": ["yamllint {file_path}"],
|
|
131
|
+
".yml": ["yamllint {file_path}"],
|
|
98
132
|
# Markdown/Documentation
|
|
99
|
-
".md": ["markdownlint"],
|
|
100
|
-
".markdown": ["markdownlint"],
|
|
101
|
-
".rst": ["rstcheck"],
|
|
102
|
-
".adoc": ["asciidoctor-lint"],
|
|
133
|
+
".md": ["markdownlint {file_path}"],
|
|
134
|
+
".markdown": ["markdownlint {file_path}"],
|
|
135
|
+
".rst": ["rstcheck {file_path}"],
|
|
136
|
+
".adoc": ["asciidoctor-lint {file_path}"],
|
|
103
137
|
# Docker/Terraform/Makefile等无后缀文件
|
|
104
|
-
"makefile": ["checkmake"],
|
|
105
|
-
"dockerfile": ["hadolint"],
|
|
106
|
-
"docker-compose.yml": ["hadolint"],
|
|
107
|
-
"docker-compose.yaml": ["hadolint"],
|
|
108
|
-
"jenkinsfile": ["jenkinsfile-linter"],
|
|
109
|
-
"build": ["buildifier"],
|
|
110
|
-
"workspace": ["buildifier"],
|
|
111
|
-
".bashrc": ["shellcheck"],
|
|
112
|
-
".bash_profile": ["shellcheck"],
|
|
113
|
-
".zshrc": ["shellcheck"],
|
|
114
|
-
".gitignore": ["git-lint"],
|
|
115
|
-
".editorconfig": ["editorconfig-checker"],
|
|
116
|
-
".eslintrc": ["eslint"],
|
|
117
|
-
".prettierrc": ["prettier"],
|
|
138
|
+
"makefile": ["checkmake {file_path}"],
|
|
139
|
+
"dockerfile": ["hadolint {file_path}"],
|
|
140
|
+
"docker-compose.yml": ["hadolint {file_path}"],
|
|
141
|
+
"docker-compose.yaml": ["hadolint {file_path}"],
|
|
142
|
+
"jenkinsfile": ["jenkinsfile-linter {file_path}"],
|
|
143
|
+
"build": ["buildifier {file_path}"],
|
|
144
|
+
"workspace": ["buildifier {file_path}"],
|
|
145
|
+
".bashrc": ["shellcheck {file_path}"],
|
|
146
|
+
".bash_profile": ["shellcheck {file_path}"],
|
|
147
|
+
".zshrc": ["shellcheck {file_path}"],
|
|
148
|
+
".gitignore": ["git-lint {file_path}"],
|
|
149
|
+
".editorconfig": ["editorconfig-checker {file_path}"],
|
|
150
|
+
".eslintrc": ["eslint {file_path}"],
|
|
151
|
+
".prettierrc": ["prettier --check {file_path}"],
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
|
|
121
155
|
def load_lint_tools_config() -> Dict[str, List[str]]:
|
|
122
|
-
"""从yaml
|
|
156
|
+
"""从yaml文件加载全局lint工具配置
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Dict[str, List[str]]: 文件扩展名/文件名 -> 命令模板列表
|
|
160
|
+
"""
|
|
123
161
|
config_path = os.path.join(get_data_dir(), "lint_tools.yaml")
|
|
124
162
|
if not os.path.exists(config_path):
|
|
125
163
|
return {}
|
|
126
164
|
|
|
127
165
|
with open(config_path, "r") as f:
|
|
128
166
|
config = yaml.safe_load(f) or {}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
result = {}
|
|
168
|
+
for k, v in config.items():
|
|
169
|
+
k_lower = k.lower()
|
|
170
|
+
# 支持格式: ["template1", "template2"] 或 [("tool1", "template1"), ("tool2", "template2")]
|
|
171
|
+
if isinstance(v, list) and v:
|
|
172
|
+
if isinstance(v[0], str):
|
|
173
|
+
# 新格式:直接是命令模板列表
|
|
174
|
+
result[k_lower] = v
|
|
175
|
+
elif isinstance(v[0], (list, tuple)) and len(v[0]) == 2:
|
|
176
|
+
# 旧格式:需要提取模板
|
|
177
|
+
result[k_lower] = [template for _, template in v]
|
|
178
|
+
return result
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def load_project_lint_tools_config(project_root: str) -> Dict[str, List[str]]:
|
|
182
|
+
"""从项目根目录加载lint工具配置
|
|
140
183
|
|
|
141
184
|
Args:
|
|
142
|
-
filename: 文件路径或文件名(如'test.py'或'Makefile')
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
对应的lint工具列表,如果找不到则返回空列表
|
|
146
|
-
"""
|
|
147
|
-
filename = os.path.basename(filename)
|
|
148
|
-
filename_lower = filename.lower()
|
|
149
|
-
|
|
150
|
-
# 优先尝试完整文件名匹配(例如 docker-compose.yml, .eslintrc, .prettierrc)
|
|
151
|
-
lint_tools = LINT_TOOLS.get(filename_lower, [])
|
|
152
|
-
if lint_tools:
|
|
153
|
-
return lint_tools
|
|
154
|
-
|
|
155
|
-
# 如果文件名匹配失败,再尝试扩展名匹配
|
|
156
|
-
ext = os.path.splitext(filename)[1]
|
|
157
|
-
if ext:
|
|
158
|
-
return LINT_TOOLS.get(ext.lower(), [])
|
|
159
|
-
|
|
160
|
-
return []
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
# Lint工具命令模板映射
|
|
164
|
-
# 占位符说明:
|
|
165
|
-
# - {file_path}: 单个文件的完整路径
|
|
166
|
-
# - {files}: 多个文件路径,用空格分隔
|
|
167
|
-
# - {file_name}: 文件名(不含路径)
|
|
168
|
-
LINT_COMMAND_TEMPLATES: Dict[str, str] = {
|
|
169
|
-
# Python
|
|
170
|
-
"ruff": "ruff check {file_path}",
|
|
171
|
-
"mypy": "mypy {file_path}",
|
|
172
|
-
"pylint": "pylint {file_path}",
|
|
173
|
-
"flake8": "flake8 {file_path}",
|
|
174
|
-
# JavaScript/TypeScript
|
|
175
|
-
"eslint": "eslint {file_path}",
|
|
176
|
-
"tsc": "tsc --noEmit {file_path}",
|
|
177
|
-
# Rust
|
|
178
|
-
"cargo clippy": "cargo clippy --message-format=short",
|
|
179
|
-
# Go
|
|
180
|
-
"go vet": "go vet {file_path}",
|
|
181
|
-
"golint": "golint {file_path}",
|
|
182
|
-
# Java
|
|
183
|
-
"pmd": "pmd check -d {file_path}",
|
|
184
|
-
"checkstyle": "checkstyle -c {config} {file_path}",
|
|
185
|
-
# C/C++
|
|
186
|
-
"clang-tidy": "clang-tidy {file_path}",
|
|
187
|
-
"cppcheck": "cppcheck {file_path}",
|
|
188
|
-
# PHP
|
|
189
|
-
"phpstan": "phpstan analyse {file_path}",
|
|
190
|
-
# Ruby
|
|
191
|
-
"rubocop": "rubocop {file_path}",
|
|
192
|
-
# Swift
|
|
193
|
-
"swiftlint": "swiftlint lint {file_path}",
|
|
194
|
-
# Kotlin
|
|
195
|
-
"ktlint": "ktlint {file_path}",
|
|
196
|
-
# C#
|
|
197
|
-
"roslynator": "roslynator analyze {file_path}",
|
|
198
|
-
# SQL
|
|
199
|
-
"sqlfluff": "sqlfluff lint {file_path}",
|
|
200
|
-
# Shell/Bash
|
|
201
|
-
"shellcheck": "shellcheck {file_path}",
|
|
202
|
-
# HTML/CSS
|
|
203
|
-
"htmlhint": "htmlhint {file_path}",
|
|
204
|
-
"stylelint": "stylelint {file_path}",
|
|
205
|
-
# XML/JSON/YAML
|
|
206
|
-
"xmllint": "xmllint --noout {file_path}",
|
|
207
|
-
"jsonlint": "jsonlint {file_path}",
|
|
208
|
-
"yamllint": "yamllint {file_path}",
|
|
209
|
-
# Markdown
|
|
210
|
-
"markdownlint": "markdownlint {file_path}",
|
|
211
|
-
"rstcheck": "rstcheck {file_path}",
|
|
212
|
-
# Docker
|
|
213
|
-
"hadolint": "hadolint {file_path}",
|
|
214
|
-
# Makefile
|
|
215
|
-
"checkmake": "checkmake {file_path}",
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def find_config_file(
|
|
220
|
-
config_names: List[str],
|
|
221
|
-
project_root: Optional[str] = None,
|
|
222
|
-
file_path: Optional[str] = None,
|
|
223
|
-
) -> Optional[str]:
|
|
224
|
-
"""
|
|
225
|
-
查找配置文件
|
|
226
|
-
|
|
227
|
-
Args:
|
|
228
|
-
config_names: 配置文件名列表(按优先级排序)
|
|
229
185
|
project_root: 项目根目录
|
|
230
|
-
file_path: 当前文件路径(可选,用于从文件所在目录向上查找)
|
|
231
186
|
|
|
232
187
|
Returns:
|
|
233
|
-
|
|
188
|
+
Dict[str, List[str]]: 文件扩展名/文件名 -> 命令模板列表
|
|
234
189
|
"""
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
search_dirs.append(project_root)
|
|
239
|
-
|
|
240
|
-
# 如果提供了文件路径,从文件所在目录向上查找
|
|
241
|
-
if file_path:
|
|
242
|
-
if os.path.isabs(file_path):
|
|
243
|
-
current_dir = os.path.dirname(file_path)
|
|
244
|
-
elif project_root:
|
|
245
|
-
current_dir = os.path.dirname(os.path.join(project_root, file_path))
|
|
246
|
-
else:
|
|
247
|
-
current_dir = os.path.dirname(os.path.abspath(file_path))
|
|
248
|
-
|
|
249
|
-
# 向上查找直到项目根目录
|
|
250
|
-
while current_dir:
|
|
251
|
-
if current_dir not in search_dirs:
|
|
252
|
-
search_dirs.append(current_dir)
|
|
253
|
-
if project_root and current_dir == project_root:
|
|
254
|
-
break
|
|
255
|
-
parent = os.path.dirname(current_dir)
|
|
256
|
-
if parent == current_dir: # 到达根目录
|
|
257
|
-
break
|
|
258
|
-
current_dir = parent
|
|
259
|
-
|
|
260
|
-
# 按优先级查找配置文件
|
|
261
|
-
for config_name in config_names:
|
|
262
|
-
for search_dir in search_dirs:
|
|
263
|
-
config_path = os.path.join(search_dir, config_name)
|
|
264
|
-
if os.path.exists(config_path) and os.path.isfile(config_path):
|
|
265
|
-
return os.path.abspath(config_path)
|
|
266
|
-
|
|
267
|
-
return None
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
# 工具配置文件映射(工具名 -> 可能的配置文件名列表)
|
|
271
|
-
TOOL_CONFIG_FILES: Dict[str, List[str]] = {
|
|
272
|
-
"checkstyle": ["checkstyle.xml", ".checkstyle.xml", "checkstyle-config.xml"],
|
|
273
|
-
"eslint": [
|
|
274
|
-
".eslintrc.js",
|
|
275
|
-
".eslintrc.json",
|
|
276
|
-
".eslintrc.yml",
|
|
277
|
-
".eslintrc.yaml",
|
|
278
|
-
".eslintrc",
|
|
279
|
-
],
|
|
280
|
-
"stylelint": [
|
|
281
|
-
".stylelintrc",
|
|
282
|
-
".stylelintrc.json",
|
|
283
|
-
".stylelintrc.yml",
|
|
284
|
-
".stylelintrc.yaml",
|
|
285
|
-
"stylelint.config.js",
|
|
286
|
-
],
|
|
287
|
-
"yamllint": [".yamllint", ".yamllint.yml", ".yamllint.yaml"],
|
|
288
|
-
"markdownlint": [".markdownlint.json", ".markdownlintrc"],
|
|
289
|
-
"rubocop": [".rubocop.yml", ".rubocop.yaml", ".rubocop.toml"],
|
|
290
|
-
"phpstan": ["phpstan.neon", "phpstan.neon.dist"],
|
|
291
|
-
"sqlfluff": [".sqlfluff", "sqlfluff.ini", ".sqlfluff.ini"],
|
|
292
|
-
"hadolint": [".hadolint.yaml", ".hadolint.yml"],
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
# 必须配置文件的工具(未找到配置文件时不能执行)
|
|
296
|
-
REQUIRED_CONFIG_TOOLS = {"checkstyle"}
|
|
297
|
-
|
|
298
|
-
# 可选配置文件的工具(未找到配置文件时可以使用默认配置)
|
|
299
|
-
OPTIONAL_CONFIG_TOOLS = {
|
|
300
|
-
"eslint",
|
|
301
|
-
"stylelint",
|
|
302
|
-
"yamllint",
|
|
303
|
-
"markdownlint",
|
|
304
|
-
"rubocop",
|
|
305
|
-
"phpstan",
|
|
306
|
-
"sqlfluff",
|
|
307
|
-
"hadolint",
|
|
308
|
-
}
|
|
309
|
-
|
|
190
|
+
project_config_path = os.path.join(project_root, ".jarvis", "lint_tools.yaml")
|
|
191
|
+
if not os.path.exists(project_config_path):
|
|
192
|
+
return {}
|
|
310
193
|
|
|
311
|
-
|
|
312
|
-
|
|
194
|
+
with open(project_config_path, "r") as f:
|
|
195
|
+
config = yaml.safe_load(f) or {}
|
|
196
|
+
result = {}
|
|
197
|
+
for k, v in config.items():
|
|
198
|
+
k_lower = k.lower()
|
|
199
|
+
# 支持格式: ["template1", "template2"] 或 [("tool1", "template1"), ("tool2", "template2")]
|
|
200
|
+
if isinstance(v, list) and v:
|
|
201
|
+
if isinstance(v[0], str):
|
|
202
|
+
# 新格式:直接是命令模板列表
|
|
203
|
+
result[k_lower] = v
|
|
204
|
+
elif isinstance(v[0], (list, tuple)) and len(v[0]) == 2:
|
|
205
|
+
# 旧格式:需要提取模板
|
|
206
|
+
result[k_lower] = [template for _, template in v]
|
|
207
|
+
return result
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# 合并默认配置和全局yaml配置(项目级配置在运行时动态加载)
|
|
211
|
+
LINT_COMMAND_TEMPLATES_BY_FILE.update(load_lint_tools_config())
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _format_lint_command(
|
|
215
|
+
template: str,
|
|
216
|
+
file_path: str,
|
|
217
|
+
project_root: Optional[str] = None,
|
|
313
218
|
) -> Optional[str]:
|
|
314
219
|
"""
|
|
315
|
-
|
|
220
|
+
格式化lint命令模板(内部函数)
|
|
316
221
|
|
|
317
222
|
Args:
|
|
318
|
-
|
|
223
|
+
template: 命令模板字符串
|
|
319
224
|
file_path: 文件路径(相对或绝对路径)
|
|
320
225
|
project_root: 项目根目录(可选,用于处理相对路径)
|
|
321
226
|
|
|
322
227
|
Returns:
|
|
323
|
-
|
|
228
|
+
命令字符串,如果无法生成则返回None
|
|
324
229
|
"""
|
|
325
|
-
template = LINT_COMMAND_TEMPLATES.get(tool_name)
|
|
326
|
-
if not template:
|
|
327
|
-
return None
|
|
328
|
-
|
|
329
230
|
# 特殊处理:某些工具不需要文件路径(如 cargo clippy)
|
|
330
231
|
if "{file_path}" not in template and "{file_name}" not in template:
|
|
331
232
|
# 不需要文件路径,直接返回模板
|
|
@@ -345,32 +246,12 @@ def get_lint_command(
|
|
|
345
246
|
"file_name": os.path.basename(abs_file_path),
|
|
346
247
|
}
|
|
347
248
|
|
|
348
|
-
#
|
|
249
|
+
# 如果模板中有 {config} 占位符,说明需要配置文件
|
|
250
|
+
# 现在配置文件应该直接写在模板中,不支持自动查找
|
|
349
251
|
if "{config}" in template:
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if config_path:
|
|
354
|
-
placeholders["config"] = config_path
|
|
355
|
-
else:
|
|
356
|
-
# 未找到配置文件
|
|
357
|
-
if tool_name in REQUIRED_CONFIG_TOOLS:
|
|
358
|
-
# 必须配置的工具,未找到配置文件则返回None
|
|
359
|
-
return None
|
|
360
|
-
elif tool_name in OPTIONAL_CONFIG_TOOLS:
|
|
361
|
-
# 可选配置的工具,使用默认配置(移除config参数或使用默认值)
|
|
362
|
-
# 这里我们移除 {config} 占位符,让工具使用默认配置
|
|
363
|
-
# 但需要修改模板,所以这里我们返回None,让调用方知道需要处理
|
|
364
|
-
# 实际上,对于可选配置的工具,模板中不应该使用 {config},或者应该提供默认值
|
|
365
|
-
# 为了简化,我们这里返回None,表示无法生成命令
|
|
366
|
-
# 更好的做法是在模板中提供默认值,如: "checkstyle {file_path}" 或 "checkstyle -c default.xml {file_path}"
|
|
367
|
-
return None
|
|
368
|
-
else:
|
|
369
|
-
# 未定义的工具,返回None
|
|
370
|
-
return None
|
|
371
|
-
else:
|
|
372
|
-
# 工具需要配置但未定义配置文件名,返回None
|
|
373
|
-
return None
|
|
252
|
+
# 如果模板中有 {config} 但未提供,返回None
|
|
253
|
+
# 用户应该在模板中直接指定配置文件路径,或使用其他占位符
|
|
254
|
+
return None
|
|
374
255
|
|
|
375
256
|
# 替换占位符
|
|
376
257
|
try:
|
|
@@ -384,261 +265,79 @@ def get_lint_command(
|
|
|
384
265
|
|
|
385
266
|
def get_lint_commands_for_files(
|
|
386
267
|
files: List[str], project_root: Optional[str] = None
|
|
387
|
-
) -> List[Tuple[str, str
|
|
268
|
+
) -> List[Tuple[str, str]]:
|
|
388
269
|
"""
|
|
389
270
|
获取多个文件的lint命令列表
|
|
390
271
|
|
|
391
272
|
Args:
|
|
392
273
|
files: 文件路径列表
|
|
393
|
-
project_root:
|
|
274
|
+
project_root: 项目根目录(可选),如果提供则加载项目级配置
|
|
394
275
|
|
|
395
276
|
Returns:
|
|
396
|
-
[(
|
|
277
|
+
[(file_path, command), ...] 格式的命令列表
|
|
397
278
|
"""
|
|
279
|
+
# 加载项目级配置(如果提供项目根目录)
|
|
280
|
+
# 项目级配置会覆盖全局配置
|
|
281
|
+
config = LINT_COMMAND_TEMPLATES_BY_FILE.copy()
|
|
282
|
+
if project_root:
|
|
283
|
+
project_config = load_project_lint_tools_config(project_root)
|
|
284
|
+
config.update(project_config) # 项目配置覆盖全局配置
|
|
285
|
+
|
|
398
286
|
commands = []
|
|
399
|
-
#
|
|
400
|
-
|
|
287
|
+
# 记录不需要文件路径的工具模板(如 cargo clippy),避免重复执行
|
|
288
|
+
project_level_templates = set()
|
|
401
289
|
|
|
402
290
|
for file_path in files:
|
|
403
|
-
|
|
404
|
-
|
|
291
|
+
# 从文件扩展名/文件名直接获取命令模板列表
|
|
292
|
+
filename = os.path.basename(file_path)
|
|
293
|
+
filename_lower = filename.lower()
|
|
294
|
+
|
|
295
|
+
# 优先尝试完整文件名匹配
|
|
296
|
+
templates = config.get(filename_lower, [])
|
|
297
|
+
|
|
298
|
+
# 如果文件名匹配失败,再尝试扩展名匹配
|
|
299
|
+
if not templates:
|
|
300
|
+
ext = os.path.splitext(filename)[1]
|
|
301
|
+
if ext:
|
|
302
|
+
templates = config.get(ext.lower(), [])
|
|
303
|
+
|
|
304
|
+
for template in templates:
|
|
405
305
|
# 检查是否是项目级别的工具(不需要文件路径)
|
|
406
|
-
template
|
|
407
|
-
if (
|
|
408
|
-
template
|
|
409
|
-
and "{file_path}" not in template
|
|
410
|
-
and "{file_name}" not in template
|
|
411
|
-
):
|
|
306
|
+
if "{file_path}" not in template and "{file_name}" not in template:
|
|
412
307
|
# 项目级别工具,每个项目只执行一次
|
|
413
|
-
if
|
|
414
|
-
|
|
415
|
-
command =
|
|
308
|
+
if template not in project_level_templates:
|
|
309
|
+
project_level_templates.add(template)
|
|
310
|
+
command = _format_lint_command(template, file_path, project_root)
|
|
416
311
|
if command:
|
|
417
312
|
# 使用第一个文件作为标识
|
|
418
|
-
commands.append((
|
|
313
|
+
commands.append((file_path, command))
|
|
419
314
|
else:
|
|
420
315
|
# 文件级别工具,每个文件都执行
|
|
421
|
-
command =
|
|
316
|
+
command = _format_lint_command(template, file_path, project_root)
|
|
422
317
|
if command:
|
|
423
|
-
commands.append((
|
|
318
|
+
commands.append((file_path, command))
|
|
424
319
|
|
|
425
320
|
return commands
|
|
426
321
|
|
|
427
322
|
|
|
428
|
-
def
|
|
429
|
-
commands: List[Tuple[str, str
|
|
323
|
+
def group_commands_by_template(
|
|
324
|
+
commands: List[Tuple[str, str]],
|
|
430
325
|
) -> Dict[str, List[Tuple[str, str]]]:
|
|
431
326
|
"""
|
|
432
|
-
|
|
327
|
+
按命令模板分组命令(通过命令的第一个单词识别)
|
|
433
328
|
|
|
434
329
|
Args:
|
|
435
|
-
commands: [(
|
|
330
|
+
commands: [(file_path, command), ...] 格式的命令列表
|
|
436
331
|
|
|
437
332
|
Returns:
|
|
438
|
-
{
|
|
333
|
+
{template_key: [(file_path, command), ...]} 格式的字典
|
|
334
|
+
template_key 是命令的第一个单词
|
|
439
335
|
"""
|
|
440
|
-
grouped = {}
|
|
441
|
-
for
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
grouped
|
|
336
|
+
grouped: Dict[str, List[Tuple[str, str]]] = {}
|
|
337
|
+
for file_path, command in commands:
|
|
338
|
+
# 使用命令的第一个单词作为分组键
|
|
339
|
+
template_key = command.split()[0] if command.split() else "unknown"
|
|
340
|
+
if template_key not in grouped:
|
|
341
|
+
grouped[template_key] = []
|
|
342
|
+
grouped[template_key].append((file_path, command))
|
|
445
343
|
return grouped
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
# 格式化工具配置(文件扩展名/文件名 -> 格式化工具列表)
|
|
449
|
-
FORMAT_TOOLS = {
|
|
450
|
-
# Python
|
|
451
|
-
".py": ["ruff format"],
|
|
452
|
-
".pyw": ["ruff format"],
|
|
453
|
-
".pyi": ["ruff format"],
|
|
454
|
-
".pyx": ["ruff format"],
|
|
455
|
-
".pxd": ["ruff format"],
|
|
456
|
-
# JavaScript/TypeScript
|
|
457
|
-
".js": ["prettier"],
|
|
458
|
-
".mjs": ["prettier"],
|
|
459
|
-
".cjs": ["prettier"],
|
|
460
|
-
".jsx": ["prettier"],
|
|
461
|
-
".ts": ["prettier"],
|
|
462
|
-
".tsx": ["prettier"],
|
|
463
|
-
".cts": ["prettier"],
|
|
464
|
-
".mts": ["prettier"],
|
|
465
|
-
# Rust
|
|
466
|
-
".rs": ["rustfmt"],
|
|
467
|
-
# Go
|
|
468
|
-
".go": ["gofmt"],
|
|
469
|
-
# Java
|
|
470
|
-
".java": ["google-java-format"],
|
|
471
|
-
# C/C++
|
|
472
|
-
".c": ["clang-format"],
|
|
473
|
-
".cpp": ["clang-format"],
|
|
474
|
-
".cc": ["clang-format"],
|
|
475
|
-
".cxx": ["clang-format"],
|
|
476
|
-
".h": ["clang-format"],
|
|
477
|
-
".hpp": ["clang-format"],
|
|
478
|
-
".hxx": ["clang-format"],
|
|
479
|
-
".inl": ["clang-format"],
|
|
480
|
-
".ipp": ["clang-format"],
|
|
481
|
-
# HTML/CSS
|
|
482
|
-
".html": ["prettier"],
|
|
483
|
-
".htm": ["prettier"],
|
|
484
|
-
".xhtml": ["prettier"],
|
|
485
|
-
".css": ["prettier"],
|
|
486
|
-
".scss": ["prettier"],
|
|
487
|
-
".sass": ["prettier"],
|
|
488
|
-
".less": ["prettier"],
|
|
489
|
-
# JSON/YAML
|
|
490
|
-
".json": ["prettier"],
|
|
491
|
-
".jsonl": ["prettier"],
|
|
492
|
-
".json5": ["prettier"],
|
|
493
|
-
".yaml": ["prettier"],
|
|
494
|
-
".yml": ["prettier"],
|
|
495
|
-
# Markdown
|
|
496
|
-
".md": ["prettier"],
|
|
497
|
-
".markdown": ["prettier"],
|
|
498
|
-
# SQL
|
|
499
|
-
".sql": ["sqlfluff format"],
|
|
500
|
-
# Shell/Bash
|
|
501
|
-
".sh": ["shfmt"],
|
|
502
|
-
".bash": ["shfmt"],
|
|
503
|
-
# XML
|
|
504
|
-
".xml": ["xmllint --format"],
|
|
505
|
-
".xsd": ["xmllint --format"],
|
|
506
|
-
".dtd": ["xmllint --format"],
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
def load_format_tools_config() -> Dict[str, List[str]]:
|
|
511
|
-
"""从yaml文件加载格式化工具配置"""
|
|
512
|
-
config_path = os.path.join(get_data_dir(), "format_tools.yaml")
|
|
513
|
-
if not os.path.exists(config_path):
|
|
514
|
-
return {}
|
|
515
|
-
|
|
516
|
-
with open(config_path, "r") as f:
|
|
517
|
-
config = yaml.safe_load(f) or {}
|
|
518
|
-
return {k.lower(): v for k, v in config.items()} # 确保key是小写
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
# 合并默认配置和yaml配置
|
|
522
|
-
FORMAT_TOOLS.update(load_format_tools_config())
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
def get_format_tools(filename: str) -> List[str]:
|
|
526
|
-
"""
|
|
527
|
-
根据文件扩展名或文件名获取对应的格式化工具列表
|
|
528
|
-
优先级:完整文件名匹配 > 扩展名匹配
|
|
529
|
-
|
|
530
|
-
Args:
|
|
531
|
-
filename: 文件路径或文件名(如'test.py'或'Makefile')
|
|
532
|
-
|
|
533
|
-
Returns:
|
|
534
|
-
对应的格式化工具列表,如果找不到则返回空列表
|
|
535
|
-
"""
|
|
536
|
-
filename = os.path.basename(filename)
|
|
537
|
-
filename_lower = filename.lower()
|
|
538
|
-
|
|
539
|
-
# 优先尝试完整文件名匹配
|
|
540
|
-
format_tools = FORMAT_TOOLS.get(filename_lower, [])
|
|
541
|
-
if format_tools:
|
|
542
|
-
return format_tools
|
|
543
|
-
|
|
544
|
-
# 如果文件名匹配失败,再尝试扩展名匹配
|
|
545
|
-
ext = os.path.splitext(filename)[1]
|
|
546
|
-
if ext:
|
|
547
|
-
return FORMAT_TOOLS.get(ext.lower(), [])
|
|
548
|
-
|
|
549
|
-
return []
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
# 格式化工具命令模板映射
|
|
553
|
-
# 占位符说明:
|
|
554
|
-
# - {file_path}: 单个文件的完整路径
|
|
555
|
-
# - {files}: 多个文件路径,用空格分隔
|
|
556
|
-
# - {file_name}: 文件名(不含路径)
|
|
557
|
-
FORMAT_COMMAND_TEMPLATES: Dict[str, str] = {
|
|
558
|
-
# Python
|
|
559
|
-
"black": "black {file_path}",
|
|
560
|
-
"ruff format": "ruff format {file_path}",
|
|
561
|
-
# JavaScript/TypeScript
|
|
562
|
-
"prettier": "prettier --write {file_path}",
|
|
563
|
-
# Rust
|
|
564
|
-
"rustfmt": "rustfmt {file_path}",
|
|
565
|
-
# Go
|
|
566
|
-
"gofmt": "gofmt -w {file_path}",
|
|
567
|
-
# Java
|
|
568
|
-
"google-java-format": "google-java-format -i {file_path}",
|
|
569
|
-
# C/C++
|
|
570
|
-
"clang-format": "clang-format -i {file_path}",
|
|
571
|
-
# SQL
|
|
572
|
-
"sqlfluff format": "sqlfluff format {file_path}",
|
|
573
|
-
# Shell/Bash
|
|
574
|
-
"shfmt": "shfmt -w {file_path}",
|
|
575
|
-
# XML (xmllint格式化需要特殊处理,使用临时文件)
|
|
576
|
-
"xmllint --format": "xmllint --format {file_path} > {file_path}.tmp && mv {file_path}.tmp {file_path}",
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
def get_format_command(
|
|
581
|
-
tool_name: str, file_path: str, project_root: Optional[str] = None
|
|
582
|
-
) -> Optional[str]:
|
|
583
|
-
"""
|
|
584
|
-
获取格式化工具的具体命令
|
|
585
|
-
|
|
586
|
-
Args:
|
|
587
|
-
tool_name: 格式化工具名称(如 'black', 'prettier')
|
|
588
|
-
file_path: 文件路径(相对或绝对路径)
|
|
589
|
-
project_root: 项目根目录(可选,用于处理相对路径)
|
|
590
|
-
|
|
591
|
-
Returns:
|
|
592
|
-
命令字符串,如果工具不支持则返回None
|
|
593
|
-
"""
|
|
594
|
-
template = FORMAT_COMMAND_TEMPLATES.get(tool_name)
|
|
595
|
-
if not template:
|
|
596
|
-
return None
|
|
597
|
-
|
|
598
|
-
# 如果是绝对路径,直接使用;否则转换为绝对路径
|
|
599
|
-
if os.path.isabs(file_path):
|
|
600
|
-
abs_file_path = file_path
|
|
601
|
-
elif project_root:
|
|
602
|
-
abs_file_path = os.path.join(project_root, file_path)
|
|
603
|
-
else:
|
|
604
|
-
abs_file_path = os.path.abspath(file_path)
|
|
605
|
-
|
|
606
|
-
# 准备占位符替换字典
|
|
607
|
-
placeholders = {
|
|
608
|
-
"file_path": abs_file_path,
|
|
609
|
-
"file_name": os.path.basename(abs_file_path),
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
# 替换占位符
|
|
613
|
-
try:
|
|
614
|
-
command = template.format(**placeholders)
|
|
615
|
-
except KeyError:
|
|
616
|
-
# 如果模板中有未定义的占位符,返回None
|
|
617
|
-
return None
|
|
618
|
-
|
|
619
|
-
return command
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
def get_format_commands_for_files(
|
|
623
|
-
files: List[str], project_root: Optional[str] = None
|
|
624
|
-
) -> List[Tuple[str, str, str]]:
|
|
625
|
-
"""
|
|
626
|
-
获取多个文件的格式化命令列表
|
|
627
|
-
|
|
628
|
-
Args:
|
|
629
|
-
files: 文件路径列表
|
|
630
|
-
project_root: 项目根目录(可选)
|
|
631
|
-
|
|
632
|
-
Returns:
|
|
633
|
-
[(tool_name, file_path, command), ...] 格式的命令列表
|
|
634
|
-
"""
|
|
635
|
-
commands = []
|
|
636
|
-
|
|
637
|
-
for file_path in files:
|
|
638
|
-
tools = get_format_tools(file_path)
|
|
639
|
-
for tool_name in tools:
|
|
640
|
-
command = get_format_command(tool_name, file_path, project_root)
|
|
641
|
-
if command:
|
|
642
|
-
commands.append((tool_name, file_path, command))
|
|
643
|
-
|
|
644
|
-
return commands
|