[codex] Refactor skills into focused references (#135)

* docs: add runtime-neutral User Input Tools convention across skills

Introduce docs/user-input-tools.md as the author-side canonical source
and inline the tool-selection rule into every SKILL.md that prompts
the user. Also add Skill Self-Containment and User Input Tools sections
to CLAUDE.md and the copy-verbatim template to docs/creating-skills.md,
so skills stay portable across Claude Code, Codex, Hermes, and other
runtimes.

* feat: runtime-neutral image generation convention across skills

- Introduce inline `## Image Generation Tools` rule in every rendering SKILL.md so skills delegate backend choice instead of hard-coding one; author-side canonical copy lives in docs/image-generation-tools.md.
- Add `## Reference Images` support (`--ref`, frontmatter `references:` with direct/style/palette usage) to all seven image-rendering skills.
- Move build-batch.ts (with ref propagation into batch JSON) from baoyu-article-illustrator to baoyu-imagine so non-backend skills don't own backend-specific scripts; update baoyu-image-gen stub in sync and relax the CLAUDE.md deprecation note accordingly.

* refactor: slim heavy SKILL.md files and move detail to references/

Trim the four largest active skills and move presets, option tables, and
confirmation scripts into per-skill references/ so SKILL.md stays focused
on the decision flow.

- baoyu-slide-deck: 761→258, + styles-gallery.md, confirmation.md
- baoyu-image-cards: 657→280, + gallery.md, confirmation.md
- baoyu-post-to-wechat: 518→267, + multi-account.md, api-setup.md
- baoyu-imagine: 500→230, + providers/, usage-examples.md

Also un-deprecate baoyu-image-gen (drop stub warning) so it stays functional
alongside baoyu-imagine, and update CLAUDE.md to reflect that both
superseded skills are kept in sync rather than stubbed.

* refactor: slim four medium SKILL.md files into references/

Continue the P2 pattern on the next tier of skills — move option catalogs,
per-provider/adapter detail, and repeated EXTEND.md path boilerplate into
their own references so SKILL.md stays focused on the decision flow.

- baoyu-comic: 380→297 (art/tone/preset tables → auto-selection.md;
  Step 7 expanded detail → workflow.md)
- baoyu-infographic: 312→207 (layouts/styles/combinations/keywords →
  gallery.md; ASCII box tables → markdown tables)
- baoyu-format-markdown: 376→296 (title + summary generation →
  title-summary.md; ASCII box tables → markdown tables)
- baoyu-url-to-markdown: 334→169 (quality gate + recovery →
  quality-gate.md; adapters + media download → adapters.md)

* chore: sync deprecated skills with their replacements

Per project policy, baoyu-xhs-images and baoyu-image-gen are kept
functional alongside the active skills they were superseded by. Sync
their SKILL.md bodies and references/ to the slimmed baoyu-image-cards
and baoyu-imagine versions respectively, so cross-cutting fixes stay
consistent. Only the frontmatter (name, description, version, homepage)
differs — content is identical.

- baoyu-xhs-images: 657→281 (synced with baoyu-image-cards + new
  confirmation.md, gallery.md)
- baoyu-image-gen: 408→231 (synced with baoyu-imagine + new
  providers/, usage-examples.md)

* refactor: collapse EXTEND.md boilerplate into priority tables

Replace the dual bash/powershell existence-check blocks and ASCII box
art with a single markdown priority table across nine SKILL.md files.
The runtime-neutral phrasing removes shell-specific snippets without
losing the priority semantics.

* fix: address refactor-skills branch review findings

- image-gen: restore EXTEND.md paths to baoyu-image-gen (were pointing at
  baoyu-imagine) and mark descriptions of both deprecated skills as
  [Deprecated].
- xhs-images: sync neon/warm palettes with image-cards to add the
  "do not render color names/hex as visible text" safety sentence.
- infographic: restore Layout Gallery (21), Style Gallery (21),
  Recommended Combinations, and Keyword Shortcuts inline (previous
  refactor split them out but SKILL.md still depended on them), and add
  the missing references/config/first-time-setup.md + preferences-schema.md.
- image-cards / xhs-images / slide-deck / format-markdown: restore the
  sections that got over-slimmed into references/ (galleries, presets,
  dimensions, auto-selection, style x layout matrix, title/summary flow)
  and drop the now-empty shell files.
- docs/image-generation-tools.md: note that backend skills themselves
  (baoyu-imagine, baoyu-image-gen, baoyu-danger-gemini-web) are exempt
  from the ## Image Generation Tools section requirement.

* feat(image-gen): sync Z.AI GLM-Image provider from baoyu-imagine

Add Z.AI as a full provider in the deprecated baoyu-image-gen skill so
it stays in sync with baoyu-imagine's provider list.

- new scripts/providers/zai.ts + zai.test.ts (verbatim port; test
  factory trimmed to match image-gen's CliArgs shape).
- types.ts: "zai" added to Provider union and default_model.
- main.ts: rate-limit defaults, provider help text, env var help,
  --provider validation, loadProviderModule, detectProvider
  auto-detect chain, getModelForProvider, YAML parser allow-lists.
- references/config: Q2e Z.AI model question + zai slot in the
  preferences schema and batch.provider_limits.

Scope is intentionally limited to the Z.AI chain; unrelated drift
between image-gen and imagine (OpenAI image-API dialect,
aspectRatioSource, imageSizeSource) is left alone.

* docs: align inline-convention wording and note backend-skill exemption

- docs/user-input-tools.md: fix stale "links here" wording so it
  matches the inline convention already enforced everywhere else.
- CLAUDE.md §Image Generation Tools: inline the backend-skill
  exemption so readers don't need to cross-reference
  docs/image-generation-tools.md.
This commit is contained in:
Jim Liu 宝玉
2026-04-19 00:48:44 -05:00
committed by GitHub
parent 5b20f9a746
commit 2c800c670a
58 changed files with 3895 additions and 3269 deletions
+10
View File
@@ -7,6 +7,16 @@ description: Universal release workflow. Auto-detects version files and changelo
Universal release workflow supporting any project type with multi-language changelog. Universal release workflow supporting any project type with multi-language changelog.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Quick Start ## Quick Start
Just run `/release-skills` - auto-detects your project configuration. Just run `/release-skills` - auto-detects your project configuration.
+22 -4
View File
@@ -46,16 +46,32 @@ Execute: `${BUN_X} skills/<skill>/scripts/main.ts [options]`
| Rule | Description | | Rule | Description |
|------|-------------| |------|-------------|
| **Load project skills first** | Project skills override system/user-level skills with same name | | **Load project skills first** | Project skills override system/user-level skills with same name |
| **Default image generation** | Use `skills/baoyu-imagine/SKILL.md` unless user specifies otherwise | | **Default image generation** | Use whatever image backend is available in the current runtime; if multiple are available, ask the user which to use. See `## Image Generation Tools` below. |
Priority: project `skills/``$HOME/.baoyu-skills/` → system-level. Priority: project `skills/``$HOME/.baoyu-skills/` → system-level.
## Skill Self-Containment
Each skill under `skills/` (and `.claude/skills/`) is distributed and consumed independently — the folder may be extracted, copied into another project, or loaded without the rest of this repo. Therefore:
- **Never link from `SKILL.md` or its `references/` to files outside the skill's own directory.** This includes `docs/`, sibling skills, and the repo root. Relative paths like `../../docs/foo.md` break when the skill is used standalone.
- **Inline any shared convention** (e.g., user-input rules, image-generation backend selection) directly in the skill rather than referencing an out-of-skill doc.
- Shared docs under `docs/` exist for **repo-author guidance only** — they may be referenced from `CLAUDE.md` and `docs/creating-skills.md`, but NOT from any `SKILL.md`. This applies to `docs/user-input-tools.md`, `docs/image-generation-tools.md`, `docs/image-generation.md`, and any other `docs/` file.
## User Input Tools
Skills that prompt users for choices MUST declare the tool-selection convention **inline** in exactly one place per `SKILL.md` — a `## User Input Tools` section near the top. Do NOT link out to [docs/user-input-tools.md](docs/user-input-tools.md); that doc is the author-side canonical source — copy its body into each SKILL.md. Concrete `AskUserQuestion` mentions elsewhere in a skill are treated as examples — other runtimes substitute their local equivalent under the rule.
## Image Generation Tools
Skills that render images MUST declare the backend-selection convention **inline** in exactly one place per `SKILL.md` — a `## Image Generation Tools` section near the top (after `## User Input Tools`). Do NOT link out to [docs/image-generation-tools.md](docs/image-generation-tools.md); that doc is the author-side canonical source — copy its body into each SKILL.md. Concrete tool names (`imagegen`, `image_generate`, `baoyu-imagine`) elsewhere in a skill are treated as examples — other runtimes substitute their local equivalent under the rule. The rule is stateless: use whatever backend is available; if multiple, ask the user once; if none, ask how to proceed. Every rendered image's full prompt must be written to a standalone `prompts/NN-*.md` file before any backend is invoked. Backend skills (`baoyu-imagine`, `baoyu-image-gen`, `baoyu-danger-gemini-web`) are exempt — they render directly rather than selecting a backend.
## Deprecated Skills ## Deprecated Skills
| Skill | Note | | Skill | Note |
|-------|------| |-------|------|
| `baoyu-image-gen` | Migrated to `baoyu-imagine`. Do NOT add to `.claude-plugin/marketplace.json`. Do NOT update README for this skill. | | `baoyu-image-gen` | Superseded by `baoyu-imagine`. Not in `.claude-plugin/marketplace.json`. Kept functional — sync any cross-cutting changes with `baoyu-imagine`. |
| `baoyu-xhs-images` | Migrated to `baoyu-image-cards`. Do NOT add to `.claude-plugin/marketplace.json`. Do NOT update README for this skill. | | `baoyu-xhs-images` | Superseded by `baoyu-image-cards`. Not in `.claude-plugin/marketplace.json`. Kept functional — sync any cross-cutting changes with `baoyu-image-cards`. Do NOT update README for this skill. |
## Release Process ## Release Process
@@ -77,7 +93,9 @@ All skills MUST use `baoyu-` prefix. Details: [docs/creating-skills.md](docs/cre
| Topic | File | | Topic | File |
|-------|------| |-------|------|
| Image generation guidelines | [docs/image-generation.md](docs/image-generation.md) | | Image generation output guidelines | [docs/image-generation.md](docs/image-generation.md) |
| Image generation backend selection | [docs/image-generation-tools.md](docs/image-generation-tools.md) |
| User input tool convention | [docs/user-input-tools.md](docs/user-input-tools.md) |
| Chrome profile platform paths | [docs/chrome-profile.md](docs/chrome-profile.md) | | Chrome profile platform paths | [docs/chrome-profile.md](docs/chrome-profile.md) |
| Comic style maintenance | [docs/comic-style-maintenance.md](docs/comic-style-maintenance.md) | | Comic style maintenance | [docs/comic-style-maintenance.md](docs/comic-style-maintenance.md) |
| ClawHub/OpenClaw publishing | [docs/publishing.md](docs/publishing.md) | | ClawHub/OpenClaw publishing | [docs/publishing.md](docs/publishing.md) |
+39 -1
View File
@@ -127,7 +127,7 @@ test -f "$HOME/.baoyu-skills/<skill-name>/EXTEND.md" && echo "user"
| Result | Action | | Result | Action |
|--------|--------| |--------|--------|
| Found | Read, parse, display summary | | Found | Read, parse, display summary |
| Not found | Ask user with AskUserQuestion | | Not found | Ask user via the runtime's user-input tool (see [user-input-tools.md](user-input-tools.md)) |
``` ```
End of SKILL.md should include: End of SKILL.md should include:
@@ -135,3 +135,41 @@ End of SKILL.md should include:
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options. Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
``` ```
## User Input Tools Section (Required)
Every SKILL.md that prompts the user for choices MUST include exactly one `## User Input Tools` section near the top (right after the intro, before the main workflow). The rule must be **inlined** — do NOT link to `docs/user-input-tools.md` (skills are self-contained; see [CLAUDE.md → Skill Self-Containment](../CLAUDE.md)). The author-side canonical reference lives at [user-input-tools.md](user-input-tools.md); copy its body into each new SKILL.md.
Standard snippet (copy verbatim):
```markdown
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
```
## Image Generation Tools Section (Required for image-gen skills)
Every SKILL.md that renders images — whether by calling an image-generation API directly or by delegating to another skill — MUST include exactly one `## Image Generation Tools` section near the top (after `## User Input Tools`, before the main workflow). The rule must be **inlined** — do NOT link to `docs/image-generation-tools.md` (skills are self-contained; see [CLAUDE.md → Skill Self-Containment](../CLAUDE.md)). The author-side canonical reference lives at [image-generation-tools.md](image-generation-tools.md); copy its body into each new SKILL.md.
Standard snippet (copy verbatim):
```markdown
## Image Generation Tools
When this skill needs to render an image:
- **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.
**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.
```
+27
View File
@@ -0,0 +1,27 @@
# Image Generation Tools
Skills in this repo are loaded by multiple agent runtimes (Claude Code, Codex, Hermes, other agents, bare CLI). Each runtime exposes a different image-generation capability — some have a runtime-native tool (Codex `imagegen`, Hermes `image_generate`), others rely on an installed skill (`baoyu-imagine`, or user-defined). This document defines the canonical **backend-selection rule** every skill that renders images follows so skills stay portable.
## The Rule
When a skill needs to render an image:
- **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.
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.
## Prompt File Requirement (hard)
Regardless of which backend is chosen, every skill that renders images MUST 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 allows switching backends without regenerating prompts.
## How Skills Declare This
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).
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
Skills that **are themselves** image-generation backends — currently `baoyu-imagine`, `baoyu-image-gen` (deprecated), and `baoyu-danger-gemini-web` — do NOT include a `## Image Generation Tools` section. They render directly via their own provider integrations and have no need to "select a backend." The rule applies only to consumer skills that delegate rendering to whatever backend the runtime exposes.
+15 -14
View File
@@ -1,30 +1,31 @@
# Image Generation Guidelines # Image Generation Guidelines
Skills that require image generation MUST delegate to available image generation skills. Skills that require image generation MUST delegate to available image generation tools (runtime-native tools or installed skills).
**Backend selection convention**: see [image-generation-tools.md](image-generation-tools.md) for the runtime-neutral rule. Short version: use whatever backend is available; if multiple, ask the user once; if none, ask how to proceed. This document covers output conventions (naming, paths) that apply regardless of which backend is selected.
## Skill Selection ## Skill Selection
**Default**: `skills/baoyu-imagine/SKILL.md` (unless user specifies otherwise). 1. Follow the rule in [image-generation-tools.md](image-generation-tools.md): use whatever backend is available; ask only on ambiguity.
2. Read the chosen backend's documentation for parameters and capabilities.
1. Read skill's SKILL.md for parameters and capabilities 3. If user requests a specific backend, honor it.
2. If user requests different skill, check `skills/` for alternatives
3. Only ask user when multiple viable options exist
## Generation Flow Template ## Generation Flow Template
```markdown ```markdown
### Step N: Generate Images ### Step N: Generate Images
**Skill Selection**: **Backend Selection**:
1. Check available skills (`baoyu-imagine` default, or `baoyu-danger-gemini-web`) 1. Detect available image-generation tools/skills (runtime-native + installed)
2. Read selected skill's SKILL.md for parameters 2. If one available → use it. If multiple → ask user once. If none → ask how to proceed.
3. If multiple skills available, ask user to choose 3. Read the chosen backend's docs for parameters
**Generation Flow**: **Generation Flow**:
1. Call skill with prompt, output path, and skill-specific parameters 1. Write the full prompt to `prompts/NN-{type}-[slug].md` BEFORE invoking the backend
2. Generate sequentially by default (batch parallel only when user has multiple prompts) 2. Call backend with the prompt (or prompt file), output path, and parameters
3. Output progress: "Generated X/N" 3. Generate sequentially by default (batch parallel only when backend supports it and user has multiple prompts)
4. On failure, auto-retry once before reporting error 4. Output progress: "Generated X/N"
5. On failure, auto-retry once before reporting error
``` ```
**Batch Parallel** (`baoyu-imagine` only): concurrent workers with per-provider throttling via `batch.max_workers` in EXTEND.md. **Batch Parallel** (`baoyu-imagine` only): concurrent workers with per-provider throttling via `batch.max_workers` in EXTEND.md.
+17
View File
@@ -0,0 +1,17 @@
# User Input Tools
Skills in this repo are loaded by multiple agent runtimes (Claude Code, other agents, bare CLI). Each runtime exposes a different API for asking the user questions. This document defines the canonical **tool-selection rule** every skill follows so skills stay portable.
## Tool Selection (priority order)
1. **Prefer built-in user-input tools** if the current agent runtime exposes one — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback to plain text**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching rule**:
- If the tool supports **multiple questions per call** (e.g., `AskUserQuestion`): **Combine all applicable questions into a single call. Do NOT split into separate calls.**
- If the tool supports **only one question per call** (e.g., single-prompt `clarify`): ask **one question per call, in priority order**.
## How Skills Declare This
Each `SKILL.md` that uses interactive user input includes **exactly one** `## User Input Tools` section (typically near the top, right after the intro) that **inlines** this rule. Do NOT link here from a SKILL.md — skills are self-contained (see [CLAUDE.md → Skill Self-Containment](../CLAUDE.md)). This document is the author-side canonical source; copy its body into each SKILL.md. The rule then governs every user-input interaction in that skill and its `references/` files.
Specific mentions of a concrete tool (e.g., `AskUserQuestion`) elsewhere in a skill are **concrete examples** — agents in other runtimes apply the rule above and substitute the local equivalent. Tool-specific parameters (e.g., `header:`, `multiSelect:`) are illustrative; runtimes without those knobs can omit them.
+40 -19
View File
@@ -11,6 +11,34 @@ metadata:
Analyze articles, identify illustration positions, generate images with Type × Style × Palette consistency. Analyze articles, identify illustration positions, generate images with Type × Style × Palette consistency.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Image Generation Tools
When this skill needs to render an image:
- **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.
**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.
## 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.
Full detection, storage, and processing rules are in [references/workflow.md](references/workflow.md) (Step 1.0 saves to `references/NN-ref-{slug}.{ext}`; Step 5.3 processes per-illustration usage `direct | style | palette`). When the chosen backend supports batch input, `direct`-usage entries in each prompt file's `references:` frontmatter should be propagated into its batch payload so backends can pass them through (e.g. `baoyu-imagine` accepts `ref` per task).
## Three Dimensions ## Three Dimensions
| Dimension | Controls | Examples | | Dimension | Controls | Examples |
@@ -53,20 +81,13 @@ See [references/styles.md](references/styles.md) for Core Styles, full gallery,
**1.5 Load Preferences (EXTEND.md) ⛔ BLOCKING** **1.5 Load Preferences (EXTEND.md) ⛔ BLOCKING**
```bash Check EXTEND.md in priority order — the first one found wins:
# macOS, Linux, WSL, Git Bash
test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project"
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "xdg"
test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
```
```powershell | Priority | Path | Scope |
# PowerShell (Windows) |----------|------|-------|
if (Test-Path .baoyu-skills/baoyu-article-illustrator/EXTEND.md) { "project" } | 1 | `.baoyu-skills/baoyu-article-illustrator/EXTEND.md` | Project |
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" } | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-article-illustrator/EXTEND.md` | XDG |
if (Test-Path "$xdg/baoyu-skills/baoyu-article-illustrator/EXTEND.md") { "xdg" } | 3 | `$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md` | User home |
if (Test-Path "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md") { "user" }
```
| Result | Action | | Result | Action |
|--------|--------| |--------|--------|
@@ -118,18 +139,18 @@ Full template: [references/workflow.md](references/workflow.md#step-4-generate-o
### Step 5: Generate Images ### Step 5: Generate Images
**BLOCKING: Prompt files MUST be saved before ANY image generation.** **BLOCKING: Prompt files MUST be saved before ANY image generation.** This is a hard requirement regardless of which backend is chosen — the prompt file is the reproducibility record.
**Execution strategy**: When multiple illustrations have saved prompt files and the task is now plain generation, prefer `baoyu-imagine` batch mode (`build-batch.ts``--batchfile`) over spawning subagents. Use subagents only when each image still needs separate prompt iteration or creative exploration.
1. For each illustration, create a prompt file per [references/prompt-construction.md](references/prompt-construction.md) 1. For each illustration, create a prompt file per [references/prompt-construction.md](references/prompt-construction.md)
2. Save to `prompts/NN-{type}-{slug}.md` with YAML frontmatter 2. Save to `prompts/NN-{type}-{slug}.md` with YAML frontmatter
3. Prompts **MUST** use type-specific templates with structured sections (ZONES / LABELS / COLORS / STYLE / ASPECT) 3. Prompts **MUST** use type-specific templates with structured sections (ZONES / LABELS / COLORS / STYLE / ASPECT)
4. LABELS **MUST** include article-specific data: actual numbers, terms, metrics, quotes 4. LABELS **MUST** include article-specific data: actual numbers, terms, metrics, quotes
5. **DO NOT** pass ad-hoc inline prompts to `--prompt` without saving prompt files first 5. **DO NOT** pass ad-hoc inline prompts to `--prompt` without saving prompt files first
6. Select generation skill, process references (`direct`/`style`/`palette`) 6. 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 before any generation.
7. Apply watermark if EXTEND.md enabled 7. **Execution strategy**: When multiple illustrations have saved prompt files and the task is now plain generation, prefer the chosen backend's batch interface (if it offers one) over spawning subagents. Use subagents only when each image still needs separate prompt iteration or creative exploration. If the backend has no batch interface, generate sequentially.
8. Generate from saved prompt files; retry once on failure 8. Process references (`direct`/`style`/`palette`) per prompt frontmatter
9. Apply watermark if EXTEND.md enabled
10. Generate from saved prompt files; retry once on failure
Full procedures: [references/workflow.md](references/workflow.md#step-5-generate-images) Full procedures: [references/workflow.md](references/workflow.md#step-5-generate-images)
@@ -331,7 +331,7 @@ Prompt Files:
**DO NOT** pass ad-hoc inline text to `--prompt` without first saving prompt files. The generation command should either use `--promptfiles prompts/NN-{type}-{slug}.md` or read the saved file content for `--prompt`. **DO NOT** pass ad-hoc inline text to `--prompt` without first saving prompt files. The generation command should either use `--promptfiles prompts/NN-{type}-{slug}.md` or read the saved file content for `--prompt`.
**Execution choice**: **Execution choice**:
- If multiple illustrations already have saved prompt files and the task is now plain generation, prefer `baoyu-imagine` batch mode (`build-batch.ts` -> `main.ts --batchfile`) - If multiple illustrations already have saved prompt files and the task is now plain generation, prefer the chosen backend's batch interface (if it offers one); otherwise generate sequentially
- Use subagents only when each illustration still needs separate prompt rewriting, style exploration, or other per-image reasoning before generation - Use subagents only when each illustration still needs separate prompt rewriting, style exploration, or other per-image reasoning before generation
**CRITICAL - References in Frontmatter**: **CRITICAL - References in Frontmatter**:
@@ -1,85 +0,0 @@
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import test from "node:test";
const execFileAsync = promisify(execFile);
const repoRoot = path.resolve(import.meta.dirname, "..", "..", "..");
const scriptPath = path.join(repoRoot, "skills", "baoyu-article-illustrator", "scripts", "build-batch.ts");
async function makeFixture(): Promise<{
root: string;
outlinePath: string;
promptsDir: string;
outputPath: string;
}> {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "baoyu-article-illustrator-build-batch-"));
const outlinePath = path.join(root, "outline.md");
const promptsDir = path.join(root, "prompts");
const outputPath = path.join(root, "batch.json");
await fs.mkdir(promptsDir, { recursive: true });
await fs.writeFile(
outlinePath,
`## Illustration 1
**Position**: demo
**Purpose**: demo
**Visual Content**: demo
**Filename**: 01-demo.png
`,
);
await fs.writeFile(path.join(promptsDir, "01-demo.md"), "A demo prompt\n");
return { root, outlinePath, promptsDir, outputPath };
}
async function runBuildBatch(args: string[]): Promise<void> {
await execFileAsync(process.execPath, ["--import", "tsx", scriptPath, ...args], {
cwd: repoRoot,
});
}
test("build-batch omits default model so baoyu-imagine can resolve env or EXTEND defaults", async () => {
const fixture = await makeFixture();
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.equal(batch.tasks.length, 1);
assert.equal(batch.tasks[0]?.provider, "replicate");
assert.equal(Object.hasOwn(batch.tasks[0]!, "model"), false);
});
test("build-batch preserves explicit model overrides", async () => {
const fixture = await makeFixture();
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
"--model",
"acme/custom-model",
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.equal(batch.tasks[0]?.model, "acme/custom-model");
});
+84 -119
View File
@@ -15,14 +15,60 @@ metadata:
Create original knowledge comics with flexible art style × tone combinations. Create original knowledge comics with flexible art style × tone combinations.
## Usage ## User Input Tools
```bash When this skill prompts the user, follow this tool-selection rule (priority order):
/baoyu-comic posts/turing-story/source.md
/baoyu-comic article.md --art manga --tone warm 1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
/baoyu-comic # then paste content 2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Image Generation Tools
When this skill needs to render an image:
- **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.
**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.
## Reference Images
Users may supply reference images to guide art style, palette, scene composition, or subject. This is **separate from** the auto-generated character sheet (Step 7.1) — both can coexist: user refs guide the look, the character sheet anchors recurring character identity.
**Intake**: Accept via `--ref <files...>` or when the user provides file paths / pastes images in conversation.
- File path(s) → copy to `refs/NN-ref-{slug}.{ext}` alongside the comic output
- Pasted image with no path → ask the user for the path (per the User Input Tools rule above), or extract style traits verbally as a text fallback
- No reference → skip this section
**Usage modes** (per reference):
| Usage | Effect |
|-------|--------|
| `direct` | Pass the file to the backend as a reference image on every page (or selected pages) |
| `style` | Extract style traits (line treatment, texture, mood) and append to every page's prompt body |
| `palette` | Extract hex colors and append to every page's prompt body |
**Record in each page's prompt frontmatter** when refs exist:
```yaml
references:
- ref_id: 01
filename: 01-ref-scene.png
usage: direct
``` ```
**At generation time**:
- Verify each referenced file exists on disk
- If `usage: direct` AND the chosen backend accepts multiple reference images → pass both the character sheet (Step 7.2) and the user refs via the backend's ref parameter; compress images first per Step 7.1's guidance to avoid payload failures
- If the backend accepts only one ref → prefer the character sheet for pages with recurring characters; embed user-ref traits in the prompt body instead
- For `style`/`palette` usage → embed extracted traits in every page's prompt text (applies regardless of backend capability)
## Options ## Options
### Visual Dimensions ### Visual Dimensions
@@ -34,6 +80,7 @@ Create original knowledge comics with flexible art style × tone combinations.
| `--layout` | standard (default), cinematic, dense, splash, mixed, webtoon, four-panel | Panel arrangement | | `--layout` | standard (default), cinematic, dense, splash, mixed, webtoon, four-panel | Panel arrangement |
| `--aspect` | 3:4 (default, portrait), 4:3 (landscape), 16:9 (widescreen) | Page aspect ratio | | `--aspect` | 3:4 (default, portrait), 4:3 (landscape), 16:9 (widescreen) | Page aspect ratio |
| `--lang` | auto (default), zh, en, ja, etc. | Output language | | `--lang` | auto (default), zh, en, ja, etc. | Output language |
| `--ref <files...>` | File paths | Reference images applied to every page for style / palette / scene guidance. See [Reference Images](#reference-images) above. |
### Partial Workflow Options ### Partial Workflow Options
@@ -46,72 +93,23 @@ Create original knowledge comics with flexible art style × tone combinations.
Details: [references/partial-workflows.md](references/partial-workflows.md) Details: [references/partial-workflows.md](references/partial-workflows.md)
### Art Styles (画风) ### Art, Tone & Preset Catalogue
| Style | 中文 | Description | - **Art styles** (6): `ligne-claire`, `manga`, `realistic`, `ink-brush`, `chalk`, `minimalist`. Full definitions at `references/art-styles/<style>.md`.
|-------|------|-------------| - **Tones** (7): `neutral`, `warm`, `dramatic`, `romantic`, `energetic`, `vintage`, `action`. Full definitions at `references/tones/<tone>.md`.
| `ligne-claire` | 清线 | Uniform lines, flat colors, European comic tradition (Tintin, Logicomix) | - **Presets** (5) with special rules beyond plain art+tone:
| `manga` | 日漫 | Large eyes, manga conventions, expressive emotions |
| `realistic` | 写实 | Digital painting, realistic proportions, sophisticated |
| `ink-brush` | 水墨 | Chinese brush strokes, ink wash effects |
| `chalk` | 粉笔 | Chalkboard aesthetic, hand-drawn warmth |
| `minimalist` | 极简 | Clean black line art, limited spot color, stick-figure characters |
### Tones (基调) | Preset | Equivalent | Hook |
|--------|-----------|------|
| `ohmsha` | manga + neutral | Visual metaphors, no talking heads, gadget reveals |
| `wuxia` | ink-brush + action | Qi effects, combat visuals, atmospheric |
| `shoujo` | manga + romantic | Decorative elements, eye details, romantic beats |
| `concept-story` | manga + warm | Visual symbol system, growth arc, dialogue+action balance |
| `four-panel` | minimalist + neutral + four-panel layout | 起承转合 structure, B&W + spot color, stick-figure characters |
| Tone | 中文 | Description | Full rules at `references/presets/<preset>.md` — load the file when a preset is picked.
|------|------|-------------|
| `neutral` | 中性 | Balanced, rational, educational |
| `warm` | 温馨 | Nostalgic, personal, comforting |
| `dramatic` | 戏剧 | High contrast, intense, powerful |
| `romantic` | 浪漫 | Soft, beautiful, decorative elements |
| `energetic` | 活力 | Bright, dynamic, exciting |
| `vintage` | 复古 | Historical, aged, period authenticity |
| `action` | 动作 | Speed lines, impact effects, combat |
### Preset Shortcuts - **Compatibility matrix** and **content-signal → preset** table live in [references/auto-selection.md](references/auto-selection.md). Read it before recommending combinations in Step 2.
Presets with special rules beyond art+tone:
| Preset | Equivalent | Special Rules |
|--------|-----------|---------------|
| `--style ohmsha` | `--art manga --tone neutral` | Visual metaphors, NO talking heads, gadget reveals |
| `--style wuxia` | `--art ink-brush --tone action` | Qi effects, combat visuals, atmospheric elements |
| `--style shoujo` | `--art manga --tone romantic` | Decorative elements, eye details, romantic beats |
| `--style concept-story` | `--art manga --tone warm` | Visual symbol system, growth arc, dialogue+action balance |
| `--style four-panel` | `--art minimalist --tone neutral --layout four-panel` | 起承转合 4-panel structure, B&W + spot color, stick-figure characters |
### Compatibility Matrix
| Art Style | ✓✓ Best | ✓ Works | ✗ Avoid |
|-----------|---------|---------|---------|
| ligne-claire | neutral, warm | dramatic, vintage, energetic | romantic, action |
| manga | neutral, romantic, energetic, action | warm, dramatic | vintage |
| realistic | neutral, warm, dramatic, vintage | action | romantic, energetic |
| ink-brush | neutral, dramatic, action, vintage | warm | romantic, energetic |
| chalk | neutral, warm, energetic | vintage | dramatic, action, romantic |
| minimalist | neutral | warm, energetic | dramatic, vintage, romantic, action |
Details: [references/auto-selection.md](references/auto-selection.md)
## Auto Selection
Content signals determine default art + tone + layout (or preset):
| Content Signals | Recommended |
|-----------------|-------------|
| Tutorial, how-to, programming, educational | **ohmsha** preset |
| Pre-1950, classical, ancient | realistic + vintage |
| Personal story, mentor | ligne-claire + warm |
| Martial arts, wuxia | **wuxia** preset |
| Romance, school life | **shoujo** preset |
| Psychology, motivation, business narrative | **concept-story** preset |
| Business allegory, fable, parable, short insight, 四格 | **four-panel** preset |
| Biography, balanced | ligne-claire + neutral |
**When preset is recommended**: Load `references/presets/{preset}.md` and apply all special rules.
Details: [references/auto-selection.md](references/auto-selection.md)
## Script Directory ## Script Directory
@@ -218,72 +216,39 @@ Analyze → [Check Existing?] → [Confirm: Style + Reviews] → Storyboard →
### Step 7: Image Generation ### Step 7: Image Generation
**7.1 Generate character sheet (conditional)**: **Pick a backend once per session** using the `## Image Generation Tools` rule at the top. If the backend is a repo skill (e.g., `baoyu-imagine`), read its `SKILL.md` and use its documented interface rather than its scripts.
Character sheet is recommended for multi-page comics with recurring characters, but **NOT required** for all presets: **7.1 Character sheet** — generate it (to `characters/characters.png`, aspect `4:3`) when the comic is multi-page with recurring characters. Skip for simple presets (e.g., four-panel minimalist) or single-page comics. Compress to JPEG before use-as-`--ref` (`sips -s format jpeg -s formatOptions 80 …` on macOS, `pngquant --quality=65-80 …` elsewhere) to avoid payload failures. The prompt file at `characters/characters.md` must exist before invoking the backend.
| Condition | Action | **7.2 Pages** — each page's prompt MUST already be at `prompts/NN-{cover|page}-[slug].md` before invoking the backend; the file is the reproducibility record. Strategy depends on the character sheet:
|-----------|--------|
| Multi-page comic with detailed characters | Generate character sheet (recommended) |
| Preset with simplified characters (e.g., four-panel minimalist) | Skip — prompt descriptions are sufficient |
| Single-page comic | Skip unless characters are complex |
**When generating character sheet**: | Character sheet | Backend `--ref` | Strategy |
- **Backup rule**: If `characters/characters.png` exists, rename to `characters/characters-backup-YYYYMMDD-HHMMSS.png` |-----------------|-----------------|----------|
- Invoke an installed image generation skill such as `baoyu-imagine` | Exists | Supported | Pass sheet as `--ref` on every page |
- Read that skill's `SKILL.md` and follow its documented interface rather than calling its scripts directly | Exists | Not supported | Prepend character descriptions to every prompt file |
- Use `characters/characters.md` as the prompt-file input | Skipped | — | All descriptions inline in prompt |
- Save output to `characters/characters.png`
- Use aspect ratio `4:3`
**Compress character sheet** (recommended when using as `--ref`): **Backup rule**: existing `prompts/…md` and `…png` files → rename with `-backup-YYYYMMDD-HHMMSS` suffix before regenerating. Aspect ratio from storyboard (default `3:4`; preset may override).
- Use available image compression skill (if any)
- Or system tools: `sips -s format jpeg -s formatOptions 80 input.png --out output.jpg` (macOS)
- Or: `pngquant --quality=65-80 input.png -o output.png`
- Compression reduces API payload size and avoids `--ref` failures
**7.2 Generate each page**: **`--ref` failure recovery**: compress sheet → retry → still fails → drop `--ref` and embed character descriptions in the prompt text.
| Character Sheet | Skill Capability | Strategy | Full step-by-step workflow (analysis, storyboard, review gates, regeneration variants): [references/workflow.md](references/workflow.md).
|-----------------|------------------|----------|
| Exists | Supports `--ref` | Pass `characters/characters.png` with EVERY page |
| Exists | No `--ref` support | Prepend character descriptions to EVERY prompt file |
| Skipped | — | Prompt file contains all character descriptions inline |
**`--ref` failure recovery**: If generation fails with `--ref`:
1. **Compress**: Convert reference image to JPEG with reduced quality:
`sips -s format jpeg -s formatOptions 70 characters.png --out characters-compressed.jpg`
2. **Retry** with compressed image as `--ref`
3. **If still fails**: Fall back to generating WITHOUT `--ref` (prompt-only, character descriptions embedded in prompt text)
**Backup rules for page generation**:
- If prompt file exists: rename to `prompts/NN-{cover|page}-[slug]-backup-YYYYMMDD-HHMMSS.md`
- If image file exists: rename to `NN-{cover|page}-[slug]-backup-YYYYMMDD-HHMMSS.png`
- Invoke the installed image generation skill for each page
- Use `prompts/01-page-xxx.md` as the prompt-file input
- Save output to `01-page-xxx.png`
- Use aspect ratio from storyboard (default `3:4`, preset may override)
- If character sheet exists and skill supports reference images, pass as `--ref`
**Full workflow details**: [references/workflow.md](references/workflow.md)
### EXTEND.md Paths ⛔ BLOCKING ### EXTEND.md Paths ⛔ BLOCKING
**CRITICAL**: If EXTEND.md not found, MUST complete first-time setup before ANY other questions or steps. Do NOT proceed to content analysis, do NOT ask about art style, do NOT ask about tone — ONLY complete the preferences setup first. If EXTEND.md is not found, first-time setup is **blocking** — complete it before any content analysis or style/tone questions.
| Path | Location | | Priority | Path | Scope |
|------|----------| |----------|------|-------|
| `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory | | 1 | `.baoyu-skills/baoyu-comic/EXTEND.md` | Project |
| `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home | | 2 | `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home |
| Result | Action | | Result | Action |
|--------|--------| |--------|--------|
| Found | Read, parse, display summary → Continue | | Found | Read, parse, display summary → continue |
| Not found | ⛔ **BLOCKING**: Run first-time setup ONLY ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Complete and save EXTEND.md → Then continue | | Not found | ⛔ Run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → save EXTEND.md → continue |
**EXTEND.md Supports**: Watermark | Preferred art/tone/layout | Custom style definitions | Character presets | Language preference **EXTEND.md supports**: watermark, preferred art/tone/layout, custom style definitions, character presets, language preference. Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md).
Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
## References ## References
+8 -31
View File
@@ -25,40 +25,17 @@ Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-compress-image/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-compress-image/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-compress-image/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-compress-image/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md` | User home |
```
```powershell If none found, use defaults.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-compress-image/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-compress-image/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md") { "user" }
```
┌────────────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: Default format, default quality, keep-original preference.
│ Path │ Location │
├────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-compress-image/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md │ User home │
└────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default format | Default quality | Keep original preference
## Usage ## Usage
+26 -34
View File
@@ -11,28 +11,27 @@ metadata:
Generate elegant cover images for articles with 5-dimensional customization. Generate elegant cover images for articles with 5-dimensional customization.
## Usage ## User Input Tools
```bash When this skill prompts the user, follow this tool-selection rule (priority order):
# Auto-select dimensions based on content
/baoyu-cover-image path/to/article.md
# Quick mode: skip confirmation 1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
/baoyu-cover-image article.md --quick 2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
# Specify dimensions Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
/baoyu-cover-image article.md --type conceptual --palette warm --rendering flat-vector
# Style presets (shorthand for palette + rendering) ## Image Generation Tools
/baoyu-cover-image article.md --style blueprint
# With reference images When this skill needs to render an image:
/baoyu-cover-image article.md --ref style-ref.png
# Direct content input - **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.
/baoyu-cover-image --palette mono --aspect 1:1 --quick - **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
[paste content] - **If none are available**, tell the user and ask how to proceed.
```
**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.
## Options ## Options
@@ -130,21 +129,13 @@ Analyze + Save Refs → [Output Dir] → [Confirm: 6 Dimensions] → Prompt →
### Step 0: Load Preferences ⛔ BLOCKING ### Step 0: Load Preferences ⛔ BLOCKING
Check EXTEND.md existence (priority: project → user): Check EXTEND.md in priority order — the first one found wins:
```bash
# macOS, Linux, WSL, Git Bash
test -f .baoyu-skills/baoyu-cover-image/EXTEND.md && echo "project"
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-cover-image/EXTEND.md" && echo "xdg"
test -f "$HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md" && echo "user"
```
```powershell | Priority | Path | Scope |
# PowerShell (Windows) |----------|------|-------|
if (Test-Path .baoyu-skills/baoyu-cover-image/EXTEND.md) { "project" } | 1 | `.baoyu-skills/baoyu-cover-image/EXTEND.md` | Project |
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" } | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-cover-image/EXTEND.md` | XDG |
if (Test-Path "$xdg/baoyu-skills/baoyu-cover-image/EXTEND.md") { "xdg" } | 3 | `$HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md` | User home |
if (Test-Path "$HOME/.baoyu-skills/baoyu-cover-image/EXTEND.md") { "user" }
```
| Result | Action | | Result | Action |
|--------|--------| |--------|--------|
@@ -196,12 +187,13 @@ Save to `prompts/cover.md`. Template: [references/workflow/prompt-template.md](r
### Step 4: Generate Image ### Step 4: Generate Image
1. **Backup existing** `cover.png` if regenerating 1. **Backup existing** `cover.png` if regenerating
2. **Check image generation skills**; if multiple, ask preference 2. **Select 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 before any generation.
3. **Process references** from prompt frontmatter: 3. **Write the full final prompt** to `prompts/01-cover-[slug].md` (hard requirement) BEFORE invoking the backend.
4. **Process references** from prompt frontmatter:
- `direct` usage → pass via `--ref` (use ref-capable backend) - `direct` usage → pass via `--ref` (use ref-capable backend)
- `style`/`palette` → extract traits, append to prompt - `style`/`palette` → extract traits, append to prompt
4. **Generate**: Call skill with prompt file, output path, aspect ratio 5. **Generate**: Call the chosen backend with the prompt file, output path, aspect ratio
5. On failure: auto-retry once 6. On failure: auto-retry once
### Step 5: Completion Report ### Step 5: Completion Report
+18 -31
View File
@@ -15,6 +15,16 @@ metadata:
Text/image generation via Gemini Web API. Supports reference images and multi-turn conversations. Text/image generation via Gemini Web API. Supports reference images and multi-turn conversations.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
**Important**: All scripts are located in the `scripts/` subdirectory of this skill. **Important**: All scripts are located in the `scripts/` subdirectory of this skill.
@@ -52,40 +62,17 @@ Before first use, verify user consent for reverse-engineered API usage.
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-danger-gemini-web/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-danger-gemini-web/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-danger-gemini-web/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md` | User home |
```
```powershell If none found, use defaults.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-danger-gemini-web/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-danger-gemini-web/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md") { "user" }
```
┌──────────────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: Default model, proxy settings, custom data directory.
│ Path │ Location │
├──────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-danger-gemini-web/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md │ User home │
└──────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default model | Proxy settings | Custom data directory
## Usage ## Usage
+21 -31
View File
@@ -17,6 +17,16 @@ Converts X content to markdown:
- Tweets/threads → Markdown with YAML front matter - Tweets/threads → Markdown with YAML front matter
- X Articles → Full content extraction - X Articles → Full content extraction
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
Scripts located in `scripts/` subdirectory. Scripts located in `scripts/` subdirectory.
@@ -78,40 +88,20 @@ Use `AskUserQuestion` with options: "Yes, I accept" | "No, I decline"
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` | User home |
```
```powershell | Result | Action |
# PowerShell (Windows) |--------|--------|
if (Test-Path .baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md) { "project" } | Found | Read, parse, apply settings |
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" } | Not found | **MUST** run first-time setup (see below) — do NOT silently create defaults |
if (Test-Path "$xdg/baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md") { "user" }
```
┌────────────────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: Download media by default, default output directory.
│ Path │ Location │
├────────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md │ User home │
└────────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ **MUST** run first-time setup (see below) — do NOT silently create defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Download media by default | Default output directory
### First-Time Setup (BLOCKING) ### First-Time Setup (BLOCKING)
+34 -45
View File
@@ -17,6 +17,16 @@ Transforms plain text or markdown into well-structured, reader-friendly markdown
**Core principle**: Only adjust formatting and fix obvious typos. Never add, delete, or rewrite content. **Core principle**: Only adjust formatting and fix obvious typos. Never add, delete, or rewrite content.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values. Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values.
@@ -29,40 +39,17 @@ Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-format-markdown/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-format-markdown/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-format-markdown/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-format-markdown/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md` | User home |
```
```powershell If none found, use defaults — no first-time setup required for this skill.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-format-markdown/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-format-markdown/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md") { "user" }
```
┌──────────────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**:
│ Path │ Location │
├──────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-format-markdown/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-format-markdown/EXTEND.md │ User home │
└──────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**:
| Setting | Values | Default | Description | | Setting | Values | Default | Description |
|---------|--------|---------|-------------| |---------|--------|---------|-------------|
@@ -178,9 +165,9 @@ Check for YAML frontmatter (`---` block). Create if missing.
| `description` | Longer descriptive summary (see **Summary Generation** below) | | `description` | Longer descriptive summary (see **Summary Generation** below) |
| `coverImage` | Check if `imgs/cover.png` exists in same directory; if so, use relative path | | `coverImage` | Check if `imgs/cover.png` exists in same directory; if so, use relative path |
**Title Generation:** #### Title Generation
Whether or not a title already exists, always run the title optimization flow (unless `auto_select_title` is set). Whether or not a title already exists, run the title optimization flow unless `auto_select_title` is set.
**Preparation** — read the full text and extract: **Preparation** — read the full text and extract:
- Core argument (one sentence: "what is this article about?") - Core argument (one sentence: "what is this article about?")
@@ -188,14 +175,14 @@ Whether or not a title already exists, always run the title optimization flow (u
- Reader pain point or curiosity trigger - Reader pain point or curiosity trigger
- Most memorable metaphor or golden quote - Most memorable metaphor or golden quote
**Generate titles** using formulas from `references/title-formulas.md`: **Generate candidates** using formulas from `references/title-formulas.md`:
1. Select the **2-3 best-matching hook formulas** based on the article's content, tone, and structure (see "When to pick each formula" in the reference) 1. Select the **2-3 best-matching hook formulas** based on the article's content, tone, and structure (see "When to pick each formula" in the reference)
2. Generate **1-2 straightforward titles** (descriptive or declarative, no formula — clear and accurate) 2. Generate **1-2 straightforward titles** (descriptive or declarative, no formula — clear and accurate)
3. If the user specifies a direction (e.g., "make it suspenseful"), prioritize that direction 3. If the user specifies a direction (e.g., "make it suspenseful"), prioritize that direction
4. Total: **4-5 candidates** 4. Total: **4-5 candidates**
Use `AskUserQuestion` to present candidates: Present via `AskUserQuestion`:
``` ```
Pick a title: Pick a title:
@@ -209,33 +196,35 @@ Pick a title:
Enter number, or type a custom title: Enter number, or type a custom title:
``` ```
Put the strongest hook first and mark it (recommended). See `references/title-formulas.md` for title principles and prohibited patterns. Put the strongest hook first and mark it `(recommended)`. See `references/title-formulas.md` for principles and prohibited patterns.
If first line is H1, extract to frontmatter and remove from body. If frontmatter already has `title`, include it as context but still generate fresh candidates. If the first line is an H1, extract it to frontmatter and remove it from the body. If frontmatter already has a `title`, include it as context but still generate fresh candidates — the existing title may be weak.
**Summary Generation:** **Skip behavior**: If `auto_select: true` or `auto_select_title: true`, skip the user prompt and use the top candidate directly.
Generate two versions directly (no user selection needed), both stored in frontmatter: #### Summary Generation
Generate two versions directly (no user selection), both stored in frontmatter:
| Field | Length | Purpose | | Field | Length | Purpose |
|-------|--------|---------| |-------|--------|---------|
| `summary` | 1 sentence, ~50-80 chars | Concise hook — for feeds, social sharing, SEO meta | | `summary` | 1 sentence, ~50-80 chars | Concise hook — for feeds, social sharing, SEO meta |
| `description` | 2-3 sentences, ~100-200 chars | Richer context — for article previews, newsletter blurbs | | `description` | 2-3 sentences, ~100-200 chars | Richer context — for article previews, newsletter blurbs |
**Principles:** **Principles**:
- Convey **core value** to the reader, not just the topic - Convey **core value** to the reader, not just the topic
- Use concrete details (numbers, outcomes, specific methods) over vague descriptions - Use concrete details (numbers, outcomes, specific methods) over vague descriptions
- `summary` should be punchy and self-contained; `description` can expand with supporting details - `summary` should be punchy and self-contained; `description` can expand with supporting details
- If frontmatter already has `summary` or `description`, keep existing and only generate the missing one - If frontmatter already has `summary` or `description`, keep the existing one and only generate the missing field
**Prohibited patterns**:
**Prohibited patterns:**
- "This article introduces...", "This article explores..." - "This article introduces...", "This article explores..."
- Pure topic description without value proposition - Pure topic description without value proposition
- Repeating the title in different words - Repeating the title in different words
**EXTEND.md skip behavior:** If `auto_select: true` or `auto_select_title: true` is set in EXTEND.md, skip title selection — generate the best candidate directly without asking. Once the title is in frontmatter, the body should NOT contain an H1 (avoid duplication).
Once title is in frontmatter, body should NOT have H1 (avoid duplication).
### Step 4: Format Content ### Step 4: Format Content
+225 -446
View File
@@ -11,75 +11,56 @@ metadata:
Break down complex content into eye-catching image card series with multiple style options. Break down complex content into eye-catching image card series with multiple style options.
## Usage ## User Input Tools
```bash When this skill prompts the user, follow this tool-selection rule (priority order):
# Auto-select style and layout based on content
/baoyu-image-cards posts/ai-future/article.md
# Specify style 1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
/baoyu-image-cards posts/ai-future/article.md --style notion 2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
# Specify layout Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
/baoyu-image-cards posts/ai-future/article.md --layout dense
# Combine style and layout ## Image Generation Tools
/baoyu-image-cards posts/ai-future/article.md --style notion --layout list
# Specify palette (override style colors) When this skill needs to render an image:
/baoyu-image-cards posts/ai-future/article.md --style notion --palette macaron
# Use preset (style + layout + optional palette shorthand) - **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.
/baoyu-image-cards posts/ai-future/article.md --preset knowledge-card - **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.
# Preset with override **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.
/baoyu-image-cards posts/ai-future/article.md --preset poster --layout quadrant
# Preset with palette override ## Language
/baoyu-image-cards posts/ai-future/article.md --preset hand-drawn-edu --palette warm
# Direct content input Respond in the user's language across questions, progress, errors, and completion summary. Keep technical tokens (style names, file paths, code) in English.
/baoyu-image-cards
[paste content]
# Direct input with options
/baoyu-image-cards --style bold --layout comparison
[paste content]
# Non-interactive (for scheduled tasks / automation)
/baoyu-image-cards posts/ai-future/article.md --yes
/baoyu-image-cards posts/ai-future/article.md --yes --preset knowledge-card
```
## Options ## Options
| Option | Description | | Option | Description |
|--------|-------------| |--------|-------------|
| `--style <name>` | Visual style (see Style Gallery) | | `--style <name>` | Visual style (see Styles below) |
| `--layout <name>` | Information layout (see Layout Gallery) | | `--layout <name>` | Information layout (see Layouts below) |
| `--palette <name>` | Color palette override (see Palette Gallery) | | `--palette <name>` | Color override: macaron / warm / neon |
| `--preset <name>` | Style + layout + optional palette shorthand (see [Style Presets](references/style-presets.md)) | | `--preset <name>` | Style + layout + optional palette shorthand (see Presets below; per-preset prompt fragments in `references/style-presets.md`) |
| `--yes` | Non-interactive mode: skip all confirmations. Uses EXTEND.md preferences if found, otherwise uses defaults (no watermark, auto style/layout). Auto-confirms recommended plan (Path A). Suitable for scheduled tasks and automation. | | `--ref <files...>` | Reference images applied to image 1 as the series anchor |
| `--yes` | Non-interactive: skip all confirmations, use EXTEND.md or built-in defaults, auto-confirm recommended plan (Path A) |
## Dimensions ## Dimensions
Three independent knobs combine freely:
| Dimension | Controls | Options | | Dimension | Controls | Options |
|-----------|----------|---------| |-----------|----------|---------|
| **Style** | Visual aesthetics: lines, decorations, rendering | cute, fresh, warm, bold, minimal, retro, pop, notion, chalkboard, study-notes, screen-print, sketch-notes | | **Style** | Visual aesthetics (lines, decorations, rendering) | 12 styles (see Styles below) |
| **Layout** | Information structure: density, arrangement | sparse, balanced, dense, list, comparison, flow, mindmap, quadrant | | **Layout** | Information structure (density, arrangement) | 8 layouts (see Layouts below) |
| **Palette** (optional) | Color override: replaces style's default colors | macaron, warm, neon | | **Palette** (optional) | Color override, replaces the style's default colors | macaron / warm / neon (see Palettes below) |
Style × Layout can be freely combined, with optional palette override. Example: `--style notion --layout dense` creates an intellectual-looking knowledge card with high information density. Add `--palette macaron` to swap colors to soft pastels while keeping notion's rendering style. Example: `--style notion --layout dense` makes an intellectual knowledge card; add `--palette macaron` to soften the colors without changing notion's rendering rules. A `--preset` is a shorthand for style + layout (+ optional palette).
Or use presets: `--preset knowledge-card` → style + layout in one flag. See [Style Presets](references/style-presets.md). **Palette behavior**: no `--palette` → style's built-in colors; `--palette <name>` → overrides colors only, rendering rules unchanged. Some styles declare a `default_palette` (e.g., sketch-notes defaults to macaron).
**Palette behavior**: ## Styles (12)
- No `--palette` → style uses its built-in colors (or its `default_palette` if defined)
- `--palette macaron` → overrides any style's colors with macaron palette
- Palette replaces colors only; style rendering rules (line treatment, elements, textures) stay unchanged
- Some styles declare a `default_palette` (e.g., sketch-notes defaults to macaron)
## Style Gallery
| Style | Description | | Style | Description |
|-------|-------------| |-------|-------------|
@@ -96,25 +77,52 @@ Or use presets: `--preset knowledge-card` → style + layout in one flag. See [S
| `screen-print` | Bold poster art, halftone textures, limited colors, symbolic storytelling | | `screen-print` | Bold poster art, halftone textures, limited colors, symbolic storytelling |
| `sketch-notes` | Hand-drawn educational infographic, macaron pastels on warm cream, wobble lines | | `sketch-notes` | Hand-drawn educational infographic, macaron pastels on warm cream, wobble lines |
Detailed style definitions: `references/presets/<style>.md` Per-style specifications: `references/presets/<style>.md`.
## Preset Gallery ## Layouts (8)
Quick-start presets by content scenario. Use `--preset <name>` or recommend during Step 2. | Layout | Description |
|--------|-------------|
| `sparse` (Default) | 1-2 points, maximum impact |
| `balanced` | 3-4 points, standard |
| `dense` | 5-8 points, knowledge-card style |
| `list` | Enumeration / ranking (4-7 items) |
| `comparison` | Side-by-side contrast |
| `flow` | Process / timeline (3-6 steps) |
| `mindmap` | Center-radial (4-8 branches) |
| `quadrant` | Four-quadrant / circular sections |
Layout specs: `references/elements/canvas.md`.
## Palettes (optional override)
Replaces the style's colors while keeping rendering rules (line treatment, textures) intact.
| Palette | Background | Zone Colors | Accent | Feel |
|---------|------------|-------------|--------|------|
| `macaron` | Warm cream #F5F0E8 | Blue #A8D8EA, Lavender #D5C6E0, Mint #B5E5CF, Peach #F8D5C4 | Coral #E8655A | Soft, educational |
| `warm` | Soft peach #FFECD2 | Orange #ED8936, Terracotta #C05621, Golden #F6AD55, Rose #D4A09A | Sienna #A0522D | Earth tones, cozy |
| `neon` | Dark purple #1A1025 | Cyan #00F5FF, Magenta #FF00FF, Green #39FF14, Pink #FF6EC7 | Yellow #FFFF00 | High-energy, futuristic |
Palette specs: `references/palettes/<palette>.md`.
## Presets (style + layout shortcuts)
Quick-start combos, grouped by scenario. Use `--preset <name>` or recommend during Step 2.
**Knowledge & Learning**: **Knowledge & Learning**:
| Preset | Style | Layout | Best For | | Preset | Style | Layout | Best For |
|--------|-------|--------|----------| |--------|-------|--------|----------|
| `knowledge-card` | notion | dense | 干货知识卡、概念科普 | | `knowledge-card` | notion | dense | 干货知识卡、概念科普 |
| `checklist` | notion | list | 清单、排行榜、必备清单 | | `checklist` | notion | list | 清单、排行榜 |
| `concept-map` | notion | mindmap | 概念图、知识脉络 | | `concept-map` | notion | mindmap | 概念图、知识脉络 |
| `swot` | notion | quadrant | SWOT分析、四象限分类 | | `swot` | notion | quadrant | SWOT 分析、四象限 |
| `tutorial` | chalkboard | flow | 教程步骤、操作流程 | | `tutorial` | chalkboard | flow | 教程步骤、操作流程 |
| `classroom` | chalkboard | balanced | 课堂笔记、知识讲解 | | `classroom` | chalkboard | balanced | 课堂笔记、知识讲解 |
| `study-guide` | study-notes | dense | 学习笔记、考试重点 | | `study-guide` | study-notes | dense | 学习笔记、考试重点 |
| `hand-drawn-edu` | sketch-notes | flow | 手绘教程、流程图解 | | `hand-drawn-edu` | sketch-notes | flow | 手绘教程、流程图解 |
| `sketch-card` | sketch-notes | dense | 手绘知识卡、概念科普 | | `sketch-card` | sketch-notes | dense | 手绘知识卡 |
| `sketch-summary` | sketch-notes | balanced | 手绘总结、图文笔记 | | `sketch-summary` | sketch-notes | balanced | 手绘总结、图文笔记 |
**Lifestyle & Sharing**: **Lifestyle & Sharing**:
@@ -132,7 +140,7 @@ Quick-start presets by content scenario. Use `--preset <name>` or recommend duri
| Preset | Style | Layout | Best For | | Preset | Style | Layout | Best For |
|--------|-------|--------|----------| |--------|-------|--------|----------|
| `warning` | bold | list | 避坑指南、重要提醒 | | `warning` | bold | list | 避坑指南、重要提醒 |
| `versus` | bold | comparison | 正反对比、强烈对照 | | `versus` | bold | comparison | 正反对比 |
| `clean-quote` | minimal | sparse | 金句、极简封面 | | `clean-quote` | minimal | sparse | 金句、极简封面 |
| `pro-summary` | minimal | balanced | 专业总结、商务内容 | | `pro-summary` | minimal | balanced | 专业总结、商务内容 |
@@ -141,8 +149,8 @@ Quick-start presets by content scenario. Use `--preset <name>` or recommend duri
| Preset | Style | Layout | Best For | | Preset | Style | Layout | Best For |
|--------|-------|--------|----------| |--------|-------|--------|----------|
| `retro-ranking` | retro | list | 复古排行、经典盘点 | | `retro-ranking` | retro | list | 复古排行、经典盘点 |
| `throwback` | retro | balanced | 怀旧分享、老物件 | | `throwback` | retro | balanced | 怀旧分享 |
| `pop-facts` | pop | list | 趣味冷知识、好玩的事 | | `pop-facts` | pop | list | 趣味冷知识 |
| `hype` | pop | sparse | 炸裂封面、惊叹分享 | | `hype` | pop | sparse | 炸裂封面、惊叹分享 |
**Poster & Editorial**: **Poster & Editorial**:
@@ -153,397 +161,192 @@ Quick-start presets by content scenario. Use `--preset <name>` or recommend duri
| `editorial` | screen-print | balanced | 观点文章、文化评论 | | `editorial` | screen-print | balanced | 观点文章、文化评论 |
| `cinematic` | screen-print | comparison | 电影对比、戏剧张力 | | `cinematic` | screen-print | comparison | 电影对比、戏剧张力 |
Full preset definitions: [references/style-presets.md](references/style-presets.md) Full prompt-fragment definitions: `references/style-presets.md`.
## Layout Gallery ## Auto-Selection
| Layout | Description | Match content signals to the best combo. First row whose keywords appear wins; fall back to `cute-share` if nothing matches.
|--------|-------------|
| `sparse` (Default) | Minimal information, maximum impact (1-2 points) |
| `balanced` | Standard content layout (3-4 points) |
| `dense` | High information density, knowledge card style (5-8 points) |
| `list` | Enumeration and ranking format (4-7 items) |
| `comparison` | Side-by-side contrast layout |
| `flow` | Process and timeline layout (3-6 steps) |
| `mindmap` | Center radial mind map layout (4-8 branches) |
| `quadrant` | Four-quadrant / circular section layout |
Detailed layout definitions: `references/elements/canvas.md` | Signals in source | Style | Layout | Recommended preset |
|-------------------|-------|--------|--------------------|
| beauty, fashion, cute, girl, pink | `cute` | sparse/balanced | `cute-share`, `girly` |
| health, nature, fresh, organic | `fresh` | balanced/flow | `product-review`, `nature-flow` |
| life, story, emotion, warm | `warm` | balanced | `cozy-story` |
| warning, important, must, critical | `bold` | list/comparison | `warning`, `versus` |
| professional, business, elegant | `minimal` | sparse/balanced | `clean-quote`, `pro-summary` |
| classic, vintage, traditional | `retro` | balanced | `throwback`, `retro-ranking` |
| fun, exciting, wow, amazing | `pop` | sparse/list | `hype`, `pop-facts` |
| knowledge, concept, productivity, SaaS | `notion` | dense/list | `knowledge-card`, `checklist` |
| education, tutorial, learning, classroom | `chalkboard` | balanced/dense | `tutorial`, `classroom` |
| notes, handwritten, study guide, realistic | `study-notes` | dense/list/mindmap | `study-guide` |
| movie, poster, opinion, editorial, cinematic | `screen-print` | sparse/comparison | `poster`, `editorial`, `cinematic` |
| hand-drawn, infographic, workflow, 手绘, 图解 | `sketch-notes` | flow/balanced/dense | `hand-drawn-edu`, `sketch-card`, `sketch-summary` |
## Palette Gallery ## Style × Layout Matrix
Optional color override. Replaces style's built-in colors while preserving rendering rules. Compatibility scores (✓✓ highly recommended, ✓ works well, ✗ avoid). Use when the user picks a non-default combo and you want to flag a poor match.
| Palette | Background | Zone Colors | Accent | Feel | | | sparse | balanced | dense | list | comparison | flow | mindmap | quadrant |
|---------|------------|-------------|--------|------| |--------------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| `macaron` | Warm cream #F5F0E8 | Blue #A8D8EA, Lavender #D5C6E0, Mint #B5E5CF, Peach #F8D5C4 | Coral #E8655A | Soft, educational, approachable | | cute | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
| `warm` | Soft Peach #FFECD2 | Orange #ED8936, Terracotta #C05621, Golden #F6AD55, Rose #D4A09A | Sienna #A0522D | Cozy, earth tones, no cool colors | | fresh | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ |
| `neon` | Dark Purple #1A1025 | Cyan #00F5FF, Magenta #FF00FF, Green #39FF14, Pink #FF6EC7 | Yellow #FFFF00 | High-energy, futuristic | | warm | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ |
| bold | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ |
Detailed palette definitions: `references/palettes/<palette>.md` | minimal | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| retro | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
## Auto Selection | pop | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ |
| notion | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ |
| Content Signals | Style | Layout | Recommended Preset | | chalkboard | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
|-----------------|-------|--------|--------------------| | study-notes | ✗ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ |
| Beauty, fashion, cute, girl, pink | `cute` | sparse/balanced | `cute-share`, `girly` | | screen-print | ✓✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ |
| Health, nature, clean, fresh, organic | `fresh` | balanced/flow | `product-review`, `nature-flow` | | sketch-notes | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| Life, story, emotion, feeling, warm | `warm` | balanced | `cozy-story` |
| Warning, important, must, critical | `bold` | list/comparison | `warning`, `versus` |
| Professional, business, elegant, simple | `minimal` | sparse/balanced | `clean-quote`, `pro-summary` |
| Classic, vintage, old, traditional | `retro` | balanced | `throwback`, `retro-ranking` |
| Fun, exciting, wow, amazing | `pop` | sparse/list | `hype`, `pop-facts` |
| Knowledge, concept, productivity, SaaS | `notion` | dense/list | `knowledge-card`, `checklist` |
| Education, tutorial, learning, teaching, classroom | `chalkboard` | balanced/dense | `tutorial`, `classroom` |
| Notes, handwritten, study guide, knowledge, realistic, photo | `study-notes` | dense/list/mindmap | `study-guide` |
| Movie, album, concert, poster, opinion, editorial, dramatic, cinematic | `screen-print` | sparse/comparison | `poster`, `editorial`, `cinematic` |
| Hand-drawn, infographic, diagram, visual summary, 手绘, 图解, workflow, process | `sketch-notes` | flow/balanced/dense | `hand-drawn-edu`, `sketch-card`, `sketch-summary` |
## Outline Strategies ## Outline Strategies
Three differentiated outline strategies for different content goals: Three differentiated approaches — each produces a structurally different outline. The workflow recommends one; Path C generates all three and lets the user choose.
### Strategy A: Story-Driven (故事驱动型) | Strategy | Concept | Best for | Structure |
|----------|---------|----------|-----------|
| **A — Story-Driven** | Personal experience as the thread, emotional resonance first | Reviews, personal shares, transformation | Hook → Problem → Discovery → Experience → Conclusion |
| **B — Information-Dense** | Value-first, efficient information delivery | Tutorials, comparisons, checklists | Core conclusion → Info card → Pros/Cons → Recommendation |
| **C — Visual-First** | Visual impact as core, minimal text | High-aesthetic products, lifestyle, mood content | Hero image → Detail shots → Lifestyle scene → CTA |
| Aspect | Description | ## Reference Images
|--------|-------------|
| **Concept** | Personal experience as main thread, emotional resonance first |
| **Features** | Start from pain point, show before/after change, strong authenticity |
| **Best for** | Reviews, personal shares, transformation stories |
| **Structure** | Hook → Problem → Discovery → Experience → Conclusion |
### Strategy B: Information-Dense (信息密集型) User-supplied refs are **separate from** the internal "image-1 as anchor" chain (Step 3) — they layer on top of it.
| Aspect | Description | **Intake**: via `--ref <files...>` or paths pasted in conversation.
|--------|-------------| - File path → copy to `refs/NN-ref-{slug}.{ext}`
| **Concept** | Value-first, efficient information delivery | - Pasted with no path → ask for the path, or extract style traits as a text fallback
| **Features** | Clear structure, explicit points, professional credibility |
| **Best for** | Tutorials, comparisons, product reviews, checklists |
| **Structure** | Core conclusion → Info card → Pros/Cons → Recommendation |
### Strategy C: Visual-First (视觉优先型) **Usage modes** (per reference):
| Aspect | Description | | Usage | Effect |
|--------|-------------| |-------|--------|
| **Concept** | Visual impact as core, minimal text | | `direct` | Pass the file to the backend (typically on image 1 only, so the anchor propagates through the chain) |
| **Features** | Large images, atmospheric, instant appeal | | `style` | Extract style traits and append to every card's prompt body |
| **Best for** | High-aesthetic products, lifestyle, mood-based content | | `palette` | Extract hex colors and append to every card's prompt body |
| **Structure** | Hero image → Detail shots → Lifestyle scene → CTA |
## File Structure Record refs in each affected card's prompt frontmatter:
Each session creates an independent directory named by content slug: ```yaml
references:
- ref_id: 01
filename: 01-ref-brand.png
usage: direct
```
At generation time: verify files exist. Image 1 with `usage: direct` + backend that accepts refs → pass via the backend's ref parameter (becomes the chain anchor). Images 2+ keep using image-1 as `--ref` per Step 3 — do NOT re-stack user refs on top (avoids conflicting signals). For `style`/`palette`, embed extracted traits in every prompt.
## File Layout
``` ```
image-cards/{topic-slug}/ image-cards/{topic-slug}/
├── source-{slug}.{ext} # Source files (text, images, etc.) ├── source-{slug}.{ext}
├── analysis.md # Deep analysis + questions asked ├── analysis.md
├── outline-strategy-a.md # Strategy A: Story-driven ├── outline-strategy-{a,b,c}.md # Path C only
├── outline-strategy-b.md # Strategy B: Information-dense ├── outline.md
├── outline-strategy-c.md # Strategy C: Visual-first ├── prompts/NN-{type}-{slug}.md
├── outline.md # Final selected/merged outline ├── NN-{type}-{slug}.png
── prompts/ ── refs/ # only if --ref used
│ ├── 01-cover-[slug].md
│ ├── 02-content-[slug].md
│ └── ...
├── 01-cover-[slug].png
├── 02-content-[slug].png
└── NN-ending-[slug].png
``` ```
**Slug Generation**: **Slug**: 2-4 words, kebab-case. "AI 工具推荐" → `ai-tools-recommend`. On collision, append `-YYYYMMDD-HHMMSS`.
1. Extract main topic from content (2-4 words, kebab-case)
2. Example: "AI工具推荐" → `ai-tools-recommend`
**Conflict Resolution**: **Backup rule** (applies throughout): before overwriting any file — source, outline, prompt, image — rename the existing one to `<name>-backup-YYYYMMDD-HHMMSS.<ext>`. This protects user edits.
If `image-cards/{topic-slug}/` already exists:
- Append timestamp: `{topic-slug}-YYYYMMDD-HHMMSS`
- Example: `ai-tools` exists → `ai-tools-20260118-143052`
**Source Files**:
Copy all sources with naming `source-{slug}.{ext}`:
- `source-article.md`, `source-photo.jpg`, etc.
- Multiple sources supported: text, images, files from conversation
## Workflow ## Workflow
### Progress Checklist
Copy and track progress:
``` ```
Image Card Series Progress: - [ ] Step 0: Load EXTEND.md ⛔ BLOCKING (interactive only)
- [ ] Step 0: Check preferences (EXTEND.md) ⛔ BLOCKING (--yes: use defaults if not found)
- [ ] Found → load preferences → continue
- [ ] Not found → run first-time setup → MUST complete before Step 1 (--yes: skip setup, use defaults)
- [ ] Step 1: Analyze content → analysis.md - [ ] Step 1: Analyze content → analysis.md
- [ ] Step 2: Smart Confirm ⚠️ REQUIRED (--yes: auto-confirm Path A) - [ ] Step 2: Smart Confirm ⚠️ REQUIRED (Path A / B / C)
- [ ] Path A: Quick confirm → generate recommended outline - [ ] Step 3: Generate images
- [ ] Path B: Customize → adjust then generate outline
- [ ] Path C: Detailed → 3 outlines → second confirm → generate outline
- [ ] Step 3: Generate images (sequential)
- [ ] Step 4: Completion report - [ ] Step 4: Completion report
``` ```
### Flow ### Step 0: Load EXTEND.md ⛔ BLOCKING
``` Check these paths in order; first hit wins:
Input → [--yes?] ─┬─ Yes → [Step 0: Load or defaults] → Analyze → Auto-confirm → Generate → Complete
└─ No → [Step 0: Preferences] ─┬─ Found → Continue
└─ Not found → First-Time Setup ⛔ BLOCKING
└─ Complete setup → Save EXTEND.md → Continue
┌─────────────────────────────────────────────────────────────────────────────────────┘
Analyze → [Smart Confirm] ─┬─ Quick: confirm recommended → outline.md → Generate → Complete
├─ Customize: adjust options → outline.md → Generate → Complete
└─ Detailed: 3 outlines → [Confirm 2] → outline.md → Generate → Complete
```
### Step 0: Load Preferences (EXTEND.md) ⛔ BLOCKING | Path | Scope |
|------|-------|
| `.baoyu-skills/baoyu-image-cards/EXTEND.md` | Project |
| `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-image-cards/EXTEND.md` | XDG |
| `$HOME/.baoyu-skills/baoyu-image-cards/EXTEND.md` | User home |
**Purpose**: Load user preferences or run first-time setup. - **Found** → read, parse, print a summary (style / layout / watermark / language), continue.
- **Not found + interactive** → run first-time setup (see `references/config/first-time-setup.md`) and save before anything else. Do NOT analyze content or ask style questions until preferences exist — this keeps first-run behavior predictable.
- **Not found + `--yes`** → skip setup, use built-in defaults (no watermark, style/layout auto-selected, language from content). Do not prompt, do not create EXTEND.md.
**`--yes` mode**: If EXTEND.md found → load it. If not found → use built-in defaults (no watermark, style/layout auto-select, language from content). Do NOT run first-time setup, do NOT create EXTEND.md, do NOT ask any questions. Proceed directly to Step 1. **EXTEND.md keys**: watermark, preferred style/layout, custom style definitions, language preference. Schema: `references/config/preferences-schema.md`.
**CRITICAL** (interactive mode only): If EXTEND.md not found, MUST complete first-time setup before ANY other questions or steps. Do NOT proceed to content analysis, do NOT ask about style, do NOT ask about layout — ONLY complete the preferences setup first.
Check EXTEND.md existence (priority order):
```bash
# macOS, Linux, WSL, Git Bash
test -f .baoyu-skills/baoyu-image-cards/EXTEND.md && echo "project"
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-image-cards/EXTEND.md" && echo "xdg"
test -f "$HOME/.baoyu-skills/baoyu-image-cards/EXTEND.md" && echo "user"
```
```powershell
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-image-cards/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-image-cards/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-image-cards/EXTEND.md") { "user" }
```
┌────────────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-image-cards/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-image-cards/EXTEND.md │ User home │
└────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, display summary → Continue to Step 1 │
├───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Not found │ ⛔ BLOCKING: Run first-time setup ONLY (see below) → Complete and save EXTEND.md → Then Step 1 │
└───────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────┘
**First-Time Setup** (when EXTEND.md not found):
**Language**: Use user's input language or saved language preference.
Use AskUserQuestion with ALL questions in ONE call. See `references/config/first-time-setup.md` for question details.
**EXTEND.md Supports**: Watermark | Preferred style/layout | Custom style definitions | Language preference
Schema: `references/config/preferences-schema.md`
### Step 1: Analyze Content → `analysis.md` ### Step 1: Analyze Content → `analysis.md`
Read source content, save it if needed, and perform deep analysis. 1. Save the source (backup rule applies if `source.md` exists).
2. Run the deep analysis in `references/workflows/analysis-framework.md`: content type, hook potential, audience, engagement signals, visual opportunity map, swipe flow.
3. Detect source language, pick recommended image count (2-10).
4. Auto-recommend strategy + style + layout + palette using the **Auto-Selection** table above.
5. Write everything to `analysis.md`.
**Actions**: ### Step 2: Smart Confirm ⚠️ REQUIRED
1. **Save source content** (if not already a file):
- If user provides a file path: use as-is
- If user pastes content: save to `source.md` in target directory
- **Backup rule**: If `source.md` exists, rename to `source-backup-YYYYMMDD-HHMMSS.md`
2. Read source content
3. **Deep analysis** following `references/workflows/analysis-framework.md`:
- Content type classification (种草/干货/测评/教程/避坑...)
- Hook analysis (爆款标题潜力)
- Target audience identification
- Engagement potential (收藏/分享/评论)
- Visual opportunity mapping
- Swipe flow design
4. Detect source language
5. Determine recommended image count (2-10)
6. **Auto-recommend** best strategy + style + layout + palette based on content signals
7. **Save to `analysis.md`**
### Step 2: Smart Confirm ⚠️ 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.
**Purpose**: Present auto-recommended plan, let user confirm or adjust. **Display summary** before asking:
**`--yes` mode**: Skip this entire step. Use auto-recommended strategy + style + layout + palette from Step 1 analysis (or `--style`/`--layout`/`--palette`/`--preset` if provided). Generate outline directly using Path A logic → save to `outline.md` → proceed to Step 3. No AskUserQuestion calls.
**Interactive mode**: Do NOT skip.
**Auto-Recommendation Logic**:
1. Use Auto Selection table to match content signals → best strategy + style + layout + palette
2. Infer optimal image count from content density
3. Load style's default elements from preset (apply palette override if applicable)
**Display** (analysis summary + recommended plan):
``` ```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 内容分析 📋 内容分析
主题:[topic] | 类型:[content_type] 主题:[topic] | 类型:[content_type]
要点:[key points summary] 要点:[key points]
受众:[target audience] 受众:[audience]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎨 推荐方案(自动匹配) 🎨 推荐方案(自动匹配)
策略:[A/B/C] [strategy name][reason] 策略:[A/B/C] [name][reason]
风格:[style] · 布局:[layout] · 配色:[palette or "默认"] · 预设:[preset] 风格:[style] · 布局:[layout] · 配色:[palette or 默认] · 预设:[preset]
图片:[N]张(封面+[N-2]内容+结尾) 图片:[N]张(封面+[N-2]内容+结尾)
元素:[background] / [decorations] / [emphasis] 元素:[background] / [decorations] / [emphasis]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
``` ```
**Use AskUserQuestion** with single question: Then ask one question — three paths. Verbatim option copy: `references/confirmation.md`.
| Option | Description | **Path A — Quick confirm** (trust auto-recommendation): generate a single outline using the recommended strategy + style → save to `outline.md` → Step 3.
|--------|-------------|
| 1. ✅ 确认,直接生成(推荐) | Trust auto-recommendation, proceed immediately |
| 2. 🎛️ 自定义调整 | Modify strategy/style/layout/count in one step |
| 3. 📋 详细模式 | Generate 3 outlines, then choose (two confirmations) |
#### Path A: Quick Confirm (Option 1) **Path B — Customize**: ask five questions (strategy/style, layout, palette, count, optional notes) with the recommendation pre-filled — blanks keep the recommendation. Generate one outline with the user's choices → `outline.md` → Step 3. See `references/confirmation.md`.
Generate single outline using recommended strategy + style → save to `outline.md` → Step 3. **Path C — Detailed mode**: two sub-confirmations.
#### Path B: Customize (Option 2) - *Step 2a — Content understanding*: ask selling points (multi-select), audience, style preference (authentic / professional / aesthetic / auto), optional context. Update `analysis.md`.
- *Step 2b — Three outline variants*: generate `outline-strategy-a.md`, `outline-strategy-b.md`, `outline-strategy-c.md`. Each MUST have a different structure AND a different recommended style — include `style_reason` in the frontmatter. Page-count heuristic: A ~4-6, B ~3-5, C ~3-4. Template: `references/workflows/outline-template.md`; frontmatter example in `references/confirmation.md`.
**Use AskUserQuestion** with adjustable options (leave blank = keep recommended): - *Step 2c — Selection*: ask three questions (outline A/B/C/Combined, style, visual elements). Save selected/merged outline to `outline.md` → Step 3.
1. **策略风格**: Current: [strategy + style]. Options: A Story-Driven(warm) | B Information-Dense(notion) | C Visual-First(screen-print). Or specify style directly: cute/fresh/warm/bold/minimal/retro/pop/notion/chalkboard/study-notes/screen-print/sketch-notes. Or use preset: knowledge-card / checklist / tutorial / poster / hand-drawn-edu / etc.
2. **布局**: Current: [layout]. Options: sparse | balanced | dense | list | comparison | flow | mindmap | quadrant
3. **配色**: Current: [palette or "默认"]. Options: 默认 | macaron | warm | neon
4. **图片数量**: Current: [N]. Range: 2-10
5. **补充说明**(可选): Selling point emphasis, audience adjustment, custom color preference, etc.
**After response**: Generate single outline with user's choices → save to `outline.md` → Step 3.
#### Path C: Detailed Mode (Option 3)
Full two-confirmation flow for maximum control:
**Step 2a: Content Understanding**
**Use AskUserQuestion** for:
1. Core selling point (multiSelect: true)
2. Target audience
3. Style preference: Authentic sharing / Professional review / Aesthetic mood / Auto
4. Additional context (optional)
**After response**: Update `analysis.md`.
**Step 2b: Generate 3 Outline Variants**
| Strategy | Filename | Outline | Recommended Style |
|----------|----------|---------|-------------------|
| A | `outline-strategy-a.md` | Story-driven: emotional, before/after | warm, cute, fresh |
| B | `outline-strategy-b.md` | Information-dense: structured, factual | notion, minimal, chalkboard |
| C | `outline-strategy-c.md` | Visual-first: atmospheric, minimal text | bold, pop, retro, screen-print |
**Outline format** (YAML front matter + content):
```yaml
---
strategy: a # a, b, or c
name: Story-Driven
style: warm # recommended style for this strategy
palette: ~ # optional palette override (macaron, warm, neon, or ~ for style default)
style_reason: "Warm tones enhance emotional storytelling and personal connection"
elements: # from style preset, can be customized
background: solid-pastel
decorations: [clouds, stars-sparkles]
emphasis: star-burst
typography: highlight
layout: balanced # primary layout
image_count: 5
---
## P1 Cover
**Type**: cover
**Hook**: "入冬后脸不干了🥹终于找到对的面霜"
**Visual**: Product hero shot with cozy winter atmosphere
**Layout**: sparse
## P2 Problem
**Type**: pain-point
**Message**: Previous struggles with dry skin
**Visual**: Before state, relatable scenario
**Layout**: balanced
...
```
**Differentiation requirements**:
- Each strategy MUST have different outline structure AND different recommended style
- Adapt page count: A typically 4-6, B typically 3-5, C typically 3-4
- Include `style_reason` explaining why this style fits the strategy
Reference: `references/workflows/outline-template.md`
**Step 2c: Outline & Style Selection**
**Use AskUserQuestion** with three questions:
**Q1: Outline Strategy**: A / B / C / Combine (specify pages from each)
**Q2: Visual Style**: Use recommended | Select preset | Select style | Custom description
**Q3: Visual Elements**: Use defaults (Recommended) | Adjust background | Adjust decorations | Custom
**After response**: Save selected/merged outline to `outline.md` with confirmed style and elements → Step 3.
### Step 3: Generate Images ### Step 3: Generate Images
With confirmed outline + style + layout: With confirmed outline + style + layout + palette:
**Visual Consistency — Reference Image Chain**: **Visual consistency — image-1 anchor chain**: character / mascot / color rendering drifts between calls unless you anchor them. Generate image 1 (cover) first WITHOUT `--ref`, then pass image 1 as `--ref` to every subsequent image. This is the single most important consistency trick for this skill — don't skip it even if the backend also supports a session ID.
To ensure character/style consistency across all images in a series:
1. **Generate image 1 (cover) FIRST** — without `--ref`
2. **Use image 1 as `--ref` for ALL remaining images** (2, 3, ..., N)
- This anchors the character design, color rendering, and illustration style
- Command pattern: `--ref <path-to-image-01.png>` added to every subsequent generation
This is critical for styles that use recurring characters, mascots, or illustration elements. Image 1 becomes the visual anchor for the entire series. For each image (cover, content, ending):
**For each image (cover + content + ending)**: 1. Write the full prompt to `prompts/NN-{type}-{slug}.md` in the user's preferred language (backup rule applies).
1. Save prompt to `prompts/NN-{type}-[slug].md` (in user's preferred language) 2. Generate:
- **Backup rule**: If prompt file exists, rename to `prompts/NN-{type}-[slug]-backup-YYYYMMDD-HHMMSS.md` - **Image 1**: no `--ref` (establishes the anchor).
2. Generate image: - **Images 2+**: add `--ref <path-to-image-01.png>`.
- **Image 1**: Generate without `--ref` (this establishes the visual anchor) - Backup rule applies to the PNG files.
- **Images 2+**: Generate with `--ref <image-01-path>` for consistency 3. Report progress after each image.
- **Backup rule**: If image file exists, rename to `NN-{type}-[slug]-backup-YYYYMMDD-HHMMSS.png`
3. Report progress after each generation **Watermark** (if enabled in EXTEND.md): append to the generation prompt:
**Watermark Application** (if enabled in preferences):
Add to each image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position]. Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the main content. The watermark should be legible but not distracting.
``` ```
Reference: `references/config/watermark-guide.md`
**Image Generation Skill Selection**: See `references/config/watermark-guide.md`.
- Check available image generation skills
- If multiple skills available: ask user preference (interactive) or use first available skill (`--yes` mode)
**Session Management**: **Backend selection**: per the Image Generation Tools rule at the top — use whatever is available, ask once if multiple, before any generation. Under `--yes`, use the EXTEND.md preference and fall back to the first available backend. Prompt files MUST exist before invoking any backend.
If image generation skill supports `--sessionId`:
1. Generate unique session ID: `cards-{topic-slug}-{timestamp}` **Session ID** (if the backend supports `--sessionId`): use `cards-{topic-slug}-{timestamp}` for every image; combined with the ref chain this gives maximum consistency.
2. Use same session ID for all images
3. Combined with reference image chain, ensures maximum visual consistency
### Step 4: Completion Report ### Step 4: Completion Report
@@ -553,89 +356,65 @@ Image Card Series Complete!
Topic: [topic] Topic: [topic]
Mode: [Quick / Custom / Detailed] Mode: [Quick / Custom / Detailed]
Strategy: [A/B/C/Combined] Strategy: [A/B/C/Combined]
Style: [style name] Style: [name]
Palette: [palette name or "default"] Palette: [name or "default"]
Layout: [layout name or "varies"] Layout: [name or "varies"]
Location: [directory path] Location: [directory]
Images: N total Images: N total
✓ analysis.md ✓ analysis.md
✓ outline.md ✓ outline.md
✓ outline-strategy-a/b/c.md (detailed mode only) ✓ outline-strategy-a/b/c.md (detailed mode only)
Files:
- 01-cover-[slug].png ✓ Cover (sparse) - 01-cover-[slug].png ✓ Cover (sparse)
- 02-content-[slug].png ✓ Content (balanced) - 02-content-[slug].png ✓ Content (balanced)
- 03-content-[slug].png ✓ Content (dense) - ...
- 04-ending-[slug].png ✓ Ending (sparse) - NN-ending-[slug].png ✓ Ending (sparse)
``` ```
## Image Modification
| Action | Steps |
|--------|-------|
| **Edit** | **Update prompt file FIRST** → Regenerate with same session ID |
| **Add** | Specify position → Create prompt → Generate → Renumber subsequent files (NN+1) → Update outline |
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update outline |
**IMPORTANT**: When updating images, ALWAYS update the prompt file (`prompts/NN-{type}-[slug].md`) FIRST before regenerating. This ensures changes are documented and reproducible.
## Content Breakdown Principles ## Content Breakdown Principles
1. **Cover (Image 1)**: Hook + visual impact → `sparse` layout | Position | Purpose | Typical layout |
2. **Content (Middle)**: Core value per image → `balanced`/`dense`/`list`/`comparison`/`flow` |----------|---------|----------------|
3. **Ending (Last)**: CTA / summary → `sparse` or `balanced` | Cover (image 1) | Hook + visual impact | `sparse` |
| Content (middle) | Core value per image | `balanced` / `dense` / `list` / `comparison` / `flow` |
| Ending (last) | CTA / summary | `sparse` or `balanced` |
**Style × Layout Matrix** (✓✓ = highly recommended, ✓ = works well): For the style × layout compatibility matrix, see the **Style × Layout Matrix** above.
| | sparse | balanced | dense | list | comparison | flow | mindmap | quadrant | ## Image Modification
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| cute | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | | Action | How |
| fresh | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ | |--------|-----|
| warm | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | | Edit | Update `prompts/NN-{type}-{slug}.md` **first**, then regenerate with the same session ID |
| bold | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | | Add | Specify position, create prompt, generate, renumber subsequent files `NN+1`, update outline |
| minimal | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Delete | Remove files, renumber subsequent `NN-1`, update outline |
| retro | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
| pop | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | Always update the prompt file before regenerating — it's the source of truth and makes changes reproducible.
| notion | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ |
| chalkboard | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| study-notes | ✗ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ |
| screen-print | ✓✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ |
| sketch-notes | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
## References ## References
Detailed templates in `references/` directory: | File | Content |
|------|---------|
**Elements** (Visual building blocks): | `references/confirmation.md` | Verbatim AskUserQuestion copy for every confirmation path |
- `elements/canvas.md` - Aspect ratios, safe zones, grid layouts | `references/style-presets.md` | Full preset shortcut definitions |
- `elements/image-effects.md` - Cutout, stroke, filters | `references/presets/<style>.md` | Per-style element definitions |
- `elements/typography.md` - Decorated text (花字), tags, text direction | `references/palettes/<name>.md` | Per-palette color definitions |
- `elements/decorations.md` - Emphasis marks, backgrounds, doodles, frames | `references/elements/canvas.md` | Aspect ratios, safe zones, grid layouts |
| `references/elements/image-effects.md` | Cutout, stroke, filters |
**Presets** (Style presets): | `references/elements/typography.md` | Decorated text, tags, text direction |
- `presets/<name>.md` - Element combination definitions (cute, notion, warm...) | `references/elements/decorations.md` | Emphasis marks, backgrounds, doodles, frames |
- `style-presets.md` - Preset shortcuts (style + layout + palette combos) | `references/workflows/analysis-framework.md` | Content analysis framework |
| `references/workflows/outline-template.md` | Outline template with layout guide |
**Palettes** (Color overrides): | `references/workflows/prompt-assembly.md` | Prompt assembly guide |
- `palettes/<name>.md` - Color palette definitions (macaron, warm, neon) | `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow |
**Workflows** (Process guides): | `references/config/watermark-guide.md` | Watermark configuration |
- `workflows/analysis-framework.md` - Content analysis framework
- `workflows/outline-template.md` - Outline template with layout guide
- `workflows/prompt-assembly.md` - Prompt assembly guide
**Config** (Settings):
- `config/preferences-schema.md` - EXTEND.md schema
- `config/first-time-setup.md` - First-time setup flow
- `config/watermark-guide.md` - Watermark configuration
## Notes ## Notes
- Auto-retry once on failure | Cartoon alternatives for sensitive figures - Auto-retry once on generation failure before reporting an error.
- Use confirmed language preference | Maintain style consistency - For sensitive public figures, use stylized cartoon alternatives.
- **Smart Confirm required** (Step 2) - do not skip; detailed mode uses two sub-confirmations - Smart Confirm (Step 2) is required; Detailed mode adds a second confirmation (2a + 2c).
## Extension Support Custom configurations via EXTEND.md. See Step 0 for paths and schema.
Custom configurations via EXTEND.md. See **Step 0** for paths and supported options.
@@ -0,0 +1,156 @@
# Confirmation Questions
Concrete option copy for Step 2 Smart Confirm. SKILL.md states which question to ask and when — this file supplies the verbatim options used in Claude Code. Other runtimes should adapt the wording to their native user-input tool while preserving intent.
## Step 2 — Smart Confirm Entry
Single-question confirmation presented right after the auto-recommended plan.
```yaml
header: Mode
question: How to proceed with the recommended plan?
options:
- label: 1. ✅ 确认,直接生成(推荐)
description: Trust auto-recommendation and proceed immediately
- label: 2. 🎛️ 自定义调整
description: Modify strategy/style/layout/count in one step
- label: 3. 📋 详细模式
description: Generate 3 outline variants, then choose (two confirmations)
```
## Path B — Customize (Option 2)
Batch these five questions. Leaving a field blank keeps the recommended value.
```yaml
header: Style/Strategy
question: "Strategy + style. Current: {strategy} + {style}"
hint: |
Strategies: A Story-Driven (warm) | B Information-Dense (notion) | C Visual-First (screen-print)
Styles: cute / fresh / warm / bold / minimal / retro / pop / notion / chalkboard / study-notes / screen-print / sketch-notes
Presets: knowledge-card / checklist / tutorial / poster / hand-drawn-edu / ...
```
```yaml
header: Layout
question: "Layout. Current: {layout}"
options: [sparse, balanced, dense, list, comparison, flow, mindmap, quadrant]
```
```yaml
header: Palette
question: "Palette. Current: {palette or 默认}"
options: [默认, macaron, warm, neon]
```
```yaml
header: Count
question: "Image count. Current: {N}"
hint: Range 2-10
```
```yaml
header: Notes
question: Optional notes (selling-point emphasis, audience adjustment, color preference)
optional: true
```
## Path C — Detailed Mode
### Step 2a: Content Understanding
Batch these questions.
```yaml
header: SellingPoints
question: Core selling points (pick all that apply)
multiSelect: true
```
```yaml
header: Audience
question: Target audience
```
```yaml
header: Tone
question: Style preference
options:
- label: Authentic sharing
- label: Professional review
- label: Aesthetic mood
- label: Auto
```
```yaml
header: Context
question: Additional context (optional)
optional: true
```
### Step 2c: Outline & Style Selection
Batch these three questions.
```yaml
header: Strategy
question: Which outline strategy?
options:
- label: A — Story-Driven
- label: B — Information-Dense
- label: C — Visual-First
- label: Combine (specify pages from each)
```
```yaml
header: Style
question: Visual style?
options:
- label: Use recommended
- label: Select preset
- label: Select style directly
- label: Custom description
```
```yaml
header: Elements
question: Visual elements?
options:
- label: Use defaults (Recommended)
- label: Adjust background
- label: Adjust decorations
- label: Custom
```
## Outline Variant Frontmatter
Used by Path C when writing the three `outline-strategy-{a,b,c}.md` files. Each variant MUST have a different structure AND a different recommended style — include `style_reason` explaining why the style fits the strategy.
```yaml
---
strategy: a # a | b | c
name: Story-Driven
style: warm # recommended style for this strategy
palette: ~ # optional: macaron | warm | neon | ~ (style default)
style_reason: "Warm tones enhance emotional storytelling and personal connection"
elements:
background: solid-pastel
decorations: [clouds, stars-sparkles]
emphasis: star-burst
typography: highlight
layout: balanced
image_count: 5
---
## P1 Cover
**Type**: cover
**Hook**: "入冬后脸不干了🥹终于找到对的面霜"
**Visual**: Product hero shot with cozy winter atmosphere
**Layout**: sparse
## P2 Problem
**Type**: pain-point
...
```
Page-count heuristic: strategy A typically 4-6 pages, B typically 3-5, C typically 3-4.
+112 -291
View File
@@ -1,6 +1,6 @@
--- ---
name: baoyu-image-gen name: baoyu-image-gen
description: AI image generation with OpenAI, Azure OpenAI, Google, OpenRouter, DashScope, MiniMax, Jimeng, Seedream and Replicate APIs. Supports text-to-image, reference images, aspect ratios, and batch generation from saved prompt files. Sequential by default; use batch parallel generation when the user already has multiple prompts or wants stable multi-image throughput. Use when user asks to generate, create, or draw images. description: "[Deprecated: use baoyu-imagine] AI image generation with OpenAI, Azure OpenAI, Google, OpenRouter, DashScope, Z.AI GLM-Image, MiniMax, Jimeng, Seedream and Replicate APIs. Supports text-to-image, reference images, aspect ratios, and batch generation from saved prompt files. Sequential by default; use batch parallel generation when the user already has multiple prompts or wants stable multi-image throughput. Use when user asks to generate, create, or draw images."
version: 1.56.4 version: 1.56.4
metadata: metadata:
openclaw: openclaw:
@@ -11,147 +11,66 @@ metadata:
- npx - npx
--- ---
> **⚠️ DEPRECATED**: This skill has been migrated to `baoyu-imagine`. Print a bold red warning to the user **in the user's language** (use ANSI escape `\033[1;31m`) stating that `baoyu-image-gen` has been migrated to `baoyu-imagine` and they should use `baoyu-imagine` instead. Then STOP — do NOT continue executing this skill.
# Image Generation (AI SDK) # Image Generation (AI SDK)
Official API-based image generation. Supports OpenAI, Azure OpenAI, Google, OpenRouter, DashScope (阿里通义万象), MiniMax, Jimeng (即梦), Seedream (豆包) and Replicate providers. Official API-based image generation. Supports OpenAI, Azure OpenAI, Google, OpenRouter, DashScope (阿里通义万象), Z.AI GLM-Image, MiniMax, Jimeng (即梦), Seedream (豆包) and Replicate.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
**Agent Execution**: `{baseDir}` = this SKILL.md's directory. Main script: `{baseDir}/scripts/main.ts`. Resolve `${BUN_X}`: prefer `bun`; else `npx -y bun`; else suggest `brew install oven-sh/bun/bun`.
1. `{baseDir}` = this SKILL.md file's directory
2. Script path = `{baseDir}/scripts/main.ts`
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
## Step 0: Load Preferences ⛔ BLOCKING ## Step 0: Load Preferences ⛔ BLOCKING
**CRITICAL**: This step MUST complete BEFORE any image generation. Do NOT skip or defer. This step MUST complete before any image generation — generation is blocked until EXTEND.md exists.
Check EXTEND.md existence (priority: project → user): Check these paths in order; first hit wins:
```bash | Path | Scope |
# macOS, Linux, WSL, Git Bash |------|-------|
test -f .baoyu-skills/baoyu-image-gen/EXTEND.md && echo "project" | `.baoyu-skills/baoyu-image-gen/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-image-gen/EXTEND.md" && echo "xdg" | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-image-gen/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-image-gen/EXTEND.md" && echo "user"
```
```powershell
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-image-gen/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-image-gen/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-image-gen/EXTEND.md") { "user" }
```
| Result | Action |
|--------|--------|
| Found | Load, parse, apply settings. If `default_model.[provider]` is null → ask model only (Flow 2) |
| Not found | ⛔ Run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Save EXTEND.md → Then continue |
**CRITICAL**: If not found, complete the full setup (provider + model + quality + save location) using AskUserQuestion BEFORE generating any images. Generation is BLOCKED until EXTEND.md is created.
| Path | Location |
|------|----------|
| `.baoyu-skills/baoyu-image-gen/EXTEND.md` | Project directory |
| `$HOME/.baoyu-skills/baoyu-image-gen/EXTEND.md` | User home | | `$HOME/.baoyu-skills/baoyu-image-gen/EXTEND.md` | User home |
**EXTEND.md Supports**: Default provider | Default quality | Default aspect ratio | Default image size | Default models | Batch worker cap | Provider-specific batch limits - **Found** → load, parse, apply. If `default_model.[provider]` is null → ask model only.
- **Not found** → run first-time setup (`references/config/first-time-setup.md`) using AskUserQuestion to collect provider + model + quality + save location. Save EXTEND.md, then continue. Do not generate images before this completes.
Schema: `references/config/preferences-schema.md` **EXTEND.md keys**: default provider, default quality, default aspect ratio, default image size, OpenAI image API dialect, default models, batch worker cap, provider-specific batch limits. Schema: `references/config/preferences-schema.md`.
## Usage ## Usage
Minimum working examples — see `references/usage-examples.md` for the full set including per-provider invocations and batch mode.
```bash ```bash
# Basic # Basic
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image cat.png ${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image cat.png
# With aspect ratio # With aspect ratio and high quality
${BUN_X} {baseDir}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9 ${BUN_X} {baseDir}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9 --quality 2k
# High quality # Prompt from files
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --quality 2k
# From prompt files
${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png ${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png
# With reference images (Google, OpenAI, Azure OpenAI, OpenRouter, Replicate, MiniMax, or Seedream 4.0/4.5/5.0) # With reference image
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png ${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png
# With reference images (explicit provider/model)
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider google --model gemini-3-pro-image-preview --ref source.png
# Azure OpenAI (model means deployment name)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider azure --model gpt-image-1.5
# OpenRouter (recommended default model)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openrouter
# OpenRouter with reference images
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider openrouter --model google/gemini-3.1-flash-image-preview --ref source.png
# Specific provider # Specific provider
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openai ${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider dashscope --model qwen-image-2.0-pro
# DashScope (阿里通义万象) # Batch mode
${BUN_X} {baseDir}/scripts/main.ts --prompt "一只可爱的猫" --image out.png --provider dashscope ${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4
# DashScope Qwen-Image 2.0 Pro (recommended for custom sizes and text rendering)
${BUN_X} {baseDir}/scripts/main.ts --prompt "为咖啡品牌设计一张 21:9 横幅海报,包含清晰中文标题" --image out.png --provider dashscope --model qwen-image-2.0-pro --size 2048x872
# DashScope legacy Qwen fixed-size model
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张电影感海报" --image out.png --provider dashscope --model qwen-image-max --size 1664x928
# MiniMax
${BUN_X} {baseDir}/scripts/main.ts --prompt "A fashion editorial portrait by a bright studio window" --image out.jpg --provider minimax
# MiniMax with subject reference (best for character/portrait consistency)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A girl stands by the library window, cinematic lighting" --image out.jpg --provider minimax --model image-01 --ref portrait.png --ar 16:9
# MiniMax with custom size (documented for image-01)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cinematic poster" --image out.jpg --provider minimax --model image-01 --size 1536x1024
# Replicate (google/nano-banana-pro)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Replicate with specific model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate --model google/nano-banana
# Batch mode with saved prompt files
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json
# Batch mode with explicit worker count
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4 --json
``` ```
### Batch File Format
```json
{
"jobs": 4,
"tasks": [
{
"id": "hero",
"promptFiles": ["prompts/hero.md"],
"image": "out/hero.png",
"provider": "replicate",
"model": "google/nano-banana-pro",
"ar": "16:9",
"quality": "2k"
},
{
"id": "diagram",
"promptFiles": ["prompts/diagram.md"],
"image": "out/diagram.png",
"ref": ["references/original.png"]
}
]
}
```
Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch file's directory. `jobs` is optional (overridden by CLI `--jobs`). Top-level array format (without `jobs` wrapper) is also accepted.
## Options ## Options
| Option | Description | | Option | Description |
@@ -161,14 +80,15 @@ Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch fi
| `--image <path>` | Output image path (required in single-image mode) | | `--image <path>` | Output image path (required in single-image mode) |
| `--batchfile <path>` | JSON batch file for multi-image generation | | `--batchfile <path>` | JSON batch file for multi-image generation |
| `--jobs <count>` | Worker count for batch mode (default: auto, max from config, built-in default 10) | | `--jobs <count>` | Worker count for batch mode (default: auto, max from config, built-in default 10) |
| `--provider google\|openai\|azure\|openrouter\|dashscope\|minimax\|jimeng\|seedream\|replicate` | Force provider (default: auto-detect) | | `--provider google\|openai\|azure\|openrouter\|dashscope\|zai\|minimax\|jimeng\|seedream\|replicate` | Force provider (default: auto-detect) |
| `--model <id>`, `-m` | Model ID (Google: `gemini-3-pro-image-preview`; OpenAI: `gpt-image-1.5`; Azure: deployment name such as `gpt-image-1.5` or `image-prod`; OpenRouter: `google/gemini-3.1-flash-image-preview`; DashScope: `qwen-image-2.0-pro`; MiniMax: `image-01`) | | `--model <id>`, `-m` | Model ID — see provider references for defaults and allowed values |
| `--ar <ratio>` | Aspect ratio (e.g., `16:9`, `1:1`, `4:3`) | | `--ar <ratio>` | Aspect ratio (`16:9`, `1:1`, `4:3`, …) |
| `--size <WxH>` | Size (e.g., `1024x1024`) | | `--size <WxH>` | Explicit size (e.g., `1024x1024`) |
| `--quality normal\|2k` | Quality preset (default: `2k`) | | `--quality normal\|2k` | Quality preset (default: `2k`) |
| `--imageSize 1K\|2K\|4K` | Image size for Google/OpenRouter (default: from quality) | | `--imageSize 1K\|2K\|4K` | Image size for Google/OpenRouter (default: from quality) |
| `--ref <files...>` | Reference images. Supported by Google multimodal, OpenAI GPT Image edits, Azure OpenAI edits (PNG/JPG only), OpenRouter multimodal models, Replicate, MiniMax subject-reference, and Seedream 5.0/4.5/4.0. Not supported by Jimeng, Seedream 3.0, or removed SeedEdit 3.0 | | `--imageApiDialect openai-native\|ratio-metadata` | OpenAI-compatible endpoint dialect — use `ratio-metadata` for gateways that expect aspect-ratio `size` plus `metadata.resolution` |
| `--n <count>` | Number of images | | `--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 |
| `--n <count>` | Number of images. Replicate requires `--n 1` (single-output save semantics) |
| `--json` | JSON output | | `--json` | JSON output |
## Environment Variables ## Environment Variables
@@ -179,221 +99,109 @@ Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch fi
| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key | | `AZURE_OPENAI_API_KEY` | Azure OpenAI API key |
| `OPENROUTER_API_KEY` | OpenRouter API key | | `OPENROUTER_API_KEY` | OpenRouter API key |
| `GOOGLE_API_KEY` | Google API key | | `GOOGLE_API_KEY` | Google API key |
| `DASHSCOPE_API_KEY` | DashScope API key (阿里云) | | `DASHSCOPE_API_KEY` | DashScope API key |
| `ZAI_API_KEY` (alias `BIGMODEL_API_KEY`) | Z.AI API key |
| `MINIMAX_API_KEY` | MiniMax API key | | `MINIMAX_API_KEY` | MiniMax API key |
| `REPLICATE_API_TOKEN` | Replicate API token | | `REPLICATE_API_TOKEN` | Replicate API token |
| `JIMENG_ACCESS_KEY_ID` | Jimeng (即梦) Volcengine access key | | `JIMENG_ACCESS_KEY_ID`, `JIMENG_SECRET_ACCESS_KEY` | Jimeng (即梦) Volcengine credentials |
| `JIMENG_SECRET_ACCESS_KEY` | Jimeng (即梦) Volcengine secret key |
| `ARK_API_KEY` | Seedream (豆包) Volcengine ARK API key | | `ARK_API_KEY` | Seedream (豆包) Volcengine ARK API key |
| `OPENAI_IMAGE_MODEL` | OpenAI model override | | `<PROVIDER>_IMAGE_MODEL` | Per-provider model override (`OPENAI_IMAGE_MODEL`, `GOOGLE_IMAGE_MODEL`, `DASHSCOPE_IMAGE_MODEL`, `ZAI_IMAGE_MODEL`/`BIGMODEL_IMAGE_MODEL`, `MINIMAX_IMAGE_MODEL`, `OPENROUTER_IMAGE_MODEL`, `REPLICATE_IMAGE_MODEL`, `JIMENG_IMAGE_MODEL`, `SEEDREAM_IMAGE_MODEL`) |
| `AZURE_OPENAI_DEPLOYMENT` | Azure default deployment name | | `AZURE_OPENAI_DEPLOYMENT` (alias `AZURE_OPENAI_IMAGE_MODEL`) | Azure default deployment |
| `AZURE_OPENAI_IMAGE_MODEL` | Backward-compatible alias for Azure default deployment/model name | | `<PROVIDER>_BASE_URL` | Per-provider endpoint override |
| `OPENROUTER_IMAGE_MODEL` | OpenRouter model override (default: `google/gemini-3.1-flash-image-preview`) | | `AZURE_API_VERSION` | Azure image API version (default `2025-04-01-preview`) |
| `GOOGLE_IMAGE_MODEL` | Google model override | | `JIMENG_REGION` | Jimeng region (default `cn-north-1`) |
| `DASHSCOPE_IMAGE_MODEL` | DashScope model override (default: `qwen-image-2.0-pro`) | | `OPENAI_IMAGE_API_DIALECT` | `openai-native` \| `ratio-metadata` |
| `MINIMAX_IMAGE_MODEL` | MiniMax model override (default: `image-01`) | | `OPENROUTER_HTTP_REFERER`, `OPENROUTER_TITLE` | Optional OpenRouter attribution |
| `REPLICATE_IMAGE_MODEL` | Replicate model override (default: google/nano-banana-pro) |
| `JIMENG_IMAGE_MODEL` | Jimeng model override (default: jimeng_t2i_v40) |
| `SEEDREAM_IMAGE_MODEL` | Seedream model override (default: doubao-seedream-5-0-260128) |
| `OPENAI_BASE_URL` | Custom OpenAI endpoint |
| `AZURE_OPENAI_BASE_URL` | Azure resource endpoint or deployment endpoint |
| `AZURE_API_VERSION` | Azure image API version (default: `2025-04-01-preview`) |
| `OPENROUTER_BASE_URL` | Custom OpenRouter endpoint (default: `https://openrouter.ai/api/v1`) |
| `OPENROUTER_HTTP_REFERER` | Optional app/site URL for OpenRouter attribution |
| `OPENROUTER_TITLE` | Optional app name for OpenRouter attribution |
| `GOOGLE_BASE_URL` | Custom Google endpoint |
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint |
| `MINIMAX_BASE_URL` | Custom MiniMax endpoint (default: `https://api.minimax.io`) |
| `REPLICATE_BASE_URL` | Custom Replicate endpoint |
| `JIMENG_BASE_URL` | Custom Jimeng endpoint (default: `https://visual.volcengineapi.com`) |
| `JIMENG_REGION` | Jimeng region (default: `cn-north-1`) |
| `SEEDREAM_BASE_URL` | Custom Seedream endpoint (default: `https://ark.cn-beijing.volces.com/api/v3`) |
| `BAOYU_IMAGE_GEN_MAX_WORKERS` | Override batch worker cap | | `BAOYU_IMAGE_GEN_MAX_WORKERS` | Override batch worker cap |
| `BAOYU_IMAGE_GEN_<PROVIDER>_CONCURRENCY` | Override provider concurrency, e.g. `BAOYU_IMAGE_GEN_REPLICATE_CONCURRENCY` | | `BAOYU_IMAGE_GEN_<PROVIDER>_CONCURRENCY` | Per-provider concurrency (e.g., `BAOYU_IMAGE_GEN_REPLICATE_CONCURRENCY`) |
| `BAOYU_IMAGE_GEN_<PROVIDER>_START_INTERVAL_MS` | Override provider start gap, e.g. `BAOYU_IMAGE_GEN_REPLICATE_START_INTERVAL_MS` | | `BAOYU_IMAGE_GEN_<PROVIDER>_START_INTERVAL_MS` | Per-provider start-gap |
**Load Priority**: CLI args > EXTEND.md > env vars > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env` **Load priority**: CLI args > EXTEND.md > env vars > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env`
## Model Resolution ## Model Resolution
Model priority (highest → lowest), applies to all providers: Priority (highest → lowest) applies to every provider:
1. CLI flag: `--model <id>` 1. CLI flag `--model <id>`
2. EXTEND.md: `default_model.[provider]` 2. EXTEND.md `default_model.[provider]`
3. Env var: `<PROVIDER>_IMAGE_MODEL` (e.g., `GOOGLE_IMAGE_MODEL`) 3. Env var `<PROVIDER>_IMAGE_MODEL`
4. Built-in default 4. Built-in default
For Azure, `--model` / `default_model.azure` should be the Azure deployment name. `AZURE_OPENAI_DEPLOYMENT` is the preferred env var, and `AZURE_OPENAI_IMAGE_MODEL` remains as a backward-compatible alias. For Azure, `--model` / `default_model.azure` is the Azure deployment name. `AZURE_OPENAI_DEPLOYMENT` is the preferred env var; `AZURE_OPENAI_IMAGE_MODEL` is kept as a backward-compatible alias.
**EXTEND.md overrides env vars**. If both EXTEND.md `default_model.google: "gemini-3-pro-image-preview"` and env var `GOOGLE_IMAGE_MODEL=gemini-3.1-flash-image-preview` exist, EXTEND.md wins. EXTEND.md overrides env vars: if EXTEND.md sets `default_model.google: "gemini-3-pro-image-preview"` and the env var sets `GOOGLE_IMAGE_MODEL=gemini-3.1-flash-image-preview`, EXTEND.md wins.
**Agent MUST display model info** before each generation: **Display model info before each generation**:
- Show: `Using [provider] / [model]`
- Show switch hint: `Switch model: --model <id> | EXTEND.md default_model.[provider] | env <PROVIDER>_IMAGE_MODEL`
### DashScope Models - `Using [provider] / [model]`
- `Switch model: --model <id> | EXTEND.md default_model.[provider] | env <PROVIDER>_IMAGE_MODEL`
Use `--model qwen-image-2.0-pro` or set `default_model.dashscope` / `DASHSCOPE_IMAGE_MODEL` when the user wants official Qwen-Image behavior. ## OpenAI-Compatible Gateway Dialects
Official DashScope model families: `provider=openai` means the auth and routing entrypoint is OpenAI-compatible. It does **not** guarantee the upstream image API uses OpenAI native semantics. When a gateway expects a different wire format, set `default_image_api_dialect` in EXTEND.md, `OPENAI_IMAGE_API_DIALECT`, or `--imageApiDialect`:
- `qwen-image-2.0-pro`, `qwen-image-2.0-pro-2026-03-03`, `qwen-image-2.0`, `qwen-image-2.0-2026-03-03` - `openai-native`: pixel `size` (`1536x1024`) and native OpenAI quality fields
- Free-form `size` in `宽*高` format - `ratio-metadata`: aspect-ratio `size` (`16:9`) plus `metadata.resolution` (`1K|2K|4K`) and `metadata.orientation`
- Total pixels must stay between `512*512` and `2048*2048`
- Default size is approximately `1024*1024`
- Best choice for custom ratios such as `21:9` and text-heavy Chinese/English layouts
- `qwen-image-max`, `qwen-image-max-2025-12-30`, `qwen-image-plus`, `qwen-image-plus-2026-01-09`, `qwen-image`
- Fixed sizes only: `1664*928`, `1472*1104`, `1328*1328`, `1104*1472`, `928*1664`
- Default size is `1664*928`
- `qwen-image` currently has the same capability as `qwen-image-plus`
- Legacy DashScope models such as `z-image-turbo`, `z-image-ultra`, `wanx-v1`
- Keep using them only when the user explicitly asks for legacy behavior or compatibility
When translating CLI args into DashScope behavior: Use `openai-native` for the OpenAI native API or strict clones; try `ratio-metadata` for compatibility gateways in front of Gemini or similar models. Current limitation: `ratio-metadata` applies only to text-to-image; reference-image edits still need `openai-native` or a provider with first-class edit support.
- `--size` wins over `--ar` ## Provider-Specific Guides
- For `qwen-image-2.0*`, prefer explicit `--size`; otherwise infer from `--ar` and use the official recommended resolutions below
- For `qwen-image-max/plus/image`, only use the five official fixed sizes; if the requested ratio is not covered, switch to `qwen-image-2.0-pro`
- `--quality` is a baoyu-image-gen compatibility preset, not a native DashScope API field. Mapping `normal` / `2k` onto the `qwen-image-2.0*` table below is an implementation inference, not an official API guarantee
Recommended `qwen-image-2.0*` sizes for common aspect ratios: Each provider has its own quirks (model families, size rules, ref support, limits). Read these when the user picks that provider or asks for non-default behavior:
| Ratio | `normal` | `2k` | | Provider | Reference |
|-------|----------|------| |----------|-----------|
| `1:1` | `1024*1024` | `1536*1536` | | DashScope (Qwen-Image families, custom sizes) | `references/providers/dashscope.md` |
| `2:3` | `768*1152` | `1024*1536` | | Z.AI (GLM-Image, cogview-4) | `references/providers/zai.md` |
| `3:2` | `1152*768` | `1536*1024` | | MiniMax (image-01, subject-reference) | `references/providers/minimax.md` |
| `3:4` | `960*1280` | `1080*1440` | | OpenRouter (multimodal models, `/chat/completions` flow) | `references/providers/openrouter.md` |
| `4:3` | `1280*960` | `1440*1080` | | Replicate (nano-banana, Seedream, Wan) | `references/providers/replicate.md` |
| `9:16` | `720*1280` | `1080*1920` |
| `16:9` | `1280*720` | `1920*1080` |
| `21:9` | `1344*576` | `2048*872` |
DashScope official APIs also expose `negative_prompt`, `prompt_extend`, and `watermark`, but `baoyu-image-gen` does not expose them as dedicated CLI flags today.
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)
### MiniMax Models
Use `--model image-01` or set `default_model.minimax` / `MINIMAX_IMAGE_MODEL` when the user wants MiniMax image generation.
Official MiniMax image model options currently documented in the API reference:
- `image-01` (recommended default)
- Supports text-to-image and subject-reference image generation
- Supports official `aspect_ratio` values: `1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`
- Supports documented custom `width` / `height` output sizes when using `--size <WxH>`
- `width` and `height` must both be between `512` and `2048`, and both must be divisible by `8`
- `image-01-live`
- Lower-latency variant
- Use `--ar` for sizing; MiniMax documents custom `width` / `height` as only effective for `image-01`
MiniMax subject reference notes:
- `--ref` files are sent as MiniMax `subject_reference`
- MiniMax docs currently describe `subject_reference[].type` as `character`
- Official docs say `image_file` supports public URLs or Base64 Data URLs; `baoyu-image-gen` sends local refs as Data URLs
- Official docs recommend front-facing portrait references in JPG/JPEG/PNG under 10MB
Official references:
- [MiniMax Image Generation Guide](https://platform.minimax.io/docs/guides/image-generation)
- [MiniMax Text-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-t2i)
- [MiniMax Image-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-i2i)
### OpenRouter Models
Use full OpenRouter model IDs, e.g.:
- `google/gemini-3.1-flash-image-preview` (recommended, supports image output and reference-image workflows)
- `google/gemini-2.5-flash-image-preview`
- `black-forest-labs/flux.2-pro`
- Other OpenRouter image-capable model IDs
Notes:
- OpenRouter image generation uses `/chat/completions`, not the OpenAI `/images` endpoints
- If `--ref` is used, choose a multimodal model that supports image input and image output
- `--imageSize` maps to OpenRouter `imageGenerationOptions.size`; `--size <WxH>` is converted to the nearest OpenRouter size and inferred aspect ratio when possible
### Replicate Models
Supported model formats:
- `owner/name` (recommended for official models), e.g. `google/nano-banana-pro`
- `owner/name:version` (community models by version), e.g. `stability-ai/sdxl:<version>`
Examples:
```bash
# Use Replicate default model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Override model explicitly
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate --model google/nano-banana
```
## Provider Selection ## Provider Selection
1. `--ref` provided + no `--provider` → auto-select Google first, then OpenAI, then Azure, then OpenRouter, then Replicate, then Seedream, then MiniMax (MiniMax subject reference is more specialized toward character/portrait consistency) 1. `--ref` provided + no `--provider` → auto-select Google → OpenAI → Azure → OpenRouter Replicate Seedream MiniMax (MiniMax's subject reference is more specialized toward character/portrait consistency)
2. `--provider` specified → use it (if `--ref`, must be `google`, `openai`, `azure`, `openrouter`, `replicate`, `seedream`, or `minimax`) 2. `--provider` specified → use it (if `--ref`, must be google/openai/azure/openrouter/replicate/seedream/minimax)
3. Only one API key available → use that provider 3. Only one API key present → use that provider
4. Multiple available → default to Google 4. Multiple keys → default priority: Google → OpenAI → Azure → OpenRouter → DashScope → Z.AI → MiniMax → Replicate → Jimeng → Seedream
## Quality Presets ## Quality Presets
| Preset | Google imageSize | OpenAI Size | OpenRouter size | Replicate resolution | Use Case | | Preset | Google imageSize | OpenAI size | OpenRouter size | Replicate resolution | Use case |
|--------|------------------|-------------|-----------------|----------------------|----------| |--------|------------------|-------------|-----------------|----------------------|----------|
| `normal` | 1K | 1024px | 1K | 1K | Quick previews | | `normal` | 1K | 1024px | 1K | 1K | Quick previews |
| `2k` (default) | 2K | 2048px | 2K | 2K | Covers, illustrations, infographics | | `2k` (default) | 2K | 2048px | 2K | 2K | Covers, illustrations, infographics |
**Google/OpenRouter imageSize**: Can be overridden with `--imageSize 1K|2K|4K` Google/OpenRouter `imageSize` can be overridden with `--imageSize 1K|2K|4K`.
## Aspect Ratios ## Aspect Ratios
Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1` Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1`.
- Google multimodal: uses `imageConfig.aspectRatio` - Google multimodal: `imageConfig.aspectRatio`
- OpenAI: maps to closest supported size - OpenAI: closest supported size
- OpenRouter: sends `imageGenerationOptions.aspect_ratio`; if only `--size <WxH>` is given, aspect ratio is inferred automatically - OpenRouter: `imageGenerationOptions.aspect_ratio`; if only `--size <WxH>` is given, the ratio is inferred
- Replicate: passes `aspect_ratio` to model; when `--ref` is provided without `--ar`, defaults to `match_input_image` - Replicate: behavior is model-specific — `google/nano-banana*` uses `aspect_ratio`, `bytedance/seedream-*` uses documented Replicate ratios, Wan 2.7 maps `--ar` to a concrete `size`
- MiniMax: sends official `aspect_ratio` values directly; if `--size <WxH>` is given without `--ar`, `width` / `height` are sent for `image-01` - MiniMax: official `aspect_ratio` values; if `--size <WxH>` is given without `--ar`, sends `width`/`height` for `image-01`
## Generation Mode ## Generation Mode
**Default**: Sequential generation. **Default**: sequential. **Batch parallel**: enabled automatically when `--batchfile` contains 2+ pending tasks.
**Batch Parallel Generation**: When `--batchfile` contains 2 or more pending tasks, the script automatically enables parallel generation. | Situation | Prefer | Why |
|-----------|--------|-----|
| One image, or 1-2 simple images | Sequential | Lower coordination overhead, easier debugging |
| Multiple images with saved prompt files | Batch (`--batchfile`) | Reuses finalized prompts, applies shared throttling/retries, predictable throughput |
| Each image still needs its own reasoning / prompt writing / style exploration | Subagents | Work is still exploratory, each needs independent analysis |
| Input is `outline.md` + `prompts/` (e.g. from `baoyu-article-illustrator`) | Batch — use `scripts/build-batch.ts` to assemble the payload | The outline + prompt files already contain everything needed |
| Mode | When to Use | Rule of thumb: once prompt files are saved and the task is "generate all of these", prefer batch over subagents. Use subagents only when generation is coupled with per-image thinking or divergent creative exploration.
|------|-------------|
| Sequential (default) | Normal usage, single images, small batches |
| Parallel batch | Batch mode with 2+ tasks |
Execution choice: **Parallel behavior**:
| Situation | Preferred approach | Why |
|-----------|--------------------|-----|
| One image, or 1-2 simple images | Sequential | Lower coordination overhead and easier debugging |
| Multiple images already have saved prompt files | Batch (`--batchfile`) | Reuses finalized prompts, applies shared throttling/retries, and gives predictable throughput |
| Each image still needs separate reasoning, prompt writing, or style exploration | Subagents | The work is still exploratory, so each image may need independent analysis before generation |
| Output comes from `baoyu-article-illustrator` with `outline.md` + `prompts/` | Batch (`build-batch.ts` -> `--batchfile`) | That workflow already produces prompt files, so direct batch execution is the intended path |
Rule of thumb:
- Prefer batch over subagents once prompt files are already saved and the task is "generate all of these"
- Use subagents only when generation is coupled with per-image thinking, rewriting, or divergent creative exploration
Parallel behavior:
- Default worker count is automatic, capped by config, built-in default 10 - Default worker count is automatic, capped by config, built-in default 10
- Provider-specific throttling is applied only in batch mode, and the built-in defaults are tuned for faster throughput while still avoiding obvious RPM bursts - Provider-specific throttling applies only in batch mode; defaults are tuned for throughput while avoiding RPM bursts
- You can override worker count with `--jobs <count>` - Override with `--jobs <count>`
- Each image retries automatically up to 3 attempts - Each image retries up to 3 attempts
- Final output includes success count, failure count, and per-image failure reasons - Final output includes success count, failure count, and per-image failure reasons
## Error Handling ## Error Handling
@@ -403,6 +211,19 @@ Parallel behavior:
- Invalid aspect ratio → warning, proceed with default - Invalid aspect ratio → warning, proceed with default
- Reference images with unsupported provider/model → error with fix hint - Reference images with unsupported provider/model → error with fix hint
## References
| File | Content |
|------|---------|
| `references/usage-examples.md` | Extended CLI examples across providers and batch mode |
| `references/providers/dashscope.md` | DashScope families, sizes, limits |
| `references/providers/zai.md` | Z.AI GLM-image / cogview-4 |
| `references/providers/minimax.md` | MiniMax image-01 + subject reference |
| `references/providers/openrouter.md` | OpenRouter multimodal flow |
| `references/providers/replicate.md` | Replicate supported families + guardrails |
| `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow |
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options. Custom configurations via EXTEND.md. See Step 0 for paths and schema.
@@ -57,6 +57,8 @@ options:
description: "MiniMax image generation with subject-reference character workflows" description: "MiniMax image generation with subject-reference character workflows"
- label: "Replicate" - label: "Replicate"
description: "Community models - nano-banana-pro, flexible model selection" description: "Community models - nano-banana-pro, flexible model selection"
- label: "Z.AI"
description: "GLM-Image - text-to-image with recommended aspect sizes"
``` ```
### Question 2: Default Google Model ### Question 2: Default Google Model
@@ -119,6 +121,22 @@ options:
description: "Faster variant, use aspect ratio instead of custom size" description: "Faster variant, use aspect ratio instead of custom size"
``` ```
### Question 2e: Default Z.AI Model
Only show if user selected Z.AI.
```yaml
header: "Z.AI Model"
question: "Default Z.AI image generation model?"
options:
- label: "glm-image (Recommended)"
description: "Latest GLM-Image, best aspect-ratio coverage and text rendering"
- label: "cogview-4-250304"
description: "Legacy CogView-4 model with 16-pixel size stepping"
- label: "cogview-4"
description: "Previous CogView-4 snapshot for compatibility"
```
### Question 3: Default Quality ### Question 3: Default Quality
```yaml ```yaml
@@ -167,6 +185,7 @@ default_model:
dashscope: null dashscope: null
minimax: [selected minimax model or null] minimax: [selected minimax model or null]
replicate: null replicate: null
zai: [selected zai model or null]
--- ---
``` ```
@@ -287,6 +306,27 @@ Notes for MiniMax setup:
- `image-01-live` is useful when the user prefers faster generation and can work with aspect-ratio-based sizing. - `image-01-live` is useful when the user prefers faster generation and can work with aspect-ratio-based sizing.
- MiniMax subject reference currently uses `subject_reference[].type = character`; docs recommend front-facing portrait references in JPG/JPEG/PNG under 10MB. - MiniMax subject reference currently uses `subject_reference[].type = character`; docs recommend front-facing portrait references in JPG/JPEG/PNG under 10MB.
### Z.AI Model Selection
```yaml
header: "Z.AI Model"
question: "Choose a default Z.AI image generation model?"
options:
- label: "glm-image (Recommended)"
description: "Latest GLM-Image; pixels round to multiples of 32 and cap at 2^22"
- label: "cogview-4-250304"
description: "Legacy CogView-4 snapshot with 16-pixel size stepping"
- label: "cogview-4"
description: "Earlier CogView-4 snapshot for compatibility"
```
Notes for Z.AI setup:
- Set `ZAI_API_KEY` (or legacy `BIGMODEL_API_KEY`) from https://docs.z.ai/.
- `glm-image` supports recommended aspect sizes (1280x1280, 1728x960, 1568x1056, …); uncommon ratios auto-fit to the 2^22 pixel budget on multiples of 32.
- Legacy CogView models use 16-pixel stepping and cap at 2^21 pixels per image.
- Z.AI does not accept reference images or `n > 1` in `baoyu-image-gen`; use Google/OpenAI providers for those workflows.
### Update EXTEND.md ### Update EXTEND.md
After user selects a model: After user selects a model:
@@ -304,6 +344,7 @@ default_model:
dashscope: [value or null] dashscope: [value or null]
minimax: [value or null] minimax: [value or null]
replicate: [value or null] replicate: [value or null]
zai: [value or null]
``` ```
Only set the selected provider's model; leave others as their current value or null. Only set the selected provider's model; leave others as their current value or null.
@@ -11,7 +11,7 @@ description: EXTEND.md YAML schema for baoyu-image-gen user preferences
--- ---
version: 1 version: 1
default_provider: null # google|openai|azure|openrouter|dashscope|minimax|replicate|null (null = auto-detect) default_provider: null # google|openai|azure|openrouter|dashscope|minimax|replicate|zai|null (null = auto-detect)
default_quality: null # normal|2k|null (null = use default: 2k) default_quality: null # normal|2k|null (null = use default: 2k)
@@ -27,6 +27,7 @@ default_model:
dashscope: null # e.g., "qwen-image-2.0-pro" dashscope: null # e.g., "qwen-image-2.0-pro"
minimax: null # e.g., "image-01" minimax: null # e.g., "image-01"
replicate: null # e.g., "google/nano-banana-pro" replicate: null # e.g., "google/nano-banana-pro"
zai: null # e.g., "glm-image", "cogview-4-250304"
batch: batch:
max_workers: 10 max_workers: 10
@@ -52,6 +53,9 @@ batch:
minimax: minimax:
concurrency: 3 concurrency: 3
start_interval_ms: 1100 start_interval_ms: 1100
zai:
concurrency: 3
start_interval_ms: 1100
--- ---
``` ```
@@ -71,6 +75,7 @@ batch:
| `default_model.dashscope` | string\|null | null | DashScope default model | | `default_model.dashscope` | string\|null | null | DashScope default model |
| `default_model.minimax` | string\|null | null | MiniMax default model | | `default_model.minimax` | string\|null | null | MiniMax default model |
| `default_model.replicate` | string\|null | null | Replicate default model | | `default_model.replicate` | string\|null | null | Replicate default model |
| `default_model.zai` | string\|null | null | Z.AI default model (glm-image / cogview-4-*) |
| `batch.max_workers` | int\|null | 10 | Batch worker cap | | `batch.max_workers` | int\|null | 10 | Batch worker cap |
| `batch.provider_limits.<provider>.concurrency` | int\|null | provider default | Max simultaneous requests per provider | | `batch.provider_limits.<provider>.concurrency` | int\|null | provider default | Max simultaneous requests per provider |
| `batch.provider_limits.<provider>.start_interval_ms` | int\|null | provider default | Minimum gap between request starts per provider | | `batch.provider_limits.<provider>.start_interval_ms` | int\|null | provider default | Minimum gap between request starts per provider |
@@ -102,6 +107,7 @@ default_model:
dashscope: "qwen-image-2.0-pro" dashscope: "qwen-image-2.0-pro"
minimax: "image-01" minimax: "image-01"
replicate: "google/nano-banana-pro" replicate: "google/nano-banana-pro"
zai: "glm-image"
batch: batch:
max_workers: 10 max_workers: 10
provider_limits: provider_limits:
@@ -117,5 +123,8 @@ batch:
minimax: minimax:
concurrency: 3 concurrency: 3
start_interval_ms: 1100 start_interval_ms: 1100
zai:
concurrency: 3
start_interval_ms: 1100
--- ---
``` ```
@@ -0,0 +1,50 @@
# DashScope (阿里通义万象)
Read when the user picks `--provider dashscope`, sets `default_model.dashscope`, or asks for Qwen-Image behavior. The SKILL.md only names the default — this file covers model families, sizing rules, and limits.
## Model Families
**`qwen-image-2.0*`** — recommended modern family. Members: `qwen-image-2.0-pro`, `qwen-image-2.0-pro-2026-03-03`, `qwen-image-2.0`, `qwen-image-2.0-2026-03-03`.
- Free-form `size` in `宽*高` format
- Total pixels must be between `512*512` and `2048*2048`
- Default ≈ `1024*1024`
- Best choice for custom ratios (e.g. `21:9`) and text-heavy Chinese/English layouts
**Fixed-size family**`qwen-image-max`, `qwen-image-max-2025-12-30`, `qwen-image-plus`, `qwen-image-plus-2026-01-09`, `qwen-image`.
- Only five sizes allowed: `1664*928`, `1472*1104`, `1328*1328`, `1104*1472`, `928*1664`
- Default is `1664*928`
- `qwen-image` currently has the same capability as `qwen-image-plus`
**Legacy**`z-image-turbo`, `z-image-ultra`, `wanx-v1`. Only use when the user explicitly asks for legacy behavior.
## Size Resolution
- `--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
### Recommended `qwen-image-2.0*` sizes
| Ratio | `normal` | `2k` |
|-------|----------|------|
| `1:1` | `1024*1024` | `1536*1536` |
| `2:3` | `768*1152` | `1024*1536` |
| `3:2` | `1152*768` | `1536*1024` |
| `3:4` | `960*1280` | `1080*1440` |
| `4:3` | `1280*960` | `1440*1080` |
| `9:16` | `720*1280` | `1080*1920` |
| `16:9` | `1280*720` | `1920*1080` |
| `21:9` | `1344*576` | `2048*872` |
## Not Exposed
DashScope APIs also support `negative_prompt`, `prompt_extend`, and `watermark`, but `baoyu-imagine` does not expose them as CLI flags today.
## 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)
@@ -0,0 +1,29 @@
# MiniMax
Read when the user picks `--provider minimax` or sets `default_model.minimax`. Default model is `image-01`.
## Models
**`image-01`** (recommended default)
- Supports text-to-image and subject-reference image generation
- Supports official `aspect_ratio` values: `1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`
- Supports documented custom `width` / `height` via `--size <WxH>`
- Both width and height must be in `[512, 2048]` and divisible by `8`
**`image-01-live`** — lower-latency variant
- Use `--ar` for sizing; MiniMax documents custom `width`/`height` only for `image-01`
## Subject Reference
- `--ref` files are sent as MiniMax `subject_reference`
- `subject_reference[].type` is currently `character`
- Official docs say `image_file` supports public URLs or Base64 Data URLs; baoyu-imagine sends local refs as Data URLs
- Recommended refs: front-facing portraits, JPG/JPEG/PNG, under 10MB
## 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)
@@ -0,0 +1,19 @@
# OpenRouter
Read when the user picks `--provider openrouter`. Default model is `google/gemini-3.1-flash-image-preview`.
## Common Models
Use full OpenRouter model IDs:
- `google/gemini-3.1-flash-image-preview` (recommended — supports image output and reference-image workflows)
- `google/gemini-2.5-flash-image-preview`
- `black-forest-labs/flux.2-pro`
- Any other OpenRouter image-capable model ID
## Behavior Notes
- OpenRouter image generation uses `/chat/completions`, not the OpenAI `/images` endpoints
- `--ref` requires a multimodal model that supports both image input and image output
- `--imageSize` maps to `imageGenerationOptions.size`
- `--size <WxH>` is converted to the nearest supported OpenRouter size, and the aspect ratio is inferred when possible
@@ -0,0 +1,50 @@
# Replicate
Read when the user picks `--provider replicate`. Replicate support is intentionally scoped to model families baoyu-imagine can validate locally and save without dropping outputs.
## Supported Families
**`google/nano-banana*`** (default: `google/nano-banana-2`)
- Supports prompt-only and reference-image generation
- Uses Replicate `aspect_ratio`, `resolution`, and `output_format`
- `--size <WxH>` is accepted only as a shorthand for a documented `aspect_ratio` plus `1K` / `2K`
**`bytedance/seedream-4.5`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size`, `aspect_ratio`, and `image_input`
- Local validation blocks unsupported `1K` requests before the API call
**`bytedance/seedream-5-lite`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size`, `aspect_ratio`, and `image_input`
- Local validation currently accepts `2K` / `3K` only
**`wan-video/wan-2.7-image`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size` and `images`
- Max output is 2K
**`wan-video/wan-2.7-image-pro`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size` and `images`
- 4K is allowed only for text-to-image; local validation blocks `4K + --ref`
## Guardrails
- Replicate currently supports only single-output save semantics in this tool — keep `--n 1`
- If a model is outside the compatibility list above, baoyu-imagine treats it as prompt-only and rejects advanced local options instead of guessing a nano-banana-style schema
## Examples
```bash
# Default model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Explicit model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate --model google/nano-banana
```
@@ -0,0 +1,27 @@
# Z.AI GLM-Image
Read when the user picks `--provider zai` or sets `default_model.zai`. Default model is `glm-image`.
## Models
**`glm-image`** (recommended default)
- Text-to-image only in baoyu-imagine (no `--ref` support yet)
- Native `quality` options are `hd` and `standard`; this skill maps `2k → hd` and `normal → standard`
- Recommended sizes: `1280x1280`, `1568x1056`, `1056x1568`, `1472x1088`, `1088x1472`, `1728x960`, `960x1728`
- Custom `--size` requires width/height in `[1024, 2048]`, divisible by `32`, total pixels ≤ `2^22`
**`cogview-4-250304`** (legacy family, same endpoint)
- Custom `--size` requires width/height in `[512, 2048]`, divisible by `16`, total pixels ≤ `2^21`
## Behavior Notes
- The sync API returns a temporary URL; baoyu-imagine downloads it and writes locally
- `--ref` is not supported for Z.AI in this skill yet
- The sync API returns a single image, so `--n > 1` is rejected
## Official References
- [GLM-Image Guide](https://docs.z.ai/guides/image/glm-image)
- [Generate Image API](https://docs.z.ai/api-reference/image/generate-image)
@@ -0,0 +1,108 @@
# Usage Examples
Extended CLI examples. SKILL.md shows the minimum set; read this file when the user asks about provider-specific invocation, batch generation, or less-common flags.
## Core Patterns
```bash
# Basic text-to-image
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image cat.png
# With aspect ratio
${BUN_X} {baseDir}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9
# High quality
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --quality 2k
# Prompt from files
${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png
# With reference images (any provider family that supports refs)
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png
```
## Per-Provider
```bash
# OpenAI
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openai
# Azure OpenAI (model = deployment name)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider azure --model gpt-image-1.5
# Google with explicit model
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider google --model gemini-3-pro-image-preview --ref source.png
# OpenRouter (recommended default)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openrouter
# OpenRouter with reference
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider openrouter --model google/gemini-3.1-flash-image-preview --ref source.png
# DashScope (default model)
${BUN_X} {baseDir}/scripts/main.ts --prompt "一只可爱的猫" --image out.png --provider dashscope
# DashScope Qwen-Image 2.0 Pro (custom size, Chinese text)
${BUN_X} {baseDir}/scripts/main.ts --prompt "为咖啡品牌设计一张 21:9 横幅海报,包含清晰中文标题" --image out.png --provider dashscope --model qwen-image-2.0-pro --size 2048x872
# DashScope legacy fixed-size
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张电影感海报" --image out.png --provider dashscope --model qwen-image-max --size 1664x928
# Z.AI GLM-image
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张带清晰中文标题的科技海报" --image out.png --provider zai
# Z.AI with custom size
${BUN_X} {baseDir}/scripts/main.ts --prompt "A science illustration with labels" --image out.png --provider zai --model glm-image --size 1472x1088
# MiniMax
${BUN_X} {baseDir}/scripts/main.ts --prompt "A fashion editorial portrait" --image out.jpg --provider minimax
# MiniMax with subject reference (character/portrait consistency)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A girl by the library window" --image out.jpg --provider minimax --model image-01 --ref portrait.png --ar 16:9
# Replicate (default: google/nano-banana-2)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Replicate Seedream 4.5
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cinematic portrait" --image out.png --provider replicate --model bytedance/seedream-4.5 --ar 3:2
# Replicate Wan 2.7 Image Pro
${BUN_X} {baseDir}/scripts/main.ts --prompt "A concept frame" --image out.png --provider replicate --model wan-video/wan-2.7-image-pro --size 2048x1152
```
## Batch Mode
```bash
# Batch from saved prompt files
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json
# Batch with explicit worker count
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4 --json
```
### Batch File Format
```json
{
"jobs": 4,
"tasks": [
{
"id": "hero",
"promptFiles": ["prompts/hero.md"],
"image": "out/hero.png",
"provider": "replicate",
"model": "google/nano-banana-2",
"ar": "16:9",
"quality": "2k"
},
{
"id": "diagram",
"promptFiles": ["prompts/diagram.md"],
"image": "out/diagram.png",
"ref": ["references/original.png"]
}
]
}
```
Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch file's directory. `jobs` is optional (overridden by CLI `--jobs`). A top-level array without the `jobs` wrapper is also accepted.
+23 -7
View File
@@ -62,6 +62,7 @@ const DEFAULT_PROVIDER_RATE_LIMITS: Record<Provider, ProviderRateLimit> = {
jimeng: { concurrency: 3, startIntervalMs: 1100 }, jimeng: { concurrency: 3, startIntervalMs: 1100 },
seedream: { concurrency: 3, startIntervalMs: 1100 }, seedream: { concurrency: 3, startIntervalMs: 1100 },
azure: { concurrency: 3, startIntervalMs: 1100 }, azure: { concurrency: 3, startIntervalMs: 1100 },
zai: { concurrency: 3, startIntervalMs: 1100 },
}; };
function printUsage(): void { function printUsage(): void {
@@ -76,7 +77,7 @@ Options:
--image <path> Output image path (required in single-image mode) --image <path> Output image path (required in single-image mode)
--batchfile <path> JSON batch file for multi-image generation --batchfile <path> JSON batch file for multi-image generation
--jobs <count> Worker count for batch mode (default: auto, max from config, built-in default 10) --jobs <count> Worker count for batch mode (default: auto, max from config, built-in default 10)
--provider google|openai|openrouter|dashscope|minimax|replicate|jimeng|seedream|azure Force provider (auto-detect by default) --provider google|openai|openrouter|dashscope|minimax|replicate|jimeng|seedream|azure|zai Force provider (auto-detect by default)
-m, --model <id> Model ID -m, --model <id> Model ID
--ar <ratio> Aspect ratio (e.g., 16:9, 1:1, 4:3) --ar <ratio> Aspect ratio (e.g., 16:9, 1:1, 4:3)
--size <WxH> Size (e.g., 1024x1024) --size <WxH> Size (e.g., 1024x1024)
@@ -118,6 +119,8 @@ Environment variables:
JIMENG_ACCESS_KEY_ID Jimeng Access Key ID JIMENG_ACCESS_KEY_ID Jimeng Access Key ID
JIMENG_SECRET_ACCESS_KEY Jimeng Secret Access Key JIMENG_SECRET_ACCESS_KEY Jimeng Secret Access Key
ARK_API_KEY Seedream/Ark API key ARK_API_KEY Seedream/Ark API key
ZAI_API_KEY Z.AI API key (alias: BIGMODEL_API_KEY)
BIGMODEL_API_KEY Z.AI API key alias (legacy BigModel credentials)
OPENAI_IMAGE_MODEL Default OpenAI model (gpt-image-1.5) OPENAI_IMAGE_MODEL Default OpenAI model (gpt-image-1.5)
OPENROUTER_IMAGE_MODEL Default OpenRouter model (google/gemini-3.1-flash-image-preview) OPENROUTER_IMAGE_MODEL Default OpenRouter model (google/gemini-3.1-flash-image-preview)
GOOGLE_IMAGE_MODEL Default Google model (gemini-3-pro-image-preview) GOOGLE_IMAGE_MODEL Default Google model (gemini-3-pro-image-preview)
@@ -126,6 +129,8 @@ Environment variables:
REPLICATE_IMAGE_MODEL Default Replicate model (google/nano-banana-pro) REPLICATE_IMAGE_MODEL Default Replicate model (google/nano-banana-pro)
JIMENG_IMAGE_MODEL Default Jimeng model (jimeng_t2i_v40) JIMENG_IMAGE_MODEL Default Jimeng model (jimeng_t2i_v40)
SEEDREAM_IMAGE_MODEL Default Seedream model (doubao-seedream-5-0-260128) SEEDREAM_IMAGE_MODEL Default Seedream model (doubao-seedream-5-0-260128)
ZAI_IMAGE_MODEL Default Z.AI model (glm-image)
BIGMODEL_IMAGE_MODEL Z.AI model alias (legacy BigModel variable)
OPENAI_BASE_URL Custom OpenAI endpoint OPENAI_BASE_URL Custom OpenAI endpoint
OPENAI_IMAGE_USE_CHAT Use /chat/completions instead of /images/generations (true|false) OPENAI_IMAGE_USE_CHAT Use /chat/completions instead of /images/generations (true|false)
OPENROUTER_BASE_URL Custom OpenRouter endpoint OPENROUTER_BASE_URL Custom OpenRouter endpoint
@@ -142,6 +147,8 @@ Environment variables:
AZURE_API_VERSION Azure API version (default: 2025-04-01-preview) AZURE_API_VERSION Azure API version (default: 2025-04-01-preview)
AZURE_OPENAI_IMAGE_MODEL Backward-compatible Azure deployment/model alias (defaults to gpt-image-1.5) AZURE_OPENAI_IMAGE_MODEL Backward-compatible Azure deployment/model alias (defaults to gpt-image-1.5)
SEEDREAM_BASE_URL Custom Seedream endpoint SEEDREAM_BASE_URL Custom Seedream endpoint
ZAI_BASE_URL Custom Z.AI endpoint (defaults to https://api.z.ai/api/paas/v4)
BIGMODEL_BASE_URL Z.AI endpoint alias (legacy BigModel variable)
BAOYU_IMAGE_GEN_MAX_WORKERS Override batch worker cap BAOYU_IMAGE_GEN_MAX_WORKERS Override batch worker cap
BAOYU_IMAGE_GEN_<PROVIDER>_CONCURRENCY Override provider concurrency BAOYU_IMAGE_GEN_<PROVIDER>_CONCURRENCY Override provider concurrency
BAOYU_IMAGE_GEN_<PROVIDER>_START_INTERVAL_MS Override provider start gap in ms BAOYU_IMAGE_GEN_<PROVIDER>_START_INTERVAL_MS Override provider start gap in ms
@@ -243,7 +250,8 @@ export function parseArgs(argv: string[]): CliArgs {
v !== "replicate" && v !== "replicate" &&
v !== "jimeng" && v !== "jimeng" &&
v !== "seedream" && v !== "seedream" &&
v !== "azure" v !== "azure" &&
v !== "zai"
) { ) {
throw new Error(`Invalid provider: ${v}`); throw new Error(`Invalid provider: ${v}`);
} }
@@ -400,6 +408,7 @@ export function parseSimpleYaml(yaml: string): Partial<ExtendConfig> {
jimeng: null, jimeng: null,
seedream: null, seedream: null,
azure: null, azure: null,
zai: null,
}; };
currentKey = "default_model"; currentKey = "default_model";
currentProvider = null; currentProvider = null;
@@ -427,7 +436,8 @@ export function parseSimpleYaml(yaml: string): Partial<ExtendConfig> {
key === "replicate" || key === "replicate" ||
key === "jimeng" || key === "jimeng" ||
key === "seedream" || key === "seedream" ||
key === "azure" key === "azure" ||
key === "zai"
) )
) { ) {
config.batch ??= {}; config.batch ??= {};
@@ -445,7 +455,8 @@ export function parseSimpleYaml(yaml: string): Partial<ExtendConfig> {
key === "replicate" || key === "replicate" ||
key === "jimeng" || key === "jimeng" ||
key === "seedream" || key === "seedream" ||
key === "azure" key === "azure" ||
key === "zai"
) )
) { ) {
const cleaned = value.replace(/['"]/g, ""); const cleaned = value.replace(/['"]/g, "");
@@ -540,9 +551,10 @@ export function getConfiguredProviderRateLimits(
jimeng: { ...DEFAULT_PROVIDER_RATE_LIMITS.jimeng }, jimeng: { ...DEFAULT_PROVIDER_RATE_LIMITS.jimeng },
seedream: { ...DEFAULT_PROVIDER_RATE_LIMITS.seedream }, seedream: { ...DEFAULT_PROVIDER_RATE_LIMITS.seedream },
azure: { ...DEFAULT_PROVIDER_RATE_LIMITS.azure }, azure: { ...DEFAULT_PROVIDER_RATE_LIMITS.azure },
zai: { ...DEFAULT_PROVIDER_RATE_LIMITS.zai },
}; };
for (const provider of ["replicate", "google", "openai", "openrouter", "dashscope", "minimax", "jimeng", "seedream", "azure"] as Provider[]) { for (const provider of ["replicate", "google", "openai", "openrouter", "dashscope", "minimax", "jimeng", "seedream", "azure", "zai"] as Provider[]) {
const envPrefix = `BAOYU_IMAGE_GEN_${provider.toUpperCase()}`; const envPrefix = `BAOYU_IMAGE_GEN_${provider.toUpperCase()}`;
const extendLimit = extendConfig.batch?.provider_limits?.[provider]; const extendLimit = extendConfig.batch?.provider_limits?.[provider];
configured[provider] = { configured[provider] = {
@@ -625,6 +637,7 @@ export function detectProvider(args: CliArgs): Provider {
const hasReplicate = !!process.env.REPLICATE_API_TOKEN; const hasReplicate = !!process.env.REPLICATE_API_TOKEN;
const hasJimeng = !!(process.env.JIMENG_ACCESS_KEY_ID && process.env.JIMENG_SECRET_ACCESS_KEY); const hasJimeng = !!(process.env.JIMENG_ACCESS_KEY_ID && process.env.JIMENG_SECRET_ACCESS_KEY);
const hasSeedream = !!process.env.ARK_API_KEY; const hasSeedream = !!process.env.ARK_API_KEY;
const hasZai = !!(process.env.ZAI_API_KEY || process.env.BIGMODEL_API_KEY);
const modelProvider = inferProviderFromModel(args.model); const modelProvider = inferProviderFromModel(args.model);
if (modelProvider === "seedream") { if (modelProvider === "seedream") {
@@ -664,13 +677,14 @@ export function detectProvider(args: CliArgs): Provider {
hasReplicate && "replicate", hasReplicate && "replicate",
hasJimeng && "jimeng", hasJimeng && "jimeng",
hasSeedream && "seedream", hasSeedream && "seedream",
hasZai && "zai",
].filter(Boolean) as Provider[]; ].filter(Boolean) as Provider[];
if (available.length === 1) return available[0]!; if (available.length === 1) return available[0]!;
if (available.length > 1) return available[0]!; if (available.length > 1) return available[0]!;
throw new Error( throw new Error(
"No API key found. Set GOOGLE_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY+AZURE_OPENAI_BASE_URL, OPENROUTER_API_KEY, DASHSCOPE_API_KEY, MINIMAX_API_KEY, REPLICATE_API_TOKEN, JIMENG keys, or ARK_API_KEY.\n" + "No API key found. Set GOOGLE_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY+AZURE_OPENAI_BASE_URL, OPENROUTER_API_KEY, DASHSCOPE_API_KEY, MINIMAX_API_KEY, REPLICATE_API_TOKEN, JIMENG keys, ARK_API_KEY, or ZAI_API_KEY/BIGMODEL_API_KEY.\n" +
"Create ~/.baoyu-skills/.env or <cwd>/.baoyu-skills/.env with your keys." "Create ~/.baoyu-skills/.env or <cwd>/.baoyu-skills/.env with your keys."
); );
} }
@@ -715,6 +729,7 @@ async function loadProviderModule(provider: Provider): Promise<ProviderModule> {
if (provider === "jimeng") return (await import("./providers/jimeng")) as ProviderModule; if (provider === "jimeng") return (await import("./providers/jimeng")) as ProviderModule;
if (provider === "seedream") return (await import("./providers/seedream")) as ProviderModule; if (provider === "seedream") return (await import("./providers/seedream")) as ProviderModule;
if (provider === "azure") return (await import("./providers/azure")) as ProviderModule; if (provider === "azure") return (await import("./providers/azure")) as ProviderModule;
if (provider === "zai") return (await import("./providers/zai")) as ProviderModule;
return (await import("./providers/openai")) as ProviderModule; return (await import("./providers/openai")) as ProviderModule;
} }
@@ -745,6 +760,7 @@ function getModelForProvider(
if (provider === "jimeng" && extendConfig.default_model.jimeng) return extendConfig.default_model.jimeng; if (provider === "jimeng" && extendConfig.default_model.jimeng) return extendConfig.default_model.jimeng;
if (provider === "seedream" && extendConfig.default_model.seedream) return extendConfig.default_model.seedream; if (provider === "seedream" && extendConfig.default_model.seedream) return extendConfig.default_model.seedream;
if (provider === "azure" && extendConfig.default_model.azure) return extendConfig.default_model.azure; if (provider === "azure" && extendConfig.default_model.azure) return extendConfig.default_model.azure;
if (provider === "zai" && extendConfig.default_model.zai) return extendConfig.default_model.zai;
} }
return providerModule.getDefaultModel(); return providerModule.getDefaultModel();
} }
@@ -964,7 +980,7 @@ async function runBatchTasks(
const acquireProvider = createProviderGate(providerRateLimits); const acquireProvider = createProviderGate(providerRateLimits);
const workerCount = getWorkerCount(tasks.length, jobs, maxWorkers); const workerCount = getWorkerCount(tasks.length, jobs, maxWorkers);
console.error(`Batch mode: ${tasks.length} tasks, ${workerCount} workers, parallel mode enabled.`); console.error(`Batch mode: ${tasks.length} tasks, ${workerCount} workers, parallel mode enabled.`);
for (const provider of ["replicate", "google", "openai", "openrouter", "dashscope", "jimeng", "seedream", "azure"] as Provider[]) { for (const provider of ["replicate", "google", "openai", "openrouter", "dashscope", "jimeng", "seedream", "azure", "zai"] as Provider[]) {
const limit = providerRateLimits[provider]; const limit = providerRateLimits[provider];
console.error(`- ${provider}: concurrency=${limit.concurrency}, startIntervalMs=${limit.startIntervalMs}`); console.error(`- ${provider}: concurrency=${limit.concurrency}, startIntervalMs=${limit.startIntervalMs}`);
} }
@@ -0,0 +1,180 @@
import assert from "node:assert/strict";
import test, { type TestContext } from "node:test";
import type { CliArgs } from "../types.ts";
import {
buildRequestBody,
buildZaiUrl,
extractImageFromResponse,
getDefaultModel,
getModelFamily,
parseAspectRatio,
parseSize,
resolveSizeForModel,
validateArgs,
} from "./zai.ts";
function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
return {
prompt: null,
promptFiles: [],
imagePath: null,
provider: null,
model: null,
aspectRatio: null,
size: null,
quality: null,
imageSize: null,
referenceImages: [],
n: 1,
batchFile: null,
jobs: null,
json: false,
help: false,
...overrides,
};
}
function useEnv(
t: TestContext,
values: Record<string, string | null>,
): void {
const previous = new Map<string, string | undefined>();
for (const [key, value] of Object.entries(values)) {
previous.set(key, process.env[key]);
if (value == null) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
t.after(() => {
for (const [key, value] of previous.entries()) {
if (value == null) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
});
}
test("Z.AI default model prefers env override and otherwise uses glm-image", (t) => {
useEnv(t, {
ZAI_IMAGE_MODEL: null,
BIGMODEL_IMAGE_MODEL: null,
});
assert.equal(getDefaultModel(), "glm-image");
process.env.BIGMODEL_IMAGE_MODEL = "cogview-4-250304";
assert.equal(getDefaultModel(), "cogview-4-250304");
});
test("Z.AI URL builder normalizes host, v4 base, and full endpoint inputs", (t) => {
useEnv(t, { ZAI_BASE_URL: "https://api.z.ai" });
assert.equal(buildZaiUrl(), "https://api.z.ai/api/paas/v4/images/generations");
process.env.ZAI_BASE_URL = "https://proxy.example.com/api/paas/v4/";
assert.equal(buildZaiUrl(), "https://proxy.example.com/api/paas/v4/images/generations");
process.env.ZAI_BASE_URL = "https://proxy.example.com/custom/images/generations";
assert.equal(buildZaiUrl(), "https://proxy.example.com/custom/images/generations");
});
test("Z.AI model family and parsing helpers recognize documented formats", () => {
assert.equal(getModelFamily("glm-image"), "glm");
assert.equal(getModelFamily("cogview-4-250304"), "legacy");
assert.deepEqual(parseAspectRatio("16:9"), { width: 16, height: 9 });
assert.equal(parseAspectRatio("wide"), null);
assert.deepEqual(parseSize("1280x1280"), { width: 1280, height: 1280 });
assert.deepEqual(parseSize("1472*1088"), { width: 1472, height: 1088 });
assert.equal(parseSize("big"), null);
});
test("Z.AI size resolution follows documented recommended ratios and validates custom sizes", () => {
assert.equal(
resolveSizeForModel("glm-image", makeArgs({ aspectRatio: "16:9", quality: "2k" })),
"1728x960",
);
assert.equal(
resolveSizeForModel("cogview-4-250304", makeArgs({ aspectRatio: "4:3", quality: "normal" })),
"1152x864",
);
assert.equal(
resolveSizeForModel("glm-image", makeArgs({ size: "1568x1056", quality: "2k" })),
"1568x1056",
);
const uncommon = resolveSizeForModel(
"glm-image",
makeArgs({ aspectRatio: "5:2", quality: "normal" }),
);
const parsed = parseSize(uncommon);
assert.ok(parsed);
assert.ok(parsed.width % 32 === 0);
assert.ok(parsed.height % 32 === 0);
assert.ok(parsed.width * parsed.height <= 2 ** 22);
assert.throws(
() => resolveSizeForModel("glm-image", makeArgs({ size: "1000x1000", quality: "2k" })),
/between 1024 and 2048/,
);
assert.throws(
() => resolveSizeForModel("glm-image", makeArgs({ size: "1280x1260", quality: "2k" })),
/divisible by 32/,
);
assert.throws(
() => resolveSizeForModel("cogview-4-250304", makeArgs({ size: "2048x2048", quality: "2k" })),
/must not exceed 2\^21 total pixels/,
);
});
test("Z.AI validation rejects unsupported refs and multi-image requests", () => {
assert.throws(
() => validateArgs("glm-image", makeArgs({ referenceImages: ["ref.png"] })),
/text-to-image only/,
);
assert.throws(
() => validateArgs("glm-image", makeArgs({ n: 2 })),
/single image per request/,
);
});
test("Z.AI request body maps skill quality and resolved size into provider fields", () => {
const body = buildRequestBody(
"A cinematic science poster",
"glm-image",
makeArgs({ aspectRatio: "4:3", quality: "normal" }),
);
assert.deepEqual(body, {
model: "glm-image",
prompt: "A cinematic science poster",
quality: "standard",
size: "1472x1088",
});
});
test("Z.AI response extraction downloads the returned image URL", async (t) => {
const originalFetch = globalThis.fetch;
t.after(() => {
globalThis.fetch = originalFetch;
});
globalThis.fetch = async () =>
new Response(Uint8Array.from([1, 2, 3]), {
status: 200,
headers: { "Content-Type": "image/png" },
});
const image = await extractImageFromResponse({
data: [{ url: "https://cdn.example.com/glm-image.png" }],
});
assert.deepEqual([...image], [1, 2, 3]);
await assert.rejects(
() => extractImageFromResponse({ data: [{}] }),
/No image URL/,
);
});
@@ -0,0 +1,306 @@
import type { CliArgs, Quality } from "../types";
type ZaiModelFamily = "glm" | "legacy";
type ZaiRequestBody = {
model: string;
prompt: string;
quality: "hd" | "standard";
size: string;
};
type ZaiResponse = {
data?: Array<{ url?: string }>;
};
const DEFAULT_MODEL = "glm-image";
const GLM_MAX_PIXELS = 2 ** 22;
const LEGACY_MAX_PIXELS = 2 ** 21;
const GLM_SIZE_STEP = 32;
const LEGACY_SIZE_STEP = 16;
const GLM_RECOMMENDED_SIZES: Record<string, string> = {
"1:1": "1280x1280",
"3:2": "1568x1056",
"2:3": "1056x1568",
"4:3": "1472x1088",
"3:4": "1088x1472",
"16:9": "1728x960",
"9:16": "960x1728",
};
const LEGACY_RECOMMENDED_SIZES: Record<string, string> = {
"1:1": "1024x1024",
"9:16": "768x1344",
"3:4": "864x1152",
"16:9": "1344x768",
"4:3": "1152x864",
"2:1": "1440x720",
"1:2": "720x1440",
};
export function getDefaultModel(): string {
return process.env.ZAI_IMAGE_MODEL || process.env.BIGMODEL_IMAGE_MODEL || DEFAULT_MODEL;
}
function getApiKey(): string | null {
return process.env.ZAI_API_KEY || process.env.BIGMODEL_API_KEY || null;
}
export function buildZaiUrl(): string {
const base = (process.env.ZAI_BASE_URL || process.env.BIGMODEL_BASE_URL || "https://api.z.ai/api/paas/v4")
.replace(/\/+$/g, "");
if (base.endsWith("/images/generations")) return base;
if (base.endsWith("/api/paas/v4")) return `${base}/images/generations`;
if (base.endsWith("/v4")) return `${base}/images/generations`;
return `${base}/api/paas/v4/images/generations`;
}
export function getModelFamily(model: string): ZaiModelFamily {
return model.trim().toLowerCase() === "glm-image" ? "glm" : "legacy";
}
export function parseAspectRatio(ar: string): { width: number; height: number } | null {
const match = ar.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
if (!match) return null;
const width = Number(match[1]);
const height = Number(match[2]);
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
return null;
}
return { width, height };
}
export function parseSize(size: string): { width: number; height: number } | null {
const match = size.trim().match(/^(\d+)\s*[xX*]\s*(\d+)$/);
if (!match) return null;
const width = parseInt(match[1]!, 10);
const height = parseInt(match[2]!, 10);
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
return null;
}
return { width, height };
}
function formatSize(width: number, height: number): string {
return `${width}x${height}`;
}
function roundToStep(value: number, step: number): number {
return Math.max(step, Math.round(value / step) * step);
}
function getRatioValue(ar: string): number | null {
const parsed = parseAspectRatio(ar);
if (!parsed) return null;
return parsed.width / parsed.height;
}
function findClosestRatioKey(ar: string, candidates: string[]): string | null {
const targetRatio = getRatioValue(ar);
if (targetRatio == null) return null;
let bestKey: string | null = null;
let bestDiff = Infinity;
for (const candidate of candidates) {
const candidateRatio = getRatioValue(candidate);
if (candidateRatio == null) continue;
const diff = Math.abs(candidateRatio - targetRatio);
if (diff < bestDiff) {
bestDiff = diff;
bestKey = candidate;
}
}
return bestDiff <= 0.05 ? bestKey : null;
}
function getTargetPixels(quality: Quality): number {
return quality === "normal" ? 1024 * 1024 : 1536 * 1536;
}
function fitToPixelBudget(
width: number,
height: number,
targetPixels: number,
maxPixels: number,
step: number,
): { width: number; height: number } {
let nextWidth = width;
let nextHeight = height;
const pixels = nextWidth * nextHeight;
if (pixels > maxPixels) {
const scale = Math.sqrt(maxPixels / pixels);
nextWidth *= scale;
nextHeight *= scale;
} else {
const scale = Math.sqrt(targetPixels / pixels);
nextWidth *= scale;
nextHeight *= scale;
}
let roundedWidth = roundToStep(nextWidth, step);
let roundedHeight = roundToStep(nextHeight, step);
let roundedPixels = roundedWidth * roundedHeight;
while (roundedPixels > maxPixels && (roundedWidth > step || roundedHeight > step)) {
if (roundedWidth >= roundedHeight && roundedWidth > step) {
roundedWidth -= step;
} else if (roundedHeight > step) {
roundedHeight -= step;
} else {
break;
}
roundedPixels = roundedWidth * roundedHeight;
}
return { width: roundedWidth, height: roundedHeight };
}
function validateCustomSize(
size: string,
family: ZaiModelFamily,
): string {
const parsed = parseSize(size);
if (!parsed) {
throw new Error("Z.AI --size must be in WxH format, for example 1280x1280.");
}
const widthStep = family === "glm" ? GLM_SIZE_STEP : LEGACY_SIZE_STEP;
const minEdge = family === "glm" ? 1024 : 512;
const maxPixels = family === "glm" ? GLM_MAX_PIXELS : LEGACY_MAX_PIXELS;
if (parsed.width < minEdge || parsed.width > 2048 || parsed.height < minEdge || parsed.height > 2048) {
throw new Error(
family === "glm"
? "GLM-image custom size requires width and height between 1024 and 2048."
: "Z.AI legacy image models require width and height between 512 and 2048."
);
}
if (parsed.width % widthStep !== 0 || parsed.height % widthStep !== 0) {
throw new Error(
family === "glm"
? "GLM-image custom size requires width and height divisible by 32."
: "Z.AI legacy image models require width and height divisible by 16."
);
}
if (parsed.width * parsed.height > maxPixels) {
throw new Error(
family === "glm"
? "GLM-image custom size must not exceed 2^22 total pixels."
: "Z.AI legacy image size must not exceed 2^21 total pixels."
);
}
return formatSize(parsed.width, parsed.height);
}
export function resolveSizeForModel(
model: string,
args: Pick<CliArgs, "size" | "aspectRatio" | "quality">,
): string {
const family = getModelFamily(model);
const quality = args.quality === "normal" ? "normal" : "2k";
if (args.size) {
return validateCustomSize(args.size, family);
}
const recommended = family === "glm" ? GLM_RECOMMENDED_SIZES : LEGACY_RECOMMENDED_SIZES;
const defaultSize = family === "glm" ? "1280x1280" : "1024x1024";
if (!args.aspectRatio) return defaultSize;
const recommendedRatio = findClosestRatioKey(args.aspectRatio, Object.keys(recommended));
if (recommendedRatio) {
return recommended[recommendedRatio]!;
}
const parsedRatio = parseAspectRatio(args.aspectRatio);
if (!parsedRatio) return defaultSize;
const targetPixels = getTargetPixels(quality);
const maxPixels = family === "glm" ? GLM_MAX_PIXELS : LEGACY_MAX_PIXELS;
const step = family === "glm" ? GLM_SIZE_STEP : LEGACY_SIZE_STEP;
const fit = fitToPixelBudget(
parsedRatio.width,
parsedRatio.height,
targetPixels,
maxPixels,
step,
);
return formatSize(fit.width, fit.height);
}
function getZaiQuality(quality: CliArgs["quality"]): "hd" | "standard" {
return quality === "normal" ? "standard" : "hd";
}
export function validateArgs(_model: string, args: CliArgs): void {
if (args.referenceImages.length > 0) {
throw new Error("Z.AI GLM-image currently supports text-to-image only in baoyu-image-gen. Remove --ref or choose another provider.");
}
if (args.n > 1) {
throw new Error("Z.AI image generation currently returns a single image per request in baoyu-image-gen.");
}
}
export function buildRequestBody(
prompt: string,
model: string,
args: CliArgs,
): ZaiRequestBody {
validateArgs(model, args);
return {
model,
prompt,
quality: getZaiQuality(args.quality),
size: resolveSizeForModel(model, args),
};
}
export async function extractImageFromResponse(result: ZaiResponse): Promise<Uint8Array> {
const url = result.data?.[0]?.url;
if (!url) {
throw new Error("No image URL in Z.AI response");
}
const imageResponse = await fetch(url);
if (!imageResponse.ok) {
throw new Error(`Failed to download image from Z.AI: ${imageResponse.status}`);
}
return new Uint8Array(await imageResponse.arrayBuffer());
}
export async function generateImage(
prompt: string,
model: string,
args: CliArgs,
): Promise<Uint8Array> {
const apiKey = getApiKey();
if (!apiKey) {
throw new Error("ZAI_API_KEY is required. Get one from https://docs.z.ai/.");
}
const response = await fetch(buildZaiUrl(), {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify(buildRequestBody(prompt, model, args)),
});
if (!response.ok) {
const err = await response.text();
throw new Error(`Z.AI API error (${response.status}): ${err}`);
}
const result = (await response.json()) as ZaiResponse;
return extractImageFromResponse(result);
}
+3 -1
View File
@@ -7,7 +7,8 @@ export type Provider =
| "replicate" | "replicate"
| "jimeng" | "jimeng"
| "seedream" | "seedream"
| "azure"; | "azure"
| "zai";
export type Quality = "normal" | "2k"; export type Quality = "normal" | "2k";
export type CliArgs = { export type CliArgs = {
@@ -66,6 +67,7 @@ export type ExtendConfig = {
jimeng: string | null; jimeng: string | null;
seedream: string | null; seedream: string | null;
azure: string | null; azure: string | null;
zai: string | null;
}; };
batch?: { batch?: {
max_workers?: number | null; max_workers?: number | null;
+111 -371
View File
@@ -13,154 +13,65 @@ metadata:
# Image Generation (AI SDK) # Image Generation (AI SDK)
Official API-based image generation. Supports OpenAI, Azure OpenAI, Google, OpenRouter, DashScope (阿里通义万象), Z.AI GLM-Image, MiniMax, Jimeng (即梦), Seedream (豆包) and Replicate providers. Official API-based image generation. Supports OpenAI, Azure OpenAI, Google, OpenRouter, DashScope (阿里通义万象), Z.AI GLM-Image, MiniMax, Jimeng (即梦), Seedream (豆包) and Replicate.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
**Agent Execution**: `{baseDir}` = this SKILL.md's directory. Main script: `{baseDir}/scripts/main.ts`. Resolve `${BUN_X}`: prefer `bun`; else `npx -y bun`; else suggest `brew install oven-sh/bun/bun`.
1. `{baseDir}` = this SKILL.md file's directory
2. Script path = `{baseDir}/scripts/main.ts`
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
## Step 0: Load Preferences ⛔ BLOCKING ## Step 0: Load Preferences ⛔ BLOCKING
**CRITICAL**: This step MUST complete BEFORE any image generation. Do NOT skip or defer. This step MUST complete before any image generation — generation is blocked until EXTEND.md exists.
Check EXTEND.md existence (priority: project → user): Check these paths in order; first hit wins:
```bash | Path | Scope |
# macOS, Linux, WSL, Git Bash |------|-------|
test -f .baoyu-skills/baoyu-imagine/EXTEND.md && echo "project" | `.baoyu-skills/baoyu-imagine/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-imagine/EXTEND.md" && echo "xdg" | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-imagine/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md" && echo "user"
```
```powershell
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-imagine/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-imagine/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md") { "user" }
```
| Result | Action |
|--------|--------|
| Found | Load, parse, apply settings. If `default_model.[provider]` is null → ask model only (Flow 2) |
| Not found | ⛔ Run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Save EXTEND.md → Then continue |
**CRITICAL**: If not found, complete the full setup (provider + model + quality + save location) using AskUserQuestion BEFORE generating any images. Generation is BLOCKED until EXTEND.md is created.
| Path | Location |
|------|----------|
| `.baoyu-skills/baoyu-imagine/EXTEND.md` | Project directory |
| `$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md` | User home | | `$HOME/.baoyu-skills/baoyu-imagine/EXTEND.md` | User home |
Legacy compatibility: if `.baoyu-skills/baoyu-image-gen/EXTEND.md` exists and the new path does not, runtime renames it to `baoyu-imagine`. If both files exist, runtime leaves them unchanged and uses the new path. - **Found** → load, parse, apply. If `default_model.[provider]` is null → ask model only.
- **Not found** → run first-time setup (`references/config/first-time-setup.md`) using AskUserQuestion to collect provider + model + quality + save location. Save EXTEND.md, then continue. Do not generate images before this completes.
**EXTEND.md Supports**: Default provider | Default quality | Default aspect ratio | Default image size | OpenAI image API dialect | Default models | Batch worker cap | Provider-specific batch limits Legacy compatibility: if `.baoyu-skills/baoyu-image-gen/EXTEND.md` exists and the new path doesn't, the runtime renames it to `baoyu-imagine`. If both exist, the runtime leaves them alone and uses the new path.
Schema: `references/config/preferences-schema.md` **EXTEND.md keys**: default provider, default quality, default aspect ratio, default image size, OpenAI image API dialect, default models, batch worker cap, provider-specific batch limits. Schema: `references/config/preferences-schema.md`.
## Usage ## Usage
Minimum working examples — see `references/usage-examples.md` for the full set including per-provider invocations and batch mode.
```bash ```bash
# Basic # Basic
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image cat.png ${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image cat.png
# With aspect ratio # With aspect ratio and high quality
${BUN_X} {baseDir}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9 ${BUN_X} {baseDir}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9 --quality 2k
# High quality # Prompt from files
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --quality 2k
# From prompt files
${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png ${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png
# With reference images (Google, OpenAI, Azure OpenAI, OpenRouter, Replicate supported families, MiniMax, or Seedream 4.0/4.5/5.0) # With reference image
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png ${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png
# With reference images (explicit provider/model)
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider google --model gemini-3-pro-image-preview --ref source.png
# Azure OpenAI (model means deployment name)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider azure --model gpt-image-1.5
# OpenRouter (recommended default model)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openrouter
# OpenRouter with reference images
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider openrouter --model google/gemini-3.1-flash-image-preview --ref source.png
# Specific provider # Specific provider
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openai ${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider dashscope --model qwen-image-2.0-pro
# DashScope (阿里通义万象) # Batch mode
${BUN_X} {baseDir}/scripts/main.ts --prompt "一只可爱的猫" --image out.png --provider dashscope ${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4
# DashScope Qwen-Image 2.0 Pro (recommended for custom sizes and text rendering)
${BUN_X} {baseDir}/scripts/main.ts --prompt "为咖啡品牌设计一张 21:9 横幅海报,包含清晰中文标题" --image out.png --provider dashscope --model qwen-image-2.0-pro --size 2048x872
# DashScope legacy Qwen fixed-size model
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张电影感海报" --image out.png --provider dashscope --model qwen-image-max --size 1664x928
# Z.AI GLM-image
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张带清晰中文标题的科技海报" --image out.png --provider zai
# Z.AI GLM-image with explicit custom size
${BUN_X} {baseDir}/scripts/main.ts --prompt "A science illustration with labels" --image out.png --provider zai --model glm-image --size 1472x1088
# MiniMax
${BUN_X} {baseDir}/scripts/main.ts --prompt "A fashion editorial portrait by a bright studio window" --image out.jpg --provider minimax
# MiniMax with subject reference (best for character/portrait consistency)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A girl stands by the library window, cinematic lighting" --image out.jpg --provider minimax --model image-01 --ref portrait.png --ar 16:9
# MiniMax with custom size (documented for image-01)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cinematic poster" --image out.jpg --provider minimax --model image-01 --size 1536x1024
# Replicate (default: google/nano-banana-2)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Replicate Seedream 4.5
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cinematic portrait" --image out.png --provider replicate --model bytedance/seedream-4.5 --ar 3:2
# Replicate Wan 2.7 Image Pro
${BUN_X} {baseDir}/scripts/main.ts --prompt "A concept frame" --image out.png --provider replicate --model wan-video/wan-2.7-image-pro --size 2048x1152
# Batch mode with saved prompt files
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json
# Batch mode with explicit worker count
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4 --json
``` ```
### Batch File Format
```json
{
"jobs": 4,
"tasks": [
{
"id": "hero",
"promptFiles": ["prompts/hero.md"],
"image": "out/hero.png",
"provider": "replicate",
"model": "google/nano-banana-2",
"ar": "16:9",
"quality": "2k"
},
{
"id": "diagram",
"promptFiles": ["prompts/diagram.md"],
"image": "out/diagram.png",
"ref": ["references/original.png"]
}
]
}
```
Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch file's directory. `jobs` is optional (overridden by CLI `--jobs`). Top-level array format (without `jobs` wrapper) is also accepted.
## Options ## Options
| Option | Description | | Option | Description |
@@ -171,14 +82,14 @@ Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch fi
| `--batchfile <path>` | JSON batch file for multi-image generation | | `--batchfile <path>` | JSON batch file for multi-image generation |
| `--jobs <count>` | Worker count for batch mode (default: auto, max from config, built-in default 10) | | `--jobs <count>` | Worker count for batch mode (default: auto, max from config, built-in default 10) |
| `--provider google\|openai\|azure\|openrouter\|dashscope\|zai\|minimax\|jimeng\|seedream\|replicate` | Force provider (default: auto-detect) | | `--provider google\|openai\|azure\|openrouter\|dashscope\|zai\|minimax\|jimeng\|seedream\|replicate` | Force provider (default: auto-detect) |
| `--model <id>`, `-m` | Model ID (Google: `gemini-3-pro-image-preview`; OpenAI: `gpt-image-1.5`; Azure: deployment name such as `gpt-image-1.5` or `image-prod`; OpenRouter: `google/gemini-3.1-flash-image-preview`; DashScope: `qwen-image-2.0-pro`; Z.AI: `glm-image`; MiniMax: `image-01`) | | `--model <id>`, `-m` | Model ID — see provider references for defaults and allowed values |
| `--ar <ratio>` | Aspect ratio (e.g., `16:9`, `1:1`, `4:3`) | | `--ar <ratio>` | Aspect ratio (`16:9`, `1:1`, `4:3`, …) |
| `--size <WxH>` | Size (e.g., `1024x1024`) | | `--size <WxH>` | Explicit size (e.g., `1024x1024`) |
| `--quality normal\|2k` | Quality preset (default: `2k`) | | `--quality normal\|2k` | Quality preset (default: `2k`) |
| `--imageSize 1K\|2K\|4K` | Image size for Google/OpenRouter (default: from quality) | | `--imageSize 1K\|2K\|4K` | Image size for Google/OpenRouter (default: from quality) |
| `--imageApiDialect openai-native\|ratio-metadata` | OpenAI-compatible image API dialect. Use `ratio-metadata` when the endpoint is OpenAI-compatible but expects aspect-ratio `size` plus `metadata.resolution` instead of pixel `size` | | `--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, and Seedream 5.0/4.5/4.0. Not supported by Jimeng, Seedream 3.0, or removed 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. Not supported by Jimeng, Seedream 3.0, SeedEdit 3.0 |
| `--n <count>` | Number of images. Replicate currently supports only `--n 1` because this path saves exactly one output image | | `--n <count>` | Number of images. Replicate requires `--n 1` (single-output save semantics) |
| `--json` | JSON output | | `--json` | JSON output |
## Environment Variables ## Environment Variables
@@ -189,293 +100,109 @@ Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch fi
| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key | | `AZURE_OPENAI_API_KEY` | Azure OpenAI API key |
| `OPENROUTER_API_KEY` | OpenRouter API key | | `OPENROUTER_API_KEY` | OpenRouter API key |
| `GOOGLE_API_KEY` | Google API key | | `GOOGLE_API_KEY` | Google API key |
| `DASHSCOPE_API_KEY` | DashScope API key (阿里云) | | `DASHSCOPE_API_KEY` | DashScope API key |
| `ZAI_API_KEY` | Z.AI API key | | `ZAI_API_KEY` (alias `BIGMODEL_API_KEY`) | Z.AI API key |
| `BIGMODEL_API_KEY` | Backward-compatible alias for Z.AI API key |
| `MINIMAX_API_KEY` | MiniMax API key | | `MINIMAX_API_KEY` | MiniMax API key |
| `REPLICATE_API_TOKEN` | Replicate API token | | `REPLICATE_API_TOKEN` | Replicate API token |
| `JIMENG_ACCESS_KEY_ID` | Jimeng (即梦) Volcengine access key | | `JIMENG_ACCESS_KEY_ID`, `JIMENG_SECRET_ACCESS_KEY` | Jimeng (即梦) Volcengine credentials |
| `JIMENG_SECRET_ACCESS_KEY` | Jimeng (即梦) Volcengine secret key |
| `ARK_API_KEY` | Seedream (豆包) Volcengine ARK API key | | `ARK_API_KEY` | Seedream (豆包) Volcengine ARK API key |
| `OPENAI_IMAGE_MODEL` | OpenAI model override | | `<PROVIDER>_IMAGE_MODEL` | Per-provider model override (`OPENAI_IMAGE_MODEL`, `GOOGLE_IMAGE_MODEL`, `DASHSCOPE_IMAGE_MODEL`, `ZAI_IMAGE_MODEL`/`BIGMODEL_IMAGE_MODEL`, `MINIMAX_IMAGE_MODEL`, `OPENROUTER_IMAGE_MODEL`, `REPLICATE_IMAGE_MODEL`, `JIMENG_IMAGE_MODEL`, `SEEDREAM_IMAGE_MODEL`) |
| `AZURE_OPENAI_DEPLOYMENT` | Azure default deployment name | | `AZURE_OPENAI_DEPLOYMENT` (alias `AZURE_OPENAI_IMAGE_MODEL`) | Azure default deployment |
| `AZURE_OPENAI_IMAGE_MODEL` | Backward-compatible alias for Azure default deployment/model name | | `<PROVIDER>_BASE_URL` | Per-provider endpoint override |
| `OPENROUTER_IMAGE_MODEL` | OpenRouter model override (default: `google/gemini-3.1-flash-image-preview`) | | `AZURE_API_VERSION` | Azure image API version (default `2025-04-01-preview`) |
| `GOOGLE_IMAGE_MODEL` | Google model override | | `JIMENG_REGION` | Jimeng region (default `cn-north-1`) |
| `DASHSCOPE_IMAGE_MODEL` | DashScope model override (default: `qwen-image-2.0-pro`) | | `OPENAI_IMAGE_API_DIALECT` | `openai-native` \| `ratio-metadata` |
| `ZAI_IMAGE_MODEL` | Z.AI model override (default: `glm-image`) | | `OPENROUTER_HTTP_REFERER`, `OPENROUTER_TITLE` | Optional OpenRouter attribution |
| `BIGMODEL_IMAGE_MODEL` | Backward-compatible alias for Z.AI model override |
| `MINIMAX_IMAGE_MODEL` | MiniMax model override (default: `image-01`) |
| `REPLICATE_IMAGE_MODEL` | Replicate model override (default: google/nano-banana-2) |
| `JIMENG_IMAGE_MODEL` | Jimeng model override (default: jimeng_t2i_v40) |
| `SEEDREAM_IMAGE_MODEL` | Seedream model override (default: doubao-seedream-5-0-260128) |
| `OPENAI_BASE_URL` | Custom OpenAI endpoint |
| `OPENAI_IMAGE_API_DIALECT` | OpenAI-compatible image API dialect override (`openai-native` or `ratio-metadata`) |
| `AZURE_OPENAI_BASE_URL` | Azure resource endpoint or deployment endpoint |
| `AZURE_API_VERSION` | Azure image API version (default: `2025-04-01-preview`) |
| `OPENROUTER_BASE_URL` | Custom OpenRouter endpoint (default: `https://openrouter.ai/api/v1`) |
| `OPENROUTER_HTTP_REFERER` | Optional app/site URL for OpenRouter attribution |
| `OPENROUTER_TITLE` | Optional app name for OpenRouter attribution |
| `GOOGLE_BASE_URL` | Custom Google endpoint |
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint |
| `ZAI_BASE_URL` | Custom Z.AI endpoint (default: `https://api.z.ai/api/paas/v4`) |
| `BIGMODEL_BASE_URL` | Backward-compatible alias for Z.AI endpoint |
| `MINIMAX_BASE_URL` | Custom MiniMax endpoint (default: `https://api.minimax.io`) |
| `REPLICATE_BASE_URL` | Custom Replicate endpoint |
| `JIMENG_BASE_URL` | Custom Jimeng endpoint (default: `https://visual.volcengineapi.com`) |
| `JIMENG_REGION` | Jimeng region (default: `cn-north-1`) |
| `SEEDREAM_BASE_URL` | Custom Seedream endpoint (default: `https://ark.cn-beijing.volces.com/api/v3`) |
| `BAOYU_IMAGE_GEN_MAX_WORKERS` | Override batch worker cap | | `BAOYU_IMAGE_GEN_MAX_WORKERS` | Override batch worker cap |
| `BAOYU_IMAGE_GEN_<PROVIDER>_CONCURRENCY` | Override provider concurrency, e.g. `BAOYU_IMAGE_GEN_REPLICATE_CONCURRENCY` | | `BAOYU_IMAGE_GEN_<PROVIDER>_CONCURRENCY` | Per-provider concurrency (e.g., `BAOYU_IMAGE_GEN_REPLICATE_CONCURRENCY`) |
| `BAOYU_IMAGE_GEN_<PROVIDER>_START_INTERVAL_MS` | Override provider start gap, e.g. `BAOYU_IMAGE_GEN_REPLICATE_START_INTERVAL_MS` | | `BAOYU_IMAGE_GEN_<PROVIDER>_START_INTERVAL_MS` | Per-provider start-gap |
**Load Priority**: CLI args > EXTEND.md > env vars > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env` **Load priority**: CLI args > EXTEND.md > env vars > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env`
## Model Resolution ## Model Resolution
Model priority (highest → lowest), applies to all providers: Priority (highest → lowest) applies to every provider:
1. CLI flag: `--model <id>` 1. CLI flag `--model <id>`
2. EXTEND.md: `default_model.[provider]` 2. EXTEND.md `default_model.[provider]`
3. Env var: `<PROVIDER>_IMAGE_MODEL` (e.g., `GOOGLE_IMAGE_MODEL`) 3. Env var `<PROVIDER>_IMAGE_MODEL`
4. Built-in default 4. Built-in default
For Azure, `--model` / `default_model.azure` should be the Azure deployment name. `AZURE_OPENAI_DEPLOYMENT` is the preferred env var, and `AZURE_OPENAI_IMAGE_MODEL` remains as a backward-compatible alias. For Azure, `--model` / `default_model.azure` is the Azure deployment name. `AZURE_OPENAI_DEPLOYMENT` is the preferred env var; `AZURE_OPENAI_IMAGE_MODEL` is kept as a backward-compatible alias.
**EXTEND.md overrides env vars**. If both EXTEND.md `default_model.google: "gemini-3-pro-image-preview"` and env var `GOOGLE_IMAGE_MODEL=gemini-3.1-flash-image-preview` exist, EXTEND.md wins. EXTEND.md overrides env vars: if EXTEND.md sets `default_model.google: "gemini-3-pro-image-preview"` and the env var sets `GOOGLE_IMAGE_MODEL=gemini-3.1-flash-image-preview`, EXTEND.md wins.
### OpenAI-Compatible Gateway Dialects **Display model info before each generation**:
`provider=openai` means the auth and routing entrypoint is OpenAI-compatible. It does **not** guarantee that the upstream image API uses OpenAI native image-request semantics. - `Using [provider] / [model]`
- `Switch model: --model <id> | EXTEND.md default_model.[provider] | env <PROVIDER>_IMAGE_MODEL`
Use `default_image_api_dialect` in `EXTEND.md`, `OPENAI_IMAGE_API_DIALECT`, or `--imageApiDialect` when the endpoint expects a different wire format: ## OpenAI-Compatible Gateway Dialects
- `openai-native`: Sends pixel `size` such as `1536x1024` and native OpenAI quality fields when supported `provider=openai` means the auth and routing entrypoint is OpenAI-compatible. It does **not** guarantee the upstream image API uses OpenAI native semantics. When a gateway expects a different wire format, set `default_image_api_dialect` in EXTEND.md, `OPENAI_IMAGE_API_DIALECT`, or `--imageApiDialect`:
- `ratio-metadata`: Sends aspect-ratio `size` such as `16:9` and maps quality/size intent into `metadata.resolution` (`1K|2K|4K`) plus `metadata.orientation`
Recommended use: - `openai-native`: pixel `size` (`1536x1024`) and native OpenAI quality fields
- `ratio-metadata`: aspect-ratio `size` (`16:9`) plus `metadata.resolution` (`1K|2K|4K`) and `metadata.orientation`
- OpenAI native Images API or strict clones: keep `openai-native` Use `openai-native` for the OpenAI native API or strict clones; try `ratio-metadata` for compatibility gateways in front of Gemini or similar models. Current limitation: `ratio-metadata` applies only to text-to-image; reference-image edits still need `openai-native` or a provider with first-class edit support.
- OpenAI-compatible gateways in front of Gemini or similar models: try `ratio-metadata`
Current limitation: `ratio-metadata` only applies to text-to-image generation. Reference-image edit flows still require `openai-native` or another provider with first-class edit support. ## Provider-Specific Guides
**Agent MUST display model info** before each generation: Each provider has its own quirks (model families, size rules, ref support, limits). Read these when the user picks that provider or asks for non-default behavior:
- Show: `Using [provider] / [model]`
- Show switch hint: `Switch model: --model <id> | EXTEND.md default_model.[provider] | env <PROVIDER>_IMAGE_MODEL`
### DashScope Models | Provider | Reference |
|----------|-----------|
Use `--model qwen-image-2.0-pro` or set `default_model.dashscope` / `DASHSCOPE_IMAGE_MODEL` when the user wants official Qwen-Image behavior. | DashScope (Qwen-Image families, custom sizes) | `references/providers/dashscope.md` |
| Z.AI (GLM-Image, cogview-4) | `references/providers/zai.md` |
Official DashScope model families: | MiniMax (image-01, subject-reference) | `references/providers/minimax.md` |
| OpenRouter (multimodal models, `/chat/completions` flow) | `references/providers/openrouter.md` |
- `qwen-image-2.0-pro`, `qwen-image-2.0-pro-2026-03-03`, `qwen-image-2.0`, `qwen-image-2.0-2026-03-03` | Replicate (nano-banana, Seedream, Wan) | `references/providers/replicate.md` |
- Free-form `size` in `宽*高` format
- Total pixels must stay between `512*512` and `2048*2048`
- Default size is approximately `1024*1024`
- Best choice for custom ratios such as `21:9` and text-heavy Chinese/English layouts
- `qwen-image-max`, `qwen-image-max-2025-12-30`, `qwen-image-plus`, `qwen-image-plus-2026-01-09`, `qwen-image`
- Fixed sizes only: `1664*928`, `1472*1104`, `1328*1328`, `1104*1472`, `928*1664`
- Default size is `1664*928`
- `qwen-image` currently has the same capability as `qwen-image-plus`
- Legacy DashScope models such as `z-image-turbo`, `z-image-ultra`, `wanx-v1`
- Keep using them only when the user explicitly asks for legacy behavior or compatibility
When translating CLI args into DashScope behavior:
- `--size` wins over `--ar`
- For `qwen-image-2.0*`, prefer explicit `--size`; otherwise infer from `--ar` and use the official recommended resolutions below
- For `qwen-image-max/plus/image`, only use the five official fixed sizes; if the requested ratio is not covered, switch to `qwen-image-2.0-pro`
- `--quality` is a baoyu-imagine compatibility preset, not a native DashScope API field. Mapping `normal` / `2k` onto the `qwen-image-2.0*` table below is an implementation inference, not an official API guarantee
Recommended `qwen-image-2.0*` sizes for common aspect ratios:
| Ratio | `normal` | `2k` |
|-------|----------|------|
| `1:1` | `1024*1024` | `1536*1536` |
| `2:3` | `768*1152` | `1024*1536` |
| `3:2` | `1152*768` | `1536*1024` |
| `3:4` | `960*1280` | `1080*1440` |
| `4:3` | `1280*960` | `1440*1080` |
| `9:16` | `720*1280` | `1080*1920` |
| `16:9` | `1280*720` | `1920*1080` |
| `21:9` | `1344*576` | `2048*872` |
DashScope official APIs also expose `negative_prompt`, `prompt_extend`, and `watermark`, but `baoyu-imagine` does not expose them as dedicated CLI flags today.
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)
### Z.AI Models
Use `--model glm-image` or set `default_model.zai` / `ZAI_IMAGE_MODEL` when the user wants GLM-image output.
Official Z.AI image model options currently documented in the sync image API:
- `glm-image` (recommended default)
- Text-to-image only in `baoyu-imagine`
- Native `quality` options are `hd` and `standard`; this skill maps `2k -> hd` and `normal -> standard`
- Recommended sizes: `1280x1280`, `1568x1056`, `1056x1568`, `1472x1088`, `1088x1472`, `1728x960`, `960x1728`
- Custom `--size` requires width and height between `1024` and `2048`, divisible by `32`, with total pixels <= `2^22`
- `cogview-4-250304`
- Legacy Z.AI image model family exposed by the same endpoint
- Custom `--size` requires width and height between `512` and `2048`, divisible by `16`, with total pixels <= `2^21`
Notes:
- The official sync API returns a temporary image URL; `baoyu-imagine` downloads that URL and writes the image locally
- `--ref` is not supported for Z.AI in this skill yet
- The sync API currently returns a single image, so `--n > 1` is rejected
Official references:
- [GLM-Image Guide](https://docs.z.ai/guides/image/glm-image)
- [Generate Image API](https://docs.z.ai/api-reference/image/generate-image)
### MiniMax Models
Use `--model image-01` or set `default_model.minimax` / `MINIMAX_IMAGE_MODEL` when the user wants MiniMax image generation.
Official MiniMax image model options currently documented in the API reference:
- `image-01` (recommended default)
- Supports text-to-image and subject-reference image generation
- Supports official `aspect_ratio` values: `1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`
- Supports documented custom `width` / `height` output sizes when using `--size <WxH>`
- `width` and `height` must both be between `512` and `2048`, and both must be divisible by `8`
- `image-01-live`
- Lower-latency variant
- Use `--ar` for sizing; MiniMax documents custom `width` / `height` as only effective for `image-01`
MiniMax subject reference notes:
- `--ref` files are sent as MiniMax `subject_reference`
- MiniMax docs currently describe `subject_reference[].type` as `character`
- Official docs say `image_file` supports public URLs or Base64 Data URLs; `baoyu-imagine` sends local refs as Data URLs
- Official docs recommend front-facing portrait references in JPG/JPEG/PNG under 10MB
Official references:
- [MiniMax Image Generation Guide](https://platform.minimax.io/docs/guides/image-generation)
- [MiniMax Text-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-t2i)
- [MiniMax Image-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-i2i)
### OpenRouter Models
Use full OpenRouter model IDs, e.g.:
- `google/gemini-3.1-flash-image-preview` (recommended, supports image output and reference-image workflows)
- `google/gemini-2.5-flash-image-preview`
- `black-forest-labs/flux.2-pro`
- Other OpenRouter image-capable model IDs
Notes:
- OpenRouter image generation uses `/chat/completions`, not the OpenAI `/images` endpoints
- If `--ref` is used, choose a multimodal model that supports image input and image output
- `--imageSize` maps to OpenRouter `imageGenerationOptions.size`; `--size <WxH>` is converted to the nearest OpenRouter size and inferred aspect ratio when possible
### Replicate Models
Replicate support in `baoyu-imagine` is intentionally scoped to the model families that the tool can validate locally and save without dropping outputs:
- `google/nano-banana*` (default: `google/nano-banana-2`)
- Supports prompt-only and reference-image generation
- Uses Replicate `aspect_ratio`, `resolution`, and `output_format`
- `--size <WxH>` is accepted only as a shorthand for a documented aspect ratio plus `1K` / `2K`
- `bytedance/seedream-4.5`
- Supports prompt-only and reference-image generation
- Uses Replicate `size`, `aspect_ratio`, and `image_input`
- Local validation blocks unsupported `1K` requests before the API call
- `bytedance/seedream-5-lite`
- Supports prompt-only and reference-image generation
- Uses Replicate `size`, `aspect_ratio`, and `image_input`
- Local validation currently accepts `2K` / `3K` only
- `wan-video/wan-2.7-image`
- Supports prompt-only and reference-image generation
- Uses Replicate `size` and `images`
- Max output size is 2K
- `wan-video/wan-2.7-image-pro`
- Supports prompt-only and reference-image generation
- Uses Replicate `size` and `images`
- 4K is allowed only for text-to-image; local validation blocks `4K + --ref`
Guardrails:
- Replicate currently supports only single-output save semantics in this tool. Keep `--n 1`.
- If a Replicate model is outside the compatibility list above, `baoyu-imagine` only treats it as prompt-only and rejects advanced local options instead of guessing a nano-banana-style schema.
Examples:
```bash
# Use Replicate default model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Override model explicitly
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate --model google/nano-banana
```
## Provider Selection ## Provider Selection
1. `--ref` provided + no `--provider` → auto-select Google first, then OpenAI, then Azure, then OpenRouter, then Replicate, then Seedream, then MiniMax (MiniMax subject reference is more specialized toward character/portrait consistency) 1. `--ref` provided + no `--provider` → auto-select Google → OpenAI → Azure → OpenRouter Replicate Seedream MiniMax (MiniMax's subject reference is more specialized toward character/portrait consistency)
2. `--provider` specified → use it (if `--ref`, must be `google`, `openai`, `azure`, `openrouter`, `replicate`, `seedream`, or `minimax`) 2. `--provider` specified → use it (if `--ref`, must be google/openai/azure/openrouter/replicate/seedream/minimax)
3. Only one API key available → use that provider 3. Only one API key present → use that provider
4. Multiple available → default to Google, then OpenAI, Azure, OpenRouter, DashScope, Z.AI, MiniMax, Replicate, Jimeng, Seedream 4. Multiple keys → default priority: Google OpenAI Azure OpenRouter DashScope Z.AI MiniMax Replicate Jimeng Seedream
## Quality Presets ## Quality Presets
| Preset | Google imageSize | OpenAI Size | OpenRouter size | Replicate resolution | Use Case | | Preset | Google imageSize | OpenAI size | OpenRouter size | Replicate resolution | Use case |
|--------|------------------|-------------|-----------------|----------------------|----------| |--------|------------------|-------------|-----------------|----------------------|----------|
| `normal` | 1K | 1024px | 1K | 1K | Quick previews | | `normal` | 1K | 1024px | 1K | 1K | Quick previews |
| `2k` (default) | 2K | 2048px | 2K | 2K | Covers, illustrations, infographics | | `2k` (default) | 2K | 2048px | 2K | 2K | Covers, illustrations, infographics |
**Google/OpenRouter imageSize**: Can be overridden with `--imageSize 1K|2K|4K` Google/OpenRouter `imageSize` can be overridden with `--imageSize 1K|2K|4K`.
## Aspect Ratios ## Aspect Ratios
Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1` Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1`.
- Google multimodal: uses `imageConfig.aspectRatio` - Google multimodal: `imageConfig.aspectRatio`
- OpenAI: maps to closest supported size - OpenAI: closest supported size
- OpenRouter: sends `imageGenerationOptions.aspect_ratio`; if only `--size <WxH>` is given, aspect ratio is inferred automatically - OpenRouter: `imageGenerationOptions.aspect_ratio`; if only `--size <WxH>` is given, the ratio is inferred
- Replicate: behavior is model-family-specific. `google/nano-banana*` uses `aspect_ratio`; `bytedance/seedream-*` uses documented Replicate aspect ratios; Wan 2.7 maps `--ar` to a concrete `size` - Replicate: behavior is model-specific `google/nano-banana*` uses `aspect_ratio`, `bytedance/seedream-*` uses documented Replicate ratios, Wan 2.7 maps `--ar` to a concrete `size`
- MiniMax: sends official `aspect_ratio` values directly; if `--size <WxH>` is given without `--ar`, `width` / `height` are sent for `image-01` - MiniMax: official `aspect_ratio` values; if `--size <WxH>` is given without `--ar`, sends `width`/`height` for `image-01`
## Generation Mode ## Generation Mode
**Default**: Sequential generation. **Default**: sequential. **Batch parallel**: enabled automatically when `--batchfile` contains 2+ pending tasks.
**Batch Parallel Generation**: When `--batchfile` contains 2 or more pending tasks, the script automatically enables parallel generation. | Situation | Prefer | Why |
|-----------|--------|-----|
| One image, or 1-2 simple images | Sequential | Lower coordination overhead, easier debugging |
| Multiple images with saved prompt files | Batch (`--batchfile`) | Reuses finalized prompts, applies shared throttling/retries, predictable throughput |
| Each image still needs its own reasoning / prompt writing / style exploration | Subagents | Work is still exploratory, each needs independent analysis |
| Input is `outline.md` + `prompts/` (e.g. from `baoyu-article-illustrator`) | Batch — use `scripts/build-batch.ts` to assemble the payload | The outline + prompt files already contain everything needed |
| Mode | When to Use | Rule of thumb: once prompt files are saved and the task is "generate all of these", prefer batch over subagents. Use subagents only when generation is coupled with per-image thinking or divergent creative exploration.
|------|-------------|
| Sequential (default) | Normal usage, single images, small batches |
| Parallel batch | Batch mode with 2+ tasks |
Execution choice: **Parallel behavior**:
| Situation | Preferred approach | Why |
|-----------|--------------------|-----|
| One image, or 1-2 simple images | Sequential | Lower coordination overhead and easier debugging |
| Multiple images already have saved prompt files | Batch (`--batchfile`) | Reuses finalized prompts, applies shared throttling/retries, and gives predictable throughput |
| Each image still needs separate reasoning, prompt writing, or style exploration | Subagents | The work is still exploratory, so each image may need independent analysis before generation |
| Output comes from `baoyu-article-illustrator` with `outline.md` + `prompts/` | Batch (`build-batch.ts` -> `--batchfile`) | That workflow already produces prompt files, so direct batch execution is the intended path |
Rule of thumb:
- Prefer batch over subagents once prompt files are already saved and the task is "generate all of these"
- Use subagents only when generation is coupled with per-image thinking, rewriting, or divergent creative exploration
Parallel behavior:
- Default worker count is automatic, capped by config, built-in default 10 - Default worker count is automatic, capped by config, built-in default 10
- Provider-specific throttling is applied only in batch mode, and the built-in defaults are tuned for faster throughput while still avoiding obvious RPM bursts - Provider-specific throttling applies only in batch mode; defaults are tuned for throughput while avoiding RPM bursts
- You can override worker count with `--jobs <count>` - Override with `--jobs <count>`
- Each image retries automatically up to 3 attempts - Each image retries up to 3 attempts
- Final output includes success count, failure count, and per-image failure reasons - Final output includes success count, failure count, and per-image failure reasons
## Error Handling ## Error Handling
@@ -485,6 +212,19 @@ Parallel behavior:
- Invalid aspect ratio → warning, proceed with default - Invalid aspect ratio → warning, proceed with default
- Reference images with unsupported provider/model → error with fix hint - Reference images with unsupported provider/model → error with fix hint
## References
| File | Content |
|------|---------|
| `references/usage-examples.md` | Extended CLI examples across providers and batch mode |
| `references/providers/dashscope.md` | DashScope families, sizes, limits |
| `references/providers/zai.md` | Z.AI GLM-image / cogview-4 |
| `references/providers/minimax.md` | MiniMax image-01 + subject reference |
| `references/providers/openrouter.md` | OpenRouter multimodal flow |
| `references/providers/replicate.md` | Replicate supported families + guardrails |
| `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow |
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options. Custom configurations via EXTEND.md. See Step 0 for paths and schema.
@@ -0,0 +1,50 @@
# DashScope (阿里通义万象)
Read when the user picks `--provider dashscope`, sets `default_model.dashscope`, or asks for Qwen-Image behavior. The SKILL.md only names the default — this file covers model families, sizing rules, and limits.
## Model Families
**`qwen-image-2.0*`** — recommended modern family. Members: `qwen-image-2.0-pro`, `qwen-image-2.0-pro-2026-03-03`, `qwen-image-2.0`, `qwen-image-2.0-2026-03-03`.
- Free-form `size` in `宽*高` format
- Total pixels must be between `512*512` and `2048*2048`
- Default ≈ `1024*1024`
- Best choice for custom ratios (e.g. `21:9`) and text-heavy Chinese/English layouts
**Fixed-size family**`qwen-image-max`, `qwen-image-max-2025-12-30`, `qwen-image-plus`, `qwen-image-plus-2026-01-09`, `qwen-image`.
- Only five sizes allowed: `1664*928`, `1472*1104`, `1328*1328`, `1104*1472`, `928*1664`
- Default is `1664*928`
- `qwen-image` currently has the same capability as `qwen-image-plus`
**Legacy**`z-image-turbo`, `z-image-ultra`, `wanx-v1`. Only use when the user explicitly asks for legacy behavior.
## Size Resolution
- `--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
### Recommended `qwen-image-2.0*` sizes
| Ratio | `normal` | `2k` |
|-------|----------|------|
| `1:1` | `1024*1024` | `1536*1536` |
| `2:3` | `768*1152` | `1024*1536` |
| `3:2` | `1152*768` | `1536*1024` |
| `3:4` | `960*1280` | `1080*1440` |
| `4:3` | `1280*960` | `1440*1080` |
| `9:16` | `720*1280` | `1080*1920` |
| `16:9` | `1280*720` | `1920*1080` |
| `21:9` | `1344*576` | `2048*872` |
## Not Exposed
DashScope APIs also support `negative_prompt`, `prompt_extend`, and `watermark`, but `baoyu-imagine` does not expose them as CLI flags today.
## 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)
@@ -0,0 +1,29 @@
# MiniMax
Read when the user picks `--provider minimax` or sets `default_model.minimax`. Default model is `image-01`.
## Models
**`image-01`** (recommended default)
- Supports text-to-image and subject-reference image generation
- Supports official `aspect_ratio` values: `1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`
- Supports documented custom `width` / `height` via `--size <WxH>`
- Both width and height must be in `[512, 2048]` and divisible by `8`
**`image-01-live`** — lower-latency variant
- Use `--ar` for sizing; MiniMax documents custom `width`/`height` only for `image-01`
## Subject Reference
- `--ref` files are sent as MiniMax `subject_reference`
- `subject_reference[].type` is currently `character`
- Official docs say `image_file` supports public URLs or Base64 Data URLs; baoyu-imagine sends local refs as Data URLs
- Recommended refs: front-facing portraits, JPG/JPEG/PNG, under 10MB
## 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)
@@ -0,0 +1,19 @@
# OpenRouter
Read when the user picks `--provider openrouter`. Default model is `google/gemini-3.1-flash-image-preview`.
## Common Models
Use full OpenRouter model IDs:
- `google/gemini-3.1-flash-image-preview` (recommended — supports image output and reference-image workflows)
- `google/gemini-2.5-flash-image-preview`
- `black-forest-labs/flux.2-pro`
- Any other OpenRouter image-capable model ID
## Behavior Notes
- OpenRouter image generation uses `/chat/completions`, not the OpenAI `/images` endpoints
- `--ref` requires a multimodal model that supports both image input and image output
- `--imageSize` maps to `imageGenerationOptions.size`
- `--size <WxH>` is converted to the nearest supported OpenRouter size, and the aspect ratio is inferred when possible
@@ -0,0 +1,50 @@
# Replicate
Read when the user picks `--provider replicate`. Replicate support is intentionally scoped to model families baoyu-imagine can validate locally and save without dropping outputs.
## Supported Families
**`google/nano-banana*`** (default: `google/nano-banana-2`)
- Supports prompt-only and reference-image generation
- Uses Replicate `aspect_ratio`, `resolution`, and `output_format`
- `--size <WxH>` is accepted only as a shorthand for a documented `aspect_ratio` plus `1K` / `2K`
**`bytedance/seedream-4.5`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size`, `aspect_ratio`, and `image_input`
- Local validation blocks unsupported `1K` requests before the API call
**`bytedance/seedream-5-lite`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size`, `aspect_ratio`, and `image_input`
- Local validation currently accepts `2K` / `3K` only
**`wan-video/wan-2.7-image`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size` and `images`
- Max output is 2K
**`wan-video/wan-2.7-image-pro`**
- Supports prompt-only and reference-image generation
- Uses Replicate `size` and `images`
- 4K is allowed only for text-to-image; local validation blocks `4K + --ref`
## Guardrails
- Replicate currently supports only single-output save semantics in this tool — keep `--n 1`
- If a model is outside the compatibility list above, baoyu-imagine treats it as prompt-only and rejects advanced local options instead of guessing a nano-banana-style schema
## Examples
```bash
# Default model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Explicit model
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate --model google/nano-banana
```
@@ -0,0 +1,27 @@
# Z.AI GLM-Image
Read when the user picks `--provider zai` or sets `default_model.zai`. Default model is `glm-image`.
## Models
**`glm-image`** (recommended default)
- Text-to-image only in baoyu-imagine (no `--ref` support yet)
- Native `quality` options are `hd` and `standard`; this skill maps `2k → hd` and `normal → standard`
- Recommended sizes: `1280x1280`, `1568x1056`, `1056x1568`, `1472x1088`, `1088x1472`, `1728x960`, `960x1728`
- Custom `--size` requires width/height in `[1024, 2048]`, divisible by `32`, total pixels ≤ `2^22`
**`cogview-4-250304`** (legacy family, same endpoint)
- Custom `--size` requires width/height in `[512, 2048]`, divisible by `16`, total pixels ≤ `2^21`
## Behavior Notes
- The sync API returns a temporary URL; baoyu-imagine downloads it and writes locally
- `--ref` is not supported for Z.AI in this skill yet
- The sync API returns a single image, so `--n > 1` is rejected
## Official References
- [GLM-Image Guide](https://docs.z.ai/guides/image/glm-image)
- [Generate Image API](https://docs.z.ai/api-reference/image/generate-image)
@@ -0,0 +1,108 @@
# Usage Examples
Extended CLI examples. SKILL.md shows the minimum set; read this file when the user asks about provider-specific invocation, batch generation, or less-common flags.
## Core Patterns
```bash
# Basic text-to-image
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image cat.png
# With aspect ratio
${BUN_X} {baseDir}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9
# High quality
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --quality 2k
# Prompt from files
${BUN_X} {baseDir}/scripts/main.ts --promptfiles system.md content.md --image out.png
# With reference images (any provider family that supports refs)
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png
```
## Per-Provider
```bash
# OpenAI
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openai
# Azure OpenAI (model = deployment name)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider azure --model gpt-image-1.5
# Google with explicit model
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider google --model gemini-3-pro-image-preview --ref source.png
# OpenRouter (recommended default)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider openrouter
# OpenRouter with reference
${BUN_X} {baseDir}/scripts/main.ts --prompt "Make blue" --image out.png --provider openrouter --model google/gemini-3.1-flash-image-preview --ref source.png
# DashScope (default model)
${BUN_X} {baseDir}/scripts/main.ts --prompt "一只可爱的猫" --image out.png --provider dashscope
# DashScope Qwen-Image 2.0 Pro (custom size, Chinese text)
${BUN_X} {baseDir}/scripts/main.ts --prompt "为咖啡品牌设计一张 21:9 横幅海报,包含清晰中文标题" --image out.png --provider dashscope --model qwen-image-2.0-pro --size 2048x872
# DashScope legacy fixed-size
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张电影感海报" --image out.png --provider dashscope --model qwen-image-max --size 1664x928
# Z.AI GLM-image
${BUN_X} {baseDir}/scripts/main.ts --prompt "一张带清晰中文标题的科技海报" --image out.png --provider zai
# Z.AI with custom size
${BUN_X} {baseDir}/scripts/main.ts --prompt "A science illustration with labels" --image out.png --provider zai --model glm-image --size 1472x1088
# MiniMax
${BUN_X} {baseDir}/scripts/main.ts --prompt "A fashion editorial portrait" --image out.jpg --provider minimax
# MiniMax with subject reference (character/portrait consistency)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A girl by the library window" --image out.jpg --provider minimax --model image-01 --ref portrait.png --ar 16:9
# Replicate (default: google/nano-banana-2)
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cat" --image out.png --provider replicate
# Replicate Seedream 4.5
${BUN_X} {baseDir}/scripts/main.ts --prompt "A cinematic portrait" --image out.png --provider replicate --model bytedance/seedream-4.5 --ar 3:2
# Replicate Wan 2.7 Image Pro
${BUN_X} {baseDir}/scripts/main.ts --prompt "A concept frame" --image out.png --provider replicate --model wan-video/wan-2.7-image-pro --size 2048x1152
```
## Batch Mode
```bash
# Batch from saved prompt files
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json
# Batch with explicit worker count
${BUN_X} {baseDir}/scripts/main.ts --batchfile batch.json --jobs 4 --json
```
### Batch File Format
```json
{
"jobs": 4,
"tasks": [
{
"id": "hero",
"promptFiles": ["prompts/hero.md"],
"image": "out/hero.png",
"provider": "replicate",
"model": "google/nano-banana-2",
"ar": "16:9",
"quality": "2k"
},
{
"id": "diagram",
"promptFiles": ["prompts/diagram.md"],
"image": "out/diagram.png",
"ref": ["references/original.png"]
}
]
}
```
Paths in `promptFiles`, `image`, and `ref` are resolved relative to the batch file's directory. `jobs` is optional (overridden by CLI `--jobs`). A top-level array without the `jobs` wrapper is also accepted.
@@ -0,0 +1,190 @@
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import test from "node:test";
const execFileAsync = promisify(execFile);
const repoRoot = path.resolve(import.meta.dirname, "..", "..", "..");
const scriptPath = path.join(repoRoot, "skills", "baoyu-imagine", "scripts", "build-batch.ts");
async function makeFixture(): Promise<{
root: string;
outlinePath: string;
promptsDir: string;
outputPath: string;
}> {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "baoyu-imagine-build-batch-"));
const outlinePath = path.join(root, "outline.md");
const promptsDir = path.join(root, "prompts");
const outputPath = path.join(root, "batch.json");
await fs.mkdir(promptsDir, { recursive: true });
await fs.writeFile(
outlinePath,
`## Illustration 1
**Position**: demo
**Purpose**: demo
**Visual Content**: demo
**Filename**: 01-demo.png
`,
);
await fs.writeFile(path.join(promptsDir, "01-demo.md"), "A demo prompt\n");
return { root, outlinePath, promptsDir, outputPath };
}
async function runBuildBatch(args: string[]): Promise<void> {
await execFileAsync(process.execPath, ["--import", "tsx", scriptPath, ...args], {
cwd: repoRoot,
});
}
test("build-batch omits default model so baoyu-imagine can resolve env or EXTEND defaults", async () => {
const fixture = await makeFixture();
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.equal(batch.tasks.length, 1);
assert.equal(batch.tasks[0]?.provider, "replicate");
assert.equal(Object.hasOwn(batch.tasks[0]!, "model"), false);
});
test("build-batch preserves explicit model overrides", async () => {
const fixture = await makeFixture();
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
"--model",
"acme/custom-model",
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.equal(batch.tasks[0]?.model, "acme/custom-model");
});
test("build-batch propagates direct-usage references from prompt frontmatter", async () => {
const fixture = await makeFixture();
await fs.writeFile(
path.join(fixture.promptsDir, "01-demo.md"),
`---
illustration_id: 01
type: infographic
references:
- ref_id: 01
filename: 01-ref-brand.png
usage: direct
- ref_id: 02
filename: 02-ref-style.png
usage: style
---
A demo prompt
`,
);
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.deepEqual(batch.tasks[0]?.ref, ["references/01-ref-brand.png"]);
});
test("build-batch omits ref field when no direct references exist", async () => {
const fixture = await makeFixture();
await fs.writeFile(
path.join(fixture.promptsDir, "01-demo.md"),
`---
illustration_id: 01
references:
- ref_id: 01
filename: 01-ref-palette.png
usage: palette
---
A demo prompt
`,
);
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.equal(Object.hasOwn(batch.tasks[0]!, "ref"), false);
});
test("build-batch honors --refs-dir override", async () => {
const fixture = await makeFixture();
await fs.writeFile(
path.join(fixture.promptsDir, "01-demo.md"),
`---
illustration_id: 01
references:
- ref_id: 01
filename: brand.png
usage: direct
---
A demo prompt
`,
);
await runBuildBatch([
"--outline",
fixture.outlinePath,
"--prompts",
fixture.promptsDir,
"--output",
fixture.outputPath,
"--refs-dir",
"refs",
]);
const batch = JSON.parse(await fs.readFile(fixture.outputPath, "utf8")) as {
tasks: Array<Record<string, unknown>>;
};
assert.deepEqual(batch.tasks[0]?.ref, ["refs/brand.png"]);
});
@@ -7,6 +7,7 @@ type CliArgs = {
promptsDir: string | null; promptsDir: string | null;
outputPath: string | null; outputPath: string | null;
imagesDir: string | null; imagesDir: string | null;
refsDir: string;
provider: string; provider: string;
model: string | null; model: string | null;
aspectRatio: string; aspectRatio: string;
@@ -20,6 +21,11 @@ type OutlineEntry = {
filename: string; filename: string;
}; };
type PromptReference = {
filename: string;
usage: "direct" | "style" | "palette";
};
function printUsage(): void { function printUsage(): void {
console.log(`Usage: console.log(`Usage:
npx -y tsx scripts/build-batch.ts --outline outline.md --prompts prompts --output batch.json --images-dir attachments npx -y tsx scripts/build-batch.ts --outline outline.md --prompts prompts --output batch.json --images-dir attachments
@@ -29,6 +35,7 @@ Options:
--prompts <path> Path to prompts directory --prompts <path> Path to prompts directory
--output <path> Path to output batch.json --output <path> Path to output batch.json
--images-dir <path> Directory for generated images --images-dir <path> Directory for generated images
--refs-dir <path> Directory holding reference images, relative to batch file (default: references)
--provider <name> Provider for baoyu-imagine batch tasks (default: replicate) --provider <name> Provider for baoyu-imagine batch tasks (default: replicate)
--model <id> Explicit model for baoyu-imagine batch tasks (default: resolved by baoyu-imagine config/env) --model <id> Explicit model for baoyu-imagine batch tasks (default: resolved by baoyu-imagine config/env)
--ar <ratio> Aspect ratio for all tasks (default: 16:9) --ar <ratio> Aspect ratio for all tasks (default: 16:9)
@@ -43,6 +50,7 @@ function parseArgs(argv: string[]): CliArgs {
promptsDir: null, promptsDir: null,
outputPath: null, outputPath: null,
imagesDir: null, imagesDir: null,
refsDir: "references",
provider: "replicate", provider: "replicate",
model: null, model: null,
aspectRatio: "16:9", aspectRatio: "16:9",
@@ -57,6 +65,7 @@ function parseArgs(argv: string[]): CliArgs {
else if (current === "--prompts") args.promptsDir = argv[++i] ?? null; else if (current === "--prompts") args.promptsDir = argv[++i] ?? null;
else if (current === "--output") args.outputPath = argv[++i] ?? null; else if (current === "--output") args.outputPath = argv[++i] ?? null;
else if (current === "--images-dir") args.imagesDir = argv[++i] ?? null; else if (current === "--images-dir") args.imagesDir = argv[++i] ?? null;
else if (current === "--refs-dir") args.refsDir = argv[++i] ?? args.refsDir;
else if (current === "--provider") args.provider = argv[++i] ?? args.provider; else if (current === "--provider") args.provider = argv[++i] ?? args.provider;
else if (current === "--model") args.model = argv[++i] ?? args.model; else if (current === "--model") args.model = argv[++i] ?? args.model;
else if (current === "--ar") args.aspectRatio = argv[++i] ?? args.aspectRatio; else if (current === "--ar") args.aspectRatio = argv[++i] ?? args.aspectRatio;
@@ -71,6 +80,72 @@ function parseArgs(argv: string[]): CliArgs {
return args; return args;
} }
function parsePromptReferences(content: string): PromptReference[] {
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/);
if (!fmMatch) return [];
const lines = fmMatch[1]!.split(/\r?\n/);
const refs: PromptReference[] = [];
let current: Partial<PromptReference> | null = null;
let inReferences = false;
let listIndent = 0;
const flush = () => {
if (current?.filename) {
refs.push({
filename: current.filename,
usage: (current.usage ?? "direct") as PromptReference["usage"],
});
}
current = null;
};
const unquote = (raw: string): string => raw.trim().replace(/^["']|["']$/g, "");
for (const line of lines) {
if (!line.trim() || line.trim().startsWith("#")) continue;
const keyMatch = line.match(/^(\S[^:]*):\s*(.*)$/);
if (keyMatch) {
flush();
if (keyMatch[1] === "references") {
inReferences = true;
listIndent = 0;
continue;
}
inReferences = false;
continue;
}
if (!inReferences) continue;
const itemMatch = line.match(/^(\s*)-\s*(.*)$/);
if (itemMatch) {
flush();
listIndent = itemMatch[1]!.length;
current = {};
const rest = itemMatch[2]!.trim();
if (rest) {
const kv = rest.match(/^(\w+)\s*:\s*(.*)$/);
if (kv && (kv[1] === "filename" || kv[1] === "usage")) {
(current as Record<string, string>)[kv[1]] = unquote(kv[2]!);
}
}
continue;
}
const kvMatch = line.match(/^(\s+)(\w+)\s*:\s*(.*)$/);
if (kvMatch && kvMatch[1]!.length > listIndent && current) {
if (kvMatch[2] === "filename" || kvMatch[2] === "usage") {
(current as Record<string, string>)[kvMatch[2]!] = unquote(kvMatch[3]!);
}
}
}
flush();
return refs;
}
function parseOutline(content: string): OutlineEntry[] { function parseOutline(content: string): OutlineEntry[] {
const entries: OutlineEntry[] = []; const entries: OutlineEntry[] = [];
const blocks = content.split(/^## Illustration\s+/m).slice(1); const blocks = content.split(/^## Illustration\s+/m).slice(1);
@@ -132,6 +207,11 @@ async function main(): Promise<void> {
} }
const imageDir = args.imagesDir ?? path.dirname(args.outputPath); const imageDir = args.imagesDir ?? path.dirname(args.outputPath);
const promptContent = await readFile(promptFile, "utf8");
const refs = parsePromptReferences(promptContent)
.filter((r) => r.usage === "direct")
.map((r) => path.posix.join(args.refsDir, r.filename));
const task: Record<string, unknown> = { const task: Record<string, unknown> = {
id: `illustration-${String(entry.index).padStart(2, "0")}`, id: `illustration-${String(entry.index).padStart(2, "0")}`,
promptFiles: [promptFile], promptFiles: [promptFile],
@@ -141,6 +221,7 @@ async function main(): Promise<void> {
quality: args.quality, quality: args.quality,
}; };
if (args.model) task.model = args.model; if (args.model) task.model = args.model;
if (refs.length > 0) task.ref = refs;
tasks.push(task); tasks.push(task);
} }
+80 -59
View File
@@ -1,6 +1,6 @@
--- ---
name: baoyu-infographic name: baoyu-infographic
description: Generates 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 "高密度信息大图". 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 version: 1.56.1
metadata: metadata:
openclaw: openclaw:
@@ -11,16 +11,59 @@ metadata:
Two dimensions: **layout** (information structure) × **style** (visual aesthetics). Freely combine any layout with any style. Two dimensions: **layout** (information structure) × **style** (visual aesthetics). Freely combine any layout with any style.
## Usage ## User Input Tools
```bash When this skill prompts the user, follow this tool-selection rule (priority order):
/baoyu-infographic path/to/content.md
/baoyu-infographic path/to/content.md --layout hierarchical-layers --style technical-schematic 1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
/baoyu-infographic path/to/content.md --aspect portrait --lang zh 2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
/baoyu-infographic path/to/content.md --aspect 3:4 3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
/baoyu-infographic # then paste content
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Image Generation Tools
When this skill needs to render an image:
- **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.
**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.
## Reference Images
Users may supply reference images to guide style, palette, composition, or subject.
**Intake**: Accept via `--ref <files...>` or when the user provides file paths / pastes images in conversation.
- File path(s) → copy to `refs/NN-ref-{slug}.{ext}` alongside the output
- Pasted image with no path → ask the user for the path (per the User Input Tools rule above), or extract style traits verbally as a text fallback
- No reference → skip this section
**Usage modes** (per reference):
| Usage | Effect |
|-------|--------|
| `direct` | Pass the file to the backend as a reference image |
| `style` | Extract style traits (line treatment, texture, mood) and append to the prompt body |
| `palette` | Extract hex colors from the image and append to the prompt body |
**Record in `prompts/infographic.md` frontmatter** when refs exist:
```yaml
references:
- ref_id: 01
filename: 01-ref-brand.png
usage: direct
``` ```
**At generation time**:
- Verify each referenced file exists on disk
- 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
## Options ## Options
| Option | Values | | Option | Values |
@@ -29,8 +72,9 @@ Two dimensions: **layout** (information structure) × **style** (visual aestheti
| `--style` | 21 options (see Style Gallery), default: craft-handmade | | `--style` | 21 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) | | `--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. | | `--lang` | en, zh, ja, etc. |
| `--ref <files...>` | Reference images (file paths) for style / palette / composition / subject guidance |
## Layout Gallery ## Layout Gallery (21)
| Layout | Best For | | Layout | Best For |
|--------|----------| |--------|----------|
@@ -56,9 +100,9 @@ Two dimensions: **layout** (information structure) × **style** (visual aestheti
| `circular-flow` | Cycles, recurring processes | | `circular-flow` | Cycles, recurring processes |
| `dense-modules` | High-density modules, data-rich guides | | `dense-modules` | High-density modules, data-rich guides |
Full definitions: `references/layouts/<layout>.md` Full definitions live at `references/layouts/<layout>.md`.
## Style Gallery ## Style Gallery (21)
| Style | Description | | Style | Description |
|-------|-------------| |-------|-------------|
@@ -84,7 +128,7 @@ Full definitions: `references/layouts/<layout>.md`
| `retro-pop-grid` | 1970s retro pop art, Swiss grid, thick outlines | | `retro-pop-grid` | 1970s retro pop art, Swiss grid, thick outlines |
| `hand-drawn-edu` | Macaron pastels, hand-drawn wobble, stick figures | | `hand-drawn-edu` | Macaron pastels, hand-drawn wobble, stick figures |
Full definitions: `references/styles/<style>.md` Full definitions live at `references/styles/<style>.md`.
## Recommended Combinations ## Recommended Combinations
@@ -108,13 +152,11 @@ Full definitions: `references/styles/<style>.md`
| Educational Diagram | `hub-spoke` + `hand-drawn-edu` | | Educational Diagram | `hub-spoke` + `hand-drawn-edu` |
| Process Tutorial | `linear-progression` + `hand-drawn-edu` | | Process Tutorial | `linear-progression` + `hand-drawn-edu` |
Default: `bento-grid` + `craft-handmade` Default combination: `bento-grid` + `craft-handmade`.
## Keyword Shortcuts ## Keyword Shortcuts
When user input contains these keywords, **auto-select** the associated layout and offer associated styles as top recommendations in Step 3. Skip content-based layout inference for matched keywords. 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.
If a shortcut has **Prompt Notes**, append them to the generated prompt (Step 5) as additional style instructions.
| User Keyword | Layout | Recommended Styles | Default Aspect | Prompt Notes | | User Keyword | Layout | Recommended Styles | Default Aspect | Prompt Notes |
|--------------|--------|--------------------|----------------|--------------| |--------------|--------|--------------------|----------------|--------------|
@@ -146,40 +188,20 @@ Slug: 2-4 words kebab-case from topic. Conflict: append `-YYYYMMDD-HHMMSS`.
**1.1 Load Preferences (EXTEND.md)** **1.1 Load Preferences (EXTEND.md)**
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-infographic/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-infographic/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-infographic/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-infographic/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-infographic/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-infographic/EXTEND.md` | User home |
```
```powershell | Result | Action |
# PowerShell (Windows) |--------|--------|
if (Test-Path .baoyu-skills/baoyu-infographic/EXTEND.md) { "project" } | Found | Read, parse, display a one-line summary |
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" } | Not found | Ask the user with `AskUserQuestion` (see `references/config/first-time-setup.md`) |
if (Test-Path "$xdg/baoyu-skills/baoyu-infographic/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-infographic/EXTEND.md") { "user" }
```
┌────────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: preferred layout/style, default aspect ratio, custom style definitions, language preference.
│ Path │ Location │
├────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-infographic/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-infographic/EXTEND.md │ User home │
└────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, display summary │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Ask user with AskUserQuestion (see references/config/first-time-setup.md) │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Preferred layout/style | Default aspect ratio | Custom style definitions | Language preference
Schema: `references/config/preferences-schema.md` Schema: `references/config/preferences-schema.md`
@@ -219,15 +241,13 @@ See `references/structured-content-template.md` for detailed format.
### Step 4: Confirm Options ### Step 4: Confirm Options
Use **single AskUserQuestion call** with multiple questions to confirm all options together: 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).
| Question | When | Options | | Priority | Question | When | Options |
|----------|------|---------| |----------|----------|------|---------|
| **Combination** | Always | 3+ layout×style combos with rationale | | 1 | **Combination** | Always | 3+ layout×style combos with rationale |
| **Aspect** | Always | Named presets (landscape/portrait/square) or custom W:H ratio (e.g., 3:4, 4:3, 2.35:1) | | 2 | **Aspect** | Always | Named presets (landscape/portrait/square) or custom W:H ratio (e.g., 3:4, 4:3, 2.35:1) |
| **Language** | Only if source ≠ user language | Language for text content | | 3 | **Language** | Only if source ≠ user language | Language for text content |
**Important**: Do NOT split into separate AskUserQuestion calls. Combine all applicable questions into one call.
### Step 5: Generate Prompt → `prompts/infographic.md` ### Step 5: Generate Prompt → `prompts/infographic.md`
@@ -246,11 +266,12 @@ Combine:
### Step 6: Generate Image ### Step 6: Generate Image
1. Select available image generation skill (ask user if multiple) 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.
2. **Check for existing file**: Before generating, check if `infographic.png` exists 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` - If exists: Rename to `infographic-backup-YYYYMMDD-HHMMSS.png`
3. Call with prompt file and output path 4. Call the chosen backend with the prompt file and output path
4. On failure, auto-retry once 5. On failure, auto-retry once
### Step 7: Output Summary ### Step 7: Output Summary
@@ -0,0 +1,153 @@
---
name: first-time-setup
description: First-time setup flow for baoyu-infographic preferences
---
# First-Time Setup
## Overview
When no EXTEND.md is found, guide the user through preference setup before generating any infographic.
**⛔ BLOCKING OPERATION**: This setup MUST complete before ANY other workflow steps. Do NOT:
- Ask about source content or topic
- Ask about layout, style, or aspect
- Begin Step 1.2 content analysis
ONLY ask the questions in this setup flow, save EXTEND.md, then continue to Step 1.2.
## Setup Flow
```
No EXTEND.md found
┌─────────────────────┐
│ AskUserQuestion │
│ (all questions) │
└─────────────────────┘
┌─────────────────────┐
│ Create EXTEND.md │
└─────────────────────┘
Continue to Step 1.2
```
## Questions
**Language**: Use the user's input language for question text. Do not always default to English.
Use a single `AskUserQuestion` with multiple questions (the runtime auto-adds an "Other" option):
### Question 1: Preferred Layout
```
header: "Layout"
question: "Default layout preference?"
options:
- label: "Auto-select (Recommended)"
description: "Pick layout per content in Step 3"
- label: "bento-grid"
description: "Multiple topics, overview (general default)"
- label: "linear-progression"
description: "Timelines, processes, tutorials"
- label: "dense-modules"
description: "High-density modules, data-rich guides"
```
### Question 2: Preferred Style
```
header: "Style"
question: "Default visual style preference?"
options:
- label: "Auto-select (Recommended)"
description: "Pick style per tone in Step 3"
- label: "craft-handmade"
description: "Hand-drawn, paper craft (general default)"
- label: "corporate-memphis"
description: "Flat vector, vibrant"
- label: "morandi-journal"
description: "Hand-drawn doodle, warm Morandi tones"
```
### Question 3: Preferred Aspect
```
header: "Aspect"
question: "Default aspect ratio?"
options:
- label: "Auto-select (Recommended)"
description: "Pick per layout in Step 4"
- label: "landscape"
description: "16:9 (slides, blogs, web)"
- label: "portrait"
description: "9:16 (mobile, social, dense modules)"
- label: "square"
description: "1:1 (social, thumbnails)"
```
### Question 4: Language
```
header: "Language"
question: "Output language for infographic text?"
options:
- label: "Auto-detect (Recommended)"
description: "Match source content language"
- label: "zh"
description: "Chinese (中文)"
- label: "en"
description: "English"
```
### Question 5: Save Location
```
header: "Save"
question: "Where to save preferences?"
options:
- label: "Project"
description: ".baoyu-skills/ (this project only)"
- label: "User"
description: "~/.baoyu-skills/ (all projects)"
```
## Save Locations
| Choice | Path | Scope |
|--------|------|-------|
| Project | `.baoyu-skills/baoyu-infographic/EXTEND.md` | Current project |
| User | `~/.baoyu-skills/baoyu-infographic/EXTEND.md` | All projects |
XDG path (`${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-infographic/EXTEND.md`) is also recognized at read time but not offered as a save target during first-time setup.
## After Setup
1. Create the directory if needed
2. Write EXTEND.md with frontmatter (see template below)
3. Confirm: "Preferences saved to [path]"
4. Continue to Step 1.2
## EXTEND.md Template
```yaml
---
version: 1
preferred_layout: [selected layout or null]
preferred_style: [selected style or null]
preferred_aspect: [landscape|portrait|square|null]
language: [selected language or null]
custom_styles: []
---
```
## 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`
@@ -0,0 +1,107 @@
---
name: preferences-schema
description: EXTEND.md YAML schema for baoyu-infographic user preferences
---
# Preferences Schema
## Full Schema
```yaml
---
version: 1
preferred_layout: null # any of the 21 layouts (see Layout Gallery in SKILL.md) or null
preferred_style: null # any of the 21 styles (see Style Gallery in SKILL.md) or null
preferred_aspect: null # landscape|portrait|square|null (custom W:H also accepted)
language: null # zh|en|ja|ko|null (null = auto-detect from source)
custom_styles: # extra style definitions merged with the 21 built-ins
- name: my-brand
description: "Short description shown in Step 3 recommendations"
prompt_fragment: "Style traits to inject into Step 5 prompt"
---
```
## Field Reference
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `version` | int | 1 | Schema version |
| `preferred_layout` | string\|null | null | Pre-selected layout — surfaces as the top recommendation in Step 3 |
| `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) |
| `custom_styles` | array | [] | Additional styles available alongside the 21 built-ins |
## Layout Options
See the **Layout Gallery (21)** table in `SKILL.md` for the canonical list. Common picks:
| Value | Best For |
|-------|----------|
| `bento-grid` | General default — overview, multiple topics |
| `linear-progression` | Timelines, processes, tutorials |
| `dense-modules` | High-density modules, data-rich guides |
| `hub-spoke` | Central concept with related items |
| `dashboard` | Metrics, KPIs |
## Style Options
See the **Style Gallery (21)** table in `SKILL.md` for the canonical list. Common picks:
| Value | Description |
|-------|-------------|
| `craft-handmade` | Hand-drawn, paper craft (default) |
| `corporate-memphis` | Flat vector, vibrant |
| `morandi-journal` | Hand-drawn doodle, warm Morandi tones |
| `pop-laboratory` | Blueprint grid, lab precision |
| `retro-pop-grid` | 1970s retro pop art, Swiss grid |
## Aspect Options
| Value | Ratio | Notes |
|-------|-------|-------|
| `landscape` | 16:9 | Slides, blog headers, web banners |
| `portrait` | 9:16 | Mobile, social, dense modules (default for `dense-modules`) |
| `square` | 1:1 | Social posts, thumbnails |
| Custom W:H | e.g. `3:4`, `4:3`, `2.35:1` | Pass through verbatim to the prompt |
## Custom Style Fields
| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | Unique style identifier (kebab-case) |
| `description` | Yes | One-line description shown in Step 3 recommendations |
| `prompt_fragment` | Yes | Style traits appended into the Step 5 prompt body |
## Example: Minimal Preferences
```yaml
---
version: 1
preferred_layout: bento-grid
preferred_style: craft-handmade
language: zh
---
```
## Example: Full Preferences
```yaml
---
version: 1
preferred_layout: dense-modules
preferred_style: morandi-journal
preferred_aspect: portrait
language: zh
custom_styles:
- name: my-brand
description: "Brand-aligned warm pastel infographic"
prompt_fragment: "Use brand pastel palette (#F2C7B6, #B6D7E8, #C8E0B4); rounded rectangles; warm hand-drawn outlines; ample whitespace."
---
```
+20 -48
View File
@@ -15,6 +15,16 @@ metadata:
Converts Markdown files to beautifully styled HTML with inline CSS, optimized for WeChat Official Account and other platforms. Converts Markdown files to beautifully styled HTML with inline CSS, optimized for WeChat Official Account and other platforms.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
**Agent Execution**: Determine this SKILL.md directory as `{baseDir}`. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values. **Agent Execution**: Determine this SKILL.md directory as `{baseDir}`. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values.
@@ -25,40 +35,17 @@ Converts Markdown files to beautifully styled HTML with inline CSS, optimized fo
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-markdown-to-html/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-markdown-to-html/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-markdown-to-html/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-markdown-to-html/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-markdown-to-html/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-markdown-to-html/EXTEND.md` | User home |
```
```powershell If none found, use defaults.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-markdown-to-html/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-markdown-to-html/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-markdown-to-html/EXTEND.md") { "user" }
```
┌──────────────────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: default theme, custom CSS variables, code block style.
│ Path │ Location │
├──────────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-markdown-to-html/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-markdown-to-html/EXTEND.md │ User home │
└──────────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default theme | Custom CSS variables | Code block style
## Workflow ## Workflow
@@ -93,26 +80,11 @@ Use `AskUserQuestion` to ask whether to format first. Formatting can fix:
**Cross-skill EXTEND.md check** (only if this skill's EXTEND.md has no `default_theme`): **Cross-skill EXTEND.md check** (only if this skill's EXTEND.md has no `default_theme`):
```bash Read `$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md` if it exists and look for a `default_theme:` line. Use the value if present; otherwise fall through.
# Check baoyu-post-to-wechat EXTEND.md for default_theme
test -f "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md" && grep -o 'default_theme:.*' "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md"
```
```powershell
# PowerShell (Windows)
if (Test-Path "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md") { Select-String -Pattern 'default_theme:.*' -Path "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md" | ForEach-Object { $_.Matches.Value } }
```
**If theme is resolved from EXTEND.md**: Use it directly, do NOT ask the user. **If theme is resolved from EXTEND.md**: Use it directly, do NOT ask the user.
**If no default found**: Use AskUserQuestion to confirm: **If no default found**: use `AskUserQuestion` to confirm a theme from the [Themes](#themes) table below.
| Theme | Description |
|-------|-------------|
| `default` (Recommended) | Classic - traditional layout, centered title with bottom border, H2 with white text on colored background |
| `grace` | Elegant - text shadow, rounded cards, refined blockquotes |
| `simple` | Minimal - modern minimalist, asymmetric rounded corners, clean whitespace |
| `modern` | Modern - large radius, pill-shaped titles, relaxed line height (pair with `--color red` for traditional red-gold style) |
### Step 1.5: Determine Citation Mode ### Step 1.5: Determine Citation Mode
+96 -337
View File
@@ -13,13 +13,23 @@ metadata:
# Post to WeChat Official Account # Post to WeChat Official Account
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Language ## Language
**Match user's language**: Respond in the same language the user uses. If user writes in Chinese, respond in Chinese. If user writes in English, respond in English. Respond in the user's language. If they write in Chinese, reply in Chinese; if English, English. Keep technical tokens (paths, flags, field names) in English.
## Script Directory ## Script Directory
**Agent Execution**: Determine this SKILL.md directory as `{baseDir}`, then use `{baseDir}/scripts/<name>.ts`. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. `{baseDir}` = this SKILL.md's directory. Resolve `${BUN_X}`: prefer `bun`; else `npx -y bun`; else suggest `brew install oven-sh/bun/bun`.
| Script | Purpose | | Script | Purpose |
|--------|---------| |--------|---------|
@@ -31,52 +41,25 @@ metadata:
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check these paths in order; first hit wins:
```bash | Path | Scope |
# macOS, Linux, WSL, Git Bash |------|-------|
test -f .baoyu-skills/baoyu-post-to-wechat/EXTEND.md && echo "project" | `.baoyu-skills/baoyu-post-to-wechat/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-post-to-wechat/EXTEND.md" && echo "xdg" | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-post-to-wechat/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md" && echo "user" | `$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md` | User home |
```
```powershell Found → read, parse, apply. Not found → run first-time setup (`references/config/first-time-setup.md`) before anything else.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-post-to-wechat/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-post-to-wechat/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md") { "user" }
```
┌────────────────────────────────────────────────────────┬───────────────────┐ **Minimum keys** (case-insensitive, accept `1/0` or `true/false`):
│ Path │ Location │
├────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-post-to-wechat/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md │ User home │
└────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Run first-time setup ([references/config/first-time-setup.md](references/config/first-time-setup.md)) → Save → Continue │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default theme | Default color | Default publishing method (api/browser) | Default author | Default open-comment switch | Default fans-only-comment switch | Chrome profile path
First-time setup: [references/config/first-time-setup.md](references/config/first-time-setup.md)
**Minimum supported keys** (case-insensitive, accept `1/0` or `true/false`):
| Key | Default | Mapping | | Key | Default | Mapping |
|-----|---------|---------| |-----|---------|---------|
| `default_author` | empty | Fallback for `author` when CLI/frontmatter not provided | | `default_author` | empty | Fallback for `author` when CLI/frontmatter not provided |
| `need_open_comment` | `1` | `articles[].need_open_comment` in `draft/add` request | | `need_open_comment` | `1` | `articles[].need_open_comment` in `draft/add` |
| `only_fans_can_comment` | `0` | `articles[].only_fans_can_comment` in `draft/add` request | | `only_fans_can_comment` | `0` | `articles[].only_fans_can_comment` in `draft/add` |
**Recommended EXTEND.md example**: **Recommended EXTEND.md**:
```md ```md
default_theme: default default_theme: default
@@ -88,125 +71,19 @@ only_fans_can_comment: 0
chrome_profile_path: /path/to/chrome/profile chrome_profile_path: /path/to/chrome/profile
``` ```
**Theme options**: default, grace, simple, modern **Theme options**: default, grace, simple, modern. **Color presets**: blue, green, vermilion, yellow, purple, sky, rose, olive, black, gray, pink, red, orange (or hex).
**Color presets**: blue, green, vermilion, yellow, purple, sky, rose, olive, black, gray, pink, red, orange (or hex value) **Value priority**: CLI args → frontmatter → EXTEND.md (account-level → global) → skill defaults.
**Value priority**:
1. CLI arguments
2. Frontmatter
3. EXTEND.md (account-level → global-level)
4. Skill defaults
## Multi-Account Support ## Multi-Account Support
EXTEND.md supports managing multiple WeChat Official Accounts. When `accounts:` block is present, each account can have its own credentials, Chrome profile, and default settings. EXTEND.md supports an `accounts:` block for managing multiple Official Accounts. With 2+ entries, the workflow inserts a Step 0.5 to prompt for account selection (or auto-selects based on `default: true` or `--account <alias>`).
**Compatibility rules**: Full details — compatibility rules, per-account keys, credential resolution, per-account Chrome profiles, CLI usage — in `references/multi-account.md`.
| Condition | Mode | Behavior |
|-----------|------|----------|
| No `accounts` block | Single-account | Current behavior, unchanged |
| `accounts` with 1 entry | Single-account | Auto-select, no prompt |
| `accounts` with 2+ entries | Multi-account | Prompt to select before publishing |
| `accounts` with `default: true` | Multi-account | Pre-select default, user can switch |
**Multi-account EXTEND.md example**:
```md
default_theme: default
default_color: blue
accounts:
- name: 宝玉的技术分享
alias: baoyu
default: true
default_publish_method: api
default_author: 宝玉
need_open_comment: 1
only_fans_can_comment: 0
app_id: your_wechat_app_id
app_secret: your_wechat_app_secret
- name: AI工具集
alias: ai-tools
default_publish_method: browser
default_author: AI工具集
need_open_comment: 1
only_fans_can_comment: 0
```
**Per-account keys** (can be set per-account or globally as fallback):
`default_publish_method`, `default_author`, `need_open_comment`, `only_fans_can_comment`, `app_id`, `app_secret`, `chrome_profile_path`
**Global-only keys** (always shared across accounts):
`default_theme`, `default_color`
### Account Selection (Step 0.5)
Insert between Step 0 and Step 1 in the Article Posting Workflow:
```
if no accounts block:
→ single-account mode (current behavior)
elif accounts.length == 1:
→ auto-select the only account
elif --account <alias> CLI arg:
→ select matching account
elif one account has default: true:
→ pre-select, show: "Using account: <name> (--account to switch)"
else:
→ prompt user:
"Multiple WeChat accounts configured:
1) <name1> (<alias1>)
2) <name2> (<alias2>)
Select account [1-N]:"
```
### Credential Resolution (API Method)
For a selected account with alias `{alias}`:
1. `app_id` / `app_secret` inline in EXTEND.md account block
2. Env var `WECHAT_{ALIAS}_APP_ID` / `WECHAT_{ALIAS}_APP_SECRET` (alias uppercased, hyphens → underscores)
3. `.baoyu-skills/.env` with prefixed key `WECHAT_{ALIAS}_APP_ID`
4. `~/.baoyu-skills/.env` with prefixed key
5. Fallback to unprefixed `WECHAT_APP_ID` / `WECHAT_APP_SECRET`
**.env multi-account example**:
```bash
# Account: baoyu
WECHAT_BAOYU_APP_ID=your_wechat_app_id
WECHAT_BAOYU_APP_SECRET=your_wechat_app_secret
# Account: ai-tools
WECHAT_AI_TOOLS_APP_ID=your_ai_tools_wechat_app_id
WECHAT_AI_TOOLS_APP_SECRET=your_ai_tools_wechat_app_secret
```
### Chrome Profile (Browser Method)
Each account uses an isolated Chrome profile for independent login sessions:
| Source | Path |
|--------|------|
| Account `chrome_profile_path` in EXTEND.md | Use as-is |
| Auto-generated from alias | `{shared_profile_parent}/wechat-{alias}/` |
| Single-account fallback | Shared default profile (current behavior) |
### CLI `--account` Argument
All publishing scripts accept `--account <alias>`:
```bash
${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme default --account ai-tools
${BUN_X} {baseDir}/scripts/wechat-article.ts --markdown <file> --theme default --account baoyu
${BUN_X} {baseDir}/scripts/wechat-browser.ts --markdown <file> --images ./photos/ --account baoyu
```
## Pre-flight Check (Optional) ## Pre-flight Check (Optional)
Before first use, suggest running the environment check. User can skip if they prefer. Before first use, suggest the environment check (user can skip):
```bash ```bash
${BUN_X} {baseDir}/scripts/check-permissions.ts ${BUN_X} {baseDir}/scripts/check-permissions.ts
@@ -214,38 +91,32 @@ ${BUN_X} {baseDir}/scripts/check-permissions.ts
Checks: Chrome, profile isolation, Bun, Accessibility, clipboard, paste keystroke, API credentials, Chrome conflicts. Checks: Chrome, profile isolation, Bun, Accessibility, clipboard, paste keystroke, API credentials, Chrome conflicts.
**If any check fails**, provide fix guidance per item: | Check fails | Fix |
|-------------|-----|
| Check | Fix | | Chrome | Install Chrome or set `WECHAT_BROWSER_CHROME_PATH` |
|-------|-----| | Profile dir | Shared profile at `baoyu-skills/chrome-profile` |
| Chrome | Install Chrome or set `WECHAT_BROWSER_CHROME_PATH` env var | | Bun runtime | `brew install oven-sh/bun/bun` or `npm install -g bun` |
| Profile dir | Shared profile at `baoyu-skills/chrome-profile` (see CLAUDE.md Chrome Profile section) |
| Bun runtime | `brew install oven-sh/bun/bun` (macOS) or `npm install -g bun` |
| Accessibility (macOS) | System Settings → Privacy & Security → Accessibility → enable terminal app | | Accessibility (macOS) | System Settings → Privacy & Security → Accessibility → enable terminal app |
| Clipboard copy | Ensure Swift/AppKit available (macOS Xcode CLI tools: `xcode-select --install`) | | Clipboard copy | Ensure Swift/AppKit (macOS: `xcode-select --install`) |
| Paste keystroke (macOS) | Same as Accessibility fix above |
| Paste keystroke (Linux) | Install `xdotool` (X11) or `ydotool` (Wayland) | | Paste keystroke (Linux) | Install `xdotool` (X11) or `ydotool` (Wayland) |
| API credentials | Follow guided setup in Step 2, or manually set in `.baoyu-skills/.env` | | API credentials | Follow guided setup in Step 2, or set in `.baoyu-skills/.env` |
## Image-Text Posting (图文) ## Image-Text Posting (图文)
For short posts with multiple images (up to 9): Short posts with multiple images (up to 9):
```bash ```bash
${BUN_X} {baseDir}/scripts/wechat-browser.ts --markdown article.md --images ./images/ ${BUN_X} {baseDir}/scripts/wechat-browser.ts --markdown article.md --images ./images/
${BUN_X} {baseDir}/scripts/wechat-browser.ts --title "标题" --content "内容" --image img.png --submit ${BUN_X} {baseDir}/scripts/wechat-browser.ts --title "标题" --content "内容" --image img.png --submit
``` ```
See [references/image-text-posting.md](references/image-text-posting.md) for details. Details: `references/image-text-posting.md`.
## Article Posting Workflow (文章) ## Article Posting Workflow (文章)
Copy this checklist and check off items as you complete them:
``` ```
Publishing Progress:
- [ ] Step 0: Load preferences (EXTEND.md) - [ ] Step 0: Load preferences (EXTEND.md)
- [ ] Step 0.5: Resolve account (multi-account only) - [ ] Step 0.5: Resolve account (multi-account only — see references/multi-account.md)
- [ ] Step 1: Determine input type - [ ] Step 1: Determine input type
- [ ] Step 2: Select method and configure credentials - [ ] Step 2: Select method and configure credentials
- [ ] Step 3: Resolve theme/color and validate metadata - [ ] Step 3: Resolve theme/color and validate metadata
@@ -255,125 +126,54 @@ Publishing Progress:
### Step 0: Load Preferences ### Step 0: Load Preferences
Check and load EXTEND.md settings (see Preferences section above). Check and load EXTEND.md (see "Preferences" above). If not found, complete first-time setup before any other questions. Resolve and cache for later steps: `default_theme`, `default_color`, `default_author`, `need_open_comment`, `only_fans_can_comment`.
**CRITICAL**: If not found, complete first-time setup BEFORE any other steps or questions.
Resolve and store these defaults for later steps:
- `default_theme` (default `default`)
- `default_color` (omit if not set — theme default applies)
- `default_author`
- `need_open_comment` (default `1`)
- `only_fans_can_comment` (default `0`)
### Step 1: Determine Input Type ### Step 1: Determine Input Type
| Input Type | Detection | Action | | Input | Detection | Next |
|------------|-----------|--------| |-------|-----------|------|
| HTML file | Path ends with `.html`, file exists | Skip to Step 3 | | HTML file | Path ends `.html`, file exists | Skip to Step 3 |
| Markdown file | Path ends with `.md`, file exists | Continue to Step 2 | | Markdown file | Path ends `.md`, file exists | Step 2 |
| Plain text | Not a file path, or file doesn't exist | Save to markdown, continue to Step 2 | | Plain text | Not a file path, or file doesn't exist | Save to markdown, then Step 2 |
**Plain Text Handling**: **Plain-text handling**:
1. Generate slug from content (first 2-4 meaningful words, kebab-case) 1. Generate slug (first 2-4 meaningful words, kebab-case; translate Chinese to English for the slug).
2. Create directory and save file: 2. Save to `post-to-wechat/YYYY-MM-DD/<slug>.md` (create directory if needed).
3. Continue as a markdown file.
```bash
mkdir -p "$(pwd)/post-to-wechat/$(date +%Y-%m-%d)"
# Save content to: post-to-wechat/yyyy-MM-dd/[slug].md
```
3. Continue processing as markdown file
**Slug Examples**:
- "Understanding AI Models" → `understanding-ai-models`
- "人工智能的未来" → `ai-future` (translate to English for slug)
### Step 2: Select Publishing Method and Configure ### Step 2: Select Publishing Method and Configure
**Ask publishing method** (unless specified in EXTEND.md or CLI): Ask method unless specified in EXTEND.md or CLI:
| Method | Speed | Requirements | | Method | Speed | Requires |
|--------|-------|--------------| |--------|-------|----------|
| `api` (Recommended) | Fast | API credentials | | `api` (Recommended) | Fast | API credentials |
| `browser` | Slow | Chrome, login session | | `browser` | Slow | Chrome + logged-in session |
**If API Selected - Check Credentials**: **API selected + missing credentials** → run guided setup per `references/api-setup.md` (writes to `.baoyu-skills/.env`).
```bash
# macOS, Linux, WSL, Git Bash
test -f .baoyu-skills/.env && grep -q "WECHAT_APP_ID" .baoyu-skills/.env && echo "project"
test -f "$HOME/.baoyu-skills/.env" && grep -q "WECHAT_APP_ID" "$HOME/.baoyu-skills/.env" && echo "user"
```
```powershell
# PowerShell (Windows)
if ((Test-Path .baoyu-skills/.env) -and (Select-String -Quiet -Pattern "WECHAT_APP_ID" .baoyu-skills/.env)) { "project" }
if ((Test-Path "$HOME/.baoyu-skills/.env") -and (Select-String -Quiet -Pattern "WECHAT_APP_ID" "$HOME/.baoyu-skills/.env")) { "user" }
```
**If Credentials Missing - Guide Setup**:
```
WeChat API credentials not found.
To obtain credentials:
1. Visit https://mp.weixin.qq.com
2. Go to: 开发 → 基本配置
3. Copy AppID and AppSecret
Where to save?
A) Project-level: .baoyu-skills/.env (this project only)
B) User-level: ~/.baoyu-skills/.env (all projects)
```
After location choice, prompt for values and write to `.env`:
```
WECHAT_APP_ID=<user_input>
WECHAT_APP_SECRET=<user_input>
```
### Step 3: Resolve Theme/Color and Validate Metadata ### Step 3: Resolve Theme/Color and Validate Metadata
1. **Resolve theme** (first match wins, do NOT ask user if resolved): 1. **Theme**: CLI `--theme` → EXTEND.md `default_theme``default` (first match wins; do NOT ask if resolved).
- CLI `--theme` argument 2. **Color**: CLI `--color` → EXTEND.md `default_color` → omit (theme default applies).
- EXTEND.md `default_theme` (loaded in Step 0) 3. **Validate metadata** (frontmatter for markdown, meta tags for HTML):
- Fallback: `default`
2. **Resolve color** (first match wins): | Field | Missing → |
- CLI `--color` argument |-------|-----------|
- EXTEND.md `default_color` (loaded in Step 0) | Title | Ask, or press Enter to auto-generate from content |
- Omit if not set (theme default applies) | Summary | Frontmatter `description``summary` → ask or auto-generate |
| Author | CLI `--author` → frontmatter `author` → EXTEND.md `default_author` |
3. **Validate metadata** from frontmatter (markdown) or HTML meta tags (HTML input): Auto-generation: title = first H1/H2 or first sentence; summary = first paragraph, truncated to 120 chars.
| Field | If Missing | 4. **Cover image** (required for API `article_type=news`): CLI `--cover` → frontmatter (`coverImage` / `featureImage` / `cover` / `image`) → `imgs/cover.png` → first inline image → stop and request one if still missing.
|-------|------------|
| Title | Prompt: "Enter title, or press Enter to auto-generate from content" |
| Summary | Use fallback chain: frontmatter `description` → frontmatter `summary` → prompt user or auto-generate |
| Author | Use fallback chain: CLI `--author` → frontmatter `author` → EXTEND.md `default_author` |
**Auto-Generation Logic**: ### Step 4: Publish
- **Title**: First H1/H2 heading, or first sentence
- **Summary**: First paragraph, truncated to 120 characters
4. **Cover Image Check** (required for API `article_type=news`): **Important — never pre-convert markdown to HTML.** Publishing scripts handle the conversion internally and the two methods render images differently: API renders `<img>` tags for upload, browser uses placeholders for paste-and-replace. Passing a pre-converted HTML breaks one or the other.
1. Use CLI `--cover` if provided.
2. Else use frontmatter (`coverImage`, `featureImage`, `cover`, `image`).
3. Else check article directory default path: `imgs/cover.png`.
4. Else fallback to first inline content image.
5. If still missing, stop and request a cover image before publishing.
### Step 4: Publish to WeChat **Markdown citation default**: for markdown input, ordinary external links are converted to bottom citations by default. Use `--no-cite` only if the user explicitly wants to keep inline links. Existing HTML input is left as-is.
**CRITICAL**: Publishing scripts handle markdown conversion internally. Do NOT pre-convert markdown to HTML — pass the original markdown file directly. This ensures the API method renders images as `<img>` tags (for API upload) while the browser method uses placeholders (for paste-and-replace workflow).
**Markdown citation default**:
- For markdown input, ordinary external links are converted to bottom citations by default.
- Use `--no-cite` only if the user explicitly wants to keep ordinary external links inline.
- Existing HTML input is left as-is; no extra citation conversion is applied.
**API method** (accepts `.md` or `.html`): **API method** (accepts `.md` or `.html`):
@@ -381,18 +181,13 @@ WECHAT_APP_SECRET=<user_input>
${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme <theme> [--color <color>] [--title <title>] [--summary <summary>] [--author <author>] [--cover <cover_path>] [--no-cite] ${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme <theme> [--color <color>] [--title <title>] [--summary <summary>] [--author <author>] [--cover <cover_path>] [--no-cite]
``` ```
**CRITICAL**: Always include `--theme` parameter. Never omit it, even if using `default`. Only include `--color` if explicitly set by user or EXTEND.md. Always pass `--theme` even if it's `default`. Only pass `--color` when explicitly set by the user or EXTEND.md.
**`draft/add` payload rules**: **`draft/add` payload rules**:
- Use endpoint: `POST https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN` - Endpoint: `POST https://api.weixin.qq.com/cgi-bin/draft/add?access_token=ACCESS_TOKEN`
- `article_type`: `news` (default) or `newspic` - `article_type`: `news` (default) or `newspic`
- For `news`, include `thumb_media_id` (cover is required) - For `news`, include `thumb_media_id` (cover required)
- Always resolve and send: - Always include `need_open_comment` (default `1`) and `only_fans_can_comment` (default `0`) in the request body, even if the CLI doesn't expose them
- `need_open_comment` (default `1`)
- `only_fans_can_comment` (default `0`)
- `author` resolution: CLI `--author` → frontmatter `author` → EXTEND.md `default_author`
If script parameters do not expose the two comment fields, still ensure final API request body includes resolved values.
**Browser method** (accepts `--markdown` or `--html`): **Browser method** (accepts `--markdown` or `--html`):
@@ -403,66 +198,35 @@ ${BUN_X} {baseDir}/scripts/wechat-article.ts --html <html_file>
### Step 5: Completion Report ### Step 5: Completion Report
**For API method**, include draft management link:
``` ```
WeChat Publishing Complete! WeChat Publishing Complete!
Input: [type] - [path] Input: [type] - [path]
Method: API Method: [API | Browser]
Theme: [theme name] [color if set] Theme: [theme] [color if set]
Article: Article:
• Title: [title] • Title: [title]
• Summary: [summary] • Summary: [summary]
• Images: [N] inline images • Images: [N] inline
• Comments: [open/closed], [fans-only/all users] • Comments: [open/closed], [fans-only/all] ← API method only
Result: Result:
✓ Draft saved to WeChat Official Account ✓ Draft saved to WeChat Official Account
• media_id: [media_id] • media_id: [media_id] ← API method only
Next Steps: Next Steps (API):
→ Manage drafts: https://mp.weixin.qq.com (登录后进入「内容管理」→「草稿箱」) → Manage drafts: https://mp.weixin.qq.com (登录后进入「内容管理」→「草稿箱」)
Files created: Files created:
[• post-to-wechat/yyyy-MM-dd/slug.md (if plain text)] [• post-to-wechat/YYYY-MM-DD/slug.md (if plain text input)]
[• slug.html (converted)] [• slug.html (converted)]
``` ```
**For Browser method**:
```
WeChat Publishing Complete!
Input: [type] - [path]
Method: Browser
Theme: [theme name] [color if set]
Article:
• Title: [title]
• Summary: [summary]
• Images: [N] inline images
Result:
✓ Draft saved to WeChat Official Account
Files created:
[• post-to-wechat/yyyy-MM-dd/slug.md (if plain text)]
[• slug.html (converted)]
```
## Detailed References
| Topic | Reference |
|-------|-----------|
| Image-text parameters, auto-compression | [references/image-text-posting.md](references/image-text-posting.md) |
| Article themes, image handling | [references/article-posting.md](references/article-posting.md) |
## Feature Comparison ## Feature Comparison
| Feature | Image-Text | Article (API) | Article (Browser) | | Feature | Image-Text | Article (API) | Article (Browser) |
|---------|------------|---------------|-------------------| |---------|:---:|:---:|:---:|
| Plain text input | ✗ | ✓ | ✓ | | Plain text input | ✗ | ✓ | ✓ |
| HTML input | ✗ | ✓ | ✓ | | HTML input | ✗ | ✓ | ✓ |
| Markdown input | Title/content | ✓ | ✓ | | Markdown input | Title/content | ✓ | ✓ |
@@ -470,39 +234,34 @@ Files created:
| Themes | ✗ | ✓ | ✓ | | Themes | ✗ | ✓ | ✓ |
| Auto-generate metadata | ✗ | ✓ | ✓ | | Auto-generate metadata | ✗ | ✓ | ✓ |
| Default cover fallback (`imgs/cover.png`) | ✗ | ✓ | ✗ | | Default cover fallback (`imgs/cover.png`) | ✗ | ✓ | ✗ |
| Comment control (`need_open_comment`, `only_fans_can_comment`) | ✗ | ✓ | ✗ | | Comment control | ✗ | ✓ | ✗ |
| Requires Chrome | ✓ | ✗ | ✓ | | Requires Chrome | ✓ | ✗ | ✓ |
| Requires API credentials | ✗ | ✓ | ✗ | | Requires API credentials | ✗ | ✓ | ✗ |
| Speed | Medium | Fast | Slow | | Speed | Medium | Fast | Slow |
## Prerequisites
**For API method**:
- WeChat Official Account API credentials
- Guided setup in Step 2, or manually set in `.baoyu-skills/.env`
**For Browser method**:
- Google Chrome
- First run: log in to WeChat Official Account (session preserved)
**Config File Locations** (priority order):
1. Environment variables
2. `<cwd>/.baoyu-skills/.env`
3. `~/.baoyu-skills/.env`
## Troubleshooting ## Troubleshooting
| Issue | Solution | | Issue | Fix |
|-------|----------| |-------|-----|
| Missing API credentials | Follow guided setup in Step 2 | | Missing API credentials | Follow guided setup in Step 2 |
| Access token error | Check if API credentials are valid and not expired | | Access token error | Verify credentials valid and not expired |
| Not logged in (browser) | First run opens browser - scan QR to log in | | Not logged in (browser) | First run opens browser scan QR to log in |
| Chrome not found | Set `WECHAT_BROWSER_CHROME_PATH` env var | | Chrome not found | Set `WECHAT_BROWSER_CHROME_PATH` |
| Title/summary missing | Use auto-generation or provide manually | | Title/summary missing | Use auto-generation or provide manually |
| No cover image | Add frontmatter cover or place `imgs/cover.png` in article directory | | No cover image | Add frontmatter cover or place `imgs/cover.png` in article directory |
| Wrong comment defaults | Check `EXTEND.md` keys `need_open_comment` and `only_fans_can_comment` | | Wrong comment defaults | Check `need_open_comment` / `only_fans_can_comment` in EXTEND.md |
| Paste fails | Check system clipboard permissions | | Paste fails | Check system clipboard permissions |
## References
| File | Content |
|------|---------|
| `references/image-text-posting.md` | Image-text parameters, auto-compression |
| `references/article-posting.md` | Article themes, image handling |
| `references/multi-account.md` | Multi-account compatibility, credentials, Chrome profiles, CLI |
| `references/api-setup.md` | Guided credential setup |
| `references/config/first-time-setup.md` | First-time EXTEND.md setup |
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options. Custom configurations via EXTEND.md. See "Preferences" for paths and supported options.
@@ -0,0 +1,41 @@
# API Credential Setup
Guided setup when `WECHAT_APP_ID` / `WECHAT_APP_SECRET` are missing. Invoked by Step 2 of the article-posting workflow.
## Detection
Look for the credentials in this order:
1. Env vars `WECHAT_APP_ID` / `WECHAT_APP_SECRET`
2. `<cwd>/.baoyu-skills/.env` with `WECHAT_APP_ID=...`
3. `$HOME/.baoyu-skills/.env` with `WECHAT_APP_ID=...`
If none are present, run the guided setup below.
## Guided Setup
Show this message to the user and ask where to save:
```
WeChat API credentials not found.
To obtain credentials:
1. Visit https://mp.weixin.qq.com
2. Go to: 开发 → 基本配置
3. Copy AppID and AppSecret
Where to save?
A) Project-level: .baoyu-skills/.env (this project only)
B) User-level: ~/.baoyu-skills/.env (all projects)
```
After they choose a location, collect the values (prefer a user-input tool, fall back to a numbered prompt per the User Input Tools rule in SKILL.md) and append:
```
WECHAT_APP_ID=<user_input>
WECHAT_APP_SECRET=<user_input>
```
## Multi-Account Variant
If the user has multiple accounts configured (`accounts:` block in EXTEND.md), use prefixed keys instead — see `multi-account.md` → "Credential Resolution".
@@ -0,0 +1,101 @@
# Multi-Account Support
Details for managing multiple WeChat Official Accounts through one EXTEND.md. SKILL.md only covers single-account flow and the selection prompt — read this file when the user has an `accounts:` block, asks to publish to a specific account, or needs per-account credentials.
## Compatibility
| Condition | Mode | Behavior |
|-----------|------|----------|
| No `accounts` block | Single-account | Original behavior, no changes |
| `accounts` with 1 entry | Single-account | Auto-select, no prompt |
| `accounts` with 2+ entries | Multi-account | Prompt to select before publishing |
| `accounts` with `default: true` | Multi-account | Pre-select default; user can switch |
## EXTEND.md Example
```md
default_theme: default
default_color: blue
accounts:
- name: 宝玉的技术分享
alias: baoyu
default: true
default_publish_method: api
default_author: 宝玉
need_open_comment: 1
only_fans_can_comment: 0
app_id: your_wechat_app_id
app_secret: your_wechat_app_secret
- name: AI工具集
alias: ai-tools
default_publish_method: browser
default_author: AI工具集
need_open_comment: 1
only_fans_can_comment: 0
```
## Per-Account vs Global Keys
**Per-account** (also accepted globally as fallback): `default_publish_method`, `default_author`, `need_open_comment`, `only_fans_can_comment`, `app_id`, `app_secret`, `chrome_profile_path`.
**Global-only** (always shared): `default_theme`, `default_color`.
## Account Selection (Step 0.5)
Insert between Step 0 (Load EXTEND.md) and Step 1 (Determine input type):
```
if no accounts block:
→ single-account mode (original behavior)
elif accounts.length == 1:
→ auto-select the only account
elif --account <alias> CLI arg:
→ select matching account
elif one account has default: true:
→ pre-select, display: "Using account: <name> (--account to switch)"
else:
→ prompt user to choose from the list
```
## Credential Resolution (API Method)
For the selected account with alias `{alias}`, try in this order (first hit wins):
1. `app_id` / `app_secret` inline in the EXTEND.md account block
2. Env vars `WECHAT_{ALIAS}_APP_ID` / `WECHAT_{ALIAS}_APP_SECRET` (alias uppercased, hyphens → underscores)
3. `.baoyu-skills/.env` with the prefixed key `WECHAT_{ALIAS}_APP_ID`
4. `~/.baoyu-skills/.env` with the prefixed key
5. Fallback to unprefixed `WECHAT_APP_ID` / `WECHAT_APP_SECRET`
### .env Multi-Account Example
```bash
# Account: baoyu
WECHAT_BAOYU_APP_ID=your_wechat_app_id
WECHAT_BAOYU_APP_SECRET=your_wechat_app_secret
# Account: ai-tools
WECHAT_AI_TOOLS_APP_ID=your_ai_tools_wechat_app_id
WECHAT_AI_TOOLS_APP_SECRET=your_ai_tools_wechat_app_secret
```
## Chrome Profile (Browser Method)
Each account uses an isolated Chrome profile so logins don't collide.
| Source | Path |
|--------|------|
| Account `chrome_profile_path` in EXTEND.md | Use as-is |
| Auto-generated from alias | `{shared_profile_parent}/wechat-{alias}/` |
| Single-account fallback | Shared default profile |
## CLI `--account` Flag
All publishing scripts accept `--account <alias>`:
```bash
${BUN_X} {baseDir}/scripts/wechat-api.ts <file> --theme default --account ai-tools
${BUN_X} {baseDir}/scripts/wechat-article.ts --markdown <file> --theme default --account baoyu
${BUN_X} {baseDir}/scripts/wechat-browser.ts --markdown <file> --images ./photos/ --account baoyu
```
+8 -31
View File
@@ -35,40 +35,17 @@ Posts text, images, videos, and long-form articles to Weibo via real Chrome brow
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-post-to-weibo/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-post-to-weibo/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-post-to-weibo/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-post-to-weibo/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-post-to-weibo/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-post-to-weibo/EXTEND.md` | User home |
```
```powershell If none found, use defaults.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-post-to-weibo/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-post-to-weibo/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-post-to-weibo/EXTEND.md") { "user" }
```
┌──────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: Default Chrome profile
│ Path │ Location │
├──────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-post-to-weibo/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-post-to-weibo/EXTEND.md│ User home │
└──────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default Chrome profile
## Prerequisites ## Prerequisites
+8 -31
View File
@@ -39,40 +39,17 @@ Posts text, images, videos, and long-form articles to X via real Chrome browser
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-post-to-x/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-post-to-x/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-post-to-x/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-post-to-x/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-post-to-x/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-post-to-x/EXTEND.md` | User home |
```
```powershell If none found, use defaults.
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-post-to-x/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-post-to-x/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-post-to-x/EXTEND.md") { "user" }
```
┌──────────────────────────────────────────────────┬───────────────────┐ **EXTEND.md supports**: Default Chrome profile
│ Path │ Location │
├──────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-post-to-x/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-post-to-x/EXTEND.md │ User home │
└──────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default Chrome profile
## Prerequisites ## Prerequisites
+152 -547
View File
@@ -13,26 +13,35 @@ metadata:
# Slide Deck Generator # Slide Deck Generator
Transform content into professional slide deck images. Transform content into professional slide deck images. The deck is designed for **reading and sharing** (self-explanatory slides, logical scroll flow, social-media-friendly) rather than live presentation — that assumption drives every layout and density decision below.
## Usage ## User Input Tools
```bash When this skill prompts the user, follow this tool-selection rule (priority order):
/baoyu-slide-deck path/to/content.md
/baoyu-slide-deck path/to/content.md --style sketch-notes 1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
/baoyu-slide-deck path/to/content.md --audience executives 2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
/baoyu-slide-deck path/to/content.md --lang zh 3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
/baoyu-slide-deck path/to/content.md --slides 10
/baoyu-slide-deck path/to/content.md --outline-only Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
/baoyu-slide-deck # Then paste content
``` ## Image Generation Tools
When this skill needs to render an image:
- **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.
**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.
## 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.
## Script Directory ## Script Directory
**Agent Execution Instructions**: `{baseDir}` = this SKILL.md's directory. Resolve `${BUN_X}`: prefer `bun`; else `npx -y bun`; else suggest `brew install oven-sh/bun/bun`.
1. Determine this SKILL.md file's directory path as `{baseDir}`
2. Script path = `{baseDir}/scripts/<script-name>.ts`
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
| Script | Purpose | | Script | Purpose |
|--------|---------| |--------|---------|
@@ -43,26 +52,21 @@ Transform content into professional slide deck images.
| Option | Description | | Option | Description |
|--------|-------------| |--------|-------------|
| `--style <name>` | Visual style: preset name, `custom`, or custom style name | | `--style <name>` | Preset (see Presets below), `custom`, or custom style name |
| `--audience <type>` | Target: beginners, intermediate, experts, executives, general | | `--audience <type>` | beginners / intermediate / experts / executives / general |
| `--lang <code>` | Output language (en, zh, ja, etc.) | | `--lang <code>` | Output language (en, zh, ja, ...) |
| `--slides <number>` | Target slide count (8-25 recommended, max 30) | | `--slides <N>` | Target slide count (8-25 recommended, max 30) |
| `--outline-only` | Generate outline only, skip image generation | | `--ref <files...>` | Reference images applied per slide (style / palette / composition / subject) |
| `--prompts-only` | Generate outline + prompts, skip images | | `--outline-only` | Stop after outline |
| `--images-only` | Generate images from existing prompts directory | | `--prompts-only` | Stop after prompts (skip image generation) |
| `--regenerate <N>` | Regenerate specific slide(s): `--regenerate 3` or `--regenerate 2,5,8` | | `--images-only` | Skip to Step 7; requires existing `prompts/` |
| `--regenerate <N>` | Regenerate specific slide(s): `3` or `2,5,8` |
**Slide Count by Content Length**:
| Content | Slides |
|---------|--------|
| < 1000 words | 5-10 |
| 1000-3000 words | 10-18 |
| 3000-5000 words | 15-25 |
| > 5000 words | 20-30 (consider splitting) |
## Style System ## Style System
### Presets 17 presets covering technical / educational / lifestyle / editorial use cases. Every preset is a combination of four dimensions (texture / mood / typography / density). If the user picks "Custom dimensions" in Round 1, Round 2 of the confirmation asks one question per dimension — options and verbatim copy live in `references/confirmation.md`.
### Presets (17)
| Preset | Dimensions | Best For | | Preset | Dimensions | Best For |
|--------|------------|----------| |--------|------------|----------|
@@ -84,21 +88,25 @@ Transform content into professional slide deck images.
| `vector-illustration` | clean + vibrant + humanist + balanced | Creative, children's content | | `vector-illustration` | clean + vibrant + humanist + balanced | Creative, children's content |
| `vintage` | paper + warm + editorial + balanced | Historical, heritage | | `vintage` | paper + warm + editorial + balanced | Historical, heritage |
### Style Dimensions Per-preset specs: `references/styles/<preset>.md`. Preset → dimension mapping: `references/dimensions/presets.md`.
| Dimension | Options | Description | ### Dimensions (when "Custom dimensions" picked)
|-----------|---------|-------------|
| **Texture** | clean, grid, organic, pixel, paper | Visual texture and background treatment |
| **Mood** | professional, warm, cool, vibrant, dark, neutral, macaron | Color temperature and palette style |
| **Typography** | geometric, humanist, handwritten, editorial, technical | Headline and body text styling |
| **Density** | minimal, balanced, dense | Information density per slide |
Full specs: `references/dimensions/*.md` | Dimension | Options | Purpose |
|-----------|---------|---------|
| **Texture** | clean, grid, organic, pixel, paper | Background treatment |
| **Mood** | professional, warm, cool, vibrant, dark, neutral, macaron | Color temperature |
| **Typography** | geometric, humanist, handwritten, editorial, technical | Headline/body styling |
| **Density** | minimal, balanced, dense | Information per slide |
### Auto Style Selection Full per-dimension specs: `references/dimensions/*.md`.
| Content Signals | Preset | ### Auto-Selection
|-----------------|--------|
Match content signals to a preset. Pick the first row whose signal keywords appear in the source; fall back to `blueprint` if nothing matches.
| Signals in source | Preset |
|-------------------|--------|
| tutorial, learn, education, guide, beginner | `sketch-notes` | | tutorial, learn, education, guide, beginner | `sketch-notes` |
| hand-drawn, infographic, diagram, process, onboarding | `hand-drawn-edu` | | hand-drawn, infographic, diagram, process, onboarding | `hand-drawn-edu` |
| classroom, teaching, school, chalkboard | `chalkboard` | | classroom, teaching, school, chalkboard | `chalkboard` |
@@ -116,71 +124,66 @@ Full specs: `references/dimensions/*.md`
| biology, chemistry, medical, scientific | `scientific` | | biology, chemistry, medical, scientific | `scientific` |
| history, heritage, vintage, expedition | `vintage` | | history, heritage, vintage, expedition | `vintage` |
| lifestyle, wellness, travel, artistic | `watercolor` | | lifestyle, wellness, travel, artistic | `watercolor` |
| Default | `blueprint` |
## Design Philosophy ### Slide Count Heuristic
Decks designed for **reading and sharing**, not live presentation: | Source length | Recommended slides |
- Each slide self-explanatory without verbal commentary |---------------|--------------------|
- Logical flow when scrolling | < 1000 words | 5-10 |
- All necessary context within each slide | 1000-3000 words | 10-18 |
- Optimized for social media sharing | 3000-5000 words | 15-25 |
| > 5000 words | 20-30 (consider splitting) |
See `references/design-guidelines.md` for: ## Reference Images
- Audience-specific principles
- Visual hierarchy
- Content density guidelines
- Color and typography selection
- Font recommendations
See `references/layouts.md` for layout options. Users may supply reference images to guide style, palette, layout, or subject.
## File Management **Intake**: Accept via `--ref <files...>` or when the user provides file paths / pastes images in conversation.
- File path → copy to `{slide-deck-dir}/refs/NN-ref-{slug}.{ext}`
- Pasted image with no path → ask for the path, or extract style traits verbally as a text fallback
### Output Directory **Usage modes** (per reference):
| Usage | Effect |
|-------|--------|
| `direct` | Pass the file to the backend as a reference image for each slide |
| `style` | Extract style traits (line treatment, texture, mood) and append to every slide's prompt body |
| `palette` | Extract hex colors and append to every slide's prompt body |
Record refs in each slide's prompt frontmatter:
```yaml
references:
- ref_id: 01
filename: 01-ref-brand.png
usage: direct
```
At generation time, verify files exist. If `usage: direct` and the backend accepts refs (e.g., `baoyu-imagine --ref`), pass the file on every slide. Otherwise embed extracted `style`/`palette` traits in the prompt text.
## File Layout
``` ```
slide-deck/{topic-slug}/ slide-deck/{topic-slug}/
├── source-{slug}.{ext} ├── source-{slug}.{ext}
├── outline.md ├── outline.md
├── prompts/ ├── prompts/NN-slide-{slug}.md
│ └── 01-slide-cover.md, 02-slide-{slug}.md, ... ├── NN-slide-{slug}.png
├── 01-slide-cover.png, 02-slide-{slug}.png, ...
├── {topic-slug}.pptx ├── {topic-slug}.pptx
└── {topic-slug}.pdf └── {topic-slug}.pdf
``` ```
**Slug**: Extract topic (2-4 words, kebab-case). Example: "Introduction to Machine Learning" → `intro-machine-learning` **Slug**: 2-4 words, kebab-case, extracted from topic. "Introduction to Machine Learning" → `intro-machine-learning`.
**Conflict Handling**: See Step 1.3 for existing content detection and user options. **Backup rule** (applies across steps): if a file about to be written already exists, rename it to `<name>-backup-YYYYMMDD-HHMMSS.<ext>` before writing the new one. This protects user edits and enables rollback.
## Language Handling
**Detection Priority**:
1. `--lang` flag (explicit)
2. EXTEND.md `language` setting
3. User's conversation language (input language)
4. Source content language
**Rule**: ALL responses use user's preferred language:
- Questions and confirmations
- Progress reports
- Error messages
- Completion summaries
Technical terms (style names, file paths, code) remain in English.
## Workflow ## Workflow
Copy this checklist and check off items as you complete them: Copy this checklist and check off items as you complete them:
``` ```
Slide Deck Progress: - [ ] Step 1: Setup & analyze
- [ ] Step 1: Setup & Analyze - [ ] Step 2: Confirmation ⚠️ REQUIRED (Round 1; Round 2 only if "Custom dimensions")
- [ ] 1.1 Load preferences
- [ ] 1.2 Analyze content
- [ ] 1.3 Check existing ⚠️ REQUIRED
- [ ] Step 2: Confirmation ⚠️ REQUIRED (Round 1, optional Round 2)
- [ ] Step 3: Generate outline - [ ] Step 3: Generate outline
- [ ] Step 4: Review outline (conditional) - [ ] Step 4: Review outline (conditional)
- [ ] Step 5: Generate prompts - [ ] Step 5: Generate prompts
@@ -190,529 +193,131 @@ Slide Deck Progress:
- [ ] Step 9: Output summary - [ ] Step 9: Output summary
``` ```
### Flow
```
Input → Preferences → Analyze → [Check Existing?] → Confirm (1-2 rounds) → Outline → [Review Outline?] → Prompts → [Review Prompts?] → Images → Merge → Complete
```
### Step 1: Setup & Analyze ### Step 1: Setup & Analyze
**1.1 Load Preferences (EXTEND.md)** **1.1 Load EXTEND.md** — check these paths in order; first hit wins:
Check EXTEND.md existence (priority order): | Path | Scope |
|------|-------|
| `.baoyu-skills/baoyu-slide-deck/EXTEND.md` | Project |
| `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-slide-deck/EXTEND.md` | XDG |
| `$HOME/.baoyu-skills/baoyu-slide-deck/EXTEND.md` | User home |
```bash If found, read, parse, and print a summary (style / audience / language / review). If not, proceed with defaults — first-time setup is not blocking for this skill. Schema: `references/config/preferences-schema.md`.
# macOS, Linux, WSL, Git Bash
test -f .baoyu-skills/baoyu-slide-deck/EXTEND.md && echo "project"
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-slide-deck/EXTEND.md" && echo "xdg"
test -f "$HOME/.baoyu-skills/baoyu-slide-deck/EXTEND.md" && echo "user"
```
```powershell **1.2 Analyze content** — follow `references/analysis-framework.md`: classify content, detect language, note signals for style selection, estimate slide count from length (see the **Slide Count Heuristic** in Style System above), generate topic slug. Save source as `source.md` (honor backup rule if one exists).
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-slide-deck/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-slide-deck/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-slide-deck/EXTEND.md") { "user" }
```
┌──────────────────────────────────────────────────┬───────────────────┐ **1.3 Check existing output** ⚠️ REQUIRED before Step 2. If `slide-deck/{topic-slug}/` exists, ask how to proceed — four options (regenerate outline / regenerate images / backup and regenerate / exit), verbatim copy in `references/confirmation.md`.
│ Path │ Location │
├──────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-slide-deck/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-slide-deck/EXTEND.md │ User home │
└──────────────────────────────────────────────────┴───────────────────┘
**When EXTEND.md Found** → Read, parse, **output summary to user**: Save findings to `analysis.md`: topic, audience, signals, recommended style and slide count, language detection.
```
📋 Loaded preferences from [full path]
├─ Style: [preset/custom name]
├─ Audience: [audience or "auto-detect"]
├─ Language: [language or "auto-detect"]
└─ Review: [enabled/disabled]
```
**When EXTEND.md Not Found** → First-time setup using AskUserQuestion or proceed with defaults.
**EXTEND.md Supports**: Preferred style | Custom dimensions | Default audience | Language preference | Review preference
Schema: `references/config/preferences-schema.md`
**1.2 Analyze Content**
1. Save source content (if pasted, save as `source.md`)
- **Backup rule**: If `source.md` exists, rename to `source-backup-YYYYMMDD-HHMMSS.md`
2. Follow `references/analysis-framework.md` for content analysis
3. Analyze content signals for style recommendations
4. Detect source language
5. Determine recommended slide count
6. Generate topic slug from content
**1.3 Check Existing Content** ⚠️ REQUIRED
**MUST execute before proceeding to Step 2.**
Use Bash to check if output directory exists:
```bash
test -d "slide-deck/{topic-slug}" && echo "exists"
```
**If directory exists**, use AskUserQuestion:
```
header: "Existing"
question: "Existing content found. How to proceed?"
options:
- label: "Regenerate outline"
description: "Keep images, regenerate outline only"
- label: "Regenerate images"
description: "Keep outline, regenerate images only"
- label: "Backup and regenerate"
description: "Backup to {slug}-backup-{timestamp}, then regenerate all"
- label: "Exit"
description: "Cancel, keep existing content unchanged"
```
**Save to `analysis.md`** with:
- Topic, audience, content signals
- Recommended style (based on Auto Style Selection)
- Recommended slide count
- Language detection
### Step 2: Confirmation ⚠️ REQUIRED ### Step 2: Confirmation ⚠️ REQUIRED
**Two-round confirmation**: Round 1 always, Round 2 only if "Custom dimensions" selected. **Round 1 (always)** — batch five questions in one `AskUserQuestion` call: style, audience, slide count, review-outline?, review-prompts?. Verbatim options in `references/confirmation.md`.
**Language**: Use user's input language or saved language preference. Summary displayed before the questions:
- Content type + topic
- Detected language
- Recommended style (based on signals)
- Recommended slide count (based on length)
**Display summary**: **Round 2 (only if "Custom dimensions" in Round 1)** — batch four questions: texture, mood, typography, density. Verbatim options in `references/confirmation.md`. The four answers replace the preset.
- Content type + topic identified
- Language: [from EXTEND.md or detected]
- **Recommended style**: [preset] (based on content signals)
- **Recommended slides**: [N] (based on content length)
#### Round 1 (Always) **After confirmation**: update `analysis.md` with final choices and store `skip_outline_review` / `skip_prompt_review` flags from Q4/Q5.
**Use AskUserQuestion** for all 5 questions:
**Question 1: Style**
```
header: "Style"
question: "Which visual style for this deck?"
options:
- label: "{recommended_preset} (Recommended)"
description: "Best match based on content analysis"
- label: "{alternative_preset}"
description: "[alternative style description]"
- label: "Custom dimensions"
description: "Choose texture, mood, typography, density separately"
```
**Question 2: Audience**
```
header: "Audience"
question: "Who is the primary reader?"
options:
- label: "General readers (Recommended)"
description: "Broad appeal, accessible content"
- label: "Beginners/learners"
description: "Educational focus, clear explanations"
- label: "Experts/professionals"
description: "Technical depth, domain knowledge"
- label: "Executives"
description: "High-level insights, minimal detail"
```
**Question 3: Slide Count**
```
header: "Slides"
question: "How many slides?"
options:
- label: "{N} slides (Recommended)"
description: "Based on content length"
- label: "Fewer ({N-3} slides)"
description: "More condensed, less detail"
- label: "More ({N+3} slides)"
description: "More detailed breakdown"
```
**Question 4: Review Outline**
```
header: "Outline"
question: "Review outline before generating prompts?"
options:
- label: "Yes, review outline (Recommended)"
description: "Review slide titles and structure"
- label: "No, skip outline review"
description: "Proceed directly to prompt generation"
```
**Question 5: Review Prompts**
```
header: "Prompts"
question: "Review prompts before generating images?"
options:
- label: "Yes, review prompts (Recommended)"
description: "Review image generation prompts"
- label: "No, skip prompt review"
description: "Proceed directly to image generation"
```
#### Round 2 (Only if "Custom dimensions" selected)
**Use AskUserQuestion** for all 4 dimensions:
**Question 1: Texture**
```
header: "Texture"
question: "Which visual texture?"
options:
- label: "clean"
description: "Pure solid color, no texture"
- label: "grid"
description: "Subtle grid overlay, technical"
- label: "organic"
description: "Soft textures, hand-drawn feel"
- label: "pixel"
description: "Chunky pixels, 8-bit aesthetic"
```
(Note: "paper" available via Other)
**Question 2: Mood**
```
header: "Mood"
question: "Which color mood?"
options:
- label: "professional"
description: "Cool-neutral, navy/gold"
- label: "warm"
description: "Earth tones, friendly"
- label: "cool"
description: "Blues, grays, analytical"
- label: "vibrant"
description: "High saturation, bold"
- label: "macaron"
description: "Pastel blocks (blue, mint, lavender, peach) on cream"
```
(Note: "dark", "neutral" available via Other)
**Question 3: Typography**
```
header: "Typography"
question: "Which typography style?"
options:
- label: "geometric"
description: "Modern sans-serif, clean"
- label: "humanist"
description: "Friendly, readable"
- label: "handwritten"
description: "Marker/brush, organic"
- label: "editorial"
description: "Magazine style, dramatic"
```
(Note: "technical" available via Other)
**Question 4: Density**
```
header: "Density"
question: "Information density?"
options:
- label: "balanced (Recommended)"
description: "2-3 key points per slide"
- label: "minimal"
description: "One focus point, maximum whitespace"
- label: "dense"
description: "Multiple data points, compact"
```
**After Round 2**: Store custom dimensions as the style configuration.
**After Confirmation**:
1. Update `analysis.md` with confirmed preferences
2. Store `skip_outline_review` flag from Question 4
3. Store `skip_prompt_review` flag from Question 5
4. → Step 3
### Step 3: Generate Outline ### Step 3: Generate Outline
Create outline using the confirmed style from Step 2. Resolve style: preset → `references/styles/{preset}.md`; custom dimensions → combine files in `references/dimensions/`. Build `STYLE_INSTRUCTIONS` from the resolved style, apply confirmed audience + language + slide count, follow `references/outline-template.md`, and save as `outline.md`.
**Style Resolution**: Stop here if `--outline-only`. Skip Step 4 if `skip_outline_review`.
- If preset selected → Read `references/styles/{preset}.md`
- If custom dimensions → Read dimension files from `references/dimensions/` and combine
**Generate**:
1. Follow `references/outline-template.md` for structure
2. Build STYLE_INSTRUCTIONS from style or dimensions
3. Apply confirmed audience, language, slide count
4. Save as `outline.md`
**After generation**:
- If `--outline-only`, stop here
- If `skip_outline_review` is true → Skip Step 4, go to Step 5
- If `skip_outline_review` is false → Continue to Step 4
### Step 4: Review Outline (Conditional) ### Step 4: Review Outline (Conditional)
**Skip this step** if user selected "No, skip outline review" in Step 2. Display a slide-by-slide table (`# | Title | Type | Layout`) along with total count and resolved style. Ask: proceed / edit outline first / regenerate — verbatim in `references/confirmation.md`.
**Purpose**: Review outline structure before prompt generation. On "Edit outline first", tell the user to edit `outline.md` and ask again when ready. On "Regenerate outline", return to Step 3.
**Language**: Use user's input language or saved language preference.
**Display**:
- Total slides: N
- Style: [preset name or "custom: texture+mood+typography+density"]
- Slide-by-slide summary table:
```
| # | Title | Type | Layout |
|---|-------|------|--------|
| 1 | [title] | Cover | title-hero |
| 2 | [title] | Content | [layout] |
| 3 | [title] | Content | [layout] |
| ... | ... | ... | ... |
```
**Use AskUserQuestion**:
```
header: "Confirm"
question: "Ready to generate prompts?"
options:
- label: "Yes, proceed (Recommended)"
description: "Generate image prompts"
- label: "Edit outline first"
description: "I'll modify outline.md before continuing"
- label: "Regenerate outline"
description: "Create new outline with different approach"
```
**After response**:
1. If "Edit outline first" → Inform user to edit `outline.md`, ask again when ready
2. If "Regenerate outline" → Back to Step 3
3. If "Yes, proceed" → Continue to Step 5
### Step 5: Generate Prompts ### Step 5: Generate Prompts
For each slide in outline:
1. Read `references/base-prompt.md` 1. Read `references/base-prompt.md`
2. For each slide in outline: 2. Extract `STYLE_INSTRUCTIONS` from the outline (don't re-read the style file)
- Extract STYLE_INSTRUCTIONS from outline (not from style file again) 3. Add the slide's content
- Add slide-specific content 4. If a `Layout:` is specified, include guidance from `references/layouts.md`
- If `Layout:` specified, include layout guidance from `references/layouts.md` 5. Save to `prompts/NN-slide-{slug}.md` (backup rule applies)
3. Save to `prompts/` directory
- **Backup rule**: If prompt file exists, rename to `prompts/NN-slide-{slug}-backup-YYYYMMDD-HHMMSS.md`
**After generation**: Stop here if `--prompts-only`. Skip Step 6 if `skip_prompt_review`.
- If `--prompts-only`, stop here and output prompt summary
- If `skip_prompt_review` is true → Skip Step 6, go to Step 7
- If `skip_prompt_review` is false → Continue to Step 6
### Step 6: Review Prompts (Conditional) ### Step 6: Review Prompts (Conditional)
**Skip this step** if user selected "No, skip prompt review" in Step 2. Display the prompts index (`# | Filename | Slide Title`) and ask: proceed / edit prompts first / regenerate — verbatim in `references/confirmation.md`. Branches mirror Step 4.
**Purpose**: Review prompts before image generation.
**Language**: Use user's input language or saved language preference.
**Display**:
- Total prompts: N
- Style: [preset name or custom dimensions]
- Prompt list:
```
| # | Filename | Slide Title |
|---|----------|-------------|
| 1 | 01-slide-cover.md | [title] |
| 2 | 02-slide-xxx.md | [title] |
| ... | ... | ... |
```
- Path to prompts directory: `prompts/`
**Use AskUserQuestion**:
```
header: "Confirm"
question: "Ready to generate slide images?"
options:
- label: "Yes, proceed (Recommended)"
description: "Generate all slide images"
- label: "Edit prompts first"
description: "I'll modify prompts before continuing"
- label: "Regenerate prompts"
description: "Create new prompts with different approach"
```
**After response**:
1. If "Edit prompts first" → Inform user to edit prompts, ask again when ready
2. If "Regenerate prompts" → Back to Step 5
3. If "Yes, proceed" → Continue to Step 7
### Step 7: Generate Images ### Step 7: Generate Images
**For `--images-only`**: Start here with existing prompts. 1. Resolve the image backend via the Image Generation Tools rule at the top — ask once if multiple are installed.
2. Confirm every `prompts/NN-slide-{slug}.md` exists (hard requirement; prompt files are the reproducibility record regardless of backend).
3. Session ID: `slides-{topic-slug}-{timestamp}` — pass to the backend only if it supports sessions.
4. For each slide: generate sequentially, reusing the session ID. Backup rule applies to PNG files. Report progress as `Generated X/N`. Auto-retry once on failure before reporting an error.
**For `--regenerate N`**: Only regenerate specified slide(s). `--regenerate N` jumps to this step for the named slides only. `--images-only` starts here with existing prompts.
**Standard flow**: ### Step 8: Merge
1. Select available image generation skill
2. Generate session ID: `slides-{topic-slug}-{timestamp}`
3. For each slide:
- **Backup rule**: If image file exists, rename to `NN-slide-{slug}-backup-YYYYMMDD-HHMMSS.png`
- Generate image sequentially with same session ID
4. Report progress: "Generated X/N" (in user's language)
5. Auto-retry once on failure before reporting error
### Step 8: Merge to PPTX and PDF
```bash ```bash
${BUN_X} {baseDir}/scripts/merge-to-pptx.ts <slide-deck-dir> ${BUN_X} {baseDir}/scripts/merge-to-pptx.ts <slide-deck-dir>
${BUN_X} {baseDir}/scripts/merge-to-pdf.ts <slide-deck-dir> ${BUN_X} {baseDir}/scripts/merge-to-pdf.ts <slide-deck-dir>
``` ```
### Step 9: Output Summary ### Step 9: Summary
**Language**: Use user's input language or saved language preference.
``` ```
Slide Deck Complete! Slide Deck Complete!
Topic: [topic] Topic: [topic]
Style: [preset name or custom dimensions] Style: [preset or "custom: texture+mood+typography+density"]
Location: [directory path] Location: [directory]
Slides: N total Slides: N
- 01-slide-cover.png - Cover - 01-slide-cover.png
- 02-slide-intro.png - Content
- ... - ...
- {NN}-slide-back-cover.png - Back Cover - NN-slide-back-cover.png
Outline: outline.md Outline: outline.md
PPTX: {topic-slug}.pptx PPTX: {topic-slug}.pptx
PDF: {topic-slug}.pdf PDF: {topic-slug}.pdf
``` ```
## Partial Workflows
| Option | Workflow |
|--------|----------|
| `--outline-only` | Steps 1-3 only (stop after outline) |
| `--prompts-only` | Steps 1-5 (generate prompts, skip images) |
| `--images-only` | Skip to Step 7 (requires existing prompts/) |
| `--regenerate N` | Regenerate specific slide(s) only |
### Using `--prompts-only`
Generate outline and prompts without images:
```bash
/baoyu-slide-deck content.md --prompts-only
```
Output: `outline.md` + `prompts/*.md` ready for review/editing.
### Using `--images-only`
Generate images from existing prompts (starts at Step 7):
```bash
/baoyu-slide-deck slide-deck/topic-slug/ --images-only
```
Prerequisites:
- `prompts/` directory with slide prompt files
- `outline.md` with style information
### Using `--regenerate`
Regenerate specific slides:
```bash
# Single slide
/baoyu-slide-deck slide-deck/topic-slug/ --regenerate 3
# Multiple slides
/baoyu-slide-deck slide-deck/topic-slug/ --regenerate 2,5,8
```
Flow:
1. Read existing prompts for specified slides
2. Regenerate images only for those slides
3. Regenerate PPTX/PDF
## Slide Modification ## Slide Modification
### Quick Reference | Action | How |
|--------|-----|
| Edit | Update `prompts/NN-slide-{slug}.md` **first**, then `--regenerate N` |
| Add | Create new prompt at position, generate image, renumber subsequent `NN` (slugs unchanged), update `outline.md`, re-merge |
| Delete | Remove PNG + prompt, renumber subsequent, update `outline.md`, re-merge |
| Action | Command | Manual Steps | Always update the prompt file before regenerating the image — this keeps the prompts directory as the source of truth and makes changes reproducible. Only `NN` changes on renumber; slugs stay stable so references remain valid.
|--------|---------|--------------|
| **Edit** | `--regenerate N` | **Update prompt file FIRST** → Regenerate image → Regenerate PDF |
| **Add** | Manual | Create prompt → Generate image → Renumber subsequent → Update outline → Regenerate PDF |
| **Delete** | Manual | Remove files → Renumber subsequent → Update outline → Regenerate PDF |
### Edit Single Slide See `references/modification-guide.md` for full details.
1. **Update prompt file FIRST** in `prompts/NN-slide-{slug}.md`
2. Run: `/baoyu-slide-deck <dir> --regenerate N`
3. Or manually regenerate image + PDF
**IMPORTANT**: When updating slides, ALWAYS update the prompt file (`prompts/NN-slide-{slug}.md`) FIRST before regenerating. This ensures changes are documented and reproducible.
### Add New Slide
1. Create prompt at position: `prompts/NN-slide-{new-slug}.md`
2. Generate image using same session ID
3. **Renumber**: Subsequent files NN+1 (slugs unchanged)
4. Update `outline.md`
5. Regenerate PPTX/PDF
### Delete Slide
1. Remove `NN-slide-{slug}.png` and `prompts/NN-slide-{slug}.md`
2. **Renumber**: Subsequent files NN-1 (slugs unchanged)
3. Update `outline.md`
4. Regenerate PPTX/PDF
### File Naming
Format: `NN-slide-[slug].png`
- `NN`: Two-digit sequence (01, 02, ...)
- `slug`: Kebab-case from content (2-5 words, unique)
**Renumbering Rule**: Only NN changes, slugs remain unchanged.
See `references/modification-guide.md` for complete details.
## References ## References
| File | Content | | File | Content |
|------|---------| |------|---------|
| `references/analysis-framework.md` | Content analysis for presentations | | `references/confirmation.md` | Verbatim AskUserQuestion option copy for every confirmation |
| `references/outline-template.md` | Outline structure and format | | `references/analysis-framework.md` | Content analysis framework |
| `references/modification-guide.md` | Edit, add, delete slide workflows | | `references/outline-template.md` | Outline structure |
| `references/content-rules.md` | Content and style guidelines | | `references/base-prompt.md` | Base prompt body for image generation |
| `references/design-guidelines.md` | Audience, typography, colors, visual elements | | `references/layouts.md` | Layout options |
| `references/layouts.md` | Layout options and selection tips | | `references/design-guidelines.md` | Audience, typography, color selection |
| `references/base-prompt.md` | Base prompt for image generation | | `references/content-rules.md` | Content guidelines |
| `references/dimensions/*.md` | Dimension specifications (texture, mood, typography, density) | | `references/modification-guide.md` | Edit/add/delete workflows |
| `references/dimensions/presets.md` | Preset → dimension mapping | | `references/styles/<preset>.md` | Per-preset specifications |
| `references/styles/<style>.md` | Full style specifications (legacy) | | `references/dimensions/*.md` | Per-dimension specifications |
| `references/config/preferences-schema.md` | EXTEND.md structure | | `references/config/preferences-schema.md` | EXTEND.md schema |
## Notes ## Notes
- Image generation: 10-30 seconds per slide - Image generation takes ~10-30s per slide; report progress between them.
- Auto-retry once on generation failure - For sensitive public figures, prefer stylized alternatives to avoid likeness issues.
- Use stylized alternatives for sensitive public figures - Maintain visual consistency via the session ID when the backend supports it.
- Maintain style consistency via session ID
- **Step 2 confirmation required** - do not skip (style, audience, slides, outline review, prompt review)
- **Step 4 conditional** - only if user requested outline review in Step 2
- **Step 6 conditional** - only if user requested prompt review in Step 2
## Extension Support Custom configurations via EXTEND.md. See Step 1.1 for paths and schema.
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
@@ -0,0 +1,193 @@
# Confirmation Questions
Concrete option copy for the confirmation steps. SKILL.md lists which questions to ask — this file gives the verbatim options used in Claude Code. Adapt copy to the runtime's native user-input tool; the intent matters more than the exact wording.
## Round 1 (Always)
Batch all five questions in a single `AskUserQuestion` call.
### Q1: Style
```yaml
header: Style
question: Which visual style for this deck?
options:
- label: "{recommended_preset} (Recommended)"
description: Best match based on content analysis
- label: "{alternative_preset}"
description: "{alternative style description}"
- label: Custom dimensions
description: Choose texture, mood, typography, density separately
```
### Q2: Audience
```yaml
header: Audience
question: Who is the primary reader?
options:
- label: General readers (Recommended)
description: Broad appeal, accessible content
- label: Beginners/learners
description: Educational focus, clear explanations
- label: Experts/professionals
description: Technical depth, domain knowledge
- label: Executives
description: High-level insights, minimal detail
```
### Q3: Slide Count
```yaml
header: Slides
question: How many slides?
options:
- label: "{N} slides (Recommended)"
description: Based on content length
- label: "Fewer ({N-3} slides)"
description: More condensed, less detail
- label: "More ({N+3} slides)"
description: More detailed breakdown
```
### Q4: Review Outline
```yaml
header: Outline
question: Review outline before generating prompts?
options:
- label: Yes, review outline (Recommended)
description: Review slide titles and structure
- label: No, skip outline review
description: Proceed directly to prompt generation
```
### Q5: Review Prompts
```yaml
header: Prompts
question: Review prompts before generating images?
options:
- label: Yes, review prompts (Recommended)
description: Review image generation prompts
- label: No, skip prompt review
description: Proceed directly to image generation
```
## Round 2 — Custom Dimensions
Triggered only when Q1 of Round 1 = "Custom dimensions". Batch all four dimension questions.
### Texture
```yaml
header: Texture
question: Which visual texture?
options:
- label: clean
description: Pure solid color, no texture
- label: grid
description: Subtle grid overlay, technical
- label: organic
description: Soft textures, hand-drawn feel
- label: pixel
description: Chunky pixels, 8-bit aesthetic
```
`paper` is also valid — accept via "Other".
### Mood
```yaml
header: Mood
question: Which color mood?
options:
- label: professional
description: Cool-neutral, navy/gold
- label: warm
description: Earth tones, friendly
- label: cool
description: Blues, grays, analytical
- label: vibrant
description: High saturation, bold
- label: macaron
description: Pastel blocks on cream
```
`dark`, `neutral` valid via "Other".
### Typography
```yaml
header: Typography
question: Which typography style?
options:
- label: geometric
description: Modern sans-serif, clean
- label: humanist
description: Friendly, readable
- label: handwritten
description: Marker/brush, organic
- label: editorial
description: Magazine style, dramatic
```
`technical` valid via "Other".
### Density
```yaml
header: Density
question: Information density?
options:
- label: balanced (Recommended)
description: 2-3 key points per slide
- label: minimal
description: One focus point, maximum whitespace
- label: dense
description: Multiple data points, compact
```
## Outline Review (Step 4)
```yaml
header: Confirm
question: Ready to generate prompts?
options:
- label: Yes, proceed (Recommended)
description: Generate image prompts
- label: Edit outline first
description: I'll modify outline.md before continuing
- label: Regenerate outline
description: Create new outline with different approach
```
## Prompt Review (Step 6)
```yaml
header: Confirm
question: Ready to generate slide images?
options:
- label: Yes, proceed (Recommended)
description: Generate all slide images
- label: Edit prompts first
description: I'll modify prompts before continuing
- label: Regenerate prompts
description: Create new prompts with different approach
```
## Existing Content (Step 1.3)
```yaml
header: Existing
question: Existing content found. How to proceed?
options:
- label: Regenerate outline
description: Keep images, regenerate outline only
- label: Regenerate images
description: Keep outline, regenerate images only
- label: Backup and regenerate
description: Backup to {slug}-backup-{timestamp}, then regenerate all
- label: Exit
description: Cancel, keep existing content unchanged
```
+19 -36
View File
@@ -15,6 +15,16 @@ metadata:
Three-mode translation skill: **quick** for direct translation, **normal** for analysis-informed translation, **refined** for full publication-quality workflow with review and polish. Three-mode translation skill: **quick** for direct translation, **normal** for analysis-informed translation, **refined** for full publication-quality workflow with review and polish.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## Script Directory ## Script Directory
Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values. Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun. Replace `{baseDir}` and `${BUN_X}` with actual values.
@@ -26,36 +36,22 @@ Scripts in `scripts/` subdirectory. `{baseDir}` = this SKILL.md's directory path
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-translate/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-translate/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-translate/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-translate/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-translate/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-translate/EXTEND.md` | User home |
```
```powershell
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-translate/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-translate/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-translate/EXTEND.md") { "user" }
```
| Path | Location |
|------|----------|
| `.baoyu-skills/baoyu-translate/EXTEND.md` | Project directory |
| `$HOME/.baoyu-skills/baoyu-translate/EXTEND.md` | User home |
| Result | Action | | Result | Action |
|--------|--------| |--------|--------|
| Found | Read, parse, apply settings. On first use in session, briefly remind: "Using preferences from [path]. You can edit EXTEND.md to customize glossary, audience, etc." | | Found | Read, parse, apply. On first use in session, briefly remind: "Using preferences from [path]. You can edit EXTEND.md to customize glossary, audience, etc." |
| Not found | **MUST** run first-time setup (see below) — do NOT silently use defaults | | Not found | **MUST** run first-time setup (see below) — do NOT silently use defaults |
**EXTEND.md Supports**: Default target language | Default mode | Target audience | Custom glossaries (inline or file path) | Translation style | Chunk settings **EXTEND.md supports**: default target language, default mode, target audience, custom glossaries (inline or file path), translation style, chunk settings.
Schema: [references/config/extend-schema.md](references/config/extend-schema.md) Schema: [references/config/extend-schema.md](references/config/extend-schema.md).
### First-Time Setup (BLOCKING) ### First-Time Setup (BLOCKING)
@@ -114,19 +110,6 @@ Custom style descriptions are also accepted, e.g., `--style "poetic and lyrical"
If user responds, continue with review → polish steps (same as refined mode Steps 4-6 in refined-workflow.md) on the existing output. If user responds, continue with review → polish steps (same as refined mode Steps 4-6 in refined-workflow.md) on the existing output.
## Usage
```
/translate [--mode quick|normal|refined] [--from <lang>] [--to <lang>] [--audience <audience>] [--style <style>] [--glossary <file>] <source>
```
- `<source>`: File path, URL, or inline text
- `--from`: Source language (auto-detect if omitted)
- `--to`: Target language (from EXTEND.md or default `zh-CN`)
- `--audience`: Target reader profile (from EXTEND.md or default `general`)
- `--style`: Translation style (from EXTEND.md or default `storytelling`)
- `--glossary`: Additional glossary file to merge with EXTEND.md glossary
**Audience presets**: **Audience presets**:
| Value | Description | Effect | | Value | Description | Effect |
+43 -195
View File
@@ -15,9 +15,19 @@ metadata:
Fetches any URL via `baoyu-fetch` CLI (Chrome CDP + site-specific adapters) and converts it to clean markdown. Fetches any URL via `baoyu-fetch` CLI (Chrome CDP + site-specific adapters) and converts it to clean markdown.
## User Input Tools
When this skill prompts the user, follow this tool-selection rule (priority order):
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
## CLI Setup ## CLI Setup
**Important**: The CLI is provided by the npm package dependency `baoyu-fetch`. Do not use a vendored `scripts/vendor/baoyu-fetch` copy. **Important**: The CLI is provided by the npm package dependency `baoyu-fetch`.
**Agent Execution Instructions**: **Agent Execution Instructions**:
1. Determine this SKILL.md file's directory path as `{baseDir}` 1. Determine this SKILL.md file's directory path as `{baseDir}`
@@ -28,92 +38,55 @@ Fetches any URL via `baoyu-fetch` CLI (Chrome CDP + site-specific adapters) and
## Preferences (EXTEND.md) ## Preferences (EXTEND.md)
Check EXTEND.md existence (priority order): Check EXTEND.md in priority order — the first one found wins:
```bash | Priority | Path | Scope |
# macOS, Linux, WSL, Git Bash |----------|------|-------|
test -f .baoyu-skills/baoyu-url-to-markdown/EXTEND.md && echo "project" | 1 | `.baoyu-skills/baoyu-url-to-markdown/EXTEND.md` | Project |
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-url-to-markdown/EXTEND.md" && echo "xdg" | 2 | `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-url-to-markdown/EXTEND.md` | XDG |
test -f "$HOME/.baoyu-skills/baoyu-url-to-markdown/EXTEND.md" && echo "user" | 3 | `$HOME/.baoyu-skills/baoyu-url-to-markdown/EXTEND.md` | User home |
```
```powershell
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-url-to-markdown/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-url-to-markdown/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-url-to-markdown/EXTEND.md") { "user" }
```
| Path | Location |
|------|----------|
| `.baoyu-skills/baoyu-url-to-markdown/EXTEND.md` | Project directory |
| `$HOME/.baoyu-skills/baoyu-url-to-markdown/EXTEND.md` | User home |
| Result | Action | | Result | Action |
|--------|--------| |--------|--------|
| Found | Read, parse, apply settings | | Found | Read, parse, apply settings |
| Not found | **MUST** run first-time setup (see below) — do NOT silently create defaults | | Not found | **MUST** run first-time setup (see below) — do NOT silently create defaults |
**EXTEND.md Supports**: Download media by default | Default output directory **EXTEND.md supports**: download media by default, default output directory.
### First-Time Setup (BLOCKING) ### First-Time Setup BLOCKING
**CRITICAL**: When EXTEND.md is not found, you **MUST use `AskUserQuestion`** to ask the user for their preferences before creating EXTEND.md. **NEVER** create EXTEND.md with defaults without asking. This is a **BLOCKING** operation — do NOT proceed with any conversion until setup is complete. When EXTEND.md is not found, you **MUST** use `AskUserQuestion` to gather preferences before creating EXTEND.md. **NEVER** create EXTEND.md with silent defaults. Generation is BLOCKED until setup completes. Batch all three questions into a single call:
Use `AskUserQuestion` with ALL questions in ONE call: - **Q1 — Media** (header "Media"): "How to handle images and videos in pages?"
- "Ask each time (Recommended)" — Prompt after each save
- "Always download" — Download to local `imgs/` and `videos/`
- "Never download" — Keep remote URLs
- **Q2 — Output** (header "Output"): "Default output directory?"
- "url-to-markdown (Recommended)" — Save to `./url-to-markdown/{domain}/{slug}.md`
- User may pick "Other" and type a custom path
- **Q3 — Save** (header "Save"): "Where to save preferences?"
- "User (Recommended)" — `~/.baoyu-skills/` (all projects)
- "Project" — `.baoyu-skills/` (this project only)
**Question 1** — header: "Media", question: "How to handle images and videos in pages?" After answers, write EXTEND.md, confirm "Preferences saved to [path]", then continue.
- "Ask each time (Recommended)" — After saving markdown, ask whether to download media
- "Always download" — Always download media to local imgs/ and videos/ directories
- "Never download" — Keep original remote URLs in markdown
**Question 2** — header: "Output", question: "Default output directory?" Full template: [references/config/first-time-setup.md](references/config/first-time-setup.md).
- "url-to-markdown (Recommended)" — Save to ./url-to-markdown/{domain}/{slug}.md
- (User may choose "Other" to type a custom path)
**Question 3** — header: "Save", question: "Where to save preferences?"
- "User (Recommended)" — ~/.baoyu-skills/ (all projects)
- "Project" — .baoyu-skills/ (this project only)
After user answers, create EXTEND.md at the chosen location, confirm "Preferences saved to [path]", then continue.
Full reference: [references/config/first-time-setup.md](references/config/first-time-setup.md)
### Supported Keys ### Supported Keys
| Key | Default | Values | Description | | Key | Default | Values | Description |
|-----|---------|--------|-------------| |-----|---------|--------|-------------|
| `download_media` | `ask` | `ask` / `1` / `0` | `ask` = prompt each time, `1` = always download, `0` = never | | `download_media` | `ask` | `ask` / `1` / `0` | `ask` = prompt each time, `1` = always, `0` = never |
| `default_output_dir` | empty | path or empty | Default output directory (empty = `./url-to-markdown/`) | | `default_output_dir` | empty | path or empty | Default output directory (empty = `./url-to-markdown/`) |
**EXTEND.md → CLI mapping**: **EXTEND.md → CLI mapping**:
| EXTEND.md key | CLI argument | Notes | | EXTEND.md key | CLI argument | Notes |
|---------------|-------------|-------| |---------------|-------------|-------|
| `download_media: 1` | `--download-media` | Requires `--output` to be set | | `download_media: 1` | `--download-media` | Requires `--output` to be set |
| `default_output_dir: ./posts/` | Agent constructs `--output ./posts/{domain}/{slug}.md` | Agent generates path, not a direct CLI flag | | `default_output_dir: ./posts/` | Agent constructs `--output ./posts/{domain}/{slug}.md` | Agent generates path, not a direct flag |
**Value priority**: **Value priority**: CLI arguments → EXTEND.md → skill defaults.
1. CLI arguments (`--download-media`, `--output`)
2. EXTEND.md
3. Skill defaults
## Features
- Chrome CDP for full JavaScript rendering via `baoyu-fetch` CLI
- Site-specific adapters: X/Twitter, YouTube, Hacker News, generic (Defuddle)
- Automatic adapter selection based on URL, or force with `--adapter`
- Interaction gate detection: Cloudflare, reCAPTCHA, hCAPTCHA, custom challenges
- Two capture modes: headless (default) or interactive with wait-for-interaction
- Clean markdown output with YAML front matter
- Structured JSON output available via `--format json`
- X/Twitter: extracts tweets, threads, and X Articles with media
- YouTube: transcript/caption extraction, chapters, cover images
- Hacker News: threaded comment parsing with proper nesting
- Generic: Defuddle extraction with Readability fallback
- Download images and videos to local directories
- Chrome profile persistence for authenticated sessions
- Debug artifact output for troubleshooting
## Usage ## Usage
@@ -127,9 +100,6 @@ ${READER} <url> --output article.md
# Save with media download # Save with media download
${READER} <url> --output article.md --download-media ${READER} <url> --output article.md --download-media
# Headless mode (explicit)
${READER} <url> --headless --output article.md
# Wait for interaction (login/CAPTCHA) — auto-detect and continue # Wait for interaction (login/CAPTCHA) — auto-detect and continue
${READER} <url> --wait-for interaction --output article.md ${READER} <url> --wait-for interaction --output article.md
@@ -141,12 +111,6 @@ ${READER} <url> --format json --output article.json
# Force specific adapter # Force specific adapter
${READER} <url> --adapter youtube --output transcript.md ${READER} <url> --adapter youtube --output transcript.md
# Connect to existing Chrome
${READER} <url> --cdp-url http://localhost:9222 --output article.md
# Debug artifacts
${READER} <url> --output article.md --debug-dir ./debug/
``` ```
## Options ## Options
@@ -172,65 +136,15 @@ ${READER} <url> --output article.md --debug-dir ./debug/
| `--chrome-profile-dir <path>` | Chrome user data directory (default: `BAOYU_CHROME_PROFILE_DIR` env or `./baoyu-skills/chrome-profile`) | | `--chrome-profile-dir <path>` | Chrome user data directory (default: `BAOYU_CHROME_PROFILE_DIR` env or `./baoyu-skills/chrome-profile`) |
| `--debug-dir <dir>` | Write debug artifacts (document.json, markdown.md, page.html, network.json) | | `--debug-dir <dir>` | Write debug artifacts (document.json, markdown.md, page.html, network.json) |
## Capture Modes
| Mode | Behavior | Use When |
|------|----------|----------|
| Default | Headless Chrome, auto-extract on network idle | Public pages, static content |
| `--headless` | Explicit headless (same as default) | Clarify intent |
| `--wait-for interaction` | Opens visible Chrome, auto-detects login/CAPTCHA gates, waits for them to clear, then continues | Login-required, CAPTCHA-protected |
| `--wait-for force` | Opens visible Chrome, auto-detects OR accepts Enter keypress to continue | Complex flows, lazy loading, paywalls |
**Interaction gate auto-detection**:
- Cloudflare Turnstile / "just a moment" pages
- Google reCAPTCHA
- hCaptcha
- Custom challenge / verification screens
**Wait-for-interaction workflow**:
1. Run with `--wait-for interaction` → Chrome opens visibly
2. CLI auto-detects login/CAPTCHA gates
3. User completes login or solves CAPTCHA in the browser
4. CLI auto-detects gate cleared → captures page
5. If `--wait-for force` is used, user can also press Enter to trigger capture manually
## Agent Quality Gate ## Agent Quality Gate
**CRITICAL**: The agent must treat default headless capture as provisional. Some sites render differently in headless mode and can silently return low-quality content without causing the CLI to fail. **CRITICAL**: treat default headless capture as provisional. Some sites render differently in headless mode and can silently return low-quality content without failing the CLI.
After every headless run, the agent **MUST** inspect the saved markdown output. After every headless run, inspect the saved markdown. See [references/quality-gate.md](references/quality-gate.md) for the full checklist, recovery workflow, and capture-mode table. Read it whenever a run looks suspicious or the user asks about login/CAPTCHA handling.
### Quality checks the agent must perform
1. Confirm the markdown title matches the target page, not a generic site shell
2. Confirm the body contains the expected article or page content, not just navigation, footer, or a generic error
3. Watch for obvious failure signs:
- `Application error`
- `This page could not be found`
- Login, signup, subscribe, or verification shells
- Extremely short markdown for a page that should be long-form
- Raw framework payloads or mostly boilerplate content
4. If the result is low quality, incomplete, or clearly wrong, do **not** accept the run as successful just because the CLI exited with code 0
**Tip**: Use `--format json` to get structured output including `status`, `login.state`, and `interaction` fields for programmatic quality assessment. A `"status": "needs_interaction"` response means the page requires manual interaction.
### Recovery workflow the agent must follow
1. First run headless (default) unless there is already a clear reason to use interaction mode
2. Review markdown quality immediately after the run
3. If the content is low quality or indicates login/CAPTCHA:
- `--wait-for interaction` for auto-detected gates (login, CAPTCHA, Cloudflare)
- `--wait-for force` when the page needs manual browsing, scroll loading, or complex interaction
4. If `--wait-for` is used, tell the user exactly what to do:
- If login is required, ask them to sign in in the browser
- If CAPTCHA appears, ask them to solve it
- If the page needs time to load, ask them to wait until content is visible
- For `--wait-for force`: tell them to press Enter when ready
5. If JSON output shows `"status": "needs_interaction"`, switch to `--wait-for interaction` automatically
## Output Path Generation ## Output Path Generation
The agent must construct the output file path since `baoyu-fetch` does not auto-generate paths. The agent must construct the output file path `baoyu-fetch` does not auto-generate paths.
**Algorithm**: **Algorithm**:
1. Determine base directory from EXTEND.md `default_output_dir` or default `./url-to-markdown/` 1. Determine base directory from EXTEND.md `default_output_dir` or default `./url-to-markdown/`
@@ -241,56 +155,9 @@ The agent must construct the output file path since `baoyu-fetch` does not auto-
Pass the constructed path to `--output`. Media files (`--download-media`) are saved into subdirectories next to the markdown file, keeping each URL's assets self-contained. Pass the constructed path to `--output`. Media files (`--download-media`) are saved into subdirectories next to the markdown file, keeping each URL's assets self-contained.
## Output Format ## Adapters & Media
Markdown output to stdout (or file with `--output`) as clean markdown text. See [references/adapters.md](references/adapters.md) for the adapter catalog (X, YouTube, Hacker News, generic), per-adapter notes, the media download flow (`ask` / always / never), and the JSON output schema. Read it before answering adapter-specific questions or handling media prompts.
JSON output (`--format json`) returns structured data including:
- `adapter` — which adapter handled the URL
- `status``"ok"` or `"needs_interaction"`
- `login` — login state detection (`logged_in`, `logged_out`, `unknown`)
- `interaction` — interaction gate details (kind, provider, prompt)
- `document` — structured content (url, title, author, publishedAt, content blocks, metadata)
- `media` — collected media assets with url, kind, role
- `markdown` — converted markdown text
- `downloads` — media download results (when `--download-media` used)
When `--download-media` is enabled:
- Images are saved to `imgs/` next to the output file (or in `--media-dir`)
- Videos are saved to `videos/` next to the output file (or in `--media-dir`)
- Markdown media links are rewritten to local relative paths
## Built-in Adapters
| Adapter | URLs | Key Features |
|---------|------|-------------|
| `x` | x.com, twitter.com | Tweets, threads, X Articles, media, login detection |
| `youtube` | youtube.com, youtu.be | Transcript/captions, chapters, cover image, metadata |
| `hn` | news.ycombinator.com | Threaded comments, story metadata, nested replies |
| `generic` | Any URL (fallback) | Defuddle extraction, Readability fallback, auto-scroll, network idle detection |
Adapter is auto-selected based on URL. Use `--adapter <name>` to override.
## Media Download Workflow
Based on `download_media` setting in EXTEND.md:
| Setting | Behavior |
|---------|----------|
| `1` (always) | Run CLI with `--download-media --output <path>` |
| `0` (never) | Run CLI with `--output <path>` (no media download) |
| `ask` (default) | Follow the ask-each-time flow below |
### Ask-Each-Time Flow
1. Run CLI **without** `--download-media` with `--output <path>` → markdown saved
2. Check saved markdown for remote media URLs (`https://` in image/video links)
3. **If no remote media found** → done, no prompt needed
4. **If remote media found** → use `AskUserQuestion`:
- header: "Media", question: "Download N images/videos to local files?"
- "Yes" — Download to local directories
- "No" — Keep remote URLs
5. If user confirms → run CLI **again** with `--download-media --output <same-path>` (overwrites markdown with localized links)
## Environment Variables ## Environment Variables
@@ -298,27 +165,8 @@ Based on `download_media` setting in EXTEND.md:
|----------|-------------| |----------|-------------|
| `BAOYU_CHROME_PROFILE_DIR` | Chrome user data directory (can also use `--chrome-profile-dir`) | | `BAOYU_CHROME_PROFILE_DIR` | Chrome user data directory (can also use `--chrome-profile-dir`) |
**Troubleshooting**: Chrome not found → use `--browser-path`. Timeout → increase `--timeout`. Login/CAPTCHA pages → use `--wait-for interaction`. Debug → use `--debug-dir` to inspect captured HTML and network logs. **Troubleshooting**: Chrome not found → use `--browser-path`. Timeout → increase `--timeout`. Login/CAPTCHA `--wait-for interaction`. Debug → `--debug-dir` to inspect captured HTML and network logs.
### YouTube Notes
- YouTube adapter extracts transcripts/captions automatically when available
- Transcript format: `[MM:SS] Text segment` with chapter headings
- Transcript availability depends on YouTube exposing a caption track. Videos with captions disabled or restricted playback may produce description-only output
- Use `--wait-for force` if the page needs time to finish loading player metadata
### X/Twitter Notes
- Extracts single tweets, threads, and X Articles
- Auto-detects login state; if logged out and content requires auth, JSON output will show `"status": "needs_interaction"`
- Use `--wait-for interaction` for login-protected content
### Hacker News Notes
- Parses threaded comments with proper nesting and reply hierarchy
- Includes story metadata (title, URL, author, score, comment count)
- Shows comment deletion/dead status
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options. Custom configurations via EXTEND.md. See **Preferences** section above for paths and supported keys.
@@ -0,0 +1,77 @@
# Adapters & Media
Read when choosing an adapter, handling media, or answering adapter-specific questions.
## Built-in Adapters
| Adapter | URLs | Key Features |
|---------|------|-------------|
| `x` | x.com, twitter.com | Tweets, threads, X Articles, media, login detection |
| `youtube` | youtube.com, youtu.be | Transcript/captions, chapters, cover image, metadata |
| `hn` | news.ycombinator.com | Threaded comments, story metadata, nested replies |
| `generic` | Any URL (fallback) | Defuddle extraction, Readability fallback, auto-scroll, network idle detection |
Adapter is auto-selected based on URL. Override with `--adapter <name>`.
### YouTube
- Extracts transcripts/captions when available
- Transcript format: `[MM:SS] Text segment` with chapter headings
- Availability depends on YouTube exposing a caption track; videos with captions disabled or restricted playback may produce description-only output
- Use `--wait-for force` if the page needs time to finish loading player metadata
### X/Twitter
- Extracts single tweets, threads, and X Articles
- Auto-detects login state; if logged out and content requires auth, JSON output shows `"status": "needs_interaction"`
- Use `--wait-for interaction` for login-protected content
### Hacker News
- Parses threaded comments with proper nesting and reply hierarchy
- Includes story metadata (title, URL, author, score, comment count)
- Shows comment deletion/dead status
## Media Download Workflow
Driven by `download_media` in EXTEND.md:
| Setting | Behavior |
|---------|----------|
| `1` (always) | Run CLI with `--download-media --output <path>` |
| `0` (never) | Run CLI with `--output <path>` (no media download) |
| `ask` (default) | Follow the ask-each-time flow below |
### Ask-Each-Time Flow
1. Run the CLI **without** `--download-media` with `--output <path>` → markdown saved
2. Check the saved markdown for remote media URLs (`https://` in image/video links)
3. **If no remote media found** → done, no prompt needed
4. **If remote media found** → ask via `AskUserQuestion`:
- header: "Media", question: "Download N images/videos to local files?"
- "Yes" — Download to local directories
- "No" — Keep remote URLs
5. If the user confirms → run the CLI **again** with `--download-media --output <same-path>` (overwrites markdown with localized links)
### Media Layout
When `--download-media` is enabled:
- Images → `imgs/` next to the output file (or `--media-dir`)
- Videos → `videos/` next to the output file (or `--media-dir`)
- Markdown media links are rewritten to local relative paths
## Output Format
Markdown to stdout (or file with `--output`).
JSON output (`--format json`) returns structured data:
- `adapter` — which adapter handled the URL
- `status``"ok"` or `"needs_interaction"`
- `login` — login state detection (`logged_in`, `logged_out`, `unknown`)
- `interaction` — interaction gate details (kind, provider, prompt)
- `document` — structured content (url, title, author, publishedAt, content blocks, metadata)
- `media` — collected media assets with url, kind, role
- `markdown` — converted markdown text
- `downloads` — media download results (when `--download-media` used)
@@ -0,0 +1,42 @@
# Quality Gate & Recovery
Headless Chrome can silently return low-quality content — layout shells, login walls, or framework payloads — without the CLI returning a non-zero exit code. Read this after every headless run so you can catch and recover from those cases.
## Checks the Agent Must Run
1. Confirm the markdown title matches the target page, not a generic site shell
2. Confirm the body contains the expected article/page content, not just navigation, footer, or a generic error
3. Watch for obvious failure signs:
- `Application error`
- `This page could not be found`
- Login, signup, subscribe, or verification shells
- Extremely short markdown for a page that should be long-form
- Raw framework payloads or mostly boilerplate content
4. Do NOT accept a run as successful just because the CLI exited `0`
**Tip**: run with `--format json` to get structured signals including `status`, `login.state`, and `interaction`. `"status": "needs_interaction"` means the page requires manual interaction.
## Recovery Workflow
1. Start headless (default) unless there is already a clear reason to use interaction mode
2. Review markdown quality immediately after the run
3. If the content is low quality or indicates login/CAPTCHA:
- `--wait-for interaction` for auto-detected gates (login, CAPTCHA, Cloudflare)
- `--wait-for force` when the page needs manual browsing, scroll loading, or complex interaction
4. If `--wait-for` is used, tell the user exactly what to do:
- Login required → sign in in the browser
- CAPTCHA visible → solve it
- Slow loading → wait until content is visible
- `--wait-for force` → press Enter when ready
5. If JSON output shows `"status": "needs_interaction"`, switch to `--wait-for interaction` automatically
## Capture Modes
| Mode | Behavior | Use When |
|------|----------|----------|
| Default | Headless Chrome, auto-extract on network idle | Public pages, static content |
| `--headless` | Explicit headless (same as default) | Clarify intent |
| `--wait-for interaction` | Opens visible Chrome, auto-detects login/CAPTCHA gates, waits for them to clear, then continues | Login-required, CAPTCHA-protected |
| `--wait-for force` | Opens visible Chrome, auto-detects OR accepts Enter keypress to continue | Complex flows, lazy loading, paywalls |
**Interaction gate auto-detection**: Cloudflare Turnstile / "just a moment" pages, Google reCAPTCHA, hCaptcha, custom challenge / verification screens.
+231 -451
View File
@@ -1,85 +1,67 @@
--- ---
name: baoyu-xhs-images name: baoyu-xhs-images
description: 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. 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.1
metadata: metadata:
openclaw: openclaw:
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-xhs-images homepage: https://github.com/JimLiu/baoyu-skills#baoyu-xhs-images
--- ---
# Xiaohongshu Image Card Series Generator
Break down complex content into eye-catching Xiaohongshu image card series with multiple style options. # Image Card Series Generator
## Usage Break down complex content into eye-catching image card series with multiple style options.
```bash ## User Input Tools
# Auto-select style and layout based on content
/baoyu-xhs-images posts/ai-future/article.md
# Specify style When this skill prompts the user, follow this tool-selection rule (priority order):
/baoyu-xhs-images posts/ai-future/article.md --style notion
# Specify layout 1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
/baoyu-xhs-images posts/ai-future/article.md --layout dense 2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
# Combine style and layout Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
/baoyu-xhs-images posts/ai-future/article.md --style notion --layout list
# Specify palette (override style colors) ## Image Generation Tools
/baoyu-xhs-images posts/ai-future/article.md --style notion --palette macaron
# Use preset (style + layout + optional palette shorthand) When this skill needs to render an image:
/baoyu-xhs-images posts/ai-future/article.md --preset knowledge-card
# Preset with override - **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.
/baoyu-xhs-images posts/ai-future/article.md --preset poster --layout quadrant - **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.
# Preset with palette override **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.
/baoyu-xhs-images posts/ai-future/article.md --preset hand-drawn-edu --palette warm
# Direct content input ## Language
/baoyu-xhs-images
[paste content]
# Direct input with options Respond in the user's language across questions, progress, errors, and completion summary. Keep technical tokens (style names, file paths, code) in English.
/baoyu-xhs-images --style bold --layout comparison
[paste content]
# Non-interactive (for scheduled tasks / automation)
/baoyu-xhs-images posts/ai-future/article.md --yes
/baoyu-xhs-images posts/ai-future/article.md --yes --preset knowledge-card
```
## Options ## Options
| Option | Description | | Option | Description |
|--------|-------------| |--------|-------------|
| `--style <name>` | Visual style (see Style Gallery) | | `--style <name>` | Visual style (see Styles below) |
| `--layout <name>` | Information layout (see Layout Gallery) | | `--layout <name>` | Information layout (see Layouts below) |
| `--palette <name>` | Color palette override (see Palette Gallery) | | `--palette <name>` | Color override: macaron / warm / neon |
| `--preset <name>` | Style + layout + optional palette shorthand (see [Style Presets](references/style-presets.md)) | | `--preset <name>` | Style + layout + optional palette shorthand (see Presets below; per-preset prompt fragments in `references/style-presets.md`) |
| `--yes` | Non-interactive mode: skip all confirmations. Uses EXTEND.md preferences if found, otherwise uses defaults (no watermark, auto style/layout). Auto-confirms recommended plan (Path A). Suitable for scheduled tasks and automation. | | `--ref <files...>` | Reference images applied to image 1 as the series anchor |
| `--yes` | Non-interactive: skip all confirmations, use EXTEND.md or built-in defaults, auto-confirm recommended plan (Path A) |
## Dimensions ## Dimensions
Three independent knobs combine freely:
| Dimension | Controls | Options | | Dimension | Controls | Options |
|-----------|----------|---------| |-----------|----------|---------|
| **Style** | Visual aesthetics: lines, decorations, rendering | cute, fresh, warm, bold, minimal, retro, pop, notion, chalkboard, study-notes, screen-print, sketch-notes | | **Style** | Visual aesthetics (lines, decorations, rendering) | 12 styles (see Styles below) |
| **Layout** | Information structure: density, arrangement | sparse, balanced, dense, list, comparison, flow, mindmap, quadrant | | **Layout** | Information structure (density, arrangement) | 8 layouts (see Layouts below) |
| **Palette** (optional) | Color override: replaces style's default colors | macaron, warm, neon | | **Palette** (optional) | Color override, replaces the style's default colors | macaron / warm / neon (see Palettes below) |
Style × Layout can be freely combined, with optional palette override. Example: `--style notion --layout dense` creates an intellectual-looking knowledge card with high information density. Add `--palette macaron` to swap colors to soft pastels while keeping notion's rendering style. Example: `--style notion --layout dense` makes an intellectual knowledge card; add `--palette macaron` to soften the colors without changing notion's rendering rules. A `--preset` is a shorthand for style + layout (+ optional palette).
Or use presets: `--preset knowledge-card` → style + layout in one flag. See [Style Presets](references/style-presets.md). **Palette behavior**: no `--palette` → style's built-in colors; `--palette <name>` → overrides colors only, rendering rules unchanged. Some styles declare a `default_palette` (e.g., sketch-notes defaults to macaron).
**Palette behavior**: ## Styles (12)
- No `--palette` → style uses its built-in colors (or its `default_palette` if defined)
- `--palette macaron` → overrides any style's colors with macaron palette
- Palette replaces colors only; style rendering rules (line treatment, elements, textures) stay unchanged
- Some styles declare a `default_palette` (e.g., sketch-notes defaults to macaron)
## Style Gallery
| Style | Description | | Style | Description |
|-------|-------------| |-------|-------------|
@@ -96,25 +78,52 @@ Or use presets: `--preset knowledge-card` → style + layout in one flag. See [S
| `screen-print` | Bold poster art, halftone textures, limited colors, symbolic storytelling | | `screen-print` | Bold poster art, halftone textures, limited colors, symbolic storytelling |
| `sketch-notes` | Hand-drawn educational infographic, macaron pastels on warm cream, wobble lines | | `sketch-notes` | Hand-drawn educational infographic, macaron pastels on warm cream, wobble lines |
Detailed style definitions: `references/presets/<style>.md` Per-style specifications: `references/presets/<style>.md`.
## Preset Gallery ## Layouts (8)
Quick-start presets by content scenario. Use `--preset <name>` or recommend during Step 2. | Layout | Description |
|--------|-------------|
| `sparse` (Default) | 1-2 points, maximum impact |
| `balanced` | 3-4 points, standard |
| `dense` | 5-8 points, knowledge-card style |
| `list` | Enumeration / ranking (4-7 items) |
| `comparison` | Side-by-side contrast |
| `flow` | Process / timeline (3-6 steps) |
| `mindmap` | Center-radial (4-8 branches) |
| `quadrant` | Four-quadrant / circular sections |
Layout specs: `references/elements/canvas.md`.
## Palettes (optional override)
Replaces the style's colors while keeping rendering rules (line treatment, textures) intact.
| Palette | Background | Zone Colors | Accent | Feel |
|---------|------------|-------------|--------|------|
| `macaron` | Warm cream #F5F0E8 | Blue #A8D8EA, Lavender #D5C6E0, Mint #B5E5CF, Peach #F8D5C4 | Coral #E8655A | Soft, educational |
| `warm` | Soft peach #FFECD2 | Orange #ED8936, Terracotta #C05621, Golden #F6AD55, Rose #D4A09A | Sienna #A0522D | Earth tones, cozy |
| `neon` | Dark purple #1A1025 | Cyan #00F5FF, Magenta #FF00FF, Green #39FF14, Pink #FF6EC7 | Yellow #FFFF00 | High-energy, futuristic |
Palette specs: `references/palettes/<palette>.md`.
## Presets (style + layout shortcuts)
Quick-start combos, grouped by scenario. Use `--preset <name>` or recommend during Step 2.
**Knowledge & Learning**: **Knowledge & Learning**:
| Preset | Style | Layout | Best For | | Preset | Style | Layout | Best For |
|--------|-------|--------|----------| |--------|-------|--------|----------|
| `knowledge-card` | notion | dense | 干货知识卡、概念科普 | | `knowledge-card` | notion | dense | 干货知识卡、概念科普 |
| `checklist` | notion | list | 清单、排行榜、必备清单 | | `checklist` | notion | list | 清单、排行榜 |
| `concept-map` | notion | mindmap | 概念图、知识脉络 | | `concept-map` | notion | mindmap | 概念图、知识脉络 |
| `swot` | notion | quadrant | SWOT分析、四象限分类 | | `swot` | notion | quadrant | SWOT 分析、四象限 |
| `tutorial` | chalkboard | flow | 教程步骤、操作流程 | | `tutorial` | chalkboard | flow | 教程步骤、操作流程 |
| `classroom` | chalkboard | balanced | 课堂笔记、知识讲解 | | `classroom` | chalkboard | balanced | 课堂笔记、知识讲解 |
| `study-guide` | study-notes | dense | 学习笔记、考试重点 | | `study-guide` | study-notes | dense | 学习笔记、考试重点 |
| `hand-drawn-edu` | sketch-notes | flow | 手绘教程、流程图解 | | `hand-drawn-edu` | sketch-notes | flow | 手绘教程、流程图解 |
| `sketch-card` | sketch-notes | dense | 手绘知识卡、概念科普 | | `sketch-card` | sketch-notes | dense | 手绘知识卡 |
| `sketch-summary` | sketch-notes | balanced | 手绘总结、图文笔记 | | `sketch-summary` | sketch-notes | balanced | 手绘总结、图文笔记 |
**Lifestyle & Sharing**: **Lifestyle & Sharing**:
@@ -132,7 +141,7 @@ Quick-start presets by content scenario. Use `--preset <name>` or recommend duri
| Preset | Style | Layout | Best For | | Preset | Style | Layout | Best For |
|--------|-------|--------|----------| |--------|-------|--------|----------|
| `warning` | bold | list | 避坑指南、重要提醒 | | `warning` | bold | list | 避坑指南、重要提醒 |
| `versus` | bold | comparison | 正反对比、强烈对照 | | `versus` | bold | comparison | 正反对比 |
| `clean-quote` | minimal | sparse | 金句、极简封面 | | `clean-quote` | minimal | sparse | 金句、极简封面 |
| `pro-summary` | minimal | balanced | 专业总结、商务内容 | | `pro-summary` | minimal | balanced | 专业总结、商务内容 |
@@ -141,8 +150,8 @@ Quick-start presets by content scenario. Use `--preset <name>` or recommend duri
| Preset | Style | Layout | Best For | | Preset | Style | Layout | Best For |
|--------|-------|--------|----------| |--------|-------|--------|----------|
| `retro-ranking` | retro | list | 复古排行、经典盘点 | | `retro-ranking` | retro | list | 复古排行、经典盘点 |
| `throwback` | retro | balanced | 怀旧分享、老物件 | | `throwback` | retro | balanced | 怀旧分享 |
| `pop-facts` | pop | list | 趣味冷知识、好玩的事 | | `pop-facts` | pop | list | 趣味冷知识 |
| `hype` | pop | sparse | 炸裂封面、惊叹分享 | | `hype` | pop | sparse | 炸裂封面、惊叹分享 |
**Poster & Editorial**: **Poster & Editorial**:
@@ -153,489 +162,260 @@ Quick-start presets by content scenario. Use `--preset <name>` or recommend duri
| `editorial` | screen-print | balanced | 观点文章、文化评论 | | `editorial` | screen-print | balanced | 观点文章、文化评论 |
| `cinematic` | screen-print | comparison | 电影对比、戏剧张力 | | `cinematic` | screen-print | comparison | 电影对比、戏剧张力 |
Full preset definitions: [references/style-presets.md](references/style-presets.md) Full prompt-fragment definitions: `references/style-presets.md`.
## Layout Gallery ## Auto-Selection
| Layout | Description | Match content signals to the best combo. First row whose keywords appear wins; fall back to `cute-share` if nothing matches.
|--------|-------------|
| `sparse` (Default) | Minimal information, maximum impact (1-2 points) |
| `balanced` | Standard content layout (3-4 points) |
| `dense` | High information density, knowledge card style (5-8 points) |
| `list` | Enumeration and ranking format (4-7 items) |
| `comparison` | Side-by-side contrast layout |
| `flow` | Process and timeline layout (3-6 steps) |
| `mindmap` | Center radial mind map layout (4-8 branches) |
| `quadrant` | Four-quadrant / circular section layout |
Detailed layout definitions: `references/elements/canvas.md` | Signals in source | Style | Layout | Recommended preset |
|-------------------|-------|--------|--------------------|
| beauty, fashion, cute, girl, pink | `cute` | sparse/balanced | `cute-share`, `girly` |
| health, nature, fresh, organic | `fresh` | balanced/flow | `product-review`, `nature-flow` |
| life, story, emotion, warm | `warm` | balanced | `cozy-story` |
| warning, important, must, critical | `bold` | list/comparison | `warning`, `versus` |
| professional, business, elegant | `minimal` | sparse/balanced | `clean-quote`, `pro-summary` |
| classic, vintage, traditional | `retro` | balanced | `throwback`, `retro-ranking` |
| fun, exciting, wow, amazing | `pop` | sparse/list | `hype`, `pop-facts` |
| knowledge, concept, productivity, SaaS | `notion` | dense/list | `knowledge-card`, `checklist` |
| education, tutorial, learning, classroom | `chalkboard` | balanced/dense | `tutorial`, `classroom` |
| notes, handwritten, study guide, realistic | `study-notes` | dense/list/mindmap | `study-guide` |
| movie, poster, opinion, editorial, cinematic | `screen-print` | sparse/comparison | `poster`, `editorial`, `cinematic` |
| hand-drawn, infographic, workflow, 手绘, 图解 | `sketch-notes` | flow/balanced/dense | `hand-drawn-edu`, `sketch-card`, `sketch-summary` |
## Palette Gallery ## Style × Layout Matrix
Optional color override. Replaces style's built-in colors while preserving rendering rules. Compatibility scores (✓✓ highly recommended, ✓ works well, ✗ avoid). Use when the user picks a non-default combo and you want to flag a poor match.
| Palette | Background | Zone Colors | Accent | Feel | | | sparse | balanced | dense | list | comparison | flow | mindmap | quadrant |
|---------|------------|-------------|--------|------| |--------------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| `macaron` | Warm cream #F5F0E8 | Blue #A8D8EA, Lavender #D5C6E0, Mint #B5E5CF, Peach #F8D5C4 | Coral #E8655A | Soft, educational, approachable | | cute | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
| `warm` | Soft Peach #FFECD2 | Orange #ED8936, Terracotta #C05621, Golden #F6AD55, Rose #D4A09A | Sienna #A0522D | Cozy, earth tones, no cool colors | | fresh | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ |
| `neon` | Dark Purple #1A1025 | Cyan #00F5FF, Magenta #FF00FF, Green #39FF14, Pink #FF6EC7 | Yellow #FFFF00 | High-energy, futuristic | | warm | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ |
| bold | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ |
Detailed palette definitions: `references/palettes/<palette>.md` | minimal | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| retro | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
## Auto Selection | pop | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ |
| notion | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ |
| Content Signals | Style | Layout | Recommended Preset | | chalkboard | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
|-----------------|-------|--------|--------------------| | study-notes | ✗ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ |
| Beauty, fashion, cute, girl, pink | `cute` | sparse/balanced | `cute-share`, `girly` | | screen-print | ✓✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ |
| Health, nature, clean, fresh, organic | `fresh` | balanced/flow | `product-review`, `nature-flow` | | sketch-notes | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| Life, story, emotion, feeling, warm | `warm` | balanced | `cozy-story` |
| Warning, important, must, critical | `bold` | list/comparison | `warning`, `versus` |
| Professional, business, elegant, simple | `minimal` | sparse/balanced | `clean-quote`, `pro-summary` |
| Classic, vintage, old, traditional | `retro` | balanced | `throwback`, `retro-ranking` |
| Fun, exciting, wow, amazing | `pop` | sparse/list | `hype`, `pop-facts` |
| Knowledge, concept, productivity, SaaS | `notion` | dense/list | `knowledge-card`, `checklist` |
| Education, tutorial, learning, teaching, classroom | `chalkboard` | balanced/dense | `tutorial`, `classroom` |
| Notes, handwritten, study guide, knowledge, realistic, photo | `study-notes` | dense/list/mindmap | `study-guide` |
| Movie, album, concert, poster, opinion, editorial, dramatic, cinematic | `screen-print` | sparse/comparison | `poster`, `editorial`, `cinematic` |
| Hand-drawn, infographic, diagram, visual summary, 手绘, 图解, workflow, process | `sketch-notes` | flow/balanced/dense | `hand-drawn-edu`, `sketch-card`, `sketch-summary` |
## Outline Strategies ## Outline Strategies
Three differentiated outline strategies for different content goals: Three differentiated approaches — each produces a structurally different outline. The workflow recommends one; Path C generates all three and lets the user choose.
### Strategy A: Story-Driven (故事驱动型) | Strategy | Concept | Best for | Structure |
|----------|---------|----------|-----------|
| **A — Story-Driven** | Personal experience as the thread, emotional resonance first | Reviews, personal shares, transformation | Hook → Problem → Discovery → Experience → Conclusion |
| **B — Information-Dense** | Value-first, efficient information delivery | Tutorials, comparisons, checklists | Core conclusion → Info card → Pros/Cons → Recommendation |
| **C — Visual-First** | Visual impact as core, minimal text | High-aesthetic products, lifestyle, mood content | Hero image → Detail shots → Lifestyle scene → CTA |
| Aspect | Description | ## Reference Images
|--------|-------------|
| **Concept** | Personal experience as main thread, emotional resonance first |
| **Features** | Start from pain point, show before/after change, strong authenticity |
| **Best for** | Reviews, personal shares, transformation stories |
| **Structure** | Hook → Problem → Discovery → Experience → Conclusion |
### Strategy B: Information-Dense (信息密集型) User-supplied refs are **separate from** the internal "image-1 as anchor" chain (Step 3) — they layer on top of it.
| Aspect | Description | **Intake**: via `--ref <files...>` or paths pasted in conversation.
|--------|-------------| - File path → copy to `refs/NN-ref-{slug}.{ext}`
| **Concept** | Value-first, efficient information delivery | - Pasted with no path → ask for the path, or extract style traits as a text fallback
| **Features** | Clear structure, explicit points, professional credibility |
| **Best for** | Tutorials, comparisons, product reviews, checklists |
| **Structure** | Core conclusion → Info card → Pros/Cons → Recommendation |
### Strategy C: Visual-First (视觉优先型) **Usage modes** (per reference):
| Aspect | Description | | Usage | Effect |
|--------|-------------| |-------|--------|
| **Concept** | Visual impact as core, minimal text | | `direct` | Pass the file to the backend (typically on image 1 only, so the anchor propagates through the chain) |
| **Features** | Large images, atmospheric, instant appeal | | `style` | Extract style traits and append to every card's prompt body |
| **Best for** | High-aesthetic products, lifestyle, mood-based content | | `palette` | Extract hex colors and append to every card's prompt body |
| **Structure** | Hero image → Detail shots → Lifestyle scene → CTA |
## File Structure Record refs in each affected card's prompt frontmatter:
Each session creates an independent directory named by content slug: ```yaml
references:
``` - ref_id: 01
xhs-images/{topic-slug}/ filename: 01-ref-brand.png
├── source-{slug}.{ext} # Source files (text, images, etc.) usage: direct
├── analysis.md # Deep analysis + questions asked
├── outline-strategy-a.md # Strategy A: Story-driven
├── outline-strategy-b.md # Strategy B: Information-dense
├── outline-strategy-c.md # Strategy C: Visual-first
├── outline.md # Final selected/merged outline
├── prompts/
│ ├── 01-cover-[slug].md
│ ├── 02-content-[slug].md
│ └── ...
├── 01-cover-[slug].png
├── 02-content-[slug].png
└── NN-ending-[slug].png
``` ```
**Slug Generation**: At generation time: verify files exist. Image 1 with `usage: direct` + backend that accepts refs → pass via the backend's ref parameter (becomes the chain anchor). Images 2+ keep using image-1 as `--ref` per Step 3 — do NOT re-stack user refs on top (avoids conflicting signals). For `style`/`palette`, embed extracted traits in every prompt.
1. Extract main topic from content (2-4 words, kebab-case)
2. Example: "AI工具推荐" → `ai-tools-recommend`
**Conflict Resolution**: ## File Layout
If `xhs-images/{topic-slug}/` already exists:
- Append timestamp: `{topic-slug}-YYYYMMDD-HHMMSS`
- Example: `ai-tools` exists → `ai-tools-20260118-143052`
**Source Files**: ```
Copy all sources with naming `source-{slug}.{ext}`: image-cards/{topic-slug}/
- `source-article.md`, `source-photo.jpg`, etc. ├── source-{slug}.{ext}
- Multiple sources supported: text, images, files from conversation ├── analysis.md
├── outline-strategy-{a,b,c}.md # Path C only
├── outline.md
├── prompts/NN-{type}-{slug}.md
├── NN-{type}-{slug}.png
└── refs/ # only if --ref used
```
**Slug**: 2-4 words, kebab-case. "AI 工具推荐" → `ai-tools-recommend`. On collision, append `-YYYYMMDD-HHMMSS`.
**Backup rule** (applies throughout): before overwriting any file — source, outline, prompt, image — rename the existing one to `<name>-backup-YYYYMMDD-HHMMSS.<ext>`. This protects user edits.
## Workflow ## Workflow
### Progress Checklist
Copy and track progress:
``` ```
XHS Infographic Progress: - [ ] Step 0: Load EXTEND.md ⛔ BLOCKING (interactive only)
- [ ] Step 0: Check preferences (EXTEND.md) ⛔ BLOCKING (--yes: use defaults if not found)
- [ ] Found → load preferences → continue
- [ ] Not found → run first-time setup → MUST complete before Step 1 (--yes: skip setup, use defaults)
- [ ] Step 1: Analyze content → analysis.md - [ ] Step 1: Analyze content → analysis.md
- [ ] Step 2: Smart Confirm ⚠️ REQUIRED (--yes: auto-confirm Path A) - [ ] Step 2: Smart Confirm ⚠️ REQUIRED (Path A / B / C)
- [ ] Path A: Quick confirm → generate recommended outline - [ ] Step 3: Generate images
- [ ] Path B: Customize → adjust then generate outline
- [ ] Path C: Detailed → 3 outlines → second confirm → generate outline
- [ ] Step 3: Generate images (sequential)
- [ ] Step 4: Completion report - [ ] Step 4: Completion report
``` ```
### Flow ### Step 0: Load EXTEND.md ⛔ BLOCKING
``` Check these paths in order; first hit wins:
Input → [--yes?] ─┬─ Yes → [Step 0: Load or defaults] → Analyze → Auto-confirm → Generate → Complete
└─ No → [Step 0: Preferences] ─┬─ Found → Continue
└─ Not found → First-Time Setup ⛔ BLOCKING
└─ Complete setup → Save EXTEND.md → Continue
┌─────────────────────────────────────────────────────────────────────────────────────┘
Analyze → [Smart Confirm] ─┬─ Quick: confirm recommended → outline.md → Generate → Complete
├─ Customize: adjust options → outline.md → Generate → Complete
└─ Detailed: 3 outlines → [Confirm 2] → outline.md → Generate → Complete
```
### Step 0: Load Preferences (EXTEND.md) ⛔ BLOCKING | Path | Scope |
|------|-------|
| `.baoyu-skills/baoyu-image-cards/EXTEND.md` | Project |
| `${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-image-cards/EXTEND.md` | XDG |
| `$HOME/.baoyu-skills/baoyu-image-cards/EXTEND.md` | User home |
**Purpose**: Load user preferences or run first-time setup. - **Found** → read, parse, print a summary (style / layout / watermark / language), continue.
- **Not found + interactive** → run first-time setup (see `references/config/first-time-setup.md`) and save before anything else. Do NOT analyze content or ask style questions until preferences exist — this keeps first-run behavior predictable.
- **Not found + `--yes`** → skip setup, use built-in defaults (no watermark, style/layout auto-selected, language from content). Do not prompt, do not create EXTEND.md.
**`--yes` mode**: If EXTEND.md found → load it. If not found → use built-in defaults (no watermark, style/layout auto-select, language from content). Do NOT run first-time setup, do NOT create EXTEND.md, do NOT ask any questions. Proceed directly to Step 1. **EXTEND.md keys**: watermark, preferred style/layout, custom style definitions, language preference. Schema: `references/config/preferences-schema.md`.
**CRITICAL** (interactive mode only): If EXTEND.md not found, MUST complete first-time setup before ANY other questions or steps. Do NOT proceed to content analysis, do NOT ask about style, do NOT ask about layout — ONLY complete the preferences setup first.
Check EXTEND.md existence (priority order):
```bash
# macOS, Linux, WSL, Git Bash
test -f .baoyu-skills/baoyu-xhs-images/EXTEND.md && echo "project"
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/baoyu-xhs-images/EXTEND.md" && echo "xdg"
test -f "$HOME/.baoyu-skills/baoyu-xhs-images/EXTEND.md" && echo "user"
```
```powershell
# PowerShell (Windows)
if (Test-Path .baoyu-skills/baoyu-xhs-images/EXTEND.md) { "project" }
$xdg = if ($env:XDG_CONFIG_HOME) { $env:XDG_CONFIG_HOME } else { "$HOME/.config" }
if (Test-Path "$xdg/baoyu-skills/baoyu-xhs-images/EXTEND.md") { "xdg" }
if (Test-Path "$HOME/.baoyu-skills/baoyu-xhs-images/EXTEND.md") { "user" }
```
┌────────────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-xhs-images/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-xhs-images/EXTEND.md │ User home │
└────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, display summary → Continue to Step 1 │
├───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Not found │ ⛔ BLOCKING: Run first-time setup ONLY (see below) → Complete and save EXTEND.md → Then Step 1 │
└───────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────┘
**First-Time Setup** (when EXTEND.md not found):
**Language**: Use user's input language or saved language preference.
Use AskUserQuestion with ALL questions in ONE call. See `references/config/first-time-setup.md` for question details.
**EXTEND.md Supports**: Watermark | Preferred style/layout | Custom style definitions | Language preference
Schema: `references/config/preferences-schema.md`
### Step 1: Analyze Content → `analysis.md` ### Step 1: Analyze Content → `analysis.md`
Read source content, save it if needed, and perform deep analysis. 1. Save the source (backup rule applies if `source.md` exists).
2. Run the deep analysis in `references/workflows/analysis-framework.md`: content type, hook potential, audience, engagement signals, visual opportunity map, swipe flow.
3. Detect source language, pick recommended image count (2-10).
4. Auto-recommend strategy + style + layout + palette using the **Auto-Selection** table above.
5. Write everything to `analysis.md`.
**Actions**: ### Step 2: Smart Confirm ⚠️ REQUIRED
1. **Save source content** (if not already a file):
- If user provides a file path: use as-is
- If user pastes content: save to `source.md` in target directory
- **Backup rule**: If `source.md` exists, rename to `source-backup-YYYYMMDD-HHMMSS.md`
2. Read source content
3. **Deep analysis** following `references/workflows/analysis-framework.md`:
- Content type classification (种草/干货/测评/教程/避坑...)
- Hook analysis (爆款标题潜力)
- Target audience identification
- Engagement potential (收藏/分享/评论)
- Visual opportunity mapping
- Swipe flow design
4. Detect source language
5. Determine recommended image count (2-10)
6. **Auto-recommend** best strategy + style + layout + palette based on content signals
7. **Save to `analysis.md`**
### Step 2: Smart Confirm ⚠️ 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.
**Purpose**: Present auto-recommended plan, let user confirm or adjust. **Display summary** before asking:
**`--yes` mode**: Skip this entire step. Use auto-recommended strategy + style + layout + palette from Step 1 analysis (or `--style`/`--layout`/`--palette`/`--preset` if provided). Generate outline directly using Path A logic → save to `outline.md` → proceed to Step 3. No AskUserQuestion calls.
**Interactive mode**: Do NOT skip.
**Auto-Recommendation Logic**:
1. Use Auto Selection table to match content signals → best strategy + style + layout + palette
2. Infer optimal image count from content density
3. Load style's default elements from preset (apply palette override if applicable)
**Display** (analysis summary + recommended plan):
``` ```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 内容分析 📋 内容分析
主题:[topic] | 类型:[content_type] 主题:[topic] | 类型:[content_type]
要点:[key points summary] 要点:[key points]
受众:[target audience] 受众:[audience]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎨 推荐方案(自动匹配) 🎨 推荐方案(自动匹配)
策略:[A/B/C] [strategy name][reason] 策略:[A/B/C] [name][reason]
风格:[style] · 布局:[layout] · 配色:[palette or "默认"] · 预设:[preset] 风格:[style] · 布局:[layout] · 配色:[palette or 默认] · 预设:[preset]
图片:[N]张(封面+[N-2]内容+结尾) 图片:[N]张(封面+[N-2]内容+结尾)
元素:[background] / [decorations] / [emphasis] 元素:[background] / [decorations] / [emphasis]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
``` ```
**Use AskUserQuestion** with single question: Then ask one question — three paths. Verbatim option copy: `references/confirmation.md`.
| Option | Description | **Path A — Quick confirm** (trust auto-recommendation): generate a single outline using the recommended strategy + style → save to `outline.md` → Step 3.
|--------|-------------|
| 1. ✅ 确认,直接生成(推荐) | Trust auto-recommendation, proceed immediately |
| 2. 🎛️ 自定义调整 | Modify strategy/style/layout/count in one step |
| 3. 📋 详细模式 | Generate 3 outlines, then choose (two confirmations) |
#### Path A: Quick Confirm (Option 1) **Path B — Customize**: ask five questions (strategy/style, layout, palette, count, optional notes) with the recommendation pre-filled — blanks keep the recommendation. Generate one outline with the user's choices → `outline.md` → Step 3. See `references/confirmation.md`.
Generate single outline using recommended strategy + style → save to `outline.md` → Step 3. **Path C — Detailed mode**: two sub-confirmations.
#### Path B: Customize (Option 2) - *Step 2a — Content understanding*: ask selling points (multi-select), audience, style preference (authentic / professional / aesthetic / auto), optional context. Update `analysis.md`.
- *Step 2b — Three outline variants*: generate `outline-strategy-a.md`, `outline-strategy-b.md`, `outline-strategy-c.md`. Each MUST have a different structure AND a different recommended style — include `style_reason` in the frontmatter. Page-count heuristic: A ~4-6, B ~3-5, C ~3-4. Template: `references/workflows/outline-template.md`; frontmatter example in `references/confirmation.md`.
**Use AskUserQuestion** with adjustable options (leave blank = keep recommended): - *Step 2c — Selection*: ask three questions (outline A/B/C/Combined, style, visual elements). Save selected/merged outline to `outline.md` → Step 3.
1. **策略风格**: Current: [strategy + style]. Options: A Story-Driven(warm) | B Information-Dense(notion) | C Visual-First(screen-print). Or specify style directly: cute/fresh/warm/bold/minimal/retro/pop/notion/chalkboard/study-notes/screen-print/sketch-notes. Or use preset: knowledge-card / checklist / tutorial / poster / hand-drawn-edu / etc.
2. **布局**: Current: [layout]. Options: sparse | balanced | dense | list | comparison | flow | mindmap | quadrant
3. **配色**: Current: [palette or "默认"]. Options: 默认 | macaron | warm | neon
4. **图片数量**: Current: [N]. Range: 2-10
5. **补充说明**(可选): Selling point emphasis, audience adjustment, custom color preference, etc.
**After response**: Generate single outline with user's choices → save to `outline.md` → Step 3.
#### Path C: Detailed Mode (Option 3)
Full two-confirmation flow for maximum control:
**Step 2a: Content Understanding**
**Use AskUserQuestion** for:
1. Core selling point (multiSelect: true)
2. Target audience
3. Style preference: Authentic sharing / Professional review / Aesthetic mood / Auto
4. Additional context (optional)
**After response**: Update `analysis.md`.
**Step 2b: Generate 3 Outline Variants**
| Strategy | Filename | Outline | Recommended Style |
|----------|----------|---------|-------------------|
| A | `outline-strategy-a.md` | Story-driven: emotional, before/after | warm, cute, fresh |
| B | `outline-strategy-b.md` | Information-dense: structured, factual | notion, minimal, chalkboard |
| C | `outline-strategy-c.md` | Visual-first: atmospheric, minimal text | bold, pop, retro, screen-print |
**Outline format** (YAML front matter + content):
```yaml
---
strategy: a # a, b, or c
name: Story-Driven
style: warm # recommended style for this strategy
palette: ~ # optional palette override (macaron, warm, neon, or ~ for style default)
style_reason: "Warm tones enhance emotional storytelling and personal connection"
elements: # from style preset, can be customized
background: solid-pastel
decorations: [clouds, stars-sparkles]
emphasis: star-burst
typography: highlight
layout: balanced # primary layout
image_count: 5
---
## P1 Cover
**Type**: cover
**Hook**: "入冬后脸不干了🥹终于找到对的面霜"
**Visual**: Product hero shot with cozy winter atmosphere
**Layout**: sparse
## P2 Problem
**Type**: pain-point
**Message**: Previous struggles with dry skin
**Visual**: Before state, relatable scenario
**Layout**: balanced
...
```
**Differentiation requirements**:
- Each strategy MUST have different outline structure AND different recommended style
- Adapt page count: A typically 4-6, B typically 3-5, C typically 3-4
- Include `style_reason` explaining why this style fits the strategy
Reference: `references/workflows/outline-template.md`
**Step 2c: Outline & Style Selection**
**Use AskUserQuestion** with three questions:
**Q1: Outline Strategy**: A / B / C / Combine (specify pages from each)
**Q2: Visual Style**: Use recommended | Select preset | Select style | Custom description
**Q3: Visual Elements**: Use defaults (Recommended) | Adjust background | Adjust decorations | Custom
**After response**: Save selected/merged outline to `outline.md` with confirmed style and elements → Step 3.
### Step 3: Generate Images ### Step 3: Generate Images
With confirmed outline + style + layout: With confirmed outline + style + layout + palette:
**Visual Consistency — Reference Image Chain**: **Visual consistency — image-1 anchor chain**: character / mascot / color rendering drifts between calls unless you anchor them. Generate image 1 (cover) first WITHOUT `--ref`, then pass image 1 as `--ref` to every subsequent image. This is the single most important consistency trick for this skill — don't skip it even if the backend also supports a session ID.
To ensure character/style consistency across all images in a series:
1. **Generate image 1 (cover) FIRST** — without `--ref`
2. **Use image 1 as `--ref` for ALL remaining images** (2, 3, ..., N)
- This anchors the character design, color rendering, and illustration style
- Command pattern: `--ref <path-to-image-01.png>` added to every subsequent generation
This is critical for styles that use recurring characters, mascots, or illustration elements. Image 1 becomes the visual anchor for the entire series. For each image (cover, content, ending):
**For each image (cover + content + ending)**: 1. Write the full prompt to `prompts/NN-{type}-{slug}.md` in the user's preferred language (backup rule applies).
1. Save prompt to `prompts/NN-{type}-[slug].md` (in user's preferred language) 2. Generate:
- **Backup rule**: If prompt file exists, rename to `prompts/NN-{type}-[slug]-backup-YYYYMMDD-HHMMSS.md` - **Image 1**: no `--ref` (establishes the anchor).
2. Generate image: - **Images 2+**: add `--ref <path-to-image-01.png>`.
- **Image 1**: Generate without `--ref` (this establishes the visual anchor) - Backup rule applies to the PNG files.
- **Images 2+**: Generate with `--ref <image-01-path>` for consistency 3. Report progress after each image.
- **Backup rule**: If image file exists, rename to `NN-{type}-[slug]-backup-YYYYMMDD-HHMMSS.png`
3. Report progress after each generation **Watermark** (if enabled in EXTEND.md): append to the generation prompt:
**Watermark Application** (if enabled in preferences):
Add to each image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position]. Include a subtle watermark "[content]" positioned at [position].
The watermark should be legible but not distracting from the main content. The watermark should be legible but not distracting.
``` ```
Reference: `references/config/watermark-guide.md`
**Image Generation Skill Selection**: See `references/config/watermark-guide.md`.
- Check available image generation skills
- If multiple skills available: ask user preference (interactive) or use first available skill (`--yes` mode)
**Session Management**: **Backend selection**: per the Image Generation Tools rule at the top — use whatever is available, ask once if multiple, before any generation. Under `--yes`, use the EXTEND.md preference and fall back to the first available backend. Prompt files MUST exist before invoking any backend.
If image generation skill supports `--sessionId`:
1. Generate unique session ID: `xhs-{topic-slug}-{timestamp}` **Session ID** (if the backend supports `--sessionId`): use `cards-{topic-slug}-{timestamp}` for every image; combined with the ref chain this gives maximum consistency.
2. Use same session ID for all images
3. Combined with reference image chain, ensures maximum visual consistency
### Step 4: Completion Report ### Step 4: Completion Report
``` ```
Xiaohongshu Image Card Series Complete! Image Card Series Complete!
Topic: [topic] Topic: [topic]
Mode: [Quick / Custom / Detailed] Mode: [Quick / Custom / Detailed]
Strategy: [A/B/C/Combined] Strategy: [A/B/C/Combined]
Style: [style name] Style: [name]
Palette: [palette name or "default"] Palette: [name or "default"]
Layout: [layout name or "varies"] Layout: [name or "varies"]
Location: [directory path] Location: [directory]
Images: N total Images: N total
✓ analysis.md ✓ analysis.md
✓ outline.md ✓ outline.md
✓ outline-strategy-a/b/c.md (detailed mode only) ✓ outline-strategy-a/b/c.md (detailed mode only)
Files:
- 01-cover-[slug].png ✓ Cover (sparse) - 01-cover-[slug].png ✓ Cover (sparse)
- 02-content-[slug].png ✓ Content (balanced) - 02-content-[slug].png ✓ Content (balanced)
- 03-content-[slug].png ✓ Content (dense) - ...
- 04-ending-[slug].png ✓ Ending (sparse) - NN-ending-[slug].png ✓ Ending (sparse)
``` ```
## Image Modification
| Action | Steps |
|--------|-------|
| **Edit** | **Update prompt file FIRST** → Regenerate with same session ID |
| **Add** | Specify position → Create prompt → Generate → Renumber subsequent files (NN+1) → Update outline |
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update outline |
**IMPORTANT**: When updating images, ALWAYS update the prompt file (`prompts/NN-{type}-[slug].md`) FIRST before regenerating. This ensures changes are documented and reproducible.
## Content Breakdown Principles ## Content Breakdown Principles
1. **Cover (Image 1)**: Hook + visual impact → `sparse` layout | Position | Purpose | Typical layout |
2. **Content (Middle)**: Core value per image → `balanced`/`dense`/`list`/`comparison`/`flow` |----------|---------|----------------|
3. **Ending (Last)**: CTA / summary → `sparse` or `balanced` | Cover (image 1) | Hook + visual impact | `sparse` |
| Content (middle) | Core value per image | `balanced` / `dense` / `list` / `comparison` / `flow` |
| Ending (last) | CTA / summary | `sparse` or `balanced` |
**Style × Layout Matrix** (✓✓ = highly recommended, ✓ = works well): For the style × layout compatibility matrix, see the **Style × Layout Matrix** above.
| | sparse | balanced | dense | list | comparison | flow | mindmap | quadrant | ## Image Modification
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| cute | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | | Action | How |
| fresh | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ | |--------|-----|
| warm | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | | Edit | Update `prompts/NN-{type}-{slug}.md` **first**, then regenerate with the same session ID |
| bold | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | | Add | Specify position, create prompt, generate, renumber subsequent files `NN+1`, update outline |
| minimal | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Delete | Remove files, renumber subsequent `NN-1`, update outline |
| retro | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓ | ✓ |
| pop | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓ | Always update the prompt file before regenerating — it's the source of truth and makes changes reproducible.
| notion | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓✓ |
| chalkboard | ✓✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| study-notes | ✗ | ✓ | ✓✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓ |
| screen-print | ✓✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✗ | ✓✓ |
| sketch-notes | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ |
## References ## References
Detailed templates in `references/` directory: | File | Content |
|------|---------|
**Elements** (Visual building blocks): | `references/confirmation.md` | Verbatim AskUserQuestion copy for every confirmation path |
- `elements/canvas.md` - Aspect ratios, safe zones, grid layouts | `references/style-presets.md` | Full preset shortcut definitions |
- `elements/image-effects.md` - Cutout, stroke, filters | `references/presets/<style>.md` | Per-style element definitions |
- `elements/typography.md` - Decorated text (花字), tags, text direction | `references/palettes/<name>.md` | Per-palette color definitions |
- `elements/decorations.md` - Emphasis marks, backgrounds, doodles, frames | `references/elements/canvas.md` | Aspect ratios, safe zones, grid layouts |
| `references/elements/image-effects.md` | Cutout, stroke, filters |
**Presets** (Style presets): | `references/elements/typography.md` | Decorated text, tags, text direction |
- `presets/<name>.md` - Element combination definitions (cute, notion, warm...) | `references/elements/decorations.md` | Emphasis marks, backgrounds, doodles, frames |
- `style-presets.md` - Preset shortcuts (style + layout + palette combos) | `references/workflows/analysis-framework.md` | Content analysis framework |
| `references/workflows/outline-template.md` | Outline template with layout guide |
**Palettes** (Color overrides): | `references/workflows/prompt-assembly.md` | Prompt assembly guide |
- `palettes/<name>.md` - Color palette definitions (macaron, warm, neon) | `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow |
**Workflows** (Process guides): | `references/config/watermark-guide.md` | Watermark configuration |
- `workflows/analysis-framework.md` - Content analysis framework
- `workflows/outline-template.md` - Outline template with layout guide
- `workflows/prompt-assembly.md` - Prompt assembly guide
**Config** (Settings):
- `config/preferences-schema.md` - EXTEND.md schema
- `config/first-time-setup.md` - First-time setup flow
- `config/watermark-guide.md` - Watermark configuration
## Notes ## Notes
- Auto-retry once on failure | Cartoon alternatives for sensitive figures - Auto-retry once on generation failure before reporting an error.
- Use confirmed language preference | Maintain style consistency - For sensitive public figures, use stylized cartoon alternatives.
- **Smart Confirm required** (Step 2) - do not skip; detailed mode uses two sub-confirmations - Smart Confirm (Step 2) is required; Detailed mode adds a second confirmation (2a + 2c).
## Extension Support Custom configurations via EXTEND.md. See Step 0 for paths and schema.
Custom configurations via EXTEND.md. See **Step 0** for paths and supported options.
@@ -0,0 +1,156 @@
# Confirmation Questions
Concrete option copy for Step 2 Smart Confirm. SKILL.md states which question to ask and when — this file supplies the verbatim options used in Claude Code. Other runtimes should adapt the wording to their native user-input tool while preserving intent.
## Step 2 — Smart Confirm Entry
Single-question confirmation presented right after the auto-recommended plan.
```yaml
header: Mode
question: How to proceed with the recommended plan?
options:
- label: 1. ✅ 确认,直接生成(推荐)
description: Trust auto-recommendation and proceed immediately
- label: 2. 🎛️ 自定义调整
description: Modify strategy/style/layout/count in one step
- label: 3. 📋 详细模式
description: Generate 3 outline variants, then choose (two confirmations)
```
## Path B — Customize (Option 2)
Batch these five questions. Leaving a field blank keeps the recommended value.
```yaml
header: Style/Strategy
question: "Strategy + style. Current: {strategy} + {style}"
hint: |
Strategies: A Story-Driven (warm) | B Information-Dense (notion) | C Visual-First (screen-print)
Styles: cute / fresh / warm / bold / minimal / retro / pop / notion / chalkboard / study-notes / screen-print / sketch-notes
Presets: knowledge-card / checklist / tutorial / poster / hand-drawn-edu / ...
```
```yaml
header: Layout
question: "Layout. Current: {layout}"
options: [sparse, balanced, dense, list, comparison, flow, mindmap, quadrant]
```
```yaml
header: Palette
question: "Palette. Current: {palette or 默认}"
options: [默认, macaron, warm, neon]
```
```yaml
header: Count
question: "Image count. Current: {N}"
hint: Range 2-10
```
```yaml
header: Notes
question: Optional notes (selling-point emphasis, audience adjustment, color preference)
optional: true
```
## Path C — Detailed Mode
### Step 2a: Content Understanding
Batch these questions.
```yaml
header: SellingPoints
question: Core selling points (pick all that apply)
multiSelect: true
```
```yaml
header: Audience
question: Target audience
```
```yaml
header: Tone
question: Style preference
options:
- label: Authentic sharing
- label: Professional review
- label: Aesthetic mood
- label: Auto
```
```yaml
header: Context
question: Additional context (optional)
optional: true
```
### Step 2c: Outline & Style Selection
Batch these three questions.
```yaml
header: Strategy
question: Which outline strategy?
options:
- label: A — Story-Driven
- label: B — Information-Dense
- label: C — Visual-First
- label: Combine (specify pages from each)
```
```yaml
header: Style
question: Visual style?
options:
- label: Use recommended
- label: Select preset
- label: Select style directly
- label: Custom description
```
```yaml
header: Elements
question: Visual elements?
options:
- label: Use defaults (Recommended)
- label: Adjust background
- label: Adjust decorations
- label: Custom
```
## Outline Variant Frontmatter
Used by Path C when writing the three `outline-strategy-{a,b,c}.md` files. Each variant MUST have a different structure AND a different recommended style — include `style_reason` explaining why the style fits the strategy.
```yaml
---
strategy: a # a | b | c
name: Story-Driven
style: warm # recommended style for this strategy
palette: ~ # optional: macaron | warm | neon | ~ (style default)
style_reason: "Warm tones enhance emotional storytelling and personal connection"
elements:
background: solid-pastel
decorations: [clouds, stars-sparkles]
emphasis: star-burst
typography: highlight
layout: balanced
image_count: 5
---
## P1 Cover
**Type**: cover
**Hook**: "入冬后脸不干了🥹终于找到对的面霜"
**Visual**: Product hero shot with cozy winter atmosphere
**Layout**: sparse
## P2 Problem
**Type**: pain-point
...
```
Page-count heuristic: strategy A typically 4-6 pages, B typically 3-5, C typically 3-4.
@@ -22,7 +22,7 @@ Vibrant neon colors on dark background. High-energy, futuristic, eye-catching.
## Semantic Constraint ## Semantic Constraint
Vibrant neon color palette on dark background. Colors should glow against the dark base. High contrast, futuristic feel. Use neon sparingly — too many glowing elements become chaotic. Let dark background breathe. Vibrant neon color palette on dark background. Colors should glow against the dark base. High contrast, futuristic feel. Use neon sparingly — too many glowing elements become chaotic. Let dark background breathe. Do NOT render color names, hex codes, or role labels as visible text in the image.
## Best Paired With ## Best Paired With
@@ -22,7 +22,7 @@ Warm earth tones on soft peach background. Cozy, inviting, no cool colors.
## Semantic Constraint ## Semantic Constraint
Warm-only color palette, no cool colors (no blue, green, purple). Earth tones throughout. Evokes comfort, warmth, and trust. All colors should feel like autumn sunlight. Warm-only color palette, no cool colors (no blue, green, purple). Earth tones throughout. Evokes comfort, warmth, and trust. All colors should feel like autumn sunlight. Do NOT render color names, hex codes, or role labels as visible text in the image.
## Best Paired With ## Best Paired With