Refactor cockpit to use DockerTmuxController pattern
Based on claude-code-tools TmuxCLIController, this refactor: - Added DockerTmuxController class for robust tmux session management - Implements send_keys() with configurable delay_enter - Implements capture_pane() for output retrieval - Implements wait_for_prompt() for pattern-based completion detection - Implements wait_for_idle() for content-hash-based idle detection - Implements wait_for_shell_prompt() for shell prompt detection Also includes workflow improvements: - Pre-task git snapshot before agent execution - Post-task commit protocol in agent guidelines Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
229
lib/chat_response_formatter.py
Normal file
229
lib/chat_response_formatter.py
Normal file
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Chat Response Formatter - Format responses for readability
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
import json
|
||||
|
||||
|
||||
class ChatResponseFormatter:
|
||||
"""Format chat responses in readable markdown"""
|
||||
|
||||
def format_kg_search_results(self, results: Dict) -> str:
|
||||
"""Format KG search results"""
|
||||
output = []
|
||||
output.append(f"**Search:** {results.get('query', 'N/A')}")
|
||||
output.append(f"**Time:** {results.get('execution_time_ms', 0)}ms")
|
||||
output.append("")
|
||||
|
||||
domains = results.get('domains', {})
|
||||
if not domains:
|
||||
return "\n".join(output) + "\nNo results found."
|
||||
|
||||
for domain, domain_results in domains.items():
|
||||
if domain_results.get('error'):
|
||||
continue
|
||||
|
||||
entities = domain_results.get('entities', [])
|
||||
if entities:
|
||||
output.append(f"### {domain.upper()}")
|
||||
for entity in entities:
|
||||
output.append(f"- **{entity['name']}** (`{entity['type']}`)")
|
||||
output.append("")
|
||||
|
||||
if results.get('timeout'):
|
||||
output.append("⏱️ *Search timed out, showing partial results*")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
def format_entity_details(self, entity: Dict) -> str:
|
||||
"""Format entity details"""
|
||||
if 'error' in entity:
|
||||
return f"❌ {entity['error']}"
|
||||
|
||||
output = []
|
||||
output.append(f"# {entity.get('name', 'Unknown')}")
|
||||
output.append(f"**Type:** {entity.get('type', 'N/A')}")
|
||||
output.append(f"**Domain:** {entity.get('domain', 'N/A')}")
|
||||
output.append("")
|
||||
|
||||
if entity.get('description'):
|
||||
output.append(f"**Description:** {entity['description']}")
|
||||
output.append("")
|
||||
|
||||
if entity.get('observations'):
|
||||
output.append("**Observations:**")
|
||||
for obs in entity['observations'][:3]:
|
||||
output.append(f"- {obs}")
|
||||
output.append("")
|
||||
|
||||
if entity.get('relations'):
|
||||
output.append("**Relations:**")
|
||||
for rel in entity['relations'][:5]:
|
||||
output.append(f"- {rel['from']} **{rel['type']}** {rel['to']}")
|
||||
output.append("")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
def format_system_status(self, status: Dict) -> str:
|
||||
"""Format system status"""
|
||||
output = []
|
||||
output.append("# System Status")
|
||||
output.append("")
|
||||
|
||||
components = status.get('components', {})
|
||||
|
||||
# Uptime
|
||||
if components.get('uptime', {}).get('output'):
|
||||
output.append(f"**Uptime:** {components['uptime']['output']}")
|
||||
|
||||
# Load
|
||||
if components.get('load', {}).get('output'):
|
||||
output.append(f"**Load:** {components['load']['output']}")
|
||||
|
||||
# Disk
|
||||
if components.get('disk', {}).get('output'):
|
||||
disk_lines = components['disk']['output'].split('\n')
|
||||
if disk_lines:
|
||||
output.append(f"**Disk:** {disk_lines[1] if len(disk_lines) > 1 else disk_lines[0]}")
|
||||
|
||||
# Memory
|
||||
if components.get('memory', {}).get('output'):
|
||||
mem_lines = components['memory']['output'].split('\n')
|
||||
if mem_lines:
|
||||
output.append(f"**Memory:** {mem_lines[1] if len(mem_lines) > 1 else mem_lines[0]}")
|
||||
|
||||
output.append("")
|
||||
return "\n".join(output)
|
||||
|
||||
def format_command_output(self, result: Dict) -> str:
|
||||
"""Format bash command output"""
|
||||
output = []
|
||||
|
||||
if not result.get('success'):
|
||||
error = result.get('error', 'Unknown error')
|
||||
return f"❌ **Error:** {error}"
|
||||
|
||||
output.append(f"**Command:** `{result.get('command', 'N/A')}`")
|
||||
output.append(f"**Time:** {result.get('execution_time_ms', 0)}ms")
|
||||
output.append("")
|
||||
|
||||
cmd_output = result.get('output', '').strip()
|
||||
if cmd_output:
|
||||
# Format output as code block
|
||||
output.append("```")
|
||||
# Limit to 20 lines
|
||||
lines = cmd_output.split('\n')
|
||||
for line in lines[:20]:
|
||||
output.append(line)
|
||||
if len(lines) > 20:
|
||||
output.append(f"... ({len(lines) - 20} more lines)")
|
||||
output.append("```")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
def format_project_list(self, projects: Dict) -> str:
|
||||
"""Format list of projects"""
|
||||
output = []
|
||||
output.append("# Projects")
|
||||
output.append("")
|
||||
|
||||
project_list = projects.get('projects', [])
|
||||
if not project_list:
|
||||
return "No projects found."
|
||||
|
||||
for proj in project_list:
|
||||
output.append(f"- **{proj['name']}**")
|
||||
if proj.get('description'):
|
||||
output.append(f" > {proj['description']}")
|
||||
|
||||
output.append("")
|
||||
output.append(f"*Total: {projects.get('count', len(project_list))} projects*")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
def format_memory_statistics(self, stats: Dict) -> str:
|
||||
"""Format memory database statistics"""
|
||||
if not stats.get('available'):
|
||||
return "❌ Memory database not available"
|
||||
|
||||
output = []
|
||||
output.append("# Memory Database Status")
|
||||
output.append("")
|
||||
output.append(f"**Entities:** {stats.get('entities', 0)}")
|
||||
output.append(f"**Relations:** {stats.get('relations', 0)}")
|
||||
output.append("")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
def format_help(self) -> str:
|
||||
"""Format help message"""
|
||||
output = [
|
||||
"# Luzia Chat Help",
|
||||
"",
|
||||
"## Commands",
|
||||
"",
|
||||
"### Search",
|
||||
"```",
|
||||
"luzia chat \"search term\"",
|
||||
"luzia chat --kg \"knowledge graph search\"",
|
||||
"luzia chat --local \"project memory search\"",
|
||||
"```",
|
||||
"",
|
||||
"### System Status",
|
||||
"```",
|
||||
"luzia chat \"system status\"",
|
||||
"luzia chat --bash \"uptime\"",
|
||||
"luzia chat --bash \"disk usage\"",
|
||||
"```",
|
||||
"",
|
||||
"### Information",
|
||||
"```",
|
||||
"luzia chat \"list projects\"",
|
||||
"luzia chat \"architecture\"",
|
||||
"luzia chat --think \"analyze performance\"",
|
||||
"```",
|
||||
"",
|
||||
"### Interactive",
|
||||
"```",
|
||||
"luzia chat # Start interactive session",
|
||||
"> your query",
|
||||
"> another query",
|
||||
"> exit",
|
||||
"```",
|
||||
"",
|
||||
]
|
||||
return "\n".join(output)
|
||||
|
||||
def format_error(self, error: str, suggestions: list = None) -> str:
|
||||
"""Format error message"""
|
||||
output = [f"❌ **Error:** {error}"]
|
||||
|
||||
if suggestions:
|
||||
output.append("")
|
||||
output.append("**Suggestions:**")
|
||||
for suggestion in suggestions[:3]:
|
||||
output.append(f"- {suggestion}")
|
||||
|
||||
return "\n".join(output)
|
||||
|
||||
def format_response_time(self, time_ms: float) -> str:
|
||||
"""Format response time indicator"""
|
||||
if time_ms < 100:
|
||||
indicator = "⚡ instant"
|
||||
elif time_ms < 300:
|
||||
indicator = "✓ quick"
|
||||
elif time_ms < 500:
|
||||
indicator = "↻ normal"
|
||||
else:
|
||||
indicator = "⏱ slow"
|
||||
|
||||
return f"{indicator} ({time_ms:.0f}ms)"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
formatter = ChatResponseFormatter()
|
||||
|
||||
# Test
|
||||
print(formatter.format_help())
|
||||
Reference in New Issue
Block a user