#!/usr/bin/env python3 """ capture - Quick thought capture Quickly capture thoughts, tasks, ideas, and links. They go to inbox for later processing. """ import json import sys from datetime import datetime from pathlib import Path WORKSPACE = Path("/home/wdjones/.openclaw/workspace") CAPTURE_FILE = WORKSPACE / "inbox" / "captures.json" TYPES = { 'thought': 'šŸ’­', 'task': 'āœ…', 'idea': 'šŸ’”', 'link': 'šŸ”—', 'note': 'šŸ“', 'quote': 'šŸ’¬', 'question': 'ā“', } def load_captures() -> list: """Load captured items.""" CAPTURE_FILE.parent.mkdir(parents=True, exist_ok=True) if CAPTURE_FILE.exists(): with open(CAPTURE_FILE) as f: return json.load(f) return [] def save_captures(captures: list): """Save captures.""" with open(CAPTURE_FILE, 'w') as f: json.dump(captures, f, indent=2) def capture(content: str, capture_type: str = 'note', tags: list = None): """Capture something quickly.""" captures = load_captures() item = { 'id': len(captures) + 1, 'content': content, 'type': capture_type if capture_type in TYPES else 'note', 'tags': tags or [], 'captured': datetime.now().isoformat(), 'processed': False, } captures.append(item) save_captures(captures) emoji = TYPES.get(capture_type, 'šŸ“') print(f"{emoji} Captured #{item['id']}: {content[:50]}...") def list_captures(show_all: bool = False, type_filter: str = None): """List captured items.""" captures = load_captures() if not show_all: captures = [c for c in captures if not c.get('processed')] if type_filter: captures = [c for c in captures if c['type'] == type_filter] if not captures: print("šŸ“­ Inbox empty") return print(f"\nšŸ“„ Inbox ({len(captures)} items)") print("=" * 50) for item in captures: emoji = TYPES.get(item['type'], 'šŸ“') status = "āœ“" if item.get('processed') else " " content = item['content'][:60] if len(item['content']) > 60: content += "..." print(f"[{status}] #{item['id']:3} {emoji} {content}") if item.get('tags'): tags = ' '.join(f"#{t}" for t in item['tags']) print(f" {tags}") print() def process_item(item_id: int): """Mark an item as processed.""" captures = load_captures() for item in captures: if item['id'] == item_id: item['processed'] = True save_captures(captures) print(f"āœ“ Processed #{item_id}") return print(f"Item not found: #{item_id}") def clear_processed(): """Remove processed items.""" captures = load_captures() unprocessed = [c for c in captures if not c.get('processed')] removed = len(captures) - len(unprocessed) save_captures(unprocessed) print(f"āœ“ Removed {removed} processed items") def export_markdown(): """Export unprocessed items as markdown.""" captures = load_captures() unprocessed = [c for c in captures if not c.get('processed')] if not unprocessed: print("Nothing to export") return # Group by type by_type = {} for item in unprocessed: t = item['type'] if t not in by_type: by_type[t] = [] by_type[t].append(item) print("# Inbox Export") print(f"*Exported: {datetime.now().strftime('%Y-%m-%d %H:%M')}*\n") for type_name, items in sorted(by_type.items()): emoji = TYPES.get(type_name, 'šŸ“') print(f"## {emoji} {type_name.title()}s\n") for item in items: tags = ' '.join(f"`#{t}`" for t in item.get('tags', [])) print(f"- {item['content']} {tags}") print() def main(): if len(sys.argv) < 2: print("Usage:") print(" capture - Quick capture") print(" capture -t task - Capture as task") print(" capture -t idea - Capture as idea") print(" capture -t link - Capture a link") print(" capture list - Show inbox") print(" capture list --all - Show all (incl. processed)") print(" capture done - Mark as processed") print(" capture clear - Clear processed items") print(" capture export - Export as markdown") print("") print(f"Types: {', '.join(TYPES.keys())}") list_captures() return # Parse args if sys.argv[1] == 'list': show_all = '--all' in sys.argv list_captures(show_all=show_all) elif sys.argv[1] == 'done' and len(sys.argv) > 2: process_item(int(sys.argv[2])) elif sys.argv[1] == 'clear': clear_processed() elif sys.argv[1] == 'export': export_markdown() elif sys.argv[1] == '-t' and len(sys.argv) > 3: capture_type = sys.argv[2] content = ' '.join(sys.argv[3:]) capture(content, capture_type) else: content = ' '.join(sys.argv[1:]) capture(content) if __name__ == "__main__": main()