feat: Update artifact-ai prompt with PlantUML syntax rules
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
e83177d248
commit
51633f1e0b
|
|
@ -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
|
name: artifact-ai
|
||||||
description: Generate or modify artifacts (diagrams, 3D models, code) using AI
|
description: Generate or modify artifacts (diagrams, 3D models, code) using AI
|
||||||
category: Artifact
|
category: Developer
|
||||||
|
|
||||||
arguments:
|
arguments:
|
||||||
- flag: --format
|
- flag: --format
|
||||||
variable: format
|
variable: format
|
||||||
required: true
|
description: 'Artifact format: plantuml, mermaid, openscad, svg, excalidraw, code'
|
||||||
description: "Artifact format: plantuml, mermaid, openscad, svg, excalidraw, code"
|
- flag: --instruction
|
||||||
- flag: --instruction
|
|
||||||
variable: instruction
|
variable: instruction
|
||||||
required: true
|
|
||||||
description: Natural language instruction for generating or modifying the artifact
|
description: Natural language instruction for generating or modifying the artifact
|
||||||
|
|
||||||
output: "{result}"
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Step 1: Build format-specific prompt and call AI
|
- type: code
|
||||||
- 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: <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>, <path>\n\
|
||||||
|
- Text: <text>, <tspan>\n- Groups: <g>, <defs>, <use>, <symbol>\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\
|
||||||
|
\ <?xml or <svg and ending with </svg>.\",\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
|
output_var: prompt
|
||||||
code: |
|
- type: prompt
|
||||||
import json
|
prompt: '{prompt}'
|
||||||
|
provider: gemini-flash
|
||||||
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: <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>, <path>
|
|
||||||
- Text: <text>, <tspan>
|
|
||||||
- Groups: <g>, <defs>, <use>, <symbol>
|
|
||||||
- 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 <?xml or <svg and ending with </svg>.",
|
|
||||||
'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
|
output_var: ai_output
|
||||||
|
prompt_file: prompt.txt
|
||||||
# Step 2: Clean up output based on format
|
- type: code
|
||||||
- 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*(<svg[\\s\\S]*?</svg>)', 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_var: result
|
||||||
code: |
|
output: '{result}'
|
||||||
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*(<svg[\s\S]*?</svg>)', 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()
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue