OpenSenseNova/SenseNova-Skills/skills/sn-ppt-entry/SKILL.md
sn-ppt-entry
Entry point for PPT generation. Asks the user to choose a mode (fast, standard, or creative), then collects role / audience / scene / page_count as needed. For standard mode, also asks how images should be sourced (AI generation, web search, or none), whether charts should use AI-generated infographics or ECharts, and whether the final deliverable should be PPTX or PDF. Parses uploaded pdf/docx/md/txt files, produces task_pack.json + info_pack.json in a new deck_dir, then dispatches to sn-ppt-cr
- Source repository stars
- 4,855
- Declared platforms
- 0
- Static risk flags
- 4
- Last source update
- 2026-07-28
- Source checked
- 2026-08-04
Decision brief
What it does—and where it fits
Entry point for PPT generation. Asks the user to choose a mode (fast, standard, or creative), then collects role / audience / scene / page_count as needed.
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/OpenSenseNova/SenseNova-Skills --skill "skills/sn-ppt-entry"Inspect the Agent Skill "sn-ppt-entry" from https://github.com/OpenSenseNova/SenseNova-Skills/blob/24abfbb1eb5168027be74ecc18f2e5ac55890f5d/skills/sn-ppt-entry/SKILL.md at commit 24abfbb1eb5168027be74ecc18f2e5ac55890f5d. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Hard preconditions
Run sn-ppt-doctor hard checks (SNAPIKEY or capability-specific API keys / node / sn-image-base) at the start of this skill. If any fails, stop and tell the user to run /skill sn-ppt-doctor.
Run sn-ppt-doctor hard checks (SNAPIKEY or capability-specific API keys / node / sn-image-base) at the start of this skill. If any fails, stop and tell the user to run /skill sn-ppt-doctor.If the user is not asking to generate a new deck and only wants to open an existing/generated deck in the WebUI, do not run these generation preconditions. Dispatch directly to /skill sn-ppt-workbench. - 02
Flow
1. Extract parameters from the user's message: - role (speaker identity) - audience - scene (where the deck will be used) - pagecount - language — detect from the user's query: zh-Hans (Simplified Chinese), zh-Hant (Traditional Chinese), or en (English). Do NOT ask the user; jus…
Extract parameters from the user's message:role (speaker identity)audience - 03
askuser boundary conditions
User answers multiple params in one turn - extract all with a single sn-text-optimize call; skip asked-already params.
User answers multiple params in one turn - extract all with a single sn-text-optimize call; skip asked-already params.User's answer isn't in the 2-3 options - record verbatim; don't force into the enumeration.Session interrupted before taskpack.json written - discard temp params; next entry starts over. - 04
Invoking the LLM for documentdigest
parseuserdocs.py --output /rawdocuments.json already creates the file. Then call the LLM with a user prompt that gives only counts + indices of tables/images (not row contents) so the LLM can't accidentally paraphrase numbers:
parseuserdocs.py --output /rawdocuments.json already creates the file. Then call the LLM with a user prompt that gives only counts + indices of tables/images (not row contents) so the LLM can't accidentally paraphrase n…bash python3 -c " import sys, json, pathlib sys.path.insert(0, '$PPTSTANDARDDIR/lib') from modelclient import llmraw = json.loads(pathlib.Path('/rawdocuments.json').readtext()) - 05
Build the digest-safe view: strip tables[] and image paths, keep text + indices
docsview = [] for d in raw.get('documents', []): docsview.append({ 'docindex': d['docindex'], 'type': d['type'], 'text': d.get('text',''), 'tablescount': len(d.get('tables') or []), 'imagescount': len(d.get('inheritedimages') or []), })
docsview = [] for d in raw.get('documents', []): docsview.append({ 'docindex': d['docindex'], 'type': d['type'], 'text': d.get('text',''), 'tablescount': len(d.get('tables') or []), 'imagescount': len(d.get('inheritedim…userprompt = json.dumps({ 'userquery': '', 'documents': docsview, }, ensureascii=False)sysprompt = open('$SKILLDIR/prompts/documentdigest.md').read()
Permission review
Static risk signals and limitations
Network access
The documentation includes network, browsing, or remote request actions.
"Web search — pull real photos from the web (requires Serper API key)"Runs scripts
The documentation asks the agent to run terminal commands or scripts.
python3 $PPT_STANDARD_DIR/scripts/progress_event.py --deck-dir <deck_dir> --stage entry --status ok --artifact "task_pack.json / info_pack.json" --label "task_pack.json / info_pack.json 已写入"Runs scripts
The documentation asks the agent to run terminal commands or scripts.
python3 $PPT_STANDARD_DIR/scripts/launch_workbench.py --deck-dir <deck_dir> --source-session-id "${HERMES_SESSION_KEY:-}" --agent-managed 1Reads files
The documentation asks the agent to read local files, directories, or repositories.
raw = json.loads(pathlib.Path('<deck_dir>/raw_documents.json').read_text())Writes files
The documentation asks the agent to create, modify, or delete local files.
pathlib.Path('<deck_dir>/digest_tmp.json').write_text(json.dumps(digest, ensure_ascii=False))Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 86/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 4,855 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- OpenSenseNova/SenseNova-Skills
- Skill path
- skills/sn-ppt-entry/SKILL.md
- Commit
- 24abfbb1eb5168027be74ecc18f2e5ac55890f5d
- License
- MIT
- Collected
- 2026-08-04
- Default branch
- main
View the original SKILL.md
sn-ppt-entry
Hard preconditions
Run sn-ppt-doctor hard checks (SN_API_KEY or capability-specific API keys / node / sn-image-base) at the start of this skill. If any fails, stop and tell the user to run /skill sn-ppt-doctor.
If the user is not asking to generate a new deck and only wants to open an existing/generated deck in the WebUI, do not run these generation preconditions. Dispatch directly to /skill sn-ppt-workbench.
Flow
-
Extract parameters from the user's message:
role(speaker identity)audiencescene(where the deck will be used)page_countlanguage— detect from the user's query:zh-Hans(Simplified Chinese),zh-Hant(Traditional Chinese), oren(English). Do NOT ask the user; just infer and record it. If unsure, usezh-Hans.
-
If the user asks only to open/preview/edit an existing generated deck in the WebUI, dispatch to
/skill sn-ppt-workbench deck_dir=<abs-or-user-provided-path>and stop. Do not ask mode questions. -
If
task_pack.json+info_pack.jsonalready exist in a deck_dir the user refers to and the user asks to continue generation, read them and jump to step 10 (see "Resume" below). -
Always ask the user which mode to use first. Call
ask_user:Question — Mode: "Which generation mode should I use?"
- "Fast mode — build the slides now so you can review and iterate"
- "Standard mode — plan the style and content thoroughly first, then build"
- "Creative mode — full-page AI-generated images per slide"
Store as
ppt_modeintask_pack. -
Only ask standard-mode option questions for standard mode. Fast mode and creative mode have fixed defaults — asking extra questions defeats the purpose of "fast."
If
ppt_mode == "standard", ask three more questions:Question — Normal images (decorative / conceptual): "Should I include images, and how should they be sourced?"
- "AI generation — create images from scratch"
- "Web search — pull real photos from the web (requires Serper API key)"
- "No images — use text, charts, and CSS visuals only"
If the user picks web search and
SERPER_API_KEYis not set, tell them how to get a free key at https://serper.dev. Store asimage_sourceintask_pack.params.Question — Infographics (charts, flowcharts, diagrams): "For charts and diagrams, should I use AI-generated infographics or ECharts?"
- "AI-generated infographics — U1 creates custom diagram images"
- "ECharts — rendered as interactive charts in the HTML"
Store as
infographic_sourceintask_pack.params("ai-gen"or"echarts").Question — Final output: "Which final file format should I generate?"
- "PPTX — editable PowerPoint deck"
- "PDF — fixed-layout presentation file"
Store as
output_formatintask_pack.params("pptx"or"pdf"). Default to"pptx"only if resuming an oldertask_pack.jsonthat lacks this field; do not silently default during a new standard-mode run.If
ppt_mode == "fast": skip image/output questions. Default toimage_source = "ai-gen",infographic_source = "echarts", andoutput_format = "pptx". Also skip role/audience/scene/page_count questions — infer reasonable defaults from the user's query and move directly to building slides. Fast mode means fewer questions, faster start. If the user didn't explicitly state these, make your best guess and proceed.If
ppt_mode == "creative": skip image/output questions. Default toimage_source = "ai-gen"(full-page T2I rendering) andoutput_format = "pptx". Infographics are not applicable. Skip role/audience/scene/page_count unless explicitly stated. -
Collect
role -> audience -> scene -> page_count— for standard mode only. Use the wording inreferences/ask_user_templates.md. 2-3 options per question; do not write "其他". For fast/creative modes, infer from the query and move on. -
Create deck_dir — location is FIXED, do not guess:
- Parent: always
$(pwd)/ppt_decks/. In OpenClaw, cwd at skill-invocation time is the agent's workspace directory (e.g.~/.openclaw/workspace/). Do NOT use/tmp, the home directory, the repo root, or$SKILL_DIRas the parent. Do NOT honor$PPT_DECK_ROOTeither — it's been removed to avoid drift. - Parent directory must be created if missing:
mkdir -p $(pwd)/ppt_decks. - Deck name:
<topic_concise>_<YYYYMMDD_HHMMSS>. - Full deck_dir path:
$(pwd)/ppt_decks/<topic_concise>_<YYYYMMDD_HHMMSS>/. - Immediately resolve to absolute (
realpath/Path.resolve()) before writing it intotask_pack.json— downstream must see an absolute path. - Create subdirs:
pages/always;images/ifppt_mode in {standard, fast}. - If
$(pwd)/ppt_decks/cannot be created (permission denied) → abort, tell the user to check workspace permissions.
- Parent: always
-
If user attached reference_docs (pdf/docx/md/txt):
- Run
$SKILL_DIR/scripts/parse_user_docs.py --files <paths...> --output <deck_dir>/raw_documents.json. The--outputflag tells the script to write the JSON itself (recommended — works reliably even on agents that don't handle shell redirection well). The script prints a single-line JSON status{"status":"ok","output":"...","documents":N,"errors":M}to stdout when--outputis used. - Call the LLM with
$SKILL_DIR/prompts/document_digest.mdas system prompt + (user_query + concatenated document text) as user prompt. See "Invoking the LLM" below. - On success: write
document_digestJSON intoinfo_pack.document_digest. - On failure: degrade — set
info_pack.document_digest = null, continue (do NOT abort entry).
- Run
-
Write
task_pack.json+info_pack.jsonto deck_dir (see "Schemas" below). All path-bearing fields absolute. -
Start the generation progress WebUI (best-effort, non-blocking):
- First write the initial progress event:
python3 $PPT_STANDARD_DIR/scripts/progress_event.py --deck-dir <deck_dir> --stage entry --status ok --artifact "task_pack.json / info_pack.json" --label "task_pack.json / info_pack.json 已写入" - Then start/reuse the WebUI and immediately echo the returned
generation_url:python3 $PPT_STANDARD_DIR/scripts/launch_workbench.py --deck-dir <deck_dir> --source-session-id "${HERMES_SESSION_KEY:-}" --agent-managed 1 - On native Windows Hermes installs where
python3is unavailable, usepython. - If the helper returns
{"status":"ok",...}, tell the user生成进度工作台已启动:<generation_url>before continuing. The returnedgeneration_urlis the progress page at/progress; the editor is a separate/editorURL exposed aseditor_url. - If it returns
{"status":"skipped","reason":"nodejs_missing",...}, ask the user whether to install NodeJS/dependencies. If they decline, say generation will continue without the WebUI and proceed. If they agree, first use any approved dependency-install skill/tool exposed by the active environment; otherwise use platform install means only after explicit dangerous-operation confirmation. - If it returns any other skipped/failed status, echo a short reason and continue generation without the WebUI.
- Default bind behavior is handled by the helper: explicit host/env wins; otherwise Docker/WSL binds
0.0.0.0, native hosts bind localhost unless the user requested another IP/host.
- Caption every image once with VLM (mandatory, idempotent — runs after
info_pack.jsonis written so both pools are visible):
PPT_STANDARD_DIR="$(dirname "$SKILL_DIR")/sn-ppt-standard" python3 $SKILL_DIR/scripts/caption_images.py --deck-dir <deck_dir>
⚠️ Set PPT_STANDARD_DIR — caption_images.py imports from sn-ppt-standard/lib/model_client.py and resolves it via $PPT_STANDARD_DIR. Without this env var, the script fails with FileNotFoundError: ppt-standard/lib/model_client.py not found. On Windows, use python instead of python3 and set the env var inline:
set PPT_STANDARD_DIR=C:\Users\...\Repository\ppt-editor\skills\sn-ppt-standard && python %SKILL_DIR%\scripts\caption_images.py --deck-dir <deck_dir>
Safe to skip when there are no attachment images (user_assets.reference_images empty and no doc-embedded images) — the script is a no-op with no images, and the failure is harmless.
This script is the single source of truth for image-content descriptions:
- Pool A — doc-embedded images (
raw_documents.jsondocuments[*].inherited_images[*]): caption written into the same JSON asvlm_caption. - Pool B — standalone uploads (
info_pack.user_assets.reference_images): caption written into a sister fieldinfo_pack.user_assets.reference_image_captions: {abs_path: caption}. - Already-captioned images are skipped silently, so re-running is cheap and safe. Only newly added images incur a VLM call.
- Failures don't abort: the script reports them in the JSON status; downstream stages fall back to filename / alt / digest hint when a caption is missing.
Downstream (sn-ppt-standard
cmd_page_html) reads these cached captions and never re-captions — that's the "single source of truth" rule. If you change image files in a deck, delete theirvlm_caption(orreference_image_captions[path]) entry and re-run this script to refresh.
- Dispatch to
sn-ppt-creativeorsn-ppt-standardbased ontask_pack.ppt_mode.
ask_user boundary conditions
- User answers multiple params in one turn -> extract all with a single
sn-text-optimizecall; skip asked-already params. - User's answer isn't in the 2-3 options -> record verbatim; don't force into the enumeration.
- Session interrupted before task_pack.json written -> discard temp params; next entry starts over.
- task_pack.json already exists -> skip param collection, go straight to dispatch.
Invoking the LLM for document_digest
parse_user_docs.py --output <deck_dir>/raw_documents.json already creates the file. Then call the LLM with a user prompt that gives only counts + indices of tables/images (not row contents) so the LLM can't accidentally paraphrase numbers:
python3 -c "
import sys, json, pathlib
sys.path.insert(0, '$PPT_STANDARD_DIR/lib')
from model_client import llm
raw = json.loads(pathlib.Path('<deck_dir>/raw_documents.json').read_text())
# Build the digest-safe view: strip tables[] and image paths, keep text + indices
docs_view = []
for d in raw.get('documents', []):
docs_view.append({
'doc_index': d['doc_index'],
'type': d['type'],
'text': d.get('text',''),
'tables_count': len(d.get('tables') or []),
'images_count': len(d.get('inherited_images') or []),
})
user_prompt = json.dumps({
'user_query': '<the user's original query>',
'documents': docs_view,
}, ensure_ascii=False)
sys_prompt = open('$SKILL_DIR/prompts/document_digest.md').read()
out = llm(sys_prompt, user_prompt)
# Parse JSON; if it fails, degrade digest to null (not abort entry)
try:
digest = json.loads(out)
except Exception:
digest = None
pathlib.Path('<deck_dir>/digest_tmp.json').write_text(json.dumps(digest, ensure_ascii=False))
"
The digest JSON then merges into info_pack.document_digest. Downstream stages (outline, page_html) read both info_pack.document_digest (structured summary + inherited_tables/images index lists) AND raw_documents.json (actual table rows + image paths).
Substitute $PPT_STANDARD_DIR with the sn-ppt-standard skill install dir.
Schemas
task_pack.json:
{
"deck_id": "AI产品发布会_20260318_154500",
"deck_dir": "/abs/path/ppt_decks/AI产品发布会_20260318_154500",
"ppt_mode": "standard",
"params": {
"role": "...",
"audience": "...",
"scene": "...",
"page_count": 10,
"language": "zh",
"image_source": "ai-gen",
"infographic_source": "ai-gen",
"output_format": "pptx"
},
"created_at": "2026-04-21T15:45:00+08:00",
"skill_version": "0.1.0"
}
info_pack.json:
{
"user_query": "...",
"user_assets": {
"reference_images": ["/abs/..."],
"reference_docs": ["/abs/..."],
"reference_docs_failed": []
},
"document_digest": {
"topic_summary": "...",
"key_sections": [],
"key_points": [],
"data_highlights": [],
"inherited_tables": [{"doc_index": 0, "table_index": 2, "title_hint": "..."}],
"inherited_images": [{"doc_index": 0, "image_index": 0, "caption_hint": "..."}]
},
"raw_document_excerpts": {
"enabled": true,
"path": "/abs/.../raw_documents.json"
}
}
🚫 Hard rules
- Do NOT use python-pptx, pptxgenjs, or any alternative PPTX builder. PPTX is produced by the downstream mode skills (sn-ppt-standard / sn-ppt-creative) through their designated scripts. Never
pip install python-pptxor write Node scripts that importpptxgenjs. - Wait for
ask_userresponses. When you ask the user a question, do NOT proceed until they reply. Never continue with assumed or default values. - Validate paths before writing. Always
lsorpwdto verify the current working directory before creating files. The only valid output location is$(pwd)/ppt_decks/<deck_dir>/. Never write to/workspace/,/tmp/,~/, or any hallucinated path. If a path doesn't start with the verified$(pwd), it's wrong.
Failure handling
- Missing required env var -> stop, tell user
/skill sn-ppt-doctor. $(pwd)/ppt_decks/not creatable / not writable -> stop, tell user to check workspace permissions.- Per-file doc parse failure -> record in
reference_docs_failed, continue. document_digestLLM failure -> set to null, continue.
Progress echo — MANDATORY
Emit a short chat reply at each boundary. Silence between ask_user rounds and mode dispatch is a bug.
| When | Example |
|---|---|
| Right after entering sn-ppt-entry | 已进入 sn-ppt-entry,开始收集参数... |
| Missing a param | 缺少参数:<role>,马上问你 (then ask_user) |
| All params collected | 参数齐备:mode=standard, image_source=ai-gen, output_format=pptx, role=...。开始创建 deck_dir... |
| Before doc parse | 检测到 2 个附件,开始解析... |
| After doc parse | 解析完成:sample.pdf (12 页) / sample.docx (45 段) |
| Before digest | [LLM] 正在汇总文档要点... |
| After digest | 文档摘要已入 info_pack.json |
| task_pack / info_pack written | task_pack.json / info_pack.json 已写入 <deck_dir> |
| After WebUI launch | 生成进度工作台已启动:<url> or NodeJS 不可用;将继续生成但不启动 WebUI |
| Dispatching | 分发到 sn-ppt-creative(deck_dir=...) |
Output and handoff
Final message includes a short summary:
准备就绪:
- 模式: <creative | standard>
- 页数: <n>
- deck_dir: <abs path>
即将进入<创意 | 标准>模式...
Then dispatch:
- ppt_mode=creative -> invoke
/skill sn-ppt-creative deck_dir=<abs> - ppt_mode=standard -> invoke
/skill sn-ppt-standard deck_dir=<abs>
Does NOT
- Do not generate any style / outline / page content (that's the mode skill's job).
- Do not run any image generation.