{"id":"story-cover","name":"story-cover","summary":"小説の表紙生成。本名と著者名に基づいて主題と文体を自動的に分析し、GPT-Image-2を呼び出してタイトルと作者名を含むプロフェッショナルグレードのウェブ小説カバーを生成します。","body":"# story-cover：小说封面生成\n\n你是小说封面设计师。根据书名和题材，调用 GPT-Image-2 一次性生成包含书名和作者名的完整封面。\n\n**核心原则：封面是读者的第一印象，一眼传达题材和氛围。**\n\n---\n\n## 生成通路\n\n- **Codex 内置（优先）**：当前 Codex CLI 会话可调用 `$imagegen` / `image_gen` 时，直接生成并落盘；计入 Codex 通用用量，无需 `OPENAI_API_KEY` 或 `GPT_IMAGE_API_KEY`，也不运行 `curl`。`story-cover` 自行调用工具，不让用户另开命令。\n- **API 回退**：仅在会话没有内置工具或用户明确指定 API 时使用，需要 `GPT_IMAGE_API_KEY`。工具缺失不等于 Codex 订阅不支持生图；内置调用失败时先报告错误，不静默切换到可能收费的 API。\n\n## 输出参数与 API 回退环境变量\n\n| 变量 | 必填 | 默认 | 说明 |\n|:-----|:----:|:-----|:-----|\n| `GPT_IMAGE_API_KEY` | API 回退必填 | — | OpenAI 或兼容代理的 API Key；Codex 内置通路不用 |\n| `GPT_IMAGE_BASE_URL` | | `https://api.openai.com/v1` | 兼容代理时改这个 |\n| `GPT_IMAGE_MODEL` | | `gpt-image-2` | 仅在测试新模型时覆盖 |\n| `GPT_IMAGE_SIZE` | | `1024x1536` | API 回退的目标比例提示（番茄 3:4→`768x1024`，默认 2:3→`1024x1536`）。官方 gpt-image-2 认任意 16 倍数尺寸（比例≤3:1），但**很多中转代理会忽略 size、按预设返回约 2:3**（已实测）——平台尺寸不靠它，由「导出平台上传尺寸」步骤兜底 |\n| `UPLOAD_SIZE` | | — | 平台固定上传像素（番茄 `600x800`）；设置后由「导出平台上传尺寸」步骤居中裁剪+缩放出上传版（不变形、不依赖出图尺寸） |\n| `BOOK_DIR` | ✅ | — | 输出目录，建议 `./covers/<书名>` |\n| `REF_IMAGE` | | — | 参考图本地路径或 URL；内置通路先把图片载入会话，API 回退走 `images/edits` 图生图 |\n\n---\n\n## 生成流程\n\n### Step 1：收集信息\n\n必填：书名、作者名（笔名）、目标平台、输出目录 `BOOK_DIR`（建议 `./covers/<书名>`；API 回退用环境变量，内置通路直接使用当前任务值）\n选填：参考图 `REF_IMAGE`（本地路径或 URL，设置后切换到图生图）、风格偏好、尺寸\n\n> **书名和笔名是封面必需信息**：缺任一必须先用 AskUserQuestion 问用户补全，不得编造或留空。\n\n**按目标平台定封面尺寸**：番茄上传 600×800 是 **3:4**（不是 2:3），出图比例不对、平台二次裁剪就会切掉书名/笔名。\n\n| 平台 | 上传尺寸 | 比例 | 生成 `GPT_IMAGE_SIZE`（尽量） |\n|:-----|:--------|:-----|:-------------------|\n| 番茄小说 | 600×800 | 3:4 | `768x1024` |\n| 其他平台（默认竖版） | 按平台规格 | 2:3 | `1024x1536` |\n\n内置通路把目标比例写进提示词；API 回退再 `export GPT_IMAGE_SIZE`（很多代理会忽略、返回约 2:3）。平台有固定上传像素时设置 `UPLOAD_SIZE`（番茄 `600x800`）。**平台尺寸最终由「导出平台上传尺寸」步骤居中裁剪+缩放保证，不依赖实际出图尺寸。** 平台与题材风格见 [references/cover-styles.md](references/cover-styles.md)。\n\n### Step 2：题材判定\n\n扫描书名（必要时简介）中的关键词，对照 [references/cover-styles.md](references/cover-styles.md) 的「题材推断规则」表选定题材。\n\n- 单题材命中 → 直接采用\n- 多题材命中 → 按优先级取一：仙侠 > 西幻 > 古言 > 现言 > 都市 > 悬疑 > 科幻 > 历史 > 灵异 > 轻小说\n- 零命中 → 默认 `都市`\n\n### Step 3：构建提示词\n\n提示词 = **文字层** + **风格层** + **画面层**，全部用英文编写。\n\n#### 文字层：书名 + 作者名字体设计\n\n在提示词中直接包含中文书名和作者名，GPT-Image-2 可直接渲染。**重点描述字体风格**：\n\n```\nTitle text '书名' at top center in [书名字体风格].\nAuthor name '作者名' at bottom center in [作者名字体风格].\n```\n\n#### 书名字体风格\n\n| 题材 | 描述关键词 |\n|:-----|:-----------|\n| 玄幻/仙侠 | `bold golden brush calligraphy with metallic glow and sharp strokes` |\n| 都市 | `modern bold sans-serif with metallic silver finish` |\n| 古言/宫斗 | `elegant golden traditional Kai script with ornate decoration` |\n| 现言/甜宠 | `soft rounded handwritten style in white with pink glow` |\n| 悬疑/推理 | `distorted bold cracked letters in blood red` |\n| 科幻/末世 | `neon glowing futuristic font in electric blue` |\n| 西幻 | `metallic embossed fantasy lettering with glow effect` |\n| 历史/军事 | `heavy stone-carved seal script in deep red` |\n| 灵异/恐怖 | `eerie dripping handwritten font in sickly green` |\n| 轻小说 | `colorful cartoon outlined bubbly font` |\n\n#### 作者名字体风格（重点：作者名必须精心设计，不能只是\"小字\"）\n\n作者名虽小，但是封面专业感的关键。必须指定：**字体 + 颜色 + 装饰元素**，让作者名与书名风格呼应但不抢焦点。\n\n| 题材 | 作者名风格提示词 |\n|:-----|:----------------|\n| 玄幻/仙侠 | `small refined white serif text with faint golden glow, flanked by delicate cloud-scroll ornaments on both sides, resting on a thin horizontal gold line` |\n| 都市 | `small clean white modern text with subtle drop shadow, positioned above a thin silver horizontal divider line` |\n| 古言/宫斗 | `small elegant dark red traditional text inside a thin golden rectangular border frame with corner decorations` |\n| 现言/甜宠 | `small soft pink-white handwritten text with a tiny heart motif on the left side, light sparkle effect` |\n| 悬疑/推理 | `small pale grey text with slight blur effect, almost hidden in the shadows, a thin cracked line underneath` |\n| 科幻/末世 | `small crisp white monospace text with subtle cyan scanline overlay, flanked by small geometric brackets` |\n| 西幻 | `small bronze medieval script text with aged parchment texture, enclosed in a small decorative shield or banner shape` |\n| 历史/军事 | `small dignified white Song typeface text above a double horizontal line in dark red` |\n| 灵异/恐怖 | `small faded grey-green text slightly tilted, with a thin dripping ink line above` |\n| 轻小说 | `small playful rounded white text with pastel color outline, tiny star decorations on both sides` |\n\n**作者名通用规则**：\n- 大小：`small`（不能太大抢书名焦点，也不能太小看不清）\n- 位置：`at bottom center`，与画面底部保持适当间距\n- 必须有装饰元素：线条/边框/小图标/光效中至少一种\n- 颜色与背景形成对比但不刺眼\n\n#### 风格层：平台风格\n\n平台风格的描述关键词统一来自 [references/cover-styles.md](references/cover-styles.md) 的「平台风格」节，按目标平台直接取对应关键词串使用，不在本文件维护副本以免与参考文件漂移。\n\n#### 画面层：题材 + 构图\n\n从 [references/cover-styles.md](references/cover-styles.md) 读取题材对应的风格标签、色彩、人物、背景描述。\n\n构图变体（首次输出 2-3 个方案）：\n\n| 方案 | 构图 | 适合题材 |\n|:-----|:-----|:---------|\n| A | 人物特写 + 场景 | 全题材通用 |\n| B | 全身像 + 动态姿势 | 玄幻、都市、西幻 |\n| C | 纯场景/氛围图 | 悬疑、科幻、历史 |\n\n#### 完整提示词模板\n\n```\nChinese web novel cover design, [平台风格].\nTitle text '{书名}' at top center in [书名字体风格].\nAuthor name '{作者名}' at bottom center in [作者名字体风格 — 从上表选择].\n[题材风格标签]. [人物描述]. [背景描述].\n[色彩指令]. [光效指令].\nProfessional book cover, high detail digital painting, portrait [平台比例：番茄=3:4，默认=2:3] ratio, keep title and author name inside the central safe area away from edges (inner ~85%), no watermark\n```\n\n#### 提示词技巧（实测验证）\n\n- 人物描述越具体越好：服饰、姿态、发型、表情、道具每个维度都指定\n- 背景分层：前景（人物）→ 中景（场景）→ 远景（氛围）\n- 光效是指定光源方向 + 颜色（如 `dramatic golden light from above`）\n- 用 `digital painting style` 而非 `photo`，避免真人照片感\n\n### Step 4：生成并保存\n\n#### Codex 内置 ImageGen（优先）\n\n1. 用 Step 3 的完整提示词调用 `image_gen`。比例和安全区写进提示词，不传 `GPT_IMAGE_MODEL`、`GPT_IMAGE_SIZE`、`response_format` 等 API 参数。\n2. 有 `REF_IMAGE` 时，本地文件先用图片查看工具载入会话；URL 先下载再载入。说明它是编辑目标还是风格参考，并列出必须保持的内容。\n3. 每个构图方案单独调用一次。先创建 `BOOK_DIR/封面/`，再把工具返回的图片复制为 `封面_vN.png`，`N` 自增且不覆盖旧版；保留 `$CODEX_HOME/generated_images/` 原文件，同时保存同名 `.prompt.txt`，有参考图再保存 `.ref.txt`。确认图片可读，并把原图绝对路径交给 Step 5。\n\n#### API 回退\n\n`gpt-image-2` 始终返回 base64，请求体不要带 `response_format`（旧 DALL-E 参数，gpt-image 系列不支持）。`$PROMPT` 为「构建提示词」步骤拼出的完整提示词。\n\n两种调用方式二选一：未设置 `REF_IMAGE` → 走「文生图」；设置了 → 走「图生图」。\n\n#### 文生图（默认）\n\n```bash\nset -euo pipefail\n: \"${GPT_IMAGE_API_KEY:?请设置 export GPT_IMAGE_API_KEY=你的key}\"\n: \"${PROMPT:?请先 export PROMPT=构建提示词步骤拼好的完整提示词}\"\nBASE_URL=\"${GPT_IMAGE_BASE_URL:-https://api.openai.com/v1}\"\nMODEL=\"${GPT_IMAGE_MODEL:-gpt-image-2}\"\nSIZE=\"${GPT_IMAGE_SIZE:-1024x1536}\"\nBOOK_DIR=\"${BOOK_DIR:?请先 export BOOK_DIR=./covers/<书名>}\"\n\nmkdir -p \"$BOOK_DIR/封面\"\n\n# 自增版本号，避免覆盖之前生成的封面\ni=1\nwhile [ -f \"$BOOK_DIR/封面/封面_v${i}.png\" ]; do i=$((i+1)); done\nOUT=\"$BOOK_DIR/封面/封面_v${i}.png\"\nRESP=$(mktemp)\ntrap 'rm -f \"$RESP\"' EXIT\n\n# 用 jq 拼 JSON 体，避免 PROMPT 里的引号/换行/中文把 shell 字符串撑破\nBODY=$(jq -n \\\n  --arg m \"$MODEL\" \\\n  --arg p \"$PROMPT\" \\\n  --arg s \"$SIZE\" \\\n  '{model:$m, prompt:$p, size:$s}')\n\ncurl -fsS --max-time 180 --retry 2 --retry-delay 5 \\\n  \"$BASE_URL/images/generations\" \\\n  -H \"Authorization: Bearer $GPT_IMAGE_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d \"$BODY\" > \"$RESP\"\n\n# API 出错时早退，避免把 error JSON 当成 base64 写成损坏 PNG\nif jq -e '.error' \"$RESP\" >/dev/null 2>&1; then\n  echo \"API error:\" >&2\n  jq '.error' \"$RESP\" >&2\n  exit 1\nfi\n\n# `// empty` 让缺失字段输出空串而非 \"null\"，配合下面的 -s 检查避免写出 3 字节假 PNG\njq -er '.data[0].b64_json // empty' \"$RESP\" | base64 --decode > \"$OUT\"\n[ -s \"$OUT\" ] || { echo \"empty or malformed output: $OUT\" >&2; head -c 300 \"$RESP\" >&2; exit 1; }\n\n# 落地提示词副本，方便迭代时基于上一次微调\nprintf '%s\\n' \"$PROMPT\" > \"${OUT%.png}.prompt.txt\"\n\nfile \"$OUT\"\nls -lt \"$BOOK_DIR/封面/\"\n```\n\n#### 图生图（提供参考图时）\n\n`/v1/images/edits` 走 `multipart/form-data`，**不能** 用 `Content-Type: application/json`。文本字段用 `--form-string`（避免 `@` 被误判为文件引用），图片字段用 `-F image=@path`。\n\n```bash\nset -euo pipefail\n: \"${GPT_IMAGE_API_KEY:?请设置 export GPT_IMAGE_API_KEY=你的key}\"\n: \"${PROMPT:?请先 export PROMPT=构建提示词步骤拼好的完整提示词}\"\nBASE_URL=\"${GPT_IMAGE_BASE_URL:-https://api.openai.com/v1}\"\nMODEL=\"${GPT_IMAGE_MODEL:-gpt-image-2}\"\nSIZE=\"${GPT_IMAGE_SIZE:-1024x1536}\"\nBOOK_DIR=\"${BOOK_DIR:?请先 export BOOK_DIR=./covers/<书名>}\"\nREF_IMAGE=\"${REF_IMAGE:?请先 export REF_IMAGE=本地路径或 URL}\"\n\nmkdir -p \"$BOOK_DIR/封面\"\n\n# 自增版本号\ni=1\nwhile [ -f \"$BOOK_DIR/封面/封面_v${i}.png\" ]; do i=$((i+1)); done\nOUT=\"$BOOK_DIR/封面/封面_v${i}.png\"\nRESP=$(mktemp)\nREF_TMP=\"\"\ntrap '[ -n \"$REF_TMP\" ] && rm -f \"$REF_TMP\"; rm -f \"$RESP\"' EXIT\n\n# URL 先下载到临时文件，本地路径直接用。用裸 mktemp 以保证 macOS/Linux 行为一致。\ncase \"$REF_IMAGE\" in\n  http://*|https://*)\n    REF_TMP=$(mktemp)\n    curl -fsSL --max-time 60 -o \"$REF_TMP\" \"$REF_IMAGE\"\n    REF_LOCAL=\"$REF_TMP\"\n    ;;\n  *)\n    [ -f \"$REF_IMAGE\" ] || { echo \"参考图不存在: $REF_IMAGE\" >&2; exit 1; }\n    REF_LOCAL=\"$REF_IMAGE\"\n    ;;\nesac\n\ncurl -fsS --max-time 240 --retry 2 --retry-delay 5 \\\n  \"$BASE_URL/images/edits\" \\\n  -H \"Authorization: Bearer $GPT_IMAGE_API_KEY\" \\\n  --form-string \"model=$MODEL\" \\\n  --form-string \"size=$SIZE\" \\\n  --form-string \"prompt=$PROMPT\" \\\n  -F \"image=@$REF_LOCAL\" > \"$RESP\"\n\nif jq -e '.error' \"$RESP\" >/dev/null 2>&1; then\n  echo \"API error:\" >&2\n  jq '.error' \"$RESP\" >&2\n  exit 1\nfi\n\n# `// empty` 让缺失字段输出空串而非 \"null\"，配合 -s 检查避免写出 3 字节假 PNG\njq -er '.data[0].b64_json // empty' \"$RESP\" | base64 --decode > \"$OUT\"\n[ -s \"$OUT\" ] || { echo \"empty or malformed output: $OUT\" >&2; head -c 300 \"$RESP\" >&2; exit 1; }\n\nprintf '%s\\n' \"$PROMPT\"    > \"${OUT%.png}.prompt.txt\"\nprintf '%s\\n' \"$REF_IMAGE\" > \"${OUT%.png}.ref.txt\"\n\nfile \"$OUT\"\nls -lt \"$BOOK_DIR/封面/\"\n```\n\n### Step 5：导出平台上传尺寸（平台有固定像素时）\n\n平台有固定上传像素（番茄 600×800）时，把原图**居中裁剪+缩放**成上传尺寸——不论出图是 2:3 还是 3:4 都裁成平台精确像素，不变形，避免平台再裁切掉书名/笔名。原图保留、另存 `_上传` 版；`SRC` 和 `TARGET` 直接使用前序步骤的任务值，不依赖跨 shell 的临时变量：\n\n```bash\nSRC='<Step 4 生成的原图绝对路径>'\nTARGET='<Step 1 确定的平台上传尺寸；无则留空>'\n[ -f \"$SRC\" ] || { echo \"封面原图不存在: $SRC\" >&2; exit 1; }\nif [ -n \"$TARGET\" ] && [ -f \"$SRC\" ]; then\n  UP=\"${SRC%.png}_上传.png\"; W=\"${TARGET%x*}\"; H=\"${TARGET#*x}\"\n  if command -v magick >/dev/null 2>&1; then M=magick\n  elif command -v convert >/dev/null 2>&1; then M=convert; else M=\"\"; fi\n  if [ -n \"$M\" ]; then\n    \"$M\" \"$SRC\" -resize \"${W}x${H}^\" -gravity center -extent \"${W}x${H}\" \"$UP\"  # 缩放填满后居中裁\n  elif command -v sips >/dev/null 2>&1; then\n    cp \"$SRC\" \"$UP\"\n    sw=$(sips -g pixelWidth \"$UP\" | awk '/pixelWidth/{print $NF}')\n    sh=$(sips -g pixelHeight \"$UP\" | awk '/pixelHeight/{print $NF}')\n    if [ $((sw*H)) -ge $((sh*W)) ]; then sips --resampleHeight \"$H\" \"$UP\" >/dev/null\n    else sips --resampleWidth \"$W\" \"$UP\" >/dev/null; fi\n    sips -c \"$H\" \"$W\" \"$UP\" >/dev/null   # sips -c 是 高 宽，居中裁\n  else\n    echo \"无 magick/convert/sips，跳过；手动把 $SRC 居中裁剪+缩放到 $TARGET 再上传\" >&2\n  fi\n  [ -f \"$UP\" ] && file \"$UP\"\nfi\n```\n\n> 书名/笔名已在提示词里留中心安全区，居中裁剪不会切到。\n\n### Step 6：质量检查 + 迭代\n\n| 检查项 | 标准 |\n|:-------|:-----|\n| 文字渲染 | 书名清晰可辨，字体风格匹配题材 |\n| 题材匹配 | 视觉风格与书名题材一致 |\n| 构图合理 | 主体突出，文字不遮挡核心画面 |\n| 平台适配 | 符合目标平台的封面风格调性 |\n| 平台尺寸 | 比例与平台一致；缩放到上传尺寸后书名、笔名完整可见、未被裁切 |\n\n不满意时调整方向：更换构图、调整色调、换字体风格、换平台风格。\n\n---\n\n## 参考资料\n\n| 文件 | 何时加载 |\n|:-----|:---------|\n| [references/cover-styles.md](references/cover-styles.md) | 题材→视觉风格映射、平台风格详情、提示词模板 |\n\n---\n\n## 语言\n\n- 跟随用户的语言回复，用户用什么语言就用什么语言回复\n- 中文回复遵循《中文文案排版指北》","author":"@worldwonderer","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/worldwonderer/oh-story-claudecode/tree/main/skills/story-cover","license":"MIT","category":null,"lang":"multi","tokens":4753,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"references/cover-styles.md","size":8992,"sha256":"e5c426310a8e444f8597856889fff614a79f562626ca43be76e8f4c4732b5b3e"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["api.openai.com"]}}