Add baoyu-skills package
This commit is contained in:
27
baoyu-skills/docs/chrome-profile.md
Normal file
27
baoyu-skills/docs/chrome-profile.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Chrome Profile
|
||||
|
||||
All CDP skills share a single profile directory. Do NOT create per-skill profiles.
|
||||
|
||||
Override: `BAOYU_CHROME_PROFILE_DIR` env var (takes priority over all defaults).
|
||||
|
||||
| Platform | Default Path |
|
||||
|----------|-------------|
|
||||
| macOS | `~/Library/Application Support/baoyu-skills/chrome-profile` |
|
||||
| Linux | `$XDG_DATA_HOME/baoyu-skills/chrome-profile` (fallback `~/.local/share/`) |
|
||||
| Windows | `%APPDATA%/baoyu-skills/chrome-profile` |
|
||||
| WSL | Windows home `/.local/share/baoyu-skills/chrome-profile` |
|
||||
|
||||
New skills: use `BAOYU_CHROME_PROFILE_DIR` only (not per-skill env vars like `X_BROWSER_PROFILE_DIR`).
|
||||
|
||||
## Implementation Pattern
|
||||
|
||||
```typescript
|
||||
function getDefaultProfileDir(): string {
|
||||
const override = process.env.BAOYU_CHROME_PROFILE_DIR?.trim();
|
||||
if (override) return path.resolve(override);
|
||||
const base = process.platform === 'darwin'
|
||||
? path.join(os.homedir(), 'Library', 'Application Support')
|
||||
: process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local', 'share');
|
||||
return path.join(base, 'baoyu-skills', 'chrome-profile');
|
||||
}
|
||||
```
|
||||
272
baoyu-skills/docs/codex-imagegen-backend.md
Normal file
272
baoyu-skills/docs/codex-imagegen-backend.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# `codex-imagegen` Backend
|
||||
|
||||
Generate images via Codex CLI's built-in `image_gen` tool from non-Codex runtimes (e.g., Claude Code). The wrapper spawns `codex exec --json` and lets the user's existing Codex subscription drive image generation — **no `OPENAI_API_KEY` required**.
|
||||
|
||||
This backend implements the `preferred_image_backend: codex-imagegen` config key already referenced in several `SKILL.md` files across this repo.
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Status |
|
||||
|---------|--------|
|
||||
| **Reliability**: retry + exponential backoff | Default 2 retries |
|
||||
| **Verification**: confirms `image_gen` was actually invoked (not bypassed) | Checks `$CODEX_HOME/generated_images/{thread_id}/` |
|
||||
| **Verification**: PNG magic-byte sanity check | ✓ |
|
||||
| **Idempotency cache**: reuses output for same prompt+aspect+refs | `--cache-dir` |
|
||||
| **Concurrency control**: file lock prevents parallel `codex exec` collisions | Built-in |
|
||||
| **Structured logging**: JSONL log file | `--log-file` |
|
||||
| **Token usage returned** | Embedded in result JSON |
|
||||
| **`--ref` reference images** | Repeatable |
|
||||
| **Unit tests** | 16 tests (parser / cache / validator) |
|
||||
| **Error classification**: retryable vs non-retryable | 9 `error_kind` values |
|
||||
|
||||
## Why this backend
|
||||
|
||||
| Scenario | Conventional backend | This backend |
|
||||
|----------|---------------------|--------------|
|
||||
| You have a Codex subscription | OpenAI Images API costs add up per image | Subscription already covers it — zero marginal API cost |
|
||||
| No `OPENAI_API_KEY` available | `baoyu-image-gen` needs an API key | `codex login` is enough |
|
||||
| Want to use GPT Image 2 | Only via OpenAI API | Codex's `image_gen` *is* GPT Image 2 |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
npm install -g @openai/codex
|
||||
codex login # signs in with your OpenAI account (subscription)
|
||||
codex --version # confirm >= 0.130
|
||||
```
|
||||
|
||||
`bun` is required for running the wrapper. On macOS:
|
||||
|
||||
```bash
|
||||
brew install oven-sh/bun/bun
|
||||
```
|
||||
|
||||
If `bun` is not on `PATH`, fall back to `npx -y bun packages/baoyu-codex-imagegen/src/main.ts …`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Direct CLI
|
||||
|
||||
```bash
|
||||
# Inline prompt
|
||||
bun packages/baoyu-codex-imagegen/src/main.ts \
|
||||
--image /tmp/cat.png \
|
||||
--prompt "A friendly orange cat, watercolor"
|
||||
|
||||
# Prompt from file
|
||||
bun packages/baoyu-codex-imagegen/src/main.ts \
|
||||
--image cover.png \
|
||||
--prompt-file prompts/01-cover.md \
|
||||
--aspect 16:9
|
||||
|
||||
# Verbose mode for debugging
|
||||
bun packages/baoyu-codex-imagegen/src/main.ts -v --image dog.png --prompt "A corgi" --aspect 1:1
|
||||
```
|
||||
|
||||
### Through `baoyu-image-gen`
|
||||
|
||||
```bash
|
||||
${BUN_X} skills/baoyu-image-gen/scripts/main.ts \
|
||||
--provider codex-cli \
|
||||
--prompt "A friendly orange cat, watercolor" \
|
||||
--image /tmp/cat.png \
|
||||
--ar 1:1
|
||||
```
|
||||
|
||||
The `codex-cli` provider spawns the bundled `codex-imagegen` TS entrypoint internally and surfaces its retry/cache machinery through baoyu-image-gen's standard CLI + batch flow.
|
||||
|
||||
On success, stdout emits a single JSON line:
|
||||
|
||||
```json
|
||||
{"status":"ok","path":"/tmp/cat.png","bytes":2567101,"elapsed_seconds":53}
|
||||
```
|
||||
|
||||
On failure, exit code is non-zero and stderr contains the error message.
|
||||
|
||||
### Enabling within image skills
|
||||
|
||||
Image-generating skills (e.g., `baoyu-cover-image`, `baoyu-article-illustrator`) already support a `preferred_image_backend` preference. To route them through this backend, set the following in the corresponding `EXTEND.md`:
|
||||
|
||||
```yaml
|
||||
# ~/.baoyu-skills/baoyu-cover-image/EXTEND.md
|
||||
preferred_image_backend: codex-imagegen
|
||||
```
|
||||
|
||||
When the LLM runs the skill, it reads the preference and — guided by the `### codex-imagegen Backend` section in `CLAUDE.md` — invokes `bun packages/baoyu-codex-imagegen/src/main.ts`.
|
||||
|
||||
> **Note**: The integration is mediated by the LLM reading `CLAUDE.md`. It is not a hard binding. If a skill does not route to the backend automatically, mentioning it explicitly in the prompt works.
|
||||
|
||||
## Parameters
|
||||
|
||||
| Flag | Required | Description |
|
||||
|------|----------|-------------|
|
||||
| `--image <path>` | ✓ | Output PNG path (absolute recommended; relative paths are resolved against cwd) |
|
||||
| `--prompt <text>` | one of | Prompt string (mutually exclusive with `--prompt-file`) |
|
||||
| `--prompt-file <path>` | one of | Read prompt from file (mutually exclusive with `--prompt`) |
|
||||
| `--aspect <ratio>` | | Aspect ratio. Default `1:1`. Common: `16:9`, `9:16`, `4:3`, `2.35:1` |
|
||||
| `--ref <file>` | | Reference image path (repeatable) |
|
||||
| `--timeout <ms>` | | `codex exec` timeout in ms. Default `300000` |
|
||||
| `--retries <n>` | | Retry count on retryable errors. Default `2` (total attempts = retries + 1) |
|
||||
| `--retry-delay <ms>` | | Base delay between retries (exponential backoff). Default `1500` |
|
||||
| `--cache-dir <path>` | | Enable idempotency cache (reuses output for same prompt+aspect+refs) |
|
||||
| `--log-file <path>` | | Structured JSONL log path (appended) |
|
||||
| `-v` / `--verbose` | | Mirror log entries to stderr |
|
||||
| `-h` / `--help` | | Show usage |
|
||||
|
||||
## Structured Output
|
||||
|
||||
On success, stdout contains a single JSON line:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"path": "/tmp/owl.png",
|
||||
"bytes": 1693831,
|
||||
"elapsed_seconds": 87,
|
||||
"thread_id": "019e40e8-daef-7c60-943d-5e7bb3f6cb3d",
|
||||
"attempts": 1,
|
||||
"cached": false,
|
||||
"usage": {
|
||||
"input": 110899,
|
||||
"cached_input": 83456,
|
||||
"output": 457,
|
||||
"reasoning": 47
|
||||
},
|
||||
"tool_calls": [
|
||||
{"tool": "shell", "status": "completed"},
|
||||
{"tool": "agent_message", "status": "completed"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Cache hits return with `elapsed_seconds: 0`, `cached: true`, `attempts: 0`.
|
||||
|
||||
On failure, exit code is `1` and the JSON contains `error` and `error_kind`:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": "image_gen was not invoked: no PNG in ...",
|
||||
"error_kind": "no_image_gen_tool_use"
|
||||
}
|
||||
```
|
||||
|
||||
## Error Kinds
|
||||
|
||||
| `error_kind` | Retryable | Meaning |
|
||||
|--------------|-----------|---------|
|
||||
| `codex_not_installed` | ✗ | `codex` CLI not found |
|
||||
| `invalid_args` | ✗ | Argument parsing error |
|
||||
| `prompt_file_missing` | ✗ | `--prompt-file` path does not exist |
|
||||
| `spawn_failed` | ✓ | `codex exec` exited non-zero |
|
||||
| `timeout` | ✓ | Exceeded `--timeout` |
|
||||
| `no_image_gen_tool_use` | ✓ | Agent did not invoke `image_gen` (it took another path) |
|
||||
| `output_missing` | ✓ | Output file not created |
|
||||
| `invalid_png` | ✓ | Output is not a valid PNG |
|
||||
| `agent_refused` | ✓ | No `thread_id` in event stream (Codex refused to respond) |
|
||||
| `lock_busy` | ✗ | Concurrency lock acquisition timed out |
|
||||
|
||||
## Measured Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| First-run latency | 50–90 s |
|
||||
| Cache-hit latency | < 0.3 s |
|
||||
| Output dimensions | 1024×1024, 1672×941 (16:9), etc. — chosen by `image_gen` |
|
||||
| Output format | PNG (RGB, 8-bit) |
|
||||
| Token usage per call | ~110k input (~80k cached) + ~500 output |
|
||||
| Quota source | Codex subscription (does not consume OpenAI API quota) |
|
||||
| Default timeout | 300 s (5 min) |
|
||||
|
||||
## Limitations & Risks
|
||||
|
||||
1. **5–10× slower than direct API**. `codex exec` cold-starts the agent, loads the built-in `image_gen` SKILL.md, and runs reasoning before invoking the tool. Cache hits avoid this for repeated prompts.
|
||||
2. **ToS gray area**. Codex's `image_gen` tool is designed for interactive use. Invoking it programmatically via `codex exec` from an external agent is not explicitly addressed by current OpenAI policies. Suggested guardrails:
|
||||
- Personal, low-volume use is reasonable.
|
||||
- Not recommended for production automation or high-volume batch jobs.
|
||||
- Users are responsible for ensuring their usage complies with applicable terms of service.
|
||||
3. **Sandbox permissions**. The wrapper passes `--sandbox danger-full-access` so the spawned agent can move the rendered PNG out of `$CODEX_HOME/generated_images/`. This is necessary because the agent must `cp`/`mv` the file to the user-specified output path.
|
||||
4. **Concurrency = 1**. The file lock serializes concurrent invocations to avoid `codex exec` collisions. Parallel calls queue.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | `error_kind` | Resolution |
|
||||
|---------|--------------|------------|
|
||||
| `command not found: codex` | `codex_not_installed` | `npm install -g @openai/codex` |
|
||||
| `codex exec` fails | `spawn_failed` | Check `codex login` status; inspect `raw_log` path |
|
||||
| Timeout | `timeout` | Pass `--timeout 600000` (10 min) for slow networks |
|
||||
| Agent skipped `image_gen` | `no_image_gen_tool_use` | Auto-retries; consider sharpening the prompt — abstract prompts let the agent wander |
|
||||
| Output missing | `output_missing` | Agent did not `cp` to the target path; check `raw_log` for the actual save location under `generated_images/` |
|
||||
| Lock held | `lock_busy` | Wait for the in-flight request to finish; or `rm ~/.cache/baoyu-codex-imagegen/codex-exec.lock` |
|
||||
| Low image quality | — | Sharpen the prompt, try a different aspect, or supply `--ref` |
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
packages/baoyu-codex-imagegen/
|
||||
├── src/
|
||||
│ ├── main.ts # parseArgs → cache → lock → retry loop → emit JSON (`#!/usr/bin/env bun`)
|
||||
│ ├── types.ts # CliOptions, GenerateResult, GenError, ErrorKind
|
||||
│ ├── spawn.ts # spawn codex exec --json --sandbox danger-full-access
|
||||
│ ├── parser.ts # parse JSONL event stream → toolCalls, usage, thread_id
|
||||
│ ├── validator.ts # verify image_gen invocation + PNG magic + file size
|
||||
│ ├── cache.ts # cacheKey(sha256), FileLock, lookup/store
|
||||
│ ├── logger.ts # JsonLogger (verbose stderr + JSONL file)
|
||||
│ ├── parser.test.ts
|
||||
│ ├── cache.test.ts
|
||||
│ └── validator.test.ts
|
||||
├── package.json # workspace package: `bin` → `src/main.ts`, no build step
|
||||
└── README.md
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
cd packages/baoyu-codex-imagegen && bun test
|
||||
```
|
||||
|
||||
## Internal Flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CC[Claude Code / any caller]
|
||||
WRAPPER[bun packages/baoyu-codex-imagegen/<br/>src/main.ts]
|
||||
CODEX["codex exec --json<br/>--sandbox danger-full-access"]
|
||||
AGENT[Codex agent]
|
||||
TOOL[image_gen built-in tool]
|
||||
DEFAULT["$CODEX_HOME/<br/>generated_images/{thread_id}/"]
|
||||
OUT[/specified OUTPUT path/]
|
||||
|
||||
CC -->|exec wrapper| WRAPPER
|
||||
WRAPPER -->|stdin: instruction| CODEX
|
||||
CODEX --> AGENT
|
||||
AGENT -->|tool call| TOOL
|
||||
TOOL -->|writes file| DEFAULT
|
||||
AGENT -->|agent cp/mv| OUT
|
||||
WRAPPER -->|verify + parse| CC
|
||||
|
||||
classDef cc fill:#1e40af,color:#fff,stroke:#93c5fd
|
||||
classDef cdx fill:#7c2d12,color:#fff,stroke:#fdba74
|
||||
class CC,WRAPPER cc
|
||||
class CODEX,AGENT,TOOL cdx
|
||||
```
|
||||
|
||||
## Design Decisions
|
||||
|
||||
1. **Pure TypeScript entrypoint** — `src/main.ts` carries a `#!/usr/bin/env bun` shebang and is the sole entry. There is no shell shim: callers either invoke `bun src/main.ts …` directly, run the file as an executable (when `bun` is on `PATH`), or fall back to `npx -y bun src/main.ts …`. This matches the project's `skills/<skill>/scripts/main.ts` convention.
|
||||
2. **`--sandbox danger-full-access`** — necessary so the spawned agent can `cp`/`mv` the rendered PNG out of `$CODEX_HOME/generated_images/` to the user-specified path. Standard sandboxes block this.
|
||||
3. **Parse the JSONL event stream** — the final `agent_message` and intermediate `command_execution` events let the wrapper verify what actually happened (was `image_gen` called? did `cp` reach the right destination?), which is far more reliable than scraping freeform stdout.
|
||||
4. **Shared package, not a skill** — this backend is a CLI utility that skills route to via `preferred_image_backend` and that `baoyu-image-gen --provider codex-cli` spawns internally. It lives under `packages/` alongside the other shared workspaces (`baoyu-md`, `baoyu-chrome-cdp`, `baoyu-fetch`) because it has no `SKILL.md` and is never loaded directly by an agent.
|
||||
5. **File lock instead of internal queue** — keeps the implementation small and works across multiple shell sessions or processes invoking the same wrapper concurrently.
|
||||
|
||||
## Related Files
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `packages/baoyu-codex-imagegen/src/main.ts` | TypeScript CLI entrypoint (`#!/usr/bin/env bun`) |
|
||||
| `packages/baoyu-codex-imagegen/src/` | TypeScript implementation |
|
||||
| `packages/baoyu-codex-imagegen/package.json` | Workspace manifest |
|
||||
| `skills/baoyu-image-gen/scripts/providers/codex-cli.ts` | Provider adapter that lets `baoyu-image-gen --provider codex-cli` spawn this wrapper |
|
||||
| `docs/codex-imagegen-backend.md` | This document |
|
||||
| `CLAUDE.md` | Tells LLMs how to invoke this backend |
|
||||
| `.github/workflows/codex-imagegen-tests.yml` | CI unit tests |
|
||||
35
baoyu-skills/docs/comic-style-maintenance.md
Normal file
35
baoyu-skills/docs/comic-style-maintenance.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Style Maintenance (baoyu-comic)
|
||||
|
||||
## Adding a New Style
|
||||
|
||||
1. Create style definition: `skills/baoyu-comic/references/styles/<style-name>.md`
|
||||
2. Update SKILL.md: add to `--style` options table + auto-selection entry
|
||||
3. Generate showcase image:
|
||||
```bash
|
||||
${BUN_X} skills/baoyu-danger-gemini-web/scripts/main.ts \
|
||||
--prompt "A single comic book page in <style-name> style showing [scene]. Features: [characteristics]. 3:4 portrait aspect ratio comic page." \
|
||||
--image screenshots/comic-styles/<style-name>.png
|
||||
```
|
||||
4. Compress: `${BUN_X} skills/baoyu-compress-image/scripts/main.ts screenshots/comic-styles/<style-name>.png`
|
||||
5. Update both READMEs (`README.md` + `README.zh.md`): add style to options, description table, preview grid
|
||||
|
||||
## Updating an Existing Style
|
||||
|
||||
1. Update style definition in `references/styles/`
|
||||
2. Regenerate showcase image if visual characteristics changed (steps 3-4 above)
|
||||
3. Update READMEs if description changed
|
||||
|
||||
## Deleting a Style
|
||||
|
||||
1. Delete style definition + showcase image (`.webp`)
|
||||
2. Remove from SKILL.md `--style` options + auto-selection
|
||||
3. Remove from both READMEs (options, description table, preview grid)
|
||||
|
||||
## Style Preview Grid Format
|
||||
|
||||
```markdown
|
||||
| | | |
|
||||
|:---:|:---:|:---:|
|
||||
|  |  |  |
|
||||
| style1 | style2 | style3 |
|
||||
```
|
||||
175
baoyu-skills/docs/creating-skills.md
Normal file
175
baoyu-skills/docs/creating-skills.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Creating New Skills
|
||||
|
||||
**REQUIRED READING**: [Skill authoring best practices](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices)
|
||||
|
||||
## Key Requirements
|
||||
|
||||
| Requirement | Details |
|
||||
|-------------|---------|
|
||||
| **Prefix** | All skills MUST use `baoyu-` prefix |
|
||||
| **name field** | Max 64 chars, lowercase letters/numbers/hyphens only, no "anthropic"/"claude" |
|
||||
| **description** | Max 1024 chars, third person, include what + when to use |
|
||||
| **SKILL.md body** | Keep under 500 lines; use `references/` for additional content |
|
||||
| **References** | One level deep from SKILL.md; avoid nested references |
|
||||
|
||||
## SKILL.md Frontmatter Template
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: baoyu-<name>
|
||||
description: <Third-person description. What it does + when to use it.>
|
||||
version: <semver matching marketplace.json>
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-<name>
|
||||
requires: # include only if skill has scripts
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
---
|
||||
```
|
||||
|
||||
## Steps
|
||||
|
||||
1. Create `skills/baoyu-<name>/SKILL.md` with YAML front matter
|
||||
2. Add TypeScript in `skills/baoyu-<name>/scripts/` (if applicable)
|
||||
3. Add prompt templates in `skills/baoyu-<name>/prompts/` if needed
|
||||
4. Register the skill in `.claude-plugin/marketplace.json` under the `baoyu-skills` plugin entry
|
||||
5. Add Script Directory section to SKILL.md if skill has scripts
|
||||
6. Add openclaw metadata to frontmatter
|
||||
|
||||
## Skill Grouping
|
||||
|
||||
All skills are registered under the single `baoyu-skills` plugin. Use these logical groups when deciding where the skill should appear in the docs:
|
||||
|
||||
| If your skill... | Use group |
|
||||
|------------------|-----------|
|
||||
| Generates visual content (images, slides, comics) | Content Skills |
|
||||
| Publishes to platforms (X, WeChat, Weibo) | Content Skills |
|
||||
| Provides AI generation backend | AI Generation Skills |
|
||||
| Converts or processes content | Utility Skills |
|
||||
|
||||
If you add a new logical group, update the docs that present grouped skills, but keep the skill registered under the single `baoyu-skills` plugin entry.
|
||||
|
||||
## Writing Descriptions
|
||||
|
||||
**MUST write in third person**:
|
||||
|
||||
```yaml
|
||||
# Good
|
||||
description: Generates Xiaohongshu infographic series from content. Use when user asks for "小红书图片", "XHS images".
|
||||
|
||||
# Bad
|
||||
description: I can help you create Xiaohongshu images
|
||||
```
|
||||
|
||||
## Script Directory Template
|
||||
|
||||
Every SKILL.md with scripts MUST include:
|
||||
|
||||
```markdown
|
||||
## Script Directory
|
||||
|
||||
**Important**: All scripts are located in the `scripts/` subdirectory of this skill.
|
||||
|
||||
**Agent Execution Instructions**:
|
||||
1. Determine this SKILL.md file's directory path as `{baseDir}`
|
||||
2. Script path = `{baseDir}/scripts/<script-name>.ts`
|
||||
3. Resolve `${BUN_X}` runtime: if `bun` installed → `bun`; if `npx` available → `npx -y bun`; else suggest installing bun
|
||||
4. Replace all `{baseDir}` and `${BUN_X}` in this document with actual values
|
||||
|
||||
**Script Reference**:
|
||||
| Script | Purpose |
|
||||
|--------|---------|
|
||||
| `scripts/main.ts` | Main entry point |
|
||||
```
|
||||
|
||||
## Progressive Disclosure
|
||||
|
||||
For skills with extensive content:
|
||||
|
||||
```
|
||||
skills/baoyu-example/
|
||||
├── SKILL.md # Main instructions (<500 lines)
|
||||
├── references/
|
||||
│ ├── styles.md # Loaded as needed
|
||||
│ └── examples.md # Loaded as needed
|
||||
└── scripts/
|
||||
└── main.ts
|
||||
```
|
||||
|
||||
Link from SKILL.md (one level deep only):
|
||||
```markdown
|
||||
**Available styles**: See [references/styles.md](references/styles.md)
|
||||
```
|
||||
|
||||
## Extension Support (EXTEND.md)
|
||||
|
||||
Every SKILL.md MUST include EXTEND.md loading. Add as Step 1.1 (workflow skills) or "Preferences" section (utility skills):
|
||||
|
||||
```markdown
|
||||
**1.1 Load Preferences (EXTEND.md)**
|
||||
|
||||
Check EXTEND.md existence (priority order):
|
||||
|
||||
\`\`\`bash
|
||||
test -f .baoyu-skills/<skill-name>/EXTEND.md && echo "project"
|
||||
test -f "${XDG_CONFIG_HOME:-$HOME/.config}/baoyu-skills/<skill-name>/EXTEND.md" && echo "xdg"
|
||||
test -f "$HOME/.baoyu-skills/<skill-name>/EXTEND.md" && echo "user"
|
||||
\`\`\`
|
||||
|
||||
| Path | Location |
|
||||
|------|----------|
|
||||
| `.baoyu-skills/<skill-name>/EXTEND.md` | Project directory |
|
||||
| `$XDG_CONFIG_HOME/baoyu-skills/<skill-name>/EXTEND.md` | XDG config (~/.config) |
|
||||
| `$HOME/.baoyu-skills/<skill-name>/EXTEND.md` | User home (legacy) |
|
||||
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| Found | Read, parse, display summary |
|
||||
| Not found | Ask user via the runtime's user-input tool (see [user-input-tools.md](user-input-tools.md)) |
|
||||
```
|
||||
|
||||
End of SKILL.md should include:
|
||||
```markdown
|
||||
## Extension Support
|
||||
Custom configurations via EXTEND.md. See **Step 1.1** for paths and supported options.
|
||||
```
|
||||
|
||||
## User Input Tools Section (Required)
|
||||
|
||||
Every SKILL.md that prompts the user for choices MUST include exactly one `## User Input Tools` section near the top (right after the intro, before the main workflow). The rule must be **inlined** — do NOT link to `docs/user-input-tools.md` (skills are self-contained; see [CLAUDE.md → Skill Self-Containment](../CLAUDE.md)). The author-side canonical reference lives at [user-input-tools.md](user-input-tools.md); copy its body into each new SKILL.md.
|
||||
|
||||
Standard snippet (copy verbatim):
|
||||
|
||||
```markdown
|
||||
## User Input Tools
|
||||
|
||||
When this skill prompts the user, follow this tool-selection rule (priority order):
|
||||
|
||||
1. **Prefer built-in user-input tools** exposed by the current agent runtime — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
|
||||
2. **Fallback**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
|
||||
3. **Batching**: if the tool supports multiple questions per call, combine all applicable questions into a single call; if only single-question, ask them one at a time in priority order.
|
||||
|
||||
Concrete `AskUserQuestion` references below are examples — substitute the local equivalent in other runtimes.
|
||||
```
|
||||
|
||||
## Image Generation Tools Section (Required for image-gen skills)
|
||||
|
||||
Every SKILL.md that renders images — whether by calling an image-generation API directly or by delegating to another skill — MUST include exactly one `## Image Generation Tools` section near the top (after `## User Input Tools`, before the main workflow). The rule must be **inlined** — do NOT link to `docs/image-generation-tools.md` (skills are self-contained; see [CLAUDE.md → Skill Self-Containment](../CLAUDE.md)). The author-side canonical reference lives at [image-generation-tools.md](image-generation-tools.md); copy its body into each new SKILL.md.
|
||||
|
||||
Standard snippet (copy verbatim):
|
||||
|
||||
```markdown
|
||||
## Image Generation Tools
|
||||
|
||||
When this skill needs to render an image:
|
||||
|
||||
- **Use whatever image-generation tool or skill is available** in the current runtime — e.g., Codex `imagegen`, Cursor `GenerateImage`, Hermes `image_generate`, `baoyu-image-gen`, or any equivalent the user has installed.
|
||||
- **If multiple are available**, ask the user **once** at the start which to use (batch with any other initial questions).
|
||||
- **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
**Prompt file requirement (hard)**: write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The backend receives the prompt file (or its content); the file is the reproducibility record and lets you switch backends without regenerating prompts.
|
||||
|
||||
Concrete tool names (`imagegen`, `GenerateImage`, `image_generate`, `baoyu-image-gen`) above are examples — substitute the local equivalents under the same rule.
|
||||
```
|
||||
49
baoyu-skills/docs/image-generation-tools.md
Normal file
49
baoyu-skills/docs/image-generation-tools.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Image Generation Tools
|
||||
|
||||
Skills in this repo are loaded by multiple agent runtimes (Claude Code, Codex, Cursor, Hermes, other agents, bare CLI). Each runtime exposes a different image-generation capability — some have a runtime-native tool (Codex `imagegen`, Cursor `GenerateImage`, Hermes `image_generate`), others rely on an installed skill (`baoyu-image-gen`, or user-defined). This document defines the canonical **backend-selection rule** every skill that renders images follows so skills stay portable.
|
||||
|
||||
## The Rule
|
||||
|
||||
When a skill needs to render an image, resolve the backend in this order:
|
||||
|
||||
1. **Current-request override** — if the user names a specific backend in the current message, use it.
|
||||
2. **Saved preference** — if the skill's `EXTEND.md` sets `preferred_image_backend` to a backend available right now, use it.
|
||||
3. **Auto-select** (when the preference is `auto`, unset, or the pinned backend isn't available):
|
||||
- **Codex (`imagegen`)** — first, inspect your available-skills / tool inventory. If a skill named `imagegen` is listed, you are running inside Codex and MUST use it: invoke via the `Skill` tool with `skill: "imagegen"`, passing the saved prompt file's content (plus output path and aspect ratio per Codex `imagegen`'s own args). Codex `imagegen` is the official raster backend in that runtime and outranks any non-native skill (e.g., `baoyu-image-gen`) unless the user has explicitly pinned a different `preferred_image_backend`.
|
||||
- **Cursor (`GenerateImage`)** — if the runtime exposes a native `GenerateImage` tool, you are running inside Cursor and it outranks any non-native skill the same way Codex `imagegen` does. Two hard caveats: (a) it has no aspect-ratio parameter — state the target aspect ratio / dimensions explicitly in the prompt text passed as `description`; (b) it does not accept an output directory — it saves to a tool-managed location, so after generation copy/move the file to the skill's expected output path (e.g., `outputs/.../NN-xxx.png`). Reference images go in `reference_image_paths`.
|
||||
- **Other runtime-native tools** — if the runtime exposes a different native image tool (e.g., Hermes `image_generate`), use it the same way.
|
||||
- Otherwise, if exactly one non-native backend is installed (e.g., `baoyu-image-gen`), use it.
|
||||
- Otherwise (multiple non-native backends with no runtime-native tool), ask the user once — batch with any other initial questions.
|
||||
4. **If none are available**, tell the user and ask how to proceed.
|
||||
|
||||
**⛔ Never substitute SVG, HTML, canvas, or other code-based rendering for raster image generation.** Codex `imagegen`'s own description says it should be used "when the output should be a bitmap asset rather than repo-native code or vector." If you cannot resolve a raster backend via step 3, fall through to step 4 and ask the user — do **not** silently emit SVG, write inline `<svg>` markup, or produce HTML/CSS art as a substitute. This applies even if the article/section seems "diagram-like": the consumer skill calling this rule has already decided that a raster image is what it needs.
|
||||
|
||||
Setting `preferred_image_backend: ask` forces the step-3 prompt every run regardless of available backends.
|
||||
|
||||
## The Preference Field
|
||||
|
||||
Each image-consuming skill's `EXTEND.md` carries a single `preferred_image_backend` field:
|
||||
|
||||
| Value | Meaning |
|
||||
|---|---|
|
||||
| `auto` (default) | Apply the auto-select rule — runtime-native preferred, fall back to only installed backend, ask if multiple non-native. |
|
||||
| `ask` | Always confirm the backend on every run, even when a runtime-native tool exists. |
|
||||
| `<backend-id>` (e.g., `codex-imagegen`, `baoyu-image-gen`, `GenerateImage`, `image_generate`) | Pin this backend when available; fall back to `auto` if it isn't. |
|
||||
|
||||
The field is **absent-equals-auto**: older `EXTEND.md` files without this field behave exactly as if `preferred_image_backend: auto` were set. No schema version bump is needed to introduce it.
|
||||
|
||||
## Prompt File Requirement (hard)
|
||||
|
||||
Regardless of which backend is chosen, every skill that renders images MUST write each image's full, final prompt to a standalone file under `prompts/` (naming: `NN-{type}-[slug].md`) BEFORE invoking any backend. The backend receives the prompt file (or its content); the file is the reproducibility record and allows switching backends without regenerating prompts.
|
||||
|
||||
## How Skills Declare This
|
||||
|
||||
Each `SKILL.md` that renders images includes **exactly one** `## Image Generation Tools` section (near the top, after `## User Input Tools` and before the main workflow) that **inlines** this rule. Skills are self-contained and cannot link to `docs/` — each skill folder must ship the rule inside its own `SKILL.md`. See [CLAUDE.md → Skill Self-Containment](../CLAUDE.md).
|
||||
|
||||
Each skill's `references/config/preferences-schema.md` (and its `EXTEND.md` template in `first-time-setup.md`) lists `preferred_image_backend` alongside other preference fields. First-time setup does NOT ask the user about the backend — `auto` is set silently. Users who want to pin a specific backend edit `EXTEND.md` later, and each skill's `## Changing Preferences` section documents the common one-line edits.
|
||||
|
||||
Concrete tool names (`imagegen`, `GenerateImage`, `image_generate`, `baoyu-image-gen`) in this document and in SKILL.md are **examples** — agents in other runtimes apply the rule above and substitute the local equivalent. Skill-specific parameters for these backends are illustrative; runtimes without those knobs can omit them.
|
||||
|
||||
## Backend Skills Are Exempt
|
||||
|
||||
Skills that **are themselves** image-generation backends — currently `baoyu-image-gen`, `baoyu-image-gen` (deprecated), and `baoyu-danger-gemini-web` — do NOT include a `## Image Generation Tools` section. They render directly via their own provider integrations and have no need to "select a backend." The rule applies only to consumer skills that delegate rendering to whatever backend the runtime exposes.
|
||||
54
baoyu-skills/docs/image-generation.md
Normal file
54
baoyu-skills/docs/image-generation.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Image Generation Guidelines
|
||||
|
||||
Skills that require image generation MUST delegate to available image generation tools (runtime-native tools or installed skills).
|
||||
|
||||
**Backend selection convention**: see [image-generation-tools.md](image-generation-tools.md) for the runtime-neutral rule. Short version: use whatever backend is available; if multiple, ask the user once; if none, ask how to proceed. This document covers output conventions (naming, paths) that apply regardless of which backend is selected.
|
||||
|
||||
## Skill Selection
|
||||
|
||||
1. Follow the rule in [image-generation-tools.md](image-generation-tools.md): use whatever backend is available; ask only on ambiguity.
|
||||
2. Read the chosen backend's documentation for parameters and capabilities.
|
||||
3. If user requests a specific backend, honor it.
|
||||
|
||||
## Generation Flow Template
|
||||
|
||||
```markdown
|
||||
### Step N: Generate Images
|
||||
|
||||
**Backend Selection**:
|
||||
1. Detect available image-generation tools/skills (runtime-native + installed)
|
||||
2. If one available → use it. If multiple → ask user once. If none → ask how to proceed.
|
||||
3. Read the chosen backend's docs for parameters
|
||||
|
||||
**Generation Flow**:
|
||||
1. Write the full prompt to `prompts/NN-{type}-[slug].md` BEFORE invoking the backend
|
||||
2. Call backend with the prompt (or prompt file), output path, and parameters
|
||||
3. Generate sequentially by default (batch parallel only when backend supports it and user has multiple prompts)
|
||||
4. Output progress: "Generated X/N"
|
||||
5. On failure, auto-retry once before reporting error
|
||||
```
|
||||
|
||||
**Batch Parallel** (`baoyu-image-gen` only): concurrent workers with per-provider throttling via `batch.max_workers` in EXTEND.md.
|
||||
|
||||
## Output Path Convention
|
||||
|
||||
**Output Directory**: `<skill-suffix>/<topic-slug>/`
|
||||
- `<skill-suffix>`: e.g., `xhs-images`, `cover-image`, `slide-deck`, `comic`
|
||||
- `<topic-slug>`: 2-4 words, kebab-case from content topic
|
||||
- Conflict: append timestamp `<topic-slug>-YYYYMMDD-HHMMSS`
|
||||
|
||||
**Source Files**: Copy to output dir as `source-{slug}.{ext}`
|
||||
|
||||
## Image Naming Convention
|
||||
|
||||
**Format**: `NN-{type}-[slug].png`
|
||||
- `NN`: Two-digit sequence (01, 02, ...)
|
||||
- `{type}`: cover, content, page, slide, illustration, etc.
|
||||
- `[slug]`: 2-5 word kebab-case descriptor, unique within directory
|
||||
|
||||
Examples:
|
||||
```
|
||||
01-cover-ai-future.png
|
||||
02-content-key-benefits.png
|
||||
03-slide-architecture-overview.png
|
||||
```
|
||||
48
baoyu-skills/docs/publishing.md
Normal file
48
baoyu-skills/docs/publishing.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# ClawHub / OpenClaw Publishing
|
||||
|
||||
## OpenClaw Metadata
|
||||
|
||||
Skills include `metadata.openclaw` in YAML front matter:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
openclaw:
|
||||
homepage: https://github.com/JimLiu/baoyu-skills#<skill-name>
|
||||
requires: # only for skills with scripts
|
||||
anyBins:
|
||||
- bun
|
||||
- npx
|
||||
```
|
||||
|
||||
## Publishing Commands
|
||||
|
||||
```bash
|
||||
bash scripts/sync-clawhub.sh # sync all skills
|
||||
bash scripts/sync-clawhub.sh <skill> # sync one skill
|
||||
```
|
||||
|
||||
Release hooks are configured via `.releaserc.yml`. This repo does not stage a separate release directory: publish reads the skill directory directly and validates that local package references and CLI bin targets are self-contained.
|
||||
|
||||
Every skill release must keep the `version:` in that skill's `SKILL.md` aligned with the version being published. `publish-skill.mjs` and `sync-clawhub.mjs` both reject mismatches so a registry payload cannot ship with stale skill metadata.
|
||||
|
||||
Commits that touch `skills/<name>/**` must use Conventional Commit subjects, for example `fix(baoyu-post-to-wechat): handle WeChat editor focus`. CI runs `npm run verify:skill-release-commits` against the pushed or PR commit range so bare subjects like `Fix WeChat browser article publishing` cannot bypass per-skill release versioning silently.
|
||||
|
||||
## Shared Workspace Packages
|
||||
|
||||
`packages/` is the source of truth for shared runtime code. Most skills consume shared packages from npm with semver ranges. `baoyu-url-to-markdown` is the exception: it vendors the `baoyu-fetch` runtime into `skills/baoyu-url-to-markdown/scripts/lib/` so the published skill is self-contained and does not depend on the `baoyu-fetch` npm package.
|
||||
|
||||
Current packages:
|
||||
- `baoyu-chrome-cdp` (Chrome CDP utilities), consumed by 5 skills (`baoyu-danger-gemini-web`, `baoyu-danger-x-to-markdown`, `baoyu-post-to-wechat`, `baoyu-post-to-weibo`, `baoyu-post-to-x`)
|
||||
- `baoyu-md` (shared Markdown rendering and placeholder pipeline), consumed by 3 skills (`baoyu-markdown-to-html`, `baoyu-post-to-wechat`, `baoyu-post-to-weibo`)
|
||||
- `baoyu-fetch` (URL-to-Markdown CLI), vendored into 1 skill (`baoyu-url-to-markdown`)
|
||||
|
||||
**How it works**: npm packages are built from `packages/` and published to the public npm registry. Skills normally depend on those packages with `^<version>` specs. Release prep runs `node scripts/verify-shared-package-deps.mjs` so accidental `file:` dependencies cannot slip back in. For vendored skill runtimes, keep the copied code under the skill directory and run `node scripts/publish-skill.mjs --skill-dir <skill> --version <version> --dry-run` before publishing.
|
||||
|
||||
**Update workflow**:
|
||||
1. Edit package under `packages/`
|
||||
2. Run the package build, e.g. `bun run --cwd packages/baoyu-md build`
|
||||
3. Publish the changed npm package with `npm publish --access public`
|
||||
4. Update consuming skill `package.json` semver ranges if the package version changed
|
||||
5. Run `node scripts/verify-shared-package-deps.mjs`
|
||||
|
||||
**Git hook**: Run `node scripts/install-git-hooks.mjs` once to enable the `pre-push` hook. It blocks pushes when a skill uses a local `file:` dependency or a vendored workspace package.
|
||||
73
baoyu-skills/docs/testing.md
Normal file
73
baoyu-skills/docs/testing.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Testing Strategy
|
||||
|
||||
This repository has many scripts, but they do not share a single runtime or dependency graph. The lowest-risk testing strategy is to start from stable Node-based library code, then expand outward to CLI and skill-specific smoke tests.
|
||||
|
||||
## Current Baseline
|
||||
|
||||
- Root test runner: `node:test`
|
||||
- Entry point: `npm test`
|
||||
- Coverage command: `npm run test:coverage`
|
||||
- CI trigger: GitHub Actions on `push`, `pull_request`, and manual dispatch
|
||||
|
||||
This avoids introducing Jest/Vitest across a repo that already mixes plain Node scripts, Bun-based skill packages, npm-published shared packages, and browser automation.
|
||||
|
||||
## Rollout Plan
|
||||
|
||||
### Phase 1: Stable library coverage
|
||||
|
||||
Focus on pure functions under `scripts/lib/` first.
|
||||
|
||||
- `scripts/lib/release-files.mjs`
|
||||
- `scripts/verify-shared-package-deps.mjs`
|
||||
|
||||
Goals:
|
||||
|
||||
- Validate file filtering and release packaging rules
|
||||
- Catch regressions that reintroduce local `file:` dependencies or vendored workspace packages
|
||||
- Keep tests deterministic and free of network, Bun, or browser requirements
|
||||
|
||||
### Phase 2: Root CLI integration tests
|
||||
|
||||
Add temp-directory integration tests for root CLIs that already support dry-run or local-only flows.
|
||||
|
||||
- `scripts/verify-shared-package-deps.mjs`
|
||||
- `scripts/publish-skill.mjs --dry-run`
|
||||
- `scripts/sync-clawhub.mjs` argument handling and local skill discovery
|
||||
|
||||
Goals:
|
||||
|
||||
- Assert exit codes and stdout for common flows
|
||||
- Cover CLI argument parsing without hitting external services
|
||||
|
||||
### Phase 3: Skill script smoke tests
|
||||
|
||||
Add opt-in smoke tests for selected `skills/*/scripts/` packages, starting with those that:
|
||||
|
||||
- accept local input files
|
||||
- have deterministic output
|
||||
- do not require authenticated browser sessions
|
||||
|
||||
Examples:
|
||||
|
||||
- markdown transforms
|
||||
- file conversion helpers
|
||||
- local content analyzers
|
||||
|
||||
Keep browser automation, login flows, and live API publishing scripts outside the default CI path unless they are explicitly mocked.
|
||||
|
||||
### Phase 4: Coverage gates
|
||||
|
||||
After the stable Node path has enough breadth, add coverage thresholds in CI for the tested root modules.
|
||||
|
||||
Recommended order:
|
||||
|
||||
1. Start with reporting only
|
||||
2. Add line/function thresholds for `scripts/lib/**`
|
||||
3. Expand include patterns once skill-level smoke tests are reliable
|
||||
|
||||
## Conventions For New Tests
|
||||
|
||||
- Prefer temp directories over committed fixtures unless the fixture is reused heavily
|
||||
- Test exported functions before testing CLI wrappers
|
||||
- Avoid network, browser, and credential dependencies in default CI
|
||||
- Keep tests isolated so they can run with plain `node --test`
|
||||
17
baoyu-skills/docs/user-input-tools.md
Normal file
17
baoyu-skills/docs/user-input-tools.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# User Input Tools
|
||||
|
||||
Skills in this repo are loaded by multiple agent runtimes (Claude Code, other agents, bare CLI). Each runtime exposes a different API for asking the user questions. This document defines the canonical **tool-selection rule** every skill follows so skills stay portable.
|
||||
|
||||
## Tool Selection (priority order)
|
||||
|
||||
1. **Prefer built-in user-input tools** if the current agent runtime exposes one — e.g., `AskUserQuestion`, `request_user_input`, `clarify`, `ask_user`, or any equivalent.
|
||||
2. **Fallback to plain text**: if no such tool exists, emit a numbered plain-text message and ask the user to reply with the chosen number/answer for each question.
|
||||
3. **Batching rule**:
|
||||
- If the tool supports **multiple questions per call** (e.g., `AskUserQuestion`): **Combine all applicable questions into a single call. Do NOT split into separate calls.**
|
||||
- If the tool supports **only one question per call** (e.g., single-prompt `clarify`): ask **one question per call, in priority order**.
|
||||
|
||||
## How Skills Declare This
|
||||
|
||||
Each `SKILL.md` that uses interactive user input includes **exactly one** `## User Input Tools` section (typically near the top, right after the intro) that **inlines** this rule. Do NOT link here from a SKILL.md — skills are self-contained (see [CLAUDE.md → Skill Self-Containment](../CLAUDE.md)). This document is the author-side canonical source; copy its body into each SKILL.md. The rule then governs every user-input interaction in that skill and its `references/` files.
|
||||
|
||||
Specific mentions of a concrete tool (e.g., `AskUserQuestion`) elsewhere in a skill are **concrete examples** — agents in other runtimes apply the rule above and substitute the local equivalent. Tool-specific parameters (e.g., `header:`, `multiSelect:`) are illustrative; runtimes without those knobs can omit them.
|
||||
Reference in New Issue
Block a user