omnimancer-cli 0.1.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.
- omnimancer/__init__.py +42 -0
- omnimancer/__main__.py +9 -0
- omnimancer/cli/__init__.py +8 -0
- omnimancer/cli/agent_manager.py +826 -0
- omnimancer/cli/agent_wizard.py +944 -0
- omnimancer/cli/approval_formatter.py +734 -0
- omnimancer/cli/approval_integration.py +734 -0
- omnimancer/cli/approval_prompt.py +1286 -0
- omnimancer/cli/batch_approval_display.py +617 -0
- omnimancer/cli/batch_approval_filters.py +729 -0
- omnimancer/cli/batch_operation_monitor.py +775 -0
- omnimancer/cli/commands/performance_cmd.py +624 -0
- omnimancer/cli/commands.py +757 -0
- omnimancer/cli/completion_handler.py +281 -0
- omnimancer/cli/config_setup_wizard.py +783 -0
- omnimancer/cli/dynamic_commands.py +371 -0
- omnimancer/cli/handlers/__init__.py +18 -0
- omnimancer/cli/handlers/agent_handler.py +865 -0
- omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
- omnimancer/cli/handlers/custom_agent_handler.py +352 -0
- omnimancer/cli/handlers/permissions_handler.py +1609 -0
- omnimancer/cli/interface.py +6695 -0
- omnimancer/cli/performance_dashboard.py +612 -0
- omnimancer/core/__init__.py +34 -0
- omnimancer/core/agent/__init__.py +256 -0
- omnimancer/core/agent/agent_config.py +715 -0
- omnimancer/core/agent/agent_switcher.py +668 -0
- omnimancer/core/agent/approval_context.py +460 -0
- omnimancer/core/agent/approval_dialog.py +902 -0
- omnimancer/core/agent/approval_interface.py +628 -0
- omnimancer/core/agent/approval_manager.py +760 -0
- omnimancer/core/agent/cli_integration.py +470 -0
- omnimancer/core/agent/config.py +845 -0
- omnimancer/core/agent/custom_agent_integration.py +654 -0
- omnimancer/core/agent/diff_renderer.py +706 -0
- omnimancer/core/agent/file_content_display.py +503 -0
- omnimancer/core/agent/file_modification_workflow.py +658 -0
- omnimancer/core/agent/file_system_manager.py +1790 -0
- omnimancer/core/agent/input_handler.py +614 -0
- omnimancer/core/agent/metrics_collector.py +771 -0
- omnimancer/core/agent/optimization_engine.py +729 -0
- omnimancer/core/agent/performance_monitor.py +717 -0
- omnimancer/core/agent/persona.py +870 -0
- omnimancer/core/agent/persona_validator.py +672 -0
- omnimancer/core/agent/program_executor.py +748 -0
- omnimancer/core/agent/proposed_changes_integration.py +663 -0
- omnimancer/core/agent/read_before_write_errors.py +492 -0
- omnimancer/core/agent/read_before_write_ui.py +647 -0
- omnimancer/core/agent/rich_renderer.py +763 -0
- omnimancer/core/agent/status_config.py +238 -0
- omnimancer/core/agent/status_core.py +446 -0
- omnimancer/core/agent/status_integrated_engine.py +540 -0
- omnimancer/core/agent/status_manager.py +878 -0
- omnimancer/core/agent/status_ui_components.py +535 -0
- omnimancer/core/agent/template_applicator.py +548 -0
- omnimancer/core/agent/terminal_status_listener.py +394 -0
- omnimancer/core/agent/token_tracker.py +679 -0
- omnimancer/core/agent/types.py +60 -0
- omnimancer/core/agent/web_client.py +745 -0
- omnimancer/core/agent/workflow_orchestrator.py +826 -0
- omnimancer/core/agent_engine.py +1625 -0
- omnimancer/core/agent_mode_manager.py +706 -0
- omnimancer/core/agent_progress_ui.py +545 -0
- omnimancer/core/chat_manager.py +141 -0
- omnimancer/core/config_manager.py +2199 -0
- omnimancer/core/config_migration.py +772 -0
- omnimancer/core/config_provider.py +1974 -0
- omnimancer/core/config_security_validator.py +393 -0
- omnimancer/core/config_validator.py +768 -0
- omnimancer/core/conversation_manager.py +352 -0
- omnimancer/core/engine.py +787 -0
- omnimancer/core/enhanced_input.py +523 -0
- omnimancer/core/env_loader.py +83 -0
- omnimancer/core/fallback_manager.py +568 -0
- omnimancer/core/health_monitor.py +462 -0
- omnimancer/core/history_manager.py +462 -0
- omnimancer/core/mcp_integration_layer.py +878 -0
- omnimancer/core/models.py +1889 -0
- omnimancer/core/provider_capabilities.py +403 -0
- omnimancer/core/provider_initializer.py +450 -0
- omnimancer/core/provider_registry.py +432 -0
- omnimancer/core/security/__init__.py +16 -0
- omnimancer/core/security/approval_workflow.py +406 -0
- omnimancer/core/security/audit_logger.py +511 -0
- omnimancer/core/security/permission_controller.py +511 -0
- omnimancer/core/security/sandbox_manager.py +379 -0
- omnimancer/core/security/security_manager.py +530 -0
- omnimancer/core/setup_wizard.py +345 -0
- omnimancer/core/setup_wizard_core.py +449 -0
- omnimancer/core/setup_wizard_guide_generator.py +376 -0
- omnimancer/core/setup_wizard_provider_setup.py +454 -0
- omnimancer/core/setup_wizard_ui.py +317 -0
- omnimancer/core/setup_wizard_validation.py +371 -0
- omnimancer/core/signal_handler.py +174 -0
- omnimancer/mcp/__init__.py +11 -0
- omnimancer/mcp/client.py +725 -0
- omnimancer/mcp/manager.py +649 -0
- omnimancer/providers/__init__.py +24 -0
- omnimancer/providers/azure.py +608 -0
- omnimancer/providers/base.py +262 -0
- omnimancer/providers/bedrock.py +711 -0
- omnimancer/providers/claude.py +426 -0
- omnimancer/providers/claude_code.py +406 -0
- omnimancer/providers/cohere.py +412 -0
- omnimancer/providers/factory.py +988 -0
- omnimancer/providers/gemini.py +708 -0
- omnimancer/providers/mistral.py +554 -0
- omnimancer/providers/ollama.py +510 -0
- omnimancer/providers/openai.py +362 -0
- omnimancer/providers/openrouter.py +709 -0
- omnimancer/providers/perplexity.py +476 -0
- omnimancer/providers/vertex.py +617 -0
- omnimancer/providers/xai.py +484 -0
- omnimancer/ui/__init__.py +6 -0
- omnimancer/ui/cancellation_handler.py +235 -0
- omnimancer/ui/progress_indicator.py +318 -0
- omnimancer/utils/__init__.py +22 -0
- omnimancer/utils/error_handler.py +589 -0
- omnimancer/utils/errors.py +667 -0
- omnimancer/utils/retry.py +238 -0
- omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
- omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
- omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
- omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
- omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handler for agent persona CLI commands.
|
|
3
|
+
|
|
4
|
+
This module provides the command handling logic for managing agent personas
|
|
5
|
+
through the CLI interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from rich.box import ROUNDED
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
|
|
15
|
+
from ...core.agent.agent_switcher import (
|
|
16
|
+
SwitchState,
|
|
17
|
+
get_agent_switcher,
|
|
18
|
+
)
|
|
19
|
+
from ...core.agent.persona import (
|
|
20
|
+
PersonaStatus,
|
|
21
|
+
get_persona_manager,
|
|
22
|
+
)
|
|
23
|
+
from ..commands import Command
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AgentPersonaHandler:
|
|
27
|
+
"""Handles agent persona-related CLI commands."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, console: Optional[Console] = None):
|
|
30
|
+
"""
|
|
31
|
+
Initialize the handler.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
console: Rich console for output
|
|
35
|
+
"""
|
|
36
|
+
self.console = console or Console()
|
|
37
|
+
self.persona_manager = get_persona_manager()
|
|
38
|
+
self.agent_switcher = get_agent_switcher(self.persona_manager)
|
|
39
|
+
|
|
40
|
+
async def handle_agent_command(self, command: Command) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Main entry point for handling agent commands.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
command: The command to handle
|
|
46
|
+
"""
|
|
47
|
+
args = command.parameters.get("args", [])
|
|
48
|
+
|
|
49
|
+
if not args:
|
|
50
|
+
# Default to 'list' if no subcommand
|
|
51
|
+
await self.handle_list_personas()
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
subcommand = args[0].lower()
|
|
55
|
+
|
|
56
|
+
# Route to appropriate handler
|
|
57
|
+
if subcommand == "list":
|
|
58
|
+
await self.handle_list_personas()
|
|
59
|
+
elif subcommand == "use":
|
|
60
|
+
if len(args) < 2:
|
|
61
|
+
self._show_error("Usage: /agent use <persona_name>")
|
|
62
|
+
return
|
|
63
|
+
await self.handle_use_persona(args[1])
|
|
64
|
+
elif subcommand == "current":
|
|
65
|
+
await self.handle_current_persona()
|
|
66
|
+
elif subcommand == "info":
|
|
67
|
+
if len(args) < 2:
|
|
68
|
+
self._show_error("Usage: /agent info <persona_name>")
|
|
69
|
+
return
|
|
70
|
+
await self.handle_persona_info(args[1])
|
|
71
|
+
elif subcommand == "status":
|
|
72
|
+
# Show detailed status including switch state
|
|
73
|
+
await self.handle_persona_status()
|
|
74
|
+
elif subcommand == "history":
|
|
75
|
+
# Show switch history
|
|
76
|
+
await self.handle_switch_history()
|
|
77
|
+
elif subcommand == "recommend":
|
|
78
|
+
user_query = " ".join(args[1:]) if len(args) > 1 else None
|
|
79
|
+
await self.handle_recommend_persona(user_query)
|
|
80
|
+
elif subcommand == "compare":
|
|
81
|
+
await self.handle_compare_personas()
|
|
82
|
+
elif subcommand == "preview":
|
|
83
|
+
if len(args) < 2:
|
|
84
|
+
self._show_error("Usage: /agent preview <persona_name>")
|
|
85
|
+
return
|
|
86
|
+
await self.handle_preview_persona(args[1])
|
|
87
|
+
elif subcommand == "discover":
|
|
88
|
+
await self.handle_discover_personas()
|
|
89
|
+
elif subcommand == "help":
|
|
90
|
+
self._show_agent_help()
|
|
91
|
+
else:
|
|
92
|
+
self._show_error(f"Unknown agent subcommand: {subcommand}")
|
|
93
|
+
self._show_agent_help()
|
|
94
|
+
|
|
95
|
+
async def handle_list_personas(self) -> None:
|
|
96
|
+
"""List all available agent personas."""
|
|
97
|
+
personas = self.persona_manager.get_all_personas()
|
|
98
|
+
|
|
99
|
+
if not personas:
|
|
100
|
+
self._show_warning("No agent personas available.")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
# Create a table for personas
|
|
104
|
+
table = Table(
|
|
105
|
+
title="🤖 Available Agent Personas",
|
|
106
|
+
box=ROUNDED,
|
|
107
|
+
show_header=True,
|
|
108
|
+
header_style="bold cyan",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
table.add_column("Icon", style="cyan", justify="center")
|
|
112
|
+
table.add_column("ID", style="green")
|
|
113
|
+
table.add_column("Name", style="bold white")
|
|
114
|
+
table.add_column("Category", style="yellow")
|
|
115
|
+
table.add_column("Status", justify="center")
|
|
116
|
+
table.add_column("Description", style="dim")
|
|
117
|
+
|
|
118
|
+
# Sort personas by category and name
|
|
119
|
+
sorted_personas = sorted(
|
|
120
|
+
personas.values(), key=lambda p: (p.category.value, p.name)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
active_persona = self.persona_manager.active_persona
|
|
124
|
+
|
|
125
|
+
for persona in sorted_personas:
|
|
126
|
+
# Format status with color
|
|
127
|
+
status = self._format_status(persona.status)
|
|
128
|
+
|
|
129
|
+
# Highlight active persona
|
|
130
|
+
if active_persona and persona.id == active_persona.id:
|
|
131
|
+
status = "[bold green]● ACTIVE[/bold green]"
|
|
132
|
+
name_style = "[bold cyan]" + persona.name + "[/bold cyan]"
|
|
133
|
+
else:
|
|
134
|
+
name_style = persona.name
|
|
135
|
+
|
|
136
|
+
table.add_row(
|
|
137
|
+
persona.icon,
|
|
138
|
+
persona.id,
|
|
139
|
+
name_style,
|
|
140
|
+
persona.category.value.title(),
|
|
141
|
+
status,
|
|
142
|
+
(
|
|
143
|
+
persona.description[:50] + "..."
|
|
144
|
+
if len(persona.description) > 50
|
|
145
|
+
else persona.description
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
self.console.print(table)
|
|
150
|
+
|
|
151
|
+
# Show usage hint
|
|
152
|
+
self.console.print(
|
|
153
|
+
"\n[dim]Use '/agent use <persona_id>' to switch to a persona[/dim]"
|
|
154
|
+
)
|
|
155
|
+
self.console.print(
|
|
156
|
+
"[dim]Use '/agent info <persona_id>' for detailed information[/dim]"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
async def handle_use_persona(self, persona_id: str) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Switch to a specified persona.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
persona_id: ID of the persona to switch to
|
|
165
|
+
"""
|
|
166
|
+
# Check if persona exists
|
|
167
|
+
persona = self.persona_manager.get_persona(persona_id.lower())
|
|
168
|
+
if not persona:
|
|
169
|
+
self._show_error(f"Persona '{persona_id}' not found.")
|
|
170
|
+
self._suggest_personas(persona_id)
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
# Check if already active
|
|
174
|
+
if self.persona_manager.active_persona == persona:
|
|
175
|
+
self._show_info(f"Already using {persona.name} ({persona.icon})")
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
# Show switching message
|
|
179
|
+
self.console.print(
|
|
180
|
+
f"\n[yellow]Switching to {persona.name} ({persona.icon})...[/yellow]"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Perform the switch
|
|
184
|
+
success, message = self.agent_switcher.switch_persona(
|
|
185
|
+
persona_id.lower(), reason="User requested switch via CLI"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if success:
|
|
189
|
+
self._show_success(f"✓ {message}")
|
|
190
|
+
|
|
191
|
+
# Show persona capabilities
|
|
192
|
+
if persona.capabilities:
|
|
193
|
+
caps_text = ", ".join(
|
|
194
|
+
[
|
|
195
|
+
cap.value.replace("_", " ").title()
|
|
196
|
+
for cap in persona.capabilities
|
|
197
|
+
]
|
|
198
|
+
)
|
|
199
|
+
self.console.print(f"[dim]Capabilities: {caps_text}[/dim]")
|
|
200
|
+
else:
|
|
201
|
+
self._show_error(f"Failed to switch persona: {message}")
|
|
202
|
+
|
|
203
|
+
async def handle_current_persona(self) -> None:
|
|
204
|
+
"""Show the currently active persona."""
|
|
205
|
+
active_persona = self.persona_manager.active_persona
|
|
206
|
+
|
|
207
|
+
if not active_persona:
|
|
208
|
+
self._show_info("No agent persona is currently active.")
|
|
209
|
+
self.console.print(
|
|
210
|
+
"[dim]Use '/agent use <persona_id>' to activate a persona[/dim]"
|
|
211
|
+
)
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
# Create a panel with current persona info
|
|
215
|
+
info_lines = [
|
|
216
|
+
f"[bold cyan]{active_persona.icon} {active_persona.name}[/bold cyan]",
|
|
217
|
+
f"[yellow]ID:[/yellow] {active_persona.id}",
|
|
218
|
+
f"[yellow]Category:[/yellow] {active_persona.category.value.title()}",
|
|
219
|
+
f"[yellow]Status:[/yellow] {self._format_status(active_persona.status)}",
|
|
220
|
+
"",
|
|
221
|
+
f"[dim]{active_persona.description}[/dim]",
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
if active_persona.capabilities:
|
|
225
|
+
caps_text = ", ".join(
|
|
226
|
+
[
|
|
227
|
+
cap.value.replace("_", " ").title()
|
|
228
|
+
for cap in active_persona.capabilities
|
|
229
|
+
]
|
|
230
|
+
)
|
|
231
|
+
info_lines.append("")
|
|
232
|
+
info_lines.append(f"[yellow]Capabilities:[/yellow] {caps_text}")
|
|
233
|
+
|
|
234
|
+
# Add usage stats if available
|
|
235
|
+
if active_persona.metadata:
|
|
236
|
+
info_lines.append("")
|
|
237
|
+
info_lines.append(
|
|
238
|
+
f"[dim]Usage Count: {active_persona.metadata.usage_count}[/dim]"
|
|
239
|
+
)
|
|
240
|
+
if active_persona.metadata.last_used:
|
|
241
|
+
info_lines.append(
|
|
242
|
+
f"[dim]Last Used: {active_persona.metadata.last_used.strftime('%Y-%m-%d %H:%M')}[/dim]"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
panel = Panel(
|
|
246
|
+
"\n".join(info_lines),
|
|
247
|
+
title="Current Agent Persona",
|
|
248
|
+
border_style="green",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
self.console.print(panel)
|
|
252
|
+
|
|
253
|
+
async def handle_persona_info(self, persona_id: str) -> None:
|
|
254
|
+
"""
|
|
255
|
+
Show detailed information about a persona.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
persona_id: ID of the persona
|
|
259
|
+
"""
|
|
260
|
+
persona = self.persona_manager.get_persona(persona_id.lower())
|
|
261
|
+
|
|
262
|
+
if not persona:
|
|
263
|
+
self._show_error(f"Persona '{persona_id}' not found.")
|
|
264
|
+
self._suggest_personas(persona_id)
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
# Create detailed info panel
|
|
268
|
+
info_sections = []
|
|
269
|
+
|
|
270
|
+
# Basic info section
|
|
271
|
+
basic_info = [
|
|
272
|
+
f"[bold cyan]{persona.icon} {persona.name}[/bold cyan]",
|
|
273
|
+
f"[yellow]ID:[/yellow] {persona.id}",
|
|
274
|
+
f"[yellow]Category:[/yellow] {persona.category.value.title()}",
|
|
275
|
+
f"[yellow]Status:[/yellow] {self._format_status(persona.status)}",
|
|
276
|
+
"",
|
|
277
|
+
f"{persona.description}",
|
|
278
|
+
]
|
|
279
|
+
info_sections.append(Panel("\n".join(basic_info), title="Basic Information"))
|
|
280
|
+
|
|
281
|
+
# Capabilities section
|
|
282
|
+
if persona.capabilities:
|
|
283
|
+
cap_list = []
|
|
284
|
+
for cap in sorted(persona.capabilities, key=lambda c: c.value):
|
|
285
|
+
cap_name = cap.value.replace("_", " ").title()
|
|
286
|
+
cap_list.append(f"• {cap_name}")
|
|
287
|
+
|
|
288
|
+
info_sections.append(Panel("\n".join(cap_list), title="Capabilities"))
|
|
289
|
+
|
|
290
|
+
# Configuration section
|
|
291
|
+
if persona.configuration:
|
|
292
|
+
config_info = [
|
|
293
|
+
f"[yellow]Template:[/yellow] {persona.configuration.template_id}",
|
|
294
|
+
f"[yellow]Primary Provider:[/yellow] {persona.configuration.primary_provider}",
|
|
295
|
+
f"[yellow]Tools Enabled:[/yellow] {'Yes' if persona.configuration.tools_enabled else 'No'}",
|
|
296
|
+
f"[yellow]Web Search:[/yellow] {'Yes' if persona.configuration.web_search_enabled else 'No'}",
|
|
297
|
+
f"[yellow]File Operations:[/yellow] {'Yes' if persona.configuration.file_operations_enabled else 'No'}",
|
|
298
|
+
f"[yellow]Approval Required:[/yellow] {'Yes' if persona.configuration.approval_required else 'No'}",
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
if (
|
|
302
|
+
hasattr(persona.configuration, "fallback_providers")
|
|
303
|
+
and persona.configuration.fallback_providers
|
|
304
|
+
):
|
|
305
|
+
fallback_list = persona.configuration.fallback_providers
|
|
306
|
+
if isinstance(fallback_list, (list, tuple)):
|
|
307
|
+
config_info.append(
|
|
308
|
+
f"[yellow]Fallback Providers:[/yellow] {', '.join(fallback_list)}"
|
|
309
|
+
)
|
|
310
|
+
else:
|
|
311
|
+
config_info.append(
|
|
312
|
+
f"[yellow]Fallback Providers:[/yellow] {str(fallback_list)}"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
info_sections.append(Panel("\n".join(config_info), title="Configuration"))
|
|
316
|
+
|
|
317
|
+
# Metadata section
|
|
318
|
+
if persona.metadata:
|
|
319
|
+
meta_info = [
|
|
320
|
+
f"[yellow]Version:[/yellow] {persona.metadata.version}",
|
|
321
|
+
f"[yellow]Author:[/yellow] {persona.metadata.author}",
|
|
322
|
+
f"[yellow]Usage Count:[/yellow] {persona.metadata.usage_count}",
|
|
323
|
+
f"[yellow]Built-in:[/yellow] {'Yes' if persona.metadata.is_builtin else 'No'}",
|
|
324
|
+
]
|
|
325
|
+
|
|
326
|
+
if persona.metadata.created_at:
|
|
327
|
+
meta_info.append(
|
|
328
|
+
f"[yellow]Created:[/yellow] {persona.metadata.created_at.strftime('%Y-%m-%d %H:%M')}"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
if persona.metadata.last_used:
|
|
332
|
+
meta_info.append(
|
|
333
|
+
f"[yellow]Last Used:[/yellow] {persona.metadata.last_used.strftime('%Y-%m-%d %H:%M')}"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
if persona.metadata.tags:
|
|
337
|
+
meta_info.append(
|
|
338
|
+
f"[yellow]Tags:[/yellow] {', '.join(persona.metadata.tags)}"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
info_sections.append(Panel("\n".join(meta_info), title="Metadata"))
|
|
342
|
+
|
|
343
|
+
# Display all sections
|
|
344
|
+
for section in info_sections:
|
|
345
|
+
self.console.print(section)
|
|
346
|
+
|
|
347
|
+
# Show activation hint if not active
|
|
348
|
+
if self.persona_manager.active_persona != persona:
|
|
349
|
+
self.console.print(
|
|
350
|
+
f"\n[dim]Use '/agent use {persona.id}' to activate this persona[/dim]"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
async def handle_persona_status(self) -> None:
|
|
354
|
+
"""Show detailed agent persona status."""
|
|
355
|
+
# Current persona
|
|
356
|
+
active_persona = self.persona_manager.active_persona
|
|
357
|
+
|
|
358
|
+
# Switch state
|
|
359
|
+
switch_state = self.agent_switcher.get_current_state()
|
|
360
|
+
|
|
361
|
+
# Session state info
|
|
362
|
+
session_state = self.agent_switcher.current_session_state
|
|
363
|
+
|
|
364
|
+
# Create status sections
|
|
365
|
+
status_lines = []
|
|
366
|
+
|
|
367
|
+
# Active persona section
|
|
368
|
+
status_lines.append("[bold]Active Persona:[/bold]")
|
|
369
|
+
if active_persona:
|
|
370
|
+
status_lines.append(
|
|
371
|
+
f" {active_persona.icon} {active_persona.name} ({active_persona.id})"
|
|
372
|
+
)
|
|
373
|
+
status_lines.append(
|
|
374
|
+
f" Status: {self._format_status(active_persona.status)}"
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
status_lines.append(" [dim]No persona active[/dim]")
|
|
378
|
+
|
|
379
|
+
status_lines.append("")
|
|
380
|
+
|
|
381
|
+
# Switch state section
|
|
382
|
+
status_lines.append("[bold]Switch State:[/bold]")
|
|
383
|
+
status_lines.append(f" {self._format_switch_state(switch_state)}")
|
|
384
|
+
|
|
385
|
+
status_lines.append("")
|
|
386
|
+
|
|
387
|
+
# Session state section
|
|
388
|
+
status_lines.append("[bold]Session State:[/bold]")
|
|
389
|
+
if session_state:
|
|
390
|
+
status_lines.append(
|
|
391
|
+
f" Session ID: {session_state.session_id or '[dim]None[/dim]'}"
|
|
392
|
+
)
|
|
393
|
+
status_lines.append(
|
|
394
|
+
f" History Items: {len(session_state.conversation_history)}"
|
|
395
|
+
)
|
|
396
|
+
status_lines.append(f" Active Tools: {len(session_state.active_tools)}")
|
|
397
|
+
status_lines.append(
|
|
398
|
+
f" Active Operations: {len(session_state.active_operations)}"
|
|
399
|
+
)
|
|
400
|
+
if session_state.last_modified:
|
|
401
|
+
status_lines.append(
|
|
402
|
+
f" Last Modified: {session_state.last_modified.strftime('%H:%M:%S')}"
|
|
403
|
+
)
|
|
404
|
+
else:
|
|
405
|
+
status_lines.append(" [dim]No session state[/dim]")
|
|
406
|
+
|
|
407
|
+
status_lines.append("")
|
|
408
|
+
|
|
409
|
+
# Manager stats
|
|
410
|
+
stats = self.persona_manager.get_stats()
|
|
411
|
+
status_lines.append("[bold]Manager Statistics:[/bold]")
|
|
412
|
+
status_lines.append(f" Total Personas: {stats['total_personas']}")
|
|
413
|
+
status_lines.append(f" Built-in: {stats['builtin_personas']}")
|
|
414
|
+
status_lines.append(f" Custom: {stats['custom_personas']}")
|
|
415
|
+
status_lines.append(f" Available: {stats['available_personas']}")
|
|
416
|
+
|
|
417
|
+
panel = Panel(
|
|
418
|
+
"\n".join(status_lines),
|
|
419
|
+
title="🤖 Agent Persona Status",
|
|
420
|
+
border_style="blue",
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
self.console.print(panel)
|
|
424
|
+
|
|
425
|
+
async def handle_switch_history(self) -> None:
|
|
426
|
+
"""Show persona switch history."""
|
|
427
|
+
history = self.agent_switcher.get_switch_history()
|
|
428
|
+
|
|
429
|
+
if not history:
|
|
430
|
+
self._show_info("No persona switch history available.")
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
# Create history table
|
|
434
|
+
table = Table(
|
|
435
|
+
title="Persona Switch History",
|
|
436
|
+
box=ROUNDED,
|
|
437
|
+
show_header=True,
|
|
438
|
+
header_style="bold cyan",
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
table.add_column("#", style="dim", width=3)
|
|
442
|
+
table.add_column("Time", style="cyan")
|
|
443
|
+
table.add_column("From", style="yellow")
|
|
444
|
+
table.add_column("To", style="green")
|
|
445
|
+
table.add_column("Reason", style="dim")
|
|
446
|
+
table.add_column("Status", justify="center")
|
|
447
|
+
|
|
448
|
+
for idx, context in enumerate(history[-10:], 1): # Show last 10 switches
|
|
449
|
+
from_name = (
|
|
450
|
+
context.from_persona.name if context.from_persona else "[dim]None[/dim]"
|
|
451
|
+
)
|
|
452
|
+
to_name = context.to_persona.name
|
|
453
|
+
time_str = context.timestamp.strftime("%H:%M:%S")
|
|
454
|
+
|
|
455
|
+
# Determine status
|
|
456
|
+
if context.error_message:
|
|
457
|
+
status = "[red]✗ Failed[/red]"
|
|
458
|
+
else:
|
|
459
|
+
status = "[green]✓ Success[/green]"
|
|
460
|
+
|
|
461
|
+
reason = (
|
|
462
|
+
context.switch_reason[:30] + "..."
|
|
463
|
+
if len(context.switch_reason) > 30
|
|
464
|
+
else context.switch_reason
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
table.add_row(
|
|
468
|
+
str(idx),
|
|
469
|
+
time_str,
|
|
470
|
+
from_name,
|
|
471
|
+
to_name,
|
|
472
|
+
reason or "[dim]Manual[/dim]",
|
|
473
|
+
status,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
self.console.print(table)
|
|
477
|
+
|
|
478
|
+
def _show_agent_help(self) -> None:
|
|
479
|
+
"""Show help for agent commands."""
|
|
480
|
+
help_text = """
|
|
481
|
+
[bold cyan]Agent Persona Commands:[/bold cyan]
|
|
482
|
+
|
|
483
|
+
[yellow]Basic Commands:[/yellow]
|
|
484
|
+
/agent list - List all available agent personas
|
|
485
|
+
/agent use <persona> - Switch to a specific persona
|
|
486
|
+
/agent current - Show the currently active persona
|
|
487
|
+
/agent info <persona> - Show detailed information about a persona
|
|
488
|
+
|
|
489
|
+
[yellow]Discovery & Selection:[/yellow]
|
|
490
|
+
/agent discover - Interactive persona discovery guide
|
|
491
|
+
/agent recommend [query] - Get persona recommendations for a task
|
|
492
|
+
/agent compare - Compare capabilities of all personas
|
|
493
|
+
/agent preview <persona> - Preview persona without switching
|
|
494
|
+
|
|
495
|
+
[yellow]Advanced Commands:[/yellow]
|
|
496
|
+
/agent status - Show detailed agent system status
|
|
497
|
+
/agent history - Show persona switch history
|
|
498
|
+
|
|
499
|
+
[yellow]Available Personas:[/yellow]
|
|
500
|
+
• coding - Optimized for software development
|
|
501
|
+
• research - Configured for research with web search
|
|
502
|
+
• creative - Set up for creative writing
|
|
503
|
+
• performance - Optimized for speed and efficiency
|
|
504
|
+
• general - Balanced general-purpose configuration
|
|
505
|
+
|
|
506
|
+
[yellow]Examples:[/yellow]
|
|
507
|
+
/agent discover - Start interactive discovery
|
|
508
|
+
/agent recommend "debug python" - Get recommendations for debugging
|
|
509
|
+
/agent compare - See capability comparison table
|
|
510
|
+
/agent preview coding - Preview coding persona behavior
|
|
511
|
+
/agent use coding - Switch to coding persona
|
|
512
|
+
"""
|
|
513
|
+
panel = Panel(
|
|
514
|
+
help_text.strip(), title="Agent Persona Help", border_style="cyan"
|
|
515
|
+
)
|
|
516
|
+
self.console.print(panel)
|
|
517
|
+
|
|
518
|
+
def _format_status(self, status: PersonaStatus) -> str:
|
|
519
|
+
"""Format persona status with color."""
|
|
520
|
+
status_colors = {
|
|
521
|
+
PersonaStatus.AVAILABLE: "[green]Available[/green]",
|
|
522
|
+
PersonaStatus.ACTIVE: "[bold green]Active[/bold green]",
|
|
523
|
+
PersonaStatus.DISABLED: "[dim]Disabled[/dim]",
|
|
524
|
+
PersonaStatus.ERROR: "[red]Error[/red]",
|
|
525
|
+
PersonaStatus.LOADING: "[yellow]Loading...[/yellow]",
|
|
526
|
+
}
|
|
527
|
+
return status_colors.get(status, str(status.value))
|
|
528
|
+
|
|
529
|
+
def _format_switch_state(self, state: SwitchState) -> str:
|
|
530
|
+
"""Format switch state with color."""
|
|
531
|
+
state_colors = {
|
|
532
|
+
SwitchState.IDLE: "[green]Idle[/green]",
|
|
533
|
+
SwitchState.PREPARING: "[yellow]Preparing[/yellow]",
|
|
534
|
+
SwitchState.SWITCHING: "[yellow]Switching[/yellow]",
|
|
535
|
+
SwitchState.VALIDATING: "[yellow]Validating[/yellow]",
|
|
536
|
+
SwitchState.COMPLETE: "[green]Complete[/green]",
|
|
537
|
+
SwitchState.ERROR: "[red]Error[/red]",
|
|
538
|
+
SwitchState.ROLLBACK: "[red]Rollback[/red]",
|
|
539
|
+
}
|
|
540
|
+
return state_colors.get(state, str(state.value))
|
|
541
|
+
|
|
542
|
+
def _suggest_personas(self, typed: str) -> None:
|
|
543
|
+
"""Suggest similar persona names."""
|
|
544
|
+
personas = self.persona_manager.get_all_personas()
|
|
545
|
+
suggestions = []
|
|
546
|
+
|
|
547
|
+
typed_lower = typed.lower()
|
|
548
|
+
for pid, persona in personas.items():
|
|
549
|
+
if typed_lower in pid or typed_lower in persona.name.lower():
|
|
550
|
+
suggestions.append(f"{pid} ({persona.name})")
|
|
551
|
+
|
|
552
|
+
if suggestions:
|
|
553
|
+
self.console.print("[dim]Did you mean one of these?[/dim]")
|
|
554
|
+
for suggestion in suggestions[:3]:
|
|
555
|
+
self.console.print(f" • {suggestion}")
|
|
556
|
+
|
|
557
|
+
def _show_info(self, message: str) -> None:
|
|
558
|
+
"""Show info message."""
|
|
559
|
+
self.console.print(f"[cyan]ℹ {message}[/cyan]")
|
|
560
|
+
|
|
561
|
+
def _show_success(self, message: str) -> None:
|
|
562
|
+
"""Show success message."""
|
|
563
|
+
self.console.print(f"[green]{message}[/green]")
|
|
564
|
+
|
|
565
|
+
def _show_warning(self, message: str) -> None:
|
|
566
|
+
"""Show warning message."""
|
|
567
|
+
self.console.print(f"[yellow]⚠ {message}[/yellow]")
|
|
568
|
+
|
|
569
|
+
def _show_error(self, message: str) -> None:
|
|
570
|
+
"""Show error message."""
|
|
571
|
+
self.console.print(f"[red]✗ {message}[/red]")
|
|
572
|
+
|
|
573
|
+
async def handle_recommend_persona(self, user_query: Optional[str] = None) -> None:
|
|
574
|
+
"""
|
|
575
|
+
Recommend a persona based on user query or task.
|
|
576
|
+
|
|
577
|
+
Args:
|
|
578
|
+
user_query: Optional user query to base recommendations on
|
|
579
|
+
"""
|
|
580
|
+
self.console.print("\n[bold cyan]🎯 Persona Recommendations[/bold cyan]")
|
|
581
|
+
|
|
582
|
+
personas = self.persona_manager.get_all_personas()
|
|
583
|
+
if not personas:
|
|
584
|
+
self._show_warning("No personas available for recommendations.")
|
|
585
|
+
return
|
|
586
|
+
|
|
587
|
+
# If no query provided, show general recommendations
|
|
588
|
+
if not user_query:
|
|
589
|
+
self.console.print(
|
|
590
|
+
"\n[dim]Here are some general persona recommendations based on common tasks:[/dim]\n"
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
# Show recommended personas for common tasks
|
|
594
|
+
recommendations = [
|
|
595
|
+
("💻 For software development and coding tasks", "coding"),
|
|
596
|
+
("🔍 For research and information gathering", "research"),
|
|
597
|
+
("✍️ For creative writing and content creation", "creative"),
|
|
598
|
+
("⚡ For quick responses and simple tasks", "performance"),
|
|
599
|
+
("🎯 For balanced general-purpose work", "general"),
|
|
600
|
+
]
|
|
601
|
+
|
|
602
|
+
for task_desc, persona_id in recommendations:
|
|
603
|
+
persona = personas.get(persona_id)
|
|
604
|
+
if persona:
|
|
605
|
+
self.console.print(f"{task_desc}")
|
|
606
|
+
self.console.print(
|
|
607
|
+
f" → Use: [cyan]/agent use {persona_id}[/cyan] ({persona.name})"
|
|
608
|
+
)
|
|
609
|
+
self.console.print("")
|
|
610
|
+
|
|
611
|
+
else:
|
|
612
|
+
# Analyze query and recommend appropriate persona
|
|
613
|
+
self.console.print(f'[dim]Based on your query: "{user_query}"[/dim]\n')
|
|
614
|
+
|
|
615
|
+
query_lower = user_query.lower()
|
|
616
|
+
recommendations = []
|
|
617
|
+
|
|
618
|
+
# Simple keyword-based matching
|
|
619
|
+
if any(
|
|
620
|
+
word in query_lower
|
|
621
|
+
for word in [
|
|
622
|
+
"code",
|
|
623
|
+
"program",
|
|
624
|
+
"debug",
|
|
625
|
+
"develop",
|
|
626
|
+
"build",
|
|
627
|
+
"fix",
|
|
628
|
+
]
|
|
629
|
+
):
|
|
630
|
+
if "coding" in personas:
|
|
631
|
+
recommendations.append(
|
|
632
|
+
(
|
|
633
|
+
personas["coding"],
|
|
634
|
+
"Optimized for programming and development tasks",
|
|
635
|
+
)
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
if any(
|
|
639
|
+
word in query_lower
|
|
640
|
+
for word in [
|
|
641
|
+
"research",
|
|
642
|
+
"find",
|
|
643
|
+
"search",
|
|
644
|
+
"learn",
|
|
645
|
+
"study",
|
|
646
|
+
"investigate",
|
|
647
|
+
]
|
|
648
|
+
):
|
|
649
|
+
if "research" in personas:
|
|
650
|
+
recommendations.append(
|
|
651
|
+
(
|
|
652
|
+
personas["research"],
|
|
653
|
+
"Enhanced with web search capabilities",
|
|
654
|
+
)
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
if any(
|
|
658
|
+
word in query_lower
|
|
659
|
+
for word in [
|
|
660
|
+
"write",
|
|
661
|
+
"creative",
|
|
662
|
+
"story",
|
|
663
|
+
"content",
|
|
664
|
+
"blog",
|
|
665
|
+
"article",
|
|
666
|
+
]
|
|
667
|
+
):
|
|
668
|
+
if "creative" in personas:
|
|
669
|
+
recommendations.append(
|
|
670
|
+
(
|
|
671
|
+
personas["creative"],
|
|
672
|
+
"Specialized for creative writing tasks",
|
|
673
|
+
)
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
if any(
|
|
677
|
+
word in query_lower
|
|
678
|
+
for word in ["fast", "quick", "speed", "urgent", "simple"]
|
|
679
|
+
):
|
|
680
|
+
if "performance" in personas:
|
|
681
|
+
recommendations.append(
|
|
682
|
+
(
|
|
683
|
+
personas["performance"],
|
|
684
|
+
"Optimized for speed and efficiency",
|
|
685
|
+
)
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
# If no specific matches, recommend general
|
|
689
|
+
if not recommendations and "general" in personas:
|
|
690
|
+
recommendations.append(
|
|
691
|
+
(personas["general"], "Balanced for general-purpose tasks")
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
if recommendations:
|
|
695
|
+
for persona, reason in recommendations[:3]: # Show top 3
|
|
696
|
+
self.console.print(
|
|
697
|
+
f"[green]✓[/green] [bold]{persona.icon} {persona.name}[/bold]"
|
|
698
|
+
)
|
|
699
|
+
self.console.print(f" {reason}")
|
|
700
|
+
self.console.print(f" Use: [cyan]/agent use {persona.id}[/cyan]")
|
|
701
|
+
self.console.print("")
|
|
702
|
+
else:
|
|
703
|
+
self.console.print(
|
|
704
|
+
"[dim]No specific recommendations found. Try /agent discover for interactive selection.[/dim]"
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
# Show additional help
|
|
708
|
+
self.console.print(
|
|
709
|
+
"[dim]💡 Tip: Use '/agent compare' to see capability differences[/dim]"
|
|
710
|
+
)
|
|
711
|
+
self.console.print(
|
|
712
|
+
"[dim]💡 Tip: Use '/agent preview <persona>' to test without switching[/dim]"
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
async def handle_compare_personas(self) -> None:
|
|
716
|
+
"""Show capability comparison matrix for all personas."""
|
|
717
|
+
personas = self.persona_manager.get_all_personas()
|
|
718
|
+
if not personas:
|
|
719
|
+
self._show_warning("No personas available for comparison.")
|
|
720
|
+
return
|
|
721
|
+
|
|
722
|
+
# Create comparison table
|
|
723
|
+
table = Table(
|
|
724
|
+
title="🔀 Persona Capability Comparison",
|
|
725
|
+
box=ROUNDED,
|
|
726
|
+
show_header=True,
|
|
727
|
+
header_style="bold cyan",
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
table.add_column("Persona", style="bold white", width=12)
|
|
731
|
+
table.add_column("Category", style="yellow", width=12)
|
|
732
|
+
table.add_column("Tools", justify="center", width=8)
|
|
733
|
+
table.add_column("Web Search", justify="center", width=10)
|
|
734
|
+
table.add_column("File Ops", justify="center", width=8)
|
|
735
|
+
table.add_column("Speed", justify="center", width=8)
|
|
736
|
+
table.add_column("Best For", style="dim", width=25)
|
|
737
|
+
|
|
738
|
+
# Sort personas by category and name
|
|
739
|
+
sorted_personas = sorted(
|
|
740
|
+
personas.values(), key=lambda p: (p.category.value, p.name)
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
for persona in sorted_personas:
|
|
744
|
+
# Get capabilities from configuration
|
|
745
|
+
config = persona.configuration
|
|
746
|
+
tools_enabled = "✅" if (config and config.tools_enabled) else "❌"
|
|
747
|
+
web_search = (
|
|
748
|
+
"✅"
|
|
749
|
+
if (config and getattr(config, "web_search_enabled", False))
|
|
750
|
+
else "❌"
|
|
751
|
+
)
|
|
752
|
+
file_ops = (
|
|
753
|
+
"✅"
|
|
754
|
+
if (config and getattr(config, "file_operations_enabled", False))
|
|
755
|
+
else "❌"
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
# Determine speed based on persona type
|
|
759
|
+
speed_rating = (
|
|
760
|
+
"⚡"
|
|
761
|
+
if persona.id == "performance"
|
|
762
|
+
else "🐢" if persona.id == "research" else "⚖️"
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
# Best use case
|
|
766
|
+
use_cases = {
|
|
767
|
+
"coding": "Software development",
|
|
768
|
+
"research": "Research & analysis",
|
|
769
|
+
"creative": "Writing & content",
|
|
770
|
+
"performance": "Quick tasks",
|
|
771
|
+
"general": "Balanced workflows",
|
|
772
|
+
}
|
|
773
|
+
best_for = use_cases.get(persona.id, persona.description[:25])
|
|
774
|
+
|
|
775
|
+
table.add_row(
|
|
776
|
+
f"{persona.icon} {persona.name}",
|
|
777
|
+
persona.category.value.title(),
|
|
778
|
+
tools_enabled,
|
|
779
|
+
web_search,
|
|
780
|
+
file_ops,
|
|
781
|
+
speed_rating,
|
|
782
|
+
best_for,
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
self.console.print(table)
|
|
786
|
+
|
|
787
|
+
# Legend
|
|
788
|
+
legend_lines = [
|
|
789
|
+
"[bold]Legend:[/bold]",
|
|
790
|
+
"✅ = Enabled/Available ❌ = Disabled/Unavailable",
|
|
791
|
+
"⚡ = Fast ⚖️ = Balanced 🐢 = Thorough",
|
|
792
|
+
"",
|
|
793
|
+
"[dim]💡 Use '/agent info <persona>' for detailed information[/dim]",
|
|
794
|
+
"[dim]💡 Use '/agent use <persona>' to switch to a persona[/dim]",
|
|
795
|
+
]
|
|
796
|
+
|
|
797
|
+
legend_panel = Panel(
|
|
798
|
+
"\n".join(legend_lines), title="Usage Guide", border_style="dim"
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
self.console.print("\n")
|
|
802
|
+
self.console.print(legend_panel)
|
|
803
|
+
|
|
804
|
+
async def handle_preview_persona(self, persona_id: str) -> None:
|
|
805
|
+
"""
|
|
806
|
+
Preview a persona without switching to it.
|
|
807
|
+
|
|
808
|
+
Args:
|
|
809
|
+
persona_id: ID of the persona to preview
|
|
810
|
+
"""
|
|
811
|
+
persona = self.persona_manager.get_persona(persona_id.lower())
|
|
812
|
+
if not persona:
|
|
813
|
+
self._show_error(f"Persona '{persona_id}' not found.")
|
|
814
|
+
self._suggest_personas(persona_id)
|
|
815
|
+
return
|
|
816
|
+
|
|
817
|
+
# Show preview header
|
|
818
|
+
self.console.print(
|
|
819
|
+
f"\n[bold cyan]👁️ Preview: {persona.icon} {persona.name}[/bold cyan]"
|
|
820
|
+
)
|
|
821
|
+
self.console.print(
|
|
822
|
+
f"[dim]This is how '{persona.name}' would behave if activated[/dim]\n"
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
# Show configuration preview
|
|
826
|
+
config_lines = [
|
|
827
|
+
f"[bold]Configuration Preview[/bold]",
|
|
828
|
+
f"• Template: {persona.configuration.template_id if persona.configuration else 'None'}",
|
|
829
|
+
f"• Provider: {persona.configuration.primary_provider if persona.configuration else 'None'}",
|
|
830
|
+
]
|
|
831
|
+
|
|
832
|
+
if persona.configuration:
|
|
833
|
+
config_lines.extend(
|
|
834
|
+
[
|
|
835
|
+
f"• Tools: {'✅ Enabled' if persona.configuration.tools_enabled else '❌ Disabled'}",
|
|
836
|
+
f"• Web Search: {'✅ Enabled' if getattr(persona.configuration, 'web_search_enabled', False) else '❌ Disabled'}",
|
|
837
|
+
f"• File Operations: {'✅ Enabled' if getattr(persona.configuration, 'file_operations_enabled', False) else '❌ Disabled'}",
|
|
838
|
+
f"• Approval Required: {'✅ Yes' if getattr(persona.configuration, 'approval_required', True) else '❌ No'}",
|
|
839
|
+
]
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
config_panel = Panel(
|
|
843
|
+
"\n".join(config_lines), title="Configuration", border_style="blue"
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
# Show capabilities preview
|
|
847
|
+
if persona.capabilities:
|
|
848
|
+
caps_text = ", ".join(
|
|
849
|
+
[cap.value.replace("_", " ").title() for cap in persona.capabilities]
|
|
850
|
+
)
|
|
851
|
+
caps_panel = Panel(caps_text, title="Capabilities", border_style="green")
|
|
852
|
+
else:
|
|
853
|
+
caps_panel = Panel(
|
|
854
|
+
"[dim]No specific capabilities defined[/dim]",
|
|
855
|
+
title="Capabilities",
|
|
856
|
+
border_style="dim",
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
# Show behavioral description
|
|
860
|
+
behavior_lines = [
|
|
861
|
+
persona.description,
|
|
862
|
+
"",
|
|
863
|
+
"[bold]Expected Behavior:[/bold]",
|
|
864
|
+
]
|
|
865
|
+
|
|
866
|
+
# Add behavior predictions based on persona type
|
|
867
|
+
behavior_predictions = {
|
|
868
|
+
"coding": [
|
|
869
|
+
"• Will focus on code quality and best practices",
|
|
870
|
+
"• Provides detailed technical explanations",
|
|
871
|
+
"• Suggests testing and documentation",
|
|
872
|
+
"• Uses development tools and file operations",
|
|
873
|
+
],
|
|
874
|
+
"research": [
|
|
875
|
+
"• Will search for current information online",
|
|
876
|
+
"• Provides comprehensive analysis",
|
|
877
|
+
"• Cites sources and references",
|
|
878
|
+
"• Takes more time for thorough responses",
|
|
879
|
+
],
|
|
880
|
+
"creative": [
|
|
881
|
+
"• Focuses on engaging and original content",
|
|
882
|
+
"• Uses varied writing styles and techniques",
|
|
883
|
+
"• Emphasizes creativity over strict accuracy",
|
|
884
|
+
"• May suggest multiple creative approaches",
|
|
885
|
+
],
|
|
886
|
+
"performance": [
|
|
887
|
+
"• Provides quick, concise responses",
|
|
888
|
+
"• Prioritizes speed over depth",
|
|
889
|
+
"• Uses minimal tools to avoid delays",
|
|
890
|
+
"• Best for simple, straightforward tasks",
|
|
891
|
+
],
|
|
892
|
+
"general": [
|
|
893
|
+
"• Provides balanced responses",
|
|
894
|
+
"• Adapts approach to task requirements",
|
|
895
|
+
"• Uses moderate depth and detail",
|
|
896
|
+
"• Good all-around performance",
|
|
897
|
+
],
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
if persona.id in behavior_predictions:
|
|
901
|
+
behavior_lines.extend(behavior_predictions[persona.id])
|
|
902
|
+
else:
|
|
903
|
+
behavior_lines.append(
|
|
904
|
+
"• Behavior will depend on configured template and settings"
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
behavior_panel = Panel(
|
|
908
|
+
"\n".join(behavior_lines),
|
|
909
|
+
title="Behavioral Preview",
|
|
910
|
+
border_style="yellow",
|
|
911
|
+
)
|
|
912
|
+
|
|
913
|
+
# Display all panels
|
|
914
|
+
self.console.print(config_panel)
|
|
915
|
+
self.console.print(caps_panel)
|
|
916
|
+
self.console.print(behavior_panel)
|
|
917
|
+
|
|
918
|
+
# Show action options
|
|
919
|
+
action_lines = [
|
|
920
|
+
f"[bold]Ready to switch?[/bold]",
|
|
921
|
+
f"• [cyan]/agent use {persona.id}[/cyan] - Switch to this persona",
|
|
922
|
+
f"• [cyan]/agent compare[/cyan] - Compare with other personas",
|
|
923
|
+
f"• [cyan]/agent info {persona.id}[/cyan] - View detailed information",
|
|
924
|
+
]
|
|
925
|
+
|
|
926
|
+
action_panel = Panel(
|
|
927
|
+
"\n".join(action_lines), title="Next Steps", border_style="cyan"
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
self.console.print(action_panel)
|
|
931
|
+
|
|
932
|
+
async def handle_discover_personas(self) -> None:
|
|
933
|
+
"""Provide interactive persona discovery interface."""
|
|
934
|
+
self.console.print("\n[bold cyan]🧭 Persona Discovery[/bold cyan]")
|
|
935
|
+
|
|
936
|
+
personas = self.persona_manager.get_all_personas()
|
|
937
|
+
if not personas:
|
|
938
|
+
self._show_warning("No personas available for discovery.")
|
|
939
|
+
return
|
|
940
|
+
|
|
941
|
+
# Show discovery intro
|
|
942
|
+
intro_lines = [
|
|
943
|
+
"[bold]Welcome to Persona Discovery![/bold]",
|
|
944
|
+
"",
|
|
945
|
+
"This guide will help you find the perfect agent persona for your needs.",
|
|
946
|
+
"Each persona is optimized for different types of tasks and workflows.",
|
|
947
|
+
"",
|
|
948
|
+
"[dim]💡 Tip: You can always switch personas later with '/agent use <name>'[/dim]",
|
|
949
|
+
]
|
|
950
|
+
|
|
951
|
+
intro_panel = Panel(
|
|
952
|
+
"\n".join(intro_lines),
|
|
953
|
+
title="Getting Started",
|
|
954
|
+
border_style="cyan",
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
self.console.print(intro_panel)
|
|
958
|
+
|
|
959
|
+
# Group personas by category
|
|
960
|
+
categories = {}
|
|
961
|
+
for persona in personas.values():
|
|
962
|
+
cat = persona.category.value
|
|
963
|
+
if cat not in categories:
|
|
964
|
+
categories[cat] = []
|
|
965
|
+
categories[cat].append(persona)
|
|
966
|
+
|
|
967
|
+
# Show personas by category
|
|
968
|
+
for category, persona_list in categories.items():
|
|
969
|
+
category_title = f"📂 {category.title()} Personas"
|
|
970
|
+
self.console.print(f"\n[bold yellow]{category_title}[/bold yellow]")
|
|
971
|
+
|
|
972
|
+
for persona in sorted(persona_list, key=lambda p: p.name):
|
|
973
|
+
# Create persona card
|
|
974
|
+
card_lines = [
|
|
975
|
+
f"[bold]{persona.icon} {persona.name}[/bold]",
|
|
976
|
+
persona.description,
|
|
977
|
+
"",
|
|
978
|
+
]
|
|
979
|
+
|
|
980
|
+
# Add key features
|
|
981
|
+
features = []
|
|
982
|
+
if persona.configuration:
|
|
983
|
+
if persona.configuration.tools_enabled:
|
|
984
|
+
features.append("🛠️ Tools enabled")
|
|
985
|
+
if getattr(persona.configuration, "web_search_enabled", False):
|
|
986
|
+
features.append("🌐 Web search")
|
|
987
|
+
if getattr(persona.configuration, "file_operations_enabled", False):
|
|
988
|
+
features.append("📁 File operations")
|
|
989
|
+
|
|
990
|
+
if features:
|
|
991
|
+
card_lines.extend(
|
|
992
|
+
[
|
|
993
|
+
"[dim]Key features:[/dim]",
|
|
994
|
+
"[dim]" + " • ".join(features) + "[/dim]",
|
|
995
|
+
"",
|
|
996
|
+
]
|
|
997
|
+
)
|
|
998
|
+
|
|
999
|
+
# Add usage stats
|
|
1000
|
+
if persona.metadata and persona.metadata.usage_count > 0:
|
|
1001
|
+
card_lines.append(
|
|
1002
|
+
f"[dim]Used {persona.metadata.usage_count} times[/dim]"
|
|
1003
|
+
)
|
|
1004
|
+
|
|
1005
|
+
# Add quick actions
|
|
1006
|
+
card_lines.extend(
|
|
1007
|
+
[
|
|
1008
|
+
"",
|
|
1009
|
+
f"[cyan]Preview:[/cyan] /agent preview {persona.id}",
|
|
1010
|
+
f"[green]Use:[/green] /agent use {persona.id}",
|
|
1011
|
+
]
|
|
1012
|
+
)
|
|
1013
|
+
|
|
1014
|
+
# Show card
|
|
1015
|
+
persona_panel = Panel(
|
|
1016
|
+
"\n".join(card_lines),
|
|
1017
|
+
title=f"{persona.id}",
|
|
1018
|
+
border_style=(
|
|
1019
|
+
"blue"
|
|
1020
|
+
if persona == self.persona_manager.active_persona
|
|
1021
|
+
else "dim"
|
|
1022
|
+
),
|
|
1023
|
+
width=50,
|
|
1024
|
+
)
|
|
1025
|
+
|
|
1026
|
+
self.console.print(persona_panel)
|
|
1027
|
+
|
|
1028
|
+
# Show discovery footer
|
|
1029
|
+
footer_lines = [
|
|
1030
|
+
"[bold]Need help choosing?[/bold]",
|
|
1031
|
+
"",
|
|
1032
|
+
'• [cyan]/agent recommend "<your task>"[/cyan] - Get recommendations for a specific task',
|
|
1033
|
+
"• [cyan]/agent compare[/cyan] - See side-by-side capability comparison",
|
|
1034
|
+
"• [cyan]/agent info <persona>[/cyan] - Get detailed information about a persona",
|
|
1035
|
+
"• [cyan]/agent preview <persona>[/cyan] - Preview how a persona would behave",
|
|
1036
|
+
"",
|
|
1037
|
+
"[bold]Popular starting choices:[/bold]",
|
|
1038
|
+
"• [green]coding[/green] - For developers and programmers",
|
|
1039
|
+
"• [green]research[/green] - For information gathering and analysis",
|
|
1040
|
+
"• [green]general[/green] - For balanced, general-purpose work",
|
|
1041
|
+
]
|
|
1042
|
+
|
|
1043
|
+
footer_panel = Panel(
|
|
1044
|
+
"\n".join(footer_lines), title="Next Steps", border_style="green"
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
self.console.print("\n")
|
|
1048
|
+
self.console.print(footer_panel)
|