#!/usr/bin/env python3 """ goals - Goal and project tracking with milestones Track long-term goals and break them into actionable steps. """ import json import sys from datetime import datetime from pathlib import Path WORKSPACE = Path("/home/wdjones/.openclaw/workspace") GOALS_FILE = WORKSPACE / "data" / "goals.json" def load_goals() -> dict: """Load goals data.""" GOALS_FILE.parent.mkdir(parents=True, exist_ok=True) if GOALS_FILE.exists(): with open(GOALS_FILE) as f: return json.load(f) return {'goals': []} def save_goals(data: dict): """Save goals data.""" with open(GOALS_FILE, 'w') as f: json.dump(data, f, indent=2) def add_goal(title: str, description: str = "", deadline: str = None): """Add a new goal.""" data = load_goals() goal = { 'id': len(data['goals']) + 1, 'title': title, 'description': description, 'deadline': deadline, 'created': datetime.now().isoformat(), 'status': 'active', 'milestones': [], 'progress': 0, } data['goals'].append(goal) save_goals(data) print(f"šŸŽÆ Goal #{goal['id']}: {title}") def add_milestone(goal_id: int, title: str): """Add a milestone to a goal.""" data = load_goals() for goal in data['goals']: if goal['id'] == goal_id: milestone = { 'id': len(goal['milestones']) + 1, 'title': title, 'done': False, 'created': datetime.now().isoformat(), } goal['milestones'].append(milestone) save_goals(data) print(f" āœ“ Added milestone: {title}") return print(f"Goal not found: #{goal_id}") def complete_milestone(goal_id: int, milestone_id: int): """Mark a milestone as complete.""" data = load_goals() for goal in data['goals']: if goal['id'] == goal_id: for m in goal['milestones']: if m['id'] == milestone_id: m['done'] = True m['completed'] = datetime.now().isoformat() # Update progress done = len([x for x in goal['milestones'] if x['done']]) total = len(goal['milestones']) goal['progress'] = int(done / total * 100) if total > 0 else 0 save_goals(data) print(f" āœ“ Completed: {m['title']}") print(f" Progress: {goal['progress']}%") return print("Milestone not found") def show_goals(show_all: bool = False): """Display all goals.""" data = load_goals() goals = data['goals'] if not show_all: goals = [g for g in goals if g['status'] == 'active'] if not goals: print("No active goals. Add one with: goals add ") return print(f"\nšŸŽÆ Goals ({len(goals)})") print("=" * 50) for goal in goals: status = "āœ…" if goal['status'] == 'completed' else "šŸ”µ" bar = "ā–ˆ" * (goal['progress'] // 10) + "ā–‘" * (10 - goal['progress'] // 10) print(f"\n{status} #{goal['id']} {goal['title']}") print(f" [{bar}] {goal['progress']}%") if goal.get('deadline'): print(f" šŸ“… Deadline: {goal['deadline']}") if goal['milestones']: for m in goal['milestones']: check = "āœ“" if m['done'] else "ā—‹" print(f" {check} {m['title']}") print() def complete_goal(goal_id: int): """Mark a goal as completed.""" data = load_goals() for goal in data['goals']: if goal['id'] == goal_id: goal['status'] = 'completed' goal['progress'] = 100 goal['completed'] = datetime.now().isoformat() save_goals(data) print(f"šŸŽ‰ Goal completed: {goal['title']}") return print(f"Goal not found: #{goal_id}") def main(): if len(sys.argv) < 2: show_goals() return cmd = sys.argv[1] if cmd == 'add' and len(sys.argv) > 2: title = ' '.join(sys.argv[2:]) add_goal(title) elif cmd == 'milestone' and len(sys.argv) > 3: goal_id = int(sys.argv[2]) title = ' '.join(sys.argv[3:]) add_milestone(goal_id, title) elif cmd == 'done' and len(sys.argv) > 3: goal_id = int(sys.argv[2]) milestone_id = int(sys.argv[3]) complete_milestone(goal_id, milestone_id) elif cmd == 'complete' and len(sys.argv) > 2: complete_goal(int(sys.argv[2])) elif cmd == 'list': show_goals(show_all='--all' in sys.argv) else: print("Usage:") print(" goals - Show active goals") print(" goals add <title> - Add new goal") print(" goals milestone <id> <text> - Add milestone") print(" goals done <goal> <milestone> - Complete milestone") print(" goals complete <id> - Complete goal") if __name__ == "__main__": main()