cicada-mcp 0.1.4__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.
Potentially problematic release.
This version of cicada-mcp might be problematic. Click here for more details.
- cicada/__init__.py +30 -0
- cicada/clean.py +297 -0
- cicada/command_logger.py +293 -0
- cicada/dead_code_analyzer.py +282 -0
- cicada/extractors/__init__.py +36 -0
- cicada/extractors/base.py +66 -0
- cicada/extractors/call.py +176 -0
- cicada/extractors/dependency.py +361 -0
- cicada/extractors/doc.py +179 -0
- cicada/extractors/function.py +246 -0
- cicada/extractors/module.py +123 -0
- cicada/extractors/spec.py +151 -0
- cicada/find_dead_code.py +270 -0
- cicada/formatter.py +918 -0
- cicada/git_helper.py +646 -0
- cicada/indexer.py +629 -0
- cicada/install.py +724 -0
- cicada/keyword_extractor.py +364 -0
- cicada/keyword_search.py +553 -0
- cicada/lightweight_keyword_extractor.py +298 -0
- cicada/mcp_server.py +1559 -0
- cicada/mcp_tools.py +291 -0
- cicada/parser.py +124 -0
- cicada/pr_finder.py +435 -0
- cicada/pr_indexer/__init__.py +20 -0
- cicada/pr_indexer/cli.py +62 -0
- cicada/pr_indexer/github_api_client.py +431 -0
- cicada/pr_indexer/indexer.py +297 -0
- cicada/pr_indexer/line_mapper.py +209 -0
- cicada/pr_indexer/pr_index_builder.py +253 -0
- cicada/setup.py +339 -0
- cicada/utils/__init__.py +52 -0
- cicada/utils/call_site_formatter.py +95 -0
- cicada/utils/function_grouper.py +57 -0
- cicada/utils/hash_utils.py +173 -0
- cicada/utils/index_utils.py +290 -0
- cicada/utils/path_utils.py +240 -0
- cicada/utils/signature_builder.py +106 -0
- cicada/utils/storage.py +111 -0
- cicada/utils/subprocess_runner.py +182 -0
- cicada/utils/text_utils.py +90 -0
- cicada/version_check.py +116 -0
- cicada_mcp-0.1.4.dist-info/METADATA +619 -0
- cicada_mcp-0.1.4.dist-info/RECORD +48 -0
- cicada_mcp-0.1.4.dist-info/WHEEL +5 -0
- cicada_mcp-0.1.4.dist-info/entry_points.txt +8 -0
- cicada_mcp-0.1.4.dist-info/licenses/LICENSE +21 -0
- cicada_mcp-0.1.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Text utilities for identifier manipulation and processing.
|
|
3
|
+
|
|
4
|
+
This module provides shared utilities for working with code identifiers,
|
|
5
|
+
including splitting camelCase, PascalCase, and snake_case identifiers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import List
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def split_identifier(identifier: str, lowercase: bool = True) -> List[str]:
|
|
13
|
+
"""
|
|
14
|
+
Split an identifier by camelCase, PascalCase, and snake_case.
|
|
15
|
+
|
|
16
|
+
Handles various identifier patterns:
|
|
17
|
+
- snake_case: get_user_data -> ['get', 'user', 'data']
|
|
18
|
+
- camelCase: getUserData -> ['get', 'user', 'data']
|
|
19
|
+
- PascalCase: UserController -> ['user', 'controller']
|
|
20
|
+
- HTTPServer: HTTP Server -> ['http', 'server']
|
|
21
|
+
- PostgreSQL: Postgre SQL -> ['postgre', 'sql']
|
|
22
|
+
- getHTTPResponseCode -> ['get', 'http', 'response', 'code']
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
identifier: The identifier string to split
|
|
26
|
+
lowercase: If True, convert all words to lowercase (default: True)
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
List of words from the identifier
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
>>> split_identifier("getUserData")
|
|
33
|
+
['get', 'user', 'data']
|
|
34
|
+
>>> split_identifier("HTTPServer")
|
|
35
|
+
['http', 'server']
|
|
36
|
+
>>> split_identifier("snake_case_name")
|
|
37
|
+
['snake', 'case', 'name']
|
|
38
|
+
"""
|
|
39
|
+
if not identifier:
|
|
40
|
+
return []
|
|
41
|
+
|
|
42
|
+
# Handle snake_case
|
|
43
|
+
text = identifier.replace("_", " ")
|
|
44
|
+
|
|
45
|
+
# Split on transitions from lowercase to uppercase (camelCase)
|
|
46
|
+
# This handles: camelCase, PascalCase, and words ending with uppercase like PostgreSQL
|
|
47
|
+
text = re.sub(r"([a-z])([A-Z])", r"\1 \2", text)
|
|
48
|
+
|
|
49
|
+
# Split on transitions from uppercase sequence to a capitalized word (HTTPServer -> HTTP Server)
|
|
50
|
+
text = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1 \2", text)
|
|
51
|
+
|
|
52
|
+
# Split consecutive uppercase letters to separate potential acronyms
|
|
53
|
+
# This helps extract individual letters from acronyms at the end (e.g., SQL -> S Q L)
|
|
54
|
+
# But only if they're part of a longer word (not standalone acronyms)
|
|
55
|
+
text = re.sub(r"(?<=[a-z])([A-Z])(?=[A-Z])", r" \1", text)
|
|
56
|
+
|
|
57
|
+
# Split and filter
|
|
58
|
+
words = text.split()
|
|
59
|
+
|
|
60
|
+
if lowercase:
|
|
61
|
+
words = [word.lower() for word in words if word]
|
|
62
|
+
else:
|
|
63
|
+
words = [word for word in words if word]
|
|
64
|
+
|
|
65
|
+
return words
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def split_camel_snake_case(text: str) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Split camelCase, PascalCase, and snake_case identifiers into separate words as a string.
|
|
71
|
+
|
|
72
|
+
This is a convenience wrapper around split_identifier() that returns a space-separated
|
|
73
|
+
string instead of a list.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
text: Input text containing identifiers
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
String with identifiers split into separate words
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
>>> split_camel_snake_case("camelCase")
|
|
83
|
+
'camel case'
|
|
84
|
+
>>> split_camel_snake_case("PascalCase")
|
|
85
|
+
'pascal case'
|
|
86
|
+
>>> split_camel_snake_case("snake_case")
|
|
87
|
+
'snake case'
|
|
88
|
+
"""
|
|
89
|
+
words = split_identifier(text, lowercase=True)
|
|
90
|
+
return " ".join(words)
|
cicada/version_check.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Version checking utility.
|
|
3
|
+
|
|
4
|
+
Checks if a newer version of cicada is available on GitHub.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_current_version() -> str:
|
|
12
|
+
"""
|
|
13
|
+
Get the current version of cicada from pyproject.toml.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
Current version string (e.g., "0.1.0")
|
|
17
|
+
"""
|
|
18
|
+
from cicada import __version__
|
|
19
|
+
|
|
20
|
+
return __version__
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_latest_github_tag(repo: str = "wende/cicada") -> Optional[str]:
|
|
24
|
+
"""
|
|
25
|
+
Get the latest tag from GitHub repository.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
repo: GitHub repository in format "owner/repo"
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Latest tag name, or None if unable to fetch
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
result = subprocess.run(
|
|
35
|
+
[
|
|
36
|
+
"gh",
|
|
37
|
+
"api",
|
|
38
|
+
f"repos/{repo}/tags",
|
|
39
|
+
"--jq",
|
|
40
|
+
".[0].name",
|
|
41
|
+
],
|
|
42
|
+
capture_output=True,
|
|
43
|
+
text=True,
|
|
44
|
+
timeout=5,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
48
|
+
tag = result.stdout.strip()
|
|
49
|
+
# Remove 'v' prefix if present (e.g., "v0.1.0" -> "0.1.0")
|
|
50
|
+
if tag.startswith("v"):
|
|
51
|
+
tag = tag[1:]
|
|
52
|
+
return tag
|
|
53
|
+
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
|
|
57
|
+
# Silently fail - this is a non-critical check
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def compare_versions(current: str, latest: str) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Compare two version strings.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
current: Current version (e.g., "0.1.0")
|
|
67
|
+
latest: Latest version (e.g., "0.2.0")
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
True if latest is newer than current, False otherwise
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
# Simple comparison by splitting on dots
|
|
74
|
+
current_parts = [int(x) for x in current.split(".")]
|
|
75
|
+
latest_parts = [int(x) for x in latest.split(".")]
|
|
76
|
+
|
|
77
|
+
# Pad with zeros if needed
|
|
78
|
+
max_len = max(len(current_parts), len(latest_parts))
|
|
79
|
+
current_parts += [0] * (max_len - len(current_parts))
|
|
80
|
+
latest_parts += [0] * (max_len - len(latest_parts))
|
|
81
|
+
|
|
82
|
+
return latest_parts > current_parts
|
|
83
|
+
|
|
84
|
+
except (ValueError, AttributeError):
|
|
85
|
+
# If we can't parse versions, assume they're the same
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def check_for_updates() -> None:
|
|
90
|
+
"""
|
|
91
|
+
Check if there's a newer version available on GitHub.
|
|
92
|
+
|
|
93
|
+
Prints a yellow warning message if a newer version is found.
|
|
94
|
+
This function never raises exceptions - it fails silently if unable to check.
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
current = get_current_version()
|
|
98
|
+
latest = get_latest_github_tag()
|
|
99
|
+
|
|
100
|
+
if latest and compare_versions(current, latest):
|
|
101
|
+
# ANSI escape code for yellow text
|
|
102
|
+
yellow = "\033[93m"
|
|
103
|
+
reset = "\033[0m"
|
|
104
|
+
|
|
105
|
+
print(
|
|
106
|
+
f"{yellow}⚠️ A newer version of cicada is available: v{latest} (current: v{current}){reset}"
|
|
107
|
+
)
|
|
108
|
+
print(f"{yellow} To update, run:{reset}")
|
|
109
|
+
print(
|
|
110
|
+
f"{yellow} uv tool install git+https://github.com/wende/cicada.git{reset}"
|
|
111
|
+
)
|
|
112
|
+
print()
|
|
113
|
+
|
|
114
|
+
except Exception:
|
|
115
|
+
# Silently fail - version check is non-critical
|
|
116
|
+
pass
|