# discussion-pragmatist - Shipping-focused pragmatist participant for discussions # Usage: cat discussion.md | discussion-pragmatist --callout "Is this MVP-ready?" name: discussion-pragmatist description: Shipping-focused pragmatist participant for discussions category: Discussion meta: display_name: AI-Pragmatist alias: pragmatist type: voting expertise: - MVP scoping - Shipping velocity - Trade-off analysis - Iterative development - Technical debt management concerns: - "Can we ship this incrementally?" - "Are we over-engineering this?" - "What's the simplest thing that could work?" - "Is this scope creep?" arguments: - flag: --callout variable: callout default: "" description: Specific question or @mention context - flag: --templates-dir variable: templates_dir default: "templates" description: Path to templates directory - flag: --diagrams-dir variable: diagrams_dir default: "diagrams" description: Path to save diagrams - flag: --log-file variable: log_file default: "" description: Path to log file for progress updates steps: # Step 1: Extract phase context from template - type: code code: | import re import os phase_match = re.search(r'', input, re.IGNORECASE) template_match = re.search(r'', input, re.IGNORECASE) current_phase = phase_match.group(1) if phase_match else "initial_feedback" template_name = template_match.group(1) if template_match else "feature" template_path = os.path.join(templates_dir, template_name + ".yaml") phase_goal = "Provide practical feedback" phase_instructions = "Review the proposal for complexity and shipping readiness." if os.path.exists(template_path): import yaml with open(template_path, 'r') as f: template = yaml.safe_load(f) phases = template.get("phases", {}) phase_info = phases.get(current_phase, {}) phase_goal = phase_info.get("goal", phase_goal) phase_instructions = phase_info.get("instructions", phase_instructions) phase_context = "Current Phase: " + current_phase + "\n" phase_context += "Phase Goal: " + phase_goal + "\n" phase_context += "Phase Instructions:\n" + phase_instructions output_var: phase_context, current_phase # Step 2: Prepare diagram path (pragmatist uses diagrams sparingly) - type: code code: | import re import os title_match = re.search(r'', input) discussion_name = "discussion" if title_match: discussion_name = title_match.group(1).strip().lower() discussion_name = re.sub(r'[^a-z0-9]+', '-', discussion_name) os.makedirs(diagrams_dir, exist_ok=True) existing = [] if os.path.exists(diagrams_dir): for f in os.listdir(diagrams_dir): if f.startswith(discussion_name): existing.append(f) next_num = len(existing) + 1 diagram_path = diagrams_dir + "/" + discussion_name + "_mvp_" + str(next_num) + ".puml" output_var: diagram_path # Step 3: Log progress before AI call - type: code code: | import sys import datetime as dt timestamp = dt.datetime.now().strftime("%H:%M:%S") for msg in [f"Phase: {current_phase}", "Calling AI provider..."]: line = f"[{timestamp}] [pragmatist] {msg}" print(line, file=sys.stderr) sys.stderr.flush() if log_file: with open(log_file, 'a') as f: f.write(line + "\n") f.flush() output_var: _progress1 # Step 4: Generate response - type: prompt prompt: | You are AI-Pragmatist (also known as Maya), a shipping-focused engineer who advocates for practical solutions and incremental delivery. ## Your Role - Advocate for simpler solutions - Identify over-engineering and scope creep - Suggest MVP approaches - Balance quality with delivery speed - Challenge unnecessary complexity ## Your Perspective - "Done is better than perfect when it's good enough" - Ship early, iterate often - Complexity is the enemy of delivery - Technical debt is acceptable if managed - Users need features, not architectural purity - Perfect is the enemy of good ## Questions You Ask - Is this the simplest solution that works? - Can we defer this complexity? - What's the minimum viable version? - Are we solving problems we don't have yet? - What can we cut and still ship? ## Phase Context {phase_context} ## Diagrams Only create a diagram if it helps show a simpler approach. Use simple flowcharts to contrast complex vs MVP solutions. Diagram path to use: {diagram_path} IMPORTANT: When you create a diagram, your comment MUST include: DIAGRAM: {diagram_path} This marker makes the diagram discoverable. Example comment structure: "Here's my MVP analysis... [Your comparison of complex vs simple approaches] DIAGRAM: {diagram_path}" ## Current Discussion {input} ## Your Task {callout} Follow the phase instructions. Analyze from a practical shipping perspective. Flag over-engineering with CONCERN: COMPLEXITY. ## Response Format Respond with valid JSON only. Use \n for newlines in strings (not literal newlines): {{ "comment": "Line 1\nLine 2\nCONCERN: COMPLEXITY", "vote": "READY" or "CHANGES" or "REJECT" or null, "diagram": "@startuml\nrectangle MVP\n@enduml" }} Important: The diagram field must use \n for newlines, not actual line breaks. Vote meanings: - READY: Good enough to ship - CHANGES: Simpler approach possible (suggest what) - REJECT: Too complex, needs fundamental simplification - null: Comment only, no vote change If you have nothing meaningful to add, respond: {{"sentinel": "NO_RESPONSE"}} provider: claude-sonnet output_var: response # Step 5: Log progress after AI call - type: code code: | import sys import datetime as dt timestamp = dt.datetime.now().strftime("%H:%M:%S") line = f"[{timestamp}] [pragmatist] AI response received" print(line, file=sys.stderr) sys.stderr.flush() if log_file: with open(log_file, 'a') as f: f.write(line + "\n") f.flush() output_var: _progress2 # Step 6: Extract JSON from response (may be wrapped in markdown code block) - type: code code: | import re json_text = response.strip() code_block = re.search(r'```(?:json)?\s*(.*?)```', json_text, re.DOTALL) if code_block: json_text = code_block.group(1).strip() output_var: json_text # Step 5: Parse JSON - type: code code: | import json try: parsed = json.loads(json_text) except json.JSONDecodeError as e: # AI often returns literal newlines in JSON strings - escape them fixed = json_text.replace('\n', '\\n') try: parsed = json.loads(fixed) except json.JSONDecodeError: # Last resort: try to extract just the fields we need via regex import re comment_match = re.search(r'"comment"\s*:\s*"(.*?)"(?=\s*[,}])', json_text, re.DOTALL) vote_match = re.search(r'"vote"\s*:\s*("?\w+"?|null)', json_text) diagram_match = re.search(r'"diagram"\s*:\s*"(.*?)"(?=\s*[,}])', json_text, re.DOTALL) parsed = { "comment": comment_match.group(1).replace('\n', ' ') if comment_match else "Parse error", "vote": vote_match.group(1).strip('"') if vote_match else None, "diagram": diagram_match.group(1) if diagram_match else None } if parsed["vote"] == "null": parsed["vote"] = None comment = parsed.get("comment", "") vote = parsed.get("vote") diagram_content = parsed.get("diagram") has_diagram = "true" if diagram_content else "false" output_var: comment, vote, diagram_content, has_diagram # Step 6: Save diagram if present - type: code code: | if has_diagram == "true" and diagram_content: with open(diagram_path, 'w') as f: f.write(diagram_content) saved_diagram = diagram_path else: saved_diagram = "" output_var: saved_diagram # Step 7: Build final response - type: code code: | import json result = {"comment": comment, "vote": vote} if saved_diagram: result["diagram_file"] = saved_diagram final_response = json.dumps(result) output_var: final_response output: "{final_response}"