Build plan generation tool (with automatic verification + user approval). Args: task_board: TaskBoard for tracking plan_file on tasks. plan_generator: PlanGenerator instance. verifier: PlanVerifier instance. user_input_fn: Callback for asking user approval.
(
task_board: TaskBoard,
plan_generator: Any, # PlanGenerator (avoid circular import)
verifier: Any, # PlanVerifier
user_input_fn: Callable[[str, Optional[list[str]]], str],
)
| 162 | |
| 163 | |
| 164 | def build_plan_tools( |
| 165 | task_board: TaskBoard, |
| 166 | plan_generator: Any, # PlanGenerator (avoid circular import) |
| 167 | verifier: Any, # PlanVerifier |
| 168 | user_input_fn: Callable[[str, Optional[list[str]]], str], |
| 169 | ) -> list[OrchestratorTool]: |
| 170 | """Build plan generation tool (with automatic verification + user approval). |
| 171 | |
| 172 | Args: |
| 173 | task_board: TaskBoard for tracking plan_file on tasks. |
| 174 | plan_generator: PlanGenerator instance. |
| 175 | verifier: PlanVerifier instance. |
| 176 | user_input_fn: Callback for asking user approval. |
| 177 | """ |
| 178 | |
| 179 | def handle_generate_plan(args: dict) -> str: |
| 180 | task_id = args["task_id"] |
| 181 | task = task_board.get_task(task_id) |
| 182 | if not task: |
| 183 | return f"Error: Task {task_id} not found on the task board." |
| 184 | |
| 185 | # 1. Generate — pass all orchestrator-provided context to the plan generator |
| 186 | try: |
| 187 | plan_path = plan_generator.generate( |
| 188 | task_id=task_id, |
| 189 | task_description=args["task_description"], |
| 190 | context_summary=args.get("context_summary"), |
| 191 | hints=args.get("hints"), |
| 192 | recommended_tools=args.get("recommended_tools"), |
| 193 | ) |
| 194 | except ValueError as e: |
| 195 | return f"Error generating plan: {e}" |
| 196 | |
| 197 | task_board.update_task(task_id, plan_file=str(plan_path)) |
| 198 | |
| 199 | # 2. Verify automatically |
| 200 | result = verifier.verify(plan_path) |
| 201 | task_board.update_task(task_id, plan_verified=result.is_valid) |
| 202 | |
| 203 | if not result.is_valid: |
| 204 | return ( |
| 205 | f"Plan generated at {plan_path} but verification FAILED: " |
| 206 | f"{result.summary()}" |
| 207 | ) |
| 208 | |
| 209 | # 3. Show full YAML to user and ask for approval |
| 210 | yaml_content = Path(plan_path).read_text(encoding="utf-8") |
| 211 | approval = user_input_fn( |
| 212 | f"Plan generated and verified for {task_id}. Approve?\n\n" |
| 213 | f"```yaml\n{yaml_content}\n```", |
| 214 | ["Yes, approve", "No, reject"], |
| 215 | ) |
| 216 | approved = approval.lower() in ("yes", "yes, approve", "y", "1") |
| 217 | if approved: |
| 218 | return ( |
| 219 | f"Plan generated, verified, and APPROVED.\n" |
| 220 | f"Path: {plan_path}\n\n```yaml\n{yaml_content}\n```" |
| 221 | ) |