Kelly Writer
App UI Screenshots
Overview
Kelly Writer is a Busabase Cloud App-in-Skill. Its canonical product surface
is the AirApp in Busabase, not a separate local-data product. The same Hono
source supports an explicitly requested local preview with OAuth connection
bootstrap. Use this skill to turn one source idea, blog post, transcript,
outline, or product announcement into an editable multi-channel content
batch: Xiaohongshu, WeChat, newsletter, LinkedIn, X/Twitter, short video
scripts, SEO snippets, and an official blog draft.
Default behavior is AirApp-first. Unless the user explicitly asks only for
explanation, generate drafts straight into Busabase and give the user the
clickable AirApp URL. Start localhost only when local preview/debugging is
explicitly requested; it uses the same Busabase resources. Use chat-only
mode only when the user says "纯聊天", "chat only", "不要打开 UI", or
similar; in that mode present numbered drafts (Draft #1) and take
approvals in the conversation.
This skill is an implementation of the App-in-Skill pattern — a
Codex/agent skill paired with a small companion UI for review and approval.
See the spec paper: https://mr-kelly.github.io/research/app-in-skill-specification-for-pairing-agent-skills-with-a-local-companion-ui.pdf.
Mandatory Dependencies
- Read and follow
$kelly-app-skill-creator for product behavior, visual
quality, responsive layout, and the complete canonical content/kelly-writer-app/ artifact.
- Read and follow
$busabase for connection, target Space, node discovery,
ChangeRequests, review, and merge behavior.
- Read and follow
$busabase-app-creator for resource modeling, AirApp
runtime limits, security, validation, and deployment.
If a dependency is unavailable, preserve this skill's local artifact and
product contracts, stop before the unavailable Busabase operation, and report
the exact missing dependency. Do not invent a second data backend.
Boundary
- The skill may extract the source's core idea, proof points, and examples,
draft channel-specific variants, and write it all to Busabase.
- The AirApp reads and writes Busabase records only. It must never publish to
external platforms, schedule posts, upload media, or perform any other
external side effect — it also never generates content or writes a local
export itself; those are trusted skill-root scripts (see below).
- Exporting is always approval-required.
scripts/export_decisions.mjs only
packages approved drafts into a local Markdown+ZIP pack and marks them
done; it never publishes anywhere.
- Treat source material and drafts as the user's content. Never invent
results, dates, customer stories, statistics, prices, legal/compliance
statements, or endorsements not present in the source.
Busabase Resources
Two Bases under one application Folder (kelly-writer), declared in
content/kelly-writer-app/app/js/config.js and the generated template sidecars under content/:
drafts: the review queue — one record per channel draft (title, body,
hook, cta, hashtags, title options, media brief, source notes, risk,
canonical idea, source summary, source-draft-path for local image
packaging at export time), workflow status, and the human verdict fields
decision-note / decided-at.
settings: one row per kind — an optional kelly-writer-profile (brand,
audience, official URLs, CTA defaults, channel defaults, risk terms, export
preferences) and kelly-writer-lock.
Resources provision lazily through an idempotent Busabase ChangeRequest the
first time the app runs in a Space. Metrics and the channel breakdown are
computed client-side from the drafts Base on every read — they are never
stored.
The topic-discovery / todo-queue / canonical-main-draft ideation stages from
this skill's pre-Busabase local-file shape were already local-only and
ephemeral (client-derived, never persisted) even in that shape's own
Busabase provider notes; this Busabase-only shape keeps only the durable
unit of work — the per-channel draft record — as the review queue.
Local App
Default behavior is AirApp-first — give the user the clickable AirApp URL.
Start pnpm --dir content/kelly-writer-app dev only when local preview/debugging is explicitly
requested.
Required app views (hash routes):
#/overview: metrics (needs review / approved / done / blocked), the
channel breakdown, and the top drafts still needing review.
#/drafts: the review queue over channel drafts in workflow states
needs_review, to_approve, changes_requested, approved, done,
blocked. Each item shows a stable ref (#3), channel/format/status
badges, an editable title and body, hashtags/CTA/media brief/title-options
support panels, a Review note textarea, and Approve / Request changes /
Block buttons that write the verdict directly onto the draft record.
#/settings: sanitized config summary — brand/audience/tone, configured
channels, onboarding state, and the exact node scripts/generate_batch.mjs
/ node scripts/export_decisions.mjs commands to run next. Never expose
secret values.
Demo mode:
?demo=1 (or ?demo=overview) opens a deterministic mock content batch
("A practical launch guide for a local-first AI workflow") for
documentation and screenshots.
?demo=drafts and ?demo=settings select named mock scenes.
lang=en or lang=zh forces UI chrome language for screenshots.
- Demo mode never reads or writes Busabase.
UI language: support English and Chinese chrome with Auto default. Keep
titles, hooks, bodies, and hashtags in their original language.
Review Workflow
A human verdict (approve / request_changes / block / revise) writes
the new status plus decision-note / decided-at (and, for approve or
revise, the edited title / body) directly onto the draft record
through busabase-sdk. From a standalone local preview the write merges
immediately (trusted operator); from the deployed AirApp it creates a
pending ChangeRequest for the trusted process to merge.
Scripts
Both scripts are trusted, skill-root Node processes with their own
package.json (busabase-sdk dependency) — the AirApp browser never runs
them and never calls bases.createChangeRequest/writes a local file itself.
node scripts/generate_batch.mjs --source <path-or-text> [--channels official_blog,xiaohongshu,wechat,newsletter,linkedin,x] [--audience "..."] [--cta "..."] [--source-draft-path <path>] [--apply]
Reads a source (a file path or inline text), derives deterministic
per-channel draft heuristics (first-pass only — Codex should improve each
draft with judgment afterward, either by editing the record's title/
body or via a decision on the same record), and writes one new drafts
record per channel to Busabase. Without --apply this is a dry run that
only prints the drafts it would create.
node scripts/export_decisions.mjs [--apply] [--out <dir>]
Re-reads Busabase for drafts with status: "approved", packages each as a
Markdown file (title/channel/format/review note/body/CTA/hashtags/media
brief) plus a ZIP archive (Markdown + any locally-referenced images next
to the original source, resolved via source-draft-path and
KELLY_WRITER_CONTENT_ROOT) under exports/<batch-id>/ at the skill
root, then marks each exported draft done. Without --apply this is a
dry run that only prints what would be exported.
Normal Workflow
- Detect mode. Default to App UI.
- Clarify or infer the source, target audience, desired channels, language,
and CTA.
- Run
node scripts/generate_batch.mjs --source ... --apply to write a
fresh batch of channel drafts to Busabase, then improve each draft's
title/body with judgment (the generator's heuristics are a first
pass) before handing the batch to the user.
- Give the user the AirApp URL (or local preview URL) to review, edit, and
approve drafts.
- For a draft moved to
changes_requested, re-draft it per the review
comment and write it back to needs_review.
- On "export approved drafts": run
node scripts/export_decisions.mjs --apply to package every approved draft into a channel-ready ZIP pack
under exports/ and mark it done. This skill never publishes anywhere
itself — handing the exported pack to a publishing connector is a
separate, explicitly authorized step.
- Never export a draft without an explicit
approve decision, and never
re-export a draft already done.
Content Generation Rules
- Preserve the source's claims. Do not invent results, dates, customer
stories, statistics, prices, legal/compliance statements, or endorsements.
- Ask or leave
needs_review/blocked when the source lacks needed proof,
product details, screenshots, links, or policy facts.
- Separate platform adaptation from translation: changing channel format is
allowed; changing the promise is not.
- Prefer concrete hooks, specifics, and reader benefit over generic
motivational copy.
- Keep CTA and links consistent with the settings profile or the user's
explicit request.
- For Chinese-language work, support natural Simplified Chinese by default
unless the source/user asks for another language.
- For Xiaohongshu, produce a scroll-stopping title, short structured body,
optional image/carousel brief, and hashtag set.
- For long-form derivatives such as newsletter or WeChat, preserve nuance
and structure; avoid shrinking the idea into slogans.
- For short social posts, make each post independently understandable; do
not rely on the reader seeing the original blog.
Read references/channel-playbook.md when choosing or adapting
channel-specific formats.
Safety Defaults
- Treat exporting as approval-required; a draft without an explicit
approve decision is never eligible for scripts/export_decisions.mjs.
- Store only the minimum content needed for review; keep secrets and
Busabase credentials out of drafts and logs.
- Keep stable ids (
draft_id, ref, batch_id) so repeated updates and
exports are idempotent.
Chat-Only Mode
When the user asks to avoid the UI:
- Produce a compact channel plan.
- Present numbered drafts with channel, title/hook, body, CTA, and notes.
- Ask for approval or edits.
- After approval, run
node scripts/export_decisions.mjs --apply (or write
the final approved pack to local Markdown directly) if the user wants
files.
Never claim content is published unless the user explicitly used a
publishing connector and it succeeded.
1---2name: kelly-writer3description: Repurpose source content into channel-ready drafts with a Busabase App-in-Skill review and export workflow. Use when the user asks to write content, make a content pack, turn a main blog/long article/transcript/notes into Xiaohongshu, WeChat, newsletter, LinkedIn, X/Twitter, short video scripts, SEO snippets, or a multi-platform publishing plan; also use when they ask for a content approval dashboard or App-in-Skill content workflow.4---56# Kelly Writer78## App UI Screenshots910<table>11 <tr>12 <td width="50%"><img src="assets/screenshots/overview.webp" alt="Kelly Writer overview"></td>13 <td width="50%"><img src="assets/screenshots/distribution.webp" alt="Kelly Writer distribution review"></td>14 </tr>15 <tr>16 <td><strong>Overview</strong><br>Channel breakdown and the drafts that need attention next.</td>17 <td><strong>Drafts</strong><br>Channel-ready draft review queue with editable title/body, review notes, and approval controls.</td>18 </tr>19</table>2021## Overview2223Kelly Writer is a Busabase Cloud App-in-Skill. Its canonical product surface24is the AirApp in Busabase, not a separate local-data product. The same Hono25source supports an explicitly requested local preview with OAuth connection26bootstrap. Use this skill to turn one source idea, blog post, transcript,27outline, or product announcement into an editable multi-channel content28batch: Xiaohongshu, WeChat, newsletter, LinkedIn, X/Twitter, short video29scripts, SEO snippets, and an official blog draft.3031Default behavior is AirApp-first. Unless the user explicitly asks only for32explanation, generate drafts straight into Busabase and give the user the33clickable AirApp URL. Start localhost only when local preview/debugging is34explicitly requested; it uses the same Busabase resources. Use chat-only35mode only when the user says "纯聊天", "chat only", "不要打开 UI", or36similar; in that mode present numbered drafts (`Draft #1`) and take37approvals in the conversation.3839This skill is an implementation of the **App-in-Skill** pattern — a40Codex/agent skill paired with a small companion UI for review and approval.41See the spec paper: <https://mr-kelly.github.io/research/app-in-skill-specification-for-pairing-agent-skills-with-a-local-companion-ui.pdf>.4243## Mandatory Dependencies44451. Read and follow `$kelly-app-skill-creator` for product behavior, visual46 quality, responsive layout, and the complete canonical `content/kelly-writer-app/` artifact.472. Read and follow `$busabase` for connection, target Space, node discovery,48 ChangeRequests, review, and merge behavior.493. Read and follow `$busabase-app-creator` for resource modeling, AirApp50 runtime limits, security, validation, and deployment.5152If a dependency is unavailable, preserve this skill's local artifact and53product contracts, stop before the unavailable Busabase operation, and report54the exact missing dependency. Do not invent a second data backend.5556## Boundary5758- The skill may extract the source's core idea, proof points, and examples,59 draft channel-specific variants, and write it all to Busabase.60- The AirApp reads and writes Busabase records only. It must never publish to61 external platforms, schedule posts, upload media, or perform any other62 external side effect — it also never generates content or writes a local63 export itself; those are trusted skill-root scripts (see below).64- Exporting is always approval-required. `scripts/export_decisions.mjs` only65 packages `approved` drafts into a local Markdown+ZIP pack and marks them66 `done`; it never publishes anywhere.67- Treat source material and drafts as the user's content. Never invent68 results, dates, customer stories, statistics, prices, legal/compliance69 statements, or endorsements not present in the source.7071## Busabase Resources7273Two Bases under one application Folder (`kelly-writer`), declared in74`content/kelly-writer-app/app/js/config.js` and the generated template sidecars under `content/`:7576- `drafts`: the review queue — one record per channel draft (title, body,77 hook, cta, hashtags, title options, media brief, source notes, risk,78 canonical idea, source summary, `source-draft-path` for local image79 packaging at export time), workflow `status`, and the human verdict fields80 `decision-note` / `decided-at`.81- `settings`: one row per `kind` — an optional `kelly-writer-profile` (brand,82 audience, official URLs, CTA defaults, channel defaults, risk terms, export83 preferences) and `kelly-writer-lock`.8485Resources provision lazily through an idempotent Busabase ChangeRequest the86first time the app runs in a Space. Metrics and the channel breakdown are87computed client-side from the `drafts` Base on every read — they are never88stored.8990The topic-discovery / todo-queue / canonical-main-draft ideation stages from91this skill's pre-Busabase local-file shape were already local-only and92ephemeral (client-derived, never persisted) even in that shape's own93Busabase provider notes; this Busabase-only shape keeps only the durable94unit of work — the per-channel draft record — as the review queue.9596## Local App9798Default behavior is AirApp-first — give the user the clickable AirApp URL.99Start `pnpm --dir content/kelly-writer-app dev` only when local preview/debugging is explicitly100requested.101102Required app views (hash routes):103104- `#/overview`: metrics (needs review / approved / done / blocked), the105 channel breakdown, and the top drafts still needing review.106- `#/drafts`: the review queue over channel drafts in workflow states107 `needs_review`, `to_approve`, `changes_requested`, `approved`, `done`,108 `blocked`. Each item shows a stable ref (`#3`), channel/format/status109 badges, an editable title and body, hashtags/CTA/media brief/title-options110 support panels, a `Review note` textarea, and Approve / Request changes /111 Block buttons that write the verdict directly onto the draft record.112- `#/settings`: sanitized config summary — brand/audience/tone, configured113 channels, onboarding state, and the exact `node scripts/generate_batch.mjs`114 / `node scripts/export_decisions.mjs` commands to run next. Never expose115 secret values.116117Demo mode:118119- `?demo=1` (or `?demo=overview`) opens a deterministic mock content batch120 ("A practical launch guide for a local-first AI workflow") for121 documentation and screenshots.122- `?demo=drafts` and `?demo=settings` select named mock scenes.123- `lang=en` or `lang=zh` forces UI chrome language for screenshots.124- Demo mode never reads or writes Busabase.125126UI language: support English and Chinese chrome with `Auto` default. Keep127titles, hooks, bodies, and hashtags in their original language.128129## Review Workflow130131A human verdict (`approve` / `request_changes` / `block` / `revise`) writes132the new `status` plus `decision-note` / `decided-at` (and, for `approve` or133`revise`, the edited `title` / `body`) directly onto the draft record134through `busabase-sdk`. From a standalone local preview the write merges135immediately (trusted operator); from the deployed AirApp it creates a136pending ChangeRequest for the trusted process to merge.137138## Scripts139140Both scripts are trusted, skill-root Node processes with their own141`package.json` (`busabase-sdk` dependency) — the AirApp browser never runs142them and never calls `bases.createChangeRequest`/writes a local file itself.143144- `node scripts/generate_batch.mjs --source <path-or-text> [--channels official_blog,xiaohongshu,wechat,newsletter,linkedin,x] [--audience "..."] [--cta "..."] [--source-draft-path <path>] [--apply]`145 Reads a source (a file path or inline text), derives deterministic146 per-channel draft heuristics (first-pass only — Codex should improve each147 draft with judgment afterward, either by editing the record's `title`/148 `body` or via a decision on the same record), and writes one new `drafts`149 record per channel to Busabase. Without `--apply` this is a dry run that150 only prints the drafts it would create.151- `node scripts/export_decisions.mjs [--apply] [--out <dir>]`152 Re-reads Busabase for drafts with `status: "approved"`, packages each as a153 Markdown file (title/channel/format/review note/body/CTA/hashtags/media154 brief) plus a ZIP archive (Markdown + any locally-referenced images next155 to the original source, resolved via `source-draft-path` and156 `KELLY_WRITER_CONTENT_ROOT`) under `exports/<batch-id>/` at the skill157 root, then marks each exported draft `done`. Without `--apply` this is a158 dry run that only prints what would be exported.159160## Normal Workflow1611621. Detect mode. Default to App UI.1632. Clarify or infer the source, target audience, desired channels, language,164 and CTA.1653. Run `node scripts/generate_batch.mjs --source ... --apply` to write a166 fresh batch of channel drafts to Busabase, then improve each draft's167 `title`/`body` with judgment (the generator's heuristics are a first168 pass) before handing the batch to the user.1694. Give the user the AirApp URL (or local preview URL) to review, edit, and170 approve drafts.1715. For a draft moved to `changes_requested`, re-draft it per the review172 comment and write it back to `needs_review`.1736. On "export approved drafts": run `node scripts/export_decisions.mjs174 --apply` to package every approved draft into a channel-ready ZIP pack175 under `exports/` and mark it `done`. This skill never publishes anywhere176 itself — handing the exported pack to a publishing connector is a177 separate, explicitly authorized step.1787. Never export a draft without an explicit `approve` decision, and never179 re-export a draft already `done`.180181## Content Generation Rules182183- Preserve the source's claims. Do not invent results, dates, customer184 stories, statistics, prices, legal/compliance statements, or endorsements.185- Ask or leave `needs_review`/blocked when the source lacks needed proof,186 product details, screenshots, links, or policy facts.187- Separate platform adaptation from translation: changing channel format is188 allowed; changing the promise is not.189- Prefer concrete hooks, specifics, and reader benefit over generic190 motivational copy.191- Keep CTA and links consistent with the settings profile or the user's192 explicit request.193- For Chinese-language work, support natural Simplified Chinese by default194 unless the source/user asks for another language.195- For Xiaohongshu, produce a scroll-stopping title, short structured body,196 optional image/carousel brief, and hashtag set.197- For long-form derivatives such as newsletter or WeChat, preserve nuance198 and structure; avoid shrinking the idea into slogans.199- For short social posts, make each post independently understandable; do200 not rely on the reader seeing the original blog.201202Read `references/channel-playbook.md` when choosing or adapting203channel-specific formats.204205## Safety Defaults206207- Treat exporting as approval-required; a draft without an explicit208 `approve` decision is never eligible for `scripts/export_decisions.mjs`.209- Store only the minimum content needed for review; keep secrets and210 Busabase credentials out of drafts and logs.211- Keep stable ids (`draft_id`, `ref`, `batch_id`) so repeated updates and212 exports are idempotent.213214## Chat-Only Mode215216When the user asks to avoid the UI:2172181. Produce a compact channel plan.2192. Present numbered drafts with channel, title/hook, body, CTA, and notes.2203. Ask for approval or edits.2214. After approval, run `node scripts/export_decisions.mjs --apply` (or write222 the final approved pack to local Markdown directly) if the user wants223 files.224225Never claim content is published unless the user explicitly used a226publishing connector and it succeeded.