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>
216 lines
6.6 KiB
Python
216 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Chat Memory Lookup - Fast local memory queries
|
|
Queries shared project memory without external calls
|
|
"""
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
import time
|
|
|
|
|
|
class ChatMemoryLookup:
|
|
"""Query local project memory for chat interface"""
|
|
|
|
MEMORY_DB = Path('/etc/zen-swarm/memory/projects.db')
|
|
|
|
def __init__(self, timeout_ms: int = 150):
|
|
"""Initialize with query timeout"""
|
|
self.timeout_ms = timeout_ms
|
|
self.timeout_seconds = timeout_ms / 1000.0
|
|
|
|
def search_entities(self, query: str, limit: int = 10) -> Dict:
|
|
"""Search for entities by name"""
|
|
if not self.MEMORY_DB.exists():
|
|
return {'error': 'Memory database not found', 'entities': []}
|
|
|
|
try:
|
|
conn = sqlite3.connect(str(self.MEMORY_DB), timeout=self.timeout_seconds)
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute(
|
|
"SELECT id, name, type FROM entities WHERE name LIKE ? LIMIT ?",
|
|
(f'%{query}%', limit)
|
|
)
|
|
|
|
entities = [
|
|
{
|
|
'id': row['id'],
|
|
'name': row['name'],
|
|
'type': row['type']
|
|
}
|
|
for row in cursor.fetchall()
|
|
]
|
|
|
|
conn.close()
|
|
return {'entities': entities, 'count': len(entities)}
|
|
|
|
except Exception as e:
|
|
return {'error': str(e), 'entities': []}
|
|
|
|
def get_entity(self, entity_name: str) -> Dict:
|
|
"""Get entity and its relations"""
|
|
if not self.MEMORY_DB.exists():
|
|
return {'error': 'Memory database not found'}
|
|
|
|
try:
|
|
conn = sqlite3.connect(str(self.MEMORY_DB), timeout=self.timeout_seconds)
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
|
|
# Get entity
|
|
cursor.execute(
|
|
"SELECT id, name, type FROM entities WHERE name = ?",
|
|
(entity_name,)
|
|
)
|
|
entity_row = cursor.fetchone()
|
|
|
|
if not entity_row:
|
|
conn.close()
|
|
return {'error': f'Entity {entity_name} not found'}
|
|
|
|
entity_id = entity_row['id']
|
|
entity = {
|
|
'name': entity_row['name'],
|
|
'type': entity_row['type'],
|
|
'relations': []
|
|
}
|
|
|
|
# Get relations (join to get entity names)
|
|
cursor.execute("""
|
|
SELECT e1.name as from_name, e2.name as to_name, r.relation, r.context
|
|
FROM relations r
|
|
JOIN entities e1 ON r.source_id = e1.id
|
|
JOIN entities e2 ON r.target_id = e2.id
|
|
WHERE r.source_id = ? OR r.target_id = ?
|
|
LIMIT 20
|
|
""", (entity_id, entity_id))
|
|
|
|
for row in cursor.fetchall():
|
|
entity['relations'].append({
|
|
'from': row['from_name'],
|
|
'to': row['to_name'],
|
|
'type': row['relation'],
|
|
'context': row['context']
|
|
})
|
|
|
|
conn.close()
|
|
return entity
|
|
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
def get_project_info(self, project_name: str) -> Dict:
|
|
"""Get project-specific information"""
|
|
if not self.MEMORY_DB.exists():
|
|
return {'error': 'Memory database not found'}
|
|
|
|
try:
|
|
conn = sqlite3.connect(str(self.MEMORY_DB), timeout=self.timeout_seconds)
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
|
|
# Get project entity
|
|
cursor.execute(
|
|
"SELECT id, name, type FROM entities WHERE name = ? AND type = 'project'",
|
|
(project_name,)
|
|
)
|
|
project_row = cursor.fetchone()
|
|
|
|
if not project_row:
|
|
conn.close()
|
|
return {'error': f'Project {project_name} not found'}
|
|
|
|
project_id = project_row['id']
|
|
project = {
|
|
'name': project_row['name'],
|
|
'type': project_row['type'],
|
|
'related_entities': []
|
|
}
|
|
|
|
# Get related entities
|
|
cursor.execute("""
|
|
SELECT e.name FROM entities e
|
|
JOIN relations r ON r.target_id = e.id
|
|
WHERE r.source_id = ?
|
|
LIMIT 10
|
|
""", (project_id,))
|
|
|
|
for row in cursor.fetchall():
|
|
project['related_entities'].append(row['name'])
|
|
|
|
conn.close()
|
|
return project
|
|
|
|
except Exception as e:
|
|
return {'error': str(e)}
|
|
|
|
def list_all_projects(self) -> Dict:
|
|
"""List all projects in memory"""
|
|
if not self.MEMORY_DB.exists():
|
|
return {'error': 'Memory database not found', 'projects': []}
|
|
|
|
try:
|
|
conn = sqlite3.connect(str(self.MEMORY_DB), timeout=self.timeout_seconds)
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute(
|
|
"SELECT name, type FROM entities WHERE type = 'project' OR type = 'Project' LIMIT 50"
|
|
)
|
|
|
|
projects = [
|
|
{
|
|
'name': row['name'],
|
|
'type': row['type']
|
|
}
|
|
for row in cursor.fetchall()
|
|
]
|
|
|
|
conn.close()
|
|
return {'projects': projects, 'count': len(projects)}
|
|
|
|
except Exception as e:
|
|
return {'error': str(e), 'projects': []}
|
|
|
|
def memory_statistics(self) -> Dict:
|
|
"""Get memory database statistics"""
|
|
if not self.MEMORY_DB.exists():
|
|
return {'available': False}
|
|
|
|
try:
|
|
conn = sqlite3.connect(str(self.MEMORY_DB), timeout=self.timeout_seconds)
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM entities")
|
|
entity_count = cursor.fetchone()[0]
|
|
|
|
cursor.execute("SELECT COUNT(*) FROM relations")
|
|
relation_count = cursor.fetchone()[0]
|
|
|
|
stats = {
|
|
'available': True,
|
|
'entities': entity_count,
|
|
'relations': relation_count
|
|
}
|
|
|
|
conn.close()
|
|
return stats
|
|
|
|
except Exception as e:
|
|
return {'available': False, 'error': str(e)}
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import json
|
|
lookup = ChatMemoryLookup()
|
|
|
|
print("Memory Statistics:")
|
|
print(json.dumps(lookup.memory_statistics(), indent=2))
|
|
print()
|
|
|
|
print("List Projects:")
|
|
print(json.dumps(lookup.list_all_projects(), indent=2))
|