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:
admin
2026-01-14 10:42:16 -03:00
commit ec33ac1936
265 changed files with 92011 additions and 0 deletions

260
lib/plugin_cli.py Normal file
View File

@@ -0,0 +1,260 @@
#!/usr/bin/env python3
"""
Plugin CLI Integration - Command handlers for plugin marketplace operations
Provides CLI commands for:
- luzia plugins list
- luzia plugins <name>
- luzia plugins skills
- luzia plugins find <task>
- luzia plugins export
"""
import json
import sys
from pathlib import Path
from typing import Dict, Any, Optional, List
from plugin_marketplace import get_marketplace_registry
from plugin_skill_loader import get_plugin_skill_loader
from dispatcher_plugin_integration import get_dispatcher_bridge
from plugin_kg_integration import export_plugins_to_kg
class PluginCLI:
"""CLI handler for plugin marketplace operations"""
def __init__(self):
self.registry = get_marketplace_registry()
self.skill_loader = get_plugin_skill_loader()
self.bridge = get_dispatcher_bridge(self.registry)
def handle_plugins_command(self, args: List[str]) -> int:
"""
Handle: luzia plugins <subcommand> [args...]
Subcommands:
- list: List all available plugins
- <name>: Show plugin details
- skills: List all plugin skills
- find <task>: Find plugins for a task
- export: Export all plugin data
- stats: Show statistics
"""
if not args:
return self.show_plugins_help()
subcommand = args[0]
if subcommand == "list":
return self.cmd_list_plugins(args[1:])
elif subcommand == "skills":
return self.cmd_list_skills(args[1:])
elif subcommand == "find":
return self.cmd_find_plugins(args[1:])
elif subcommand == "export":
return self.cmd_export_plugins(args[1:])
elif subcommand == "stats":
return self.cmd_show_stats(args[1:])
elif subcommand == "help":
return self.show_plugins_help()
else:
# Try as plugin name for details
return self.cmd_show_plugin(subcommand)
def show_plugins_help(self) -> int:
"""Show plugin CLI help"""
help_text = """
Plugin Marketplace Commands:
luzia plugins list List all plugins
luzia plugins <name> Show plugin details
luzia plugins skills List all plugin skills
luzia plugins find "<task>" Find plugins for a task
luzia plugins export Export plugin data to files
luzia plugins stats Show statistics
luzia plugins help Show this help
Examples:
luzia plugins list
luzia plugins code-simplifier
luzia plugins find "review code for security"
luzia plugins export
"""
print(help_text)
return 0
def cmd_list_plugins(self, args: List[str]) -> int:
"""Handle: luzia plugins list"""
plugins = self.registry.list_plugins()
if not plugins:
print("No plugins available")
return 0
print(f"\n{'Name':<30} {'Vendor':<15} {'Trust':<10} {'Capabilities'}")
print("-" * 75)
for plugin in plugins:
cap_count = len(plugin.capabilities)
print(f"{plugin.name:<30} {plugin.vendor:<15} {plugin.trust_level:<10} {cap_count}")
print(f"\nTotal: {len(plugins)} plugins\n")
return 0
def cmd_show_plugin(self, plugin_id: str) -> int:
"""Show plugin details"""
plugin = self.registry.get_plugin(plugin_id)
if not plugin:
# Try to find by name
plugins = self.registry.list_plugins()
for p in plugins:
if p.name.lower() == plugin_id.lower() or p.id.lower() == plugin_id.lower():
plugin = p
break
if not plugin:
print(f"Plugin not found: {plugin_id}")
return 1
output = {
'id': plugin.id,
'name': plugin.name,
'description': plugin.description,
'vendor': plugin.vendor,
'version': plugin.version,
'url': plugin.url,
'trust_level': plugin.trust_level,
'capabilities': [
{
'name': c.name,
'description': c.description,
'category': c.category,
'tags': c.tags
}
for c in plugin.capabilities
]
}
print(json.dumps(output, indent=2))
return 0
def cmd_list_skills(self, args: List[str]) -> int:
"""Handle: luzia plugins skills"""
if not self.skill_loader.skills:
self.skill_loader.generate_skills_from_plugins()
skills = self.skill_loader.list_skills()
if not skills:
print("No skills available")
return 0
print(f"\n{'Skill ID':<40} {'Category':<20} {'Trust':<10}")
print("-" * 75)
for skill in skills:
print(f"{skill.skill_id:<40} {skill.category:<20} {skill.trust_level:<10}")
print(f"\nTotal: {len(skills)} skills\n")
return 0
def cmd_find_plugins(self, args: List[str]) -> int:
"""Handle: luzia plugins find <task>"""
if not args:
print("Usage: luzia plugins find '<task description>'")
return 1
task = " ".join(args)
matched_skills = self.skill_loader.find_skills_for_task(task, min_relevance=0.3)
if not matched_skills:
print(f"No matching skills found for: {task}\n")
return 0
output = {
'query': task,
'matched_skills': matched_skills,
'count': len(matched_skills)
}
print(json.dumps(output, indent=2))
return 0
def cmd_export_plugins(self, args: List[str]) -> int:
"""Handle: luzia plugins export"""
output_dir = None
if args and args[0].startswith('--output='):
output_dir = Path(args[0].split('=')[1])
saved_files = export_plugins_to_kg(export_dir=output_dir)
output = {
'action': 'export_plugins',
'status': 'success',
'files': {k: str(v) for k, v in saved_files.items()},
'count': len(saved_files)
}
print(json.dumps(output, indent=2))
return 0
def cmd_show_stats(self, args: List[str]) -> int:
"""Handle: luzia plugins stats"""
if not self.skill_loader.skills:
self.skill_loader.generate_skills_from_plugins()
stats = {
'total_plugins': len(self.registry.plugins),
'total_skills': len(self.skill_loader.skills),
'categories': list(self.skill_loader.category_index.keys()),
'category_counts': {
cat: len(skill_ids)
for cat, skill_ids in self.skill_loader.category_index.items()
},
'keywords': len(self.skill_loader.skill_index),
'trust_distribution': self._get_trust_distribution()
}
print(json.dumps(stats, indent=2))
return 0
def _get_trust_distribution(self) -> Dict[str, int]:
"""Get trust level distribution"""
distribution: Dict[str, int] = {}
for plugin in self.registry.list_plugins():
trust = plugin.trust_level
distribution[trust] = distribution.get(trust, 0) + 1
return distribution
def cmd_dispatch_with_plugins(self, task: str, project: str = "test",
job_id: str = "job-test") -> Dict[str, Any]:
"""
Example: Dispatch a task with plugin context
Returns dispatch result with plugin recommendations
"""
dispatch_result = self.bridge.dispatch_with_plugin_context(task, project, job_id)
return dispatch_result
# Integration functions for main CLI
def match_plugins_command(args: list) -> Optional[list]:
"""Match 'luzia plugins' command"""
if args and args[0] == "plugins":
return args[1:]
return None
def route_plugins_command(config: dict, args: list, kwargs: dict) -> int:
"""Route to plugin CLI handler"""
cli = PluginCLI()
return cli.handle_plugins_command(args)
# Convenience functions
def get_plugin_cli() -> PluginCLI:
"""Get plugin CLI instance"""
return PluginCLI()