brewpage-publish
Publish content to brewpage.app — text, markdown, JSON, file, or multi-file site. Asks namespace and password, returns public URL. Triggers: publish, share link, upload to brewpage, host page, brewpage, publish site, upload site, upload directory, deploy site, сделай публичную ссылку, опубликуй.
By kochetkov-ma · 596 installs
npx skills add kochetkov-ma/claude-brewcode --skill brewpage-publish
Source repository · Upstream listing
brewpage publish
Publish content to brewpage.app — free instant hosting for HTML pages, JSON documents, files, and multi file sites. No sign up required.
Workflow
Step 1: Parse Arguments
Extract from $ARGUMENTS :
ttl N → TTL in days (default: 15 )
entry <filename → entry file for SITE uploads (default: auto detect)
Remaining text → content arg
Step 2: Detect Content Type
Input Type API
content arg is a directory ( test d ) SITE POST /api/sites (dir auto zipped — primary path)
content arg ends with .zip AND file exists ( test f ) SITE POST /api/sites (pre built archive upload)
content arg is a file path AND file exists ( test f ) FILE POST /api/files (multipart)
content arg starts with { or [ JSON POST /api/json
Anything else HTML POST /api/html (format=markdown)
Mode rule: directory/ZIP → SITE. Single file → FILE. POST /api/sites accepts ONLY a multipart archive=@file.zip — there is no raw folder upload, so a directory is auto zipped on the fly (the robust default; archive sealing keeps relative paths intact). Stats per type — SITE (dir): HTML count, total size, entry file. SITE (ZIP): file size, entry override. FILE: size + MIME via file mime type b . TEXT/JSON: char count.
Step 3: Show Pre Publish Stats
For HTML/JSON/FILE:
For SITE: detect entry file using priority: 1) entry flag, 2) index.html exists, 3) first .html file alphabetically.
Built static guard (run BEFORE zipping). Publish BUILT output, never project sources:
If the directory contains no .html file at all → FAIL with an explicit error: "No .html found — build the site first, then point at the build output directory." Do not guess an entry.
If the directory looks like un built sources (has package.json + src/ but no top level .html ) → warn and ask the user to point at the build output instead ( dist/ , build/ , out/ , site/ , or public/ ). Do not zip the source tree.
Step 4: Ask Namespace
Use AskUserQuestion :
Auto suggest: generate a meaningful short slug (3 16 chars, lowercase alphanumeric + hyphens) from content context:
File → topic/purpose of the file (e.g. api docs , login page , report q2 )
Text/HTML → main subject or title (e.g. pricing , team intro , changelog )
JSON → data type or schema name (e.g. user config , metrics )
Fallback → project name or directory name if content is ambiguous
Never use random strings or truncated filenames — the slug should be human readable and describe what's being published.
Resolution:
2 , 4 , or empty → suggested slug
1 → public
3 or any other string → use as is
Step 5: Ask Password
Use AskUserQuestion :
Generate random password EXECUTE using Bash tool:
Resolution:
1 , 4 , or empty → no password
2 → use generated random password
3 or custom text → use as is
Step 6: Publish and Save Token (secure)
SECURITY: The ownerToken MUST NEVER appear in conversation output. The bash blocks below handle curl + token parsing + history save atomically; the LLM sees only the URL. Each block sets PASS H first (empty array when no password) and uses "${PASS H[@]}" quoted — passwords are never string interpolated into the command. The site dir zip excludes ( .git/ , .env , etc.) are also a secret leak safeguard — they keep credentials and VCS data out of the published archive.
6a. Init history file (run once, before the publish block) — EXECUTE using Bash tool:
Then run ONE of the following publish blocks based on detected type. Each assumes HISTORY FILE already exists from 6a.
HTML/Markdown text — EXECUTE using Bash tool:
JSON — EXECUTE using Bash tool:
File — EXECUTE using Bash tool:
Site (directory) — EXECUTE using Bash tool:
Site (ZIP file) — EXECUTE using Bash tool:
Step 7: Output Result
Success (bash printed OK {url} ):
Success for SITE (bash printed OK {url} Files: {count} ):
For a private (non public ) namespace, append one short line after the link (skip if the reply must stay ultra brief): Unlisted link — anyone who has it can open it, but it's not in the gallery or search. Want it discoverable? Publish to public .
NEVER print the ownerToken in conversation. The token lives only in the history file.
Error (bash printed FAILED: ... ):
Notes
Always use absolute file paths with curl F "file=@..." .
Use jq n arg c "$CONTENT" '{content: $c}' to safely encode text content. format is a query param , not a body field — /api/html ignores any format key inside the JSON body and reads only ?format= from the URL. Wrong location = server applies default html and stores your markdown as raw text.
TTL default is 15 days. Namespace must be alphanumeric (3 32 chars), default: suggested (private) namespace.
To delete a published page, find the owner token in .claude/brewpage history.md and use the delete command shown in that file's header.
To update a published site , PUT the new bundle to the same site URL ( PUT /api/sites/{ns}/{id} ) with your X Owner Token — the uploaded bundle fully replaces the file set (adds new files, removes absent ones, overwrites matching) and the link never changes. No DELETE then POST needed.
Sites: directory is the primary input — it is auto zipped (the only thing POST /api/sites accepts), which seals relative paths. A pre built .zip is the alternative input, uploaded as is. Always publish BUILT output ( dist/ , build/ , out/ , site/ , public/ ), never project sources.
The auto zip excludes .git/ , .env / .env. , node modules/ , editor/OS junk, sourcemaps and logs — a deliberate secret leak safeguard so credentials and VCS history never reach the public archive.
Entry file detection: entry override index.html first .html alphabetically.
SITE URL — NO trailing slash. API returns .link = "https://brewpage.app/public/<id " without trailing / . Appending / routes to brewpage.app's own landing page; the JS redirect that rescues the no slash form does NOT fire for the slash dir form → site becomes inaccessible.
SITE verification cannot be done via curl . The no slash URL serves the BrewPage landing HTML with an inline JS redirect that only executes in a real browser. Verify with Playwright / browser navigate , or fetch <url /index.html explicitly.
Powered by
[brewpage.app](https://brewpage.app) Free instant hosting — HTML, JSON, files, sites, KV. No sign up.
[brewcode](https://github.com/kochetkov ma/claude brewcode) Claude Code plugin suite — infinite tasks, code review, skills, hooks.