@hostinger/mcp 3.2.2 → 3.3.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/CHANGELOG.md +7 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/headless/SKILL.md +247 -36
- package/skills/headless/connect/SKILL.md +103 -0
- package/skills/headless/{entry → connect}/bootstrap.mjs +8 -3
- package/skills/headless/connect/tools/antigravity.md +54 -0
- package/skills/headless/connect/tools/claude-code.md +46 -0
- package/skills/headless/connect/tools/codex.md +48 -0
- package/skills/headless/connect/tools/cursor.md +109 -0
- package/skills/headless/connect/tools/devin.md +50 -0
- package/skills/headless/connect/tools/vscode.md +57 -0
- package/skills/headless/report.schema.json +149 -0
- package/skills/headless/run-log.mjs +662 -0
- package/skills/headless/steps/01-check-state/SKILL.md +193 -0
- package/skills/headless/steps/02-refine/SKILL.md +206 -0
- package/skills/headless/steps/03-provision/SKILL.md +62 -0
- package/skills/headless/steps/03-provision/ecommerce/SKILL.md +133 -0
- package/skills/headless/steps/03-provision/webapp/SKILL.md +154 -0
- package/skills/headless/steps/03-provision/wordpress/SKILL.md +100 -0
- package/skills/headless/steps/04-content-model/SKILL.md +150 -0
- package/skills/headless/steps/04-content-model/ecommerce/SKILL.md +75 -0
- package/skills/headless/steps/04-content-model/wordpress/SKILL.md +178 -0
- package/skills/headless/steps/05-design/SKILL.md +455 -0
- package/skills/headless/steps/05-design/logo-kit.mjs +201 -0
- package/skills/headless/steps/06-frontend/SKILL.md +131 -0
- package/skills/headless/steps/07-validate/SKILL.md +53 -0
- package/skills/headless/steps/08-deploy/SKILL.md +171 -0
- package/skills/headless/steps/09-done/SKILL.md +151 -0
- package/src/core/skills.js +164 -24
- package/src/core/skills.ts +164 -24
- package/src/servers/agency-hosting.js +1 -1
- package/src/servers/agency-hosting.ts +1 -1
- package/src/servers/agent-readiness.js +1 -1
- package/src/servers/agent-readiness.ts +1 -1
- package/src/servers/all.js +1 -1
- package/src/servers/all.ts +1 -1
- package/src/servers/billing.js +1 -1
- package/src/servers/billing.ts +1 -1
- package/src/servers/dns.js +1 -1
- package/src/servers/dns.ts +1 -1
- package/src/servers/domains.js +1 -1
- package/src/servers/domains.ts +1 -1
- package/src/servers/ecommerce.js +1 -1
- package/src/servers/ecommerce.ts +1 -1
- package/src/servers/horizons.js +1 -1
- package/src/servers/horizons.ts +1 -1
- package/src/servers/hosting.js +1 -1
- package/src/servers/hosting.ts +1 -1
- package/src/servers/mail.js +1 -1
- package/src/servers/mail.ts +1 -1
- package/src/servers/reach.js +1 -1
- package/src/servers/reach.ts +1 -1
- package/src/servers/vps.js +1 -1
- package/src/servers/vps.ts +1 -1
- package/src/servers/wordpress.js +1 -1
- package/src/servers/wordpress.ts +1 -1
- package/skills/headless/entry/skill.md +0 -79
- package/skills/headless/references/DATABASE.md +0 -49
- package/skills/headless/references/DEPLOYMENT.md +0 -34
- package/skills/headless/references/SETUP.md +0 -35
- package/skills/headless/references/STORE.md +0 -52
- package/skills/headless/references/WORDPRESS.md +0 -51
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: headless-step-check-state
|
|
3
|
+
description: "First build step of the Hostinger Headless flow, run right after connect. Works out where a given domain's build stands — is a run already in progress, does the site already exist, how far did it get — by reconciling the global workspace state under ~/.hostinger/<domain>/ with what the Hostinger connector reports, then guides the user to continue, switch or buy a domain, or start over. Owns all resume/position logic for the flow."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# headless-step-check-state
|
|
7
|
+
|
|
8
|
+
The first build step of the Hostinger Headless flow (orchestrated by `hostinger.com/ai/skill.md`),
|
|
9
|
+
run right after connect. Connect has already connected the Hostinger connector and confirmed the
|
|
10
|
+
account can build. Your job here is to
|
|
11
|
+
**figure out where this domain stands and agree the route with the user** before any new work begins.
|
|
12
|
+
This step owns all resume/position logic — no other stage second-guesses it.
|
|
13
|
+
|
|
14
|
+
Nothing in this step creates, provisions, deploys, or deletes anything. It reads and guides.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Where state lives
|
|
19
|
+
|
|
20
|
+
Everything for a domain lives under the global site workspace **`~/.hostinger/<domain>/`** — one
|
|
21
|
+
directory per domain, under the user's home (`~` = `$HOME`), **not** the current working directory.
|
|
22
|
+
The directory name **is** the domain, so `state.json` inside does not store it. Because it's global,
|
|
23
|
+
`~/.hostinger/` holds every site the user has built and each one is discoverable and resumable from
|
|
24
|
+
any directory — no collisions.
|
|
25
|
+
|
|
26
|
+
`state.json` shape (`stage` is the *next* stage to run; `provision` is the background task's status,
|
|
27
|
+
present only once provision has fired):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{ "tool": "claude-code", "stage": "content_model",
|
|
31
|
+
"provision": { "status": "in_progress", "domain": "<domain>", "started_at": "…" },
|
|
32
|
+
"updated_at": "…" }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`provision.status` is `in_progress` or `completed` — but it is a **hint**: always re-verify it against
|
|
36
|
+
`hosting_websites_list-setups` (Step 1) before trusting it to gate an instance-barrier step.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Step 0 — Start the run log
|
|
41
|
+
|
|
42
|
+
Before anything else, start a run and the stage (see *Run log and build report* in the top-level
|
|
43
|
+
`SKILL.md`):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
node <skill>/run-log.mjs <domain> run-start --tool <tool> --model <model> --skill <skill version>
|
|
47
|
+
node <skill>/run-log.mjs <domain> stage-start check_state
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If an earlier run on this domain never wrote its report, `run-start` closes it as `abandoned` first.
|
|
51
|
+
|
|
52
|
+
Then send any build reports that haven't reached Hostinger yet (that abandoned run, or a send that
|
|
53
|
+
failed last time):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
node <skill>/run-log.mjs <domain> unsent
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Each line is `<run> <path>`. For each, send the file through `hosting_headless_submit-build-report`
|
|
60
|
+
and record the result, as described in *Run log and build report* in the top-level `SKILL.md`. One
|
|
61
|
+
try each. Don't ask first; if any went out, say so in one line (e.g. "Sent 1 earlier build report to
|
|
62
|
+
Hostinger."). Don't let a failure hold up this step.
|
|
63
|
+
Check-state runs once per session. A follow-up after the final report, in the same session, doesn't
|
|
64
|
+
come back here: it calls `run-resume` (see *Run log and build report* in the top-level `SKILL.md`).
|
|
65
|
+
|
|
66
|
+
## Step 1 — Is a build actively running right now? (check this before anything else)
|
|
67
|
+
|
|
68
|
+
Keep two things separate, and **never guess from a timestamp.**
|
|
69
|
+
|
|
70
|
+
- **A platform build actively in progress** — the only *reliable* "running now" signal. Check
|
|
71
|
+
`hosting_websites_list-setups { "domain": "<domain>" }` and exact-match the domain row:
|
|
72
|
+
- `status: running` → provisioning is underway **right now** (a background provision from an earlier
|
|
73
|
+
turn). Don't start a second one — reconcile `state.json`'s `provision` to `in_progress`, and
|
|
74
|
+
either resume prepare work while it finishes or poll it to `completed` at the barrier.
|
|
75
|
+
- `status: failed` → not running; route to provision to self-heal (Step 3).
|
|
76
|
+
- `completed` → provision is done; set `provision.status` to `completed`. Instance-barrier steps
|
|
77
|
+
(content push, frontend data, deploy) are now unblocked.
|
|
78
|
+
- no row → not provisioned yet; carry on.
|
|
79
|
+
|
|
80
|
+
This check **is** the source of truth for `provision.status`: a stored `in_progress`/`completed` in
|
|
81
|
+
`state.json` is only a hint until this reconciles it.
|
|
82
|
+
|
|
83
|
+
- **A `.lock`** — `~/.hostinger/<domain>/.lock`. It only tells you *a previous run
|
|
84
|
+
touched this domain*. **Do not judge it by age** — a 2-minute-old lock and a 3-year-old lock get the
|
|
85
|
+
same treatment, and a lock on its own is **not** a reason to interrupt the user. Carry it into Step 5:
|
|
86
|
+
if there's nothing to resume (new build), silently take it over; only if there's real state to resume
|
|
87
|
+
do you also ask *is another Claude window building this domain right now?* (yes → stop; no → take
|
|
88
|
+
over).
|
|
89
|
+
|
|
90
|
+
Once you're actively working this domain, **acquire/refresh the lock** (write the current timestamp),
|
|
91
|
+
and **release it on a clean stop** (Step 6). The lock marks an active session, nothing more.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Step 2 — Read the workspace directory
|
|
96
|
+
|
|
97
|
+
Look at `~/.hostinger/<domain>/`:
|
|
98
|
+
|
|
99
|
+
- **Absent** → no history for this domain in the workspace.
|
|
100
|
+
- **Present** → read `state.json` (its `stage`) and whatever artifacts exist (`brief.md`,
|
|
101
|
+
`site.json`, …). Treat these as a hint, not the truth.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Step 3 — Verify against reality (reality wins)
|
|
106
|
+
|
|
107
|
+
Query the Hostinger connector — the local files are never trusted on their own:
|
|
108
|
+
|
|
109
|
+
- **Does the site exist?** `hosting_websites_list { "domain": "<domain>" }`. ⚠️ This filter is a
|
|
110
|
+
**case-insensitive substring match**, not exact — so `test.com` would also return
|
|
111
|
+
`shop.test.com`. **Compare each returned row's `domain` to your target for an *exact* match**
|
|
112
|
+
(case-insensitive) and ignore the rest. No exact row ⇒ the domain is free. An exact row ⇒ the site
|
|
113
|
+
exists; note its `website_type` (`headless`, `wordpress`, `nodejs`, …) and `username`.
|
|
114
|
+
- **How far did it get?** If the site exists, check its setup via
|
|
115
|
+
`hosting_websites_list-setups { "domain": "<domain>" }` (exact-match the row the same way):
|
|
116
|
+
- `status: completed` → provisioned; then, depending on backend, check whether a build has succeeded.
|
|
117
|
+
- `status: running` → still provisioning (see Step 1).
|
|
118
|
+
- `status: failed` → provisioning stopped before finishing → route to **provision** to self-heal.
|
|
119
|
+
|
|
120
|
+
Reconcile with the local state. **When they disagree, reality wins** and the local `stage` is
|
|
121
|
+
corrected.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Step 4 — Determine the position
|
|
126
|
+
|
|
127
|
+
Connect always writes `state.json` (with `stage: check_state`) before this step runs, so local
|
|
128
|
+
state is never truly empty. **"Local state: none" here means only connect artifacts exist** —
|
|
129
|
+
`connect.json` + a `state.json` still pointing at `check_state`/`refine`, and **no build artifacts
|
|
130
|
+
yet** (`brief.md`, `site.json`, …).
|
|
131
|
+
|
|
132
|
+
| Local state | Reality (domain) | Position |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| none (connect only) | free | **new build** — start from refine |
|
|
135
|
+
| none (connect only) | site exists | **adopt / resume** — never create a second site; route to the first unfinished stage |
|
|
136
|
+
| build artifacts present, incomplete | matches | **resume** at `state.json`'s `stage` |
|
|
137
|
+
| build artifacts present | disagrees | **reality wins** — recompute the stage from what actually exists |
|
|
138
|
+
| build artifacts present, all done | live site exists | **finished** — this is now an *iterate* request, not a build |
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Step 5 — Guide the user
|
|
143
|
+
|
|
144
|
+
The question to ask depends on whether there is actually **something to resume** — i.e. **build
|
|
145
|
+
artifacts** (`brief.md`, `site.json`, …) or a **live site** for this domain. A stray `.lock` or
|
|
146
|
+
connect-only files are *not* something to resume.
|
|
147
|
+
|
|
148
|
+
- **New build** (no build artifacts and the domain is free) → just proceed to refine. **Don't ask
|
|
149
|
+
resume vs start over** — there's nothing to resume; both would land in the same place. If a leftover
|
|
150
|
+
`.lock` is lying around, silently take it over and continue (optionally mention it in one line).
|
|
151
|
+
- **Something to resume** (build artifacts exist, or a site exists for the domain) → tell the user
|
|
152
|
+
plainly what's there and **ask them to choose**, regardless of how old it is (don't guess from
|
|
153
|
+
timestamps):
|
|
154
|
+
|
|
155
|
+
- **Resume** — pick up at the computed stage. (If a `.lock` is present in this case, also confirm no
|
|
156
|
+
other window is actively building this domain right now.)
|
|
157
|
+
- **Start over / redo a step** — only safe **before anything is provisioned**. If the site is already
|
|
158
|
+
provisioned or live, you **cannot** re-create it: that is forward-only *iterate* (change content or
|
|
159
|
+
design and redeploy), not a fresh build. Never re-provision or overwrite a live site; if the user
|
|
160
|
+
insists on a clean slate for a live domain, explain the limitation and point them at hPanel.
|
|
161
|
+
- **Switch domain** — they want a different domain. A new domain is a **new workspace**
|
|
162
|
+
(`~/.hostinger/<other-domain>/`, with its own `src/`). Hand off to start that domain's run.
|
|
163
|
+
- **Buy a domain** — they want a custom domain instead of the temporary one. Route to the domain
|
|
164
|
+
purchase / `connect-domain` flow, then continue with the chosen domain.
|
|
165
|
+
|
|
166
|
+
When the intent is ambiguous, **ask** — don't guess.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Step 6 — Record and route
|
|
171
|
+
|
|
172
|
+
Write `~/.hostinger/<domain>/state.json` with the agreed next `stage` and a fresh timestamp. If the
|
|
173
|
+
route isn't a new build, record it with `run-log.mjs <domain> run-update --kind resume` (or
|
|
174
|
+
`redesign` / `iterate`), then end the stage with `stage-end check_state ok --note "<the route>"`.
|
|
175
|
+
Then:
|
|
176
|
+
|
|
177
|
+
- **Handing straight to the next stage in this same session?** Keep the `.lock` (you are still
|
|
178
|
+
actively running) and continue.
|
|
179
|
+
- **Stopping and returning control to the user?** (e.g. the next stage isn't built yet, or you are
|
|
180
|
+
waiting on them.) **Delete the `.lock`** — the session is no longer active, and a leftover lock just
|
|
181
|
+
forces an unnecessary "is another window running?" question on the next run. Always release the lock
|
|
182
|
+
on a clean stop; it exists only to mark an actively-running session. A stop is still the end of a
|
|
183
|
+
run: give the final report and save the build report (`09-done`).
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Invariants
|
|
188
|
+
|
|
189
|
+
- **One directory per domain.** The domain is the key; state files never repeat it.
|
|
190
|
+
- **Never create a second site** on a domain that already has one.
|
|
191
|
+
- **Never re-provision or overwrite a live site.** Recovery is forward-only.
|
|
192
|
+
- **Reality beats local state**, always.
|
|
193
|
+
- **When ambiguous, ask.**
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: headless-step-refine
|
|
3
|
+
description: "Step 2 (refine) of the Hostinger Headless flow. Turns the user's seed — often just a domain — into a brief through conversation: asks what they want to build (purpose, audience, tone, pages, products, must-haves; images, local files, links all welcome), recommends the backend that fits (WordPress, Ecommerce, WordPress + Ecommerce, or Webapp for a custom app/SPA), lets the user confirm it, then writes brief.md."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# headless-step-refine
|
|
7
|
+
|
|
8
|
+
Find out what the user wants to build, guess which backend fits, and write it down. This is a
|
|
9
|
+
**conversation, not a form**: the pasted prompt names a domain and nothing else, so everything about
|
|
10
|
+
the site comes from talking to the user.
|
|
11
|
+
|
|
12
|
+
**Keep it short.** The only thing you must know is *what they are building* — enough to guess the
|
|
13
|
+
backend. Every other question is skippable: the more the user gives, the better the site, but never
|
|
14
|
+
hold the build hostage to refinement. Fill gaps with sensible defaults and say so in the brief.
|
|
15
|
+
|
|
16
|
+
**In:** the domain (the workspace directory name), plus anything the user already shared.
|
|
17
|
+
**Out:** `~/.hostinger/<domain>/brief.md`.
|
|
18
|
+
|
|
19
|
+
Nothing in this step touches Hostinger. It only asks, decides and writes.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Step 1 — Read the workspace, then the resume check
|
|
24
|
+
|
|
25
|
+
Read everything already in `~/.hostinger/<domain>/` (`state.json`, `connect.json`, any notes, images
|
|
26
|
+
or files the user dropped there) — it all counts as input.
|
|
27
|
+
|
|
28
|
+
If `brief.md` already exists for this domain, **don't overwrite it**. Show the user a three-line
|
|
29
|
+
summary (what the site is, its pages, the suggested backend) and ask: *keep it, or change something?*
|
|
30
|
+
Keep → go to Step 6. Change → edit only what they asked for, then re-confirm (Step 5).
|
|
31
|
+
|
|
32
|
+
If the site is already provisioned (`site.json` exists), the backend is **fixed** — never change the
|
|
33
|
+
Suggested backend on a provisioned site. Explain that switching backends means a new domain.
|
|
34
|
+
|
|
35
|
+
## Step 2 — Gather what you already have
|
|
36
|
+
|
|
37
|
+
Before asking anything, collect what the user has already given you, so you don't ask for it again:
|
|
38
|
+
|
|
39
|
+
- Anything in the pasted prompt beyond the domain (a sentence, a link, a screenshot).
|
|
40
|
+
- Files in the current project that look like site material (copy, logos, product lists, images).
|
|
41
|
+
- What the agent already knows about the user from local memory or this conversation.
|
|
42
|
+
- The domain name itself — a hint only (`coral-pottery.com` suggests pottery), never a conclusion.
|
|
43
|
+
|
|
44
|
+
## Step 3 — Ask
|
|
45
|
+
|
|
46
|
+
If Step 2 already answers *what are you building* and *will they sell*, skip straight to Step 4.
|
|
47
|
+
|
|
48
|
+
Otherwise open with one line: *"Tell me what you want to build — a sentence is enough. Links,
|
|
49
|
+
screenshots or files help too. Skip anything you don't want to answer."*
|
|
50
|
+
|
|
51
|
+
Ask in **short rounds of 2–3 questions**, and adapt each round to the previous answers. If your tool
|
|
52
|
+
has a structured question UI (multiple choice with an "other" field), use it for the choice-shaped
|
|
53
|
+
questions. **At most 3 rounds**, and stop early as soon as you can guess the backend and the user
|
|
54
|
+
seems done — "just build it", "you decide", or a skipped question means *use defaults*, not *ask
|
|
55
|
+
again*.
|
|
56
|
+
|
|
57
|
+
**Round 1 — what it is (the only required round):**
|
|
58
|
+
|
|
59
|
+
1. **What are you building?** The business or project, and what the site is for.
|
|
60
|
+
2. **Will you sell anything on it?** Physical products, digital products, services with checkout —
|
|
61
|
+
or nothing. *This decides the backend.* Skip it only when the answer is already unmistakable
|
|
62
|
+
("an online shop for my candles", "my personal blog"); when in doubt, ask.
|
|
63
|
+
3. **Who is it for?** The audience, and how they'll find the site. *(skippable)*
|
|
64
|
+
|
|
65
|
+
**Round 2 — what goes on it (optional, adapt to Round 1):**
|
|
66
|
+
|
|
67
|
+
- **Content they'll publish over time** — blog posts, articles, news, recipes, portfolio pieces,
|
|
68
|
+
case studies? How often? Or is it a handful of pages that rarely change?
|
|
69
|
+
- **Pages** — propose a page list from what you know and ask them to add or remove.
|
|
70
|
+
- If they sell: **what and how many** — a rough product count, categories, whether products have
|
|
71
|
+
variants (size, colour), and the shipping regions.
|
|
72
|
+
|
|
73
|
+
**Round 3 — how it should feel (optional, skip what you already know):**
|
|
74
|
+
|
|
75
|
+
This is the round that captures **all design input** — the design step reads it from `brief.md` to derive
|
|
76
|
+
the design directions, so it is **not** asked again later; gather it here:
|
|
77
|
+
|
|
78
|
+
- **Reference & look** — a **site they like (URL)** and what to take from it, plus a few adjectives for
|
|
79
|
+
the vibe. Screenshots welcome.
|
|
80
|
+
- **Colours & brand** — brand/accent colours, logo, fonts, existing copy, photos. Note where they live.
|
|
81
|
+
- **Must-haves and no-gos** — contact form, newsletter, bookings, languages, things to avoid.
|
|
82
|
+
|
|
83
|
+
If the user asks for something outside the scope (user accounts, member areas, bookings with a
|
|
84
|
+
calendar backend, user-generated content), say plainly that the headless flow doesn't support it yet
|
|
85
|
+
and record it under **Out of scope** — don't promise it.
|
|
86
|
+
|
|
87
|
+
## Step 4 — Recommend the backend
|
|
88
|
+
|
|
89
|
+
Pick the backend that fits what the user told you:
|
|
90
|
+
|
|
91
|
+
| Backend | Choose when | What it gives the owner |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| **WordPress** | No selling. The owner publishes and edits content: pages, blog, articles, portfolio. | Headless WordPress at `/cms` — write and edit content in wp-admin. |
|
|
94
|
+
| **Ecommerce** | Selling is the point, and the rest is a few static pages (About, Contact, FAQ, policies). No ongoing blog. | Hostinger Ecommerce store — products, collections, checkout, payments, shipping, orders. |
|
|
95
|
+
| **WordPress + Ecommerce** | Selling **and** regular editorial content (a blog, guides, lookbooks, recipes) the owner keeps adding to. | Both: WordPress for content, the Ecommerce store for products and checkout. |
|
|
96
|
+
| **Webapp** | A custom **app, SPA or web application** that brings its own code and data — a dashboard, tool, calculator, booking app, a project the user is deploying — **not** a content site or a store. No CMS and no store engine. | A plain Node.js app host (an empty website, no `/cms`), with a MySQL database when the app needs one. The app owns all its routes and data. |
|
|
97
|
+
|
|
98
|
+
Rules of thumb:
|
|
99
|
+
|
|
100
|
+
- **Is it an app, or a site?** A custom application the user builds/brings (its own logic, its own data,
|
|
101
|
+
maybe a database) → **Webapp**. A content site or a shop → one of the three above. When the user says
|
|
102
|
+
"my app / SPA / dashboard / tool", or hands you an existing app project to deploy, that's **Webapp**.
|
|
103
|
+
- **Selling decides first (for sites).** No selling → WordPress. Selling → Ecommerce or
|
|
104
|
+
WordPress + Ecommerce.
|
|
105
|
+
- **Then the content.** Selling plus only a few fixed pages → Ecommerce. Selling plus content the
|
|
106
|
+
owner will keep publishing → WordPress + Ecommerce.
|
|
107
|
+
- **Don't add WordPress "just in case".** A second backend is a second admin to learn. Recommend the
|
|
108
|
+
combination only when the user described ongoing content.
|
|
109
|
+
- Selling a single service by enquiry (no checkout, no cart) is **not** commerce → WordPress with a
|
|
110
|
+
contact page.
|
|
111
|
+
- **Webapp never uses WordPress or a store.** If the user wants a custom app *and* a CMS/shop, that's two
|
|
112
|
+
different sites — build the app as a Webapp and tell them the content/commerce backend is a separate
|
|
113
|
+
domain.
|
|
114
|
+
|
|
115
|
+
**Then set rendering** (write it in the brief's **Rendering** section): **Ecommerce → SSR required**
|
|
116
|
+
(carts/search need per-request rendering — any SSR framework works; Next.js is the default, not a
|
|
117
|
+
requirement); **WordPress → SSG (Astro)** if the site is mostly static and the owner is fine rebuilding
|
|
118
|
+
to publish, otherwise **SSR**; **Webapp → whatever the app is** (SSR for a server app, SPA for a
|
|
119
|
+
client-only app) — it deploys as a Node.js app either way. When unsure, default to **Next.js SSR**.
|
|
120
|
+
|
|
121
|
+
## Step 5 — Confirm in one message
|
|
122
|
+
|
|
123
|
+
Send **one** message that carries both the recommendation and the brief summary:
|
|
124
|
+
|
|
125
|
+
- **Backend** — the choice, the one-line reason, and what it means day to day (where they'll edit
|
|
126
|
+
content and products). Name the alternative if it was a close call.
|
|
127
|
+
- **Brief** — what it is, audience, pages, products (if any), tone. Mark anything you defaulted
|
|
128
|
+
(*"assumed: warm, minimal look"*) so the user sees what they skipped.
|
|
129
|
+
|
|
130
|
+
End with *"Good to go, or anything to change?"* A yes (or silence-equivalent like "go") → write
|
|
131
|
+
`brief.md`. Changes → apply them and write it; don't loop back into questions. If they pick a
|
|
132
|
+
different backend, use theirs and note it in the brief.
|
|
133
|
+
|
|
134
|
+
## Step 6 — Write `brief.md` and hand off
|
|
135
|
+
|
|
136
|
+
Write `brief.md` from the template below. Fill every section from the conversation, defaulting what
|
|
137
|
+
the user skipped and tagging it `(assumed)`; leave out **Products** when nothing is sold. Use the
|
|
138
|
+
user's own words where you can. Then update `state.json`:
|
|
139
|
+
`"stage": "provision"`, fresh `updated_at`.
|
|
140
|
+
|
|
141
|
+
The **Suggested backend** section is the contract with `03-provision/SKILL.md`, which routes on it.
|
|
142
|
+
Its first line must be exactly one of `**WordPress**`, `**Ecommerce**`, `**WordPress + Ecommerce**` or
|
|
143
|
+
`**Webapp**`, followed by an em dash and the one-line reason.
|
|
144
|
+
|
|
145
|
+
## `brief.md` template
|
|
146
|
+
|
|
147
|
+
```markdown
|
|
148
|
+
# Brief — <domain>
|
|
149
|
+
|
|
150
|
+
> Brief agreed with the user on <today>.
|
|
151
|
+
|
|
152
|
+
## Purpose
|
|
153
|
+
<what the site is, who runs it, what it's for — 2–3 sentences>
|
|
154
|
+
|
|
155
|
+
## Audience
|
|
156
|
+
<who visits, how they arrive, what device they mostly use>
|
|
157
|
+
|
|
158
|
+
## Tone
|
|
159
|
+
<voice in a sentence or two>
|
|
160
|
+
|
|
161
|
+
## Design direction
|
|
162
|
+
<look & feel in a few adjectives; **reference site(s)** the user named (URLs) and what to take from them
|
|
163
|
+
(layout, structure, mood); colours / brand palette; any screenshots. This is where ALL design questions
|
|
164
|
+
land — the frontend step reads this and does not re-ask. Write "Agent's choice: <short style>" when the
|
|
165
|
+
user didn't specify.>
|
|
166
|
+
|
|
167
|
+
## Pages
|
|
168
|
+
- **<Page>** — <one-line purpose>
|
|
169
|
+
- …
|
|
170
|
+
|
|
171
|
+
## Products
|
|
172
|
+
<only when selling: what is sold, rough count, categories, variants, shipping regions, payments>
|
|
173
|
+
|
|
174
|
+
## Content
|
|
175
|
+
<what the owner will publish over time and how often — or "A few fixed pages; no ongoing content.">
|
|
176
|
+
|
|
177
|
+
## Must-haves
|
|
178
|
+
- …
|
|
179
|
+
|
|
180
|
+
## Out of scope
|
|
181
|
+
- …
|
|
182
|
+
|
|
183
|
+
## Assets
|
|
184
|
+
<logo, colours, photos, copy the user provided and where they are; "None yet" if none>
|
|
185
|
+
|
|
186
|
+
## Suggested backend
|
|
187
|
+
**<WordPress | Ecommerce | WordPress + Ecommerce | Webapp>** — <one-line reason>.
|
|
188
|
+
<"Confirmed by the user." or "User chose this over the recommended <X> because <reason>.">
|
|
189
|
+
|
|
190
|
+
## Rendering
|
|
191
|
+
**<SSR | SSG>** — <reason>. Rules: **Ecommerce requires SSR** (carts/search need per-request
|
|
192
|
+
rendering — any SSR framework works; Next.js is the default, not a requirement); **WordPress may be
|
|
193
|
+
SSG (Astro)** when the site is mostly static and rarely changes, otherwise **SSR**. Default when
|
|
194
|
+
unsure: **Next.js SSR**.
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Example — WordPress + Ecommerce
|
|
198
|
+
|
|
199
|
+
> A ceramicist sells around 30 handmade pieces and writes a monthly studio journal.
|
|
200
|
+
|
|
201
|
+
```markdown
|
|
202
|
+
## Suggested backend
|
|
203
|
+
**WordPress + Ecommerce** — sells ~30 products with checkout, and the owner publishes a monthly
|
|
204
|
+
studio journal they want to edit themselves.
|
|
205
|
+
Confirmed by the user.
|
|
206
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: headless-step-provision
|
|
3
|
+
description: "Step 3 (provision) of the Hostinger Headless flow. Chooses the backend from brief.md (wordpress | ecommerce | webapp), then routes to the matching subfolder to provision it; writes site.json."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# headless-step-provision
|
|
7
|
+
|
|
8
|
+
Router. The backend is decided in **refine** and recorded in `brief.md`; this step only routes to the
|
|
9
|
+
matching subfolder, which provisions it.
|
|
10
|
+
|
|
11
|
+
**In:** `brief.md`. **Out:** `site.json` (written by the subfolder).
|
|
12
|
+
|
|
13
|
+
**This stage runs in the background.** Provisioning is mostly a poll wait, so it splits in two: a
|
|
14
|
+
**fire** half (detect + create, returns straight away) and a **join** half (poll to `completed`,
|
|
15
|
+
verify, write `site.json`). Fire it, record `provision.in_progress`, then go do the prepare work of
|
|
16
|
+
the other stages (content-model prepare, frontend shell) while it settles. Only cross back to
|
|
17
|
+
the join half when an instance-barrier step needs the live backend. See *Background provision & the
|
|
18
|
+
instance barrier* in the top-level `SKILL.md`. An agent that can't interleave simply does fire and
|
|
19
|
+
join back-to-back — still correct, just no overlap.
|
|
20
|
+
|
|
21
|
+
## Step 1 — Route on the suggested backend
|
|
22
|
+
|
|
23
|
+
Read the brief's **Suggested backend** section:
|
|
24
|
+
|
|
25
|
+
| Suggested backend | Follow |
|
|
26
|
+
|---|---|
|
|
27
|
+
| WordPress | `wordpress/SKILL.md` |
|
|
28
|
+
| Ecommerce | `ecommerce/SKILL.md` |
|
|
29
|
+
| Webapp | `webapp/SKILL.md` |
|
|
30
|
+
|
|
31
|
+
If the section is missing or names something else, go back to **refine** — don't pick one here.
|
|
32
|
+
Tell the user the backend and its one-line reason before provisioning.
|
|
33
|
+
|
|
34
|
+
> **Supported backends: `wordpress`, `ecommerce` and `webapp`.** `webapp` is a plain Node.js app host
|
|
35
|
+
> (an empty website — no CMS, no store, no `/cms`) for any app/SPA that brings its own code and data; it
|
|
36
|
+
> never installs WordPress. PocketBase is not available in this version — refine never proposes it, and
|
|
37
|
+
> there is no `pocketbase/` subfolder; if a brief somehow names it, stop and tell the user rather than
|
|
38
|
+
> improvising it. A **WordPress + Ecommerce** brief has no single combined `start-setup` type yet: stop,
|
|
39
|
+
> tell the user it isn't supported as one site, and offer to continue with just WordPress or just
|
|
40
|
+
> Ecommerce — then route to that one subfolder.
|
|
41
|
+
|
|
42
|
+
## Step 2 — Fire (don't block)
|
|
43
|
+
|
|
44
|
+
Follow the chosen subfolder's `SKILL.md` through **detect + create** (its "start-setup" call). The
|
|
45
|
+
moment the create is accepted and queued, record it in `state.json` and **return** — do not sit on the
|
|
46
|
+
readiness poll:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{ "stage": "content_model",
|
|
50
|
+
"provision": { "status": "in_progress", "domain": "<domain>", "started_at": "<now>" } }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Now go run the prepare work that doesn't need the backend (content-model prepare, frontend
|
|
54
|
+
shell). The subfolder's readiness poll, verification and `site.json` are the **join** half below.
|
|
55
|
+
|
|
56
|
+
## Step 3 — Join (at the instance barrier)
|
|
57
|
+
|
|
58
|
+
When an instance-barrier step is about to run (content-model push, frontend data wiring, deploy) — or
|
|
59
|
+
right away, if the agent can't interleave — finish the subfolder's `SKILL.md`: **poll to `completed`**,
|
|
60
|
+
run its verification, and write `site.json` (which records the chosen `backend`). Then set
|
|
61
|
+
`provision.status` to `"completed"` in `state.json` with a fresh `updated_at`. On resume, treat a
|
|
62
|
+
stored `in_progress` as a hint only — re-poll `hosting_websites_list-setups` for the truth first.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: headless-step-provision-ecommerce
|
|
3
|
+
description: "Provision a Hostinger Ecommerce backend for the Hostinger Headless flow: ensure the hostinger-ecommerce MCP is connected, create the headless website (frontend host) and a Hostinger Ecommerce STORE with a custom (headless) sales channel, set shipping + payment, and record it all in site.json."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# headless-step-provision-ecommerce
|
|
7
|
+
|
|
8
|
+
Provision a **Hostinger Ecommerce** backend for `<domain>` and prove it is ready. Two distinct things:
|
|
9
|
+
|
|
10
|
+
1. A **headless website** (`type: "headless_ecommerce"`) — this is only the **hosting slot for the
|
|
11
|
+
storefront you'll build**. It installs **no store engine** (no `/cms`, no API on the domain).
|
|
12
|
+
2. A **Hostinger Ecommerce store** — a separate object created through the **Ecommerce API**, with a
|
|
13
|
+
**custom (headless) sales channel**. The storefront reads this store's **public Storefront Core V2
|
|
14
|
+
API**, keyed on the sales channel. This is the real backend.
|
|
15
|
+
|
|
16
|
+
**Out:** `site.json` (store id, custom sales channel id, URLs).
|
|
17
|
+
|
|
18
|
+
> ## ⚠️ Prerequisite: the `hostinger-ecommerce` MCP server must be connected
|
|
19
|
+
> Store management is **not** in the `hosting`/`wordpress` MCP groups — it's the separate
|
|
20
|
+
> **`hostinger-ecommerce`** group (`ecommerce_*` ops, OAuth-backed, no API token needed). If those
|
|
21
|
+
> tools aren't available, add the server and **reconnect the session** before continuing:
|
|
22
|
+
> ```bash
|
|
23
|
+
> claude mcp add-json hostinger-ecommerce '{"command":"npx","args":["--package=@hostinger/mcp@latest","hostinger-ecommerce-mcp"],"env":{"USER_AGENT":"extension;claude-code;via-cursor;1.4.2"}}' --scope user
|
|
24
|
+
> ```
|
|
25
|
+
> (For other tools, enable the **Ecommerce** MCP in the Hostinger Connector.) A newly added MCP server
|
|
26
|
+
> only loads on a session restart/reconnect.
|
|
27
|
+
>
|
|
28
|
+
> **Can't restart your own session? Don't block — drive the server directly.** A coding agent usually
|
|
29
|
+
> can't reconnect itself, so instead of stopping, run the stdio server yourself and speak MCP to it:
|
|
30
|
+
> spawn `npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp` (same `USER_AGENT` as above) and
|
|
31
|
+
> send newline-delimited JSON-RPC over its stdin/stdout — `initialize` → the `notifications/initialized`
|
|
32
|
+
> notification → `tools/list` → `tools/call`, where the tools are `search` / `execute` / `multi-execute`
|
|
33
|
+
> (call ops exactly as in-session: `{ "name": "execute", "arguments": { "operation": "…", "params": {…} } }`).
|
|
34
|
+
> It self-authenticates with the same stored Hostinger OAuth creds as the other connected servers, so
|
|
35
|
+
> there is **no token to handle** (keep any secret off the command line). Your sandbox may ask the user
|
|
36
|
+
> to approve spawning it — ask once, then proceed. Only fall back to asking the user to restart if
|
|
37
|
+
> driving the server directly isn't permitted.
|
|
38
|
+
>
|
|
39
|
+
> Verify the tools work (in-session or via the direct drive) with a search — e.g. "list stores" →
|
|
40
|
+
> `ecommerce_stores_list` — before proceeding.
|
|
41
|
+
> The authoritative, always-current build guide is `ecommerce_miscellaneous_custom-storefront-setup-instructions` — read it once per build.
|
|
42
|
+
|
|
43
|
+
All website-lifecycle calls go through the `hosting` connector (`execute`); all store calls through the
|
|
44
|
+
`hostinger-ecommerce` MCP.
|
|
45
|
+
|
|
46
|
+
## Step 1 — The headless website (frontend host)
|
|
47
|
+
|
|
48
|
+
Same lifecycle as `../wordpress/SKILL.md` Steps 1–2, but `type: "headless_ecommerce"`:
|
|
49
|
+
|
|
50
|
+
1. Detect: `hosting_websites_list` / `hosting_websites_list-setups` (exact-match the domain). `completed`
|
|
51
|
+
→ adopt. `running` → poll. `failed` → stop, point at hPanel.
|
|
52
|
+
2. Create (only if absent): `hosting_websites_start-setup { order_id, type: "headless_ecommerce", domain }`
|
|
53
|
+
— **omit `domain`** for a temporary subdomain: the setup generates one itself (don't call a separate
|
|
54
|
+
subdomain op first — passing a generated free subdomain back as `domain` 422s). The response carries
|
|
55
|
+
the generated domain; if it differs from the workspace dir, move `~/.hostinger/<old>/` → `<new>/` and
|
|
56
|
+
update `brief.md`. Poll `list-setups` to `completed`. `website_type` will be `headless`.
|
|
57
|
+
|
|
58
|
+
> Don't expect `/cms` or any API on this domain — it serves the storefront you build (Step 6) and
|
|
59
|
+
> deploy (Step 8).
|
|
60
|
+
|
|
61
|
+
## Step 2 — Resolve or create the store + custom sales channel
|
|
62
|
+
|
|
63
|
+
`ecommerce_stores_list` → reuse an existing store if this build already made one; otherwise create:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{ "operation": "ecommerce_stores_create",
|
|
67
|
+
"params": { "name": "<store name>", "country_code": "US", "company_email": "<email>",
|
|
68
|
+
"company_name": "<store name>", "language": "en",
|
|
69
|
+
"sales_channel": { "type": "custom", "external_id": "<slug>-headless" } } }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This returns `store.id` (`store_*`) **and** a custom `sales_channel.id` (`scha_*`) in one call — the
|
|
73
|
+
custom channel is what the headless storefront is keyed on. (You can also add one later with
|
|
74
|
+
`ecommerce_sales-channels_create { store_id, type:"custom", name }`.) Currency comes from
|
|
75
|
+
`country_code` (`default_currency_code`); all prices are **minor units** (cents).
|
|
76
|
+
|
|
77
|
+
## Step 3 — Store readiness: shipping + payment
|
|
78
|
+
|
|
79
|
+
A store needs ≥1 shipping zone and ≥1 payment method to check out.
|
|
80
|
+
|
|
81
|
+
- **Shipping:** `ecommerce_shipping_set-store { store_id, price }` (minor units; 0 = free).
|
|
82
|
+
- **Payment:** `ecommerce_payments_enable-manual-method { store_id, title }`.
|
|
83
|
+
|
|
84
|
+
> **Known gate — free accounts have no payment method, and therefore no checkout.** On a **free
|
|
85
|
+
> account** (and any account/store without a paid Hostinger Ecommerce plan), `enable-manual-method`
|
|
86
|
+
> returns **HTTP 402**, and checkout later fails with `400 payment_provider_not_configured`. This is
|
|
87
|
+
> **expected, not an error** — payment, and therefore checkout, is simply unavailable on the plan. The
|
|
88
|
+
> catalog and the storefront still work fully. **Never block, stop, loop or retry the build on this:**
|
|
89
|
+
> - Record `payment_enabled: false` in `site.json` (set `shipping_enabled` as usual).
|
|
90
|
+
> - Tell the user in one line that checkout needs a paid Ecommerce plan, and carry straight on.
|
|
91
|
+
> - Treat the 402 as a normal outcome — don't surface it as a failure or re-attempt the call.
|
|
92
|
+
> - Never ask the user for payment credentials.
|
|
93
|
+
>
|
|
94
|
+
> Every downstream stage reads `site.json → ecommerce.payment_enabled` and, when it is `false`, skips
|
|
95
|
+
> all checkout wiring and verification (see content-model Step 5, frontend, deploy). A store with
|
|
96
|
+
> `payment_enabled: false` is a **complete, valid build** — a browsable catalog without checkout.
|
|
97
|
+
|
|
98
|
+
## Step 4 — Verify
|
|
99
|
+
|
|
100
|
+
- `ecommerce_stores_metadata { store_id }` → `has_shipping` true; `has_payment_methods` (may be false,
|
|
101
|
+
see the gate); read `default_currency` (`decimal_digits`, `template`) here, don't guess.
|
|
102
|
+
- Public Storefront V2 answers for the channel (no auth):
|
|
103
|
+
`GET https://api-ecommerce.hostinger.com/v2/channels/<scha_*>/products?limit=100` → 200 (empty until
|
|
104
|
+
content-model seeds products — fine here).
|
|
105
|
+
- `GET https://<domain>/` returns the Hostinger default page until the storefront is deployed — expected.
|
|
106
|
+
|
|
107
|
+
## Step 5 — Record `site.json`
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"backend": "ecommerce",
|
|
112
|
+
"website_type": "headless",
|
|
113
|
+
"order_id": 1007223428,
|
|
114
|
+
"username": "u…",
|
|
115
|
+
"ecommerce": {
|
|
116
|
+
"store_id": "store_…",
|
|
117
|
+
"sales_channel": { "id": "scha_…", "type": "custom", "external_id": "…-headless" },
|
|
118
|
+
"currency": "usd",
|
|
119
|
+
"payment_enabled": false,
|
|
120
|
+
"shipping_enabled": true
|
|
121
|
+
},
|
|
122
|
+
"urls": {
|
|
123
|
+
"site": "https://<domain>",
|
|
124
|
+
"storefront_api": "https://api-ecommerce.hostinger.com/v2/channels/<scha_*>",
|
|
125
|
+
"store_admin": "https://ecommerce.hostinger.com/store/<store_*>"
|
|
126
|
+
},
|
|
127
|
+
"provisioned_at": "…",
|
|
128
|
+
"verified_at": "…"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
No secrets in `site.json`. After deploy, link the channel to the live domain with
|
|
133
|
+
`ecommerce_sales-channels_update { store_id, sales_channel_id, url }` (done in the deploy stage).
|