sync_daily_note
Sync completed tasks from Obsidian daily notes to Coach AI's database using markdown checkboxes, enabling bidirectional task management for productivity workflows.
Instructions
Sync completed tasks from Obsidian daily note to database.
NEW TOOL - Bidirectional sync: Reads markdown checkboxes (- [x]) from your daily note and automatically marks matching todos as complete in the database. Uses fuzzy matching to handle partial text matches.
This enables an Obsidian-first workflow: work in your daily note, check off tasks there, then sync back to Coach AI's database.
Args: date_str: Optional date in YYYY-MM-DD format (defaults to today)
Returns: Summary of tasks synced and any warnings about unmatched checkboxes
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| date_str | No |
Implementation Reference
- src/coach_ai/daily_notes.py:560-601 (handler)The core implementation of the sync_daily_note logic which interacts with the Obsidian vault and the database to update task statuses.
async def sync_daily_note(date_str: str = None) -> str: """Sync completed tasks from Obsidian daily note to database. NEW TOOL - Bidirectional sync: Reads markdown checkboxes from daily note and marks matching todos as complete in the database. Args: date_str: Optional date in YYYY-MM-DD format (defaults to today) Returns: Summary of synced tasks """ vault = get_vault() if not vault: return "ā Obsidian vault not configured." if date_str: try: target_date = datetime.strptime(date_str, "%Y-%m-%d").date() except ValueError: return f"ā Invalid date format: {date_str}" else: target_date = date.today() db = await get_db() sync_result = await _sync_completed_tasks(vault, target_date, db) result = f"š Synced daily note for {target_date.strftime('%Y-%m-%d')}\n\n" if sync_result["completed_count"] > 0: result += f"ā Marked {sync_result['completed_count']} tasks complete:\n" for task in sync_result["completed_tasks"]: result += f" - {task}\n" else: result += "No new completed tasks found.\n" if sync_result["warnings"]: result += f"\nā ļø {len(sync_result['warnings'])} checkboxes couldn't be matched to todos\n" return result - src/coach_ai/server.py:661-679 (registration)MCP tool registration for sync_daily_note which delegates execution to the daily_notes module.
@mcp.tool() async def sync_daily_note(date_str: str = None) -> str: """Sync completed tasks from Obsidian daily note to database. NEW TOOL - Bidirectional sync: Reads markdown checkboxes (- [x]) from your daily note and automatically marks matching todos as complete in the database. Uses fuzzy matching to handle partial text matches. This enables an Obsidian-first workflow: work in your daily note, check off tasks there, then sync back to Coach AI's database. Args: date_str: Optional date in YYYY-MM-DD format (defaults to today) Returns: Summary of tasks synced and any warnings about unmatched checkboxes """ return await daily_notes.sync_daily_note(date_str) - src/coach_ai/daily_notes.py:604-655 (helper)Internal helper _sync_completed_tasks that performs the actual database operations and fuzzy matching for the sync.
async def _sync_completed_tasks( vault: ObsidianVault, target_date: date, db: aiosqlite.Connection ) -> dict: """Internal helper to sync completed tasks from daily note. Returns: Dict with completed_count, completed_tasks list, and warnings """ note_data = vault.read_daily_note(datetime.combine(target_date, datetime.min.time())) if not note_data: return {"completed_count": 0, "completed_tasks": [], "warnings": []} completed_checkboxes = [ task for task in note_data.get("tasks", []) if task.get("completed") ] if not completed_checkboxes: return {"completed_count": 0, "completed_tasks": [], "warnings": []} # Get all active todos from database cursor = await db.execute( "SELECT id, title FROM todos WHERE status = 'active'" ) active_todos = {row["id"]: row["title"] for row in await cursor.fetchall()} completed_tasks = [] warnings = [] for checkbox in completed_checkboxes: checkbox_text = checkbox["text"] # Try to match checkbox to a todo matched_id = _fuzzy_match_task(checkbox_text, active_todos) if matched_id: # Mark as complete await db.execute( "UPDATE todos SET status = 'completed', completed_at = ? WHERE id = ?", (datetime.now().isoformat(), matched_id), ) completed_tasks.append(active_todos[matched_id]) else: warnings.append(checkbox_text) await db.commit() return { "completed_count": len(completed_tasks), "completed_tasks": completed_tasks, "warnings": warnings, }