k-cli-for-devs 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 (75) hide show
  1. k_cli/__init__.py +77 -0
  2. k_cli/agents/__init__.py +0 -0
  3. k_cli/agents/adversarial_swarm.py +338 -0
  4. k_cli/agents/agent_core.py +255 -0
  5. k_cli/agents/background_daemon.py +141 -0
  6. k_cli/agents/orchestrator.py +376 -0
  7. k_cli/agents/persona.py +649 -0
  8. k_cli/agents/scaffold_engine.py +121 -0
  9. k_cli/agents/strands_agent.py +832 -0
  10. k_cli/agents/subagents.py +1496 -0
  11. k_cli/cli.py +3297 -0
  12. k_cli/core/__init__.py +0 -0
  13. k_cli/core/airgap.py +95 -0
  14. k_cli/core/credentials.py +548 -0
  15. k_cli/core/intent_sensor.py +177 -0
  16. k_cli/core/llm_driver.py +1028 -0
  17. k_cli/core/model_manager.py +1109 -0
  18. k_cli/core/models_hub.py +913 -0
  19. k_cli/core/prompting.py +41 -0
  20. k_cli/core/sdk.py +322 -0
  21. k_cli/core/session.py +826 -0
  22. k_cli/core/smart_router.py +230 -0
  23. k_cli/core/storage_manager.py +176 -0
  24. k_cli/core/viewport_engine.py +117 -0
  25. k_cli/demo/demo_runner.py +579 -0
  26. k_cli/git/__init__.py +0 -0
  27. k_cli/git/ai_bisect.py +208 -0
  28. k_cli/git/conflict_resolver.py +1039 -0
  29. k_cli/git/git_guard.py +417 -0
  30. k_cli/git/patcher.py +1175 -0
  31. k_cli/git/repo_map.py +1780 -0
  32. k_cli/git/smart_git.py +928 -0
  33. k_cli/git/verifier.py +969 -0
  34. k_cli/github/__init__.py +0 -0
  35. k_cli/github/dedup_engine.py +787 -0
  36. k_cli/github/github_client.py +1702 -0
  37. k_cli/github/github_engine.py +641 -0
  38. k_cli/github/local_hub.py +209 -0
  39. k_cli/github/pr_watcher.py +129 -0
  40. k_cli/github/trending.py +205 -0
  41. k_cli/tools/__init__.py +0 -0
  42. k_cli/tools/audit.py +79 -0
  43. k_cli/tools/chaos_immunity.py +377 -0
  44. k_cli/tools/codebase_qa.py +106 -0
  45. k_cli/tools/command_runner.py +256 -0
  46. k_cli/tools/diagram_generator.py +547 -0
  47. k_cli/tools/doc_retriever.py +1332 -0
  48. k_cli/tools/feature.py +105 -0
  49. k_cli/tools/ghost_daemon.py +122 -0
  50. k_cli/tools/incident_triage.py +1365 -0
  51. k_cli/tools/mcp_client.py +1846 -0
  52. k_cli/tools/repo_gardener.py +142 -0
  53. k_cli/tools/rules.py +109 -0
  54. k_cli/tools/security.py +52 -0
  55. k_cli/tools/security_healer.py +999 -0
  56. k_cli/tools/synapse_graph.py +155 -0
  57. k_cli/tui/__init__.py +0 -0
  58. k_cli/tui/diff_viewer.py +223 -0
  59. k_cli/tui/tui.py +1145 -0
  60. k_cli/tui/tui_animations.py +648 -0
  61. k_cli/tui/tui_app.py +2788 -0
  62. k_cli/ui/__init__.py +10 -0
  63. k_cli/ui/simple_repl.py +315 -0
  64. k_cli/web/__init__.py +7 -0
  65. k_cli/web/server.py +624 -0
  66. k_cli/web/static/app.js +830 -0
  67. k_cli/web/static/index.html +495 -0
  68. k_cli/web/static/monitor.html +189 -0
  69. k_cli/web/static/style.css +838 -0
  70. k_cli_for_devs-1.0.0.dist-info/METADATA +461 -0
  71. k_cli_for_devs-1.0.0.dist-info/RECORD +75 -0
  72. k_cli_for_devs-1.0.0.dist-info/WHEEL +5 -0
  73. k_cli_for_devs-1.0.0.dist-info/entry_points.txt +2 -0
  74. k_cli_for_devs-1.0.0.dist-info/licenses/LICENSE +21 -0
  75. k_cli_for_devs-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,648 @@
1
+ """
2
+ tui_animations.py - Premium Cyberpunk & Neon Visual Experience Engine for K-CLI
3
+ Project Bankai Engine v1.0.0
4
+
5
+ Features:
6
+ 1. Cyberpunk / Neon ASCII banners, logo splash with smooth multi-color gradient rendering.
7
+ 2. Smooth animated spinners & loaders (Radar pulse, Quantum flux, Neon orbit, Cyber matrix cascade, Hex pulse).
8
+ 3. Live Token Speedometer & Real-time Cost Ticker ($ USD calculation based on model token usage).
9
+ 4. Dynamic Status Glow Badges (Git branch, Active Model, Verifier Status, MCP Server count, RAM RSS).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import math
15
+ import os
16
+ import sys
17
+ import time
18
+ from dataclasses import dataclass, field
19
+ from enum import Enum
20
+ from typing import Any, Dict, Generator, List, Optional, Tuple, Union
21
+
22
+ from rich.console import Console, RenderableType
23
+ from rich.panel import Panel
24
+ from rich.style import Style
25
+ from rich.table import Table
26
+ from rich.text import Text
27
+
28
+
29
+ # ==============================================================================
30
+ # 1. Cyberpunk Palettes & Gradient Color Interpolation Engine
31
+ # ==============================================================================
32
+
33
+ CYBER_PALETTES: Dict[str, List[str]] = {
34
+ "neon_cyan": ["#00f0ff", "#00b4d8", "#7000ff", "#d946ef", "#ff007f"],
35
+ "cyber_pink": ["#ff007f", "#ff0055", "#ff5500", "#ffe600"],
36
+ "matrix_green": ["#5af78e", "#00ff41", "#00e676", "#00c853", "#008f11"],
37
+ "quantum_purple": ["#b026ff", "#7000ff", "#00f0ff", "#ff007f"],
38
+ "synthwave": ["#ff71ce", "#01cdfe", "#05ffa1", "#b967ff", "#fffb96"],
39
+ "gold_amber": ["#ffe600", "#ffaa00", "#ff7700", "#ff3300"],
40
+ "laser_blue": ["#00ffff", "#0080ff", "#0000ff", "#8000ff"],
41
+ "blood_neon": ["#ff0055", "#ff3366", "#cc0033", "#990000"],
42
+ }
43
+
44
+
45
+ def hex_to_rgb(hex_str: str) -> Tuple[int, int, int]:
46
+ """Converts hex string (#RRGGBB or RRGGBB) to (r, g, b) tuple."""
47
+ clean = hex_str.lstrip("#")
48
+ if len(clean) == 3:
49
+ clean = "".join(c * 2 for c in clean)
50
+ if len(clean) != 6:
51
+ return 0, 240, 255
52
+ try:
53
+ r = int(clean[0:2], 16)
54
+ g = int(clean[2:4], 16)
55
+ b = int(clean[4:6], 16)
56
+ return r, g, b
57
+ except ValueError:
58
+ return 0, 240, 255
59
+
60
+
61
+ def rgb_to_hex(r: int, g: int, b: int) -> str:
62
+ """Converts RGB integers (0-255) to hex string (#RRGGBB)."""
63
+ r_clamped = max(0, min(255, int(r)))
64
+ g_clamped = max(0, min(255, int(g)))
65
+ b_clamped = max(0, min(255, int(b)))
66
+ return f"#{r_clamped:02x}{g_clamped:02x}{b_clamped:02x}"
67
+
68
+
69
+ def interpolate_color(hex1: str, hex2: str, factor: float) -> str:
70
+ """Linearly interpolates between two hex colors by factor (0.0 to 1.0)."""
71
+ factor = max(0.0, min(1.0, factor))
72
+ r1, g1, b1 = hex_to_rgb(hex1)
73
+ r2, g2, b2 = hex_to_rgb(hex2)
74
+ r = r1 + (r2 - r1) * factor
75
+ g = g1 + (g2 - g1) * factor
76
+ b = b1 + (b2 - b1) * factor
77
+ return rgb_to_hex(int(r), int(g), int(b))
78
+
79
+
80
+ def generate_gradient_colors(palette: Union[str, List[str]], steps: int) -> List[str]:
81
+ """
82
+ Generates a list of interpolated hex color codes of length `steps` across palette stops.
83
+ """
84
+ if steps <= 0:
85
+ return []
86
+ if isinstance(palette, str):
87
+ colors = CYBER_PALETTES.get(palette, CYBER_PALETTES["neon_cyan"])
88
+ else:
89
+ colors = palette or CYBER_PALETTES["neon_cyan"]
90
+
91
+ if len(colors) == 1 or steps == 1:
92
+ return [colors[0]] * steps
93
+
94
+ result: List[str] = []
95
+ num_segments = len(colors) - 1
96
+ steps_per_segment = steps / num_segments
97
+
98
+ for i in range(steps):
99
+ segment_idx = min(int(i / steps_per_segment), num_segments - 1)
100
+ sub_factor = (i - segment_idx * steps_per_segment) / steps_per_segment
101
+ c = interpolate_color(colors[segment_idx], colors[segment_idx + 1], sub_factor)
102
+ result.append(c)
103
+
104
+ return result
105
+
106
+
107
+ def apply_gradient_to_text(
108
+ text: str,
109
+ palette_name: str = "neon_cyan",
110
+ direction: str = "horizontal",
111
+ bold: bool = True,
112
+ ) -> Text:
113
+ """
114
+ Applies a smooth multi-color cyberpunk gradient to input string using Rich Text.
115
+ direction: 'horizontal', 'vertical', or 'diagonal'.
116
+ """
117
+ lines = text.split("\n")
118
+ rich_text = Text()
119
+
120
+ if direction == "vertical":
121
+ colors = generate_gradient_colors(palette_name, max(1, len(lines)))
122
+ for i, line in enumerate(lines):
123
+ style = Style(color=colors[i], bold=bold)
124
+ rich_text.append(line + ("\n" if i < len(lines) - 1 else ""), style=style)
125
+ return rich_text
126
+
127
+ # Horizontal or diagonal per character
128
+ max_len = max(len(l) for l in lines) if lines else 1
129
+ total_cols = max(1, max_len)
130
+
131
+ for l_idx, line in enumerate(lines):
132
+ if not line:
133
+ if l_idx < len(lines) - 1:
134
+ rich_text.append("\n")
135
+ continue
136
+
137
+ colors = generate_gradient_colors(
138
+ palette_name,
139
+ total_cols if direction == "horizontal" else (total_cols + len(lines)),
140
+ )
141
+
142
+ for c_idx, ch in enumerate(line):
143
+ idx = c_idx if direction == "horizontal" else (c_idx + l_idx)
144
+ color = colors[min(idx, len(colors) - 1)]
145
+ rich_text.append(ch, style=Style(color=color, bold=bold))
146
+
147
+ if l_idx < len(lines) - 1:
148
+ rich_text.append("\n")
149
+
150
+ return rich_text
151
+
152
+
153
+ # ==============================================================================
154
+ # 2. Cyberpunk ASCII Art Banners & Logo Splash
155
+ # ==============================================================================
156
+
157
+ CYBER_ASCII_BANNER_ART = r"""
158
+ ██╗ ██╗ ██████╗██╗ ██╗
159
+ ██║ ██╔╝ ██╔════╝██║ ██║
160
+ █████═╝ ██║ ██║ ██║
161
+ ██╔═██╗ ██║ ██║ ██║
162
+ ██║ ██╗ ╚██████╗███████╗██║
163
+ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═╝
164
+ """.strip("\n")
165
+
166
+ CYBER_LOGO_COMPACT = r"""
167
+ [ ⚡ K - C L I // P R O J E C T B A N K A I ⚡ ]
168
+ """
169
+
170
+ CYBER_ASCII_SUBAGENTS = r"""
171
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
172
+ │ EXPLORE │ ──▶ │ REFACTOR│ ──▶ │ TEST & │
173
+ │ SWARM │ │ ENGINE │ │ VERIFY │
174
+ └─────────┘ └─────────┘ └─────────┘
175
+ """.strip("\n")
176
+
177
+
178
+ def render_cyber_banner(
179
+ title: str = "K-CLI // AGENTIC WORKSTATION",
180
+ subtitle: str = "Compiler Guard • Verification-First Architecture",
181
+ palette: str = "neon_cyan",
182
+ border_style: str = "#00f0ff",
183
+ glitch_step: int = 0,
184
+ ) -> Panel:
185
+ """
186
+ Renders a glowing, gradient-rendered Cyberpunk banner with active status headers.
187
+ """
188
+ art_text = CYBER_ASCII_BANNER_ART
189
+ if glitch_step > 0:
190
+ # Subtle glitch effect on glyphs
191
+ glitch_chars = ["⚡", "◈", "◇", "█", "░", "▒"]
192
+ art_lines = art_text.split("\n")
193
+ mod_lines = []
194
+ for line in art_lines:
195
+ if line.strip():
196
+ g_char = glitch_chars[glitch_step % len(glitch_chars)]
197
+ mod_lines.append(line.replace("═", g_char).replace("╗", "╝" if glitch_step % 2 == 0 else "╗"))
198
+ else:
199
+ mod_lines.append(line)
200
+ art_text = "\n".join(mod_lines)
201
+
202
+ gradient_art = apply_gradient_to_text(art_text, palette_name=palette, direction="horizontal")
203
+
204
+ # Build stylized subtitle and status line
205
+ table = Table(box=None, expand=True, padding=(0, 0))
206
+ table.add_column("Art", justify="center")
207
+ table.add_row(gradient_art)
208
+ table.add_row(Text(f"─── {title} ───", style="bold #00f0ff", justify="center"))
209
+ table.add_row(Text(subtitle, style="dim white", justify="center"))
210
+
211
+ return Panel(
212
+ table,
213
+ border_style=border_style,
214
+ title="[bold #00f0ff]◈ K-CLI CYBER WORKSTATION ◈[/bold #00f0ff]",
215
+ subtitle="[dim #7000ff]Bankai v1.0.0 • Online[/dim #7000ff]",
216
+ )
217
+
218
+
219
+ def generate_splash_frames(steps: int = 8, palette: str = "neon_cyan") -> List[Panel]:
220
+ """Generates animated splash frames for startup sequences or REPL transitions."""
221
+ frames: List[Panel] = []
222
+ palettes = list(CYBER_PALETTES.keys())
223
+ for s in range(steps):
224
+ cur_pal = palettes[s % len(palettes)] if palette == "rainbow" else palette
225
+ frame = render_cyber_banner(
226
+ title=f"K-CLI // SYSTEM BOOT [{s+1}/{steps}]",
227
+ subtitle="Ground-Truth Compiler Verification Active",
228
+ palette=cur_pal,
229
+ glitch_step=s if s % 2 == 1 else 0,
230
+ )
231
+ frames.append(frame)
232
+ return frames
233
+
234
+
235
+ # ==============================================================================
236
+ # 3. Smooth Animated Spinners & Cyber Loaders
237
+ # ==============================================================================
238
+
239
+ class SpinnerType(str, Enum):
240
+ RADAR_PULSE = "radar_pulse"
241
+ QUANTUM_FLUX = "quantum_flux"
242
+ NEON_ORBIT = "neon_orbit"
243
+ CYBER_MATRIX = "cyber_matrix"
244
+ HEX_PULSE = "hex_pulse"
245
+ SYNTH_BARS = "synth_bars"
246
+
247
+
248
+ SPINNER_GLYPH_SETS: Dict[SpinnerType, List[str]] = {
249
+ SpinnerType.RADAR_PULSE: [
250
+ "📡 ⠋ [SCANNING »» ]",
251
+ "📡 ⠙ [SCANNING »»» ]",
252
+ "📡 ⠹ [SCANNING »»» ]",
253
+ "📡 ⠸ [LOCKED »»» ]",
254
+ "📡 ⠼ [PULSE •»» ]",
255
+ "📡 ⠴ [SWEEP ••» ]",
256
+ "📡 ⠦ [RANGE ••• ]",
257
+ "📡 ⠧ [ECHO »•• ]",
258
+ "📡 ⠇ [PING »»• ]",
259
+ "📡 ⠏ [TARGET »»» ]",
260
+ ],
261
+ SpinnerType.QUANTUM_FLUX: [
262
+ "⟨ψ| ∿∿∿ |φ⟩",
263
+ "⟨.ψ| ∾∾∾ |φ.⟩",
264
+ "⟨..ψ| ⚡∿⚡ |φ..⟩",
265
+ "⟨...ψ| ✧∾✦ |φ...⟩",
266
+ "⟨....ψ| ░▒▓ |φ....⟩",
267
+ "⟨...ψ| ▒▓█ |φ...⟩",
268
+ "⟨..ψ| ▓██ |φ..⟩",
269
+ "⟨.ψ| █▓▒ |φ.⟩",
270
+ ],
271
+ SpinnerType.NEON_ORBIT: [
272
+ "🪐 ⠋ ✦ Orbit Alpha",
273
+ "💫 ⠙ ✧ Orbit Beta",
274
+ "✨ ⠚ ✦ Orbit Gamma",
275
+ "🌟 ⠞ ✧ Orbit Delta",
276
+ "⚡ ⠖ ✦ Flux Sync",
277
+ "🔥 ⠦ ✧ Node Pulse",
278
+ "💎 ⠴ ✦ Core Cycle",
279
+ "🔮 ⠲ ✧ Energy Weave",
280
+ "🌀 ⠳ ✦ Vortex Gate",
281
+ "💠 ⠓ ✧ Matrix Mesh",
282
+ ],
283
+ SpinnerType.CYBER_MATRIX: [
284
+ "1010110 ░ 010101",
285
+ "0110101 ▒ 101010",
286
+ "1101001 ▓ 010101",
287
+ "0011110 █ 110011",
288
+ "1010011 ▓ 001100",
289
+ "0101100 ▒ 111001",
290
+ "1110001 ░ 000111",
291
+ ],
292
+ SpinnerType.HEX_PULSE: [
293
+ "⬡ ⬡ ⬡ [0x0000]",
294
+ "⬢ ⬡ ⬡ [0x00F0]",
295
+ "⬡ ⬢ ⬡ [0x0FF0]",
296
+ "⬡ ⬡ ⬢ [0xFFFF]",
297
+ "⬢ ⬢ ⬡ [0x7000]",
298
+ "⬡ ⬢ ⬢ [0xB026]",
299
+ "⬢ ⬢ ⬢ [0xBANK]",
300
+ ],
301
+ SpinnerType.SYNTH_BARS: [
302
+ " ▃▄▅▆▇█ [FLUX 10%]",
303
+ "▃▄▅▆▇█▇ [FLUX 35%]",
304
+ "▄▅▆▇█▇▆ [FLUX 60%]",
305
+ "▅▆▇█▇▆▅ [FLUX 85%]",
306
+ "▆▇█▇▆▅▄ [FLUX 99%]",
307
+ "▇█▇▆▅▄▃ [FLUX 100%]",
308
+ "█▇▆▅▄▃ [FLUX SYNC]",
309
+ ],
310
+ }
311
+
312
+
313
+ class AnimatedSpinner:
314
+ """
315
+ Rich-compatible Cyberpunk animated spinner with custom neon glyph sets,
316
+ pulsating color cycles, and frame generator methods.
317
+ """
318
+
319
+ def __init__(
320
+ self,
321
+ spinner_type: Union[SpinnerType, str] = SpinnerType.RADAR_PULSE,
322
+ label: str = "Processing...",
323
+ palette: str = "neon_cyan",
324
+ ):
325
+ if isinstance(spinner_type, str):
326
+ try:
327
+ self.spinner_type = SpinnerType(spinner_type)
328
+ except ValueError:
329
+ self.spinner_type = SpinnerType.RADAR_PULSE
330
+ else:
331
+ self.spinner_type = spinner_type
332
+
333
+ self.label = label
334
+ self.palette = palette
335
+ self.frames = SPINNER_GLYPH_SETS.get(self.spinner_type, SPINNER_GLYPH_SETS[SpinnerType.RADAR_PULSE])
336
+
337
+ def get_frame(self, step: int) -> Text:
338
+ """Returns stylized Text for the frame at given step index."""
339
+ raw_glyph = self.frames[step % len(self.frames)]
340
+ colors = generate_gradient_colors(self.palette, max(1, len(self.frames)))
341
+ color = colors[step % len(colors)]
342
+
343
+ text = Text()
344
+ text.append(raw_glyph, style=Style(color=color, bold=True))
345
+ text.append(f" {self.label}", style=Style(color="#e2e8f0", bold=False))
346
+ return text
347
+
348
+ def get_all_frames(self) -> List[str]:
349
+ """Returns raw glyph frames."""
350
+ return list(self.frames)
351
+
352
+ def render(self, step: int) -> Text:
353
+ """Alias for get_frame to allow direct Rich rendering."""
354
+ return self.get_frame(step)
355
+
356
+
357
+ # ==============================================================================
358
+ # 4. Live Token Speedometer & Real-Time Cost Ticker ($ USD)
359
+ # ==============================================================================
360
+
361
+ # Pricing Catalog: (prompt_price_per_1m, completion_price_per_1m) in USD
362
+ MODEL_PRICING_CATALOG: Dict[str, Tuple[float, float]] = {
363
+ # Project Bankai Local SLMs & GGUF (100% Free / Local Compute)
364
+ "bankai-7b": (0.00, 0.00),
365
+ "bankai-14b": (0.00, 0.00),
366
+ "qwen2.5-coder:1.5b": (0.00, 0.00),
367
+ "qwen2.5-coder:7b": (0.00, 0.00),
368
+ "qwen2.5-coder:14b": (0.00, 0.00),
369
+ "local ollama": (0.00, 0.00),
370
+ "ollama": (0.00, 0.00),
371
+ # Cloud Models
372
+ "gemini": (0.075, 0.30),
373
+ "gemini-2.0-flash": (0.075, 0.30),
374
+ "gemini-2.0-pro": (1.25, 5.00),
375
+ "claude": (3.00, 15.00),
376
+ "claude-3-5-sonnet": (3.00, 15.00),
377
+ "claude-3-opus": (15.00, 75.00),
378
+ "gpt-4o": (2.50, 10.00),
379
+ "gpt-4o-mini": (0.15, 0.60),
380
+ }
381
+
382
+
383
+ def calculate_token_cost(model_name: str, prompt_tokens: int, completion_tokens: int) -> float:
384
+ """
385
+ Calculates USD cost for a given model and token usage.
386
+ Returns $0.00 for local SLMs/GGUFs.
387
+ """
388
+ m_clean = (model_name or "bankai-7b").lower().strip()
389
+ prompt_rate, compl_rate = (0.00, 0.00)
390
+
391
+ for key, rates in MODEL_PRICING_CATALOG.items():
392
+ if key in m_clean or m_clean in key:
393
+ prompt_rate, compl_rate = rates
394
+ break
395
+
396
+ cost = (prompt_tokens / 1_000_000.0) * prompt_rate + (completion_tokens / 1_000_000.0) * compl_rate
397
+ return round(cost, 6)
398
+
399
+
400
+ class TokenSpeedometer:
401
+ """
402
+ Real-time Token Throughput Speedometer.
403
+ Tracks instantaneous tok/s, peak speed, rolling average, and renders visual RPM gauge.
404
+ """
405
+
406
+ def __init__(self, target_max_speed: float = 200.0):
407
+ self.target_max_speed = max(10.0, target_max_speed)
408
+ self.token_history: List[Tuple[float, int]] = [] # (timestamp, delta_tokens)
409
+ self.total_tokens: int = 0
410
+ self.peak_speed: float = 0.0
411
+ self.start_time: float = time.time()
412
+ self.last_update_time: float = self.start_time
413
+
414
+ def record_tokens(self, count: int) -> None:
415
+ """Records addition of generated tokens at current timestamp."""
416
+ now = time.time()
417
+ self.token_history.append((now, count))
418
+ self.total_tokens += count
419
+ self.last_update_time = now
420
+
421
+ # Prune history older than 5.0 seconds for rolling speed
422
+ cutoff = now - 5.0
423
+ self.token_history = [item for item in self.token_history if item[0] >= cutoff]
424
+
425
+ # Calculate current rolling speed and update peak
426
+ speed = self.get_current_speed()
427
+ if speed > self.peak_speed:
428
+ self.peak_speed = speed
429
+
430
+ def get_current_speed(self) -> float:
431
+ """Calculates rolling tokens per second over recent window."""
432
+ if not self.token_history:
433
+ return 0.0
434
+ now = time.time()
435
+ window_start = self.token_history[0][0]
436
+ duration = max(0.05, now - window_start)
437
+ recent_tokens = sum(cnt for _, cnt in self.token_history)
438
+ return round(recent_tokens / duration, 1)
439
+
440
+ def get_average_speed(self) -> float:
441
+ """Calculates cumulative average tokens per second since inception."""
442
+ total_time = max(0.05, time.time() - self.start_time)
443
+ return round(self.total_tokens / total_time, 1)
444
+
445
+ def render_gauge(self, width: int = 16, style: str = "neon") -> Text:
446
+ """
447
+ Renders a cyber speedometer visual gauge bar with speed readout.
448
+ """
449
+ speed = self.get_current_speed()
450
+ pct = min(1.0, speed / self.target_max_speed)
451
+ filled_bars = int(pct * width)
452
+ empty_bars = width - filled_bars
453
+
454
+ # Dynamic speed color tiers
455
+ if speed < 30.0:
456
+ bar_color = "#00f0ff"
457
+ elif speed < 80.0:
458
+ bar_color = "#5af78e"
459
+ elif speed < 140.0:
460
+ bar_color = "#ffe600"
461
+ else:
462
+ bar_color = "#ff007f"
463
+
464
+ gauge = Text()
465
+ gauge.append("⚡ ", style="bold #ffe600")
466
+ gauge.append(f"{speed:5.1f} tok/s ", style=f"bold {bar_color}")
467
+ gauge.append("[", style="dim white")
468
+ gauge.append("█" * filled_bars, style=f"bold {bar_color}")
469
+ gauge.append("░" * empty_bars, style="dim #1e293b")
470
+ gauge.append("]", style="dim white")
471
+ gauge.append(f" (peak: {self.peak_speed:.0f})", style="dim #94a3b8")
472
+ return gauge
473
+
474
+
475
+ class CostTicker:
476
+ """
477
+ Real-time USD Cost Ticker tracking cumulative session spending by model.
478
+ """
479
+
480
+ def __init__(self, active_model: str = "Bankai-7B"):
481
+ self.active_model = active_model
482
+ self.total_prompt_tokens: int = 0
483
+ self.total_completion_tokens: int = 0
484
+ self.model_breakdown: Dict[str, Tuple[int, int]] = {}
485
+
486
+ def record_usage(self, model_name: str, prompt_tokens: int, completion_tokens: int) -> None:
487
+ """Records token usage for a model."""
488
+ self.active_model = model_name
489
+ self.total_prompt_tokens += prompt_tokens
490
+ self.total_completion_tokens += completion_tokens
491
+
492
+ prev_p, prev_c = self.model_breakdown.get(model_name, (0, 0))
493
+ self.model_breakdown[model_name] = (prev_p + prompt_tokens, prev_c + completion_tokens)
494
+
495
+ @property
496
+ def total_cost(self) -> float:
497
+ """Calculates total cumulative USD cost across all recorded models."""
498
+ total = 0.0
499
+ for m_name, (p_cnt, c_cnt) in self.model_breakdown.items():
500
+ total += calculate_token_cost(m_name, p_cnt, c_cnt)
501
+ return round(total, 6)
502
+
503
+ def render_ticker(self, compact: bool = False) -> Text:
504
+ """
505
+ Renders glowing USD cost ticker text.
506
+ """
507
+ cost = self.total_cost
508
+ tot_tokens = self.total_prompt_tokens + self.total_completion_tokens
509
+ is_free = (cost == 0.0)
510
+
511
+ ticker = Text()
512
+ ticker.append("💰 ", style="bold #ffe600")
513
+
514
+ if is_free:
515
+ ticker.append("$0.00 USD (LOCAL FREE)", style="bold #5af78e")
516
+ else:
517
+ ticker.append(f"${cost:.5f} USD", style="bold #00f0ff")
518
+
519
+ if not compact:
520
+ ticker.append(f" │ 📊 {tot_tokens:,} tokens", style="dim white")
521
+ ticker.append(f" │ 🤖 {self.active_model}", style="dim #94a3b8")
522
+
523
+ return ticker
524
+
525
+
526
+ # ==============================================================================
527
+ # 5. Dynamic Status Glow Badges
528
+ # ==============================================================================
529
+
530
+ class GlowBadgeStatus(str, Enum):
531
+ ONLINE = "online"
532
+ ACTIVE = "active"
533
+ SUCCESS = "success"
534
+ WARNING = "warning"
535
+ ERROR = "error"
536
+ IDLE = "idle"
537
+ INFO = "info"
538
+
539
+
540
+ STATUS_COLORS: Dict[GlowBadgeStatus, Tuple[str, str, str]] = {
541
+ # (fg_color, bg_color, border_color)
542
+ GlowBadgeStatus.ONLINE: ("#00ff88", "#082d1c", "#00ff88"),
543
+ GlowBadgeStatus.ACTIVE: ("#00f0ff", "#092336", "#00f0ff"),
544
+ GlowBadgeStatus.SUCCESS: ("#5af78e", "#0a2612", "#5af78e"),
545
+ GlowBadgeStatus.WARNING: ("#ffe600", "#332b00", "#ffe600"),
546
+ GlowBadgeStatus.ERROR: ("#ff3366", "#360914", "#ff3366"),
547
+ GlowBadgeStatus.IDLE: ("#94a3b8", "#121a29", "#1e293b"),
548
+ GlowBadgeStatus.INFO: ("#b026ff", "#220d36", "#b026ff"),
549
+ }
550
+
551
+
552
+ @dataclass
553
+ class StatusGlowBadge:
554
+ """
555
+ Dynamic neon glow badge representation for terminal HUDs and Textual headers.
556
+ """
557
+ label: str
558
+ value: str
559
+ status: GlowBadgeStatus = GlowBadgeStatus.ACTIVE
560
+ icon: str = ""
561
+ glow_color: Optional[str] = None
562
+
563
+ def render(self) -> Text:
564
+ """Renders stylized Rich Text badge with neon border brackets."""
565
+ fg, bg, default_glow = STATUS_COLORS.get(self.status, STATUS_COLORS[GlowBadgeStatus.ACTIVE])
566
+ color = self.glow_color or fg
567
+
568
+ badge = Text()
569
+ badge.append("[", style="dim #1e293b")
570
+ if self.icon:
571
+ badge.append(f"{self.icon} ", style=f"bold {color}")
572
+ badge.append(f"{self.label}: ", style="dim white")
573
+ badge.append(f"{self.value}", style=f"bold {color}")
574
+ badge.append("]", style="dim #1e293b")
575
+ return badge
576
+
577
+ def render_as_tag(self) -> str:
578
+ """Returns string with Rich markup tags."""
579
+ fg, _, _ = STATUS_COLORS.get(self.status, STATUS_COLORS[GlowBadgeStatus.ACTIVE])
580
+ color = self.glow_color or fg
581
+ icon_str = f"{self.icon} " if self.icon else ""
582
+ return f"[{color}][bold]{icon_str}{self.label}: {self.value}[/bold][/{color}]"
583
+
584
+
585
+ def create_branch_badge(branch_name: str = "main", is_dirty: bool = False) -> StatusGlowBadge:
586
+ """Creates a Git Branch glow badge."""
587
+ val = f"{branch_name}{'*' if is_dirty else ''}"
588
+ stat = GlowBadgeStatus.WARNING if is_dirty else GlowBadgeStatus.ONLINE
589
+ return StatusGlowBadge(label="Git", value=val, status=stat, icon="🌿")
590
+
591
+
592
+ def create_model_badge(model_name: str = "Bankai-7B", is_active: bool = True) -> StatusGlowBadge:
593
+ """Creates an Active Model glow badge."""
594
+ stat = GlowBadgeStatus.ACTIVE if is_active else GlowBadgeStatus.IDLE
595
+ return StatusGlowBadge(label="Model", value=model_name, status=stat, icon="🤖")
596
+
597
+
598
+ def create_verifier_badge(
599
+ status: str = "PASS",
600
+ pass_rate: float = 1.0,
601
+ attempts: int = 1,
602
+ ) -> StatusGlowBadge:
603
+ """Creates an AST/Compiler Verifier Status glow badge."""
604
+ if status.upper() in ("PASS", "VERIFIED", "OK"):
605
+ stat = GlowBadgeStatus.SUCCESS
606
+ icon = "🛡️"
607
+ val = f"VERIFIED ({int(pass_rate*100)}%)"
608
+ elif status.upper() in ("WARN", "RETRYING"):
609
+ stat = GlowBadgeStatus.WARNING
610
+ icon = "⚠️"
611
+ val = f"RETRY ({attempts})"
612
+ else:
613
+ stat = GlowBadgeStatus.ERROR
614
+ icon = "✘"
615
+ val = f"FAILED ({attempts})"
616
+
617
+ return StatusGlowBadge(label="Verifier", value=val, status=stat, icon=icon)
618
+
619
+
620
+ def create_mcp_badge(server_count: int = 0, active_tools: int = 0) -> StatusGlowBadge:
621
+ """Creates an MCP Server count and tool availability glow badge."""
622
+ stat = GlowBadgeStatus.INFO if server_count > 0 else GlowBadgeStatus.IDLE
623
+ val = f"{server_count} svr ({active_tools} tools)" if active_tools > 0 else f"{server_count} svr"
624
+ return StatusGlowBadge(label="MCP", value=val, status=stat, icon="🔌")
625
+
626
+
627
+ def create_ram_badge(ram_mb: float = 0.0, max_ram_mb: float = 1024.0) -> StatusGlowBadge:
628
+ """Creates a RAM Allocation RSS glow badge with budget threshold colors."""
629
+ pct = (ram_mb / max_ram_mb) * 100 if max_ram_mb > 0 else 0.0
630
+ if pct > 90.0:
631
+ stat = GlowBadgeStatus.ERROR
632
+ elif pct > 70.0:
633
+ stat = GlowBadgeStatus.WARNING
634
+ else:
635
+ stat = GlowBadgeStatus.ONLINE
636
+
637
+ val = f"{ram_mb:.1f}MB ({pct:.0f}%)"
638
+ return StatusGlowBadge(label="RAM", value=val, status=stat, icon="💾")
639
+
640
+
641
+ def render_hud_status_bar(badges: List[StatusGlowBadge]) -> Text:
642
+ """Combines multiple glow badges into a continuous horizontal HUD status bar."""
643
+ bar = Text()
644
+ for i, b in enumerate(badges):
645
+ bar.append_text(b.render())
646
+ if i < len(badges) - 1:
647
+ bar.append(" ")
648
+ return bar