diff --git a/automation/completions.bash b/automation/completions.bash index 8e2ceee..9b4e064 100644 --- a/automation/completions.bash +++ b/automation/completions.bash @@ -33,6 +33,18 @@ _claude_debug() { COMPREPLY=($(compgen -W "--full --json --paths" -- "${cur}")) } +_claude_export() { + local cur="${COMP_WORDS[COMP_CWORD]}" + + COMPREPLY=($(compgen -W "--list --format --output" -- "${cur}")) +} + +_claude_mcp_status() { + local cur="${COMP_WORDS[COMP_CWORD]}" + + COMPREPLY=($(compgen -W "--json" -- "${cur}")) +} + _claude_memory_add() { local cur="${COMP_WORDS[COMP_CWORD]}" local prev="${COMP_WORDS[COMP_CWORD-1]}" @@ -66,6 +78,8 @@ complete -F _claude_search search.py complete -F _claude_history history-browser.py complete -F _claude_log log-viewer.py complete -F _claude_debug debug.sh +complete -F _claude_export session-export.py +complete -F _claude_mcp_status mcp-status.sh # Alias completions for convenience alias claude-validate='~/.claude/automation/validate-setup.sh' @@ -82,7 +96,9 @@ alias claude-test='~/.claude/automation/test-scripts.sh' alias claude-maintenance='~/.claude/automation/daily-maintenance.sh' alias claude-log='python3 ~/.claude/automation/log-viewer.py' alias claude-debug='~/.claude/automation/debug.sh' +alias claude-export='python3 ~/.claude/automation/session-export.py' +alias claude-mcp='~/.claude/automation/mcp-status.sh' echo "Claude Code completions loaded. Available aliases:" echo " claude-{validate,status,backup,restore,clean,memory-add,memory-list}" -echo " claude-{search,history,install,test,maintenance,log,debug}" +echo " claude-{search,history,install,test,maintenance,log,debug,export,mcp}" diff --git a/automation/completions.zsh b/automation/completions.zsh index 924cd5b..bb2b86b 100644 --- a/automation/completions.zsh +++ b/automation/completions.zsh @@ -83,6 +83,21 @@ _claude_debug() { '--paths[Show paths only]' } +# Export completion +_claude_export() { + _arguments \ + '--list[List sessions]' \ + '--format[Output format]:format:(json markdown md)' \ + '--output[Output file]:file:_files' \ + '*:session_id:' +} + +# MCP status completion +_claude_mcp_status() { + _arguments \ + '--json[JSON output]' +} + # Register completions compdef _memory_add memory-add.py compdef _memory_list memory-list.py @@ -91,6 +106,8 @@ compdef _claude_search search.py compdef _claude_history history-browser.py compdef _claude_log log-viewer.py compdef _claude_debug debug.sh +compdef _claude_export session-export.py +compdef _claude_mcp_status mcp-status.sh # Aliases alias claude-validate='~/.claude/automation/validate-setup.sh' @@ -107,7 +124,9 @@ alias claude-test='~/.claude/automation/test-scripts.sh' alias claude-maintenance='~/.claude/automation/daily-maintenance.sh' alias claude-log='python3 ~/.claude/automation/log-viewer.py' alias claude-debug='~/.claude/automation/debug.sh' +alias claude-export='python3 ~/.claude/automation/session-export.py' +alias claude-mcp='~/.claude/automation/mcp-status.sh' echo "Claude Code completions loaded (zsh)" echo " Aliases: claude-{validate,status,backup,restore,clean,memory-add,memory-list}" -echo " claude-{search,history,install,test,maintenance,log,debug}" +echo " claude-{search,history,install,test,maintenance,log,debug,export,mcp}" diff --git a/automation/mcp-status.sh b/automation/mcp-status.sh new file mode 100755 index 0000000..ccc7f9c --- /dev/null +++ b/automation/mcp-status.sh @@ -0,0 +1,207 @@ +#!/bin/bash +# Check status of MCP integrations +# Usage: ./mcp-status.sh [--json] + +set -euo pipefail + +CLAUDE_DIR="${HOME}/.claude" +MCP_DIR="${CLAUDE_DIR}/mcp" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +JSON_OUTPUT=false +[[ "${1:-}" == "--json" ]] && JSON_OUTPUT=true + +check_gmail() { + local status="unknown" + local details="" + + # Check venv exists + local venv_dir="${MCP_DIR}/gmail/venv" + if [[ -d "$venv_dir" ]]; then + # Check python in venv + if [[ -x "${venv_dir}/bin/python" ]]; then + # Check if gmail-mcp is installed + if "${venv_dir}/bin/python" -c "import gmail_mcp" 2>/dev/null; then + # Check credentials + local creds_file="${HOME}/.gmail-mcp/credentials.json" + local token_file="${HOME}/.gmail-mcp/token.json" + + if [[ -f "$creds_file" ]]; then + if [[ -f "$token_file" ]]; then + status="ready" + details="Credentials and token present" + else + status="needs_auth" + details="Credentials present, needs OAuth flow" + fi + else + status="needs_setup" + details="Missing credentials.json" + fi + else + status="incomplete" + details="gmail-mcp not installed in venv" + fi + else + status="broken" + details="Venv python not found" + fi + else + status="not_installed" + details="No venv at ${venv_dir}" + fi + + if $JSON_OUTPUT; then + echo " \"gmail\": {\"status\": \"$status\", \"details\": \"$details\"}" + else + case "$status" in + ready) + echo -e "${GREEN}✓${NC} Gmail: $details" + ;; + needs_auth) + echo -e "${YELLOW}○${NC} Gmail: $details" + ;; + *) + echo -e "${RED}✗${NC} Gmail: $details" + ;; + esac + fi +} + +check_gcal() { + local status="unknown" + local details="" + + # GCal uses same credentials as Gmail + local creds_file="${HOME}/.gmail-mcp/credentials.json" + local token_file="${HOME}/.gmail-mcp/token.json" + + # Check if google-api-python-client is available + if python3 -c "import googleapiclient.discovery" 2>/dev/null; then + if [[ -f "$creds_file" ]]; then + if [[ -f "$token_file" ]]; then + status="ready" + details="Credentials and token present" + else + status="needs_auth" + details="Credentials present, needs OAuth flow" + fi + else + status="needs_setup" + details="Missing credentials.json" + fi + else + status="missing_deps" + details="google-api-python-client not installed" + fi + + if $JSON_OUTPUT; then + echo " \"gcal\": {\"status\": \"$status\", \"details\": \"$details\"}" + else + case "$status" in + ready) + echo -e "${GREEN}✓${NC} Calendar: $details" + ;; + needs_auth|missing_deps) + echo -e "${YELLOW}○${NC} Calendar: $details" + ;; + *) + echo -e "${RED}✗${NC} Calendar: $details" + ;; + esac + fi +} + +check_delegation() { + local status="ready" + local gmail_helper="${MCP_DIR}/delegation/gmail_delegate.py" + local gcal_helper="${MCP_DIR}/delegation/gcal_delegate.py" + local details="Delegation helpers" + + if [[ -f "$gmail_helper" ]] && [[ -f "$gcal_helper" ]]; then + status="ready" + details="Both helpers present" + elif [[ -f "$gmail_helper" ]] || [[ -f "$gcal_helper" ]]; then + status="partial" + details="Some helpers missing" + else + status="not_installed" + details="No delegation helpers found" + fi + + if $JSON_OUTPUT; then + echo " \"delegation\": {\"status\": \"$status\", \"details\": \"$details\"}" + else + case "$status" in + ready) + echo -e "${GREEN}✓${NC} Delegation: $details" + ;; + partial) + echo -e "${YELLOW}○${NC} Delegation: $details" + ;; + *) + echo -e "${RED}✗${NC} Delegation: $details" + ;; + esac + fi +} + +check_mcp_servers() { + # Check for .mcp.json or mcp server configurations + local mcp_config="${CLAUDE_DIR}/.mcp.json" + local status="none" + local count=0 + + if [[ -f "$mcp_config" ]]; then + # Count servers + count=$(python3 -c "import json; data = json.load(open('$mcp_config')); print(len(data.get('mcpServers', {})))" 2>/dev/null || echo "0") + if [[ "$count" -gt 0 ]]; then + status="configured" + else + status="empty" + fi + fi + + if $JSON_OUTPUT; then + echo " \"mcp_servers\": {\"status\": \"$status\", \"count\": $count}" + else + if [[ "$status" == "configured" ]]; then + echo -e "${GREEN}✓${NC} MCP Servers: $count configured" + elif [[ "$status" == "empty" ]]; then + echo -e "${YELLOW}○${NC} MCP Servers: Config exists but empty" + else + echo -e "${BLUE}ℹ${NC} MCP Servers: No .mcp.json found (using direct API)" + fi + fi +} + +# Main output +if $JSON_OUTPUT; then + echo "{" + echo " \"timestamp\": \"$(date -Iseconds)\"," + echo " \"integrations\": {" + check_gmail + echo "," + check_gcal + echo "," + check_delegation + echo "," + check_mcp_servers + echo " }" + echo "}" +else + echo "" + echo "🔌 MCP Integration Status" + echo "" + check_gmail + check_gcal + check_delegation + check_mcp_servers + echo "" +fi diff --git a/automation/session-export.py b/automation/session-export.py new file mode 100755 index 0000000..a084be2 --- /dev/null +++ b/automation/session-export.py @@ -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 ") + 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()) diff --git a/automation/test-scripts.sh b/automation/test-scripts.sh index 4baf327..1ac7e64 100755 --- a/automation/test-scripts.sh +++ b/automation/test-scripts.sh @@ -78,6 +78,13 @@ else fail "log-viewer.py syntax error" fi +# Test 9: session-export.py +if python3 -m py_compile "${AUTOMATION_DIR}/session-export.py" 2>/dev/null; then + pass "session-export.py syntax valid" +else + fail "session-export.py syntax error" +fi + echo "" echo "=== Skill Scripts ===" @@ -122,7 +129,7 @@ else fi # Test automation bash scripts -for script in install.sh daily-maintenance.sh backup.sh restore.sh clean.sh debug.sh; do +for script in install.sh daily-maintenance.sh backup.sh restore.sh clean.sh debug.sh mcp-status.sh; do if [[ -f "${AUTOMATION_DIR}/${script}" ]]; then if bash -n "${AUTOMATION_DIR}/${script}" 2>/dev/null; then pass "${script} syntax valid" diff --git a/commands/README.md b/commands/README.md index 6493f8e..b6d7b12 100644 --- a/commands/README.md +++ b/commands/README.md @@ -17,6 +17,8 @@ Slash commands for quick actions. User-invoked (type `/command` to trigger). | `/search` | `/find`, `/lookup` | Search memory, history, config | | `/log` | `/logs`, `/logview` | View and analyze logs | | `/debug` | `/diag`, `/diagnose` | Debug and troubleshoot config | +| `/export` | `/session-export`, `/share` | Export session for sharing | +| `/mcp-status` | `/mcp`, `/integrations` | Check MCP integrations | | `/maintain` | `/maintenance`, `/admin` | Configuration maintenance | | `/programmer` | | Code development tasks | | `/gcal` | `/calendar`, `/cal` | Google Calendar access | diff --git a/commands/export.md b/commands/export.md new file mode 100644 index 0000000..91db914 --- /dev/null +++ b/commands/export.md @@ -0,0 +1,44 @@ +--- +name: export +description: Export session data for sharing or archiving +aliases: [session-export, share] +invokes: skill:session-export +--- + +# Export Command + +Export session data to Markdown or JSON for sharing. + +## Usage + +``` +/export # Export most recent session +/export # Export specific session +/export --list # List sessions available to export +/export --format json # Export as JSON +/export --output FILE # Save to file +``` + +## Implementation + +Run the session export script: + +```bash +python3 ~/.claude/automation/session-export.py [options] [session_id] +``` + +## Output Includes + +| Section | Description | +|---------|-------------| +| Session Details | ID, date, summarized status | +| Topics | Topics discussed | +| Summary | Session summary | +| Memory Items | Preferences, decisions, projects, facts created | + +## Use Cases + +- Share session context with another Claude instance +- Archive important sessions +- Create documentation from sessions +- Review what was accomplished diff --git a/commands/mcp-status.md b/commands/mcp-status.md new file mode 100644 index 0000000..e516342 --- /dev/null +++ b/commands/mcp-status.md @@ -0,0 +1,44 @@ +--- +name: mcp-status +description: Check status of MCP integrations +aliases: [mcp, integrations] +invokes: skill:mcp-status +--- + +# MCP Status Command + +Check the status of MCP integrations (Gmail, Calendar, etc.). + +## Usage + +``` +/mcp-status # Show integration status +/mcp-status --json # Output as JSON +``` + +## Implementation + +Run the MCP status script: + +```bash +~/.claude/automation/mcp-status.sh [--json] +``` + +## Integrations Checked + +| Integration | What's Checked | +|-------------|----------------| +| Gmail | Venv, gmail-mcp package, credentials, token | +| Calendar | google-api-python-client, credentials, token | +| Delegation | Helper scripts present | +| MCP Servers | .mcp.json configuration | + +## Status Levels + +| Status | Meaning | +|--------|---------| +| `ready` | Fully configured and working | +| `needs_auth` | Credentials present, needs OAuth | +| `needs_setup` | Missing configuration | +| `missing_deps` | Python packages not installed | +| `not_installed` | Integration not set up | diff --git a/state/component-registry.json b/state/component-registry.json index 8ca176e..9c428c8 100644 --- a/state/component-registry.json +++ b/state/component-registry.json @@ -174,6 +174,16 @@ "description": "Debug and troubleshoot configuration", "aliases": ["/diag", "/diagnose"], "invokes": "command:debug" + }, + "/export": { + "description": "Export session data for sharing", + "aliases": ["/session-export", "/share"], + "invokes": "command:export" + }, + "/mcp-status": { + "description": "Check MCP integration status", + "aliases": ["/mcp", "/integrations"], + "invokes": "command:mcp-status" } }, "agents": { @@ -305,7 +315,9 @@ "history-browser": "~/.claude/automation/history-browser.py", "log-viewer": "~/.claude/automation/log-viewer.py", "debug": "~/.claude/automation/debug.sh", - "daily-maintenance": "~/.claude/automation/daily-maintenance.sh" + "daily-maintenance": "~/.claude/automation/daily-maintenance.sh", + "session-export": "~/.claude/automation/session-export.py", + "mcp-status": "~/.claude/automation/mcp-status.sh" }, "completions": { "bash": "~/.claude/automation/completions.bash",