codemap-cli 0.1.0__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.
- codemap/__init__.py +0 -0
- codemap/cli.py +74 -0
- codemap/constants.py +11 -0
- codemap/formatter.py +18 -0
- codemap/plugins/__init__.py +0 -0
- codemap/plugins/base.py +23 -0
- codemap/plugins/javascript.py +22 -0
- codemap/plugins/python.py +23 -0
- codemap/root.py +29 -0
- codemap/scanner.py +49 -0
- codemap/writer.py +20 -0
- codemap_cli-0.1.0.dist-info/METADATA +7 -0
- codemap_cli-0.1.0.dist-info/RECORD +16 -0
- codemap_cli-0.1.0.dist-info/WHEEL +5 -0
- codemap_cli-0.1.0.dist-info/entry_points.txt +2 -0
- codemap_cli-0.1.0.dist-info/top_level.txt +1 -0
codemap/__init__.py
ADDED
|
File without changes
|
codemap/cli.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from .root import find_project_root, detect_plugin
|
|
4
|
+
from .scanner import build_tree
|
|
5
|
+
from .formatter import format_tree
|
|
6
|
+
from .writer import build_markdown, write_file
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(invoke_without_command=True)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.callback()
|
|
12
|
+
def main():
|
|
13
|
+
start_path = Path.cwd()
|
|
14
|
+
|
|
15
|
+
root = find_project_root(start_path)
|
|
16
|
+
|
|
17
|
+
if not root:
|
|
18
|
+
typer.secho(
|
|
19
|
+
"Error: No project root detected.",
|
|
20
|
+
fg=typer.colors.RED,
|
|
21
|
+
)
|
|
22
|
+
raise typer.Exit(code=1)
|
|
23
|
+
|
|
24
|
+
if root != start_path:
|
|
25
|
+
typer.secho(
|
|
26
|
+
f"Detected project root at: {root}",
|
|
27
|
+
fg=typer.colors.CYAN,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
plugin = detect_plugin(root)
|
|
31
|
+
|
|
32
|
+
output_file = root / "codemap.md"
|
|
33
|
+
|
|
34
|
+
# Build tree
|
|
35
|
+
tree = build_tree(root, plugin)
|
|
36
|
+
tree_text = format_tree(tree)
|
|
37
|
+
|
|
38
|
+
# Build full markdown content
|
|
39
|
+
new_content = build_markdown(
|
|
40
|
+
tree_text,
|
|
41
|
+
root,
|
|
42
|
+
plugin.name if plugin else "unknown",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if output_file.exists():
|
|
46
|
+
old_content = output_file.read_text(encoding="utf-8")
|
|
47
|
+
|
|
48
|
+
# Extract old tree block
|
|
49
|
+
if "```" in old_content:
|
|
50
|
+
old_tree = old_content.split("```")[1].strip()
|
|
51
|
+
else:
|
|
52
|
+
old_tree = ""
|
|
53
|
+
|
|
54
|
+
if old_tree == tree_text.strip():
|
|
55
|
+
typer.secho(
|
|
56
|
+
"No structural changes detected. codemap.md is up to date.",
|
|
57
|
+
fg=typer.colors.BLUE,
|
|
58
|
+
)
|
|
59
|
+
raise typer.Exit()
|
|
60
|
+
|
|
61
|
+
# Structure changed → rewrite file with new timestamp
|
|
62
|
+
write_file(output_file, new_content)
|
|
63
|
+
|
|
64
|
+
typer.secho(
|
|
65
|
+
"codemap.md updated successfully.",
|
|
66
|
+
fg=typer.colors.YELLOW,
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
write_file(output_file, new_content)
|
|
70
|
+
|
|
71
|
+
typer.secho(
|
|
72
|
+
"codemap.md generated successfully.",
|
|
73
|
+
fg=typer.colors.GREEN,
|
|
74
|
+
)
|
codemap/constants.py
ADDED
codemap/formatter.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
def format_tree(tree):
|
|
2
|
+
lines = [tree["name"]]
|
|
3
|
+
|
|
4
|
+
def _format(node, prefix=""):
|
|
5
|
+
children = node.get("children", [])
|
|
6
|
+
total = len(children)
|
|
7
|
+
|
|
8
|
+
for index, child in enumerate(children):
|
|
9
|
+
connector = "└── " if index == total - 1 else "├── "
|
|
10
|
+
line = prefix + connector + child["name"]
|
|
11
|
+
lines.append(line)
|
|
12
|
+
|
|
13
|
+
if child["type"] == "dir" and not child.get("collapsed"):
|
|
14
|
+
extension = " " if index == total - 1 else "│ "
|
|
15
|
+
_format(child, prefix + extension)
|
|
16
|
+
|
|
17
|
+
_format(tree)
|
|
18
|
+
return "\n".join(lines)
|
|
File without changes
|
codemap/plugins/base.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class BasePlugin:
|
|
2
|
+
name = "base"
|
|
3
|
+
|
|
4
|
+
# Files that indicate the current framework
|
|
5
|
+
detection_files = []
|
|
6
|
+
|
|
7
|
+
# Files to ignore when scanning the code
|
|
8
|
+
ignore = set()
|
|
9
|
+
|
|
10
|
+
# Show folder but do not traverse inside
|
|
11
|
+
collapse = set()
|
|
12
|
+
|
|
13
|
+
# Files that should never be shown
|
|
14
|
+
hidden_files = set()
|
|
15
|
+
|
|
16
|
+
def matches(self, root_path):
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
for file in self.detection_files:
|
|
20
|
+
if (Path(root_path) / file).exists():
|
|
21
|
+
return True
|
|
22
|
+
return False
|
|
23
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .base import BasePlugin
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class JavaScriptPlugin(BasePlugin):
|
|
5
|
+
name = "javascript"
|
|
6
|
+
|
|
7
|
+
detection_files = ["package.json"]
|
|
8
|
+
|
|
9
|
+
ignore = {
|
|
10
|
+
".next",
|
|
11
|
+
"dist",
|
|
12
|
+
"build",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
collapse = {
|
|
16
|
+
"node_modules",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
hidden_files = {
|
|
20
|
+
".env",
|
|
21
|
+
".env.local",
|
|
22
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .base import BasePlugin
|
|
2
|
+
|
|
3
|
+
class PythonPlugin(BasePlugin):
|
|
4
|
+
name = "python"
|
|
5
|
+
|
|
6
|
+
detection_files = ["pyproject.toml","requirements.txt","setup.py"]
|
|
7
|
+
|
|
8
|
+
ignore = {
|
|
9
|
+
"__pycache__",
|
|
10
|
+
".venv",
|
|
11
|
+
"venv",
|
|
12
|
+
"env",
|
|
13
|
+
".env",
|
|
14
|
+
".mypy_cache",
|
|
15
|
+
".pytest_cache",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
collapse = set()
|
|
19
|
+
|
|
20
|
+
hidden_files = {
|
|
21
|
+
".env",
|
|
22
|
+
".env.local",
|
|
23
|
+
}
|
codemap/root.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from .plugins.python import PythonPlugin
|
|
3
|
+
from .plugins.javascript import JavaScriptPlugin
|
|
4
|
+
|
|
5
|
+
PLUGINS = [
|
|
6
|
+
PythonPlugin(),
|
|
7
|
+
JavaScriptPlugin(),
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
def find_project_root(start_path: Path) -> Path | None:
|
|
11
|
+
current = start_path.resolve()
|
|
12
|
+
|
|
13
|
+
while current != current.parent:
|
|
14
|
+
for plugin in PLUGINS:
|
|
15
|
+
for marker in plugin.detection_files:
|
|
16
|
+
if (current / marker).exists():
|
|
17
|
+
return current
|
|
18
|
+
if (current / ".git").exists():
|
|
19
|
+
return current
|
|
20
|
+
current = current.parent
|
|
21
|
+
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def detect_plugin(root_path: Path):
|
|
26
|
+
for plugin in PLUGINS:
|
|
27
|
+
if plugin.matches(root_path):
|
|
28
|
+
return plugin
|
|
29
|
+
return None
|
codemap/scanner.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from .constants import GLOBAL_IGNORE, GLOBAL_HIDDEN_FILES
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def build_tree(path: Path, plugin):
|
|
6
|
+
name = path.name
|
|
7
|
+
|
|
8
|
+
if path.is_file():
|
|
9
|
+
return {
|
|
10
|
+
"type": "file",
|
|
11
|
+
"name": name,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
children = []
|
|
15
|
+
|
|
16
|
+
for item in sorted(path.iterdir(), key=lambda x: x.name.lower()):
|
|
17
|
+
if item.name in GLOBAL_IGNORE:
|
|
18
|
+
continue
|
|
19
|
+
|
|
20
|
+
if plugin and item.name in plugin.ignore:
|
|
21
|
+
continue
|
|
22
|
+
|
|
23
|
+
if item.name in GLOBAL_HIDDEN_FILES:
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
if plugin and item.name in plugin.hidden_files:
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
if item.name.endswith(".egg-info"):
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
if item.is_dir() and plugin and item.name in plugin.collapse:
|
|
33
|
+
children.append({
|
|
34
|
+
"type": "dir",
|
|
35
|
+
"name": item.name,
|
|
36
|
+
"collapsed": True,
|
|
37
|
+
})
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
children.append(build_tree(item, plugin))
|
|
41
|
+
|
|
42
|
+
if item.name == "codemap.md":
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
"type": "dir",
|
|
47
|
+
"name": name,
|
|
48
|
+
"children": children,
|
|
49
|
+
}
|
codemap/writer.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def build_markdown(tree_text: str, root_path, plugin_name: str) -> str:
|
|
5
|
+
header = (
|
|
6
|
+
"# CodeMap\n\n"
|
|
7
|
+
f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
|
8
|
+
f"Root: {root_path}\n"
|
|
9
|
+
f"Framework: {plugin_name}\n\n"
|
|
10
|
+
"```\n"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
footer = "\n```\n"
|
|
14
|
+
|
|
15
|
+
return header + tree_text + footer
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def write_file(output_path, content: str):
|
|
19
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
20
|
+
f.write(content)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
codemap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
codemap/cli.py,sha256=8VcVKxe6uZWjj7ddmVCEvIY62La0YdFSPM7GM0posvQ,1927
|
|
3
|
+
codemap/constants.py,sha256=ELbCJ4ORJQI4oentnP4K7rrSFsbpkKiNkOxRo9V28s8,149
|
|
4
|
+
codemap/formatter.py,sha256=DvZjlAmwAwV707DS3x5YVMqp1GkEJokumm7Mn2E18Pw,627
|
|
5
|
+
codemap/root.py,sha256=CF9EmkojPcDT502YI1OcYcqFeS9UPykANP6Y6KNPTzA,757
|
|
6
|
+
codemap/scanner.py,sha256=BskhRyrjn0Q4EzWRMbH0mTrjldTqmQbv35HPyKPgClQ,1183
|
|
7
|
+
codemap/writer.py,sha256=0zAFx0enFuZJXZ7Ha4WKHFeVDUHcsoyLzuI0aYEXtWE,515
|
|
8
|
+
codemap/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
codemap/plugins/base.py,sha256=PxLByzaQag3ZiyRuFz_yJBv_iMqsECUHys5H0FcjC9c,541
|
|
10
|
+
codemap/plugins/javascript.py,sha256=tapB1xF4Q_yVe5qK5NpExTvPGksYJuX5nJNjXBTeCvE,337
|
|
11
|
+
codemap/plugins/python.py,sha256=SYT5ygo4BlosTyVuEyYgsR0cQPUfJ36xpU6mU0VtS5s,420
|
|
12
|
+
codemap_cli-0.1.0.dist-info/METADATA,sha256=SHhXAaE1FYvactX6qQ-4_AZmbkbQKFwam1H3vMpOeos,201
|
|
13
|
+
codemap_cli-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
14
|
+
codemap_cli-0.1.0.dist-info/entry_points.txt,sha256=K4QNwvaqaYXHZDLYvzb6lAj0O0Q--84HV5kMbhXhtaE,44
|
|
15
|
+
codemap_cli-0.1.0.dist-info/top_level.txt,sha256=y3cIjxmjvwNiAspXYGejsYO5jxkQ4UXIEgLe3iHpL8g,8
|
|
16
|
+
codemap_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
codemap
|