devsquad 3.6.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.
- devsquad-3.6.0.dist-info/METADATA +944 -0
- devsquad-3.6.0.dist-info/RECORD +95 -0
- devsquad-3.6.0.dist-info/WHEEL +5 -0
- devsquad-3.6.0.dist-info/entry_points.txt +2 -0
- devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
- devsquad-3.6.0.dist-info/top_level.txt +2 -0
- scripts/__init__.py +0 -0
- scripts/ai_semantic_matcher.py +512 -0
- scripts/alert_manager.py +505 -0
- scripts/api/__init__.py +43 -0
- scripts/api/models.py +386 -0
- scripts/api/routes/__init__.py +20 -0
- scripts/api/routes/dispatch.py +348 -0
- scripts/api/routes/lifecycle.py +330 -0
- scripts/api/routes/metrics_gates.py +347 -0
- scripts/api_server.py +318 -0
- scripts/auth.py +451 -0
- scripts/cli/__init__.py +1 -0
- scripts/cli/cli_visual.py +642 -0
- scripts/cli.py +1094 -0
- scripts/collaboration/__init__.py +212 -0
- scripts/collaboration/_version.py +1 -0
- scripts/collaboration/agent_briefing.py +656 -0
- scripts/collaboration/ai_semantic_matcher.py +260 -0
- scripts/collaboration/anchor_checker.py +281 -0
- scripts/collaboration/anti_rationalization.py +470 -0
- scripts/collaboration/async_integration_example.py +255 -0
- scripts/collaboration/batch_scheduler.py +149 -0
- scripts/collaboration/checkpoint_manager.py +561 -0
- scripts/collaboration/ci_feedback_adapter.py +351 -0
- scripts/collaboration/code_map_generator.py +247 -0
- scripts/collaboration/concern_pack_loader.py +352 -0
- scripts/collaboration/confidence_score.py +496 -0
- scripts/collaboration/config_loader.py +188 -0
- scripts/collaboration/consensus.py +244 -0
- scripts/collaboration/context_compressor.py +533 -0
- scripts/collaboration/coordinator.py +668 -0
- scripts/collaboration/dispatcher.py +1636 -0
- scripts/collaboration/dual_layer_context.py +128 -0
- scripts/collaboration/enhanced_worker.py +539 -0
- scripts/collaboration/feature_usage_tracker.py +206 -0
- scripts/collaboration/five_axis_consensus.py +334 -0
- scripts/collaboration/input_validator.py +401 -0
- scripts/collaboration/integration_example.py +287 -0
- scripts/collaboration/intent_workflow_mapper.py +350 -0
- scripts/collaboration/language_parsers.py +269 -0
- scripts/collaboration/lifecycle_protocol.py +1446 -0
- scripts/collaboration/llm_backend.py +453 -0
- scripts/collaboration/llm_cache.py +448 -0
- scripts/collaboration/llm_cache_async.py +347 -0
- scripts/collaboration/llm_retry.py +387 -0
- scripts/collaboration/llm_retry_async.py +389 -0
- scripts/collaboration/mce_adapter.py +597 -0
- scripts/collaboration/memory_bridge.py +1607 -0
- scripts/collaboration/models.py +537 -0
- scripts/collaboration/null_providers.py +297 -0
- scripts/collaboration/operation_classifier.py +289 -0
- scripts/collaboration/output_slicer.py +225 -0
- scripts/collaboration/performance_monitor.py +462 -0
- scripts/collaboration/permission_guard.py +865 -0
- scripts/collaboration/prompt_assembler.py +756 -0
- scripts/collaboration/prompt_variant_generator.py +483 -0
- scripts/collaboration/protocols.py +267 -0
- scripts/collaboration/report_formatter.py +352 -0
- scripts/collaboration/retrospective.py +279 -0
- scripts/collaboration/role_matcher.py +92 -0
- scripts/collaboration/role_template_market.py +352 -0
- scripts/collaboration/rule_collector.py +678 -0
- scripts/collaboration/scratchpad.py +346 -0
- scripts/collaboration/skill_registry.py +151 -0
- scripts/collaboration/skillifier.py +878 -0
- scripts/collaboration/standardized_role_template.py +317 -0
- scripts/collaboration/task_completion_checker.py +237 -0
- scripts/collaboration/test_quality_guard.py +695 -0
- scripts/collaboration/unified_gate_engine.py +598 -0
- scripts/collaboration/usage_tracker.py +309 -0
- scripts/collaboration/user_friendly_error.py +176 -0
- scripts/collaboration/verification_gate.py +312 -0
- scripts/collaboration/warmup_manager.py +635 -0
- scripts/collaboration/worker.py +513 -0
- scripts/collaboration/workflow_engine.py +684 -0
- scripts/dashboard.py +1088 -0
- scripts/generate_benchmark_report.py +786 -0
- scripts/history_manager.py +604 -0
- scripts/mcp_server.py +289 -0
- skills/__init__.py +32 -0
- skills/dispatch/handler.py +52 -0
- skills/intent/handler.py +59 -0
- skills/registry.py +67 -0
- skills/retrospective/__init__.py +0 -0
- skills/retrospective/handler.py +125 -0
- skills/review/handler.py +356 -0
- skills/security/handler.py +454 -0
- skills/test/__init__.py +0 -0
- skills/test/handler.py +78 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
CLI Visual Enhancement Module for DevSquad V3.6.0
|
|
5
|
+
|
|
6
|
+
Provides rich visual output for lifecycle commands including:
|
|
7
|
+
- Colored progress bars
|
|
8
|
+
- Phase status icons
|
|
9
|
+
- Percentage displays
|
|
10
|
+
- Gate status visualization
|
|
11
|
+
- Beautiful formatted tables
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
from scripts.cli.cli_visual import VisualFormatter
|
|
15
|
+
|
|
16
|
+
vf = VisualFormatter()
|
|
17
|
+
vf.print_lifecycle_header(command, mapping, preset)
|
|
18
|
+
vf.print_phase_progress(phases, current_phase)
|
|
19
|
+
vf.print_gate_status(gate_result)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
from typing import Any, Dict, List, Optional
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Colors:
|
|
27
|
+
"""ANSI color codes for terminal output."""
|
|
28
|
+
RESET = '\033[0m'
|
|
29
|
+
BOLD = '\033[1m'
|
|
30
|
+
DIM = '\033[2m'
|
|
31
|
+
|
|
32
|
+
# Foreground colors
|
|
33
|
+
BLACK = '\033[30m'
|
|
34
|
+
RED = '\033[31m'
|
|
35
|
+
GREEN = '\033[32m'
|
|
36
|
+
YELLOW = '\033[33m'
|
|
37
|
+
BLUE = '\033[34m'
|
|
38
|
+
MAGENTA = '\033[35m'
|
|
39
|
+
CYAN = '\033[36m'
|
|
40
|
+
WHITE = '\033[37m'
|
|
41
|
+
|
|
42
|
+
# Background colors
|
|
43
|
+
BG_RED = '\033[41m'
|
|
44
|
+
BG_GREEN = '\033[42m'
|
|
45
|
+
BG_YELLOW = '\033[43m'
|
|
46
|
+
BG_BLUE = '\033[44m'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Icons:
|
|
50
|
+
"""Unicode icons for visual enhancement."""
|
|
51
|
+
# Phase states
|
|
52
|
+
CHECK = '✅'
|
|
53
|
+
CROSS = '❌'
|
|
54
|
+
ARROW_RIGHT = '▶'
|
|
55
|
+
CIRCLE = '⭕'
|
|
56
|
+
SKIP = '⏭️'
|
|
57
|
+
BLOCKED = '🚫'
|
|
58
|
+
RUNNING = '🔄'
|
|
59
|
+
|
|
60
|
+
# Status
|
|
61
|
+
SUCCESS = '✓'
|
|
62
|
+
FAIL = '✗'
|
|
63
|
+
WARNING = '⚠'
|
|
64
|
+
INFO = 'ℹ'
|
|
65
|
+
|
|
66
|
+
# Objects
|
|
67
|
+
FILE = '📄'
|
|
68
|
+
FOLDER = '📁'
|
|
69
|
+
GEAR = '⚙️'
|
|
70
|
+
ROCKET = '🚀'
|
|
71
|
+
CHART = '📊'
|
|
72
|
+
LOCK = '🔒'
|
|
73
|
+
KEY = '🔑'
|
|
74
|
+
|
|
75
|
+
# People
|
|
76
|
+
USER = '👤'
|
|
77
|
+
USERS = '👥'
|
|
78
|
+
ROBOT = '🤖'
|
|
79
|
+
|
|
80
|
+
# Misc
|
|
81
|
+
STAR = '★'
|
|
82
|
+
SPARKLE = '✨'
|
|
83
|
+
FIRE = '🔥'
|
|
84
|
+
BULB = '💡'
|
|
85
|
+
TARGET = '🎯'
|
|
86
|
+
FLAG = '🚩'
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ProgressBar:
|
|
90
|
+
"""Animated progress bar for terminal output."""
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
total: int = 100,
|
|
95
|
+
width: int = 40,
|
|
96
|
+
fill_char: str = '█',
|
|
97
|
+
empty_char: str = '░',
|
|
98
|
+
prefix: str = '',
|
|
99
|
+
suffix: str = '',
|
|
100
|
+
):
|
|
101
|
+
self.total = total
|
|
102
|
+
self.width = width
|
|
103
|
+
self.fill_char = fill_char
|
|
104
|
+
self.empty_char = empty_char
|
|
105
|
+
self.prefix = prefix
|
|
106
|
+
self.suffix = suffix
|
|
107
|
+
|
|
108
|
+
def render(self, current: int) -> str:
|
|
109
|
+
"""Render progress bar at given position."""
|
|
110
|
+
if self.total == 0:
|
|
111
|
+
percent = 100
|
|
112
|
+
else:
|
|
113
|
+
percent = int(current / self.total * 100)
|
|
114
|
+
|
|
115
|
+
filled = int(self.width * current / self.total)
|
|
116
|
+
bar = self.fill_char * filled + self.empty_char * (self.width - filled)
|
|
117
|
+
|
|
118
|
+
# Color based on percentage
|
|
119
|
+
if percent >= 80:
|
|
120
|
+
color = Colors.GREEN
|
|
121
|
+
elif percent >= 50:
|
|
122
|
+
color = Colors.YELLOW
|
|
123
|
+
elif percent >= 25:
|
|
124
|
+
color = Colors.CYAN
|
|
125
|
+
else:
|
|
126
|
+
color = Colors.RED
|
|
127
|
+
|
|
128
|
+
return f"{color}{self.prefix}[{bar}]{Colors.RESET}{self.suffix} {percent}%"
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def simple(current: int, total: int, width: int = 20) -> str:
|
|
132
|
+
"""Quick static progress bar."""
|
|
133
|
+
if total == 0:
|
|
134
|
+
return f"[{'█' * width}] 100%"
|
|
135
|
+
|
|
136
|
+
percent = int(current / total * 100)
|
|
137
|
+
filled = int(width * current / total)
|
|
138
|
+
bar = '█' * filled + '░' * (width - filled)
|
|
139
|
+
return f"[{bar}] {percent}%"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class VisualFormatter:
|
|
143
|
+
"""
|
|
144
|
+
Main formatter class for CLI visual enhancement.
|
|
145
|
+
|
|
146
|
+
Provides methods to format and display lifecycle information
|
|
147
|
+
in a visually appealing way.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, use_color: bool = True):
|
|
151
|
+
self.use_color = use_color
|
|
152
|
+
self.icons = Icons()
|
|
153
|
+
self.colors = Colors()
|
|
154
|
+
|
|
155
|
+
def _c(self, text: str, color: str) -> str:
|
|
156
|
+
"""Apply color to text (if enabled)."""
|
|
157
|
+
if self.use_color:
|
|
158
|
+
return f"{color}{text}{self.colors.RESET}"
|
|
159
|
+
return text
|
|
160
|
+
|
|
161
|
+
def print_separator(
|
|
162
|
+
self,
|
|
163
|
+
char: str = '=',
|
|
164
|
+
length: int = 60,
|
|
165
|
+
color: Optional[str] = None,
|
|
166
|
+
) -> None:
|
|
167
|
+
"""Print a separator line."""
|
|
168
|
+
line = char * length
|
|
169
|
+
if color:
|
|
170
|
+
print(self._c(line, color))
|
|
171
|
+
else:
|
|
172
|
+
print(line)
|
|
173
|
+
|
|
174
|
+
def print_title(self, title: str, subtitle: str = '') -> None:
|
|
175
|
+
"""Print a formatted title."""
|
|
176
|
+
self.print_separator('=', 60, Colors.CYAN)
|
|
177
|
+
|
|
178
|
+
main = self._c(f" {title}", Colors.BOLD + Colors.CYAN)
|
|
179
|
+
print(main)
|
|
180
|
+
|
|
181
|
+
if subtitle:
|
|
182
|
+
sub = self._c(f" {subtitle}", Colors.DIM)
|
|
183
|
+
print(sub)
|
|
184
|
+
|
|
185
|
+
self.print_separator('=', 60, Colors.CYAN)
|
|
186
|
+
print()
|
|
187
|
+
|
|
188
|
+
def print_lifecycle_header(
|
|
189
|
+
self,
|
|
190
|
+
command: str,
|
|
191
|
+
mapping: Optional[Any] = None,
|
|
192
|
+
preset: Optional[Dict] = None,
|
|
193
|
+
) -> None:
|
|
194
|
+
"""Print enhanced lifecycle command header."""
|
|
195
|
+
self.print_title(
|
|
196
|
+
f"🔄 DevSquad Lifecycle [{command.upper()}]",
|
|
197
|
+
"View Layer Mode (Plan C Architecture)"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Command info box
|
|
201
|
+
print(self._c(" ┌─────────────────────────────────────────┐", Colors.CYAN))
|
|
202
|
+
print(self._c(" │", Colors.CYAN) +
|
|
203
|
+
f" 📌 Command: ".ljust(15) +
|
|
204
|
+
self._c(command.upper(), Colors.BOLD + Colors.YELLOW))
|
|
205
|
+
print(self._c(" │", Colors.CYAN))
|
|
206
|
+
|
|
207
|
+
if mapping:
|
|
208
|
+
phases_str = ', '.join(mapping.phases[:4])
|
|
209
|
+
if len(mapping.phases) > 4:
|
|
210
|
+
phases_str += f" (+{len(mapping.phases)-4} more)"
|
|
211
|
+
|
|
212
|
+
print(self._c(" │", Colors.CYAN) +
|
|
213
|
+
f" 📋 Phases: ".ljust(15) +
|
|
214
|
+
self._c(phases_str, Colors.GREEN))
|
|
215
|
+
|
|
216
|
+
if preset:
|
|
217
|
+
roles_str = ', '.join(preset.get('required_roles', [])[:3])
|
|
218
|
+
print(self._c(" │", Colors.CYAN) +
|
|
219
|
+
f" 👥 Roles: ".ljust(15) +
|
|
220
|
+
self._c(roles_str, Colors.BLUE))
|
|
221
|
+
|
|
222
|
+
mode = preset.get('mode', 'unknown')
|
|
223
|
+
gate = preset.get('gate', 'unknown')
|
|
224
|
+
print(self._c(" │", Colors.CYAN) +
|
|
225
|
+
f" ⚙️ Mode: ".ljust(15) +
|
|
226
|
+
self._c(mode, Colors.MAGENTA) +
|
|
227
|
+
" | ".ljust(5) +
|
|
228
|
+
f"🚧 Gate: {gate}")
|
|
229
|
+
|
|
230
|
+
print(self._c(" └─────────────────────────────────────────┘", Colors.CYAN))
|
|
231
|
+
print()
|
|
232
|
+
|
|
233
|
+
def print_phase_list(
|
|
234
|
+
self,
|
|
235
|
+
phases: List[Any],
|
|
236
|
+
current_phase: Optional[str] = None,
|
|
237
|
+
completed_phases: Optional[List[str]] = None,
|
|
238
|
+
) -> None:
|
|
239
|
+
"""Print formatted phase list with status icons."""
|
|
240
|
+
completed_phases = completed_phases or []
|
|
241
|
+
|
|
242
|
+
print(self._c(" 📋 Lifecycle Phases:", Colors.BOLD))
|
|
243
|
+
print()
|
|
244
|
+
|
|
245
|
+
for i, phase in enumerate(phases, 1):
|
|
246
|
+
phase_id = getattr(phase, 'phase_id', str(phase))
|
|
247
|
+
name = getattr(phase, 'name', phase_id)
|
|
248
|
+
optional = getattr(phase, 'optional', False)
|
|
249
|
+
|
|
250
|
+
# Determine status icon and color
|
|
251
|
+
if phase_id in completed_phases:
|
|
252
|
+
icon = Icons.CHECK
|
|
253
|
+
color = Colors.GREEN
|
|
254
|
+
status_text = "COMPLETED"
|
|
255
|
+
elif phase_id == current_phase:
|
|
256
|
+
icon = Icons.ARROW_RIGHT
|
|
257
|
+
color = Colors.YELLOW
|
|
258
|
+
status_text = "RUNNING"
|
|
259
|
+
elif optional:
|
|
260
|
+
icon = Icons.SKIP
|
|
261
|
+
color = Colors.DIM
|
|
262
|
+
status_text = "OPTIONAL"
|
|
263
|
+
else:
|
|
264
|
+
icon = Icons.CIRCLE
|
|
265
|
+
color = Colors.DIM
|
|
266
|
+
status_text = "PENDING"
|
|
267
|
+
|
|
268
|
+
# Format line
|
|
269
|
+
num = self._c(f"{i:2}.", Colors.DIM)
|
|
270
|
+
pid = self._c(f"[{phase_id}]", color)
|
|
271
|
+
pname = self._c(name.ljust(25), color if phase_id == current_phase else '')
|
|
272
|
+
picon = f" {icon}"
|
|
273
|
+
pstatus = self._c(f"<{status_text}>", Colors.DIM)
|
|
274
|
+
|
|
275
|
+
opt_tag = ""
|
|
276
|
+
if optional:
|
|
277
|
+
opt_tag = self._c(" [opt]", Colors.DIM)
|
|
278
|
+
|
|
279
|
+
print(f" {num} {pid} {pname}{picon}{pstatus}{opt_tag}")
|
|
280
|
+
|
|
281
|
+
print()
|
|
282
|
+
|
|
283
|
+
def print_progress_overview(
|
|
284
|
+
self,
|
|
285
|
+
current: int,
|
|
286
|
+
total: int,
|
|
287
|
+
label: str = "Progress",
|
|
288
|
+
) -> None:
|
|
289
|
+
"""Print progress overview with bar and stats."""
|
|
290
|
+
percent = int(current / total * 100) if total > 0 else 0
|
|
291
|
+
|
|
292
|
+
print(self._c(f" {Icons.CHART} {label}:", Colors.BOLD))
|
|
293
|
+
print()
|
|
294
|
+
|
|
295
|
+
# Progress bar
|
|
296
|
+
bar = ProgressBar.simple(current, total, 35)
|
|
297
|
+
colored_bar = self._colorize_bar(bar, percent)
|
|
298
|
+
print(f" {colored_bar}")
|
|
299
|
+
print()
|
|
300
|
+
|
|
301
|
+
# Stats
|
|
302
|
+
stats_line = (
|
|
303
|
+
f" {Icons.CHECK} Completed: "
|
|
304
|
+
f"{self._c(str(current), Colors.GREEN)} / "
|
|
305
|
+
f"{self._c(str(total), Colors.WHITE)} "
|
|
306
|
+
f"({self._c(f'{percent}%', Colors.BOLD)})"
|
|
307
|
+
)
|
|
308
|
+
print(stats_line)
|
|
309
|
+
print()
|
|
310
|
+
|
|
311
|
+
def _colorize_bar(self, bar: str, percent: int) -> str:
|
|
312
|
+
"""Colorize progress bar based on percentage."""
|
|
313
|
+
if percent >= 80:
|
|
314
|
+
return self._c(bar, Colors.GREEN)
|
|
315
|
+
elif percent >= 50:
|
|
316
|
+
return self._c(bar, Colors.YELLOW)
|
|
317
|
+
elif percent >= 25:
|
|
318
|
+
return self._c(bar, Colors.CYAN)
|
|
319
|
+
else:
|
|
320
|
+
return self._c(bar, Colors.RED)
|
|
321
|
+
|
|
322
|
+
def print_gate_status(
|
|
323
|
+
self,
|
|
324
|
+
gate_result: Optional[Any] = None,
|
|
325
|
+
gate_type: str = "Phase",
|
|
326
|
+
) -> None:
|
|
327
|
+
"""Print gate check result with visual indicators."""
|
|
328
|
+
print(self._c(f" {Icons.LOCK} {gate_type} Gate Status:", Colors.BOLD))
|
|
329
|
+
print()
|
|
330
|
+
|
|
331
|
+
if gate_result is None:
|
|
332
|
+
print(f" {self._c(Icons.INFO, Colors.BLUE)} No gate result available")
|
|
333
|
+
print()
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
passed = getattr(gate_result, 'passed', False)
|
|
337
|
+
verdict = getattr(gate_result, 'verdict', 'UNKNOWN')
|
|
338
|
+
checks_run = getattr(gate_result, 'checks_run', 0)
|
|
339
|
+
checks_passed = getattr(gate_result, 'checks_passed', 0)
|
|
340
|
+
|
|
341
|
+
# Verdict display
|
|
342
|
+
if verdict.upper() == 'APPROVE':
|
|
343
|
+
verdict_icon = Icons.CHECK
|
|
344
|
+
verdict_color = Colors.GREEN
|
|
345
|
+
verdict_text = "APPROVED"
|
|
346
|
+
elif verdict.upper() == 'REJECT':
|
|
347
|
+
verdict_icon = Icons.CROSS
|
|
348
|
+
verdict_color = Colors.RED
|
|
349
|
+
verdict_text = "REJECTED"
|
|
350
|
+
elif verdict.upper() == 'CONDITIONAL':
|
|
351
|
+
verdict_icon = Icons.WARNING
|
|
352
|
+
verdict_color = Colors.YELLOW
|
|
353
|
+
verdict_text = "CONDITIONAL"
|
|
354
|
+
else:
|
|
355
|
+
verdict_icon = Icons.INFO
|
|
356
|
+
verdict_color = Colors.BLUE
|
|
357
|
+
verdict_text = verdict.upper()
|
|
358
|
+
|
|
359
|
+
verdict_line = (
|
|
360
|
+
f" {self._c(verdict_icon, verdict_color)} "
|
|
361
|
+
f"Verdict: "
|
|
362
|
+
f"{self._c(verdict_text, Colors.BOLD + verdict_color)}"
|
|
363
|
+
)
|
|
364
|
+
print(verdict_line)
|
|
365
|
+
|
|
366
|
+
# Checks summary
|
|
367
|
+
if checks_run > 0:
|
|
368
|
+
check_percent = int(checks_passed / checks_run * 100)
|
|
369
|
+
check_line = (
|
|
370
|
+
f" {Icons.GEAR} Checks: "
|
|
371
|
+
f"{self._c(str(checks_passed), Colors.GREEN)}/"
|
|
372
|
+
f"{self._c(str(checks_run), Colors.WHITE)} "
|
|
373
|
+
f"({check_percent}%)"
|
|
374
|
+
)
|
|
375
|
+
print(check_line)
|
|
376
|
+
|
|
377
|
+
# Issues summary
|
|
378
|
+
critical_issues = getattr(gate_result, 'critical_issues', [])
|
|
379
|
+
warnings = getattr(gate_result, 'warnings', [])
|
|
380
|
+
evidence_required = getattr(gate_result, 'evidence_required', [])
|
|
381
|
+
|
|
382
|
+
if critical_issues:
|
|
383
|
+
print(f"\n {self._c(Icons.CROSS + ' Critical Issues:', Colors.RED)}")
|
|
384
|
+
for issue in critical_issues[:3]:
|
|
385
|
+
msg = issue.get('message', str(issue))[:60]
|
|
386
|
+
print(f" {self._c('•', Colors.RED)} {msg}")
|
|
387
|
+
if len(critical_issues) > 3:
|
|
388
|
+
print(f" ... and {len(critical_issues)-3} more")
|
|
389
|
+
|
|
390
|
+
if warnings:
|
|
391
|
+
print(f"\n {self._c(Icons.WARNING + ' Warnings:', Colors.YELLOW)}")
|
|
392
|
+
for warning in warnings[:3]:
|
|
393
|
+
msg = warning.get('message', str(warning))[:60]
|
|
394
|
+
print(f" {self._c('•', Colors.YELLOW)} {msg}")
|
|
395
|
+
if len(warnings) > 3:
|
|
396
|
+
print(f" ... and {len(warnings)-3} more")
|
|
397
|
+
|
|
398
|
+
if evidence_required:
|
|
399
|
+
print(f"\n {self._c(Icons.KEY + ' Evidence Required:', Colors.CYAN)}")
|
|
400
|
+
for ev in evidence_required[:5]:
|
|
401
|
+
print(f" {self._c('•', Colors.CYAN)} {ev}")
|
|
402
|
+
|
|
403
|
+
print()
|
|
404
|
+
|
|
405
|
+
def print_status_summary(
|
|
406
|
+
self,
|
|
407
|
+
status: Any,
|
|
408
|
+
) -> None:
|
|
409
|
+
"""Print comprehensive status summary."""
|
|
410
|
+
mode = getattr(status, 'mode', None)
|
|
411
|
+
current_phase = getattr(status, 'current_phase', None)
|
|
412
|
+
completed = getattr(status, 'completed_phases', [])
|
|
413
|
+
failed = getattr(status, 'failed_phases', [])
|
|
414
|
+
blocked = getattr(status, 'blocked_phases', [])
|
|
415
|
+
progress = getattr(status, 'progress_percent', 0)
|
|
416
|
+
can_advance = getattr(status, 'can_advance', True)
|
|
417
|
+
next_phase = getattr(status, 'next_phase', None)
|
|
418
|
+
|
|
419
|
+
self.print_title(
|
|
420
|
+
f"📊 Lifecycle Status Summary",
|
|
421
|
+
f"Mode: {mode.value if hasattr(mode, 'value') else mode}" if mode else ''
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Overview stats
|
|
425
|
+
print(self._c(" ┌─ Overview ─────────────────────────────┐", Colors.CYAN))
|
|
426
|
+
print(self._c(" │", Colors.CYAN))
|
|
427
|
+
|
|
428
|
+
mode_str = mode.value if hasattr(mode, 'value') else str(mode)
|
|
429
|
+
print(self._c(" │", Colors.CYAN) +
|
|
430
|
+
f" Mode: ".ljust(12) +
|
|
431
|
+
self._c(mode_str, Colors.BOLD + Colors.MAGENTA))
|
|
432
|
+
|
|
433
|
+
progress_str = f"{progress:.1f}%"
|
|
434
|
+
print(self._c(" │", Colors.CYAN) +
|
|
435
|
+
f" Progress: ".ljust(12) +
|
|
436
|
+
self._c(progress_str, Colors.BOLD + self._progress_color(progress)))
|
|
437
|
+
|
|
438
|
+
can_advance_str = "Yes ✅" if can_advance else "No ❌"
|
|
439
|
+
print(self._c(" │", Colors.CYAN) +
|
|
440
|
+
f" Can Advance: ".ljust(12) +
|
|
441
|
+
self._c(can_advance_str, Colors.GREEN if can_advance else Colors.RED))
|
|
442
|
+
|
|
443
|
+
print(self._c(" └────────────────────────────────────────┘", Colors.CYAN))
|
|
444
|
+
print()
|
|
445
|
+
|
|
446
|
+
# Phase counts
|
|
447
|
+
total_phases = len(completed) + len(failed) + len(blocked)
|
|
448
|
+
print(f" {Icons.CHART} Phase Statistics:")
|
|
449
|
+
print(f" {Icons.CHECK} Completed: {self._c(str(len(completed)), Colors.GREEN)}")
|
|
450
|
+
print(f" {Icons.CROSS} Failed: {self._c(str(len(failed)), Colors.RED)}")
|
|
451
|
+
print(f" {Icons.BLOCKED} Blocked: {self._c(str(len(blocked)), Colors.YELLOW)}")
|
|
452
|
+
print(f" {Icons.ARROW_RIGHT} Current: {current_phase or 'None'}")
|
|
453
|
+
print(f" {Icons.TARGET} Next: {next_phase or 'N/A'}")
|
|
454
|
+
print()
|
|
455
|
+
|
|
456
|
+
def _progress_color(self, percent: float) -> str:
|
|
457
|
+
"""Get color for progress percentage."""
|
|
458
|
+
if percent >= 80:
|
|
459
|
+
return Colors.GREEN
|
|
460
|
+
elif percent >= 50:
|
|
461
|
+
return Colors.YELLOW
|
|
462
|
+
elif percent >= 25:
|
|
463
|
+
return Colors.CYAN
|
|
464
|
+
else:
|
|
465
|
+
return Colors.RED
|
|
466
|
+
|
|
467
|
+
def print_footer(self, version: str = "V3.6.0") -> None:
|
|
468
|
+
"""Print footer with version and timestamp."""
|
|
469
|
+
from datetime import datetime
|
|
470
|
+
|
|
471
|
+
self.print_separator('-', 40, Colors.DIM)
|
|
472
|
+
|
|
473
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
474
|
+
footer = (
|
|
475
|
+
f" DevSquad {version} | "
|
|
476
|
+
f"{timestamp} | "
|
|
477
|
+
f"{Icons.SPARKLE} Plan C Layered Architecture"
|
|
478
|
+
)
|
|
479
|
+
print(self._c(footer, Colors.DIM))
|
|
480
|
+
self.print_separator('-', 40, Colors.DIM)
|
|
481
|
+
|
|
482
|
+
def print_success_message(self, message: str = "Operation completed successfully!") -> None:
|
|
483
|
+
"""Print success message with icon."""
|
|
484
|
+
print()
|
|
485
|
+
print(f" {self._c(Icons.CHECK + ' ' + message, Colors.GREEN + Colors.BOLD)}")
|
|
486
|
+
print()
|
|
487
|
+
|
|
488
|
+
def print_error_message(self, message: str) -> None:
|
|
489
|
+
"""Print error message with icon."""
|
|
490
|
+
print()
|
|
491
|
+
print(f" {self._c(Icons.CROSS + ' Error: ' + message, Colors.RED + Colors.BOLD)}")
|
|
492
|
+
print()
|
|
493
|
+
|
|
494
|
+
def print_info_box(
|
|
495
|
+
self,
|
|
496
|
+
title: str,
|
|
497
|
+
content: List[str],
|
|
498
|
+
icon: str = Icons.INFO,
|
|
499
|
+
color: str = Colors.BLUE,
|
|
500
|
+
) -> None:
|
|
501
|
+
"""Print an info box with title and content."""
|
|
502
|
+
print(f"\n {self._c(icon + ' ' + title, color + Colors.BOLD)}")
|
|
503
|
+
print(f" {'─' * (len(title) + 4)}")
|
|
504
|
+
for line in content:
|
|
505
|
+
print(f" {self._c('•', color)} {line}")
|
|
506
|
+
print()
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def get_visual_formatter(use_color: bool = True) -> VisualFormatter:
|
|
510
|
+
"""Get a VisualFormatter instance (factory function)."""
|
|
511
|
+
return VisualFormatter(use_color=use_color)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def print_enhanced_lifecycle_command(
|
|
515
|
+
command: str,
|
|
516
|
+
task: str,
|
|
517
|
+
use_visual: bool = True,
|
|
518
|
+
) -> int:
|
|
519
|
+
"""
|
|
520
|
+
Print enhanced lifecycle command output.
|
|
521
|
+
|
|
522
|
+
This is the main entry point for visual CLI output.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
command: Lifecycle command (spec/plan/build/test/review/ship)
|
|
526
|
+
task: Task description
|
|
527
|
+
use_visual: Whether to use visual formatting
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
Exit code (0 for success)
|
|
531
|
+
"""
|
|
532
|
+
if not use_visual:
|
|
533
|
+
return 0 # Let normal output proceed
|
|
534
|
+
|
|
535
|
+
try:
|
|
536
|
+
from scripts.collaboration.lifecycle_protocol import (
|
|
537
|
+
VIEW_MAPPINGS,
|
|
538
|
+
create_lifecycle_protocol,
|
|
539
|
+
LifecycleMode,
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
vf = VisualFormatter(use_color=True)
|
|
543
|
+
|
|
544
|
+
# Get mapping and protocol
|
|
545
|
+
mapping = VIEW_MAPPINGS.get(command)
|
|
546
|
+
protocol = create_lifecycle_protocol(LifecycleMode.SHORTCUT)
|
|
547
|
+
preset = _get_preset(command)
|
|
548
|
+
|
|
549
|
+
# Print enhanced header
|
|
550
|
+
vf.print_lifecycle_header(command, mapping, preset)
|
|
551
|
+
|
|
552
|
+
# Show resolved phases
|
|
553
|
+
if mapping:
|
|
554
|
+
phases = protocol.resolve_command_to_phases(command)
|
|
555
|
+
if phases:
|
|
556
|
+
vf.print_phase_list(phases)
|
|
557
|
+
|
|
558
|
+
# Show progress placeholder
|
|
559
|
+
vf.print_progress_overview(0, len(phases), "Command Coverage")
|
|
560
|
+
|
|
561
|
+
# Show gate info
|
|
562
|
+
gate_name = preset.get('gate', 'Unknown') if preset else 'Unknown'
|
|
563
|
+
vf.print_gate_status(None, gate_name)
|
|
564
|
+
|
|
565
|
+
# Print next steps
|
|
566
|
+
vf.print_info_box(
|
|
567
|
+
"Next Steps",
|
|
568
|
+
[
|
|
569
|
+
f"Run: python3 scripts/cli.py dispatch -t \"{task}\" --roles architect solo-coder",
|
|
570
|
+
f"Or use: python3 examples/quick_start.py",
|
|
571
|
+
f"View guide: cat docs/USAGE_GUIDE.md",
|
|
572
|
+
],
|
|
573
|
+
icon=Icons.ROCKET,
|
|
574
|
+
color=Colors.GREEN,
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
vf.print_footer()
|
|
578
|
+
vf.print_success_message("Ready to execute!")
|
|
579
|
+
|
|
580
|
+
return 0
|
|
581
|
+
|
|
582
|
+
except Exception as e:
|
|
583
|
+
print(f"\nVisual enhancement error: {e}\n")
|
|
584
|
+
return 0 # Don't block normal operation
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _get_preset(command: str) -> Optional[Dict]:
|
|
588
|
+
"""Get preset configuration for a command."""
|
|
589
|
+
presets = {
|
|
590
|
+
"spec": {
|
|
591
|
+
"description": "Define and refine requirements before implementation",
|
|
592
|
+
"required_roles": ["architect", "product-manager"],
|
|
593
|
+
"mode": "sequential",
|
|
594
|
+
"gate": "spec_first",
|
|
595
|
+
},
|
|
596
|
+
"plan": {
|
|
597
|
+
"description": "Break down work into small, verifiable tasks",
|
|
598
|
+
"required_roles": ["architect", "product-manager"],
|
|
599
|
+
"mode": "auto",
|
|
600
|
+
"gate": "task_breakdown_complete",
|
|
601
|
+
},
|
|
602
|
+
"build": {
|
|
603
|
+
"description": "Implement incrementally with TDD discipline",
|
|
604
|
+
"required_roles": ["architect", "solo-coder", "tester"],
|
|
605
|
+
"mode": "parallel",
|
|
606
|
+
"gate": "incremental_verification",
|
|
607
|
+
},
|
|
608
|
+
"test": {
|
|
609
|
+
"description": "Run tests with mandatory evidence requirements",
|
|
610
|
+
"required_roles": ["tester", "solo-coder"],
|
|
611
|
+
"mode": "consensus",
|
|
612
|
+
"gate": "evidence_required",
|
|
613
|
+
},
|
|
614
|
+
"review": {
|
|
615
|
+
"description": "Five-axis code review (correctness/readability/arch/security/performance)",
|
|
616
|
+
"required_roles": ["solo-coder", "security", "tester", "architect"],
|
|
617
|
+
"mode": "consensus",
|
|
618
|
+
"gate": "change_size_limit",
|
|
619
|
+
},
|
|
620
|
+
"ship": {
|
|
621
|
+
"description": "Pre-launch verification and deployment preparation",
|
|
622
|
+
"required_roles": ["devops", "security", "architect"],
|
|
623
|
+
"mode": "sequential",
|
|
624
|
+
"gate": "pre_launch_checklist",
|
|
625
|
+
},
|
|
626
|
+
}
|
|
627
|
+
return presets.get(command)
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
if __name__ == "__main__":
|
|
631
|
+
# Quick demo of visual formatter
|
|
632
|
+
vf = VisualFormatter()
|
|
633
|
+
|
|
634
|
+
vf.print_title("Visual Formatter Demo", "Testing all features")
|
|
635
|
+
|
|
636
|
+
# Demo progress bars
|
|
637
|
+
for i in [0, 25, 50, 75, 100]:
|
|
638
|
+
bar = ProgressBar.simple(i, 100, 30)
|
|
639
|
+
print(f" {vf._colorize_bar(bar, i)}")
|
|
640
|
+
|
|
641
|
+
print()
|
|
642
|
+
vf.print_success_message("Demo complete!")
|