Compare commits
No commits in common. "3fb46c211140e776208c8acb57dfb45766d88476" and "e83177d24861630f050680ef15e726a6e9813797" have entirely different histories.
3fb46c2111
...
e83177d248
12
CLAUDE.md
12
CLAUDE.md
|
|
@ -28,7 +28,7 @@ Artifact Editor is a standalone PyQt6-based GUI for creating visual artifacts (d
|
||||||
### Project Ecosystem
|
### Project Ecosystem
|
||||||
|
|
||||||
This is part of a three-project stack:
|
This is part of a three-project stack:
|
||||||
1. **CmdForge** - AI provider abstraction and tool execution (dependency)
|
1. **SmartTools** - AI provider abstraction and tool execution (dependency)
|
||||||
2. **Orchestrated Discussions** - Conversation orchestration (launches this editor)
|
2. **Orchestrated Discussions** - Conversation orchestration (launches this editor)
|
||||||
3. **Artifact Editor** (this) - Visual artifact creation
|
3. **Artifact Editor** (this) - Visual artifact creation
|
||||||
|
|
||||||
|
|
@ -101,7 +101,7 @@ src/artifact_editor/
|
||||||
|
|
||||||
### AI Integration
|
### AI Integration
|
||||||
|
|
||||||
Uses the `artifact-ai` CmdForge tool for AI-powered generation:
|
Uses the `artifact-ai` SmartTool for AI-powered generation:
|
||||||
```python
|
```python
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
["artifact-ai", "--format", format_type, "--instruction", instruction],
|
["artifact-ai", "--format", format_type, "--instruction", instruction],
|
||||||
|
|
@ -112,12 +112,12 @@ subprocess.run(
|
||||||
|
|
||||||
No direct AI provider imports - follows Unix philosophy.
|
No direct AI provider imports - follows Unix philosophy.
|
||||||
|
|
||||||
### CmdForge Tools
|
### SmartTools
|
||||||
|
|
||||||
Artifact Editor includes CmdForge tools for CLI/scripting use:
|
Artifact Editor includes SmartTools for CLI/scripting use:
|
||||||
|
|
||||||
```
|
```
|
||||||
cmdforge/
|
smarttools/
|
||||||
├── artifact-ai/ # AI-powered artifact generation/modification
|
├── artifact-ai/ # AI-powered artifact generation/modification
|
||||||
│ └── config.yaml # Supports all formats: plantuml, mermaid, openscad, svg, excalidraw, code
|
│ └── config.yaml # Supports all formats: plantuml, mermaid, openscad, svg, excalidraw, code
|
||||||
└── artifact-export/ # Render source to binary formats (PNG, SVG, PDF)
|
└── artifact-export/ # Render source to binary formats (PNG, SVG, PDF)
|
||||||
|
|
@ -139,7 +139,7 @@ cat diagram.puml | artifact-export --format plantuml --to output.svg
|
||||||
cat model.scad | artifact-export --format openscad --to output.png
|
cat model.scad | artifact-export --format openscad --to output.png
|
||||||
```
|
```
|
||||||
|
|
||||||
These tools are installed to `~/.cmdforge/` and `~/.local/bin/` by `./install.sh`.
|
These tools are installed to `~/.smarttools/` and `~/.local/bin/` by `./install.sh`.
|
||||||
|
|
||||||
### Supported Formats
|
### Supported Formats
|
||||||
|
|
||||||
|
|
|
||||||
36
Dockerfile
36
Dockerfile
|
|
@ -1,23 +1,23 @@
|
||||||
# Artifact Editor - AI-enhanced visual artifact creation
|
# Artifact Editor - AI-enhanced visual artifact creation
|
||||||
#
|
#
|
||||||
# Multi-stage build:
|
# Multi-stage build:
|
||||||
# Stage 1: Build CmdForge base
|
# Stage 1: Build SmartTools base
|
||||||
# Stage 2: Build Artifact Editor with CmdForge
|
# Stage 2: Build Artifact Editor with SmartTools
|
||||||
#
|
#
|
||||||
# Build: docker build -t artifact-editor .
|
# Build: docker build -t artifact-editor .
|
||||||
# Run: docker run -it --rm artifact-editor artifact-ai --help
|
# Run: docker run -it --rm artifact-editor artifact-ai --help
|
||||||
# GUI: See usage examples at bottom
|
# GUI: See usage examples at bottom
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Stage 1: CmdForge Base
|
# Stage 1: SmartTools Base
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
FROM python:3.12-slim AS cmdforge
|
FROM python:3.12-slim AS smarttools
|
||||||
|
|
||||||
WORKDIR /cmdforge
|
WORKDIR /smarttools
|
||||||
|
|
||||||
ARG CMDFORGE_REPO=https://gitea.brrd.tech/rob/CmdForge.git
|
ARG SMARTTOOLS_REPO=https://gitea.brrd.tech/rob/SmartTools.git
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends git && \
|
RUN apt-get update && apt-get install -y --no-install-recommends git && \
|
||||||
git clone ${CMDFORGE_REPO} . || \
|
git clone ${SMARTTOOLS_REPO} . || \
|
||||||
echo "Clone failed - will need COPY in next stage"
|
echo "Clone failed - will need COPY in next stage"
|
||||||
|
|
||||||
RUN pip install --no-cache-dir -e . || true
|
RUN pip install --no-cache-dir -e . || true
|
||||||
|
|
@ -59,16 +59,16 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy CmdForge from stage 1
|
# Copy SmartTools from stage 1
|
||||||
COPY --from=cmdforge /cmdforge /cmdforge
|
COPY --from=smarttools /smarttools /smarttools
|
||||||
|
|
||||||
# Install CmdForge
|
# Install SmartTools
|
||||||
RUN pip install --no-cache-dir -e /cmdforge
|
RUN pip install --no-cache-dir -e /smarttools
|
||||||
|
|
||||||
# Copy Artifact Editor files
|
# Copy Artifact Editor files
|
||||||
COPY pyproject.toml README.md ./
|
COPY pyproject.toml README.md ./
|
||||||
COPY src/ ./src/
|
COPY src/ ./src/
|
||||||
COPY cmdforge/ ./cmdforge/
|
COPY smarttools/ ./smarttools/
|
||||||
COPY install.sh ./
|
COPY install.sh ./
|
||||||
|
|
||||||
# Install Artifact Editor and dependencies
|
# Install Artifact Editor and dependencies
|
||||||
|
|
@ -76,14 +76,14 @@ RUN pip install --no-cache-dir -e . && \
|
||||||
pip install --no-cache-dir PyQt6 pygments
|
pip install --no-cache-dir PyQt6 pygments
|
||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
RUN mkdir -p /root/.cmdforge /root/.local/bin
|
RUN mkdir -p /root/.smarttools /root/.local/bin
|
||||||
|
|
||||||
# Install artifact CmdForge tools
|
# Install artifact SmartTools
|
||||||
RUN ./install.sh
|
RUN ./install.sh
|
||||||
|
|
||||||
# Install CmdForge example tools
|
# Install SmartTools example tools
|
||||||
RUN python /cmdforge/examples/install.py 2>/dev/null || true && \
|
RUN python /smarttools/examples/install.py 2>/dev/null || true && \
|
||||||
cmdforge refresh 2>/dev/null || true
|
smarttools refresh 2>/dev/null || true
|
||||||
|
|
||||||
# Add local bin to PATH
|
# Add local bin to PATH
|
||||||
ENV PATH="/root/.local/bin:${PATH}"
|
ENV PATH="/root/.local/bin:${PATH}"
|
||||||
|
|
@ -91,7 +91,7 @@ ENV PATH="/root/.local/bin:${PATH}"
|
||||||
# Healthcheck - verify CLI tools work
|
# Healthcheck - verify CLI tools work
|
||||||
RUN artifact-ai --help && \
|
RUN artifact-ai --help && \
|
||||||
artifact-export --help && \
|
artifact-export --help && \
|
||||||
cmdforge list | head -5
|
smarttools list | head -5
|
||||||
|
|
||||||
# Default: show help
|
# Default: show help
|
||||||
CMD ["artifact-ai", "--help"]
|
CMD ["artifact-ai", "--help"]
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -59,7 +59,7 @@ Run without installing anything locally (all dependencies included):
|
||||||
git clone https://gitea.brrd.tech/rob/artifact-editor.git
|
git clone https://gitea.brrd.tech/rob/artifact-editor.git
|
||||||
cd artifact-editor
|
cd artifact-editor
|
||||||
|
|
||||||
# Build (automatically clones CmdForge from Gitea)
|
# Build (automatically clones SmartTools from Gitea)
|
||||||
docker-compose build
|
docker-compose build
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
|
|
@ -164,14 +164,14 @@ artifact-editor/
|
||||||
│ ├── code.py # Pygments-based syntax highlighting
|
│ ├── code.py # Pygments-based syntax highlighting
|
||||||
│ ├── svg.py # Direct SVG editing with UI components
|
│ ├── svg.py # Direct SVG editing with UI components
|
||||||
│ └── excalidraw.py # Hand-drawn style diagrams
|
│ └── excalidraw.py # Hand-drawn style diagrams
|
||||||
└── cmdforge/ # CLI tools following Unix philosophy
|
└── smarttools/ # CLI tools following Unix philosophy
|
||||||
├── artifact-ai/ # AI-powered artifact generation/modification
|
├── artifact-ai/ # AI-powered artifact generation/modification
|
||||||
└── artifact-export/ # Render source to binary formats
|
└── artifact-export/ # Render source to binary formats
|
||||||
```
|
```
|
||||||
|
|
||||||
## CLI CmdForge Tools
|
## CLI SmartTools
|
||||||
|
|
||||||
In addition to the GUI, artifact-editor provides command-line CmdForge tools:
|
In addition to the GUI, artifact-editor provides command-line SmartTools:
|
||||||
|
|
||||||
### artifact-ai
|
### artifact-ai
|
||||||
|
|
||||||
|
|
@ -202,7 +202,7 @@ cat model.scad | artifact-export --format openscad --to output.png
|
||||||
cat flowchart.mmd | artifact-export --format mermaid --to output.pdf
|
cat flowchart.mmd | artifact-export --format mermaid --to output.pdf
|
||||||
```
|
```
|
||||||
|
|
||||||
Install CmdForge tools with `./install.sh` (requires CmdForge framework).
|
Install SmartTools with `./install.sh` (requires SmartTools framework).
|
||||||
|
|
||||||
## Keyboard Shortcuts
|
## Keyboard Shortcuts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
name: artifact-ai
|
|
||||||
description: Generate or modify artifacts (diagrams, 3D models, code) using AI
|
|
||||||
category: Developer
|
|
||||||
arguments:
|
|
||||||
- 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:
|
|
||||||
- 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
|
|
||||||
- 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*(<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: '{result}'
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# Artifact Editor - AI-enhanced diagram and artifact creation
|
# Artifact Editor - AI-enhanced diagram and artifact creation
|
||||||
#
|
#
|
||||||
# Quick Start:
|
# Quick Start:
|
||||||
# docker-compose build # Build the image (clones CmdForge from Gitea)
|
# docker-compose build # Build the image (clones SmartTools from Gitea)
|
||||||
# docker-compose run --rm test # Run tests
|
# docker-compose run --rm test # Run tests
|
||||||
# docker-compose run --rm cli artifact-ai --help
|
# docker-compose run --rm cli artifact-ai --help
|
||||||
#
|
#
|
||||||
# Dependencies:
|
# Dependencies:
|
||||||
# - CmdForge (cloned automatically from Gitea during build)
|
# - SmartTools (cloned automatically from Gitea during build)
|
||||||
|
|
||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
|
|
@ -19,10 +19,10 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
CMDFORGE_REPO: https://gitea.brrd.tech/rob/CmdForge.git
|
SMARTTOOLS_REPO: https://gitea.brrd.tech/rob/SmartTools.git
|
||||||
image: artifact-editor:latest
|
image: artifact-editor:latest
|
||||||
volumes:
|
volumes:
|
||||||
- cmdforge-data:/root/.cmdforge
|
- smarttools-data:/root/.smarttools
|
||||||
- /tmp/artifacts:/tmp/artifacts
|
- /tmp/artifacts:/tmp/artifacts
|
||||||
command: ["artifact-ai", "--help"]
|
command: ["artifact-ai", "--help"]
|
||||||
|
|
||||||
|
|
@ -34,16 +34,16 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
CMDFORGE_REPO: https://gitea.brrd.tech/rob/CmdForge.git
|
SMARTTOOLS_REPO: https://gitea.brrd.tech/rob/SmartTools.git
|
||||||
image: artifact-editor:latest
|
image: artifact-editor:latest
|
||||||
volumes:
|
volumes:
|
||||||
- cmdforge-data:/root/.cmdforge
|
- smarttools-data:/root/.smarttools
|
||||||
command: >
|
command: >
|
||||||
bash -c "
|
bash -c "
|
||||||
echo '=== Artifact Editor Tests ==='
|
echo '=== Artifact Editor Tests ==='
|
||||||
echo ''
|
echo ''
|
||||||
echo '1. Checking CmdForge...'
|
echo '1. Checking SmartTools...'
|
||||||
cmdforge list | head -5
|
smarttools list | head -5
|
||||||
echo ''
|
echo ''
|
||||||
echo '2. Checking artifact-ai...'
|
echo '2. Checking artifact-ai...'
|
||||||
artifact-ai --help > /dev/null && echo 'artifact-ai: OK'
|
artifact-ai --help > /dev/null && echo 'artifact-ai: OK'
|
||||||
|
|
@ -65,13 +65,13 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
CMDFORGE_REPO: https://gitea.brrd.tech/rob/CmdForge.git
|
SMARTTOOLS_REPO: https://gitea.brrd.tech/rob/SmartTools.git
|
||||||
image: artifact-editor:latest
|
image: artifact-editor:latest
|
||||||
environment:
|
environment:
|
||||||
- DISPLAY=${DISPLAY:-:0}
|
- DISPLAY=${DISPLAY:-:0}
|
||||||
- QT_QPA_PLATFORM=xcb
|
- QT_QPA_PLATFORM=xcb
|
||||||
volumes:
|
volumes:
|
||||||
- cmdforge-data:/root/.cmdforge
|
- smarttools-data:/root/.smarttools
|
||||||
- /tmp/artifacts:/tmp/artifacts
|
- /tmp/artifacts:/tmp/artifacts
|
||||||
- /tmp/.X11-unix:/tmp/.X11-unix:ro
|
- /tmp/.X11-unix:/tmp/.X11-unix:ro
|
||||||
command: ["artifact-editor"]
|
command: ["artifact-editor"]
|
||||||
|
|
@ -85,24 +85,24 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
CMDFORGE_REPO: https://gitea.brrd.tech/rob/CmdForge.git
|
SMARTTOOLS_REPO: https://gitea.brrd.tech/rob/SmartTools.git
|
||||||
image: artifact-editor:latest
|
image: artifact-editor:latest
|
||||||
volumes:
|
volumes:
|
||||||
- cmdforge-data:/root/.cmdforge
|
- smarttools-data:/root/.smarttools
|
||||||
- /tmp/artifacts:/tmp/artifacts
|
- /tmp/artifacts:/tmp/artifacts
|
||||||
command: ["/bin/bash"]
|
command: ["/bin/bash"]
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
tty: true
|
tty: true
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
cmdforge-data:
|
smarttools-data:
|
||||||
# Persists ~/.cmdforge between container runs
|
# Persists ~/.smarttools between container runs
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Usage Examples
|
# Usage Examples
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
#
|
#
|
||||||
# Build (fetches CmdForge from Gitea automatically):
|
# Build (fetches SmartTools from Gitea automatically):
|
||||||
# docker-compose build
|
# docker-compose build
|
||||||
#
|
#
|
||||||
# Run tests:
|
# Run tests:
|
||||||
|
|
|
||||||
20
install.sh
20
install.sh
|
|
@ -5,10 +5,10 @@ set -e
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
INSTALL_DIR="$HOME/.local/bin"
|
INSTALL_DIR="$HOME/.local/bin"
|
||||||
CMDFORGE_DIR="$HOME/.cmdforge"
|
SMARTTOOLS_DIR="$HOME/.smarttools"
|
||||||
|
|
||||||
mkdir -p "$INSTALL_DIR"
|
mkdir -p "$INSTALL_DIR"
|
||||||
mkdir -p "$CMDFORGE_DIR"
|
mkdir -p "$SMARTTOOLS_DIR"
|
||||||
|
|
||||||
# Create wrapper script for artifact-editor GUI
|
# Create wrapper script for artifact-editor GUI
|
||||||
cat > "$INSTALL_DIR/artifact-editor" << 'WRAPPER'
|
cat > "$INSTALL_DIR/artifact-editor" << 'WRAPPER'
|
||||||
|
|
@ -23,19 +23,19 @@ WRAPPER
|
||||||
sed -i "s|PLACEHOLDER_DIR|$SCRIPT_DIR|g" "$INSTALL_DIR/artifact-editor"
|
sed -i "s|PLACEHOLDER_DIR|$SCRIPT_DIR|g" "$INSTALL_DIR/artifact-editor"
|
||||||
chmod +x "$INSTALL_DIR/artifact-editor"
|
chmod +x "$INSTALL_DIR/artifact-editor"
|
||||||
|
|
||||||
# Install CmdForge tools
|
# Install SmartTools
|
||||||
if [ -d "$SCRIPT_DIR/cmdforge" ]; then
|
if [ -d "$SCRIPT_DIR/smarttools" ]; then
|
||||||
echo "Installing CmdForge tools..."
|
echo "Installing SmartTools..."
|
||||||
for tool in "$SCRIPT_DIR/cmdforge"/*/; do
|
for tool in "$SCRIPT_DIR/smarttools"/*/; do
|
||||||
tool_name=$(basename "$tool")
|
tool_name=$(basename "$tool")
|
||||||
cp -r "$tool" "$CMDFORGE_DIR/"
|
cp -r "$tool" "$SMARTTOOLS_DIR/"
|
||||||
|
|
||||||
# Create CLI wrapper
|
# Create CLI wrapper
|
||||||
cat > "$INSTALL_DIR/$tool_name" << WRAPPER
|
cat > "$INSTALL_DIR/$tool_name" << WRAPPER
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# CmdForge wrapper for '$tool_name'
|
# SmartTools wrapper for '$tool_name'
|
||||||
# Auto-generated
|
# Auto-generated
|
||||||
exec /usr/bin/python3 -m cmdforge.runner $tool_name "\$@"
|
exec /usr/bin/python3 -m smarttools.runner $tool_name "\$@"
|
||||||
WRAPPER
|
WRAPPER
|
||||||
chmod +x "$INSTALL_DIR/$tool_name"
|
chmod +x "$INSTALL_DIR/$tool_name"
|
||||||
echo " Installed: $tool_name"
|
echo " Installed: $tool_name"
|
||||||
|
|
@ -49,7 +49,7 @@ echo "Usage:"
|
||||||
echo " artifact-editor diagram.puml # Edit existing file"
|
echo " artifact-editor diagram.puml # Edit existing file"
|
||||||
echo " artifact-editor -o new.puml # Create new file"
|
echo " artifact-editor -o new.puml # Create new file"
|
||||||
echo ""
|
echo ""
|
||||||
echo "CmdForge tools:"
|
echo "SmartTools:"
|
||||||
echo " artifact-ai --format plantuml --instruction 'Create a class diagram'"
|
echo " artifact-ai --format plantuml --instruction 'Create a class diagram'"
|
||||||
echo " artifact-export --format plantuml --to output.svg"
|
echo " artifact-export --format plantuml --to output.svg"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"PyYAML>=6.0",
|
"PyYAML>=6.0",
|
||||||
"cmdforge @ git+https://gitea.brrd.tech/rob/CmdForge.git",
|
"smarttools @ git+https://gitea.brrd.tech/rob/SmartTools.git",
|
||||||
# GUI is required for the editor - include by default
|
# GUI is required for the editor - include by default
|
||||||
"PyQt6>=6.4.0",
|
"PyQt6>=6.4.0",
|
||||||
"QScintilla>=2.14.0",
|
"QScintilla>=2.14.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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}"
|
||||||
|
|
||||||
|
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: <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
|
||||||
|
|
||||||
|
# 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*(<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()
|
||||||
|
|
@ -2410,7 +2410,7 @@ class ArtifactEditorWindow(QMainWindow):
|
||||||
"• Structured element/relationship dialogs<br>"
|
"• Structured element/relationship dialogs<br>"
|
||||||
"• AI-powered diagram generation<br>"
|
"• AI-powered diagram generation<br>"
|
||||||
"• Voice input via dictation<br><br>"
|
"• Voice input via dictation<br><br>"
|
||||||
"<i>Part of the CmdForge ecosystem</i>"
|
"<i>Part of the SmartTools ecosystem</i>"
|
||||||
)
|
)
|
||||||
msg.setStandardButtons(QMessageBox.StandardButton.Ok)
|
msg.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||||
msg.exec()
|
msg.exec()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue