velune-cli 1.0.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 (229) 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 +212 -0
  5. velune/cli/autocomplete.py +76 -0
  6. velune/cli/banner.py +98 -0
  7. velune/cli/commands/__init__.py +32 -0
  8. velune/cli/commands/ask.py +149 -0
  9. velune/cli/commands/base.py +16 -0
  10. velune/cli/commands/chat.py +188 -0
  11. velune/cli/commands/config.py +182 -0
  12. velune/cli/commands/daemon.py +85 -0
  13. velune/cli/commands/doctor.py +373 -0
  14. velune/cli/commands/init.py +160 -0
  15. velune/cli/commands/mcp.py +80 -0
  16. velune/cli/commands/memory.py +269 -0
  17. velune/cli/commands/models.py +462 -0
  18. velune/cli/commands/preflight.py +95 -0
  19. velune/cli/commands/run.py +171 -0
  20. velune/cli/commands/setup.py +182 -0
  21. velune/cli/commands/workspace.py +217 -0
  22. velune/cli/context.py +37 -0
  23. velune/cli/councilmodel_ui.py +171 -0
  24. velune/cli/display/council_view.py +240 -0
  25. velune/cli/display/memory_view.py +93 -0
  26. velune/cli/display/panels.py +35 -0
  27. velune/cli/display/progress.py +25 -0
  28. velune/cli/display/themes.py +21 -0
  29. velune/cli/main.py +15 -0
  30. velune/cli/model_selector.py +44 -0
  31. velune/cli/modes.py +86 -0
  32. velune/cli/pull_ui.py +118 -0
  33. velune/cli/registry.py +81 -0
  34. velune/cli/repl.py +1178 -0
  35. velune/cli/session_manager.py +69 -0
  36. velune/cli/slash_commands.py +37 -0
  37. velune/cognition/__init__.py +19 -0
  38. velune/cognition/arbitrator.py +216 -0
  39. velune/cognition/architecture.py +398 -0
  40. velune/cognition/council/__init__.py +47 -0
  41. velune/cognition/council/base.py +216 -0
  42. velune/cognition/council/challenger.py +70 -0
  43. velune/cognition/council/coder.py +79 -0
  44. velune/cognition/council/critic_agent.py +39 -0
  45. velune/cognition/council/critic_configs.py +111 -0
  46. velune/cognition/council/critics.py +41 -0
  47. velune/cognition/council/debate.py +44 -0
  48. velune/cognition/council/factory.py +140 -0
  49. velune/cognition/council/messages.py +53 -0
  50. velune/cognition/council/planner.py +119 -0
  51. velune/cognition/council/reviewer.py +72 -0
  52. velune/cognition/council/synthesizer.py +67 -0
  53. velune/cognition/council/tiers.py +181 -0
  54. velune/cognition/firewall.py +256 -0
  55. velune/cognition/module.py +38 -0
  56. velune/cognition/orchestrator.py +886 -0
  57. velune/cognition/personality.py +236 -0
  58. velune/cognition/style_resolver.py +62 -0
  59. velune/cognition/verification.py +201 -0
  60. velune/context/__init__.py +11 -0
  61. velune/context/extractive.py +94 -0
  62. velune/context/window.py +62 -0
  63. velune/core/__init__.py +89 -0
  64. velune/core/background.py +5 -0
  65. velune/core/config/__init__.py +37 -0
  66. velune/core/errors/__init__.py +53 -0
  67. velune/core/errors/execution.py +26 -0
  68. velune/core/errors/memory.py +21 -0
  69. velune/core/errors/orchestration.py +26 -0
  70. velune/core/errors/provider.py +31 -0
  71. velune/core/event_loop.py +30 -0
  72. velune/core/logging.py +83 -0
  73. velune/core/runtime.py +106 -0
  74. velune/core/task_registry.py +120 -0
  75. velune/core/trace.py +80 -0
  76. velune/core/types/__init__.py +48 -0
  77. velune/core/types/agent.py +49 -0
  78. velune/core/types/context.py +39 -0
  79. velune/core/types/inference.py +35 -0
  80. velune/core/types/memory.py +39 -0
  81. velune/core/types/model.py +64 -0
  82. velune/core/types/provider.py +35 -0
  83. velune/core/types/repository.py +35 -0
  84. velune/core/types/task.py +56 -0
  85. velune/core/types/workspace.py +28 -0
  86. velune/daemon/client.py +13 -0
  87. velune/daemon/server.py +115 -0
  88. velune/daemon/transport.py +169 -0
  89. velune/events.py +194 -0
  90. velune/execution/__init__.py +22 -0
  91. velune/execution/benchmarker.py +311 -0
  92. velune/execution/cancellation.py +53 -0
  93. velune/execution/checkpointer.py +128 -0
  94. velune/execution/command_spec.py +140 -0
  95. velune/execution/diff_preview.py +172 -0
  96. velune/execution/executor.py +173 -0
  97. velune/execution/module.py +16 -0
  98. velune/execution/multi_diff.py +70 -0
  99. velune/execution/path_guard.py +19 -0
  100. velune/execution/planner.py +91 -0
  101. velune/execution/rollback.py +80 -0
  102. velune/execution/sandbox.py +257 -0
  103. velune/execution/validator.py +113 -0
  104. velune/hardware/__init__.py +1 -0
  105. velune/hardware/detector.py +162 -0
  106. velune/kernel/__init__.py +58 -0
  107. velune/kernel/bootstrap.py +107 -0
  108. velune/kernel/config.py +252 -0
  109. velune/kernel/health.py +54 -0
  110. velune/kernel/lifecycle.py +102 -0
  111. velune/kernel/module.py +15 -0
  112. velune/kernel/modules.py +23 -0
  113. velune/kernel/registry.py +93 -0
  114. velune/kernel/schemas.py +28 -0
  115. velune/main.py +9 -0
  116. velune/mcp/__init__.py +9 -0
  117. velune/mcp/client.py +113 -0
  118. velune/mcp/config.py +19 -0
  119. velune/mcp/server.py +90 -0
  120. velune/memory/__init__.py +28 -0
  121. velune/memory/lifecycle.py +154 -0
  122. velune/memory/module.py +94 -0
  123. velune/memory/prioritizer.py +65 -0
  124. velune/memory/storage/sqlite_manager.py +368 -0
  125. velune/memory/tiers/episodic.py +156 -0
  126. velune/memory/tiers/graph.py +282 -0
  127. velune/memory/tiers/lineage.py +367 -0
  128. velune/memory/tiers/semantic.py +198 -0
  129. velune/memory/tiers/working.py +165 -0
  130. velune/models/__init__.py +16 -0
  131. velune/models/module.py +18 -0
  132. velune/models/probes.py +182 -0
  133. velune/models/profile_cache.py +82 -0
  134. velune/models/profiler.py +105 -0
  135. velune/models/registry.py +201 -0
  136. velune/models/scorer.py +225 -0
  137. velune/models/specializations.py +196 -0
  138. velune/orchestration/__init__.py +15 -0
  139. velune/orchestration/module.py +14 -0
  140. velune/orchestration/role_assignments.py +78 -0
  141. velune/orchestration/schemas.py +99 -0
  142. velune/plugins/__init__.py +13 -0
  143. velune/plugins/hooks.py +49 -0
  144. velune/plugins/loader.py +95 -0
  145. velune/plugins/registry.py +54 -0
  146. velune/plugins/schemas.py +19 -0
  147. velune/providers/__init__.py +18 -0
  148. velune/providers/adapters/anthropic.py +231 -0
  149. velune/providers/adapters/fireworks.py +115 -0
  150. velune/providers/adapters/google.py +234 -0
  151. velune/providers/adapters/groq.py +151 -0
  152. velune/providers/adapters/huggingface.py +203 -0
  153. velune/providers/adapters/llamacpp.py +202 -0
  154. velune/providers/adapters/lmstudio.py +173 -0
  155. velune/providers/adapters/ollama.py +186 -0
  156. velune/providers/adapters/openai.py +207 -0
  157. velune/providers/adapters/openrouter.py +81 -0
  158. velune/providers/adapters/together.py +134 -0
  159. velune/providers/adapters/xai.py +60 -0
  160. velune/providers/base.py +86 -0
  161. velune/providers/benchmarker.py +135 -0
  162. velune/providers/discovery/__init__.py +33 -0
  163. velune/providers/discovery/anthropic.py +77 -0
  164. velune/providers/discovery/benchmarks.py +44 -0
  165. velune/providers/discovery/classifier.py +69 -0
  166. velune/providers/discovery/fireworks.py +95 -0
  167. velune/providers/discovery/gguf.py +87 -0
  168. velune/providers/discovery/google.py +95 -0
  169. velune/providers/discovery/gpu.py +109 -0
  170. velune/providers/discovery/groq.py +20 -0
  171. velune/providers/discovery/huggingface.py +67 -0
  172. velune/providers/discovery/lmstudio.py +80 -0
  173. velune/providers/discovery/ollama.py +165 -0
  174. velune/providers/discovery/openai.py +96 -0
  175. velune/providers/discovery/openrouter.py +113 -0
  176. velune/providers/discovery/scanner.py +114 -0
  177. velune/providers/discovery/together.py +114 -0
  178. velune/providers/discovery/xai.py +57 -0
  179. velune/providers/keystore.py +83 -0
  180. velune/providers/local_paths.py +49 -0
  181. velune/providers/local_resolver.py +208 -0
  182. velune/providers/module.py +15 -0
  183. velune/providers/ollama_manager.py +193 -0
  184. velune/providers/registry.py +173 -0
  185. velune/providers/router.py +82 -0
  186. velune/py.typed +0 -0
  187. velune/repository/__init__.py +21 -0
  188. velune/repository/analyzer.py +123 -0
  189. velune/repository/cognition.py +172 -0
  190. velune/repository/grapher.py +182 -0
  191. velune/repository/indexer.py +229 -0
  192. velune/repository/module.py +15 -0
  193. velune/repository/parser.py +378 -0
  194. velune/repository/project_type.py +293 -0
  195. velune/repository/scanner.py +177 -0
  196. velune/repository/schemas.py +102 -0
  197. velune/repository/tracker.py +233 -0
  198. velune/retrieval/__init__.py +27 -0
  199. velune/retrieval/graph.py +117 -0
  200. velune/retrieval/hybrid.py +250 -0
  201. velune/retrieval/keyword.py +113 -0
  202. velune/retrieval/module.py +19 -0
  203. velune/retrieval/reranker.py +68 -0
  204. velune/retrieval/schemas.py +59 -0
  205. velune/retrieval/vector.py +163 -0
  206. velune/telemetry/__init__.py +7 -0
  207. velune/telemetry/cognition.py +260 -0
  208. velune/telemetry/token_tracker.py +133 -0
  209. velune/tools/__init__.py +41 -0
  210. velune/tools/base/registry.py +84 -0
  211. velune/tools/base/tool.py +63 -0
  212. velune/tools/code/navigate.py +107 -0
  213. velune/tools/code/search.py +112 -0
  214. velune/tools/filesystem/read.py +75 -0
  215. velune/tools/filesystem/search.py +123 -0
  216. velune/tools/filesystem/write.py +160 -0
  217. velune/tools/git/history.py +185 -0
  218. velune/tools/git/operations.py +132 -0
  219. velune/tools/git/state.py +134 -0
  220. velune/tools/module.py +65 -0
  221. velune/tools/terminal/execute.py +72 -0
  222. velune/tools/terminal/history.py +47 -0
  223. velune/tools/web/fetch.py +55 -0
  224. velune/tools/web/validator.py +96 -0
  225. velune_cli-1.0.0.dist-info/METADATA +497 -0
  226. velune_cli-1.0.0.dist-info/RECORD +229 -0
  227. velune_cli-1.0.0.dist-info/WHEEL +4 -0
  228. velune_cli-1.0.0.dist-info/entry_points.txt +2 -0
  229. velune_cli-1.0.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,171 @@
1
+ """Interactive two-stage TUI for assigning models to council agent roles."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from prompt_toolkit.application import Application
8
+ from prompt_toolkit.formatted_text import FormattedText
9
+ from prompt_toolkit.key_binding import KeyBindings
10
+ from prompt_toolkit.layout import Layout
11
+ from prompt_toolkit.layout.containers import Window
12
+ from prompt_toolkit.layout.controls import FormattedTextControl
13
+
14
+ from velune.orchestration.role_assignments import (
15
+ COUNCIL_ROLES,
16
+ ROLE_DESCRIPTIONS,
17
+ CouncilRoleMap,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from rich.console import Console
22
+
23
+ from velune.core.types.model import ModelDescriptor
24
+
25
+ # Sentinel to distinguish "user pressed Escape" from "user selected clear"
26
+ _CANCELLED = object()
27
+
28
+
29
+ async def run_councilmodel_ui(
30
+ role_map: CouncilRoleMap,
31
+ available_models: list[ModelDescriptor],
32
+ console: Console,
33
+ ) -> CouncilRoleMap | None:
34
+ """Two-stage interactive UI: select a role, then select a model for it.
35
+
36
+ Returns the updated role_map, or None if cancelled at the role-select stage.
37
+ """
38
+
39
+ # ── Stage 1: Role selection ────────────────────────────────────────
40
+ selected_role_idx = [0]
41
+ role_result: list[str | None] = [None]
42
+
43
+ def render_role_list() -> FormattedText:
44
+ lines: list[tuple[str, str]] = []
45
+ lines.append(("bold", " Assign model to council role\n"))
46
+ lines.append(("fg:ansibrightblack", " ↑↓ navigate · Enter select · Esc cancel\n\n"))
47
+ for i, role in enumerate(COUNCIL_ROLES):
48
+ is_active = i == selected_role_idx[0]
49
+ prefix = "❯ " if is_active else " "
50
+ row_style = "bold fg:cyan" if is_active else ""
51
+ desc = ROLE_DESCRIPTIONS.get(role, "")
52
+ lines.append((row_style, f" {prefix}{role:<14} {desc}\n"))
53
+ current = role_map.get(role)
54
+ if current:
55
+ lines.append(("fg:ansibrightblack", f" currently: {current.model_id}\n"))
56
+ return FormattedText(lines)
57
+
58
+ kb1 = KeyBindings()
59
+
60
+ @kb1.add("up")
61
+ def _up(event) -> None:
62
+ selected_role_idx[0] = (selected_role_idx[0] - 1) % len(COUNCIL_ROLES)
63
+
64
+ @kb1.add("down")
65
+ def _down(event) -> None:
66
+ selected_role_idx[0] = (selected_role_idx[0] + 1) % len(COUNCIL_ROLES)
67
+
68
+ @kb1.add("enter")
69
+ def _select_role(event) -> None:
70
+ role_result[0] = COUNCIL_ROLES[selected_role_idx[0]]
71
+ event.app.exit()
72
+
73
+ @kb1.add("escape")
74
+ @kb1.add("c-c")
75
+ def _cancel_role(event) -> None:
76
+ event.app.exit() # role_result stays None
77
+
78
+ app1 = Application(
79
+ layout=Layout(Window(
80
+ content=FormattedTextControl(render_role_list, focusable=True),
81
+ )),
82
+ key_bindings=kb1,
83
+ full_screen=False,
84
+ mouse_support=False,
85
+ )
86
+ await app1.run_async()
87
+
88
+ selected_role = role_result[0]
89
+ if selected_role is None:
90
+ return None # cancelled at role stage
91
+
92
+ # ── Stage 2: Model selection for chosen role ───────────────────────
93
+ # First entry is None = "clear assignment"
94
+ model_options: list[ModelDescriptor | None] = [None] + list(available_models)
95
+ selected_model_idx = [0]
96
+ model_result: list[object] = [_CANCELLED]
97
+
98
+ def render_model_list() -> FormattedText:
99
+ lines: list[tuple[str, str]] = []
100
+ lines.append(("bold", f" Select model for [{selected_role}]\n"))
101
+ lines.append(("fg:ansibrightblack", " ↑↓ navigate · Enter select · Esc back\n\n"))
102
+
103
+ for i, model in enumerate(model_options):
104
+ is_active = i == selected_model_idx[0]
105
+ prefix = "❯ " if is_active else " "
106
+ row_style = "bold fg:cyan" if is_active else ""
107
+
108
+ if model is None:
109
+ lines.append((row_style, f" {prefix}(clear — use default routing)\n"))
110
+ continue
111
+
112
+ current = role_map.get(selected_role)
113
+ is_current = current is not None and current.model_id == model.model_id
114
+ current_marker = " ← current" if is_current else ""
115
+ local_cloud = "local" if model.is_local else "cloud"
116
+ cost = getattr(model, "cost_per_1k_tokens", None)
117
+ free_str = " free" if cost == 0.0 else ""
118
+ lines.append((
119
+ row_style,
120
+ f" {prefix}{model.model_id:<42}"
121
+ f" [{local_cloud}{free_str} · {model.speed_tier}]"
122
+ f"{current_marker}\n",
123
+ ))
124
+ return FormattedText(lines)
125
+
126
+ kb2 = KeyBindings()
127
+
128
+ @kb2.add("up")
129
+ def _up2(event) -> None:
130
+ selected_model_idx[0] = (selected_model_idx[0] - 1) % len(model_options)
131
+
132
+ @kb2.add("down")
133
+ def _down2(event) -> None:
134
+ selected_model_idx[0] = (selected_model_idx[0] + 1) % len(model_options)
135
+
136
+ @kb2.add("enter")
137
+ def _select_model(event) -> None:
138
+ model_result[0] = model_options[selected_model_idx[0]]
139
+ event.app.exit()
140
+
141
+ @kb2.add("escape")
142
+ @kb2.add("c-c")
143
+ def _cancel_model(event) -> None:
144
+ event.app.exit() # model_result stays _CANCELLED
145
+
146
+ app2 = Application(
147
+ layout=Layout(Window(
148
+ content=FormattedTextControl(render_model_list, focusable=True),
149
+ )),
150
+ key_bindings=kb2,
151
+ full_screen=False,
152
+ mouse_support=False,
153
+ )
154
+ await app2.run_async()
155
+
156
+ if model_result[0] is _CANCELLED:
157
+ return role_map # user backed out — return map unchanged
158
+
159
+ chosen_model = model_result[0]
160
+ if chosen_model is None:
161
+ role_map.clear_role(selected_role)
162
+ console.print(f"[yellow]✓ Cleared assignment for [{selected_role}][/yellow]")
163
+ else:
164
+ role_map.assign(selected_role, chosen_model.model_id, chosen_model.provider_id)
165
+ console.print(
166
+ f"[green]✓ [{selected_role}][/green] → "
167
+ f"[cyan]{chosen_model.model_id}[/cyan] "
168
+ f"[dim]({chosen_model.provider_id})[/dim]"
169
+ )
170
+
171
+ return role_map
@@ -0,0 +1,240 @@
1
+ """Rich terminal visualization of the Reasoning Council debate, scoring, and votes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from rich.box import ROUNDED
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+
13
+ from velune.cognition.council.planner import TaskPlan
14
+ from velune.models.specializations import CouncilRole
15
+
16
+
17
+ class CouncilDisplayView:
18
+ """Beautiful Rich-based UI components to visualize Reasoning Council deliberations."""
19
+
20
+ def __init__(self, console: Console) -> None:
21
+ self.console = console
22
+
23
+ def render_header(self, task: str) -> None:
24
+ """Display an eye-catching header for the council run."""
25
+ self.console.print()
26
+ self.console.print(
27
+ Panel(
28
+ Text.assemble(
29
+ ("[bold magenta]VELUNE COGNITIVE OS[/bold magenta] — [cyan]Reasoning Council Active[/cyan]\n"),
30
+ ("[dim]Objective:[/dim] ", "[italic white]" + task + "[/italic white]")
31
+ ),
32
+ border_style="magenta",
33
+ box=ROUNDED,
34
+ title="[bold white]🧠 Cognitive Deliberation[/bold white]",
35
+ title_align="left"
36
+ )
37
+ )
38
+
39
+ def render_role_assignments(self, assignments: dict[CouncilRole, Any]) -> None:
40
+ """Render a table displaying mapped specialized models for the council."""
41
+ table = Table(
42
+ title="[bold cyan]Mapped Council Specializations[/bold cyan]",
43
+ box=ROUNDED,
44
+ border_style="dim",
45
+ expand=True
46
+ )
47
+ table.add_column("Council Seat", style="bold yellow")
48
+ table.add_column("Provider / Endpoint", style="green")
49
+ table.add_column("Target Model", style="cyan")
50
+ table.add_column("Key Skills / Tags", style="magenta")
51
+
52
+ for role, desc in assignments.items():
53
+ caps = []
54
+ if hasattr(desc, "capabilities") and desc.capabilities:
55
+ for cap_name in ["coding", "reasoning", "planning", "summarization", "tool_use"]:
56
+ level = getattr(desc.capabilities, cap_name, None)
57
+ if level and level > 0:
58
+ caps.append(f"{cap_name} ({level.name})")
59
+ tags = ", ".join(caps) if caps else ", ".join(desc.tags) if getattr(desc, "tags", None) else "reasoning"
60
+ table.add_row(
61
+ role.value.upper(),
62
+ desc.provider_id.capitalize(),
63
+ desc.model_id,
64
+ tags
65
+ )
66
+ self.console.print(table)
67
+ self.console.print()
68
+
69
+ def render_step_header(self, step_name: str, agent_emoji: str = "🤖") -> None:
70
+ """Draw an elegant boundary indicating a change in agent active deliberation."""
71
+ self.console.print(f"\n[bold magenta]●[/bold magenta] [bold white]{agent_emoji} {step_name}[/bold white] is deliberating...")
72
+
73
+ def render_planner_dag(self, plan: TaskPlan) -> None:
74
+ """Render the Planner's task plan DAG as a neat hierarchical or sequential table."""
75
+ table = Table(
76
+ title="[bold yellow]Execution Plan Compiled by Council Planner[/bold yellow]",
77
+ box=ROUNDED,
78
+ border_style="yellow",
79
+ expand=True
80
+ )
81
+ table.add_column("ID", style="bold cyan", width=8)
82
+ table.add_column("Description", style="white")
83
+ table.add_column("Dependencies", style="magenta")
84
+ table.add_column("Validation Strategy", style="green")
85
+
86
+ for step in plan.steps:
87
+ deps = ", ".join(step.dependencies) if step.dependencies else "[dim]None[/dim]"
88
+ val = step.metadata.get("test_command") or "Syntax check + File existence"
89
+ table.add_row(
90
+ step.id,
91
+ step.description,
92
+ deps,
93
+ str(val)
94
+ )
95
+ self.console.print(table)
96
+ self.console.print()
97
+
98
+ def render_code_proposal(self, code_proposal: str) -> None:
99
+ """Format the coder's proposed implementation code inside a syntax-focused block."""
100
+ self.console.print(
101
+ Panel(
102
+ Text(code_proposal, style="green"),
103
+ title="[bold green]💻 Coder Proposed Patch[/bold green]",
104
+ border_style="green",
105
+ box=ROUNDED,
106
+ expand=True
107
+ )
108
+ )
109
+
110
+ def render_reviewer_report(self, report: Any) -> None:
111
+ """Render the Reviewer's static audit, showing passed status and critical issues."""
112
+ if isinstance(report, dict):
113
+ passed = report.get("passed", True)
114
+ confidence = report.get("confidence_rating", 0.8)
115
+ issues = report.get("critical_issues", [])
116
+ else:
117
+ passed = report.passed
118
+ confidence = report.confidence_rating
119
+ issues = report.critical_issues
120
+
121
+ status_text = "[bold green]PASS[/bold green]" if passed else "[bold red]FAIL / BLOCKED[/bold red]"
122
+ border_style = "green" if passed else "red"
123
+
124
+ content = []
125
+ content.append(f"[bold]Verification Status:[/bold] {status_text}")
126
+ content.append(f"[bold]Confidence Rating:[/bold] {confidence:.2f}")
127
+
128
+ if issues:
129
+ content.append("\n[bold red]⚠️ Critical Issues Detected:[/bold red]")
130
+ for issue in issues:
131
+ content.append(f" [red]•[/red] {issue}")
132
+ else:
133
+ content.append("\n[green]✓ Static static checks passed. No syntactical or safety concerns raised.[/green]")
134
+
135
+ self.console.print(
136
+ Panel(
137
+ "\n".join(content),
138
+ title="[bold]🔍 Reviewer Audit[/bold]",
139
+ border_style=border_style,
140
+ box=ROUNDED,
141
+ expand=True
142
+ )
143
+ )
144
+
145
+ def render_challenger_report(self, report: Any) -> None:
146
+ """Render the Challenger's adversarial audit and failure vector probes."""
147
+ if isinstance(report, dict):
148
+ severity = report.get("severity_rating", 0.0)
149
+ vectors = report.get("failure_vectors", [])
150
+ else:
151
+ severity = report.severity_rating
152
+ vectors = report.failure_vectors
153
+
154
+ border_style = "yellow" if severity > 0.4 else "dim"
155
+
156
+ content = []
157
+ content.append(f"[bold]Adversarial Severity Rating:[/bold] [bold red]{severity:.2f}[/bold red] / 1.00")
158
+
159
+ if vectors:
160
+ content.append("\n[bold yellow]⚡ Failure Vectors Simulated:[/bold yellow]")
161
+ for vec in vectors:
162
+ content.append(f" [yellow]•[/yellow] {vec}")
163
+ else:
164
+ content.append("\n[dim]No significant failure vectors or edge-case gaps simulated.[/dim]")
165
+
166
+ self.console.print(
167
+ Panel(
168
+ "\n".join(content),
169
+ title="[bold]⚡ Challenger Adversarial Check[/bold]",
170
+ border_style=border_style,
171
+ box=ROUNDED,
172
+ expand=True
173
+ )
174
+ )
175
+
176
+ def render_arbitration_result(self, res: Any) -> None:
177
+ """Display calibrated confidence score, contradiction matches, and human-review flags."""
178
+ if isinstance(res, dict):
179
+ confidence = res.get("overall_confidence", 0.8)
180
+ review_required = res.get("requires_human_review", False)
181
+ flags = res.get("flags", [])
182
+ winning_claims = res.get("winning_claims", [])
183
+ synthesis_inst = res.get("synthesis_instructions", "")
184
+ else:
185
+ confidence = res.overall_confidence
186
+ review_required = res.requires_human_review
187
+ flags = res.flags
188
+ winning_claims = res.winning_claims
189
+ synthesis_inst = res.synthesis_instructions
190
+
191
+ # Color calibrated confidence based on score
192
+ if confidence > 0.75:
193
+ conf_str = f"[bold green]{confidence * 100:.1f}% (High Confidence)[/bold green]"
194
+ border_style = "green"
195
+ elif confidence > 0.55:
196
+ conf_str = f"[bold yellow]{confidence * 100:.1f}% (Medium Confidence)[/bold yellow]"
197
+ border_style = "yellow"
198
+ else:
199
+ conf_str = f"[bold red]{confidence * 100:.1f}% (Low Confidence / High Volatility)[/bold red]"
200
+ border_style = "red"
201
+
202
+ status_text = "[bold red]YES (Blocked / Escalate)[/bold red]" if review_required else "[bold green]NO (Autonomous Pass)[/bold green]"
203
+
204
+ content = []
205
+ content.append(f"[bold]Calibrated Council Confidence Score:[/bold] {conf_str}")
206
+ content.append(f"[bold]Escalate to Human-in-the-Loop Review:[/bold] {status_text}")
207
+
208
+ if flags:
209
+ content.append(f"[bold red]System Flags Raised:[/bold red] {', '.join(flags)}")
210
+
211
+ if winning_claims:
212
+ content.append("\n[bold cyan]Winning Claims & Arbitration Compromise:[/bold cyan]")
213
+ for claim in winning_claims:
214
+ content.append(f" [cyan]✓[/cyan] {claim}")
215
+
216
+ if synthesis_inst:
217
+ content.append("\n[bold dim]Arbitrator Instructions for Synthesizer:[/bold dim]")
218
+ content.append(f"[dim]{synthesis_inst}[/dim]")
219
+
220
+ self.console.print(
221
+ Panel(
222
+ "\n".join(content),
223
+ title="[bold white]⚖️ Council Arbitration Engine[/bold white]",
224
+ border_style=border_style,
225
+ box=ROUNDED,
226
+ expand=True
227
+ )
228
+ )
229
+
230
+ def render_synthesized_response(self, text: str) -> None:
231
+ """Display final walker walkthrough summary and accomplishments."""
232
+ self.console.print(
233
+ Panel(
234
+ Text(text, style="white"),
235
+ title="[bold magenta]🚀 Deliberated Walkthrough & Accomplishments[/bold magenta]",
236
+ border_style="magenta",
237
+ box=ROUNDED,
238
+ expand=True
239
+ )
240
+ )
@@ -0,0 +1,93 @@
1
+ """Rich terminal rendering of hierarchical memory tiers, priority decays, and graph entities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from rich.box import ROUNDED
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+ from rich.tree import Tree
13
+
14
+
15
+ class MemoryDisplayView:
16
+ """Beautiful Rich-based UI components to visualize Velune's 5-tier Hierarchical Memory system."""
17
+
18
+ def __init__(self, console: Console) -> None:
19
+ self.console = console
20
+
21
+ def render_memory_architecture(self, stats: dict[str, Any]) -> None:
22
+ """Render a magnificent visual map of the memory tiers and active index statistics."""
23
+ self.console.print(
24
+ Panel(
25
+ Text.assemble(
26
+ ("[bold magenta]VELUNE CORE HIERARCHICAL MEMORY MAP[/bold magenta]\n"),
27
+ ("[dim]Active Workspace:[/dim] [italic cyan]" + str(stats.get("workspace", "current")) + "[/italic cyan]\n\n"),
28
+ ("[bold yellow]Tier 1: Working Memory[/bold yellow] ──► In-memory state, fast lookups (TTL: " + str(stats.get("working_memory_ttl", 3600)) + "s)\n"),
29
+ ("[bold green]Tier 2: Episodic SQLite[/bold green] ──► Task runs, step histories (Retention: " + str(stats.get("episodic_retention_days", 30)) + " days)\n"),
30
+ ("[bold blue]Tier 3: Semantic Qdrant[/bold blue] ──► Vector code snippet indices (Similarity Threshold: " + str(stats.get("semantic_threshold", 0.85)) + ")\n"),
31
+ ("[bold cyan]Tier 4: Graphiti Graph[/bold cyan] ──► Entity relationships & AST Dependency Graph (Graphiti Enabled: " + str(stats.get("graph_enabled", True)) + ")\n"),
32
+ ("[bold red]Tier 5: Archive Storage[/bold red] ──► Long-term zstd-compressed cold files")
33
+ ),
34
+ title="[bold white]🧠 Memory Architecture Map[/bold white]",
35
+ border_style="magenta",
36
+ box=ROUNDED,
37
+ title_align="left"
38
+ )
39
+ )
40
+
41
+ def render_memory_records_table(self, records: list[dict[str, Any]], memory_type: str) -> None:
42
+ """Render a structured table showing registered records across specific memory tiers."""
43
+ table = Table(
44
+ title=f"[bold green]Registered Memory Records ({memory_type.capitalize()})[/bold green]",
45
+ box=ROUNDED,
46
+ border_style="green",
47
+ expand=True
48
+ )
49
+ table.add_column("Record ID / Key", style="bold cyan", width=25)
50
+ table.add_column("Memory Tier", style="magenta")
51
+ table.add_column("Importance Score", style="yellow")
52
+ table.add_column("Content Preview", style="white")
53
+ table.add_column("Age (s) / Status", style="blue")
54
+
55
+ for rec in records:
56
+ importance = rec.get("importance", 1.0)
57
+ importance_bar = "★" * int(importance * 5)
58
+ table.add_row(
59
+ rec.get("id", "N/A"),
60
+ rec.get("tier", memory_type),
61
+ f"{importance:.2f} ({importance_bar})",
62
+ rec.get("content_preview", ""),
63
+ rec.get("status", "Active")
64
+ )
65
+ self.console.print(table)
66
+ self.console.print()
67
+
68
+ def render_knowledge_graph(self, entities: list[dict[str, Any]], relations: list[dict[str, Any]]) -> None:
69
+ """Render a beautiful hierarchical tree of knowledge graph entities and their relational links."""
70
+ root = Tree("[bold cyan]🌐 Graphiti Knowledge Graph Root[/bold cyan]")
71
+
72
+ # Index entities by type for rendering
73
+ by_type: dict[str, list[dict[str, Any]]] = {}
74
+ for ent in entities:
75
+ etype = ent.get("type", "entity").upper()
76
+ if etype not in by_type:
77
+ by_type[etype] = []
78
+ by_type[etype].append(ent)
79
+
80
+ for etype, items in by_type.items():
81
+ type_node = root.add(f"[bold yellow]🏷️ {etype}[/bold yellow]")
82
+ for item in items:
83
+ name = item.get("name", item.get("id", "Unknown"))
84
+ importance = item.get("importance", 1.0)
85
+ item_node = type_node.add(f"[cyan]{name}[/cyan] [dim](imp: {importance:.2f})[/dim]")
86
+
87
+ # Find relations where this item is the source
88
+ for rel in relations:
89
+ if rel.get("source") == item.get("id"):
90
+ item_node.add(f"──[magenta]{rel.get('relation', 'connected')}[/magenta]──► [white]{rel.get('target')}[/white]")
91
+
92
+ self.console.print(Panel(root, title="[bold white]Knowledge Graph Visualization[/bold white]", border_style="cyan", box=ROUNDED))
93
+ self.console.print()
@@ -0,0 +1,35 @@
1
+ """Rich-based terminal panels."""
2
+
3
+ from rich.console import Console
4
+ from rich.panel import Panel
5
+ from rich.table import Table
6
+
7
+
8
+ class DisplayPanels:
9
+ """Utility class for creating rich display panels."""
10
+
11
+ def __init__(self, console: Console):
12
+ self.console = console
13
+
14
+ def info_panel(self, title: str, content: str) -> None:
15
+ """Display an info panel."""
16
+ self.console.print(Panel(content, title=title, border_style="blue"))
17
+
18
+ def success_panel(self, title: str, content: str) -> None:
19
+ """Display a success panel."""
20
+ self.console.print(Panel(content, title=title, border_style="green"))
21
+
22
+ def warning_panel(self, title: str, content: str) -> None:
23
+ """Display a warning panel."""
24
+ self.console.print(Panel(content, title=title, border_style="yellow"))
25
+
26
+ def error_panel(self, title: str, content: str) -> None:
27
+ """Display an error panel."""
28
+ self.console.print(Panel(content, title=title, border_style="red"))
29
+
30
+ def create_table(self, title: str, columns: list[str]) -> Table:
31
+ """Create a rich table."""
32
+ table = Table(title=title)
33
+ for column in columns:
34
+ table.add_column(column)
35
+ return table
@@ -0,0 +1,25 @@
1
+ """Rich-based progress display."""
2
+
3
+ from rich.console import Console
4
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
5
+
6
+
7
+ class ProgressDisplay:
8
+ """Utility class for displaying progress."""
9
+
10
+ def __init__(self, console: Console):
11
+ self.console = console
12
+
13
+ def create_progress(self) -> Progress:
14
+ """Create a rich progress bar."""
15
+ return Progress(
16
+ SpinnerColumn(),
17
+ TextColumn("[progress.description]{task.description}"),
18
+ BarColumn(),
19
+ TaskProgressColumn(),
20
+ console=self.console,
21
+ )
22
+
23
+ def spinner(self, description: str):
24
+ """Create a spinner context manager."""
25
+ return self.console.status(description)
@@ -0,0 +1,21 @@
1
+ """Rich terminal themes."""
2
+
3
+ from rich.theme import Theme
4
+
5
+
6
+ class VeluneTheme:
7
+ """Velune CLI theme."""
8
+
9
+ @staticmethod
10
+ def get_theme() -> Theme:
11
+ """Get the Velune theme."""
12
+ return Theme({
13
+ "info": "cyan",
14
+ "warning": "yellow",
15
+ "error": "red",
16
+ "success": "green",
17
+ "title": "bold blue",
18
+ "subtitle": "bold cyan",
19
+ "key": "magenta",
20
+ "value": "green",
21
+ })
velune/cli/main.py ADDED
@@ -0,0 +1,15 @@
1
+ """Backward-compatible CLI entry point."""
2
+
3
+ import sys
4
+
5
+ if sys.platform == "win32":
6
+ try:
7
+ sys.stdout.reconfigure(encoding="utf-8")
8
+ sys.stderr.reconfigure(encoding="utf-8")
9
+ except Exception:
10
+ pass
11
+
12
+ from velune.cli.app import app
13
+
14
+ if __name__ == "__main__":
15
+ app()
@@ -0,0 +1,44 @@
1
+ from velune.cli.modes import ModeConfig
2
+ from velune.core.types.model import ModelDescriptor
3
+
4
+
5
+ class ModeAwareModelSelector:
6
+ def __init__(self, model_registry, provider_registry) -> None:
7
+ self.model_registry = model_registry
8
+ self.provider_registry = provider_registry
9
+
10
+ def select_for_mode(
11
+ self,
12
+ config: ModeConfig,
13
+ current_model: ModelDescriptor | None,
14
+ ) -> ModelDescriptor | None:
15
+ all_models = [
16
+ m for m in self.model_registry.list_all()
17
+ if self.provider_registry.get(m.provider_id) is not None
18
+ ]
19
+ if not all_models:
20
+ return current_model
21
+
22
+ if config.use_fastest_model:
23
+ # Smallest context window = fastest / lightest
24
+ # Prefer local 3B/7B over cloud in optimus
25
+ local = [m for m in all_models if m.is_local]
26
+ pool = local if local else all_models
27
+ return min(pool, key=lambda m: m.context_length)
28
+
29
+ if config.use_largest_model:
30
+ # Largest context + highest capability score
31
+ def capability_score(m: ModelDescriptor) -> int:
32
+ caps = m.capabilities
33
+ if not caps:
34
+ return 0
35
+ return sum([
36
+ getattr(caps, f, 0).value if hasattr(
37
+ getattr(caps, f, None), "value"
38
+ ) else 0
39
+ for f in ["coding", "reasoning", "planning",
40
+ "summarization", "instruction_following"]
41
+ ])
42
+ return max(all_models, key=capability_score)
43
+
44
+ return current_model