--- name: brewpage-publish description: "Publish content to brewpage.app โ€” text, markdown, JSON, or file. Asks namespace and password, returns public URL. Triggers: publish, share link, upload to brewpage, host page, brewpage." argument-hint: " [--ttl N]" user-invocable: true allowed-tools: Read, Bash, AskUserQuestion, Glob model: haiku --- # brewpage Publish content to **brewpage.app** โ€” free instant hosting for HTML pages, JSON documents, and files. No sign-up required. ## Workflow ### Step 1: Parse Arguments Extract from `$ARGUMENTS`: - `--ttl N` โ†’ TTL in days (default: `15`) - Remaining text โ†’ `content_arg` ### Step 2: Detect Content Type | Input | Type | API | |-------|------|-----| | `content_arg` is a 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) | For FILE: get file size and MIME type via Bash (`file --mime-type -b`). For TEXT/JSON: count characters. ### Step 3: Show Pre-Publish Stats ``` ๐Ÿ“Š Content: ยท ยท TTL: days ``` ### Step 4: Ask Namespace Use **AskUserQuestion**: ``` Namespace determines the URL prefix and gallery visibility on brewpage.app. Options: 1) public โ€” visible in gallery (default) 2) {auto-suggested 6-8 char slug} 3) Enter custom namespace 4) Skip โ†’ use public Reply with a number or your custom namespace (alphanumeric, 3-32 chars). ``` 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: - `1`, `4`, or empty โ†’ `public` - `2` โ†’ suggested slug - `3` or any other string โ†’ use as-is ### Step 5: Ask Password Use **AskUserQuestion**: ``` Password protection (if set, page is hidden from gallery): Options: 1) No password (default) 2) Random: {generated 6-char password, e.g. "kx7p2m"} 3) Enter custom password (min 4 chars) 4) Skip โ†’ no password Reply with a number or your custom password. ``` Generate random password **EXECUTE** using Bash tool: ```bash LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | head -c6 2>/dev/null ``` 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 block below handles curl, token parsing, and history saving atomically. The LLM only sees the URL. **HTML/Markdown text** โ€” **EXECUTE** using Bash tool: ```bash HISTORY_FILE=".claude/brewpage-history.md" if [ ! -f "$HISTORY_FILE" ]; then mkdir -p "$(dirname "$HISTORY_FILE")" cat > "$HISTORY_FILE" <<'HEADER' # brewpage.app โ€” Published Pages > Owner tokens allow update/delete. Keep this file private. > Delete: `curl -s -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: TOKEN"` | Date | URL | Owner Token | TTL | |------|-----|-------------|-----| HEADER fi CONTENT=$(cat <<'BREWPAGE_EOF' {content} BREWPAGE_EOF ) PAYLOAD=$(jq -n --arg c "$CONTENT" '{content: $c}') RESPONSE=$(curl -s -X POST "https://brewpage.app/api/html?ns={ns}&ttl={days}&format=markdown" \ -H "Content-Type: application/json" \ {password_header} \ -d "$PAYLOAD") URL=$(echo "$RESPONSE" | jq -r '.link // empty') TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty') if [ -n "$URL" ]; then [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d |" >> "$HISTORY_FILE" echo "โœ… $URL" else echo "โŒ FAILED: $RESPONSE" fi ``` **JSON** โ€” **EXECUTE** using Bash tool: ```bash HISTORY_FILE=".claude/brewpage-history.md" if [ ! -f "$HISTORY_FILE" ]; then mkdir -p "$(dirname "$HISTORY_FILE")" cat > "$HISTORY_FILE" <<'HEADER' # brewpage.app โ€” Published Pages > Owner tokens allow update/delete. Keep this file private. > Delete: `curl -s -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: TOKEN"` | Date | URL | Owner Token | TTL | |------|-----|-------------|-----| HEADER fi RESPONSE=$(curl -s -X POST "https://brewpage.app/api/json?ns={ns}&ttl={days}" \ -H "Content-Type: application/json" \ {password_header} \ -d '{original_json}') URL=$(echo "$RESPONSE" | jq -r '.link // empty') TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty') if [ -n "$URL" ]; then [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d |" >> "$HISTORY_FILE" echo "โœ… $URL" else echo "โŒ FAILED: $RESPONSE" fi ``` **File** โ€” **EXECUTE** using Bash tool: ```bash HISTORY_FILE=".claude/brewpage-history.md" if [ ! -f "$HISTORY_FILE" ]; then mkdir -p "$(dirname "$HISTORY_FILE")" cat > "$HISTORY_FILE" <<'HEADER' # brewpage.app โ€” Published Pages > Owner tokens allow update/delete. Keep this file private. > Delete: `curl -s -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: TOKEN"` | Date | URL | Owner Token | TTL | |------|-----|-------------|-----| HEADER fi RESPONSE=$(curl -s -X POST "https://brewpage.app/api/files?ns={ns}&ttl={days}" \ {password_header} \ -F "file=@/absolute/path/to/file") URL=$(echo "$RESPONSE" | jq -r '.link // empty') TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty') if [ -n "$URL" ]; then [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d |" >> "$HISTORY_FILE" echo "โœ… $URL" else echo "โŒ FAILED: $RESPONSE" fi ``` Replace `{password_header}` with `-H "X-Password: {pass}"` only when password was set; otherwise remove it entirely. ### Step 7: Output Result **Success** (bash printed `โœ… {url}`): ``` โœ… Published! ๐Ÿ”— {url from bash output} ๐Ÿ“ Owner token saved to .claude/brewpage-history.md ``` **NEVER print ownerToken in conversation.** The token is only in the history file. **Error** (bash printed `โŒ FAILED: ...`): ``` โŒ Publish 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: `public`. - 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. --- ## Powered by | | | |-|-| | **[brewpage.app](https://brewpage.app)** | Free instant hosting โ€” HTML, JSON, files, KV. No sign-up. | | **[brewcode](https://github.com/kochetkov-ma/claude-brewcode)** | Claude Code plugin suite โ€” infinite tasks, code review, skills, hooks. |