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
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from dataclasses import field
|
|
5
|
+
from typing import Any
|
|
6
|
+
from typing import Dict
|
|
7
|
+
from typing import List
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from typing import Set
|
|
10
|
+
from typing import Tuple
|
|
5
11
|
|
|
6
12
|
from .dependency_analyzer import DependencyGraph
|
|
7
13
|
from .file_ignore import filter_walk_dirs
|
|
8
|
-
from .
|
|
9
|
-
from .language_support import
|
|
14
|
+
from .language_support import detect_language
|
|
15
|
+
from .language_support import get_dependency_analyzer
|
|
16
|
+
from .language_support import get_symbol_extractor
|
|
17
|
+
from .symbol_extractor import Symbol
|
|
18
|
+
from .symbol_extractor import SymbolTable
|
|
10
19
|
|
|
11
20
|
|
|
12
21
|
@dataclass
|
|
13
22
|
class EditContext:
|
|
14
23
|
"""Provides contextual information for a specific code location."""
|
|
24
|
+
|
|
15
25
|
file_path: str
|
|
16
26
|
line_start: int
|
|
17
27
|
line_end: int
|
|
@@ -25,6 +35,7 @@ class EditContext:
|
|
|
25
35
|
@dataclass
|
|
26
36
|
class Reference:
|
|
27
37
|
"""Represents a reference to a symbol."""
|
|
38
|
+
|
|
28
39
|
symbol: Symbol
|
|
29
40
|
file_path: str
|
|
30
41
|
line: int
|
|
@@ -42,10 +53,12 @@ class ContextManager:
|
|
|
42
53
|
self.dependency_graph = DependencyGraph()
|
|
43
54
|
self._file_cache: dict[str, str] = {} # Cache file contents
|
|
44
55
|
|
|
45
|
-
def get_edit_context(
|
|
56
|
+
def get_edit_context(
|
|
57
|
+
self, file_path: str, line_start: int, line_end: int
|
|
58
|
+
) -> EditContext:
|
|
46
59
|
"""
|
|
47
60
|
Gets contextual information for a given edit location.
|
|
48
|
-
|
|
61
|
+
|
|
49
62
|
Returns:
|
|
50
63
|
EditContext with information about the current scope, used symbols,
|
|
51
64
|
imported symbols, and relevant files.
|
|
@@ -53,25 +66,27 @@ class ContextManager:
|
|
|
53
66
|
# Get file content
|
|
54
67
|
content = self._get_file_content(file_path)
|
|
55
68
|
if not content:
|
|
56
|
-
return EditContext(
|
|
57
|
-
|
|
69
|
+
return EditContext(
|
|
70
|
+
file_path=file_path, line_start=line_start, line_end=line_end
|
|
71
|
+
)
|
|
72
|
+
|
|
58
73
|
# Find current scope (function or class)
|
|
59
74
|
current_scope = self._find_current_scope(file_path, line_start)
|
|
60
|
-
|
|
75
|
+
|
|
61
76
|
# Find symbols used in the edit region
|
|
62
77
|
used_symbols = self._find_used_symbols(file_path, content, line_start, line_end)
|
|
63
|
-
|
|
78
|
+
|
|
64
79
|
# Find imported symbols
|
|
65
80
|
imported_symbols = self._find_imported_symbols(file_path)
|
|
66
|
-
|
|
81
|
+
|
|
67
82
|
# Find relevant files (dependencies and dependents)
|
|
68
83
|
relevant_files = self._find_relevant_files(file_path)
|
|
69
|
-
|
|
84
|
+
|
|
70
85
|
# Build context summary
|
|
71
86
|
context_summary = self._build_context_summary(
|
|
72
87
|
current_scope, used_symbols, imported_symbols, relevant_files
|
|
73
88
|
)
|
|
74
|
-
|
|
89
|
+
|
|
75
90
|
return EditContext(
|
|
76
91
|
file_path=file_path,
|
|
77
92
|
line_start=line_start,
|
|
@@ -83,95 +98,104 @@ class ContextManager:
|
|
|
83
98
|
context_summary=context_summary,
|
|
84
99
|
)
|
|
85
100
|
|
|
86
|
-
def find_references(
|
|
101
|
+
def find_references(
|
|
102
|
+
self, symbol_name: str, file_path: Optional[str] = None
|
|
103
|
+
) -> List[Reference]:
|
|
87
104
|
"""
|
|
88
105
|
Finds all references to a symbol.
|
|
89
|
-
|
|
106
|
+
|
|
90
107
|
Args:
|
|
91
108
|
symbol_name: Name of the symbol to find references for
|
|
92
109
|
file_path: Optional file path to limit search scope
|
|
93
|
-
|
|
110
|
+
|
|
94
111
|
Returns:
|
|
95
112
|
List of Reference objects pointing to where the symbol is used
|
|
96
113
|
"""
|
|
97
114
|
references: List[Reference] = []
|
|
98
|
-
|
|
115
|
+
|
|
99
116
|
# Check if file is stale and update if needed
|
|
100
117
|
if file_path and self.symbol_table.is_file_stale(file_path):
|
|
101
118
|
self._refresh_file_symbols(file_path)
|
|
102
|
-
|
|
119
|
+
|
|
103
120
|
# Find symbol definitions
|
|
104
121
|
symbols = self.symbol_table.find_symbol(symbol_name, file_path)
|
|
105
122
|
if not symbols:
|
|
106
123
|
return references
|
|
107
|
-
|
|
124
|
+
|
|
108
125
|
# Search in files that might reference this symbol
|
|
109
126
|
search_files: Set[str] = set()
|
|
110
|
-
|
|
127
|
+
|
|
111
128
|
# Add files that depend on the symbol's file
|
|
112
129
|
for symbol in symbols:
|
|
113
130
|
search_files.add(symbol.file_path)
|
|
114
131
|
dependents = self.dependency_graph.get_dependents(symbol.file_path)
|
|
115
132
|
search_files.update(dependents)
|
|
116
|
-
|
|
133
|
+
|
|
117
134
|
# If file_path is specified, limit search to that file
|
|
118
135
|
if file_path:
|
|
119
136
|
search_files = {f for f in search_files if f == file_path}
|
|
120
|
-
|
|
137
|
+
|
|
121
138
|
# Search for references in each file
|
|
122
139
|
for file_path_to_search in search_files:
|
|
123
140
|
# Check if file is stale and update if needed
|
|
124
141
|
if self.symbol_table.is_file_stale(file_path_to_search):
|
|
125
142
|
self._refresh_file_symbols(file_path_to_search)
|
|
126
|
-
|
|
143
|
+
|
|
127
144
|
content = self._get_file_content(file_path_to_search)
|
|
128
145
|
if not content:
|
|
129
146
|
continue
|
|
130
|
-
|
|
147
|
+
|
|
131
148
|
# Simple pattern matching for symbol references
|
|
132
149
|
# This is a basic implementation; could be enhanced with AST analysis
|
|
133
|
-
pattern = r
|
|
150
|
+
pattern = r"\b" + re.escape(symbol_name) + r"\b"
|
|
134
151
|
for match in re.finditer(pattern, content):
|
|
135
|
-
line_num = content[:match.start()].count(
|
|
136
|
-
col_num = match.start() - content.rfind(
|
|
137
|
-
|
|
152
|
+
line_num = content[: match.start()].count("\n") + 1
|
|
153
|
+
col_num = match.start() - content.rfind("\n", 0, match.start()) - 1
|
|
154
|
+
|
|
138
155
|
# Check if this is not a definition (basic check)
|
|
139
|
-
line_start = content.rfind(
|
|
140
|
-
line_end = content.find(
|
|
156
|
+
line_start = content.rfind("\n", 0, match.start()) + 1
|
|
157
|
+
line_end = content.find("\n", match.end())
|
|
141
158
|
if line_end == -1:
|
|
142
159
|
line_end = len(content)
|
|
143
160
|
line_content = content[line_start:line_end]
|
|
144
|
-
|
|
161
|
+
|
|
145
162
|
# Skip if it's a definition (contains 'def', 'class', etc.)
|
|
146
|
-
if any(
|
|
163
|
+
if any(
|
|
164
|
+
keyword in line_content
|
|
165
|
+
for keyword in ["def ", "class ", "import ", "from "]
|
|
166
|
+
):
|
|
147
167
|
continue
|
|
148
|
-
|
|
168
|
+
|
|
149
169
|
# Use the first matching symbol definition
|
|
150
170
|
if symbols:
|
|
151
|
-
references.append(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
171
|
+
references.append(
|
|
172
|
+
Reference(
|
|
173
|
+
symbol=symbols[0],
|
|
174
|
+
file_path=file_path_to_search,
|
|
175
|
+
line=line_num,
|
|
176
|
+
column=col_num,
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
158
180
|
return references
|
|
159
181
|
|
|
160
|
-
def find_definition(
|
|
182
|
+
def find_definition(
|
|
183
|
+
self, symbol_name: str, file_path: Optional[str] = None
|
|
184
|
+
) -> Optional[Symbol]:
|
|
161
185
|
"""
|
|
162
186
|
Finds the definition of a symbol.
|
|
163
|
-
|
|
187
|
+
|
|
164
188
|
Args:
|
|
165
189
|
symbol_name: Name of the symbol to find
|
|
166
190
|
file_path: Optional file path to limit search scope
|
|
167
|
-
|
|
191
|
+
|
|
168
192
|
Returns:
|
|
169
193
|
Symbol object if found, None otherwise
|
|
170
194
|
"""
|
|
171
195
|
# Check if file is stale and update if needed
|
|
172
196
|
if file_path and self.symbol_table.is_file_stale(file_path):
|
|
173
197
|
self._refresh_file_symbols(file_path)
|
|
174
|
-
|
|
198
|
+
|
|
175
199
|
symbols = self.symbol_table.find_symbol(symbol_name, file_path)
|
|
176
200
|
if symbols:
|
|
177
201
|
# Return the first definition (could be enhanced to find the most relevant one)
|
|
@@ -200,11 +224,13 @@ class ContextManager:
|
|
|
200
224
|
symbols = extractor.extract_symbols(file_path, content)
|
|
201
225
|
for symbol in symbols:
|
|
202
226
|
self.symbol_table.add_symbol(symbol)
|
|
203
|
-
|
|
227
|
+
|
|
204
228
|
# Update file modification time after extracting symbols
|
|
205
229
|
if os.path.exists(file_path):
|
|
206
230
|
try:
|
|
207
|
-
self.symbol_table._file_mtimes[file_path] = os.path.getmtime(
|
|
231
|
+
self.symbol_table._file_mtimes[file_path] = os.path.getmtime(
|
|
232
|
+
file_path
|
|
233
|
+
)
|
|
208
234
|
except Exception:
|
|
209
235
|
pass
|
|
210
236
|
|
|
@@ -217,21 +243,21 @@ class ContextManager:
|
|
|
217
243
|
dep_path = self._resolve_dependency_path(file_path, dep.from_module)
|
|
218
244
|
if dep_path:
|
|
219
245
|
self.dependency_graph.add_dependency(file_path, dep_path)
|
|
220
|
-
|
|
246
|
+
|
|
221
247
|
# 6. Save updated symbols to cache
|
|
222
248
|
self.symbol_table.save_cache()
|
|
223
|
-
|
|
249
|
+
|
|
224
250
|
def _refresh_file_symbols(self, file_path: str):
|
|
225
251
|
"""Refresh symbols for a file that has been modified externally.
|
|
226
|
-
|
|
252
|
+
|
|
227
253
|
This method is called when a file is detected to be stale (modified
|
|
228
254
|
outside of Jarvis's control).
|
|
229
255
|
"""
|
|
230
256
|
if not os.path.exists(file_path):
|
|
231
257
|
return
|
|
232
|
-
|
|
258
|
+
|
|
233
259
|
try:
|
|
234
|
-
with open(file_path,
|
|
260
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
|
235
261
|
content = f.read()
|
|
236
262
|
self.update_context_for_file(file_path, content)
|
|
237
263
|
except Exception:
|
|
@@ -242,12 +268,12 @@ class ContextManager:
|
|
|
242
268
|
"""Get file content, using cache if available."""
|
|
243
269
|
if file_path in self._file_cache:
|
|
244
270
|
return self._file_cache[file_path]
|
|
245
|
-
|
|
271
|
+
|
|
246
272
|
if not os.path.exists(file_path):
|
|
247
273
|
return None
|
|
248
|
-
|
|
274
|
+
|
|
249
275
|
try:
|
|
250
|
-
with open(file_path,
|
|
276
|
+
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
|
|
251
277
|
content = f.read()
|
|
252
278
|
self._file_cache[file_path] = content
|
|
253
279
|
return content
|
|
@@ -260,110 +286,113 @@ class ContextManager:
|
|
|
260
286
|
if self.symbol_table.is_file_stale(file_path):
|
|
261
287
|
self._refresh_file_symbols(file_path)
|
|
262
288
|
symbols = self.symbol_table.get_file_symbols(file_path)
|
|
263
|
-
|
|
289
|
+
|
|
264
290
|
# Find the innermost scope containing the line
|
|
265
291
|
current_scope = None
|
|
266
292
|
for symbol in symbols:
|
|
267
|
-
if symbol.kind in (
|
|
293
|
+
if symbol.kind in ("function", "class", "method"):
|
|
268
294
|
if symbol.line_start <= line_num <= symbol.line_end:
|
|
269
295
|
# Choose the most nested scope
|
|
270
296
|
if current_scope is None or (
|
|
271
|
-
symbol.line_start >= current_scope.line_start
|
|
272
|
-
symbol.line_end <= current_scope.line_end
|
|
297
|
+
symbol.line_start >= current_scope.line_start
|
|
298
|
+
and symbol.line_end <= current_scope.line_end
|
|
273
299
|
):
|
|
274
300
|
current_scope = symbol
|
|
275
|
-
|
|
301
|
+
|
|
276
302
|
return current_scope
|
|
277
303
|
|
|
278
|
-
def _find_used_symbols(
|
|
304
|
+
def _find_used_symbols(
|
|
305
|
+
self, file_path: str, content: str, line_start: int, line_end: int
|
|
306
|
+
) -> List[Symbol]:
|
|
279
307
|
"""Find symbols used in the specified line range.
|
|
280
|
-
|
|
308
|
+
|
|
281
309
|
改进版本:
|
|
282
310
|
1. 区分定义和调用:检查符号是否在当前行范围内定义
|
|
283
|
-
2.
|
|
311
|
+
2. 获取定义位置:使用 tree-sitter 符号提取器
|
|
284
312
|
3. 为每个使用的符号添加定义位置信息
|
|
285
313
|
"""
|
|
286
314
|
# Check if file is stale and update if needed
|
|
287
315
|
if self.symbol_table.is_file_stale(file_path):
|
|
288
316
|
self._refresh_file_symbols(file_path)
|
|
289
|
-
|
|
317
|
+
|
|
290
318
|
# Extract the code in the range
|
|
291
|
-
lines = content.split(
|
|
292
|
-
region_content =
|
|
293
|
-
|
|
319
|
+
lines = content.split("\n")
|
|
320
|
+
region_content = "\n".join(lines[line_start - 1 : line_end])
|
|
321
|
+
|
|
294
322
|
used_symbols: List[Symbol] = []
|
|
295
323
|
all_symbols = self.symbol_table.get_file_symbols(file_path)
|
|
296
|
-
|
|
297
|
-
# 尝试获取
|
|
298
|
-
lsp_client = None
|
|
299
|
-
try:
|
|
300
|
-
from jarvis.jarvis_tools.lsp_client import LSPClientTool
|
|
301
|
-
lsp_client = LSPClientTool.get_client_for_file(file_path, self.project_root)
|
|
302
|
-
except Exception:
|
|
303
|
-
pass
|
|
304
|
-
|
|
305
|
-
# 尝试获取 tree-sitter 提取器(作为后备)
|
|
324
|
+
|
|
325
|
+
# 尝试获取 tree-sitter 提取器
|
|
306
326
|
treesitter_extractor = None
|
|
307
327
|
try:
|
|
308
328
|
from jarvis.jarvis_code_agent.code_analyzer.language_support import (
|
|
309
329
|
detect_language,
|
|
330
|
+
)
|
|
331
|
+
from jarvis.jarvis_code_agent.code_analyzer.language_support import (
|
|
310
332
|
get_symbol_extractor,
|
|
311
333
|
)
|
|
334
|
+
|
|
312
335
|
language = detect_language(file_path)
|
|
313
336
|
if language:
|
|
314
337
|
treesitter_extractor = get_symbol_extractor(language)
|
|
315
338
|
except Exception:
|
|
316
339
|
pass
|
|
317
|
-
|
|
340
|
+
|
|
318
341
|
# 用于跟踪已处理的符号,避免重复
|
|
319
|
-
processed_symbols
|
|
320
|
-
|
|
342
|
+
processed_symbols: Dict[
|
|
343
|
+
str, Tuple[Symbol, bool, Optional[Symbol]]
|
|
344
|
+
] = {} # {symbol_name: (symbol, is_definition, definition_location)}
|
|
345
|
+
|
|
321
346
|
# Simple pattern matching to find symbol usage
|
|
322
347
|
for symbol in all_symbols:
|
|
323
|
-
if symbol.kind ==
|
|
348
|
+
if symbol.kind == "import":
|
|
324
349
|
continue
|
|
325
|
-
|
|
326
|
-
pattern = r
|
|
350
|
+
|
|
351
|
+
pattern = r"\b" + re.escape(symbol.name) + r"\b"
|
|
327
352
|
matches = list(re.finditer(pattern, region_content))
|
|
328
353
|
if not matches:
|
|
329
354
|
continue
|
|
330
|
-
|
|
355
|
+
|
|
331
356
|
# 检查符号是否在当前行范围内定义
|
|
332
357
|
is_definition_in_range = (
|
|
333
|
-
symbol.file_path == file_path
|
|
334
|
-
symbol.line_start >= line_start
|
|
335
|
-
symbol.line_start <= line_end
|
|
358
|
+
symbol.file_path == file_path
|
|
359
|
+
and symbol.line_start >= line_start
|
|
360
|
+
and symbol.line_start <= line_end
|
|
336
361
|
)
|
|
337
|
-
|
|
362
|
+
|
|
338
363
|
# 检查是否有调用(不在定义行的使用)
|
|
339
364
|
has_calls = False
|
|
340
365
|
call_line = None
|
|
341
366
|
call_column = None
|
|
342
367
|
for match in matches:
|
|
343
368
|
match_start = match.start()
|
|
344
|
-
match_line_in_region = region_content[:match_start].count(
|
|
369
|
+
match_line_in_region = region_content[:match_start].count("\n") + 1
|
|
345
370
|
match_line = line_start + match_line_in_region - 1
|
|
346
|
-
|
|
371
|
+
|
|
347
372
|
# 如果使用位置不在定义行,则认为是调用
|
|
348
373
|
if not is_definition_in_range or match_line != symbol.line_start:
|
|
349
374
|
has_calls = True
|
|
350
375
|
call_line = match_line
|
|
351
376
|
# 计算列号
|
|
352
|
-
line_start_pos = region_content[:match_start].rfind(
|
|
377
|
+
line_start_pos = region_content[:match_start].rfind("\n") + 1
|
|
353
378
|
call_column = match_start - line_start_pos
|
|
354
379
|
break
|
|
355
|
-
|
|
380
|
+
|
|
356
381
|
# 处理定义
|
|
357
382
|
if is_definition_in_range:
|
|
358
383
|
symbol.is_definition = True
|
|
359
384
|
if symbol.name not in processed_symbols:
|
|
360
385
|
processed_symbols[symbol.name] = (symbol, True, None)
|
|
361
|
-
|
|
386
|
+
|
|
362
387
|
# 处理调用
|
|
363
388
|
if has_calls or not is_definition_in_range:
|
|
364
389
|
# 创建或更新引用符号
|
|
365
390
|
if symbol.name in processed_symbols:
|
|
366
|
-
|
|
391
|
+
(
|
|
392
|
+
existing_symbol,
|
|
393
|
+
existing_is_def,
|
|
394
|
+
existing_def_loc,
|
|
395
|
+
) = processed_symbols[symbol.name]
|
|
367
396
|
# 如果已有定义,跳过;否则更新定义位置
|
|
368
397
|
if not existing_is_def and not existing_def_loc:
|
|
369
398
|
# 尝试获取定义位置
|
|
@@ -372,16 +401,21 @@ class ContextManager:
|
|
|
372
401
|
file_path,
|
|
373
402
|
call_line or line_start,
|
|
374
403
|
call_column or 0,
|
|
375
|
-
lsp_client,
|
|
376
404
|
treesitter_extractor,
|
|
377
405
|
content,
|
|
378
406
|
)
|
|
379
|
-
|
|
407
|
+
|
|
380
408
|
if definition_location:
|
|
381
|
-
processed_symbols[symbol.name] = (
|
|
409
|
+
processed_symbols[symbol.name] = (
|
|
410
|
+
existing_symbol,
|
|
411
|
+
False,
|
|
412
|
+
definition_location,
|
|
413
|
+
)
|
|
382
414
|
else:
|
|
383
415
|
# 从符号表中查找
|
|
384
|
-
definition_symbols = self.symbol_table.find_symbol(
|
|
416
|
+
definition_symbols = self.symbol_table.find_symbol(
|
|
417
|
+
symbol.name
|
|
418
|
+
)
|
|
385
419
|
if definition_symbols:
|
|
386
420
|
def_symbol = definition_symbols[0]
|
|
387
421
|
definition_location = Symbol(
|
|
@@ -392,7 +426,11 @@ class ContextManager:
|
|
|
392
426
|
line_end=def_symbol.line_end,
|
|
393
427
|
signature=def_symbol.signature,
|
|
394
428
|
)
|
|
395
|
-
processed_symbols[symbol.name] = (
|
|
429
|
+
processed_symbols[symbol.name] = (
|
|
430
|
+
existing_symbol,
|
|
431
|
+
False,
|
|
432
|
+
definition_location,
|
|
433
|
+
)
|
|
396
434
|
else:
|
|
397
435
|
# 创建新的引用符号
|
|
398
436
|
reference_symbol = Symbol(
|
|
@@ -406,18 +444,17 @@ class ContextManager:
|
|
|
406
444
|
parent=symbol.parent,
|
|
407
445
|
is_definition=False,
|
|
408
446
|
)
|
|
409
|
-
|
|
447
|
+
|
|
410
448
|
# 尝试获取定义位置
|
|
411
449
|
definition_location = self._find_definition_location(
|
|
412
450
|
symbol.name,
|
|
413
451
|
file_path,
|
|
414
452
|
call_line or line_start,
|
|
415
453
|
call_column or 0,
|
|
416
|
-
lsp_client,
|
|
417
454
|
treesitter_extractor,
|
|
418
455
|
content,
|
|
419
456
|
)
|
|
420
|
-
|
|
457
|
+
|
|
421
458
|
if definition_location:
|
|
422
459
|
reference_symbol.definition_location = definition_location
|
|
423
460
|
else:
|
|
@@ -433,9 +470,13 @@ class ContextManager:
|
|
|
433
470
|
line_end=def_symbol.line_end,
|
|
434
471
|
signature=def_symbol.signature,
|
|
435
472
|
)
|
|
436
|
-
|
|
437
|
-
processed_symbols[symbol.name] = (
|
|
438
|
-
|
|
473
|
+
|
|
474
|
+
processed_symbols[symbol.name] = (
|
|
475
|
+
reference_symbol,
|
|
476
|
+
False,
|
|
477
|
+
reference_symbol.definition_location,
|
|
478
|
+
)
|
|
479
|
+
|
|
439
480
|
# 将处理后的符号添加到结果列表
|
|
440
481
|
for symbol, is_def, def_loc in processed_symbols.values():
|
|
441
482
|
if is_def:
|
|
@@ -443,75 +484,34 @@ class ContextManager:
|
|
|
443
484
|
if def_loc:
|
|
444
485
|
symbol.definition_location = def_loc
|
|
445
486
|
used_symbols.append(symbol)
|
|
446
|
-
|
|
487
|
+
|
|
447
488
|
return used_symbols
|
|
448
|
-
|
|
489
|
+
|
|
449
490
|
def _find_definition_location(
|
|
450
491
|
self,
|
|
451
492
|
symbol_name: str,
|
|
452
493
|
file_path: str,
|
|
453
494
|
line: int,
|
|
454
495
|
column: int,
|
|
455
|
-
lsp_client: Optional[Any],
|
|
456
496
|
treesitter_extractor: Optional[Any],
|
|
457
497
|
content: str,
|
|
458
498
|
) -> Optional[Symbol]:
|
|
459
499
|
"""查找符号的定义位置。
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
500
|
+
|
|
501
|
+
使用 tree-sitter 符号提取器。
|
|
502
|
+
|
|
463
503
|
Args:
|
|
464
504
|
symbol_name: 符号名称
|
|
465
505
|
file_path: 文件路径
|
|
466
506
|
line: 行号(1-based)
|
|
467
507
|
column: 列号(0-based)
|
|
468
|
-
lsp_client: LSP 客户端(可选)
|
|
469
508
|
treesitter_extractor: tree-sitter 提取器(可选)
|
|
470
509
|
content: 文件内容
|
|
471
|
-
|
|
510
|
+
|
|
472
511
|
Returns:
|
|
473
512
|
定义位置的 Symbol 对象,如果找不到则返回 None
|
|
474
513
|
"""
|
|
475
|
-
#
|
|
476
|
-
if lsp_client:
|
|
477
|
-
try:
|
|
478
|
-
# LSP 使用 0-based 行号
|
|
479
|
-
definition = lsp_client.get_definition(file_path, line - 1, column)
|
|
480
|
-
if definition:
|
|
481
|
-
# 处理 LSP 返回的定义(可能是单个对象或列表)
|
|
482
|
-
if isinstance(definition, list):
|
|
483
|
-
if definition:
|
|
484
|
-
definition = definition[0]
|
|
485
|
-
else:
|
|
486
|
-
return None
|
|
487
|
-
|
|
488
|
-
# 解析 LSP 返回的定义位置
|
|
489
|
-
uri = definition.get("uri", "")
|
|
490
|
-
if uri.startswith("file://"):
|
|
491
|
-
def_file_path = uri[7:] # 移除 "file://" 前缀
|
|
492
|
-
else:
|
|
493
|
-
def_file_path = uri
|
|
494
|
-
|
|
495
|
-
range_info = definition.get("range", {})
|
|
496
|
-
start = range_info.get("start", {})
|
|
497
|
-
end = range_info.get("end", {})
|
|
498
|
-
|
|
499
|
-
# LSP 使用 0-based 行号,转换为 1-based
|
|
500
|
-
def_line_start = start.get("line", 0) + 1
|
|
501
|
-
def_line_end = end.get("line", 0) + 1
|
|
502
|
-
|
|
503
|
-
return Symbol(
|
|
504
|
-
name=symbol_name,
|
|
505
|
-
kind="", # LSP 可能不提供类型信息
|
|
506
|
-
file_path=def_file_path,
|
|
507
|
-
line_start=def_line_start,
|
|
508
|
-
line_end=def_line_end,
|
|
509
|
-
)
|
|
510
|
-
except Exception:
|
|
511
|
-
# LSP 失败,继续尝试 tree-sitter
|
|
512
|
-
pass
|
|
513
|
-
|
|
514
|
-
# 后备:使用 tree-sitter
|
|
514
|
+
# 使用 tree-sitter
|
|
515
515
|
if treesitter_extractor:
|
|
516
516
|
try:
|
|
517
517
|
# 从符号表中查找定义(tree-sitter 已经提取了符号)
|
|
@@ -529,7 +529,7 @@ class ContextManager:
|
|
|
529
529
|
)
|
|
530
530
|
except Exception:
|
|
531
531
|
pass
|
|
532
|
-
|
|
532
|
+
|
|
533
533
|
return None
|
|
534
534
|
|
|
535
535
|
def _find_imported_symbols(self, file_path: str) -> List[Symbol]:
|
|
@@ -537,78 +537,80 @@ class ContextManager:
|
|
|
537
537
|
# Check if file is stale and update if needed
|
|
538
538
|
if self.symbol_table.is_file_stale(file_path):
|
|
539
539
|
self._refresh_file_symbols(file_path)
|
|
540
|
-
|
|
540
|
+
|
|
541
541
|
symbols = self.symbol_table.get_file_symbols(file_path)
|
|
542
|
-
return [s for s in symbols if s.kind ==
|
|
542
|
+
return [s for s in symbols if s.kind == "import"]
|
|
543
543
|
|
|
544
544
|
def _find_relevant_files(self, file_path: str) -> List[str]:
|
|
545
545
|
"""Find relevant files (dependencies and dependents)."""
|
|
546
546
|
relevant = set()
|
|
547
|
-
|
|
547
|
+
|
|
548
548
|
# Add dependencies
|
|
549
549
|
deps = self.dependency_graph.get_dependencies(file_path)
|
|
550
550
|
relevant.update(deps)
|
|
551
|
-
|
|
551
|
+
|
|
552
552
|
# Add dependents
|
|
553
553
|
dependents = self.dependency_graph.get_dependents(file_path)
|
|
554
554
|
relevant.update(dependents)
|
|
555
|
-
|
|
555
|
+
|
|
556
556
|
return list(relevant)
|
|
557
557
|
|
|
558
|
-
def _resolve_dependency_path(
|
|
558
|
+
def _resolve_dependency_path(
|
|
559
|
+
self, file_path: str, module_name: str
|
|
560
|
+
) -> Optional[str]:
|
|
559
561
|
"""Resolve a module name to a file path."""
|
|
560
562
|
# Handle relative imports
|
|
561
|
-
if module_name.startswith(
|
|
563
|
+
if module_name.startswith("."):
|
|
562
564
|
# Relative import
|
|
563
565
|
base_dir = os.path.dirname(file_path)
|
|
564
|
-
parts = module_name.split(
|
|
565
|
-
depth = len([p for p in parts if p ==
|
|
566
|
+
parts = module_name.split(".")
|
|
567
|
+
depth = len([p for p in parts if p == ""])
|
|
566
568
|
module_parts = [p for p in parts if p]
|
|
567
|
-
|
|
569
|
+
|
|
568
570
|
# Navigate up directories
|
|
569
571
|
current_dir = base_dir
|
|
570
572
|
for _ in range(depth - 1):
|
|
571
573
|
current_dir = os.path.dirname(current_dir)
|
|
572
|
-
|
|
574
|
+
|
|
573
575
|
# Try to find the module file
|
|
574
576
|
if module_parts:
|
|
575
577
|
module_path = os.path.join(current_dir, *module_parts)
|
|
576
578
|
else:
|
|
577
579
|
module_path = current_dir
|
|
578
|
-
|
|
580
|
+
|
|
579
581
|
# Try common extensions
|
|
580
|
-
for ext in [
|
|
582
|
+
for ext in [".py", ".rs", ".go", ".js", ".ts"]:
|
|
581
583
|
full_path = module_path + ext
|
|
582
584
|
if os.path.exists(full_path):
|
|
583
585
|
return full_path
|
|
584
|
-
|
|
586
|
+
|
|
585
587
|
# Try __init__.py for Python packages
|
|
586
|
-
if ext ==
|
|
587
|
-
init_path = os.path.join(module_path,
|
|
588
|
+
if ext == ".py":
|
|
589
|
+
init_path = os.path.join(module_path, "__init__.py")
|
|
588
590
|
if os.path.exists(init_path):
|
|
589
591
|
return init_path
|
|
590
592
|
else:
|
|
591
593
|
# Absolute import - search in project
|
|
592
|
-
parts = module_name.split(
|
|
594
|
+
parts = module_name.split(".")
|
|
593
595
|
for root, dirs, files in os.walk(self.project_root):
|
|
594
596
|
# Skip hidden directories and common ignore patterns
|
|
595
597
|
dirs[:] = filter_walk_dirs(dirs)
|
|
596
|
-
|
|
598
|
+
|
|
597
599
|
if parts[0] in dirs or f"{parts[0]}.py" in files:
|
|
598
600
|
module_path = os.path.join(root, *parts)
|
|
599
|
-
|
|
601
|
+
|
|
600
602
|
# Try common extensions
|
|
601
|
-
for ext in [
|
|
603
|
+
for ext in [".py", ".rs", ".go", ".js", ".ts"]:
|
|
602
604
|
full_path = module_path + ext
|
|
603
605
|
if os.path.exists(full_path):
|
|
604
606
|
return full_path
|
|
605
|
-
|
|
607
|
+
|
|
606
608
|
# Try __init__.py for Python packages
|
|
607
|
-
if ext ==
|
|
608
|
-
init_path = os.path.join(module_path,
|
|
609
|
+
if ext == ".py":
|
|
610
|
+
init_path = os.path.join(module_path, "__init__.py")
|
|
609
611
|
if os.path.exists(init_path):
|
|
610
612
|
return init_path
|
|
611
|
-
|
|
613
|
+
|
|
612
614
|
return None
|
|
613
615
|
|
|
614
616
|
def _build_context_summary(
|
|
@@ -620,29 +622,29 @@ class ContextManager:
|
|
|
620
622
|
) -> str:
|
|
621
623
|
"""Build a human-readable context summary."""
|
|
622
624
|
parts = []
|
|
623
|
-
|
|
625
|
+
|
|
624
626
|
if current_scope:
|
|
625
627
|
parts.append(f"Current scope: {current_scope.kind} {current_scope.name}")
|
|
626
628
|
if current_scope.signature:
|
|
627
629
|
parts.append(f" Signature: {current_scope.signature}")
|
|
628
|
-
|
|
630
|
+
|
|
629
631
|
if used_symbols:
|
|
630
632
|
symbol_names = [s.name for s in used_symbols[:5]]
|
|
631
633
|
parts.append(f"Used symbols: {', '.join(symbol_names)}")
|
|
632
634
|
if len(used_symbols) > 5:
|
|
633
635
|
parts.append(f" ... and {len(used_symbols) - 5} more")
|
|
634
|
-
|
|
636
|
+
|
|
635
637
|
if imported_symbols:
|
|
636
638
|
import_names = [s.name for s in imported_symbols[:5]]
|
|
637
639
|
parts.append(f"Imported symbols: {', '.join(import_names)}")
|
|
638
640
|
if len(imported_symbols) > 5:
|
|
639
641
|
parts.append(f" ... and {len(imported_symbols) - 5} more")
|
|
640
|
-
|
|
642
|
+
|
|
641
643
|
if relevant_files:
|
|
642
644
|
parts.append(f"Relevant files: {len(relevant_files)} files")
|
|
643
645
|
for f in relevant_files[:3]:
|
|
644
646
|
parts.append(f" - {os.path.relpath(f, self.project_root)}")
|
|
645
647
|
if len(relevant_files) > 3:
|
|
646
648
|
parts.append(f" ... and {len(relevant_files) - 3} more")
|
|
647
|
-
|
|
648
|
-
return
|
|
649
|
+
|
|
650
|
+
return "\n".join(parts) if parts else "No context available"
|