sync_planning_doc
Update project planning documents by appending progress logs, updating active work status, or marking tasks as complete to maintain current development tracking.
Instructions
Update .context/dev/{branch}/ planning documents. Can append to progress log, update active work, or mark tasks complete.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | Update mode: append_progress_log, update_active_work, or mark_tasks_complete | |
| completed_tasks | No | List of completed task descriptions (for append_progress_log or mark_tasks_complete) | |
| in_progress | No | Current work in progress (for update_active_work) | |
| decisions | No | Key decisions made (for append_progress_log) | |
| blockers | No | Current blockers or issues (for update_active_work) | |
| next_steps | No | Next immediate steps (for update_active_work) | |
| working_directory | No | Working directory for git operations. Defaults to current directory. |
Implementation Reference
- src/ccsession/server.py:296-367 (handler)The _sync_planning_doc function handles the logic for syncing planning documents, including parsing arguments, locating the plan file, and delegating the update to helper functions based on the mode.
async def _sync_planning_doc(arguments: dict[str, Any]) -> dict: """Update .context planning documents.""" mode = arguments.get("mode") working_dir = arguments.get("working_directory") or _get_working_directory() if not mode: return {"success": False, "error": "mode is required"} git = GitUtils(working_dir) plan_path = git.get_context_plan_path() if not plan_path: # Try to construct path even if file doesn't exist branch = git.get_current_branch() root = git.get_project_root() if branch and root: plan_path = root / ".context" / "dev" / branch / f"{branch}-detailed-plan.md" else: return { "success": False, "error": "Could not determine plan path. Not a git repo or no branch.", } if not plan_path.exists(): return { "success": False, "error": f"Plan file not found: {plan_path}", "plan_path": str(plan_path), } # Read existing content content = plan_path.read_text(encoding="utf-8") sections_updated = [] if mode == "append_progress_log": content, updated = _append_progress_log( content, completed_tasks=arguments.get("completed_tasks", []), decisions=arguments.get("decisions", []), blockers=arguments.get("blockers", []), ) if updated: sections_updated.append("Progress Log") elif mode == "update_active_work": content, updated = _update_active_work( content, in_progress=arguments.get("in_progress"), next_steps=arguments.get("next_steps", []), blockers=arguments.get("blockers", []), ) if updated: sections_updated.append("Active Work") elif mode == "mark_tasks_complete": completed_tasks = arguments.get("completed_tasks", []) for task in completed_tasks: content = content.replace(f"- [ ] {task}", f"- [x] {task}") if completed_tasks: sections_updated.append("Implementation Plan") else: return {"success": False, "error": f"Unknown mode: {mode}"} # Write updated content plan_path.write_text(content, encoding="utf-8") return { "success": True, "plan_path": str(plan_path), "sections_updated": sections_updated, } - src/ccsession/server.py:81-122 (registration)Tool registration for 'sync_planning_doc' in the MCP server's list_tools definition.
name="sync_planning_doc", description="Update .context/dev/{branch}/ planning documents. Can append to progress log, update active work, or mark tasks complete.", inputSchema={ "type": "object", "properties": { "mode": { "type": "string", "enum": ["append_progress_log", "update_active_work", "mark_tasks_complete"], "description": "Update mode: append_progress_log, update_active_work, or mark_tasks_complete", }, "completed_tasks": { "type": "array", "items": {"type": "string"}, "description": "List of completed task descriptions (for append_progress_log or mark_tasks_complete)", }, "in_progress": { "type": "string", "description": "Current work in progress (for update_active_work)", }, "decisions": { "type": "array", "items": {"type": "string"}, "description": "Key decisions made (for append_progress_log)", }, "blockers": { "type": "array", "items": {"type": "string"}, "description": "Current blockers or issues (for update_active_work)", }, "next_steps": { "type": "array", "items": {"type": "string"}, "description": "Next immediate steps (for update_active_work)", }, "working_directory": { "type": "string", "description": "Working directory for git operations. Defaults to current directory.", }, }, "required": ["mode"], }, ), - src/ccsession/server.py:370-406 (helper)Helper function _append_progress_log for updating the progress log section of the plan file.
def _append_progress_log( content: str, completed_tasks: list[str], decisions: list[str], blockers: list[str], ) -> tuple[str, bool]: """Append entry to Progress Log section.""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M") entry_lines = [f"\n### {timestamp} - Session Update"] if completed_tasks: entry_lines.append("- **Completed:**") for task in completed_tasks: entry_lines.append(f" - {task}") if decisions: entry_lines.append("- **Decisions:**") for decision in decisions: entry_lines.append(f" - {decision}") if blockers: entry_lines.append("- **Blockers:**") for blocker in blockers: entry_lines.append(f" - {blocker}") entry = "\n".join(entry_lines) + "\n" # Find Progress Log section and insert after header marker = "## Progress Log" if marker in content: idx = content.find(marker) # Find end of line end_of_line = content.find("\n", idx) if end_of_line != -1: content = content[:end_of_line + 1] + entry + content[end_of_line + 1:] return content, True return content, False - src/ccsession/server.py:408-455 (helper)Helper function _update_active_work for updating the active work section of the plan file.
def _update_active_work( content: str, in_progress: str | None, next_steps: list[str], blockers: list[str], ) -> tuple[str, bool]: """Update Active Work section.""" timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") new_section_lines = [ "## Active Work (Current Session)", f"**Last updated:** {timestamp}", "", "### In Progress:", ] if in_progress: new_section_lines.append(f"- {in_progress}") else: new_section_lines.append("- (none)") new_section_lines.extend(["", "### Next Immediate Steps:"]) if next_steps: for step in next_steps: new_section_lines.append(f"- {step}") else: new_section_lines.append("- (none)") if blockers: new_section_lines.extend(["", "### Blockers/Notes:"]) for blocker in blockers: new_section_lines.append(f"- {blocker}") new_section = "\n".join(new_section_lines) + "\n" # Find and replace Active Work section start_marker = "## Active Work" if start_marker in content: start_idx = content.find(start_marker) # Find next ## section next_section = content.find("\n## ", start_idx + 1) if next_section != -1: content = content[:start_idx] + new_section + "\n" + content[next_section + 1:] else: # Active Work is last section content = content[:start_idx] + new_section return content, True return content, False