{"id":"143eae0d-5f39-4731-a1ad-ecec926c0657","shortId":"VDcuxP","kind":"skill","title":"sn-ppt-entry","tagline":"Entry point for PPT generation. Collects role / audience / scene / page_count /\nppt_mode (creative or standard), parses uploaded pdf/docx/md/txt files,\nproduces task_pack.json + info_pack.json in a new deck_dir, then dispatches\nto sn-ppt-creative or sn-ppt-standard. Use when the ","description":"# sn-ppt-entry\n\n## Hard preconditions\n\nRun `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`.\n\n## Flow\n\n1. Extract parameters from the user's message:\n   - `role` (speaker identity)\n   - `audience`\n   - `scene` (where the deck will be used)\n   - `page_count`\n   - `ppt_mode` in {creative, standard}\n2. If `task_pack.json` + `info_pack.json` already exist in a deck_dir the user refers to, read them and jump to step 7 (see \"Resume\" below).\n3. For each parameter missing or ambiguous, call `ask_user` one at a time, in the order:\n   `ppt_mode -> role -> audience -> scene -> page_count`.\n   Use the wording in `references/ask_user_templates.md`. 2-3 options per question; do not write \"其他\".\n4. Create deck_dir — **location is FIXED, do not guess**:\n   - 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_DIR` as the parent. Do NOT honor `$PPT_DECK_ROOT` either — it's been removed to avoid drift.\n   - Parent directory must be created if missing: `mkdir -p $(pwd)/ppt_decks`.\n   - Deck name: `<topic_concise>_<YYYYMMDD_HHMMSS>`.\n   - Full deck_dir path: `$(pwd)/ppt_decks/<topic_concise>_<YYYYMMDD_HHMMSS>/`.\n   - Immediately resolve to absolute (`realpath` / `Path.resolve()`) before writing it into `task_pack.json` — downstream must see an absolute path.\n   - Create subdirs: `pages/` always; `images/` only if `ppt_mode=standard`.\n   - If `$(pwd)/ppt_decks/` cannot be created (permission denied) → **abort**, tell the user to check workspace permissions.\n5. If user attached reference_docs (pdf/docx/md/txt):\n   - Run `$SKILL_DIR/scripts/parse_user_docs.py --files <paths...> --output <deck_dir>/raw_documents.json`. The `--output` flag 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 `--output` is used.\n   - Call the LLM with `$SKILL_DIR/prompts/document_digest.md` as system prompt + (user_query + concatenated document text) as user prompt. See \"Invoking the LLM\" below.\n   - On success: write `document_digest` JSON into `info_pack.document_digest`.\n   - On failure: degrade — set `info_pack.document_digest = null`, continue (do NOT abort entry).\n6. Write `task_pack.json` + `info_pack.json` to deck_dir (see \"Schemas\" below). All path-bearing fields **absolute**.\n7. **Caption every image once with VLM** (mandatory, idempotent — runs after `info_pack.json` is written so both pools are visible):\n   ```bash\n   python3 $SKILL_DIR/scripts/caption_images.py --deck-dir <deck_dir>\n   ```\n   This script is the **single source of truth** for image-content descriptions:\n   - Pool A — doc-embedded images (`raw_documents.json` `documents[*].inherited_images[*]`): caption written into the same JSON as `vlm_caption`.\n   - Pool B — standalone uploads (`info_pack.user_assets.reference_images`): caption written into a sister field `info_pack.user_assets.reference_image_captions: {abs_path: caption}`.\n   - Already-captioned images are **skipped silently**, so re-running is cheap and safe. Only newly added images incur a VLM call.\n   - 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.\n   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 their `vlm_caption` (or `reference_image_captions[path]`) entry and re-run this script to refresh.\n8. Dispatch to `sn-ppt-creative` or `sn-ppt-standard` based on `task_pack.ppt_mode`.\n\n## ask_user boundary conditions\n\n- User answers multiple params in one turn -> extract all with a single `sn-text-optimize` call; skip asked-already params.\n- User's answer isn't in the 2-3 options -> record verbatim; don't force into the enumeration.\n- Session interrupted before task_pack.json written -> discard temp params; next entry starts over.\n- task_pack.json already exists -> skip param collection, go straight to dispatch.\n\n## Invoking the LLM for document_digest\n\n`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:\n\n```bash\npython3 -c \"\nimport sys, json, pathlib\nsys.path.insert(0, '$PPT_STANDARD_DIR/lib')\nfrom model_client import llm\n\nraw = json.loads(pathlib.Path('<deck_dir>/raw_documents.json').read_text())\n\n# Build the digest-safe view: strip tables[] and image paths, keep text + indices\ndocs_view = []\nfor d in raw.get('documents', []):\n    docs_view.append({\n        'doc_index': d['doc_index'],\n        'type': d['type'],\n        'text': d.get('text',''),\n        'tables_count': len(d.get('tables') or []),\n        'images_count': len(d.get('inherited_images') or []),\n    })\n\nuser_prompt = json.dumps({\n    'user_query': '<the user's original query>',\n    'documents': docs_view,\n}, ensure_ascii=False)\n\nsys_prompt = open('$SKILL_DIR/prompts/document_digest.md').read()\n\nout = llm(sys_prompt, user_prompt)\n# Parse JSON; if it fails, degrade digest to null (not abort entry)\ntry:\n    digest = json.loads(out)\nexcept Exception:\n    digest = None\npathlib.Path('<deck_dir>/digest_tmp.json').write_text(json.dumps(digest, ensure_ascii=False))\n\"\n```\n\nThe 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).\n\nSubstitute `$PPT_STANDARD_DIR` with the `sn-ppt-standard` skill install dir.\n\n## Schemas\n\n`task_pack.json`:\n\n```json\n{\n  \"deck_id\": \"AI产品发布会_20260318_154500\",\n  \"deck_dir\": \"/abs/path/ppt_decks/AI产品发布会_20260318_154500\",\n  \"ppt_mode\": \"creative\",\n  \"params\": {\n    \"role\": \"...\",\n    \"audience\": \"...\",\n    \"scene\": \"...\",\n    \"page_count\": 10\n  },\n  \"created_at\": \"2026-04-21T15:45:00+08:00\",\n  \"skill_version\": \"0.1.0\"\n}\n```\n\n`info_pack.json`:\n\n```json\n{\n  \"user_query\": \"...\",\n  \"user_assets\": {\n    \"reference_images\": [\"/abs/...\"],\n    \"reference_docs\": [\"/abs/...\"],\n    \"reference_docs_failed\": []\n  },\n  \"document_digest\": {\n    \"topic_summary\": \"...\",\n    \"key_sections\": [],\n    \"key_points\": [],\n    \"data_highlights\": [],\n    \"inherited_tables\": [{\"doc_index\": 0, \"table_index\": 2, \"title_hint\": \"...\"}],\n    \"inherited_images\": [{\"doc_index\": 0, \"image_index\": 0, \"caption_hint\": \"...\"}]\n  },\n  \"raw_document_excerpts\": {\n    \"enabled\": true,\n    \"path\": \"/abs/.../raw_documents.json\"\n  }\n}\n```\n\n## Failure handling\n\n- Missing required env var -> stop, tell user `/skill sn-ppt-doctor`.\n- `$(pwd)/ppt_decks/` not creatable / not writable -> stop, tell user to check workspace permissions.\n- Per-file doc parse failure -> record in `reference_docs_failed`, continue.\n- `document_digest` LLM failure -> set to null, continue.\n\n## Progress echo — MANDATORY\n\nEmit a short chat reply at each boundary. Silence between ask_user rounds and mode dispatch is a bug.\n\n| When | Example |\n|---|---|\n| Right after entering sn-ppt-entry | `已进入 sn-ppt-entry，开始收集参数...` |\n| Missing a param | `缺少参数：<role>，马上问你` (then ask_user) |\n| All 5 params collected | `参数齐备：mode=standard, role=...。开始创建 deck_dir...` |\n| Before doc parse | `检测到 2 个附件，开始解析...` |\n| After doc parse | `解析完成：sample.pdf (12 页) / sample.docx (45 段)` |\n| Before digest | `[LLM] 正在汇总文档要点...` |\n| After digest | `文档摘要已入 info_pack.json` |\n| task_pack / info_pack written | `task_pack.json / info_pack.json 已写入 <deck_dir>` |\n| Dispatching | `分发到 sn-ppt-creative（deck_dir=...）` |\n\n## Output and handoff\n\nFinal message includes a short summary:\n\n```\n准备就绪：\n- 模式: <creative | standard>\n- 页数: <n>\n- deck_dir: <abs path>\n即将进入<创意 | 标准>模式...\n```\n\nThen dispatch:\n- ppt_mode=creative -> invoke `/skill sn-ppt-creative deck_dir=<abs>`\n- ppt_mode=standard -> invoke `/skill sn-ppt-standard deck_dir=<abs>`\n\n## Does NOT\n\n- Do not generate any style / outline / page content (that's the mode skill's job).\n- Do not run any image generation.","tags":["ppt","entry","sensenova","skills","opensensenova","agent","agent-skills","ai-agents","ai-assistant","data-analysis","document-processing","office-automation"],"capabilities":["skill","source-opensensenova","skill-sn-ppt-entry","topic-agent","topic-agent-skills","topic-ai-agents","topic-ai-assistant","topic-data-analysis","topic-document-processing","topic-office-automation","topic-presentation-slides"],"categories":["SenseNova-Skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/OpenSenseNova/SenseNova-Skills/sn-ppt-entry","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add OpenSenseNova/SenseNova-Skills","source_repo":"https://github.com/OpenSenseNova/SenseNova-Skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 1627 github stars · SKILL.md body (8,481 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:53:05.544Z","embedding":null,"createdAt":"2026-05-15T06:53:10.693Z","updatedAt":"2026-05-18T18:53:05.544Z","lastSeenAt":"2026-05-18T18:53:05.544Z","tsv":"'+08':923 '-04':918 '-21':919 '-3':177,651 '/.openclaw/workspace':214 '/abs':936,939,979 '/abs/path/ppt_decks/ai':901 '/digest_tmp.json':840 '/ppt_decks':198,255,263,293,996 '/raw_documents.json':319,691,742,980 '/skill':91,990,1151,1162 '/tmp':218 '0':730,957,967,970 '0.1.0':927 '00':922,924 '1':97 '10':914 '12':1096 '154500':898,904 '2':123,176,650,960,1088 '2026':917 '20260318':897,903 '3':147 '4':185 '45':921,1099 '5':307,1074 '6':409 '7':143,425 '8':601 'ab':498 'abort':299,407,527,829 'absolut':267,279,424 'accident':719 'actual':873 'ad':518 'agent':209,336 'ai产品发布会':896 'alreadi':127,502,641,674,692 'already-capt':501 'alt':542 'alway':196,284 'ambigu':153 'answer':622,645 'api':62,68 'ascii':805,846 'ask':155,617,640,1041,1071 'asked-alreadi':639 'asset':933 'attach':310 'audienc':12,108,167,910 'avoid':243 'b':484 'back':539 'base':74,613 'bash':444,722 'bear':422 'boundari':619,1038 'bug':1049 'build':745 'c':724 'cach':560 'call':154,366,523,637,697 'cannot':294 'capability-specif':65 'capabl':66 'caption':426,474,482,489,497,500,503,547,561,566,586,590,971 'chang':577 'chat':1034 'cheap':513 'check':60,304,1005 'client':736 'cmd':555 'collect':10,678,1076 'concaten':377 'condit':620 'content':462,713,1178 'continu':404,1019,1027 'count':15,117,170,707,779,785,913 'creat':186,249,281,296,693,915 'creatabl':998 'creativ':18,39,121,607,907,1122,1136,1149,1155 'cwd':201 'd':762,769,773 'd.get':776,781,787 'data':951 'deck':31,112,131,187,235,256,259,414,449,582,894,899,1082,1123,1139,1156,1167 'deck-dir':448 'degrad':399,824 'delet':583 'deni':298 'descript':463 'digest':392,396,402,543,688,748,825,832,837,844,849,855,864,944,1021,1102,1106 'digest-saf':747 'dir':32,132,188,227,260,415,450,881,890,900,1083,1124,1140,1157,1168 'dir/lib':733 'dir/prompts/document_digest.md':371,811 'dir/scripts/caption_images.py':447 'dir/scripts/parse_user_docs.py':316 'directori':212,221,246 'discard':666 'dispatch':34,602,682,1046,1117,1146 'doc':312,467,759,767,770,802,938,941,955,965,1011,1017,1085,1092 'doc-embed':466 'docs_view.append':766 'doctor':58,95,994 'document':356,378,391,471,687,765,801,943,974,1020 'downstream':275,536,550,856 'drift':244 'e.g':213 'echo':1029 'either':237 'embed':468 'emit':1031 'enabl':976 'ensur':804,845 'enter':1054 'entri':4,5,51,408,592,670,830,1058,1063 'enumer':660 'env':985 'error':358 'even':334 'everi':427 'exampl':1051 'except':835,836 'excerpt':975 'exist':128,675 'extract':98,628 'fail':83,823,942,1018 'failur':398,524,981,1013,1023 'fall':538 'fals':806,847 'field':423,494 'file':24,317,579,695,1010 'filenam':541 'final':1128 'fix':191 'flag':322 'flow':96 'forc':657 'full':258 'generat':9,1173,1191 'give':705 'go':679 'guess':194 'handl':340,982 'handoff':1127 'hard':52,59 'highlight':952 'hint':544,962,972 'home':220 'honor':233 'html':557,860 'id':895 'idempot':433 'ident':107 'imag':73,285,428,461,469,473,488,496,504,519,578,589,754,784,789,876,935,964,968,1190 'image-cont':460 'immedi':264 'import':725,737 'includ':1130 'incur':520 'index':768,771,869,956,959,966,969 'indic':708,758 'info':1111 'info_pack.document':395,401,854,863 'info_pack.json':27,126,412,436,928,1108,1115 'info_pack.user_assets.reference':487,495 'inherit':472,788,867,953,963 'instal':889 'interrupt':662 'invoc':205 'invok':384,683,1150,1161 'isn':646 'job':1185 'json':329,351,393,479,534,727,820,850,893,929 'json.dumps':793,843 'json.loads':740,833 'jump':140 'keep':756 'key':63,69,947,949 'len':780,786 'line':350 'list':870 'llm':368,386,685,699,716,738,814,1022,1103 'locat':189 'm':359 'mandatori':432,1030 'merg':852 'messag':104,1129 'miss':151,251,549,983,1065 'mkdir':252 'mode':17,119,165,289,616,906,1045,1078,1148,1159,1182 'model':735 'multipl':623 'must':247,276 'n':357 'name':257 'never':563 'new':30 'newli':517 'next':669 'node':70 'none':838 'null':403,827,1026 'number':721 'ok':354 'one':157,626 'open':809 'openclaw':200 'optim':636 'option':178,652 'order':163 'origin':799 'outlin':858,1176 'output':318,321,355,363,690,1125 'p':253 'pack':1110,1112 'page':14,116,169,283,556,859,912,1177 'param':624,642,668,677,908,1067,1075 'paramet':99,150 'paraphras':720 'parent':195,230,245 'pars':21,819,1012,1086,1093 'parse_user_docs.py':689 'path':261,280,421,499,591,755,877,978 'path-bear':420 'path.resolve':269 'pathlib':728 'pathlib.path':741,839 'pdf/docx/md/txt':23,313 'per':179,1009 'per-fil':1008 'permiss':297,306,1007 'point':6,950 'pool':441,464,483 'ppt':3,8,16,38,43,50,57,94,118,164,234,288,553,606,611,731,879,886,905,993,1057,1062,1121,1147,1154,1158,1165 'precondit':53 'print':346 'produc':25 'progress':1028 'prompt':374,382,703,792,808,816,818 'pwd':197,254,262,292,995 'python3':445,723 'queri':376,795,800,931 'question':180 'raw':739,973 'raw.get':764 'raw_documents.json':470,872 're':510,565,595 're-capt':564 're-run':509,594 'read':137,558,743,812,861 'realpath':268 'recommend':331 'record':653,1014 'redirect':342 'refer':135,311,588,934,937,940,1016 'references/ask_user_templates.md':175 'refresh':600 'reliabl':333 'remov':241 'repli':1035 'repo':223 'report':530 'requir':984 'resolv':265 'resum':145 'right':1052 'role':11,105,166,909,1080 'root':224,236 'round':1043 'row':712,875 'rule':574 'run':54,90,314,434,511,596,1188 'safe':515,749 'sample.docx':1098 'sample.pdf':1095 'scene':13,109,168,911 'schema':417,891 'script':325,345,452,529,598 'section':948 'see':144,277,383,416 'session':661 'set':400,1024 'shell':341 'short':1033,1132 'silenc':1039 'silent':507 'singl':349,455,570,632 'single-lin':348 'sister':493 'skill':80,204,226,315,370,446,810,888,925,1183 'skill-invoc':203 'skill-sn-ppt-entry' 'skip':506,638,676 'sn':2,37,42,49,56,61,72,93,552,605,610,634,885,992,1056,1061,1120,1153,1164 'sn-image-bas':71 'sn-ppt-creativ':36,604,1119,1152 'sn-ppt-doctor':55,92,991 'sn-ppt-entri':1,48,1055,1060 'sn-ppt-standard':41,551,609,884,1163 'sn-text-optim':633 'sourc':456,571 'source-opensensenova' 'speaker':106 'specif':67 'stage':537,857 'standalon':485 'standard':20,44,122,290,554,612,732,880,887,1079,1137,1160,1166 'start':77,671 'status':352,353,535 'stdout':361 'step':142 'stop':84,987,1001 'straight':680 'strip':751 'structur':865 'style':1175 'subdir':282 'substitut':878 'success':389 'summari':866,946,1133 'sys':726,807,815 'sys.path.insert':729 'system':373 't15':920 'tabl':752,778,782,874,954,958 'tables/images':710,868 'task':1109 'task_pack.json':26,125,274,411,664,673,892,1114 'task_pack.ppt':615 'tell':86,300,323,988,1002 'temp':667 'text':379,635,744,757,775,777,842 'time':160,206 'titl':961 'topic':945 'topic-agent' 'topic-agent-skills' 'topic-ai-agents' 'topic-ai-assistant' 'topic-data-analysis' 'topic-document-processing' 'topic-office-automation' 'topic-presentation-slides' 'tri':831 'true':977 'truth':458,573 'turn':627 'type':772,774 'upload':22,486 'use':45,115,171,217,365 'user':88,102,134,156,302,309,375,381,618,621,643,702,791,794,797,817,930,932,989,1003,1042,1072 'var':986 'verbatim':654 'version':926 'view':750,760,803 'visibl':443 'vlm':431,481,522,585 'well':343 'word':173 'work':332 'workspac':211,305,1006 'writabl':1000 'write':183,271,327,390,410,841 'written':438,475,490,665,1113 '个附件':1089 '产品发布会':902 '其他':184 '准备就绪':1134 '分发到':1118 '创意':1142 '即将进入':1141 '参数齐备':1077 '已写入':1116 '已进入':1059 '开始创建':1081 '开始收集参数':1064 '开始解析':1090 '文档摘要已入':1107 '标准':1143 '检测到':1087 '模式':1135,1144 '正在汇总文档要点':1104 '段':1100 '缺少参数':1068 '解析完成':1094 '页':1097 '页数':1138 '马上问你':1069","prices":[{"id":"adef905d-08ae-402f-8e3c-91183ff0ead3","listingId":"143eae0d-5f39-4731-a1ad-ecec926c0657","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"OpenSenseNova","category":"SenseNova-Skills","install_from":"skills.sh"},"createdAt":"2026-05-15T06:53:10.693Z"}],"sources":[{"listingId":"143eae0d-5f39-4731-a1ad-ecec926c0657","source":"github","sourceId":"OpenSenseNova/SenseNova-Skills/sn-ppt-entry","sourceUrl":"https://github.com/OpenSenseNova/SenseNova-Skills/tree/main/skills/sn-ppt-entry","isPrimary":false,"firstSeenAt":"2026-05-15T06:53:10.693Z","lastSeenAt":"2026-05-18T18:53:05.544Z"}],"details":{"listingId":"143eae0d-5f39-4731-a1ad-ecec926c0657","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"OpenSenseNova","slug":"sn-ppt-entry","github":{"repo":"OpenSenseNova/SenseNova-Skills","stars":1627,"topics":["agent","agent-skills","ai-agents","ai-assistant","data-analysis","document-processing","office-automation","presentation-slides"],"license":"mit","html_url":"https://github.com/OpenSenseNova/SenseNova-Skills","pushed_at":"2026-05-15T04:43:37Z","description":"Modular SenseNova skills for building AI-powered office assistants and productivity workflows","skill_md_sha":"724ec1bbc7ab82d5d35f41dc7c0a02be083e5c6e","skill_md_path":"skills/sn-ppt-entry/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/OpenSenseNova/SenseNova-Skills/tree/main/skills/sn-ppt-entry"},"layout":"multi","source":"github","category":"SenseNova-Skills","frontmatter":{"name":"sn-ppt-entry","description":"Entry point for PPT generation. Collects role / audience / scene / page_count /\nppt_mode (creative or standard), parses uploaded pdf/docx/md/txt files,\nproduces task_pack.json + info_pack.json in a new deck_dir, then dispatches\nto sn-ppt-creative or sn-ppt-standard. Use when the user asks to make a PPT /\npresentation / 演示 / PPT."},"skills_sh_url":"https://skills.sh/OpenSenseNova/SenseNova-Skills/sn-ppt-entry"},"updatedAt":"2026-05-18T18:53:05.544Z"}}