klapcontext 0.2.0__tar.gz → 0.2.2__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.
- {klapcontext-0.2.0/src/klapcontext.egg-info → klapcontext-0.2.2}/PKG-INFO +1 -1
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/__init__.py +1 -1
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/context_builder.py +1 -1
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/detector.py +23 -2
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/frameworks/laravel.py +4 -1
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/portal.py +5 -5
- {klapcontext-0.2.0 → klapcontext-0.2.2/src/klapcontext.egg-info}/PKG-INFO +1 -1
- {klapcontext-0.2.0 → klapcontext-0.2.2}/tests/test_klapcontext.py +18 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/LICENSE +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/README.md +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/pyproject.toml +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/setup.cfg +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/__init__.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/capabilities.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/compiler.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/impact.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/intents.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/models.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/planner.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/verification.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent/workflows.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/agent_context.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/cli.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/evidence.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/frameworks/__init__.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/frameworks/base.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/frameworks/fastapi.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/frameworks/generic_php.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/frameworks/node.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/git.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/graphify.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/__init__.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/base.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/code_intelligence.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/graphify.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/javascript_intelligence.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/python_intelligence.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/semantic.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/system.py +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext.egg-info/SOURCES.txt +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext.egg-info/dependency_links.txt +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext.egg-info/entry_points.txt +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext.egg-info/requires.txt +0 -0
- {klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext.egg-info/top_level.txt +0 -0
|
@@ -86,7 +86,7 @@ def _empty_raw() -> dict:
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
def _semantic(root: Path, stack: dict) -> tuple[SemanticModel, dict]:
|
|
89
|
-
if "Laravel" in stack["frameworks"]:
|
|
89
|
+
if "Laravel" in stack["frameworks"] or "Lumen" in stack["frameworks"]:
|
|
90
90
|
model = LaravelAdapter().analyze(root)
|
|
91
91
|
return model, model.metadata["raw"]
|
|
92
92
|
if "FastAPI" in stack["frameworks"]:
|
|
@@ -8,16 +8,20 @@ from .evidence import Evidence
|
|
|
8
8
|
|
|
9
9
|
def detect_project(root: Path) -> tuple[dict, list[Evidence]]:
|
|
10
10
|
evidence: list[Evidence] = []
|
|
11
|
-
stack = {"languages": [], "frameworks": [], "databases": [], "runtime": [], "infrastructure": []}
|
|
11
|
+
stack = {"languages": [], "frameworks": [], "databases": [], "runtime": [], "infrastructure": [], "package_managers": [], "project_type": "unknown"}
|
|
12
12
|
def add(bucket: str, value: str, path: str, reason: str) -> None:
|
|
13
13
|
if value not in stack[bucket]: stack[bucket].append(value)
|
|
14
14
|
evidence.append(Evidence("file", path, reason, "CONFIRMED", 1.0))
|
|
15
15
|
composer = root / "composer.json"
|
|
16
16
|
if composer.exists():
|
|
17
17
|
add("languages", "PHP", "composer.json", "Composer manifest detected")
|
|
18
|
+
stack["runtime"].append("PHP"); stack["package_managers"].append("composer")
|
|
18
19
|
try: deps = {**json.loads(composer.read_text(encoding="utf-8")).get("require", {}), **json.loads(composer.read_text(encoding="utf-8")).get("require-dev", {})}
|
|
19
20
|
except json.JSONDecodeError: deps = {}
|
|
20
21
|
if "laravel/framework" in deps: add("frameworks", "Laravel", "composer.json", "laravel/framework dependency detected")
|
|
22
|
+
if "laravel/lumen-framework" in deps:
|
|
23
|
+
add("frameworks", "Lumen", "composer.json", "laravel/lumen-framework dependency detected")
|
|
24
|
+
stack["project_type"] = "backend-api"
|
|
21
25
|
elif any(root.rglob("*.php")):
|
|
22
26
|
# A standalone PHP repository has no Composer manifest to identify it,
|
|
23
27
|
# but language analysis can still provide a useful generic model.
|
|
@@ -26,16 +30,33 @@ def detect_project(root: Path) -> tuple[dict, list[Evidence]]:
|
|
|
26
30
|
package = root / "package.json"
|
|
27
31
|
if package.exists():
|
|
28
32
|
add("languages", "JavaScript", "package.json", "npm manifest detected")
|
|
33
|
+
stack["runtime"].append("Node.js"); stack["package_managers"].append("npm")
|
|
29
34
|
try: deps = {**json.loads(package.read_text(encoding="utf-8")).get("dependencies", {}), **json.loads(package.read_text(encoding="utf-8")).get("devDependencies", {})}
|
|
30
35
|
except json.JSONDecodeError: deps = {}
|
|
31
36
|
for name, label in (("next", "Next.js"), ("vue", "Vue"), ("react", "React"), ("express", "Express"), ("@nestjs/core", "NestJS")):
|
|
32
37
|
if name in deps: add("frameworks", label, "package.json", f"{name} dependency detected")
|
|
38
|
+
if any(root.rglob("*.ts")) or any(root.rglob("*.tsx")): add("languages", "TypeScript", "package.json", "TypeScript source detected")
|
|
33
39
|
if (root / "pyproject.toml").exists() or (root / "requirements.txt").exists():
|
|
34
40
|
python_manifest = root / "pyproject.toml" if (root / "pyproject.toml").exists() else root / "requirements.txt"
|
|
35
41
|
add("languages", "Python", python_manifest.name, "Python manifest detected")
|
|
42
|
+
stack["runtime"].append("Python"); stack["package_managers"].append("pip")
|
|
36
43
|
if "fastapi" in python_manifest.read_text(encoding="utf-8", errors="ignore").casefold(): add("frameworks", "FastAPI", python_manifest.name, "FastAPI dependency detected")
|
|
37
44
|
if (root / "manage.py").exists(): add("frameworks", "Django", "manage.py", "Django entry point detected")
|
|
38
|
-
if list(root.glob("*.csproj")):
|
|
45
|
+
if list(root.glob("*.csproj")):
|
|
46
|
+
add("languages", "C#", next(root.glob("*.csproj")).name, ".NET project detected"); stack["runtime"].append(".NET"); stack["package_managers"].append("NuGet")
|
|
47
|
+
if (root / "Program.cs").exists(): add("frameworks", ".NET Web API", "Program.cs", "ASP.NET entry point detected")
|
|
48
|
+
if (root / "go.mod").exists():
|
|
49
|
+
add("languages", "Go", "go.mod", "Go module detected"); stack["runtime"].append("Go"); stack["package_managers"].append("go modules")
|
|
50
|
+
if (root / "Cargo.toml").exists():
|
|
51
|
+
add("languages", "Rust", "Cargo.toml", "Rust manifest detected"); stack["runtime"].append("Rust"); stack["package_managers"].append("cargo")
|
|
52
|
+
if (root / "pom.xml").exists() or (root / "build.gradle").exists() or (root / "build.gradle.kts").exists():
|
|
53
|
+
manifest = "pom.xml" if (root / "pom.xml").exists() else "build.gradle" if (root / "build.gradle").exists() else "build.gradle.kts"
|
|
54
|
+
add("languages", "Java", manifest, "Java build manifest detected"); stack["runtime"].append("JVM"); stack["package_managers"].append("maven" if manifest == "pom.xml" else "gradle")
|
|
55
|
+
# Keep generic analysis useful for small repositories without a manifest.
|
|
56
|
+
for suffix, language in (("*.py", "Python"), ("*.js", "JavaScript"), ("*.ts", "TypeScript"), ("*.java", "Java"), ("*.go", "Go"), ("*.rs", "Rust")):
|
|
57
|
+
if language not in stack["languages"] and any(root.rglob(suffix)):
|
|
58
|
+
first = next(root.rglob(suffix)); add("languages", language, first.relative_to(root).as_posix(), f"{language} source file detected")
|
|
59
|
+
if any(name in stack["frameworks"] for name in ("Laravel", "Lumen", "FastAPI", "Express", "NestJS", ".NET Web API")): stack["project_type"] = "backend-api"
|
|
39
60
|
if (root / "Dockerfile").exists(): add("infrastructure", "Docker", "Dockerfile", "Dockerfile detected")
|
|
40
61
|
if list(root.glob("docker-compose*")) or list(root.glob("compose*")): add("infrastructure", "Docker Compose", "docker-compose", "Compose configuration detected")
|
|
41
62
|
if (root / ".github" / "workflows").exists(): add("infrastructure", "GitHub Actions", ".github/workflows", "GitHub Actions workflows detected")
|
|
@@ -61,7 +61,7 @@ def _routes(root: Path) -> tuple[list[dict], list[dict]]:
|
|
|
61
61
|
directory = root / "routes"
|
|
62
62
|
if not directory.is_dir():
|
|
63
63
|
return routes, scheduled
|
|
64
|
-
route_pattern = re.compile(r"Route
|
|
64
|
+
route_pattern = re.compile(r"(?:Route|\$app|\$router)(?:::|->)(get|post|put|patch|delete|options|any)\s*\(\s*['\"]([^'\"]+)['\"]\s*,\s*(.+?)\);", re.S | re.I)
|
|
65
65
|
resource_pattern = re.compile(r"Route::(?:api)?resource\s*\(\s*['\"]([^'\"]+)['\"]\s*,\s*([A-Za-z_]\w*(?:\\[A-Za-z_]\w*)*)::class", re.I)
|
|
66
66
|
schedule_pattern = re.compile(r"Schedule::(?:command|job)\s*\(\s*['\"]([^'\"]+)['\"]\s*\).*?->([A-Za-z]\w*)\s*\(", re.S)
|
|
67
67
|
for file in directory.glob("*.php"):
|
|
@@ -69,6 +69,9 @@ def _routes(root: Path) -> tuple[list[dict], list[dict]]:
|
|
|
69
69
|
for match in route_pattern.finditer(text):
|
|
70
70
|
method, uri, target = match.groups()
|
|
71
71
|
handler = _symbol(target)
|
|
72
|
+
if not handler:
|
|
73
|
+
callback = re.search(r"['\"]([A-Za-z_]\w*)@([A-Za-z_]\w*)['\"]", target)
|
|
74
|
+
handler = f"{callback.group(1)}::{callback.group(2)}" if callback else None
|
|
72
75
|
name = f"{method.upper()} /{uri.lstrip('/')}"
|
|
73
76
|
routes.append({"type": "http", "method": method.upper(), "uri": "/" + uri.lstrip("/"), "name": name, "path": str(file.relative_to(root)), "source": str(file.relative_to(root)), "line": text.count("\n", 0, match.start()) + 1, "target": handler, "handler": handler, "middleware": [], "status": "CONFIRMED", "evidence": _e(root, file, match.start(), "Declaración de ruta Laravel", symbol=name)})
|
|
74
77
|
for match in resource_pattern.finditer(text):
|
|
@@ -43,7 +43,7 @@ def system_map(interactions: dict) -> str:
|
|
|
43
43
|
if edge.get("source") in points and edge.get("target") in points
|
|
44
44
|
)
|
|
45
45
|
boxes = "".join(
|
|
46
|
-
f"<g class='map-node'><rect x='{points[node['name']][0] - 65}' y='{points[node['name']][1] - 24}' width='130' height='48' rx='8'/>"
|
|
46
|
+
f"<g class='map-node' tabindex='0' role='button' data-node='{e(node['name'])}' data-kind='{e(node.get('type', 'componente'))}'><rect x='{points[node['name']][0] - 65}' y='{points[node['name']][1] - 24}' width='130' height='48' rx='8'/>"
|
|
47
47
|
f"<text x='{points[node['name']][0]}' y='{points[node['name']][1] - 2}'>{e(node['name'])[:18]}</text>"
|
|
48
48
|
f"<text class='map-kind' x='{points[node['name']][0]}' y='{points[node['name']][1] + 13}'>{e(node.get('type', 'componente'))[:18]}</text></g>"
|
|
49
49
|
for node in nodes
|
|
@@ -113,10 +113,10 @@ def render(context: dict, agent_text: str, graphify_files: list[str]) -> str:
|
|
|
113
113
|
</section>
|
|
114
114
|
<section class='map-card' aria-label='Mapa de interacción del sistema'><header><div><span class='icon'>⌘</span><div><h2>Mapa del sistema</h2><p>Componentes, relaciones y puntos de entrada en una sola lectura.</p></div></div><button class='outline' data-go='tecnico'>Explorar evidencia <span>→</span></button></header>{system_map(system.get('system_interactions', {}))}</section>
|
|
115
115
|
<section class='quick-grid'>
|
|
116
|
-
<article><b>{len(points)}</b><span>Puntos de entrada</span><small>CLI, HTTP, archivos o scheduler</small></article>
|
|
117
|
-
<article><b>{len(flows)}</b><span>Flujos principales</span><small>Procesos y secuencias detectadas</small></article>
|
|
118
|
-
<article><b>{len(evidence)}</b><span>Hechos confirmados</span><small>Con respaldo en el código</small></article>
|
|
119
|
-
<article class='attention'><b>{len(unknowns)}</b><span>
|
|
116
|
+
<article data-go='entradas'><b>{len(points)}</b><span>Puntos de entrada</span><small>CLI, HTTP, archivos o scheduler</small></article>
|
|
117
|
+
<article data-go='flujos'><b>{len(flows)}</b><span>Flujos principales</span><small>Procesos y secuencias detectadas</small></article>
|
|
118
|
+
<article data-go='evidencia'><b>{len(evidence)}</b><span>Hechos confirmados</span><small>Con respaldo en el código</small></article>
|
|
119
|
+
<article class='attention' data-go='evidencia'><b>{len(unknowns)}</b><span>Pendientes de validar</span><small>Aspectos con evidencia insuficiente</small></article>
|
|
120
120
|
</section>
|
|
121
121
|
<section class='dashboard-bottom'><div class='compact-panel'><header><h2>Cómo se ejecuta</h2><button class='text-button' data-go='entradas'>Ver entradas →</button></header><div class='surfaces'>{surface_cards}</div></div>
|
|
122
122
|
<div class='compact-panel'><header><h2>Lectura recomendada</h2><button class='text-button' data-go='flujos'>Ver flujos →</button></header><ul class='start-list'>{start_rows}</ul></div></section>
|
|
@@ -37,6 +37,22 @@ def test_detector_collects_confirmed_evidence(tmp_path):
|
|
|
37
37
|
assert all(item.status == "CONFIRMED" for item in evidence)
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def test_detector_identifies_manifest_and_source_languages(tmp_path):
|
|
41
|
+
(tmp_path / "go.mod").write_text("module example")
|
|
42
|
+
(tmp_path / "src").mkdir(); (tmp_path / "src" / "main.ts").write_text("export {}")
|
|
43
|
+
stack, _ = detect_project(tmp_path)
|
|
44
|
+
assert {"Go", "TypeScript"} <= set(stack["languages"])
|
|
45
|
+
assert "go modules" in stack["package_managers"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_lumen_detection_and_route_adapter():
|
|
49
|
+
root = Path(__file__).parent / "fixtures" / "lumen_semantic"
|
|
50
|
+
stack, _ = detect_project(root)
|
|
51
|
+
assert "Lumen" in stack["frameworks"]
|
|
52
|
+
context = build(root, {"nodes": []})
|
|
53
|
+
assert context["system_model"]["routes"][0]["name"] == "POST /api/v1/call-vtex"
|
|
54
|
+
|
|
55
|
+
|
|
40
56
|
def test_portal_has_dashboard_and_real_views(tmp_path):
|
|
41
57
|
context = build(fixture_repo(tmp_path), {"nodes": [{"name": "UserController", "path": "app/Http/UserController.php", "type": "controller"}]})
|
|
42
58
|
page = render_portal(context, render_agent(context), ["graph.html"])
|
|
@@ -45,6 +61,8 @@ def test_portal_has_dashboard_and_real_views(tmp_path):
|
|
|
45
61
|
assert "data-view='flujos'" in page
|
|
46
62
|
assert "data-go='evidencia'" in page
|
|
47
63
|
assert "Copiar contexto" in page
|
|
64
|
+
assert "data-go='entradas'" in page
|
|
65
|
+
assert "data-node=" in page or "map-empty" in page
|
|
48
66
|
|
|
49
67
|
|
|
50
68
|
def test_exclude_klap_is_idempotent(tmp_path, monkeypatch):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{klapcontext-0.2.0 → klapcontext-0.2.2}/src/klapcontext/providers/javascript_intelligence.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|