athena-code 0.0.14__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 athena-code might be problematic. Click here for more details.
- athena/README.md +132 -0
- athena/__init__.py +8 -0
- athena/__main__.py +5 -0
- athena/cli.py +347 -0
- athena/docstring_updater.py +133 -0
- athena/entity_path.py +146 -0
- athena/hashing.py +156 -0
- athena/info.py +84 -0
- athena/locate.py +52 -0
- athena/mcp_config.py +103 -0
- athena/mcp_server.py +215 -0
- athena/models.py +90 -0
- athena/parsers/__init__.py +22 -0
- athena/parsers/base.py +39 -0
- athena/parsers/python_parser.py +633 -0
- athena/repository.py +75 -0
- athena/status.py +88 -0
- athena/sync.py +577 -0
- athena_code-0.0.14.dist-info/METADATA +152 -0
- athena_code-0.0.14.dist-info/RECORD +24 -0
- athena_code-0.0.14.dist-info/WHEEL +5 -0
- athena_code-0.0.14.dist-info/entry_points.txt +3 -0
- athena_code-0.0.14.dist-info/licenses/LICENSE +21 -0
- athena_code-0.0.14.dist-info/top_level.txt +1 -0
athena/repository.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Iterator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
EXCLUDED_DIRS = {
|
|
6
|
+
"node_modules",
|
|
7
|
+
".venv",
|
|
8
|
+
"venv",
|
|
9
|
+
"__pycache__",
|
|
10
|
+
"build",
|
|
11
|
+
"dist",
|
|
12
|
+
".git",
|
|
13
|
+
".tox",
|
|
14
|
+
"vendor",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RepositoryNotFoundError(Exception):
|
|
19
|
+
"""Raised when no git repository is found."""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def find_repository_root(start_path: Path = Path.cwd()) -> Path:
|
|
24
|
+
"""Find the root of the git repository by walking up the directory tree.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
start_path: The directory to start searching from (defaults to current directory)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Path to the repository root (the directory containing .git/)
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
RepositoryNotFoundError: If no .git directory is found
|
|
34
|
+
"""
|
|
35
|
+
current = start_path.resolve()
|
|
36
|
+
|
|
37
|
+
while current != current.parent:
|
|
38
|
+
if (current / ".git").exists():
|
|
39
|
+
return current
|
|
40
|
+
current = current.parent
|
|
41
|
+
|
|
42
|
+
if (current / ".git").exists():
|
|
43
|
+
return current
|
|
44
|
+
|
|
45
|
+
raise RepositoryNotFoundError(
|
|
46
|
+
f"No git repository found from {start_path}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def find_python_files(root: Path) -> Iterator[Path]:
|
|
51
|
+
"""Find all Python files in the repository, excluding common directories.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
root: The repository root directory to search
|
|
55
|
+
|
|
56
|
+
Yields:
|
|
57
|
+
Path objects for each .py file found
|
|
58
|
+
"""
|
|
59
|
+
for path in root.rglob("*.py"):
|
|
60
|
+
if any(excluded in path.parts for excluded in EXCLUDED_DIRS):
|
|
61
|
+
continue
|
|
62
|
+
yield path
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_relative_path(file_path: Path, root: Path) -> str:
|
|
66
|
+
"""Convert an absolute file path to a POSIX-style relative path from root.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
file_path: Absolute path to a file
|
|
70
|
+
root: Repository root directory
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
POSIX-style relative path as a string
|
|
74
|
+
"""
|
|
75
|
+
return file_path.relative_to(root).as_posix()
|
athena/status.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Status command - check entity docstring hash synchronization state."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from athena.entity_path import EntityPath, parse_entity_path
|
|
6
|
+
from athena.models import EntityStatus
|
|
7
|
+
from athena.sync import collect_sub_entities, inspect_entity
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def check_status(entity_path_str: str, repo_root: Path) -> list[EntityStatus]:
|
|
11
|
+
"""Check status of a single entity.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
entity_path_str: Entity path string (e.g., "src/foo.py:Bar")
|
|
15
|
+
repo_root: Repository root directory
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
List containing single EntityStatus
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
FileNotFoundError: If entity file doesn't exist
|
|
22
|
+
ValueError: If entity is not found in file or path is invalid
|
|
23
|
+
NotImplementedError: For package/module level status
|
|
24
|
+
"""
|
|
25
|
+
status = inspect_entity(entity_path_str, repo_root)
|
|
26
|
+
return [status]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def check_status_recursive(entity_path_str: str, repo_root: Path) -> list[EntityStatus]:
|
|
30
|
+
"""Check status of an entity and all its sub-entities recursively.
|
|
31
|
+
|
|
32
|
+
For modules: checks all functions, classes, and methods
|
|
33
|
+
For packages: checks all modules and their entities
|
|
34
|
+
For classes: checks the class and all its methods
|
|
35
|
+
For functions/methods: checks only that entity
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
entity_path_str: Entity path string
|
|
39
|
+
repo_root: Repository root directory
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of EntityStatus for all entities
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
FileNotFoundError: If entity file doesn't exist
|
|
46
|
+
ValueError: If entity is not found in file or path is invalid
|
|
47
|
+
"""
|
|
48
|
+
entity_path = parse_entity_path(entity_path_str)
|
|
49
|
+
|
|
50
|
+
entities_to_check = []
|
|
51
|
+
|
|
52
|
+
if entity_path.is_package or entity_path.is_module:
|
|
53
|
+
entities_to_check = collect_sub_entities(entity_path, repo_root)
|
|
54
|
+
else:
|
|
55
|
+
if entity_path.is_class:
|
|
56
|
+
entities_to_check.append(entity_path_str)
|
|
57
|
+
entities_to_check.extend(collect_sub_entities(entity_path, repo_root))
|
|
58
|
+
else:
|
|
59
|
+
entities_to_check.append(entity_path_str)
|
|
60
|
+
|
|
61
|
+
statuses = []
|
|
62
|
+
for entity in entities_to_check:
|
|
63
|
+
try:
|
|
64
|
+
status = inspect_entity(entity, repo_root)
|
|
65
|
+
statuses.append(status)
|
|
66
|
+
except (ValueError, FileNotFoundError) as e:
|
|
67
|
+
print(f"Warning: Failed to inspect {entity}: {e}")
|
|
68
|
+
|
|
69
|
+
return statuses
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def filter_out_of_sync(statuses: list[EntityStatus]) -> list[EntityStatus]:
|
|
73
|
+
"""Filter list to only out-of-sync entities.
|
|
74
|
+
|
|
75
|
+
An entity is out-of-sync if:
|
|
76
|
+
- It has no recorded hash (None)
|
|
77
|
+
- The recorded hash doesn't match the calculated hash
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
statuses: List of EntityStatus
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Filtered list containing only out-of-sync entities
|
|
84
|
+
"""
|
|
85
|
+
return [
|
|
86
|
+
s for s in statuses
|
|
87
|
+
if s.recorded_hash is None or s.recorded_hash != s.calculated_hash
|
|
88
|
+
]
|