Add /export and /mcp-status commands
- /export command to export session data as Markdown or JSON - Export for sharing with other Claude instances - Include session details, topics, summary, and memory items - /mcp-status command to check MCP integration health - Gmail venv and credentials status - Calendar API dependencies - Delegation helpers presence - MCP server configuration - Updated shell completions with 15 aliases total - Test suite now covers 23 tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
204
automation/session-export.py
Executable file
204
automation/session-export.py
Executable file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Export session data for sharing or archiving.
|
||||
Usage: python3 session-export.py [--format json|markdown] [--output FILE] [session_id]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
CLAUDE_DIR = Path.home() / ".claude"
|
||||
HISTORY_DIR = CLAUDE_DIR / "state" / "personal-assistant" / "history"
|
||||
MEMORY_DIR = CLAUDE_DIR / "state" / "personal-assistant" / "memory"
|
||||
|
||||
|
||||
def load_json(path: Path) -> Optional[Dict]:
|
||||
"""Load JSON file safely."""
|
||||
try:
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return None
|
||||
|
||||
|
||||
def get_session(session_id: Optional[str] = None) -> Optional[Dict]:
|
||||
"""Get a specific session or the most recent one."""
|
||||
index = load_json(HISTORY_DIR / "index.json")
|
||||
if not index or "sessions" not in index:
|
||||
return None
|
||||
|
||||
sessions = index["sessions"]
|
||||
if not sessions:
|
||||
return None
|
||||
|
||||
if session_id:
|
||||
# Find by ID prefix
|
||||
for s in sessions:
|
||||
if s.get("id", "").startswith(session_id):
|
||||
return s
|
||||
return None
|
||||
else:
|
||||
# Return most recent
|
||||
return sessions[-1]
|
||||
|
||||
|
||||
def get_memory_items(session_id: str) -> Dict[str, List]:
|
||||
"""Get memory items from a specific session."""
|
||||
result = {
|
||||
"preferences": [],
|
||||
"decisions": [],
|
||||
"projects": [],
|
||||
"facts": []
|
||||
}
|
||||
|
||||
for category in result.keys():
|
||||
data = load_json(MEMORY_DIR / f"{category}.json")
|
||||
if data and "items" in data:
|
||||
for item in data["items"]:
|
||||
if item.get("session", "") == session_id:
|
||||
result[category].append(item)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def export_json(session: Dict, memory: Dict) -> str:
|
||||
"""Export as JSON."""
|
||||
export = {
|
||||
"exported_at": datetime.now().isoformat(),
|
||||
"session": session,
|
||||
"memory_items": memory
|
||||
}
|
||||
return json.dumps(export, indent=2)
|
||||
|
||||
|
||||
def export_markdown(session: Dict, memory: Dict) -> str:
|
||||
"""Export as Markdown."""
|
||||
lines = [
|
||||
f"# Session Export",
|
||||
f"",
|
||||
f"**Exported:** {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
||||
f"",
|
||||
f"## Session Details",
|
||||
f"",
|
||||
f"- **ID:** {session.get('id', 'unknown')}",
|
||||
f"- **Date:** {session.get('date', 'unknown')}",
|
||||
f"- **Summarized:** {'Yes' if session.get('summarized') else 'No'}",
|
||||
f"",
|
||||
]
|
||||
|
||||
# Topics
|
||||
topics = session.get("topics", [])
|
||||
if topics:
|
||||
lines.append("### Topics")
|
||||
lines.append("")
|
||||
for topic in topics:
|
||||
lines.append(f"- {topic}")
|
||||
lines.append("")
|
||||
|
||||
# Summary
|
||||
summary = session.get("summary", "")
|
||||
if summary:
|
||||
lines.append("### Summary")
|
||||
lines.append("")
|
||||
lines.append(summary)
|
||||
lines.append("")
|
||||
|
||||
# Memory items
|
||||
has_memory = any(items for items in memory.values())
|
||||
if has_memory:
|
||||
lines.append("## Memory Items Created")
|
||||
lines.append("")
|
||||
|
||||
for category, items in memory.items():
|
||||
if items:
|
||||
lines.append(f"### {category.title()}")
|
||||
lines.append("")
|
||||
for item in items:
|
||||
content = item.get("content", "")
|
||||
context = item.get("context", "")
|
||||
lines.append(f"- {content}")
|
||||
if context:
|
||||
lines.append(f" - *Context: {context}*")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def list_sessions(limit: int = 10):
|
||||
"""List recent sessions for export."""
|
||||
index = load_json(HISTORY_DIR / "index.json")
|
||||
if not index or "sessions" not in index:
|
||||
print("No sessions found.")
|
||||
return
|
||||
|
||||
sessions = index["sessions"][-limit:]
|
||||
|
||||
print(f"\n📋 Recent Sessions (showing {len(sessions)})\n")
|
||||
print(f"{'ID':<12} {'Date':<12} {'Summarized':<12} {'Topics'}")
|
||||
print("-" * 60)
|
||||
|
||||
for session in reversed(sessions):
|
||||
sid = session.get("id", "unknown")[:10]
|
||||
date = session.get("date", "unknown")[:10]
|
||||
summarized = "✓" if session.get("summarized", False) else "○"
|
||||
topics = ", ".join(session.get("topics", [])[:2])
|
||||
if len(session.get("topics", [])) > 2:
|
||||
topics += "..."
|
||||
|
||||
print(f"{sid:<12} {date:<12} {summarized:<12} {topics}")
|
||||
|
||||
print(f"\nExport with: python3 session-export.py <session_id>")
|
||||
print("")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Export session data")
|
||||
parser.add_argument("session_id", nargs="?", help="Session ID to export")
|
||||
parser.add_argument("--format", "-f", choices=["json", "markdown", "md"],
|
||||
default="markdown", help="Export format (default: markdown)")
|
||||
parser.add_argument("--output", "-o", type=str, help="Output file (default: stdout)")
|
||||
parser.add_argument("--list", "-l", action="store_true", help="List recent sessions")
|
||||
parser.add_argument("--limit", "-n", type=int, default=10,
|
||||
help="Number of sessions to list (default: 10)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list:
|
||||
list_sessions(args.limit)
|
||||
return 0
|
||||
|
||||
# Get session
|
||||
session = get_session(args.session_id)
|
||||
if not session:
|
||||
if args.session_id:
|
||||
print(f"Session '{args.session_id}' not found.")
|
||||
else:
|
||||
print("No sessions found. Run some sessions first.")
|
||||
return 1
|
||||
|
||||
# Get memory items for this session
|
||||
memory = get_memory_items(session.get("id", ""))
|
||||
|
||||
# Export
|
||||
if args.format in ["markdown", "md"]:
|
||||
content = export_markdown(session, memory)
|
||||
else:
|
||||
content = export_json(session, memory)
|
||||
|
||||
# Output
|
||||
if args.output:
|
||||
output_path = Path(args.output)
|
||||
output_path.write_text(content)
|
||||
print(f"Exported to: {output_path}")
|
||||
else:
|
||||
print(content)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user