dekko 0.7.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.
- dekko/__init__.py +1 -0
- dekko/_plugin/.claude-plugin/marketplace.json +18 -0
- dekko/_plugin/.claude-plugin/plugin.json +8 -0
- dekko/_plugin/.mcp.json +9 -0
- dekko/_plugin/commands/map.md +29 -0
- dekko/cache.py +167 -0
- dekko/cli.py +1086 -0
- dekko/contextpack.py +283 -0
- dekko/diff.py +253 -0
- dekko/export.py +134 -0
- dekko/extractor.py +798 -0
- dekko/extractor_generic.py +239 -0
- dekko/languages.py +526 -0
- dekko/mapfile.py +216 -0
- dekko/model.py +127 -0
- dekko/query.py +222 -0
- dekko/render_json.py +55 -0
- dekko/render_md.py +207 -0
- dekko/resolver.py +227 -0
- dekko/server.py +496 -0
- dekko/stats.py +117 -0
- dekko/trace.py +197 -0
- dekko/unused.py +172 -0
- dekko/walker.py +194 -0
- dekko-0.7.0.dist-info/METADATA +238 -0
- dekko-0.7.0.dist-info/RECORD +28 -0
- dekko-0.7.0.dist-info/WHEEL +4 -0
- dekko-0.7.0.dist-info/entry_points.txt +2 -0
dekko/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""dekko: static code map generator (MAP.md + map.json)."""
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dekko",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Austin Ahlijian",
|
|
5
|
+
"email": "aahlijia@gmail.com"
|
|
6
|
+
},
|
|
7
|
+
"metadata": {
|
|
8
|
+
"description": "dekko — static code map generator for Claude Code",
|
|
9
|
+
"version": "0.7.0"
|
|
10
|
+
},
|
|
11
|
+
"plugins": [
|
|
12
|
+
{
|
|
13
|
+
"name": "dekko",
|
|
14
|
+
"description": "Adds /map: maps every file, function, type, and call edge without spending model tokens on parsing.",
|
|
15
|
+
"source": "./"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dekko",
|
|
3
|
+
"description": "Adds a /map command that programmatically scans the repository: every code file, function, parameter, type, return type, and the call relationships between functions. Writes MAP.md and map.json without spending model tokens on parsing.",
|
|
4
|
+
"version": "0.7.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Austin Ahlijian"
|
|
7
|
+
}
|
|
8
|
+
}
|
dekko/_plugin/.mcp.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate MAP.md — a relational map of every file, function, signature, and call in the repo
|
|
3
|
+
argument-hint: "[subpath]"
|
|
4
|
+
allowed-tools: Bash(dekko:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Code map results
|
|
8
|
+
|
|
9
|
+
The mapping tool has already run; its summary is below.
|
|
10
|
+
|
|
11
|
+
!`dekko map --if-stale . $ARGUMENTS`
|
|
12
|
+
|
|
13
|
+
## Your task
|
|
14
|
+
|
|
15
|
+
The repository map was generated programmatically by the tool above — do
|
|
16
|
+
NOT parse any source files yourself.
|
|
17
|
+
|
|
18
|
+
1. If the summary above shows an error, explain the problem to the user
|
|
19
|
+
and how to fix it. In particular, if the `dekko` command was not
|
|
20
|
+
found, tell them to install it with `pip install dekko` (or
|
|
21
|
+
`uv tool install dekko`). Otherwise:
|
|
22
|
+
2. Relay the summary to the user: how many files were mapped, in which
|
|
23
|
+
languages, how many functions and call relationships were found, and
|
|
24
|
+
anything skipped. If the summary says "map fresh", tell the user the
|
|
25
|
+
existing MAP.md is already up to date and nothing was regenerated.
|
|
26
|
+
3. Tell the user the map was written to `MAP.md` (human-readable) and
|
|
27
|
+
`map.json` (machine-readable) at the repo root.
|
|
28
|
+
4. Do not read MAP.md back into context unless the user asks a question
|
|
29
|
+
that requires it.
|
dekko/cache.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Per-file extraction cache stored under ``.dekko/``.
|
|
2
|
+
|
|
3
|
+
Parsing every file with tree-sitter dominates a map run. The cache
|
|
4
|
+
keys each file's extracted ``FileMap`` on the same content hash used
|
|
5
|
+
for provenance: on the next run, files whose hash is unchanged reuse
|
|
6
|
+
their cached ``FileMap`` and skip re-parsing. Resolution still runs
|
|
7
|
+
repo-wide (it is cheap relative to parsing).
|
|
8
|
+
|
|
9
|
+
The cache lives in ``<root>/.dekko/cache.json``. On first creation the
|
|
10
|
+
directory is made self-ignoring (``.dekko/.gitignore`` of ``*``) and
|
|
11
|
+
``.dekko/`` is appended to the repository ``.gitignore``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from dataclasses import asdict
|
|
16
|
+
from importlib.metadata import version as _pkg_version
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from .mapfile import _file_hash, _symbol_from_dict
|
|
20
|
+
from .model import FileMap, Import, RawCall
|
|
21
|
+
|
|
22
|
+
CACHE_VERSION = 1
|
|
23
|
+
CACHE_DIR = ".dekko"
|
|
24
|
+
CACHE_FILE = "cache.json"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _tool_version() -> str:
|
|
28
|
+
"""Current dekko version, used to invalidate stale extractions."""
|
|
29
|
+
return _pkg_version("dekko")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _filemap_to_dict(fm: FileMap) -> dict:
|
|
33
|
+
"""Serialize a ``FileMap`` for the cache."""
|
|
34
|
+
return asdict(fm)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _filemap_from_dict(d: dict) -> FileMap:
|
|
38
|
+
"""Rebuild a ``FileMap`` from its cached dict."""
|
|
39
|
+
return FileMap(
|
|
40
|
+
path=d["path"],
|
|
41
|
+
language=d["language"],
|
|
42
|
+
symbols=[_symbol_from_dict(s) for s in d.get("symbols", [])],
|
|
43
|
+
calls=[RawCall(**c) for c in d.get("calls", [])],
|
|
44
|
+
imports=[Import(**i) for i in d.get("imports", [])],
|
|
45
|
+
error=d.get("error"),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class IncrementalCache:
|
|
50
|
+
"""A read-old / write-new view over the per-file extraction cache.
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
entries: Cache entries to persist after the run — populated by
|
|
54
|
+
both reused and freshly extracted files.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, old: dict[str, dict]) -> None:
|
|
58
|
+
"""Initialize with the entries loaded from a prior run.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
old: Previous ``path -> {"hash", "file"}`` entries, or an
|
|
62
|
+
empty dict to force every file to re-parse.
|
|
63
|
+
"""
|
|
64
|
+
self._old = old
|
|
65
|
+
self.entries: dict[str, dict] = {}
|
|
66
|
+
|
|
67
|
+
def reuse(self, root: Path, rel: str) -> FileMap | None:
|
|
68
|
+
"""Return the cached ``FileMap`` for an unchanged file.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
root: Repository root.
|
|
72
|
+
rel: Repo-relative path of the file.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
The cached ``FileMap`` when a prior entry's hash matches the
|
|
76
|
+
current file, else ``None``.
|
|
77
|
+
"""
|
|
78
|
+
entry = self._old.get(rel)
|
|
79
|
+
if entry is None or entry.get("hash") != _file_hash(root / rel):
|
|
80
|
+
return None
|
|
81
|
+
self.entries[rel] = entry
|
|
82
|
+
return _filemap_from_dict(entry["file"])
|
|
83
|
+
|
|
84
|
+
def store(self, root: Path, rel: str, fm: FileMap) -> None:
|
|
85
|
+
"""Record a freshly extracted ``FileMap`` for persistence."""
|
|
86
|
+
self.entries[rel] = {
|
|
87
|
+
"hash": _file_hash(root / rel),
|
|
88
|
+
"file": _filemap_to_dict(fm),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def load(root: Path) -> dict[str, dict]:
|
|
93
|
+
"""Load the prior cache entries for a repository.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
root: Repository root.
|
|
97
|
+
|
|
98
|
+
A cache written by a different dekko version is discarded, so
|
|
99
|
+
extractor changes always take effect on the next run without a
|
|
100
|
+
manual ``--full``.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
``path -> entry`` mapping, or an empty dict when no usable
|
|
104
|
+
cache exists.
|
|
105
|
+
"""
|
|
106
|
+
path = root / CACHE_DIR / CACHE_FILE
|
|
107
|
+
try:
|
|
108
|
+
doc = json.loads(path.read_text())
|
|
109
|
+
except (OSError, json.JSONDecodeError):
|
|
110
|
+
return {}
|
|
111
|
+
if doc.get("version") != CACHE_VERSION:
|
|
112
|
+
return {}
|
|
113
|
+
if doc.get("tool_version") != _tool_version():
|
|
114
|
+
return {}
|
|
115
|
+
files = doc.get("files")
|
|
116
|
+
return files if isinstance(files, dict) else {}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def save(root: Path, cache: IncrementalCache) -> None:
|
|
120
|
+
"""Persist a cache and ensure ``.dekko/`` is git-ignored.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
root: Repository root.
|
|
124
|
+
cache: The cache whose ``entries`` should be written.
|
|
125
|
+
"""
|
|
126
|
+
cache_dir = root / CACHE_DIR
|
|
127
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
_ensure_ignored(root, cache_dir)
|
|
129
|
+
doc = {
|
|
130
|
+
"version": CACHE_VERSION,
|
|
131
|
+
"tool_version": _tool_version(),
|
|
132
|
+
"files": cache.entries,
|
|
133
|
+
}
|
|
134
|
+
(cache_dir / CACHE_FILE).write_text(json.dumps(doc) + "\n")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def ensure_dir(root: Path) -> Path:
|
|
138
|
+
"""Create ``.dekko/`` and set up gitignore entries.
|
|
139
|
+
|
|
140
|
+
Idempotent — safe to call on every map run. Returns the cache dir.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
root: Repository root.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Path to the ``.dekko/`` directory.
|
|
147
|
+
"""
|
|
148
|
+
cache_dir = root / CACHE_DIR
|
|
149
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
_ensure_ignored(root, cache_dir)
|
|
151
|
+
return cache_dir
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _ensure_ignored(root: Path, cache_dir: Path) -> None:
|
|
155
|
+
"""Make ``.dekko/`` self-ignoring and ignored by the repo."""
|
|
156
|
+
inner = cache_dir / ".gitignore"
|
|
157
|
+
if not inner.exists():
|
|
158
|
+
inner.write_text("*\n")
|
|
159
|
+
|
|
160
|
+
gitignore = root / ".gitignore"
|
|
161
|
+
entry = f"{CACHE_DIR}/"
|
|
162
|
+
text = gitignore.read_text() if gitignore.exists() else ""
|
|
163
|
+
if entry in text.splitlines():
|
|
164
|
+
return
|
|
165
|
+
if text and not text.endswith("\n"):
|
|
166
|
+
text += "\n"
|
|
167
|
+
gitignore.write_text(text + entry + "\n")
|