Skip to content
Productivity
Skill

/sn-ppt-creative

Creative-mode PPT pipeline. One full-page 16:9 PNG per slide. LLM / VLM calls go through sn-ppt-standard/lib/model_client.py (shared thin client). Text-to-image (the actual png rendering) goes through sn-image-base/scripts/sn_agent_runner.py. Falls back to web image search when

From plugin
sensenova-skills
4.9k76 skills9 agents
Install
$ npx -y skills add OpenSenseNova/SenseNova-Skills --skill sn-ppt-creative --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/sn-ppt-creative

Context preview

The summary Claude sees to decide when to auto-load this skill.

Creative-mode PPT pipeline. One full-page 16:9 PNG per slide. LLM / VLM calls go through sn-ppt-standard/lib/model_client.py (shared thin client). Text-to-image (the actual png rendering) goes through sn-image-base/scripts/sn_agent_runner.py. Falls back to web image search when

SKILL.md

sn-ppt-creative.SKILL.md
name: sn-ppt-creative
description: |
  Creative-mode PPT pipeline. One full-page 16:9 PNG per slide.
  LLM / VLM calls go through sn-ppt-standard/lib/model_client.py (shared thin
  client). Text-to-image (the actual png rendering) goes through
  sn-image-base/scripts/sn_agent_runner.py. Falls back to web image search
  when T2I generation fails. Expects task_pack.json + info_pack.json already
  written by sn-ppt-entry.
metadata:
  project: SenseNova-Skills
  tier: 1
  category: scene
  user_visible: false
triggers:
  - "sn-ppt-creative"

sn-ppt-creative

> **⚠️ This skill must be invoked through `/skill sn-ppt-entry`.** Never start here directly — the entry skill collects parameters and writes `task_pack.json` + `info_pack.json` that this skill requires. If you arrived here without those files, stop and tell the user to enter via `/skill sn-ppt-entry` or "生成 PPT".

Call-routing policy

| Kind | Backend | |---|---| | LLM (text) | `$PPT_STANDARD_DIR/lib/model_client.py` → `llm(sys, user)` | | VLM (image understanding) | `$PPT_STANDARD_DIR/lib/model_client.py` → `vlm(sys, user, images)` | | T2I (image generation) | `$SN_IMAGE_BASE/scripts/sn_agent_runner.py sn-image-generate` |

Never mix — LLM / VLM through sn-image-base, or T2I through model_client — both violate policy.

Visual asset priority

  • Creative mode renders each slide as a generated full-page PNG, so **image generation is the first-priority visual path**.
  • If image generation fails for a page, use web search (`sn-search-image`) as a fallback to find a real image that fits the page's topic. Each search result includes the image URL, source page, title, and domain for traceability.
  • Do not create placeholders. If generation and search both fail, record the page failure and continue; never write fake PNGs, grey boxes, broken-image icons, or "image pending" text.
  • Do not mention the search provider name in prompts, visible slide text, progress, or summaries.

Preconditions

  • `<deck_dir>/task_pack.json` exists and `ppt_mode == "creative"`
  • `<deck_dir>/info_pack.json` exists
  • `<deck_dir>/pages/` exists
  • `$SN_IMAGE_BASE` env var (OpenClaw-injected) points at the sn-image-base skill root
  • `$PPT_STANDARD_DIR` env var points at the sn-ppt-standard skill root (so we can import `model_client`)

Any missing → stop and tell user to enter via `/skill sn-ppt-entry`.

Generation progress WebUI

`sn-ppt-entry` starts the generation progress WebUI after `task_pack.json` / `info_pack.json` are written. During creative-mode generation, publish progress with the shared writer from `sn-ppt-standard`:

P="python3 $PPT_STANDARD_DIR/scripts/progress_event.py"
$P --deck-dir <deck_dir> --stage creative-style --status running
$P --deck-dir <deck_dir> --stage creative-style --status ok --artifact style_spec.md
$P --deck-dir <deck_dir> --stage creative-outline --status running
$P --deck-dir <deck_dir> --stage creative-outline --status ok --artifact outline.json
$P --deck-dir <deck_dir> --stage creative-prompt --page N --status running
$P --deck-dir <deck_dir> --stage creative-prompt --page N --status ok
$P --deck-dir <deck_dir> --stage creative-render --page N --status running
$P --deck-dir <deck_dir> --stage creative-render --page N --status ok
$P --deck-dir <deck_dir> --stage export --status running
$P --deck-dir <deck_dir> --stage export --status ok

On failure, write the same stage with `--status failed --error "<short reason>"` before moving on or aborting. On native Windows, use `python` if `python3` is unavailable.

Resume

python3 $SKILL_DIR/scripts/resume_scan.py --deck-dir <deck_dir>
# => {"style_spec_done": bool, "outline_done": bool, "pptx_done": bool,
#     "pages": [{"page_no": 1, "action": "skip|render_only|full"}, ...]}

Dispatch:

| Manifest | Do | |---|---| | `style_spec_done == false` | Run Stage 2 | | `outline_done == false` | Run Stage 3 | | per-page `action == "full"` | Run Stage 4.1 + 4.2 | | per-page `action == "render_only"` | Run Stage 4.2 only (prompt.txt already on disk) | | per-page `action == "skip"` | Skip | | `pptx_done == false` (all pages done or failed) | Run Stage 5 |

Stage 2 — style_spec.md (LLM or VLM via model_client)

One independent exec tool_call. Two branches based on reference images.

**Branch A (no ref images, or all missing on disk)** — use `model_client.llm`:

python3 -c "
import sys, pathlib, json
sys.path.insert(0, '$PPT_STANDARD_DIR/lib')
from model_client import llm

deck = pathlib.Path('<deck_dir>')
tp = json.loads((deck / 'task_pack.json').read_text())
ip = json.loads((deck / 'info_pack.json').read_text())

sys_prompt = open('$SKILL_DIR/prompts/style_from_query.md').read()
user_prompt = json.dumps({
    'params': tp['params'],
    'query': ip.get('user_query'),
    'digest': ip.get('document_digest'),
}, ensure_ascii=False)

md = llm(sys_prompt, user_prompt)
(deck / 'style_spec.md').write_text(md, encoding='utf-8')
print('style_spec.md ok')
"

**Branch B (≥1 reference image on disk)** — use `model_client.vlm`:

python3 -c "
import sys, pathlib, json
sys.path.insert(0, '$PPT_STANDARD_DIR/lib')
from model_client import vlm

deck = pathlib.Path('<deck_dir>')
ip = json.loads((deck / 'info_pack.json').read_text())
tp = json.loads((deck / 'task_pack.json').read_text())

refs = [p for p in (ip.get('user_assets') or {}).get('reference_images', []) if pathlib.Path(p).exists()]

sys_prompt = open('$SKILL_DIR/prompts/style_from_image.md').read()
user_prompt = f'PPT 主题/参数: {json.dumps(tp[\"params\"], ensure_ascii=False)}\nuser_query: {ip.get(\"user_query\") or \"\"}'

md = vlm(sys_prompt, user_prompt, images=refs)
(deck / 'style_spec.md').write_text(md, encoding='utf-8')
print(f'style_spec.md ok (from {len(refs)} ref images)')
"

If `user_assets.reference_images` is non-empty but **all** paths missing on disk: fall through to Branch A and prepend a line `reference_images_missing: <original paths>` at the top of style_spec.md.

St

Read more
Ships withsensenova-skills

The SenseNova model family plugs directly into agent runtimes such as OpenClaw and hermes-agent, with the skills in this repository extending the models with concrete, end-to-end office capabilities.

Get the whole plugin

Other skills on sensenova-skills.