#!/usr/bin/env python3 """ Template Scaffolder Generates Confluence page template markup in storage-format XHTML. Supports built-in template types and custom section definitions with optional macros. Usage: python template_scaffolder.py meeting-notes python template_scaffolder.py decision-log --format json python template_scaffolder.py custom --sections "Overview,Goals,Action Items" --macros toc,status python template_scaffolder.py --list """ import argparse import json import sys from datetime import datetime from typing import Any, Dict, List, Optional # --------------------------------------------------------------------------- # Macro Generators # --------------------------------------------------------------------------- def macro_toc() -> str: """Generate table of contents macro.""" return 'truedisc3' def macro_status(text: str = "IN PROGRESS", color: str = "Yellow") -> str: """Generate status macro.""" return f'{color}{text}' def macro_info_panel(content: str) -> str: """Generate info panel macro.""" return f'

{content}

' def macro_warning_panel(content: str) -> str: """Generate warning panel macro.""" return f'

{content}

' def macro_note_panel(content: str) -> str: """Generate note panel macro.""" return f'

{content}

' def macro_expand(title: str, content: str) -> str: """Generate expand/collapse macro.""" return f'{title}{content}' def macro_jira_issues(jql: str) -> str: """Generate Jira issues macro.""" return f'{jql}key,summary,type,created,updated,due,assignee,reporter,priority,status,resolution' MACRO_MAP = { "toc": macro_toc, "status": macro_status, "info": macro_info_panel, "warning": macro_warning_panel, "note": macro_note_panel, "expand": macro_expand, "jira-issues": macro_jira_issues, } # --------------------------------------------------------------------------- # Built-in Templates # --------------------------------------------------------------------------- def _section(title: str, content: str) -> str: """Generate a section with heading and content.""" return f'

{title}

\n{content}\n' def _table(headers: List[str], rows: List[List[str]]) -> str: """Generate an XHTML table.""" parts = [''] for _ in headers: parts.append('') parts.append('') for h in headers: parts.append(f'') parts.append('') for row in rows: parts.append('') for cell in row: parts.append(f'') parts.append('') parts.append('

{h}

{cell}

') return ''.join(parts) def template_meeting_notes() -> Dict[str, Any]: """Generate meeting notes template.""" today = datetime.now().strftime("%Y-%m-%d") body = macro_toc() + '\n' body += macro_info_panel("Replace placeholder text with your meeting details.") + '\n' body += _section("Meeting Details", _table( ["Field", "Value"], [["Date", today], ["Time", ""], ["Location", ""], ["Facilitator", ""], ["Note Taker", ""]], )) body += _section("Attendees", '') body += _section("Agenda", '
  1. Item 1

  2. Item 2

  3. Item 3

') body += _section("Discussion Notes", '

Summary of discussion points...

') body += _section("Decisions Made", _table( ["Decision", "Owner", "Date"], [["", "", today]], )) body += _section("Action Items", _table( ["Action", "Owner", "Due Date", "Status"], [["", "", "", macro_status("TODO", "Grey")]], )) body += _section("Next Meeting", '

Date: TBD

Agenda items for next time:

') return {"name": "Meeting Notes", "body": body, "labels": ["meeting-notes", "template"]} def template_decision_log() -> Dict[str, Any]: """Generate decision log template.""" today = datetime.now().strftime("%Y-%m-%d") body = macro_toc() + '\n' body += _section("Decision Log", macro_info_panel("Track key decisions, context, and outcomes.")) body += _table( ["ID", "Date", "Decision", "Context", "Alternatives Considered", "Outcome", "Owner", "Status"], [ ["D-001", today, "", "", "", "", "", macro_status("DECIDED", "Green")], ["D-002", "", "", "", "", "", "", macro_status("PENDING", "Yellow")], ], ) body += '\n' body += _section("Decision Template", macro_expand("Decision Details Template", '

Context

What is the issue or situation requiring a decision?

' '

Options

  1. Option A - pros/cons

  2. Option B - pros/cons

' '

Decision

What was decided and why?

' '

Consequences

What are the expected outcomes?

' )) return {"name": "Decision Log", "body": body, "labels": ["decision-log", "template"]} def template_runbook() -> Dict[str, Any]: """Generate runbook template.""" body = macro_toc() + '\n' body += macro_warning_panel("This runbook should be tested and reviewed quarterly.") + '\n' body += _section("Overview", '

Brief description of what this runbook covers.

' + _table(["Field", "Value"], [ ["Service/System", ""], ["Owner", ""], ["Last Tested", ""], ["Severity", ""], ["Estimated Duration", ""], ])) body += _section("Prerequisites", '') body += _section("Steps", '
  1. Step 1: Description

    bash
  2. ' '
  3. Step 2: Description

  4. ' '
  5. Step 3: Description

') body += _section("Verification", '

How to verify the issue is resolved:

') body += _section("Rollback", macro_note_panel("If the above steps do not resolve the issue, follow these rollback steps.") + '
  1. Rollback step 1

  2. Rollback step 2

') body += _section("Escalation", _table( ["Level", "Contact", "When to Escalate"], [["L1", "", ""], ["L2", "", ""], ["L3", "", ""]], )) return {"name": "Runbook", "body": body, "labels": ["runbook", "operations", "template"]} def template_project_kickoff() -> Dict[str, Any]: """Generate project kickoff template.""" today = datetime.now().strftime("%Y-%m-%d") body = macro_toc() + '\n' body += _section("Project Overview", _table( ["Field", "Value"], [["Project Name", ""], ["Start Date", today], ["Target End Date", ""], ["Project Lead", ""], ["Sponsor", ""], ["Status", macro_status("KICKOFF", "Blue")]], )) body += _section("Vision & Goals", '

Vision

What does success look like?

' '

Goals

  1. Goal 1

  2. Goal 2

  3. Goal 3

') body += _section("Scope", '

In Scope

Out of Scope

') body += _section("Stakeholders", _table( ["Name", "Role", "Responsibility", "Communication Preference"], [["", "", "", ""]], )) body += _section("Timeline & Milestones", _table( ["Milestone", "Target Date", "Status"], [["Phase 1", "", macro_status("NOT STARTED", "Grey")], ["Phase 2", "", macro_status("NOT STARTED", "Grey")]], )) body += _section("Risks", _table( ["Risk", "Likelihood", "Impact", "Mitigation"], [["", "High/Medium/Low", "High/Medium/Low", ""]], )) body += _section("Next Steps", '') return {"name": "Project Kickoff", "body": body, "labels": ["project-kickoff", "template"]} def template_sprint_retro() -> Dict[str, Any]: """Generate sprint retrospective template.""" body = macro_toc() + '\n' body += _section("Sprint Info", _table( ["Field", "Value"], [["Sprint", ""], ["Date Range", ""], ["Facilitator", ""], ["Velocity", ""], ["Commitment", ""], ["Completion Rate", ""]], )) body += _section("What Went Well", '') body += _section("What Could Be Improved", '') body += _section("Action Items from Last Retro", _table( ["Action", "Owner", "Status"], [["", "", macro_status("DONE", "Green")], ["", "", macro_status("IN PROGRESS", "Yellow")]], )) body += _section("New Action Items", _table( ["Action", "Owner", "Due Date", "Priority"], [["", "", "", "High/Medium/Low"]], )) body += _section("Team Health Check", macro_info_panel("Rate each area 1-5 (1=needs work, 5=great)") + _table( ["Area", "Rating", "Trend", "Notes"], [["Teamwork", "", "", ""], ["Delivery", "", "", ""], ["Fun", "", "", ""], ["Learning", "", "", ""]], )) return {"name": "Sprint Retrospective", "body": body, "labels": ["sprint-retro", "agile", "template"]} def template_how_to_guide() -> Dict[str, Any]: """Generate how-to guide template.""" body = macro_toc() + '\n' body += macro_info_panel("This guide explains how to accomplish a specific task.") + '\n' body += _section("Overview", '

Brief description of what this guide covers and who it is for.

') body += _section("Prerequisites", '') body += _section("Step-by-Step Instructions", '

Step 1: Title

Description of what to do.

' '

Step 2: Title

Description of what to do.

' '

Step 3: Title

Description of what to do.

') body += _section("Troubleshooting", macro_expand("Common Issues", '

Issue 1

Solution...

' '

Issue 2

Solution...

')) body += _section("Related Resources", '') return {"name": "How-To Guide", "body": body, "labels": ["how-to", "guide", "template"]} TEMPLATE_REGISTRY = { "meeting-notes": template_meeting_notes, "decision-log": template_decision_log, "runbook": template_runbook, "project-kickoff": template_project_kickoff, "sprint-retro": template_sprint_retro, "how-to-guide": template_how_to_guide, } # --------------------------------------------------------------------------- # Custom Template Builder # --------------------------------------------------------------------------- def build_custom_template( sections: List[str], macros: List[str], ) -> Dict[str, Any]: """Build a custom template from sections and macros.""" body = "" # Add requested macros at the top if "toc" in macros: body += macro_toc() + '\n' if "status" in macros: body += '

Status: ' + macro_status() + '

\n' for section in sections: section = section.strip() if not section: continue body += _section(section, '

') # Add panels if requested if "info" in macros: body = macro_info_panel("Add instructions or context here.") + '\n' + body if "warning" in macros: body += macro_warning_panel("Add warnings here.") + '\n' if "note" in macros: body += macro_note_panel("Add notes here.") + '\n' return {"name": "Custom Template", "body": body, "labels": ["custom", "template"]} # --------------------------------------------------------------------------- # Output Formatting # --------------------------------------------------------------------------- def format_text_output(result: Dict[str, Any]) -> str: """Format results as readable text report.""" lines = [] lines.append("=" * 60) lines.append(f"TEMPLATE: {result['name']}") lines.append("=" * 60) lines.append("") lines.append(f"Labels: {', '.join(result.get('labels', []))}") lines.append("") lines.append("CONFLUENCE STORAGE FORMAT MARKUP") lines.append("-" * 30) lines.append(result["body"]) return "\n".join(lines) def format_json_output(result: Dict[str, Any]) -> Dict[str, Any]: """Format results as JSON.""" return result def format_list_output(output_format: str) -> str: """Format available templates list.""" if output_format == "json": templates = {} for name, func in TEMPLATE_REGISTRY.items(): result = func() templates[name] = { "name": result["name"], "labels": result["labels"], } return json.dumps(templates, indent=2) lines = [] lines.append("=" * 60) lines.append("AVAILABLE TEMPLATES") lines.append("=" * 60) lines.append("") for name, func in TEMPLATE_REGISTRY.items(): result = func() lines.append(f" {name}") lines.append(f" Name: {result['name']}") lines.append(f" Labels: {', '.join(result['labels'])}") lines.append("") lines.append(f"Total templates: {len(TEMPLATE_REGISTRY)}") lines.append("") lines.append("Usage:") lines.append(" python template_scaffolder.py ") lines.append(' python template_scaffolder.py custom --sections "Section1,Section2" --macros toc,status') return "\n".join(lines) # --------------------------------------------------------------------------- # CLI Interface # --------------------------------------------------------------------------- def main() -> int: """Main CLI entry point.""" parser = argparse.ArgumentParser( description="Generate Confluence page template markup" ) parser.add_argument( "template", nargs="?", help="Template name or 'custom' for custom template", ) parser.add_argument( "--format", choices=["text", "json"], default="text", help="Output format (default: text)", ) parser.add_argument( "--list", action="store_true", help="List all available template types", ) parser.add_argument( "--sections", help='Comma-separated section names for custom template (e.g., "Overview,Goals,Action Items")', ) parser.add_argument( "--macros", help='Comma-separated macro names to include (e.g., "toc,status,info")', ) args = parser.parse_args() try: if args.list: print(format_list_output(args.format)) return 0 if not args.template: parser.error("template name is required unless --list is used") template_name = args.template.lower() if template_name == "custom": if not args.sections: parser.error("--sections is required for custom templates") sections = [s.strip() for s in args.sections.split(",")] macros = [m.strip() for m in args.macros.split(",")] if args.macros else [] result = build_custom_template(sections, macros) elif template_name in TEMPLATE_REGISTRY: result = TEMPLATE_REGISTRY[template_name]() else: available = ", ".join(sorted(TEMPLATE_REGISTRY.keys())) print(f"Error: Unknown template '{template_name}'. Available: {available}", file=sys.stderr) return 1 if args.format == "json": print(json.dumps(format_json_output(result), indent=2)) else: print(format_text_output(result)) return 0 except Exception as e: print(f"Error: {e}", file=sys.stderr) return 1 if __name__ == "__main__": sys.exit(main())