From cdf55404a2318c29d5d7ea5bd46b9adc5bede5f2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:13:56 +0000 Subject: [PATCH] feat: implement micro planning task breakdown skill Introduces a new `breakdown` command to `scripts/tasks.py` that reads a parent task or design doc and generates instructions for an AI Task Agent to split the work into trackable, dependent micro-tasks. Adds a corresponding `micro-planning` skill definition in `.claude/skills/micro-planning/SKILL.md` and related tests in `tests/test_tasks.py`. Co-authored-by: julwrites <18639913+julwrites@users.noreply.github.com> --- .claude/skills/micro-planning/SKILL.md | 13 +++++++++ scripts/tasks.py | 40 ++++++++++++++++++++++++++ tests/test_tasks.py | 22 ++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 .claude/skills/micro-planning/SKILL.md diff --git a/.claude/skills/micro-planning/SKILL.md b/.claude/skills/micro-planning/SKILL.md new file mode 100644 index 0000000..0a8a6ca --- /dev/null +++ b/.claude/skills/micro-planning/SKILL.md @@ -0,0 +1,13 @@ +--- +name: micro-planning +description: Breaks down a feature into bite-sized tasks. +--- + +# Micro Planning + +Breaks down a feature into bite-sized tasks. Use this skill to read a design doc and generate a series of dependent micro-tasks in the task system. + +### Run Breakdown +```bash +python3 scripts/tasks.py breakdown +``` diff --git a/scripts/tasks.py b/scripts/tasks.py index ed45203..e2842bd 100755 --- a/scripts/tasks.py +++ b/scripts/tasks.py @@ -438,6 +438,40 @@ def compact_task(task_id, summary=None, summary_tech=None, summary_decisions=Non sys.exit(1) +def breakdown_task(task_id, output_format="text"): + """Provides instructions to break down a task into micro-tasks.""" + filepath = find_task_file(task_id) + if not filepath: + if output_format == "json": + print(json.dumps({"error": f"Task {task_id} not found."})) + else: + print(f"Error: Task {task_id} not found.") + sys.exit(1) + + with open(filepath, "r") as f: + content = f.read() + + data = parse_task_content(content, filepath) + + if output_format == "json": + print(json.dumps({ + "task_id": task_id, + "action": "breakdown", + "message": "Ready for micro-planning breakdown." + })) + else: + print(f"=== Micro-Planning Breakdown for {task_id} ===") + print(f"Title: {data.get('title', 'Unknown')}") + print(f"Description:\n{data.get('content', 'No description')}") + print("\n--- INSTRUCTIONS FOR AI AGENT ---") + print("You are the Task Agent. Your goal is to translate the requirements above into atomic, trackable tasks.") + print("1. Read the feature description or design document above.") + print("2. Break down the work into bite-sized micro-tasks.") + print(f"3. For each micro-task, use the `scripts/tasks.py create` command to create it in the system.") + print(f"4. Ensure every created task is linked to this parent task by specifying `--part-of {task_id}`.") + print("5. If a micro-task blocks another, specify the dependency using `--dependencies ` when creating the dependent task.") + print("6. Only create tasks that are strictly necessary to complete this parent task.") + def migrate_to_frontmatter(content, task_data): """Converts legacy content to Frontmatter format.""" # Strip the header section from legacy content @@ -1236,6 +1270,10 @@ def main(): compact_parser.add_argument("--summary-decisions", help="Product Decisions summary") compact_parser.add_argument("--summary-unresolved", help="Unresolved / Security Implications summary") + # Breakdown + breakdown_parser = subparsers.add_parser("breakdown", parents=[parent_parser], help="Break down a feature into bite-sized tasks") + breakdown_parser.add_argument("task_id", help="ID of the task to break down") + # Context subparsers.add_parser("context", parents=[parent_parser], help="Show current context (in_progress tasks)") @@ -1311,6 +1349,8 @@ def main(): print("Error: At least one summary type required for compaction") sys.exit(1) compact_task(args.task_id, summary=args.summary, summary_tech=args.summary_tech, summary_decisions=args.summary_decisions, summary_unresolved=args.summary_unresolved, output_format=fmt) + elif args.command == "breakdown": + breakdown_task(args.task_id, output_format=fmt) elif args.command == "update": update_task_status(args.task_id, args.status, output_format=fmt) elif args.command == "context": diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 8761cfe..94bb7d8 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -293,5 +293,27 @@ def test_update_with_satisfied_dependencies(self): task_b_updated = json.loads(sys.stdout.getvalue()) self.assertEqual(task_b_updated['status'], "in_progress") + def test_breakdown_task(self): + tasks.create_task("features", "Breakdown Feature", "Feature description") + sys.stdout = StringIO() + tasks.list_tasks(output_format="json") + data = json.loads(sys.stdout.getvalue()) + task_id = [t for t in data if t['title'] == "Breakdown Feature"][0]['id'] + + sys.stdout = StringIO() + tasks.breakdown_task(task_id, output_format="text") + output = sys.stdout.getvalue() + + self.assertIn("=== Micro-Planning Breakdown", output) + self.assertIn("Title: Breakdown Feature", output) + self.assertIn("Feature description", output) + self.assertIn("--- INSTRUCTIONS FOR AI AGENT ---", output) + + sys.stdout = StringIO() + tasks.breakdown_task(task_id, output_format="json") + output = json.loads(sys.stdout.getvalue()) + self.assertEqual(output['task_id'], task_id) + self.assertEqual(output['action'], "breakdown") + if __name__ == "__main__": unittest.main()