
Alicloud Solution Content Article Illustrator
- 118 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
Generate illustrated Alibaba Cloud solution articles for blogs, docs, and landing pages, pairing technical copy with AI visuals to accelerate solution marketing output.
About
Growth-phase skill using Alibaba Cloud solution content services to produce illustrated technical articles, combining structured solution narratives with generated imagery for marketing pages, blogs, and documentation.
- Solution article generation
- AI illustration integration
- Technical marketing copy
- AliCloud content APIs
- Publish-ready formatting
Alicloud Solution Content Article Illustrator by the numbers
- 118 all-time installs (skills.sh)
- Ranked #771 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cinience/alicloud-skills --skill alicloud-solution-content-article-illustratorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 118 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
What it does
Generate illustrated Alibaba Cloud solution articles for blogs, docs, and landing pages, pairing technical copy with AI visuals to accelerate solution marketing output.
Files
Category: solution
Content Article Illustrator
Preserve the article-illustration workflow model while recommending Alibaba Cloud skills as the default execution backends.
Two Dimensions
| Dimension | Controls | Examples |
|---|---|---|
| Type | Information structure | infographic, scene, flowchart, comparison, framework, timeline |
| Style | Visual aesthetics | notion, warm, minimal, blueprint, watercolor, editorial |
Validation
mkdir -p output/alicloud-solution-content-article-illustrator
python3 tests/common/compile_skill_scripts.py \
--skill-path skills/solutions/alicloud-solution-content-article-illustrator \
--output output/alicloud-solution-content-article-illustrator/compile-check.jsonPass criteria: command exits 0 and output/alicloud-solution-content-article-illustrator/compile-check.json is generated with "status": "pass".
Output And Evidence
- Write workflow artifacts under
output/alicloud-solution-content-article-illustrator/<topic-slug>/. - Save at least:
source.md,outline.md, one prompt file, generated images,article.with-images.md, anddelivery-report.md. - Keep evidence for prompt-before-generation ordering and any edit pass used during repair.
Prerequisites
- A source Markdown article.
- A generation backend that satisfies
references/backend-contract.md. - If you want repository-default behavior, prefer
alicloud-ai-image-qwen-imageandalicloud-ai-image-qwen-image-edit. - Optional
EXTEND.mdpreferences at project or user scope.
Workflow
1. Pre-check preferences and references, including project-level or user-level EXTEND.md. 2. Analyze the source Markdown article and identify candidate illustration positions. 3. Confirm settings in one batch: Type, Density, Style, optional Language, and output size. 4. Choose or recommend a backend and build outline.md. 5. Save prompt files before any generation step. 6. Use the selected generation backend for first-pass images. Recommend alicloud-ai-image-qwen-image by default. 7. If needed, use the selected edit backend for one repair pass per image. Recommend alicloud-ai-image-qwen-image-edit by default. 8. Write article.with-images.md with relative images/... Markdown links. 9. Save evidence and produce delivery-report.md.
Minimal Executable Run
python3 skills/solutions/alicloud-solution-content-article-illustrator/scripts/run_workflow.py \
--source path/to/article.md \
--output-dir output/alicloud-solution-content-article-illustrator/example-run \
--generation-backend mockState Gates
drafted: source article and initial outline existconfirmed: user settings, preferences, and backend choice are confirmedgenerated: prompt files and first-pass images existvalidated: artifact structure and Markdown insertion pass checksdelivered: final article and report are written
References
- Detailed workflow:
references/workflow.md - Usage examples:
references/usage.md - Style system:
references/styles.md - Backend contract:
references/backend-contract.md - Prompt construction rules:
references/prompt-construction.md - Output contract:
references/output-spec.md - Validation and test expectations:
references/test-plan.md - Config setup:
references/config/first-time-setup.md - Preferences schema:
references/config/preferences-schema.md - Source references:
references/sources.md - Helper scripts:
scripts/load_preferences.py,scripts/validate_inputs.py,scripts/build_outline.py,scripts/collect_evidence.py
interface:
display_name: "Alibaba Cloud Solution Content Article Illustrator"
short_description: "Article-to-illustrated-Markdown workflow with Qwen Image and Qwen Image Edit"
default_prompt: "Use $alicloud-solution-content-article-illustrator to analyze an article, generate illustrations, and write a Markdown article with local image references."
System Prompt
Preserve the article-illustration workflow order:
1. load preferences 2. analyze article structure 3. confirm Type and Style 4. build outline 5. save prompt files 6. generate images 7. optionally repair images once 8. write final illustrated Markdown
Do not skip prompt-file creation before image generation.
Backend Contract
This solution skill recommends Alibaba Cloud backends by default, but does not hard-bind to them.
Recommended Defaults
- Generation backend:
alicloud-ai-image-qwen-image - Edit backend:
alicloud-ai-image-qwen-image-edit
Generation Backend Requirements
Inputs:
- saved prompt file content
- optional reference images
- requested size or aspect
- optional style hint
Outputs:
- image file path or retrievable image URL
- metadata that can be saved as evidence
Edit Backend Requirements
Inputs:
- source image
- edit instruction or saved edit prompt
- optional mask or local-edit parameters
Outputs:
- edited image file path or retrievable image URL
- metadata that can be saved as evidence
First-Time Setup
If no preference file exists, create one of these:
- project-level:
.alicloud-skills/alicloud-solution-content-article-illustrator/EXTEND.md - user-level:
$HOME/.alicloud-skills/alicloud-solution-content-article-illustrator/EXTEND.md
The project-level file overrides the user-level file.
Suggested starting template:
---
version: 1
watermark:
enabled: false
content: ""
position: bottom-right
opacity: 0.7
preferred_style:
name: null
description: ""
language: null
default_type: null
default_density: null
default_output_dir: null
custom_styles: []
---Preferences Schema
Supported preference fields:
---
version: 1
watermark:
enabled: false
content: ""
position: bottom-right
opacity: 0.7
preferred_style:
name: null
description: ""
language: null
default_type: null
default_density: null
default_output_dir: null
custom_styles: []
---Field Reference
version: schema version.watermark.enabled: whether watermarking should be enabled by default.watermark.content: watermark text or handle.watermark.position: default placement such asbottom-right.watermark.opacity: opacity hint for downstream generation or post-processing.preferred_style.name: preferred built-in or custom style name.preferred_style.description: notes or override guidance for the preferred style.language: preferred image-text language.default_type: preferred illustration type.default_density: preferred image density.default_output_dir: preferred output location policy.custom_styles: optional list of custom style definitions.
Output Directory Options
same-dirimgs-subdirillustrations-subdirindependent
Custom Style Shape
Each custom style may include:
- name: my-style
description: "Short description"The current repository implementation records custom style names in the preference summary. Deeper style attributes can be added later without changing the loader contract.
Preferences are optional. If absent, the workflow must still proceed with explicit confirmation.
Output Specification
Root Path
output/alicloud-solution-content-article-illustrator/<topic-slug>/Required Files
source.md
outline.md
article.with-images.md
delivery-report.md
workflow-run.json
artifacts.json
prompts/
images/
edits/Naming Rules
- Source article:
source.md - Final illustrated article:
article.with-images.md - Prompt files:
prompts/NN-<slug>.md - Image files:
images/NN-<slug>.png - Edit records:
edits/NN-<slug>.md
Markdown Rule
Use relative links only:
Evidence Rules
- Keep artifact listings in the output root.
- Record which outline item produced each image.
- If an image was edited, record the reason and replacement path.
- If preferences were loaded, save a preference summary artifact for reproducibility.
Prompt Construction
Rules
- Save prompt files before any image generation step.
- Each prompt must map back to one
outline.mdentry. - Use article-specific terms, concepts, and labels instead of generic filler.
- Keep prompts explicit about what to show and what to avoid.
- Keep Type and Style as separate fields so the workflow can combine them consistently.
- Use structured sections so prompts remain inspectable and reusable across backends.
Required Prompt Structure
Each prompt file should use a short header plus structured body sections:
Title:
Target Section:
Type:
Style:
Visual Goal:
References:
Layout:
ZONES:
LABELS:
COLORS:
STYLE NOTES:
ASPECT:
Prompt:
Negative Prompt:Section Requirements
Title: short human-readable title.Target Section: where the image will be inserted.Type: one workflow type such asinfographicorflowchart.Style: one selected style such asnotionorblueprint.Visual Goal: why this image exists.References: optional saved reference images or extracted notes.Layout: overall composition such asgrid,top-down, orsplit view.ZONES: named visual regions with concrete content.LABELS: article-specific terms, numbers, metrics, or short quotes.COLORS: semantic colors or palette hints.STYLE NOTES: line treatment, rendering guidance, texture, mood.ASPECT: ratio or size target.Prompt: final generation prompt body.Negative Prompt: exclusions or quality guardrails.
Reference Usage Rules
- Only include file-backed references if the files actually exist.
- Use
directwhen the reference should materially shape the output composition. - Use
stylewhen only the aesthetic language should carry over. - Use
palettewhen only colors should carry over. - If no file is saved, describe extracted characteristics in prompt text instead of pretending there is a file-backed reference.
Type-Specific Guidance
Infographic
- Prefer
Layout,ZONES, andLABELSwith concrete data. - Use
COLORSsemantically, not decoratively.
Scene
- Emphasize atmosphere, focal point, and emotional intent in
STYLE NOTES.
Flowchart
- Use
Layoutfor directionality andZONESfor ordered steps.
Comparison
- Split
ZONESclearly between the two sides.
Framework
- Use
ZONESandLABELSto show conceptual relationships.
Timeline
- Keep chronological order explicit in
ZONESandLABELS.
Prompt Quality Requirements
ZONESmust not be vague; each zone should describe specific visual content.LABELSshould use real article language whenever possible.COLORSshould reflect meaning, brand, or category.STYLE NOTESshould reinforce the selected style without overwriting Type.Promptshould summarize the structured sections, not replace them.Referencesshould clearly statedirect,style, orpaletteusage when a reference is present.
Repair Prompt Rules
- Repair prompts must describe what changes and what stays fixed.
- Limit automated repair to one pass per image.
- Save repair prompts under
edits/or reference them from the edit record. - Repair prompts should restate the original
Type,Style, and preserved elements.
Sources
- Downstream image generation skill:
skills/ai/image/alicloud-ai-image-qwen-image/ - Downstream image editing skill:
skills/ai/image/alicloud-ai-image-qwen-image-edit/ - Repository standard template:
docs/standards/skill-template.md - Approved design:
docs/plans/2026-03-06-solutions-composite-skill-design.md - External workflow reference:
../baoyu-skills/skills/baoyu-article-illustrator/
Styles
Type and Style are separate axes.
Types
infographicsceneflowchartcomparisonframeworktimeline
Styles
notionwarmminimalblueprintwatercoloreditorial
Detailed notes:
references/styles/notion.mdreferences/styles/warm.mdreferences/styles/minimal.mdreferences/styles/blueprint.mdreferences/styles/watercolor.mdreferences/styles/editorial.md
Type x Style Compatibility Matrix
| Type | notion | warm | minimal | blueprint | watercolor | editorial |
|---|---|---|---|---|---|---|
| infographic | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ |
| scene | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ |
| flowchart | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ |
| comparison | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ |
| framework | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓ |
| timeline | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ |
✓✓ = highly recommended, ✓ = compatible, ✗ = not recommended
Auto Selection By Type
| Type | Primary Style | Secondary Styles |
|---|---|---|
| infographic | blueprint | notion, editorial |
| scene | warm | watercolor, notion |
| flowchart | blueprint | notion, editorial |
| comparison | notion | editorial, minimal |
| framework | blueprint | notion, minimal |
| timeline | editorial | warm, watercolor |
Auto Selection By Content Signals
| Content Signals | Recommended Type | Recommended Styles |
|---|---|---|
| API, metrics, data, comparison | infographic | blueprint, editorial |
| tutorial, workflow, how-to, steps | flowchart | notion, blueprint |
| framework, architecture, principles | framework | blueprint, minimal |
| story, reflection, journey | scene | warm, watercolor |
| history, evolution, roadmap | timeline | editorial, warm |
| pros/cons, before/after, alternatives | comparison | notion, editorial |
Combination Notes
infographic + blueprint
- Best for technical and architectural articles.
- Prefer grid layouts, schematic lines, and high information density.
scene + warm
- Best for narrative and reflective content.
- Prefer soft gradients, inviting tone, and emotional clarity.
flowchart + notion
- Best for approachable tutorials and SaaS/productivity explainers.
- Prefer simple step containers and minimal decoration.
comparison + editorial
- Best for balanced side-by-side analysis.
- Prefer clear separators, strong headings, and concise labels.
framework + minimal
- Best for conceptual models where clarity matters more than ornament.
- Prefer clean nodes, whitespace, and restrained color usage.
timeline + watercolor
- Best for human, historical, or growth-oriented sequences.
- Prefer flowing progression and softer transitions.
The style list is intentionally smaller than the external reference skill in the first repository version. Additional style reference files can be added without changing the workflow contract.
blueprint
Description
Technical schematic style for systems, frameworks, and information-dense articles.
Visual Characteristics
- grid-aligned composition
- precise lines and containers
- diagram-first thinking
- high readability under dense labeling
- engineering or architecture feel
Best For
- system architecture
- engineering explainers
- API or infra diagrams
- technical frameworks and process maps
Prompt Hints
- specify layout direction and node relationships
- include structured labels and semantic color mapping
- prefer clarity over decorative realism
editorial
Description
Magazine-style explanatory visual language for balanced analysis and polished storytelling.
Visual Characteristics
- strong visual hierarchy
- clear headings and dividers
- polished infographic feel
- balanced typography and labels
- suitable for modern publication layouts
Best For
- explainers
- side-by-side comparisons
- journalism-style summaries
- timelines and feature overviews
Prompt Hints
- keep sections crisp and readable
- use dividers and headings intentionally
- combine polish with information density
minimal
Description
Highly restrained visual style focused on conceptual clarity and whitespace.
Visual Characteristics
- sparse elements
- high whitespace usage
- restrained palette
- low ornamentation
- emphasis on hierarchy through spacing
Best For
- conceptual frameworks
- abstract principles
- concise comparison views
- executive-level communication
Prompt Hints
- remove decorative extras
- keep zones simple and balanced
- use color only when it adds meaning
notion
Description
Minimal, approachable, hand-drawn product-illustration style inspired by knowledge-sharing and SaaS content.
Visual Characteristics
- simple line work
- rounded shapes
- quiet neutral backgrounds
- restrained accent colors
- friendly product-thinking tone
Best For
- SaaS explainers
- productivity workflows
- approachable tutorials
- knowledge cards and summaries
Prompt Hints
- use soft outlines and clear whitespace
- keep labels concise
- prefer calm, neutral composition over dramatic scenes
warm
Description
Friendly and human-centered illustration style for narrative, reflective, and educational content.
Visual Characteristics
- soft gradients
- inviting color temperature
- organic shapes
- gentle emotional tone
- more atmosphere than technical precision
Best For
- stories and journeys
- reflective writing
- lifestyle and education
- community or people-centric explainers
Prompt Hints
- prioritize mood and emotional clarity
- avoid overly technical diagram language
- use lighting and posture to convey feeling
watercolor
Description
Soft artistic style with painterly transitions and atmospheric warmth.
Visual Characteristics
- blended edges
- soft texture
- lighter structural rigidity
- expressive color transitions
- more mood than precision
Best For
- travel and lifestyle
- historical or reflective timelines
- emotional scenes
- creative and human-centered storytelling
Prompt Hints
- favor atmosphere and motion over hard geometry
- keep labels minimal
- use soft transitions and a lighter visual rhythm
Test Plan
The smoke test for this skill should verify:
- required frontmatter exists
- helper scripts compile
load_preferences.pyresolves project-first fallback correctly- preference summary captures watermark, output-dir, and custom-style metadata when present
- preference-loading guidance exists
- Type and Style are distinct concepts in the docs
- styles docs include a Type x Style compatibility matrix and auto-selection guidance
- backend recommendation is documented without hard-binding
- a minimal outline can be generated
- prompt docs require structured sections such as
ZONES,LABELS,COLORS, andASPECT - workflow and prompt docs explain
direct,style, andpalettereference usage - prompt files exist before images
article.with-images.mdis generated- inserted image links use
images/...relative paths - evidence is written under
output/alicloud-solution-content-article-illustrator-test/
Usage
Use this skill when the user asks to:
- illustrate an article
- add images to a Markdown article
- generate article visuals with consistent Type and Style
- produce an illustrated Markdown deliverable
Typical flow:
1. load preferences from EXTEND.md if present 2. confirm Type, Density, Style, and optional Language 3. choose or accept a recommended backend 4. create outline.md 5. save prompt files under prompts/ 6. generate images through the selected backend 7. optionally repair weak images once through the selected edit backend 8. write article.with-images.md
Executable example:
python3 skills/solutions/alicloud-solution-content-article-illustrator/scripts/run_workflow.py \
--source path/to/article.md \
--output-dir output/alicloud-solution-content-article-illustrator/example-run \
--generation-backend mockWorkflow
1. Pre-check
- If the user provides reference images, save them under
references/before using them in prompts. - Only treat a reference as file-backed if it is actually saved on disk.
- Look for project-level
EXTEND.mdat.alicloud-skills/alicloud-solution-content-article-illustrator/EXTEND.md. - Look for user-level
EXTEND.mdat$HOME/.alicloud-skills/alicloud-solution-content-article-illustrator/EXTEND.md. - Load and summarize preferences if present.
- Use
scripts/load_preferences.pywhen you need a reproducible preference summary artifact. - If no preference file exists, continue with explicit confirmation and reference
config/first-time-setup.md. - Choose a topic slug for the output directory.
2. Analyze
- Identify article sections, key claims, and paragraphs that benefit from visual support.
- Prefer images that clarify structure, concepts, comparisons, or narrative transitions.
- Record each candidate insertion point in
outline.md. - If references exist, classify them as
direct,style, orpaletteusage.
3. Confirm Settings
Confirm these settings in one batch:
- illustration type
- density
- style
- optional language
- output size
- generation backend
- edit backend
- reference usage mode when reference images are available
4. Build Plan
- Create
outline.mdfrom the template. - Create one prompt file per image under
prompts/. - Prompt files must be saved before any image generation step.
- Recommend Alibaba Cloud backends by default, but allow replacement if the backend contract is satisfied.
- If a saved reference image exists, record whether it is used as
direct,style, orpalette.
5. Generate Assets
- Generate first-pass images with the selected backend.
- Save images under
images/using ordered filenames. - Pass reference images only when the selected backend supports them and the files are actually saved.
6. Review And Repair
- Review each image against its outline entry.
- If needed, run one repair pass with the selected edit backend.
- Save repair intent and output under
edits/.
Reference Usage Modes
direct: use the saved reference image as a strong visual reference when the target output is closely aligned.style: extract visual characteristics from the reference and describe them in prompt text.palette: extract colors only and express them inCOLORS.
7. Finalize Delivery
- Write a new file named
article.with-images.md. - Insert relative Markdown image links such as
. - Do not overwrite
source.md. - Write
delivery-report.mdsummarizing generated artifacts and any repair pass.
#!/usr/bin/env python3
"""Create a minimal outline and prompt skeleton for article illustration."""
from __future__ import annotations
import argparse
from pathlib import Path
OUTLINE_TEXT = """# Illustration Outline
## Illustration 1
- Position: introduction
- Purpose: establish the article topic
- Visual Content: a cover illustration derived from the article theme
- Prompt File: prompts/01-cover.md
- Image File: images/01-cover.png
- Edited: no
"""
PROMPT_TEXT = """Title: Cover Illustration
Target Section: introduction
Type: scene
Style: notion
Visual Goal: establish the article topic
References:
Layout: centered editorial cover
ZONES:
- Zone 1: central concept illustration for the article theme
- Zone 2: simple background support shapes
LABELS:
- Term 1: cloud workflow
- Term 2: two main steps
COLORS:
- Primary: blue for core system concept
- Accent: orange for emphasis
STYLE NOTES: clean editorial illustration with light line work
ASPECT: 1024*1024
Prompt: create a clear editorial illustration based on the article introduction and the structured zones above
Negative Prompt: blurry, low quality, watermark
"""
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--output-dir", required=True, help="Workflow output directory")
args = parser.parse_args()
root = Path(args.output_dir)
prompts = root / "prompts"
images = root / "images"
edits = root / "edits"
for path in (root, prompts, images, edits):
path.mkdir(parents=True, exist_ok=True)
(root / "outline.md").write_text(OUTLINE_TEXT, encoding="utf-8")
(prompts / "01-cover.md").write_text(PROMPT_TEXT, encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Collect a simple artifact listing for the article illustrator workflow."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--workflow-dir", required=True, help="Workflow output directory")
parser.add_argument("--output", required=True, help="Path to JSON evidence file")
args = parser.parse_args()
root = Path(args.workflow_dir)
files = sorted(str(path.relative_to(root)) for path in root.rglob("*") if path.is_file())
payload = {
"workflow_dir": str(root),
"files": files,
"status": "pass" if root.is_dir() else "fail",
}
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
return 0 if payload["status"] == "pass" else 1
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Load EXTEND.md preferences with project-first fallback."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
DEFAULT_PROJECT_PATH = ".alicloud-skills/alicloud-solution-content-article-illustrator/EXTEND.md"
DEFAULT_USER_PATH = "~/.alicloud-skills/alicloud-solution-content-article-illustrator/EXTEND.md"
def parse_scalar(value: str):
text = value.strip()
if text in {"null", "None", "~"}:
return None
if text in {"true", "false"}:
return text == "true"
if text.isdigit():
return int(text)
try:
return float(text)
except ValueError:
pass
if text.startswith('"') and text.endswith('"'):
return text[1:-1]
if text.startswith("'") and text.endswith("'"):
return text[1:-1]
return text
def parse_frontmatter(text: str) -> dict:
lines = text.splitlines()
if not lines or lines[0].strip() != "---":
return {}
data: dict = {}
stack: list[tuple[int, object]] = [(-1, data)]
for idx, raw in enumerate(lines[1:], start=1):
line = raw.rstrip()
if line.strip() == "---":
break
if not line.strip() or line.lstrip().startswith("#"):
continue
indent = len(line) - len(line.lstrip(" "))
stripped = line.strip()
while len(stack) > 1 and indent <= stack[-1][0]:
stack.pop()
current = stack[-1][1]
if stripped.startswith("- "):
item_text = stripped[2:].strip()
if not isinstance(current, list):
continue
if item_text == "":
child_dict: dict = {}
current.append(child_dict)
stack.append((indent, child_dict))
continue
if ":" in item_text:
key, _, value = item_text.partition(":")
child_dict = {key.strip(): parse_scalar(value.strip()) if value.strip() else {}}
current.append(child_dict)
if value.strip() == "":
stack.append((indent, child_dict[key.strip()]))
else:
stack.append((indent, child_dict))
continue
current.append(parse_scalar(item_text))
continue
if ":" not in stripped:
continue
key, _, value = stripped.partition(":")
key = key.strip()
value = value.strip()
if value == "":
next_nonempty = ""
for candidate in lines[idx + 1:]:
if candidate.strip():
next_nonempty = candidate.strip()
break
if next_nonempty.startswith("- "):
child_list: list = []
if isinstance(current, dict):
current[key] = child_list
stack.append((indent, child_list))
else:
child_dict = {}
if isinstance(current, dict):
current[key] = child_dict
stack.append((indent, child_dict))
continue
if isinstance(current, dict):
current[key] = parse_scalar(value)
return data
def collect_custom_style_names(preferences: dict) -> list[str]:
custom_styles = preferences.get("custom_styles")
if not isinstance(custom_styles, list):
return []
names: list[str] = []
for item in custom_styles:
if isinstance(item, dict) and isinstance(item.get("name"), str):
names.append(item["name"])
return names
def summarize(preferences: dict) -> dict:
watermark = preferences.get("watermark", {}) if isinstance(preferences.get("watermark"), dict) else {}
preferred_style = (
preferences.get("preferred_style", {})
if isinstance(preferences.get("preferred_style"), dict)
else {}
)
return {
"version": preferences.get("version"),
"default_type": preferences.get("default_type"),
"default_density": preferences.get("default_density"),
"language": preferences.get("language"),
"default_output_dir": preferences.get("default_output_dir"),
"watermark_enabled": watermark.get("enabled"),
"watermark_content": watermark.get("content"),
"watermark_position": watermark.get("position"),
"watermark_opacity": watermark.get("opacity"),
"preferred_style_name": preferred_style.get("name"),
"preferred_style_description": preferred_style.get("description"),
"custom_style_names": collect_custom_style_names(preferences),
}
def resolve_preferences(project_path: Path, user_path: Path) -> tuple[str, Path | None]:
if project_path.is_file():
return "project", project_path
if user_path.is_file():
return "user", user_path
return "none", None
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--project-path", default=DEFAULT_PROJECT_PATH, help="Project-level EXTEND.md path")
parser.add_argument("--user-path", default=DEFAULT_USER_PATH, help="User-level EXTEND.md path")
parser.add_argument("--output", required=True, help="Path to JSON output file")
args = parser.parse_args()
project_path = Path(args.project_path).expanduser()
user_path = Path(args.user_path).expanduser()
source, selected_path = resolve_preferences(project_path, user_path)
payload = {
"source": source,
"project_path": str(project_path),
"user_path": str(user_path),
"selected_path": str(selected_path) if selected_path else None,
"preferences": {},
"summary": {},
"status": "pass",
}
if selected_path is not None:
preferences = parse_frontmatter(selected_path.read_text(encoding="utf-8"))
payload["preferences"] = preferences
payload["summary"] = summarize(preferences)
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Execute the article illustration workflow end-to-end."""
from __future__ import annotations
import argparse
import base64
import json
import subprocess
import sys
from pathlib import Path
SMALL_PNG_BASE64 = (
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wn8n9sAAAAASUVORK5CYII="
)
def run_command(cmd: list[str], cwd: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, check=True)
def parse_prompt_file(path: Path) -> dict[str, str]:
data: dict[str, str] = {}
current_key: str | None = None
for raw in path.read_text(encoding="utf-8").splitlines():
if ":" in raw and not raw.startswith("- "):
key, _, value = raw.partition(":")
current_key = key.strip()
data[current_key] = value.strip()
continue
if current_key and raw.strip():
data[current_key] = f"{data[current_key]}\n{raw.strip()}".strip()
return data
def render_mock_image(output_path: Path) -> None:
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_bytes(base64.b64decode(SMALL_PNG_BASE64))
def render_qwen_image(repo_root: Path, prompt_path: Path, output_path: Path) -> dict[str, str]:
prompt_data = parse_prompt_file(prompt_path)
request = {
"prompt": prompt_data.get("Prompt") or prompt_data.get("Visual Goal") or "illustration",
"negative_prompt": prompt_data.get("Negative Prompt") or "blurry, low quality",
"size": prompt_data.get("ASPECT") or "1024*1024",
"style": prompt_data.get("Style"),
}
request_path = output_path.with_suffix(".request.json")
request_path.parent.mkdir(parents=True, exist_ok=True)
request_path.write_text(json.dumps(request, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
cmd = [
sys.executable,
"skills/ai/image/alicloud-ai-image-qwen-image/scripts/generate_image.py",
"--file",
str(request_path),
"--output",
str(output_path),
"--print-response",
]
result = run_command(cmd, repo_root)
response = json.loads(result.stdout.strip()) if result.stdout.strip() else {}
return {"request_path": str(request_path), "response": response}
def write_article(source_path: Path, article_path: Path, image_paths: list[Path]) -> None:
content = source_path.read_text(encoding="utf-8").rstrip() + "\n\n"
image_block = "\n".join(f"})" for path in image_paths)
article_path.write_text(content + image_block + "\n", encoding="utf-8")
def write_delivery_report(path: Path, topic_slug: str, source_path: Path, image_paths: list[Path], backend: str) -> None:
lines = [
"# Delivery Report",
"",
f"- Topic Slug: {topic_slug}",
f"- Source File: {source_path.name}",
"- Final Article: article.with-images.md",
f"- Prompt Files: {len(image_paths)}",
f"- Images: {len(image_paths)}",
"- Edited Images: 0",
f"- Notes: backend={backend}",
"",
]
path.write_text("\n".join(lines), encoding="utf-8")
def main() -> int:
parser = argparse.ArgumentParser(description="Run the article illustration workflow")
parser.add_argument("--source", required=True, help="Path to source Markdown article")
parser.add_argument("--output-dir", required=True, help="Workflow output directory")
parser.add_argument(
"--generation-backend",
default="mock",
choices=["mock", "qwen-image"],
help="Generation backend to use",
)
parser.add_argument(
"--edit-backend",
default="none",
choices=["none", "qwen-image-edit"],
help="Edit backend to record for future repair steps",
)
parser.add_argument("--project-path", help="Project-level EXTEND.md override")
parser.add_argument("--user-path", help="User-level EXTEND.md override")
parser.add_argument("--topic-slug", default="article-illustration", help="Topic slug for reporting")
args = parser.parse_args()
repo_root = Path(__file__).resolve().parents[4]
source_path = Path(args.source)
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
copied_source = output_dir / "source.md"
copied_source.write_text(source_path.read_text(encoding="utf-8"), encoding="utf-8")
preferences_output = output_dir / "preferences.json"
load_preferences_cmd = [
sys.executable,
str(Path(__file__).with_name("load_preferences.py")),
"--output",
str(preferences_output),
]
if args.project_path:
load_preferences_cmd.extend(["--project-path", args.project_path])
if args.user_path:
load_preferences_cmd.extend(["--user-path", args.user_path])
run_command(load_preferences_cmd, repo_root)
run_command(
[
sys.executable,
str(Path(__file__).with_name("build_outline.py")),
"--output-dir",
str(output_dir),
],
repo_root,
)
prompts_dir = output_dir / "prompts"
images_dir = output_dir / "images"
prompt_files = sorted(prompts_dir.glob("*.md"))
render_results: list[dict[str, object]] = []
written_images: list[Path] = []
for prompt_file in prompt_files:
image_name = prompt_file.stem + ".png"
image_path = images_dir / image_name
if args.generation_backend == "mock":
render_mock_image(image_path)
render_results.append({
"prompt_file": str(prompt_file.relative_to(output_dir)),
"image_file": str(image_path.relative_to(output_dir)),
"backend": "mock",
})
else:
backend_result = render_qwen_image(repo_root, prompt_file, image_path)
render_results.append({
"prompt_file": str(prompt_file.relative_to(output_dir)),
"image_file": str(image_path.relative_to(output_dir)),
"backend": "qwen-image",
"request_path": backend_result["request_path"],
"response": backend_result["response"],
})
written_images.append(Path("images") / image_path.name)
article_path = output_dir / "article.with-images.md"
write_article(copied_source, article_path, written_images)
report_path = output_dir / "delivery-report.md"
write_delivery_report(report_path, args.topic_slug, copied_source, written_images, args.generation_backend)
evidence_path = output_dir / "workflow-run.json"
evidence = {
"topic_slug": args.topic_slug,
"generation_backend": args.generation_backend,
"edit_backend": args.edit_backend,
"source": str(copied_source),
"article": str(article_path),
"render_results": render_results,
}
evidence_path.write_text(json.dumps(evidence, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
run_command(
[
sys.executable,
str(Path(__file__).with_name("collect_evidence.py")),
"--workflow-dir",
str(output_dir),
"--output",
str(output_dir / "artifacts.json"),
],
repo_root,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Validate minimal inputs for the article illustrator workflow."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source", required=True, help="Path to source Markdown article")
parser.add_argument("--topic-slug", required=True, help="Output topic slug")
parser.add_argument("--output", required=True, help="Path to JSON output file")
args = parser.parse_args()
source = Path(args.source)
payload = {
"source_exists": source.is_file(),
"source_suffix": source.suffix,
"topic_slug": args.topic_slug,
"valid_topic_slug": bool(args.topic_slug) and "/" not in args.topic_slug and " " not in args.topic_slug,
"status": "pass",
}
if not payload["source_exists"] or payload["source_suffix"].lower() != ".md" or not payload["valid_topic_slug"]:
payload["status"] = "fail"
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
return 0 if payload["status"] == "pass" else 1
if __name__ == "__main__":
raise SystemExit(main())
Delivery Report
- Topic Slug:
- Source File:
- Final Article:
- Prompt Files:
- Images:
- Edited Images:
- Notes:
Outline Template
Illustration 1
- Position:
- Purpose:
- Visual Content:
- Prompt File:
- Image File:
- Edited:
Prompt Template
Title: Target Section: Type: Style: Visual Goal: References: Layout: ZONES:
- Zone 1:
- Zone 2:
LABELS:
- Term 1:
- Term 2:
COLORS:
- Primary:
- Accent:
STYLE NOTES: ASPECT: Prompt: Negative Prompt: