{"id":"37037034-820c-47af-b67e-554a1fd7c48e","entityType":"agent","slug":"clawhub-durenzidu-powpow-publisher","name":"PowPow Publisher — turn photos into travelogues, pin them to the map, create chat-capable digital humans","canonicalUrl":"https://www.xpersona.co/agent/clawhub-durenzidu-powpow-publisher","canonicalPath":"/agent/clawhub-durenzidu-powpow-publisher","generatedAt":"2026-10-11T21:01:20.214Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T15:57:02.783Z","emptyReason":null},"description":"Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my travel photos into a travelogue on PowPow\", \"post these photos to PowPow\", \"发一篇 PowPow 帖子\", \"把这次旅行的照片发到泡泡\"; also triggers when the user wants to create/publish a digital human onto the map, e.g. \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\". Requires a PowPow account (register first if none). Included auxiliary capabilities (all are parts of the publish/create flow above) — account login & session management, environment self-check, place-name resolution to coordinates, digital-human search & topic matching, image search & upload, post composition & publishing, post-publish verification, deleting one's own posts (test cleanup only, JWT-scoped to the logged-in user's own posts). Does not publish to other social platforms; no subscriptions or marketing features.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s17dzq04k30f6dsx2j1nhwn4qd83eptn:powpow-publisher","sourceUrl":"https://clawhub.ai/durenzidu/powpow-publisher","homepage":"https://clawhub.ai/durenzidu/skills/powpow-publisher","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/durenzidu/powpow-publisher","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/durenzidu/skills/powpow-publisher","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"PowPow Publisher — turn photos into travelogues, pin them to the map, create chat-capable digital humans technical dossier on Xpersona with agent coverage, OPEN"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T15:57:02.783Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T15:57:02.783Z","emptyReason":null},"stars":null,"forks":null,"downloads":1033,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.0.5","tractionLabel":"1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T15:57:02.769Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T15:57:02.783Z","lastCrawledAt":"2026-10-11T15:57:02.769Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T15:57:02.769Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.5","createdAt":"2026-09-25T02:47:59.773Z","changelog":"Renamed from powpow-openclaw-test to powpow-publisher (old slug kept as a redirect). Frontmatter name fixed: powpow_openclaw -> powpow-publisher. No functional changes.","fileCount":25,"zipByteSize":64416},{"version":"1.0.4","createdAt":"2026-09-24T21:13:46.978Z","changelog":"Listing metadata: add display name, search keywords and categories (communication, lifestyle, creative)","fileCount":25,"zipByteSize":63937},{"version":"1.0.3","createdAt":"2026-09-23T02:29:45.398Z","changelog":"Content replaced with powpow-simple-en v1.0.0 (instruction-style publish pipeline for posts + digital humans; English edition of powpow-simple v5.7.3). Old v1.0.2 command simulator removed. Scripts byte-identical; display name kept.","fileCount":25,"zipByteSize":64179},{"version":"1.0.2","createdAt":"2026-04-07T07:31:01.772Z","changelog":"- Major simplification: skill now auto-handles communication via PowPow backend, no need for OpenClaw Gateway setup. - Account registration, digital human creation, and messaging are streamlined into a three-step flow. - Command set updated: added email/password to registration and a simpler `send` message command. - User documentation and usage examples rewritten for clarity and ease of onboarding. - Removed unused files and legacy features related to badge system and rate-limiting.","fileCount":12,"zipByteSize":28073},{"version":"1.0.1","createdAt":"2026-04-01T12:08:09.746Z","changelog":"Version 1.0.1 - Migrated source files to a compiled distribution in the dist/ directory. - Updated and expanded documentation in SKILL.md, including detailed usage, configuration, and command instructions. - Changed skill name and description to \"powpow-integration\" for clarity and alignment with intended functionality. - Added new utility files, type definitions, and PowPow client JS modules. - Removed original src/index.ts source file.","fileCount":15,"zipByteSize":22336},{"version":"1.0.0","createdAt":"2026-03-29T11:43:25.065Z","changelog":"Initial release: PowPow OpenClaw test skill. - Provides the same capabilities as powpow-integration for comparison testing (register, login, create/list digital avatars, chat, renew, badge query, help). - Uses in-memory storage to simulate PowPow/OpenClaw interactions—no external network required. - Intended for rapid side-by-side testing; not for production use.","fileCount":5,"zipByteSize":2971}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17dzq04k30f6dsx2j1nhwn4qd83eptn:powpow-publisher","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17dzq04k30f6dsx2j1nhwn4qd83eptn:powpow-publisher` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/durenzidu/powpow-publisher before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T21:01:20.210Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-durenzidu-powpow-publisher/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T15:57:02.783Z","emptyReason":null},"readme":"Skill: PowPow Publisher — turn photos into travelogues, pin them to the map, create chat-capable digital humans\n\nOwner: durenzidu\n\nSummary: Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my travel photos into a travelogue on PowPow\", \"post these photos to PowPow\", \"发一篇 PowPow 帖子\", \"把这次旅行的照片发到泡泡\"; also triggers when the user wants to create/publish a digital human onto the map, e.g. \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\". Requires a PowPow account (register first if none). Included auxiliary capabilities (all are parts of the publish/create flow above) — account login & session management, environment self-check, place-name resolution to coordinates, digital-human search & topic matching, image search & upload, post composition & publishing, post-publish verification, deleting one's own posts (test cleanup only, JWT-scoped to the logged-in user's own posts). Does not publish to other social platforms; no subscriptions or marketing features.\n\nTags: latest:1.0.5\n\nVersion history:\n\nv1.0.5 | 2026-09-25T02:47:59.773Z | user\n\nRenamed from powpow-openclaw-test to powpow-publisher (old slug kept as a redirect). Frontmatter name fixed: powpow_openclaw -> powpow-publisher. No functional changes.\n\nv1.0.4 | 2026-09-24T21:13:46.978Z | user\n\nListing metadata: add display name, search keywords and categories (communication, lifestyle, creative)\n\nv1.0.3 | 2026-09-23T02:29:45.398Z | user\n\nContent replaced with powpow-simple-en v1.0.0 (instruction-style publish pipeline for posts + digital humans; English edition of powpow-simple v5.7.3). Old v1.0.2 command simulator removed. Scripts byte-identical; display name kept.\n\nv1.0.2 | 2026-04-07T07:31:01.772Z | user\n\n- Major simplification: skill now auto-handles communication via PowPow backend, no need for OpenClaw Gateway setup.\n- Account registration, digital human creation, and messaging are streamlined into a three-step flow.\n- Command set updated: added email/password to registration and a simpler `send` message command.\n- User documentation and usage examples rewritten for clarity and ease of onboarding.\n- Removed unused files and legacy features related to badge system and rate-limiting.\n\nv1.0.1 | 2026-04-01T12:08:09.746Z | user\n\nVersion 1.0.1\n\n- Migrated source files to a compiled distribution in the dist/ directory.\n- Updated and expanded documentation in SKILL.md, including detailed usage, configuration, and command instructions.\n- Changed skill name and description to \"powpow-integration\" for clarity and alignment with intended functionality.\n- Added new utility files, type definitions, and PowPow client JS modules.\n- Removed original src/index.ts source file.\n\nv1.0.0 | 2026-03-29T11:43:25.065Z | user\n\nInitial release: PowPow OpenClaw test skill.\n\n- Provides the same capabilities as powpow-integration for comparison testing (register, login, create/list digital avatars, chat, renew, badge query, help).\n- Uses in-memory storage to simulate PowPow/OpenClaw interactions—no external network required.\n- Intended for rapid side-by-side testing; not for production use.\n\nArchive index:\n\nArchive v1.0.5: 25 files, 64416 bytes\n\nFiles: config.json (132b), README.md (3842b), references/changelog.md (4107b), references/file-structure.md (2758b), references/screenshots.md (4123b), references/security.md (2707b), scripts/compose.js (18328b), scripts/create-digital-human.js (9742b), scripts/delete-post.js (1037b), scripts/doctor.js (4980b), scripts/geocode.js (3699b), scripts/lib/api-client.js (7194b), scripts/lib/coord-transform.js (2377b), scripts/lib/dh-match.js (3273b), scripts/lib/html-formatter.js (8335b), scripts/list-digital-humans.js (3331b), scripts/login.js (3169b), scripts/match-digital-human.js (2429b), scripts/publish.js (7320b), scripts/search-image.js (9692b), scripts/upload-image.js (3998b), scripts/verify.js (3218b), skill-card.md (2359b), SKILL.md (37551b), _meta.json (135b)\n\nFile v1.0.5:SKILL.md\n\n---\nname: powpow-publisher\ndescription: Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my travel photos into a travelogue on PowPow\", \"post these photos to PowPow\", \"发一篇 PowPow 帖子\", \"把这次旅行的照片发到泡泡\"; also triggers when the user wants to create/publish a digital human onto the map, e.g. \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\". Requires a PowPow account (register first if none). Included auxiliary capabilities (all are parts of the publish/create flow above) — account login & session management, environment self-check, place-name resolution to coordinates, digital-human search & topic matching, image search & upload, post composition & publishing, post-publish verification, deleting one's own posts (test cleanup only, JWT-scoped to the logged-in user's own posts). Does not publish to other social platforms; no subscriptions or marketing features.\nversion: 1.0.5\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n    emoji: \"\\U0001FAE7\"\n    homepage: https://global.powpow.online\n    envVars:\n      - name: POWPOW_STATE_DIR\n        required: false\n        description: Directory for session and config state. Defaults to ~/.powpow. The skill bundle stays read-only; login state is written here.\n      - name: POWPOW_PASSWORD\n        required: false\n        description: Optional. Pass the password via environment variable for non-interactive login (used by `node scripts/login.js <username>` on first login). If unset, login prompts interactively; the password is never persisted.\n      - name: UNSPLASH_ACCESS_KEY\n        required: false\n        description: Optional Unsplash Access Key, used only for keyword-based automatic image search. If unset, fall back to local images or direct URLs.\n---\n\n# powpow-publisher — Publish posts & digital humans to the public map\n\nTurn your photos or raw material into a travelogue and publish it to PowPow on your behalf; or turn a person/character into a chat-capable digital human pinned to the public map.\n\n**Version 1.0.5** · 2026-09-25 (history: `references/changelog.md`; security architecture & API list: `references/security.md`; file structure: `references/file-structure.md`; product screenshots: `references/screenshots.md`)\n\n**Language policy (hard rule)**: Always converse in the user's language. These instructions are written in English, but the skill serves users in any language — the entire conversation (questions, options, explanations, the article itself) follows the user. Chinese trigger phrases are recognized too.\n\n## Runtime Environment (OpenClaw)\n\nThis is an **instruction-style skill**: SKILL.md describes the flow, and `scripts/*.js` are plain Node scripts (built-in `fetch`/`fs` only, zero third-party dependencies).\n\n- **Requires Node.js 18+** (scripts use global `fetch`). Verify first: `node -v`\n- **Scripts live under `scripts/` in this skill's directory.** Before running, change to the skill root, e.g.:\n  `cd <this-skill-dir> && node scripts/doctor.js`\n- **State directory**: the login token is written to `POWPOW_STATE_DIR` (default `~/.powpow/session.json`). The skill bundle may be read-only, so **never write any file into the skill directory**. Users can customize the location via that environment variable.\n- **Always pass absolute paths** for images to `--image`; relative paths fail to resolve.\n\n## Product Screenshots (let users *see* PowPow)\n\nMost users have never seen what PowPow looks like. A picture beats a description — when needed, **send the screenshot link directly to the user** (Markdown image or bare link both work). Full list, descriptions, and when to use each: `references/screenshots.md`. Quick mapping:\n\n- User has no concept of the product / opening \"what does the result look like\" → map page + post detail page\n- User asks \"what is a digital human\" / \"how do I chat with it\" → digital-human detail page\n- User asks \"what can badges do\" / \"what do I have\" → profile page (digital assets)\n- User asks \"how does PowPow work\" → map page (bubbles cluster on the map)\n\nRules:\n- Screenshots are hosted in the public repo `durenzidu/durenzidu` under `screenshots/` (`powpow-0N.jpg`). **Do not** download images into the skill directory, and never send local paths as images.\n- If `raw.githubusercontent.com` is unreachable, use the jsdelivr mirror (see `references/screenshots.md`).\n- Send only the 1–3 screenshots relevant to the current topic — never dump all six at once.\n- If it's not clear which one applies, don't guess — pick against the descriptions in `references/screenshots.md`.\n\n## Trigger Conditions\n\nThis skill triggers when the user wants to publish travel content (travelogue/photos/trip) to PowPow, or wants to create a digital human pinned to the map. Typical phrasings:\n\n- Travelogue/photos: \"turn my travel photos into a travelogue on PowPow\", \"post a trip story to powpow\", \"post these photos to PowPow\", \"把这次旅行的照片发到泡泡\"\n- Platform-command style: \"publish a PowPow post\", \"post to PowPow feed\", \"powpow 发帖\"\n- Digital human: \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\", \"创建一个数字人\"\n\n**Co-occurrence rule (prevent false triggers)**: mentioning travel/photos/travelogue alone is not enough — the user must also express publish intent (post/publish/upload/发/发布) or name the platform (PowPow/泡泡/powpow). \"This photo is beautiful\" does not trigger; \"post this photo to PowPow\" does.\n\nDoes not trigger for generic social media or other platforms.\n\n## Conversation UX (first-time users)\n\nMany users have never published anything through a chat assistant. Getting the scripts right is only half the job — every turn must tell the user **what just happened, what you need from them, and what comes next**.\n\n### Tone — applies to every turn, from first sentence to last\n\nThis is a product for ordinary users. You are **serving** the user, not **instructing** them. Keep the tone gentle and respectful, consistently.\n\n| Don't say (lecturing / talking down) | Say instead (serving / side-by-side / inviting) |\n|---|---|\n| First, let me be clear about one thing: … | There's one thing I'd like to explain first: … |\n| A reminder: … / I have to remind you that … | There's one thing I'd like you to double-check with me |\n| There are two things I can't guess — you have to tell me | I can't see when or where a photo was taken, so I'll need you to tell me those two things |\n| I won't decide this for you / you need to know this | I'd rather not decide this one for you — it goes into the article, and the wrong pick would feel off |\n| Do you need to worry about coordinates? No. | Leave the coordinates to me — you just pick the place name |\n\n**Always:**\n1. **The user is the subject of the sentence.** \"You can…\" beats \"I need you to…\".\n2. **\"I\" am the service, not the lecturer.** Whatever can be done for the user (coordinate conversion, format assembly, validation, retries, failure fallbacks) — do it first, then report. Don't narrate the process as a knowledge lecture.\n3. **Every step carries forward momentum.** \"Now, let's start with step one\" feels like doing things together; \"please answer my question\" does not.\n\n**Explicitly forbidden**: no lecturing; no announcing \"I need to remind you\"; no judging the user's choices; never \"I have to…\"; when the user hasn't asked, don't explain internal mechanics, limits, or security design.\n\n**Provider-neutral naming (hard rule)**: **never mention the map provider's name to the user.** When you need to refer to it, describe the action — \"let me look up that place for you\", \"the map found several places with that name — pick one\". **Don't use jargon like \"map database\" either.** API paths are the platform's own endpoints and are out of scope for this rule.\n\n### Opening: introduce yourself, then ask the first question\n\nOn first trigger (doctor reports no session, or the user asks what you can do), open with a short paragraph (~120 words, don't pile up features), **branch by user intent**, then ask the first question:\n\n- **Post intent** → 4-part opening:\n  1. **What I can do**: turn your photos or material into a travelogue and publish it to PowPow for you.\n  2. **What the result looks like**: a post can carry digital-human tags (a red capsule — tap the avatar to jump into a chat), location tags (pin + place name — tap to open the map), and images; a post with a location appears on the public map as a \"bubble\" and slowly fades out over time — that's the core of how PowPow works. (Want them to see it right away? Send screenshots — see \"Product Screenshots\".)\n  3. **What's needed to start**: digital humans, locations, and publishing all require the platform, so step one is a one-time login with your PowPow account. No account yet? Register: https://global.powpow.online/register (To learn what PowPow is first, watch this intro video — in Chinese: https://www.bilibili.com/video/BV1Wu826UEVz/ )\n  4. **The flow (compressed to 3 beats)**: ① log in → ② you give me material, I write and format → ③ you confirm, only then I publish. Everything is editable until you confirm; nothing goes live without confirmation.\n- **Digital-human intent** → short opening: \"Give me a name + persona + location, and I'll turn them into a chat-capable digital human pinned to the public map. This costs 2 badges and expires after 30 days — confirm and I'll do it. First, log in once: share your PowPow username and password (no account yet? Register: https://global.powpow.online/register ).\"\n\nTemplate (use as a starting point, adapt to the user's language — don't parrot it into template-speak):\n\n> It's a pleasure to serve you — welcome to the world of PowPow.\n> Here, **you can** turn the photos you took today into a travelogue, and **I'll** write it, pin it to the map, and publish it to PowPow for you — when someone taps it on the map, they'll see the path you walked that day.\n> [Part 2: what the result looks like — capsules, images, the map-bubble mechanic (use the standard line above)]\n> [Part 3: what's needed — one login; include the register link + intro video if they may not have an account]\n> [Part 4: the 3-beat flow, login first]\n> Ready? **Hand me what you'd like to publish today, and we'll start with step one.**\n\n- Never ask for the password before the self-introduction — a stranger suddenly saying \"give me your password\" is what phishing looks like.\n- Ask for credentials once, in a single plain message, with the register link (add the intro video only if they may not have an account). Don't ask for the password, then the material, then spring more conditions.\n- **No password-security lectures.** Ask plainly and move on. You may add one neutral, non-alarming line: \"Your password is only used to obtain a temporary login token — I won't save it.\" Don't elaborate.\n\n### Every turn: narrate + guide\n\n- **One decision per turn.** Tightly related follow-ups (location + time) may share one message; don't bundle material, length, and location choice together.\n- **When asking for facts, say why**: we never read photo EXIF/location (privacy), so the location and time must come from the user.\n- **Never paste raw script output.** After each run, give the user one human-language summary: what happened, what it means, what's next. After login, e.g.: \"You're logged in with your account. Now send me your material: photos, a piece of writing, or just an idea — anything works.\"\n- **Announce stage transitions**: \"Material's all in — next I'll pick a few digital humans related to your topic for you to choose from.\" The user should always know which step of the flow they're on.\n- **Give a reason with every option** (digital humans, location candidates, length) — a bare numbered list is hard to choose from.\n- **Before publishing**, include one line noting the post will appear on the public map (as gameplay context, not as an opt-out).\n\n### Closing \"Next step\" block (mandatory every turn)\n\nIn long messages users lose the point — not because the content is wrong, but because the action gets buried. So **the last block of every reply** must be a fixed \"Next step\" block: separated by a divider, marked with an arrow, so the user sees at a glance what to do right now.\n\nFixed format:\n```\n────────────────────\n👉 Your move: reply 1 / 2 / 3 to pick a location\n   (Once you pick, I'll draft the post — next turn you'll see the preview with capsules)\n```\nThree hard rules:\n1. **Only one action per block.** Two todos → split into two turns, or demote one to a plain FYI.\n2. **All options go into this block.** Don't make the user scroll back into the body to find \"reply 1–6\".\n3. **Add one \"once that's done, I'll…\" line**, so the user knows where the action leads.\n\nPublish results follow the same rule (see Step 8): link and location go at the very end of the message, closest to where the eye lands.\n\n## Prerequisites\n\n- **Node.js 18+ required** (scripts use global `fetch`).\n- **PowPow account required**: register at https://global.powpow.online/register\n\n### First-Time Setup (per user)\n\n0. Run the self-check first, and fix whatever it reports. It **silently probes the stored session against the server** — if the session is valid, skip login and jump straight into the Workflow:\n   ```bash\n   node scripts/doctor.js\n   ```\n1. If doctor reports no valid session and this is first contact, deliver the **Conversation UX opening** above, then ask for credentials in **one plain message**:\n   \"Next I need a one-time login: please share your PowPow username and password. No account yet? Register first: https://global.powpow.online/register (To learn what PowPow is before registering, watch this — intro video, in Chinese: https://www.bilibili.com/video/BV1Wu826UEVz/ )\"\n   Then stop. No security lecturing about the credentials themselves (see Tone). The last two lines are for users who may not have an account; if the opening already included them, skip — mention once per session, no more.\n2. Run login:\n   ```bash\n   printf '%s' '<password>' | node scripts/login.js <username>\n   ```\n   - **stdin only, never positional arguments** — the positional form lingers in shell history and process lists, a real exposure.\n   - **Windows** (PowerShell/cmd have no `printf`): you can run\n     ```bash\n     node scripts/login.js <username>\n     ```\n     then type the password at the prompt (the script reads it hidden, no echo). Either way, **never put the password in a command-line argument**.\n   - Never echo the password, never write it to disk, never keep it in a shell variable.\n   - The login token lands in the state directory (`POWPOW_STATE_DIR`, default `~/.powpow/session.json`), not inside the skill directory.\n   - This is an internal handling rule — don't explain it to the user.\n3. **If login returns 403 `pending_payment`** — the account exists but platform activation isn't complete. Stop, don't retry, say: \"Your account isn't fully activated yet, so publishing isn't possible right now. Please open PowPow, log in, and follow the on-page instructions to finish activation; tell me when it's done and we'll continue.\" This is neither a transient error nor a login problem — don't make the user re-enter credentials, and don't keep collecting material.\n\n### Configuration\n\n`config.json` (ships defaults, no secrets; read-only):\n\n```json\n{\n  \"platformUrl\": \"https://global.powpow.online\",\n  \"unsplashAccessKey\": \"\",\n  \"sessionMaxAgeDays\": 6\n}\n```\n`unsplashAccessKey` is optional, user-supplied; without it, skip image search (image-less posts are fully supported) or use direct URLs from the user.\n\n**Overriding config under OpenClaw**: don't modify the bundled `config.json` (may be read-only). Put a `config.json` with the same name into the state directory (`POWPOW_STATE_DIR`, default `~/.powpow/config.json`) — its values override the bundled defaults. Or set the `UNSPLASH_ACCESS_KEY` environment variable.\n\n## Workflow\n\n> **Execution convention (OpenClaw)**: all `node scripts/...` commands below assume the current directory is this skill's root. If unsure, run with absolute paths: `node <skill-dir>/scripts/xxx.js`. Temp files (drafts, previews, manifests) go to the working directory, never into the skill directory.\n\n### Step 1: Content gathering (photos → travelogue)\n\nThe flagship flow: the user sends photos, you write a first-person travelogue around them.\n\n1. **Look at the photos** (you can see images). Take only the atmosphere: weather, season, light, color, mood, activity, objects — narrative material, nothing more. **Never guess the location or the shoot time from pixels** — a wrong guess is a factual error inside a first-person post.\n2. **Ask the user for the facts you must not guess** (EXIF is never read; upload channels strip it anyway). Give the reason, then ask:\n   - Location: \"To protect your privacy, I don't read the location data inside your photos — so let me ask: where was this taken?\"\n   - Time: \"Roughly when was it taken?\"\n3. **Offer writing styles** (before asking about length). List 4–5, one feel per style, e.g.:\n   \"What writing style would you like? a. Lyrical & detailed — light, mood, breathing sentences b. Easy & everyday — like chatting with a friend c. Witty & self-deprecating — good for travel-fail stories d. Deep & cultural — history and geography lore e. Minimal & spare — short sentences, white space\"\n   **Always add the imitation option** (the strongest personalization): \"You can also paste a snippet of something you've written — a tweet or a moment post — and I'll write in your own voice (I learn the voice only, never reuse the content).\" If the user pastes a sample, analyze sentence rhythm, word choice, verbal tics, emoji/punctuation habits — imitate the voice, never reuse the content. The sample stays in the chat and is stored nowhere.\n4. **Offer length options**: \"How long should the travelogue be? ~150 words / ~300 words / ~2000 words / your call (platform limit: 50,000 characters, images included)\"\n5. **Writing rules**:\n   - **First person = the user.** The user is the narrator; a digital human, if present, is someone being mentioned — never the narrator.\n   - **Use the chosen style** (or the imitated voice). If the user both picks a style and gives a sample, the sample wins — their own voice beats the menu.\n   - Weave time into the wording (early morning / dusk / after the first snow); never write a hard timestamp.\n   - Only include digital humans that genuinely fit — never force one in.\n   - Language follows the user's preference (any language works).\n\nOther inputs are fine too: **Mode A** user provides finished text — skip generation; **Mode C** partial input — you fill in. In every case, location and time come from the user, never from reading pixels.\n\n### Step 2: Digital-human matching\n\nList/suggest healthy digital humans:\n```bash\nnode scripts/match-digital-human.js \"<topic>\" --limit 3 --json   # ranked suggestions\nnode scripts/list-digital-humans.js \"<name>\" --json              # search by name\n```\nHealth filtering is built in: disabled, test-named, garbled, and placeholder-avatar digital humans are excluded automatically. Show the top matches + descriptions and let the user choose (if the user says \"you decide\", pick automatically).\nIf the digital human the user wants doesn't exist, give the creation page: https://global.powpow.online\nNote: the platform's digital-human library is currently mostly Chinese-language; matching works best when the topic is given in Chinese.\n\n### Step 3: Location handling\n\n**Location is strongly recommended, but optional.** It decides whether the post appears on the public map (the core gameplay).\n\n- **User gives a place name** (the norm): resolve it to coordinates, then let the user pick from same-name candidates:\n  ```bash\n  node scripts/geocode.js \"地坛公园\" --limit 5        # add --city 北京 to narrow\n  ```\n  The script queries the platform's places and locally converts each candidate from GCJ-02 to WGS-84, matching the web editor's coordinate system. Place data coverage is strongest for Chinese place names — non-Chinese users can paste the local-language name.\n  **To the user, describe only actions**: \"let me look up that place for you\", \"the map found several places with that name — pick one\" — never name the map provider, and never say \"map database\".\n  Show candidates (name/district/address/coordinates) each with a reason, and let the user choose; duplicate place names are common (Chaobai River Bridge has 90 hits). The user picks **by name**; coordinates are your job, not their problem.\n  Assembly: `--loc <name> --lng <x> --lat <y>`.\n- **Nothing found — degrade in this order, never make the user give coordinates:**\n  1. **Rephrase / narrow**: try different words, or add `--city <city>` (district, landmark, road name).\n  2. **Widen to help recognition**: raise `--limit` (e.g. 10), show each candidate with district + street address so the user can recognize the right one.\n  3. **Publish without a location** — the post is still valid (`isLocationExposed: false`). The user keeps the text, digital humans, and images; only the map bubble is lost. State this as the outcome, not as a failure.\n  Making an ordinary user supply latitude/longitude is never a legitimate fallback: they have no way to obtain it, and what they find is usually GCJ-02, which pins off by hundreds of meters — a visible factual error in a first-person post. Coordinates are an expert channel: use them only if the user offers.\n- **No location in the material or the conversation**: don't silently produce a location-less post. Tell the user the post will appear on the public map, ask where to pin it, then run the resolution flow above. This question is the only action of the turn — put it in the \"Next step\" block.\n- **User declines to give a location — accept it.** Publish without location (`isLocationExposed: false`). Never pressure, never block publishing — it's their choice.\n- User directly provides coordinates: use them as-is.\n- Otherwise use the chosen digital human's location (`locationName`, `lng`, `lat`).\n\n**A provided location always goes on the public map (`isLocationExposed: true`) — this is an internal rule; do not offer the user a \"hide from map\" option.** Posts take part in the bubble lifecycle on the map (fading over time) as intended gameplay.\n\nLocation component format:\n```html\n<span data-type=\"location\" data-lng=\"116.316\" data-lat=\"39.979\" name=\"中关村\">\n  <span class=\"location-name\">中关村</span>\n</span>\n```\n\n### Step 4: Image handling (optional)\n\nThree ways to attach images:\n- **Local files** (the norm): `--image @<local-path>`. **Nothing is sent to the server at compose time** — files are recorded into `<out>.manifest.json` and previewed locally via `file://`. Upload happens only at publish time in publish.js (`POST /api/upload/post-image`, JPEG/PNG/WebP/GIF/BMP/HEIC, no SVG, ≤10MB each, server-side compression). Editing drafts never touches the server and never creates orphan files.\n- **Direct URLs**: HEAD-check first, then embed. Unsplash photo-page links are recognized too (`unsplash.com/photos/...`, commonly copied from a browser) — automatically resolved to `images.unsplash.com` direct links. File-ID links (`unsplash.com/photos/1507513319174-...`) resolve locally without a key; slug/short-ID links require an Unsplash API key (the site's bot protection blocks keyless scraping). Without a key, skip the slug form and say so — ask the user for a direct link; never embed a page link as an image (it renders broken).\n- **Automatic image search** (only if an Unsplash key is configured): `--image search:<english keywords>` (or `node scripts/search-image.js \"<keywords>\"`)\n- No key, no URL, no file → publish without images. Never bundle an image API key.\n\n**No fixed image-count cap.** The platform's boundary is 50,000 characters (each image tag ≈100 chars); remind the user when approaching 200 images. Display: the feed grid shows the first 9 + \"+N\"; the full-screen viewer shows all.\n\n### Step 5: Content formatting (critical — read carefully)\n\nInteractive components (digital human / location) are rendered by the web frontend from **rich HTML** inside `content`. Writing just `<span data-type=\"digital-human\">Marie Curie</span>` renders as plain text — only a span carrying the tiptap editor's exact structure (Tailwind classes, inner avatar `<img>`, pin icon, `.location-name`) displays as a tappable capsule.\n\n**Mandatory: always use `scripts/compose.js` to assemble post HTML. Never hand-write component spans, never write glue code to bypass html-formatter.js.**\n\n```bash\n# 1. Save the post text to a file. Separate paragraphs with blank lines.\n#    Placeholders (all optional; any misuse is a hard error, never silently altered):\n#      {{dh}}  or {{数字人}}   -> digital-human capsule (requires --dh)\n#      {{loc}} or {{位置}}     -> location capsule (requires --loc)\n#      {{img}} or {{图}}       -> image paragraph; must be its own paragraph, consumes --image in order\n#                                (each one needs a spare --image, otherwise compose fails)\n#    Note: the Chinese aliases above are literal tokens recognized by the script —\n#    use either form, but never invent new tokens.\n#    Unknown/extra/inline placeholders make compose.js exit 1 and write nothing.\n#    Fix the text file and rerun — never delete text to force it through.\n\n# 2. Assemble in one command (fully local, no network):\nnode scripts/compose.js --text-file post.txt \\\n  --dh name:岳飞 \\                    # or --dh <id>, or --dh auto --topic \"...\"\n  --loc dh \\                          # or --loc <name> --lng 116.3 --lat 39.9\n  --image @./photos/west-lake.jpg \\   # local file (uploaded at publish); or <direct-url>, or search:<keywords>\n  --out post-draft.html\n```\n\ncompose.js enforced rules:\n- Capsules always match the editor's exact structure (validated before writing).\n- **Unknown/extra/misplaced placeholders are hard failures** (exit 1, nothing written). The old version only deleted the placeholder + printed a stdout warning — and since raw script output is never forwarded to the user, a broken sentence (\"we sat at for an entire afternoon\") could silently go live.\n- If `--dh`/`--loc` is given but the text has no placeholder, the capsule is auto-prepended to the first paragraph / appended to the last one.\n- Local images become `powpow-local://N` placeholders + `post-draft.html.manifest.json`; publish.js uploads them at publish time.\n- compose.js also writes `post-draft.html.preview.html` — a **Chinese-language** preview page the user opens in a browser. Its banner states whether the post will appear on the public map and, if so, where it's pinned — so the map consequence is visible on the page the user actually reads (the local map renders via `file://`). Tell the user in their own language what the banner says.\n- `publish.js` re-runs `validateEditorFormat()` and rejects anything else.\n- **Do not send `contentItems` in the publish payload** — the frontend prefers structured rendering when `contentItems` is present, and an incomplete list drops post text. `publish.js` handles this.\n- The server automatically takes the first `<img>` as the card thumbnail.\n- Note: for posts without block-level images, the server takes the digital-human capsule's inner avatar as the thumbnail (platform behavior, cosmetic).\n\n### Step 6: Pre-publish confirmation (mandatory)\n\nFirst show the four-item breakdown, then end the turn with a \"Next step\" block whose **only action** is the publish decision (publish / revise more / hold off):\n\n1. **Full post text** (with capsule positions marked)\n2. **Component list**: digital human (name + avatar), **location (name + coordinates)**, image count, and **the logged-in account** (taken from the session, not from user input)\n3. **Local preview file path** (`<out>.preview.html`) — Chinese-language preview; its banner also states whether the post goes on the public map. One line noting it's an approximation, not a pixel-perfect copy.\n4. Ask \"Anything to change? If it all looks right, reply 'publish'\", plus one line reminding that the post will appear on the public map as a bubble (PowPow gameplay, not a side effect).\n\nThe decision must be the last block — never buried after the breakdown. (Why so strict: in testing, users read exactly this content and replied \"and then?\" — the information was all there, but the action didn't land where the eye stops.)\n\n**Revision loop**: if the user wants changes, make them and rerun compose.js — fully local, any number of rounds with zero server contact and zero orphan uploads. No iteration cap; only an explicit 发布/publish/confirm from the user ends it. Until then, never publish.\n\n### Step 7: Publish\n\n```bash\nnode scripts/publish.js <html-file-path>\n```\nIf the draft contains local images, this is the moment they upload — one by one, before the post goes out. Progress is persisted after each success (HTML + manifest rewritten with real URLs), so a failed rerun doesn't re-upload what already succeeded.\nThe post is created as the logged-in user — identity comes from the JWT; there is no way to post as someone else. The server enforces content moderation and rate limits. On 429 the script honors `Retry-After` automatically.\n\n### Step 8: Post-publish verification (mandatory)\n\n```bash\nnode scripts/verify.js <post-id>\n```\nChecks post accessibility, author, digital-human/location components, image count, word count. Report in this order — **breakdown first, result last**:\n\n1. **Verification breakdown**: what was read back and what matched — author, digital-human capsule, location capsule, image count, word count. Quote verify.js's numbers as-is (it really counts images; a 3-image post reports 3).\n2. One line on the bubble lifecycle (standard phrasing): \"A post with a location appears on the public map as a 'bubble' and slowly fades out over time — that's the core of how PowPow works.\"\n3. **Result block, as the very last message line** — closest to the input box, where the eye lands. Must include the link **and the post's location**:\n\n```\n────────────────────\n✅ Post published\n📍 Pinned at: Yanjiao Xinggong Ruins (116.816667, 39.95)\n👉 Please open the link and check: do both capsules tap, is the image order right?\n   (The command line can't verify that — your eyes are the final gate)\n🔗 https://global.powpow.online/posts/<post-id>\n```\n\nIf there's no location, say so in the same block: \"📍 No location — this post won't appear on the public map\".\n\nInteractive rendering (capsule taps, image lightbox) can't be verified from the CLI — the user's eyes are the final gate.\n\nTest-post cleanup:\n```bash\nnode scripts/delete-post.js <post-id>\n```\n\n## Creating a Digital Human (publishing a digital human)\n\nA separate flow from posting: turn a person (historical figure, character, the user's own avatar…) into a chat-capable digital human pinned to the public map. Triggers: \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\".\n\n**Cost — confirm before running, just like the publish decision:**\n- **What badges are**: badges are a virtual resource earned through activity on PowPow (posting, check-ins) — not money, no card attached.\n- **What it costs**: **2 badges per creation, non-refundable**; the finished digital human **expires after 30 days**.\n- Run the balance check first (the script does it automatically and aborts if insufficient), then put the cost into the \"Next step\" block and wait for an explicit confirmation.\n\nCollect from the user:\n1. **Name + persona** (both required): who they are, how they talk — 2~4 sentences of persona is enough; the platform's native LLM handles the chatting.\n2. **Location** (required): a place meaningful to the character — reuse Step 3's resolution flow and show candidates for the user to pick. Never guess.\n3. **Avatar**, one of three (show the options):\n   - **AI-generated** (the norm): needs a reference image URL (`--avatar-ref`). The platform generates the avatar from persona + reference (8~30 s). A local reference photo works too — upload it to `/api/upload/post-image` first, then feed the public URL to `--avatar-ref`.\n   - **Local upload** (`--avatar @<path>`): use the user's own image directly.\n   - **Direct URL** (`--avatar <url>`): a direct image URL.\n\n```bash\nnode scripts/create-digital-human.js \\\n  --name 史铁生 \\\n  --desc-file persona.txt \\            # or --desc \"...\"\n  --avatar-ref https://...jpg \\        # or --avatar @./photo.jpg, or --avatar <url>\n  --lng 116.408195 --lat 39.952372     # from the resolution result the user picked\n```\n\nAfter creation, report the digital-human ID, location, **expiry date (30 days)**, and badge balance, and link the map: https://global.powpow.online/map . The user can immediately @ it in a post (`compose.js --dh <id>`), and it becomes a related candidate for `match-digital-human.js`.\n\nTo look up existing ones: `node scripts/list-digital-humans.js --search \"<name>\"`.\n\n## Error Handling\n\n**Hard rule: never throw raw script output, node commands, or error codes (e.g. DEVICE_MISMATCH) at the user; translate every error into one human sentence + the next action.**\n\n- **401 / session expired (mid-flow)**: it means \"the last login expired\", not \"never logged in\" — the two are completely different to the user, and treating expiry as first login makes them think they're starting over. Say \"Your previous login has expired — share your account once more and we'll pick up right where we left off\", rerun `login.js`, retry once. **Everything done so far is kept**: drafts, material, chosen digital human and location stay local — resume from where you were, never rerun earlier steps.\n- **429 rate limit**: the server allows 10 posts/hour/user. Tell the user the wait time; don't hammer retries.\n- **400 CONTENT_BLOCKED**: the content failed moderation. Tell the user and ask how they'd like to change it.\n- **403 pending_payment**: account not fully activated. This is a hard gate on every login-required call (digital humans, geocoding, image upload, publishing all blocked), with no degraded mode — it can also pop up mid-flow after a successful login. Have the user log in on the platform and finish activation (register: https://global.powpow.online/register); don't retry, don't treat it as transient.\n- **423 account locked**: too many failed logins; wait 30 minutes.\n- **Wrong credentials (401 at login)**: have the user re-enter credentials, or point to https://global.powpow.online/login (locks for 30 minutes after 5 failures).\n- **Local image missing at publish time**: the file was moved/deleted after assembly. publish.js aborts (nothing is sent); rerun compose.js with a valid path.\n- **Image search unavailable**: continue without images, or ask the user for a direct URL.\n- **Insufficient badges (digital-human creation)** (the script aborts before charging): tell the user the balance; badges are earned through platform activity (posting, check-ins). Don't retry on the same account.\n\n## Support\n\n- Email: dongtao@outlook.com\n- Platform: https://global.powpow.online\n- Register: https://global.powpow.online/register\n- What is PowPow (intro video, in Chinese): https://www.bilibili.com/video/BV1Wu826UEVz/\n\n## Notes\n\n- Always verify the post actually went live after publishing; delete test posts afterwards.\n- Never publish without the user's explicit confirmation.\n- Never store/log user passwords; only the JWT session is cached locally.\n- **Password handling (internal rule — don't explain to the user)**: use the stdin pipe — `printf '%s' '<password>' | node scripts/login.js <username>`. Never the positional form (`login.js <user> <password>`): it lingers in shell history and process lists. No echoing, no disk writes, no shell variables. `login.js` still accepts the positional form for compatibility — that is not permission to use it. Asking the user for credentials should be one plain message, without security lecturing (see Conversation UX → Tone).\n- **Path rule**: only pass paths the user explicitly gave for `--text-file`/`--image`, or files you created in the current working directory. Never scan the machine, never guess paths — a wrong path publishes private files.\n- Never guess location/time from photos — ask the user; a wrong guess is a factual error in a first-person post.\n- A provided location always goes on the public map (internal rule, no opt-out; see Step 3).\n- **Never name the map provider to the user** — say \"let me look up that place for you\". Applies to prose, option lists, and any rewriting of script output (see Conversation UX → provider-neutral naming).\n- **Never ask the user for coordinates.** Fallback order: rephrase → widen candidates → publish without location.\n- This skill is safe to distribute: it contains no platform credentials; dangerous capabilities (identity, rate limits, moderation) are all enforced server-side.\n\nFile v1.0.5:README.md\n\n# powpow-publisher — Publish posts & digital humans to the public map\n\nPowPow assistant (OpenClaw): turn your photos or raw material into a\ntravelogue and publish it to PowPow, or turn a person/character into a chat-capable\ndigital human pinned to the public map.\n\nThis package carries the content of\n[powpow-simple-en](https://clawhub.ai/durenzidu/skills/powpow-simple-en) v1.0.0\n(the English edition of [powpow-simple](https://clawhub.ai/durenzidu/skills/powpow-simple),\nfeature parity with v5.7.3; scripts are byte-identical).\n\n## Install\n\n```\nopenclaw skills install @durenzidu/powpow-publisher\n```\n\n## Requirements\n\n- Node.js 18+ (scripts use global `fetch`, no third-party dependencies)\n- A PowPow account: https://global.powpow.online/register\n\n## State directory\n\nThe skill bundle stays read-only. Login tokens and user config live in a state directory:\n\n- Default: `~/.powpow/`\n- Override with the `POWPOW_STATE_DIR` environment variable\n\nTo customize the platform URL or the Unsplash key, drop a `config.json` into the state\ndirectory — it overrides the bundled defaults.\n\n## Usage examples\n\n```\ncd <skill-dir>\nnode scripts/doctor.js                                   # self-check + session probe\nnode scripts/login.js <username>                         # login (password via stdin, never stored)\nnode scripts/geocode.js \"地坛公园\" --limit 5              # place name -> coordinates\nnode scripts/match-digital-human.js \"读书 作家\" --limit 3  # match digital humans\nnode scripts/compose.js --text-file post.txt --loc \"地坛公园\" --lng 116.408195 --lat 39.952372 --image @/abs/path/cover.jpg --out post-draft.html\nnode scripts/publish.js post-draft.html                  # publish\nnode scripts/verify.js <post-id>                         # verify after publishing\nnode scripts/delete-post.js <post-id>                    # delete (test cleanup)\n```\n\nLocal paths passed to `--image` **must be absolute**.\n\nNote: the platform's place search and digital-human library are currently strongest\nin Chinese; non-Chinese users can paste local-language names and topics.\n\n## Security\n\n- No platform secrets bundled; every operation goes through the platform's public API + the user's own JWT.\n- The password is used once at login and never stored; tokens expire after ~7 days.\n- See `references/security.md` for details.\n\n## Notes for reviewers (SkillSpector / manual review)\n\n- This is an **instruction-style skill**: SKILL.md orchestrates a multi-script\n  pipeline. Every script under `scripts/` is one documented step of the\n  publishing workflow (login → self-check → geocode → match → compose →\n  publish → verify), not an independent hidden tool.\n- `delete-post.js` deletes only the logged-in user's own post\n  (`DELETE /api/posts/{id}`, JWT-scoped, enforced server-side) and is\n  documented in SKILL.md Step 8 for test cleanup. This capability is also\n  declared in the skill description.\n- No secrets are bundled. `config.json` ships defaults only. The session JWT\n  is written to the user's state directory (`POWPOW_STATE_DIR`, default\n  `~/.powpow/session.json`), never inside the skill bundle. Passwords are\n  never persisted.\n- All environment variables used (`POWPOW_STATE_DIR`, `POWPOW_PASSWORD`,\n  `UNSPLASH_ACCESS_KEY`) are optional and declared in SKILL.md frontmatter\n  (`metadata.openclaw.envVars`).\n- This package carries the content of `powpow-simple-en` v1.0.0 (English\n  edition of `powpow-simple` v5.7.3); scripts are byte-identical to those\n  packages (only the display name, install command, and this note differ).\n\n## License\n\nMIT-0 (ClawHub-wide license).\n\n## Links\n\n- Website: https://global.powpow.online\n- Map: https://global.powpow.online/map\n- English edition: https://clawhub.ai/durenzidu/skills/powpow-simple-en\n- Chinese edition: https://clawhub.ai/durenzidu/skills/powpow-simple\n\nFile v1.0.5:_meta.json\n\n{\n  \"ownerId\": \"kn7cqvkwtq6fbamzq5yz9g1smx818bfx\",\n  \"slug\": \"powpow-publisher\",\n  \"version\": \"1.0.5\",\n  \"publishedAt\": 1790304479773\n}\n\nFile v1.0.5:references/changelog.md\n\n# Changelog — powpow-publisher\n\nVersion history (no need to read at runtime; for maintenance/troubleshooting only).\n\n## 1.0.5 — 2026-09-25\n\nRenamed from powpow-openclaw-test to powpow-publisher. No functional changes.\n\n- ClawHub keeps the old slug as a redirect: old links and `@durenzidu/powpow-openclaw-test` references keep resolving to this skill.\n- SKILL.md frontmatter `name` fixed: `powpow_openclaw` (underscore violation) → `powpow-publisher`; internal headings and the README install command updated to match.\n\n## 1.0.4 — 2026-09-25 (retroactive entry)\n\nStore listing metadata refresh, published via CLI parameters (package file changes limited to the frontmatter version bump).\n\n- Display name set to \"PowPow Publisher — turn photos into travelogues, pin them to the map, create chat-capable digital humans\"; 5 English topics and categories (communication/lifestyle/creative) added.\n- Entry added retroactively to the local changelog (was omitted at release time).\n\n## 1.0.3 — 2026-09-23\n\nContent replaced: the old v1.0.2 command-style simulator (register /\ncreateDigitalHuman / send / status, in-memory) is replaced by the full\ninstruction-style skill carried by `powpow-simple-en` v1.0.0 (English edition\nof `powpow-simple` v5.7.3). `scripts/` are byte-identical to those packages;\nonly display name, install command, and this changelog differ.\n\n- SKILL.md, README.md and `references/` taken from `powpow-simple-en` v1.0.0.\n- Display name kept as `powpow_openclaw`; routable slug stays\n  `powpow-openclaw-test`.\n- Version bumped 1.0.2 → 1.0.3 (next registry patch).\n\n## 1.0.0 — 2026-09-20\n\nFirst release of the English edition. Forked from `powpow-simple` v5.7.3 with\nfeature parity; `scripts/` are byte-identical (SHA256-verified). Documentation\nand metadata only — zero script changes.\n\n- **Display name set to a functional title**: \"PowPow Simple EN - Publish\n  posts & digital humans to the public map\" (mirroring the Chinese package's\n  descriptive display name; the routable slug stays `powpow-simple-en`).\n\n- **SKILL.md fully translated to English**: triggers (English + Chinese\n  phrases), conversation UX (tone table, opening templates, \"next step\" block),\n  the full 8-step workflow, digital-human creation, error handling, and all\n  hard rules.\n- **Language policy added (hard rule)**: the conversation always follows the\n  user's language — the English instructions serve users in any language.\n- **Placeholders documented exactly as the script accepts them**: `{{dh}}` /\n  `{{数字人}}`, `{{loc}}` / `{{位置}}`, `{{img}}` / `{{图}}` (the Chinese\n  aliases are literal tokens recognized by compose.js — never invent new ones).\n- **Honest localization notes added**: the platform's place search and\n  digital-human library are currently strongest in Chinese; the preview page\n  banner and the product UI shown in screenshots are Chinese-language.\n- **README.md translated**, including Notes for reviewers (reviewer-facing\n  context previously added in the Chinese package's 5.7.3).\n- **references/ translated**: security.md, file-structure.md, screenshots.md.\n\n## Inherited history (from powpow-simple-en / powpow-simple)\n\nThis package inherits all behavior of `powpow-simple-en` v1.0.0, itself the\nEnglish edition of `powpow-simple` (Chinese). Key milestones:\n\n- **5.7.3 — 2026-09-19**: audit-noise reduction (description capability list,\n  security.md rewrite, reviewer notes, session-path doc fixes). No script changes.\n- **5.7.2 — 2026-09-19**: removed the incorrect \"likes revive bubbles\" claim;\n  added product screenshots (6 images + jsdelivr mirror). No script changes.\n- **5.7.1 — 2026-09-18**: `POWPOW_PASSWORD` env var renamed (fixing a\n  long-standing \"PowWow\" typo).\n- **5.7.0 — 2026-09-18**, **5.6.0 / 5.5.0 — 2026-09-18**, **5.4.1 / 5.4 — 2026-09-17**,\n  **5.3 — 2026-09-17**: earlier evolution of the multi-script pipeline\n  (login/session handling, compose placeholder hard-fail semantics, geocode\n  degradation flow, digital-human creation, health filtering). Full details:\n  the Chinese package's `references/changelog.md`.\n\nFile v1.0.5:references/file-structure.md\n\n# File Structure\n\n> For maintenance/troubleshooting only. SKILL.md runtime does not require reading this.\n\n```\npowpow-publisher/\n├── SKILL.md                          # main file (kept lean; details in references/)\n├── config.json                       # platformUrl + optional user's Unsplash key (NO secrets; read-only)\n├── scripts/\n│   ├── doctor.js                     # first-run self-check (node/config/network/session)\n│   ├── login.js                      # username/password → JWT (password never stored, hidden prompt)\n│   ├── compose.js                    # ONE-COMMAND post HTML assembly (the only supported way; fully local)\n│   ├── publish.js                    # upload local images + POST /api/posts as the logged-in user\n│   ├── verify.js                     # verify post by ID via platform API\n│   ├── delete-post.js                # delete own post (test cleanup)\n│   ├── geocode.js                    # place name → WGS-84 candidates (user confirms)\n│   ├── list-digital-humans.js        # list/search with health filtering\n│   ├── match-digital-human.js        # topic → ranked healthy candidates\n│   ├── create-digital-human.js       # create a map digital human (2 badges, 30-day expiry; avatar: generate/upload/URL)\n│   ├── search-image.js               # Unsplash (user's own key, optional; also resolves photo-page links)\n│   ├── upload-image.js               # local image → platform storage → public URL (used by publish.js)\n│   └── lib/\n│       ├── api-client.js             # platform API client (JWT, pinned headers)\n│       ├── dh-match.js               # shared DH health filter + topic ranking (name 3x, desc 1x, discipline-term masking)\n│       ├── coord-transform.js        # GCJ-02 ↔ WGS-84 conversion (iterative inverse)\n│       └── html-formatter.js         # HTML components + CJK-aware word count\n└── references/\n    ├── changelog.md                  # version history (this package: 1.0.3; inherited: 5.3 → 5.7.3)\n    ├── security.md                   # security architecture + platform API list\n    ├── screenshots.md                # product screenshot inventory + external links\n    └── file-structure.md             # this file\n```\n\nNote: the login session (`session.json`) is **never written into the skill bundle**;\nit lives in the user's state directory (`POWPOW_STATE_DIR`, default `~/.powpow/`)\nand expires after ~7 days (the skill keeps a 6-day re-login margin).\n\nScripts in this package are byte-identical to `powpow-simple-en` v1.0.0 and\n`powpow-simple` v5.7.3 — only documentation and metadata differ.\n\nFile v1.0.5:references/screenshots.md\n\n# Product Screenshots — powpow-publisher\n\nUsed at runtime, on demand. When the user has no concept of PowPow, or needs to \"see\"\nwhat the finished result looks like, send the corresponding link directly.\n\n## Link rules\n\n- Primary (raw):\n  `https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-0N.jpg`\n- Mirror (use when raw is unreachable, `N` = 1–6):\n  `https://cdn.jsdelivr.net/gh/durenzidu/durenzidu@main/screenshots/powpow-0N.jpg`\n- The images live in a public repo and can be sent as-is; **do not** download them\n  into the skill directory, and never send local paths as images.\n- Send only the 1–3 screenshots relevant to the current topic.\n\n## Inventory\n\nNote: the screenshots show the product's Chinese UI. When sending them to a\nnon-Chinese user, briefly say in the user's language what each screen shows.\n\n### powpow-01.jpg — Digital-human detail page\n\n![Digital-human detail page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-01.jpg)\n\nThe card for a real/historical person turned into a digital human: online status,\nlocation (e.g. a residence address), bio, \"Start conversation\" / \"I was here\" /\n\"Favorite\" / \"Share\" actions, and the publisher.\n**Use when**: the user asks \"what does a digital human look like\" / \"what can it do\nonce created\" / \"how do I chat with it\".\n\n### powpow-02.jpg — Post editor page\n\n![Post editor page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-02.jpg)\n\nThe editor for writing posts/travelogues, with a cluster of floating buttons in the\nbottom-right corner (locate, bubble, card, image, etc.).\n**Use when**: the user asks \"where do I write\" / \"can I edit it myself\" / \"how do I\npost from my phone\".\n\n### powpow-03.jpg — Map page (bubbles + digital humans)\n\n![Map page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-03.jpg)\n\nThe public map at a glance: posts with locations cluster on the map as \"bubbles\"\n(numbers are cluster counts), digital humans are pinned by avatar at their real-world\nspots; search and an \"All / Bubbles / Digital humans\" filter sit at the top.\n**Use when**: explaining \"where will people see my post\" / \"what are the bubbles on\nthe map\" — also the first-choice image for the opening \"what does the result look\nlike\".\n\n### powpow-04.jpg — Profile page (digital assets)\n\n![Profile page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-04.jpg)\n\nThe user's own page: friends/followers/fans, plus \"Digital assets\" — **badge balance**\n(3 in the shot), **my bubbles**, **my digital humans** (manage published ones), with\nthe note \"2 badges publish 1 digital human, valid for 30 days\".\n**Use when**: the user asks \"what are badges, what do they do\" / \"how many badges do\nI have left\" / \"what have I posted\".\n\n### powpow-05.jpg — Story-progress page\n\n![Story-progress page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-05.jpg)\n\nThe in-platform story mode (example: \"Yongle · 1421\"): chapter progress, badges\nearned, a character portrait generated from the user's avatar, \"Start roleplay\".\n**Use when**: the user asks \"what else can I do on PowPow besides posting\" / \"how\nelse can I earn badges\". Note: this is platform gameplay, not part of this skill's\nflow.\n\n### powpow-06.jpg — Post detail page (finished example)\n\n![Post detail page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-06.jpg)\n\nWhat a published travelogue looks like: author, location tag (\"Ditan Park 26.9km\"),\ntime, body text, photos, and like/comment/share/favorite actions at the bottom.\n**Use when**: the user asks \"what does the published result look like\" / \"long or\nshort\" / \"can it carry images\".\n\n## Quick scenario lookup\n\n| What the user says | Which screenshot |\n|---|---|\n| \"What is PowPow?\" / \"What does the result look like?\" | powpow-03 + powpow-06 |\n| \"What is a digital human like?\" | powpow-01 |\n| \"What can badges do?\" | powpow-04 |\n| \"How do I post / where's the editor?\" | powpow-02 |\n| \"What else can I do besides posting?\" | powpow-05 |\n\nFile v1.0.5:references/security.md\n\n# Security Architecture & Platform API\n\n> For maintenance/troubleshooting only. SKILL.md runtime does not require reading this.\n\n## Why this skill is safe to distribute\n\nThis skill holds **no platform secrets**:\n\n| Item | Guarantee |\n|---|---|\n| Platform secrets | None bundled — every operation goes through the platform's own HTTP API |\n| Identity | JWT from a real login; you can only post as yourself |\n| User password | Never stored; used only in memory, once, to obtain a short-lived (~7-day) token |\n| Rate limits / moderation | Enforced server-side (10 posts/hr/user, content moderation) |\n| Unsplash key | User-supplied, optional (images are optional) |\n\nThe skill never reads, writes, or ships any credential files. The only state it\nwrites is the user's own session token, stored in the user's state directory\n(see below), never inside the skill bundle.\n\n`config.json` contains ONLY `{ \"platformUrl\", \"unsplashAccessKey\"(optional),\n\"sessionMaxAgeDays\", \"skillVersion\" }`. Never add Supabase keys, service roles,\nor admin tokens to this skill.\n\n### Files that must never leave the user's machine\n- `<POWPOW_STATE_DIR>/session.json` (default `~/.powpow/session.json`) — the\n  logged-in user's JWT (auto-expires ~7 days). Written to the user's state\n  directory, never into the (possibly read-only) skill bundle.\n\n## Platform API endpoints used\n\n- `POST /api/auth/login` — `{step:1, username, password}` → JWT (cookie `powpow_token`)\n- `GET  /api/digital-humans?scope=all&search=...` — list/search digital humans (login required)\n- `GET  /api/amap/place/text?keywords=...` — place-name search (login required;\n  returns GCJ-02, converted to WGS-84 client-side by geocode.js). The endpoint\n  path is platform-internal and must not be surfaced to the user; in internal\n  discussion refer to it neutrally as the place-lookup service (never name it\n  to the user).\n- `POST /api/upload/post-image` — local image upload (multipart `file`; login required)\n- `POST /api/posts` — create post (login required; server enforces limits & moderation)\n- `GET  /api/posts/{id}` — verify a post\n- `DELETE /api/posts/{id}` — delete own post (test cleanup)\n- `POST /api/digital-humans` — create a digital human (login required;\n  consumes 2 badges, expires after 30 days)\n- `POST /api/digital-humans/generate-avatar` — AI avatar from the persona\n  (login required; 8~30s; needs a reference image URL by platform design)\n- `GET  /api/badges/balance?userId=...` — badge balance pre-check\n\nNote: the JWT is bound to a device fingerprint (User-Agent + Accept-Language). The API client pins these headers at login and reuses them automatically. If you see `DEVICE_MISMATCH` or 401, just re-login.\n\nFile v1.0.5:skill-card.md\n\n## Description:\n\nHelps users turn photos into travelogues, publish them to PowPow, and create chat-capable digital humans pinned to its public map.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[durenzidu](https://clawhub.ai/user/durenzidu)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nPowPow account holders use this skill to draft and publish photo-based travel stories and create map-pinned digital humans. It can help select locations and images, preview posts, and verify published results.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: A reusable session token can authorize publishing, image uploads, digital-human creation, and post deletion without an enforced confirmation gate.\n\nMitigation: Require a visible preview and explicit user approval before publishing, creating a digital human, or deleting a post.\n\nRisk: An agent handling account credentials and a stored login token could expose the account if local state is accessible to others.\n\nMitigation: Confirm the state configuration points to the official PowPow site and protect or delete ~/.powpow/session.json on shared machines.\n\nRisk: Publishing a post or digital human may expose photos and locations publicly; digital-human creation consumes badges.\n\nMitigation: Review the intended content, images, map location, and badge cost with the user before approval.\n\n## Reference(s):\n\n- [PowPow Publisher on ClawHub](https://clawhub.ai/durenzidu/skills/powpow-publisher)\n- [PowPow website](https://global.powpow.online)\n- [PowPow public map](https://global.powpow.online/map)\n- [Skill security notes](artifact/references/security.md)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Shell commands, Guidance]\n\n**Output Format:** [Conversational text and Markdown drafts, previews, and publication links]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Can publish public posts and map-pinned digital humans; reports publication results and links.]\n\n## Skill Version(s):\n\n1.0.5 (source: frontmatter, ClawHub release, changelog)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.5:config.json\n\n{\n  \"platformUrl\": \"https://global.powpow.online\",\n  \"unsplashAccessKey\": \"\",\n  \"sessionMaxAgeDays\": 6,\n  \"skillVersion\": \"1.0.3\"\n}\n\nArchive v1.0.4: 25 files, 63937 bytes\n\nFiles: config.json (132b), README.md (3845b), references/changelog.md (3244b), references/file-structure.md (2762b), references/screenshots.md (4127b), references/security.md (2707b), scripts/compose.js (18328b), scripts/create-digital-human.js (9742b), scripts/delete-post.js (1037b), scripts/doctor.js (4980b), scripts/geocode.js (3699b), scripts/lib/api-client.js (7194b), scripts/lib/coord-transform.js (2377b), scripts/lib/dh-match.js (3273b), scripts/lib/html-formatter.js (8335b), scripts/list-digital-humans.js (3331b), scripts/login.js (3169b), scripts/match-digital-human.js (2429b), scripts/publish.js (7320b), scripts/search-image.js (9692b), scripts/upload-image.js (3998b), scripts/verify.js (3218b), skill-card.md (2037b), SKILL.md (37551b), _meta.json (135b)\n\nFile v1.0.4:SKILL.md\n\n---\nname: \"powpow_openclaw\"\ndescription: Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my travel photos into a travelogue on PowPow\", \"post these photos to PowPow\", \"发一篇 PowPow 帖子\", \"把这次旅行的照片发到泡泡\"; also triggers when the user wants to create/publish a digital human onto the map, e.g. \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\". Requires a PowPow account (register first if none). Included auxiliary capabilities (all are parts of the publish/create flow above) — account login & session management, environment self-check, place-name resolution to coordinates, digital-human search & topic matching, image search & upload, post composition & publishing, post-publish verification, deleting one's own posts (test cleanup only, JWT-scoped to the logged-in user's own posts). Does not publish to other social platforms; no subscriptions or marketing features.\nversion: 1.0.4\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n    emoji: \"\\U0001FAE7\"\n    homepage: https://global.powpow.online\n    envVars:\n      - name: POWPOW_STATE_DIR\n        required: false\n        description: Directory for session and config state. Defaults to ~/.powpow. The skill bundle stays read-only; login state is written here.\n      - name: POWPOW_PASSWORD\n        required: false\n        description: Optional. Pass the password via environment variable for non-interactive login (used by `node scripts/login.js <username>` on first login). If unset, login prompts interactively; the password is never persisted.\n      - name: UNSPLASH_ACCESS_KEY\n        required: false\n        description: Optional Unsplash Access Key, used only for keyword-based automatic image search. If unset, fall back to local images or direct URLs.\n---\n\n# powpow_openclaw — Publish posts & digital humans to the public map\n\nTurn your photos or raw material into a travelogue and publish it to PowPow on your behalf; or turn a person/character into a chat-capable digital human pinned to the public map.\n\n**Version 1.0.3** · 2026-09-23 (history: `references/changelog.md`; security architecture & API list: `references/security.md`; file structure: `references/file-structure.md`; product screenshots: `references/screenshots.md`)\n\n**Language policy (hard rule)**: Always converse in the user's language. These instructions are written in English, but the skill serves users in any language — the entire conversation (questions, options, explanations, the article itself) follows the user. Chinese trigger phrases are recognized too.\n\n## Runtime Environment (OpenClaw)\n\nThis is an **instruction-style skill**: SKILL.md describes the flow, and `scripts/*.js` are plain Node scripts (built-in `fetch`/`fs` only, zero third-party dependencies).\n\n- **Requires Node.js 18+** (scripts use global `fetch`). Verify first: `node -v`\n- **Scripts live under `scripts/` in this skill's directory.** Before running, change to the skill root, e.g.:\n  `cd <this-skill-dir> && node scripts/doctor.js`\n- **State directory**: the login token is written to `POWPOW_STATE_DIR` (default `~/.powpow/session.json`). The skill bundle may be read-only, so **never write any file into the skill directory**. Users can customize the location via that environment variable.\n- **Always pass absolute paths** for images to `--image`; relative paths fail to resolve.\n\n## Product Screenshots (let users *see* PowPow)\n\nMost users have never seen what PowPow looks like. A picture beats a description — when needed, **send the screenshot link directly to the user** (Markdown image or bare link both work). Full list, descriptions, and when to use each: `references/screenshots.md`. Quick mapping:\n\n- User has no concept of the product / opening \"what does the result look like\" → map page + post detail page\n- User asks \"what is a digital human\" / \"how do I chat with it\" → digital-human detail page\n- User asks \"what can badges do\" / \"what do I have\" → profile page (digital assets)\n- User asks \"how does PowPow work\" → map page (bubbles cluster on the map)\n\nRules:\n- Screenshots are hosted in the public repo `durenzidu/durenzidu` under `screenshots/` (`powpow-0N.jpg`). **Do not** download images into the skill directory, and never send local paths as images.\n- If `raw.githubusercontent.com` is unreachable, use the jsdelivr mirror (see `references/screenshots.md`).\n- Send only the 1–3 screenshots relevant to the current topic — never dump all six at once.\n- If it's not clear which one applies, don't guess — pick against the descriptions in `references/screenshots.md`.\n\n## Trigger Conditions\n\nThis skill triggers when the user wants to publish travel content (travelogue/photos/trip) to PowPow, or wants to create a digital human pinned to the map. Typical phrasings:\n\n- Travelogue/photos: \"turn my travel photos into a travelogue on PowPow\", \"post a trip story to powpow\", \"post these photos to PowPow\", \"把这次旅行的照片发到泡泡\"\n- Platform-command style: \"publish a PowPow post\", \"post to PowPow feed\", \"powpow 发帖\"\n- Digital human: \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\", \"创建一个数字人\"\n\n**Co-occurrence rule (prevent false triggers)**: mentioning travel/photos/travelogue alone is not enough — the user must also express publish intent (post/publish/upload/发/发布) or name the platform (PowPow/泡泡/powpow). \"This photo is beautiful\" does not trigger; \"post this photo to PowPow\" does.\n\nDoes not trigger for generic social media or other platforms.\n\n## Conversation UX (first-time users)\n\nMany users have never published anything through a chat assistant. Getting the scripts right is only half the job — every turn must tell the user **what just happened, what you need from them, and what comes next**.\n\n### Tone — applies to every turn, from first sentence to last\n\nThis is a product for ordinary users. You are **serving** the user, not **instructing** them. Keep the tone gentle and respectful, consistently.\n\n| Don't say (lecturing / talking down) | Say instead (serving / side-by-side / inviting) |\n|---|---|\n| First, let me be clear about one thing: … | There's one thing I'd like to explain first: … |\n| A reminder: … / I have to remind you that … | There's one thing I'd like you to double-check with me |\n| There are two things I can't guess — you have to tell me | I can't see when or where a photo was taken, so I'll need you to tell me those two things |\n| I won't decide this for you / you need to know this | I'd rather not decide this one for you — it goes into the article, and the wrong pick would feel off |\n| Do you need to worry about coordinates? No. | Leave the coordinates to me — you just pick the place name |\n\n**Always:**\n1. **The user is the subject of the sentence.** \"You can…\" beats \"I need you to…\".\n2. **\"I\" am the service, not the lecturer.** Whatever can be done for the user (coordinate conversion, format assembly, validation, retries, failure fallbacks) — do it first, then report. Don't narrate the process as a knowledge lecture.\n3. **Every step carries forward momentum.** \"Now, let's start with step one\" feels like doing things together; \"please answer my question\" does not.\n\n**Explicitly forbidden**: no lecturing; no announcing \"I need to remind you\"; no judging the user's choices; never \"I have to…\"; when the user hasn't asked, don't explain internal mechanics, limits, or security design.\n\n**Provider-neutral naming (hard rule)**: **never mention the map provider's name to the user.** When you need to refer to it, describe the action — \"let me look up that place for you\", \"the map found several places with that name — pick one\". **Don't use jargon like \"map database\" either.** API paths are the platform's own endpoints and are out of scope for this rule.\n\n### Opening: introduce yourself, then ask the first question\n\nOn first trigger (doctor reports no session, or the user asks what you can do), open with a short paragraph (~120 words, don't pile up features), **branch by user intent**, then ask the first question:\n\n- **Post intent** → 4-part opening:\n  1. **What I can do**: turn your photos or material into a travelogue and publish it to PowPow for you.\n  2. **What the result looks like**: a post can carry digital-human tags (a red capsule — tap the avatar to jump into a chat), location tags (pin + place name — tap to open the map), and images; a post with a location appears on the public map as a \"bubble\" and slowly fades out over time — that's the core of how PowPow works. (Want them to see it right away? Send screenshots — see \"Product Screenshots\".)\n  3. **What's needed to start**: digital humans, locations, and publishing all require the platform, so step one is a one-time login with your PowPow account. No account yet? Register: https://global.powpow.online/register (To learn what PowPow is first, watch this intro video — in Chinese: https://www.bilibili.com/video/BV1Wu826UEVz/ )\n  4. **The flow (compressed to 3 beats)**: ① log in → ② you give me material, I write and format → ③ you confirm, only then I publish. Everything is editable until you confirm; nothing goes live without confirmation.\n- **Digital-human intent** → short opening: \"Give me a name + persona + location, and I'll turn them into a chat-capable digital human pinned to the public map. This costs 2 badges and expires after 30 days — confirm and I'll do it. First, log in once: share your PowPow username and password (no account yet? Register: https://global.powpow.online/register ).\"\n\nTemplate (use as a starting point, adapt to the user's language — don't parrot it into template-speak):\n\n> It's a pleasure to serve you — welcome to the world of PowPow.\n> Here, **you can** turn the photos you took today into a travelogue, and **I'll** write it, pin it to the map, and publish it to PowPow for you — when someone taps it on the map, they'll see the path you walked that day.\n> [Part 2: what the result looks like — capsules, images, the map-bubble mechanic (use the standard line above)]\n> [Part 3: what's needed — one login; include the register link + intro video if they may not have an account]\n> [Part 4: the 3-beat flow, login first]\n> Ready? **Hand me what you'd like to publish today, and we'll start with step one.**\n\n- Never ask for the password before the self-introduction — a stranger suddenly saying \"give me your password\" is what phishing looks like.\n- Ask for credentials once, in a single plain message, with the register link (add the intro video only if they may not have an account). Don't ask for the password, then the material, then spring more conditions.\n- **No password-security lectures.** Ask plainly and move on. You may add one neutral, non-alarming line: \"Your password is only used to obtain a temporary login token — I won't save it.\" Don't elaborate.\n\n### Every turn: narrate + guide\n\n- **One decision per turn.** Tightly related follow-ups (location + time) may share one message; don't bundle material, length, and location choice together.\n- **When asking for facts, say why**: we never read photo EXIF/location (privacy), so the location and time must come from the user.\n- **Never paste raw script output.** After each run, give the user one human-language summary: what happened, what it means, what's next. After login, e.g.: \"You're logged in with your account. Now send me your material: photos, a piece of writing, or just an idea — anything works.\"\n- **Announce stage transitions**: \"Material's all in — next I'll pick a few digital humans related to your topic for you to choose from.\" The user should always know which step of the flow they're on.\n- **Give a reason with every option** (digital humans, location candidates, length) — a bare numbered list is hard to choose from.\n- **Before publishing**, include one line noting the post will appear on the public map (as gameplay context, not as an opt-out).\n\n### Closing \"Next step\" block (mandatory every turn)\n\nIn long messages users lose the point — not because the content is wrong, but because the action gets buried. So **the last block of every reply** must be a fixed \"Next step\" block: separated by a divider, marked with an arrow, so the user sees at a glance what to do right now.\n\nFixed format:\n```\n────────────────────\n👉 Your move: reply 1 / 2 / 3 to pick a location\n   (Once you pick, I'll draft the post — next turn you'll see the preview with capsules)\n```\nThree hard rules:\n1. **Only one action per block.** Two todos → split into two turns, or demote one to a plain FYI.\n2. **All options go into this block.** Don't make the user scroll back into the body to find \"reply 1–6\".\n3. **Add one \"once that's done, I'll…\" line**, so the user knows where the action leads.\n\nPublish results follow the same rule (see Step 8): link and location go at the very end of the message, closest to where the eye lands.\n\n## Prerequisites\n\n- **Node.js 18+ required** (scripts use global `fetch`).\n- **PowPow account required**: register at https://global.powpow.online/register\n\n### First-Time Setup (per user)\n\n0. Run the self-check first, and fix whatever it reports. It **silently probes the stored session against the server** — if the session is valid, skip login and jump straight into the Workflow:\n   ```bash\n   node scripts/doctor.js\n   ```\n1. If doctor reports no valid session and this is first contact, deliver the **Conversation UX opening** above, then ask for credentials in **one plain message**:\n   \"Next I need a one-time login: please share your PowPow username and password. No account yet? Register first: https://global.powpow.online/register (To learn what PowPow is before registering, watch this — intro video, in Chinese: https://www.bilibili.com/video/BV1Wu826UEVz/ )\"\n   Then stop. No security lecturing about the credentials themselves (see Tone). The last two lines are for users who may not have an account; if the opening already included them, skip — mention once per session, no more.\n2. Run login:\n   ```bash\n   printf '%s' '<password>' | node scripts/login.js <username>\n   ```\n   - **stdin only, never positional arguments** — the positional form lingers in shell history and process lists, a real exposure.\n   - **Windows** (PowerShell/cmd have no `printf`): you can run\n     ```bash\n     node scripts/login.js <username>\n     ```\n     then type the password at the prompt (the script reads it hidden, no echo). Either way, **never put the password in a command-line argument**.\n   - Never echo the password, never write it to disk, never keep it in a shell variable.\n   - The login token lands in the state directory (`POWPOW_STATE_DIR`, default `~/.powpow/session.json`), not inside the skill directory.\n   - This is an internal handling rule — don't explain it to the user.\n3. **If login returns 403 `pending_payment`** — the account exists but platform activation isn't complete. Stop, don't retry, say: \"Your account isn't fully activated yet, so publishing isn't possible right now. Please open PowPow, log in, and follow the on-page instructions to finish activation; tell me when it's done and we'll continue.\" This is neither a transient error nor a login problem — don't make the user re-enter credentials, and don't keep collecting material.\n\n### Configuration\n\n`config.json` (ships defaults, no secrets; read-only):\n\n```json\n{\n  \"platformUrl\": \"https://global.powpow.online\",\n  \"unsplashAccessKey\": \"\",\n  \"sessionMaxAgeDays\": 6\n}\n```\n`unsplashAccessKey` is optional, user-supplied; without it, skip image search (image-less posts are fully supported) or use direct URLs from the user.\n\n**Overriding config under OpenClaw**: don't modify the bundled `config.json` (may be read-only). Put a `config.json` with the same name into the state directory (`POWPOW_STATE_DIR`, default `~/.powpow/config.json`) — its values override the bundled defaults. Or set the `UNSPLASH_ACCESS_KEY` environment variable.\n\n## Workflow\n\n> **Execution convention (OpenClaw)**: all `node scripts/...` commands below assume the current directory is this skill's root. If unsure, run with absolute paths: `node <skill-dir>/scripts/xxx.js`. Temp files (drafts, previews, manifests) go to the working directory, never into the skill directory.\n\n### Step 1: Content gathering (photos → travelogue)\n\nThe flagship flow: the user sends photos, you write a first-person travelogue around them.\n\n1. **Look at the photos** (you can see images). Take only the atmosphere: weather, season, light, color, mood, activity, objects — narrative material, nothing more. **Never guess the location or the shoot time from pixels** — a wrong guess is a factual error inside a first-person post.\n2. **Ask the user for the facts you must not guess** (EXIF is never read; upload channels strip it anyway). Give the reason, then ask:\n   - Location: \"To protect your privacy, I don't read the location data inside your photos — so let me ask: where was this taken?\"\n   - Time: \"Roughly when was it taken?\"\n3. **Offer writing styles** (before asking about length). List 4–5, one feel per style, e.g.:\n   \"What writing style would you like? a. Lyrical & detailed — light, mood, breathing sentences b. Easy & everyday — like chatting with a friend c. Witty & self-deprecating — good for travel-fail stories d. Deep & cultural — history and geography lore e. Minimal & spare — short sentences, white space\"\n   **Always add the imitation option** (the strongest personalization): \"You can also paste a snippet of something you've written — a tweet or a moment post — and I'll write in your own voice (I learn the voice only, never reuse the content).\" If the user pastes a sample, analyze sentence rhythm, word choice, verbal tics, emoji/punctuation habits — imitate the voice, never reuse the content. The sample stays in the chat and is stored nowhere.\n4. **Offer length options**: \"How long should the travelogue be? ~150 words / ~300 words / ~2000 words / your call (platform limit: 50,000 characters, images included)\"\n5. **Writing rules**:\n   - **First person = the user.** The user is the narrator; a digital human, if present, is someone being mentioned — never the narrator.\n   - **Use the chosen style** (or the imitated voice). If the user both picks a style and gives a sample, the sample wins — their own voice beats the menu.\n   - Weave time into the wording (early morning / dusk / after the first snow); never write a hard timestamp.\n   - Only include digital humans that genuinely fit — never force one in.\n   - Language follows the user's preference (any language works).\n\nOther inputs are fine too: **Mode A** user provides finished text — skip generation; **Mode C** partial input — you fill in. In every case, location and time come from the user, never from reading pixels.\n\n### Step 2: Digital-human matching\n\nList/suggest healthy digital humans:\n```bash\nnode scripts/match-digital-human.js \"<topic>\" --limit 3 --json   # ranked suggestions\nnode scripts/list-digital-humans.js \"<name>\" --json              # search by name\n```\nHealth filtering is built in: disabled, test-named, garbled, and placeholder-avatar digital humans are excluded automatically. Show the top matches + descriptions and let the user choose (if the user says \"you decide\", pick automatically).\nIf the digital human the user wants doesn't exist, give the creation page: https://global.powpow.online\nNote: the platform's digital-human library is currently mostly Chinese-language; matching works best when the topic is given in Chinese.\n\n### Step 3: Location handling\n\n**Location is strongly recommended, but optional.** It decides whether the post appears on the public map (the core gameplay).\n\n- **User gives a place name** (the norm): resolve it to coordinates, then let the user pick from same-name candidates:\n  ```bash\n  node scripts/geocode.js \"地坛公园\" --limit 5        # add --city 北京 to narrow\n  ```\n  The script queries the platform's places and locally converts each candidate from GCJ-02 to WGS-84, matching the web editor's coordinate system. Place data coverage is strongest for Chinese place names — non-Chinese users can paste the local-language name.\n  **To the user, describe only actions**: \"let me look up that place for you\", \"the map found several places with that name — pick one\" — never name the map provider, and never say \"map database\".\n  Show candidates (name/district/address/coordinates) each with a reason, and let the user choose; duplicate place names are common (Chaobai River Bridge has 90 hits). The user picks **by name**; coordinates are your job, not their problem.\n  Assembly: `--loc <name> --lng <x> --lat <y>`.\n- **Nothing found — degrade in this order, never make the user give coordinates:**\n  1. **Rephrase / narrow**: try different words, or add `--city <city>` (district, landmark, road name).\n  2. **Widen to help recognition**: raise `--limit` (e.g. 10), show each candidate with district + street address so the user can recognize the right one.\n  3. **Publish without a location** — the post is still valid (`isLocationExposed: false`). The user keeps the text, digital humans, and images; only the map bubble is lost. State this as the outcome, not as a failure.\n  Making an ordinary user supply latitude/longitude is never a legitimate fallback: they have no way to obtain it, and what they find is usually GCJ-02, which pins off by hundreds of meters — a visible factual error in a first-person post. Coordinates are an expert channel: use them only if the user offers.\n- **No location in the material or the conversation**: don't silently produce a location-less post. Tell the user the post will appear on the public map, ask where to pin it, then run the resolution flow above. This question is the only action of the turn — put it in the \"Next step\" block.\n- **User declines to give a location — accept it.** Publish without location (`isLocationExposed: false`). Never pressure, never block publishing — it's their choice.\n- User directly provides coordinates: use them as-is.\n- Otherwise use the chosen digital human's location (`locationName`, `lng`, `lat`).\n\n**A provided location always goes on the public map (`isLocationExposed: true`) — this is an internal rule; do not offer the user a \"hide from map\" option.** Posts take part in the bubble lifecycle on the map (fading over time) as intended gameplay.\n\nLocation component format:\n```html\n<span data-type=\"location\" data-lng=\"116.316\" data-lat=\"39.979\" name=\"中关村\">\n  <span class=\"location-name\">中关村</span>\n</span>\n```\n\n### Step 4: Image handling (optional)\n\nThree ways to attach images:\n- **Local files** (the norm): `--image @<local-path>`. **Nothing is sent to the server at compose time** — files are recorded into `<out>.manifest.json` and previewed locally via `file://`. Upload happens only at publish time in publish.js (`POST /api/upload/post-image`, JPEG/PNG/WebP/GIF/BMP/HEIC, no SVG, ≤10MB each, server-side compression). Editing drafts never touches the server and never creates orphan files.\n- **Direct URLs**: HEAD-check first, then embed. Unsplash photo-page links are recognized too (`unsplash.com/photos/...`, commonly copied from a browser) — automatically resolved to `images.unsplash.com` direct links. File-ID links (`unsplash.com/photos/1507513319174-...`) resolve locally without a key; slug/short-ID links require an Unsplash API key (the site's bot protection blocks keyless scraping). Without a key, skip the slug form and say so — ask the user for a direct link; never embed a page link as an image (it renders broken).\n- **Automatic image search** (only if an Unsplash key is configured): `--image search:<english keywords>` (or `node scripts/search-image.js \"<keywords>\"`)\n- No key, no URL, no file → publish without images. Never bundle an image API key.\n\n**No fixed image-count cap.** The platform's boundary is 50,000 characters (each image tag ≈100 chars); remind the user when approaching 200 images. Display: the feed grid shows the first 9 + \"+N\"; the full-screen viewer shows all.\n\n### Step 5: Content formatting (critical — read carefully)\n\nInteractive components (digital human / location) are rendered by the web frontend from **rich HTML** inside `content`. Writing just `<span data-type=\"digital-human\">Marie Curie</span>` renders as plain text — only a span carrying the tiptap editor's exact structure (Tailwind classes, inner avatar `<img>`, pin icon, `.location-name`) displays as a tappable capsule.\n\n**Mandatory: always use `scripts/compose.js` to assemble post HTML. Never hand-write component spans, never write glue code to bypass html-formatter.js.**\n\n```bash\n# 1. Save the post text to a file. Separate paragraphs with blank lines.\n#    Placeholders (all optional; any misuse is a hard error, never silently altered):\n#      {{dh}}  or {{数字人}}   -> digital-human capsule (requires --dh)\n#      {{loc}} or {{位置}}     -> location capsule (requires --loc)\n#      {{img}} or {{图}}       -> image paragraph; must be its own paragraph, consumes --image in order\n#                                (each one needs a spare --image, otherwise compose fails)\n#    Note: the Chinese aliases above are literal tokens recognized by the script —\n#    use either form, but never invent new tokens.\n#    Unknown/extra/inline placeholders make compose.js exit 1 and write nothing.\n#    Fix the text file and rerun — never delete text to force it through.\n\n# 2. Assemble in one command (fully local, no network):\nnode scripts/compose.js --text-file post.txt \\\n  --dh name:岳飞 \\                    # or --dh <id>, or --dh auto --topic \"...\"\n  --loc dh \\                          # or --loc <name> --lng 116.3 --lat 39.9\n  --image @./photos/west-lake.jpg \\   # local file (uploaded at publish); or <direct-url>, or search:<keywords>\n  --out post-draft.html\n```\n\ncompose.js enforced rules:\n- Capsules always match the editor's exact structure (validated before writing).\n- **Unknown/extra/misplaced placeholders are hard failures** (exit 1, nothing written). The old version only deleted the placeholder + printed a stdout warning — and since raw script output is never forwarded to the user, a broken sentence (\"we sat at for an entire afternoon\") could silently go live.\n- If `--dh`/`--loc` is given but the text has no placeholder, the capsule is auto-prepended to the first paragraph / appended to the last one.\n- Local images become `powpow-local://N` placeholders + `post-draft.html.manifest.json`; publish.js uploads them at publish time.\n- compose.js also writes `post-draft.html.preview.html` — a **Chinese-language** preview page the user opens in a browser. Its banner states whether the post will appear on the public map and, if so, where it's pinned — so the map consequence is visible on the page the user actually reads (the local map renders via `file://`). Tell the user in their own language what the banner says.\n- `publish.js` re-runs `validateEditorFormat()` and rejects anything else.\n- **Do not send `contentItems` in the publish payload** — the frontend prefers structured rendering when `contentItems` is present, and an incomplete list drops post text. `publish.js` handles this.\n- The server automatically takes the first `<img>` as the card thumbnail.\n- Note: for posts without block-level images, the server takes the digital-human capsule's inner avatar as the thumbnail (platform behavior, cosmetic).\n\n### Step 6: Pre-publish confirmation (mandatory)\n\nFirst show the four-item breakdown, then end the turn with a \"Next step\" block whose **only action** is the publish decision (publish / revise more / hold off):\n\n1. **Full post text** (with capsule positions marked)\n2. **Component list**: digital human (name + avatar), **location (name + coordinates)**, image count, and **the logged-in account** (taken from the session, not from user input)\n3. **Local preview file path** (`<out>.preview.html`) — Chinese-language preview; its banner also states whether the post goes on the public map. One line noting it's an approximation, not a pixel-perfect copy.\n4. Ask \"Anything to change? If it all looks right, reply 'publish'\", plus one line reminding that the post will appear on the public map as a bubble (PowPow gameplay, not a side effect).\n\nThe decision must be the last block — never buried after the breakdown. (Why so strict: in testing, users read exactly this content and replied \"and then?\" — the information was all there, but the action didn't land where the eye stops.)\n\n**Revision loop**: if the user wants changes, make them and rerun compose.js — fully local, any number of rounds with zero server contact and zero orphan uploads. No iteration cap; only an explicit 发布/publish/confirm from the user ends it. Until then, never publish.\n\n### Step 7: Publish\n\n```bash\nnode scripts/publish.js <html-file-path>\n```\nIf the draft contains local images, this is the moment they upload — one by one, before the post goes out. Progress is persisted after each success (HTML + manifest rewritten with real URLs), so a failed rerun doesn't re-upload what already succeeded.\nThe post is created as the logged-in user — identity comes from the JWT; there is no way to post as someone else. The server enforces content moderation and rate limits. On 429 the script honors `Retry-After` automatically.\n\n### Step 8: Post-publish verification (mandatory)\n\n```bash\nnode scripts/verify.js <post-id>\n```\nChecks post accessibility, author, digital-human/location components, image count, word count. Report in this order — **breakdown first, result last**:\n\n1. **Verification breakdown**: what was read back and what matched — author, digital-human capsule, location capsule, image count, word count. Quote verify.js's numbers as-is (it really counts images; a 3-image post reports 3).\n2. One line on the bubble lifecycle (standard phrasing): \"A post with a location appears on the public map as a 'bubble' and slowly fades out over time — that's the core of how PowPow works.\"\n3. **Result block, as the very last message line** — closest to the input box, where the eye lands. Must include the link **and the post's location**:\n\n```\n────────────────────\n✅ Post published\n📍 Pinned at: Yanjiao Xinggong Ruins (116.816667, 39.95)\n👉 Please open the link and check: do both capsules tap, is the image order right?\n   (The command line can't verify that — your eyes are the final gate)\n🔗 https://global.powpow.online/posts/<post-id>\n```\n\nIf there's no location, say so in the same block: \"📍 No location — this post won't appear on the public map\".\n\nInteractive rendering (capsule taps, image lightbox) can't be verified from the CLI — the user's eyes are the final gate.\n\nTest-post cleanup:\n```bash\nnode scripts/delete-post.js <post-id>\n```\n\n## Creating a Digital Human (publishing a digital human)\n\nA separate flow from posting: turn a person (historical figure, character, the user's own avatar…) into a chat-capable digital human pinned to the public map. Triggers: \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\".\n\n**Cost — confirm before running, just like the publish decision:**\n- **What badges are**: badges are a virtual resource earned through activity on PowPow (posting, check-ins) — not money, no card attached.\n- **What it costs**: **2 badges per creation, non-refundable**; the finished digital human **expires after 30 days**.\n- Run the balance check first (the script does it automatically and aborts if insufficient), then put the cost into the \"Next step\" block and wait for an explicit confirmation.\n\nCollect from the user:\n1. **Name + persona** (both required): who they are, how they talk — 2~4 sentences of persona is enough; the platform's native LLM handles the chatting.\n2. **Location** (required): a place meaningful to the character — reuse Step 3's resolution flow and show candidates for the user to pick. Never guess.\n3. **Avatar**, one of three (show the options):\n   - **AI-generated** (the norm): needs a reference image URL (`--avatar-ref`). The platform generates the avatar from persona + reference (8~30 s). A local reference photo works too — upload it to `/api/upload/post-image` first, then feed the public URL to `--avatar-ref`.\n   - **Local upload** (`--avatar @<path>`): use the user's own image directly.\n   - **Direct URL** (`--avatar <url>`): a direct image URL.\n\n```bash\nnode scripts/create-digital-human.js \\\n  --name 史铁生 \\\n  --desc-file persona.txt \\            # or --desc \"...\"\n  --avatar-ref https://...jpg \\        # or --avatar @./photo.jpg, or --avatar <url>\n  --lng 116.408195 --lat 39.952372     # from the resolution result the user picked\n```\n\nAfter creation, report the digital-human ID, location, **expiry date (30 days)**, and badge balance, and link the map: https://global.powpow.online/map . The user can immediately @ it in a post (`compose.js --dh <id>`), and it becomes a related candidate for `match-digital-human.js`.\n\nTo look up existing ones: `node scripts/list-digital-humans.js --search \"<name>\"`.\n\n## Error Handling\n\n**Hard rule: never throw raw script output, node commands, or error codes (e.g. DEVICE_MISMATCH) at the user; translate every error into one human sentence + the next action.**\n\n- **401 / session expired (mid-flow)**: it means \"the last login expired\", not \"never logged in\" — the two are completely different to the user, and treating expiry as first login makes them think they're starting over. Say \"Your previous login has expired — share your account once more and we'll pick up right where we left off\", rerun `login.js`, retry once. **Everything done so far is kept**: drafts, material, chosen digital human and location stay local — resume from where you were, never rerun earlier steps.\n- **429 rate limit**: the server allows 10 posts/hour/user. Tell the user the wait time; don't hammer retries.\n- **400 CONTENT_BLOCKED**: the content failed moderation. Tell the user and ask how they'd like to change it.\n- **403 pending_payment**: account not fully activated. This is a hard gate on every login-required call (digital humans, geocoding, image upload, publishing all blocked), with no degraded mode — it can also pop up mid-flow after a successful login. Have the user log in on the platform and finish activation (register: https://global.powpow.online/register); don't retry, don't treat it as transient.\n- **423 account locked**: too many failed logins; wait 30 minutes.\n- **Wrong credentials (401 at login)**: have the user re-enter credentials, or point to https://global.powpow.online/login (locks for 30 minutes after 5 failures).\n- **Local image missing at publish time**: the file was moved/deleted after assembly. publish.js aborts (nothing is sent); rerun compose.js with a valid path.\n- **Image search unavailable**: continue without images, or ask the user for a direct URL.\n- **Insufficient badges (digital-human creation)** (the script aborts before charging): tell the user the balance; badges are earned through platform activity (posting, check-ins). Don't retry on the same account.\n\n## Support\n\n- Email: dongtao@outlook.com\n- Platform: https://global.powpow.online\n- Register: https://global.powpow.online/register\n- What is PowPow (intro video, in Chinese): https://www.bilibili.com/video/BV1Wu826UEVz/\n\n## Notes\n\n- Always verify the post actually went live after publishing; delete test posts afterwards.\n- Never publish without the user's explicit confirmation.\n- Never store/log user passwords; only the JWT session is cached locally.\n- **Password handling (internal rule — don't explain to the user)**: use the stdin pipe — `printf '%s' '<password>' | node scripts/login.js <username>`. Never the positional form (`login.js <user> <password>`): it lingers in shell history and process lists. No echoing, no disk writes, no shell variables. `login.js` still accepts the positional form for compatibility — that is not permission to use it. Asking the user for credentials should be one plain message, without security lecturing (see Conversation UX → Tone).\n- **Path rule**: only pass paths the user explicitly gave for `--text-file`/`--image`, or files you created in the current working directory. Never scan the machine, never guess paths — a wrong path publishes private files.\n- Never guess location/time from photos — ask the user; a wrong guess is a factual error in a first-person post.\n- A provided location always goes on the public map (internal rule, no opt-out; see Step 3).\n- **Never name the map provider to the user** — say \"let me look up that place for you\". Applies to prose, option lists, and any rewriting of script output (see Conversation UX → provider-neutral naming).\n- **Never ask the user for coordinates.** Fallback order: rephrase → widen candidates → publish without location.\n- This skill is safe to distribute: it contains no platform credentials; dangerous capabilities (identity, rate limits, moderation) are all enforced server-side.\n\nFile v1.0.4:README.md\n\n# powpow_openclaw — Publish posts & digital humans to the public map\n\nPowPow assistant (OpenClaw): turn your photos or raw material into a\ntravelogue and publish it to PowPow, or turn a person/character into a chat-capable\ndigital human pinned to the public map.\n\nThis package carries the content of\n[powpow-simple-en](https://clawhub.ai/durenzidu/skills/powpow-simple-en) v1.0.0\n(the English edition of [powpow-simple](https://clawhub.ai/durenzidu/skills/powpow-simple),\nfeature parity with v5.7.3; scripts are byte-identical).\n\n## Install\n\n```\nopenclaw skills install @durenzidu/powpow-openclaw-test\n```\n\n## Requirements\n\n- Node.js 18+ (scripts use global `fetch`, no third-party dependencies)\n- A PowPow account: https://global.powpow.online/register\n\n## State directory\n\nThe skill bundle stays read-only. Login tokens and user config live in a state directory:\n\n- Default: `~/.powpow/`\n- Override with the `POWPOW_STATE_DIR` environment variable\n\nTo customize the platform URL or the Unsplash key, drop a `config.json` into the state\ndirectory — it overrides the bundled defaults.\n\n## Usage examples\n\n```\ncd <skill-dir>\nnode scripts/doctor.js                                   # self-check + session probe\nnode scripts/login.js <username>                         # login (password via stdin, never stored)\nnode scripts/geocode.js \"地坛公园\" --limit 5              # place name -> coordinates\nnode scripts/match-digital-human.js \"读书 作家\" --limit 3  # match digital humans\nnode scripts/compose.js --text-file post.txt --loc \"地坛公园\" --lng 116.408195 --lat 39.952372 --image @/abs/path/cover.jpg --out post-draft.html\nnode scripts/publish.js post-draft.html                  # publish\nnode scripts/verify.js <post-id>                         # verify after publishing\nnode scripts/delete-post.js <post-id>                    # delete (test cleanup)\n```\n\nLocal paths passed to `--image` **must be absolute**.\n\nNote: the platform's place search and digital-human library are currently strongest\nin Chinese; non-Chinese users can paste local-language names and topics.\n\n## Security\n\n- No platform secrets bundled; every operation goes through the platform's public API + the user's own JWT.\n- The password is used once at login and never stored; tokens expire after ~7 days.\n- See `references/security.md` for details.\n\n## Notes for reviewers (SkillSpector / manual review)\n\n- This is an **instruction-style skill**: SKILL.md orchestrates a multi-script\n  pipeline. Every script under `scripts/` is one documented step of the\n  publishing workflow (login → self-check → geocode → match → compose →\n  publish → verify), not an independent hidden tool.\n- `delete-post.js` deletes only the logged-in user's own post\n  (`DELETE /api/posts/{id}`, JWT-scoped, enforced server-side) and is\n  documented in SKILL.md Step 8 for test cleanup. This capability is also\n  declared in the skill description.\n- No secrets are bundled. `config.json` ships defaults only. The session JWT\n  is written to the user's state directory (`POWPOW_STATE_DIR`, default\n  `~/.powpow/session.json`), never inside the skill bundle. Passwords are\n  never persisted.\n- All environment variables used (`POWPOW_STATE_DIR`, `POWPOW_PASSWORD`,\n  `UNSPLASH_ACCESS_KEY`) are optional and declared in SKILL.md frontmatter\n  (`metadata.openclaw.envVars`).\n- This package carries the content of `powpow-simple-en` v1.0.0 (English\n  edition of `powpow-simple` v5.7.3); scripts are byte-identical to those\n  packages (only the display name, install command, and this note differ).\n\n## License\n\nMIT-0 (ClawHub-wide license).\n\n## Links\n\n- Website: https://global.powpow.online\n- Map: https://global.powpow.online/map\n- English edition: https://clawhub.ai/durenzidu/skills/powpow-simple-en\n- Chinese edition: https://clawhub.ai/durenzidu/skills/powpow-simple\n\nFile v1.0.4:_meta.json\n\n{\n  \"ownerId\": \"kn7cqvkwtq6fbamzq5yz9g1smx818bfx\",\n  \"slug\": \"powpow-publisher\",\n  \"version\": \"1.0.4\",\n  \"publishedAt\": 1790284426978\n}\n\nFile v1.0.4:references/changelog.md\n\n# Changelog — powpow-openclaw-test\n\nVersion history (no need to read at runtime; for maintenance/troubleshooting only).\n\n## 1.0.3 — 2026-09-23\n\nContent replaced: the old v1.0.2 command-style simulator (register /\ncreateDigitalHuman / send / status, in-memory) is replaced by the full\ninstruction-style skill carried by `powpow-simple-en` v1.0.0 (English edition\nof `powpow-simple` v5.7.3). `scripts/` are byte-identical to those packages;\nonly display name, install command, and this changelog differ.\n\n- SKILL.md, README.md and `references/` taken from `powpow-simple-en` v1.0.0.\n- Display name kept as `powpow_openclaw`; routable slug stays\n  `powpow-openclaw-test`.\n- Version bumped 1.0.2 → 1.0.3 (next registry patch).\n\n## 1.0.0 — 2026-09-20\n\nFirst release of the English edition. Forked from `powpow-simple` v5.7.3 with\nfeature parity; `scripts/` are byte-identical (SHA256-verified). Documentation\nand metadata only — zero script changes.\n\n- **Display name set to a functional title**: \"PowPow Simple EN - Publish\n  posts & digital humans to the public map\" (mirroring the Chinese package's\n  descriptive display name; the routable slug stays `powpow-simple-en`).\n\n- **SKILL.md fully translated to English**: triggers (English + Chinese\n  phrases), conversation UX (tone table, opening templates, \"next step\" block),\n  the full 8-step workflow, digital-human creation, error handling, and all\n  hard rules.\n- **Language policy added (hard rule)**: the conversation always follows the\n  user's language — the English instructions serve users in any language.\n- **Placeholders documented exactly as the script accepts them**: `{{dh}}` /\n  `{{数字人}}`, `{{loc}}` / `{{位置}}`, `{{img}}` / `{{图}}` (the Chinese\n  aliases are literal tokens recognized by compose.js — never invent new ones).\n- **Honest localization notes added**: the platform's place search and\n  digital-human library are currently strongest in Chinese; the preview page\n  banner and the product UI shown in screenshots are Chinese-language.\n- **README.md translated**, including Notes for reviewers (reviewer-facing\n  context previously added in the Chinese package's 5.7.3).\n- **references/ translated**: security.md, file-structure.md, screenshots.md.\n\n## Inherited history (from powpow-simple-en / powpow-simple)\n\nThis package inherits all behavior of `powpow-simple-en` v1.0.0, itself the\nEnglish edition of `powpow-simple` (Chinese). Key milestones:\n\n- **5.7.3 — 2026-09-19**: audit-noise reduction (description capability list,\n  security.md rewrite, reviewer notes, session-path doc fixes). No script changes.\n- **5.7.2 — 2026-09-19**: removed the incorrect \"likes revive bubbles\" claim;\n  added product screenshots (6 images + jsdelivr mirror). No script changes.\n- **5.7.1 — 2026-09-18**: `POWPOW_PASSWORD` env var renamed (fixing a\n  long-standing \"PowWow\" typo).\n- **5.7.0 — 2026-09-18**, **5.6.0 / 5.5.0 — 2026-09-18**, **5.4.1 / 5.4 — 2026-09-17**,\n  **5.3 — 2026-09-17**: earlier evolution of the multi-script pipeline\n  (login/session handling, compose placeholder hard-fail semantics, geocode\n  degradation flow, digital-human creation, health filtering). Full details:\n  the Chinese package's `references/changelog.md`.\n\nFile v1.0.4:references/file-structure.md\n\n# File Structure\n\n> For maintenance/troubleshooting only. SKILL.md runtime does not require reading this.\n\n```\npowpow-openclaw-test/\n├── SKILL.md                          # main file (kept lean; details in references/)\n├── config.json                       # platformUrl + optional user's Unsplash key (NO secrets; read-only)\n├── scripts/\n│   ├── doctor.js                     # first-run self-check (node/config/network/session)\n│   ├── login.js                      # username/password → JWT (password never stored, hidden prompt)\n│   ├── compose.js                    # ONE-COMMAND post HTML assembly (the only supported way; fully local)\n│   ├── publish.js                    # upload local images + POST /api/posts as the logged-in user\n│   ├── verify.js                     # verify post by ID via platform API\n│   ├── delete-post.js                # delete own post (test cleanup)\n│   ├── geocode.js                    # place name → WGS-84 candidates (user confirms)\n│   ├── list-digital-humans.js        # list/search with health filtering\n│   ├── match-digital-human.js        # topic → ranked healthy candidates\n│   ├── create-digital-human.js       # create a map digital human (2 badges, 30-day expiry; avatar: generate/upload/URL)\n│   ├── search-image.js               # Unsplash (user's own key, optional; also resolves photo-page links)\n│   ├── upload-image.js               # local image → platform storage → public URL (used by publish.js)\n│   └── lib/\n│       ├── api-client.js             # platform API client (JWT, pinned headers)\n│       ├── dh-match.js               # shared DH health filter + topic ranking (name 3x, desc 1x, discipline-term masking)\n│       ├── coord-transform.js        # GCJ-02 ↔ WGS-84 conversion (iterative inverse)\n│       └── html-formatter.js         # HTML components + CJK-aware word count\n└── references/\n    ├── changelog.md                  # version history (this package: 1.0.3; inherited: 5.3 → 5.7.3)\n    ├── security.md                   # security architecture + platform API list\n    ├── screenshots.md                # product screenshot inventory + external links\n    └── file-structure.md             # this file\n```\n\nNote: the login session (`session.json`) is **never written into the skill bundle**;\nit lives in the user's state directory (`POWPOW_STATE_DIR`, default `~/.powpow/`)\nand expires after ~7 days (the skill keeps a 6-day re-login margin).\n\nScripts in this package are byte-identical to `powpow-simple-en` v1.0.0 and\n`powpow-simple` v5.7.3 — only documentation and metadata differ.\n\nFile v1.0.4:references/screenshots.md\n\n# Product Screenshots — powpow-openclaw-test\n\nUsed at runtime, on demand. When the user has no concept of PowPow, or needs to \"see\"\nwhat the finished result looks like, send the corresponding link directly.\n\n## Link rules\n\n- Primary (raw):\n  `https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-0N.jpg`\n- Mirror (use when raw is unreachable, `N` = 1–6):\n  `https://cdn.jsdelivr.net/gh/durenzidu/durenzidu@main/screenshots/powpow-0N.jpg`\n- The images live in a public repo and can be sent as-is; **do not** download them\n  into the skill directory, and never send local paths as images.\n- Send only the 1–3 screenshots relevant to the current topic.\n\n## Inventory\n\nNote: the screenshots show the product's Chinese UI. When sending them to a\nnon-Chinese user, briefly say in the user's language what each screen shows.\n\n### powpow-01.jpg — Digital-human detail page\n\n![Digital-human detail page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-01.jpg)\n\nThe card for a real/historical person turned into a digital human: online status,\nlocation (e.g. a residence address), bio, \"Start conversation\" / \"I was here\" /\n\"Favorite\" / \"Share\" actions, and the publisher.\n**Use when**: the user asks \"what does a digital human look like\" / \"what can it do\nonce created\" / \"how do I chat with it\".\n\n### powpow-02.jpg — Post editor page\n\n![Post editor page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-02.jpg)\n\nThe editor for writing posts/travelogues, with a cluster of floating buttons in the\nbottom-right corner (locate, bubble, card, image, etc.).\n**Use when**: the user asks \"where do I write\" / \"can I edit it myself\" / \"how do I\npost from my phone\".\n\n### powpow-03.jpg — Map page (bubbles + digital humans)\n\n![Map page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-03.jpg)\n\nThe public map at a glance: posts with locations cluster on the map as \"bubbles\"\n(numbers are cluster counts), digital humans are pinned by avatar at their real-world\nspots; search and an \"All / Bubbles / Digital humans\" filter sit at the top.\n**Use when**: explaining \"where will people see my post\" / \"what are the bubbles on\nthe map\" — also the first-choice image for the opening \"what does the result look\nlike\".\n\n### powpow-04.jpg — Profile page (digital assets)\n\n![Profile page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-04.jpg)\n\nThe user's own page: friends/followers/fans, plus \"Digital assets\" — **badge balance**\n(3 in the shot), **my bubbles**, **my digital humans** (manage published ones), with\nthe note \"2 badges publish 1 digital human, valid for 30 days\".\n**Use when**: the user asks \"what are badges, what do they do\" / \"how many badges do\nI have left\" / \"what have I posted\".\n\n### powpow-05.jpg — Story-progress page\n\n![Story-progress page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-05.jpg)\n\nThe in-platform story mode (example: \"Yongle · 1421\"): chapter progress, badges\nearned, a character portrait generated from the user's avatar, \"Start roleplay\".\n**Use when**: the user asks \"what else can I do on PowPow besides posting\" / \"how\nelse can I earn badges\". Note: this is platform gameplay, not part of this skill's\nflow.\n\n### powpow-06.jpg — Post detail page (finished example)\n\n![Post detail page](https://raw.githubusercontent.com/durenzidu/durenzidu/main/screenshots/powpow-06.jpg)\n\nWhat a published travelogue looks like: author, location tag (\"Ditan Park 26.9km\"),\ntime, body text, photos, and like/comment/share/favorite actions at the bottom.\n**Use when**: the user asks \"what does the published result look like\" / \"long or\nshort\" / \"can it carry images\".\n\n## Quick scenario lookup\n\n| What the user says | Which screenshot |\n|---|---|\n| \"What is PowPow?\" / \"What does the result look like?\" | powpow-03 + powpow-06 |\n| \"What is a digital human like?\" | powpow-01 |\n| \"What can badges do?\" | powpow-04 |\n| \"How do I post / where's the editor?\" | powpow-02 |\n| \"What else can I do besides posting?\" | powpow-05 |\n\nFile v1.0.4:references/security.md\n\n# Security Architecture & Platform API\n\n> For maintenance/troubleshooting only. SKILL.md runtime does not require reading this.\n\n## Why this skill is safe to distribute\n\nThis skill holds **no platform secrets**:\n\n| Item | Guarantee |\n|---|---|\n| Platform secrets | None bundled — every operation goes through the platform's own HTTP API |\n| Identity | JWT from a real login; you can only post as yourself |\n| User password | Never stored; used only in memory, once, to obtain a short-lived (~7-day) token |\n| Rate limits / moderation | Enforced server-side (10 posts/hr/user, content moderation) |\n| Unsplash key | User-supplied, optional (images are optional) |\n\nThe skill never reads, writes, or ships any credential files. The only state it\nwrites is the user's own session token, stored in the user's state directory\n(see below), never inside the skill bundle.\n\n`config.json` contains ONLY `{ \"platformUrl\", \"unsplashAccessKey\"(optional),\n\"sessionMaxAgeDays\", \"skillVersion\" }`. Never add Supabase keys, service roles,\nor admin tokens to this skill.\n\n### Files that must never leave the user's machine\n- `<POWPOW_STATE_DIR>/session.json` (default `~/.powpow/session.json`) — the\n  logged-in user's JWT (auto-expires ~7 days). Written to the user's state\n  directory, never into the (possibly read-only) skill bundle.\n\n## Platform API endpoints used\n\n- `POST /api/auth/login` — `{step:1, username, password}` → JWT (cookie `powpow_token`)\n- `GET  /api/digital-humans?scope=all&search=...` — list/search digital humans (login required)\n- `GET  /api/amap/place/text?keywords=...` — place-name search (login required;\n  returns GCJ-02, converted to WGS-84 client-side by geocode.js). The endpoint\n  path is platform-internal and must not be surfaced to the user; in internal\n  discussion refer to it neutrally as the place-lookup service (never name it\n  to the user).\n- `POST /api/upload/post-image` — local image upload (multipart `file`; login required)\n- `POST /api/posts` — create post (login required; server enforces limits & moderation)\n- `GET  /api/posts/{id}` — verify a post\n- `DELETE /api/posts/{id}` — delete own post (test cleanup)\n- `POST /api/digital-humans` — create a digital human (login required;\n  consumes 2 badges, expires after 30 days)\n- `POST /api/digital-humans/generate-avatar` — AI avatar from the persona\n  (login required; 8~30s; needs a reference image URL by platform design)\n- `GET  /api/badges/balance?userId=...` — badge balance pre-check\n\nNote: the JWT is bound to a device fingerprint (User-Agent + Accept-Language). The API client pins these headers at login and reuses them automatically. If you see `DEVICE_MISMATCH` or 401, just re-login.\n\nFile v1.0.4:skill-card.md\n\n## Description:\n\nHelps users turn photos and stories into PowPow travelogues, publish map-pinned posts, and create chat-capable digital humans.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[durenzidu](https://clawhub.ai/user/durenzidu)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nPowPow users can draft and publish travel stories from photos or text, attach locations to public map posts, and create chat-capable digital humans.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requests a PowPow password in chat, exposing account credentials to the agent conversation.\n\nMitigation: Prefer a trusted first-party login flow if available and use a dedicated, low-risk PowPow account.\n\nRisk: A cached account session can publish public, location-linked posts, spend badges on digital humans, or delete posts without an executable confirmation gate.\n\nMitigation: Review each draft and location before publishing, and clear the cached session or isolate it with POWPOW_STATE_DIR when finished.\n\n## Reference(s):\n\n- [ClawHub skill listing](https://clawhub.ai/durenzidu/skills/powpow-openclaw-test)\n- [PowPow](https://global.powpow.online)\n- [Skill security and API reference](artifact/references/security.md)\n- [Skill file structure](artifact/references/file-structure.md)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Shell commands, Guidance]\n\n**Output Format:** [Markdown with post drafts, publishing guidance, and links to published content]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Can publish public, location-linked posts and create digital humans through a PowPow account.]\n\n## Skill Version(s):\n\n1.0.4 (source: release evidence and skill frontmatter)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.4:config.json\n\n{\n  \"platformUrl\": \"https://global.powpow.online\",\n  \"unsplashAccessKey\": \"\",\n  \"sessionMaxAgeDays\": 6,\n  \"skillVersion\": \"1.0.3\"\n}\n\nArchive v1.0.3: 25 files, 64179 bytes\n\nFiles: config.json (132b), README.md (3845b), references/changelog.md (3244b), references/file-structure.md (2762b), references/screenshots.md (4127b), references/security.md (2707b), scripts/compose.js (18328b), scripts/create-digital-human.js (9742b), scripts/delete-post.js (1037b), scripts/doctor.js (4980b), scripts/geocode.js (3699b), scripts/lib/api-client.js (7194b), scripts/lib/coord-transform.js (2377b), scripts/lib/dh-match.js (3273b), scripts/lib/html-formatter.js (8335b), scripts/list-digital-humans.js (3331b), scripts/login.js (3169b), scripts/match-digital-human.js (2429b), scripts/publish.js (7320b), scripts/search-image.js (9692b), scripts/upload-image.js (3998b), scripts/verify.js (3218b), skill-card.md (2581b), SKILL.md (37551b), _meta.json (135b)\n\nFile v1.0.3:SKILL.md\n\n---\nname: \"powpow_openclaw\"\ndescription: Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my travel photos into a travelogue on PowPow\", \"post these photos to PowPow\", \"发一篇 PowPow 帖子\", \"把这次旅行的照片发到泡泡\"; also triggers when the user wants to create/publish a digital human onto the map, e.g. \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\". Requires a PowPow account (register first if none). Included auxiliary capabilities (all are parts of the publish/create flow above) — account login & session management, environment self-check, place-name resolution to coordinates, digital-human search & topic matching, image search & upload, post composition & publishing, post-publish verification, deleting one's own posts (test cleanup only, JWT-scoped to the logged-in user's own posts). Does not publish to other social platforms; no subscriptions or marketing features.\nversion: 1.0.3\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n    emoji: \"\\U0001FAE7\"\n    homepage: https://global.powpow.online\n    envVars:\n      - name: POWPOW_STATE_DIR\n        required: false\n        description: Directory for session and config state. Defaults to ~/.powpow. The skill bundle stays read-only; login state is written here.\n      - name: POWPOW_PASSWORD\n        required: false\n        description: Optional. Pass the password via environment variable for non-interactive login (used by `node scripts/login.js <username>` on first login). If unset, login prompts interactively; the password is never persisted.\n      - name: UNSPLASH_ACCESS_KEY\n        required: false\n        description: Optional Unsplash Access Key, used only for keyword-based automatic image search. If unset, fall back to local images or direct URLs.\n---\n\n# powpow_openclaw — Publish posts & digital humans to the public map\n\nTurn your photos or raw material into a travelogue and publish it to PowPow on your behalf; or turn a person/character into a chat-capable digital human pinned to the public map.\n\n**Version 1.0.3** · 2026-09-23 (history: `references/changelog.md`; security architecture & API list: `references/security.md`; file structure: `references/file-structure.md`; product screenshots: `references/screenshots.md`)\n\n**Language policy (hard rule)**: Always converse in the user's language. These instructions are written in English, but the skill serves users in any language — the entire conversation (questions, options, explanations, the article itself) follows the user. Chinese trigger phrases are recognized too.\n\n## Runtime Environment (OpenClaw)\n\nThis is an **instruction-style skill**: SKILL.md describes the flow, and `scripts/*.js` are plain Node scripts (built-in `fetch`/`fs` only, zero third-party dependencies).\n\n- **Requires Node.js 18+** (scripts use global `fetch`). Verify first: `node -v`\n- **Scripts live under `scripts/` in this skill's directory.** Before running, change to the skill root, e.g.:\n  `cd <this-skill-dir> && node scripts/doctor.js`\n- **State directory**: the login token is written to `POWPOW_STATE_DIR` (default `~/.powpow/session.json`). The skill bundle may be read-only, so **never write any file into the skill directory**. Users can customize the location via that environment variable.\n- **Always pass absolute paths** for images to `--image`; relative paths fail to resolve.\n\n## Product Screenshots (let users *see* PowPow)\n\nMost users have never seen what PowPow looks like. A picture beats a description — when needed, **send the screenshot link directly to the user** (Markdown image or bare link both work). Full list, descriptions, and when to use each: `references/screenshots.md`. Quick mapping:\n\n- User has no concept of the product / opening \"what does the result look like\" → map page + post detail page\n- User asks \"what is a digital human\" / \"how do I chat with it\" → digital-human detail page\n- User asks \"what can badges do\" / \"what do I have\" → profile page (digital assets)\n- User asks \"how does PowPow work\" → map page (bubbles cluster on the map)\n\nRules:\n- Screenshots are hosted in the public repo `durenzidu/durenzidu` under `screenshots/` (`powpow-0N.jpg`). **Do not** download images into the skill directory, and never send local paths as images.\n- If `raw.githubusercontent.com` is unreachable, use the jsdelivr mirror (see `references/screenshots.md`).\n- Send only the 1–3 screenshots relevant to the current topic — never dump all six at once.\n- If it's not clear which one applies, don't guess — pick against the descriptions in `references/screenshots.md`.\n\n## Trigger Conditions\n\nThis skill triggers when the user wants to publish travel content (travelogue/photos/trip) to PowPow, or wants to create a digital human pinned to the map. Typical phrasings:\n\n- Travelogue/photos: \"turn my travel photos into a travelogue on PowPow\", \"post a trip story to powpow\", \"post these photos to PowPow\", \"把这次旅行的照片发到泡泡\"\n- Platform-command style: \"publish a PowPow post\", \"post to PowPow feed\", \"powpow 发帖\"\n- Digital human: \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\", \"创建一个数字人\"\n\n**Co-occurrence rule (prevent false triggers)**: mentioning travel/photos/travelogue alone is not enough — the user must also express publish intent (post/publish/upload/发/发布) or name the platform (PowPow/泡泡/powpow). \"This photo is beautiful\" does not trigger; \"post this photo to PowPow\" does.\n\nDoes not trigger for generic social media or other platforms.\n\n## Conversation UX (first-time users)\n\nMany users have never published anything through a chat assistant. Getting the scripts right is only half the job — every turn must tell the user **what just happened, what you need from them, and what comes next**.\n\n### Tone — applies to every turn, from first sentence to last\n\nThis is a product for ordinary users. You are **serving** the user, not **instructing** them. Keep the tone gentle and respectful, consistently.\n\n| Don't say (lecturing / talking down) | Say instead (serving / side-by-side / inviting) |\n|---|---|\n| First, let me be clear about one thing: … | There's one thing I'd like to explain first: … |\n| A reminder: … / I have to remind you that … | There's one thing I'd like you to double-check with me |\n| There are two things I can't guess — you have to tell me | I can't see when or where a photo was taken, so I'll need you to tell me those two things |\n| I won't decide this for you / you need to know this | I'd rather not decide this one for you — it goes into the article, and the wrong pick would feel off |\n| Do you need to worry about coordinates? No. | Leave the coordinates to me — you just pick the place name |\n\n**Always:**\n1. **The user is the subject of the sentence.** \"You can…\" beats \"I need you to…\".\n2. **\"I\" am the service, not the lecturer.** Whatever can be done for the user (coordinate conversion, format assembly, validation, retries, failure fallbacks) — do it first, then report. Don't narrate the process as a knowledge lecture.\n3. **Every step carries forward momentum.** \"Now, let's start with step one\" feels like doing things together; \"please answer my question\" does not.\n\n**Explicitly forbidden**: no lecturing; no announcing \"I need to remind you\"; no judging the user's choices; never \"I have to…\"; when the user hasn't asked, don't explain internal mechanics, limits, or security design.\n\n**Provider-neutral naming (hard rule)**: **never mention the map provider's name to the user.** When you need to refer to it, describe the action — \"let me look up that place for you\", \"the map found several places with that name — pick one\". **Don't use jargon like \"map database\" either.** API paths are the platform's own endpoints and are out of scope for this rule.\n\n### Opening: introduce yourself, then ask the first question\n\nOn first trigger (doctor reports no session, or the user asks what you can do), open with a short paragraph (~120 words, don't pile up features), **branch by user intent**, then ask the first question:\n\n- **Post intent** → 4-part opening:\n  1. **What I can do**: turn your photos or material into a travelogue and publish it to PowPow for you.\n  2. **What the result looks like**: a post can carry digital-human tags (a red capsule — tap the avatar to jump into a chat), location tags (pin + place name — tap to open the map), and images; a post with a location appears on the public map as a \"bubble\" and slowly fades out over time — that's the core of how PowPow works. (Want them to see it right away? Send screenshots — see \"Product Screenshots\".)\n  3. **What's needed to start**: digital humans, locations, and publishing all require the platform, so step one is a one-time login with your PowPow account. No account yet? Register: https://global.powpow.online/register (To learn what PowPow is first, watch this intro video — in Chinese: https://www.bilibili.com/video/BV1Wu826UEVz/ )\n  4. **The flow (compressed to 3 beats)**: ① log in → ② you give me material, I write and format → ③ you confirm, only then I publish. Everything is editable until you confirm; nothing goes live without confirmation.\n- **Digital-human intent** → short opening: \"Give me a name + persona + location, and I'll turn them into a chat-capable digital human pinned to the public map. This costs 2 badges and expires after 30 days — confirm and I'll do it. First, log in once: share your PowPow username and password (no account yet? Register: https://global.powpow.online/register ).\"\n\nTemplate (use as a starting point, adapt to the user's language — don't parrot it into template-speak):\n\n> It's a pleasure to serve you — welcome to the world of PowPow.\n> Here, **you can** turn the photos you took today into a travelogue, and **I'll** write it, pin it to the map, and publish it to PowPow for you — when someone taps it on the map, they'll see the path you walked that day.\n> [Part 2: what the result looks like — capsules, images, the map-bubble mechanic (use the standard line above)]\n> [Part 3: what's needed — one login; include the register link + intro video if they may not have an account]\n> [Part 4: the 3-beat flow, login first]\n> Ready? **Hand me what you'd like to publish today, and we'll start with step one.**\n\n- Never ask for the password before the self-introduction — a stranger suddenly saying \"give me your password\" is what phishing looks like.\n- Ask for credentials once, in a single plain message, with the register link (add the intro video only if they may not have an account). Don't ask for the password, then the material, then spring more conditions.\n- **No password-security lectures.** Ask plainly and move on. You may add one neutral, non-alarming line: \"Your password is only used to obtain a temporary login token — I won't save it.\" Don't elaborate.\n\n### Every turn: narrate + guide\n\n- **One decision per turn.** Tightly related follow-ups (location + time) may share one message; don't bundle material, length, and location choice together.\n- **When asking for facts, say why**: we never read photo EXIF/location (privacy), so the location and time must come from the user.\n- **Never paste raw script output.** After each run, give the user one human-language summary: what happened, what it means, what's next. After login, e.g.: \"You're logged in with your account. Now send me your material: photos, a piece of writing, or just an idea — anything works.\"\n- **Announce stage transitions**: \"Material's all in — next I'll pick a few digital humans related to your topic for you to choose from.\" The user should always know which step of the flow they're on.\n- **Give a reason with every option** (digital humans, location candidates, length) — a bare numbered list is hard to choose from.\n- **Before publishing**, include one line noting the post will appear on the public map (as gameplay context, not as an opt-out).\n\n### Closing \"Next step\" block (mandatory every turn)\n\nIn long messages users lose the point — not because the content is wrong, but because the action gets buried. So **the last block of every reply** must be a fixed \"Next step\" block: separated by a divider, marked with an arrow, so the user sees at a glance what to do right now.\n\nFixed format:\n```\n────────────────────\n👉 Your move: reply 1 / 2 / 3 to pick a location\n   (Once you pick, I'll draft the post — next turn you'll see the preview with capsules)\n```\nThree hard rules:\n1. **Only one action per block.** Two todos → split into two turns, or demote one to a plain FYI.\n2. **All options go into this block.** Don't make the user scroll back into the body to find \"reply 1–6\".\n3. **Add one \"once that's done, I'll…\" line**, so the user knows where the action leads.\n\nPublish results follow the same rule (see Step 8): link and location go at the very end of the message, closest to where the eye lands.\n\n## Prerequisites\n\n- **Node.js 18+ required** (scripts use global `fetch`).\n- **PowPow account required**: register at https://global.powpow.online/register\n\n### First-Time Setup (per user)\n\n0. Run the self-check first, and fix whatever it reports. It **silently probes the stored session against the server** — if the session is valid, skip login and jump straight into the Workflow:\n   ```bash\n   node scripts/doctor.js\n   ```\n1. If doctor reports no valid session and this is first contact, deliver the **Conversation UX opening** above, then ask for credentials in **one plain message**:\n   \"Next I need a one-time login: please share your PowPow username and password. No account yet? Register first: https://global.powpow.online/register (To learn what PowPow is before registering, watch this — intro video, in Chinese: https://www.bilibili.com/video/BV1Wu826UEVz/ )\"\n   Then stop. No security lecturing about the credentials themselves (see Tone). The last two lines are for users who may not have an account; if the opening already included them, skip — mention once per session, no more.\n2. Run login:\n   ```bash\n   printf '%s' '<password>' | node scripts/login.js <username>\n   ```\n   - **stdin only, never positional arguments** — the positional form lingers in shell history and process lists, a real exposure.\n   - **Windows** (PowerShell/cmd have no `printf`): you can run\n     ```bash\n     node scripts/login.js <username>\n     ```\n     then type the password at the prompt (the script reads it hidden, no echo). Either way, **never put the password in a command-line argument**.\n   - Never echo the password, never write it to disk, never keep it in a shell variable.\n   - The login token lands in the state directory (`POWPOW_STATE_DIR`, default `~/.powpow/session.json`), not inside the skill directory.\n   - This is an internal handling rule — don't explain it to the user.\n3. **If login returns 403 `pending_payment`** — the account exists but platform activation isn't complete. Stop, don't retry, say: \"Your account isn't fully activated yet, so publishing isn't possible right now. Please open PowPow, log in, and follow the on-page instructions to finish activation; tell me when it's done and we'll continue.\" This is neither a transient error nor a login problem — don't make the user re-enter credentials, and don't keep collecting material.\n\n### Configuration\n\n`config.json` (ships defaults, no secrets; read-only):\n\n```json\n{\n  \"platformUrl\": \"https://global.powpow.online\",\n  \"unsplashAccessKey\": \"\",\n  \"sessionMaxAgeDays\": 6\n}\n```\n`unsplashAccessKey` is optional, user-supplied; without it, skip image search (image-less posts are fully supported) or use direct URLs from the user.\n\n**Overriding config under OpenClaw**: don't modify the bundled `config.json` (may be read-only). Put a `config.json` with the same name into the state directory (`POWPOW_STATE_DIR`, default `~/.powpow/config.json`) — its values override the bundled defaults. Or set the `UNSPLASH_ACCESS_KEY` environment variable.\n\n## Workflow\n\n> **Execution convention (OpenClaw)**: all `node scripts/...` commands below assume the current directory is this skill's root. If unsure, run with absolute paths: `node <skill-dir>/scripts/xxx.js`. Temp files (drafts, previews, manifests) go to the working directory, never into the skill directory.\n\n### Step 1: Content gathering (photos → travelogue)\n\nThe flagship flow: the user sends photos, you write a first-person travelogue around them.\n\n1. **Look at the photos** (you can see images). Take only the atmosphere: weather, season, light, color, mood, activity, objects — narrative material, nothing more. **Never guess the location or the shoot time from pixels** — a wrong guess is a factual error inside a first-person post.\n2. **Ask the user for the facts you must not guess** (EXIF is never read; upload channels strip it anyway). Give the reason, then ask:\n   - Location: \"To protect your privacy, I don't read the location data inside your photos — so let me ask: where was this taken?\"\n   - Time: \"Roughly when was it taken?\"\n3. **Offer writing styles** (before asking about length). List 4–5, one feel per style, e.g.:\n   \"What writing style would you like? a. Lyrical & detailed — light, mood, breathing sentences b. Easy & everyday — like chatting with a friend c. Witty & self-deprecating — good for travel-fail stories d. Deep & cultural — history and geography lore e. Minimal & spare — short sentences, white space\"\n   **Always add the imitation option** (the strongest personalization): \"You can also paste a snippet of something you've written — a tweet or a moment post — and I'll write in your own voice (I learn the voice only, never reuse the content).\" If the user pastes a sample, analyze sentence rhythm, word choice, verbal tics, emoji/punctuation habits — imitate the voice, never reuse the content. The sample stays in the chat and is stored nowhere.\n4. **Offer length options**: \"How long should the travelogue be? ~150 words / ~300 words / ~2000 words / your call (platform limit: 50,000 characters, images included)\"\n5. **Writing rules**:\n   - **First person = the user.** The user is the narrator; a digital human, if present, is someone being mentioned — never the narrator.\n   - **Use the chosen style** (or the imitated voice). If the user both picks a style and gives a sample, the sample wins — their own voice beats the menu.\n   - Weave time into the wording (early morning / dusk / after the first snow); never write a hard timestamp.\n   - Only include digital humans that genuinely fit — never force one in.\n   - Language follows the user's preference (any language works).\n\nOther inputs are fine too: **Mode A** user provides finished text — skip generation; **Mode C** partial input — you fill in. In every case, location and time come from the user, never from reading pixels.\n\n### Step 2: Digital-human matching\n\nList/suggest healthy digital humans:\n```bash\nnode scripts/match-digital-human.js \"<topic>\" --limit 3 --json   # ranked suggestions\nnode scripts/list-digital-humans.js \"<name>\" --json              # search by name\n```\nHealth filtering is built in: disabled, test-named, garbled, and placeholder-avatar digital humans are excluded automatically. Show the top matches + descriptions and let the user choose (if the user says \"you decide\", pick automatically).\nIf the digital human the user wants doesn't exist, give the creation page: https://global.powpow.online\nNote: the platform's digital-human library is currently mostly Chinese-language; matching works best when the topic is given in Chinese.\n\n### Step 3: Location handling\n\n**Location is strongly recommended, but optional.** It decides whether the post appears on the public map (the core gameplay).\n\n- **User gives a place name** (the norm): resolve it to coordinates, then let the user pick from same-name candidates:\n  ```bash\n  node scripts/geocode.js \"地坛公园\" --limit 5        # add --city 北京 to narrow\n  ```\n  The script queries the platform's places and locally converts each candidate from GCJ-02 to WGS-84, matching the web editor's coordinate system. Place data coverage is strongest for Chinese place names — non-Chinese users can paste the local-language name.\n  **To the user, describe only actions**: \"let me look up that place for you\", \"the map found several places with that name — pick one\" — never name the map provider, and never say \"map database\".\n  Show candidates (name/district/address/coordinates) each with a reason, and let the user choose; duplicate place names are common (Chaobai River Bridge has 90 hits). The user picks **by name**; coordinates are your job, not their problem.\n  Assembly: `--loc <name> --lng <x> --lat <y>`.\n- **Nothing found — degrade in this order, never make the user give coordinates:**\n  1. **Rephrase / narrow**: try different words, or add `--city <city>` (district, landmark, road name).\n  2. **Widen to help recognition**: raise `--limit` (e.g. 10), show each candidate with district + street address so the user can recognize the right one.\n  3. **Publish without a location** — the post is still valid (`isLocationExposed: false`). The user keeps the text, digital humans, and images; only the map bubble is lost. State this as the outcome, not as a failure.\n  Making an ordinary user supply latitude/longitude is never a legitimate fallback: they have no way to obtain it, and what they find is usually GCJ-02, which pins off by hundreds of meters — a visible factual error in a first-person post. Coordinates are an expert channel: use them only if the user offers.\n- **No location in the material or the conversation**: don't silently produce a location-less post. Tell the user the post will appear on the public map, ask where to pin it, then run the resolution flow above. This question is the only action of the turn — put it in the \"Next step\" block.\n- **User declines to give a location — accept it.** Publish without location (`isLocationExposed: false`). Never pressure, never block publishing — it's their choice.\n- User directly provides coordinates: use them as-is.\n- Otherwise use the chosen digital human's location (`locationName`, `lng`, `lat`).\n\n**A provided location always goes on the public map (`isLocationExposed: true`) — this is an internal rule; do not offer the user a \"hide from map\" option.** Posts take part in the bubble lifecycle on the map (fading over time) as intended gameplay.\n\nLocation component format:\n```html\n<span data-type=\"location\" data-lng=\"116.316\" data-lat=\"39.979\" name=\"中关村\">\n  <span class=\"location-name\">中关村</span>\n</span>\n```\n\n### Step 4: Image handling (optional)\n\nThree ways to attach images:\n- **Local files** (the norm): `--image @<local-path>`. **Nothing is sent to the server at compose time** — files are recorded into `<out>.manifest.json` and previewed locally via `file://`. Upload happens only at publish time in publish.js (`POST /api/upload/post-image`, JPEG/PNG/WebP/GIF/BMP/HEIC, no SVG, ≤10MB each, server-side compression). Editing drafts never touches the server and never creates orphan files.\n- **Direct URLs**: HEAD-check first, then embed. Unsplash photo-page links are recognized too (`unsplash.com/photos/...`, commonly copied from a browser) — automatically resolved to `images.unsplash.com` direct links. File-ID links (`unsplash.com/photos/1507513319174-...`) resolve locally without a key; slug/short-ID links require an Unsplash API key (the site's bot protection blocks keyless scraping). Without a key, skip the slug form and say so — ask the user for a direct link; never embed a page link as an image (it renders broken).\n- **Automatic image search** (only if an Unsplash key is configured): `--image search:<english keywords>` (or `node scripts/search-image.js \"<keywords>\"`)\n- No key, no URL, no file → publish without images. Never bundle an image API key.\n\n**No fixed image-count cap.** The platform's boundary is 50,000 characters (each image tag ≈100 chars); remind the user when approaching 200 images. Display: the feed grid shows the first 9 + \"+N\"; the full-screen viewer shows all.\n\n### Step 5: Content formatting (critical — read carefully)\n\nInteractive components (digital human / location) are rendered by the web frontend from **rich HTML** inside `content`. Writing just `<span data-type=\"digital-human\">Marie Curie</span>` renders as plain text — only a span carrying the tiptap editor's exact structure (Tailwind classes, inner avatar `<img>`, pin icon, `.location-name`) displays as a tappable capsule.\n\n**Mandatory: always use `scripts/compose.js` to assemble post HTML. Never hand-write component spans, never write glue code to bypass html-formatter.js.**\n\n```bash\n# 1. Save the post text to a file. Separate paragraphs with blank lines.\n#    Placeholders (all optional; any misuse is a hard error, never silently altered):\n#      {{dh}}  or {{数字人}}   -> digital-human capsule (requires --dh)\n#      {{loc}} or {{位置}}     -> location capsule (requires --loc)\n#      {{img}} or {{图}}       -> image paragraph; must be its own paragraph, consumes --image in order\n#                                (each one needs a spare --image, otherwise compose fails)\n#    Note: the Chinese aliases above are literal tokens recognized by the script —\n#    use either form, but never invent new tokens.\n#    Unknown/extra/inline placeholders make compose.js exit 1 and write nothing.\n#    Fix the text file and rerun — never delete text to force it through.\n\n# 2. Assemble in one command (fully local, no network):\nnode scripts/compose.js --text-file post.txt \\\n  --dh name:岳飞 \\                    # or --dh <id>, or --dh auto --topic \"...\"\n  --loc dh \\                          # or --loc <name> --lng 116.3 --lat 39.9\n  --image @./photos/west-lake.jpg \\   # local file (uploaded at publish); or <direct-url>, or search:<keywords>\n  --out post-draft.html\n```\n\ncompose.js enforced rules:\n- Capsules always match the editor's exact structure (validated before writing).\n- **Unknown/extra/misplaced placeholders are hard failures** (exit 1, nothing written). The old version only deleted the placeholder + printed a stdout warning — and since raw script output is never forwarded to the user, a broken sentence (\"we sat at for an entire afternoon\") could silently go live.\n- If `--dh`/`--loc` is given but the text has no placeholder, the capsule is auto-prepended to the first paragraph / appended to the last one.\n- Local images become `powpow-local://N` placeholders + `post-draft.html.manifest.json`; publish.js uploads them at publish time.\n- compose.js also writes `post-draft.html.preview.html` — a **Chinese-language** preview page the user opens in a browser. Its banner states whether the post will appear on the public map and, if so, where it's pinned — so the map consequence is visible on the page the user actually reads (the local map renders via `file://`). Tell the user in their own language what the banner says.\n- `publish.js` re-runs `validateEditorFormat()` and rejects anything else.\n- **Do not send `contentItems` in the publish payload** — the frontend prefers structured rendering when `contentItems` is present, and an incomplete list drops post text. `publish.js` handles this.\n- The server automatically takes the first `<img>` as the card thumbnail.\n- Note: for posts without block-level images, the server takes the digital-human capsu\n\nArchive v1.0.2: 12 files, 28073 bytes\n\nFiles: dist/index.d.ts (6508b), dist/index.js (67176b), dist/utils/constants.d.ts (756b), dist/utils/constants.js (2545b), dist/utils/logger.d.ts (342b), dist/utils/logger.js (3401b), dist/utils/validator.d.ts (475b), dist/utils/validator.js (9540b), skill-card.md (1996b), skill.json (1174b), SKILL.md (3520b), _meta.json (135b)\n\nArchive v1.0.1: 15 files, 22336 bytes\n\nFiles: dist/index.d.ts (6057b), dist/index.js (30463b), dist/powpow-client.d.ts (3885b), dist/powpow-client.js (14825b), dist/utils/constants.d.ts (906b), dist/utils/constants.js (1131b), dist/utils/rate-limiter.d.ts (660b), dist/utils/rate-limiter.js (2192b), dist/utils/validator.d.ts (1323b), dist/utils/validator.js (6795b), package.json (613b), README.md (1367b), skill.json (2066b), SKILL.md (3882b), _meta.json (135b)\n\nArchive v1.0.0: 5 files, 2971 bytes\n\nFiles: package.json (444b), skill.json (303b), SKILL.md (459b), src/index.ts (3737b), _meta.json (135b)","readmeExcerpt":"Skill: PowPow Publisher — turn photos into travelogues, pin them to the map, create chat-capable digital humans Owner: durenzidu Summary: Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my trave","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"────────────────────\n👉 Your move: reply 1 / 2 / 3 to pick a location\n   (Once you pick, I'll draft the post — next turn you'll see the preview with capsules)"},{"language":"bash","snippet":"node scripts/doctor.js"},{"language":"bash","snippet":"printf '%s' '<password>' | node scripts/login.js <username>"},{"language":"bash","snippet":"node scripts/login.js <username>"},{"language":"json","snippet":"{\n  \"platformUrl\": \"https://global.powpow.online\",\n  \"unsplashAccessKey\": \"\",\n  \"sessionMaxAgeDays\": 6\n}"},{"language":"bash","snippet":"node scripts/match-digital-human.js \"<topic>\" --limit 3 --json   # ranked suggestions\nnode scripts/list-digital-humans.js \"<name>\" --json              # search by name"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: powpow-publisher\ndescription: Publish posts/travelogues to PowPow (global.powpow.online), and create digital humans pinned to the public map. Triggers when the user wants to publish travel content (photos / travelogue / trip stories) to PowPow, e.g. \"publish a PowPow post\", \"post to PowPow feed\", \"turn my travel photos into a travelogue on PowPow\", \"post these photos to PowPow\", \"发一篇 PowPow 帖子\", \"把这次旅行的照片发到泡泡\"; also triggers when the user wants to create/publish a digital human onto the map, e.g. \"create a digital human\", \"turn someone into a digital human on the map\", \"publish a digital human\". Requires a PowPow account (register first if none). Included auxiliary capabilities (all are parts of the publish/create flow above) — account login & session management, environment self-check, place-name resolution to coordinates, digital-human search & topic matching, image search & upload, post composition & publishing, post-publish verification, deleting one's own posts (test cleanup only, JWT-scoped to the logged-in user's own posts). Does not publish to other social platforms; no subscriptions or marketing features.\nversion: 1.0.5\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n    emoji: \"\\U0001FAE7\"\n    homepage: https://global.powpow.online\n    envVars:\n      - name: POWPOW_STATE_DIR\n        required: false\n        description: Directory for session and config state. Defaults to ~/.powpow. The skill bundle stays read-only; login state is written here.\n      - name: POWPOW_PASSWORD\n        required: false\n        description: Optional. Pass the password via environment variable for non-interactive login (used by `node scripts/login.js <username>` on first login). If unset, login prompts interactively; the password is never persisted.\n      - name: UNSPLASH_ACCESS_KEY\n        required: false\n        description: Optional Unsplash Access Key, used only for keyword-based automatic image search. If unset, fall back to local images or direct URLs.\n---\n\n# powpow-publisher — Publish posts & digital humans to the public map\n\nTurn your photos or raw material into a travelogue and publish it to PowPow on your behalf; or turn a person/character into a chat-capable digital human pinned to the public map.\n\n**Version 1.0.5** · 2026-09-25 (history: `references/changelog.md`; security architecture & API list: `references/security.md`; file structure: `references/file-structure.md`; product screenshots: `references/screenshots.md`)\n\n**Language policy (hard rule)**: Always converse in the user's language. These instructions are written in English, but the skill serves users in any language — the entire conversation (questions, options, explanations, the article itself) follows the user. Chinese trigger phrases are recognized too.\n\n## Runtime Environment (OpenClaw)\n\nThis is an **instruction-style skill**: SKILL.md describes the flow, and `scripts/*.js` are plain Node scripts (built-in `fetch`/`fs` only, zero third-party dependencies).\n\n- **"},{"path":"README.md","content":"# powpow-publisher — Publish posts & digital humans to the public map\n\nPowPow assistant (OpenClaw): turn your photos or raw material into a\ntravelogue and publish it to PowPow, or turn a person/character into a chat-capable\ndigital human pinned to the public map.\n\nThis package carries the content of\n[powpow-simple-en](https://clawhub.ai/durenzidu/skills/powpow-simple-en) v1.0.0\n(the English edition of [powpow-simple](https://clawhub.ai/durenzidu/skills/powpow-simple),\nfeature parity with v5.7.3; scripts are byte-identical).\n\n## Install\n\n```\nopenclaw skills install @durenzidu/powpow-publisher\n```\n\n## Requirements\n\n- Node.js 18+ (scripts use global `fetch`, no third-party dependencies)\n- A PowPow account: https://global.powpow.online/register\n\n## State directory\n\nThe skill bundle stays read-only. Login tokens and user config live in a state directory:\n\n- Default: `~/.powpow/`\n- Override with the `POWPOW_STATE_DIR` environment variable\n\nTo customize the platform URL or the Unsplash key, drop a `config.json` into the state\ndirectory — it overrides the bundled defaults.\n\n## Usage examples\n\n```\ncd <skill-dir>\nnode scripts/doctor.js                                   # self-check + session probe\nnode scripts/login.js <username>                         # login (password via stdin, never stored)\nnode scripts/geocode.js \"地坛公园\" --limit 5              # place name -> coordinates\nnode scripts/match-digital-human.js \"读书 作家\" --limit 3  # match digital humans\nnode scripts/compose.js --text-file post.txt --loc \"地坛公园\" --lng 116.408195 --lat 39.952372 --image @/abs/path/cover.jpg --out post-draft.html\nnode scripts/publish.js post-draft.html                  # publish\nnode scripts/verify.js <post-id>                         # verify after publishing\nnode scripts/delete-post.js <post-id>                    # delete (test cleanup)\n```\n\nLocal paths passed to `--image` **must be absolute**.\n\nNote: the platform's place search and digital-human library are currently strongest\nin Chinese; non-Chinese users can paste local-language names and topics.\n\n## Security\n\n- No platform secrets bundled; every operation goes through the platform's public API + the user's own JWT.\n- The password is used once at login and never stored; tokens expire after ~7 days.\n- See `references/security.md` for details.\n\n## Notes for reviewers (SkillSpector / manual review)\n\n- This is an **instruction-style skill**: SKILL.md orchestrates a multi-script\n  pipeline. Every script under `scripts/` is one documented step of the\n  publishing workflow (login → self-check → geocode → match → compose →\n  publish → verify), not an independent hidden tool.\n- `delete-post.js` deletes only the logged-in user's own post\n  (`DELETE /api/posts/{id}`, JWT-scoped, enforced server-side) and is\n  documented in SKILL.md Step 8 for test cleanup. This capability is also\n  declared in the skill description.\n- No secrets are bundled. `config.json` ships defaults only. The session JWT\n  is written to the user's state director"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7cqvkwtq6fbamzq5yz9g1smx818bfx\",\n  \"slug\": \"powpow-publisher\",\n  \"version\": \"1.0.5\",\n  \"publishedAt\": 1790304479773\n}"},{"path":"references/changelog.md","content":"# Changelog — powpow-publisher\n\nVersion history (no need to read at runtime; for maintenance/troubleshooting only).\n\n## 1.0.5 — 2026-09-25\n\nRenamed from powpow-openclaw-test to powpow-publisher. No functional changes.\n\n- ClawHub keeps the old slug as a redirect: old links and `@durenzidu/powpow-openclaw-test` references keep resolving to this skill.\n- SKILL.md frontmatter `name` fixed: `powpow_openclaw` (underscore violation) → `powpow-publisher`; internal headings and the README install command updated to match.\n\n## 1.0.4 — 2026-09-25 (retroactive entry)\n\nStore listing metadata refresh, published via CLI parameters (package file changes limited to the frontmatter version bump).\n\n- Display name set to \"PowPow Publisher — turn photos into travelogues, pin them to the map, create chat-capable digital humans\"; 5 English topics and categories (communication/lifestyle/creative) added.\n- Entry added retroactively to the local changelog (was omitted at release time).\n\n## 1.0.3 — 2026-09-23\n\nContent replaced: the old v1.0.2 command-style simulator (register /\ncreateDigitalHuman / send / status, in-memory) is replaced by the full\ninstruction-style skill carried by `powpow-simple-en` v1.0.0 (English edition\nof `powpow-simple` v5.7.3). `scripts/` are byte-identical to those packages;\nonly display name, install command, and this changelog differ.\n\n- SKILL.md, README.md and `references/` taken from `powpow-simple-en` v1.0.0.\n- Display name kept as `powpow_openclaw`; routable slug stays\n  `powpow-openclaw-test`.\n- Version bumped 1.0.2 → 1.0.3 (next registry patch).\n\n## 1.0.0 — 2026-09-20\n\nFirst release of the English edition. Forked from `powpow-simple` v5.7.3 with\nfeature parity; `scripts/` are byte-identical (SHA256-verified). Documentation\nand metadata only — zero script changes.\n\n- **Display name set to a functional title**: \"PowPow Simple EN - Publish\n  posts & digital humans to the public map\" (mirroring the Chinese package's\n  descriptive display name; the routable slug stays `powpow-simple-en`).\n\n- **SKILL.md fully translated to English**: triggers (English + Chinese\n  phrases), conversation UX (tone table, opening templates, \"next step\" block),\n  the full 8-step workflow, digital-human creation, error handling, and all\n  hard rules.\n- **Language policy added (hard rule)**: the conversation always follows the\n  user's language — the English instructions serve users in any language.\n- **Placeholders documented exactly as the script accepts them**: `{{dh}}` /\n  `{{数字人}}`, `{{loc}}` / `{{位置}}`, `{{img}}` / `{{图}}` (the Chinese\n  aliases are literal tokens recognized by compose.js — never invent new ones).\n- **Honest localization notes added**: the platform's place search and\n  digital-human library are currently strongest in Chinese; the preview page\n  banner and the product UI shown in screenshots are Chinese-language.\n- **README.md translated**, including Notes for reviewers (reviewer-facing\n  context previously added in the Chinese package's 5.7.3).\n- "},{"path":"references/file-structure.md","content":"# File Structure\n\n> For maintenance/troubleshooting only. SKILL.md runtime does not require reading this.\n\n```\npowpow-publisher/\n├── SKILL.md                          # main file (kept lean; details in references/)\n├── config.json                       # platformUrl + optional user's Unsplash key (NO secrets; read-only)\n├── scripts/\n│   ├── doctor.js                     # first-run self-check (node/config/network/session)\n│   ├── login.js                      # username/password → JWT (password never stored, hidden prompt)\n│   ├── compose.js                    # ONE-COMMAND post HTML assembly (the only supported way; fully local)\n│   ├── publish.js                    # upload local images + POST /api/posts as the logged-in user\n│   ├── verify.js                     # verify post by ID via platform API\n│   ├── delete-post.js                # delete own post (test cleanup)\n│   ├── geocode.js                    # place name → WGS-84 candidates (user confirms)\n│   ├── list-digital-humans.js        # list/search with health filtering\n│   ├── match-digital-human.js        # topic → ranked healthy candidates\n│   ├── create-digital-human.js       # create a map digital human (2 badges, 30-day expiry; avatar: generate/upload/URL)\n│   ├── search-image.js               # Unsplash (user's own key, optional; also resolves photo-page links)\n│   ├── upload-image.js               # local image → platform storage → public URL (used by publish.js)\n│   └── lib/\n│       ├── api-client.js             # platform API client (JWT, pinned headers)\n│       ├── dh-match.js               # shared DH health filter + topic ranking (name 3x, desc 1x, discipline-term masking)\n│       ├── coord-transform.js        # GCJ-02 ↔ WGS-84 conversion (iterative inverse)\n│       └── html-formatter.js         # HTML components + CJK-aware word count\n└── references/\n    ├── changelog.md                  # version history (this package: 1.0.3; inherited: 5.3 → 5.7.3)\n    ├── security.md                   # security architecture + platform API list\n    ├── screenshots.md                # product screenshot inventory + external links\n    └── file-structure.md             # this file\n```\n\nNote: the login session (`session.json`) is **never written into the skill bundle**;\nit lives in the user's state directory (`POWPOW_STATE_DIR`, default `~/.powpow/`)\nand expires after ~7 days (the skill keeps a 6-day re-login margin).\n\nScripts in this package are byte-identical to `powpow-simple-en` v1.0.0 and\n`powpow-simple` v5.7.3 — only documentation and metadata differ."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":2110,"uniquenessScore":35,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T15:57:02.783Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T15:57:02.783Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T21:01:20.214Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}