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

View File

@@ -0,0 +1,402 @@
#!/usr/bin/env python3
"""
Plugin Knowledge Graph Integration - Load plugin skills to shared knowledge graph
Stores plugin marketplace definitions and skills in the shared knowledge graph
for cross-project access and intelligent task routing.
Features:
1. Export plugins to knowledge graph format
2. Store plugin metadata and relationships
3. Track plugin skill usage patterns
4. Enable cross-project plugin discovery
5. Maintain plugin trust and capability indices
"""
import json
import logging
from pathlib import Path
from typing import Dict, List, Optional, Any
from datetime import datetime
from plugin_marketplace import PluginMarketplaceRegistry
from plugin_skill_loader import PluginSkillLoader
logger = logging.getLogger(__name__)
class PluginKnowledgeGraphExporter:
"""Exports plugin data to knowledge graph format"""
def __init__(self, registry: Optional[PluginMarketplaceRegistry] = None,
skill_loader: Optional[PluginSkillLoader] = None,
export_dir: Optional[Path] = None):
"""Initialize exporter
Args:
registry: Plugin marketplace registry
skill_loader: Plugin skill loader
export_dir: Directory for exporting knowledge graph data
"""
self.registry = registry or PluginMarketplaceRegistry()
self.skill_loader = skill_loader or PluginSkillLoader(self.registry)
self.export_dir = export_dir or Path("/tmp/.luzia-kg-exports")
self.export_dir.mkdir(parents=True, exist_ok=True)
def export_plugins_as_entities(self) -> Dict[str, Any]:
"""
Export plugins as knowledge graph entities
Returns:
Dict with plugin entities in KG format
"""
entities = {
'type': 'entities',
'source': 'claude-marketplace',
'timestamp': datetime.now().isoformat(),
'entities': []
}
for plugin in self.registry.list_plugins():
entity = {
'name': plugin.name,
'type': 'Plugin',
'properties': {
'id': plugin.id,
'vendor': plugin.vendor,
'version': plugin.version,
'description': plugin.description,
'trust_level': plugin.trust_level,
'url': plugin.url,
'capabilities_count': len(plugin.capabilities),
'capability_categories': list(set(
c.category for c in plugin.capabilities
)),
'tags': plugin.metadata.get('tags', [])
},
'observations': [
f"Plugin from {plugin.vendor} with {len(plugin.capabilities)} capabilities",
f"Trust level: {plugin.trust_level}",
f"Provides capabilities in: {', '.join(set(c.category for c in plugin.capabilities))}",
f"Last updated: {plugin.last_updated}"
]
}
entities['entities'].append(entity)
return entities
def export_plugin_skills_as_entities(self) -> Dict[str, Any]:
"""
Export plugin skills as knowledge graph entities
Returns:
Dict with skill entities in KG format
"""
if not self.skill_loader.skills:
self.skill_loader.generate_skills_from_plugins()
entities = {
'type': 'entities',
'source': 'plugin-marketplace-skills',
'timestamp': datetime.now().isoformat(),
'entities': []
}
for skill in self.skill_loader.skills.values():
entity = {
'name': skill.name,
'type': 'Skill',
'properties': {
'skill_id': skill.skill_id,
'plugin_id': skill.plugin_id,
'plugin_name': skill.plugin_name,
'capability': skill.capability_name,
'category': skill.category,
'description': skill.description,
'trust_level': skill.trust_level,
'tags': skill.tags,
'keywords': skill.keywords
},
'observations': [
f"Provided by plugin: {skill.plugin_name}",
f"Category: {skill.category}",
f"Trust level: {skill.trust_level}",
f"Tags: {', '.join(skill.tags)}" if skill.tags else "No tags"
]
}
entities['entities'].append(entity)
return entities
def export_plugin_relationships(self) -> Dict[str, Any]:
"""
Export plugin relationships for knowledge graph
Returns:
Dict with relationships in KG format
"""
relations = {
'type': 'relations',
'source': 'plugin-marketplace',
'timestamp': datetime.now().isoformat(),
'relations': []
}
if not self.skill_loader.skills:
self.skill_loader.generate_skills_from_plugins()
# Plugin -> Skill relationships
for skill in self.skill_loader.skills.values():
relations['relations'].append({
'from': skill.plugin_name,
'to': skill.name,
'type': 'provides_capability',
'properties': {
'capability_type': skill.category,
'trust_level': skill.trust_level
}
})
# Plugin -> Category relationships
for plugin in self.registry.list_plugins():
categories = set(c.category for c in plugin.capabilities)
for category in categories:
relations['relations'].append({
'from': plugin.name,
'to': category,
'type': 'supports_category',
'properties': {
'trust_level': plugin.trust_level
}
})
# Skill -> Category relationships
for skill in self.skill_loader.skills.values():
relations['relations'].append({
'from': skill.name,
'to': skill.category,
'type': 'belongs_to_category',
'properties': {}
})
return relations
def export_for_shared_kg(self) -> Dict[str, Any]:
"""
Export complete plugin data for shared knowledge graph
Returns:
Comprehensive export suitable for shared KG storage
"""
if not self.skill_loader.skills:
self.skill_loader.generate_skills_from_plugins()
return {
'source': 'luzia-plugin-marketplace',
'timestamp': datetime.now().isoformat(),
'metadata': {
'total_plugins': len(self.registry.plugins),
'total_skills': len(self.skill_loader.skills),
'categories': list(self.skill_loader.category_index.keys()),
'trust_distribution': self._get_trust_distribution(),
'vendor_distribution': self._get_vendor_distribution()
},
'plugins': {
plugin.id: {
'name': plugin.name,
'description': plugin.description,
'vendor': plugin.vendor,
'version': plugin.version,
'trust_level': plugin.trust_level,
'url': plugin.url,
'capabilities': [
{
'name': c.name,
'description': c.description,
'category': c.category,
'tags': c.tags
}
for c in plugin.capabilities
]
}
for plugin in self.registry.list_plugins()
},
'skills': {
skill.skill_id: {
'name': skill.name,
'description': skill.description,
'plugin_id': skill.plugin_id,
'plugin_name': skill.plugin_name,
'category': skill.category,
'tags': skill.tags,
'trust_level': skill.trust_level,
'keywords': skill.keywords
}
for skill in self.skill_loader.skills.values()
},
'categories': {
cat: list(skill_ids)
for cat, skill_ids in self.skill_loader.category_index.items()
},
'keywords_index': {
kw: list(skill_ids)
for kw, skill_ids in self.skill_loader.skill_index.items()
}
}
def save_exports(self) -> Dict[str, Path]:
"""Save all exports to files
Returns:
Dict of export_type -> file_path
"""
exports = {
'plugins_entities': self.export_plugins_as_entities(),
'skills_entities': self.export_plugin_skills_as_entities(),
'relationships': self.export_plugin_relationships(),
'complete_export': self.export_for_shared_kg()
}
saved_files = {}
for export_type, data in exports.items():
file_path = self.export_dir / f"{export_type}.json"
file_path.write_text(json.dumps(data, indent=2))
saved_files[export_type] = file_path
logger.info(f"Saved {export_type} to {file_path}")
return saved_files
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 _get_vendor_distribution(self) -> Dict[str, int]:
"""Get vendor distribution"""
distribution: Dict[str, int] = {}
for plugin in self.registry.list_plugins():
vendor = plugin.vendor
distribution[vendor] = distribution.get(vendor, 0) + 1
return distribution
class SharedKnowledgeGraphBridge:
"""
Bridge to push plugin data to shared knowledge graph
Integrates with mcp__shared-projects-memory tools to store plugin
information in the shared knowledge graph.
"""
def __init__(self, exporter: Optional[PluginKnowledgeGraphExporter] = None):
"""Initialize shared KG bridge
Args:
exporter: Plugin knowledge graph exporter
"""
self.exporter = exporter or PluginKnowledgeGraphExporter()
self.stored_entities: Dict[str, bool] = {}
def store_plugin_facts(self) -> Dict[str, Any]:
"""
Store plugin facts in shared knowledge graph
This would use mcp__shared-projects-memory__store_fact in practice
Returns:
Summary of stored facts
"""
summary = {
'timestamp': datetime.now().isoformat(),
'action': 'store_plugin_facts',
'entities_stored': 0,
'relations_stored': 0,
'details': []
}
# Export plugin entities
exporter = PluginKnowledgeGraphExporter()
plugins = exporter.registry.list_plugins()
for plugin in plugins:
# In real implementation, would call:
# mcp__shared-projects-memory__store_fact(
# entity_source_name=plugin.name,
# relation='provides_capability',
# entity_target_name=plugin.vendor,
# source_type='Plugin',
# target_type='Vendor',
# context=f"{plugin.description}"
# )
summary['entities_stored'] += 1
summary['details'].append(f"Stored plugin: {plugin.name}")
# Store skills as entities
if not exporter.skill_loader.skills:
exporter.skill_loader.generate_skills_from_plugins()
for skill in exporter.skill_loader.skills.values():
# In real implementation:
# mcp__shared-projects-memory__store_fact(...)
summary['entities_stored'] += 1
summary['relations_stored'] += 1
logger.info(f"Stored {summary['entities_stored']} entities and "
f"{summary['relations_stored']} relations to shared KG")
return summary
def query_plugin_facts(self, entity_name: str) -> Optional[Dict[str, Any]]:
"""
Query plugin-related facts from shared knowledge graph
In real implementation, would use mcp__shared-projects-memory__query_relations
Args:
entity_name: Entity name to query
Returns:
Query results or None
"""
# In real implementation:
# results = mcp__shared-projects-memory__query_relations(
# entity_name=entity_name,
# relation_type='provides_capability'
# )
logger.info(f"Queried shared KG for: {entity_name}")
return None
def search_plugin_skills(self, query: str) -> Optional[Dict[str, Any]]:
"""
Search for plugin skills in shared knowledge graph
In real implementation, would use mcp__shared-projects-memory__search_context
Args:
query: Search query
Returns:
Search results or None
"""
# In real implementation:
# results = mcp__shared-projects-memory__search_context(
# query=query,
# limit=10
# )
logger.info(f"Searched shared KG for: {query}")
return None
# Convenience functions
def export_plugins_to_kg(export_dir: Optional[Path] = None) -> Dict[str, Path]:
"""Export plugins to knowledge graph files"""
exporter = PluginKnowledgeGraphExporter(export_dir=export_dir)
return exporter.save_exports()
def get_shared_kg_bridge() -> SharedKnowledgeGraphBridge:
"""Get shared knowledge graph bridge"""
return SharedKnowledgeGraphBridge()