codegraphcontext 0.1.30__tar.gz → 0.1.32__tar.gz
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.
- {codegraphcontext-0.1.30/src/codegraphcontext.egg-info → codegraphcontext-0.1.32}/PKG-INFO +2 -2
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/README.md +1 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/pyproject.toml +1 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/cli/cli_helpers.py +5 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/cli/main.py +210 -100
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/core/__init__.py +3 -3
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/server.py +2 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/code_finder.py +103 -29
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/graph_builder.py +4 -4
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/c.py +1 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/cpp.py +7 -7
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/go.py +1 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/javascript.py +1 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/python.py +3 -3
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/ruby.py +3 -3
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/rust.py +3 -3
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/typescript.py +3 -3
- codegraphcontext-0.1.32/src/codegraphcontext/tools/languages/typescriptjsx.py +138 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32/src/codegraphcontext.egg-info}/PKG-INFO +2 -2
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext.egg-info/SOURCES.txt +1 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_end_to_end.py +21 -1
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/LICENSE +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/MANIFEST.in +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/setup.cfg +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/__init__.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/__main__.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/cli/__init__.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/cli/config_manager.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/cli/setup_macos.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/cli/setup_wizard.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/core/database.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/core/database_falkordb.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/core/falkor_worker.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/core/jobs.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/core/watcher.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/prompts.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/__init__.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/advanced_language_query_tool.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/csharp.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/java.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/kotlin.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/php.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/scala.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/languages/swift.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/package_resolver.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/c_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/go_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/java_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/python_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/rust_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/scala_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/swift_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/tools/system.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/utils/debug_log.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext/utils/tree_sitter_manager.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext.egg-info/dependency_links.txt +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext.egg-info/entry_points.txt +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext.egg-info/requires.txt +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/src/codegraphcontext.egg-info/top_level.txt +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_cpp_parser.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_database_validation.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_graph_indexing.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_graph_indexing_js.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_kotlin_parser.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_swift_parser.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_tree_sitter_manager.py +0 -0
- {codegraphcontext-0.1.30 → codegraphcontext-0.1.32}/tests/test_typescript_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codegraphcontext
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.32
|
|
4
4
|
Summary: An MCP server that indexes local code into a graph database to provide context to AI assistants.
|
|
5
5
|
Author-email: Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -91,7 +91,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
91
91
|

|
|
92
92
|
|
|
93
93
|
## Project Details
|
|
94
|
-
- **Version:** 0.1.
|
|
94
|
+
- **Version:** 0.1.32
|
|
95
95
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
96
96
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
97
97
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -29,7 +29,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra
|
|
|
29
29
|

|
|
30
30
|
|
|
31
31
|
## Project Details
|
|
32
|
-
- **Version:** 0.1.
|
|
32
|
+
- **Version:** 0.1.32
|
|
33
33
|
- **Authors:** Shashank Shekhar Singh <shashankshekharsingh1205@gmail.com>
|
|
34
34
|
- **License:** MIT License (See [LICENSE](LICENSE) for details)
|
|
35
35
|
- **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codegraphcontext"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.32"
|
|
4
4
|
description = "An MCP server that indexes local code into a graph database to provide context to AI assistants."
|
|
5
5
|
authors = [{ name = "Shashank Shekhar Singh", email = "shashankshekharsingh1205@gmail.com" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -423,9 +423,13 @@ def clean_helper():
|
|
|
423
423
|
try:
|
|
424
424
|
with db_manager.get_driver().session() as session:
|
|
425
425
|
# Find and delete orphaned nodes (nodes not connected to any repository)
|
|
426
|
+
# Using OPTIONAL MATCH for FalkorDB compatibility
|
|
426
427
|
query = """
|
|
427
428
|
MATCH (n)
|
|
428
|
-
WHERE NOT (n:Repository)
|
|
429
|
+
WHERE NOT (n:Repository)
|
|
430
|
+
OPTIONAL MATCH path = (n)-[*]-(r:Repository)
|
|
431
|
+
WITH n, path
|
|
432
|
+
WHERE path IS NULL
|
|
429
433
|
WITH n LIMIT 1000
|
|
430
434
|
DETACH DELETE n
|
|
431
435
|
RETURN count(n) as deleted
|
|
@@ -188,46 +188,83 @@ def neo4j_setup_alias():
|
|
|
188
188
|
|
|
189
189
|
def _load_credentials():
|
|
190
190
|
"""
|
|
191
|
-
Loads
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
Loads configuration and credentials from various sources into environment variables.
|
|
192
|
+
Uses per-variable precedence - each variable is loaded from the highest priority source.
|
|
193
|
+
Priority order (highest to lowest):
|
|
194
|
+
1. Local `mcp.json` env vars (highest - explicit MCP server config)
|
|
195
|
+
2. Local `.env` in project directory (high - project-specific overrides)
|
|
196
|
+
3. Global `~/.codegraphcontext/.env` (lowest - user defaults)
|
|
196
197
|
"""
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
mcp_config = json.load(f)
|
|
203
|
-
server_env = mcp_config.get("mcpServers", {}).get("CodeGraphContext", {}).get("env", {})
|
|
204
|
-
for key, value in server_env.items():
|
|
205
|
-
os.environ[key] = value
|
|
206
|
-
console.print("[green]Loaded Neo4j credentials from local mcp.json.[/green]")
|
|
207
|
-
return
|
|
208
|
-
except Exception as e:
|
|
209
|
-
console.print(f"[bold red]Error loading mcp.json:[/bold red] {e}")
|
|
198
|
+
from dotenv import dotenv_values
|
|
199
|
+
|
|
200
|
+
# Collect all config sources in reverse priority order (lowest to highest)
|
|
201
|
+
config_sources = []
|
|
202
|
+
config_source_names = []
|
|
210
203
|
|
|
211
|
-
#
|
|
204
|
+
# 3. Global .env file (lowest priority - user defaults)
|
|
212
205
|
global_env_path = Path.home() / ".codegraphcontext" / ".env"
|
|
213
206
|
if global_env_path.exists():
|
|
214
207
|
try:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return
|
|
208
|
+
config_sources.append(dotenv_values(str(global_env_path)))
|
|
209
|
+
config_source_names.append(str(global_env_path))
|
|
218
210
|
except Exception as e:
|
|
219
|
-
console.print(f"[
|
|
220
|
-
|
|
221
|
-
#
|
|
211
|
+
console.print(f"[yellow]Warning: Could not load global .env: {e}[/yellow]")
|
|
212
|
+
|
|
213
|
+
# 2. Local project .env (higher priority - project-specific overrides)
|
|
222
214
|
try:
|
|
223
215
|
dotenv_path = find_dotenv(usecwd=True, raise_error_if_not_found=False)
|
|
224
216
|
if dotenv_path:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
else:
|
|
228
|
-
console.print("[yellow]No local mcp.json or .env file found. Credentials may not be set.[/yellow]")
|
|
217
|
+
config_sources.append(dotenv_values(dotenv_path))
|
|
218
|
+
config_source_names.append(str(dotenv_path))
|
|
229
219
|
except Exception as e:
|
|
230
|
-
console.print(f"[
|
|
220
|
+
console.print(f"[yellow]Warning: Could not load .env from current directory: {e}[/yellow]")
|
|
221
|
+
|
|
222
|
+
# 1. Local mcp.json (highest priority - explicit MCP server config)
|
|
223
|
+
mcp_file_path = Path.cwd() / "mcp.json"
|
|
224
|
+
if mcp_file_path.exists():
|
|
225
|
+
try:
|
|
226
|
+
with open(mcp_file_path, "r") as f:
|
|
227
|
+
mcp_config = json.load(f)
|
|
228
|
+
server_env = mcp_config.get("mcpServers", {}).get("CodeGraphContext", {}).get("env", {})
|
|
229
|
+
if server_env:
|
|
230
|
+
config_sources.append(server_env)
|
|
231
|
+
config_source_names.append("mcp.json")
|
|
232
|
+
except Exception as e:
|
|
233
|
+
console.print(f"[yellow]Warning: Could not load mcp.json: {e}[/yellow]")
|
|
234
|
+
|
|
235
|
+
# Merge all configs with proper precedence (later sources override earlier ones)
|
|
236
|
+
merged_config = {}
|
|
237
|
+
for config in config_sources:
|
|
238
|
+
merged_config.update(config)
|
|
239
|
+
|
|
240
|
+
# Apply merged config to environment
|
|
241
|
+
for key, value in merged_config.items():
|
|
242
|
+
if value is not None: # Only set non-None values
|
|
243
|
+
os.environ[key] = str(value)
|
|
244
|
+
|
|
245
|
+
# Report what was loaded
|
|
246
|
+
if config_source_names:
|
|
247
|
+
if len(config_source_names) == 1:
|
|
248
|
+
console.print(f"[dim]Loaded configuration from: {config_source_names[-1]}[/dim]")
|
|
249
|
+
else:
|
|
250
|
+
console.print(f"[dim]Loaded configuration from: {', '.join(config_source_names)} (highest priority: {config_source_names[-1]})[/dim]")
|
|
251
|
+
else:
|
|
252
|
+
console.print("[yellow]No configuration file found. Using defaults.[/yellow]")
|
|
253
|
+
|
|
254
|
+
# Show which database is actually being used
|
|
255
|
+
default_db = os.environ.get("DEFAULT_DATABASE", "falkordb").lower()
|
|
256
|
+
if default_db == "neo4j":
|
|
257
|
+
has_neo4j_creds = all([
|
|
258
|
+
os.environ.get("NEO4J_URI"),
|
|
259
|
+
os.environ.get("NEO4J_USERNAME"),
|
|
260
|
+
os.environ.get("NEO4J_PASSWORD")
|
|
261
|
+
])
|
|
262
|
+
if has_neo4j_creds:
|
|
263
|
+
console.print("[cyan]Using database: Neo4j[/cyan]")
|
|
264
|
+
else:
|
|
265
|
+
console.print("[yellow]⚠ DEFAULT_DATABASE=neo4j but credentials not found. Falling back to FalkorDB.[/yellow]")
|
|
266
|
+
else:
|
|
267
|
+
console.print("[cyan]Using database: FalkorDB[/cyan]")
|
|
231
268
|
|
|
232
269
|
|
|
233
270
|
# ============================================================================
|
|
@@ -704,19 +741,26 @@ def find_by_name(
|
|
|
704
741
|
results = []
|
|
705
742
|
|
|
706
743
|
# Search based on type filter
|
|
707
|
-
if
|
|
708
|
-
# Search all
|
|
744
|
+
if type is None or type.lower() == 'all':
|
|
709
745
|
funcs = code_finder.find_by_function_name(name, fuzzy_search=False)
|
|
710
746
|
classes = code_finder.find_by_class_name(name, fuzzy_search=False)
|
|
711
747
|
variables = code_finder.find_by_variable_name(name)
|
|
712
|
-
|
|
748
|
+
modules = code_finder.find_by_module_name(name)
|
|
749
|
+
imports = code_finder.find_imports(name)
|
|
750
|
+
|
|
713
751
|
for f in funcs: f['type'] = 'Function'
|
|
714
752
|
for c in classes: c['type'] = 'Class'
|
|
715
753
|
for v in variables: v['type'] = 'Variable'
|
|
754
|
+
for m in modules: m['type'] = 'Module'; m['file_path'] = m.get('name', 'External') # Modules might differ
|
|
755
|
+
for i in imports:
|
|
756
|
+
i['type'] = 'Import'
|
|
757
|
+
i['name'] = i.get('alias') or i.get('imported_name')
|
|
716
758
|
|
|
717
759
|
results.extend(funcs)
|
|
718
760
|
results.extend(classes)
|
|
719
761
|
results.extend(variables)
|
|
762
|
+
results.extend(modules)
|
|
763
|
+
results.extend(imports)
|
|
720
764
|
|
|
721
765
|
elif type.lower() == 'function':
|
|
722
766
|
results = code_finder.find_by_function_name(name, fuzzy_search=False)
|
|
@@ -729,6 +773,12 @@ def find_by_name(
|
|
|
729
773
|
elif type.lower() == 'variable':
|
|
730
774
|
results = code_finder.find_by_variable_name(name)
|
|
731
775
|
for r in results: r['type'] = 'Variable'
|
|
776
|
+
|
|
777
|
+
elif type.lower() == 'module':
|
|
778
|
+
results = code_finder.find_by_module_name(name)
|
|
779
|
+
for r in results:
|
|
780
|
+
r['type'] = 'Module'
|
|
781
|
+
r['file_path'] = r.get('name')
|
|
732
782
|
|
|
733
783
|
elif type.lower() == 'file':
|
|
734
784
|
# Quick query for file
|
|
@@ -744,16 +794,17 @@ def find_by_name(
|
|
|
744
794
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
745
795
|
table.add_column("Name", style="cyan")
|
|
746
796
|
table.add_column("Type", style="bold blue")
|
|
747
|
-
table.add_column("
|
|
748
|
-
table.add_column("Line", style="green", justify="right")
|
|
797
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
749
798
|
|
|
750
799
|
for res in results:
|
|
751
800
|
file_path = res.get('file_path', '') or ''
|
|
801
|
+
line_str = str(res.get('line_number', ''))
|
|
802
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
803
|
+
|
|
752
804
|
table.add_row(
|
|
753
805
|
res.get('name', ''),
|
|
754
806
|
res.get('type', 'Unknown'),
|
|
755
|
-
|
|
756
|
-
str(res.get('line_number', ''))
|
|
807
|
+
location_str
|
|
757
808
|
)
|
|
758
809
|
|
|
759
810
|
console.print(f"[cyan]Found {len(results)} matches for '{name}':[/cyan]")
|
|
@@ -825,17 +876,18 @@ def find_by_pattern(
|
|
|
825
876
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
826
877
|
table.add_column("Name", style="cyan")
|
|
827
878
|
table.add_column("Type", style="blue")
|
|
828
|
-
table.add_column("
|
|
829
|
-
table.add_column("
|
|
830
|
-
table.add_column("Location", style="yellow")
|
|
879
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
880
|
+
table.add_column("Source", style="yellow")
|
|
831
881
|
|
|
832
882
|
for res in results:
|
|
833
883
|
file_path = res.get('file_path', '') or ''
|
|
884
|
+
line_str = str(res.get('line_number', '') if res.get('line_number') is not None else '')
|
|
885
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
886
|
+
|
|
834
887
|
table.add_row(
|
|
835
888
|
res.get('name', ''),
|
|
836
889
|
res.get('type', 'Unknown'),
|
|
837
|
-
|
|
838
|
-
str(res.get('line_number', '') if res.get('line_number') is not None else ''),
|
|
890
|
+
location_str,
|
|
839
891
|
"📦 Dependency" if res.get('is_dependency') else "📝 Project"
|
|
840
892
|
)
|
|
841
893
|
|
|
@@ -871,16 +923,17 @@ def find_by_type(
|
|
|
871
923
|
|
|
872
924
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
873
925
|
table.add_column("Name", style="cyan")
|
|
874
|
-
table.add_column("
|
|
875
|
-
table.add_column("
|
|
876
|
-
table.add_column("Location", style="yellow")
|
|
926
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
927
|
+
table.add_column("Source", style="yellow")
|
|
877
928
|
|
|
878
929
|
for res in results:
|
|
879
930
|
file_path = res.get('file_path', '') or ''
|
|
931
|
+
line_str = str(res.get('line_number', ''))
|
|
932
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
933
|
+
|
|
880
934
|
table.add_row(
|
|
881
935
|
res.get('name', ''),
|
|
882
|
-
|
|
883
|
-
str(res.get('line_number', '')),
|
|
936
|
+
location_str,
|
|
884
937
|
"📦 Dependency" if res.get('is_dependency') else "📝 Project"
|
|
885
938
|
)
|
|
886
939
|
|
|
@@ -915,15 +968,17 @@ def find_by_variable(
|
|
|
915
968
|
|
|
916
969
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
917
970
|
table.add_column("Name", style="cyan")
|
|
918
|
-
table.add_column("
|
|
919
|
-
table.add_column("Line", style="green", justify="right")
|
|
971
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
920
972
|
table.add_column("Context", style="yellow")
|
|
921
973
|
|
|
922
974
|
for res in results:
|
|
975
|
+
file_path = res.get('file_path', '') or ''
|
|
976
|
+
line_str = str(res.get('line_number', ''))
|
|
977
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
978
|
+
|
|
923
979
|
table.add_row(
|
|
924
980
|
res.get('name', ''),
|
|
925
|
-
|
|
926
|
-
str(res.get('line_number', '')),
|
|
981
|
+
location_str,
|
|
927
982
|
res.get('context', '') or 'module'
|
|
928
983
|
)
|
|
929
984
|
|
|
@@ -974,15 +1029,17 @@ def find_by_content_search(
|
|
|
974
1029
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
975
1030
|
table.add_column("Name", style="cyan")
|
|
976
1031
|
table.add_column("Type", style="blue")
|
|
977
|
-
table.add_column("
|
|
978
|
-
table.add_column("Line", style="green", justify="right")
|
|
1032
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
979
1033
|
|
|
980
1034
|
for res in results:
|
|
1035
|
+
file_path = res.get('file_path', '') or ''
|
|
1036
|
+
line_str = str(res.get('line_number', ''))
|
|
1037
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1038
|
+
|
|
981
1039
|
table.add_row(
|
|
982
1040
|
res.get('name', ''),
|
|
983
1041
|
res.get('type', 'Unknown'),
|
|
984
|
-
|
|
985
|
-
str(res.get('line_number', ''))
|
|
1042
|
+
location_str
|
|
986
1043
|
)
|
|
987
1044
|
|
|
988
1045
|
console.print(f"[cyan]Found {len(results)} content match(es) for '{query}':[/cyan]")
|
|
@@ -1017,16 +1074,18 @@ def find_by_decorator_search(
|
|
|
1017
1074
|
|
|
1018
1075
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1019
1076
|
table.add_column("Function", style="cyan")
|
|
1020
|
-
table.add_column("
|
|
1021
|
-
table.add_column("Line", style="green", justify="right")
|
|
1077
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1022
1078
|
table.add_column("Decorators", style="yellow")
|
|
1023
1079
|
|
|
1024
1080
|
for res in results:
|
|
1025
1081
|
decorators_str = ", ".join(res.get('decorators', []))
|
|
1082
|
+
file_path = res.get('file_path', '') or ''
|
|
1083
|
+
line_str = str(res.get('line_number', ''))
|
|
1084
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1085
|
+
|
|
1026
1086
|
table.add_row(
|
|
1027
1087
|
res.get('function_name', ''),
|
|
1028
|
-
|
|
1029
|
-
str(res.get('line_number', '')),
|
|
1088
|
+
location_str,
|
|
1030
1089
|
decorators_str
|
|
1031
1090
|
)
|
|
1032
1091
|
|
|
@@ -1062,14 +1121,16 @@ def find_by_argument_search(
|
|
|
1062
1121
|
|
|
1063
1122
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1064
1123
|
table.add_column("Function", style="cyan")
|
|
1065
|
-
table.add_column("
|
|
1066
|
-
table.add_column("Line", style="green", justify="right")
|
|
1124
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1067
1125
|
|
|
1068
1126
|
for res in results:
|
|
1127
|
+
file_path = res.get('file_path', '') or ''
|
|
1128
|
+
line_str = str(res.get('line_number', ''))
|
|
1129
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1130
|
+
|
|
1069
1131
|
table.add_row(
|
|
1070
1132
|
res.get('function_name', ''),
|
|
1071
|
-
|
|
1072
|
-
str(res.get('line_number', ''))
|
|
1133
|
+
location_str
|
|
1073
1134
|
)
|
|
1074
1135
|
|
|
1075
1136
|
console.print(f"[cyan]Found {len(results)} function(s) with argument '{argument}':[/cyan]")
|
|
@@ -1112,15 +1173,17 @@ def analyze_calls(
|
|
|
1112
1173
|
|
|
1113
1174
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1114
1175
|
table.add_column("Called Function", style="cyan")
|
|
1115
|
-
table.add_column("
|
|
1116
|
-
table.add_column("Line", style="green", justify="right")
|
|
1176
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1117
1177
|
table.add_column("Type", style="yellow")
|
|
1118
1178
|
|
|
1119
1179
|
for result in results:
|
|
1180
|
+
file_path = result.get("called_file_path", "")
|
|
1181
|
+
line_str = str(result.get("called_line_number", ""))
|
|
1182
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1183
|
+
|
|
1120
1184
|
table.add_row(
|
|
1121
1185
|
result.get("called_function", ""),
|
|
1122
|
-
|
|
1123
|
-
str(result.get("called_line_number", "")),
|
|
1186
|
+
location_str,
|
|
1124
1187
|
"📦 Dependency" if result.get("called_is_dependency") else "📝 Project"
|
|
1125
1188
|
)
|
|
1126
1189
|
|
|
@@ -1157,17 +1220,21 @@ def analyze_callers(
|
|
|
1157
1220
|
|
|
1158
1221
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1159
1222
|
table.add_column("Caller Function", style="cyan")
|
|
1160
|
-
table.add_column("
|
|
1161
|
-
table.add_column("Line", style="green", justify="right")
|
|
1223
|
+
table.add_column("Location", style="green")
|
|
1162
1224
|
table.add_column("Call Type", style="yellow")
|
|
1225
|
+
|
|
1163
1226
|
|
|
1164
1227
|
for result in results:
|
|
1228
|
+
file_path = result.get("caller_file_path", "")
|
|
1229
|
+
line_number = result.get("caller_line_number")
|
|
1230
|
+
|
|
1231
|
+
location = f"{file_path}:{line_number}" if line_number else file_path
|
|
1232
|
+
|
|
1165
1233
|
table.add_row(
|
|
1166
1234
|
result.get("caller_function", ""),
|
|
1167
|
-
|
|
1168
|
-
str(result.get("caller_line_number", "")),
|
|
1235
|
+
location,
|
|
1169
1236
|
"📦 Dependency" if result.get("caller_is_dependency") else "📝 Project"
|
|
1170
|
-
|
|
1237
|
+
)
|
|
1171
1238
|
|
|
1172
1239
|
console.print(f"\n[bold cyan]Functions that call '{function}':[/bold cyan]")
|
|
1173
1240
|
console.print(table)
|
|
@@ -1179,13 +1246,16 @@ def analyze_callers(
|
|
|
1179
1246
|
def analyze_chain(
|
|
1180
1247
|
from_func: str = typer.Argument(..., help="Starting function"),
|
|
1181
1248
|
to_func: str = typer.Argument(..., help="Target function"),
|
|
1182
|
-
max_depth: int = typer.Option(5, "--depth", "-d", help="Maximum call chain depth")
|
|
1249
|
+
max_depth: int = typer.Option(5, "--depth", "-d", help="Maximum call chain depth"),
|
|
1250
|
+
from_file: Optional[str] = typer.Option(None, "--from-file", help="File for starting function"),
|
|
1251
|
+
to_file: Optional[str] = typer.Option(None, "--to-file", help="File for target function")
|
|
1183
1252
|
):
|
|
1184
1253
|
"""
|
|
1185
1254
|
Show call chain between two functions.
|
|
1186
1255
|
|
|
1187
1256
|
Example:
|
|
1188
1257
|
cgc analyze chain main process_data --depth 10
|
|
1258
|
+
cgc analyze chain main process --from-file main.py --to-file utils.py
|
|
1189
1259
|
"""
|
|
1190
1260
|
_load_credentials()
|
|
1191
1261
|
services = _initialize_services()
|
|
@@ -1194,7 +1264,7 @@ def analyze_chain(
|
|
|
1194
1264
|
db_manager, graph_builder, code_finder = services
|
|
1195
1265
|
|
|
1196
1266
|
try:
|
|
1197
|
-
results = code_finder.find_function_call_chain(from_func, to_func, max_depth)
|
|
1267
|
+
results = code_finder.find_function_call_chain(from_func, to_func, max_depth, from_file, to_file)
|
|
1198
1268
|
|
|
1199
1269
|
if not results:
|
|
1200
1270
|
console.print(f"[yellow]No call chain found between '{from_func}' and '{to_func}' within depth {max_depth}[/yellow]")
|
|
@@ -1204,10 +1274,36 @@ def analyze_chain(
|
|
|
1204
1274
|
console.print(f"\n[bold cyan]Call Chain #{idx} (length: {chain.get('chain_length', 0)}):[/bold cyan]")
|
|
1205
1275
|
|
|
1206
1276
|
functions = chain.get('function_chain', [])
|
|
1277
|
+
call_details = chain.get('call_details', [])
|
|
1278
|
+
|
|
1207
1279
|
for i, func in enumerate(functions):
|
|
1208
1280
|
indent = " " * i
|
|
1209
|
-
|
|
1210
|
-
|
|
1281
|
+
|
|
1282
|
+
# Print function
|
|
1283
|
+
console.print(f"{indent}[cyan]{func.get('name', 'Unknown')}[/cyan] [dim]({func.get('file_path', '')}:{func.get('line_number', '')})[/dim]")
|
|
1284
|
+
|
|
1285
|
+
# If there is a next step, print the connecting call detail
|
|
1286
|
+
if i < len(functions) - 1 and i < len(call_details):
|
|
1287
|
+
detail = call_details[i]
|
|
1288
|
+
line = detail.get('call_line', '?')
|
|
1289
|
+
|
|
1290
|
+
# Format args for display
|
|
1291
|
+
args_info = ""
|
|
1292
|
+
args_val = detail.get('args', [])
|
|
1293
|
+
if args_val:
|
|
1294
|
+
if isinstance(args_val, list):
|
|
1295
|
+
# Filter legacy punctuation just in case
|
|
1296
|
+
clean_args = [str(a) for a in args_val if str(a) not in ('(', ')', ',')]
|
|
1297
|
+
args_str = ", ".join(clean_args)
|
|
1298
|
+
else:
|
|
1299
|
+
args_str = str(args_val)
|
|
1300
|
+
|
|
1301
|
+
# Truncate if too long
|
|
1302
|
+
if len(args_str) > 50:
|
|
1303
|
+
args_str = args_str[:47] + "..."
|
|
1304
|
+
args_info = f" [dim]({args_str})[/dim]"
|
|
1305
|
+
|
|
1306
|
+
console.print(f"{indent} ⬇ [dim]calls at line {line}[/dim]{args_info}")
|
|
1211
1307
|
finally:
|
|
1212
1308
|
db_manager.close_driver()
|
|
1213
1309
|
|
|
@@ -1240,13 +1336,15 @@ def analyze_dependencies(
|
|
|
1240
1336
|
if results.get('importers'):
|
|
1241
1337
|
console.print(f"\n[bold cyan]Files that import '{target}':[/bold cyan]")
|
|
1242
1338
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1243
|
-
table.add_column("
|
|
1244
|
-
table.add_column("Line", style="green", justify="right")
|
|
1339
|
+
table.add_column("Location", style="cyan", overflow="fold")
|
|
1245
1340
|
|
|
1246
1341
|
for imp in results['importers']:
|
|
1342
|
+
file_path = imp.get('importer_file_path', '')
|
|
1343
|
+
line_str = str(imp.get('import_line_number', ''))
|
|
1344
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1345
|
+
|
|
1247
1346
|
table.add_row(
|
|
1248
|
-
|
|
1249
|
-
str(imp.get('import_line_number', ''))
|
|
1347
|
+
location_str
|
|
1250
1348
|
)
|
|
1251
1349
|
console.print(table)
|
|
1252
1350
|
|
|
@@ -1323,7 +1421,8 @@ def analyze_inheritance_tree(
|
|
|
1323
1421
|
def analyze_complexity(
|
|
1324
1422
|
path: Optional[str] = typer.Argument(None, help="Specific function name to analyze"),
|
|
1325
1423
|
threshold: int = typer.Option(10, "--threshold", "-t", help="Complexity threshold for warnings"),
|
|
1326
|
-
limit: int = typer.Option(20, "--limit", "-l", help="Maximum results to show")
|
|
1424
|
+
limit: int = typer.Option(20, "--limit", "-l", help="Maximum results to show"),
|
|
1425
|
+
file: Optional[str] = typer.Option(None, "--file", "-f", help="Specific file path (only used when function name is provided)")
|
|
1327
1426
|
):
|
|
1328
1427
|
"""
|
|
1329
1428
|
Show cyclomatic complexity for functions.
|
|
@@ -1332,6 +1431,7 @@ def analyze_complexity(
|
|
|
1332
1431
|
cgc analyze complexity # Most complex functions
|
|
1333
1432
|
cgc analyze complexity --threshold 15 # Functions over threshold
|
|
1334
1433
|
cgc analyze complexity my_function # Specific function
|
|
1434
|
+
cgc analyze complexity my_function -f file.py # Specific function in file
|
|
1335
1435
|
"""
|
|
1336
1436
|
_load_credentials()
|
|
1337
1437
|
services = _initialize_services()
|
|
@@ -1342,7 +1442,7 @@ def analyze_complexity(
|
|
|
1342
1442
|
try:
|
|
1343
1443
|
if path:
|
|
1344
1444
|
# Specific function
|
|
1345
|
-
result = code_finder.get_cyclomatic_complexity(path)
|
|
1445
|
+
result = code_finder.get_cyclomatic_complexity(path, file)
|
|
1346
1446
|
if result:
|
|
1347
1447
|
console.print(f"\n[bold cyan]Complexity for '{path}':[/bold cyan]")
|
|
1348
1448
|
console.print(f" Cyclomatic Complexity: [yellow]{result.get('complexity', 'N/A')}[/yellow]")
|
|
@@ -1361,17 +1461,19 @@ def analyze_complexity(
|
|
|
1361
1461
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1362
1462
|
table.add_column("Function", style="cyan")
|
|
1363
1463
|
table.add_column("Complexity", style="yellow", justify="right")
|
|
1364
|
-
table.add_column("
|
|
1365
|
-
table.add_column("Line", style="green", justify="right")
|
|
1464
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1366
1465
|
|
|
1367
1466
|
for func in results:
|
|
1368
1467
|
complexity = func.get('complexity', 0)
|
|
1369
1468
|
color = "red" if complexity > threshold else "yellow" if complexity > threshold/2 else "green"
|
|
1469
|
+
file_path = func.get('file_path', '')
|
|
1470
|
+
line_str = str(func.get('line_number', ''))
|
|
1471
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1472
|
+
|
|
1370
1473
|
table.add_row(
|
|
1371
1474
|
func.get('function_name', ''),
|
|
1372
1475
|
f"[{color}]{complexity}[/{color}]",
|
|
1373
|
-
|
|
1374
|
-
str(func.get('line_number', ''))
|
|
1476
|
+
location_str
|
|
1375
1477
|
)
|
|
1376
1478
|
|
|
1377
1479
|
console.print(f"\n[bold cyan]Most Complex Functions (threshold: {threshold}):[/bold cyan]")
|
|
@@ -1410,14 +1512,16 @@ def analyze_dead_code(
|
|
|
1410
1512
|
|
|
1411
1513
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1412
1514
|
table.add_column("Function", style="cyan")
|
|
1413
|
-
table.add_column("
|
|
1414
|
-
table.add_column("Line", style="green", justify="right")
|
|
1515
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1415
1516
|
|
|
1416
1517
|
for func in unused_funcs:
|
|
1518
|
+
file_path = func.get('file_path', '')
|
|
1519
|
+
line_str = str(func.get('line_number', ''))
|
|
1520
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1521
|
+
|
|
1417
1522
|
table.add_row(
|
|
1418
1523
|
func.get('function_name', ''),
|
|
1419
|
-
|
|
1420
|
-
str(func.get('line_number', ''))
|
|
1524
|
+
location_str
|
|
1421
1525
|
)
|
|
1422
1526
|
|
|
1423
1527
|
console.print(f"\n[bold yellow]⚠️ Potentially Unused Functions:[/bold yellow]")
|
|
@@ -1456,15 +1560,17 @@ def analyze_overrides(
|
|
|
1456
1560
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1457
1561
|
table.add_column("Class", style="cyan")
|
|
1458
1562
|
table.add_column("Function", style="green")
|
|
1459
|
-
table.add_column("
|
|
1460
|
-
table.add_column("Line", style="yellow", justify="right")
|
|
1563
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1461
1564
|
|
|
1462
1565
|
for res in results:
|
|
1566
|
+
file_path = res.get('class_file_path', '')
|
|
1567
|
+
line_str = str(res.get('function_line_number', ''))
|
|
1568
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1569
|
+
|
|
1463
1570
|
table.add_row(
|
|
1464
1571
|
res.get('class_name', ''),
|
|
1465
1572
|
res.get('function_name', ''),
|
|
1466
|
-
|
|
1467
|
-
str(res.get('function_line_number', ''))
|
|
1573
|
+
location_str
|
|
1468
1574
|
)
|
|
1469
1575
|
|
|
1470
1576
|
console.print(f"\n[bold cyan]Found {len(results)} implementation(s) of '{function_name}':[/bold cyan]")
|
|
@@ -1474,7 +1580,8 @@ def analyze_overrides(
|
|
|
1474
1580
|
|
|
1475
1581
|
@analyze_app.command("variable")
|
|
1476
1582
|
def analyze_variable_usage(
|
|
1477
|
-
variable_name: str = typer.Argument(..., help="Variable name to analyze")
|
|
1583
|
+
variable_name: str = typer.Argument(..., help="Variable name to analyze"),
|
|
1584
|
+
file: Optional[str] = typer.Option(None, "--file", "-f", help="Specific file path")
|
|
1478
1585
|
):
|
|
1479
1586
|
"""
|
|
1480
1587
|
Analyze where a variable is defined and used across the codebase.
|
|
@@ -1484,6 +1591,7 @@ def analyze_variable_usage(
|
|
|
1484
1591
|
Example:
|
|
1485
1592
|
cgc analyze variable MAX_RETRIES
|
|
1486
1593
|
cgc analyze variable config
|
|
1594
|
+
cgc analyze variable x --file math_utils.py
|
|
1487
1595
|
"""
|
|
1488
1596
|
_load_credentials()
|
|
1489
1597
|
services = _initialize_services()
|
|
@@ -1493,7 +1601,7 @@ def analyze_variable_usage(
|
|
|
1493
1601
|
|
|
1494
1602
|
try:
|
|
1495
1603
|
# Get variable usage scope
|
|
1496
|
-
scope_results = code_finder.find_variable_usage_scope(variable_name)
|
|
1604
|
+
scope_results = code_finder.find_variable_usage_scope(variable_name, file)
|
|
1497
1605
|
instances = scope_results.get('instances', [])
|
|
1498
1606
|
|
|
1499
1607
|
if not instances:
|
|
@@ -1516,15 +1624,17 @@ def analyze_variable_usage(
|
|
|
1516
1624
|
|
|
1517
1625
|
table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1518
1626
|
table.add_column("Scope Name", style="cyan")
|
|
1519
|
-
table.add_column("
|
|
1520
|
-
table.add_column("Line", style="green", justify="right")
|
|
1627
|
+
table.add_column("Location", style="dim", overflow="fold")
|
|
1521
1628
|
table.add_column("Value", style="yellow")
|
|
1522
1629
|
|
|
1523
1630
|
for item in items:
|
|
1631
|
+
file_path = item.get('file_path', '')
|
|
1632
|
+
line_str = str(item.get('line_number', ''))
|
|
1633
|
+
location_str = f"{file_path}:{line_str}" if line_str else file_path
|
|
1634
|
+
|
|
1524
1635
|
table.add_row(
|
|
1525
1636
|
item.get('scope_name', ''),
|
|
1526
|
-
|
|
1527
|
-
str(item.get('line_number', '')),
|
|
1637
|
+
location_str,
|
|
1528
1638
|
str(item.get('variable_value', ''))[:50] if item.get('variable_value') else '-'
|
|
1529
1639
|
)
|
|
1530
1640
|
|