+"""Mega-planner: 7-stage multi-agent debate pipeline.
+
+Standalone script that orchestrates dual-proposer debate with 5 analysis agents
+and external AI consensus synthesis. Uses only agentize.workflow.api.
+
+Usage:
+ python scripts/mega-planner.py --feature-desc "..."
+ python scripts/mega-planner.py --from-issue 42
+ python scripts/mega-planner.py --refine-issue 42 --feature-desc "focus on X"
+ python scripts/mega-planner.py --resolve-issue 42 --selections "1B,2A"
+"""
+
+from __future__ import annotations
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+from datetime import datetime
+from pathlib import Path
+from typing import Callable, Optional
+
+# PYTHONPATH bootstrap: ensure python/ is importable
+_SCRIPT_DIR = Path(__file__).resolve().parent
+_REPO_ROOT = _SCRIPT_DIR.parent
+_PYTHON_DIR = _REPO_ROOT / "python"
+if str(_PYTHON_DIR) not in sys.path:
+ sys.path.insert(0, str(_PYTHON_DIR))
+
+from agentize.workflow.api import run_acw
+from agentize.workflow.api import gh as gh_utils
+from agentize.workflow.api import path as path_utils
+from agentize.workflow.api import prompt as prompt_utils
+from agentize.workflow.api.session import Session, StageResult
+
+
+# ============================================================
+# Constants
+# ============================================================
+
+PROMPTS_DIR = path_utils.relpath(__file__, "prompts")
+
+AGENT_PROMPTS = {
+ "understander": "understander.md",
+ "bold": "mega-bold-proposer.md",
+ "paranoia": "mega-paranoia-proposer.md",
+ "critique": "mega-proposal-critique.md",
+ "proposal-reducer": "mega-proposal-reducer.md",
+ "code-reducer": "mega-code-reducer.md",
+}
+
+STAGES_WITH_PLAN_GUIDELINE = {"bold", "paranoia", "critique", "proposal-reducer", "code-reducer"}
+
+DEFAULT_BACKENDS = {
+ "understander": ("claude", "sonnet"),
+ "bold": ("claude", "opus"),
+ "paranoia": ("claude", "opus"),
+ "critique": ("claude", "opus"),
+ "proposal-reducer": ("claude", "opus"),
+ "code-reducer": ("claude", "opus"),
+ "consensus": ("claude", "opus"),
+}
+
+STAGE_TOOLS = {
+ "understander": "Read,Grep,Glob",
+ "bold": "Read,Grep,Glob,WebSearch,WebFetch",
+ "paranoia": "Read,Grep,Glob",
+ "critique": "Read,Grep,Glob,Bash",
+ "proposal-reducer": "Read,Grep,Glob",
+ "code-reducer": "Read,Grep,Glob",
+ "consensus": "Read,Grep,Glob",
+}
+
+STAGE_PERMISSION_MODE = {
+ "bold": "plan",
+}
+
+
+# ============================================================
+# Prompt Rendering
+# ============================================================
+
+
+def _read_agent_prompt(stage: str) -> str:
+ """Read an agent prompt from co-located prompts directory."""
+ prompt_file = PROMPTS_DIR / AGENT_PROMPTS[stage]
+ return prompt_utils.read_prompt(prompt_file, strip_frontmatter=True)
+
+
+def _read_plan_guideline() -> str | None:
+ """Read plan-guideline if available."""
+ plan_guideline_path = (
+ _REPO_ROOT / ".claude-plugin/skills/plan-guideline/SKILL.md"
+ )
+ if plan_guideline_path.exists():
+ return prompt_utils.read_prompt(plan_guideline_path, strip_frontmatter=True)
+ return None
+
+
+def _render_stage_prompt(
+ stage: str,
+ feature_desc: str,
+ previous_output: str | None = None,
+) -> str:
+ """Render the input prompt for a single-input stage."""
+ parts = [_read_agent_prompt(stage)]
+
+ if stage in STAGES_WITH_PLAN_GUIDELINE:
+ guideline = _read_plan_guideline()
+ if guideline:
+ parts.append("\n---\n")
+ parts.append("# Planning Guidelines\n")
+ parts.append(guideline)
+
+ parts.append("\n---\n")
+ parts.append("# Feature Request\n")
+ parts.append(feature_desc)
+
+ if previous_output:
+ parts.append("\n---\n")
+ parts.append("# Previous Stage Output\n")
+ parts.append(previous_output)
+
+ return "\n".join(parts)
+
+
+def _render_dual_input_prompt(
+ stage: str,
+ feature_desc: str,
+ bold_output: str,
+ paranoia_output: str,
+) -> str:
+ """Render input for stages that receive both proposals."""
+ parts = [_read_agent_prompt(stage)]
+
+ if stage in STAGES_WITH_PLAN_GUIDELINE:
+ guideline = _read_plan_guideline()
+ if guideline:
+ parts.append("\n---\n")
+ parts.append("# Planning Guidelines\n")
+ parts.append(guideline)
+
+ parts.append("\n---\n")
+ parts.append("# Feature Request\n")
+ parts.append(feature_desc)
+ parts.append("\n---\n")
+ parts.append("# Bold Proposal\n")
+ parts.append(bold_output)
+ parts.append("\n---\n")
+ parts.append("# Paranoia Proposal\n")
+ parts.append(paranoia_output)
+
+ return "\n".join(parts)
+
+
+def _build_debate_report(
+ feature_name: str,
+ bold_output: str,
+ paranoia_output: str,
+ critique_output: str,
+ proposal_reducer_output: str,
+ code_reducer_output: str,
+) -> str:
+ """Build the combined 5-agent debate report."""
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
+ return f"""# Multi-Agent Debate Report (Mega-Planner): {feature_name}
+
+**Generated**: {timestamp}
+
+This document combines five perspectives from the mega-planner dual-proposer debate system:
+1. **Bold Proposer**: Innovative, SOTA-driven approach
+2. **Paranoia Proposer**: Destructive refactoring approach
+3. **Critique**: Feasibility analysis of both proposals
+4. **Proposal Reducer**: Simplification of both proposals
+5. **Code Reducer**: Code footprint analysis
+6. **Previous Consensus Plan**: The plan being refined (if resolve/refine mode)
+7. **Selection & Refine History**: History table with current task in last row (if resolve/refine mode)
+
+---
+
+## Part 1: Bold Proposer
+
+{bold_output}
+
+---
+
+## Part 2: Paranoia Proposer
+
+{paranoia_output}
+
+---
+
+## Part 3: Critique
+
+{critique_output}
+
+---
+
+## Part 4: Proposal Reducer
+
+{proposal_reducer_output}
+
+---
+
+## Part 5: Code Reducer
+
+{code_reducer_output}
+
+---
+"""
+
+
+def _render_consensus_prompt(
+ feature_name: str,
+ debate_report: str,
+ dest_path: Path,
+) -> str:
+ """Render the external-synthesize prompt template."""
+ template_path = PROMPTS_DIR / "external-synthesize-prompt.md"
+ return prompt_utils.render(
+ template_path,
+ {
+ "FEATURE_NAME": feature_name,
+ "FEATURE_DESCRIPTION": feature_name,
+ "COMBINED_REPORT": debate_report,
+ },
+ dest_path,
+ strip_frontmatter=True,
+ )
+
+
+def _extract_feature_name(feature_desc: str, max_len: int = 80) -> str:
+ """Extract a short feature name from description."""
+ first_line = feature_desc.strip().split("\n")[0]
+ normalized = " ".join(first_line.split())
+ if len(normalized) <= max_len:
+ return normalized
+ return f"{normalized[:max_len]}..."
+
+
+# ============================================================
+# Pipeline Orchestration
+# ============================================================
+
+
+def run_mega_pipeline(
+ feature_desc: str,
+ *,
+ output_dir: str | Path = ".tmp",
+ backends: dict[str, tuple[str, str]] | None = None,
+ runner: Callable[..., subprocess.CompletedProcess] = run_acw,
+ prefix: str | None = None,
+ output_suffix: str = "-output.md",
+ skip_consensus: bool = False,
+ report_paths: dict[str, Path] | None = None,
+ consensus_path: Path | None = None,
+ history_path: Path | None = None,
+) -> dict[str, StageResult]:
+ """Execute the 7-stage mega-planner pipeline.
+
+ If report_paths is provided, skip the debate stages and use
+ existing report files for consensus (resolve mode).
+ """
+ output_path = Path(output_dir)
+ output_path.mkdir(parents=True, exist_ok=True)
+
+ if prefix is None:
+ prefix = datetime.now().strftime("%Y%m%d-%H%M%S")
+
+ stage_backends = {**DEFAULT_BACKENDS}
+ if backends:
+ stage_backends.update(backends)
+
+ session = Session(
+ output_dir=output_path,
+ prefix=prefix,
+ runner=runner,
+ output_suffix=output_suffix,
+ )
+
+ def _log(msg: str) -> None:
+ session._log(msg)
+
+ def _backend_label(stage: str) -> str:
+ p, m = stage_backends[stage]
+ return f"{p}:{m}"
+
+ results: dict[str, StageResult] = {}
+
+ # --- Resolve mode: skip debate, load existing reports ---
+ if report_paths is not None:
+ bold_output = report_paths["bold"].read_text()
+ paranoia_output = report_paths["paranoia"].read_text()
+ critique_output = report_paths["critique"].read_text()
+ proposal_reducer_output = report_paths["proposal-reducer"].read_text()
+ code_reducer_output = report_paths["code-reducer"].read_text()
+ else:
+ # --- Tier 1: Understander ---
+ _log(f"Stage 1/7: Running understander ({_backend_label('understander')})")
+ understander_prompt = _render_stage_prompt("understander", feature_desc)
+ results["understander"] = session.run_prompt(
+ "understander",
+ understander_prompt,
+ stage_backends["understander"],
+ tools=STAGE_TOOLS.get("understander"),
+ permission_mode=STAGE_PERMISSION_MODE.get("understander"),
+ )
+ understander_output = results["understander"].text()
+
+ # --- Tier 2: Bold + Paranoia in parallel ---
+ _log(
+ f"Stage 2-3/7: Running bold + paranoia in parallel "
+ f"({_backend_label('bold')}, {_backend_label('paranoia')})"
+ )
+ bold_prompt = _render_stage_prompt("bold", feature_desc, understander_output)
+ paranoia_prompt = _render_stage_prompt("paranoia", feature_desc, understander_output)
+
+ parallel_2 = session.run_parallel(
+ [
+ session.stage("bold", bold_prompt, stage_backends["bold"],
+ tools=STAGE_TOOLS.get("bold"),
+ permission_mode=STAGE_PERMISSION_MODE.get("bold")),
+ session.stage("paranoia", paranoia_prompt, stage_backends["paranoia"],
+ tools=STAGE_TOOLS.get("paranoia"),
+ permission_mode=STAGE_PERMISSION_MODE.get("paranoia")),
+ ],
+ max_workers=2,
+ )
+ results.update(parallel_2)
+ bold_output = results["bold"].text()
+ paranoia_output = results["paranoia"].text()
+
+ # --- Tier 3: Critique + Proposal Reducer + Code Reducer in parallel ---
+ _log(
+ f"Stage 4-6/7: Running critique + reducers in parallel "
+ f"({_backend_label('critique')}, {_backend_label('proposal-reducer')}, "
+ f"{_backend_label('code-reducer')})"
+ )
+ critique_prompt = _render_dual_input_prompt(
+ "critique", feature_desc, bold_output, paranoia_output
+ )
+ proposal_reducer_prompt = _render_dual_input_prompt(
+ "proposal-reducer", feature_desc, bold_output, paranoia_output
+ )
+ code_reducer_prompt = _render_dual_input_prompt(
+ "code-reducer", feature_desc, bold_output, paranoia_output
+ )
+
+ parallel_3 = session.run_parallel(
+ [
+ session.stage("critique", critique_prompt, stage_backends["critique"],
+ tools=STAGE_TOOLS.get("critique"),
+ permission_mode=STAGE_PERMISSION_MODE.get("critique")),
+ session.stage("proposal-reducer", proposal_reducer_prompt,
+ stage_backends["proposal-reducer"],
+ tools=STAGE_TOOLS.get("proposal-reducer"),
+ permission_mode=STAGE_PERMISSION_MODE.get("proposal-reducer")),
+ session.stage("code-reducer", code_reducer_prompt,
+ stage_backends["code-reducer"],
+ tools=STAGE_TOOLS.get("code-reducer"),
+ permission_mode=STAGE_PERMISSION_MODE.get("code-reducer")),
+ ],
+ max_workers=3,
+ )
+ results.update(parallel_3)
+ critique_output = results["critique"].text()
+ proposal_reducer_output = results["proposal-reducer"].text()
+ code_reducer_output = results["code-reducer"].text()
+
+ if skip_consensus:
+ return results
+
+ # --- Tier 4: Consensus via external AI ---
+ feature_name = _extract_feature_name(feature_desc)
+ debate_report = _build_debate_report(
+ feature_name,
+ bold_output, paranoia_output,
+ critique_output, proposal_reducer_output, code_reducer_output,
+ )
+
+ # Append resolve/refine context if provided
+ if consensus_path and consensus_path.exists():
+ prev_plan = consensus_path.read_text()
+ debate_report += (
+ f"\n## Part 6: Previous Consensus Plan\n\n"
+ f"The following is the previous consensus plan being refined:\n\n"
+ f"{prev_plan}\n\n---\n"
+ )
+ if history_path and history_path.exists():
+ history_content = history_path.read_text()
+ debate_report += (
+ f"\n## Part 7: Selection & Refine History\n\n"
+ f"**IMPORTANT**: The last row of the table below contains the current task requirement.\n"
+ f"Apply the current task to the previous consensus plan to generate the updated plan.\n\n"
+ f"{history_content}\n\n---\n"
+ )
+
+ # Save debate report
+ debate_file = output_path / f"{prefix}-debate.md"
+ debate_file.write_text(debate_report)
+
+ def _write_consensus_prompt(path: Path) -> str:
+ return _render_consensus_prompt(feature_name, debate_report, path)
+
+ _log(f"Stage 7/7: Running consensus ({_backend_label('consensus')})")
+ results["consensus"] = session.run_prompt(
+ "consensus",
+ _write_consensus_prompt,
+ stage_backends["consensus"],
+ tools=STAGE_TOOLS.get("consensus"),
+ permission_mode=STAGE_PERMISSION_MODE.get("consensus"),
+ )
+
+ return results
Good —
workflow/__init__.py:13imports fromagentize.workflow.planner(the package, not the deprecated shim file). Sinceplanner/is a package directory, Python resolvesfrom agentize.workflow.planner import ...toplanner/__init__.py, notplanner.py. The deprecated shimplanner.pyis actually shadowed by the package directory and effectively dead code. Safe to delete.Now I have everything I need to produce the unified resolved plan.
Implementation Plan: Re-implement Mega-Planner as Standalone Python Script
Agent Perspectives Summary
scripts/mega-planner/package (~500 LOC) with all 4 CLI modes and 7 co-located promptsscripts/mega-planner.py(~350 LOC) +scripts/prompts/, delete old planner packagescripts/, but deletion of planner package is critically wrongpython/agentize/workflow/planner/powerslol plan(ultra-planner), NOT the mega-plannerplanner.pydeprecated shim (14 LOC) +planner.md(66 LOC) as dead code to removeGoal
Re-implement the mega-planner (currently a Claude Code plugin command in
.claude-plugin/commands/mega-planner.md) as a standalone Python script that:python/agentize/workflow/api(Session DSL, run_acw, prompt utils, path utils, gh utils)scripts/prompts/external-synthesize.shtemplate rendering with Pythonprompt.render()--from-issue,--refine-issue,--resolve-issueOut of scope:
python/agentize/workflow/planner/(ultra-planner, used bylol plan)src/cli/planner/pipeline.sh(ultra-planner adapter).claude-plugin/commands/mega-planner.md(can coexist until Python version is proven).claude-plugin/agents/prompt originals (still used by the plugin command system)Codebase Analysis
File changes:
scripts/mega-planner.pyscripts/prompts/understander.md.claude-plugin/agents/understander.mdscripts/prompts/mega-bold-proposer.md.claude-plugin/agents/mega-bold-proposer.mdscripts/prompts/mega-paranoia-proposer.md.claude-plugin/agents/mega-paranoia-proposer.mdscripts/prompts/mega-proposal-critique.md.claude-plugin/agents/mega-proposal-critique.mdscripts/prompts/mega-proposal-reducer.md.claude-plugin/agents/mega-proposal-reducer.mdscripts/prompts/mega-code-reducer.md.claude-plugin/agents/mega-code-reducer.mdscripts/prompts/external-synthesize-prompt.md.claude-plugin/skills/external-synthesize/external-synthesize-prompt.mdpython/tests/test_mega_planner.pypython/agentize/workflow/planner.pypython/agentize/workflow/planner.mdImplementation Steps
Step 1: Copy 7 prompt files to
scripts/prompts/scripts/prompts/*.md(7 files)Code Draft
# These are verbatim copies (no diff needed—exact file copies) mkdir -p scripts/prompts cp .claude-plugin/agents/understander.md scripts/prompts/understander.md cp .claude-plugin/agents/mega-bold-proposer.md scripts/prompts/mega-bold-proposer.md cp .claude-plugin/agents/mega-paranoia-proposer.md scripts/prompts/mega-paranoia-proposer.md cp .claude-plugin/agents/mega-proposal-critique.md scripts/prompts/mega-proposal-critique.md cp .claude-plugin/agents/mega-proposal-reducer.md scripts/prompts/mega-proposal-reducer.md cp .claude-plugin/agents/mega-code-reducer.md scripts/prompts/mega-code-reducer.md cp .claude-plugin/skills/external-synthesize/external-synthesize-prompt.md scripts/prompts/external-synthesize-prompt.mdStep 2: Write tests for the mega-planner pipeline
python/tests/test_mega_planner.pytest_planner_workflow.py. Verify: stage ordering, parallel execution tiers, prompt rendering, report combination,skip_consensusflag, resolve mode viareport_paths.Code Draft
Step 3: Create
scripts/mega-planner.py— Pipeline Core (~280 LOC)scripts/mega-planner.pyagentize.workflow.api. Usespath_utils.relpath(__file__, "prompts", ...)pattern for co-located prompts. Includes PYTHONPATH bootstrap viasys.path.insert. This step covers the pipeline orchestration core: constants, prompt rendering,_build_debate_report(), andrun_mega_pipeline().Code Draft
Step 4: Create
scripts/mega-planner.py— Full 4-Mode CLI (~200 LOC)scripts/mega-planner.py(appended to Step 3)main()with argparse handling all 4 modes: default,--from-issue,--refine-issue,--resolve-issue. Includes GitHub issue creation/update, history file management, plan footer, feature name extraction. Follows patterns from existingplanner/__main__.py.Code Draft
Step 5: Delete deprecated shim files
python/agentize/workflow/planner.py,python/agentize/workflow/planner.mdfrom agentize.workflow.planner import ...to theplanner/package directory (planner/__init__.py), not this file. The shim is effectively dead code. Verified:workflow/__init__.py:13imports fromagentize.workflow.plannerwhich resolves to the package.Code Draft
Success Criteria
scripts/mega-planner.pyruns 7-stage pipeline with stub runner (all 7 stage results returned)scripts/prompts/(verbatim copies from.claude-plugin/){prefix}-{stage}-output.mdfor each stageskip_consensus=Truereturns 6 stage results without consensus--resolve-issue) loads existing reports, skips debate stages, re-runs consensus--refine-issue) fetches issue body, appends refinement focus, runs full pipeline--from-issue) fetches issue body and uses it as feature description--issue-mode true)python/agentize/workflow/planner/package untouched andlol planstill workspython/agentize/workflow/planner.pyandplanner.mddeletedpython -m pytest python/tests/test_mega_planner.pyRisks and Mitigations
sys.path.insertat top of script; documented in usage{{FEATURE_NAME}},{{FEATURE_DESCRIPTION}},{{COMBINED_REPORT}}match templateif path.exists()checkplannerto package dir, not shim file; tests import from package__main__.pypatternValidation
Compatibility check: All three selected options (1B + 2A + 3A) are architecturally compatible:
scripts/prompts/) works naturally with the full CLI in a single fileNo conflicts detected. The unified plan produces a single
scripts/mega-planner.py(~500 LOC) with co-located prompts inscripts/prompts/, implementing all 7 stages and all 4 CLI modes via standard Session DSL.Selection History
scripts/prompts/; 2B: Package directoryRefine History
Dude, carefully read my response to determine what to do next.