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>
230 lines
7.3 KiB
Python
230 lines
7.3 KiB
Python
#!/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())
|