@rubar/lavish-publish-cf 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/explainer-image.png +0 -0
- package/package.json +58 -0
- package/scripts/install.sh +168 -0
- package/skill/SKILL.md +128 -0
- package/skill/references/cloudflare.md +99 -0
- package/skill/references/design-md.md +56 -0
- package/skill/references/manage.md +68 -0
- package/skill/references/themes.md +77 -0
- package/worker/CLAUDE.md +64 -0
- package/worker/README.md +222 -0
- package/worker/cli/index.js +592 -0
- package/worker/src/index.ts +1374 -0
- package/worker/tsconfig.json +19 -0
- package/worker/wrangler.toml +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nathan Kettles
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# lavish-publish-cf
|
|
2
|
+
|
|
3
|
+
Self-host a Cloudflare Worker that publishes themed HTML pages on your own domain — plus the Claude Code skill that drives it.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
API-compatible with [htmlship.com](https://htmlship.com) (same shape, same CLI verbs), but runs on **your** Cloudflare account with **your** KV. No third party in the loop, no monthly fee, no vendor lock-in.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
lavish-publish-cf/
|
|
11
|
+
├── worker/ ← Cloudflare Worker (src/index.ts) + Node CLI (cli/index.js)
|
|
12
|
+
└── skill/ ← /publish Claude Code skill
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What you get
|
|
16
|
+
|
|
17
|
+
- A **Worker** (`worker/`) that accepts `POST /api/v1/pages` and serves the resulting page at `/v/<slug>` under a strict CSP (`script-src 'none'`).
|
|
18
|
+
- A zero-dep **CLI** (`worker/cli/index.js`, installs as `publish-cf`) that handles publish, update, delete, list-mine, plus per-page inline comments.
|
|
19
|
+
- An inline **comment** UI on `/v/<slug>` — viewers select text and leave anchored comments; the owner addresses them and runs `publish-cf comments resolve <slug> <id>`.
|
|
20
|
+
- A **Claude Code skill** (`skill/`) that goes from "a markdown brief" / "a description" / "an existing HTML file" to a themed, published page in one command. Picks a theme from [`lavish-themes`](https://github.com/natekettles/lavish-themes), inlines the styling, calls the CLI, reports the URL.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
**Just the CLI** (talks to a worker you've already deployed, or any compatible API):
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm install -g @rubar/lavish-publish-cf
|
|
28
|
+
publish-cf config set --api-base https://your-worker.workers.dev
|
|
29
|
+
publish-cf --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or use it without install: `npx @rubar/lavish-publish-cf publish my-page.html`.
|
|
33
|
+
|
|
34
|
+
**Full self-host** (deploy your own Worker + KV):
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
git clone https://github.com/natekettles/lavish-publish-cf.git
|
|
38
|
+
cd lavish-publish-cf
|
|
39
|
+
./scripts/install.sh
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The installer walks through:
|
|
43
|
+
|
|
44
|
+
1. `npm install` at the repo root (Wrangler + nothing else)
|
|
45
|
+
2. `npx wrangler login` (interactive — opens browser)
|
|
46
|
+
3. `npx wrangler kv namespace create PAGES` and patches `worker/wrangler.toml`
|
|
47
|
+
4. `npm run deploy` — your worker goes live on `*.workers.dev`
|
|
48
|
+
5. `npm install -g .` so `publish-cf` is on your PATH
|
|
49
|
+
6. Optional: symlink `skill/` into `~/.claude/skills/publish` so Claude Code can find it
|
|
50
|
+
|
|
51
|
+
Steps that need your input pause and ask. Steps that don't, run. Re-running is safe — every step detects "already done" and skips.
|
|
52
|
+
|
|
53
|
+
After install, see [`worker/README.md`](worker/README.md) for the full Worker + CLI reference.
|
|
54
|
+
|
|
55
|
+
## How the pieces talk to each other
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
You ──/publish brief.md──▶ Claude Code
|
|
59
|
+
│
|
|
60
|
+
│ reads ~/.lavish-themes/tier{1,2}/<slug>.html
|
|
61
|
+
│ (from lavish-themes)
|
|
62
|
+
▼
|
|
63
|
+
themed HTML
|
|
64
|
+
│
|
|
65
|
+
│ publish-cf publish report.html …
|
|
66
|
+
▼
|
|
67
|
+
your Worker (workers.dev or custom domain)
|
|
68
|
+
│
|
|
69
|
+
│ KV: PAGES namespace
|
|
70
|
+
▼
|
|
71
|
+
https://<your-worker>/v/<slug>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The skill assumes `~/.lavish-themes` exists (the install location used by [`lavish-themes`](https://github.com/natekettles/lavish-themes)). If you skip themes installation, the skill will still work for HTML you supply pre-styled — it just can't pick a theme for you.
|
|
75
|
+
|
|
76
|
+
## Related projects
|
|
77
|
+
|
|
78
|
+
- [`lavish-axi`](https://github.com/kunchenguid/lavish-axi) — local editor and review surface. `/publish loc <source>` (handled by the same skill in `skill/`) saves to `.lavish/` and opens with `lavish-axi` instead of publishing.
|
|
79
|
+
- [`@rubar/lavish-themes`](https://www.npmjs.com/package/@rubar/lavish-themes) — the six theme shells the skill picks from. `npm i -g @rubar/lavish-themes` to get the `lavish-themes` CLI (`list`, `path <slug>`, `copy <slug> [dest]`).
|
|
80
|
+
|
|
81
|
+
## Licence
|
|
82
|
+
|
|
83
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rubar/lavish-publish-cf",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for the lavish-publish-cf Cloudflare Worker — publish themed HTML pages with inline comments to your own worker.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"publish-cf": "worker/cli/index.js",
|
|
7
|
+
"lavish-publish-cf": "worker/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"worker/cli",
|
|
11
|
+
"worker/src",
|
|
12
|
+
"worker/wrangler.toml",
|
|
13
|
+
"worker/tsconfig.json",
|
|
14
|
+
"worker/README.md",
|
|
15
|
+
"worker/CLAUDE.md",
|
|
16
|
+
"scripts",
|
|
17
|
+
"skill",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"explainer-image.png"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "wrangler dev --config worker/wrangler.toml",
|
|
24
|
+
"deploy": "wrangler deploy --config worker/wrangler.toml",
|
|
25
|
+
"tail": "wrangler tail --config worker/wrangler.toml",
|
|
26
|
+
"types": "wrangler types --config worker/wrangler.toml"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@cloudflare/workers-types": "^4.20260101.0",
|
|
30
|
+
"typescript": "^5.6.0",
|
|
31
|
+
"wrangler": "^4.0.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/natekettles/lavish-publish-cf.git"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/natekettles/lavish-publish-cf#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/natekettles/lavish-publish-cf/issues"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"cloudflare",
|
|
46
|
+
"cloudflare-workers",
|
|
47
|
+
"publish",
|
|
48
|
+
"html",
|
|
49
|
+
"static-site",
|
|
50
|
+
"lavish",
|
|
51
|
+
"htmlship"
|
|
52
|
+
],
|
|
53
|
+
"author": "Nathan Kettles",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — set up the lavish-publish-cf worker + CLI + Claude Code skills.
|
|
3
|
+
#
|
|
4
|
+
# Idempotent. Each step detects "already done" and skips. Safe to re-run.
|
|
5
|
+
#
|
|
6
|
+
# Steps:
|
|
7
|
+
# 1. cd worker && npm install
|
|
8
|
+
# 2. npx wrangler login (only if not already logged in)
|
|
9
|
+
# 3. Create the PAGES KV namespace and patch wrangler.toml
|
|
10
|
+
# 4. npx wrangler deploy
|
|
11
|
+
# 5. npm install -g . (puts `publish-cf` on your PATH)
|
|
12
|
+
# 6. Optionally symlink skill/ into ~/.claude/skills/publish
|
|
13
|
+
#
|
|
14
|
+
# Env overrides:
|
|
15
|
+
# LAVISH_PUBLISH_CF_DIR — repo location (default: $(dirname $(dirname $(realpath $0))))
|
|
16
|
+
# SKIP_GLOBAL_CLI=1 — skip step 5 (the global npm install)
|
|
17
|
+
# SKIP_SKILLS=1 — skip step 6 (the Claude Code skill symlinks)
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
REPO_DIR="${LAVISH_PUBLISH_CF_DIR:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
22
|
+
WORKER_DIR="$REPO_DIR/worker"
|
|
23
|
+
SKILL_DIR="$REPO_DIR/skill"
|
|
24
|
+
SKILLS_HOME="$HOME/.claude/skills"
|
|
25
|
+
|
|
26
|
+
# package.json moved to repo root in v0.1.0 — wrangler runs against worker/wrangler.toml.
|
|
27
|
+
PKG_DIR="$REPO_DIR"
|
|
28
|
+
|
|
29
|
+
bold() { printf "\033[1m%s\033[0m\n" "$*"; }
|
|
30
|
+
dim() { printf "\033[2m%s\033[0m\n" "$*"; }
|
|
31
|
+
warn() { printf "\033[33m%s\033[0m\n" "$*"; }
|
|
32
|
+
ok() { printf "\033[32m✓\033[0m %s\n" "$*"; }
|
|
33
|
+
err() { printf "\033[31m✗\033[0m %s\n" "$*" >&2; }
|
|
34
|
+
|
|
35
|
+
ask() {
|
|
36
|
+
local prompt="$1" default="${2:-Y}" reply
|
|
37
|
+
read -r -p "$prompt " reply
|
|
38
|
+
reply="${reply:-$default}"
|
|
39
|
+
[[ "$reply" =~ ^[Yy] ]]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
require() {
|
|
43
|
+
command -v "$1" >/dev/null 2>&1 || { err "Missing dependency: $1"; exit 1; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
bold "lavish-publish-cf installer"
|
|
47
|
+
echo "Repo: $REPO_DIR"
|
|
48
|
+
echo
|
|
49
|
+
|
|
50
|
+
require node
|
|
51
|
+
require npm
|
|
52
|
+
require git
|
|
53
|
+
|
|
54
|
+
# ---- step 1: install worker deps -------------------------------------------
|
|
55
|
+
bold "Step 1: install worker dependencies"
|
|
56
|
+
cd "$PKG_DIR"
|
|
57
|
+
if [[ -d node_modules && -f node_modules/.package-lock.json ]]; then
|
|
58
|
+
ok "node_modules already in place — skipping"
|
|
59
|
+
else
|
|
60
|
+
npm install
|
|
61
|
+
ok "Installed"
|
|
62
|
+
fi
|
|
63
|
+
echo
|
|
64
|
+
|
|
65
|
+
# ---- step 2: wrangler login -------------------------------------------------
|
|
66
|
+
bold "Step 2: authenticate with Cloudflare"
|
|
67
|
+
if npx --no-install wrangler whoami >/dev/null 2>&1; then
|
|
68
|
+
ok "Already logged in: $(npx --no-install wrangler whoami 2>/dev/null | tail -1)"
|
|
69
|
+
else
|
|
70
|
+
warn "Wrangler needs to authenticate. A browser window will open."
|
|
71
|
+
if ask "Run \`npx wrangler login\` now? [Y/n]"; then
|
|
72
|
+
npx wrangler login
|
|
73
|
+
else
|
|
74
|
+
warn "Skipped login. Re-run this script after you've authenticated."
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
echo
|
|
79
|
+
|
|
80
|
+
# ---- step 3: create KV namespace + patch wrangler.toml ----------------------
|
|
81
|
+
bold "Step 3: create the PAGES KV namespace"
|
|
82
|
+
WRANGLER_TOML="$WORKER_DIR/wrangler.toml"
|
|
83
|
+
if grep -q 'REPLACE_WITH_KV_NAMESPACE_ID' "$WRANGLER_TOML"; then
|
|
84
|
+
dim "Creating namespace…"
|
|
85
|
+
ns_output="$(npx wrangler kv namespace create PAGES 2>&1)"
|
|
86
|
+
ns_id="$(printf "%s" "$ns_output" | grep -oE 'id = "[a-f0-9]+"' | head -1 | sed -E 's/id = "([a-f0-9]+)"/\1/')"
|
|
87
|
+
if [[ -z "$ns_id" ]]; then
|
|
88
|
+
err "Could not parse KV namespace id from wrangler output:"
|
|
89
|
+
printf "%s\n" "$ns_output" >&2
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
# macOS sed needs the empty -i argument
|
|
93
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
94
|
+
sed -i '' "s/REPLACE_WITH_KV_NAMESPACE_ID/$ns_id/" "$WRANGLER_TOML"
|
|
95
|
+
else
|
|
96
|
+
sed -i "s/REPLACE_WITH_KV_NAMESPACE_ID/$ns_id/" "$WRANGLER_TOML"
|
|
97
|
+
fi
|
|
98
|
+
ok "Namespace $ns_id wired into wrangler.toml"
|
|
99
|
+
else
|
|
100
|
+
ok "wrangler.toml already has a KV namespace id — skipping"
|
|
101
|
+
fi
|
|
102
|
+
echo
|
|
103
|
+
|
|
104
|
+
# ---- step 4: deploy ---------------------------------------------------------
|
|
105
|
+
bold "Step 4: deploy the worker"
|
|
106
|
+
cd "$PKG_DIR"
|
|
107
|
+
if ask "Run \`npx wrangler deploy --config worker/wrangler.toml\` now? [Y/n]"; then
|
|
108
|
+
npx wrangler deploy --config worker/wrangler.toml
|
|
109
|
+
ok "Deployed"
|
|
110
|
+
else
|
|
111
|
+
warn "Skipped deploy. Run \`cd $PKG_DIR && npm run deploy\` when ready."
|
|
112
|
+
fi
|
|
113
|
+
echo
|
|
114
|
+
|
|
115
|
+
# ---- step 5: global CLI install --------------------------------------------
|
|
116
|
+
if [[ "${SKIP_GLOBAL_CLI:-0}" != "1" ]]; then
|
|
117
|
+
bold "Step 5: install the publish-cf CLI globally"
|
|
118
|
+
if command -v publish-cf >/dev/null 2>&1; then
|
|
119
|
+
ok "publish-cf already on PATH ($(command -v publish-cf))"
|
|
120
|
+
elif ask "Run \`npm install -g .\` from $PKG_DIR? [Y/n]"; then
|
|
121
|
+
cd "$PKG_DIR"
|
|
122
|
+
npm install -g .
|
|
123
|
+
ok "publish-cf installed globally"
|
|
124
|
+
else
|
|
125
|
+
dim "Skipped. You can call it directly: node $WORKER_DIR/cli/index.js …"
|
|
126
|
+
fi
|
|
127
|
+
echo
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# ---- step 6: skill symlink --------------------------------------------------
|
|
131
|
+
if [[ "${SKIP_SKILLS:-0}" != "1" ]]; then
|
|
132
|
+
bold "Step 6: register the /publish Claude Code skill"
|
|
133
|
+
if [[ -d "$SKILLS_HOME" ]]; then
|
|
134
|
+
link="$SKILLS_HOME/publish"
|
|
135
|
+
if [[ -L "$link" ]]; then
|
|
136
|
+
current="$(readlink "$link")"
|
|
137
|
+
if [[ "$current" == "$SKILL_DIR" ]]; then
|
|
138
|
+
ok "$link → $SKILL_DIR (already correct)"
|
|
139
|
+
else
|
|
140
|
+
warn "$link points elsewhere ($current). Re-pointing."
|
|
141
|
+
ln -sfn "$SKILL_DIR" "$link"
|
|
142
|
+
ok "$link → $SKILL_DIR (updated)"
|
|
143
|
+
fi
|
|
144
|
+
elif [[ -e "$link" ]]; then
|
|
145
|
+
warn "$link exists and is not a symlink. Skipping — move it manually to register the skill."
|
|
146
|
+
elif ask "Symlink $link → $SKILL_DIR? [Y/n]"; then
|
|
147
|
+
ln -s "$SKILL_DIR" "$link"
|
|
148
|
+
ok "$link → $SKILL_DIR"
|
|
149
|
+
else
|
|
150
|
+
dim "Skipped skill registration."
|
|
151
|
+
fi
|
|
152
|
+
else
|
|
153
|
+
dim "~/.claude/skills not found — install Claude Code first if you want skill discovery."
|
|
154
|
+
fi
|
|
155
|
+
echo
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# ---- next steps -------------------------------------------------------------
|
|
159
|
+
bold "What's next"
|
|
160
|
+
echo " publish-cf --help CLI reference"
|
|
161
|
+
echo " /publish cf <source> via Claude Code, after symlinking the skill"
|
|
162
|
+
echo " /publish loc <source> same skill, local-only with lavish-axi"
|
|
163
|
+
echo
|
|
164
|
+
bold "Related projects"
|
|
165
|
+
echo " lavish-themes https://github.com/natekettles/lavish-themes (themes/ library)"
|
|
166
|
+
echo " lavish-axi https://github.com/kunchenguid/lavish-axi (local editor)"
|
|
167
|
+
echo
|
|
168
|
+
ok "Install complete."
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: publish
|
|
3
|
+
description: "Generate a styled HTML page from a description, file path, or URL and either open it locally in `lavish-axi` for review or publish it to a self-hosted Cloudflare worker. Use whenever the user asks for a lavish page, styled brief, runbook, mock, prototype, report, manifesto, or any HTML artifact they want to view or share. Default theme is light — never dark unless the prompt explicitly says so. Also handles `/publish list` to manage existing CF pages."
|
|
4
|
+
argument-hint: "[cf|loc] <source-path | url | description> | list"
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# publish
|
|
9
|
+
|
|
10
|
+
One skill, two modes:
|
|
11
|
+
|
|
12
|
+
- **Local** (`/publish loc <source>` or `/publish <source>` when local-ish keywords are present) — generate the HTML, save under `.lavish/`, and open it in `lavish-axi` for review. No Cloudflare involved.
|
|
13
|
+
- **CF** (`/publish cf <source>`) — same generate-and-save, then publish to the self-hosted Cloudflare worker. See `references/cloudflare.md`.
|
|
14
|
+
|
|
15
|
+
Pairs with `/address-comments <slug>` for the reviewer-feedback loop on CF-published pages.
|
|
16
|
+
|
|
17
|
+
If the argument is exactly `list` (or `manage` / `pages`), jump to `references/manage.md`.
|
|
18
|
+
|
|
19
|
+
## Mode resolution
|
|
20
|
+
|
|
21
|
+
Read the first positional token of the argument:
|
|
22
|
+
|
|
23
|
+
- `loc` / `local` → **local** mode. Strip the token; treat the remainder as the source.
|
|
24
|
+
- `cf` / `cloudflare` / `publish` → **CF** mode. Strip the token.
|
|
25
|
+
- `list` / `manage` / `pages` → load `references/manage.md` and follow it.
|
|
26
|
+
|
|
27
|
+
If no mode token is present, scan the rest of the prompt for keywords (case-insensitive):
|
|
28
|
+
|
|
29
|
+
- "publish to cloudflare", "ship", "deploy", "share publicly" → CF.
|
|
30
|
+
- "view locally", "open in lavish", "preview", "review locally" → local.
|
|
31
|
+
|
|
32
|
+
If neither token nor keyword resolves the mode, fire `AskUserQuestion` **once** with destination as the question (Local recommended; CF as the alternative). Do **not** default silently — the whole point of asking once is to avoid accidental publishing.
|
|
33
|
+
|
|
34
|
+
## Inputs
|
|
35
|
+
|
|
36
|
+
The argument (after stripping the mode token) is required and can be:
|
|
37
|
+
|
|
38
|
+
| Form | Example | Treatment |
|
|
39
|
+
| --------------------- | ---------------------------------- | ------------------------------------------------------ |
|
|
40
|
+
| Existing file path | `/path/to/brief.md` | Read it. Use as the source. |
|
|
41
|
+
| HTTP(S) URL | `https://example.com/article` | Fetch via WebFetch; on error fall back to `/crawl4ai`. |
|
|
42
|
+
| Prose / "describe it" | `a 1-page brief comparing X and Y` | Generate content from scratch from the description. |
|
|
43
|
+
|
|
44
|
+
If invoked with no argument at all, prompt with `AskUserQuestion` for a source — do not start generating from an empty prompt.
|
|
45
|
+
|
|
46
|
+
## Check for a project DESIGN.md (before picking a theme)
|
|
47
|
+
|
|
48
|
+
Walk up from `cwd` (bounded by the repo root — stop at `.git`) looking for `DESIGN.md`, `design.md`, or `Design.md`. If one exists, the project's design system is the source of truth — load `references/design-md.md` and follow it. An explicit theme slug in the prompt still overrides DESIGN.md.
|
|
49
|
+
|
|
50
|
+
## Theme inference (aggressive — ask only as a last resort)
|
|
51
|
+
|
|
52
|
+
If the prompt names a theme slug verbatim (`latex`, `terminal`, `water`, `swiss`, `handwritten`, `zine`), use it. Skip the rest of this section.
|
|
53
|
+
|
|
54
|
+
Otherwise, match the prompt against this table and pick silently:
|
|
55
|
+
|
|
56
|
+
| Prompt signal | Theme |
|
|
57
|
+
| ------------------------------------------------------------------------ | --------------- |
|
|
58
|
+
| "runbook", "postmortem", "RFC", CLI/terminal-flavored | **terminal** |
|
|
59
|
+
| "brief", "strategy", "decision", "memo", decisive product/strategy prose | **swiss** |
|
|
60
|
+
| "research", "paper", "abstract", citations, academic | **latex** |
|
|
61
|
+
| "manifesto", "launch", "announcement", marketing-loud | **zine** |
|
|
62
|
+
| "letter", "note", "personal", "journal", handwritten feel | **handwritten** |
|
|
63
|
+
| Generic / neutral / no signal but theme is fine | **water** |
|
|
64
|
+
|
|
65
|
+
If the prompt gives **zero** signal AND there is no DESIGN.md, fire `AskUserQuestion` once with three bucket options:
|
|
66
|
+
|
|
67
|
+
- **Recommended: Editorial** — serif and typographic (maps to `swiss`; `latex` for research-feeling pieces).
|
|
68
|
+
- **Utility** — neutral or technical (maps to `water`; `terminal` for runbooks/CLI).
|
|
69
|
+
- **Expressive** — unconventional (maps to `zine`; `handwritten` for personal notes).
|
|
70
|
+
|
|
71
|
+
All three buckets resolve to **light themes** by default. The user can override by typing a theme slug as `Other`.
|
|
72
|
+
|
|
73
|
+
## Build the page
|
|
74
|
+
|
|
75
|
+
1. Read the matching shell at `~/.lavish-themes/tier{1,2}/<slug>.html` (Tier 1: latex, terminal, water. Tier 2: swiss, handwritten, zine). The themes library installs there via [lavish-themes](https://github.com/natekettles/lavish-themes); if it is missing, point the user at that repo's `scripts/install.sh`.
|
|
76
|
+
2. Replace the body content with the user's actual content, **keeping theme-specific markup conventions intact**.
|
|
77
|
+
3. Overwrite every placeholder string (title, masthead, sample copy).
|
|
78
|
+
|
|
79
|
+
For the per-theme markup rules and the placeholder-replacement checklist, load `references/themes.md` once before authoring.
|
|
80
|
+
|
|
81
|
+
## Save the file
|
|
82
|
+
|
|
83
|
+
- If `cwd/.lavish/` exists or `cwd` is writable, save to `<cwd>/.lavish/<kebab-slug>.html`. Create `.lavish/` if missing.
|
|
84
|
+
- Otherwise, save to `~/.lavish/throwaway/<YYYY-MM-DD>-<kebab-slug>.html` (create the dir if needed).
|
|
85
|
+
- `kebab-slug` derived from the page title — short, lowercase, alphanumeric + hyphens. Match the input file's basename when there is one.
|
|
86
|
+
|
|
87
|
+
## Local mode — open in lavish-axi
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
lavish-axi <path>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
That's it. The CLI starts (or reuses) the local server and opens the browser at the right session URL. Per global CLAUDE.md, observe the rendered page before reporting done — confirm it loaded, confirm the theme is light, confirm the content replaced cleanly. No Cloudflare, no public URL.
|
|
94
|
+
|
|
95
|
+
Reply to the user with:
|
|
96
|
+
|
|
97
|
+
- Local file path
|
|
98
|
+
- Theme used
|
|
99
|
+
- Any design decision worth review (one-liner, optional)
|
|
100
|
+
|
|
101
|
+
## CF mode — publish to Cloudflare
|
|
102
|
+
|
|
103
|
+
Before publishing, load `references/cloudflare.md`. It covers:
|
|
104
|
+
|
|
105
|
+
- Opt-in keyword/flag triggers for password, comments, and expiry (all off by default)
|
|
106
|
+
- `publish-cf publish` command + flag mapping
|
|
107
|
+
- Post-publish report shape
|
|
108
|
+
- CSP / self-containment rules
|
|
109
|
+
- "When the deploy is stale" recovery
|
|
110
|
+
|
|
111
|
+
After publishing, run `open <url>` and reply with the tight summary described in the reference.
|
|
112
|
+
|
|
113
|
+
## Trigger phrases
|
|
114
|
+
|
|
115
|
+
Match on:
|
|
116
|
+
|
|
117
|
+
- "publish this as a lavish page", "make a lavish page", "ship this as a styled page"
|
|
118
|
+
- "deploy this brief", "share this as a published page"
|
|
119
|
+
- "view this locally", "preview this in lavish", "open this in the lavish editor"
|
|
120
|
+
- "/publish list", "manage my published pages", "list my pages"
|
|
121
|
+
|
|
122
|
+
## What NOT to do
|
|
123
|
+
|
|
124
|
+
- Do **not** spawn a subagent for any flow in this skill. `AskUserQuestion` renders in the main thread; subagents can't reach the user.
|
|
125
|
+
- Do **not** publish to CF without an explicit `cf` token or a CF keyword (or a user pick in the destination question). The default route is local.
|
|
126
|
+
- Do **not** publish before opening the file locally to verify it renders.
|
|
127
|
+
- Do **not** use markdown tables in the user-facing reply (the CLI doesn't render them). For long structured output use a `.lavish/` HTML file and `lavish-axi <file>` per global CLAUDE.md.
|
|
128
|
+
- Do **not** improvise a custom palette. Use the chosen theme shell as-is and only swap content. DESIGN.md is the one sanctioned override; see `references/design-md.md`.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Cloudflare publish reference
|
|
2
|
+
|
|
3
|
+
Load when running the `cf` mode of `/publish`. Covers opt-in flags, the publish command, the post-publish report, CSP rules, and recovery when the deployed worker is stale.
|
|
4
|
+
|
|
5
|
+
## Defaults (CF mode)
|
|
6
|
+
|
|
7
|
+
Unless the user opts in (see triggers below):
|
|
8
|
+
|
|
9
|
+
- **Comments: off** — page serves directly under strict CSP, no sidebar.
|
|
10
|
+
- **Password: off** — anyone with the URL can view.
|
|
11
|
+
- **Expiry: none** — page stays up until deleted.
|
|
12
|
+
|
|
13
|
+
## Opt-in triggers in the prompt
|
|
14
|
+
|
|
15
|
+
Detect any of the following (case-insensitive). Strip matched tokens from the source string before treating the remainder as the source-path / URL / description.
|
|
16
|
+
|
|
17
|
+
| Dimension | Keyword triggers | Inline flag form | Effect |
|
|
18
|
+
| --------- | ------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| Password | "with password", "add password", "password on", "enable password" | `password=<value>` / `--password <value>` | Turns password on. Keyword with no value → generate one and share back. Inline value → use it; **do not** echo it back. |
|
|
20
|
+
| Comments | "with comments", "allow comments", "enable comments", "comments on" | `comments=on` | Turns comments on. |
|
|
21
|
+
| Expiry | n/a (too ambiguous in natural language) | `expires=<value>` (`1d` / `7d` / `30d` / `none`, or raw minutes) | Sets expiry. |
|
|
22
|
+
|
|
23
|
+
## Generating passwords
|
|
24
|
+
|
|
25
|
+
When the user opted in by keyword (no value supplied), generate one with:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
openssl rand -base64 12 | tr -d '=+/' | cut -c1-12
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Pass it via `--password`. Then **share it back in the final reply** — the user has no other way to recover it.
|
|
32
|
+
|
|
33
|
+
The "never echo a user-supplied password" rule applies only when the user typed the password into their prompt. Generated passwords must be shared, otherwise they are useless.
|
|
34
|
+
|
|
35
|
+
## Publish command
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
publish-cf publish <path> [--password "..."] [--expires-in <minutes>] [--no-comments]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Flag mapping from the resolved opt-in state:
|
|
42
|
+
|
|
43
|
+
- **Password opted in** (keyword or inline value) → `--password "<value>"`. If opted in by keyword only, generate the value first.
|
|
44
|
+
- **Comments not opted in** (the default) → always pass `--no-comments`. Comments opted in → omit the flag.
|
|
45
|
+
- **Expiry**: `1d` → `--expires-in 1440`; `7d` → `--expires-in 10080`; `30d` → `--expires-in 43200`; none → omit the flag.
|
|
46
|
+
|
|
47
|
+
After publish:
|
|
48
|
+
|
|
49
|
+
- The URL prints to stdout and auto-copies to clipboard.
|
|
50
|
+
- The `owner_key` and `source_path` are written to `~/.publish-cloudflare/keys.json` automatically — `/address-comments` reads `source_path` later to find the local file.
|
|
51
|
+
|
|
52
|
+
## Open and report
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
open <url>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Per global CLAUDE.md, observe the page in the browser before reporting done.
|
|
59
|
+
|
|
60
|
+
Reply to the user with a tight summary:
|
|
61
|
+
|
|
62
|
+
- The URL (one line, no fluff)
|
|
63
|
+
- Slug
|
|
64
|
+
- Password (if set) — quote it so the user can copy it (only when generated by us)
|
|
65
|
+
- Expiry (if set) — human-readable
|
|
66
|
+
- Comments — on/off
|
|
67
|
+
- Local file path
|
|
68
|
+
- _Optional one-liner if you'd like to call out a design decision worth review._
|
|
69
|
+
|
|
70
|
+
## Self-containment / CSP rules
|
|
71
|
+
|
|
72
|
+
The artifact is served under a strict CSP:
|
|
73
|
+
|
|
74
|
+
- `script-src 'none'` — inline `<script>` and external CDN scripts will not execute.
|
|
75
|
+
- `style-src 'self' 'unsafe-inline' https:` — inline CSS is fine, HTTPS CDN stylesheets are fine, relative paths are not.
|
|
76
|
+
|
|
77
|
+
Practical rules:
|
|
78
|
+
|
|
79
|
+
- Inline all CSS, or load it via `<link>` to a public HTTPS CDN. Both work.
|
|
80
|
+
- Don't reference local images or relative paths — they won't resolve on the worker.
|
|
81
|
+
- Every Tier 1 / Tier 2 theme shell already satisfies this.
|
|
82
|
+
|
|
83
|
+
If the artifact relies on JS to function (a chart library, an interactive widget), warn the user before publishing — it will render as a static skeleton, not as the live version.
|
|
84
|
+
|
|
85
|
+
## When the deploy is stale
|
|
86
|
+
|
|
87
|
+
If `publish-cf publish` returns a 404 on the comments endpoint after publish, the worker is older than the comments feature:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
cd ~/.lavish-publish-cf/worker && npx wrangler deploy
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Then retry.
|
|
94
|
+
|
|
95
|
+
## Related references
|
|
96
|
+
|
|
97
|
+
- `references/manage.md` — list/manage existing CF pages.
|
|
98
|
+
- `references/themes.md` — placeholder-replacement checklist (CSP requires sample copy be purged).
|
|
99
|
+
- `references/design-md.md` — when a project's DESIGN.md drives styling instead of a theme shell.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# DESIGN.md handling
|
|
2
|
+
|
|
3
|
+
Load when a project DESIGN.md was found during the walk-up step in `SKILL.md`. The project's design system wins over the six Lavish theme shells — its tokens, fonts, colors, and components are the source of truth.
|
|
4
|
+
|
|
5
|
+
## Walk-up algorithm
|
|
6
|
+
|
|
7
|
+
From `cwd`, walk **upward** looking for any of:
|
|
8
|
+
|
|
9
|
+
- `DESIGN.md`
|
|
10
|
+
- `design.md`
|
|
11
|
+
- `Design.md`
|
|
12
|
+
|
|
13
|
+
Stop at the first hit. Bound the search by the nearest `.git` directory — don't escape the repo. `lavish-axi` and `lavish-axi design` both surface this path; you can also `grep` for it directly.
|
|
14
|
+
|
|
15
|
+
## Override hierarchy
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
explicit theme slug in user prompt → use that slug, ignore DESIGN.md
|
|
19
|
+
DESIGN.md exists → build from DESIGN.md, skip the theme picker
|
|
20
|
+
neither → fall through to theme inference in SKILL.md
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If the user explicitly names one of the six theme slugs (`latex`, `terminal`, `water`, `swiss`, `handwritten`, `zine`) in their original prompt, that overrides DESIGN.md — proceed with the named template.
|
|
24
|
+
|
|
25
|
+
## When DESIGN.md is present and not overridden
|
|
26
|
+
|
|
27
|
+
Surface DESIGN.md as the recommended option in the style question instead of the Editorial/Utility/Expressive buckets:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
header: "Style"
|
|
31
|
+
- label: "Recommended: Use <repo>'s DESIGN.md"
|
|
32
|
+
description: "Build the page from the project's design system. Tokens, fonts, and components come from DESIGN.md."
|
|
33
|
+
- label: "Editorial"
|
|
34
|
+
description: "Serif and typographic Lavish theme. LaTeX or Swiss depending on content."
|
|
35
|
+
- label: "Utility"
|
|
36
|
+
description: "Neutral or technical Lavish theme. Water or Terminal depending on content."
|
|
37
|
+
- label: "Expressive"
|
|
38
|
+
description: "Unconventional Lavish theme. Handwritten or Zine depending on content."
|
|
39
|
+
multiSelect: false
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If the prompt provided **no theme signal at all**, you can skip this question and proceed directly with DESIGN.md — the walk-up itself is a strong signal that the user wants project-system styling.
|
|
43
|
+
|
|
44
|
+
## "Build from DESIGN.md" semantics
|
|
45
|
+
|
|
46
|
+
If the user picks the DESIGN.md option (or it's used by default per above), skip the template-shell flow entirely. Build the page directly from the project's design system:
|
|
47
|
+
|
|
48
|
+
- Copy its tokens, components, and markup conventions.
|
|
49
|
+
- Inline the relevant CSS or load the project's stylesheet via HTTPS CDN (subject to the same CSP constraints described in `references/cloudflare.md` if publishing).
|
|
50
|
+
- Keep `<meta name="lavish-design" content="off">` in the head so DaisyUI isn't injected on top.
|
|
51
|
+
|
|
52
|
+
Read DESIGN.md once at the start of authoring. Treat it as opinionated and prescriptive — don't mix Lavish theme conventions in.
|
|
53
|
+
|
|
54
|
+
## Local vs CF mode interaction
|
|
55
|
+
|
|
56
|
+
The DESIGN.md path works identically in both `loc` and `cf` modes. In CF mode, the CSP constraints in `references/cloudflare.md` still apply — inline the CSS or use an HTTPS CDN, never relative paths.
|