Compare commits

..

16 Commits

Author SHA1 Message Date
Jim Liu 宝玉 e6a9b217e6 chore: release v1.21.4 2026-01-25 16:10:08 -06:00
Jim Liu 宝玉 ec1743592c fix(baoyu-post-to-wechat): fix regressions from Windows compatibility PR
- Fix broken md-to-wechat-fixed.ts/wechat-article-fixed.ts filename
  references that cause runtime crash (files were never renamed)
- Restore frontmatter quote stripping for title/summary values
- Restore --title CLI parameter functionality (was silently ignored)
- Fix summary extraction to properly skip headings, quotes, lists
- Fix argument parsing to reject single-dash args as file paths
- Remove debug console.error logs left from development
2026-01-25 16:09:24 -06:00
Jim Liu 宝玉 fe647a11bb Merge pull request #20 from JadeLiang003/fix/windows-copy-paste-support
Fix: Windows compatibility for baoyu-post-to-wechat
2026-01-25 16:04:01 -06:00
JadeLiang 5a11b49b00 Fix: Windows compatibility for baoyu-post-to-wechat
## Problem
The baoyu-post-to-wechat skill failed on Windows due to:
1. `new URL(import.meta.url).pathname` returns incorrect path format on Windows
2. Copy/paste operations used `xdotool` (Linux tool) which doesn't exist on Windows

## Solution
1. **md-to-wechat.ts**: Use `fileURLToPath()` to correctly resolve file paths on Windows
2. **wechat-article.ts**: Use CDP `Input.dispatchKeyEvent` for copy/paste operations
   - Replaces system-dependent tools (xdotool/osascript)
   - Works consistently across Windows/macOS/Linux
   - More reliable as it operates within Chrome session

## Changes
- Import `fileURLToPath` from 'node:url'
- Replace `new URL(import.meta.url).pathname` with `fileURLToPath(import.meta.url)`
- Replace system tool calls with CDP keyboard events for Ctrl+C and Ctrl+V
- Add platform-specific modifier handling (Cmd for macOS, Ctrl for Windows/Linux)

## Testing
- Tested successfully on Windows 11
- Markdown conversion works correctly
- HTML copy/paste to WeChat editor works correctly

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 15:24:04 +08:00
Jim Liu 宝玉 04692515bb fix: remove opacity of watermark 2026-01-24 17:23:04 -06:00
Jim Liu 宝玉 eb54c2a2e7 chore: release v1.21.3 2026-01-24 17:20:02 -06:00
Jim Liu 宝玉 a39c8eb0bd refactor(baoyu-article-illustrator): extract content to reference files
- Simplify SKILL.md by extracting usage and prompt templates
- Add references/usage.md for command syntax
- Add references/prompt-construction.md for prompt templates
- Add default_output_dir preference option
- Reorganize workflow from 5 to 6 steps with Pre-check phase
2026-01-24 17:19:42 -06:00
Jim Liu 宝玉 715555c2ab chore: release v1.21.2 2026-01-24 13:37:58 -06:00
Jim Liu 宝玉 02b039f2ff feat(baoyu-image-gen): add parallel generation documentation 2026-01-24 13:37:42 -06:00
Jim Liu 宝玉 00306414fe docs(release-skills): add skill grouping and user confirmation workflow 2026-01-24 13:37:38 -06:00
Jim Liu 宝玉 db1179e057 chore: release v1.21.1 2026-01-24 13:25:15 -06:00
Jim Liu 宝玉 7b0c9ca372 chore: release v1.21.0 2026-01-24 03:33:10 -06:00
Jim Liu 宝玉 e7255efdd6 chore: release v1.20.0 2026-01-24 02:30:28 -06:00
Jim Liu 宝玉 b15a95e73d chore: release v1.19.0 2026-01-24 01:24:41 -06:00
Jim Liu 宝玉 07af3c4c21 chore: release v1.18.3 2026-01-23 21:08:11 -06:00
Jim Liu 宝玉 7842e4d188 chore: release v1.18.2 2026-01-23 16:10:04 -06:00
42 changed files with 3187 additions and 2004 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
}, },
"metadata": { "metadata": {
"description": "Skills shared by Baoyu for improving daily work efficiency", "description": "Skills shared by Baoyu for improving daily work efficiency",
"version": "1.18.1" "version": "1.21.4"
}, },
"plugins": [ "plugins": [
{ {
+141 -39
View File
@@ -186,18 +186,61 @@ Japanese (CHANGELOG.ja.md):
- コネクションプールのメモリリークを修正 - コネクションプールのメモリリークを修正
``` ```
### Step 5: Check README Updates ### Step 5: Group Changes by Skill/Module
**IMPORTANT**: Before updating version file, check if README needs updates. Analyze commits since last tag and group by affected skill/module:
1. **Scan for README files**: `README*.md` in project root 1. **Identify changed files** per commit
2. **For each skill/feature changed**, check if README documentation matches: 2. **Group by skill/module**:
- Options/flags documented correctly - `skills/<skill-name>/*` → Group under that skill
- Usage examples reflect current syntax - Root files (CLAUDE.md, etc.) → Group as "project"
- Feature descriptions are accurate - Multiple skills in one commit → Split into multiple groups
3. **If README is outdated**: 3. **For each group**, identify related README updates needed
- Update README.md (and README.zh.md if exists) to reflect changes
- Include in release commit **Example Grouping**:
```
baoyu-cover-image:
- feat: add new style options
- fix: handle transparent backgrounds
→ README updates: options table
baoyu-comic:
- refactor: improve panel layout algorithm
→ No README updates needed
project:
- docs: update CLAUDE.md architecture section
```
### Step 6: Commit Each Skill/Module Separately
For each skill/module group (in order of changes):
1. **Check README updates needed**:
- Scan `README*.md` for mentions of this skill/module
- Verify options/flags documented correctly
- Update usage examples if syntax changed
- Update feature descriptions if behavior changed
2. **Stage and commit**:
```bash
git add skills/<skill-name>/*
git add README.md README.zh.md # If updated for this skill
git commit -m "<type>(<skill-name>): <meaningful description>"
```
3. **Commit message format**:
- Use conventional commit format: `<type>(<scope>): <description>`
- `<type>`: feat, fix, refactor, docs, perf, etc.
- `<scope>`: skill name or "project"
- `<description>`: Clear, meaningful description of changes
**Example Commits**:
```bash
git commit -m "feat(baoyu-cover-image): add watercolor and minimalist styles"
git commit -m "fix(baoyu-comic): improve panel layout for long dialogues"
git commit -m "docs(project): update architecture documentation"
```
**Common README Updates Needed**: **Common README Updates Needed**:
| Change Type | README Section to Check | | Change Type | README Section to Check |
@@ -208,11 +251,13 @@ Japanese (CHANGELOG.ja.md):
| Breaking changes | Migration notes, deprecation warnings | | Breaking changes | Migration notes, deprecation warnings |
| Restructured internals | Architecture section (if exposed to users) | | Restructured internals | Architecture section (if exposed to users) |
### Step 6: Update Version File ### Step 7: Generate Changelog and Update Version
1. Read version file (JSON/TOML/text) 1. **Generate multi-language changelogs** (as described in Step 4)
2. Update version number 2. **Update version file**:
3. Write back (preserve formatting) - Read version file (JSON/TOML/text)
- Update version number
- Write back (preserve formatting)
**Version Paths by File Type**: **Version Paths by File Type**:
@@ -224,25 +269,77 @@ Japanese (CHANGELOG.ja.md):
| marketplace.json | `$.metadata.version` | | marketplace.json | `$.metadata.version` |
| VERSION / version.txt | Direct content | | VERSION / version.txt | Direct content |
### Step 7: Commit and Tag ### Step 8: User Confirmation
```bash Before creating the release commit, ask user to confirm:
git add <all modified files>
git commit -m "chore: release v{VERSION}" **Use AskUserQuestion with two questions**:
git tag v{VERSION}
1. **Version bump** (single select):
- Show recommended version based on Step 3 analysis
- Options: recommended (with label), other semver options
- Example: `1.2.3 → 1.3.0 (Recommended)`, `1.2.3 → 1.2.4`, `1.2.3 → 2.0.0`
2. **Push to remote** (single select):
- Options: "Yes, push after commit", "No, keep local only"
**Example Output Before Confirmation**:
``` ```
Commits created:
1. feat(baoyu-cover-image): add watercolor and minimalist styles
2. fix(baoyu-comic): improve panel layout for long dialogues
3. docs(project): update architecture documentation
Changelog preview (en):
## 1.3.0 - 2026-01-22
### Features
- Add watercolor and minimalist styles to cover-image
### Fixes
- Improve panel layout for long dialogues in comic
Ready to create release commit and tag.
```
### Step 9: Create Release Commit and Tag
After user confirmation:
1. **Stage version and changelog files**:
```bash
git add <version-file>
git add CHANGELOG*.md
```
2. **Create release commit**:
```bash
git commit -m "chore: release v{VERSION}"
```
3. **Create tag**:
```bash
git tag v{VERSION}
```
4. **Push if user confirmed** (Step 8):
```bash
git push origin main
git push origin v{VERSION}
```
**Note**: Do NOT add Co-Authored-By line. This is a release commit, not a code contribution. **Note**: Do NOT add Co-Authored-By line. This is a release commit, not a code contribution.
**Important**: Do NOT push to remote. User will push manually when ready.
**Post-Release Output**: **Post-Release Output**:
``` ```
Release v1.3.0 created locally. Release v1.3.0 created.
To publish: Commits:
git push origin main 1. feat(baoyu-cover-image): add watercolor and minimalist styles
git push origin v1.3.0 2. fix(baoyu-comic): improve panel layout for long dialogues
3. docs(project): update architecture documentation
4. chore: release v1.3.0
Tag: v1.3.0
Status: Pushed to origin # or "Local only - run git push when ready"
``` ```
## Configuration (.releaserc.yml) ## Configuration (.releaserc.yml)
@@ -307,31 +404,36 @@ Project detected:
Last tag: v1.2.3 Last tag: v1.2.3
Proposed version: v1.3.0 Proposed version: v1.3.0
Changes detected: Changes grouped by skill/module:
feat: add user authentication baoyu-cover-image:
feat: support oauth2 login - feat: add watercolor style
fix: memory leak in pool - feat: add minimalist style
→ Commit: feat(baoyu-cover-image): add watercolor and minimalist styles
→ README updates: options table
baoyu-comic:
- fix: panel layout for long dialogues
→ Commit: fix(baoyu-comic): improve panel layout for long dialogues
→ No README updates
Changelog preview (en): Changelog preview (en):
## 1.3.0 - 2026-01-22 ## 1.3.0 - 2026-01-22
### Features ### Features
- Add user authentication module - Add watercolor and minimalist styles to cover-image
- Support OAuth2 login
### Fixes ### Fixes
- Fix memory leak in connection pool - Improve panel layout for long dialogues in comic
Changelog preview (zh): Changelog preview (zh):
## 1.3.0 - 2026-01-22 ## 1.3.0 - 2026-01-22
### 新功能 ### 新功能
- 新增用户认证模块 - 为 cover-image 添加水彩和极简风格
- 支持 OAuth2 登录
### 修复 ### 修复
- 修复连接池内存泄漏问题 - 改进 comic 长对话的面板布局
Files to modify: Commits to create:
- package.json 1. feat(baoyu-cover-image): add watercolor and minimalist styles
- CHANGELOG.md 2. fix(baoyu-comic): improve panel layout for long dialogues
- CHANGELOG.zh.md 3. chore: release v1.3.0
No changes made. Run without --dry-run to execute. No changes made. Run without --dry-run to execute.
``` ```
+3
View File
@@ -149,3 +149,6 @@ xhs-images/
url-to-markdown/ url-to-markdown/
cover-image/ cover-image/
slide-deck/ slide-deck/
infographic/
illustrations/
comic/
+72
View File
@@ -2,6 +2,78 @@
English | [中文](./CHANGELOG.zh.md) English | [中文](./CHANGELOG.zh.md)
## 1.21.4 - 2026-01-25
### Fixes
- `baoyu-post-to-wechat`: adds Windows compatibility—uses `fileURLToPath` for correct path resolution, replaces system-dependent copy/paste tools (osascript/xdotool) with CDP keyboard events for cross-platform support.
- `baoyu-post-to-wechat`: fixes regressions from Windows compatibility PR—corrects broken `-fixed` filename references, restores frontmatter quote stripping, restores `--title` CLI parameter, fixes summary extraction to skip headings/quotes/lists, fixes argument parsing for single-dash flags, removes debug logs.
- `baoyu-article-illustrator`, `baoyu-cover-image`, `baoyu-xhs-images`: removes opacity option from watermark configuration.
## 1.21.3 - 2026-01-24
### Refactor
- `baoyu-article-illustrator`: simplifies SKILL.md by extracting content to reference files—adds `references/usage.md` for command syntax, `references/prompt-construction.md` for prompt templates. Reorganizes workflow from 5 to 6 steps with new Pre-check phase. Adds `default_output_dir` preference option.
## 1.21.2 - 2026-01-24
### Features
- `baoyu-image-gen`: adds parallel generation documentation with recommended 4 concurrent subagents for batch operations.
### Documentation
- `release-skills`: adds skill/module grouping workflow and user confirmation step before release.
## 1.21.1 - 2026-01-24
### Documentation
- `baoyu-comic`: adds character sheet compression step after generation to reduce token usage when used as reference image.
## 1.21.0 - 2026-01-24
### Features
- `baoyu-cover-image`: expands aspect ratio options—adds 4:3, 3:2, 3:4 ratios; changes default from 2.35:1 to 16:9 for better versatility. Aspect ratio is now always confirmed unless explicitly specified via `--aspect` flag.
- `baoyu-image-gen`: refactors Google provider to support both Gemini multimodal and Imagen models with unified API. Adds `--imageSize` parameter support (1K/2K/4K) for Gemini models.
## 1.20.0 - 2026-01-24
### Features
- `baoyu-cover-image`: upgrades from Type × Style two-dimension system to **4-dimension system**—adds `--text` dimension (none, title-only, title-subtitle, text-rich) for text density control and `--mood` dimension (subtle, balanced, bold) for emotional intensity. New `--quick` flag skips confirmation and uses auto-selection.
### Documentation
- `baoyu-cover-image`: adds dimension reference files—`references/dimensions/text.md` (text density levels) and `references/dimensions/mood.md` (mood intensity levels).
- `baoyu-cover-image`: updates base-prompt, first-time-setup, and preferences-schema to support new 4-dimension system with v2 schema.
- `README.md`, `README.zh.md`: updates baoyu-cover-image documentation to reflect new 4-dimension system with `--text`, `--mood`, and `--quick` options.
## 1.19.0 - 2026-01-24
### Features
- `baoyu-comic`: adds partial workflow options—`--storyboard-only`, `--prompts-only`, `--images-only`, and `--regenerate N` for flexible workflow control.
- `baoyu-image-gen`: adds `--imageSize` parameter for Google providers (1K/2K/4K), changes default quality to 2k.
- `baoyu-image-gen`: adds `GEMINI_API_KEY` as alias for `GOOGLE_API_KEY`.
### Refactor
- `baoyu-comic`: extracts detailed workflow to `references/workflow.md`, reduces SKILL.md by ~400 lines while preserving functionality.
- `baoyu-comic`: extracts content signal analysis to `references/auto-selection.md` and partial workflow docs to `references/partial-workflows.md`.
- `baoyu-image-gen`: modularizes code—extracts types to `types.ts`, provider implementations to `providers/google.ts` and `providers/openai.ts`.
### Documentation
- `baoyu-comic`: improves ohmsha preset documentation with explicit default Doraemon character definitions and visual descriptions.
## 1.18.3 - 2026-01-23
### Documentation
- `baoyu-comic`: improves character reference handling with explicit Strategy A/B selection—Strategy A uses `--ref` parameter for skills that support it, Strategy B embeds character descriptions in prompts for skills that don't. Includes concrete code examples for both approaches.
### Fixes
- `baoyu-image-gen`: removes unsupported Gemini models (`gemini-2.0-flash-exp-image-generation`, `gemini-2.5-flash-preview-native-audio-dialog`) from multimodal model list.
## 1.18.2 - 2026-01-23
### Refactor
- Streamline SKILL.md documentation across 7 skills (`baoyu-compress-image`, `baoyu-danger-gemini-web`, `baoyu-danger-x-to-markdown`, `baoyu-image-gen`, `baoyu-post-to-wechat`, `baoyu-post-to-x`, `baoyu-url-to-markdown`) following official best practices—reduces total documentation by ~300 lines while preserving all functionality.
### Documentation
- `CLAUDE.md`: adds official skill authoring best practices link, skill loading rules, description writing guidelines, and progressive disclosure patterns.
## 1.18.1 - 2026-01-23 ## 1.18.1 - 2026-01-23
### Documentation ### Documentation
+72
View File
@@ -2,6 +2,78 @@
[English](./CHANGELOG.md) | 中文 [English](./CHANGELOG.md) | 中文
## 1.21.4 - 2026-01-25
### 修复
- `baoyu-post-to-wechat`:新增 Windows 兼容性——使用 `fileURLToPath` 正确解析路径,将系统依赖的复制粘贴工具(osascript/xdotool)替换为 CDP 键盘事件,实现跨平台支持。
- `baoyu-post-to-wechat`:修复 Windows 兼容性 PR 引入的回退问题——修正错误的 `-fixed` 文件名引用、恢复 frontmatter 引号剥离、恢复 `--title` CLI 参数、修复摘要提取逻辑以正确跳过标题/引用/列表、修复单横线参数解析、移除调试日志。
- `baoyu-article-illustrator``baoyu-cover-image``baoyu-xhs-images`:移除水印配置中的透明度选项。
## 1.21.3 - 2026-01-24
### 重构
- `baoyu-article-illustrator`:简化 SKILL.md,提取内容至引用文件——新增 `references/usage.md` 用于命令语法,`references/prompt-construction.md` 用于提示词模板。工作流从 5 步重组为 6 步,新增 Pre-check 预检阶段。新增 `default_output_dir` 偏好设置选项。
## 1.21.2 - 2026-01-24
### 新功能
- `baoyu-image-gen`:添加并行生成文档,推荐使用 4 个并发 subagent 进行批量操作。
### 文档
- `release-skills`:新增按 skill/module 分组提交流程和发布前用户确认步骤。
## 1.21.1 - 2026-01-24
### 文档
- `baoyu-comic`:在角色参考图生成后添加压缩步骤,减少作为参考图使用时的 token 消耗。
## 1.21.0 - 2026-01-24
### 新功能
- `baoyu-cover-image`:扩展宽高比选项——新增 4:3、3:2、3:4 比例;默认值从 2.35:1 改为 16:9 以提高通用性。现在除非通过 `--aspect` 标志明确指定,否则始终确认宽高比。
- `baoyu-image-gen`:重构 Google provider 以统一支持 Gemini 多模态和 Imagen 模型。为 Gemini 模型新增 `--imageSize` 参数支持(1K/2K/4K)。
## 1.20.0 - 2026-01-24
### 新功能
- `baoyu-cover-image`:从类型 × 风格二维系统升级为**四维系统**——新增 `--text` 维度(none 无文字、title-only 仅标题、title-subtitle 标题+副标题、text-rich 丰富文字)控制文字密度,新增 `--mood` 维度(subtle 低调、balanced 平衡、bold 醒目)控制情感强度。新增 `--quick` 标志跳过确认,直接使用自动选择。
### 文档
- `baoyu-cover-image`:新增维度参考文件——`references/dimensions/text.md`(文字密度级别)和 `references/dimensions/mood.md`(氛围强度级别)。
- `baoyu-cover-image`:更新 base-prompt、first-time-setup 和 preferences-schema 以支持新的四维系统及 v2 配置模式。
- `README.md``README.zh.md`:更新 baoyu-cover-image 文档,反映新的四维系统及 `--text``--mood``--quick` 选项。
## 1.19.0 - 2026-01-24
### 新功能
- `baoyu-comic`:新增部分工作流选项——`--storyboard-only``--prompts-only``--images-only``--regenerate N`,实现灵活的工作流控制。
- `baoyu-image-gen`:新增 `--imageSize` 参数用于 Google 提供商(1K/2K/4K),默认质量改为 2k。
- `baoyu-image-gen`:新增 `GEMINI_API_KEY` 作为 `GOOGLE_API_KEY` 的别名。
### 重构
- `baoyu-comic`:将详细工作流提取至 `references/workflow.md`SKILL.md 减少约 400 行,功能完整保留。
- `baoyu-comic`:将内容信号分析提取至 `references/auto-selection.md`,部分工作流文档提取至 `references/partial-workflows.md`
- `baoyu-image-gen`:代码模块化——类型定义提取至 `types.ts`provider 实现提取至 `providers/google.ts``providers/openai.ts`
### 文档
- `baoyu-comic`:改进 ohmsha 预设文档,明确默认哆啦A梦角色定义和视觉描述。
## 1.18.3 - 2026-01-23
### 文档
- `baoyu-comic`:改进角色参考处理流程,新增明确的 Strategy A/B 选择逻辑——Strategy A 使用 `--ref` 参数(适用于支持该参数的技能),Strategy B 将角色描述嵌入提示词(适用于不支持的技能)。包含两种方法的具体代码示例。
### 修复
- `baoyu-image-gen`:从多模态模型列表中移除不支持的 Gemini 模型(`gemini-2.0-flash-exp-image-generation``gemini-2.5-flash-preview-native-audio-dialog`)。
## 1.18.2 - 2026-01-23
### 重构
- 精简 7 个技能的 SKILL.md 文档(`baoyu-compress-image``baoyu-danger-gemini-web``baoyu-danger-x-to-markdown``baoyu-image-gen``baoyu-post-to-wechat``baoyu-post-to-x``baoyu-url-to-markdown`),遵循官方最佳实践——总文档量减少约 300 行,功能完整保留。
### 文档
- `CLAUDE.md`:新增官方技能编写最佳实践链接、技能加载规则、描述编写指南和渐进式披露模式。
## 1.18.1 - 2026-01-23 ## 1.18.1 - 2026-01-23
### 文档 ### 文档
+127 -12
View File
@@ -78,6 +78,20 @@ npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.m
`.claude-plugin/marketplace.json` defines plugin metadata and skill paths. Version follows semver. `.claude-plugin/marketplace.json` defines plugin metadata and skill paths. Version follows semver.
## Skill Loading Rules
**IMPORTANT**: When working in this project, follow these rules:
| Rule | Description |
|------|-------------|
| **Load project skills first** | MUST load all skills from `skills/` directory in current project. Project skills take priority over system/user-level skills with same name. |
| **Default image generation** | When image generation is needed, use `skills/baoyu-image-gen/SKILL.md` by default (unless user specifies otherwise). |
**Loading Priority** (highest → lowest):
1. Current project `skills/` directory
2. User-level skills (`$HOME/.baoyu-skills/`)
3. System-level skills
## Release Process ## Release Process
**IMPORTANT**: When user requests release/发布/push, ALWAYS use `/release-skills` workflow. **IMPORTANT**: When user requests release/发布/push, ALWAYS use `/release-skills` workflow.
@@ -92,6 +106,22 @@ npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.m
**IMPORTANT**: All skills MUST use `baoyu-` prefix to avoid conflicts when users import this plugin. **IMPORTANT**: All skills MUST use `baoyu-` prefix to avoid conflicts when users import this plugin.
**REQUIRED READING**: Before creating a new skill, read the official [Skill authoring best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices).
### Key Requirements from Official Best Practices
| Requirement | Details |
|-------------|---------|
| **Concise is key** | Claude is smart—only add context it doesn't have. Challenge each token. |
| **name field** | Max 64 chars, lowercase letters/numbers/hyphens only, no "anthropic"/"claude" |
| **description field** | Max 1024 chars, non-empty, MUST be third person, include what + when to use |
| **SKILL.md body** | Keep under 500 lines; use separate files for additional content |
| **Naming convention** | Gerund form preferred (e.g., `processing-pdfs`), but `baoyu-` prefix required here |
| **References** | Keep one level deep from SKILL.md; avoid nested references |
| **No time-sensitive info** | Avoid dates/versions that become outdated |
### Steps
1. Create `skills/baoyu-<name>/SKILL.md` with YAML front matter 1. Create `skills/baoyu-<name>/SKILL.md` with YAML front matter
- Directory name: `baoyu-<name>` - Directory name: `baoyu-<name>`
- SKILL.md `name` field: `baoyu-<name>` - SKILL.md `name` field: `baoyu-<name>`
@@ -119,6 +149,21 @@ npx -y bun skills/baoyu-danger-gemini-web/scripts/main.ts --promptfiles system.m
- `description`: Brief description of the category - `description`: Brief description of the category
- `skills`: Array with the skill path - `skills`: Array with the skill path
### Writing Effective Descriptions
**MUST write in third person** (not "I can help you" or "You can use this"):
```yaml
# Good
description: Generates Xiaohongshu infographic series from content. Use when user asks for "小红书图片", "XHS images", or "RedNote infographics".
# Bad
description: I can help you create Xiaohongshu images
description: You can use this to generate XHS content
```
Include both **what** the skill does and **when** to use it (triggers/keywords).
### Script Directory Template ### Script Directory Template
Every SKILL.md with scripts MUST include this section after Usage: Every SKILL.md with scripts MUST include this section after Usage:
@@ -142,6 +187,26 @@ Every SKILL.md with scripts MUST include this section after Usage:
When referencing scripts in workflow sections, use `${SKILL_DIR}/scripts/<name>.ts` so agents can resolve the correct path. When referencing scripts in workflow sections, use `${SKILL_DIR}/scripts/<name>.ts` so agents can resolve the correct path.
### Progressive Disclosure
For skills with extensive content, use separate reference files:
```
skills/baoyu-example/
├── SKILL.md # Main instructions (<500 lines)
├── references/
│ ├── styles.md # Style definitions (loaded as needed)
│ └── examples.md # Usage examples (loaded as needed)
└── scripts/
└── main.ts # Executable script
```
In SKILL.md, link to reference files (one level deep only):
```markdown
**Available styles**: See [references/styles.md](references/styles.md)
**Examples**: See [references/examples.md](references/examples.md)
```
## Code Style ## Code Style
- TypeScript throughout, no comments - TypeScript throughout, no comments
@@ -155,9 +220,11 @@ Skills that require image generation MUST delegate to available image generation
### Image Generation Skill Selection ### Image Generation Skill Selection
1. Check available image generation skills in `skills/` directory **Default**: Use `skills/baoyu-image-gen/SKILL.md` (unless user specifies otherwise).
2. Read each skill's SKILL.md to understand parameters and capabilities
3. If multiple image generation skills available, ask user to choose preferred skill 1. Read `skills/baoyu-image-gen/SKILL.md` for parameters and capabilities
2. If user explicitly requests a different skill, check `skills/` directory for alternatives
3. Only ask user to choose when they haven't specified and multiple viable options exist
### Generation Flow Template ### Generation Flow Template
@@ -298,18 +365,66 @@ READMEs use 3-column tables for style previews:
## Extension Support ## Extension Support
Every SKILL.md MUST include an Extension Support section at the end: Every SKILL.md MUST include two parts for extension support:
### 1. Load Preferences Section (in Step 1 or as "Preferences" section)
For skills with workflows, add as Step 1.1. For utility skills, add as "Preferences (EXTEND.md)" section before Usage:
```markdown
**1.1 Load Preferences (EXTEND.md)**
Use Bash to check EXTEND.md existence (priority order):
\`\`\`bash
# Check project-level first
test -f .baoyu-skills/<skill-name>/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/<skill-name>/EXTEND.md" && echo "user"
\`\`\`
┌────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/<skill-name>/EXTEND.md │ Project directory │
├────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/<skill-name>/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**: [List supported configuration options for this skill]
Schema: `references/config/preferences-schema.md`
```
### 2. Extension Support Section (at the end)
Simplified section that references the preferences section:
```markdown ```markdown
## Extension Support ## Extension Support
Custom styles and configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/<skill-name>/EXTEND.md` (project)
2. `~/.baoyu-skills/<skill-name>/EXTEND.md` (user)
If found, load before Step 1. Extension content overrides defaults.
``` ```
Replace `<skill-name>` with the actual skill name (e.g., `baoyu-cover-image`). Or for utility skills without workflow steps:
```markdown
## Extension Support
Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
```
**Notes**:
- Replace `<skill-name>` with actual skill name (e.g., `baoyu-cover-image`)
- Use `$HOME` instead of `~` for cross-platform compatibility (macOS/Linux/WSL)
- Use `test -f` Bash command for explicit file existence check
- ASCII tables for clear visual formatting
+13 -6
View File
@@ -245,24 +245,31 @@ Generate professional infographics with 20 layout types and 17 visual styles. An
#### baoyu-cover-image #### baoyu-cover-image
Generate cover images for articles with Type × Style two-dimension system. Generate cover images for articles with 4 dimensions: Type × Style × Text × Mood.
```bash ```bash
# Auto-select type and style based on content # Auto-select all dimensions based on content
/baoyu-cover-image path/to/article.md /baoyu-cover-image path/to/article.md
# Specify type and/or style # Quick mode: skip confirmation, use auto-selection
/baoyu-cover-image path/to/article.md --quick
# Specify dimensions
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint /baoyu-cover-image path/to/article.md --type conceptual --style blueprint
/baoyu-cover-image path/to/article.md --style warm /baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
# Specify aspect ratio (default: 2.35:1) # Specify aspect ratio (default: 2.35:1)
/baoyu-cover-image path/to/article.md --aspect 16:9 /baoyu-cover-image path/to/article.md --aspect 16:9
# Without title text # Visual only (no title text)
/baoyu-cover-image path/to/article.md --no-title /baoyu-cover-image path/to/article.md --no-title
``` ```
Available types: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal` **Four Dimensions**:
- **Type**: `hero`, `conceptual`, `typography`, `metaphor`, `scene`, `minimal`
- **Style**: 20 built-in styles (see previews below)
- **Text**: `none`, `title-only` (default), `title-subtitle`, `text-rich`
- **Mood**: `subtle`, `balanced` (default), `bold`
Available styles: `elegant` (default), `blueprint`, `bold-editorial`, `chalkboard`, `dark-atmospheric`, `editorial-infographic`, `fantasy-animation`, `flat-doodle`, `intuition-machine`, `minimal`, `nature`, `notion`, `pixel-art`, `playful`, `retro`, `sketch-notes`, `vector-illustration`, `vintage`, `warm`, `watercolor` Available styles: `elegant` (default), `blueprint`, `bold-editorial`, `chalkboard`, `dark-atmospheric`, `editorial-infographic`, `fantasy-animation`, `flat-doodle`, `intuition-machine`, `minimal`, `nature`, `notion`, `pixel-art`, `playful`, `retro`, `sketch-notes`, `vector-illustration`, `vintage`, `warm`, `watercolor`
+13 -6
View File
@@ -245,24 +245,31 @@ npx skills add jimliu/baoyu-skills
#### baoyu-cover-image #### baoyu-cover-image
为文章生成封面图,支持类型 × 风格二维系统 为文章生成封面图,支持四维定制系统:类型 × 风格 × 文字 × 氛围
```bash ```bash
# 根据内容自动选择类型和风格 # 根据内容自动选择所有维度
/baoyu-cover-image path/to/article.md /baoyu-cover-image path/to/article.md
# 指定类型和/或风格 # 快速模式:跳过确认,使用自动选择
/baoyu-cover-image path/to/article.md --quick
# 指定维度
/baoyu-cover-image path/to/article.md --type conceptual --style blueprint /baoyu-cover-image path/to/article.md --type conceptual --style blueprint
/baoyu-cover-image path/to/article.md --style warm /baoyu-cover-image path/to/article.md --text title-subtitle --mood bold
# 指定宽高比(默认:2.35:1 # 指定宽高比(默认:2.35:1
/baoyu-cover-image path/to/article.md --aspect 16:9 /baoyu-cover-image path/to/article.md --aspect 16:9
# 不含标题文字 # 纯视觉(不含标题文字
/baoyu-cover-image path/to/article.md --no-title /baoyu-cover-image path/to/article.md --no-title
``` ```
可用类型:`hero``conceptual``typography``metaphor``scene``minimal` **四个维度**
- **类型 (Type)**`hero``conceptual``typography``metaphor``scene``minimal`
- **风格 (Style)**:20 种内置风格(见下方预览)
- **文字 (Text)**`none``title-only`(默认)、`title-subtitle``text-rich`
- **氛围 (Mood)**`subtle``balanced`(默认)、`bold`
可用风格:`elegant`(默认)、`blueprint``bold-editorial``chalkboard``dark-atmospheric``editorial-infographic``fantasy-animation``flat-doodle``intuition-machine``minimal``nature``notion``pixel-art``playful``retro``sketch-notes``vector-illustration``vintage``warm``watercolor` 可用风格:`elegant`(默认)、`blueprint``bold-editorial``chalkboard``dark-atmospheric``editorial-infographic``fantasy-animation``flat-doodle``intuition-machine``minimal``nature``notion``pixel-art``playful``retro``sketch-notes``vector-illustration``vintage``warm``watercolor`
+162 -304
View File
@@ -7,236 +7,188 @@ description: Analyzes article structure, identifies positions requiring visual a
Analyze articles, identify illustration positions, generate images with Type × Style consistency. Analyze articles, identify illustration positions, generate images with Type × Style consistency.
## Usage
```bash
# Auto-select type and style based on content
/baoyu-article-illustrator path/to/article.md
# Specify type
/baoyu-article-illustrator path/to/article.md --type infographic
# Specify style
/baoyu-article-illustrator path/to/article.md --style blueprint
# Combine type and style
/baoyu-article-illustrator path/to/article.md --type flowchart --style notion
# Specify density
/baoyu-article-illustrator path/to/article.md --density rich
# Direct content input
/baoyu-article-illustrator
[paste content]
```
## Options
| Option | Description |
|--------|-------------|
| `--type <name>` | Illustration type (see Type Gallery) |
| `--style <name>` | Visual style (see Style Gallery) |
| `--density <level>` | Image count: minimal / balanced / rich |
## Two Dimensions ## Two Dimensions
| Dimension | Controls | Examples | | Dimension | Controls | Examples |
|-----------|----------|----------| |-----------|----------|----------|
| **Type** | Information structure, content layout | infographic, scene, flowchart, comparison, framework, timeline | | **Type** | Information structure, layout | infographic, scene, flowchart, comparison, framework, timeline |
| **Style** | Visual aesthetics, colors, mood | notion, warm, minimal, blueprint, watercolor, elegant | | **Style** | Visual aesthetics, mood | notion, warm, minimal, blueprint, watercolor, elegant |
Type × Style can be freely combined. Example: `--type infographic --style blueprint` creates technical data visualization with schematic aesthetics. Type × Style can be freely combined. Example: `--type infographic --style blueprint`
## Type Gallery ## Type Gallery
| Type | Description | Best For | | Type | Best For |
|------|-------------|----------| |------|----------|
| `infographic` | Data visualization, charts, metrics | Technical articles, data analysis, comparisons | | `infographic` | Data, metrics, technical articles |
| `scene` | Atmospheric illustration, mood rendering | Narrative articles, personal stories, emotional content | | `scene` | Narratives, personal stories, emotional content |
| `flowchart` | Process diagrams, step visualization | Tutorials, workflows, decision trees | | `flowchart` | Tutorials, workflows, processes |
| `comparison` | Side-by-side, before/after contrast | Product comparisons, option evaluations | | `comparison` | Side-by-side, before/after, options |
| `framework` | Concept maps, relationship diagrams | Methodologies, models, architecture design | | `framework` | Methodologies, models, architecture |
| `timeline` | Chronological progression | History, project progress, evolution | | `timeline` | History, progress, evolution |
## Density Options
| Density | Count | Description |
|---------|-------|-------------|
| `minimal` | 1-2 | Core concepts only |
| `balanced` (Default) | 3-5 | Major sections coverage |
| `rich` | 6+ | Rich visual support |
## Style Gallery ## Style Gallery
| Style | Description | Best For | | Style | Best For |
|-------|-------------|----------| |-------|----------|
| `notion` (Default) | Minimalist hand-drawn line art | Knowledge sharing, SaaS, productivity | | `notion` (Default) | Knowledge sharing, SaaS, productivity |
| `elegant` | Refined, sophisticated | Business, thought leadership | | `elegant` | Business, thought leadership |
| `warm` | Friendly, approachable | Personal growth, lifestyle, education | | `warm` | Personal growth, lifestyle, education |
| `minimal` | Ultra-clean, zen-like | Philosophy, minimalism, core concepts | | `minimal` | Philosophy, core concepts |
| `blueprint` | Technical schematics | Architecture, system design, engineering | | `blueprint` | Architecture, system design |
| `watercolor` | Soft artistic with natural warmth | Lifestyle, travel, creative | | `watercolor` | Lifestyle, travel, creative |
| `editorial` | Magazine-style infographic | Tech explainers, journalism | | `editorial` | Tech explainers, journalism |
| `scientific` | Academic precise diagrams | Biology, chemistry, technical research | | `scientific` | Academic, technical research |
Full style specifications: `references/styles/<style>.md` Full styles: [references/styles.md](references/styles.md)
## Type × Style Compatibility
| | notion | warm | minimal | blueprint | watercolor | elegant | editorial | scientific |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| infographic | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| scene | ✓ | ✓✓ | ✓ | ✗ | ✓✓ | ✓ | ✓ | ✗ |
| flowchart | ✓✓ | ✓ | ✓ | ✓✓ | ✗ | ✓ | ✓✓ | ✓ |
| comparison | ✓✓ | ✓ | ✓✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓ |
| framework | ✓✓ | ✓ | ✓✓ | ✓✓ | ✗ | ✓✓ | ✓ | ✓✓ |
| timeline | ✓✓ | ✓ | ✓ | ✓ | ✓✓ | ✓✓ | ✓✓ | ✓ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Auto Selection ## Auto Selection
| Content Signals | Recommended Type | Recommended Style | | Content Signals | Type | Style |
|-----------------|------------------|-------------------| |-----------------|------|-------|
| API, metrics, data, comparison, numbers | infographic | blueprint, notion | | API, metrics, data, numbers | infographic | blueprint, notion |
| Story, emotion, journey, experience, personal | scene | warm, watercolor | | Story, emotion, journey | scene | warm, watercolor |
| How-to, steps, workflow, process, tutorial | flowchart | notion, minimal | | How-to, steps, workflow | flowchart | notion, minimal |
| vs, pros/cons, before/after, alternatives | comparison | notion, elegant | | vs, pros/cons, before/after | comparison | notion, elegant |
| Framework, model, architecture, principles | framework | blueprint, notion | | Framework, model, architecture | framework | blueprint, notion |
| History, timeline, progress, evolution | timeline | elegant, warm | | History, timeline, progress | timeline | elegant, warm |
## Output Directory
```
illustrations/{topic-slug}/
├── source-{slug}.{ext}
├── outline.md
├── prompts/
│ └── illustration-{slug}.md
└── NN-{type}-{slug}.png
```
**Slug**: Extract 2-4 word topic in kebab-case.
**Conflict**: Append `-YYYYMMDD-HHMMSS` if exists.
## Workflow ## Workflow
### Progress Copy this checklist and track progress:
``` ```
- [ ] Step 1: Setup & Analyze Progress:
- [ ] Step 2: Confirm Settings ⚠️ REQUIRED - [ ] Step 1: Pre-check
- [ ] Step 3: Generate Outline - [ ] Step 2: Setup & Analyze
- [ ] Step 4: Generate Images - [ ] Step 3: Confirm Settings ⚠️ REQUIRED
- [ ] Step 5: Finalize - [ ] Step 4: Generate Outline
- [ ] Step 5: Generate Images
- [ ] Step 6: Finalize
``` ```
### Step 1: Setup & Analyze ---
**1.1 Load Preferences (EXTEND.md)** ### Step 1: Pre-check
Use Bash to check EXTEND.md existence (priority order): **1.1 Determine Input Type**
| Input | Output Directory | Next |
|-------|------------------|------|
| File path | Ask user (1.2) | → 1.2 |
| Pasted content | `illustrations/{topic-slug}/` | → 1.4 |
**1.2 Determine Output Directory** (file path input only)
Check `default_output_dir` in preferences:
| Preference Value | Action |
|------------------|--------|
| `same-dir` | Use `{article-dir}/`, display "Output: {path}" |
| `illustrations-subdir` | Use `{article-dir}/illustrations/`, display "Output: {path}" |
| `independent` | Use `illustrations/{topic-slug}/`, display "Output: {path}" |
| Not configured | **MUST** ask with AskUserQuestion ↓ |
**AskUserQuestion** (when no preference):
- `{article-dir}/` - Same directory as article
- `{article-dir}/illustrations/` - Illustrations subdirectory (Recommended)
- `illustrations/{topic-slug}/` - Independent directory
- Save as default - Remember this choice for future runs
**1.3 Check Existing Images**
Scan target directory for `.png/.jpg/.webp` files.
If images exist → AskUserQuestion: How to handle?
- `supplement` - Keep existing, generate only new positions
- `overwrite` - Overwrite same-name files
- `regenerate` - Clear all and regenerate
**1.4 Confirm Article Update Method** (file path input only)
AskUserQuestion: How to update article?
- `update` - Modify original file directly
- `copy` - Create `{name}-illustrated.md` copy
**1.5 Load Preferences (EXTEND.md)**
```bash ```bash
# Check project-level first
test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project" test -f .baoyu-skills/baoyu-article-illustrator/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user" test -f "$HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md" && echo "user"
``` ```
┌──────────────────────────────────────────────────────────┬───────────────────┐ | Result | Action |
│ Path │ Location │ |--------|--------|
├──────────────────────────────────────────────────────────┼───────────────────┤ | Found | Read, parse, display summary |
│ .baoyu-skills/baoyu-article-illustrator/EXTEND.md │ Project directory │ | Not found | Ask with AskUserQuestion (see references/config/first-time-setup.md) |
├──────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-article-illustrator/EXTEND.md │ User home │
└──────────────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐ **Supports**: Watermark | Preferred type/style | Custom styles | Language | Output directory
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, display summary │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Ask user with AskUserQuestion (see references/config/first-time-setup.md) │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Watermark | Preferred type/style | Custom style definitions | Language preference ---
Schema: `references/config/preferences-schema.md` ### Step 2: Setup & Analyze
**1.2 Analyze Content** **2.1 Analyze Content**
Read article, detect language, classify content.
| Analysis | Description | | Analysis | Description |
|----------|-------------| |----------|-------------|
| Content type | Technical / Tutorial / Methodology / Narrative | | Content type | Technical / Tutorial / Methodology / Narrative |
| Core arguments | 2-5 main points that MUST be visualized | | Core arguments | 2-5 main points to visualize |
| Visual opportunities | Positions where illustrations add value | | Visual opportunities | Positions where illustrations add value |
| Recommended type | Based on content signals | | Recommended type | Based on content signals |
| Recommended density | Based on article length and complexity | | Recommended density | Based on length and complexity |
**1.3 Extract Core Arguments** **2.2 Extract Core Arguments**
Extract 2-5 core arguments that MUST be visualized:
- Main thesis - Main thesis
- Key concepts reader needs - Key concepts reader needs
- Comparisons/contrasts being made - Comparisons/contrasts
- Framework/model proposed - Framework/model proposed
**CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept** instead. **CRITICAL**: If article uses metaphors (e.g., "电锯切西瓜"), do NOT illustrate literally. Visualize the **underlying concept**.
**1.4 Identify Positions** **2.3 Identify Positions**
**What to Illustrate**: **Illustrate**:
- Core arguments (REQUIRED) - Core arguments (REQUIRED)
- Abstract concepts needing visualization - Abstract concepts
- Data comparisons, metrics - Data comparisons
- Processes, workflows - Processes, workflows
**What NOT to Illustrate**: **Do NOT Illustrate**:
- Metaphors literally - Metaphors literally
- Decorative scenes without information - Decorative scenes
- Generic illustrations - Generic illustrations
### Step 2: Confirm Settings ⚠️ ---
### Step 3: Confirm Settings ⚠️
**Do NOT skip.** Use AskUserQuestion with 3-4 questions in ONE call. **Do NOT skip.** Use AskUserQuestion with 3-4 questions in ONE call.
**Question 1: Illustration Type** **Q1: Illustration Type**
- [Recommended based on analysis] (Recommended)
- infographic / scene / flowchart / comparison / framework / timeline / mixed
Based on content analysis, recommend type: **Q2: Density**
- [Recommended type based on signals] (Recommended) - minimal (1-2) - Core concepts only
- infographic - Data visualization, charts - balanced (3-5) (Recommended) - Major sections
- scene - Atmospheric, mood rendering - rich (6+) - Comprehensive support
- flowchart - Process, steps
- comparison - Side-by-side contrast
- framework - Concept relationships
- timeline - Chronological progression
- mixed - Combine multiple types
**Question 2: Density** **Q3: Style**
Based on Type, suggest compatible styles from matrix:
- [Best compatible] (Recommended)
- [Other ✓✓ styles]
- [Other ✓ styles]
- minimal (1-2 images) - Core concepts only **Q4** (only if source ≠ user language):
- balanced (3-5 images) (Recommended) - Major sections - Language: Source / User language
- rich (6+ images) - Comprehensive visual support
**Question 3: Style** ---
Based on recommended Type, suggest compatible styles (see Type × Style Compatibility matrix): ### Step 4: Generate Outline
- [Best compatible style for recommended type] (Recommended)
- [Other highly compatible styles: ✓✓ from matrix]
- [Compatible styles: ✓ from matrix]
**Question 4** (only if source ≠ user language): Save as `outline.md`:
- Language: Source language / User language
### Step 3: Generate Outline
Based on confirmed Type + Density + Style, generate illustration outline.
**Outline Format** (`outline.md`):
```yaml ```yaml
--- ---
@@ -249,44 +201,48 @@ image_count: 4
## Illustration 1 ## Illustration 1
**Position**: [section] / [paragraph] **Position**: [section] / [paragraph]
**Purpose**: [why this illustration helps] **Purpose**: [why this helps]
**Visual Content**: [what to show] **Visual Content**: [what to show]
**Type Application**: [how type applies here] **Type Application**: [how type applies]
**Filename**: 01-infographic-concept-name.png **Filename**: 01-infographic-concept-name.png
## Illustration 2 ## Illustration 2
... ...
``` ```
**Outline Requirements**: **Requirements**:
- Each illustration position justified by content needs - Each position justified by content needs
- Type applied consistently across all illustrations - Type applied consistently
- Style characteristics reflected in visual descriptions - Style reflected in descriptions
- Count matches density selection - Count matches density
### Step 4: Generate Images ---
**4.1 Create Prompts** ### Step 5: Generate Images
Follow Prompt Construction principles below. Save each to `prompts/illustration-{slug}.md`. **5.1 Create Prompts**
**4.2 Select Generation Skill** Follow [references/prompt-construction.md](references/prompt-construction.md). Save to `prompts/illustration-{slug}.md`.
Check available image generation skills. If multiple, ask user to choose. **5.2 Select Generation Skill**
**4.3 Apply Watermark** (if enabled in preferences) Check available skills. If multiple, ask user.
Add to prompt: `Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.` **5.3 Apply Watermark** (if enabled)
**4.4 Generate** Add: `Include a subtle watermark "[content]" at [position].`
**5.4 Generate**
1. Generate sequentially 1. Generate sequentially
2. After each: "Generated X/N" 2. After each: "Generated X/N"
3. On failure: auto-retry once, then log and continue 3. On failure: retry once, then log and continue
### Step 5: Finalize ---
**5.1 Update Article** ### Step 6: Finalize
**6.1 Update Article**
Insert after corresponding paragraph: Insert after corresponding paragraph:
```markdown ```markdown
@@ -295,158 +251,60 @@ Insert after corresponding paragraph:
Alt text: concise description in article's language. Alt text: concise description in article's language.
**5.2 Output Summary** **6.2 Output Summary**
``` ```
Article Illustration Complete! Article Illustration Complete!
Article: [path] Article: [path]
Type: [type name] Type: [type] | Density: [level] | Style: [style]
Density: [minimal/balanced/rich] Location: [directory]
Style: [style name]
Location: [directory path]
Images: X/N generated Images: X/N generated
Positions: Positions:
- 01-infographic-xxx.png → After "[Section]" - 01-xxx.png → After "[Section]"
- 02-infographic-yyy.png → After "[Section]" - 02-yyy.png → After "[Section]"
[If failures] [If failures]
Failed: Failed:
- NN-type-zzz.png: [reason] - NN-zzz.png: [reason]
``` ```
## Prompt Construction ---
### Principles ## Output Directory
Good prompts must include:
1. **Layout Structure First**: Describe composition, zones, flow direction
2. **Specific Data/Labels**: Use actual numbers, terms from article
3. **Visual Relationships**: How elements connect
4. **Semantic Colors**: Meaning-based color choices (red=warning, green=efficient)
5. **Style Characteristics**: Line treatment, texture, mood
6. **Aspect Ratio**: End with ratio and complexity level
### Type-Specific Prompts
**Infographic**:
``` ```
[Title] - Data Visualization illustrations/{topic-slug}/
├── source-{slug}.{ext}
Layout: [grid/radial/hierarchical] ├── outline.md
├── prompts/
ZONES: │ └── illustration-{slug}.md
- Zone 1: [data point with specific values] └── NN-{type}-{slug}.png
- Zone 2: [comparison with metrics]
- Zone 3: [summary/conclusion]
LABELS: [specific numbers, percentages, terms from article]
COLORS: [semantic color mapping]
STYLE: [style characteristics]
ASPECT: 16:9
``` ```
**Scene**: **Slug**: 2-4 word topic in kebab-case.
``` **Conflict**: Append `-YYYYMMDD-HHMMSS` if exists.
[Title] - Atmospheric Scene
FOCAL POINT: [main subject]
ATMOSPHERE: [lighting, mood, environment]
MOOD: [emotion to convey]
COLOR TEMPERATURE: [warm/cool/neutral]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Flowchart**:
```
[Title] - Process Flow
Layout: [left-right/top-down/circular]
STEPS:
1. [Step name] - [brief description]
2. [Step name] - [brief description]
...
CONNECTIONS: [arrow types, decision points]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Comparison**:
```
[Title] - Comparison View
LEFT SIDE - [Option A]:
- [Point 1]
- [Point 2]
RIGHT SIDE - [Option B]:
- [Point 1]
- [Point 2]
DIVIDER: [visual separator]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Framework**:
```
[Title] - Conceptual Framework
STRUCTURE: [hierarchical/network/matrix]
NODES:
- [Concept 1] - [role]
- [Concept 2] - [role]
RELATIONSHIPS: [how nodes connect]
STYLE: [style characteristics]
ASPECT: 16:9
```
**Timeline**:
```
[Title] - Chronological View
DIRECTION: [horizontal/vertical]
EVENTS:
- [Date/Period 1]: [milestone]
- [Date/Period 2]: [milestone]
MARKERS: [visual indicators]
STYLE: [style characteristics]
ASPECT: 16:9
```
### What to Avoid
- Vague descriptions ("a nice image")
- Literal metaphor illustrations
- Missing concrete labels/annotations
- Generic decorative elements
## Modification ## Modification
| Action | Steps | | Action | Steps |
|--------|-------| |--------|-------|
| **Edit** | Update prompt → Regenerate → Update reference | | **Edit** | Update prompt → Regenerate → Update reference |
| **Add** | Identify position → Create prompt → Generate → Update outline → Insert reference | | **Add** | Identify position → Create prompt → Generate → Update outline → Insert |
| **Delete** | Delete files → Remove reference → Update outline | | **Delete** | Delete files → Remove reference → Update outline |
## References ## References
| File | Content | | File | Content |
|------|---------| |------|---------|
| [references/styles.md](references/styles.md) | Style gallery & compatibility matrix | | [references/usage.md](references/usage.md) | Command syntax and options |
| [references/styles.md](references/styles.md) | Style gallery & compatibility |
| [references/prompt-construction.md](references/prompt-construction.md) | Prompt templates |
| `references/styles/<style>.md` | Full style specifications | | `references/styles/<style>.md` | Full style specifications |
| `references/config/preferences-schema.md` | EXTEND.md schema | | `references/config/preferences-schema.md` | EXTEND.md schema |
| `references/config/first-time-setup.md` | First-time setup flow | | `references/config/first-time-setup.md` | First-time setup flow |
## 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.5** for paths and supported options.
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_style: preferred_style:
name: null # Built-in or custom style name name: null # Built-in or custom style name
@@ -23,6 +22,8 @@ preferred_style:
language: null # zh|en|ja|ko|auto language: null # zh|en|ja|ko|auto
default_output_dir: null # same-dir|illustrations-subdir|independent
custom_styles: custom_styles:
- name: my-style - name: my-style
description: "Style description" description: "Style description"
@@ -44,10 +45,10 @@ custom_styles:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `watermark.position` | enum | bottom-right | Position on image |
| `watermark.opacity` | float | 0.7 | Transparency (0.1-1.0) |
| `preferred_style.name` | string | null | Style name or null | | `preferred_style.name` | string | null | Style name or null |
| `preferred_style.description` | string | "" | Custom notes/override | | `preferred_style.description` | string | "" | Custom notes/override |
| `language` | string | null | Output language (null = auto-detect) | | `language` | string | null | Output language (null = auto-detect) |
| `default_output_dir` | enum | null | Output directory preference (null = ask each time) |
| `custom_styles` | array | [] | User-defined styles | | `custom_styles` | array | [] | User-defined styles |
## Position Options ## Position Options
@@ -59,6 +60,14 @@ custom_styles:
| `bottom-center` | Bottom center | | `bottom-center` | Bottom center |
| `top-right` | Upper right corner | | `top-right` | Upper right corner |
## Output Directory Options
| Value | Description |
|-------|-------------|
| `same-dir` | Same directory as article |
| `illustrations-subdir` | `{article-dir}/illustrations/` subdirectory |
| `independent` | `illustrations/{topic-slug}/` in working directory |
## Custom Style Fields ## Custom Style Fields
| Field | Required | Description | | Field | Required | Description |
@@ -94,7 +103,6 @@ watermark:
enabled: true enabled: true
content: "@myaccount" content: "@myaccount"
position: bottom-right position: bottom-right
opacity: 0.5
preferred_style: preferred_style:
name: notion name: notion
@@ -0,0 +1,127 @@
# Prompt Construction
## Principles
Good prompts must include:
1. **Layout Structure First**: Describe composition, zones, flow direction
2. **Specific Data/Labels**: Use actual numbers, terms from article
3. **Visual Relationships**: How elements connect
4. **Semantic Colors**: Meaning-based color choices (red=warning, green=efficient)
5. **Style Characteristics**: Line treatment, texture, mood
6. **Aspect Ratio**: End with ratio and complexity level
## Type-Specific Templates
### Infographic
```
[Title] - Data Visualization
Layout: [grid/radial/hierarchical]
ZONES:
- Zone 1: [data point with specific values]
- Zone 2: [comparison with metrics]
- Zone 3: [summary/conclusion]
LABELS: [specific numbers, percentages, terms from article]
COLORS: [semantic color mapping]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Scene
```
[Title] - Atmospheric Scene
FOCAL POINT: [main subject]
ATMOSPHERE: [lighting, mood, environment]
MOOD: [emotion to convey]
COLOR TEMPERATURE: [warm/cool/neutral]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Flowchart
```
[Title] - Process Flow
Layout: [left-right/top-down/circular]
STEPS:
1. [Step name] - [brief description]
2. [Step name] - [brief description]
...
CONNECTIONS: [arrow types, decision points]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Comparison
```
[Title] - Comparison View
LEFT SIDE - [Option A]:
- [Point 1]
- [Point 2]
RIGHT SIDE - [Option B]:
- [Point 1]
- [Point 2]
DIVIDER: [visual separator]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Framework
```
[Title] - Conceptual Framework
STRUCTURE: [hierarchical/network/matrix]
NODES:
- [Concept 1] - [role]
- [Concept 2] - [role]
RELATIONSHIPS: [how nodes connect]
STYLE: [style characteristics]
ASPECT: 16:9
```
### Timeline
```
[Title] - Chronological View
DIRECTION: [horizontal/vertical]
EVENTS:
- [Date/Period 1]: [milestone]
- [Date/Period 2]: [milestone]
MARKERS: [visual indicators]
STYLE: [style characteristics]
ASPECT: 16:9
```
## What to Avoid
- Vague descriptions ("a nice image")
- Literal metaphor illustrations
- Missing concrete labels/annotations
- Generic decorative elements
## Watermark Integration
If watermark enabled in preferences, append:
```
Include a subtle watermark "[content]" positioned at [position] with approximately [opacity*100]% visibility.
```
@@ -0,0 +1,66 @@
# Usage
## Command Syntax
```bash
# Auto-select type and style based on content
/baoyu-article-illustrator path/to/article.md
# Specify type
/baoyu-article-illustrator path/to/article.md --type infographic
# Specify style
/baoyu-article-illustrator path/to/article.md --style blueprint
# Combine type and style
/baoyu-article-illustrator path/to/article.md --type flowchart --style notion
# Specify density
/baoyu-article-illustrator path/to/article.md --density rich
# Direct content input (paste mode)
/baoyu-article-illustrator
[paste content]
```
## Options
| Option | Description |
|--------|-------------|
| `--type <name>` | Illustration type (see Type Gallery in SKILL.md) |
| `--style <name>` | Visual style (see references/styles.md) |
| `--density <level>` | Image count: minimal / balanced / rich |
## Input Modes
| Mode | Trigger | Output Directory |
|------|---------|------------------|
| File path | `path/to/article.md` | Use `default_output_dir` preference, or ask if not set |
| Paste content | No path argument | `illustrations/{topic-slug}/` |
## Output Directory Options
| Value | Path |
|-------|------|
| `same-dir` | `{article-dir}/` |
| `illustrations-subdir` | `{article-dir}/illustrations/` |
| `independent` | `illustrations/{topic-slug}/` |
Configure in EXTEND.md: `default_output_dir: illustrations-subdir`
## Examples
**Technical article with data**:
```bash
/baoyu-article-illustrator api-design.md --type infographic --style blueprint
```
**Personal story**:
```bash
/baoyu-article-illustrator journey.md --type scene --style warm
```
**Tutorial with steps**:
```bash
/baoyu-article-illustrator how-to-deploy.md --type flowchart --density rich
```
+136 -358
View File
@@ -27,6 +27,17 @@ Create original knowledge comics with flexible art style × tone combinations.
| `--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 |
### Partial Workflow Options
| Option | Description |
|--------|-------------|
| `--storyboard-only` | Generate storyboard only, skip prompts and images |
| `--prompts-only` | Generate storyboard + prompts, skip images |
| `--images-only` | Generate images from existing prompts directory |
| `--regenerate N` | Regenerate specific page(s) only (e.g., `3` or `2,5,8`) |
Details: [references/partial-workflows.md](references/partial-workflows.md)
### Art Styles (画风) ### Art Styles (画风)
| Style | 中文 | Description | | Style | 中文 | Description |
@@ -69,27 +80,25 @@ Presets with special rules beyond art+tone:
| ink-brush | neutral, dramatic, action, vintage | warm | romantic, energetic | | ink-brush | neutral, dramatic, action, vintage | warm | romantic, energetic |
| chalk | neutral, warm, energetic | vintage | dramatic, action, romantic | | chalk | neutral, warm, energetic | vintage | dramatic, action, romantic |
Art Style × Tone × Layout can be freely combined. Incompatible combinations work but may produce unexpected results. Details: [references/auto-selection.md](references/auto-selection.md)
## Auto Selection ## Auto Selection
Content signals determine default art + tone + layout (or preset): Content signals determine default art + tone + layout (or preset):
| Content Signals | Art Style | Tone | Layout | Preset | | Content Signals | Recommended |
|-----------------|-----------|------|--------|--------| |-----------------|-------------|
| Tutorial, how-to, beginner | manga | neutral | webtoon | **ohmsha** | | Tutorial, how-to, programming, educational | **ohmsha** preset |
| Computing, AI, programming | manga | neutral | dense | **ohmsha** | | Pre-1950, classical, ancient | realistic + vintage |
| Technical explanation, educational | manga | neutral | webtoon | **ohmsha** | | Personal story, mentor | ligne-claire + warm |
| Pre-1950, classical, ancient | realistic | vintage | cinematic | - | | Martial arts, wuxia | **wuxia** preset |
| Personal story, mentor | ligne-claire | warm | standard | - | | Romance, school life | **shoujo** preset |
| Conflict, breakthrough | (inherit) | dramatic | splash | - | | Biography, balanced | ligne-claire + neutral |
| Wine, food, business, lifestyle | realistic | neutral | cinematic | - |
| Martial arts, wuxia, xianxia | ink-brush | action | splash | **wuxia** |
| Romance, love, school life | manga | romantic | standard | **shoujo** |
| Biography, balanced | ligne-claire | neutral | mixed | - |
**When preset is recommended**: Load `references/presets/{preset}.md` and apply all special rules. **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
**Important**: All scripts are located in the `scripts/` subdirectory of this skill. **Important**: All scripts are located in the `scripts/` subdirectory of this skill.
@@ -111,384 +120,153 @@ Output directory: `comic/{topic-slug}/`
- Conflict: append timestamp (e.g., `turing-story-20260118-143052`) - Conflict: append timestamp (e.g., `turing-story-20260118-143052`)
**Contents**: **Contents**:
- `source-{slug}.{ext}` - Source files | File | Description |
- `analysis.md` - Content analysis |------|-------------|
- `storyboard.md` - Storyboard with panel breakdown | `source-{slug}.{ext}` | Source files |
- `characters/characters.md` - Character definitions | `analysis.md` | Content analysis |
- `characters/characters.png` - Character reference sheet | `storyboard.md` | Storyboard with panel breakdown |
- `prompts/NN-{cover|page}-[slug].md` - Generation prompts | `characters/characters.md` | Character definitions |
- `NN-{cover|page}-[slug].png` - Generated images | `characters/characters.png` | Character reference sheet |
- `{topic-slug}.pdf` - Final merged PDF | `prompts/NN-{cover\|page}-[slug].md` | Generation prompts |
| `NN-{cover\|page}-[slug].png` | Generated images |
| `{topic-slug}.pdf` | Final merged PDF |
## Language Handling
**Detection Priority**:
1. `--lang` flag (explicit)
2. EXTEND.md `language` setting
3. User's conversation language
4. Source content language
**Rule**: Use user's input language or saved language preference for ALL interactions:
- Storyboard outlines and scene descriptions
- Image generation prompts
- User selection options and confirmations
- Progress updates, questions, errors, summaries
Technical terms remain in English.
## Workflow ## Workflow
### Progress Checklist ### Progress Checklist
Copy and track progress:
``` ```
Comic Progress: Comic Progress:
- [ ] Step 1: Load preferences + Analyze content - [ ] Step 1: Setup & Analyze (1.1 Preferences, 1.2 Analyze, 1.3 Check existing)
- [ ] Step 2: Confirmation 1 - Style & options ⚠️ REQUIRED - [ ] Step 2: Confirmation - Style & options ⚠️ REQUIRED
- [ ] Step 3: Generate storyboard + characters - [ ] Step 3: Generate storyboard + characters
- [ ] Step 4: Confirmation 2 - Review outline (if requested) - [ ] Step 4: Review outline (conditional)
- [ ] Step 5: Generate images (sequential) - [ ] Step 5: Generate prompts
- [ ] Step 6: Merge to PDF - [ ] Step 6: Review prompts (conditional)
- [ ] Step 7: Completion report - [ ] Step 7: Generate images ⚠️ CHARACTER REF REQUIRED
- [ ] 7.1 Generate character sheet FIRST → characters/characters.png
- [ ] 7.2 Generate pages WITH --ref characters/characters.png
- [ ] Step 8: Merge to PDF
- [ ] Step 9: Completion report
``` ```
### Flow ### Flow
``` ```
Input → Preferences → Analyze → [Confirm 1: Style + Skip?] → Storyboard → [Confirm 2: if needed] → Generate → PDF → Complete Input → Preferences → Analyze → [Check Existing?] → [Confirm: Style + Reviews] → Storyboard → [Review?] → Prompts → [Review?] → Images → PDF → Complete
``` ```
### Step 1: Setup & Analyze ### Step Summary
**1.1 Load Preferences (EXTEND.md)** | Step | Action | Key Output |
|------|--------|------------|
| 1.1 | Load EXTEND.md preferences | Config loaded |
| 1.2 | Analyze content | `analysis.md` |
| 1.3 | Check existing directory | Handle conflicts |
| 2 | Confirm style, focus, audience, reviews | User preferences |
| 3 | Generate storyboard + characters | `storyboard.md`, `characters/` |
| 4 | Review outline (if requested) | User approval |
| 5 | Generate prompts | `prompts/*.md` |
| 6 | Review prompts (if requested) | User approval |
| **7.1** | **Generate character sheet FIRST** | `characters/characters.png` |
| **7.2** | Generate pages **with character ref** | `*.png` files |
| 8 | Merge to PDF | `{slug}.pdf` |
| 9 | Completion report | Summary |
Use Bash to check EXTEND.md existence (priority order): ### Step 7: Image Generation ⚠️ CRITICAL
**Character reference is MANDATORY for visual consistency.**
**7.1 Generate character sheet first**:
```bash
# Use Reference Sheet Prompt from characters/characters.md
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
--promptfiles characters/characters.md \
--image characters/characters.png --ar 4:3
```
**Compress character sheet** (recommended):
Compress to reduce token usage when used as reference image:
- Use available image compression skill (if any)
- Or system tools: `pngquant`, `optipng`, `sips` (macOS)
- **Keep PNG format**, lossless compression preferred
**7.2 Generate each page WITH character reference**:
| Skill Capability | Strategy |
|------------------|----------|
| Supports `--ref` | Pass `characters/characters.png` with EVERY page |
| No `--ref` support | Prepend character descriptions to EVERY prompt file |
```bash ```bash
# Check project-level first # Example: ALWAYS include --ref for consistency
test -f .baoyu-skills/baoyu-comic/EXTEND.md && echo "project" npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
--promptfiles prompts/01-page-xxx.md \
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL) --image 01-page-xxx.png --ar 3:4 \
test -f "$HOME/.baoyu-skills/baoyu-comic/EXTEND.md" && echo "user" --ref characters/characters.png
``` ```
┌──────────────────────────────────────────────┬───────────────────┐ **Full workflow details**: [references/workflow.md](references/workflow.md)
│ Path │ Location │
├──────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-comic/EXTEND.md │ Project directory │
├──────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-comic/EXTEND.md │ User home │
└──────────────────────────────────────────────┴───────────────────┘
**When EXTEND.md Found** → Read, parse, **output summary to user**: ### EXTEND.md Paths
``` | Path | Location |
📋 Loaded preferences from [full path] |------|----------|
├─ Watermark: [enabled/disabled] [content if enabled] | `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory |
├─ Art Style: [style name or "auto-select"] | `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home |
├─ Tone: [tone name or "auto-select"]
├─ Layout: [layout or "auto-select"]
├─ Language: [language or "auto-detect"]
└─ Character presets: [count] defined
```
**MUST output this summary** so user knows their current configuration. Do not skip or silently load.
**When EXTEND.md Not Found** → First-time setup:
1. Inform user: "No preferences found. Let's set up your defaults."
2. Use AskUserQuestion to collect preferences (see `references/config/first-time-setup.md`)
3. Create EXTEND.md at user-chosen location
4. Confirm: "✓ Preferences saved to [path]"
**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` Schema: [references/config/preferences-schema.md](references/config/preferences-schema.md)
**Important**: Once EXTEND.md exists, watermark, language, and style defaults are NOT asked again in Confirmation 1 or 2. These are session-persistent settings.
**1.2 Analyze Content → `analysis.md`**
Read source content, save it if needed, and perform deep analysis.
**Actions**:
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
2. Read source content
3. **Deep analysis** following `references/analysis-framework.md`:
- Target audience identification
- Value proposition for readers
- Core themes and narrative potential
- Key figures and their story arcs
4. Detect source language
5. **Determine language**:
- If EXTEND.md has `language` → use it
- Else if `--lang` option provided → use it
- Else → use detected source language
6. Determine recommended page count:
- Short story: 5-8 pages
- Medium complexity: 9-15 pages
- Full biography: 16-25 pages
7. Analyze content signals for art/tone/layout recommendations
8. **Save to `analysis.md`**
**analysis.md Format**: YAML front matter (title, topic, time_span, source_language, user_language, aspect_ratio, recommended_page_count, recommended_art, recommended_tone) + sections for Target Audience, Value Proposition, Core Themes, Key Figures & Story Arcs, Content Signals, Recommended Approaches. See `references/analysis-framework.md` for full template.
### Step 2: Confirmation 1 - Style & Options ⚠️
**Purpose**: Select visual style + decide whether to review outline before generation. **Do NOT skip.**
**Note**: Watermark and language already configured in EXTEND.md (Step 1).
**Display summary**:
- Content type + topic identified
- Key figures extracted
- Time span detected
- Recommended page count
- Language: [from EXTEND.md or detected]
- **Recommended style**: [art] + [tone] (based on content signals)
**Use AskUserQuestion** for:
**Question 1: Visual Style**
If a preset is recommended (see Auto Selection), show it first:
```
header: "Style"
question: "Which visual style for this comic?"
options:
- label: "[preset name] preset (Recommended)" # If preset recommended
description: "[preset description] - includes special rules"
- label: "[recommended art] + [recommended tone] (Recommended)" # If no preset
description: "Best match for your content based on analysis"
- label: "ligne-claire + neutral"
description: "Classic educational, Logicomix style"
- label: "ohmsha preset"
description: "Educational manga with visual metaphors, gadgets, NO talking heads"
- label: "Custom"
description: "Specify your own art + tone or preset"
```
**Preset vs Art+Tone**: Presets include special rules beyond art+tone. `ohmsha` = manga + neutral + visual metaphor rules + character roles + NO talking heads. Plain `manga + neutral` does NOT include these rules.
**Question 2: Narrative Focus** (multiSelect: true)
```
header: "Focus"
question: "What should the comic emphasize? (Select all that apply)"
options:
- label: "Biography/life story"
description: "Follow a person's journey through key life events"
- label: "Concept explanation"
description: "Break down complex ideas visually"
- label: "Historical event"
description: "Dramatize important historical moments"
- label: "Tutorial/how-to"
description: "Step-by-step educational guide"
```
**Question 3: Target Audience**
```
header: "Audience"
question: "Who is the primary reader?"
options:
- label: "General readers"
description: "Broad appeal, accessible content"
- label: "Students/learners"
description: "Educational focus, clear explanations"
- label: "Industry professionals"
description: "Technical depth, domain knowledge"
- label: "Children/young readers"
description: "Simplified language, engaging visuals"
```
**Question 4: Outline Review**
```
header: "Review"
question: "Do you want to review the outline before image generation?"
options:
- label: "Yes, let me review (Recommended)"
description: "Review storyboard and characters before generating images"
- label: "No, generate directly"
description: "Skip outline review, start generating immediately"
```
**After response**:
1. Update `analysis.md` with user preferences
2. **Store `skip_outline_review`** flag based on Question 4 response
3. → Step 3
### Step 3: Generate Storyboard + Characters
Create storyboard and character definitions using the confirmed style from Step 2.
**Loading Style References**:
- Art style: `references/art-styles/{art}.md`
- Tone: `references/tones/{tone}.md`
- If preset (ohmsha/wuxia/shoujo): also load `references/presets/{preset}.md`
**Generate**:
1. **Storyboard** (`storyboard.md`):
- YAML front matter with art_style, tone, layout, aspect_ratio
- Cover design
- Each page: layout, panel breakdown, visual prompts
- **Written in user's preferred language** (from Step 1)
- Reference: `references/storyboard-template.md`
- **If using preset**: Load and apply preset rules from `references/presets/`
2. **Character definitions** (`characters/characters.md`):
- Visual specs matching the art style (in user's preferred language)
- Include Reference Sheet Prompt for later image generation
- Reference: `references/character-template.md`
**After generation**:
- If `skip_outline_review` is true → Skip Step 4, go directly to Step 5
- If `skip_outline_review` is false → Continue to Step 4
### Step 4: Confirmation 2 - Review Outline (Conditional)
**Skip this step** if user selected "No, generate directly" in Step 2.
**Purpose**: User reviews and confirms storyboard + characters before generation.
**Display**:
- Page count and structure
- Art style + Tone combination
- Page-by-page summary (Cover → P1 → P2...)
- Character list with brief descriptions
**Use AskUserQuestion**:
```
header: "Confirm"
question: "Ready to generate images with this outline?"
options:
- label: "Yes, proceed (Recommended)"
description: "Generate character sheet and comic pages"
- label: "Edit storyboard first"
description: "I'll modify storyboard.md before continuing"
- label: "Edit characters first"
description: "I'll modify characters/characters.md before continuing"
- label: "Edit both"
description: "I'll modify both files before continuing"
```
**After response**:
1. If user wants to edit → Wait for user to finish editing, then ask again
2. If user confirms → Continue to Step 5
### Step 5: Generate Images
With confirmed storyboard + art style + tone + aspect ratio:
**Style Reference Loading**:
- Read `references/art-styles/{art}.md` for rendering guidelines
- Read `references/tones/{tone}.md` for mood/color adjustments
- If preset: Read `references/presets/{preset}.md` for special rules
**5.1 Generate Character Reference Sheet** (first):
1. Use Reference Sheet Prompt from `characters/characters.md`
2. Generate → `characters/characters.png`
3. This ensures visual consistency for all subsequent pages
**5.2 Generate Comic Pages**:
**For each page (cover + pages)**:
1. Save prompt to `prompts/NN-{cover|page}-[slug].md` (in user's preferred language)
2. Generate image using confirmed art style and tone guidelines
3. Report progress after each generation
**Watermark Application** (if enabled in preferences):
Add to each image generation prompt:
```
Include a subtle watermark "[content]" positioned at [position]
with approximately [opacity*100]% visibility. The watermark should
be legible but not distracting from the comic panels and storytelling.
Ensure watermark does not overlap speech bubbles or key action.
```
Reference: `references/config/watermark-guide.md`
**Image Generation Skill Selection**:
- Check available image generation skills
- If multiple skills available, ask user preference
**Character Reference Handling** (IMPORTANT for visual consistency):
1. Read the selected image generation skill's SKILL.md
2. Check if it supports reference image input (look for parameters like `--ref`, `--reference`, `--image-ref`, etc.)
| Skill Support | Action |
|---------------|--------|
| **Supports reference image** | Pass `characters/characters.png` using the skill's reference parameter |
| **Does NOT support reference image** | Prepend `characters/characters.md` content to each page prompt |
Always verify the exact parameter name from the skill's documentation before calling.
**Session Management**:
If image generation skill supports `--sessionId`:
1. Generate unique session ID: `comic-{topic-slug}-{timestamp}`
2. Use same session ID for all pages
3. Ensures visual consistency across generated images
### Step 6: Merge to PDF
After all images generated:
```bash
npx -y bun ${SKILL_DIR}/scripts/merge-to-pdf.ts <comic-dir>
```
Creates `{topic-slug}.pdf` with all pages as full-page images.
### Step 7: Completion Report
```
Comic Complete!
Title: [title] | Art: [art] | Tone: [tone] | Pages: [count] | Aspect: [ratio] | Language: [lang]
Watermark: [enabled/disabled]
Location: [path]
✓ analysis.md
✓ characters.png
✓ 00-cover-[slug].png ... NN-page-[slug].png
✓ {topic-slug}.pdf
```
## Page Modification
| Action | Steps |
|--------|-------|
| **Edit** | Update prompt → Regenerate image → Regenerate PDF |
| **Add** | Create prompt at position → Generate image → Renumber subsequent (NN+1) → Update storyboard → Regenerate PDF |
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update storyboard → Regenerate PDF |
**File naming**: `NN-{cover|page}-[slug].png` (e.g., `03-page-enigma-machine.png`)
- Slugs: kebab-case, unique, derived from content
- Renumbering: Update NN prefix only, slugs unchanged
## Preset: Ohmsha
Default: Use Doraemon characters (大雄=learner, 哆啦A梦=mentor, 胖虎=challenge, 静香=support). Custom characters via `--characters "Student:小明,Mentor:教授"` or character presets in EXTEND.md.
Requirements: Visual metaphors, NO talking heads, narrative page titles.
See `references/presets/ohmsha.md` and `references/ohmsha-guide.md` for details.
## References ## References
Detailed templates and guidelines in `references/` directory:
**Core Templates**: **Core Templates**:
- `analysis-framework.md` - Deep content analysis for comic adaptation - [analysis-framework.md](references/analysis-framework.md) - Deep content analysis
- `character-template.md` - Character definition format and examples - [character-template.md](references/character-template.md) - Character definition format
- `storyboard-template.md` - Storyboard structure and panel breakdown - [storyboard-template.md](references/storyboard-template.md) - Storyboard structure
- `ohmsha-guide.md` - Ohmsha manga style specifics - [ohmsha-guide.md](references/ohmsha-guide.md) - Ohmsha manga specifics
**Style Definitions** (new 3-dimension system): **Style Definitions**:
- `art-styles/` - Art style definitions (ligne-claire, manga, realistic, ink-brush, chalk) - `references/art-styles/` - Art styles (ligne-claire, manga, realistic, ink-brush, chalk)
- `tones/` - Tone definitions (neutral, warm, dramatic, romantic, energetic, vintage, action) - `references/tones/` - Tones (neutral, warm, dramatic, romantic, energetic, vintage, action)
- `presets/` - Preset shortcuts with special rules (ohmsha, wuxia, shoujo) - `references/presets/` - Presets with special rules (ohmsha, wuxia, shoujo)
- `layouts/` - Layout definitions (standard, cinematic, dense, splash, mixed, webtoon) - `references/layouts/` - Layouts (standard, cinematic, dense, splash, mixed, webtoon)
**Workflow**:
- [workflow.md](references/workflow.md) - Full workflow details
- [auto-selection.md](references/auto-selection.md) - Content signal analysis
- [partial-workflows.md](references/partial-workflows.md) - Partial workflow options
**Config**: **Config**:
- `config/preferences-schema.md` - EXTEND.md schema - [config/preferences-schema.md](references/config/preferences-schema.md) - EXTEND.md schema
- `config/first-time-setup.md` - First-time setup flow - [config/first-time-setup.md](references/config/first-time-setup.md) - First-time setup
- `config/watermark-guide.md` - Watermark configuration - [config/watermark-guide.md](references/config/watermark-guide.md) - Watermark configuration
## Notes ## Notes
- Auto-retry once on failure | Cartoon alternatives for sensitive figures - Image generation: 10-30 seconds per page
- Use confirmed language preference | Maintain style consistency - Auto-retry once on generation failure
- **Step 2 confirmation required** - do not skip (style selection + outline review option) - Use stylized alternatives for sensitive public figures
- **Step 4 conditional** - only if user requested outline review in Step 2 - Maintain style consistency via session ID
- Watermark/language configured once in EXTEND.md, not asked in confirmations - **Step 2 confirmation required** - do not skip
- **Steps 4/6 conditional** - only if user requested in Step 2
## Extension Support - **Step 7.1 character sheet MUST be generated before pages** - ensures consistency
- **Step 7.2 EVERY page MUST reference characters** - use `--ref` or embed descriptions
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options. - Watermark/language configured once in EXTEND.md
@@ -0,0 +1,58 @@
# Auto Selection
Content signals determine default art + tone + layout (or preset).
## Content Signal Matrix
| Content Signals | Art Style | Tone | Layout | Preset |
|-----------------|-----------|------|--------|--------|
| Tutorial, how-to, beginner | manga | neutral | webtoon | **ohmsha** |
| Computing, AI, programming | manga | neutral | dense | **ohmsha** |
| Technical explanation, educational | manga | neutral | webtoon | **ohmsha** |
| Pre-1950, classical, ancient | realistic | vintage | cinematic | - |
| Personal story, mentor | ligne-claire | warm | standard | - |
| Conflict, breakthrough | (inherit) | dramatic | splash | - |
| Wine, food, business, lifestyle | realistic | neutral | cinematic | - |
| Martial arts, wuxia, xianxia | ink-brush | action | splash | **wuxia** |
| Romance, love, school life | manga | romantic | standard | **shoujo** |
| Biography, balanced | ligne-claire | neutral | mixed | - |
## Preset Recommendation Rules
**When preset is recommended**: Load `presets/{preset}.md` and apply all special rules.
### ohmsha
- **Triggers**: Tutorial, technical, educational, computing, programming, how-to, beginner
- **Special rules**: Visual metaphors, NO talking heads, gadget reveals, Doraemon-style characters
- **Base**: manga + neutral + webtoon/dense
### wuxia
- **Triggers**: Martial arts, wuxia, xianxia, cultivation, swordplay
- **Special rules**: Qi effects, combat visuals, atmospheric elements
- **Base**: ink-brush + action + splash
### shoujo
- **Triggers**: Romance, love story, school life, emotional drama
- **Special rules**: Decorative elements, eye details, romantic beats
- **Base**: manga + romantic + standard
## Compatibility Matrix
Art Style × Tone combinations work best when matched appropriately:
| 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 |
**Note**: Art Style × Tone × Layout can be freely combined. Incompatible combinations work but may produce unexpected results.
## Priority Order
1. User-specified options (`--art`, `--tone`, `--style`)
2. EXTEND.md defaults
3. Content signal analysis → auto-selection
4. Fallback: ligne-claire + neutral + standard
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.5 # 0.1-1.0 (lower default for comics)
preferred_art: null # ligne-claire|manga|realistic|ink-brush|chalk preferred_art: null # ligne-claire|manga|realistic|ink-brush|chalk
preferred_tone: null # neutral|warm|dramatic|romantic|energetic|vintage|action preferred_tone: null # neutral|warm|dramatic|romantic|energetic|vintage|action
@@ -42,7 +41,6 @@ character_presets:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `watermark.position` | enum | bottom-right | Position on image |
| `watermark.opacity` | float | 0.5 | Transparency (0.1-1.0, lower for comics) |
| `preferred_art` | string | null | Art style (ligne-claire, manga, realistic, ink-brush, chalk) | | `preferred_art` | string | null | Art style (ligne-claire, manga, realistic, ink-brush, chalk) |
| `preferred_tone` | string | null | Tone (neutral, warm, dramatic, romantic, energetic, vintage, action) | | `preferred_tone` | string | null | Tone (neutral, warm, dramatic, romantic, energetic, vintage, action) |
| `preferred_layout` | string | null | Layout preference or null | | `preferred_layout` | string | null | Layout preference or null |
@@ -113,7 +111,6 @@ watermark:
enabled: true enabled: true
content: "@comicstudio" content: "@comicstudio"
position: bottom-right position: bottom-right
opacity: 0.4
preferred_art: manga preferred_art: manga
preferred_tone: neutral preferred_tone: neutral
@@ -28,19 +28,6 @@ description: Watermark configuration guide for baoyu-comic
| `bottom-center` | Webtoon vertical scroll, centered designs | Text-heavy bottom area | | `bottom-center` | Webtoon vertical scroll, centered designs | Text-heavy bottom area |
| `top-right` | **Not recommended for comics** | Always - conflicts with page numbers | | `top-right` | **Not recommended for comics** | Always - conflicts with page numbers |
## Opacity for Comics
Comics typically use lower opacity than infographics to avoid disrupting panel flow:
| Opacity | Visual Effect | Use Case |
|---------|---------------|----------|
| 0.3 | Very subtle, barely visible | **Recommended for comics** |
| 0.4 | Light presence | Balance of visibility and subtlety |
| 0.5 | Noticeable | Standard comics default |
| 0.6+ | Strong presence | Not recommended (distracting) |
**Default**: 0.5 (compared to 0.7 for infographics)
## Content Format ## Content Format
| Format | Example | Style | | Format | Example | Style |
@@ -63,10 +50,9 @@ Comics typically use lower opacity than infographics to avoid disrupting panel f
When watermark is enabled, add to image generation prompt: When watermark is enabled, add to image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the comic panels
be legible but not distracting from the comic panels and storytelling. and storytelling. Ensure watermark does not overlap speech bubbles or key action.
Ensure watermark does not overlap speech bubbles or key action.
``` ```
## Common Issues ## Common Issues
@@ -76,5 +62,5 @@ Ensure watermark does not overlap speech bubbles or key action.
| Watermark invisible on dark panels | Adjust contrast or add subtle outline | | Watermark invisible on dark panels | Adjust contrast or add subtle outline |
| Watermark overlaps speech bubble | Change position or lower on page | | Watermark overlaps speech bubble | Change position or lower on page |
| Watermark inconsistent across pages | Use session ID for consistency | | Watermark inconsistent across pages | Use session ID for consistency |
| Watermark too prominent | Decrease opacity (0.3-0.4 for comics) | | Watermark too prominent | Change position or reduce size |
| Conflicts with page number | Never use top-right position | | Conflicts with page number | Never use top-right position |
@@ -0,0 +1,123 @@
# Partial Workflows
Options to run specific parts of the workflow.
## Options Summary
| Option | Steps Executed | Output |
|--------|----------------|--------|
| `--storyboard-only` | 1-3 | `storyboard.md` + `characters/` |
| `--prompts-only` | 1-5 | + `prompts/*.md` |
| `--images-only` | 7-9 | + images + PDF |
| `--regenerate N` | 7 (partial) | Specific page(s) + PDF |
---
## Using `--storyboard-only`
Generate storyboard and characters without prompts or images:
```bash
/baoyu-comic content.md --storyboard-only
```
**Workflow**: Steps 1-3 only (stop after storyboard + characters)
**Output**:
- `analysis.md`
- `storyboard.md`
- `characters/characters.md`
**Use case**: Review and edit the storyboard before generating images. Useful for:
- Getting feedback on the narrative structure
- Making manual adjustments to panel layouts
- Defining custom characters
---
## Using `--prompts-only`
Generate storyboard, characters, and prompts without images:
```bash
/baoyu-comic content.md --prompts-only
```
**Workflow**: Steps 1-5 (generate prompts, skip images)
**Output**:
- `analysis.md`
- `storyboard.md`
- `characters/characters.md`
- `prompts/*.md`
**Use case**: Review and edit prompts before image generation. Useful for:
- Fine-tuning image generation prompts
- Ensuring visual consistency before committing to generation
- Making style adjustments at the prompt level
---
## Using `--images-only`
Generate images from existing prompts (starts at Step 7):
```bash
/baoyu-comic comic/topic-slug/ --images-only
```
**Workflow**: Skip to Step 7, then 8-9
**Prerequisites** (must exist in directory):
- `prompts/` directory with page prompt files
- `storyboard.md` with style information
- `characters/characters.md` with character definitions
**Output**:
- `characters/characters.png` (if not exists)
- `NN-{cover|page}-[slug].png` images
- `{topic-slug}.pdf`
**Use case**: Re-generate images after editing prompts. Useful for:
- Recovering from failed image generation
- Trying different image generation settings
- Regenerating after manual prompt edits
---
## Using `--regenerate`
Regenerate specific pages only:
```bash
# Single page
/baoyu-comic comic/topic-slug/ --regenerate 3
# Multiple pages
/baoyu-comic comic/topic-slug/ --regenerate 2,5,8
# Cover page
/baoyu-comic comic/topic-slug/ --regenerate 0
```
**Workflow**:
1. Read existing prompts for specified pages
2. Regenerate images only for those pages
3. Regenerate PDF
**Prerequisites** (must exist):
- `prompts/NN-{cover|page}-[slug].md` for specified pages
- `characters/characters.png` (for reference)
**Output**:
- Regenerated `NN-{cover|page}-[slug].png` for specified pages
- Updated `{topic-slug}.pdf`
**Use case**: Fix specific pages without regenerating entire comic. Useful for:
- Fixing a single problematic page
- Iterating on specific visuals
- Regenerating pages after prompt edits
**Page numbering**:
- `0` = Cover page
- `1-N` = Content pages
@@ -41,16 +41,18 @@ Every technical concept MUST be visualized as a metaphor:
### Character Roles (Required) ### Character Roles (Required)
Define characters before generating: **DEFAULT: Use Doraemon characters** unless user explicitly specifies `--characters` or has character presets in EXTEND.md.
| Role | Default | Traits | | Role | Default Character | Visual | Traits |
|------|---------|--------| |------|-------------------|--------|--------|
| Student (Role A) | 大雄 | Confused, asks basic but crucial questions, represents reader | | Student (Role A) | 大雄 (Nobita) | Boy, 10yo, round glasses, black hair, yellow shirt, navy shorts | Confused, asks basic but crucial questions, represents reader |
| Mentor (Role B) | 哆啦A梦 | Knowledgeable, patient, uses gadgets as technical metaphors | | Mentor (Role B) | 哆啦A梦 (Doraemon) | Blue robot cat, white belly, 4D pocket, red nose, golden bell | Knowledgeable, patient, uses gadgets as technical metaphors |
| Challenge (Role C, optional) | 胖虎 | Represents misunderstanding, or "noise" in the data | | Challenge (Role C) | 胖虎 (Gian) | Stocky boy, small eyes, orange shirt | Represents misunderstanding, or "noise" in the data |
| Support (Role D, optional) | 静香 | Asks clarifying questions, provides alternative perspectives | | Support (Role D) | 静香 (Shizuka) | Cute girl, black short hair, pink dress | Asks clarifying questions, provides alternative perspectives |
Custom characters via `--characters "Student:小明,Mentor:教授"` or EXTEND.md presets. **IMPORTANT**: These Doraemon characters ARE the default for ohmsha preset. Generate character definitions using these exact characters unless user requests otherwise.
To use custom characters: `--characters "Student:小明,Mentor:教授"` or define in EXTEND.md.
### Page Title Convention ### Page Title Convention
+505
View File
@@ -0,0 +1,505 @@
# Complete Workflow
Full workflow for generating knowledge comics.
## Progress Checklist
Copy and track progress:
```
Comic Progress:
- [ ] Step 1: Setup & Analyze
- [ ] 1.1 Load preferences
- [ ] 1.2 Analyze content
- [ ] 1.3 Check existing ⚠️ REQUIRED
- [ ] Step 2: Confirmation 1 - Style & options ⚠️ REQUIRED
- [ ] Step 3: Generate storyboard + characters
- [ ] Step 4: Review outline (conditional)
- [ ] Step 5: Generate prompts
- [ ] Step 6: Review prompts (conditional)
- [ ] Step 7: Generate images
- [ ] Step 8: Merge to PDF
- [ ] Step 9: Completion report
```
## Flow Diagram
```
Input → Preferences → Analyze → [Check Existing?] → [Confirm 1: Style + Reviews] → Storyboard → [Review Outline?] → Prompts → [Review Prompts?] → Images → PDF → Complete
```
---
## Step 1: Setup & Analyze
### 1.1 Load Preferences (EXTEND.md)
Use Bash to check EXTEND.md existence (priority order):
```bash
# Check project-level first
test -f .baoyu-skills/baoyu-comic/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-comic/EXTEND.md" && echo "user"
```
| Path | Location |
|------|----------|
| `.baoyu-skills/baoyu-comic/EXTEND.md` | Project directory |
| `$HOME/.baoyu-skills/baoyu-comic/EXTEND.md` | User home |
**When EXTEND.md Found** → Read, parse, **output summary to user**:
```
📋 Loaded preferences from [full path]
├─ Watermark: [enabled/disabled] [content if enabled]
├─ Art Style: [style name or "auto-select"]
├─ Tone: [tone name or "auto-select"]
├─ Layout: [layout or "auto-select"]
├─ Language: [language or "auto-detect"]
└─ Character presets: [count] defined
```
**MUST output this summary** so user knows their current configuration. Do not skip or silently load.
**When EXTEND.md Not Found** → First-time setup:
1. Inform user: "No preferences found. Let's set up your defaults."
2. Use AskUserQuestion to collect preferences (see `config/first-time-setup.md`)
3. Create EXTEND.md at user-chosen location
4. Confirm: "✓ Preferences saved to [path]"
**EXTEND.md Supports**: Watermark | Preferred art/tone/layout | Custom style definitions | Character presets | Language preference
Schema: `config/preferences-schema.md`
**Important**: Once EXTEND.md exists, watermark, language, and style defaults are NOT asked again in Confirmation 1 or 2. These are session-persistent settings.
### 1.2 Analyze Content → `analysis.md`
Read source content, save it if needed, and perform deep analysis.
**Actions**:
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
2. Read source content
3. **Deep analysis** following `analysis-framework.md`:
- Target audience identification
- Value proposition for readers
- Core themes and narrative potential
- Key figures and their story arcs
4. Detect source language
5. **Determine language**:
- If EXTEND.md has `language` → use it
- Else if `--lang` option provided → use it
- Else → use detected source language
6. Determine recommended page count:
- Short story: 5-8 pages
- Medium complexity: 9-15 pages
- Full biography: 16-25 pages
7. Analyze content signals for art/tone/layout recommendations
8. **Save to `analysis.md`**
**analysis.md Format**: YAML front matter (title, topic, time_span, source_language, user_language, aspect_ratio, recommended_page_count, recommended_art, recommended_tone) + sections for Target Audience, Value Proposition, Core Themes, Key Figures & Story Arcs, Content Signals, Recommended Approaches. See `analysis-framework.md` for full template.
### 1.3 Check Existing Content ⚠️ REQUIRED
**MUST execute before proceeding to Step 2.**
Use Bash to check if output directory exists:
```bash
test -d "comic/{topic-slug}" && echo "exists"
```
**If directory exists**, use AskUserQuestion:
```
header: "Existing"
question: "Existing content found. How to proceed?"
options:
- label: "Regenerate storyboard"
description: "Keep images, regenerate storyboard and characters only"
- label: "Regenerate images"
description: "Keep storyboard, 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 result and handle accordingly:
- **Regenerate storyboard**: Skip to Step 3, preserve `prompts/` and images
- **Regenerate images**: Skip to Step 7, use existing prompts
- **Backup and regenerate**: Move directory, start fresh from Step 2
- **Exit**: End workflow immediately
---
## Step 2: Confirmation 1 - Style & Options ⚠️
**Purpose**: Select visual style + decide whether to review outline before generation. **Do NOT skip.**
**Note**: Watermark and language already configured in EXTEND.md (Step 1).
**Display summary**:
- Content type + topic identified
- Key figures extracted
- Time span detected
- Recommended page count
- Language: [from EXTEND.md or detected]
- **Recommended style**: [art] + [tone] (based on content signals)
**Use AskUserQuestion** for:
### Question 1: Visual Style
If a preset is recommended (see `auto-selection.md`), show it first:
```
header: "Style"
question: "Which visual style for this comic?"
options:
- label: "[preset name] preset (Recommended)" # If preset recommended
description: "[preset description] - includes special rules"
- label: "[recommended art] + [recommended tone] (Recommended)" # If no preset
description: "Best match for your content based on analysis"
- label: "ligne-claire + neutral"
description: "Classic educational, Logicomix style"
- label: "ohmsha preset"
description: "Educational manga with visual metaphors, gadgets, NO talking heads"
- label: "Custom"
description: "Specify your own art + tone or preset"
```
**Preset vs Art+Tone**: Presets include special rules beyond art+tone. `ohmsha` = manga + neutral + visual metaphor rules + character roles + NO talking heads. Plain `manga + neutral` does NOT include these rules.
### Question 2: Narrative Focus (multiSelect: true)
```
header: "Focus"
question: "What should the comic emphasize? (Select all that apply)"
options:
- label: "Biography/life story"
description: "Follow a person's journey through key life events"
- label: "Concept explanation"
description: "Break down complex ideas visually"
- label: "Historical event"
description: "Dramatize important historical moments"
- label: "Tutorial/how-to"
description: "Step-by-step educational guide"
```
### Question 3: Target Audience
```
header: "Audience"
question: "Who is the primary reader?"
options:
- label: "General readers"
description: "Broad appeal, accessible content"
- label: "Students/learners"
description: "Educational focus, clear explanations"
- label: "Industry professionals"
description: "Technical depth, domain knowledge"
- label: "Children/young readers"
description: "Simplified language, engaging visuals"
```
### Question 4: Outline Review
```
header: "Review"
question: "Do you want to review the outline before image generation?"
options:
- label: "Yes, let me review (Recommended)"
description: "Review storyboard and characters before generating images"
- label: "No, generate directly"
description: "Skip outline review, start generating immediately"
```
### Question 5: Prompt Review
```
header: "Prompts"
question: "Review prompts before generating images?"
options:
- label: "Yes, review prompts (Recommended)"
description: "Review image generation prompts before generating"
- label: "No, skip prompt review"
description: "Proceed directly to image generation"
```
**After response**:
1. Update `analysis.md` with user preferences
2. **Store `skip_outline_review`** flag based on Question 4 response
3. **Store `skip_prompt_review`** flag based on Question 5 response
4. → Step 3
---
## Step 3: Generate Storyboard + Characters
Create storyboard and character definitions using the confirmed style from Step 2.
**Loading Style References**:
- Art style: `art-styles/{art}.md`
- Tone: `tones/{tone}.md`
- If preset (ohmsha/wuxia/shoujo): also load `presets/{preset}.md`
**Generate**:
1. **Storyboard** (`storyboard.md`):
- YAML front matter with art_style, tone, layout, aspect_ratio
- Cover design
- Each page: layout, panel breakdown, visual prompts
- **Written in user's preferred language** (from Step 1)
- Reference: `storyboard-template.md`
- **If using preset**: Load and apply preset rules from `presets/`
2. **Character definitions** (`characters/characters.md`):
- Visual specs matching the art style (in user's preferred language)
- Include Reference Sheet Prompt for later image generation
- Reference: `character-template.md`
- **If using ohmsha preset**: Use default Doraemon characters (see below)
**Ohmsha Default Characters** (use these unless user specifies `--characters`):
| Role | Character | Visual Description |
|------|-----------|-------------------|
| Student | 大雄 (Nobita) | Japanese boy, 10yo, round glasses, black hair parted in middle, yellow shirt, navy shorts |
| Mentor | 哆啦A梦 (Doraemon) | Round blue robot cat, big white eyes, red nose, whiskers, white belly with 4D pocket, golden bell, no ears |
| Challenge | 胖虎 (Gian) | Stocky boy, rough features, small eyes, orange shirt |
| Support | 静香 (Shizuka) | Cute girl, black short hair, pink dress, gentle expression |
These are the canonical ohmsha-style characters. Do NOT create custom characters for ohmsha unless explicitly requested.
**After generation**:
- If `skip_outline_review` is true → Skip Step 4, go directly to Step 5
- If `skip_outline_review` is false → Continue to Step 4
---
## Step 4: Review Outline (Conditional)
**Skip this step** if user selected "No, generate directly" in Step 2.
**Purpose**: User reviews and confirms storyboard + characters before generation.
**Display**:
- Page count and structure
- Art style + Tone combination
- Page-by-page summary (Cover → P1 → P2...)
- Character list with brief descriptions
**Use AskUserQuestion**:
```
header: "Confirm"
question: "Ready to generate images with this outline?"
options:
- label: "Yes, proceed (Recommended)"
description: "Generate character sheet and comic pages"
- label: "Edit storyboard first"
description: "I'll modify storyboard.md before continuing"
- label: "Edit characters first"
description: "I'll modify characters/characters.md before continuing"
- label: "Edit both"
description: "I'll modify both files before continuing"
```
**After response**:
1. If user wants to edit → Wait for user to finish editing, then ask again
2. If user confirms → Continue to Step 5
---
## Step 5: Generate Prompts
Create image generation prompts for all pages.
**Style Reference Loading**:
- Read `art-styles/{art}.md` for rendering guidelines
- Read `tones/{tone}.md` for mood/color adjustments
- If preset: Read `presets/{preset}.md` for special rules
**For each page (cover + pages)**:
1. Create prompt following art style + tone guidelines
2. Include character visual descriptions for consistency
3. Save to `prompts/NN-{cover|page}-[slug].md`
**Prompt File Format**:
```markdown
# Page NN: [Title]
## Visual Style
Art: [art style] | Tone: [tone] | Layout: [layout type]
## Character Reference
[Character descriptions from characters/characters.md]
## Panel Breakdown
[From storyboard.md - panel descriptions, actions, dialogue]
## Generation Prompt
[Combined prompt for image generation skill]
```
**Watermark Application** (if enabled in preferences):
Add to each prompt:
```
Include a subtle watermark "[content]" positioned at [position]
with approximately [opacity*100]% visibility. The watermark should
be legible but not distracting from the comic panels and storytelling.
Ensure watermark does not overlap speech bubbles or key action.
```
Reference: `config/watermark-guide.md`
**After generation**:
- If `skip_prompt_review` is true → Skip Step 6, go directly to Step 7
- If `skip_prompt_review` is false → Continue to Step 6
---
## Step 6: Review Prompts (Conditional)
**Skip this step** if user selected "No, skip prompt review" in Step 2.
**Purpose**: User reviews and confirms prompts before image generation.
**Display prompt summary table**:
| Page | Title | Key Elements |
|------|-------|--------------|
| Cover | [title] | [main visual] |
| P1 | [title] | [key elements] |
| ... | ... | ... |
**Use AskUserQuestion**:
```
header: "Confirm"
question: "Ready to generate images with these prompts?"
options:
- label: "Yes, proceed (Recommended)"
description: "Generate all comic page images"
- label: "Edit prompts first"
description: "I'll modify prompts/*.md before continuing"
- label: "Regenerate prompts"
description: "Regenerate all prompts with different approach"
```
**After response**:
1. If user wants to edit → Wait for user to finish editing, then ask again
2. If user wants to regenerate → Go back to Step 5
3. If user confirms → Continue to Step 7
---
## Step 7: Generate Images
With confirmed prompts from Step 5/6:
### 7.1 Generate Character Reference Sheet (first)
1. Use Reference Sheet Prompt from `characters/characters.md`
2. Generate → `characters/characters.png`
3. This ensures visual consistency for all subsequent pages
### 7.2 Generate Comic Pages
**CRITICAL: Character Reference is MANDATORY** for visual consistency across all pages.
**Before generating any page**:
1. Read the image generation skill's SKILL.md
2. Check if it supports reference image input (`--ref`, `--reference`, etc.)
3. Choose the appropriate strategy below
**Character Reference Strategy**:
| Skill Capability | Strategy | Action |
|------------------|----------|--------|
| Supports `--ref` | **Strategy A** | Pass `characters/characters.png` with EVERY page |
| Does NOT support `--ref` | **Strategy B** | Prepend character descriptions to EVERY prompt |
**Strategy A: Using `--ref` parameter** (e.g., baoyu-image-gen)
```bash
# Each page generation MUST include --ref
npx -y bun ${SKILL_DIR}/../baoyu-image-gen/scripts/main.ts \
--promptfiles prompts/01-page-xxx.md \
--image 01-page-xxx.png \
--ar 3:4 \
--ref characters/characters.png
```
**Strategy B: Embedding character descriptions in prompt**
When skill does NOT support reference images, create combined prompt files:
```markdown
# prompts/01-page-xxx.md (with embedded character reference)
## Character Reference (maintain consistency)
[Copy relevant sections from characters/characters.md here]
- 大雄: Japanese boy, round glasses, yellow shirt, navy shorts...
- 哆啦A梦: Round blue robot cat, white belly, red nose, golden bell...
## Page Content
[Original page prompt here]
```
**For each page (cover + pages)**:
1. Read prompt from `prompts/NN-{cover|page}-[slug].md`
2. Generate image using Strategy A or B (based on skill capability)
3. Save to `NN-{cover|page}-[slug].png`
4. Report progress after each generation: "Generated X/N: [page title]"
**Session Management**:
If image generation skill supports `--sessionId`:
1. Generate unique session ID: `comic-{topic-slug}-{timestamp}`
2. Use same session ID for all pages
3. Ensures visual consistency across generated images
---
## Step 8: Merge to PDF
After all images generated:
```bash
npx -y bun ${SKILL_DIR}/scripts/merge-to-pdf.ts <comic-dir>
```
Creates `{topic-slug}.pdf` with all pages as full-page images.
---
## Step 9: Completion Report
```
Comic Complete!
Title: [title] | Art: [art] | Tone: [tone] | Pages: [count] | Aspect: [ratio] | Language: [lang]
Watermark: [enabled/disabled]
Location: [path]
✓ analysis.md
✓ characters.png
✓ 00-cover-[slug].png ... NN-page-[slug].png
✓ {topic-slug}.pdf
```
---
## Page Modification
| Action | Steps |
|--------|-------|
| **Edit** | Update prompt → Regenerate image → Regenerate PDF |
| **Add** | Create prompt at position → Generate image → Renumber subsequent (NN+1) → Update storyboard → Regenerate PDF |
| **Delete** | Remove files → Renumber subsequent (NN-1) → Update storyboard → Regenerate PDF |
**File naming**: `NN-{cover|page}-[slug].png` (e.g., `03-page-enigma-machine.png`)
- Slugs: kebab-case, unique, derived from content
- Renumbering: Update NN prefix only, slugs unchanged
+49 -148
View File
@@ -1,188 +1,89 @@
--- ---
name: baoyu-compress-image name: baoyu-compress-image
description: Cross-platform image compression skill. Converts images to WebP by default with PNG-to-PNG support. Uses system tools (sips, cwebp, ImageMagick) with Sharp fallback. description: Compresses images to WebP (default) or PNG with automatic tool selection. Use when user asks to "compress image", "optimize image", "convert to webp", or reduce image file size.
--- ---
# Image Compressor # Image Compressor
Cross-platform image compression with WebP default output, PNG-to-PNG support, preferring system tools with Sharp fallback. Compresses images using best available tool (sips → cwebp → ImageMagick → Sharp).
## Script Directory ## Script Directory
**Important**: All scripts are located in the `scripts/` subdirectory of this skill. Scripts in `scripts/` subdirectory. Replace `${SKILL_DIR}` with this SKILL.md's directory path.
**Agent Execution Instructions**:
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts`
3. Replace all `${SKILL_DIR}` in this document with the actual path
**Script Reference**:
| Script | Purpose | | Script | Purpose |
|--------|---------| |--------|---------|
| `scripts/main.ts` | CLI entry point for image compression | | `scripts/main.ts` | Image compression CLI |
## Quick Start ## Preferences (EXTEND.md)
Use Bash to check EXTEND.md existence (priority order):
```bash ```bash
# Compress to WebP (default) # Check project-level first
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png test -f .baoyu-skills/baoyu-compress-image/EXTEND.md && echo "project"
# Keep original format (PNG → PNG) # Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png --format png test -f "$HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md" && echo "user"
# Custom quality
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png -q 75
# Process directory
npx -y bun ${SKILL_DIR}/scripts/main.ts ./images/ -r
``` ```
## Commands ┌────────────────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├────────────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-compress-image/EXTEND.md │ Project directory │
├────────────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-compress-image/EXTEND.md │ User home │
└────────────────────────────────────────────────────────┴───────────────────┘
### Single File Compression ┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default format | Default quality | Keep original preference
## Usage
```bash ```bash
# Basic (converts to WebP, replaces original) npx -y bun ${SKILL_DIR}/scripts/main.ts <input> [options]
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png
# Custom output path
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png -o compressed.webp
# Keep original file
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png --keep
# Custom quality (0-100, default: 80)
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png -q 75
# Keep original format
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png -f png
```
### Directory Processing
```bash
# Process all images in directory
npx -y bun ${SKILL_DIR}/scripts/main.ts ./images/
# Recursive processing
npx -y bun ${SKILL_DIR}/scripts/main.ts ./images/ -r
# With custom quality
npx -y bun ${SKILL_DIR}/scripts/main.ts ./images/ -r -q 75
```
### Output Formats
```bash
# Plain text (default)
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png
# JSON output
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png --json
``` ```
## Options ## Options
| Option | Short | Description | Default | | Option | Short | Description | Default |
|--------|-------|-------------|---------| |--------|-------|-------------|---------|
| `<input>` | | Input file or directory | Required | | `<input>` | | File or directory | Required |
| `--output <path>` | `-o` | Output path | Same path, new extension | | `--output` | `-o` | Output path | Same path, new ext |
| `--format <fmt>` | `-f` | webp, png, jpeg | webp | | `--format` | `-f` | webp, png, jpeg | webp |
| `--quality <n>` | `-q` | Quality 0-100 | 80 | | `--quality` | `-q` | Quality 0-100 | 80 |
| `--keep` | `-k` | Keep original file | false | | `--keep` | `-k` | Keep original | false |
| `--recursive` | `-r` | Process directories recursively | false | | `--recursive` | `-r` | Process subdirs | false |
| `--json` | | JSON output | false | | `--json` | | JSON output | false |
| `--help` | `-h` | Show help | |
## Compressor Selection ## Examples
Priority order (auto-detected): ```bash
# Single file → WebP (replaces original)
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png
1. **sips** (macOS built-in, WebP support since macOS 11) # Keep PNG format
2. **cwebp** (Google's official WebP tool) npx -y bun ${SKILL_DIR}/scripts/main.ts image.png -f png --keep
3. **ImageMagick** (`convert` command)
4. **Sharp** (npm package, auto-installed by Bun)
The skill automatically selects the best available compressor. # Directory recursive
npx -y bun ${SKILL_DIR}/scripts/main.ts ./images/ -r -q 75
## Output Format # JSON output
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png --json
### Text Mode (default) ```
**Output**:
``` ```
image.png → image.webp (245KB → 89KB, 64% reduction) image.png → image.webp (245KB → 89KB, 64% reduction)
``` ```
### JSON Mode
```json
{
"input": "image.png",
"output": "image.webp",
"inputSize": 250880,
"outputSize": 91136,
"ratio": 0.36,
"compressor": "sips"
}
```
### Directory JSON Mode
```json
{
"files": [...],
"summary": {
"totalFiles": 10,
"totalInputSize": 2508800,
"totalOutputSize": 911360,
"ratio": 0.36,
"compressor": "sips"
}
}
```
## Examples
### Compress single image
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts photo.png
# photo.png → photo.webp (1.2MB → 340KB, 72% reduction)
```
### Compress with custom quality
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts photo.png -q 60
# photo.png → photo.webp (1.2MB → 280KB, 77% reduction)
```
### Keep original format
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts screenshot.png -f png --keep
# screenshot.png → screenshot-compressed.png (500KB → 380KB, 24% reduction)
```
### Process entire directory
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts ./screenshots/ -r
# Processed 15 files: 12.5MB → 4.2MB (66% reduction)
```
### Get JSON for scripting
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts image.png --json | jq '.ratio'
```
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-compress-image/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-compress-image/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
+266 -63
View File
@@ -1,23 +1,24 @@
--- ---
name: baoyu-cover-image name: baoyu-cover-image
description: Generates article cover images with 20 hand-drawn styles and auto-style selection. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图". description: Generates article cover images with 4 dimensions (type, style, text, mood) and 20 hand-drawn styles. Supports cinematic (2.35:1), widescreen (16:9), and square (1:1) aspects. Use when user asks to "generate cover image", "create article cover", "make cover", or mentions "封面图".
--- ---
# Cover Image Generator # Cover Image Generator
Generate elegant cover images for articles with multiple style options. Generate elegant cover images for articles with 4-dimensional customization.
## Usage ## Usage
```bash ```bash
# Auto-select style and aspect based on content # Auto-select all dimensions based on content
/baoyu-cover-image path/to/article.md /baoyu-cover-image path/to/article.md
# Specify style # Quick mode: skip confirmation, use auto-selection
/baoyu-cover-image article.md --style blueprint /baoyu-cover-image article.md --quick
# Specify aspect ratio # Specify dimensions
/baoyu-cover-image article.md --aspect 16:9 /baoyu-cover-image article.md --type conceptual --style blueprint
/baoyu-cover-image article.md --text title-subtitle --mood bold
# Visual only (no title text) # Visual only (no title text)
/baoyu-cover-image article.md --no-title /baoyu-cover-image article.md --no-title
@@ -27,7 +28,7 @@ Generate elegant cover images for articles with multiple style options.
[paste content] [paste content]
# Direct input with options # Direct input with options
/baoyu-cover-image --style notion --aspect 1:1 /baoyu-cover-image --style notion --aspect 1:1 --quick
[paste content] [paste content]
``` ```
@@ -35,20 +36,25 @@ Generate elegant cover images for articles with multiple style options.
| Option | Description | | Option | Description |
|--------|-------------| |--------|-------------|
| `--type <name>` | Cover type (see Type Gallery) | | `--type <name>` | Cover type: hero, conceptual, typography, metaphor, scene, minimal |
| `--style <name>` | Cover style (see Style Gallery) | | `--style <name>` | Cover style (see Style Gallery) |
| `--aspect <ratio>` | 2.35:1 (default), 16:9, 1:1 | | `--text <level>` | Text density: none, title-only, title-subtitle, text-rich |
| `--mood <level>` | Emotional intensity: subtle, balanced, bold |
| `--aspect <ratio>` | 16:9 (default), 2.35:1, 4:3, 3:2, 1:1, 3:4 |
| `--lang <code>` | Title language (en, zh, ja, etc.) | | `--lang <code>` | Title language (en, zh, ja, etc.) |
| `--no-title` | Visual only, no title text | | `--no-title` | Alias for `--text none` |
| `--quick` | Skip confirmation, use auto-selection for missing dimensions |
## Two Dimensions ## Four Dimensions
| Dimension | Controls | Examples | | Dimension | Controls | Values | Default |
|-----------|----------|----------| |-----------|----------|--------|---------|
| **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal | | **Type** | Visual composition, information structure | hero, conceptual, typography, metaphor, scene, minimal | auto |
| **Style** | Visual aesthetics, colors, mood | elegant, blueprint, notion, warm, minimal, watercolor | | **Style** | Visual aesthetics, colors, technique | 20 built-in styles | auto |
| **Text** | Text density, information hierarchy | none, title-only, title-subtitle, text-rich | title-only |
| **Mood** | Emotional intensity, visual weight | subtle, balanced, bold | balanced |
Type × Style can be freely combined. Example: `--type conceptual --style blueprint` creates technical concept visualization with schematic aesthetics. Dimensions can be freely combined. Example: `--type conceptual --style blueprint --text title-only --mood subtle` creates a calm technical concept visualization.
## Type Gallery ## Type Gallery
@@ -101,19 +107,6 @@ When `--type` is omitted, select based on content signals:
Style definitions: [references/styles/](references/styles/) Style definitions: [references/styles/](references/styles/)
## Type × Style Compatibility
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Auto Style Selection ## Auto Style Selection
When `--style` is omitted, select based on content signals: When `--style` is omitted, select based on content signals:
@@ -140,6 +133,89 @@ When `--style` is omitted, select based on content signals:
| Lifestyle, travel | `watercolor` | | Lifestyle, travel | `watercolor` |
| Business, professional | `elegant` | | Business, professional | `elegant` |
## Text Dimension
| Value | Title | Subtitle | Tags | Use Case |
|-------|:-----:|:--------:|:----:|----------|
| `none` | - | - | - | Pure visual, no text |
| `title-only` | ✓ (≤8字) | - | - | Simple headline (default) |
| `title-subtitle` | ✓ | ✓ (≤15字) | - | Title + supporting context |
| `text-rich` | ✓ | ✓ | ✓ (2-4) | Information-dense |
Full guide: [references/dimensions/text.md](references/dimensions/text.md)
## Auto Text Selection
When `--text` is omitted, select based on content signals:
| Signals | Text Level |
|---------|------------|
| Visual-only, photography, abstract, art | `none` |
| Article, blog, standard cover | `title-only` |
| Series, tutorial, technical with context | `title-subtitle` |
| Announcement, features, multiple points, infographic | `text-rich` |
Default: `title-only`
## Mood Dimension
| Value | Contrast | Saturation | Weight | Use Case |
|-------|:--------:|:----------:|:------:|----------|
| `subtle` | Low | Muted | Light | Corporate, thought leadership |
| `balanced` | Medium | Normal | Medium | General articles (default) |
| `bold` | High | Vivid | Heavy | Announcements, promotions |
Full guide: [references/dimensions/mood.md](references/dimensions/mood.md)
## Auto Mood Selection
When `--mood` is omitted, select based on content signals:
| Signals | Mood Level |
|---------|------------|
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
| General, educational, standard, blog, documentation | `balanced` |
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
Default: `balanced`
## Compatibility Matrices
### Type × Style
| | elegant | blueprint | notion | warm | minimal | watercolor | bold-editorial | dark-atmospheric |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| hero | ✓✓ | ✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓✓ | ✓ | ✓✓ | ✗ | ✓ | ✓ |
| typography | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✗ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✓ |
| scene | ✓ | ✗ | ✗ | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ |
| minimal | ✓✓ | ✓ | ✓✓ | ✓ | ✓✓ | ✓ | ✗ | ✓ |
### Type × Text
| | none | title-only | title-subtitle | text-rich |
|---|:---:|:---:|:---:|:---:|
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
| scene | ✓✓ | ✓ | ✓ | ✗ |
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
### Type × Mood
| | subtle | balanced | bold |
|---|:---:|:---:|:---:|
| hero | ✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓ |
| typography | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓✓ | ✓ |
| scene | ✓✓ | ✓✓ | ✓ |
| minimal | ✓✓ | ✓✓ | ✗ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## File Structure ## File Structure
Each session creates an independent directory named by content slug: Each session creates an independent directory named by content slug:
@@ -175,7 +251,7 @@ Copy and track progress:
Cover Image Progress: Cover Image Progress:
- [ ] Step 0: Check preferences (EXTEND.md) ⚠️ REQUIRED if not found - [ ] Step 0: Check preferences (EXTEND.md) ⚠️ REQUIRED if not found
- [ ] Step 1: Analyze content - [ ] Step 1: Analyze content
- [ ] Step 2: Confirm options (type + style + aspect) ⚠️ REQUIRED - [ ] Step 2: Confirm options (4 dimensions) ⚠️ REQUIRED unless --quick or all specified
- [ ] Step 3: Create prompt - [ ] Step 3: Create prompt
- [ ] Step 4: Generate image - [ ] Step 4: Generate image
- [ ] Step 5: Completion report - [ ] Step 5: Completion report
@@ -184,7 +260,9 @@ Cover Image Progress:
### Flow ### Flow
``` ```
Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: Type + Style + Aspect] → Prompt → Generate → Complete Input → [Step 0: Preferences/Setup] → Analyze → [Confirm: 4 Dimensions] → Prompt → Generate → Complete
(skip if --quick or all dimensions specified)
``` ```
### Step 0: Load Preferences (EXTEND.md) ⚠️ ### Step 0: Load Preferences (EXTEND.md) ⚠️
@@ -226,7 +304,10 @@ Preferences loaded from [project/user]:
• Watermark: [enabled/disabled] [content if enabled] • Watermark: [enabled/disabled] [content if enabled]
• Type: [preferred_type or "auto"] • Type: [preferred_type or "auto"]
• Style: [preferred_style or "auto"] • Style: [preferred_style or "auto"]
• Text: [preferred_text or "title-only"]
• Mood: [preferred_mood or "balanced"]
• Aspect: [default_aspect] • Aspect: [default_aspect]
• Quick mode: [enabled/disabled]
• Language: [language or "auto"] • Language: [language or "auto"]
``` ```
@@ -276,15 +357,30 @@ options:
header: "Aspect" header: "Aspect"
question: "Default aspect ratio for cover images?" question: "Default aspect ratio for cover images?"
options: options:
- label: "2.35:1 (Recommended)" - label: "16:9 (Recommended)"
description: "Cinematic widescreen, best for article headers" description: "Standard widescreen - YouTube, presentations, versatile"
- label: "16:9" - label: "2.35:1"
description: "Standard widescreen, versatile" description: "Cinematic widescreen - article headers, blog posts"
- label: "1:1" - label: "1:1"
description: "Square, social media friendly" description: "Square - Instagram, WeChat, social cards"
- label: "3:4"
description: "Portrait - Xiaohongshu, Pinterest, mobile content"
``` ```
**Q5: Save Location** Note: More ratios (4:3, 3:2) available during generation. This sets the default recommendation.
**Q5: Quick Mode**
```yaml
header: "Quick"
question: "Enable quick mode by default?"
options:
- label: "No (Recommended)"
description: "Confirm dimension choices each time"
- label: "Yes"
description: "Skip confirmation, use auto-selection"
```
**Q6: Save Location**
```yaml ```yaml
header: "Save" header: "Save"
question: "Where to save preferences?" question: "Where to save preferences?"
@@ -299,7 +395,7 @@ options:
Full setup details: `references/config/first-time-setup.md` Full setup details: `references/config/first-time-setup.md`
**EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Default aspect ratio | Custom style definitions | Language preference **EXTEND.md Supports**: Watermark | Preferred type | Preferred style | Preferred text | Preferred mood | Default aspect ratio | Quick mode | Custom style definitions | Language preference
Schema: `references/config/preferences-schema.md` Schema: `references/config/preferences-schema.md`
@@ -323,7 +419,31 @@ Read source content, save it if needed, and perform analysis.
### Step 2: Confirm Options ⚠️ ### Step 2: Confirm Options ⚠️
**Purpose**: Validate type, style and aspect ratio. **Do NOT skip.** **Purpose**: Validate all 4 dimensions + aspect ratio.
**Skip Conditions**:
| Condition | Skipped Questions | Still Asked |
|-----------|-------------------|-------------|
| `--quick` flag | Type, Style, Text, Mood | **Aspect Ratio** (unless `--aspect` specified) |
| All 4 dimensions + `--aspect` specified | All | None |
| `quick_mode: true` in EXTEND.md | Type, Style, Text, Mood | **Aspect Ratio** (unless `--aspect` specified) |
| Otherwise | None | All 5 questions |
**Important**: Aspect ratio is ALWAYS asked unless explicitly specified via `--aspect` CLI flag. User presets in EXTEND.md are shown as recommended option, not auto-selected.
**Quick Mode Output** (when skipping 4 dimensions):
```
Quick Mode: Auto-selected dimensions
• Type: [type] ([reason])
• Style: [style] ([reason])
• Text: [text] ([reason])
• Mood: [mood] ([reason])
[Then ask Question 5: Aspect Ratio]
```
**Confirmation Flow** (when NOT skipping):
**Language**: Auto-determined (user's input language > saved preference > source language). No need to ask. **Language**: Auto-determined (user's input language > saved preference > source language). No need to ask.
@@ -364,50 +484,118 @@ options:
description: "[reason]" description: "[reason]"
``` ```
**Question 3: Aspect Ratio** (if not specified via `--aspect`) **Question 3: Text** (if not specified via `--text`)
- Based on selected Type, show compatible text levels (✓✓ first from compatibility matrix)
```yaml
header: "Text"
question: "Text density level?"
multiSelect: false
options:
- label: "title-only (Recommended)"
description: "Simple headline, ≤8 characters"
- label: "none"
description: "Pure visual, no text elements"
- label: "title-subtitle"
description: "Title + supporting context"
- label: "text-rich"
description: "Title + subtitle + keyword tags"
```
**Question 4: Mood** (if not specified via `--mood`)
- Based on content analysis, show recommended mood
```yaml
header: "Mood"
question: "Emotional intensity?"
multiSelect: false
options:
- label: "balanced (Recommended)"
description: "Medium contrast and saturation, versatile"
- label: "subtle"
description: "Low contrast, muted colors, calm"
- label: "bold"
description: "High contrast, vivid colors, dynamic"
```
**Question 5: Aspect Ratio** (ALWAYS ask unless `--aspect` specified via CLI)
Note: Even if user has a preset in EXTEND.md, still ask this question. The preset is shown as the recommended option.
```yaml ```yaml
header: "Aspect" header: "Aspect"
question: "Cover aspect ratio?" question: "Cover aspect ratio?"
multiSelect: false multiSelect: false
options: options:
- label: "2.35:1 (Recommended)" - label: "[user preset or 16:9] (Recommended)"
description: "Cinematic widescreen, best for article headers" description: "[based on preset or default: Standard widescreen, versatile]"
- label: "16:9" - label: "2.35:1"
description: "Standard widescreen, versatile" description: "Cinematic widescreen - article headers, blog posts"
- label: "4:3"
description: "Traditional screen - PPT slides, classic displays"
- label: "3:2"
description: "Photography ratio - blog articles, Medium posts"
- label: "1:1" - label: "1:1"
description: "Square, social media friendly" description: "Square - Instagram, WeChat moments, social cards"
- label: "3:4"
description: "Portrait - Xiaohongshu, Pinterest, mobile-first content"
``` ```
**After response**: Proceed to Step 3 with confirmed type + style + aspect ratio. **After response**: Proceed to Step 3 with confirmed dimensions.
### Step 3: Create Prompt ### Step 3: Create Prompt
Save to `prompts/cover.md`: Save to `prompts/cover.md`:
```markdown ```markdown
Cover theme: [2-3 words] # Content Context
Article title: [full original title from source]
Content summary: [2-3 sentence summary of key points and themes]
Keywords: [5-8 key terms extracted from content]
# Visual Design
Cover theme: [2-3 words visual interpretation]
Type: [confirmed type] Type: [confirmed type]
Style: [confirmed style] Style: [confirmed style]
Text level: [confirmed text level]
Mood: [confirmed mood]
Aspect ratio: [confirmed ratio] Aspect ratio: [confirmed ratio]
Title text: [max 8 chars, or "none" if --no-title]
Language: [confirmed language] Language: [confirmed language]
# Text Elements
[Based on text level:]
- none: "No text elements"
- title-only: "Title: [max 8 chars headline]"
- title-subtitle: "Title: [headline] / Subtitle: [max 15 chars context]"
- text-rich: "Title: [headline] / Subtitle: [context] / Tags: [2-4 keywords]"
# Mood Application
[Based on mood level:]
- subtle: "Use low contrast, muted colors, light visual weight, calm aesthetic"
- balanced: "Use medium contrast, normal saturation, balanced visual weight"
- bold: "Use high contrast, vivid saturated colors, heavy visual weight, dynamic energy"
# Composition
Type composition: Type composition:
- [Type-specific layout and structure] - [Type-specific layout and structure]
Visual composition: Visual composition:
- Main visual: [type + style appropriate metaphor] - Main visual: [metaphor derived from content meaning]
- Layout: [positioning based on type and aspect ratio] - Layout: [positioning based on type and aspect ratio]
- Decorative: [style elements] - Decorative: [style elements that reinforce content theme]
Color scheme: [primary, background, accent from style] Color scheme: [primary, background, accent from style, adjusted by mood]
Type notes: [key characteristics from type definition] Type notes: [key characteristics from type definition]
Style notes: [key characteristics from style definition] Style notes: [key characteristics from style definition]
[Watermark section if enabled] [Watermark section if enabled]
``` ```
**Content-Driven Design**:
- Article title and summary inform the visual metaphor choice
- Keywords guide decorative elements and symbols
- The skill controls visual style; the content drives meaning
**Type-Specific Composition**: **Type-Specific Composition**:
| Type | Composition Guidelines | | Type | Composition Guidelines |
@@ -419,7 +607,7 @@ Style notes: [key characteristics from style definition]
| `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors | | `scene` | Atmospheric environment, narrative elements, mood-setting lighting and colors |
| `minimal` | Single focal element, generous whitespace (60%+), essential shapes only | | `minimal` | Single focal element, generous whitespace (60%+), essential shapes only |
**Title guidelines** (if included): **Title guidelines** (when text level includes title):
- Max 8 characters, punchy headline - Max 8 characters, punchy headline
- Use hooks: numbers, questions, contrasts - Use hooks: numbers, questions, contrasts
- Match confirmed language - Match confirmed language
@@ -427,14 +615,17 @@ Style notes: [key characteristics from style definition]
**Watermark Application** (if enabled in preferences): **Watermark Application** (if enabled in preferences):
Add to prompt: Add to prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
Reference: `references/config/watermark-guide.md` Reference: `references/config/watermark-guide.md`
### Step 4: Generate Image ### Step 4: Generate Image
**Backup Existing Cover** (if regenerating):
If `cover.png` already exists in the output directory:
- Rename to `cover-backup-YYYYMMDD-HHMMSS.png`
**Image Generation Skill Selection**: **Image Generation Skill Selection**:
1. Check available image generation skills 1. Check available image generation skills
2. If multiple skills available, ask user preference 2. If multiple skills available, ask user preference
@@ -453,8 +644,10 @@ Cover Generated!
Topic: [topic] Topic: [topic]
Type: [type name] Type: [type name]
Style: [style name] Style: [style name]
Text: [text level]
Mood: [mood level]
Aspect: [ratio] Aspect: [ratio]
Title: [title or "visual only"] Title: [title text or "visual only"]
Language: [lang] Language: [lang]
Watermark: [enabled/disabled] Watermark: [enabled/disabled]
Location: [directory path] Location: [directory path]
@@ -463,29 +656,39 @@ Files:
✓ source-{slug}.{ext} ✓ source-{slug}.{ext}
✓ prompts/cover.md ✓ prompts/cover.md
✓ cover.png ✓ cover.png
[✓ cover-backup-{timestamp}.png (if regenerated)]
``` ```
## Image Modification ## Image Modification
| Action | Steps | | Action | Steps |
|--------|-------| |--------|-------|
| **Regenerate** | Update prompt → Regenerate with same settings | | **Regenerate** | Backup existing → Update prompt → Regenerate with same settings |
| **Change type** | Confirm new type → Update prompt → Regenerate | | **Change type** | Backup existing → Confirm new type → Update prompt → Regenerate |
| **Change style** | Confirm new style → Update prompt → Regenerate | | **Change style** | Backup existing → Confirm new style → Update prompt → Regenerate |
| **Change aspect** | Confirm new aspect → Update prompt → Regenerate | | **Change text** | Backup existing → Confirm new text level → Update prompt → Regenerate |
| **Change mood** | Backup existing → Confirm new mood → Update prompt → Regenerate |
| **Change aspect** | Backup existing → Confirm new aspect → Update prompt → Regenerate |
All modifications automatically backup the existing `cover.png` before regenerating.
## Notes ## Notes
- Cover must be readable at small preview sizes - Cover must be readable at small preview sizes
- Visual metaphors > literal representations - Visual metaphors > literal representations
- Title: max 8 chars, readable, impactful - Title: max 8 chars, readable, impactful
- **Two confirmation points**: Step 0 (first-time setup if no EXTEND.md) + Step 2 (options) - do NOT skip - **Two confirmation points**: Step 0 (first-time setup if no EXTEND.md) + Step 2 (options) - can skip Step 2 with `--quick`
- Use confirmed language for title text - Use confirmed language for title text
- Maintain watermark consistency if enabled - Maintain watermark consistency if enabled
- Check Type × Style compatibility matrix when selecting combinations - Check compatibility matrices when selecting combinations
- `--no-title` is preserved as alias for `--text none`
## References ## References
**Dimensions**:
- `references/dimensions/text.md` - Text density dimension
- `references/dimensions/mood.md` - Mood intensity dimension
**Styles**: `references/styles/<name>.md` - Style definitions **Styles**: `references/styles/<name>.md` - Style definitions
**Config**: **Config**:
@@ -13,14 +13,49 @@ Create a WeChat article cover image following these guidelines:
- Ample whitespace, highlight core message, avoid cluttered layouts - Ample whitespace, highlight core message, avoid cluttered layouts
- Main visual elements centered or slightly left (leave right side for title area if title included) - Main visual elements centered or slightly left (leave right side for title area if title included)
## Four Dimensions
### Type (Visual Composition)
- `hero`: Large focal visual (60-70% area), dramatic composition
- `conceptual`: Abstract shapes, information hierarchy, clean zones
- `typography`: Title as primary element (40%+ area), minimal visuals
- `metaphor`: Concrete object representing abstract idea, symbolic elements
- `scene`: Atmospheric environment, narrative elements, mood lighting
- `minimal`: Single focal element, generous whitespace (60%+)
### Style (Visual Aesthetics)
Apply the specified style's color palette, visual elements, and typography characteristics.
### Text (Density Level)
- `none`: No text elements, full visual area
- `title-only`: Single headline (≤8 characters), 85% visual area
- `title-subtitle`: Title + context (≤15 chars), 75% visual area
- `text-rich`: Title + subtitle + 2-4 keyword tags, 60% visual area
### Mood (Emotional Intensity)
- `subtle`: Low contrast, muted/desaturated colors, light visual weight, calm aesthetic
- `balanced`: Medium contrast, normal saturation, balanced visual weight
- `bold`: High contrast, vivid/saturated colors, heavy visual weight, dynamic energy
## Text Style (When Title Included) ## Text Style (When Title Included)
- **ALL text MUST be hand-drawn style** - **ALL text MUST be hand-drawn style**
- Title text: Large, eye-catching, max 8 characters - Title text: Large, eye-catching, max 8 characters
- May include 1 line of subtitle or keyword tags - Subtitle: Secondary, max 15 characters (if title-subtitle or text-rich)
- Tags: 2-4 keyword badges (if text-rich)
- Font style harmonizes with illustration style - Font style harmonizes with illustration style
- **DO NOT use realistic or computer-generated fonts** - **DO NOT use realistic or computer-generated fonts**
## Mood Application
Apply mood adjustments to the base style:
| Mood | Contrast | Saturation | Weight |
|------|----------|------------|--------|
| subtle | Reduce 20-30% | Desaturate 20-30% | Lighter strokes/fills |
| balanced | Standard | Standard | Standard |
| bold | Increase 20-30% | Increase 20-30% | Heavier strokes/fills |
## Language ## Language
- Use the same language as the content provided below for any text elements - Use the same language as the content provided below for any text elements
@@ -93,7 +93,19 @@ options:
description: "Square, social media friendly" description: "Square, social media friendly"
``` ```
### Question 5: Save Location ### Question 5: Quick Mode
```
header: "Quick"
question: "Enable quick mode by default?"
options:
- label: "No (Recommended)"
description: "Confirm dimension choices each time"
- label: "Yes"
description: "Skip confirmation, use auto-selection"
```
### Question 6: Save Location
``` ```
header: "Save" header: "Save"
@@ -123,7 +135,7 @@ options:
```yaml ```yaml
--- ---
version: 1 version: 2
watermark: watermark:
enabled: [true/false] enabled: [true/false]
content: "[user input or empty]" content: "[user input or empty]"
@@ -131,12 +143,25 @@ watermark:
opacity: 0.7 opacity: 0.7
preferred_type: [selected type or null] preferred_type: [selected type or null]
preferred_style: [selected style or null] preferred_style: [selected style or null]
preferred_text: title-only
preferred_mood: balanced
default_aspect: [2.35:1/16:9/1:1] default_aspect: [2.35:1/16:9/1:1]
quick_mode: [true/false]
language: null language: null
custom_styles: [] custom_styles: []
--- ---
``` ```
## New Fields in v2
| Field | Default | Description |
|-------|---------|-------------|
| `preferred_text` | title-only | Text density (none, title-only, title-subtitle, text-rich) |
| `preferred_mood` | balanced | Mood intensity (subtle, balanced, bold) |
| `quick_mode` | false | Skip confirmation step when true |
Note: Text and Mood preferences use sensible defaults (title-only, balanced) and don't require setup questions. Users can modify these in EXTEND.md directly.
## Modifying Preferences Later ## Modifying Preferences Later
Users can edit EXTEND.md directly or run setup again: Users can edit EXTEND.md directly or run setup again:
@@ -9,20 +9,25 @@ description: EXTEND.md YAML schema for baoyu-cover-image user preferences
```yaml ```yaml
--- ---
version: 1 version: 2
watermark: watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_type: null # hero|conceptual|typography|metaphor|scene|minimal or null for auto-select preferred_type: null # hero|conceptual|typography|metaphor|scene|minimal or null for auto-select
preferred_style: null # Built-in style name or null for auto-select preferred_style: null # Built-in style name or null for auto-select
preferred_text: title-only # none|title-only|title-subtitle|text-rich
preferred_mood: balanced # subtle|balanced|bold
default_aspect: "2.35:1" # 2.35:1|16:9|1:1 default_aspect: "2.35:1" # 2.35:1|16:9|1:1
quick_mode: false # Skip confirmation when true
language: null # zh|en|ja|ko|auto (null = auto-detect) language: null # zh|en|ja|ko|auto (null = auto-detect)
custom_styles: custom_styles:
@@ -42,14 +47,16 @@ custom_styles:
| Field | Type | Default | Description | | Field | Type | Default | Description |
|-------|------|---------|-------------| |-------|------|---------|-------------|
| `version` | int | 1 | Schema version | | `version` | int | 2 | Schema version |
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `watermark.position` | enum | bottom-right | Position on image |
| `watermark.opacity` | float | 0.7 | Transparency (0.1-1.0) |
| `preferred_type` | string | null | Type name or null for auto | | `preferred_type` | string | null | Type name or null for auto |
| `preferred_style` | string | null | Style name or null for auto | | `preferred_style` | string | null | Style name or null for auto |
| `preferred_text` | string | title-only | Text density level |
| `preferred_mood` | string | balanced | Mood intensity level |
| `default_aspect` | string | "2.35:1" | Default aspect ratio | | `default_aspect` | string | "2.35:1" | Default aspect ratio |
| `quick_mode` | bool | false | Skip confirmation step |
| `language` | string | null | Output language (null = auto-detect) | | `language` | string | null | Output language (null = auto-detect) |
| `custom_styles` | array | [] | User-defined styles | | `custom_styles` | array | [] | User-defined styles |
@@ -64,6 +71,23 @@ custom_styles:
| `scene` | Atmospheric scene, narrative feel | | `scene` | Atmospheric scene, narrative feel |
| `minimal` | Minimalist composition, generous whitespace | | `minimal` | Minimalist composition, generous whitespace |
## Text Options
| Value | Description |
|-------|-------------|
| `none` | Pure visual, no text elements |
| `title-only` | Single headline (≤8 characters) |
| `title-subtitle` | Title + subtitle (≤15 characters) |
| `text-rich` | Title + subtitle + keyword tags (2-4) |
## Mood Options
| Value | Description |
|-------|-------------|
| `subtle` | Low contrast, muted colors, calm aesthetic |
| `balanced` | Medium contrast, normal saturation, versatile |
| `bold` | High contrast, vivid colors, dynamic energy |
## Position Options ## Position Options
| Value | Description | | Value | Description |
@@ -98,12 +122,15 @@ custom_styles:
```yaml ```yaml
--- ---
version: 1 version: 2
watermark: watermark:
enabled: true enabled: true
content: "@myhandle" content: "@myhandle"
preferred_type: null preferred_type: null
preferred_style: elegant preferred_style: elegant
preferred_text: title-only
preferred_mood: balanced
quick_mode: false
--- ---
``` ```
@@ -111,19 +138,24 @@ preferred_style: elegant
```yaml ```yaml
--- ---
version: 1 version: 2
watermark: watermark:
enabled: true enabled: true
content: "myblog.com" content: "myblog.com"
position: bottom-right position: bottom-right
opacity: 0.5
preferred_type: conceptual preferred_type: conceptual
preferred_style: blueprint preferred_style: blueprint
preferred_text: title-subtitle
preferred_mood: subtle
default_aspect: "16:9" default_aspect: "16:9"
quick_mode: true
language: en language: en
custom_styles: custom_styles:
@@ -138,3 +170,16 @@ custom_styles:
best_for: "SaaS, enterprise, technical" best_for: "SaaS, enterprise, technical"
--- ---
``` ```
## Migration from v1
When loading v1 schema, auto-upgrade:
| v1 Field | v2 Field | Default Value |
|----------|----------|---------------|
| (missing) | `version` | 2 |
| (missing) | `preferred_text` | title-only |
| (missing) | `preferred_mood` | balanced |
| (missing) | `quick_mode` | false |
v1 `--no-title` flag maps to `preferred_text: none`.
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-cover-image
| `bottom-center` | Centered designs | Text-heavy bottom area | | `bottom-center` | Centered designs | Text-heavy bottom area |
| `top-right` | Bottom-heavy content | Title/header in top-right | | `top-right` | Bottom-heavy content | Title/header in top-right |
## Opacity Examples
| Opacity | Visual Effect | Use Case |
|---------|---------------|----------|
| 0.3 | Very subtle, barely visible | Clean aesthetic priority |
| 0.5 | Balanced, noticeable but not distracting | Default recommendation |
| 0.7 | Clearly visible | Brand recognition priority |
| 0.9 | Strong presence | Anti-copy protection |
## Content Format ## Content Format
| Format | Example | Style | | Format | Example | Style |
@@ -51,16 +42,14 @@ description: Watermark configuration guide for baoyu-cover-image
1. **Consistency**: Use same watermark across all covers 1. **Consistency**: Use same watermark across all covers
2. **Legibility**: Ensure watermark readable on both light/dark areas 2. **Legibility**: Ensure watermark readable on both light/dark areas
3. **Size**: Keep subtle - should not distract from content 3. **Size**: Keep subtle - should not distract from content
4. **Contrast**: Adjust opacity based on cover background
## Prompt Integration ## Prompt Integration
When watermark is enabled, add to image generation prompt: When watermark is enabled, add to image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
## Cover-Specific Considerations ## Cover-Specific Considerations
@@ -75,7 +64,6 @@ be legible but not distracting from the main content.
| Issue | Solution | | Issue | Solution |
|-------|----------| |-------|----------|
| Watermark invisible | Increase opacity or adjust position | | Watermark invisible | Adjust position or check contrast |
| Watermark too prominent | Decrease opacity (0.3-0.5) | | Watermark too prominent | Change position or reduce size |
| Watermark overlaps title | Change position or reduce title area | | Watermark overlaps title | Change position or reduce title area |
| Inconsistent appearance | Use fixed opacity/position in preferences |
@@ -0,0 +1,125 @@
---
name: mood-dimension
description: Emotional intensity dimension for cover images
---
# Mood Dimension
Controls emotional intensity and visual weight of cover images.
## Values
| Value | Contrast | Saturation | Weight | Energy |
|-------|:--------:|:----------:|:------:|:------:|
| `subtle` | Low | Muted | Light | Calm |
| `balanced` | Medium | Normal | Medium | Moderate |
| `bold` | High | Vivid | Heavy | Dynamic |
## Detail
### subtle
Calm, understated visual presence.
**Characteristics**:
- Low contrast between elements
- Muted, desaturated colors
- Light visual weight
- Gentle, refined aesthetic
- Soft edges and transitions
**Use Cases**:
- Thought leadership content
- Professional/corporate communications
- Meditation, wellness topics
- Academic or scholarly articles
- Luxury brand aesthetics
**Color Guidance**:
- Pastels, earth tones, neutrals
- Low saturation (30-50%)
- Soft gradients
- Minimal color variety (2-3 colors)
### balanced
Versatile, harmonious visual presence.
**Characteristics**:
- Medium contrast
- Natural saturation levels
- Balanced visual weight
- Clear but not aggressive
- Standard aesthetic approach
**Use Cases**:
- General articles (default)
- Most blog content
- Educational material
- Product documentation
- News and updates
**Color Guidance**:
- Standard saturation (50-70%)
- Complementary color schemes
- Clear foreground/background separation
- Moderate color variety (3-4 colors)
### bold
Dynamic, high-impact visual presence.
**Characteristics**:
- High contrast between elements
- Vivid, saturated colors
- Heavy visual weight
- Energetic, attention-grabbing
- Sharp edges and strong shapes
**Use Cases**:
- Product launches
- Promotional announcements
- Event marketing
- Call-to-action content
- Entertainment/gaming topics
**Color Guidance**:
- High saturation (70-100%)
- Vibrant, primary colors
- Strong contrast ratios
- Dynamic color combinations (4+ colors)
## Type Compatibility
| Type | subtle | balanced | bold |
|------|:------:|:--------:|:----:|
| hero | ✓ | ✓✓ | ✓✓ |
| conceptual | ✓✓ | ✓✓ | ✓ |
| typography | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓✓ | ✓ |
| scene | ✓✓ | ✓✓ | ✓ |
| minimal | ✓✓ | ✓✓ | ✗ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Style Interaction
Mood modifies the base style characteristics:
| Style Category | subtle | balanced | bold |
|----------------|--------|----------|------|
| Technical (blueprint, notion) | Lighter lines, softer colors | Standard rendering | Stronger contrast, sharper edges |
| Artistic (watercolor, sketch-notes) | More whitespace, lighter strokes | Standard rendering | Deeper colors, heavier strokes |
| Editorial (bold-editorial, dark-atmospheric) | Reduced contrast, softer tones | Standard rendering | Maximum impact, vivid colors |
## Auto Selection
When `--mood` is omitted, select based on signals:
| Signals | Mood Level |
|---------|------------|
| Professional, corporate, thought leadership, academic, luxury | `subtle` |
| General, educational, standard, blog, documentation | `balanced` |
| Launch, announcement, promotion, event, gaming, entertainment | `bold` |
Default: `balanced`
@@ -0,0 +1,122 @@
---
name: text-dimension
description: Text density dimension for cover images
---
# Text Dimension
Controls text density and information hierarchy on cover images.
## Values
| Value | Title | Subtitle | Tags | Visual Area |
|-------|:-----:|:--------:|:----:|:-----------:|
| `none` | - | - | - | 100% |
| `title-only` | ✓ (≤8字) | - | - | 85% |
| `title-subtitle` | ✓ | ✓ (≤15字) | - | 75% |
| `text-rich` | ✓ | ✓ | ✓ (2-4) | 60% |
## Detail
### none
Pure visual cover with no text elements.
**Use Cases**:
- Photography-focused covers
- Abstract art pieces
- Visual-only social sharing
- When title added externally
**Composition**:
- Full visual area available
- No reserved text zones
- Emphasis on visual metaphor
### title-only
Single headline, maximum impact.
**Use Cases**:
- Most article covers (default)
- Clear single message
- Strong brand recognition
**Composition**:
- Title: ≤8 characters, prominent
- Reserved zone: top or bottom 15%
- Visual supports title message
**Title Guidelines**:
- Punchy, action-oriented
- Numbers, questions, contrasts work well
- Match content language
### title-subtitle
Title with supporting context.
**Use Cases**:
- Technical articles needing clarification
- Series with episode/part info
- Content with dual messages
**Composition**:
- Title: ≤8 characters, primary
- Subtitle: ≤15 characters, secondary
- Reserved zone: 25%
- Clear hierarchy between title/subtitle
**Subtitle Guidelines**:
- Clarify or contextualize title
- Can include series name, author, date
- Smaller, less prominent than title
### text-rich
Information-dense cover with multiple text elements.
**Use Cases**:
- Infographic-style covers
- Event announcements with details
- Promotional material with features
- Content with multiple key points
**Composition**:
- Title: primary focus
- Subtitle: supporting info
- Tags: 2-4 keyword labels
- Reserved zone: 40%
- Clear visual hierarchy
**Tag Guidelines**:
- 2-4 tags maximum
- Short keywords (1-2 words each)
- Positioned as badges/labels
- Can highlight: category, date, author, key features
## Type Compatibility
| Type | none | title-only | title-subtitle | text-rich |
|------|:----:|:----------:|:--------------:|:---------:|
| hero | ✓ | ✓✓ | ✓✓ | ✓ |
| conceptual | ✓✓ | ✓✓ | ✓ | ✓ |
| typography | ✗ | ✓ | ✓✓ | ✓✓ |
| metaphor | ✓✓ | ✓ | ✓ | ✗ |
| scene | ✓✓ | ✓ | ✓ | ✗ |
| minimal | ✓✓ | ✓✓ | ✓ | ✗ |
✓✓ = highly recommended | ✓ = compatible | ✗ = not recommended
## Auto Selection
When `--text` is omitted, select based on signals:
| Signals | Text Level |
|---------|------------|
| Visual-only, photography, abstract, art | `none` |
| Article, blog, standard cover | `title-only` |
| Series, tutorial, technical with context | `title-subtitle` |
| Announcement, features, multiple points, infographic | `text-rich` |
Default: `title-only`
+81 -226
View File
@@ -1,15 +1,11 @@
--- ---
name: baoyu-danger-gemini-web name: baoyu-danger-gemini-web
description: Image generation skill using Gemini Web. Generates images from text prompts via Google Gemini. Also supports text generation. Use as the image generation backend for other skills like cover-image, xhs-images, article-illustrator. description: Generates images and text via reverse-engineered Gemini Web API. Supports text generation, image generation from prompts, reference images for vision input, and multi-turn conversations. Use when other skills need image generation backend, or when user requests "generate image with Gemini", "Gemini text generation", or needs vision-capable AI generation.
--- ---
# Gemini Web Client # Gemini Web Client
Supports: Text/image generation via Gemini Web API. Supports reference images and multi-turn conversations.
- Text generation
- Image generation (download + save)
- Reference images for vision input (attach local images)
- Multi-turn conversations via persisted `--sessionId`
## Script Directory ## Script Directory
@@ -26,146 +22,73 @@ Supports:
| `scripts/main.ts` | CLI entry point for text/image generation | | `scripts/main.ts` | CLI entry point for text/image generation |
| `scripts/gemini-webapi/*` | TypeScript port of `gemini_webapi` (GeminiClient, types, utils) | | `scripts/gemini-webapi/*` | TypeScript port of `gemini_webapi` (GeminiClient, types, utils) |
## ⚠️ Disclaimer (REQUIRED) ## Consent Check (REQUIRED)
**Before using this skill**, the consent check MUST be performed. Before first use, verify user consent for reverse-engineered API usage.
### Consent Check Flow **Consent file locations**:
- macOS: `~/Library/Application Support/baoyu-skills/gemini-web/consent.json`
- Linux: `~/.local/share/baoyu-skills/gemini-web/consent.json`
- Windows: `%APPDATA%\baoyu-skills\gemini-web\consent.json`
**Step 1**: Check consent file **Flow**:
1. Check if consent file exists with `accepted: true` and `disclaimerVersion: "1.0"`
```bash 2. If valid consent exists → print warning with `acceptedAt` date, proceed
# macOS 3. If no consent → show disclaimer, ask user via `AskUserQuestion`:
cat ~/Library/Application\ Support/baoyu-skills/gemini-web/consent.json 2>/dev/null - "Yes, I accept" → create consent file with ISO timestamp, proceed
- "No, I decline" → output decline message, stop
# Linux 4. Consent file format: `{"version":1,"accepted":true,"acceptedAt":"<ISO>","disclaimerVersion":"1.0"}`
cat ~/.local/share/baoyu-skills/gemini-web/consent.json 2>/dev/null
# Windows (PowerShell)
Get-Content "$env:APPDATA\baoyu-skills\gemini-web\consent.json" 2>$null
```
**Step 2**: If consent exists and `accepted: true` with matching `disclaimerVersion: "1.0"`:
Print warning and proceed:
```
⚠️ Warning: Using reverse-engineered Gemini Web API (not official). Accepted on: <acceptedAt date>
```
**Step 3**: If consent file doesn't exist or `disclaimerVersion` mismatch:
Display disclaimer and ask user:
```
⚠️ DISCLAIMER
This tool uses a reverse-engineered Gemini Web API, NOT an official Google API.
Risks:
- May break without notice if Google changes their API
- No official support or guarantees
- Use at your own risk
Do you accept these terms and wish to continue?
```
Use `AskUserQuestion` tool with options:
- **Yes, I accept** - Continue and save consent
- **No, I decline** - Exit immediately
**Step 4**: On acceptance, create consent file:
```bash
# macOS
mkdir -p ~/Library/Application\ Support/baoyu-skills/gemini-web
cat > ~/Library/Application\ Support/baoyu-skills/gemini-web/consent.json << 'EOF'
{
"version": 1,
"accepted": true,
"acceptedAt": "<ISO timestamp>",
"disclaimerVersion": "1.0"
}
EOF
# Linux
mkdir -p ~/.local/share/baoyu-skills/gemini-web
cat > ~/.local/share/baoyu-skills/gemini-web/consent.json << 'EOF'
{
"version": 1,
"accepted": true,
"acceptedAt": "<ISO timestamp>",
"disclaimerVersion": "1.0"
}
EOF
```
**Step 5**: On decline, output message and stop:
```
User declined the disclaimer. Exiting.
```
--- ---
## Quick start ## Preferences (EXTEND.md)
Use Bash to check EXTEND.md existence (priority order):
```bash ```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello, Gemini" # Check project-level first
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Explain quantum computing" test -f .baoyu-skills/baoyu-danger-gemini-web/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md" && echo "user"
```
┌──────────────────────────────────────────────────────────┬───────────────────┐
│ 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
```bash
# Text generation
npx -y bun ${SKILL_DIR}/scripts/main.ts "Your prompt"
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Your prompt" --model gemini-2.5-pro
# Image generation
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cute cat" --image cat.png npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cute cat" --image cat.png
npx -y bun ${SKILL_DIR}/scripts/main.ts --promptfiles system.md content.md --image out.png npx -y bun ${SKILL_DIR}/scripts/main.ts --promptfiles system.md content.md --image out.png
# Multi-turn conversation (agent generates unique sessionId) # Vision input (reference images)
npx -y bun ${SKILL_DIR}/scripts/main.ts "Remember this: 42" --sessionId my-unique-id-123 npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Describe this" --reference image.png
npx -y bun ${SKILL_DIR}/scripts/main.ts "What number?" --sessionId my-unique-id-123 npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Create variation" --reference a.png --image out.png
```
## Commands # Multi-turn conversation
npx -y bun ${SKILL_DIR}/scripts/main.ts "Remember: 42" --sessionId session-abc
### Text generation npx -y bun ${SKILL_DIR}/scripts/main.ts "What number?" --sessionId session-abc
```bash
# Simple prompt (positional)
npx -y bun ${SKILL_DIR}/scripts/main.ts "Your prompt here"
# Explicit prompt flag
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Your prompt here"
npx -y bun ${SKILL_DIR}/scripts/main.ts -p "Your prompt here"
# With model selection
npx -y bun ${SKILL_DIR}/scripts/main.ts -p "Hello" -m gemini-2.5-pro
# Pipe from stdin
echo "Summarize this" | npx -y bun ${SKILL_DIR}/scripts/main.ts
```
### Image generation
```bash
# Generate image with default path (./generated.png)
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A sunset over mountains" --image
# Generate image with custom path
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cute robot" --image robot.png
# Shorthand
npx -y bun ${SKILL_DIR}/scripts/main.ts "A dragon" --image=dragon.png
```
### Vision input (reference images)
```bash
# Text + image -> text
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Describe this image" --reference a.png
# Text + image -> image
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Generate a variation" --reference a.png --image out.png
```
### Output formats
```bash
# Plain text (default)
npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello"
# JSON output # JSON output
npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json
@@ -175,45 +98,35 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json
| Option | Description | | Option | Description |
|--------|-------------| |--------|-------------|
| `--prompt <text>`, `-p` | Prompt text | | `--prompt`, `-p` | Prompt text |
| `--promptfiles <files...>` | Read prompt from files (concatenated in order) | | `--promptfiles` | Read prompt from files (concatenated) |
| `--model <id>`, `-m` | Model: gemini-3-pro (default), gemini-2.5-pro, gemini-2.5-flash | | `--model`, `-m` | Model: gemini-3-pro (default), gemini-2.5-pro, gemini-2.5-flash |
| `--image [path]` | Generate image, save to path (default: generated.png) | | `--image [path]` | Generate image (default: generated.png) |
| `--reference <files...>`, `--ref <files...>` | Reference images for vision input | | `--reference`, `--ref` | Reference images for vision input |
| `--sessionId <id>` | Session ID for multi-turn conversation (agent generates unique ID) | | `--sessionId` | Session ID for multi-turn conversation |
| `--list-sessions` | List saved sessions (max 100, sorted by update time) | | `--list-sessions` | List saved sessions |
| `--json` | Output as JSON | | `--json` | Output as JSON |
| `--login` | Refresh cookies only, then exit | | `--login` | Refresh cookies, then exit |
| `--cookie-path <path>` | Custom cookie file path | | `--cookie-path` | Custom cookie file path |
| `--profile-dir <path>` | Chrome profile directory | | `--profile-dir` | Chrome profile directory |
| `--help`, `-h` | Show help |
CLI note: `scripts/main.ts` supports text generation, image generation, reference images (`--reference/--ref`), and multi-turn conversations via `--sessionId`.
## Models ## Models
- `gemini-3-pro` - Default, latest model | Model | Description |
- `gemini-2.5-pro` - Previous generation pro |-------|-------------|
- `gemini-2.5-flash` - Fast, lightweight | `gemini-3-pro` | Default, latest |
| `gemini-2.5-pro` | Previous pro |
| `gemini-2.5-flash` | Fast, lightweight |
## Authentication ## Authentication
First run opens a browser to authenticate with Google. Cookies are cached for subsequent runs. First run opens browser for Google auth. Cookies cached automatically.
**Supported browsers** (auto-detected in order): Supported browsers (auto-detected): Chrome, Chrome Canary/Beta, Chromium, Edge.
- Google Chrome
- Google Chrome Canary / Beta
- Chromium
- Microsoft Edge
Override with `GEMINI_WEB_CHROME_PATH` environment variable if needed. Force refresh: `--login` flag. Override browser: `GEMINI_WEB_CHROME_PATH` env var.
```bash ## Environment Variables
# Force cookie refresh
npx -y bun ${SKILL_DIR}/scripts/main.ts --login
```
## Environment variables
| Variable | Description | | Variable | Description |
|----------|-------------| |----------|-------------|
@@ -221,72 +134,14 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --login
| `GEMINI_WEB_COOKIE_PATH` | Cookie file path | | `GEMINI_WEB_COOKIE_PATH` | Cookie file path |
| `GEMINI_WEB_CHROME_PROFILE_DIR` | Chrome profile directory | | `GEMINI_WEB_CHROME_PROFILE_DIR` | Chrome profile directory |
| `GEMINI_WEB_CHROME_PATH` | Chrome executable path | | `GEMINI_WEB_CHROME_PATH` | Chrome executable path |
| `HTTP_PROXY`, `HTTPS_PROXY` | Proxy for Google access (set inline with command) |
## Proxy Configuration ## Sessions
If you need a proxy to access Google services (e.g., in China), set `HTTP_PROXY` and `HTTPS_PROXY` environment variables before running: Session files stored in data directory under `sessions/<id>.json`.
```bash Contains: `id`, `metadata` (Gemini chat state), `messages` array, timestamps.
# Example with local proxy
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello"
# Image generation with proxy
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png
# Cookie refresh with proxy
HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 npx -y bun ${SKILL_DIR}/scripts/main.ts --login
```
**Note**: Environment variables must be set inline with the command. Shell profile settings (e.g., `.bashrc`) may not be inherited by subprocesses.
## Examples
### Generate text response
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts "What is the capital of France?"
```
### Generate image
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts "A photorealistic image of a golden retriever puppy" --image puppy.png
```
### Get JSON output for parsing
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts "Hello" --json | jq '.text'
```
### Generate image from prompt files
```bash
# Concatenate system.md + content.md as prompt
npx -y bun ${SKILL_DIR}/scripts/main.ts --promptfiles system.md content.md --image output.png
```
### Multi-turn conversation
```bash
# Start a session with unique ID (agent generates this)
npx -y bun ${SKILL_DIR}/scripts/main.ts "You are a helpful math tutor." --sessionId task-abc123
# Continue the conversation (remembers context)
npx -y bun ${SKILL_DIR}/scripts/main.ts "What is 2+2?" --sessionId task-abc123
npx -y bun ${SKILL_DIR}/scripts/main.ts "Now multiply that by 10" --sessionId task-abc123
# List recent sessions (max 100, sorted by update time)
npx -y bun ${SKILL_DIR}/scripts/main.ts --list-sessions
```
Session files are stored in `~/Library/Application Support/baoyu-skills/gemini-web/sessions/<id>.json` and contain:
- `id`: Session ID
- `metadata`: Gemini chat metadata for continuation
- `messages`: Array of `{role, content, timestamp, error?}`
- `createdAt`, `updatedAt`: Timestamps
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-danger-gemini-web/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
+69 -102
View File
@@ -1,119 +1,107 @@
--- ---
name: baoyu-danger-x-to-markdown name: baoyu-danger-x-to-markdown
description: Convert X (Twitter) tweet or article URL to markdown. Uses reverse-engineered X API (private). Requires user consent before use. description: Converts X (Twitter) tweets and articles to markdown with YAML front matter. Uses reverse-engineered API requiring user consent. Use when user mentions "X to markdown", "tweet to markdown", "save tweet", or provides x.com/twitter.com URLs for conversion.
--- ---
# X to Markdown # X to Markdown
Converts X (Twitter) content to markdown format: Converts X content to markdown:
- Tweet threads → Markdown with YAML front matter - Tweets/threads → Markdown with YAML front matter
- X Articles → Full article content extraction - X Articles → Full content extraction
## Script Directory ## Script Directory
**Important**: All scripts are located in the `scripts/` subdirectory of this skill. Scripts located in `scripts/` subdirectory.
**Agent Execution Instructions**: **Path Resolution**:
1. Determine this SKILL.md file's directory path as `SKILL_DIR` 1. `SKILL_DIR` = this SKILL.md's directory
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts` 2. Script path = `${SKILL_DIR}/scripts/main.ts`
3. Replace all `${SKILL_DIR}` in this document with the actual path
**Script Reference**: ## Consent Requirement
| Script | Purpose |
|--------|---------|
| `scripts/main.ts` | CLI entry point for URL conversion |
## ⚠️ Disclaimer (REQUIRED) **Before any conversion**, check and obtain consent.
**Before using this skill**, the consent check MUST be performed. ### Consent Flow
### Consent Check Flow
**Step 1**: Check consent file **Step 1**: Check consent file
```bash ```bash
# macOS # macOS
cat ~/Library/Application\ Support/baoyu-skills/x-to-markdown/consent.json 2>/dev/null cat ~/Library/Application\ Support/baoyu-skills/x-to-markdown/consent.json
# Linux # Linux
cat ~/.local/share/baoyu-skills/x-to-markdown/consent.json 2>/dev/null cat ~/.local/share/baoyu-skills/x-to-markdown/consent.json
# Windows (PowerShell)
Get-Content "$env:APPDATA\baoyu-skills\x-to-markdown\consent.json" 2>$null
``` ```
**Step 2**: If consent exists and `accepted: true` with matching `disclaimerVersion: "1.0"`: **Step 2**: If `accepted: true` and `disclaimerVersion: "1.0"` → print warning and proceed:
Print warning and proceed:
``` ```
⚠️ Warning: Using reverse-engineered X API (not official). Accepted on: <acceptedAt date> Warning: Using reverse-engineered X API. Accepted on: <acceptedAt>
``` ```
**Step 3**: If consent file doesn't exist or `disclaimerVersion` mismatch: **Step 3**: If missing or version mismatch → display disclaimer:
Display disclaimer and ask user:
``` ```
⚠️ DISCLAIMER DISCLAIMER
This tool uses a reverse-engineered X (Twitter) API, NOT an official API. This tool uses a reverse-engineered X API, NOT official.
Risks: Risks:
- May break without notice if X changes their API - May break if X changes API
- No official support or guarantees - No guarantees or support
- Account restrictions possible if API usage detected - Possible account restrictions
- Use at your own risk - Use at your own risk
Do you accept these terms and wish to continue? Accept terms and continue?
``` ```
Use `AskUserQuestion` tool with options: Use `AskUserQuestion` with options: "Yes, I accept" | "No, I decline"
- **Yes, I accept** - Continue and save consent
- **No, I decline** - Exit immediately
**Step 4**: On acceptance, create consent file: **Step 4**: On accept create consent file:
```json
{
"version": 1,
"accepted": true,
"acceptedAt": "<ISO timestamp>",
"disclaimerVersion": "1.0"
}
```
**Step 5**: On decline → output "User declined. Exiting." and stop.
## Preferences (EXTEND.md)
Use Bash to check EXTEND.md existence (priority order):
```bash ```bash
# macOS # Check project-level first
mkdir -p ~/Library/Application\ Support/baoyu-skills/x-to-markdown test -f .baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md && echo "project"
cat > ~/Library/Application\ Support/baoyu-skills/x-to-markdown/consent.json << 'EOF'
{
"version": 1,
"accepted": true,
"acceptedAt": "<ISO timestamp>",
"disclaimerVersion": "1.0"
}
EOF
# Linux # Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
mkdir -p ~/.local/share/baoyu-skills/x-to-markdown test -f "$HOME/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md" && echo "user"
cat > ~/.local/share/baoyu-skills/x-to-markdown/consent.json << 'EOF'
{
"version": 1,
"accepted": true,
"acceptedAt": "<ISO timestamp>",
"disclaimerVersion": "1.0"
}
EOF
``` ```
**Step 5**: On decline, output message and stop: ┌────────────────────────────────────────────────────────────┬───────────────────┐
``` │ Path │ Location │
User declined the disclaimer. Exiting. ├────────────────────────────────────────────────────────────┼───────────────────┤
``` │ .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 │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default output directory | Output format preferences
## Usage ## Usage
```bash ```bash
# Convert tweet (outputs markdown path)
npx -y bun ${SKILL_DIR}/scripts/main.ts <url> npx -y bun ${SKILL_DIR}/scripts/main.ts <url>
# Save to specific file
npx -y bun ${SKILL_DIR}/scripts/main.ts <url> -o output.md npx -y bun ${SKILL_DIR}/scripts/main.ts <url> -o output.md
# JSON output
npx -y bun ${SKILL_DIR}/scripts/main.ts <url> --json npx -y bun ${SKILL_DIR}/scripts/main.ts <url> --json
``` ```
@@ -122,56 +110,35 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts <url> --json
| Option | Description | | Option | Description |
|--------|-------------| |--------|-------------|
| `<url>` | Tweet or article URL | | `<url>` | Tweet or article URL |
| `-o <path>` | Output path (file or dir) | | `-o <path>` | Output path |
| `--json` | Output as JSON | | `--json` | JSON output |
| `--login` | Refresh cookies only | | `--login` | Refresh cookies only |
## File Structure
```
x-to-markdown/
└── {username}/
└── {tweet-id}.md
```
## Supported URLs ## Supported URLs
- `https://x.com/<user>/status/<id>` - `https://x.com/<user>/status/<id>`
- `https://twitter.com/<user>/status/<id>` - `https://twitter.com/<user>/status/<id>`
- `https://x.com/i/article/<id>` - `https://x.com/i/article/<id>`
## Output Format ## Output
```markdown ```markdown
--- ---
url: https://x.com/username/status/123 url: https://x.com/user/status/123
author: "Display Name (@username)" author: "Name (@user)"
tweet_count: 3 tweet_count: 3
--- ---
Tweet content... Content...
---
Thread continuation...
``` ```
**File structure**: `x-to-markdown/{username}/{tweet-id}.md`
## Authentication ## Authentication
**Option 1**: Environment variables (recommended) 1. **Environment variables** (preferred): `X_AUTH_TOKEN`, `X_CT0`
- `X_AUTH_TOKEN` - auth_token cookie 2. **Chrome login** (fallback): Auto-opens Chrome, caches cookies locally
- `X_CT0` - ct0 cookie
**Option 2**: Chrome login (auto if env vars not set)
- First run opens Chrome for login
- Cookies cached locally
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-danger-x-to-markdown/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
+87 -157
View File
@@ -1,98 +1,68 @@
--- ---
name: baoyu-image-gen name: baoyu-image-gen
description: AI SDK-based image generation using official OpenAI and Google APIs. Supports text-to-image, reference images, aspect ratios, and quality presets. description: AI image generation with OpenAI and Google APIs. Supports text-to-image, reference images, aspect ratios, and parallel generation (recommended 4 concurrent subagents). Use when user asks to generate, create, or draw images.
--- ---
# Image Generation (AI SDK) # Image Generation (AI SDK)
Official API-based image generation via AI SDK. Supports OpenAI (DALL-E, GPT Image) and Google (Imagen, Gemini multimodal). Official API-based image generation. Supports OpenAI and Google providers.
## Script Directory ## Script Directory
**Important**: All scripts are located in the `scripts/` subdirectory of this skill. **Agent Execution**:
1. `SKILL_DIR` = this SKILL.md file's directory
2. Script path = `${SKILL_DIR}/scripts/main.ts`
**Agent Execution Instructions**: ## Preferences (EXTEND.md)
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts`
3. Replace all `${SKILL_DIR}` in this document with the actual path
**Script Reference**: Use Bash to check EXTEND.md existence (priority order):
| Script | Purpose |
|--------|---------|
| `scripts/main.ts` | CLI entry point for image generation |
## Quick Start
```bash ```bash
# Basic generation (auto-detect provider) # Check project-level first
test -f .baoyu-skills/baoyu-image-gen/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-image-gen/EXTEND.md" && echo "user"
```
┌──────────────────────────────────────────────────┬───────────────────┐
│ Path │ Location │
├──────────────────────────────────────────────────┼───────────────────┤
│ .baoyu-skills/baoyu-image-gen/EXTEND.md │ Project directory │
├──────────────────────────────────────────────────┼───────────────────┤
│ $HOME/.baoyu-skills/baoyu-image-gen/EXTEND.md │ User home │
└──────────────────────────────────────────────────┴───────────────────┘
┌───────────┬───────────────────────────────────────────────────────────────────────────┐
│ Result │ Action │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default provider | Default quality | Default aspect ratio
## Usage
```bash
# Basic
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png
# With aspect ratio # With aspect ratio
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A landscape" --image landscape.png --ar 16:9 npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A landscape" --image out.png --ar 16:9
# High quality (2k) # High quality
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png --quality 2k npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --quality 2k
# Specific provider
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png --provider openai
# From prompt files # From prompt files
npx -y bun ${SKILL_DIR}/scripts/main.ts --promptfiles system.md content.md --image out.png npx -y bun ${SKILL_DIR}/scripts/main.ts --promptfiles system.md content.md --image out.png
# With reference images (Google multimodal only) # With reference images (Google multimodal only)
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Make blue" --image out.png --ref source.png
```
## Commands # Specific provider
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image out.png --provider openai
### Basic Image Generation
```bash
# Generate with prompt
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A sunset over mountains" --image sunset.png
# Shorthand
npx -y bun ${SKILL_DIR}/scripts/main.ts -p "A cute robot" --image robot.png
```
### Aspect Ratios
```bash
# Common ratios: 1:1, 16:9, 9:16, 4:3, 3:4, 2.35:1
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A portrait" --image portrait.png --ar 3:4
# Or specify exact size
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Banner" --image banner.png --size 1792x1024
```
### Reference Images (Google Multimodal)
```bash
# Image editing with reference
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Make it blue" --image blue.png --ref original.png
# Multiple references
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "Combine these styles" --image out.png --ref a.png b.png
```
### Quality Presets
```bash
# Normal quality (default)
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png --quality normal
# High quality (2k resolution)
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png --quality 2k
```
### Output Formats
```bash
# Plain output (prints saved path)
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png
# JSON output
npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png --json
``` ```
## Options ## Options
@@ -106,114 +76,74 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts --prompt "A cat" --image cat.png --json
| `--model <id>`, `-m` | Model ID | | `--model <id>`, `-m` | 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`) |
| `--quality normal\|2k` | Quality preset (default: normal) | | `--quality normal\|2k` | Quality preset (default: 2k) |
| `--imageSize 1K\|2K\|4K` | Image size for Google (default: from quality) |
| `--ref <files...>` | Reference images (Google multimodal only) | | `--ref <files...>` | Reference images (Google multimodal only) |
| `--n <count>` | Number of images | | `--n <count>` | Number of images |
| `--json` | JSON output | | `--json` | JSON output |
| `--help`, `-h` | Show help |
## Environment Variables ## Environment Variables
| Variable | Description | Default | | Variable | Description |
|----------|-------------|---------| |----------|-------------|
| `OPENAI_API_KEY` | OpenAI API key | - | | `OPENAI_API_KEY` | OpenAI API key |
| `GOOGLE_API_KEY` | Google API key | - | | `GOOGLE_API_KEY` | Google API key |
| `OPENAI_IMAGE_MODEL` | OpenAI model | `gpt-image-1.5` | | `OPENAI_IMAGE_MODEL` | OpenAI model override |
| `GOOGLE_IMAGE_MODEL` | Google model | `gemini-3-pro-image-preview` | | `GOOGLE_IMAGE_MODEL` | Google model override |
| `OPENAI_BASE_URL` | Custom OpenAI endpoint | - | | `OPENAI_BASE_URL` | Custom OpenAI endpoint |
| `GOOGLE_BASE_URL` | Custom Google endpoint | - | | `GOOGLE_BASE_URL` | Custom Google endpoint |
**Load Priority**: CLI args > `process.env` > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env` **Load Priority**: CLI args > env vars > `<cwd>/.baoyu-skills/.env` > `~/.baoyu-skills/.env`
## Provider & Model Strategy ## Provider Selection
### Auto-Selection 1. `--provider` specified → use it
2. Only one API key available → use that provider
1. If `--provider` specified → use it 3. Both available → default to Google
2. If only one API key available → use that provider
3. If both available → default to Google (multimodal LLMs more versatile)
### API Selection by Model Type
| Model Category | API Function | Example Models |
|----------------|--------------|----------------|
| Google Multimodal | `generateText` | `gemini-2.0-flash-exp-image-generation` |
| Google Imagen | `experimental_generateImage` | `imagen-3.0-generate-002` |
| OpenAI | `experimental_generateImage` | `gpt-image-1`, `dall-e-3` |
### Available Models
**Google**:
- `gemini-3-pro-image-preview` - Default, multimodal generation
- `gemini-2.0-flash-exp-image-generation` - Gemini 2.0 Flash
- `imagen-3.0-generate-002` - Imagen 3
**OpenAI**:
- `gpt-image-1.5` - Default, GPT Image 1.5
- `gpt-image-1` - GPT Image 1
- `dall-e-3` - DALL-E 3
## Quality Presets ## Quality Presets
| Preset | OpenAI | Google | Use Case | | Preset | Google imageSize | OpenAI Size | Use Case |
|--------|--------|--------|----------| |--------|------------------|-------------|----------|
| `normal` | 1024x1024 | Default | Covers, illustrations | | `normal` | 1K | 1024px | Quick previews |
| `2k` | 2048x2048 | "2048px" in prompt | Infographics, slides | | `2k` (default) | 2K | 2048px | Covers, illustrations, infographics |
## Aspect Ratio Handling **Google imageSize**: Can be overridden with `--imageSize 1K|2K|4K`
- **Multimodal LLMs**: Embedded in prompt (e.g., `"... aspect ratio 16:9"`) ## Aspect Ratios
- **Image-only models**: Uses `aspectRatio` or `size` parameter
- **Common ratios**: 1:1, 16:9, 9:16, 4:3, 3:4, 2.35:1
## Examples Supported: `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `2.35:1`
### Generate Cover Image - Google multimodal: uses `imageConfig.aspectRatio`
- Google Imagen: uses `aspectRatio` parameter
- OpenAI: maps to closest supported size
```bash ## Parallel Generation
npx -y bun ${SKILL_DIR}/scripts/main.ts \
--prompt "A minimalist tech illustration with blue gradients" \ Supports concurrent image generation via background subagents for batch operations.
--image cover.png --ar 2.35:1 --quality 2k
| Setting | Value |
|---------|-------|
| Recommended concurrency | 4 subagents |
| Max concurrency | 8 subagents |
| Use case | Batch generation (slides, comics, infographics) |
**Agent Implementation**:
```
# Launch multiple generations in parallel using Task tool
# Each Task runs as background subagent with run_in_background=true
# Collect results via TaskOutput when all complete
``` ```
### Generate Social Media Post **Best Practice**: When generating 4+ images, spawn background subagents (recommended 4 concurrent) instead of sequential execution.
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts \
--prompt "Instagram post about coffee" \
--image post.png --ar 1:1
```
### Edit Image with Reference
```bash
npx -y bun ${SKILL_DIR}/scripts/main.ts \
--prompt "Change the background to sunset" \
--image edited.png --ref original.png --provider google
```
### Batch Generation from Prompt File
```bash
# Create prompt file with detailed instructions
npx -y bun ${SKILL_DIR}/scripts/main.ts \
--promptfiles style-guide.md scene-description.md \
--image scene.png
```
## Error Handling ## Error Handling
- **Missing API key**: Clear error with setup instructions - Missing API key error with setup instructions
- **Generation failure**: Auto-retry once, then error - Generation failure → auto-retry once
- **Invalid aspect ratio**: Warning, proceed with default - Invalid aspect ratio → warning, proceed with default
- **Reference images with image-only model**: Warning, ignore refs - Reference images with non-multimodal model → warning, ignore refs
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-image-gen/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-image-gen/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
+26 -257
View File
@@ -1,36 +1,8 @@
import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import process from "node:process"; import process from "node:process";
import { homedir } from "node:os"; import { homedir } from "node:os";
import { mkdir, readFile, writeFile } from "node:fs/promises"; import { mkdir, readFile, writeFile } from "node:fs/promises";
import type { CliArgs, Provider } from "./types";
type Provider = "google" | "openai";
type Quality = "normal" | "2k";
type CliArgs = {
prompt: string | null;
promptFiles: string[];
imagePath: string | null;
provider: Provider | null;
model: string | null;
aspectRatio: string | null;
size: string | null;
quality: Quality;
referenceImages: string[];
n: number;
json: boolean;
help: boolean;
};
const GOOGLE_MULTIMODAL_MODELS = [
"gemini-3-pro-image-preview",
"gemini-2.0-flash-exp-image-generation",
"gemini-2.5-flash-preview-native-audio-dialog",
];
const GOOGLE_IMAGEN_MODELS = ["imagen-3.0-generate-002", "imagen-3.0-generate-001"];
const OPENAI_IMAGE_MODELS = ["gpt-image-1.5", "gpt-image-1", "dall-e-3", "dall-e-2"];
function printUsage(): void { function printUsage(): void {
console.log(`Usage: console.log(`Usage:
@@ -46,7 +18,8 @@ Options:
-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)
--quality normal|2k Quality preset (default: normal) --quality normal|2k Quality preset (default: 2k)
--imageSize 1K|2K|4K Image size for Google (default: from quality)
--ref <files...> Reference images (Google multimodal only) --ref <files...> Reference images (Google multimodal only)
--n <count> Number of images (default: 1) --n <count> Number of images (default: 1)
--json JSON output --json JSON output
@@ -55,6 +28,7 @@ Options:
Environment variables: Environment variables:
OPENAI_API_KEY OpenAI API key OPENAI_API_KEY OpenAI API key
GOOGLE_API_KEY Google API key GOOGLE_API_KEY Google API key
GEMINI_API_KEY Gemini API key (alias for GOOGLE_API_KEY)
OPENAI_IMAGE_MODEL Default OpenAI model (gpt-image-1.5) OPENAI_IMAGE_MODEL Default OpenAI model (gpt-image-1.5)
GOOGLE_IMAGE_MODEL Default Google model (gemini-3-pro-image-preview) GOOGLE_IMAGE_MODEL Default Google model (gemini-3-pro-image-preview)
OPENAI_BASE_URL Custom OpenAI endpoint OPENAI_BASE_URL Custom OpenAI endpoint
@@ -72,7 +46,8 @@ function parseArgs(argv: string[]): CliArgs {
model: null, model: null,
aspectRatio: null, aspectRatio: null,
size: null, size: null,
quality: "normal", quality: "2k",
imageSize: null,
referenceImages: [], referenceImages: [],
n: 1, n: 1,
json: false, json: false,
@@ -163,6 +138,13 @@ function parseArgs(argv: string[]): CliArgs {
continue; continue;
} }
if (a === "--imageSize") {
const v = argv[++i]?.toUpperCase();
if (v !== "1K" && v !== "2K" && v !== "4K") throw new Error(`Invalid imageSize: ${v}`);
out.imageSize = v;
continue;
}
if (a === "--ref" || a === "--reference") { if (a === "--ref" || a === "--reference") {
const { items, next } = takeMany(i); const { items, next } = takeMany(i);
if (items.length === 0) throw new Error(`Missing files for ${a}`); if (items.length === 0) throw new Error(`Missing files for ${a}`);
@@ -259,7 +241,7 @@ function normalizeOutputImagePath(p: string): string {
function detectProvider(args: CliArgs): Provider { function detectProvider(args: CliArgs): Provider {
if (args.provider) return args.provider; if (args.provider) return args.provider;
const hasGoogle = !!process.env.GOOGLE_API_KEY; const hasGoogle = !!(process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY);
const hasOpenai = !!process.env.OPENAI_API_KEY; const hasOpenai = !!process.env.OPENAI_API_KEY;
if (hasGoogle && !hasOpenai) return "google"; if (hasGoogle && !hasOpenai) return "google";
@@ -267,235 +249,21 @@ function detectProvider(args: CliArgs): Provider {
if (hasGoogle && hasOpenai) return "google"; if (hasGoogle && hasOpenai) return "google";
throw new Error( throw new Error(
"No API key found. Set GOOGLE_API_KEY or OPENAI_API_KEY.\n" + "No API key found. Set GOOGLE_API_KEY, GEMINI_API_KEY, or OPENAI_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."
); );
} }
function getDefaultModel(provider: Provider): string { type ProviderModule = {
getDefaultModel: () => string;
generateImage: (prompt: string, model: string, args: CliArgs) => Promise<Uint8Array>;
};
async function loadProviderModule(provider: Provider): Promise<ProviderModule> {
if (provider === "google") { if (provider === "google") {
return process.env.GOOGLE_IMAGE_MODEL || "gemini-3-pro-image-preview"; return (await import("./providers/google")) as ProviderModule;
} }
return process.env.OPENAI_IMAGE_MODEL || "gpt-image-1.5"; return (await import("./providers/openai")) as ProviderModule;
}
function isGoogleMultimodal(model: string): boolean {
return GOOGLE_MULTIMODAL_MODELS.some((m) => model.includes(m));
}
function isGoogleImagen(model: string): boolean {
return GOOGLE_IMAGEN_MODELS.some((m) => model.includes(m));
}
function buildPromptWithAspect(prompt: string, ar: string | null, quality: Quality): string {
let result = prompt;
if (ar) {
result += ` Aspect ratio: ${ar}.`;
}
if (quality === "2k") {
result += " High resolution 2048px.";
}
return result;
}
function parseAspectRatio(ar: string): { width: number; height: number } | null {
const match = ar.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
if (!match) return null;
const w = parseFloat(match[1]!);
const h = parseFloat(match[2]!);
if (w <= 0 || h <= 0) return null;
return { width: w, height: h };
}
function getOpenAISize(ar: string | null, quality: Quality): string {
const base = quality === "2k" ? 2048 : 1024;
if (!ar) return `${base}x${base}`;
const parsed = parseAspectRatio(ar);
if (!parsed) return `${base}x${base}`;
const ratio = parsed.width / parsed.height;
if (Math.abs(ratio - 1) < 0.1) return `${base}x${base}`;
if (ratio > 1.5) return quality === "2k" ? "2048x1024" : "1792x1024";
if (ratio < 0.67) return quality === "2k" ? "1024x2048" : "1024x1792";
return `${base}x${base}`;
}
async function readImageAsBase64(p: string): Promise<{ data: string; mimeType: string }> {
const buf = await readFile(p);
const ext = path.extname(p).toLowerCase();
let mimeType = "image/png";
if (ext === ".jpg" || ext === ".jpeg") mimeType = "image/jpeg";
else if (ext === ".gif") mimeType = "image/gif";
else if (ext === ".webp") mimeType = "image/webp";
return { data: buf.toString("base64"), mimeType };
}
async function generateWithGoogleMultimodal(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const { generateText } = await import("ai");
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
const google = createGoogleGenerativeAI({
apiKey: process.env.GOOGLE_API_KEY,
baseURL: process.env.GOOGLE_BASE_URL,
});
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
const messages: any[] = [];
const content: any[] = [];
for (const refPath of args.referenceImages) {
const { data, mimeType } = await readImageAsBase64(refPath);
content.push({ type: "image", image: data, mimeType });
}
content.push({ type: "text", text: fullPrompt });
messages.push({ role: "user", content });
const result = await generateText({
model: google(model, { useSearchGrounding: false }),
messages,
providerOptions: {
google: {
responseModalities: ["TEXT", "IMAGE"],
},
},
});
const files = (result as any).files;
if (!files || files.length === 0) {
const expRes = (result as any).response?.body?.candidates?.[0]?.content?.parts;
if (expRes) {
for (const part of expRes) {
if (part.inlineData?.data) {
return Uint8Array.from(Buffer.from(part.inlineData.data, "base64"));
}
}
}
throw new Error("No image in response");
}
const img = files[0];
if (img.uint8Array) return img.uint8Array;
if (img.base64) return Uint8Array.from(Buffer.from(img.base64, "base64"));
throw new Error("Cannot extract image data");
}
async function generateWithGoogleImagen(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const { experimental_generateImage: generateImage } = await import("ai");
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
const google = createGoogleGenerativeAI({
apiKey: process.env.GOOGLE_API_KEY,
baseURL: process.env.GOOGLE_BASE_URL,
});
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
const result = await generateImage({
model: google.image(model),
prompt: fullPrompt,
n: args.n,
aspectRatio: args.aspectRatio || undefined,
});
const img = result.images[0];
if (!img) throw new Error("No image in response");
if (img.uint8Array) return img.uint8Array;
if (img.base64) return Uint8Array.from(Buffer.from(img.base64, "base64"));
throw new Error("Cannot extract image data");
}
async function generateWithOpenAI(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) throw new Error("OPENAI_API_KEY is required");
const size = args.size || getOpenAISize(args.aspectRatio, args.quality);
const body: Record<string, any> = {
model,
prompt,
size,
};
if (model.includes("dall-e-3")) {
body.quality = args.quality === "2k" ? "hd" : "standard";
}
const res = await fetch(`${baseURL}/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`OpenAI API error: ${err}`);
}
const result = (await res.json()) as { data: Array<{ url?: string; b64_json?: string }> };
const img = result.data[0];
if (img?.b64_json) {
return Uint8Array.from(Buffer.from(img.b64_json, "base64"));
}
if (img?.url) {
const imgRes = await fetch(img.url);
if (!imgRes.ok) throw new Error("Failed to download image");
const buf = await imgRes.arrayBuffer();
return new Uint8Array(buf);
}
throw new Error("No image in response");
}
async function generate(
provider: Provider,
model: string,
prompt: string,
args: CliArgs
): Promise<Uint8Array> {
if (provider === "google") {
if (isGoogleMultimodal(model)) {
return generateWithGoogleMultimodal(prompt, model, args);
}
if (isGoogleImagen(model)) {
if (args.referenceImages.length > 0) {
console.error("Warning: Reference images not supported with Imagen models, ignoring.");
}
return generateWithGoogleImagen(prompt, model, args);
}
return generateWithGoogleMultimodal(prompt, model, args);
}
if (args.referenceImages.length > 0) {
console.error("Warning: Reference images not supported with OpenAI, ignoring.");
}
return generateWithOpenAI(prompt, model, args);
} }
async function main(): Promise<void> { async function main(): Promise<void> {
@@ -527,7 +295,8 @@ async function main(): Promise<void> {
} }
const provider = detectProvider(args); const provider = detectProvider(args);
const model = args.model || getDefaultModel(provider); const providerModule = await loadProviderModule(provider);
const model = args.model || providerModule.getDefaultModel();
const outputPath = normalizeOutputImagePath(args.imagePath); const outputPath = normalizeOutputImagePath(args.imagePath);
let imageData: Uint8Array; let imageData: Uint8Array;
@@ -535,7 +304,7 @@ async function main(): Promise<void> {
while (true) { while (true) {
try { try {
imageData = await generate(provider, model, prompt, args); imageData = await providerModule.generateImage(prompt, model, args);
break; break;
} catch (e) { } catch (e) {
if (!retried) { if (!retried) {
@@ -0,0 +1,229 @@
import path from "node:path";
import { readFile } from "node:fs/promises";
import type { CliArgs } from "../types";
const GOOGLE_MULTIMODAL_MODELS = ["gemini-3-pro-image-preview", "gemini-3-flash-preview"];
const GOOGLE_IMAGEN_MODELS = ["imagen-3.0-generate-002", "imagen-3.0-generate-001"];
export function getDefaultModel(): string {
return process.env.GOOGLE_IMAGE_MODEL || "gemini-3-pro-image-preview";
}
function normalizeGoogleModelId(model: string): string {
return model.startsWith("models/") ? model.slice("models/".length) : model;
}
function isGoogleMultimodal(model: string): boolean {
const normalized = normalizeGoogleModelId(model);
return GOOGLE_MULTIMODAL_MODELS.some((m) => normalized.includes(m));
}
function isGoogleImagen(model: string): boolean {
const normalized = normalizeGoogleModelId(model);
return GOOGLE_IMAGEN_MODELS.some((m) => normalized.includes(m));
}
function getGoogleApiKey(): string | null {
return process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || null;
}
function getGoogleImageSize(args: CliArgs): "1K" | "2K" | "4K" {
if (args.imageSize) return args.imageSize as "1K" | "2K" | "4K";
return args.quality === "2k" ? "2K" : "1K";
}
function getGoogleBaseUrl(): string {
const base = process.env.GOOGLE_BASE_URL || "https://generativelanguage.googleapis.com";
return base.replace(/\/+$/g, "");
}
function buildGoogleUrl(pathname: string): string {
const base = getGoogleBaseUrl();
const cleanedPath = pathname.replace(/^\/+/g, "");
if (base.endsWith("/v1beta")) return `${base}/${cleanedPath}`;
return `${base}/v1beta/${cleanedPath}`;
}
function toModelPath(model: string): string {
const modelId = normalizeGoogleModelId(model);
return `models/${modelId}`;
}
async function postGoogleJson<T>(pathname: string, body: unknown): Promise<T> {
const apiKey = getGoogleApiKey();
if (!apiKey) throw new Error("GOOGLE_API_KEY or GEMINI_API_KEY is required");
const res = await fetch(buildGoogleUrl(pathname), {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-goog-api-key": apiKey,
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`Google API error (${res.status}): ${err}`);
}
return (await res.json()) as T;
}
function buildPromptWithAspect(prompt: string, ar: string | null, quality: CliArgs["quality"]): string {
let result = prompt;
if (ar) {
result += ` Aspect ratio: ${ar}.`;
}
if (quality === "2k") {
result += " High resolution 2048px.";
}
return result;
}
function addAspectRatioToPrompt(prompt: string, ar: string | null): string {
if (!ar) return prompt;
return `${prompt} Aspect ratio: ${ar}.`;
}
async function readImageAsBase64(p: string): Promise<{ data: string; mimeType: string }> {
const buf = await readFile(p);
const ext = path.extname(p).toLowerCase();
let mimeType = "image/png";
if (ext === ".jpg" || ext === ".jpeg") mimeType = "image/jpeg";
else if (ext === ".gif") mimeType = "image/gif";
else if (ext === ".webp") mimeType = "image/webp";
return { data: buf.toString("base64"), mimeType };
}
function extractInlineImageData(response: {
candidates?: Array<{ content?: { parts?: Array<{ inlineData?: { data?: string } }> } }>;
}): string | null {
for (const candidate of response.candidates || []) {
for (const part of candidate.content?.parts || []) {
const data = part.inlineData?.data;
if (typeof data === "string" && data.length > 0) return data;
}
}
return null;
}
function extractPredictedImageData(response: {
predictions?: Array<any>;
generatedImages?: Array<any>;
}): string | null {
const candidates = [...(response.predictions || []), ...(response.generatedImages || [])];
for (const candidate of candidates) {
if (!candidate || typeof candidate !== "object") continue;
if (typeof candidate.imageBytes === "string") return candidate.imageBytes;
if (typeof candidate.bytesBase64Encoded === "string") return candidate.bytesBase64Encoded;
if (typeof candidate.data === "string") return candidate.data;
const image = candidate.image;
if (image && typeof image === "object") {
if (typeof image.imageBytes === "string") return image.imageBytes;
if (typeof image.bytesBase64Encoded === "string") return image.bytesBase64Encoded;
if (typeof image.data === "string") return image.data;
}
}
return null;
}
async function generateWithGemini(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const promptWithAspect = addAspectRatioToPrompt(prompt, args.aspectRatio);
const parts: Array<{ text?: string; inlineData?: { data: string; mimeType: string } }> = [];
for (const refPath of args.referenceImages) {
const { data, mimeType } = await readImageAsBase64(refPath);
parts.push({ inlineData: { data, mimeType } });
}
parts.push({ text: promptWithAspect });
const imageConfig: { imageSize: "1K" | "2K" | "4K" } = {
imageSize: getGoogleImageSize(args),
};
console.log("Generating image with Gemini...", imageConfig);
const response = await postGoogleJson<{
candidates?: Array<{ content?: { parts?: Array<{ inlineData?: { data?: string } }> } }>;
}>(`${toModelPath(model)}:generateContent`, {
contents: [
{
role: "user",
parts,
},
],
generationConfig: {
responseModalities: ["IMAGE"],
imageConfig,
},
});
console.log("Generation completed.");
const imageData = extractInlineImageData(response);
if (imageData) return Uint8Array.from(Buffer.from(imageData, "base64"));
throw new Error("No image in response");
}
async function generateWithImagen(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const fullPrompt = buildPromptWithAspect(prompt, args.aspectRatio, args.quality);
const imageSize = getGoogleImageSize(args);
if (imageSize === "4K") {
console.error("Warning: Imagen models do not support 4K imageSize, using 2K instead.");
}
const parameters: Record<string, unknown> = {
sampleCount: args.n,
};
if (args.aspectRatio) {
parameters.aspectRatio = args.aspectRatio;
}
if (imageSize === "1K" || imageSize === "2K") {
parameters.imageSize = imageSize;
} else {
parameters.imageSize = "2K";
}
const response = await postGoogleJson<{
predictions?: Array<any>;
generatedImages?: Array<any>;
}>(`${toModelPath(model)}:predict`, {
instances: [
{
prompt: fullPrompt,
},
],
parameters,
});
const imageData = extractPredictedImageData(response);
if (imageData) return Uint8Array.from(Buffer.from(imageData, "base64"));
throw new Error("No image in response");
}
export async function generateImage(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
if (isGoogleImagen(model)) {
if (args.referenceImages.length > 0) {
console.error("Warning: Reference images not supported with Imagen models, ignoring.");
}
return generateWithImagen(prompt, model, args);
}
if (!isGoogleMultimodal(model) && args.referenceImages.length > 0) {
console.error("Warning: Reference images are only supported with Gemini multimodal models.");
}
return generateWithGemini(prompt, model, args);
}
@@ -0,0 +1,114 @@
import type { CliArgs } from "../types";
export function getDefaultModel(): string {
return process.env.OPENAI_IMAGE_MODEL || "gpt-image-1.5";
}
function parseAspectRatio(ar: string): { width: number; height: number } | null {
const match = ar.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
if (!match) return null;
const w = parseFloat(match[1]!);
const h = parseFloat(match[2]!);
if (w <= 0 || h <= 0) return null;
return { width: w, height: h };
}
type SizeMapping = {
square: string;
landscape: string;
portrait: string;
};
function getOpenAISize(
model: string,
ar: string | null,
quality: CliArgs["quality"]
): string {
const isDalle3 = model.includes("dall-e-3");
const isDalle2 = model.includes("dall-e-2");
if (isDalle2) {
return "1024x1024";
}
const sizes: SizeMapping = isDalle3
? {
square: "1024x1024",
landscape: "1792x1024",
portrait: "1024x1792",
}
: {
square: "1024x1024",
landscape: "1536x1024",
portrait: "1024x1536",
};
if (!ar) return sizes.square;
const parsed = parseAspectRatio(ar);
if (!parsed) return sizes.square;
const ratio = parsed.width / parsed.height;
if (Math.abs(ratio - 1) < 0.1) return sizes.square;
if (ratio > 1.5) return sizes.landscape;
if (ratio < 0.67) return sizes.portrait;
return sizes.square;
}
export async function generateImage(
prompt: string,
model: string,
args: CliArgs
): Promise<Uint8Array> {
const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) throw new Error("OPENAI_API_KEY is required");
if (args.referenceImages.length > 0) {
console.error("Warning: Reference images not supported with OpenAI, ignoring.");
}
const size = args.size || getOpenAISize(model, args.aspectRatio, args.quality);
const body: Record<string, any> = {
model,
prompt,
size,
};
if (model.includes("dall-e-3")) {
body.quality = args.quality === "2k" ? "hd" : "standard";
}
const res = await fetch(`${baseURL}/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`OpenAI API error: ${err}`);
}
const result = (await res.json()) as { data: Array<{ url?: string; b64_json?: string }> };
const img = result.data[0];
if (img?.b64_json) {
return Uint8Array.from(Buffer.from(img.b64_json, "base64"));
}
if (img?.url) {
const imgRes = await fetch(img.url);
if (!imgRes.ok) throw new Error("Failed to download image");
const buf = await imgRes.arrayBuffer();
return new Uint8Array(buf);
}
throw new Error("No image in response");
}
+18
View File
@@ -0,0 +1,18 @@
export type Provider = "google" | "openai";
export type Quality = "normal" | "2k";
export type CliArgs = {
prompt: string | null;
promptFiles: string[];
imagePath: string | null;
provider: Provider | null;
model: string | null;
aspectRatio: string | null;
size: string | null;
quality: Quality;
imageSize: string | null;
referenceImages: string[];
n: number;
json: boolean;
help: boolean;
};
+57 -47
View File
@@ -1,84 +1,94 @@
--- ---
name: baoyu-post-to-wechat name: baoyu-post-to-wechat
description: Post content to WeChat Official Account (微信公众号). Supports both article posting (文章) and image-text posting (图文). description: Posts content to WeChat Official Account (微信公众号) via Chrome CDP automation. Supports article posting (文章) with full markdown formatting and image-text posting (图文) with multiple images. Use when user mentions "发布公众号", "post to wechat", "微信公众号", or "图文/文章".
--- ---
# Post to WeChat Official Account (微信公众号) # Post to WeChat Official Account
Post content to WeChat Official Account using Chrome CDP automation.
## Script Directory ## Script Directory
**Important**: All scripts are located in the `scripts/` subdirectory of this skill. **Agent Execution**: Determine this SKILL.md directory as `SKILL_DIR`, then use `${SKILL_DIR}/scripts/<name>.ts`.
**Agent Execution Instructions**:
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
2. Script path = `${SKILL_DIR}/scripts/<script-name>.ts`
3. Replace all `${SKILL_DIR}` in this document with the actual path
**Script Reference**:
| Script | Purpose | | Script | Purpose |
|--------|---------| |--------|---------|
| `scripts/wechat-browser.ts` | Image-text posts (图文) | | `scripts/wechat-browser.ts` | Image-text posts (图文) |
| `scripts/wechat-article.ts` | Full article posting (文章) | | `scripts/wechat-article.ts` | Article posting (文章) |
| `scripts/md-to-wechat.ts` | Markdown → WeChat HTML conversion | | `scripts/md-to-wechat.ts` | Markdown → WeChat HTML |
| `scripts/copy-to-clipboard.ts` | Copy content to clipboard |
| `scripts/paste-from-clipboard.ts` | Send real paste keystroke |
## Quick Usage ## Preferences (EXTEND.md)
### Image-Text (图文) - Multiple images with title/content Use Bash to check EXTEND.md existence (priority order):
```bash ```bash
# From markdown file and image directory # Check project-level first
npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --markdown article.md --images ./images/ test -f .baoyu-skills/baoyu-post-to-wechat/EXTEND.md && echo "project"
# With explicit parameters # Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --title "标题" --content "内容" --image img1.png --image img2.png --submit test -f "$HOME/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md" && echo "user"
``` ```
### Article (文章) - Full markdown with formatting ┌────────────────────────────────────────────────────────┬───────────────────┐
│ 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 │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default theme | Auto-submit preference | Chrome profile path
## Usage
### Image-Text (图文)
```bash
npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --markdown article.md --images ./images/
npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --title "标题" --content "内容" --image img.png --submit
```
### Article (文章)
```bash ```bash
# Post markdown article
npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme grace npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme grace
``` ```
> **Note**: `${SKILL_DIR}` represents this skill's installation directory. Agent replaces with actual path at runtime. ## Detailed References
## 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) |
- **Image-Text Posting**: See `references/image-text-posting.md` for detailed image-text posting guide ## Feature Comparison
- **Article Posting**: See `references/article-posting.md` for detailed article posting guide
## Prerequisites
- Google Chrome installed
- `bun` runtime (via `npx -y bun`)
- First run: log in to WeChat Official Account in the opened browser window
## Features
| Feature | Image-Text | Article | | Feature | Image-Text | Article |
|---------|------------|---------| |---------|------------|---------|
| Multiple images | ✓ (up to 9) | ✓ (inline) | | Multiple images | ✓ (up to 9) | ✓ (inline) |
| Markdown support | Title/content extraction | Full formatting | | Markdown support | Title/content extraction | Full formatting |
| Auto title compression | ✓ (to 20 chars) | ✗ | | Auto compression | ✓ (title: 20, content: 1000 chars) | ✗ |
| Content compression | ✓ (to 1000 chars) | ✗ |
| Themes | ✗ | ✓ (default, grace, simple) | | Themes | ✗ | ✓ (default, grace, simple) |
## Prerequisites
- Google Chrome
- First run: log in to WeChat Official Account (session preserved)
## Troubleshooting ## Troubleshooting
- **Not logged in**: First run opens browser - scan QR code to log in, session is preserved | Issue | Solution |
- **Chrome not found**: Set `WECHAT_BROWSER_CHROME_PATH` environment variable |-------|----------|
- **Paste fails**: Check system clipboard permissions | Not logged in | First run opens browser - scan QR to log in |
| Chrome not found | Set `WECHAT_BROWSER_CHROME_PATH` env var |
| Paste fails | Check system clipboard permissions |
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-post-to-wechat/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-post-to-wechat/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
@@ -3,6 +3,7 @@ import path from 'node:path';
import { mkdir, writeFile } from 'node:fs/promises'; import { mkdir, writeFile } from 'node:fs/promises';
import os from 'node:os'; import os from 'node:os';
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
import https from 'node:https'; import https from 'node:https';
import http from 'node:http'; import http from 'node:http';
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
@@ -111,17 +112,11 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
return { frontmatter, body: match[2]! }; return { frontmatter, body: match[2]! };
} }
async function parseMarkdownForWechat( export async function convertMarkdown(markdownPath: string, options?: { title?: string; theme?: string }): Promise<ParsedResult> {
markdownPath: string,
options?: { title?: string; theme?: string; tempDir?: string },
): Promise<ParsedResult> {
const content = fs.readFileSync(markdownPath, 'utf-8');
const baseDir = path.dirname(markdownPath); const baseDir = path.dirname(markdownPath);
const tempDir = options?.tempDir ?? path.join(os.tmpdir(), 'wechat-article-images'); const content = fs.readFileSync(markdownPath, 'utf-8');
const theme = options?.theme ?? 'default'; const theme = options?.theme ?? 'default';
await mkdir(tempDir, { recursive: true });
const { frontmatter, body } = parseFrontmatter(content); const { frontmatter, body } = parseFrontmatter(content);
let title = options?.title ?? frontmatter.title ?? ''; let title = options?.title ?? frontmatter.title ?? '';
@@ -129,9 +124,9 @@ async function parseMarkdownForWechat(
const h1Match = body.match(/^#\s+(.+)$/m); const h1Match = body.match(/^#\s+(.+)$/m);
if (h1Match) title = h1Match[1]!; if (h1Match) title = h1Match[1]!;
} }
if (!title) title = path.basename(markdownPath, path.extname(markdownPath));
const author = frontmatter.author ?? ''; const author = frontmatter.author || '';
let summary = frontmatter.summary ?? frontmatter.description ?? ''; let summary = frontmatter.description || frontmatter.summary || '';
if (!summary) { if (!summary) {
const lines = body.split('\n'); const lines = body.split('\n');
@@ -168,13 +163,16 @@ async function parseMarkdownForWechat(
const modifiedMarkdown = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`).join('\n')}\n---\n${modifiedBody}`; const modifiedMarkdown = `---\n${Object.entries(frontmatter).map(([k, v]) => `${k}: ${v}`).join('\n')}\n---\n${modifiedBody}`;
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-article-images-'));
const tempMdPath = path.join(tempDir, 'temp-article.md'); const tempMdPath = path.join(tempDir, 'temp-article.md');
await writeFile(tempMdPath, modifiedMarkdown, 'utf-8'); await writeFile(tempMdPath, modifiedMarkdown, 'utf-8');
const scriptDir = path.dirname(new URL(import.meta.url).pathname); const __filename = fileURLToPath(import.meta.url);
const renderScript = path.join(scriptDir, 'md', 'render.ts'); const __dirname = path.dirname(__filename);
const renderScript = path.join(__dirname, 'md', 'render.ts');
console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}`); console.error(`[md-to-wechat] Rendering markdown with theme: ${theme}`);
const result = spawnSync('npx', ['-y', 'bun', renderScript, tempMdPath, '--theme', theme], { const result = spawnSync('npx', ['-y', 'bun', renderScript, tempMdPath, '--theme', theme], {
stdio: ['inherit', 'pipe', 'pipe'], stdio: ['inherit', 'pipe', 'pipe'],
cwd: baseDir, cwd: baseDir,
@@ -227,7 +225,7 @@ Output JSON format:
"contentImages": [ "contentImages": [
{ {
"placeholder": "[[IMAGE_PLACEHOLDER_1]]", "placeholder": "[[IMAGE_PLACEHOLDER_1]]",
"localPath": "/tmp/wechat-article-images/img.png", "localPath": "/tmp/wechat-image/img.png",
"originalPath": "imgs/image.png" "originalPath": "imgs/image.png"
} }
] ]
@@ -262,7 +260,7 @@ async function main(): Promise<void> {
} }
if (!markdownPath) { if (!markdownPath) {
console.error('Error: Markdown file path required'); console.error('Error: Markdown file path is required');
process.exit(1); process.exit(1);
} }
@@ -271,7 +269,7 @@ async function main(): Promise<void> {
process.exit(1); process.exit(1);
} }
const result = await parseMarkdownForWechat(markdownPath, { title, theme }); const result = await convertMarkdown(markdownPath, { title, theme });
console.log(JSON.stringify(result, null, 2)); console.log(JSON.stringify(result, null, 2));
} }
@@ -2,6 +2,7 @@ import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { spawnSync } from 'node:child_process'; import { spawnSync } from 'node:child_process';
import process from 'node:process'; import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { launchChrome, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts'; import { launchChrome, getPageSession, waitForNewTab, clickElement, typeText, evaluate, sleep, type ChromeSession, type CdpConnection } from './cdp.ts';
const WECHAT_URL = 'https://mp.weixin.qq.com/'; const WECHAT_URL = 'https://mp.weixin.qq.com/';
@@ -65,8 +66,9 @@ async function clickMenuByText(session: ChromeSession, text: string): Promise<vo
} }
async function copyImageToClipboard(imagePath: string): Promise<void> { async function copyImageToClipboard(imagePath: string): Promise<void> {
const scriptDir = path.dirname(new URL(import.meta.url).pathname); const __filename = fileURLToPath(import.meta.url);
const copyScript = path.join(scriptDir, './copy-to-clipboard.ts'); const __dirname = path.dirname(__filename);
const copyScript = path.join(__dirname, './copy-to-clipboard.ts');
const result = spawnSync('npx', ['-y', 'bun', copyScript, 'image', imagePath], { stdio: 'inherit' }); const result = spawnSync('npx', ['-y', 'bun', copyScript, 'image', imagePath], { stdio: 'inherit' });
if (result.status !== 0) throw new Error(`Failed to copy image: ${imagePath}`); if (result.status !== 0) throw new Error(`Failed to copy image: ${imagePath}`);
} }
@@ -108,30 +110,54 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr
}, { sessionId }); }, { sessionId });
await sleep(300); await sleep(300);
console.log('[wechat] Copying with system Cmd+C...'); console.log('[wechat] Copying with CDP keyboard event...');
if (process.platform === 'darwin') { const modifiers = process.platform === 'darwin' ? 4 : 2;
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "c" using command down']); await cdp.send('Input.dispatchKeyEvent', {
} else { type: 'keyDown',
spawnSync('xdotool', ['key', 'ctrl+c']); key: 'c',
} code: 'KeyC',
modifiers,
windowsVirtualKeyCode: 67
}, { sessionId });
await sleep(50);
await cdp.send('Input.dispatchKeyEvent', {
type: 'keyUp',
key: 'c',
code: 'KeyC',
modifiers,
windowsVirtualKeyCode: 67
}, { sessionId });
await sleep(1000); await sleep(1000);
console.log('[wechat] Closing HTML tab...'); console.log('[wechat] Closing HTML tab...');
await cdp.send('Target.closeTarget', { targetId }); await cdp.send('Target.closeTarget', { targetId });
} }
async function pasteFromClipboardInEditor(): Promise<void> { async function pasteFromClipboardInEditor(session: ChromeSession): Promise<void> {
if (process.platform === 'darwin') { console.log('[wechat] Pasting with CDP keyboard event...');
spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "v" using command down']); const modifiers = process.platform === 'darwin' ? 4 : 2;
} else { await session.cdp.send('Input.dispatchKeyEvent', {
spawnSync('xdotool', ['key', 'ctrl+v']); type: 'keyDown',
} key: 'v',
code: 'KeyV',
modifiers,
windowsVirtualKeyCode: 86
}, { sessionId: session.sessionId });
await sleep(50);
await session.cdp.send('Input.dispatchKeyEvent', {
type: 'keyUp',
key: 'v',
code: 'KeyV',
modifiers,
windowsVirtualKeyCode: 86
}, { sessionId: session.sessionId });
await sleep(1000); await sleep(1000);
} }
async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> { async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> {
const scriptDir = path.dirname(new URL(import.meta.url).pathname); const __filename = fileURLToPath(import.meta.url);
const mdToWechatScript = path.join(scriptDir, 'md-to-wechat.ts'); const __dirname = path.dirname(__filename);
const mdToWechatScript = path.join(__dirname, 'md-to-wechat.ts');
const args = ['-y', 'bun', mdToWechatScript, markdownPath]; const args = ['-y', 'bun', mdToWechatScript, markdownPath];
if (theme) args.push('--theme', theme); if (theme) args.push('--theme', theme);
@@ -295,6 +321,10 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
console.log('[wechat] Clicking on editor...'); console.log('[wechat] Clicking on editor...');
await clickElement(session, '.ProseMirror'); await clickElement(session, '.ProseMirror');
await sleep(1000);
console.log('[wechat] Ensuring editor focus...');
await clickElement(session, '.ProseMirror');
await sleep(500); await sleep(500);
if (effectiveHtmlFile && fs.existsSync(effectiveHtmlFile)) { if (effectiveHtmlFile && fs.existsSync(effectiveHtmlFile)) {
@@ -302,7 +332,7 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await copyHtmlFromBrowser(cdp, effectiveHtmlFile); await copyHtmlFromBrowser(cdp, effectiveHtmlFile);
await sleep(500); await sleep(500);
console.log('[wechat] Pasting into editor...'); console.log('[wechat] Pasting into editor...');
await pasteFromClipboardInEditor(); await pasteFromClipboardInEditor(session);
await sleep(3000); await sleep(3000);
if (contentImages.length > 0) { if (contentImages.length > 0) {
@@ -328,7 +358,7 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
await sleep(200); await sleep(200);
console.log('[wechat] Pasting image...'); console.log('[wechat] Pasting image...');
await pasteFromClipboardInEditor(); await pasteFromClipboardInEditor(session);
await sleep(3000); await sleep(3000);
} }
console.log('[wechat] All images inserted.'); console.log('[wechat] All images inserted.');
+68 -71
View File
@@ -1,11 +1,11 @@
--- ---
name: baoyu-post-to-x name: baoyu-post-to-x
description: Post content and articles to X (Twitter). Supports regular posts with images/videos and X Articles (long-form Markdown). Uses real Chrome with CDP to bypass anti-automation. description: Posts content and articles to X (Twitter). Supports regular posts with images/videos and X Articles (long-form Markdown). Uses real Chrome with CDP to bypass anti-automation. Use when user asks to "post to X", "tweet", "publish to Twitter", or "share on X".
--- ---
# Post to X (Twitter) # Post to X (Twitter)
Post content, images, videos, and long-form articles to X using real Chrome browser (bypasses anti-bot detection). Posts text, images, videos, and long-form articles to X via real Chrome browser (bypasses anti-bot detection).
## Script Directory ## Script Directory
@@ -27,11 +27,41 @@ Post content, images, videos, and long-form articles to X using real Chrome brow
| `scripts/copy-to-clipboard.ts` | Copy content to clipboard | | `scripts/copy-to-clipboard.ts` | Copy content to clipboard |
| `scripts/paste-from-clipboard.ts` | Send real paste keystroke | | `scripts/paste-from-clipboard.ts` | Send real paste keystroke |
## Preferences (EXTEND.md)
Use Bash to check EXTEND.md existence (priority order):
```bash
# Check project-level first
test -f .baoyu-skills/baoyu-post-to-x/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-post-to-x/EXTEND.md" && echo "user"
```
┌──────────────────────────────────────────────────┬───────────────────┐
│ 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 | Auto-submit preference
## Prerequisites ## Prerequisites
- Google Chrome or Chromium installed - Google Chrome or Chromium
- `bun` installed (for running scripts) - `bun` runtime
- First run: log in to X in the opened browser window - First run: log in to X manually (session saved)
## References ## References
@@ -45,72 +75,57 @@ Post content, images, videos, and long-form articles to X using real Chrome brow
Text + up to 4 images. Text + up to 4 images.
```bash ```bash
# Preview mode (doesn't post) npx -y bun ${SKILL_DIR}/scripts/x-browser.ts "Hello!" --image ./photo.png # Preview
npx -y bun ${SKILL_DIR}/scripts/x-browser.ts "Hello from Claude!" --image ./screenshot.png npx -y bun ${SKILL_DIR}/scripts/x-browser.ts "Hello!" --image ./photo.png --submit # Post
# Actually post
npx -y bun ${SKILL_DIR}/scripts/x-browser.ts "Hello!" --image ./photo.png --submit
``` ```
> **Note**: `${SKILL_DIR}` represents this skill's installation directory. Agent replaces with actual path at runtime.
**Parameters**: **Parameters**:
| Parameter | Description | | Parameter | Description |
|-----------|-------------| |-----------|-------------|
| `<text>` | Post content (positional argument) | | `<text>` | Post content (positional) |
| `--image <path>` | Image file path (can be repeated, max 4) | | `--image <path>` | Image file (repeatable, max 4) |
| `--submit` | Actually post (default: preview only) | | `--submit` | Post (default: preview) |
| `--profile <dir>` | Custom Chrome profile directory | | `--profile <dir>` | Custom Chrome profile |
--- ---
## Video Posts ## Video Posts
Text + video file (MP4, MOV, WebM). Text + video file.
```bash ```bash
# Preview mode (doesn't post) npx -y bun ${SKILL_DIR}/scripts/x-video.ts "Check this out!" --video ./clip.mp4 # Preview
npx -y bun ${SKILL_DIR}/scripts/x-video.ts "Check out this video!" --video ./clip.mp4 npx -y bun ${SKILL_DIR}/scripts/x-video.ts "Amazing content" --video ./demo.mp4 --submit # Post
# Actually post
npx -y bun ${SKILL_DIR}/scripts/x-video.ts "Amazing content" --video ./demo.mp4 --submit
``` ```
**Parameters**: **Parameters**:
| Parameter | Description | | Parameter | Description |
|-----------|-------------| |-----------|-------------|
| `<text>` | Post content (positional argument) | | `<text>` | Post content (positional) |
| `--video <path>` | Video file path (required) | | `--video <path>` | Video file (MP4, MOV, WebM) |
| `--submit` | Actually post (default: preview only) | | `--submit` | Post (default: preview) |
| `--profile <dir>` | Custom Chrome profile directory | | `--profile <dir>` | Custom Chrome profile |
**Video Limits**: **Limits**: Regular 140s max, Premium 60min. Processing: 30-60s.
- Regular accounts: 140 seconds max
- X Premium: up to 60 minutes
- Supported formats: MP4, MOV, WebM
- Processing time: 30-60 seconds depending on file size
--- ---
## Quote Tweets ## Quote Tweets
Quote an existing tweet with your comment - a way to share content while giving credit to the original creator. Quote an existing tweet with comment.
```bash ```bash
# Preview mode (doesn't post) npx -y bun ${SKILL_DIR}/scripts/x-quote.ts https://x.com/user/status/123 "Great insight!" # Preview
npx -y bun ${SKILL_DIR}/scripts/x-quote.ts https://x.com/user/status/123456789 "Great insight!" npx -y bun ${SKILL_DIR}/scripts/x-quote.ts https://x.com/user/status/123 "I agree!" --submit # Post
# Actually post
npx -y bun ${SKILL_DIR}/scripts/x-quote.ts https://x.com/user/status/123456789 "I agree!" --submit
``` ```
**Parameters**: **Parameters**:
| Parameter | Description | | Parameter | Description |
|-----------|-------------| |-----------|-------------|
| `<tweet-url>` | URL of the tweet to quote (positional argument) | | `<tweet-url>` | URL to quote (positional) |
| `<comment>` | Your comment text (positional argument, optional) | | `<comment>` | Comment text (positional, optional) |
| `--submit` | Actually post (default: preview only) | | `--submit` | Post (default: preview) |
| `--profile <dir>` | Custom Chrome profile directory | | `--profile <dir>` | Custom Chrome profile |
--- ---
@@ -119,47 +134,29 @@ npx -y bun ${SKILL_DIR}/scripts/x-quote.ts https://x.com/user/status/123456789 "
Long-form Markdown articles (requires X Premium). Long-form Markdown articles (requires X Premium).
```bash ```bash
# Preview mode npx -y bun ${SKILL_DIR}/scripts/x-article.ts article.md # Preview
npx -y bun ${SKILL_DIR}/scripts/x-article.ts article.md npx -y bun ${SKILL_DIR}/scripts/x-article.ts article.md --cover ./cover.jpg # With cover
npx -y bun ${SKILL_DIR}/scripts/x-article.ts article.md --submit # Publish
# With cover image
npx -y bun ${SKILL_DIR}/scripts/x-article.ts article.md --cover ./cover.jpg
# Publish
npx -y bun ${SKILL_DIR}/scripts/x-article.ts article.md --submit
``` ```
**Parameters**: **Parameters**:
| Parameter | Description | | Parameter | Description |
|-----------|-------------| |-----------|-------------|
| `<markdown>` | Markdown file path (positional argument) | | `<markdown>` | Markdown file (positional) |
| `--cover <path>` | Cover image path | | `--cover <path>` | Cover image |
| `--title <text>` | Override article title | | `--title <text>` | Override title |
| `--submit` | Actually publish (default: preview only) | | `--submit` | Publish (default: preview) |
**Frontmatter** (optional): **Frontmatter**: `title`, `cover_image` supported in YAML front matter.
```yaml
---
title: My Article Title
cover_image: /path/to/cover.jpg
---
```
--- ---
## Notes ## Notes
- First run requires manual login (session is saved) - First run: manual login required (session persists)
- Always preview before using `--submit` - Always preview before `--submit`
- Browser closes automatically after operation - Cross-platform: macOS, Linux, Windows
- Supports macOS, Linux, and Windows
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-post-to-x/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-post-to-x/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
+45 -94
View File
@@ -21,6 +21,36 @@ Fetches any URL via Chrome CDP and converts HTML to clean markdown.
|--------|---------| |--------|---------|
| `scripts/main.ts` | CLI entry point for URL fetching | | `scripts/main.ts` | CLI entry point for URL fetching |
## Preferences (EXTEND.md)
Use Bash to check EXTEND.md existence (priority order):
```bash
# Check project-level first
test -f .baoyu-skills/baoyu-url-to-markdown/EXTEND.md && echo "project"
# Then user-level (cross-platform: $HOME works on macOS/Linux/WSL)
test -f "$HOME/.baoyu-skills/baoyu-url-to-markdown/EXTEND.md" && echo "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 │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Found │ Read, parse, apply settings │
├───────────┼───────────────────────────────────────────────────────────────────────────┤
│ Not found │ Use defaults │
└───────────┴───────────────────────────────────────────────────────────────────────────┘
**EXTEND.md Supports**: Default output directory | Default capture mode | Timeout settings
## Features ## Features
- Chrome CDP for full JavaScript rendering - Chrome CDP for full JavaScript rendering
@@ -52,103 +82,28 @@ npx -y bun ${SKILL_DIR}/scripts/main.ts <url> -o output.md
## Capture Modes ## Capture Modes
### Auto Mode (default) | Mode | Behavior | Use When |
|------|----------|----------|
| Auto (default) | Capture on network idle | Public pages, static content |
| Wait (`--wait`) | User signals when ready | Login-required, lazy loading, paywalls |
Page loads → waits for network idle → captures immediately. **Wait mode workflow**:
1. Run with `--wait` → script outputs "Press Enter when ready"
Best for: 2. Ask user to confirm page is ready
- Public pages 3. Send newline to stdin to trigger capture
- Static content
- No login required
### Wait Mode (`--wait`)
Page opens → user can interact (login, scroll, etc.) → user signals ready → captures.
Best for:
- Login-required pages
- Dynamic content needing interaction
- Pages with lazy loading
**Agent workflow for wait mode**:
1. Run script with `--wait` flag
2. Script outputs: `Page opened. Press Enter when ready to capture...`
3. Use `AskUserQuestion` to ask user if page is ready
4. When user confirms, send newline to stdin to trigger capture
## Output Format ## Output Format
```markdown YAML front matter with `url`, `title`, `description`, `author`, `published`, `captured_at` fields, followed by converted markdown content.
---
url: https://example.com/page
title: "Page Title"
description: "Meta description if available"
author: "Author if available"
published: "2024-01-01"
captured_at: "2024-01-15T10:30:00Z"
---
# Page Title
Converted markdown content...
```
## Mode Selection Guide
When user requests URL capture, help select appropriate mode:
**Suggest Auto Mode when**:
- URL is public (no login wall visible)
- Content appears static
- User doesn't mention login requirements
**Suggest Wait Mode when**:
- User mentions needing to log in
- Site known to require authentication
- User wants to scroll/interact before capture
- Content is behind paywall
**Ask user when unclear**:
```
The page may require login or interaction before capturing.
Which mode should I use?
1. Auto - Capture immediately when loaded
2. Wait - Wait for you to interact first
```
## Output Directory ## Output Directory
Each capture creates a file organized by domain:
``` ```
url-to-markdown/ url-to-markdown/<domain>/<slug>.md
└── <domain>/
└── <slug>.md
``` ```
**Path Components**: - `<slug>`: From page title or URL path (kebab-case, 2-6 words)
- `<domain>`: Site domain (e.g., `example.com`, `github.com`) - Conflict resolution: Append timestamp `<slug>-YYYYMMDD-HHMMSS.md`
- `<slug>`: Generated from page title or URL path (kebab-case)
**Slug Generation**:
1. Extract from page title (preferred) or URL path
2. Convert to kebab-case, 2-6 words
3. Example: "Getting Started with React" → `getting-started-with-react`
**Conflict Resolution**:
If `url-to-markdown/<domain>/<slug>.md` already exists:
- Append timestamp: `<slug>-YYYYMMDD-HHMMSS.md`
- Example: `getting-started.md` exists → `getting-started-20260118-143052.md`
## Error Handling
| Error | Resolution |
|-------|------------|
| Chrome not found | Install Chrome or set `URL_CHROME_PATH` env |
| Page timeout | Increase `--timeout` value |
| Capture failed | Try wait mode for complex pages |
| Empty content | Page may need JS rendering time |
## Environment Variables ## Environment Variables
@@ -158,12 +113,8 @@ If `url-to-markdown/<domain>/<slug>.md` already exists:
| `URL_DATA_DIR` | Custom data directory | | `URL_DATA_DIR` | Custom data directory |
| `URL_CHROME_PROFILE_DIR` | Custom Chrome profile directory | | `URL_CHROME_PROFILE_DIR` | Custom Chrome profile directory |
**Troubleshooting**: Chrome not found → set `URL_CHROME_PATH`. Timeout → increase `--timeout`. Complex pages → try `--wait` mode.
## Extension Support ## Extension Support
Custom configurations via EXTEND.md. Custom configurations via EXTEND.md. See **Preferences** section for paths and supported options.
**Check paths** (priority order):
1. `.baoyu-skills/baoyu-url-to-markdown/EXTEND.md` (project)
2. `~/.baoyu-skills/baoyu-url-to-markdown/EXTEND.md` (user)
If found, load before workflow. Extension content overrides defaults.
+2 -3
View File
@@ -359,9 +359,8 @@ With confirmed outline + style + layout:
**Watermark Application** (if enabled in preferences): **Watermark Application** (if enabled in preferences):
Add to each image generation prompt: Add to each image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
Reference: `references/config/watermark-guide.md` Reference: `references/config/watermark-guide.md`
@@ -15,7 +15,6 @@ watermark:
enabled: false enabled: false
content: "" content: ""
position: bottom-right # bottom-right|bottom-left|bottom-center|top-right position: bottom-right # bottom-right|bottom-left|bottom-center|top-right
opacity: 0.7 # 0.1-1.0
preferred_style: preferred_style:
name: null # Built-in or custom style name name: null # Built-in or custom style name
@@ -46,7 +45,6 @@ custom_styles:
| `watermark.enabled` | bool | false | Enable watermark | | `watermark.enabled` | bool | false | Enable watermark |
| `watermark.content` | string | "" | Watermark text (@username or custom) | | `watermark.content` | string | "" | Watermark text (@username or custom) |
| `watermark.position` | enum | bottom-right | Position on image | | `watermark.position` | enum | bottom-right | Position on image |
| `watermark.opacity` | float | 0.7 | Transparency (0.1-1.0) |
| `preferred_style.name` | string | null | Style name or null | | `preferred_style.name` | string | null | Style name or null |
| `preferred_style.description` | string | "" | Custom notes/override | | `preferred_style.description` | string | "" | Custom notes/override |
| `preferred_layout` | string | null | Layout preference or null | | `preferred_layout` | string | null | Layout preference or null |
@@ -97,7 +95,6 @@ watermark:
enabled: true enabled: true
content: "@myxhsaccount" content: "@myxhsaccount"
position: bottom-right position: bottom-right
opacity: 0.5
preferred_style: preferred_style:
name: notion name: notion
@@ -28,15 +28,6 @@ description: Watermark configuration guide for baoyu-xhs-images
| `bottom-center` | Centered designs | Text-heavy bottom area | | `bottom-center` | Centered designs | Text-heavy bottom area |
| `top-right` | Bottom-heavy content | Title/header in top-right | | `top-right` | Bottom-heavy content | Title/header in top-right |
## Opacity Examples
| Opacity | Visual Effect | Use Case |
|---------|---------------|----------|
| 0.3 | Very subtle, barely visible | Clean aesthetic priority |
| 0.5 | Balanced, noticeable but not distracting | Default recommendation |
| 0.7 | Clearly visible | Brand recognition priority |
| 0.9 | Strong presence | Anti-copy protection |
## Content Format ## Content Format
| Format | Example | Style | | Format | Example | Style |
@@ -51,23 +42,21 @@ description: Watermark configuration guide for baoyu-xhs-images
1. **Consistency**: Use same watermark across all images in series 1. **Consistency**: Use same watermark across all images in series
2. **Legibility**: Ensure watermark readable on both light/dark areas 2. **Legibility**: Ensure watermark readable on both light/dark areas
3. **Size**: Keep subtle - should not distract from content 3. **Size**: Keep subtle - should not distract from content
4. **Contrast**: Opacity may need adjustment per image background
## Prompt Integration ## Prompt Integration
When watermark is enabled, add to image generation prompt: When watermark is enabled, add to image generation prompt:
``` ```
Include a subtle watermark "[content]" positioned at [position] Include a subtle watermark "[content]" positioned at [position].
with approximately [opacity*100]% visibility. The watermark should The watermark should be legible but not distracting from the main content.
be legible but not distracting from the main content.
``` ```
## Common Issues ## Common Issues
| Issue | Solution | | Issue | Solution |
|-------|----------| |-------|----------|
| Watermark invisible | Increase opacity or adjust position | | Watermark invisible | Adjust position or check contrast |
| Watermark too prominent | Decrease opacity (0.3-0.5) | | Watermark too prominent | Change position or reduce size |
| Watermark overlaps content | Change position | | Watermark overlaps content | Change position |
| Inconsistent across images | Use session ID for consistency | | Inconsistent across images | Use session ID for consistency |