mnemo-dev 0.5.1__tar.gz → 0.5.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.
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/PKG-INFO +1 -1
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/__init__.py +1 -1
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/engine.py +1 -1
- mnemo_dev-0.5.2/mnemo/ui_static/index.html +207 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo_dev.egg-info/PKG-INFO +1 -1
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo_dev.egg-info/SOURCES.txt +1 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/pyproject.toml +4 -1
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/LICENSE +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/README.md +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/analyzers/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/api_discovery/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/breaking/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/chunking.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/cli.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/clients.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/code_review/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/commit_gen/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/commit_gen/mining.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/config.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/conventions/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/corrections/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/dead_code/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/doctor.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/drift/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/embeddings/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/embeddings/dense.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/cache.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/clustering.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/db.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/freshness.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/memory_graph.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/pipeline.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/schema.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/scope.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/engine/workers.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/enrichment.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/errors/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/health/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/hooks/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/hooks/extractor.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/incidents/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/init.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/knowledge/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/mcp_server.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/_shared.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/episodes.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/hierarchy.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/indexing.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/lessons.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/linking.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/retention.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/search.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/services.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/slots.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/store.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/memory/temporal.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/onboarding/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/persistence/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/persistence/export.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/persistence/snapshot.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/plan/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/pr_gen/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/prompts/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/regressions/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/repo_map/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/repo_map/identity.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/repo_map/parsers.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/repo_map/scanner.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/retrieval.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/security/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/serve.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/sprint/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/storage.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/team_graph/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/test_intel/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tool_registry.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/code.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/git.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/graph.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/memory.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/meta.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/observe.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/plan.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/safety.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/search.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/tools/team.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/types.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/audit.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/circuit_breaker.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/dedup.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/logger.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/metrics.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/observations.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/privacy.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/stemmer.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/utils/synonyms.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/velocity/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo/workspace/__init__.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo_dev.egg-info/dependency_links.txt +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo_dev.egg-info/entry_points.txt +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo_dev.egg-info/requires.txt +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/mnemo_dev.egg-info/top_level.txt +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/setup.cfg +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_audit.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_circuit_breaker.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_clients.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_code_quality_tools.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_engine_db.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_engine_pipeline.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_enrichment.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_export.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_full_cycle.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_init.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_mcp_integration.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_memory.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_memory_lifecycle.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_metrics.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_plan_features.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_privacy.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_search_quality.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_secondary_collections.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_self_maintenance.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_serve.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_slots.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_snapshot.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_stemmer.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_storage.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_synonyms.py +0 -0
- {mnemo_dev-0.5.1 → mnemo_dev-0.5.2}/tests/test_tool_routing.py +0 -0
|
@@ -202,7 +202,7 @@ def _traverse_callees(conn, name: str, max_depth: int) -> list[tuple[str, int, s
|
|
|
202
202
|
return results
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
@tool("
|
|
205
|
+
@tool("mnemo_find",
|
|
206
206
|
"Search the code graph for symbols by name pattern. Returns matching classes, functions, and methods.",
|
|
207
207
|
properties={
|
|
208
208
|
"query": {"type": "string", "description": "Search query (name or pattern to find)"},
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Mnemo — Code Intelligence</title>
|
|
7
|
+
<script src="https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js"></script>
|
|
8
|
+
<style>
|
|
9
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
|
+
:root { --bg: #0a0e14; --surface: #111820; --border: #1e2a36; --accent: #00ffaa; --accent2: #00ccff; --text: #f0f4f8; --muted: #7a8a9a; --red: #ff6b8a; --blue: #64d2ff; --purple: #bf7fff; --amber: #ffcc00; --pink: #ff6bcc; }
|
|
11
|
+
body { background: var(--bg); color: var(--text); font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; font-size: 14px; overflow: hidden; }
|
|
12
|
+
#app { display: grid; grid-template-columns: 280px 1fr; height: 100vh; }
|
|
13
|
+
.sidebar { background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; }
|
|
14
|
+
.sidebar-head { padding: 20px; border-bottom: 1px solid var(--border); }
|
|
15
|
+
.sidebar-head h1 { font-size: 20px; font-weight: 800; background: linear-gradient(135deg, #00ffaa, #00ccff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -0.5px; }
|
|
16
|
+
.sidebar-head .stats { font-size: 11px; color: var(--muted); margin-top: 4px; }
|
|
17
|
+
.sidebar-nav { display: flex; border-bottom: 1px solid var(--border); }
|
|
18
|
+
.sidebar-nav button { flex: 1; padding: 12px 4px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; font-weight: 600; transition: all 0.15s; }
|
|
19
|
+
.sidebar-nav button.active { color: var(--accent); border-bottom-color: var(--accent); }
|
|
20
|
+
.sidebar-nav button:hover { color: var(--text); }
|
|
21
|
+
.sidebar-list { flex: 1; overflow-y: auto; padding: 4px 0; }
|
|
22
|
+
.sidebar-item { display: flex; align-items: center; gap: 10px; padding: 10px 20px; cursor: pointer; border-left: 3px solid transparent; transition: all 0.1s; }
|
|
23
|
+
.sidebar-item:hover { background: rgba(0,255,170,0.04); border-left-color: var(--accent); }
|
|
24
|
+
.sidebar-item .icon { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; box-shadow: 0 0 6px currentColor; }
|
|
25
|
+
.sidebar-item .label { font-size: 12px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 400; }
|
|
26
|
+
.sidebar-item .badge { font-size: 10px; color: var(--muted); font-weight: 500; }
|
|
27
|
+
.main { display: flex; flex-direction: column; position: relative; }
|
|
28
|
+
.toolbar { padding: 14px 20px; border-bottom: 1px solid var(--border); background: var(--surface); display: flex; align-items: center; gap: 12px; z-index: 10; }
|
|
29
|
+
.search { width: 300px; padding: 9px 14px; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: 13px; outline: none; transition: border 0.15s; }
|
|
30
|
+
.search:focus { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(0,255,170,0.1); }
|
|
31
|
+
.pills { display: flex; gap: 5px; margin-left: 16px; }
|
|
32
|
+
.pill { padding: 4px 10px; border-radius: 14px; font-size: 11px; cursor: pointer; border: 1px solid var(--border); color: var(--muted); background: none; font-weight: 500; transition: all 0.15s; }
|
|
33
|
+
.pill.on { background: rgba(0,255,170,0.08); color: var(--accent); border-color: rgba(0,255,170,0.3); }
|
|
34
|
+
.pill:hover { border-color: var(--muted); }
|
|
35
|
+
.graph-wrap { flex: 1; position: relative; }
|
|
36
|
+
#graph { position: absolute; inset: 0; }
|
|
37
|
+
.legend { position: absolute; bottom: 16px; right: 16px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 10px 16px; display: flex; gap: 14px; font-size: 11px; color: var(--muted); z-index: 5; backdrop-filter: blur(8px); }
|
|
38
|
+
.legend span { display: flex; align-items: center; gap: 5px; font-weight: 500; }
|
|
39
|
+
.legend i { width: 10px; height: 10px; border-radius: 50%; display: inline-block; box-shadow: 0 0 4px currentColor; }
|
|
40
|
+
.detail { position: absolute; top: 16px; right: 16px; width: 300px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 18px; max-height: 65vh; overflow-y: auto; display: none; z-index: 10; backdrop-filter: blur(8px); }
|
|
41
|
+
.detail h3 { color: var(--accent); font-size: 15px; font-weight: 700; margin-bottom: 8px; }
|
|
42
|
+
.detail .meta { color: var(--muted); font-size: 11px; margin-bottom: 8px; }
|
|
43
|
+
.detail .meth { color: var(--blue); font-size: 11px; padding: 2px 0; font-family: 'JetBrains Mono', monospace; }
|
|
44
|
+
.detail .close { position: absolute; top: 10px; right: 14px; background: none; border: none; color: var(--muted); cursor: pointer; font-size: 16px; }
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<div id="app">
|
|
49
|
+
<div class="sidebar">
|
|
50
|
+
<div class="sidebar-head"><h1>Mnemo</h1><div class="stats" id="stats"></div></div>
|
|
51
|
+
<div class="sidebar-nav">
|
|
52
|
+
<button class="active" onclick="setTab('clusters')">Clusters</button>
|
|
53
|
+
<button onclick="setTab('classes')">Classes</button>
|
|
54
|
+
<button onclick="setTab('memory')">Memory</button>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="sidebar-list" id="list"></div>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="main">
|
|
59
|
+
<div class="toolbar">
|
|
60
|
+
<input class="search" id="search" placeholder="Search..." />
|
|
61
|
+
<div class="pills" id="pills"></div>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="graph-wrap">
|
|
64
|
+
<div id="graph"></div>
|
|
65
|
+
<div class="legend">
|
|
66
|
+
<span><i style="background:var(--red)"></i>Class</span>
|
|
67
|
+
<span><i style="background:var(--blue)"></i>Method</span>
|
|
68
|
+
<span><i style="background:var(--purple);border-radius:2px"></i>Cluster</span>
|
|
69
|
+
<span><i style="background:var(--pink)"></i>Memory</span>
|
|
70
|
+
<span><i style="background:var(--accent)"></i>Project</span>
|
|
71
|
+
<span><i style="background:#6b7280;border-radius:2px"></i>File</span>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="detail" id="detail">
|
|
74
|
+
<button class="close" onclick="this.parentElement.style.display='none'">×</button>
|
|
75
|
+
<div id="detail-body"></div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<script>
|
|
81
|
+
let network, graphData, allNodes=[], tab='clusters';
|
|
82
|
+
const show = { class:true, community:true, memory:true, project:true, function:true, method:true, file:true };
|
|
83
|
+
const colors = { project:'#58d68d', class:'#f87171', function:'#7dd3fc', community:'#c084fc', memory:'#f472b6', decision:'#fbbf24', method:'#93c5fd', file:'#6b7280' };
|
|
84
|
+
const nodeSize = { project:18, class:11, function:7, community:18, memory:13, decision:13, method:4, file:5 };
|
|
85
|
+
const shapes = { project:'diamond', class:'dot', function:'triangle', community:'square', memory:'star', decision:'star', method:'dot', file:'square' };
|
|
86
|
+
|
|
87
|
+
async function init() {
|
|
88
|
+
const [stats, gd, memory, comms] = await Promise.all([
|
|
89
|
+
fetch('/api/stats').then(r=>r.json()),
|
|
90
|
+
fetch('/api/graph').then(r=>r.json()),
|
|
91
|
+
fetch('/api/memory').then(r=>r.json()),
|
|
92
|
+
fetch('/api/communities').then(r=>r.json()),
|
|
93
|
+
]);
|
|
94
|
+
document.getElementById('stats').textContent = `${stats.classes}C · ${stats.communities}Cl`;
|
|
95
|
+
graphData = gd; allNodes = gd.nodes;
|
|
96
|
+
window._mem = memory; window._comms = comms;
|
|
97
|
+
renderPills(); renderList(); buildGraph();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function renderPills() {
|
|
101
|
+
document.getElementById('pills').innerHTML = Object.entries(show).map(([k,v]) =>
|
|
102
|
+
`<button class="pill ${v?'on':''}" onclick="show['${k}']=!show['${k}'];renderPills();buildGraph()">${k}</button>`
|
|
103
|
+
).join('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function buildGraph() {
|
|
107
|
+
const visible = graphData.nodes.filter(n => show[n.type] !== false);
|
|
108
|
+
const ids = new Set(visible.map(n => n.id));
|
|
109
|
+
const visEdges = graphData.edges.filter(e => ids.has(e.source) && ids.has(e.target));
|
|
110
|
+
|
|
111
|
+
const nodes = new vis.DataSet(visible.map(n => ({
|
|
112
|
+
id: n.id,
|
|
113
|
+
label: nodeSize[n.type] >= 11 ? (n.name||'').substring(0,22) : '',
|
|
114
|
+
color: { background: colors[n.type], border: colors[n.type], highlight: { background: '#fff', border: colors[n.type] } },
|
|
115
|
+
size: nodeSize[n.type],
|
|
116
|
+
shape: shapes[n.type] || 'dot',
|
|
117
|
+
title: `[${n.type}] ${n.name||n.id}`,
|
|
118
|
+
font: { size: 9, color: '#8b949e', face: 'system-ui' },
|
|
119
|
+
})));
|
|
120
|
+
const edges = new vis.DataSet(visEdges.map((e,i) => ({
|
|
121
|
+
id: i, from: e.source, to: e.target,
|
|
122
|
+
color: { color: '#586069', highlight: '#8b949e' },
|
|
123
|
+
width: 0.6,
|
|
124
|
+
arrows: { to: { enabled: true, scaleFactor: 0.4 } },
|
|
125
|
+
smooth: { type: 'continuous', roundness: 0.3 },
|
|
126
|
+
})));
|
|
127
|
+
|
|
128
|
+
if (network) network.destroy();
|
|
129
|
+
network = new vis.Network(document.getElementById('graph'), { nodes, edges }, {
|
|
130
|
+
physics: { solver: 'forceAtlas2Based', forceAtlas2Based: { gravitationalConstant: -40, springLength: 80, damping: 0.5 }, stabilization: { iterations: 120 } },
|
|
131
|
+
interaction: { hover: true, tooltipDelay: 100, zoomView: true, dragView: true, multiselect: true },
|
|
132
|
+
layout: { improvedLayout: false },
|
|
133
|
+
});
|
|
134
|
+
network.on('click', p => {
|
|
135
|
+
if (p.nodes.length) { showDetail(p.nodes[0]); }
|
|
136
|
+
else if (p.edges.length) {
|
|
137
|
+
const edge = edges.get(p.edges[0]);
|
|
138
|
+
const src = allNodes.find(n => n.id === edge.from);
|
|
139
|
+
const tgt = allNodes.find(n => n.id === edge.to);
|
|
140
|
+
if (src && tgt) {
|
|
141
|
+
document.getElementById('detail-body').innerHTML = `<h3>Edge</h3><div class="meta">${src.type}: <b>${src.name||src.id}</b></div><div style="padding:4px 0;color:var(--accent)">→</div><div class="meta">${tgt.type}: <b>${tgt.name||tgt.id}</b></div>`;
|
|
142
|
+
document.getElementById('detail').style.display = 'block';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
network.once('stabilizationIterationsDone', () => network.fit({ animation: true }));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function zoomComm(id) {
|
|
150
|
+
if (!network) return;
|
|
151
|
+
const members = graphData.edges.filter(e => e.source === id).map(e => e.target);
|
|
152
|
+
members.push(id);
|
|
153
|
+
const valid = members.filter(x => network.body.data.nodes.get(x));
|
|
154
|
+
if (valid.length) { network.fit({ nodes: valid, animation: { duration: 500, easingFunction: 'easeInOutQuad' } }); network.selectNodes(valid.slice(0,30)); }
|
|
155
|
+
}
|
|
156
|
+
function zoomNode(id) {
|
|
157
|
+
if (!network) return;
|
|
158
|
+
network.focus(id, { scale: 2.5, animation: { duration: 400, easingFunction: 'easeInOutQuad' } });
|
|
159
|
+
network.selectNodes([id]);
|
|
160
|
+
showDetail(id);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function setTab(t) { tab = t; document.querySelectorAll('.sidebar-nav button').forEach((b,i) => b.classList.toggle('active', ['clusters','classes','memory'][i]===t)); renderList(); }
|
|
164
|
+
function renderList() {
|
|
165
|
+
const el = document.getElementById('list');
|
|
166
|
+
if (tab === 'clusters') {
|
|
167
|
+
const c = (window._comms?.communities||[]).sort((a,b)=>(b.count||0)-(a.count||0));
|
|
168
|
+
el.innerHTML = c.map(x => `<div class="sidebar-item" onclick="zoomComm('${x.id}')"><div class="icon" style="background:var(--purple)"></div><div class="label">${x.name}</div><div class="badge">${x.count}</div></div>`).join('');
|
|
169
|
+
} else if (tab === 'classes') {
|
|
170
|
+
const c = allNodes.filter(n=>n.type==='class').sort((a,b)=>(a.name||'').localeCompare(b.name||''));
|
|
171
|
+
el.innerHTML = c.slice(0,100).map(n => `<div class="sidebar-item" onclick="zoomNode('${n.id}')"><div class="icon" style="background:var(--red)"></div><div class="label">${n.name}</div></div>`).join('');
|
|
172
|
+
} else {
|
|
173
|
+
const m = (window._mem?.memories||[]).filter(x=>!x.evicted&&!x.superseded_by);
|
|
174
|
+
const d = (window._mem?.decisions||[]).filter(x=>x.active!==false);
|
|
175
|
+
el.innerHTML = d.map(x => `<div class="sidebar-item"><div class="icon" style="background:var(--amber)"></div><div class="label">${x.decision}</div></div>`).join('') +
|
|
176
|
+
m.map(x => `<div class="sidebar-item"><div class="icon" style="background:var(--pink)"></div><div class="label">${x.content}</div></div>`).join('');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function showDetail(id) {
|
|
181
|
+
const n = allNodes.find(x => x.id === id);
|
|
182
|
+
if (!n) return;
|
|
183
|
+
let h = `<h3>${n.name||id}</h3><div class="meta">${n.type}</div>`;
|
|
184
|
+
if (n.type === 'class') {
|
|
185
|
+
try {
|
|
186
|
+
const s = await fetch('/api/symbol?name='+encodeURIComponent(n.name)).then(r=>r.json());
|
|
187
|
+
if (s.implements) h += `<div class="meta">: ${s.implements}</div>`;
|
|
188
|
+
if (s.methods?.length) s.methods.forEach(m => { h += `<div class="meth">${m.name||m}</div>`; });
|
|
189
|
+
} catch(e){}
|
|
190
|
+
}
|
|
191
|
+
document.getElementById('detail-body').innerHTML = h;
|
|
192
|
+
document.getElementById('detail').style.display = 'block';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
document.getElementById('search').addEventListener('input', async function() {
|
|
196
|
+
const q = this.value.trim();
|
|
197
|
+
if (q.length < 2) { renderList(); return; }
|
|
198
|
+
const d = await fetch('/api/search?q='+encodeURIComponent(q)).then(r=>r.json());
|
|
199
|
+
document.getElementById('list').innerHTML = (d.results||[]).slice(0,40).map(r =>
|
|
200
|
+
`<div class="sidebar-item" onclick="zoomNode('${r.id}')"><div class="icon" style="background:${r.type==='class'?'var(--red)':'var(--blue)'}"></div><div class="label">${r.name}</div><div class="badge">${r.type}</div></div>`
|
|
201
|
+
).join('') || '<div style="padding:20px;text-align:center;color:var(--muted)">No results</div>';
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
init();
|
|
205
|
+
</script>
|
|
206
|
+
</body>
|
|
207
|
+
</html>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mnemo-dev"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "Persistent memory and repo map for AI coding assistants"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "AGPL-3.0"}
|
|
@@ -71,6 +71,9 @@ mnemo-mcp = "mnemo.mcp_server:run_stdio"
|
|
|
71
71
|
where = ["."]
|
|
72
72
|
include = ["mnemo*"]
|
|
73
73
|
|
|
74
|
+
[tool.setuptools.package-data]
|
|
75
|
+
mnemo = ["ui_static/**"]
|
|
76
|
+
|
|
74
77
|
[tool.pytest.ini_options]
|
|
75
78
|
testpaths = ["tests"]
|
|
76
79
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|