velune-cli 0.9.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.
Files changed (279) hide show
  1. velune/__init__.py +5 -0
  2. velune/__main__.py +6 -0
  3. velune/cli/__init__.py +5 -0
  4. velune/cli/app.py +208 -0
  5. velune/cli/autocomplete.py +80 -0
  6. velune/cli/banner.py +60 -0
  7. velune/cli/commands/__init__.py +32 -0
  8. velune/cli/commands/ask.py +175 -0
  9. velune/cli/commands/base.py +16 -0
  10. velune/cli/commands/chat.py +228 -0
  11. velune/cli/commands/config.py +224 -0
  12. velune/cli/commands/daemon.py +88 -0
  13. velune/cli/commands/doctor.py +721 -0
  14. velune/cli/commands/init.py +170 -0
  15. velune/cli/commands/mcp.py +82 -0
  16. velune/cli/commands/memory.py +293 -0
  17. velune/cli/commands/models.py +683 -0
  18. velune/cli/commands/preflight.py +95 -0
  19. velune/cli/commands/run.py +270 -0
  20. velune/cli/commands/setup.py +184 -0
  21. velune/cli/commands/workspace.py +249 -0
  22. velune/cli/context.py +36 -0
  23. velune/cli/councilmodel_ui.py +199 -0
  24. velune/cli/display/council_view.py +254 -0
  25. velune/cli/display/memory_view.py +126 -0
  26. velune/cli/display/panels.py +35 -0
  27. velune/cli/display/progress.py +25 -0
  28. velune/cli/display/themes.py +25 -0
  29. velune/cli/main.py +15 -0
  30. velune/cli/model_selector.py +51 -0
  31. velune/cli/modes.py +86 -0
  32. velune/cli/pull_ui.py +123 -0
  33. velune/cli/registry.py +80 -0
  34. velune/cli/rendering/__init__.py +5 -0
  35. velune/cli/rendering/error_panel.py +79 -0
  36. velune/cli/rendering/markdown.py +63 -0
  37. velune/cli/repl.py +1855 -0
  38. velune/cli/session_manager.py +71 -0
  39. velune/cli/slash_commands.py +37 -0
  40. velune/cli/theme.py +8 -0
  41. velune/cognition/__init__.py +23 -0
  42. velune/cognition/agents/__init__.py +7 -0
  43. velune/cognition/agents/coder.py +209 -0
  44. velune/cognition/agents/planner.py +156 -0
  45. velune/cognition/agents/reviewer.py +195 -0
  46. velune/cognition/arbitrator.py +220 -0
  47. velune/cognition/architecture.py +415 -0
  48. velune/cognition/budget.py +65 -0
  49. velune/cognition/council/__init__.py +47 -0
  50. velune/cognition/council/base.py +217 -0
  51. velune/cognition/council/challenger.py +74 -0
  52. velune/cognition/council/coder.py +79 -0
  53. velune/cognition/council/critic_agent.py +43 -0
  54. velune/cognition/council/critic_configs.py +111 -0
  55. velune/cognition/council/critics.py +41 -0
  56. velune/cognition/council/debate.py +46 -0
  57. velune/cognition/council/factory.py +140 -0
  58. velune/cognition/council/messages.py +56 -0
  59. velune/cognition/council/planner.py +124 -0
  60. velune/cognition/council/reviewer.py +74 -0
  61. velune/cognition/council/synthesizer.py +67 -0
  62. velune/cognition/council/tiers.py +188 -0
  63. velune/cognition/council_orchestrator.py +282 -0
  64. velune/cognition/firewall.py +354 -0
  65. velune/cognition/module.py +46 -0
  66. velune/cognition/orchestrator.py +1205 -0
  67. velune/cognition/personality.py +238 -0
  68. velune/cognition/state.py +104 -0
  69. velune/cognition/style_resolver.py +64 -0
  70. velune/cognition/verification.py +205 -0
  71. velune/context/__init__.py +28 -0
  72. velune/context/assembler.py +240 -0
  73. velune/context/budget.py +97 -0
  74. velune/context/extractive.py +95 -0
  75. velune/context/prompt_adaptation.py +480 -0
  76. velune/context/sections.py +99 -0
  77. velune/context/token_counter.py +134 -0
  78. velune/context/utilization.py +33 -0
  79. velune/context/window.py +63 -0
  80. velune/core/__init__.py +89 -0
  81. velune/core/background.py +5 -0
  82. velune/core/config/__init__.py +37 -0
  83. velune/core/errors/__init__.py +90 -0
  84. velune/core/errors/catalog.py +188 -0
  85. velune/core/errors/execution.py +31 -0
  86. velune/core/errors/memory.py +25 -0
  87. velune/core/errors/orchestration.py +31 -0
  88. velune/core/errors/provider.py +37 -0
  89. velune/core/event_loop.py +35 -0
  90. velune/core/logging.py +83 -0
  91. velune/core/paths.py +165 -0
  92. velune/core/runtime.py +113 -0
  93. velune/core/startup_profiler.py +56 -0
  94. velune/core/task_registry.py +117 -0
  95. velune/core/trace.py +83 -0
  96. velune/core/types/__init__.py +48 -0
  97. velune/core/types/agent.py +53 -0
  98. velune/core/types/context.py +42 -0
  99. velune/core/types/inference.py +38 -0
  100. velune/core/types/memory.py +42 -0
  101. velune/core/types/model.py +70 -0
  102. velune/core/types/provider.py +62 -0
  103. velune/core/types/repository.py +38 -0
  104. velune/core/types/task.py +61 -0
  105. velune/core/types/workspace.py +28 -0
  106. velune/daemon/client.py +13 -0
  107. velune/daemon/server.py +127 -0
  108. velune/daemon/transport.py +179 -0
  109. velune/events.py +204 -0
  110. velune/execution/__init__.py +22 -0
  111. velune/execution/benchmarker.py +315 -0
  112. velune/execution/cancellation.py +53 -0
  113. velune/execution/checkpointer.py +130 -0
  114. velune/execution/command_spec.py +165 -0
  115. velune/execution/diff_preview.py +197 -0
  116. velune/execution/executor.py +181 -0
  117. velune/execution/module.py +18 -0
  118. velune/execution/multi_diff.py +67 -0
  119. velune/execution/path_guard.py +74 -0
  120. velune/execution/planner.py +91 -0
  121. velune/execution/rollback.py +89 -0
  122. velune/execution/sandbox.py +268 -0
  123. velune/execution/validator.py +115 -0
  124. velune/hardware/__init__.py +1 -0
  125. velune/hardware/detector.py +192 -0
  126. velune/kernel/__init__.py +55 -0
  127. velune/kernel/bootstrap.py +125 -0
  128. velune/kernel/config.py +426 -0
  129. velune/kernel/entrypoint.py +78 -0
  130. velune/kernel/health.py +54 -0
  131. velune/kernel/lifecycle.py +143 -0
  132. velune/kernel/module.py +17 -0
  133. velune/kernel/modules.py +23 -0
  134. velune/kernel/registry.py +96 -0
  135. velune/kernel/schemas.py +28 -0
  136. velune/main.py +9 -0
  137. velune/mcp/__init__.py +9 -0
  138. velune/mcp/client.py +115 -0
  139. velune/mcp/config.py +19 -0
  140. velune/mcp/server.py +624 -0
  141. velune/memory/__init__.py +32 -0
  142. velune/memory/compaction.py +506 -0
  143. velune/memory/embedding_pipeline.py +241 -0
  144. velune/memory/lifecycle.py +680 -0
  145. velune/memory/module.py +218 -0
  146. velune/memory/prioritizer.py +67 -0
  147. velune/memory/storage/episodic_schema.sql +53 -0
  148. velune/memory/storage/lancedb_store.py +282 -0
  149. velune/memory/storage/sqlite_manager.py +369 -0
  150. velune/memory/storage/sqlite_pool.py +149 -0
  151. velune/memory/tiers/episodic.py +588 -0
  152. velune/memory/tiers/graph.py +378 -0
  153. velune/memory/tiers/lineage.py +416 -0
  154. velune/memory/tiers/semantic.py +475 -0
  155. velune/memory/tiers/working.py +168 -0
  156. velune/memory/vitality.py +132 -0
  157. velune/models/__init__.py +15 -0
  158. velune/models/family.py +76 -0
  159. velune/models/module.py +20 -0
  160. velune/models/probes.py +192 -0
  161. velune/models/profile_cache.py +84 -0
  162. velune/models/profiler.py +108 -0
  163. velune/models/registry.py +251 -0
  164. velune/models/scorer.py +233 -0
  165. velune/models/specializations.py +205 -0
  166. velune/orchestration/__init__.py +19 -0
  167. velune/orchestration/engine.py +239 -0
  168. velune/orchestration/module.py +15 -0
  169. velune/orchestration/role_assignments.py +82 -0
  170. velune/orchestration/schemas.py +98 -0
  171. velune/plugins/__init__.py +20 -0
  172. velune/plugins/hooks.py +50 -0
  173. velune/plugins/loader.py +161 -0
  174. velune/plugins/registry.py +56 -0
  175. velune/plugins/schemas.py +21 -0
  176. velune/providers/__init__.py +23 -0
  177. velune/providers/adapters/anthropic.py +257 -0
  178. velune/providers/adapters/fireworks.py +115 -0
  179. velune/providers/adapters/google.py +234 -0
  180. velune/providers/adapters/groq.py +151 -0
  181. velune/providers/adapters/huggingface.py +210 -0
  182. velune/providers/adapters/llamacpp.py +208 -0
  183. velune/providers/adapters/lmstudio.py +175 -0
  184. velune/providers/adapters/ollama.py +233 -0
  185. velune/providers/adapters/openai.py +213 -0
  186. velune/providers/adapters/openrouter.py +81 -0
  187. velune/providers/adapters/together.py +134 -0
  188. velune/providers/adapters/xai.py +60 -0
  189. velune/providers/base.py +86 -0
  190. velune/providers/benchmarker.py +138 -0
  191. velune/providers/discovery/__init__.py +33 -0
  192. velune/providers/discovery/anthropic.py +79 -0
  193. velune/providers/discovery/benchmarks.py +44 -0
  194. velune/providers/discovery/classifier.py +69 -0
  195. velune/providers/discovery/fireworks.py +95 -0
  196. velune/providers/discovery/gguf.py +88 -0
  197. velune/providers/discovery/google.py +95 -0
  198. velune/providers/discovery/gpu.py +117 -0
  199. velune/providers/discovery/groq.py +21 -0
  200. velune/providers/discovery/huggingface.py +67 -0
  201. velune/providers/discovery/lmstudio.py +80 -0
  202. velune/providers/discovery/ollama.py +162 -0
  203. velune/providers/discovery/openai.py +96 -0
  204. velune/providers/discovery/openrouter.py +113 -0
  205. velune/providers/discovery/scanner.py +115 -0
  206. velune/providers/discovery/together.py +114 -0
  207. velune/providers/discovery/xai.py +57 -0
  208. velune/providers/health.py +67 -0
  209. velune/providers/health_monitor.py +169 -0
  210. velune/providers/keystore.py +142 -0
  211. velune/providers/local_paths.py +49 -0
  212. velune/providers/local_resolver.py +229 -0
  213. velune/providers/module.py +51 -0
  214. velune/providers/ollama_manager.py +193 -0
  215. velune/providers/registry.py +220 -0
  216. velune/providers/router.py +255 -0
  217. velune/providers/task_classifier.py +288 -0
  218. velune/py.typed +0 -0
  219. velune/repository/__init__.py +33 -0
  220. velune/repository/analyzer.py +127 -0
  221. velune/repository/ast_parser.py +822 -0
  222. velune/repository/blast_radius.py +298 -0
  223. velune/repository/boundary_classifier.py +295 -0
  224. velune/repository/cognition.py +316 -0
  225. velune/repository/grapher.py +179 -0
  226. velune/repository/import_graph.py +263 -0
  227. velune/repository/incremental_indexer.py +275 -0
  228. velune/repository/index_state.py +96 -0
  229. velune/repository/indexer.py +243 -0
  230. velune/repository/module.py +17 -0
  231. velune/repository/parser.py +474 -0
  232. velune/repository/project_type.py +300 -0
  233. velune/repository/rename_journal.py +287 -0
  234. velune/repository/scanner.py +193 -0
  235. velune/repository/schemas.py +102 -0
  236. velune/repository/symbol_registry.py +365 -0
  237. velune/repository/tracker.py +252 -0
  238. velune/retrieval/__init__.py +27 -0
  239. velune/retrieval/cache.py +110 -0
  240. velune/retrieval/fast_path.py +391 -0
  241. velune/retrieval/graph.py +124 -0
  242. velune/retrieval/hybrid.py +271 -0
  243. velune/retrieval/keyword.py +131 -0
  244. velune/retrieval/module.py +26 -0
  245. velune/retrieval/pipeline.py +303 -0
  246. velune/retrieval/reranker.py +102 -0
  247. velune/retrieval/schemas.py +59 -0
  248. velune/retrieval/slow_path.py +364 -0
  249. velune/retrieval/vector.py +203 -0
  250. velune/telemetry/__init__.py +59 -0
  251. velune/telemetry/cognition.py +267 -0
  252. velune/telemetry/cost_estimator.py +92 -0
  253. velune/telemetry/debug.py +304 -0
  254. velune/telemetry/doctor.py +244 -0
  255. velune/telemetry/logging.py +286 -0
  256. velune/telemetry/spans.py +277 -0
  257. velune/telemetry/token_tracker.py +140 -0
  258. velune/telemetry/usage_tracker.py +340 -0
  259. velune/tools/__init__.py +41 -0
  260. velune/tools/base/registry.py +87 -0
  261. velune/tools/base/tool.py +63 -0
  262. velune/tools/code/navigate.py +116 -0
  263. velune/tools/code/search.py +123 -0
  264. velune/tools/filesystem/read.py +75 -0
  265. velune/tools/filesystem/search.py +136 -0
  266. velune/tools/filesystem/write.py +163 -0
  267. velune/tools/git/history.py +177 -0
  268. velune/tools/git/operations.py +122 -0
  269. velune/tools/git/state.py +121 -0
  270. velune/tools/module.py +81 -0
  271. velune/tools/terminal/execute.py +72 -0
  272. velune/tools/terminal/history.py +47 -0
  273. velune/tools/web/fetch.py +55 -0
  274. velune/tools/web/validator.py +122 -0
  275. velune_cli-0.9.0.dist-info/METADATA +518 -0
  276. velune_cli-0.9.0.dist-info/RECORD +279 -0
  277. velune_cli-0.9.0.dist-info/WHEEL +4 -0
  278. velune_cli-0.9.0.dist-info/entry_points.txt +2 -0
  279. velune_cli-0.9.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,288 @@
1
+ """Task classification based on prompt content and context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from enum import StrEnum
7
+ from typing import Any
8
+
9
+
10
+ class TaskType(StrEnum):
11
+ """Task type categories for routing."""
12
+
13
+ CODING = "coding"
14
+ REASONING = "reasoning"
15
+ SUMMARIZATION = "summarization"
16
+ GENERAL = "general"
17
+ EMBEDDING = "embedding"
18
+
19
+
20
+ class ComplexityLevel(StrEnum):
21
+ """Task complexity estimation."""
22
+
23
+ LOW = "low"
24
+ MEDIUM = "medium"
25
+ HIGH = "high"
26
+
27
+
28
+ @dataclass
29
+ class TaskProfile:
30
+ """Profile of a task for routing decisions."""
31
+
32
+ task_type: TaskType
33
+ complexity: ComplexityLevel
34
+ latency_sensitive: bool
35
+ requires_long_context: bool
36
+ requires_tools: bool
37
+ estimated_tokens: int = 0
38
+ metadata: dict[str, Any] = field(default_factory=dict)
39
+
40
+
41
+ class TaskClassifier:
42
+ """Classifies prompts and context into task profiles for routing."""
43
+
44
+ # Coding-related keywords
45
+ CODING_KEYWORDS = {
46
+ "implement",
47
+ "write",
48
+ "code",
49
+ "function",
50
+ "class",
51
+ "method",
52
+ "fix",
53
+ "bug",
54
+ "error",
55
+ "debug",
56
+ "refactor",
57
+ "rewrite",
58
+ "modify",
59
+ "change",
60
+ "update",
61
+ "add",
62
+ "remove",
63
+ "delete",
64
+ "script",
65
+ "program",
66
+ "algorithm",
67
+ "library",
68
+ "framework",
69
+ "test",
70
+ "unittest",
71
+ "pytest",
72
+ "jest",
73
+ "mocha",
74
+ "sql",
75
+ "query",
76
+ "database",
77
+ "schema",
78
+ }
79
+
80
+ # Reasoning-related keywords
81
+ REASONING_KEYWORDS = {
82
+ "explain",
83
+ "why",
84
+ "how",
85
+ "analyze",
86
+ "reason",
87
+ "infer",
88
+ "deduce",
89
+ "conclude",
90
+ "derive",
91
+ "solve",
92
+ "think",
93
+ "understand",
94
+ "interpret",
95
+ "clarify",
96
+ "elaborate",
97
+ "compare",
98
+ "contrast",
99
+ "difference",
100
+ "similarity",
101
+ "logic",
102
+ "argument",
103
+ "evidence",
104
+ "proof",
105
+ }
106
+
107
+ # Summarization-related keywords
108
+ SUMMARIZATION_KEYWORDS = {
109
+ "summarize",
110
+ "summary",
111
+ "tldr",
112
+ "brief",
113
+ "overview",
114
+ "outline",
115
+ "abstract",
116
+ "condense",
117
+ "digest",
118
+ "recap",
119
+ "highlight",
120
+ "extract",
121
+ "key",
122
+ "main",
123
+ "point",
124
+ "synopsis",
125
+ "gist",
126
+ }
127
+
128
+ # Quick question patterns (low complexity)
129
+ QUICK_PATTERNS = {
130
+ "what is",
131
+ "what's",
132
+ "who is",
133
+ "who's",
134
+ "when is",
135
+ "where is",
136
+ "how many",
137
+ "how much",
138
+ "why",
139
+ "define",
140
+ "meaning",
141
+ }
142
+
143
+ def classify(self, prompt: str, context: dict[str, Any] | None = None) -> TaskProfile:
144
+ """Classify a prompt into a task profile.
145
+
146
+ Args:
147
+ prompt: The user prompt/question
148
+ context: Optional context dict with keys like:
149
+ - code: Code snippets present
150
+ - retrieved_context: Retrieved knowledge base content
151
+ - context_tokens: Estimated context token count
152
+
153
+ Returns:
154
+ TaskProfile with task type, complexity, and routing hints
155
+ """
156
+ context = context or {}
157
+
158
+ # Estimate prompt length in tokens (rough: 1 token ~= 4 chars)
159
+ prompt_tokens = max(1, len(prompt) // 4)
160
+ context_tokens = context.get("context_tokens", 0)
161
+ total_tokens = prompt_tokens + context_tokens
162
+
163
+ # Classify task type
164
+ task_type = self._classify_task_type(prompt)
165
+
166
+ # Estimate complexity
167
+ complexity = self._estimate_complexity(prompt, task_type, total_tokens, context)
168
+
169
+ # Determine if latency sensitive
170
+ latency_sensitive = self._is_latency_sensitive(prompt, task_type, complexity)
171
+
172
+ # Check if long context needed
173
+ requires_long_context = total_tokens > 8000
174
+
175
+ # Check if tools needed
176
+ requires_tools = self._requires_tools(prompt, context)
177
+
178
+ return TaskProfile(
179
+ task_type=task_type,
180
+ complexity=complexity,
181
+ latency_sensitive=latency_sensitive,
182
+ requires_long_context=requires_long_context,
183
+ requires_tools=requires_tools,
184
+ estimated_tokens=total_tokens,
185
+ metadata={
186
+ "prompt_tokens": prompt_tokens,
187
+ "context_tokens": context_tokens,
188
+ },
189
+ )
190
+
191
+ def _classify_task_type(self, prompt: str) -> TaskType:
192
+ """Classify the primary task type from prompt keywords."""
193
+ lower = prompt.lower()
194
+
195
+ # Check coding keywords
196
+ coding_matches = sum(1 for kw in self.CODING_KEYWORDS if kw in lower)
197
+ reasoning_matches = sum(1 for kw in self.REASONING_KEYWORDS if kw in lower)
198
+ summarization_matches = sum(1 for kw in self.SUMMARIZATION_KEYWORDS if kw in lower)
199
+
200
+ # Return task type with most matches
201
+ max_matches = max(coding_matches, reasoning_matches, summarization_matches)
202
+
203
+ if max_matches == 0:
204
+ return TaskType.GENERAL
205
+
206
+ if coding_matches == max_matches:
207
+ return TaskType.CODING
208
+ if reasoning_matches == max_matches:
209
+ return TaskType.REASONING
210
+ if summarization_matches == max_matches:
211
+ return TaskType.SUMMARIZATION
212
+
213
+ return TaskType.GENERAL
214
+
215
+ def _estimate_complexity(
216
+ self,
217
+ prompt: str,
218
+ task_type: TaskType,
219
+ total_tokens: int,
220
+ context: dict[str, Any],
221
+ ) -> ComplexityLevel:
222
+ """Estimate task complexity based on prompt and context."""
223
+ prompt.lower()
224
+
225
+ # Short prompts with no code/context => LOW
226
+ if len(prompt) < 50 and total_tokens < 500:
227
+ if task_type == TaskType.GENERAL and "code" not in context:
228
+ return ComplexityLevel.LOW
229
+
230
+ # Long prompts with code/context => HIGH
231
+ if total_tokens > 4000 and "code" in context:
232
+ return ComplexityLevel.HIGH
233
+
234
+ # Reasoning/complex analysis => at least MEDIUM
235
+ if task_type in (TaskType.REASONING, TaskType.SUMMARIZATION):
236
+ if total_tokens > 2000:
237
+ return ComplexityLevel.HIGH
238
+ return ComplexityLevel.MEDIUM
239
+
240
+ # Coding with substantial context => HIGH
241
+ if task_type == TaskType.CODING and total_tokens > 3000:
242
+ return ComplexityLevel.HIGH
243
+
244
+ # Default to MEDIUM
245
+ return ComplexityLevel.MEDIUM
246
+
247
+ def _is_latency_sensitive(
248
+ self,
249
+ prompt: str,
250
+ task_type: TaskType,
251
+ complexity: ComplexityLevel,
252
+ ) -> bool:
253
+ """Determine if response latency is critical."""
254
+ # Short quick questions are latency sensitive
255
+ if complexity == ComplexityLevel.LOW:
256
+ lower = prompt.lower()
257
+ return any(pattern in lower for pattern in self.QUICK_PATTERNS)
258
+
259
+ # Complex reasoning/analysis can wait
260
+ if complexity == ComplexityLevel.HIGH:
261
+ return False
262
+
263
+ # Quick questions/definitions
264
+ lower = prompt.lower()
265
+ quick_match = any(pattern in lower for pattern in self.QUICK_PATTERNS)
266
+ if quick_match:
267
+ return True
268
+
269
+ # Default: not latency sensitive for medium complexity
270
+ return False
271
+
272
+ def _requires_tools(self, prompt: str, context: dict[str, Any]) -> bool:
273
+ """Determine if the task likely needs external tools/APIs."""
274
+ tool_keywords = {
275
+ "api",
276
+ "request",
277
+ "fetch",
278
+ "call",
279
+ "network",
280
+ "http",
281
+ "search",
282
+ "browse",
283
+ "lookup",
284
+ "query",
285
+ "database",
286
+ }
287
+ lower = prompt.lower()
288
+ return any(kw in lower for kw in tool_keywords)
velune/py.typed ADDED
File without changes
@@ -0,0 +1,33 @@
1
+ """Repository Cognition Engine for AST parsing, Git tracking, and dependency graphs."""
2
+
3
+ from velune.repository.ast_parser import ASTParser, Symbol, SymbolKind
4
+ from velune.repository.cognition import RepositoryCognitionService
5
+ from velune.repository.parser import RepositorySnapshotParser
6
+ from velune.repository.rename_journal import RenameJournal
7
+ from velune.repository.schemas import (
8
+ RepositoryEdge,
9
+ RepositoryFile,
10
+ RepositoryLanguage,
11
+ RepositorySnapshot,
12
+ RepositorySymbol,
13
+ RepositorySymbolKind,
14
+ )
15
+ from velune.repository.symbol_registry import SymbolRegistry
16
+
17
+ __all__ = [
18
+ "RepositoryCognitionService",
19
+ "RepositoryLanguage",
20
+ "RepositorySymbolKind",
21
+ "RepositorySymbol",
22
+ "RepositoryFile",
23
+ "RepositoryEdge",
24
+ "RepositorySnapshot",
25
+ # Async AST parser (canonical for SymbolRegistry / RenameJournal)
26
+ "ASTParser",
27
+ "Symbol",
28
+ "SymbolKind",
29
+ "SymbolRegistry",
30
+ "RenameJournal",
31
+ # Sync snapshot parser (used by indexer / incremental indexer / tools)
32
+ "RepositorySnapshotParser",
33
+ ]
@@ -0,0 +1,127 @@
1
+ """Architectural layer classifier and design pattern analyzer."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ class CodebaseAnalyzer:
7
+ """Analyzes the repository's architectural layered structure and checks for design violations."""
8
+
9
+ def __init__(self, root_path: Path) -> None:
10
+ self.root_path = root_path.resolve()
11
+
12
+ def classify_architecture_layers(self, file_paths: list[str]) -> dict[str, list[str]]:
13
+ """Groups files into functional architectural layers based on folder topology."""
14
+ layers: dict[str, list[str]] = {
15
+ "kernel": [],
16
+ "providers": [],
17
+ "models": [],
18
+ "memory": [],
19
+ "context": [],
20
+ "intent": [],
21
+ "repository": [],
22
+ "retrieval": [],
23
+ "execution": [],
24
+ "cognition": [],
25
+ "cli": [],
26
+ "tools": [],
27
+ "plugins": [],
28
+ "telemetry": [],
29
+ "other": [],
30
+ }
31
+
32
+ for path in file_paths:
33
+ # Normalize to forward slashes
34
+ norm = path.replace("\\", "/")
35
+ classified = False
36
+
37
+ for layer in layers.keys():
38
+ if f"velune/{layer}/" in norm or norm.startswith(f"{layer}/"):
39
+ layers[layer].append(norm)
40
+ classified = True
41
+ break
42
+
43
+ if not classified:
44
+ layers["other"].append(norm)
45
+
46
+ return layers
47
+
48
+ def detect_dependency_violations(
49
+ self, layers: dict[str, list[str]], import_edges: list[tuple]
50
+ ) -> list[dict[str, str]]:
51
+ """Identifies circular dependencies or violations of layered architectural boundaries.
52
+
53
+ Rules:
54
+ - Kernel layer MUST NOT import higher levels (cli, cognition, retrieval).
55
+ - Providers layer MUST NOT import cli or cognition.
56
+ - Higher levels can import kernel/providers freely.
57
+ """
58
+ violations: list[dict[str, str]] = []
59
+
60
+ # Build mapping of file to its layer
61
+ layer_by_file: dict[str, str] = {}
62
+ for layer, files in layers.items():
63
+ for f in files:
64
+ layer_by_file[f] = layer
65
+
66
+ # Layer importance hierarchy (lower number is more fundamental/lower layer)
67
+ hierarchy = {
68
+ "kernel": 0,
69
+ "providers": 1,
70
+ "models": 2,
71
+ "memory": 3,
72
+ "context": 4,
73
+ "intent": 5,
74
+ "repository": 5,
75
+ "retrieval": 6,
76
+ "tools": 7,
77
+ "execution": 8,
78
+ "cognition": 9,
79
+ "plugins": 9,
80
+ "cli": 10,
81
+ "telemetry": 0, # Telemetry can be imported everywhere
82
+ "other": 10,
83
+ }
84
+
85
+ for source, target in import_edges:
86
+ src_layer = layer_by_file.get(source)
87
+ tgt_layer = layer_by_file.get(target)
88
+
89
+ if src_layer and tgt_layer:
90
+ src_val = hierarchy.get(src_layer, 10)
91
+ tgt_val = hierarchy.get(tgt_layer, 10)
92
+
93
+ # Violation: Lower layer imports a strictly higher layer (excluding telemetry)
94
+ if src_val < tgt_val and tgt_layer != "telemetry":
95
+ violations.append(
96
+ {
97
+ "source": source,
98
+ "target": target,
99
+ "source_layer": src_layer,
100
+ "target_layer": tgt_layer,
101
+ "rule": f"Layer violation: Fundamental '{src_layer}' layer imports higher-level '{tgt_layer}' layer.",
102
+ }
103
+ )
104
+
105
+ return violations
106
+
107
+ def detect_framework_footprint(self, code_files: dict[str, str]) -> list[str]:
108
+ """Detects specific framework libraries and dependencies active in the codebase."""
109
+ footprints: set[str] = set()
110
+
111
+ for code in code_files.values():
112
+ if "import typer" in code or "from typer" in code:
113
+ footprints.add("Typer (CLI)")
114
+ if "import langgraph" in code or "from langgraph" in code:
115
+ footprints.add("LangGraph (Orchestration)")
116
+ if "import qdrant_client" in code or "from qdrant_client" in code:
117
+ footprints.add("Qdrant (Vector DB)")
118
+ if "import sqlite3" in code or "from sqlite3" in code:
119
+ footprints.add("SQLite (Episodic Storage)")
120
+ if "import tree_sitter" in code or "from tree_sitter" in code:
121
+ footprints.add("Tree-sitter (AST Parsing)")
122
+ if "import networkx" in code or "from networkx" in code:
123
+ footprints.add("NetworkX (Graph Traversal)")
124
+ if "import psutil" in code or "from psutil" in code:
125
+ footprints.add("psutil (Subprocess Limits)")
126
+
127
+ return list(footprints)