From 51633f1e0b658f9f7d169720cd1eff688687f78e Mon Sep 17 00:00:00 2001 From: rob Date: Wed, 31 Dec 2025 00:32:02 -0400 Subject: [PATCH] feat: Update artifact-ai prompt with PlantUML syntax rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add explicit syntax rules to prevent invalid nested content: - NEVER put raw text or bullets inside component/rectangle braces - Use [SubComponent] notation for nested elements - Use 'note right of X' blocks for text descriptions - Use 'package' for grouping instead of 'component' - Add prompt_file for debugging AI prompts - Switch provider to gemini-flash 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- smarttools/artifact-ai/config.yaml | 261 ++++++++++------------------- 1 file changed, 85 insertions(+), 176 deletions(-) diff --git a/smarttools/artifact-ai/config.yaml b/smarttools/artifact-ai/config.yaml index 6e63573..7cfdb01 100644 --- a/smarttools/artifact-ai/config.yaml +++ b/smarttools/artifact-ai/config.yaml @@ -1,180 +1,89 @@ -# artifact-ai - Generate or modify artifacts using AI -# Usage: cat diagram.puml | artifact-ai --format plantuml --instruction "Add a cache layer" -# Usage: echo "" | artifact-ai --format mermaid --instruction "Create a flowchart for login" - name: artifact-ai description: Generate or modify artifacts (diagrams, 3D models, code) using AI -category: Artifact - +category: Developer arguments: - - flag: --format - variable: format - required: true - description: "Artifact format: plantuml, mermaid, openscad, svg, excalidraw, code" - - flag: --instruction - variable: instruction - required: true - description: Natural language instruction for generating or modifying the artifact - -output: "{result}" - +- flag: --format + variable: format + description: 'Artifact format: plantuml, mermaid, openscad, svg, excalidraw, code' +- flag: --instruction + variable: instruction + description: Natural language instruction for generating or modifying the artifact steps: - # Step 1: Build format-specific prompt and call AI - - type: code - output_var: prompt - code: | - import json - - format_prompts = { - 'plantuml': """You are a PlantUML expert. Create or modify PlantUML diagrams. - - Supported diagram types: - - Sequence diagrams: actor, participant, ->, --> - - Class diagrams: class, interface, extends, implements - - Activity diagrams: start, stop, :action;, if/then/else - - Component diagrams: component, package, [interface] - - State diagrams: state, [*], --> - - Always wrap with @startuml/@enduml.""", - - 'mermaid': """You are a Mermaid diagram expert. Create or modify Mermaid diagrams. - - Supported diagram types: - - Flowcharts: graph TD/LR, A-->B - - Sequence: sequenceDiagram, Alice->>Bob - - Class: classDiagram, class ClassName - - State: stateDiagram-v2 - - Entity Relationship: erDiagram - - Gantt: gantt, section, task - - Start with the diagram type declaration (graph TD, sequenceDiagram, etc.).""", - - 'openscad': """You are an OpenSCAD 3D modeling expert. Create or modify parametric 3D models. - - Core operations: - - Primitives: cube(), sphere(), cylinder(), polyhedron() - - Transformations: translate(), rotate(), scale(), mirror() - - Boolean: union(), difference(), intersection() - - 2D: circle(), square(), polygon(), linear_extrude(), rotate_extrude() - - Use modules for reusable components. Use $fn for smoothness.""", - - 'svg': """You are an SVG expert. Create or modify vector graphics. - - Core elements: - - Shapes: , , , , , , - - Text: , - - Groups: , , , - - Styling: fill, stroke, stroke-width, opacity, transform - - Include xmlns and viewBox. Use descriptive IDs.""", - - 'excalidraw': """You are an Excalidraw expert. Create hand-drawn style diagrams as JSON. - - Structure: - - type: element type (rectangle, diamond, ellipse, arrow, line, text) - - x, y: position - - width, height: dimensions - - strokeColor, backgroundColor, fillStyle - - roughness: 0-2 (0=smooth, 2=sketchy) - - Output valid JSON with "type": "excalidraw" and "elements" array.""", - - 'code': """You are a programming expert. Create or modify code in any language. - - Focus on: - - Clean, readable code - - Proper language conventions - - Comments for complex logic only - - Error handling where appropriate - - Detect the language from context or instruction.""" - } - - format_outputs = { - 'plantuml': "Output ONLY valid PlantUML code starting with @startuml and ending with @enduml.", - 'mermaid': "Output ONLY valid Mermaid code starting with the diagram type.", - 'openscad': "Output ONLY valid OpenSCAD code with proper syntax.", - 'svg': "Output ONLY valid SVG code starting with .", - 'excalidraw': 'Output ONLY valid Excalidraw JSON starting with { and ending with }.', - 'code': "Output ONLY the code, no markdown fences or explanations." - } - - current_code = input.strip() if input.strip() else "(empty - create from scratch)" - format_guide = format_prompts.get(format, f"You are a {format} expert.") - output_instruction = format_outputs.get(format, "Output ONLY the code, no explanations.") - - prompt = f"""{format_guide} - - CRITICAL: {output_instruction} - - Current code: - --- - {current_code} - --- - - User request: {instruction} - - {output_instruction}""" - - # Don't print - just set the variable - result = prompt - - - type: prompt - prompt: "{prompt}" - provider: opencode-deepseek - output_var: ai_output - - # Step 2: Clean up output based on format - - type: code - output_var: result - code: | - import re - - code = ai_output.strip() - - # Remove markdown code blocks if present (three backticks) - fence_pattern = r'`{3}(?:\w+)?\n(.*?)`{3}' - match = re.search(fence_pattern, code, re.DOTALL) - if match: - code = match.group(1).strip() - - # Format-specific cleanup - if format == 'svg': - # Remove any wrapper formats - code = re.sub(r'^@startuml\s*\n?', '', code) - code = re.sub(r'\n?@enduml\s*$', '', code) - # Find SVG content - svg_match = re.search(r'(<\?xml[^?]*\?>)?\s*()', code) - if svg_match: - xml_decl = svg_match.group(1) or '' - svg_content = svg_match.group(2) - code = (xml_decl + '\n' + svg_content).strip() if xml_decl else svg_content - - elif format == 'excalidraw': - # Find JSON object - json_match = re.search(r'\{[\s\S]*\}', code) - if json_match: - code = json_match.group(0) - - elif format == 'plantuml': - # Extract first complete @startuml...@enduml block (handles AI outputting duplicates) - puml_match = re.search(r'(@start\w+.*?@end\w+)', code, re.DOTALL) - if puml_match: - code = puml_match.group(1) - else: - # No complete block found - ensure proper tags - if not code.strip().startswith('@start'): - code = '@startuml\n' + code - if not code.strip().endswith('@enduml') and '@enduml' not in code: - code = code + '\n@enduml' - - elif format == 'mermaid': - # Extract first complete mermaid diagram (handles duplicates) - # Mermaid starts with diagram type declaration - mermaid_types = r'(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|journey)' - mermaid_match = re.search(rf'({mermaid_types}[\s\S]*?)(?={mermaid_types}|\Z)', code) - if mermaid_match: - code = mermaid_match.group(1).strip() - - result = code.strip() +- type: code + code: "import json\n\nformat_prompts = {\n 'plantuml': \"\"\"You are a PlantUML\ + \ expert. Create or modify PlantUML diagrams.\n\nSupported diagram types:\n- Sequence\ + \ diagrams: actor, participant, ->, -->\n- Class diagrams: class, interface, extends,\ + \ implements\n- Activity diagrams: start, stop, :action;, if/then/else\n- Component\ + \ diagrams: component, package, [interface]\n- State diagrams: state, [*], -->\n\ + \nIMPORTANT syntax rules:\n- NEVER put raw text or bullets inside component/rectangle braces\n\ + - For nested elements: component X { [SubComponent1] [SubComponent2] }\n\ + - For text descriptions: use 'note right of X' blocks\n- For grouping: use 'package' not 'component'\n\ + \nAlways wrap with @startuml/@enduml.\"\"\",\n\n 'mermaid': \"\"\"You are a\ + \ Mermaid diagram expert. Create or modify Mermaid diagrams.\n\nSupported diagram\ + \ types:\n- Flowcharts: graph TD/LR, A-->B\n- Sequence: sequenceDiagram, Alice->>Bob\n\ + - Class: classDiagram, class ClassName\n- State: stateDiagram-v2\n- Entity Relationship:\ + \ erDiagram\n- Gantt: gantt, section, task\n\nStart with the diagram type declaration\ + \ (graph TD, sequenceDiagram, etc.).\"\"\",\n\n 'openscad': \"\"\"You are an\ + \ OpenSCAD 3D modeling expert. Create or modify parametric 3D models.\n\nCore\ + \ operations:\n- Primitives: cube(), sphere(), cylinder(), polyhedron()\n- Transformations:\ + \ translate(), rotate(), scale(), mirror()\n- Boolean: union(), difference(),\ + \ intersection()\n- 2D: circle(), square(), polygon(), linear_extrude(), rotate_extrude()\n\ + \nUse modules for reusable components. Use $fn for smoothness.\"\"\",\n\n 'svg':\ + \ \"\"\"You are an SVG expert. Create or modify vector graphics.\n\nCore elements:\n\ + - Shapes: , , , , , , \n\ + - Text: , \n- Groups: , , , \n- Styling: fill,\ + \ stroke, stroke-width, opacity, transform\n\nInclude xmlns and viewBox. Use descriptive\ + \ IDs.\"\"\",\n\n 'excalidraw': \"\"\"You are an Excalidraw expert. Create\ + \ hand-drawn style diagrams as JSON.\n\nStructure:\n- type: element type (rectangle,\ + \ diamond, ellipse, arrow, line, text)\n- x, y: position\n- width, height: dimensions\n\ + - strokeColor, backgroundColor, fillStyle\n- roughness: 0-2 (0=smooth, 2=sketchy)\n\ + \nOutput valid JSON with \"type\": \"excalidraw\" and \"elements\" array.\"\"\"\ + ,\n\n 'code': \"\"\"You are a programming expert. Create or modify code in\ + \ any language.\n\nFocus on:\n- Clean, readable code\n- Proper language conventions\n\ + - Comments for complex logic only\n- Error handling where appropriate\n\nDetect\ + \ the language from context or instruction.\"\"\"\n}\n\nformat_outputs = {\n \ + \ 'plantuml': \"Output ONLY valid PlantUML code starting with @startuml and\ + \ ending with @enduml.\",\n 'mermaid': \"Output ONLY valid Mermaid code starting\ + \ with the diagram type.\",\n 'openscad': \"Output ONLY valid OpenSCAD code\ + \ with proper syntax.\",\n 'svg': \"Output ONLY valid SVG code starting with\ + \ .\",\n 'excalidraw': 'Output ONLY valid\ + \ Excalidraw JSON starting with { and ending with }.',\n 'code': \"Output ONLY\ + \ the code, no markdown fences or explanations.\"\n}\n\ncurrent_code = input.strip()\ + \ if input.strip() else \"(empty - create from scratch)\"\nformat_guide = format_prompts.get(format,\ + \ f\"You are a {format} expert.\")\noutput_instruction = format_outputs.get(format,\ + \ \"Output ONLY the code, no explanations.\")\n\nprompt = f\"\"\"{format_guide}\n\ + \nCRITICAL: {output_instruction}\n\nCurrent code:\n---\n{current_code}\n---\n\n\ + User request: {instruction}\n\n{output_instruction}\"\"\"\n\n# Don't print - just\ + \ set the variable\nresult = prompt\n" + output_var: prompt +- type: prompt + prompt: '{prompt}' + provider: gemini-flash + output_var: ai_output + prompt_file: prompt.txt +- type: code + code: "import re\n\ncode = ai_output.strip()\n\n# Remove markdown code blocks if\ + \ present (three backticks)\nfence_pattern = r'`{3}(?:\\w+)?\\n(.*?)`{3}'\nmatch\ + \ = re.search(fence_pattern, code, re.DOTALL)\nif match:\n code = match.group(1).strip()\n\ + \n# Format-specific cleanup\nif format == 'svg':\n # Remove any wrapper formats\n\ + \ code = re.sub(r'^@startuml\\s*\\n?', '', code)\n code = re.sub(r'\\n?@enduml\\\ + s*$', '', code)\n # Find SVG content\n svg_match = re.search(r'(<\\?xml[^?]*\\\ + ?>)?\\s*()', code)\n if svg_match:\n xml_decl =\ + \ svg_match.group(1) or ''\n svg_content = svg_match.group(2)\n \ + \ code = (xml_decl + '\\n' + svg_content).strip() if xml_decl else svg_content\n\ + \nelif format == 'excalidraw':\n # Find JSON object\n json_match = re.search(r'\\\ + {[\\s\\S]*\\}', code)\n if json_match:\n code = json_match.group(0)\n\ + \nelif format == 'plantuml':\n # Extract first complete @startuml...@enduml\ + \ block (handles AI outputting duplicates)\n puml_match = re.search(r'(@start\\\ + w+.*?@end\\w+)', code, re.DOTALL)\n if puml_match:\n code = puml_match.group(1)\n\ + \ else:\n # No complete block found - ensure proper tags\n if\ + \ not code.strip().startswith('@start'):\n code = '@startuml\\n' +\ + \ code\n if not code.strip().endswith('@enduml') and '@enduml' not in code:\n\ + \ code = code + '\\n@enduml'\n\nelif format == 'mermaid':\n # Extract\ + \ first complete mermaid diagram (handles duplicates)\n # Mermaid starts with\ + \ diagram type declaration\n mermaid_types = r'(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|journey)'\n\ + \ mermaid_match = re.search(rf'({mermaid_types}[\\s\\S]*?)(?={mermaid_types}|\\\ + Z)', code)\n if mermaid_match:\n code = mermaid_match.group(1).strip()\n\ + \nresult = code.strip()\n" + output_var: result +output: '{result}'