mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-12 13:59:47 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61342ecfea | |||
| 5f753dd584 | |||
| 076192d58e | |||
| d6d434e714 | |||
| 6f3600d8e5 | |||
| 045fe5e57e | |||
| aa1a967a9f | |||
| d643bad53c | |||
| 0d977787b5 | |||
| 516803feb4 | |||
| 4af0506fa3 | |||
| 80a1c2970a | |||
| cdfa0dbff9 | |||
| 505a7e10ce | |||
| 6d063734ae | |||
| 31d728b505 | |||
| f6d5df0594 | |||
| 4bd5fe573e | |||
| 8c17d77209 | |||
| fb749aae36 | |||
| a23f2e6a4b | |||
| ae32c56a3c | |||
| 4b2ad3ad18 | |||
| 512b6d2e15 | |||
| 60d0b9c95b | |||
| acf1d42a64 | |||
| adb783ae6d |
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"metadata": {
|
||||
"description": "Skills shared by Baoyu for improving daily work efficiency",
|
||||
"version": "1.109.0"
|
||||
"version": "1.115.2"
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: release-skills
|
||||
description: Universal release workflow. Auto-detects version files and changelogs. Supports Node.js, Python, Rust, Claude Plugin, and generic projects. Use when user says "release", "发布", "new version", "bump version", "push", "推送".
|
||||
description: Universal release workflow. Auto-detects version files and changelogs. Supports Node.js, Python, Rust, Claude Plugin, GitHub Releases, annotated tags, historical release backfill, and generic projects. Use when user says "release", "发布", "new version", "bump version", "push", "推送", "release notes", "GitHub Release", or "回填 Release".
|
||||
---
|
||||
|
||||
# Release Skills
|
||||
@@ -39,6 +39,7 @@ Just run `/release-skills` - auto-detects your project configuration.
|
||||
| `--major` | Force major version bump |
|
||||
| `--minor` | Force minor version bump |
|
||||
| `--patch` | Force patch version bump |
|
||||
| `--backfill-releases` | Create missing GitHub Releases for existing tags from changelog sections |
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -57,7 +58,11 @@ Just run `/release-skills` - auto-detects your project configuration.
|
||||
- `HISTORY*.md`
|
||||
- `CHANGES*.md`
|
||||
4. Identify language of each changelog by filename suffix
|
||||
5. Display detected configuration
|
||||
5. Detect GitHub release support:
|
||||
- Check whether `origin` points to GitHub
|
||||
- Check whether `gh` is installed and authenticated
|
||||
- Check existing releases with `gh release list --limit 5` when available
|
||||
6. Display detected configuration
|
||||
|
||||
**Project Hook Contract**:
|
||||
|
||||
@@ -310,6 +315,14 @@ git commit -m "docs(project): update architecture documentation"
|
||||
- Read version file (JSON/TOML/text)
|
||||
- Update version number
|
||||
- Write back (preserve formatting)
|
||||
3. **Create release notes file**:
|
||||
- Prefer the new version section from `CHANGELOG.md`
|
||||
- If no English/default changelog exists, use the first detected changelog
|
||||
- Extract only the exact `## {VERSION} - {YYYY-MM-DD}` section through the next `##`
|
||||
- Match both plain version and tag-prefixed headings when needed, e.g. `1.2.3` and `v1.2.3`
|
||||
- Keep breaking changes near the top; if needed, add a short highlight before other sections
|
||||
- Write notes to a UTF-8 temp file and reuse it for annotated tag messages, GitHub Releases, and `publish_artifact`
|
||||
- In normal mode, stop rather than creating an empty tag or GitHub Release when notes cannot be found
|
||||
|
||||
**Version Paths by File Type**:
|
||||
|
||||
@@ -325,7 +338,7 @@ git commit -m "docs(project): update architecture documentation"
|
||||
|
||||
Before creating the release commit, ask user to confirm:
|
||||
|
||||
**Use AskUserQuestion with two questions**:
|
||||
**Use AskUserQuestion with three questions**:
|
||||
|
||||
1. **Version bump** (single select):
|
||||
- Show recommended version based on Step 3 analysis
|
||||
@@ -335,6 +348,11 @@ Before creating the release commit, ask user to confirm:
|
||||
2. **Push to remote** (single select):
|
||||
- Options: "Yes, push after commit", "No, keep local only"
|
||||
|
||||
3. **Publish GitHub Release** (single select):
|
||||
- Offer this only when GitHub release support is available
|
||||
- Default to "Yes, publish after tag push" when the user also chose push
|
||||
- If the user keeps the release local, do not create or edit a GitHub Release
|
||||
|
||||
**Example Output Before Confirmation**:
|
||||
```
|
||||
Commits created:
|
||||
@@ -349,10 +367,11 @@ Changelog preview (en):
|
||||
### Fixes
|
||||
- Improve panel layout for long dialogues in comic
|
||||
|
||||
Ready to create release commit and tag.
|
||||
Release notes source: CHANGELOG.md#1.3.0
|
||||
Ready to create release commit, annotated tag, and GitHub Release.
|
||||
```
|
||||
|
||||
### Step 9: Create Release Commit and Tag
|
||||
### Step 9: Create Release Commit and Annotated Tag
|
||||
|
||||
After user confirmation:
|
||||
|
||||
@@ -367,10 +386,11 @@ After user confirmation:
|
||||
git commit -m "chore: release v{VERSION}"
|
||||
```
|
||||
|
||||
3. **Create tag**:
|
||||
3. **Create annotated tag**:
|
||||
```bash
|
||||
git tag v{VERSION}
|
||||
git tag -a v{VERSION} -F <release-notes-file>
|
||||
```
|
||||
If `.releaserc.yml` sets `tag.sign: true`, use `git tag -s` with the same notes file.
|
||||
|
||||
4. **Push if user confirmed** (Step 8):
|
||||
```bash
|
||||
@@ -380,6 +400,28 @@ After user confirmation:
|
||||
|
||||
**Note**: Do NOT add Co-Authored-By line. This is a release commit, not a code contribution.
|
||||
|
||||
### Step 10: Publish Release Artifacts and GitHub Release
|
||||
|
||||
Project artifact publishing and GitHub Releases are separate outputs:
|
||||
|
||||
1. **Project artifacts**:
|
||||
- If `release.hooks.publish_artifact` exists, run it once per prepared target
|
||||
- Pass the same `{release_notes_file}` used for the tag and GitHub Release
|
||||
- In dry-run mode, pass `{dry_run}=true` and report what would be published
|
||||
|
||||
2. **GitHub Release**:
|
||||
- Run only if the user confirmed remote publishing and GitHub support is available
|
||||
- Ensure the tag exists on the remote before creating the release
|
||||
- Create or update using the extracted notes:
|
||||
```bash
|
||||
if gh release view v{VERSION} >/dev/null 2>&1; then
|
||||
gh release edit v{VERSION} --title "v{VERSION}" --notes-file <release-notes-file>
|
||||
else
|
||||
gh release create v{VERSION} --title "v{VERSION}" --notes-file <release-notes-file> --verify-tag
|
||||
fi
|
||||
```
|
||||
- Never inline multiline release notes into shell commands
|
||||
|
||||
**Post-Release Output**:
|
||||
```
|
||||
Release v1.3.0 created.
|
||||
@@ -391,9 +433,32 @@ Commits:
|
||||
4. chore: release v1.3.0
|
||||
|
||||
Tag: v1.3.0
|
||||
Tag type: annotated
|
||||
GitHub Release: published # or "skipped/local only"
|
||||
Status: Pushed to origin # or "Local only - run git push when ready"
|
||||
```
|
||||
|
||||
## Backfill Existing GitHub Releases
|
||||
|
||||
Use this mode when the user asks to backfill historical releases or passes `--backfill-releases`.
|
||||
|
||||
1. Do not bump versions, edit changelogs, or create release commits.
|
||||
2. List existing tags in version order and detect missing releases:
|
||||
```bash
|
||||
git tag --sort=v:refname
|
||||
gh release view <tag>
|
||||
```
|
||||
3. For each tag without a GitHub Release:
|
||||
- Normalize the changelog lookup by stripping the configured tag prefix, e.g. `v1.2.3` -> `1.2.3`
|
||||
- Extract the matching section from `CHANGELOG.md`; fall back to the first matching changelog file
|
||||
- Skip or ask before publishing if no matching changelog section exists
|
||||
- Create the release with:
|
||||
```bash
|
||||
gh release create <tag> --title "<tag>" --notes-file <release-notes-file> --verify-tag
|
||||
```
|
||||
4. Detect lightweight tags with `git cat-file -t <tag>` (`commit` means lightweight, `tag` means annotated).
|
||||
5. Do not rewrite public lightweight tags by default. Converting an existing remote tag to an annotated tag requires explicit user confirmation because it rewrites a published reference.
|
||||
|
||||
## Configuration (.releaserc.yml)
|
||||
|
||||
Optional config file in project root to override defaults:
|
||||
@@ -498,6 +563,7 @@ No changes made. Run without --dry-run to execute.
|
||||
/release-skills --minor # Force minor bump
|
||||
/release-skills --patch # Force patch bump
|
||||
/release-skills --major # Force major bump (with confirmation)
|
||||
/release-skills --backfill-releases # Create missing GitHub Releases for existing tags
|
||||
```
|
||||
|
||||
## When to Use
|
||||
@@ -506,6 +572,7 @@ Trigger this skill when user requests:
|
||||
- "release", "发布", "create release", "new version", "新版本"
|
||||
- "bump version", "update version", "更新版本"
|
||||
- "prepare release"
|
||||
- "release notes", "GitHub Release", "回填 Release"
|
||||
- "push to remote" (with uncommitted changes)
|
||||
|
||||
**Important**: If user says "just push" or "直接 push" with uncommitted changes, STILL follow all steps above first.
|
||||
|
||||
+60
-1
@@ -2,6 +2,65 @@
|
||||
|
||||
English | [中文](./CHANGELOG.zh.md)
|
||||
|
||||
## 1.115.2 - 2026-05-10
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: honor explicit Codex Chrome plugin requests as a distinct browser-control mode, keep Chrome Computer Use and CDP fallbacks from silently taking over, and improve X Articles draft creation detection.
|
||||
|
||||
## 1.115.1 - 2026-05-10
|
||||
|
||||
### Fixes
|
||||
- `baoyu-imagine`: change the default MiniMax image API endpoint to `https://api.minimaxi.com` to match the current official image generation documentation, while keeping `https://api.minimax.io` available through `MINIMAX_BASE_URL` overrides.
|
||||
- `baoyu-image-gen`: sync the deprecated image-generation entrypoint with the same MiniMax default endpoint and regression coverage.
|
||||
|
||||
## 1.115.0 - 2026-05-09
|
||||
|
||||
### Features
|
||||
- `baoyu-post-to-x`: add Chrome Computer Use as the preferred execution mode in Codex. When Computer Use tools are available, all X UI actions (compose, article, quote, video) go through the user's real Chrome window instead of CDP scripts. CDP scripts become a fallback when Computer Use is unavailable or explicitly not requested.
|
||||
|
||||
## 1.114.1 - 2026-05-08
|
||||
|
||||
### Fixes
|
||||
- `baoyu-danger-gemini-web`: restore generated-image extraction for current Gemini Web responses where image URLs appear as `https://lh3.googleusercontent.com/gg-dl/` without the legacy generated-image markers. Adds regression coverage for the fallback response shape. (by @evilstar2016)
|
||||
|
||||
## 1.114.0 - 2026-05-05
|
||||
|
||||
### Features
|
||||
- `baoyu-infographic`: add `retro-popup-pop` style — retro pixel popup × pop-art collage. Renders content as a stack of 80/90s desktop popup windows (title bars, close buttons, ERROR / ALERT dialogs, file windows like `PROBLEMS.EXE`, progress bars, OK / CANCEL / FIX IT buttons) with thick black outlines, flat color fills, and bright cyan (#12B8DE) or vintage cream (#F5F0E6) backgrounds. Pairs especially well with the `dense-modules` layout; promoted as a recommended style for the `高密度信息大图` keyword shortcut and the `Product/Buying Guide` content type. Style Gallery count goes from 21 to 22.
|
||||
Credit to AJ@WaytoAGI.
|
||||
|
||||
### Documentation
|
||||
- `release-skills`: document GitHub Release publishing in the release workflow, including release-notes extraction from changelog sections, annotated tag creation, `gh release create/edit`, and historical release backfill for existing tags.
|
||||
|
||||
## 1.113.0 - 2026-04-25
|
||||
|
||||
### Features
|
||||
- `baoyu-imagine`: add DashScope Wan 2.7 image model support (`wan2.7-image-pro` and `wan2.7-image`) directly through the official Aliyun (Bailian) API. Supports text-to-image, image editing, and multi-image fusion with up to 9 reference images, with documented `[1:8, 8:1]` aspect ratio validation and per-mode pixel-budget rules. Forces `parameters.n: 1` to match baoyu-imagine's single-image save semantics and explicitly rejects `--n > 1` to prevent silent multi-image billing (the API defaults to `n=4` in non-collage mode). Allows `--provider dashscope --ref ...` opt-in for Wan 2.7 reference workflows.
|
||||
|
||||
## 1.112.0 - 2026-04-24
|
||||
|
||||
### Features
|
||||
- `baoyu-article-illustrator`: make `hand-drawn-edu` (infographic + sketch-notes + macaron) the universal fallback preset when content analysis surfaces no strong signal — warm cream paper, black hand-drawn lines, soft pastel section blocks. Elevate `sketch-notes` to primary style across infographic / flowchart / comparison / framework auto-selection; rewrite the sketch-notes style spec (macaron palette, canonical single-page layout, diagram-only rule); add matching prompt block and workflow defaults.
|
||||
- `baoyu-article-illustrator`: add `hand-drawn-edu-flow` (flowchart) and `hand-drawn-edu-compare` (comparison) presets for the same warm educational style.
|
||||
|
||||
### Breaking Changes
|
||||
- `baoyu-article-illustrator`: `hand-drawn-edu` preset now maps to `infographic` instead of `flowchart`. Users relying on the previous flowchart behavior should switch to the new `hand-drawn-edu-flow` preset.
|
||||
|
||||
### Fixes
|
||||
- `baoyu-post-to-x`: add entry point guard to `scripts/md-to-html.ts` so that importing `parseMarkdown` from `x-article.ts` no longer triggers the CLI entry point. Mirrors the same fix applied to `baoyu-post-to-weibo`.
|
||||
|
||||
## 1.111.1 - 2026-04-21
|
||||
|
||||
### Documentation
|
||||
- Add a top-level `## Confirmation Policy` section to every image-generating skill (`baoyu-infographic`, `baoyu-cover-image`, `baoyu-slide-deck`, `baoyu-image-cards`, `baoyu-xhs-images`, `baoyu-article-illustrator`) as a single source of truth: explicit skill invocation, keyword shortcuts, EXTEND.md defaults, and auto-recommendations are recommendation inputs only — they never authorize skipping the confirmation step. Opt-out requires an explicit current-request signal (`--no-confirm` / `--quick` / `--yes` / "直接生成" / equivalent).
|
||||
- `baoyu-infographic`: consolidate the scattered reminders (previously repeated across Step 5, Step 6, Default combination, Keyword Shortcuts, and the preferences docs) into a single policy section referenced from Step 4's hard gate.
|
||||
|
||||
## 1.111.0 - 2026-04-21
|
||||
|
||||
### Refactor
|
||||
- Unify image-backend resolution across all image-consuming skills (`baoyu-infographic`, `baoyu-comic`, `baoyu-cover-image`, `baoyu-image-cards`, `baoyu-article-illustrator`, `baoyu-slide-deck`, `baoyu-xhs-images`): add a single `preferred_image_backend` preference field (`auto | ask | <backend-id>`) and replace the stateless ask-once rule with a 4-step resolution (current-request override → saved preference → auto-select → ask). Runtime-native tools (Codex `imagegen`, Hermes `image_generate`) are preferred by default; existing `EXTEND.md` files without the field are treated as `auto` with no schema bump.
|
||||
- Add a top-level `## Changing Preferences` section to each image-consuming skill as a first-class surface for pinning the backend and editing common one-line preferences.
|
||||
|
||||
## 1.110.0 - 2026-04-21
|
||||
|
||||
### Features
|
||||
@@ -653,7 +712,7 @@ English | [中文](./CHANGELOG.zh.md)
|
||||
- `baoyu-format-markdown`: add reader-perspective content analysis phase — analyzes highlights, structure, and formatting issues before applying formatting
|
||||
- `baoyu-format-markdown`: restructure workflow from 8 steps to 7 with explicit do/don't formatting principles and completion report
|
||||
- `baoyu-translate`: extract Step 2 workflow mechanics to separate reference file for cleaner SKILL.md
|
||||
- `baoyu-translate`: expand trigger keywords (改成中文, 快翻, 本地化, etc.) for better skill activation
|
||||
- `baoyu-translate`: expand trigger keywords (改成中文,快翻,本地化,etc.) for better skill activation
|
||||
- `baoyu-translate`: add proactive warning for long content in quick mode
|
||||
- `baoyu-translate`: save frontmatter to `chunks/frontmatter.md` during chunking
|
||||
|
||||
|
||||
+62
-3
@@ -2,6 +2,65 @@
|
||||
|
||||
[English](./CHANGELOG.md) | 中文
|
||||
|
||||
## 1.115.2 - 2026-05-10
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:将显式请求 Codex Chrome 插件的场景作为独立浏览器控制模式处理,避免 Chrome Computer Use 或 CDP 回退流程静默接管;同时改进 X Articles 草稿创建按钮检测。
|
||||
|
||||
## 1.115.1 - 2026-05-10
|
||||
|
||||
### 修复
|
||||
- `baoyu-imagine`:将默认 MiniMax 图片 API 端点改为 `https://api.minimaxi.com`,与当前官方图片生成文档保持一致;仍可通过 `MINIMAX_BASE_URL` 覆盖为 `https://api.minimax.io`。
|
||||
- `baoyu-image-gen`:同步已废弃图片生成入口的 MiniMax 默认端点和回归测试。
|
||||
|
||||
## 1.115.0 - 2026-05-09
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-x`:新增 Chrome Computer Use 作为 Codex 环境下的首选执行模式。当 Computer Use 工具可用时,所有 X 界面操作(发帖、文章、引用、视频)均通过用户真实 Chrome 窗口完成,不再使用 CDP 脚本。CDP 脚本降级为 Computer Use 不可用或用户明确要求时的回退方案。
|
||||
|
||||
## 1.114.1 - 2026-05-08
|
||||
|
||||
### 修复
|
||||
- `baoyu-danger-gemini-web`:修复当前 Gemini Web 响应中生成图 URL 以 `https://lh3.googleusercontent.com/gg-dl/` 形式出现、但不再包含旧版生成图 marker 时的图片提取失败问题。补充该响应形态的回归测试。 (by @evilstar2016)
|
||||
|
||||
## 1.114.0 - 2026-05-05
|
||||
|
||||
### 新功能
|
||||
- `baoyu-infographic`:新增 `retro-popup-pop` 风格 —— 复古像素弹窗 × 波普信息图。画面由多个 80/90 年代桌面弹窗叠加而成(标题栏、关闭按钮、ERROR / ALERT 报错对话框、`PROBLEMS.EXE` 等复古文件窗、进度条、OK / CANCEL / FIX IT 按钮),统一粗黑描边、平涂色块,背景使用亮青蓝(#12B8DE)或复古奶油色(#F5F0E6)。与 `dense-modules` 布局尤其契合;同时升级为 `高密度信息大图` 关键词快捷方式与 `Product/Buying Guide` 内容类型的推荐风格。风格库从 21 个扩展至 22 个。
|
||||
Credit to AJ@WaytoAGI.
|
||||
|
||||
### 文档
|
||||
- `release-skills`:补充 GitHub Release 发布流程,包括从 changelog 段落提取 release notes、创建 annotated tag、执行 `gh release create/edit`,以及为已有 tag 回填历史 GitHub Releases。
|
||||
|
||||
## 1.113.0 - 2026-04-25
|
||||
|
||||
### 新功能
|
||||
- `baoyu-imagine`:新增 DashScope Wan 2.7 图像模型支持(`wan2.7-image-pro` 与 `wan2.7-image`),通过阿里云百炼官方 API 直接调用,无需经 Replicate 转发。支持文生图、图像编辑、多图融合(最多 9 张参考图),按官方文档校验 `[1:8, 8:1]` 宽高比范围,并按模式应用不同的像素预算规则。强制 `parameters.n: 1` 以匹配 baoyu-imagine 的单图保存语义,显式拒绝 `--n > 1`,避免在用户不知情的情况下产生多图计费(API 在非拼图模式下默认 `n=4`)。允许通过 `--provider dashscope --ref ...` 显式启用 Wan 2.7 参考图工作流。
|
||||
|
||||
## 1.112.0 - 2026-04-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-article-illustrator`:当内容分析未检测到明确信号时,将 `hand-drawn-edu`(infographic + sketch-notes + macaron)作为通用默认预设 —— 暖奶油色纸面背景、黑色手绘线条、柔和马卡龙色块。`sketch-notes` 升级为 infographic / flowchart / comparison / framework 自动选择的首选风格;重写 sketch-notes 风格规范(马卡龙调色板、标准单页布局、仅限示意图的规则);新增对应的 prompt 模板块和默认工作流规则。
|
||||
- `baoyu-article-illustrator`:新增 `hand-drawn-edu-flow`(flowchart)和 `hand-drawn-edu-compare`(comparison)两个预设,保持相同的温暖教育风格。
|
||||
|
||||
### 破坏性变更
|
||||
- `baoyu-article-illustrator`:`hand-drawn-edu` 预设的类型由 `flowchart` 改为 `infographic`。依赖原有流程图行为的用户请改用新增的 `hand-drawn-edu-flow` 预设。
|
||||
|
||||
### 修复
|
||||
- `baoyu-post-to-x`:为 `scripts/md-to-html.ts` 添加入口守卫,确保 `x-article.ts` 导入 `parseMarkdown` 时不再触发 CLI 入口逻辑。与 `baoyu-post-to-weibo` 此前的修复保持一致。
|
||||
|
||||
## 1.111.1 - 2026-04-21
|
||||
|
||||
### 文档
|
||||
- 为每个图片生成类技能(`baoyu-infographic`、`baoyu-cover-image`、`baoyu-slide-deck`、`baoyu-image-cards`、`baoyu-xhs-images`、`baoyu-article-illustrator`)新增顶级 `## Confirmation Policy` 章节作为单一事实源:显式调用技能、关键词快捷方式、EXTEND.md 偏好、自动推荐都只是"推荐输入",不授权跳过确认步骤。跳过确认必须由当前请求中的明确信号触发(`--no-confirm` / `--quick` / `--yes` / "直接生成" / 同义表达)。
|
||||
- `baoyu-infographic`:将原先散落在 Step 5、Step 6、Default combination、Keyword Shortcuts 及 preferences 文档中的重复提醒合并为单一策略章节,由 Step 4 的 hard gate 引用。
|
||||
|
||||
## 1.111.0 - 2026-04-21
|
||||
|
||||
### 重构
|
||||
- 统一所有图片生成类技能(`baoyu-infographic`、`baoyu-comic`、`baoyu-cover-image`、`baoyu-image-cards`、`baoyu-article-illustrator`、`baoyu-slide-deck`、`baoyu-xhs-images`)的后端选择规则:新增单一 `preferred_image_backend` 偏好字段(`auto | ask | <backend-id>`),用 4 步解析规则(当前请求覆盖 → 已保存偏好 → 自动选择 → 询问用户)替换原有的无状态询问规则。默认优先使用运行时原生工具(如 Codex `imagegen`、Hermes `image_generate`);未设置该字段的现有 `EXTEND.md` 文件视为 `auto`,无需升级 schema 版本。
|
||||
- 在每个图片技能中新增顶级 `## Changing Preferences` 章节,作为固定后端和修改常用偏好的一级入口。
|
||||
|
||||
## 1.110.0 - 2026-04-21
|
||||
|
||||
### 新功能
|
||||
@@ -609,7 +668,7 @@
|
||||
## 1.52.0 - 2026-03-06
|
||||
|
||||
### 新功能
|
||||
- `baoyu-post-to-weibo`:新增 `--video` 视频上传支持(图片+视频最多 18 个文件)
|
||||
- `baoyu-post-to-weibo`:新增 `--video` 视频上传支持(图片 + 视频最多 18 个文件)
|
||||
- `baoyu-post-to-weibo`:上传方式从剪贴板粘贴改为 `DOM.setFileInputFiles`,提升上传可靠性
|
||||
|
||||
### 修复
|
||||
@@ -1072,7 +1131,7 @@
|
||||
## 1.20.0 - 2026-01-24
|
||||
|
||||
### 新功能
|
||||
- `baoyu-cover-image`:从类型 × 风格二维系统升级为**四维系统**——新增 `--text` 维度(none 无文字、title-only 仅标题、title-subtitle 标题+副标题、text-rich 丰富文字)控制文字密度,新增 `--mood` 维度(subtle 低调、balanced 平衡、bold 醒目)控制情感强度。新增 `--quick` 标志跳过确认,直接使用自动选择。
|
||||
- `baoyu-cover-image`:从类型 × 风格二维系统升级为**四维系统**——新增 `--text` 维度(none 无文字、title-only 仅标题、title-subtitle 标题 + 副标题、text-rich 丰富文字)控制文字密度,新增 `--mood` 维度(subtle 低调、balanced 平衡、bold 醒目)控制情感强度。新增 `--quick` 标志跳过确认,直接使用自动选择。
|
||||
|
||||
### 文档
|
||||
- `baoyu-cover-image`:新增维度参考文件——`references/dimensions/text.md`(文字密度级别)和 `references/dimensions/mood.md`(氛围强度级别)。
|
||||
@@ -1092,7 +1151,7 @@
|
||||
- `baoyu-image-gen`:代码模块化——类型定义提取至 `types.ts`,provider 实现提取至 `providers/google.ts` 和 `providers/openai.ts`。
|
||||
|
||||
### 文档
|
||||
- `baoyu-comic`:改进 ohmsha 预设文档,明确默认哆啦A梦角色定义和视觉描述。
|
||||
- `baoyu-comic`:改进 ohmsha 预设文档,明确默认哆啦 A 梦角色定义和视觉描述。
|
||||
|
||||
## 1.18.3 - 2026-01-23
|
||||
|
||||
|
||||
@@ -834,7 +834,7 @@ AI SDK-based image generation using OpenAI GPT Image 2, Azure OpenAI, Google, Op
|
||||
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint | - |
|
||||
| `ZAI_BASE_URL` | Custom Z.AI endpoint | `https://api.z.ai/api/paas/v4` |
|
||||
| `BIGMODEL_BASE_URL` | Backward-compatible alias for Z.AI endpoint | - |
|
||||
| `MINIMAX_BASE_URL` | Custom MiniMax endpoint | `https://api.minimax.io` |
|
||||
| `MINIMAX_BASE_URL` | Custom MiniMax endpoint | `https://api.minimaxi.com` |
|
||||
| `REPLICATE_BASE_URL` | Custom Replicate endpoint | - |
|
||||
| `JIMENG_BASE_URL` | Custom Jimeng endpoint | `https://visual.volcengineapi.com` |
|
||||
| `JIMENG_REGION` | Jimeng region | `cn-north-1` |
|
||||
@@ -1167,7 +1167,7 @@ ZAI_IMAGE_MODEL=glm-image
|
||||
# MiniMax
|
||||
MINIMAX_API_KEY=xxx
|
||||
MINIMAX_IMAGE_MODEL=image-01
|
||||
# MINIMAX_BASE_URL=https://api.minimax.io
|
||||
# MINIMAX_BASE_URL=https://api.minimaxi.com
|
||||
|
||||
# Replicate
|
||||
REPLICATE_API_TOKEN=r8_xxx
|
||||
|
||||
+2
-2
@@ -834,7 +834,7 @@ AI 驱动的生成后端。
|
||||
| `DASHSCOPE_BASE_URL` | 自定义 DashScope 端点 | - |
|
||||
| `ZAI_BASE_URL` | 自定义 Z.AI 端点 | `https://api.z.ai/api/paas/v4` |
|
||||
| `BIGMODEL_BASE_URL` | Z.AI 端点向后兼容别名 | - |
|
||||
| `MINIMAX_BASE_URL` | 自定义 MiniMax 端点 | `https://api.minimax.io` |
|
||||
| `MINIMAX_BASE_URL` | 自定义 MiniMax 端点 | `https://api.minimaxi.com` |
|
||||
| `REPLICATE_BASE_URL` | 自定义 Replicate 端点 | - |
|
||||
| `JIMENG_BASE_URL` | 自定义即梦端点 | `https://visual.volcengineapi.com` |
|
||||
| `JIMENG_REGION` | 即梦区域 | `cn-north-1` |
|
||||
@@ -1167,7 +1167,7 @@ ZAI_IMAGE_MODEL=glm-image
|
||||
# MiniMax
|
||||
MINIMAX_API_KEY=xxx
|
||||
MINIMAX_IMAGE_MODEL=image-01
|
||||
# MINIMAX_BASE_URL=https://api.minimax.io
|
||||
# MINIMAX_BASE_URL=https://api.minimaxi.com
|
||||
|
||||
# Replicate
|
||||
REPLICATE_API_TOKEN=r8_xxx
|
||||
|
||||
@@ -4,13 +4,29 @@ Skills in this repo are loaded by multiple agent runtimes (Claude Code, Codex, H
|
||||
|
||||
## The Rule
|
||||
|
||||
When a skill needs to render an image:
|
||||
When a skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if the skill's `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
No explicit priority between runtime-native tools and repo skills — treat them equivalently and let the user decide when there's a choice. No persisted preference mechanism; the question is cheap, and the rule is stateless.
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends.
|
||||
|
||||
## The Preference Field
|
||||
|
||||
Each image-consuming skill's `EXTEND.md` carries a single `preferred_image_backend` field:
|
||||
|
||||
| Value | Meaning |
|
||||
|---|---|
|
||||
| `auto` (default) | Apply the auto-select rule — runtime-native preferred, fall back to only installed backend, ask if multiple non-native. |
|
||||
| `ask` | Always confirm the backend on every run, even when a runtime-native tool exists. |
|
||||
| `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) | Pin this backend when available; fall back to `auto` if it isn't. |
|
||||
|
||||
The field is **absent-equals-auto**: older `EXTEND.md` files without this field behave exactly as if `preferred_image_backend: auto` were set. No schema version bump is needed to introduce it.
|
||||
|
||||
## Prompt File Requirement (hard)
|
||||
|
||||
@@ -20,6 +36,8 @@ Regardless of which backend is chosen, every skill that renders images MUST writ
|
||||
|
||||
Each `SKILL.md` that renders images includes **exactly one** `## Image Generation Tools` section (near the top, after `## User Input Tools` and before the main workflow) that **inlines** this rule. Skills are self-contained and cannot link to `docs/` — each skill folder must ship the rule inside its own `SKILL.md`. See [CLAUDE.md → Skill Self-Containment](../CLAUDE.md).
|
||||
|
||||
Each skill's `references/config/preferences-schema.md` (and its `EXTEND.md` template in `first-time-setup.md`) lists `preferred_image_backend` alongside other preference fields. First-time setup does NOT ask the user about the backend — `auto` is set silently. Users who want to pin a specific backend edit `EXTEND.md` later, and each skill's `## Changing Preferences` section documents the common one-line edits.
|
||||
|
||||
Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) in this document and in SKILL.md are **examples** — agents in other runtimes apply the rule above and substitute the local equivalent. Skill-specific parameters for these backends are illustrative; runtimes without those knobs can omit them.
|
||||
|
||||
## Backend Skills Are Exempt
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-article-illustrator
|
||||
description: Analyzes article structure, identifies positions requiring visual aids, generates illustrations with Type × Style × Palette three-dimension approach. Use when user asks to "illustrate article", "add images", "generate images for article", or "为文章配图".
|
||||
version: 1.57.0
|
||||
version: 1.58.0
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-article-illustrator
|
||||
@@ -23,16 +23,31 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The backend receives the prompt file (or its content); the file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) above are examples — substitute the local equivalents under the same rule.
|
||||
|
||||
## Confirmation Policy
|
||||
|
||||
Default behavior: **confirm before generation**.
|
||||
|
||||
- Treat explicit skill invocation, a file path, matched signals/presets, and `EXTEND.md` defaults as **recommendation inputs only**. None of them authorizes skipping confirmation.
|
||||
- Do **not** start Step 4 or later until the user completes Step 3.
|
||||
- Skip confirmation only when the current request explicitly says to do so, for example: "直接生成", "不用确认", "跳过确认", "按默认出图", or equivalent wording.
|
||||
- If confirmation is skipped explicitly, state the assumed type / density / style / palette / language / backend in the next user-facing update before generating.
|
||||
|
||||
## Reference Images
|
||||
|
||||
Users may supply reference images via `--ref <files...>` or by providing file paths / pasting images in conversation. Refs guide style, palette, composition, or subject for specific illustrations.
|
||||
@@ -111,6 +126,8 @@ Full procedures: [references/workflow.md](references/workflow.md#step-2-setup--a
|
||||
|
||||
### Step 3: Confirm Settings ⚠️
|
||||
|
||||
**Hard gate**: this step is mandatory per the [Confirmation Policy](#confirmation-policy) — Steps 4+ cannot start until the user confirms here (or explicitly opts out with "直接生成" / equivalent wording in the current request).
|
||||
|
||||
**ONE AskUserQuestion, max 4 Qs. Q1-Q2 REQUIRED. Q3 required unless preset chosen.**
|
||||
|
||||
| Q | Options |
|
||||
@@ -207,3 +224,17 @@ When input is **pasted content** (no file path), always uses `illustrations/{top
|
||||
| [references/style-presets.md](references/style-presets.md) | Preset shortcuts (type + style + palette) |
|
||||
| [references/prompt-construction.md](references/prompt-construction.md) | Prompt templates |
|
||||
| [references/config/first-time-setup.md](references/config/first-time-setup.md) | First-time setup |
|
||||
|
||||
## Changing Preferences
|
||||
|
||||
EXTEND.md lives at the first matching path listed in Step 1.5. Three ways to change it:
|
||||
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: `references/config/preferences-schema.md`.
|
||||
- **Reconfigure interactively** — delete EXTEND.md (or ask "reconfigure baoyu-article-illustrator preferences" / "重新配置"). The next run re-triggers first-time setup.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `preferred_type: infographic`, `preferred_style: notion`, `preferred_palette: macaron`, `language: zh`.
|
||||
- `default_output_dir: imgs-subdir` — where to write generated images relative to the article.
|
||||
|
||||
@@ -61,8 +61,10 @@ Position defaults to bottom-right.
|
||||
header: "Style"
|
||||
question: "Default illustration style preference? Or type another style name or your custom style"
|
||||
options:
|
||||
- label: "None (Recommended)"
|
||||
description: "Auto-select based on content analysis"
|
||||
- label: "sketch-notes (Recommended)"
|
||||
description: "Warm cream paper, black hand-drawn lines, soft pastel blocks — educational infographic feel. Great default for most articles."
|
||||
- label: "None"
|
||||
description: "Auto-select based on content analysis (falls back to sketch-notes when no strong signal)"
|
||||
- label: "notion"
|
||||
description: "Minimalist hand-drawn line art"
|
||||
- label: "warm"
|
||||
@@ -126,13 +128,13 @@ preferred_style:
|
||||
description: ""
|
||||
default_output_dir: imgs-subdir # same-dir | imgs-subdir | illustrations-subdir | independent
|
||||
language: null
|
||||
preferred_image_backend: auto
|
||||
custom_styles: []
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend: auto` is the baked-in default — first-time setup does not ask about it. The `## Image Generation Tools` rule in SKILL.md then picks the runtime-native tool (Codex `imagegen`, Hermes `image_generate`, etc.) when available, and falls back to installed backends.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
- Delete EXTEND.md to trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `config/preferences-schema.md`
|
||||
See the `## Changing Preferences` section in `SKILL.md` for the canonical list of common edits (pin backend, change defaults, retrigger setup). Full schema: `preferences-schema.md`.
|
||||
|
||||
@@ -26,6 +26,8 @@ language: null # zh|en|ja|ko|auto
|
||||
|
||||
default_output_dir: null # same-dir|illustrations-subdir|independent
|
||||
|
||||
preferred_image_backend: auto # auto|ask|<backend-id>
|
||||
|
||||
custom_styles:
|
||||
- name: my-style
|
||||
description: "Style description"
|
||||
@@ -52,6 +54,7 @@ custom_styles:
|
||||
| `preferred_palette` | string | null | Palette override (macaron, warm, neon, or null) |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `default_output_dir` | enum | null | Output directory preference (null = ask each time) |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. Resolution logic is documented in `SKILL.md`'s `## Image Generation Tools` section. |
|
||||
| `custom_styles` | array | [] | User-defined styles |
|
||||
|
||||
## Position Options
|
||||
@@ -113,6 +116,8 @@ preferred_style:
|
||||
|
||||
language: zh
|
||||
|
||||
preferred_image_backend: codex-imagegen
|
||||
|
||||
custom_styles:
|
||||
- name: corporate
|
||||
description: "Professional B2B style"
|
||||
|
||||
@@ -139,6 +139,39 @@ STYLE: [style characteristics]
|
||||
ASPECT: 16:9
|
||||
```
|
||||
|
||||
**Infographic + sketch-notes + macaron palette** (default / `hand-drawn-edu` preset):
|
||||
```
|
||||
Single-page hand-drawn educational infographic in a clean presentation style.
|
||||
Warm cream paper background, black hand-drawn lines with slight wobble, soft
|
||||
pastel color blocks. Feels simple, friendly, and easy to understand at a glance.
|
||||
Diagram-style visuals ONLY — no realistic or photographic images.
|
||||
|
||||
PALETTE: macaron — soft pastel blocks on warm cream
|
||||
COLORS: Warm Cream background (#F5F0E8); Black (#1A1A1A) for ALL lines, text,
|
||||
arrows, and doodles; section fills in Light Blue (#A8D8EA), Mint Green
|
||||
(#B5E5CF), Lavender (#D5C6E0), Peach (#FFD5C2); Coral Red (#E8655A)
|
||||
sparingly for one or two emphasis points only.
|
||||
|
||||
LAYOUT (top → bottom):
|
||||
- TOP: Bold hand-lettered title, oversized, slightly wobbly, with an optional
|
||||
decorative underline or small doodle.
|
||||
- MIDDLE: 2–6 rounded-rectangle info boxes arranged in a clean grid, row, or
|
||||
radial pattern. Each box = one section, one pastel fill color, one
|
||||
simple icon or sketchy cartoon element, one short keyword/phrase.
|
||||
Hand-drawn arrows connect related zones.
|
||||
- BOTTOM: One short hand-lettered takeaway sentence summarizing the main idea.
|
||||
|
||||
ELEMENTS: Rounded info boxes with clear sectioning, wavy/straight hand-drawn
|
||||
arrows with small inline labels, simple icons and sketchy cartoon
|
||||
elements (stick figures, tools, objects), small doodle decorations
|
||||
(stars, sparkles, underlines, dots, asterisks) used sparingly.
|
||||
|
||||
STYLE: Minimal, well-organized, airy. Color fills don't completely fill
|
||||
outlines (slight "hand-painted" overshoot). ALL text hand-lettered —
|
||||
no computer fonts. Short labels and keywords only, never long
|
||||
paragraphs. Generous white space between sections.
|
||||
```
|
||||
|
||||
**Infographic + vector-illustration**:
|
||||
```
|
||||
Flat vector illustration infographic. Clean black outlines on all elements.
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
`--preset X` expands to a type + style + optional palette combination. Users can override any dimension.
|
||||
|
||||
## Default Preset
|
||||
|
||||
When content analysis surfaces no strong signal (generic knowledge article, mixed-topic post, no clear data/comparison/narrative cue), recommend **`hand-drawn-edu`** as the primary option in Step 3 Q1. It is the warm, friendly educational-infographic default — safe for most articles and universally readable.
|
||||
|
||||
## By Category
|
||||
|
||||
### Technical & Engineering
|
||||
@@ -23,7 +27,9 @@
|
||||
| `process-flow` | `flowchart` | `notion` | — | Workflow documentation, onboarding flows |
|
||||
| `warm-knowledge` | `infographic` | `vector-illustration` | `warm` | Product showcases, team intros, feature cards, brand content |
|
||||
| `edu-visual` | `infographic` | `vector-illustration` | `macaron` | Knowledge summaries, concept explainers, educational articles |
|
||||
| `hand-drawn-edu` | `flowchart` | `sketch-notes` | `macaron` | Hand-drawn educational diagrams, process explainers, onboarding visuals |
|
||||
| `hand-drawn-edu` | `infographic` | `sketch-notes` | `macaron` | **Default preset.** Hand-drawn educational infographic — warm cream paper, black lines, pastel blocks. Great for single-page explainers, concept summaries, onboarding, general knowledge articles |
|
||||
| `hand-drawn-edu-flow` | `flowchart` | `sketch-notes` | `macaron` | Hand-drawn process explainer — step-by-step workflow in the same warm educational style |
|
||||
| `hand-drawn-edu-compare` | `comparison` | `sketch-notes` | `macaron` | Hand-drawn side-by-side comparison in the warm educational style |
|
||||
| `ink-notes-compare` | `comparison` | `ink-notes` | `mono-ink` | Before/After essays, Traditional vs New, OS-style comparisons, mindset-shift narratives |
|
||||
| `ink-notes-flow` | `flowchart` | `ink-notes` | `mono-ink` | Professional process explainers, workforce pipelines, hand-drawn technical walkthroughs |
|
||||
| `ink-notes-framework` | `framework` | `ink-notes` | `mono-ink` | System analogies, command-center diagrams, architecture-as-metaphor, tech manifestos |
|
||||
@@ -59,18 +65,19 @@ Use this table during Step 3 to recommend presets based on Step 2 content analys
|
||||
|
||||
| Content Type (Step 2) | Primary Preset | Alternatives |
|
||||
|------------------------|----------------|--------------|
|
||||
| Technical | `tech-explainer` | `system-design`, `architecture` |
|
||||
| Tutorial | `tutorial` | `process-flow`, `knowledge-base`, `edu-visual` |
|
||||
| **General / No strong signal** | `hand-drawn-edu` | `edu-visual`, `knowledge-base` |
|
||||
| Education / Knowledge | `hand-drawn-edu` | `edu-visual`, `knowledge-base`, `tutorial` |
|
||||
| Tutorial | `hand-drawn-edu-flow` | `tutorial`, `process-flow`, `hand-drawn-edu` |
|
||||
| SaaS / Product | `hand-drawn-edu` | `saas-guide`, `knowledge-base`, `process-flow`, `warm-knowledge` |
|
||||
| Technical | `tech-explainer` | `system-design`, `architecture`, `hand-drawn-edu` |
|
||||
| Methodology / Framework | `system-design` | `architecture`, `process-flow` |
|
||||
| Data / Metrics | `data-report` | `versus`, `tech-explainer` |
|
||||
| Comparison / Review | `versus` | `business-compare`, `editorial-poster`, `ink-notes-compare` |
|
||||
| Comparison / Review | `versus` | `business-compare`, `hand-drawn-edu-compare`, `editorial-poster`, `ink-notes-compare` |
|
||||
| Manifesto / Mindset shift / Professional visual note | `ink-notes-compare` | `ink-notes-framework`, `ink-notes-flow` |
|
||||
| Narrative / Personal | `storytelling` | `lifestyle`, `evolution` |
|
||||
| Opinion / Editorial | `opinion-piece` | `cinematic`, `editorial-poster` |
|
||||
| Historical / Timeline | `history` | `evolution` |
|
||||
| Academic / Research | `science-paper` | `tech-explainer`, `data-report` |
|
||||
| SaaS / Product | `saas-guide` | `knowledge-base`, `process-flow`, `warm-knowledge` |
|
||||
| Education / Knowledge | `edu-visual` | `knowledge-base`, `tutorial`, `hand-drawn-edu` |
|
||||
|
||||
## Override Examples
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@ Simplified style tier for quick selection:
|
||||
|
||||
| Core Style | Maps To | Best For |
|
||||
|------------|---------|----------|
|
||||
| `hand-drawn` | sketch-notes | **Default.** Warm cream paper, black hand-drawn lines, pastel blocks — educational infographics, concept explainers, onboarding, general knowledge articles |
|
||||
| `vector` | vector-illustration | Knowledge articles, tutorials, tech content |
|
||||
| `minimal-flat` | notion | General, knowledge sharing, SaaS |
|
||||
| `sci-fi` | blueprint | AI, frontier tech, system design |
|
||||
| `hand-drawn` | sketch/warm | Relaxed, reflective, casual content |
|
||||
| `editorial` | editorial | Processes, data, journalism |
|
||||
| `scene` | warm/watercolor | Narratives, emotional, lifestyle |
|
||||
| `poster` | screen-print | Opinion, editorial, cultural, cinematic |
|
||||
|
||||
Use Core Styles for most cases. See full Style Gallery below for granular control.
|
||||
Use Core Styles for most cases. **When no strong content signal is detected, default to `hand-drawn` (→ sketch-notes).** See full Style Gallery below for granular control.
|
||||
|
||||
---
|
||||
|
||||
@@ -50,42 +50,45 @@ Full specifications: `references/styles/<style>.md`
|
||||
|
||||
## Type × Style Compatibility Matrix
|
||||
|
||||
| | vector-illustration | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific | screen-print |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| infographic | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| scene | ✓ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ | ✓✓ |
|
||||
| flowchart | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ |
|
||||
| comparison | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| framework | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ |
|
||||
| timeline | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| | sketch-notes | vector-illustration | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific | screen-print |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| infographic | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
|
||||
| scene | ✗ | ✓ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ | ✓✓ |
|
||||
| flowchart | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ |
|
||||
| comparison | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
| framework | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ |
|
||||
| timeline | ✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ |
|
||||
|
||||
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
|
||||
|
||||
## Auto Selection by Type
|
||||
|
||||
When no content signal matches strongly, `sketch-notes` is the default primary for every diagrammatic type. Only override with another primary when the content analysis in Step 2 surfaces a clear signal (technical/data/narrative/opinion).
|
||||
|
||||
| Type | Primary Style | Secondary Styles |
|
||||
|------|---------------|------------------|
|
||||
| infographic | vector-illustration | notion, blueprint, editorial |
|
||||
| infographic | sketch-notes | vector-illustration, notion, blueprint, editorial |
|
||||
| scene | warm | watercolor, elegant |
|
||||
| flowchart | vector-illustration | notion, blueprint |
|
||||
| comparison | vector-illustration | notion, elegant |
|
||||
| framework | blueprint | vector-illustration, notion |
|
||||
| timeline | elegant | warm, editorial |
|
||||
| flowchart | sketch-notes | vector-illustration, notion, blueprint |
|
||||
| comparison | sketch-notes | vector-illustration, notion, elegant |
|
||||
| framework | sketch-notes | blueprint, vector-illustration, notion |
|
||||
| timeline | elegant | sketch-notes, warm, editorial |
|
||||
|
||||
## Auto Selection by Content Signals
|
||||
|
||||
| Content Signals | Recommended Type | Recommended Style |
|
||||
|-----------------|------------------|-------------------|
|
||||
| **(no strong signal / general article)** | **infographic** | **sketch-notes** |
|
||||
| Knowledge, concept, tutorial, learning, guide, onboarding | infographic | sketch-notes, vector-illustration, notion |
|
||||
| Productivity, SaaS, tool, app, software | infographic | sketch-notes, notion, vector-illustration |
|
||||
| How-to, steps, workflow, process, tutorial | flowchart | sketch-notes, vector-illustration, notion |
|
||||
| API, metrics, data, comparison, numbers | infographic | blueprint, vector-illustration |
|
||||
| Knowledge, concept, tutorial, learning, guide | infographic | vector-illustration, notion |
|
||||
| Tech, AI, programming, development, code | infographic | vector-illustration, blueprint |
|
||||
| How-to, steps, workflow, process, tutorial | flowchart | vector-illustration, notion |
|
||||
| Framework, model, architecture, principles | framework | blueprint, vector-illustration |
|
||||
| vs, pros/cons, before/after, alternatives | comparison | vector-illustration, notion |
|
||||
| Tech, AI, programming, development, code | infographic | vector-illustration, blueprint, sketch-notes |
|
||||
| Framework, model, architecture, principles | framework | blueprint, vector-illustration, sketch-notes |
|
||||
| vs, pros/cons, before/after, alternatives | comparison | vector-illustration, notion, sketch-notes |
|
||||
| Manifesto, mindset shift, workforce, OS, whiteboard, professional visual note | comparison / framework | ink-notes |
|
||||
| Story, emotion, journey, experience, personal | scene | warm, watercolor |
|
||||
| History, timeline, progress, evolution | timeline | elegant, warm |
|
||||
| Productivity, SaaS, tool, app, software | infographic | notion, vector-illustration |
|
||||
| Business, professional, strategy, corporate | framework | elegant |
|
||||
| Opinion, editorial, culture, philosophy, cinematic, dramatic, poster | scene | screen-print |
|
||||
| Biology, chemistry, medical, scientific | infographic | scientific |
|
||||
@@ -93,6 +96,15 @@ Full specifications: `references/styles/<style>.md`
|
||||
|
||||
## Style Characteristics by Type
|
||||
|
||||
### infographic + sketch-notes (default)
|
||||
- Warm cream paper background, black hand-drawn lines with slight wobble
|
||||
- 2–6 rounded pastel info boxes (light blue / mint / lavender / peach)
|
||||
- Bold hand-lettered title at the top
|
||||
- Short keyword labels, simple icons, small doodles (stars, underlines, sparkles)
|
||||
- One-line hand-lettered takeaway sentence at the bottom
|
||||
- Airy, minimal, diagram-style — never realistic
|
||||
- Perfect for single-page educational explainers and concept summaries
|
||||
|
||||
### infographic + vector-illustration
|
||||
- Clean flat vector shapes, bold geometric forms
|
||||
- Vibrant but harmonious color palette
|
||||
|
||||
@@ -1,56 +1,91 @@
|
||||
# sketch-notes
|
||||
|
||||
Soft hand-drawn illustration style with warm, educational feel
|
||||
Hand-drawn educational infographic style with warm cream paper, black hand-drawn lines, and soft pastel section blocks. Optimized for single-page visual explainers.
|
||||
|
||||
## Design Aesthetic
|
||||
|
||||
Hand-drawn feel with soft, relaxed brush strokes. Fresh, refined style with minimalist editorial approach. Emphasis on precision, clarity and intelligent elegance while prioritizing warmth, approachability and friendliness.
|
||||
Hand-drawn educational infographic in a clean presentation style. Feels like a visual explainer slide: simple, friendly, and easy to understand at a glance. Bold handwritten-style title at the top, clearly sectioned content in the middle with rounded boxes and small doodles, and one short takeaway sentence at the bottom. Neat, airy, and visually similar to a hand-drawn concept diagram — never realistic or photographic.
|
||||
|
||||
## Background
|
||||
|
||||
- Color: Warm Off-White (#FAF8F0)
|
||||
- Texture: Subtle paper grain, warm tone
|
||||
- Color: Warm Cream Paper (#F5F0E8) — preferred; fallback Warm Off-White (#FAF8F0)
|
||||
- Texture: Subtle warm paper grain, matte finish, no gloss
|
||||
|
||||
## Color Palette
|
||||
|
||||
Default sketch-notes palette is the **macaron** pastel set. Lines are always black; pastel blocks are used only as rounded card fills for information sections.
|
||||
|
||||
| Role | Color | Hex | Usage |
|
||||
|------|-------|-----|-------|
|
||||
| Background | Warm Off-White | #FAF8F0 | Primary background |
|
||||
| Primary Text | Deep Charcoal | #2C3E50 | Main elements |
|
||||
| Alt Text | Deep Brown | #4A4A4A | Secondary elements |
|
||||
| Accent 1 | Soft Orange | #F4A261 | Highlights, emphasis |
|
||||
| Accent 2 | Mustard Yellow | #E9C46A | Secondary highlights |
|
||||
| Accent 3 | Sage Green | #87A96B | Nature, growth concepts |
|
||||
| Accent 4 | Light Blue | #7EC8E3 | Tech, digital elements |
|
||||
| Accent 5 | Red Brown | #A0522D | Earthy elements |
|
||||
| Background | Warm Cream | #F5F0E8 | Paper background |
|
||||
| Primary Ink | Black | #1A1A1A | ALL outlines, text, arrows, doodles |
|
||||
| Block Blue | Light Blue | #A8D8EA | Info block fill (cool / tech) |
|
||||
| Block Mint | Mint Green | #B5E5CF | Info block fill (growth / positive) |
|
||||
| Block Lavender | Lavender | #D5C6E0 | Info block fill (concept / abstract) |
|
||||
| Block Peach | Peach | #FFD5C2 | Info block fill (warm / human) |
|
||||
| Accent | Coral Red | #E8655A | One or two emphasis points only |
|
||||
| Muted Text | Warm Gray | #6B6B6B | Small annotations |
|
||||
|
||||
Use **4 pastel block colors max** per image, one color per section. Black ink does all the structural line work.
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- Connection lines with hand-drawn wavy feel
|
||||
- Conceptual abstract icons illustrating ideas
|
||||
- Color fills don't completely fill outlines (hand-painted feel)
|
||||
- Simple geometric shapes with rounded corners
|
||||
- Arrows and pointers with sketchy style
|
||||
- Doodle decorations: stars, spirals, underlines
|
||||
- Bold hand-lettered title at the top (oversized, slightly wobbly)
|
||||
- Rounded-rectangle info boxes with clear sectioning (2–6 zones)
|
||||
- Short keyword labels inside boxes — never long paragraphs
|
||||
- Simple icons and sketchy cartoon elements (stick figures, tools, objects) to explain each idea
|
||||
- Hand-drawn arrows (straight, curved, or wavy) connecting related zones
|
||||
- Small doodle decorations: stars, sparkles, underlines, dots, asterisks — used sparingly for emphasis
|
||||
- Single-line hand-lettered takeaway sentence at the bottom
|
||||
- Color fills do not completely fill outlines (slight "hand-painted" overshoot/undershoot)
|
||||
- Generous white space between sections — airy, never crowded
|
||||
|
||||
## Layout Guidelines
|
||||
|
||||
Canonical single-page layout (16:9 or 4:3):
|
||||
|
||||
1. **Top (10–15%)** — Bold hand-lettered title, optionally with a small decorative underline or doodle.
|
||||
2. **Middle (70–80%)** — 2–6 rounded pastel info boxes arranged in a clear grid, row, or radial pattern. Each box = one section, one color, one icon, one keyword/phrase.
|
||||
3. **Bottom (10–15%)** — One short hand-lettered takeaway sentence summarizing the core insight.
|
||||
|
||||
Keep margins generous. Aim for breathing room around every element.
|
||||
|
||||
## Style Rules
|
||||
|
||||
### Do
|
||||
|
||||
- Keep layouts open and well-structured
|
||||
- Emphasize information hierarchy
|
||||
- Use hand-drawn quality for all elements
|
||||
- Allow imperfection (slight wobbles add character)
|
||||
- Layer elements with subtle overlaps
|
||||
- Use warm cream paper background (no pure white)
|
||||
- Use black hand-drawn lines for ALL structural elements
|
||||
- Use soft pastel blocks (blue / mint / lavender / peach) for section fills
|
||||
- Keep text to short keywords and phrases only
|
||||
- Include a bold handwritten title at the top
|
||||
- Include a short takeaway sentence at the bottom
|
||||
- Use diagram-style visuals (icons, doodles, simple shapes)
|
||||
- Allow slight wobble — hand-drawn imperfection is the point
|
||||
- Maintain clear sectioning with rounded boxes
|
||||
|
||||
### Don't
|
||||
|
||||
- Use perfect geometric shapes
|
||||
- Create photorealistic elements
|
||||
- Overcrowd with too many elements
|
||||
- Use pure white backgrounds
|
||||
- Make it look computer-generated
|
||||
- Use pure white backgrounds (that's `ink-notes`' territory)
|
||||
- Render realistic or photographic images — this style is diagram-only
|
||||
- Fill zones with gradients, shadows, or digital effects
|
||||
- Use long paragraphs of text — keywords only
|
||||
- Use computer-generated / sans-serif body fonts — ALL text must be hand-lettered
|
||||
- Use more than 4 pastel block colors per image
|
||||
- Overcrowd the canvas — keep it airy and minimal
|
||||
- Use perfect geometric shapes — preserve the hand-drawn wobble
|
||||
|
||||
## Type Compatibility
|
||||
|
||||
| Type | Rating | Notes |
|
||||
|------|--------|-------|
|
||||
| infographic | ✓✓ | **Best fit** — single-page visual explainers, concept summaries, educational slides |
|
||||
| framework | ✓✓ | Labeled zones and connectors render well |
|
||||
| flowchart | ✓✓ | Rounded step boxes with wavy arrows |
|
||||
| comparison | ✓✓ | Two pastel blocks side by side; prefer `ink-notes` for strict Before/After contrasts |
|
||||
| timeline | ✓ | Hand-drawn horizontal arrow with milestone cards |
|
||||
| scene | ✗ | Not recommended — too diagrammatic |
|
||||
|
||||
## Best For
|
||||
|
||||
Educational content, knowledge sharing, technical explanations, tutorials, onboarding materials, friendly articles
|
||||
Educational content, knowledge sharing, concept explainers, tutorials, onboarding materials, product walkthroughs, single-page visual summaries, "how things work" posts, friendly technical articles
|
||||
|
||||
@@ -176,6 +176,8 @@ Based on Step 2 content analysis, recommend a preset first (sets both type & sty
|
||||
- [Alternative preset] — [brief]
|
||||
- Or choose type manually: infographic / scene / flowchart / comparison / framework / timeline / mixed
|
||||
|
||||
**Default**: if Step 2 found no strong content signal, the recommended preset MUST be `hand-drawn-edu` (infographic + sketch-notes + macaron — warm cream paper, black hand-drawn lines, soft pastel blocks). This is the universal fallback.
|
||||
|
||||
**If user picks a preset → skip Q3** (type & style both resolved).
|
||||
**If user picks a type → Q3 is REQUIRED.**
|
||||
|
||||
@@ -203,13 +205,15 @@ If no `preferred_style` (present Core Styles first):
|
||||
|
||||
| Core Style | Maps To | Best For |
|
||||
|------------|---------|----------|
|
||||
| `hand-drawn` | sketch-notes | **Default.** Warm cream paper, black hand-drawn lines, pastel blocks — educational infographics, concept explainers, onboarding, general knowledge articles |
|
||||
| `minimal-flat` | notion | General, knowledge sharing, SaaS |
|
||||
| `sci-fi` | blueprint | AI, frontier tech, system design |
|
||||
| `hand-drawn` | sketch/warm | Relaxed, reflective, casual |
|
||||
| `editorial` | editorial | Processes, data, journalism |
|
||||
| `scene` | warm/watercolor | Narratives, emotional, lifestyle |
|
||||
| `poster` | screen-print | Opinion, editorial, cultural, cinematic |
|
||||
|
||||
**Default recommendation**: when Step 2 surfaces no strong content signal, recommend **`hand-drawn-edu`** preset (→ infographic + sketch-notes + macaron) as the primary option in Q1. When the user picks a type manually without a preferred_style, recommend `sketch-notes` first in Q3.
|
||||
|
||||
Style selection based on Type × Style compatibility matrix (styles.md).
|
||||
**In Step 5.1**, read `styles/<style>.md` for visual elements and rendering rules.
|
||||
|
||||
|
||||
@@ -27,11 +27,17 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The backend receives the prompt file (or its content); the file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
@@ -295,3 +301,16 @@ If EXTEND.md is not found, first-time setup is **blocking** — complete it befo
|
||||
- **Step 7.1 character sheet** - recommended for multi-page comics, optional for simple presets
|
||||
- **Step 7.2 character reference** - use `--ref` if sheet exists; compress/convert on failure; fall back to prompt-only
|
||||
- Watermark/language configured once in EXTEND.md
|
||||
|
||||
## Changing Preferences
|
||||
|
||||
EXTEND.md lives at `.baoyu-skills/baoyu-comic/EXTEND.md` (project) or `~/.baoyu-skills/baoyu-comic/EXTEND.md` (user). Three ways to change it:
|
||||
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: `references/config/preferences-schema.md`.
|
||||
- **Reconfigure interactively** — delete EXTEND.md (or ask "reconfigure baoyu-comic preferences" / "重新配置"). The next run re-triggers first-time setup.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `watermark.enabled: true`, `preferred_art`, `preferred_tone`, `preferred_layout`, `language` — shift the auto-selection defaults and cosmetic choices.
|
||||
|
||||
@@ -142,13 +142,13 @@ preferred_tone: [selected tone or null]
|
||||
preferred_layout: null
|
||||
preferred_aspect: null
|
||||
language: [selected or null]
|
||||
preferred_image_backend: auto
|
||||
character_presets: []
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend: auto` is the baked-in default — first-time setup does not ask about it. The `## Image Generation Tools` rule in SKILL.md then picks the runtime-native tool (Codex `imagegen`, Hermes `image_generate`, etc.) when available, and falls back to installed backends.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
- Delete EXTEND.md to trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `config/preferences-schema.md`
|
||||
See the `## Changing Preferences` section in `SKILL.md` for the canonical list of common edits (pin backend, change defaults, retrigger setup). Full schema: `config/preferences-schema.md`.
|
||||
|
||||
@@ -23,6 +23,8 @@ preferred_aspect: null # 3:4|4:3|16:9
|
||||
|
||||
language: null # zh|en|ja|ko|auto
|
||||
|
||||
preferred_image_backend: auto # auto|ask|<backend-id>
|
||||
|
||||
character_presets:
|
||||
- name: my-characters
|
||||
roles:
|
||||
@@ -46,6 +48,7 @@ character_presets:
|
||||
| `preferred_layout` | string | null | Layout preference or null |
|
||||
| `preferred_aspect` | string | null | Aspect ratio (3:4, 4:3, 16:9) |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. Resolution logic is documented in `SKILL.md`'s `## Image Generation Tools` section. |
|
||||
| `character_presets` | array | [] | Preset character roles for styles like ohmsha |
|
||||
|
||||
## Art Style Options
|
||||
@@ -122,6 +125,8 @@ preferred_aspect: "3:4"
|
||||
|
||||
language: zh
|
||||
|
||||
preferred_image_backend: codex-imagegen
|
||||
|
||||
character_presets:
|
||||
- name: tech-tutorial
|
||||
roles:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-cover-image
|
||||
description: Generates article cover images with 5 dimensions (type, palette, rendering, text, mood) combining 11 color palettes and 7 rendering styles. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", or "make cover".
|
||||
version: 1.56.1
|
||||
version: 1.56.2
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-cover-image
|
||||
@@ -23,16 +23,31 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The backend receives the prompt file (or its content); the file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) above are examples — substitute the local equivalents under the same rule.
|
||||
|
||||
## Confirmation Policy
|
||||
|
||||
Default behavior: **confirm before generation**.
|
||||
|
||||
- Treat explicit skill invocation, a file path, matched keywords/presets, `EXTEND.md` defaults, and any documented auto-selection as **recommendation inputs only**. None of them authorizes skipping confirmation.
|
||||
- Do **not** start Step 3 or Step 4 until the user confirms the dimensions / aspect / language / backend choices.
|
||||
- Skip confirmation only when the current request explicitly says to do so, for example: `--quick`, "直接生成", "不用确认", "跳过确认", "按默认出图", or equivalent wording. `quick_mode: true` in `EXTEND.md` counts as a standing explicit opt-out — set it only when you want every run to skip Step 2.
|
||||
- If confirmation is skipped explicitly, state the assumed dimensions / aspect / language / backend in the next user-facing update before generating.
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
@@ -164,6 +179,8 @@ See [reference-images.md](references/workflow/reference-images.md) for full deci
|
||||
|
||||
### Step 2: Confirm Options ⚠️
|
||||
|
||||
**Hard gate**: this step is mandatory per the [Confirmation Policy](#confirmation-policy) — Steps 3–4 cannot start until the user confirms here (or explicitly opts out with `--quick` / `quick_mode: true` / equivalent wording in the current request).
|
||||
|
||||
**MUST use `AskUserQuestion` tool** to present options as interactive selection — NOT plain text tables. Present up to 4 questions in a single `AskUserQuestion` call (Type, Palette, Rendering, Font + Settings). Each question shows the recommended option first with reason, followed by alternatives.
|
||||
|
||||
Full confirmation flow and question format: [references/workflow/confirm-options.md](references/workflow/confirm-options.md)
|
||||
@@ -228,13 +245,18 @@ Files:
|
||||
- **Characters**: Simplified silhouettes; NO realistic humans
|
||||
- **Title**: Use exact title from user/source; never invent
|
||||
|
||||
## Extension Support
|
||||
## Changing Preferences
|
||||
|
||||
Custom configurations via EXTEND.md. See **Step 0** for paths.
|
||||
EXTEND.md lives at the path noted in **Step 0**. Three ways to change it:
|
||||
|
||||
Supports: Watermark | Preferred dimensions | Default aspect/output | Quick mode | Custom palettes | Language
|
||||
|
||||
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: [references/config/preferences-schema.md](references/config/preferences-schema.md).
|
||||
- **Reconfigure interactively** — delete EXTEND.md (or ask "reconfigure baoyu-cover-image preferences" / "重新配置"). The next run re-triggers first-time setup.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `watermark.enabled: true`, `preferred_type`, `preferred_palette`, `preferred_rendering`, `default_aspect`, `quick_mode: true`, `language` — shift the auto-selection defaults and confirmation flow.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -188,15 +188,15 @@ default_aspect: [16:9/2.35:1/1:1/3:4]
|
||||
default_output_dir: [independent/same-dir/imgs-subdir]
|
||||
quick_mode: [true/false]
|
||||
language: null
|
||||
preferred_image_backend: auto
|
||||
custom_palettes: []
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend: auto` is the baked-in default — first-time setup does not ask about it. The `## Image Generation Tools` rule in SKILL.md then picks the runtime-native tool (Codex `imagegen`, Hermes `image_generate`, etc.) when available, and falls back to installed backends.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
- Delete EXTEND.md to trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `preferences-schema.md`
|
||||
See the `## Changing Preferences` section in `SKILL.md` for the canonical list of common edits (pin backend, change defaults, retrigger setup). Full schema: `preferences-schema.md`.
|
||||
|
||||
**EXTEND.md Supports**: Watermark | Preferred type | Preferred palette | Preferred rendering | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Custom palette definitions | Language preference
|
||||
**EXTEND.md Supports**: Watermark | Preferred type | Preferred palette | Preferred rendering | Preferred text | Preferred mood | Default aspect ratio | Default output directory | Quick mode | Image backend preference | Custom palette definitions | Language preference
|
||||
|
||||
@@ -32,6 +32,8 @@ quick_mode: false # Skip confirmation when true
|
||||
|
||||
language: null # zh|en|ja|ko|auto (null = auto-detect)
|
||||
|
||||
preferred_image_backend: auto # auto|ask|<backend-id>
|
||||
|
||||
custom_palettes:
|
||||
- name: my-palette
|
||||
description: "Palette description"
|
||||
@@ -60,6 +62,7 @@ custom_palettes:
|
||||
| `default_aspect` | string | "2.35:1" | Default aspect ratio |
|
||||
| `quick_mode` | bool | false | Skip confirmation step |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. Resolution logic is documented in `SKILL.md`'s `## Image Generation Tools` section. |
|
||||
| `custom_palettes` | array | [] | User-defined palettes |
|
||||
|
||||
## Type Options
|
||||
@@ -187,6 +190,8 @@ quick_mode: true
|
||||
|
||||
language: en
|
||||
|
||||
preferred_image_backend: codex-imagegen
|
||||
|
||||
custom_palettes:
|
||||
- name: corporate-tech
|
||||
description: "Professional B2B tech palette"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { collect_generated_image_urls_from_response_parts } from "./client.ts";
|
||||
|
||||
test("response part fallback finds generated images when legacy generated markers are absent", () => {
|
||||
const generatedUrl = "https://lh3.googleusercontent.com/gg-dl/example-generated-image";
|
||||
const initialCandidate = ["rcid-1", ["image generated successfully"]];
|
||||
const imageCandidate = [
|
||||
"rcid-1",
|
||||
["image generated successfully"],
|
||||
{ nestedPayload: [{ media: generatedUrl }] },
|
||||
];
|
||||
const responseJson = [
|
||||
["wrb.fr", null, JSON.stringify([null, [], null, null, [initialCandidate]])],
|
||||
["wrb.fr", null, JSON.stringify([null, [], null, null, [imageCandidate]])],
|
||||
];
|
||||
|
||||
assert.equal(initialCandidate[12], undefined);
|
||||
assert.equal(
|
||||
/http:\/\/googleusercontent\.com\/image_generation_content\/\d+/.test(String(initialCandidate[1]?.[0])),
|
||||
false,
|
||||
);
|
||||
assert.deepEqual(collect_generated_image_urls_from_response_parts(responseJson, 0, 0), [generatedUrl]);
|
||||
});
|
||||
@@ -37,6 +37,8 @@ type InitOptions = {
|
||||
|
||||
type RequestKwargs = RequestInit & { timeout_ms?: number };
|
||||
|
||||
const GENERATED_IMAGE_URL_PREFIX = 'https://lh3.googleusercontent.com/gg-dl/';
|
||||
|
||||
function normalize_headers(h?: HeadersInit): Record<string, string> {
|
||||
if (!h) return {};
|
||||
if (Array.isArray(h)) return Object.fromEntries(h.map(([k, v]) => [k, v]));
|
||||
@@ -78,6 +80,59 @@ function collect_strings(root: unknown, accept: (s: string) => boolean, limit: n
|
||||
return out;
|
||||
}
|
||||
|
||||
function collect_generated_image_urls(root: unknown, limit: number = 4): string[] {
|
||||
return collect_strings(root, (s) => s.startsWith(GENERATED_IMAGE_URL_PREFIX), limit);
|
||||
}
|
||||
|
||||
function parse_response_part_body(part: unknown): unknown[] | null {
|
||||
if (!Array.isArray(part)) return null;
|
||||
const part_body = get_nested_value<string | null>(part, [2], null);
|
||||
if (!part_body) return null;
|
||||
try {
|
||||
const part_json = JSON.parse(part_body) as unknown;
|
||||
return Array.isArray(part_json) ? part_json : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function find_generated_image_part(
|
||||
response_json: unknown[],
|
||||
body_index: number,
|
||||
candidate_index: number,
|
||||
limit: number = 4,
|
||||
): { body: unknown[]; urls: string[] } | null {
|
||||
for (let part_index = body_index; part_index < response_json.length; part_index++) {
|
||||
const part_json = parse_response_part_body(response_json[part_index]);
|
||||
if (!part_json) continue;
|
||||
const cand = get_nested_value<unknown>(part_json, [4, candidate_index], null);
|
||||
if (!cand) continue;
|
||||
const urls = collect_generated_image_urls(cand, limit);
|
||||
if (urls.length > 0) return { body: part_json, urls };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function collect_generated_image_urls_from_response_parts(
|
||||
response_json: unknown[],
|
||||
body_index: number,
|
||||
candidate_index: number,
|
||||
limit: number = 4,
|
||||
): string[] {
|
||||
return find_generated_image_part(response_json, body_index, candidate_index, limit)?.urls ?? [];
|
||||
}
|
||||
|
||||
function push_generated_images(
|
||||
generated_images: GeneratedImage[],
|
||||
urls: string[],
|
||||
proxy: string | null,
|
||||
cookies: Record<string, string>,
|
||||
): void {
|
||||
for (const url of urls) {
|
||||
generated_images.push(new GeneratedImage(url, '[Generated Image]', '', proxy, cookies));
|
||||
}
|
||||
}
|
||||
|
||||
export class GeminiClient extends GemMixin {
|
||||
public cookies: Record<string, string> = {};
|
||||
public proxy: string | null = null;
|
||||
@@ -404,24 +459,8 @@ export class GeminiClient extends GemMixin {
|
||||
/http:\/\/googleusercontent\.com\/image_generation_content\/\d+/.test(text);
|
||||
|
||||
if (wants_generated) {
|
||||
let img_body: unknown[] | null = null;
|
||||
for (let part_index = body_index; part_index < (response_json as unknown[]).length; part_index++) {
|
||||
const part = (response_json as unknown[])[part_index];
|
||||
if (!Array.isArray(part)) continue;
|
||||
const part_body = get_nested_value<string | null>(part, [2], null);
|
||||
if (!part_body) continue;
|
||||
try {
|
||||
const part_json = JSON.parse(part_body) as unknown[];
|
||||
const cand = get_nested_value<unknown>(part_json, [4, candidate_index], null);
|
||||
if (!cand) continue;
|
||||
|
||||
const urls = collect_strings(cand, (s) => s.startsWith('https://lh3.googleusercontent.com/gg-dl/'), 1);
|
||||
if (urls.length > 0) {
|
||||
img_body = part_json;
|
||||
break;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
const image_part = find_generated_image_part(response_json as unknown[], body_index, candidate_index, 1);
|
||||
const img_body = image_part?.body ?? null;
|
||||
|
||||
if (!img_body) {
|
||||
throw new ImageGenerationError(
|
||||
@@ -452,13 +491,22 @@ export class GeminiClient extends GemMixin {
|
||||
}
|
||||
|
||||
if (generated_images.length === 0) {
|
||||
const urls = collect_strings(img_candidate, (s) => s.startsWith('https://lh3.googleusercontent.com/gg-dl/'), 4);
|
||||
for (const url of urls) {
|
||||
generated_images.push(new GeneratedImage(url, '[Generated Image]', '', this.proxy, this.cookies));
|
||||
}
|
||||
push_generated_images(generated_images, collect_generated_image_urls(img_candidate), this.proxy, this.cookies);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: unconditionally scan all response parts for generated image URLs.
|
||||
// The `wants_generated` detection above relies on `candidate[12][7][0]` and an old
|
||||
// `googleusercontent.com/image_generation_content/` URL pattern, both of which no
|
||||
// longer appear in the current Gemini Web API response format.
|
||||
// When Gemini does generate images, their URLs now start with
|
||||
// `https://lh3.googleusercontent.com/gg-dl/` and are present somewhere in the
|
||||
// response parts — this fallback finds them so images are not silently dropped.
|
||||
if (generated_images.length === 0) {
|
||||
const urls = collect_generated_image_urls_from_response_parts(response_json as unknown[], body_index, candidate_index);
|
||||
push_generated_images(generated_images, urls, this.proxy, this.cookies);
|
||||
}
|
||||
|
||||
out.push(new Candidate({ rcid, text, thoughts, web_images, generated_images }));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-image-cards
|
||||
description: Generates infographic image card series with 12 visual styles, 8 layouts, and 3 color palettes. Breaks content into 1-10 cartoon-style image cards optimized for social media engagement. Use when user mentions "小红书图片", "小红书种草", "小绿书", "微信图文", "微信贴图", "image cards", "图片卡片", or wants social media infographic series.
|
||||
version: 1.56.1
|
||||
version: 1.56.2
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-image-cards
|
||||
@@ -23,14 +23,31 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) above are examples — substitute the local equivalents under the same rule.
|
||||
|
||||
## Confirmation Policy
|
||||
|
||||
Default behavior: **confirm before generation**.
|
||||
|
||||
- Treat explicit skill invocation, a file path, matched signals/presets, and `EXTEND.md` defaults as **recommendation inputs only**. None of them authorizes skipping confirmation.
|
||||
- Do **not** start Step 3 until the user completes Step 2.
|
||||
- Skip confirmation only when the current request explicitly says to do so, for example: `--yes`, "直接生成", "不用确认", "跳过确认", "按默认出图", or equivalent wording.
|
||||
- If confirmation is skipped explicitly, state the assumed strategy / style / layout / palette / count / backend in the next user-facing update before generating.
|
||||
|
||||
## Language
|
||||
|
||||
Respond in the user's language across questions, progress, errors, and completion summary. Keep technical tokens (style names, file paths, code) in English.
|
||||
@@ -291,6 +308,8 @@ Check these paths in order; first hit wins:
|
||||
|
||||
### Step 2: Smart Confirm ⚠️ REQUIRED
|
||||
|
||||
**Hard gate**: this step is mandatory per the [Confirmation Policy](#confirmation-policy) — Step 3 cannot start until the user confirms here (or explicitly opts out with `--yes` / equivalent wording in the current request).
|
||||
|
||||
Goal: present the auto-recommended plan and let the user confirm or adjust. Skip this step entirely under `--yes` — proceed with Path A using the analysis and any CLI overrides.
|
||||
|
||||
**Display summary** before asking:
|
||||
@@ -417,4 +436,16 @@ Always update the prompt file before regenerating — it's the source of truth a
|
||||
- For sensitive public figures, use stylized cartoon alternatives.
|
||||
- Smart Confirm (Step 2) is required; Detailed mode adds a second confirmation (2a + 2c).
|
||||
|
||||
Custom configurations via EXTEND.md. See Step 0 for paths and schema.
|
||||
## Changing Preferences
|
||||
|
||||
EXTEND.md lives at the first matching path listed in Step 0. Three ways to change it:
|
||||
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: `references/config/preferences-schema.md`.
|
||||
- **Reconfigure interactively** — delete EXTEND.md (or ask "reconfigure baoyu-image-cards preferences" / "重新配置"). The next run re-triggers first-time setup.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `preferred_style: notion`, `preferred_layout: dense`, `preferred_palette: macaron`, `language: zh`.
|
||||
- `watermark.enabled: true` + `watermark.content: "@handle"` — add a watermark.
|
||||
|
||||
@@ -110,13 +110,13 @@ preferred_style:
|
||||
description: ""
|
||||
preferred_layout: null
|
||||
language: null
|
||||
preferred_image_backend: auto
|
||||
custom_styles: []
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend: auto` is the baked-in default — first-time setup does not ask about it. The `## Image Generation Tools` rule in SKILL.md then picks the runtime-native tool (Codex `imagegen`, Hermes `image_generate`, etc.) when available, and falls back to installed backends.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
- Delete EXTEND.md to trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `config/preferences-schema.md`
|
||||
See the `## Changing Preferences` section in `SKILL.md` for the canonical list of common edits (pin backend, change defaults, retrigger setup). Full schema: `preferences-schema.md`.
|
||||
|
||||
@@ -24,6 +24,8 @@ preferred_layout: null # sparse|balanced|dense|list|comparison|flow
|
||||
|
||||
language: null # zh|en|ja|ko|auto
|
||||
|
||||
preferred_image_backend: auto # auto|ask|<backend-id>
|
||||
|
||||
custom_styles:
|
||||
- name: my-style
|
||||
description: "Style description"
|
||||
@@ -49,6 +51,7 @@ custom_styles:
|
||||
| `preferred_style.description` | string | "" | Custom notes/override |
|
||||
| `preferred_layout` | string | null | Layout preference or null |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. Resolution logic is documented in `SKILL.md`'s `## Image Generation Tools` section. |
|
||||
| `custom_styles` | array | [] | User-defined styles |
|
||||
|
||||
## Position Options
|
||||
@@ -104,6 +107,8 @@ preferred_layout: dense
|
||||
|
||||
language: zh
|
||||
|
||||
preferred_image_backend: codex-imagegen
|
||||
|
||||
custom_styles:
|
||||
- name: corporate
|
||||
description: "Professional B2B style"
|
||||
|
||||
@@ -24,6 +24,6 @@ Read when the user picks `--provider minimax` or sets `default_model.minimax`. D
|
||||
|
||||
## Official References
|
||||
|
||||
- [Image Generation Guide](https://platform.minimax.io/docs/guides/image-generation)
|
||||
- [Text-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-t2i)
|
||||
- [Image-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-i2i)
|
||||
- [Image Generation Guide](https://platform.minimaxi.com/docs/guides/image-generation)
|
||||
- [Text-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-t2i)
|
||||
- [Image-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-i2i)
|
||||
|
||||
@@ -60,8 +60,11 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
};
|
||||
}
|
||||
|
||||
test("MiniMax URL builder normalizes /v1 suffixes", (t) => {
|
||||
useEnv(t, { MINIMAX_BASE_URL: "https://api.minimax.io" });
|
||||
test("MiniMax URL builder uses documented default and normalizes /v1 suffixes", (t) => {
|
||||
useEnv(t, { MINIMAX_BASE_URL: null });
|
||||
assert.equal(buildMinimaxUrl(), "https://api.minimaxi.com/v1/image_generation");
|
||||
|
||||
process.env.MINIMAX_BASE_URL = "https://api.minimax.io";
|
||||
assert.equal(buildMinimaxUrl(), "https://api.minimax.io/v1/image_generation");
|
||||
|
||||
process.env.MINIMAX_BASE_URL = "https://proxy.example.com/custom/v1/";
|
||||
|
||||
@@ -44,7 +44,7 @@ function getApiKey(): string | null {
|
||||
}
|
||||
|
||||
export function buildMinimaxUrl(): string {
|
||||
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimax.io").replace(/\/+$/g, "");
|
||||
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimaxi.com").replace(/\/+$/g, "");
|
||||
return base.endsWith("/v1") ? `${base}/image_generation` : `${base}/v1/image_generation`;
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export async function generateImage(
|
||||
): Promise<Uint8Array> {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) {
|
||||
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimax.io/");
|
||||
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimaxi.com/");
|
||||
}
|
||||
|
||||
const body = await buildRequestBody(prompt, model, args);
|
||||
|
||||
@@ -91,7 +91,7 @@ ${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4
|
||||
| `--quality normal\|2k` | Quality preset (default: `2k`) |
|
||||
| `--imageSize 1K\|2K\|4K` | Image size for Google/OpenRouter (default: from quality) |
|
||||
| `--imageApiDialect openai-native\|ratio-metadata` | OpenAI-compatible endpoint dialect — use `ratio-metadata` for gateways that expect aspect-ratio `size` plus `metadata.resolution` |
|
||||
| `--ref <files...>` | Reference images. Supported by Google multimodal, OpenAI GPT Image edits, Azure OpenAI edits (PNG/JPG only), OpenRouter multimodal models, Replicate supported families, MiniMax subject-reference, Seedream 5.0/4.5/4.0. Not supported by Jimeng, Seedream 3.0, SeedEdit 3.0 |
|
||||
| `--ref <files...>` | Reference images. Supported by Google multimodal, OpenAI GPT Image edits, Azure OpenAI edits (PNG/JPG only), OpenRouter multimodal models, Replicate supported families, MiniMax subject-reference, Seedream 5.0/4.5/4.0, DashScope `wan2.7-image-pro`/`wan2.7-image`. Not supported by Jimeng, Seedream 3.0, SeedEdit 3.0, or any DashScope model outside the `wan2.7-image*` family |
|
||||
| `--n <count>` | Number of images. Replicate requires `--n 1` (single-output save semantics) |
|
||||
| `--json` | JSON output |
|
||||
|
||||
|
||||
@@ -271,6 +271,10 @@ options:
|
||||
description: "Legacy Qwen model with five fixed output sizes"
|
||||
- label: "qwen-image-plus"
|
||||
description: "Legacy Qwen model, same current capability as qwen-image"
|
||||
- label: "wan2.7-image-pro"
|
||||
description: "Wan 2.7 Pro — supports up to 4K text-to-image and reference-image editing"
|
||||
- label: "wan2.7-image"
|
||||
description: "Wan 2.7 base — faster generation, up to 2K, supports reference-image editing"
|
||||
- label: "z-image-turbo"
|
||||
description: "Legacy DashScope model for compatibility"
|
||||
- label: "z-image-ultra"
|
||||
@@ -281,6 +285,7 @@ Notes for DashScope setup:
|
||||
|
||||
- Prefer `qwen-image-2.0-pro` when the user needs custom `--size`, uncommon ratios like `21:9`, or strong Chinese/English text rendering.
|
||||
- `qwen-image-max` / `qwen-image-plus` / `qwen-image` only support five fixed sizes: `1664*928`, `1472*1104`, `1328*1328`, `1104*1472`, `928*1664`.
|
||||
- `wan2.7-image-pro` and `wan2.7-image` are the only DashScope models that accept `--ref`. Pick one of these when the user wants reference-image editing or multi-image fusion via DashScope.
|
||||
- In `baoyu-imagine`, `quality` is a compatibility preset. It is not a native DashScope parameter.
|
||||
|
||||
### Z.AI Model Selection
|
||||
|
||||
@@ -17,6 +17,17 @@ Read when the user picks `--provider dashscope`, sets `default_model.dashscope`,
|
||||
- Default is `1664*928`
|
||||
- `qwen-image` currently has the same capability as `qwen-image-plus`
|
||||
|
||||
**`wan2.7-image*`** — multimodal Wan 2.7 family. Members: `wan2.7-image-pro`, `wan2.7-image`.
|
||||
|
||||
- Free-form `size` in `宽*高` format, plus aspect-ratio inference
|
||||
- `wan2.7-image-pro` text-to-image (no `--ref`): total pixels in `[768*768, 4096*4096]`, ratio in `[1:8, 8:1]`
|
||||
- `wan2.7-image-pro` with reference images and `wan2.7-image` (all scenarios): total pixels in `[768*768, 2048*2048]`, ratio in `[1:8, 8:1]`
|
||||
- Default: `1024*1024` (`--quality normal`) or `2048*2048` (`--quality 2k`); 4K requires explicit `--size`
|
||||
- Supports up to 9 reference images in `--ref` (image editing / multi-image fusion)
|
||||
- Reference images are sent inline as base64 (or passed through if the path is an `http(s)://` URL)
|
||||
- API does NOT use `prompt_extend`; the skill omits it for this family
|
||||
- The Wan 2.7 API defaults `n` to **4** in non-collage mode and bills per generated image. baoyu-imagine forces `n: 1` and rejects `--n > 1` to avoid silently paying for and discarding extra images.
|
||||
|
||||
**Legacy** — `z-image-turbo`, `z-image-ultra`, `wanx-v1`. Only use when the user explicitly asks for legacy behavior.
|
||||
|
||||
## Size Resolution
|
||||
@@ -24,7 +35,8 @@ Read when the user picks `--provider dashscope`, sets `default_model.dashscope`,
|
||||
- `--size` wins over `--ar`
|
||||
- For `qwen-image-2.0*`: prefer explicit `--size`; otherwise infer from `--ar` using the recommended table below
|
||||
- For `qwen-image-max/plus/image`: only use the five fixed sizes; if the requested ratio doesn't fit, switch to `qwen-image-2.0-pro`
|
||||
- `--quality` is a baoyu-imagine preset, not an official DashScope field. The mapping of `normal`/`2k` onto the `qwen-image-2.0*` table is an implementation choice, not an API guarantee
|
||||
- For `wan2.7-image*`: explicit `--size` is validated against the per-mode pixel/ratio limits; otherwise the size is derived from `--ar` and `--quality` (`normal` ≈ 1K, `2k` ≈ 2K). To request 4K with `wan2.7-image-pro` text-to-image, pass `--size` explicitly (e.g. `4096*4096`, `3840*2160`)
|
||||
- `--quality` is a baoyu-imagine preset, not an official DashScope field. The mapping of `normal`/`2k` onto the `qwen-image-2.0*` and `wan2.7-image*` tables is an implementation choice, not an API guarantee
|
||||
|
||||
### Recommended `qwen-image-2.0*` sizes
|
||||
|
||||
@@ -39,12 +51,19 @@ Read when the user picks `--provider dashscope`, sets `default_model.dashscope`,
|
||||
| `16:9` | `1280*720` | `1920*1080` |
|
||||
| `21:9` | `1344*576` | `2048*872` |
|
||||
|
||||
## Reference Images
|
||||
|
||||
- Only `wan2.7-image-pro` and `wan2.7-image` accept `--ref`. Other DashScope models (qwen-image-2.0*, qwen-image-max/plus/image, legacy) reject `--ref` and the user is steered to a different provider/model.
|
||||
- Up to 9 reference images per request. Local files are inlined as base64 data URLs; `http(s)://` URLs are forwarded as-is.
|
||||
- Supplying any `--ref` automatically clamps the wan2.7-image-pro pixel ceiling from 4K to 2K (the API only supports 4K for pure text-to-image with no image input).
|
||||
|
||||
## Not Exposed
|
||||
|
||||
DashScope APIs also support `negative_prompt`, `prompt_extend`, and `watermark`, but `baoyu-imagine` does not expose them as CLI flags today.
|
||||
DashScope APIs also support `negative_prompt`, `prompt_extend`, `watermark`, `thinking_mode`, `seed`, `bbox_list`, `enable_sequential`, and `color_palette`. `baoyu-imagine` does not expose them as CLI flags today; the wan2.7 family relies on the API defaults (e.g. `thinking_mode=true`). The skill always sends `n=1` for wan2.7 — if you want grid/collage mode you currently need to call the API directly.
|
||||
|
||||
## Official References
|
||||
|
||||
- [Qwen-Image API](https://help.aliyun.com/zh/model-studio/qwen-image-api)
|
||||
- [Text-to-image guide](https://help.aliyun.com/zh/model-studio/text-to-image)
|
||||
- [Qwen-Image Edit API](https://help.aliyun.com/zh/model-studio/qwen-image-edit-api)
|
||||
- [Wan 2.7 image generation & editing API](https://help.aliyun.com/zh/model-studio/wan-image-generation-and-editing-api-reference)
|
||||
|
||||
@@ -24,6 +24,6 @@ Read when the user picks `--provider minimax` or sets `default_model.minimax`. D
|
||||
|
||||
## Official References
|
||||
|
||||
- [Image Generation Guide](https://platform.minimax.io/docs/guides/image-generation)
|
||||
- [Text-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-t2i)
|
||||
- [Image-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-i2i)
|
||||
- [Image Generation Guide](https://platform.minimaxi.com/docs/guides/image-generation)
|
||||
- [Text-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-t2i)
|
||||
- [Image-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-i2i)
|
||||
|
||||
@@ -51,6 +51,12 @@ ${BUN_X} {baseDir}/scripts/main.ts --prompt "为咖啡品牌设计一张 21:9
|
||||
# DashScope legacy fixed-size
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张电影感海报" --image out.png --provider dashscope --model qwen-image-max --size 1664x928
|
||||
|
||||
# DashScope Wan 2.7 Image Pro (4K text-to-image)
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "一间有着精致窗户的花店" --image out.png --provider dashscope --model wan2.7-image-pro --size 4096x4096
|
||||
|
||||
# DashScope Wan 2.7 Image with reference image (multi-image fusion)
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "把图2的涂鸦喷绘在图1的汽车上" --image out.png --provider dashscope --model wan2.7-image-pro --ref car.webp paint.webp
|
||||
|
||||
# Z.AI GLM-image
|
||||
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张带清晰中文标题的科技海报" --image out.png --provider zai
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
parseArgs,
|
||||
parseOpenAIImageApiDialect,
|
||||
parseSimpleYaml,
|
||||
validateReferenceImages,
|
||||
} from "./main.ts";
|
||||
|
||||
function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
@@ -123,6 +124,15 @@ test("parseArgs falls back to positional prompt and rejects invalid provider", (
|
||||
);
|
||||
});
|
||||
|
||||
test("validateReferenceImages can skip remote URLs for providers that support them", async () => {
|
||||
await validateReferenceImages(["https://example.com/ref.png"], { allowRemoteUrls: true });
|
||||
|
||||
await assert.rejects(
|
||||
() => validateReferenceImages(["https://example.com/ref.png"]),
|
||||
/Reference image not found/,
|
||||
);
|
||||
});
|
||||
|
||||
test("parseSimpleYaml parses nested defaults and provider limits", () => {
|
||||
const yaml = `
|
||||
version: 2
|
||||
@@ -308,7 +318,7 @@ test("detectProvider rejects non-ref-capable providers and prefers Google first
|
||||
() =>
|
||||
detectProvider(
|
||||
makeArgs({
|
||||
provider: "dashscope",
|
||||
provider: "zai",
|
||||
referenceImages: ["ref.png"],
|
||||
}),
|
||||
),
|
||||
@@ -426,6 +436,33 @@ test("detectProvider infers Seedream from model id and allows Seedream reference
|
||||
);
|
||||
});
|
||||
|
||||
test("detectProvider allows DashScope reference-image workflows when explicitly chosen for wan2.7 models", (t) => {
|
||||
useEnv(t, {
|
||||
GOOGLE_API_KEY: null,
|
||||
OPENAI_API_KEY: null,
|
||||
AZURE_OPENAI_API_KEY: null,
|
||||
AZURE_OPENAI_BASE_URL: null,
|
||||
OPENROUTER_API_KEY: null,
|
||||
DASHSCOPE_API_KEY: "dashscope-key",
|
||||
MINIMAX_API_KEY: null,
|
||||
REPLICATE_API_TOKEN: null,
|
||||
JIMENG_ACCESS_KEY_ID: null,
|
||||
JIMENG_SECRET_ACCESS_KEY: null,
|
||||
ARK_API_KEY: null,
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
detectProvider(
|
||||
makeArgs({
|
||||
provider: "dashscope",
|
||||
model: "wan2.7-image-pro",
|
||||
referenceImages: ["ref.png"],
|
||||
}),
|
||||
),
|
||||
"dashscope",
|
||||
);
|
||||
});
|
||||
|
||||
test("detectProvider selects MiniMax when only MiniMax credentials are configured or the model id matches", (t) => {
|
||||
useEnv(t, {
|
||||
GOOGLE_API_KEY: null,
|
||||
@@ -504,7 +541,7 @@ test("loadBatchTasks and createTaskArgs resolve batch-relative paths", async (t)
|
||||
id: "hero",
|
||||
promptFiles: ["prompts/hero.md"],
|
||||
image: "out/hero",
|
||||
ref: ["refs/hero.png"],
|
||||
ref: ["refs/hero.png", "https://example.com/ref.png"],
|
||||
ar: "16:9",
|
||||
},
|
||||
],
|
||||
@@ -533,6 +570,7 @@ test("loadBatchTasks and createTaskArgs resolve batch-relative paths", async (t)
|
||||
assert.equal(taskArgs.imagePath, path.join(loaded.batchDir, "out/hero"));
|
||||
assert.deepEqual(taskArgs.referenceImages, [
|
||||
path.join(loaded.batchDir, "refs/hero.png"),
|
||||
"https://example.com/ref.png",
|
||||
]);
|
||||
assert.equal(taskArgs.provider, "replicate");
|
||||
assert.equal(taskArgs.aspectRatio, "16:9");
|
||||
@@ -557,5 +595,29 @@ test("path normalization, worker count, and retry classification follow expected
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
isRetryableGenerationError(
|
||||
new Error("DashScope wan2.7 image models accept at most 9 reference images. Received 10."),
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
isRetryableGenerationError(
|
||||
new Error("DashScope wan2.7 image models in baoyu-imagine support exactly one output image per request."),
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
isRetryableGenerationError(
|
||||
new Error("DashScope wan2.7 image models support aspect ratios in [1:8, 8:1]."),
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
isRetryableGenerationError(
|
||||
new Error("DashScope wan2.7-image requires total pixels between 768*768 and 2048*2048."),
|
||||
),
|
||||
false,
|
||||
);
|
||||
assert.equal(isRetryableGenerationError(new Error("socket hang up")), true);
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ Options:
|
||||
--quality normal|2k Quality preset (default: 2k)
|
||||
--imageSize 1K|2K|4K Image size for Google/OpenRouter (default: from quality)
|
||||
--imageApiDialect <id> OpenAI-compatible image dialect: openai-native|ratio-metadata
|
||||
--ref <files...> Reference images (Google, OpenAI, Azure, OpenRouter, Replicate supported families, MiniMax, or Seedream 4.0/4.5/5.0)
|
||||
--ref <files...> Reference images (Google, OpenAI, Azure, OpenRouter, Replicate supported families, MiniMax, Seedream 4.0/4.5/5.0, or DashScope wan2.7-image*)
|
||||
--n <count> Number of images for the current task (default: 1; Replicate currently requires 1)
|
||||
--json JSON output
|
||||
-h, --help Show help
|
||||
@@ -698,10 +698,11 @@ export function detectProvider(args: CliArgs): Provider {
|
||||
args.provider !== "openrouter" &&
|
||||
args.provider !== "replicate" &&
|
||||
args.provider !== "seedream" &&
|
||||
args.provider !== "minimax"
|
||||
args.provider !== "minimax" &&
|
||||
args.provider !== "dashscope"
|
||||
) {
|
||||
throw new Error(
|
||||
"Reference images require a ref-capable provider. Use --provider google (Gemini multimodal), --provider openai (GPT Image edits), --provider azure (Azure OpenAI), --provider openrouter (OpenRouter multimodal), --provider replicate, --provider seedream for supported Seedream models, or --provider minimax for MiniMax subject-reference workflows."
|
||||
"Reference images require a ref-capable provider. Use --provider google (Gemini multimodal), --provider openai (GPT Image edits), --provider azure (Azure OpenAI), --provider openrouter (OpenRouter multimodal), --provider replicate, --provider dashscope with a wan2.7 image model, --provider seedream for supported Seedream models, or --provider minimax for MiniMax subject-reference workflows."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -775,8 +776,24 @@ export function detectProvider(args: CliArgs): Provider {
|
||||
);
|
||||
}
|
||||
|
||||
export async function validateReferenceImages(referenceImages: string[]): Promise<void> {
|
||||
export type ReferenceImageValidationOptions = {
|
||||
allowRemoteUrls?: boolean;
|
||||
};
|
||||
|
||||
function isRemoteReferenceImage(refPath: string): boolean {
|
||||
return /^https?:\/\//i.test(refPath);
|
||||
}
|
||||
|
||||
function shouldAllowRemoteReferenceImages(provider: Provider | null): boolean {
|
||||
return provider === "dashscope";
|
||||
}
|
||||
|
||||
export async function validateReferenceImages(
|
||||
referenceImages: string[],
|
||||
options: ReferenceImageValidationOptions = {},
|
||||
): Promise<void> {
|
||||
for (const refPath of referenceImages) {
|
||||
if (options.allowRemoteUrls && isRemoteReferenceImage(refPath)) continue;
|
||||
const fullPath = path.resolve(refPath);
|
||||
try {
|
||||
await access(fullPath);
|
||||
@@ -803,6 +820,11 @@ export function isRetryableGenerationError(error: unknown): boolean {
|
||||
"API error (404)",
|
||||
"temporarily disabled",
|
||||
"supports saving exactly one image",
|
||||
"supports only",
|
||||
"support exactly one output image",
|
||||
"support aspect ratios in",
|
||||
"requires total pixels between",
|
||||
"accept at most",
|
||||
];
|
||||
return !nonRetryableMarkers.some((marker) => msg.includes(marker));
|
||||
}
|
||||
@@ -858,7 +880,11 @@ async function prepareSingleTask(args: CliArgs, extendConfig: Partial<ExtendConf
|
||||
const prompt = (await loadPromptForArgs(args)) ?? (await readPromptFromStdin());
|
||||
if (!prompt) throw new Error("Prompt is required");
|
||||
if (!args.imagePath) throw new Error("--image is required");
|
||||
if (args.referenceImages.length > 0) await validateReferenceImages(args.referenceImages);
|
||||
if (args.referenceImages.length > 0) {
|
||||
await validateReferenceImages(args.referenceImages, {
|
||||
allowRemoteUrls: shouldAllowRemoteReferenceImages(args.provider),
|
||||
});
|
||||
}
|
||||
|
||||
const provider = detectProvider(args);
|
||||
const providerModule = await loadProviderModule(provider);
|
||||
@@ -907,6 +933,10 @@ export function resolveBatchPath(batchDir: string, filePath: string): string {
|
||||
return path.isAbsolute(filePath) ? filePath : path.resolve(batchDir, filePath);
|
||||
}
|
||||
|
||||
function resolveBatchReferencePath(batchDir: string, filePath: string): string {
|
||||
return isRemoteReferenceImage(filePath) ? filePath : resolveBatchPath(batchDir, filePath);
|
||||
}
|
||||
|
||||
export function createTaskArgs(baseArgs: CliArgs, task: BatchTaskInput, batchDir: string): CliArgs {
|
||||
return {
|
||||
...baseArgs,
|
||||
@@ -922,7 +952,7 @@ export function createTaskArgs(baseArgs: CliArgs, task: BatchTaskInput, batchDir
|
||||
imageSize: task.imageSize ?? baseArgs.imageSize ?? null,
|
||||
imageSizeSource: task.imageSize != null ? "task" : (baseArgs.imageSizeSource ?? null),
|
||||
imageApiDialect: task.imageApiDialect ?? baseArgs.imageApiDialect ?? null,
|
||||
referenceImages: task.ref ? task.ref.map((filePath) => resolveBatchPath(batchDir, filePath)) : [],
|
||||
referenceImages: task.ref ? task.ref.map((filePath) => resolveBatchReferencePath(batchDir, filePath)) : [],
|
||||
n: task.n ?? baseArgs.n,
|
||||
batchFile: null,
|
||||
jobs: baseArgs.jobs,
|
||||
@@ -946,7 +976,11 @@ async function prepareBatchTasks(
|
||||
const prompt = await loadPromptForArgs(taskArgs);
|
||||
if (!prompt) throw new Error(`Task ${i + 1} is missing prompt or promptFiles.`);
|
||||
if (!taskArgs.imagePath) throw new Error(`Task ${i + 1} is missing image output path.`);
|
||||
if (taskArgs.referenceImages.length > 0) await validateReferenceImages(taskArgs.referenceImages);
|
||||
if (taskArgs.referenceImages.length > 0) {
|
||||
await validateReferenceImages(taskArgs.referenceImages, {
|
||||
allowRemoteUrls: shouldAllowRemoteReferenceImages(taskArgs.provider),
|
||||
});
|
||||
}
|
||||
|
||||
const provider = detectProvider(taskArgs);
|
||||
const providerModule = await loadProviderModule(provider);
|
||||
|
||||
@@ -2,15 +2,42 @@ import assert from "node:assert/strict";
|
||||
import test, { type TestContext } from "node:test";
|
||||
|
||||
import {
|
||||
generateImage,
|
||||
getDefaultModel,
|
||||
getModelFamily,
|
||||
getQwen2SizeFromAspectRatio,
|
||||
getSizeFromAspectRatio,
|
||||
getWan27SizeFromAspectRatio,
|
||||
normalizeSize,
|
||||
parseAspectRatio,
|
||||
parseSize,
|
||||
resolveSizeForModel,
|
||||
} from "./dashscope.ts";
|
||||
import type { CliArgs } from "../types.ts";
|
||||
|
||||
function makeCliArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
return {
|
||||
prompt: null,
|
||||
promptFiles: [],
|
||||
imagePath: null,
|
||||
provider: "dashscope",
|
||||
model: null,
|
||||
aspectRatio: null,
|
||||
aspectRatioSource: null,
|
||||
size: null,
|
||||
quality: "2k",
|
||||
imageSize: null,
|
||||
imageSizeSource: null,
|
||||
imageApiDialect: null,
|
||||
referenceImages: [],
|
||||
n: 1,
|
||||
batchFile: null,
|
||||
jobs: null,
|
||||
json: false,
|
||||
help: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function useEnv(
|
||||
t: TestContext,
|
||||
@@ -51,9 +78,11 @@ test("DashScope aspect-ratio parsing accepts numeric ratios only", () => {
|
||||
assert.equal(parseAspectRatio("-1:2"), null);
|
||||
});
|
||||
|
||||
test("DashScope model family routing distinguishes qwen-2.0, fixed-size qwen, and legacy models", () => {
|
||||
test("DashScope model family routing distinguishes qwen-2.0, fixed-size qwen, wan2.7, and legacy models", () => {
|
||||
assert.equal(getModelFamily("qwen-image-2.0-pro"), "qwen2");
|
||||
assert.equal(getModelFamily("qwen-image"), "qwenFixed");
|
||||
assert.equal(getModelFamily("wan2.7-image"), "wan27");
|
||||
assert.equal(getModelFamily("wan2.7-image-pro"), "wan27");
|
||||
assert.equal(getModelFamily("z-image-turbo"), "legacy");
|
||||
assert.equal(getModelFamily("wanx-v1"), "legacy");
|
||||
});
|
||||
@@ -146,3 +175,218 @@ test("DashScope size normalization converts WxH into provider format", () => {
|
||||
assert.equal(normalizeSize("1024x1024"), "1024*1024");
|
||||
assert.equal(normalizeSize("2048*1152"), "2048*1152");
|
||||
});
|
||||
|
||||
test("Wan 2.7 derives sizes that match the requested ratio at the chosen pixel budget", () => {
|
||||
const square2k = getWan27SizeFromAspectRatio(null, "2k", 2048 * 2048);
|
||||
const parsedSquare = parseSize(square2k);
|
||||
assert.ok(parsedSquare);
|
||||
assert.equal(parsedSquare.width, parsedSquare.height);
|
||||
assert.ok(parsedSquare.width * parsedSquare.height <= 2048 * 2048);
|
||||
|
||||
const widescreen = getWan27SizeFromAspectRatio("16:9", "2k", 2048 * 2048);
|
||||
const parsedWide = parseSize(widescreen);
|
||||
assert.ok(parsedWide);
|
||||
assert.ok(Math.abs(parsedWide.width / parsedWide.height - 16 / 9) < 0.05);
|
||||
assert.ok(parsedWide.width * parsedWide.height <= 2048 * 2048);
|
||||
|
||||
const pro4k = getWan27SizeFromAspectRatio("16:9", "2k", 4096 * 4096);
|
||||
const parsed4k = parseSize(pro4k);
|
||||
assert.ok(parsed4k);
|
||||
assert.ok(parsed4k.width * parsed4k.height > 2048 * 2048);
|
||||
assert.ok(parsed4k.width * parsed4k.height <= 4096 * 4096);
|
||||
});
|
||||
|
||||
test("Wan 2.7 rejects aspect ratios outside the [1:8, 8:1] range", () => {
|
||||
assert.throws(
|
||||
() => getWan27SizeFromAspectRatio("9:1", "2k", 2048 * 2048),
|
||||
/1:8, 8:1/,
|
||||
);
|
||||
assert.throws(
|
||||
() => getWan27SizeFromAspectRatio("1:9", "normal", 2048 * 2048),
|
||||
/1:8, 8:1/,
|
||||
);
|
||||
});
|
||||
|
||||
test("Wan 2.7 derived sizes stay inside the boundary ratio limits after rounding", () => {
|
||||
for (const ar of ["8:1", "1:8"]) {
|
||||
const size = getWan27SizeFromAspectRatio(ar, "2k", 2048 * 2048);
|
||||
const parsed = parseSize(size);
|
||||
assert.ok(parsed);
|
||||
const ratio = parsed.width / parsed.height;
|
||||
assert.ok(ratio >= 1 / 8);
|
||||
assert.ok(ratio <= 8);
|
||||
assert.ok(parsed.width * parsed.height <= 2048 * 2048);
|
||||
}
|
||||
});
|
||||
|
||||
test("resolveSizeForModel routes wan2.7-image to the 2K-capped derivation", () => {
|
||||
const size = resolveSizeForModel("wan2.7-image", {
|
||||
size: null,
|
||||
aspectRatio: "16:9",
|
||||
quality: "2k",
|
||||
});
|
||||
const parsed = parseSize(size);
|
||||
assert.ok(parsed);
|
||||
assert.ok(parsed.width * parsed.height <= 2048 * 2048);
|
||||
assert.ok(Math.abs(parsed.width / parsed.height - 16 / 9) < 0.05);
|
||||
});
|
||||
|
||||
test("resolveSizeForModel allows wan2.7-image-pro 4K only when there are no reference images", () => {
|
||||
assert.equal(
|
||||
resolveSizeForModel("wan2.7-image-pro", {
|
||||
size: "4096*4096",
|
||||
aspectRatio: null,
|
||||
quality: "2k",
|
||||
}),
|
||||
"4096*4096",
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveSizeForModel("wan2.7-image-pro", {
|
||||
size: "4096*4096",
|
||||
aspectRatio: null,
|
||||
quality: "2k",
|
||||
referenceImages: ["a.png"],
|
||||
}),
|
||||
/total pixels between 768\*768 and 2048\*2048/,
|
||||
);
|
||||
|
||||
const proWithRef = resolveSizeForModel("wan2.7-image-pro", {
|
||||
size: null,
|
||||
aspectRatio: "1:1",
|
||||
quality: "2k",
|
||||
referenceImages: ["a.png"],
|
||||
});
|
||||
const parsedRef = parseSize(proWithRef);
|
||||
assert.ok(parsedRef);
|
||||
assert.ok(parsedRef.width * parsedRef.height <= 2048 * 2048);
|
||||
});
|
||||
|
||||
test("Wan 2.7 request body forces n=1 and omits prompt_extend / negative_prompt", async (t) => {
|
||||
useEnv(t, { DASHSCOPE_API_KEY: "fake-key" });
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
let capturedBody: any = null;
|
||||
globalThis.fetch = (async (_url: string, init?: RequestInit) => {
|
||||
capturedBody = JSON.parse(String(init?.body));
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
output: {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: [{ image: "data:image/png;base64,iVBORw0KGgo=" }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}) as typeof fetch;
|
||||
t.after(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
});
|
||||
|
||||
await generateImage("hello", "wan2.7-image-pro", makeCliArgs({ aspectRatio: "1:1" }));
|
||||
|
||||
assert.equal(capturedBody.model, "wan2.7-image-pro");
|
||||
assert.deepEqual(Object.keys(capturedBody.parameters).sort(), ["n", "size", "watermark"]);
|
||||
assert.equal(capturedBody.parameters.n, 1);
|
||||
assert.equal(capturedBody.parameters.watermark, false);
|
||||
assert.equal(typeof capturedBody.parameters.size, "string");
|
||||
assert.ok(!("prompt_extend" in capturedBody.parameters));
|
||||
assert.ok(!("negative_prompt" in capturedBody.parameters));
|
||||
|
||||
assert.deepEqual(capturedBody.input.messages[0].content, [{ text: "hello" }]);
|
||||
});
|
||||
|
||||
test("Wan 2.7 request body forwards remote reference image URLs", async (t) => {
|
||||
useEnv(t, { DASHSCOPE_API_KEY: "fake-key" });
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
let capturedBody: any = null;
|
||||
globalThis.fetch = (async (_url: string, init?: RequestInit) => {
|
||||
capturedBody = JSON.parse(String(init?.body));
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
output: {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: [{ image: "data:image/png;base64,iVBORw0KGgo=" }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } },
|
||||
);
|
||||
}) as typeof fetch;
|
||||
t.after(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
});
|
||||
|
||||
await generateImage(
|
||||
"combine these",
|
||||
"wan2.7-image-pro",
|
||||
makeCliArgs({ referenceImages: ["https://example.com/ref.png"] }),
|
||||
);
|
||||
|
||||
assert.deepEqual(capturedBody.input.messages[0].content, [
|
||||
{ image: "https://example.com/ref.png" },
|
||||
{ text: "combine these" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("Wan 2.7 rejects --n > 1 to prevent silent multi-image billing", async (t) => {
|
||||
useEnv(t, { DASHSCOPE_API_KEY: "fake-key" });
|
||||
|
||||
await assert.rejects(
|
||||
() => generateImage("hi", "wan2.7-image-pro", makeCliArgs({ n: 2 })),
|
||||
/support exactly one output image/,
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveSizeForModel validates explicit wan2.7 sizes by pixel budget and ratio", () => {
|
||||
assert.equal(
|
||||
resolveSizeForModel("wan2.7-image-pro", {
|
||||
size: "3840x2160",
|
||||
aspectRatio: null,
|
||||
quality: "2k",
|
||||
}),
|
||||
"3840*2160",
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveSizeForModel("wan2.7-image-pro", {
|
||||
size: "3840x2160",
|
||||
aspectRatio: null,
|
||||
quality: "2k",
|
||||
referenceImages: ["a.png"],
|
||||
}),
|
||||
/total pixels between 768\*768 and 2048\*2048/,
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveSizeForModel("wan2.7-image", {
|
||||
size: "4096x4096",
|
||||
aspectRatio: null,
|
||||
quality: "2k",
|
||||
}),
|
||||
/total pixels between 768\*768 and 2048\*2048/,
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() =>
|
||||
resolveSizeForModel("wan2.7-image-pro", {
|
||||
size: "3072*256",
|
||||
aspectRatio: null,
|
||||
quality: "2k",
|
||||
}),
|
||||
/1:8, 8:1/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import path from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import type { CliArgs, Quality } from "../types";
|
||||
|
||||
type DashScopeModelFamily = "qwen2" | "qwenFixed" | "legacy";
|
||||
type DashScopeModelFamily = "qwen2" | "qwenFixed" | "wan27" | "legacy";
|
||||
|
||||
type DashScopeModelSpec = {
|
||||
family: DashScopeModelFamily;
|
||||
@@ -19,6 +21,16 @@ const QWEN_2_TARGET_PIXELS: Record<Quality, number> = {
|
||||
"2k": 1536 * 1536,
|
||||
};
|
||||
|
||||
const MIN_WAN27_TOTAL_PIXELS = 768 * 768;
|
||||
const MAX_WAN27_PRO_T2I_PIXELS = 4096 * 4096;
|
||||
const MAX_WAN27_GENERAL_PIXELS = 2048 * 2048;
|
||||
const WAN27_MAX_REFERENCE_IMAGES = 9;
|
||||
|
||||
const WAN27_TARGET_PIXELS: Record<Quality, number> = {
|
||||
normal: 1024 * 1024,
|
||||
"2k": 2048 * 2048,
|
||||
};
|
||||
|
||||
const QWEN_2_RECOMMENDED: Record<string, Record<Quality, string>> = {
|
||||
"1:1": { normal: "1024*1024", "2k": "1536*1536" },
|
||||
"2:3": { normal: "768*1152", "2k": "1024*1536" },
|
||||
@@ -73,6 +85,11 @@ const QWEN_FIXED_SPEC: DashScopeModelSpec = {
|
||||
defaultSize: QWEN_FIXED_SIZES_BY_RATIO["16:9"],
|
||||
};
|
||||
|
||||
const WAN27_SPEC: DashScopeModelSpec = {
|
||||
family: "wan27",
|
||||
defaultSize: "2048*2048",
|
||||
};
|
||||
|
||||
const LEGACY_SPEC: DashScopeModelSpec = {
|
||||
family: "legacy",
|
||||
defaultSize: "1536*1536",
|
||||
@@ -88,12 +105,31 @@ const MODEL_SPEC_ALIASES: Record<string, DashScopeModelSpec> = {
|
||||
"qwen-image-plus": QWEN_FIXED_SPEC,
|
||||
"qwen-image-plus-2026-01-09": QWEN_FIXED_SPEC,
|
||||
"qwen-image": QWEN_FIXED_SPEC,
|
||||
"wan2.7-image-pro": WAN27_SPEC,
|
||||
"wan2.7-image": WAN27_SPEC,
|
||||
};
|
||||
|
||||
export function getDefaultModel(): string {
|
||||
return process.env.DASHSCOPE_IMAGE_MODEL || DEFAULT_MODEL;
|
||||
}
|
||||
|
||||
function getReferenceImageMime(filePath: string): string {
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
|
||||
if (ext === ".webp") return "image/webp";
|
||||
if (ext === ".bmp") return "image/bmp";
|
||||
return "image/png";
|
||||
}
|
||||
|
||||
async function loadReferenceImage(refPath: string): Promise<string> {
|
||||
if (/^https?:\/\//i.test(refPath)) {
|
||||
return refPath;
|
||||
}
|
||||
const fullPath = path.resolve(refPath);
|
||||
const bytes = await readFile(fullPath);
|
||||
return `data:${getReferenceImageMime(fullPath)};base64,${bytes.toString("base64")}`;
|
||||
}
|
||||
|
||||
function getApiKey(): string | null {
|
||||
return process.env.DASHSCOPE_API_KEY || null;
|
||||
}
|
||||
@@ -173,6 +209,10 @@ function roundToStep(value: number): number {
|
||||
return Math.max(SIZE_STEP, Math.round(value / SIZE_STEP) * SIZE_STEP);
|
||||
}
|
||||
|
||||
function floorToStep(value: number): number {
|
||||
return Math.max(SIZE_STEP, Math.floor(value / SIZE_STEP) * SIZE_STEP);
|
||||
}
|
||||
|
||||
function fitToPixelBudget(
|
||||
width: number,
|
||||
height: number,
|
||||
@@ -220,6 +260,21 @@ function fitToPixelBudget(
|
||||
return { width: roundedWidth, height: roundedHeight };
|
||||
}
|
||||
|
||||
function clampWan27DerivedSizeToRatioBounds(
|
||||
size: { width: number; height: number },
|
||||
): { width: number; height: number } {
|
||||
let { width, height } = size;
|
||||
const ratio = width / height;
|
||||
|
||||
if (ratio > 8) {
|
||||
width = floorToStep(height * 8);
|
||||
} else if (ratio < 1 / 8) {
|
||||
height = floorToStep(width * 8);
|
||||
}
|
||||
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
export function getSizeFromAspectRatio(ar: string | null, quality: CliArgs["quality"]): string {
|
||||
const normalizedQuality = normalizeQuality(quality);
|
||||
const sizes = normalizedQuality === "2k" ? LEGACY_STANDARD_SIZES_2K : LEGACY_STANDARD_SIZES;
|
||||
@@ -276,6 +331,77 @@ export function getQwen2SizeFromAspectRatio(ar: string | null, quality: CliArgs[
|
||||
return formatSize(fitted.width, fitted.height);
|
||||
}
|
||||
|
||||
function isWan27ProModel(model: string): boolean {
|
||||
return model.trim().toLowerCase() === "wan2.7-image-pro";
|
||||
}
|
||||
|
||||
function getWan27MaxPixels(model: string, hasReferenceImages: boolean): number {
|
||||
if (isWan27ProModel(model) && !hasReferenceImages) {
|
||||
return MAX_WAN27_PRO_T2I_PIXELS;
|
||||
}
|
||||
return MAX_WAN27_GENERAL_PIXELS;
|
||||
}
|
||||
|
||||
export function getWan27SizeFromAspectRatio(
|
||||
ar: string | null,
|
||||
quality: CliArgs["quality"],
|
||||
maxPixels: number,
|
||||
): string {
|
||||
const normalizedQuality = normalizeQuality(quality);
|
||||
const targetPixels = Math.min(WAN27_TARGET_PIXELS[normalizedQuality], maxPixels);
|
||||
|
||||
if (!ar) {
|
||||
const side = roundToStep(Math.sqrt(targetPixels));
|
||||
return formatSize(side, side);
|
||||
}
|
||||
|
||||
const parsed = parseAspectRatio(ar);
|
||||
if (!parsed) {
|
||||
const side = roundToStep(Math.sqrt(targetPixels));
|
||||
return formatSize(side, side);
|
||||
}
|
||||
|
||||
const ratio = parsed.width / parsed.height;
|
||||
if (ratio < 1 / 8 || ratio > 8) {
|
||||
throw new Error(
|
||||
`DashScope wan2.7 image models support aspect ratios in [1:8, 8:1]. Received "${ar}".`
|
||||
);
|
||||
}
|
||||
|
||||
const rawWidth = Math.sqrt(targetPixels * ratio);
|
||||
const rawHeight = Math.sqrt(targetPixels / ratio);
|
||||
const fitted = fitToPixelBudget(
|
||||
rawWidth,
|
||||
rawHeight,
|
||||
MIN_WAN27_TOTAL_PIXELS,
|
||||
maxPixels,
|
||||
);
|
||||
const bounded = clampWan27DerivedSizeToRatioBounds(fitted);
|
||||
|
||||
return formatSize(bounded.width, bounded.height);
|
||||
}
|
||||
|
||||
function validateWan27Size(size: string, maxPixels: number, model: string): string {
|
||||
const normalized = normalizeSize(size);
|
||||
const parsed = validateSizeFormat(normalized);
|
||||
const totalPixels = parsed.width * parsed.height;
|
||||
if (totalPixels < MIN_WAN27_TOTAL_PIXELS || totalPixels > maxPixels) {
|
||||
const limit = maxPixels === MAX_WAN27_PRO_T2I_PIXELS ? "4096*4096" : "2048*2048";
|
||||
throw new Error(
|
||||
`DashScope ${model} requires total pixels between 768*768 and ${limit} ` +
|
||||
`for the current request. Received ${normalized} (${totalPixels} pixels).`
|
||||
);
|
||||
}
|
||||
const ratio = parsed.width / parsed.height;
|
||||
if (ratio < 1 / 8 || ratio > 8) {
|
||||
throw new Error(
|
||||
`DashScope wan2.7 image models support aspect ratios in [1:8, 8:1]. ` +
|
||||
`Received ${normalized} (ratio ${ratio.toFixed(3)}).`
|
||||
);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function getQwenFixedSizeFromAspectRatio(ar: string | null, quality: CliArgs["quality"]): string {
|
||||
if (quality === "normal") {
|
||||
console.warn(
|
||||
@@ -331,9 +457,16 @@ function validateQwenFixedSize(size: string): string {
|
||||
|
||||
export function resolveSizeForModel(
|
||||
model: string,
|
||||
args: Pick<CliArgs, "size" | "aspectRatio" | "quality">,
|
||||
args: Pick<CliArgs, "size" | "aspectRatio" | "quality"> & { referenceImages?: string[] },
|
||||
): string {
|
||||
const spec = getModelSpec(model);
|
||||
const referenceCount = args.referenceImages?.length ?? 0;
|
||||
|
||||
if (spec.family === "wan27") {
|
||||
const maxPixels = getWan27MaxPixels(model, referenceCount > 0);
|
||||
if (args.size) return validateWan27Size(args.size, maxPixels, model);
|
||||
return getWan27SizeFromAspectRatio(args.aspectRatio, args.quality, maxPixels);
|
||||
}
|
||||
|
||||
if (args.size) {
|
||||
if (spec.family === "qwen2") return validateQwen2Size(args.size);
|
||||
@@ -357,6 +490,14 @@ function buildParameters(
|
||||
family: DashScopeModelFamily,
|
||||
size: string,
|
||||
): Record<string, unknown> {
|
||||
if (family === "wan27") {
|
||||
return {
|
||||
size,
|
||||
n: 1,
|
||||
watermark: false,
|
||||
};
|
||||
}
|
||||
|
||||
const parameters: Record<string, unknown> = {
|
||||
prompt_extend: false,
|
||||
size,
|
||||
@@ -419,23 +560,44 @@ export async function generateImage(
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) throw new Error("DASHSCOPE_API_KEY is required");
|
||||
|
||||
if (args.referenceImages.length > 0) {
|
||||
const spec = getModelSpec(model);
|
||||
|
||||
if (args.referenceImages.length > 0 && spec.family !== "wan27") {
|
||||
throw new Error(
|
||||
"Reference images are not supported with DashScope provider in baoyu-imagine. Use --provider google with a Gemini multimodal model."
|
||||
"Reference images are not supported with this DashScope model. Use a wan2.7 image model (--model wan2.7-image-pro or wan2.7-image), or switch to --provider google with a Gemini multimodal model."
|
||||
);
|
||||
}
|
||||
|
||||
if (args.referenceImages.length > WAN27_MAX_REFERENCE_IMAGES) {
|
||||
throw new Error(
|
||||
`DashScope wan2.7 image models accept at most ${WAN27_MAX_REFERENCE_IMAGES} reference images. Received ${args.referenceImages.length}.`
|
||||
);
|
||||
}
|
||||
|
||||
if (spec.family === "wan27" && args.n !== 1) {
|
||||
throw new Error(
|
||||
"DashScope wan2.7 image models in baoyu-imagine support exactly one output image per request (extra images would be billed but discarded). Remove --n or use --n 1."
|
||||
);
|
||||
}
|
||||
|
||||
const spec = getModelSpec(model);
|
||||
const size = resolveSizeForModel(model, args);
|
||||
const url = `${getBaseUrl()}/api/v1/services/aigc/multimodal-generation/generation`;
|
||||
|
||||
const content: Array<Record<string, unknown>> = [];
|
||||
if (spec.family === "wan27" && args.referenceImages.length > 0) {
|
||||
for (const refPath of args.referenceImages) {
|
||||
content.push({ image: await loadReferenceImage(refPath) });
|
||||
}
|
||||
}
|
||||
content.push({ text: prompt });
|
||||
|
||||
const body = {
|
||||
model,
|
||||
input: {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [{ text: prompt }],
|
||||
content,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -61,8 +61,11 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
};
|
||||
}
|
||||
|
||||
test("MiniMax URL builder normalizes /v1 suffixes", (t) => {
|
||||
useEnv(t, { MINIMAX_BASE_URL: "https://api.minimax.io" });
|
||||
test("MiniMax URL builder uses documented default and normalizes /v1 suffixes", (t) => {
|
||||
useEnv(t, { MINIMAX_BASE_URL: null });
|
||||
assert.equal(buildMinimaxUrl(), "https://api.minimaxi.com/v1/image_generation");
|
||||
|
||||
process.env.MINIMAX_BASE_URL = "https://api.minimax.io";
|
||||
assert.equal(buildMinimaxUrl(), "https://api.minimax.io/v1/image_generation");
|
||||
|
||||
process.env.MINIMAX_BASE_URL = "https://proxy.example.com/custom/v1/";
|
||||
|
||||
@@ -44,7 +44,7 @@ function getApiKey(): string | null {
|
||||
}
|
||||
|
||||
export function buildMinimaxUrl(): string {
|
||||
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimax.io").replace(/\/+$/g, "");
|
||||
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimaxi.com").replace(/\/+$/g, "");
|
||||
return base.endsWith("/v1") ? `${base}/image_generation` : `${base}/v1/image_generation`;
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export async function generateImage(
|
||||
): Promise<Uint8Array> {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) {
|
||||
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimax.io/");
|
||||
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimaxi.com/");
|
||||
}
|
||||
|
||||
const body = await buildRequestBody(prompt, model, args);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-infographic
|
||||
description: Generate professional infographics with 21 layout types and 21 visual styles. Analyzes content, recommends layout×style combinations, and generates publication-ready infographics. Use when user asks to create "infographic", "信息图", "visual summary", "可视化", or "高密度信息大图".
|
||||
version: 1.56.1
|
||||
description: Generate professional infographics with 21 layout types and 22 visual styles. Analyzes content, recommends layout×style combinations, and generates publication-ready infographics. Use when user asks to create "infographic", "信息图", "visual summary", "可视化", or "高密度信息大图".
|
||||
version: 1.58.0
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-infographic
|
||||
@@ -23,11 +23,17 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The backend receives the prompt file (or its content); the file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
@@ -64,14 +70,24 @@ references:
|
||||
- If `usage: direct` AND the chosen backend accepts reference images (e.g., `baoyu-imagine` via `--ref`) → pass the file via the backend's ref parameter
|
||||
- Otherwise → embed extracted `style`/`palette` traits in the prompt text
|
||||
|
||||
## Confirmation Policy
|
||||
|
||||
Default behavior: **confirm before generation**.
|
||||
|
||||
- Treat explicit skill invocation, a file path, a matched keyword shortcut, `EXTEND.md` defaults, and the documented default combination as **recommendation inputs only**. None of them authorizes skipping confirmation.
|
||||
- Do **not** start Step 5 or Step 6 until the user confirms the combination/aspect/language/backend choices.
|
||||
- Skip confirmation only when the current request explicitly says to do so, for example: `--no-confirm`, "直接生成", "不用确认", "跳过确认", "按默认出图", or equivalent wording.
|
||||
- If confirmation is skipped explicitly, state the assumed combination/aspect/language/backend in the next user-facing update before generating.
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Values |
|
||||
|--------|--------|
|
||||
| `--layout` | 21 options (see Layout Gallery), default: bento-grid |
|
||||
| `--style` | 21 options (see Style Gallery), default: craft-handmade |
|
||||
| `--style` | 22 options (see Style Gallery), default: craft-handmade |
|
||||
| `--aspect` | Named: landscape (16:9), portrait (9:16), square (1:1). Custom: any W:H ratio (e.g., 3:4, 4:3, 2.35:1) |
|
||||
| `--lang` | en, zh, ja, etc. |
|
||||
| `--no-confirm` | Skip Step 4 only when the user explicitly requests direct generation without confirmation |
|
||||
| `--ref <files...>` | Reference images (file paths) for style / palette / composition / subject guidance |
|
||||
|
||||
## Layout Gallery (21)
|
||||
@@ -102,7 +118,7 @@ references:
|
||||
|
||||
Full definitions live at `references/layouts/<layout>.md`.
|
||||
|
||||
## Style Gallery (21)
|
||||
## Style Gallery (22)
|
||||
|
||||
| Style | Description |
|
||||
|-------|-------------|
|
||||
@@ -127,6 +143,7 @@ Full definitions live at `references/layouts/<layout>.md`.
|
||||
| `morandi-journal` | Hand-drawn doodle, warm Morandi tones |
|
||||
| `retro-pop-grid` | 1970s retro pop art, Swiss grid, thick outlines |
|
||||
| `hand-drawn-edu` | Macaron pastels, hand-drawn wobble, stick figures |
|
||||
| `retro-popup-pop` | Retro popup collage, vintage UI, thick outlines, flat pop colors |
|
||||
|
||||
Full definitions live at `references/styles/<style>.md`.
|
||||
|
||||
@@ -149,18 +166,19 @@ Full definitions live at `references/styles/<style>.md`.
|
||||
| Product Guide | `dense-modules` + `morandi-journal` |
|
||||
| Technical Guide | `dense-modules` + `pop-laboratory` |
|
||||
| Trendy Guide | `dense-modules` + `retro-pop-grid` |
|
||||
| Retro Pop Guide | `dense-modules` + `retro-popup-pop` |
|
||||
| Educational Diagram | `hub-spoke` + `hand-drawn-edu` |
|
||||
| Process Tutorial | `linear-progression` + `hand-drawn-edu` |
|
||||
|
||||
Default combination: `bento-grid` + `craft-handmade`.
|
||||
Default combination: `bento-grid` + `craft-handmade` (fallback recommendation only — per the [Confirmation Policy](#confirmation-policy), defaults never bypass Step 4).
|
||||
|
||||
## Keyword Shortcuts
|
||||
|
||||
When the user's input contains these keywords, auto-select the layout and promote the listed styles to the top of Step 3 recommendations. Skip content-based layout inference for matched keywords. Append any `Prompt Notes` to the Step 5 prompt.
|
||||
When the user's input contains these keywords, use the mapped layout as the leading Step 3 recommendation and promote the listed styles to the top of the Step 3 list. Skip content-based layout inference for matched keywords. Append any `Prompt Notes` to the Step 5 prompt.
|
||||
|
||||
| User Keyword | Layout | Recommended Styles | Default Aspect | Prompt Notes |
|
||||
|--------------|--------|--------------------|----------------|--------------|
|
||||
| 高密度信息大图 / high-density-info | `dense-modules` | `morandi-journal`, `pop-laboratory`, `retro-pop-grid` | portrait | — |
|
||||
| 高密度信息大图 / high-density-info | `dense-modules` | `morandi-journal`, `pop-laboratory`, `retro-pop-grid`, `retro-popup-pop` | portrait | — |
|
||||
| 信息图 / infographic | `bento-grid` | `craft-handmade` | landscape | Minimalist: clean canvas, ample whitespace, no complex background textures. Simple cartoon elements and icons only. |
|
||||
|
||||
## Output Structure
|
||||
@@ -201,7 +219,7 @@ Check EXTEND.md in priority order — the first one found wins:
|
||||
| Found | Read, parse, display a one-line summary |
|
||||
| Not found | Ask the user with `AskUserQuestion` (see `references/config/first-time-setup.md`) |
|
||||
|
||||
**EXTEND.md supports**: preferred layout/style, default aspect ratio, custom style definitions, language preference.
|
||||
**EXTEND.md supports**: preferred layout/style, default aspect ratio, language preference, preferred image backend, custom style definitions.
|
||||
|
||||
Schema: `references/config/preferences-schema.md`
|
||||
|
||||
@@ -231,7 +249,7 @@ See `references/structured-content-template.md` for detailed format.
|
||||
|
||||
### Step 3: Recommend Combinations
|
||||
|
||||
**3.1 Check Keyword Shortcuts first**: If user input matches a keyword from the **Keyword Shortcuts** table, auto-select the associated layout and prioritize associated styles as top recommendations. Skip content-based layout inference.
|
||||
**3.1 Check Keyword Shortcuts first**: If user input matches a keyword from the **Keyword Shortcuts** table, use the associated layout as the leading recommendation and prioritize associated styles as top recommendations. Skip content-based layout inference.
|
||||
|
||||
**3.2 Otherwise**, recommend 3-5 layout×style combinations based on:
|
||||
- Data structure → matching layout
|
||||
@@ -241,6 +259,8 @@ See `references/structured-content-template.md` for detailed format.
|
||||
|
||||
### Step 4: Confirm Options
|
||||
|
||||
**Hard gate**: this step is mandatory per the [Confirmation Policy](#confirmation-policy) — Steps 5–6 cannot start until the user confirms here (or explicitly opts out with `--no-confirm` / equivalent in the current request).
|
||||
|
||||
Ask the user to confirm the questions below following the [User Input Tools](#user-input-tools) rule at the top of this file (batch into one call if the runtime supports multiple questions; otherwise ask one at a time in priority order).
|
||||
|
||||
| Priority | Question | When | Options |
|
||||
@@ -248,6 +268,7 @@ Ask the user to confirm the questions below following the [User Input Tools](#us
|
||||
| 1 | **Combination** | Always | 3+ layout×style combos with rationale |
|
||||
| 2 | **Aspect** | Always | Named presets (landscape/portrait/square) or custom W:H ratio (e.g., 3:4, 4:3, 2.35:1) |
|
||||
| 3 | **Language** | Only if source ≠ user language | Language for text content |
|
||||
| 4 | **Image Backend** | Only if step 3 of the `## Image Generation Tools` rule needs to ask (no runtime-native tool AND multiple non-native backends, OR `preferred_image_backend: ask`) | Available backends |
|
||||
|
||||
### Step 5: Generate Prompt → `prompts/infographic.md`
|
||||
|
||||
@@ -266,7 +287,7 @@ Combine:
|
||||
|
||||
### Step 6: Generate Image
|
||||
|
||||
1. Select the backend via the `## Image Generation Tools` rule at the top: use whatever is available; if multiple, ask the user once. Do this once per session.
|
||||
1. Resolve the backend per the `## Image Generation Tools` rule at the top of this file.
|
||||
2. Ensure the full final prompt is persisted at `prompts/infographic.md` (already written in Step 5) BEFORE invoking the backend — the file is the reproducibility record.
|
||||
3. **Check for existing file**: Before generating, check if `infographic.png` exists
|
||||
- If exists: Rename to `infographic-backup-YYYYMMDD-HHMMSS.png`
|
||||
@@ -275,7 +296,7 @@ Combine:
|
||||
|
||||
### Step 7: Output Summary
|
||||
|
||||
Report: topic, layout, style, aspect, language, output path, files created.
|
||||
Report: topic, layout, style, aspect, language, image backend, output path, files created.
|
||||
|
||||
## References
|
||||
|
||||
@@ -285,6 +306,15 @@ Report: topic, layout, style, aspect, language, output path, files created.
|
||||
- `references/layouts/<layout>.md` - 21 layout definitions
|
||||
- `references/styles/<style>.md` - 21 style definitions
|
||||
|
||||
## Extension Support
|
||||
## Changing Preferences
|
||||
|
||||
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
|
||||
EXTEND.md lives at the first matching path in Step 1.1. Three ways to change it:
|
||||
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: `references/config/preferences-schema.md`.
|
||||
- **Reconfigure interactively** — delete EXTEND.md (or ask "reconfigure baoyu-infographic preferences" / "重新配置"). The next run re-triggers first-time setup.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `preferred_layout: dense-modules`, `preferred_style: morandi-journal`, `preferred_aspect: portrait`, `language: zh` — shift the Step-3 recommendations and Step-4 defaults (per [Confirmation Policy](#confirmation-policy), these never bypass Step 4).
|
||||
|
||||
@@ -39,7 +39,7 @@ Approach content analysis as a **world-class instructional designer**:
|
||||
| **System/Structure** | Components, architecture, anatomy | structural-breakdown, bento-grid | technical-schematic, ikea-manual |
|
||||
| **Journey/Narrative** | Stories, user flows, milestones | winding-roadmap, story-mountain | storybook-watercolor, comic-strip |
|
||||
| **Overview/Summary** | Multiple topics, feature highlights | bento-grid, periodic-table, dense-modules | chalkboard, bold-graphic |
|
||||
| **Product/Buying Guide** | Multi-dimension comparisons, specs, pitfalls | dense-modules | morandi-journal, pop-laboratory, retro-pop-grid |
|
||||
| **Product/Buying Guide** | Multi-dimension comparisons, specs, pitfalls | dense-modules | morandi-journal, pop-laboratory, retro-pop-grid, retro-popup-pop |
|
||||
|
||||
### 2. Learning Objective Identification
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ description: First-time setup flow for baoyu-infographic preferences
|
||||
|
||||
## Overview
|
||||
|
||||
When no EXTEND.md is found, guide the user through preference setup before generating any infographic.
|
||||
When no EXTEND.md is found, guide the user through preference setup before generating any infographic. Saved preferences shift Step-3 recommendations and Step-4 defaults only — they never bypass Step 4 confirmation (see the `## Confirmation Policy` section in SKILL.md).
|
||||
|
||||
**⛔ BLOCKING OPERATION**: This setup MUST complete before ANY other workflow steps. Do NOT:
|
||||
- Ask about source content or topic
|
||||
@@ -141,13 +141,13 @@ preferred_layout: [selected layout or null]
|
||||
preferred_style: [selected style or null]
|
||||
preferred_aspect: [landscape|portrait|square|null]
|
||||
language: [selected language or null]
|
||||
preferred_image_backend: auto
|
||||
custom_styles: []
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend: auto` is the baked-in default — first-time setup never asks about it. The `## Image Generation Tools` rule in SKILL.md then picks the runtime-native tool (Codex `imagegen`, Hermes `image_generate`, etc.) when one is available, and falls back to installed backends like `baoyu-imagine`.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or trigger setup again:
|
||||
- Delete EXTEND.md to re-trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `references/config/preferences-schema.md`
|
||||
See the `## Changing Preferences` section in `SKILL.md` for the canonical list of common edits (pin backend, change layout/style defaults, retrigger setup). Full schema: `references/config/preferences-schema.md`.
|
||||
|
||||
@@ -17,6 +17,8 @@ preferred_aspect: null # landscape|portrait|square|null (custom W:H also acc
|
||||
|
||||
language: null # zh|en|ja|ko|null (null = auto-detect from source)
|
||||
|
||||
preferred_image_backend: auto # auto|ask|<backend-id>
|
||||
|
||||
custom_styles: # extra style definitions merged with the 21 built-ins
|
||||
- name: my-brand
|
||||
description: "Short description shown in Step 3 recommendations"
|
||||
@@ -33,8 +35,21 @@ custom_styles: # extra style definitions merged with the 21 built-ins
|
||||
| `preferred_style` | string\|null | null | Pre-selected style — surfaces as the top recommendation in Step 3 |
|
||||
| `preferred_aspect` | string\|null | null | Default aspect for Step 4 (named preset or W:H string) |
|
||||
| `language` | string\|null | null | Output language (null = auto-detect from source content) |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. |
|
||||
| `custom_styles` | array | [] | Additional styles available alongside the 21 built-ins |
|
||||
|
||||
Backend resolution logic is documented in the `## Image Generation Tools` section of `SKILL.md`. This doc only defines the field.
|
||||
|
||||
All fields in this schema are defaults only — they shape Step-3 recommendations and Step-4 defaults but never bypass Step 4 confirmation (see the `## Confirmation Policy` section in SKILL.md).
|
||||
|
||||
Example backend ids:
|
||||
|
||||
| Value | Meaning |
|
||||
|-------|---------|
|
||||
| `codex-imagegen` | Codex built-in `imagegen` tool |
|
||||
| `baoyu-imagine` | `baoyu-imagine` skill / script backend |
|
||||
| `image_generate` | Generic runtime image tool such as Hermes |
|
||||
|
||||
## Layout Options
|
||||
|
||||
See the **Layout Gallery (21)** table in `SKILL.md` for the canonical list. Common picks:
|
||||
@@ -87,6 +102,8 @@ language: zh
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend` is omitted above; absence is treated as `auto`.
|
||||
|
||||
## Example: Full Preferences
|
||||
|
||||
```yaml
|
||||
@@ -99,6 +116,8 @@ preferred_aspect: portrait
|
||||
|
||||
language: zh
|
||||
|
||||
preferred_image_backend: codex-imagegen
|
||||
|
||||
custom_styles:
|
||||
- name: my-brand
|
||||
description: "Brand-aligned warm pastel infographic"
|
||||
|
||||
@@ -68,5 +68,6 @@ High-density modular layout with 6-7 typed information modules packed with concr
|
||||
- `pop-laboratory`: Technical precision with coordinate markers and blueprint grid
|
||||
- `morandi-journal`: Hand-drawn warmth with doodle illustrations and organic frames
|
||||
- `retro-pop-grid`: 1970s pop art with strict grid cells and bold contrast
|
||||
- `retro-popup-pop`: Vintage desktop popups with chunky pixel UI for retro-tech dense guides
|
||||
- `corporate-memphis`: Clean business feel for product comparisons
|
||||
- `technical-schematic`: Engineering precision for technical product guides
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# retro-popup-pop
|
||||
|
||||
Retro pixel popup × pop-art collage — content rendered as a stack of 80/90s desktop dialog windows with thick black outlines, flat color fills, and bright cyan or vintage cream backgrounds.
|
||||
|
||||
## Color Palette
|
||||
|
||||
- Background: Bright cyan (#12B8DE) primary canvas, vintage cream (#F5F0E6) alternate
|
||||
- Window fills: Vintage cream (#F5F0E6) and pure white (#FFFFFF)
|
||||
- Primary text and outlines: Pure black (#000000)
|
||||
- Reverse text: Pure white on solid black title bars
|
||||
- Accent fills (small areas only): Salmon pink, sky blue, mustard yellow, mint green — muted retro tones
|
||||
|
||||
## Visual Elements
|
||||
|
||||
- 80s/90s desktop popup windows with title bars, close buttons (×), and chunky borders
|
||||
- Multiple windows tiled or lightly overlapping with offset solid-black drop shadows for depth
|
||||
- ERROR / ALERT / WARNING dialogs to highlight misconceptions, common pitfalls, risks
|
||||
- File-window vignettes labeled with retro filenames (PROBLEMS.EXE, METHOD.PNG, BURNOUT.PSD, FOCUS_SCAN, SYSTEM_ALERT, IDEAS_MISSING)
|
||||
- Progress bars for completion, difficulty, conversion rate, or trend
|
||||
- Pixel-style chunky icons: folders, magnifiers, gears, exclamation marks, floppy disks, hourglasses
|
||||
- Action buttons with thick black borders and short pop-style copy: OK, CANCEL, FIX IT, SCAN, PLAY, RETRY, SAVE
|
||||
- Comparison data rendered as windowed lists or small tabular dialogs
|
||||
- Uniform thick black outlines on every window, button, icon, and divider
|
||||
- Pure 2D flat color fills throughout — no gradients, no glow, no glass
|
||||
|
||||
## Typography
|
||||
|
||||
- Headers: Pixel / dot-matrix / chunky bitmap display fonts, large and high-contrast
|
||||
- Body: Retro monospace or system-style sans-serif, high legibility
|
||||
- Title bars: Reverse white on solid black, all-caps preferred
|
||||
- Decorative all-caps English allowed for filenames, status strings, button labels (PROBLEMS.EXE, OK, CANCEL); body content text remains in the confirmed output language
|
||||
|
||||
## Style Enforcement
|
||||
|
||||
- Every information chunk must live inside a window, dialog, button, or progress bar — no floating elements
|
||||
- Uniform thick black outlines everywhere; offset solid-black drop shadows for stacked-window depth
|
||||
- Limit accent palette to ~4 colors per composition; let cyan or cream dominate the canvas
|
||||
- Maintain low-fi pixel/popup feel; humorous popup copy welcome, polished modern UI prohibited
|
||||
|
||||
## Avoid
|
||||
|
||||
- Modern UI styles: glassmorphism, neumorphism, soft shadows, blurs, gradients
|
||||
- 3D rendering, photorealism, ray-traced shadows
|
||||
- Hand-drawn wobble, watercolor washes, or sketch textures
|
||||
- Smooth anti-aliased curves on icons (prefer chunky pixel/stair-step edges)
|
||||
- Pure black backgrounds — keep cyan or cream as the canvas
|
||||
|
||||
## Best For
|
||||
|
||||
High-density "干货" knowledge guides, common-pitfall and debunking posts, knowledge-pop content for design-savvy or developer audiences, Xiaohongshu-style retro-tech posts, dense-modules portrait infographics.
|
||||
+139
-10
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-post-to-x
|
||||
description: Posts content and articles to X (Twitter). Supports regular posts with images/videos and X Articles (long-form Markdown). Uses real Chrome with CDP to bypass anti-automation. Use when user asks to "post to X", "tweet", "publish to Twitter", or "share on X".
|
||||
version: 1.56.1
|
||||
description: Posts content and articles to X (Twitter). Supports regular posts with images/videos and X Articles (long-form Markdown). In Codex, honor explicit requests for the Codex Chrome plugin/@chrome by using the Chrome Extension workflow; otherwise use Chrome Computer Use when available and fall back to real Chrome CDP scripts only when allowed. Use when user asks to "post to X", "tweet", "publish to Twitter", or "share on X".
|
||||
version: 1.57.1
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-post-to-x
|
||||
@@ -13,7 +13,12 @@ metadata:
|
||||
|
||||
# Post to X (Twitter)
|
||||
|
||||
Posts text, images, videos, and long-form articles to X via real Chrome browser (bypasses anti-bot detection).
|
||||
Posts text, images, videos, and long-form articles to X via a real Chrome browser.
|
||||
|
||||
In Codex, do not conflate these browser paths:
|
||||
- **Codex Chrome plugin / `@chrome` / Chrome Extension**: use the bundled `chrome:Chrome` skill and its Node REPL browser client. This is required whenever the user says "Codex Chrome plugin", "Codex 自带的 Chrome 插件", `@chrome`, or similar.
|
||||
- **Chrome Computer Use**: use `mcp__computer_use__.*` against the visible Google Chrome UI only when the user asks for Computer Use or no Chrome-plugin preference is stated and Computer Use is available.
|
||||
- **CDP script mode**: use only as a fallback when the selected mode is unavailable or the user explicitly asks for CDP/script mode.
|
||||
|
||||
## Script Directory
|
||||
|
||||
@@ -28,15 +33,61 @@ Posts text, images, videos, and long-form articles to X via real Chrome browser
|
||||
**Script Reference**:
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `scripts/x-browser.ts` | Regular posts (text + images) |
|
||||
| `scripts/x-video.ts` | Video posts (text + video) |
|
||||
| `scripts/x-quote.ts` | Quote tweet with comment |
|
||||
| `scripts/x-article.ts` | Long-form article publishing (Markdown) |
|
||||
| `scripts/x-browser.ts` | Regular posts (text + images), CDP fallback |
|
||||
| `scripts/x-video.ts` | Video posts (text + video), CDP fallback |
|
||||
| `scripts/x-quote.ts` | Quote tweet with comment, CDP fallback |
|
||||
| `scripts/x-article.ts` | Long-form article publishing (Markdown), CDP fallback |
|
||||
| `scripts/md-to-html.ts` | Markdown → HTML conversion |
|
||||
| `scripts/copy-to-clipboard.ts` | Copy content to clipboard |
|
||||
| `scripts/paste-from-clipboard.ts` | Send real paste keystroke |
|
||||
| `scripts/check-paste-permissions.ts` | Verify environment & permissions |
|
||||
|
||||
## Execution Mode Selection (Required)
|
||||
|
||||
Choose exactly one mode before interacting with X:
|
||||
|
||||
1. If the user explicitly asks for the Codex Chrome plugin, `@chrome`, the Chrome extension, or "Codex 自带的 Chrome 插件", use **Codex Chrome Plugin Mode**. Do not call Computer Use first.
|
||||
2. If the user explicitly asks for Chrome Computer Use, use **Chrome Computer Use Mode**. Do not fall back to CDP, Playwright, the in-app Browser, or the Chrome plugin without telling the user and getting approval.
|
||||
3. If the user explicitly asks for CDP/script mode, use **CDP Script Mode**.
|
||||
4. Otherwise, prefer **Chrome Computer Use Mode** for regular posts, video posts, and quote tweets. For Markdown **X Articles with local content images**, prefer **CDP Script Mode** because placeholder selection and image-block verification are more reliable than manual DraftJS selection through Computer Use.
|
||||
|
||||
Never use the in-app Browser for X publishing workflows.
|
||||
|
||||
## Codex Chrome Plugin Mode
|
||||
|
||||
Use this mode whenever the user requests the Codex Chrome plugin, `@chrome`, or the Chrome Extension path. This uses the user's real Chrome profile and X login through the bundled Chrome plugin, not Computer Use and not CDP.
|
||||
|
||||
**Setup**
|
||||
1. Load the `chrome:Chrome` skill before browser work.
|
||||
2. Use `tool_search` for `node_repl js` if the Node REPL `js` tool is not already visible.
|
||||
3. Initialize the Chrome browser client exactly as the Chrome skill specifies, then run a lightweight call such as `browser.user.openTabs()` to verify the extension connection.
|
||||
4. If the first lightweight call fails, wait 2 seconds and retry once. If it still fails, follow the Chrome skill's extension checks and recovery steps. If checks pass but communication still fails, ask the user before opening a new Chrome window. Do not switch to Computer Use or CDP silently.
|
||||
|
||||
**General rules**
|
||||
- Use the Chrome plugin's `browser.tabs.*`, `tab.playwright.*`, `tab.cua.*`, and file chooser APIs for X UI actions.
|
||||
- Shell commands are allowed for Markdown preprocessing and clipboard preparation.
|
||||
- If a file upload fails with `Not allowed`, tell the user: `To enable file upload, go to chrome://extensions in Chrome, click Details under the Codex extension, and enable "Allow access to file URLs." See https://developers.openai.com/codex/app/chrome-extension#upload-files for details.`
|
||||
- If the Chrome plugin reports `native pipe is closed`, retry the lightweight browser call once after 2 seconds, then run the Chrome skill health checks. If Chrome is running, the extension is enabled, and the native host manifest is correct, ask permission to open a new Chrome window and retry. Do not keep sending browser actions through the broken pipe.
|
||||
- Never click `Publish`, `Post`, or any externally visible submit action without explicit final confirmation from the user in the current conversation.
|
||||
|
||||
**X Articles**
|
||||
1. Convert Markdown and keep the image map:
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/md-to-html.ts article.md --save-html /tmp/x-article-body.html > /tmp/x-article.json
|
||||
```
|
||||
2. Read the JSON output for `title`, `coverImage`, and `contentImages` (`placeholder` → `localPath`).
|
||||
3. Open or create the article draft at `https://x.com/compose/articles`.
|
||||
4. Upload the cover with the Chrome plugin file chooser flow. If upload is blocked by extension permissions, stop and report the exact permission fix above.
|
||||
5. Fill the title, then copy rich HTML:
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/x-article-body.html
|
||||
```
|
||||
6. Paste into the article body with a real paste keystroke through the Chrome plugin. On macOS use `Meta+V`.
|
||||
7. Verify the editor text contains the article body and `XIMGPH_` placeholders. Do not rely on `tab.clipboard.readText()` as proof of the system clipboard after shell clipboard writes; on macOS verify with `pbpaste` if needed.
|
||||
8. For each `contentImages` item in placeholder order, copy the image with `copy-to-clipboard.ts image <localPath>`, select the exact placeholder text, paste with the Chrome plugin, and verify the placeholder is gone and an image block appeared.
|
||||
9. Open Preview and verify title, cover, body, links, and images.
|
||||
10. Ask for explicit confirmation before clicking `Publish`.
|
||||
|
||||
## Preferences (EXTEND.md)
|
||||
|
||||
Check EXTEND.md in priority order — the first one found wins:
|
||||
@@ -86,6 +137,73 @@ Checks: Chrome, profile isolation, Bun, Accessibility, clipboard, paste keystrok
|
||||
|
||||
---
|
||||
|
||||
## Chrome Computer Use Mode
|
||||
|
||||
Use this mode when the user explicitly asks for Chrome Computer Use, or when no Chrome-plugin preference is stated and Codex can control `Google Chrome` with Computer Use. This uses the user's existing Chrome window, cookies, login, extensions, and X session.
|
||||
|
||||
**General rules**:
|
||||
- Start each assistant turn that controls Chrome by calling `get_app_state` for `Google Chrome`.
|
||||
- Prefer element-index actions when available; use coordinates only for editor text selection or drag selection.
|
||||
- Do not use the in-app Browser, the Chrome plugin, Playwright, or CDP for X UI actions in this mode unless the user approves a mode change.
|
||||
- Never click `Publish`, `Post`, or any externally visible submit action without an explicit final confirmation from the user in the current conversation.
|
||||
|
||||
**Regular posts**:
|
||||
1. Open or navigate Chrome to `https://x.com/compose/post`.
|
||||
2. Type the post text into the composer using Computer Use.
|
||||
3. For each image, run:
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts image /absolute/path/to/image.png
|
||||
```
|
||||
4. Paste with Computer Use (`super+v` on macOS, `control+v` on Windows/Linux), then wait until X finishes uploading media.
|
||||
5. Ask for confirmation before clicking `Post`.
|
||||
|
||||
**Video posts**:
|
||||
1. Open or navigate Chrome to `https://x.com/compose/post`.
|
||||
2. Type the post text into the composer.
|
||||
3. Use the visible media upload/file picker UI to attach the video.
|
||||
4. Wait for upload and processing to complete.
|
||||
5. Ask for confirmation before clicking `Post`.
|
||||
|
||||
**Quote tweets**:
|
||||
1. Open the tweet URL in Chrome.
|
||||
2. Use the visible quote/repost UI to choose Quote.
|
||||
3. Type the comment.
|
||||
4. Ask for confirmation before clicking `Post`.
|
||||
|
||||
**X Articles**:
|
||||
1. Convert Markdown and keep the image map:
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/md-to-html.ts article.md --save-html /tmp/x-article-body.html > /tmp/x-article.json
|
||||
```
|
||||
2. Read the JSON output for `title`, `coverImage`, and `contentImages` (`placeholder` → `localPath`).
|
||||
3. In Chrome, open `https://x.com/compose/articles`, create or open the draft, upload the cover if present, and fill the title.
|
||||
4. Copy rich HTML to the clipboard:
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/x-article-body.html
|
||||
```
|
||||
5. Paste into the article body with Computer Use.
|
||||
6. For each `contentImages` entry in placeholder order:
|
||||
- Copy the image with `copy-to-clipboard.ts image <localPath>`.
|
||||
- Select the exact visible placeholder text such as `XIMGPH_3`.
|
||||
- Paste with Computer Use (`super+v`/`control+v`).
|
||||
- Wait until the X header no longer says `Uploading media...`.
|
||||
- If the placeholder remains, reselect the exact placeholder text and press `BackSpace`. Never press `BackSpace` unless the app state confirms the selected text is exactly that placeholder.
|
||||
7. Verify no `XIMGPH_` placeholders remain and the expected images appear.
|
||||
8. Open Preview and verify title, cover, body, links, and images.
|
||||
9. Ask for explicit confirmation before clicking `Publish`.
|
||||
|
||||
If Computer Use selection or paste becomes unreliable, stop and report the blocker instead of switching to the Chrome plugin or CDP silently.
|
||||
|
||||
---
|
||||
|
||||
## CDP Script Mode (Fallback)
|
||||
|
||||
Use the script sections below only when the selected browser-control mode is unavailable, unreliable, or explicitly not requested. These scripts launch or reuse a real Chrome instance via CDP and keep the browser open for review.
|
||||
|
||||
Do not use CDP Script Mode when the user explicitly requires the Codex Chrome plugin or Chrome Computer Use unless the user approves the fallback after you explain the blocker.
|
||||
|
||||
---
|
||||
|
||||
## Post Type Selection
|
||||
|
||||
Unless the user explicitly specifies the post type:
|
||||
@@ -107,6 +225,8 @@ ${BUN_X} {baseDir}/scripts/x-browser.ts "Hello!" --image ./photo.png
|
||||
|
||||
**Note**: Script opens browser with content filled in. User reviews and publishes manually.
|
||||
|
||||
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, use **Codex Chrome Plugin Mode**. Otherwise, if Chrome Computer Use is enabled, use **Chrome Computer Use Mode** instead of running `x-browser.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Video Posts
|
||||
@@ -126,6 +246,8 @@ ${BUN_X} {baseDir}/scripts/x-video.ts "Check this out!" --video ./clip.mp4
|
||||
|
||||
**Note**: Script opens browser with content filled in. User reviews and publishes manually.
|
||||
|
||||
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, use **Codex Chrome Plugin Mode**. Otherwise, if Chrome Computer Use is enabled, use **Chrome Computer Use Mode** instead of running `x-video.ts`.
|
||||
|
||||
**Limits**: Regular 140s max, Premium 60min. Processing: 30-60s.
|
||||
|
||||
---
|
||||
@@ -147,6 +269,8 @@ ${BUN_X} {baseDir}/scripts/x-quote.ts https://x.com/user/status/123 "Great insig
|
||||
|
||||
**Note**: Script opens browser with content filled in. User reviews and publishes manually.
|
||||
|
||||
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, use **Codex Chrome Plugin Mode**. Otherwise, if Chrome Computer Use is enabled, use **Chrome Computer Use Mode** instead of running `x-quote.ts`.
|
||||
|
||||
---
|
||||
|
||||
## X Articles
|
||||
@@ -167,7 +291,11 @@ ${BUN_X} {baseDir}/scripts/x-article.ts article.md --cover ./cover.jpg
|
||||
|
||||
**Frontmatter**: `title`, `cover_image` supported in YAML front matter.
|
||||
|
||||
**Note**: Script opens browser with article filled in. User reviews and publishes manually.
|
||||
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, follow **Codex Chrome Plugin Mode** above. If the user explicitly requested Chrome Computer Use, follow **Chrome Computer Use Mode**. Otherwise, for Markdown articles with local content images, use `x-article.ts` in **CDP Script Mode** so placeholders can be selected programmatically and the post-composition check can verify image count and remaining `XIMGPH_` text.
|
||||
|
||||
**CDP fallback note**: The script opens browser with article filled in. User reviews and publishes manually unless `--submit` is used.
|
||||
|
||||
**Publish safety**: Do not use `--submit` or click `Publish` unless the user explicitly confirms the final public publish action.
|
||||
|
||||
**Post-Composition Check**: The script automatically verifies after all images are inserted:
|
||||
- Remaining `XIMGPH_` placeholders in editor content
|
||||
@@ -181,7 +309,7 @@ If the check fails (warnings in output), alert the user with the specific issues
|
||||
|
||||
### Chrome debug port not ready
|
||||
|
||||
If a script fails with `Chrome debug port not ready` or `Unable to connect`, kill existing Chrome CDP instances first, then retry:
|
||||
CDP fallback only: if a script fails with `Chrome debug port not ready` or `Unable to connect`, kill existing Chrome CDP instances first, then retry:
|
||||
|
||||
```bash
|
||||
pkill -f "Chrome.*remote-debugging-port" 2>/dev/null; pkill -f "Chromium.*remote-debugging-port" 2>/dev/null; sleep 2
|
||||
@@ -192,7 +320,8 @@ pkill -f "Chrome.*remote-debugging-port" 2>/dev/null; pkill -f "Chromium.*remote
|
||||
## Notes
|
||||
|
||||
- First run: manual login required (session persists)
|
||||
- All scripts only fill content into the browser, user must review and publish manually
|
||||
- In Codex Chrome Plugin Mode and Chrome Computer Use Mode, use the user's existing Chrome session and do not launch a separate CDP profile
|
||||
- CDP scripts only fill content into the browser by default; user must review and publish manually unless `--submit` is explicitly used
|
||||
- Cross-platform: macOS, Linux, Windows
|
||||
|
||||
## Extension Support
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
Publish Markdown articles to X Articles editor with rich text formatting and images.
|
||||
|
||||
## Mode Selection
|
||||
|
||||
In Codex, choose the browser-control mode from the user's wording:
|
||||
|
||||
1. If the user says "Codex Chrome plugin", "Codex 自带的 Chrome 插件", `@chrome`, or Chrome Extension, use **Codex Chrome Plugin Workflow**. Do not try Computer Use first.
|
||||
2. If the user explicitly asks for Chrome Computer Use, use **Computer Use Workflow**.
|
||||
3. If the user explicitly asks for CDP/script mode, use **CDP Script Workflow**.
|
||||
4. Otherwise, use Computer Use when available; if unavailable or blocked, use CDP Script Workflow.
|
||||
|
||||
Never use the in-app Browser for X Article publishing. Never switch away from an explicitly requested mode without explaining the blocker and getting approval.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- X Premium subscription (required for Articles)
|
||||
@@ -10,6 +21,42 @@ Publish Markdown articles to X Articles editor with rich text formatting and ima
|
||||
|
||||
## Usage
|
||||
|
||||
### Codex Chrome Plugin (When Requested)
|
||||
|
||||
Use the `chrome:Chrome` skill and its Node REPL browser client. Verify the connection with a lightweight call such as `browser.user.openTabs()`. If it fails, wait 2 seconds and retry once, then follow the Chrome skill's health checks.
|
||||
|
||||
Prepare the article HTML and image map:
|
||||
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/md-to-html.ts article.md --save-html /tmp/x-article-body.html > /tmp/x-article.json
|
||||
```
|
||||
|
||||
Copy generated HTML as rich text:
|
||||
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/x-article-body.html
|
||||
```
|
||||
|
||||
Use the Chrome plugin's tab, Playwright-wrapper, CUA, clipboard, and file chooser APIs for all X UI operations. If upload fails with `Not allowed`, stop and tell the user to enable file URL access for the Codex Chrome Extension in `chrome://extensions` → Details.
|
||||
|
||||
### Chrome Computer Use
|
||||
|
||||
Prepare the article HTML and image map:
|
||||
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/md-to-html.ts article.md --save-html /tmp/x-article-body.html > /tmp/x-article.json
|
||||
```
|
||||
|
||||
Copy the generated HTML as rich text:
|
||||
|
||||
```bash
|
||||
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/x-article-body.html
|
||||
```
|
||||
|
||||
Then use Codex Computer Use against `Google Chrome` for all X UI operations.
|
||||
|
||||
### CDP Script Fallback
|
||||
|
||||
```bash
|
||||
# Publish markdown article (preview mode)
|
||||
${BUN_X} {baseDir}/scripts/x-article.ts article.md
|
||||
@@ -21,6 +68,8 @@ ${BUN_X} {baseDir}/scripts/x-article.ts article.md --cover ./cover.jpg
|
||||
${BUN_X} {baseDir}/scripts/x-article.ts article.md --submit
|
||||
```
|
||||
|
||||
Do not use `--submit` unless the user has explicitly confirmed the final public publish action.
|
||||
|
||||
## Markdown Format
|
||||
|
||||
```markdown
|
||||
@@ -118,7 +167,63 @@ JSON output:
|
||||
| `1. item` | `<ol><li>` |
|
||||
| `` | Image placeholder |
|
||||
|
||||
## Workflow
|
||||
## Codex Chrome Plugin Workflow
|
||||
|
||||
1. **Load Chrome skill**: use `chrome:Chrome`, not Computer Use.
|
||||
2. **Connect**: initialize the Chrome plugin browser client and verify with `browser.user.openTabs()`.
|
||||
3. **Parse Markdown**: run `md-to-html.ts --save-html /tmp/x-article-body.html > /tmp/x-article.json`.
|
||||
4. **Read the map**: use `/tmp/x-article.json` for `title`, `coverImage`, and `contentImages`.
|
||||
5. **Open X Articles**: open or claim a Chrome tab for `https://x.com/compose/articles`.
|
||||
6. **Create Draft**: click the create/write button if needed, or open the target draft.
|
||||
7. **Upload Cover**: use the Chrome plugin file chooser flow. If file upload returns `Not allowed`, report the Chrome Extension file-access fix and stop.
|
||||
8. **Fill Title**: fill the title field.
|
||||
9. **Paste Content**:
|
||||
- Run `copy-to-clipboard.ts html --file /tmp/x-article-body.html`.
|
||||
- Click the article body.
|
||||
- Press `Meta+V` on macOS or `Control+V` on Windows/Linux through the Chrome plugin.
|
||||
- Verify the article body appeared and contains `XIMGPH_` placeholders. On macOS, use `pbpaste` to verify shell-written system clipboard contents if paste is suspicious; `tab.clipboard.readText()` may not reflect the system clipboard after shell writes.
|
||||
10. **Insert Images**: for each `contentImages` item in placeholder order:
|
||||
- Run `copy-to-clipboard.ts image <localPath>`.
|
||||
- Select the exact placeholder text (`XIMGPH_N`) in the editor.
|
||||
- Press `Meta+V`/`Control+V` with the Chrome plugin.
|
||||
- Wait for X to finish uploading media.
|
||||
- If `XIMGPH_N` remains above the inserted image, reselect that exact text and press `BackSpace`.
|
||||
- Do not press `BackSpace` unless the selected text is exactly the placeholder.
|
||||
11. **Verify**:
|
||||
- Inspect the editor for `XIMGPH_` residue.
|
||||
- Confirm the expected number of image blocks is visible.
|
||||
- Open Preview and verify title, cover, body, links, and images.
|
||||
12. **Publish Safety**: ask the user for explicit final confirmation before clicking `Publish`.
|
||||
|
||||
If the Chrome plugin reports `native pipe is closed`, retry one lightweight browser call after 2 seconds, then run the Chrome skill health checks. If Chrome, the extension, and native host are healthy, ask the user before opening a new Chrome window and retrying.
|
||||
|
||||
## Computer Use Workflow
|
||||
|
||||
1. **Detect Computer Use**: call `get_app_state` for `Google Chrome`; use `tool_search` first if the tools are not visible.
|
||||
2. **Parse Markdown**: run `md-to-html.ts --save-html /tmp/x-article-body.html > /tmp/x-article.json`.
|
||||
3. **Read the map**: use `/tmp/x-article.json` for `title`, `coverImage`, and `contentImages`.
|
||||
4. **Open X Articles**: use Chrome Computer Use to navigate to `https://x.com/compose/articles`.
|
||||
5. **Create Draft**: click the create/write button if needed, or open the target draft.
|
||||
6. **Upload Cover**: if `coverImage` exists, use Chrome's visible upload/file picker UI. If the file picker cannot be operated reliably, stop and ask for help rather than switching to CDP silently.
|
||||
7. **Fill Title**: type the title into the title field.
|
||||
8. **Paste Content**:
|
||||
- Run `copy-to-clipboard.ts html --file /tmp/x-article-body.html`.
|
||||
- Click the article body.
|
||||
- Press `super+v` on macOS or `control+v` on Windows/Linux with Computer Use.
|
||||
9. **Insert Images**: for each `contentImages` item in placeholder order:
|
||||
- Run `copy-to-clipboard.ts image <localPath>`.
|
||||
- Select the exact placeholder text (`XIMGPH_N`) in the editor.
|
||||
- Press `super+v`/`control+v` with Computer Use.
|
||||
- Wait for X to finish uploading media.
|
||||
- If `XIMGPH_N` remains above the inserted image, reselect the exact text and press `BackSpace`.
|
||||
- Do not press `BackSpace` unless the Computer Use state confirms the selected text is exactly the placeholder.
|
||||
10. **Verify**:
|
||||
- Inspect the Computer Use state for `XIMGPH_` residue.
|
||||
- Confirm the expected number of image blocks is visible.
|
||||
- Open Preview and verify title, cover, body, links, and images.
|
||||
11. **Publish Safety**: ask the user for explicit final confirmation before clicking `Publish`.
|
||||
|
||||
## CDP Script Workflow (Fallback)
|
||||
|
||||
1. **Parse Markdown**: Extract title, cover, content images, generate HTML
|
||||
2. **Launch Chrome**: Real browser with CDP, persistent login
|
||||
@@ -137,7 +242,7 @@ JSON output:
|
||||
- Compare expected vs actual image count
|
||||
- Warn if issues found
|
||||
10. **Review**: Browser stays open for 60s preview
|
||||
11. **Publish**: Only with `--submit` flag
|
||||
11. **Publish**: Only with `--submit` flag and explicit user confirmation
|
||||
|
||||
## Example Session
|
||||
|
||||
@@ -145,15 +250,15 @@ JSON output:
|
||||
User: /post-to-x article ./blog/my-post.md --cover ./thumbnail.png
|
||||
|
||||
Claude:
|
||||
1. Parses markdown: title="My Post", 3 content images
|
||||
2. Launches Chrome with CDP
|
||||
3. Navigates to x.com/compose/articles
|
||||
4. Clicks create button
|
||||
1. Detects that the user requested the Codex Chrome plugin
|
||||
2. Parses markdown: title="My Post", 3 content images
|
||||
3. Saves `/tmp/x-article-body.html` and `/tmp/x-article.json`
|
||||
4. Uses the Chrome plugin to open X Articles and create a draft
|
||||
5. Uploads thumbnail.png as cover
|
||||
6. Fills title "My Post"
|
||||
7. Pastes HTML content
|
||||
7. Pastes HTML content with a real Chrome paste
|
||||
8. Inserts 3 images at placeholder positions
|
||||
9. Reports: "Article composed. Review and use --submit to publish."
|
||||
9. Opens Preview and asks before publishing
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -162,6 +267,10 @@ Claude:
|
||||
- **Cover upload fails**: Check file path and format (PNG, JPEG)
|
||||
- **Images not inserting**: Verify placeholders exist in pasted content
|
||||
- **Content not pasting**: Check HTML clipboard: `${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/test.html`
|
||||
- **Chrome plugin `native pipe is closed`**: retry once after 2 seconds, then run Chrome skill checks; ask before opening a new Chrome window if checks pass.
|
||||
- **Chrome plugin upload `Not allowed`**: enable file URL access for the Codex Chrome Extension in `chrome://extensions` → Details.
|
||||
- **Computer Use unavailable**: Use the CDP fallback script, unless the user explicitly required Chrome Computer Use.
|
||||
- **Placeholder remains after paste**: Select only the placeholder text and press BackSpace after upload completes.
|
||||
|
||||
## How It Works
|
||||
|
||||
@@ -172,7 +281,19 @@ Claude:
|
||||
- Downloads remote images locally
|
||||
- Returns structured JSON
|
||||
|
||||
2. `x-article.ts` publishes via CDP:
|
||||
2. The Codex Chrome plugin publishes through the user's real Chrome session when explicitly requested:
|
||||
- Uses the user's active Chrome profile and logged-in X session
|
||||
- Uses the Chrome Extension browser client rather than Computer Use or CDP
|
||||
- Uses `copy-to-clipboard.ts` for rich HTML and image clipboard payloads
|
||||
- Keeps the final publish click under user confirmation
|
||||
|
||||
3. Chrome Computer Use publishes through the user's visible Chrome UI:
|
||||
- Uses the user's active Chrome profile and logged-in X session
|
||||
- Uses `copy-to-clipboard.ts` for rich HTML and image clipboard payloads
|
||||
- Uses real keystrokes (`super+v`/`control+v`) through Codex Computer Use
|
||||
- Keeps the final publish click under user confirmation
|
||||
|
||||
4. `x-article.ts` publishes via CDP as a fallback:
|
||||
- Launches real Chrome (bypasses detection)
|
||||
- Uses persistent profile (saved login)
|
||||
- Navigates and fills editor via DOM manipulation
|
||||
|
||||
@@ -6,6 +6,17 @@ Detailed documentation for posting text and images to X.
|
||||
|
||||
If you prefer step-by-step control:
|
||||
|
||||
### Step 0: Prefer Chrome Computer Use in Codex
|
||||
|
||||
When running inside Codex, first detect whether Chrome Computer Use is enabled:
|
||||
|
||||
1. If Computer Use tools are already visible, call `get_app_state` for `Google Chrome`.
|
||||
2. If not, use `tool_search` for `computer-use get_app_state click press_key drag scroll Google Chrome`, then call `get_app_state`.
|
||||
3. If `get_app_state` succeeds, use the user's real Chrome with Computer Use for all X UI actions.
|
||||
4. Use CDP scripts only when Computer Use is unavailable or explicitly requested.
|
||||
|
||||
If the user explicitly asks for Chrome Computer Use, do not use Playwright, the in-app Browser, or CDP without approval.
|
||||
|
||||
### Step 1: Copy Image to Clipboard
|
||||
|
||||
```bash
|
||||
@@ -25,27 +36,15 @@ ${BUN_X} {baseDir}/scripts/paste-from-clipboard.ts --app "Google Chrome" --retri
|
||||
${BUN_X} {baseDir}/scripts/paste-from-clipboard.ts --delay 200
|
||||
```
|
||||
|
||||
### Step 3: Use Playwright MCP (if Chrome session available)
|
||||
### Step 3: Use Chrome Computer Use (Preferred)
|
||||
|
||||
```bash
|
||||
# Navigate
|
||||
mcp__playwright__browser_navigate url="https://x.com/compose/post"
|
||||
|
||||
# Get element refs
|
||||
mcp__playwright__browser_snapshot
|
||||
|
||||
# Type text
|
||||
mcp__playwright__browser_click element="editor" ref="<ref>"
|
||||
mcp__playwright__browser_type element="editor" ref="<ref>" text="Your content"
|
||||
|
||||
# Paste image (after copying to clipboard)
|
||||
mcp__playwright__browser_press_key key="Meta+v" # macOS
|
||||
# or
|
||||
mcp__playwright__browser_press_key key="Control+v" # Windows/Linux
|
||||
|
||||
# Screenshot to verify
|
||||
mcp__playwright__browser_take_screenshot filename="preview.png"
|
||||
```
|
||||
1. Use `get_app_state` for `Google Chrome`.
|
||||
2. Navigate Chrome to `https://x.com/compose/post` if needed.
|
||||
3. Click the composer and type the post text.
|
||||
4. Copy each image to the clipboard with `copy-to-clipboard.ts image <path>`.
|
||||
5. Press `super+v` on macOS or `control+v` on Windows/Linux with Computer Use.
|
||||
6. Wait until X finishes media upload.
|
||||
7. Ask for explicit confirmation before clicking `Post`.
|
||||
|
||||
## Image Support
|
||||
|
||||
@@ -59,12 +58,12 @@ mcp__playwright__browser_take_screenshot filename="preview.png"
|
||||
User: /post-to-x "Hello from Claude!" --image ./screenshot.png
|
||||
|
||||
Claude:
|
||||
1. Runs: ${BUN_X} {baseDir}/scripts/x-browser.ts "Hello from Claude!" --image ./screenshot.png
|
||||
2. Chrome opens with X compose page
|
||||
3. Text is typed into editor
|
||||
4. Image is copied to clipboard and pasted
|
||||
5. Browser stays open 30s for preview
|
||||
6. Reports: "Post composed. Use --submit to post."
|
||||
1. Detects Chrome Computer Use
|
||||
2. Opens X compose in the user's real Chrome
|
||||
3. Types text into editor
|
||||
4. Copies image to clipboard and pastes with Computer Use
|
||||
5. Waits for upload and verifies the preview
|
||||
6. Asks before clicking Post
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -80,7 +79,13 @@ Claude:
|
||||
|
||||
## How It Works
|
||||
|
||||
The `x-browser.ts` script uses Chrome DevTools Protocol (CDP) to:
|
||||
In Chrome Computer Use mode:
|
||||
1. Codex controls the user's visible Google Chrome window
|
||||
2. Text is typed through the real UI
|
||||
3. Images are copied to the system clipboard and pasted with real keystrokes
|
||||
4. The user confirms before the final public post
|
||||
|
||||
The `x-browser.ts` script is the CDP fallback. It uses Chrome DevTools Protocol (CDP) to:
|
||||
1. Launch real Chrome (not Playwright) with `--disable-blink-features=AutomationControlled`
|
||||
2. Use persistent profile directory for saved login sessions
|
||||
3. Interact with X via CDP commands (Runtime.evaluate, Input.dispatchKeyEvent)
|
||||
|
||||
@@ -5,6 +5,7 @@ import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
import frontMatter from 'front-matter';
|
||||
import hljs from 'highlight.js/lib/common';
|
||||
@@ -458,7 +459,9 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
await main().catch((err) => {
|
||||
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
||||
process.exit(1);
|
||||
});
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
await main().catch((err) => {
|
||||
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
|
||||
// Check if we're on the articles list page (has Write button)
|
||||
console.log('[x-article] Looking for Write button...');
|
||||
const writeButtonFound = await waitForElement('[data-testid="empty_state_button_text"]', 10_000);
|
||||
const writeButtonFound = await waitForElement('[data-testid="empty_state_button_text"]', 30_000);
|
||||
|
||||
if (writeButtonFound) {
|
||||
console.log('[x-article] Clicking Write button...');
|
||||
@@ -172,6 +172,40 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
|
||||
expression: `document.querySelector('[data-testid="empty_state_button_text"]')?.click()`,
|
||||
}, { sessionId });
|
||||
await sleep(2000);
|
||||
} else {
|
||||
console.log('[x-article] Write button not found, looking for create button...');
|
||||
const createClicked = await cdp.send<{ result: { value: boolean } }>('Runtime.evaluate', {
|
||||
expression: `(() => {
|
||||
const selectors = [
|
||||
'button[aria-label="create"]',
|
||||
'button[aria-label="Create"]',
|
||||
'button[aria-label*="create" i]',
|
||||
'button[aria-label*="write" i]',
|
||||
'a[href="/compose/articles"]'
|
||||
];
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el instanceof HTMLElement) {
|
||||
el.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const buttons = [...document.querySelectorAll('button')];
|
||||
const byText = buttons.find((el) => /^(create|write)$/i.test((el.textContent || '').trim()));
|
||||
if (byText instanceof HTMLElement) {
|
||||
byText.click();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})()`,
|
||||
returnByValue: true,
|
||||
}, { sessionId });
|
||||
if (createClicked.result.value) {
|
||||
console.log('[x-article] Create button clicked');
|
||||
await sleep(2000);
|
||||
} else {
|
||||
console.log('[x-article] Create button not found');
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for editor (title textarea)
|
||||
|
||||
@@ -218,6 +218,7 @@ async function launchChromeOnce(
|
||||
|
||||
try {
|
||||
await waitForChromeDebugPort(port, 30_000, { includeLastError: true });
|
||||
chrome.unref();
|
||||
return { chrome, port };
|
||||
} catch (error) {
|
||||
killChrome(chrome);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-slide-deck
|
||||
description: Generates professional slide deck images from content. Creates outlines with style instructions, then generates individual slide images. Use when user asks to "create slides", "make a presentation", "generate deck", "slide deck", or "PPT".
|
||||
version: 1.56.1
|
||||
version: 1.56.2
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-slide-deck
|
||||
@@ -27,14 +27,31 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-slide-[slug].md`) BEFORE invoking any backend. The file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) above are examples — substitute the local equivalents under the same rule.
|
||||
|
||||
## Confirmation Policy
|
||||
|
||||
Default behavior: **confirm before generation**.
|
||||
|
||||
- Treat explicit skill invocation, a file path, matched signals/presets, and `EXTEND.md` defaults as **recommendation inputs only**. None of them authorizes skipping confirmation.
|
||||
- Do **not** start Step 3 or later until the user completes Step 2.
|
||||
- Skip confirmation only when the current request explicitly says to do so, for example: "直接生成", "不用确认", "跳过确认", "按默认出幻灯片", or equivalent wording.
|
||||
- If confirmation is skipped explicitly, state the assumed style / audience / slide-count / language / backend in the next user-facing update before generating.
|
||||
|
||||
## Language
|
||||
|
||||
Respond in the user's language across questions, progress reports, error messages, and the completion summary. Keep technical tokens (style names, file paths, code) in English.
|
||||
@@ -213,6 +230,8 @@ Save findings to `analysis.md`: topic, audience, signals, recommended style and
|
||||
|
||||
### Step 2: Confirmation ⚠️ REQUIRED
|
||||
|
||||
**Hard gate**: this step is mandatory per the [Confirmation Policy](#confirmation-policy) — Steps 3+ cannot start until the user confirms here (or explicitly opts out with "直接生成" / equivalent wording in the current request).
|
||||
|
||||
**Round 1 (always)** — batch five questions in one `AskUserQuestion` call: style, audience, slide count, review-outline?, review-prompts?. Verbatim options in `references/confirmation.md`.
|
||||
|
||||
Summary displayed before the questions:
|
||||
@@ -320,4 +339,14 @@ See `references/modification-guide.md` for full details.
|
||||
- For sensitive public figures, prefer stylized alternatives to avoid likeness issues.
|
||||
- Maintain visual consistency via the session ID when the backend supports it.
|
||||
|
||||
Custom configurations via EXTEND.md. See Step 1.1 for paths and schema.
|
||||
## Changing Preferences
|
||||
|
||||
EXTEND.md lives at the first matching path listed in Step 1.1. Two ways to change it:
|
||||
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: `references/config/preferences-schema.md`.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `preferred_style: blueprint`, `preferred_audience: experts`, `language: zh`.
|
||||
|
||||
@@ -12,6 +12,7 @@ style: blueprint # Preset name OR "custom"
|
||||
audience: general # beginners | intermediate | experts | executives | general
|
||||
language: auto # auto | en | zh | ja | etc.
|
||||
review: true # true = review outline before generation
|
||||
preferred_image_backend: auto # auto | ask | <backend-id>
|
||||
|
||||
## Custom Dimensions (only when style: custom)
|
||||
dimensions:
|
||||
@@ -40,6 +41,7 @@ custom_styles:
|
||||
| `audience` | string | `general` | Default target audience |
|
||||
| `language` | string | `auto` | Output language (auto = detect from input) |
|
||||
| `review` | boolean | `true` | Show outline review before generation |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. Resolution logic is documented in `SKILL.md`'s `## Image Generation Tools` section. |
|
||||
|
||||
### Custom Dimensions
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: baoyu-xhs-images
|
||||
description: "[Deprecated: use baoyu-image-cards] Generates Xiaohongshu (Little Red Book) image card series with 12 visual styles, 8 layouts, and 3 color palettes. Breaks content into 1-10 cartoon-style image cards optimized for XHS engagement. Use when user mentions \"小红书图片\", \"XHS images\", \"RedNote infographics\", \"小红书种草\", \"小绿书\", \"微信图文\", \"微信贴图\", or wants social media infographic series for Chinese platforms."
|
||||
version: 1.56.1
|
||||
version: 1.56.2
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-xhs-images
|
||||
@@ -24,14 +24,31 @@ Concrete `AskUserQuestion` references below are examples — substitute the loca
|
||||
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
When this skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Hermes `image_generate`, `baoyu-imagine`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- If the current runtime exposes a native image tool (e.g., Codex `imagegen`, Hermes `image_generate`), use it. Runtime-native tools are preferred by default — agents that know their own tool inventory should surface the native one here.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-imagine`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends. Users change the pinned backend via the `## Changing Preferences` section below.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) above are examples — substitute the local equivalents under the same rule.
|
||||
|
||||
## Confirmation Policy
|
||||
|
||||
Default behavior: **confirm before generation**.
|
||||
|
||||
- Treat explicit skill invocation, a file path, matched signals/presets, and `EXTEND.md` defaults as **recommendation inputs only**. None of them authorizes skipping confirmation.
|
||||
- Do **not** start Step 3 until the user completes Step 2.
|
||||
- Skip confirmation only when the current request explicitly says to do so, for example: `--yes`, "直接生成", "不用确认", "跳过确认", "按默认出图", or equivalent wording.
|
||||
- If confirmation is skipped explicitly, state the assumed strategy / style / layout / palette / count / backend in the next user-facing update before generating.
|
||||
|
||||
## Language
|
||||
|
||||
Respond in the user's language across questions, progress, errors, and completion summary. Keep technical tokens (style names, file paths, code) in English.
|
||||
@@ -292,6 +309,8 @@ Check these paths in order; first hit wins:
|
||||
|
||||
### Step 2: Smart Confirm ⚠️ REQUIRED
|
||||
|
||||
**Hard gate**: this step is mandatory per the [Confirmation Policy](#confirmation-policy) — Step 3 cannot start until the user confirms here (or explicitly opts out with `--yes` / equivalent wording in the current request).
|
||||
|
||||
Goal: present the auto-recommended plan and let the user confirm or adjust. Skip this step entirely under `--yes` — proceed with Path A using the analysis and any CLI overrides.
|
||||
|
||||
**Display summary** before asking:
|
||||
@@ -418,4 +437,16 @@ Always update the prompt file before regenerating — it's the source of truth a
|
||||
- For sensitive public figures, use stylized cartoon alternatives.
|
||||
- Smart Confirm (Step 2) is required; Detailed mode adds a second confirmation (2a + 2c).
|
||||
|
||||
Custom configurations via EXTEND.md. See Step 0 for paths and schema.
|
||||
## Changing Preferences
|
||||
|
||||
EXTEND.md lives at the first matching path listed in Step 0. Three ways to change it:
|
||||
|
||||
- **Edit directly** — open EXTEND.md and change fields. Full schema: `references/config/preferences-schema.md`.
|
||||
- **Reconfigure interactively** — delete EXTEND.md (or ask "reconfigure baoyu-xhs-images preferences" / "重新配置"). The next run re-triggers first-time setup.
|
||||
- **Common one-line edits**:
|
||||
- `preferred_image_backend: auto` — default; runtime-native tool wins, falls back to the only installed backend, asks only if multiple non-native are present.
|
||||
- `preferred_image_backend: codex-imagegen` — pin to Codex's built-in.
|
||||
- `preferred_image_backend: baoyu-imagine` — pin to the baoyu-imagine skill.
|
||||
- `preferred_image_backend: ask` — confirm backend every run.
|
||||
- `preferred_style: notion`, `preferred_layout: dense`, `preferred_palette: macaron`, `language: zh`.
|
||||
- `watermark.enabled: true` + `watermark.content: "@handle"` — add a watermark.
|
||||
|
||||
@@ -110,13 +110,13 @@ preferred_style:
|
||||
description: ""
|
||||
preferred_layout: null
|
||||
language: null
|
||||
preferred_image_backend: auto
|
||||
custom_styles: []
|
||||
---
|
||||
```
|
||||
|
||||
`preferred_image_backend: auto` is the baked-in default — first-time setup does not ask about it. The `## Image Generation Tools` rule in SKILL.md then picks the runtime-native tool (Codex `imagegen`, Hermes `image_generate`, etc.) when available, and falls back to installed backends.
|
||||
|
||||
## Modifying Preferences Later
|
||||
|
||||
Users can edit EXTEND.md directly or run setup again:
|
||||
- Delete EXTEND.md to trigger setup
|
||||
- Edit YAML frontmatter for quick changes
|
||||
- Full schema: `config/preferences-schema.md`
|
||||
See the `## Changing Preferences` section in `SKILL.md` for the canonical list of common edits (pin backend, change defaults, retrigger setup). Full schema: `preferences-schema.md`.
|
||||
|
||||
@@ -24,6 +24,8 @@ preferred_layout: null # sparse|balanced|dense|list|comparison|flow
|
||||
|
||||
language: null # zh|en|ja|ko|auto
|
||||
|
||||
preferred_image_backend: auto # auto|ask|<backend-id>
|
||||
|
||||
custom_styles:
|
||||
- name: my-style
|
||||
description: "Style description"
|
||||
@@ -49,6 +51,7 @@ custom_styles:
|
||||
| `preferred_style.description` | string | "" | Custom notes/override |
|
||||
| `preferred_layout` | string | null | Layout preference or null |
|
||||
| `language` | string | null | Output language (null = auto-detect) |
|
||||
| `preferred_image_backend` | string | `auto` | Image backend selection. `auto` = prefer runtime-native tool, fall back to the only installed backend, ask if multiple non-native are present. `ask` = always confirm on every run. `<backend-id>` (e.g., `codex-imagegen`, `baoyu-imagine`, `image_generate`) = pin this backend when available; fall back to `auto` when it isn't. Absent = `auto`. Resolution logic is documented in `SKILL.md`'s `## Image Generation Tools` section. |
|
||||
| `custom_styles` | array | [] | User-defined styles |
|
||||
|
||||
## Position Options
|
||||
@@ -104,6 +107,8 @@ preferred_layout: dense
|
||||
|
||||
language: zh
|
||||
|
||||
preferred_image_backend: codex-imagegen
|
||||
|
||||
custom_styles:
|
||||
- name: corporate
|
||||
description: "Professional B2B style"
|
||||
|
||||
Reference in New Issue
Block a user