@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/gemini-extension.json +1 -1
  3. package/package.json +1 -1
  4. package/server.json +2 -2
  5. package/skills/headless/SKILL.md +247 -36
  6. package/skills/headless/connect/SKILL.md +103 -0
  7. package/skills/headless/{entry → connect}/bootstrap.mjs +8 -3
  8. package/skills/headless/connect/tools/antigravity.md +54 -0
  9. package/skills/headless/connect/tools/claude-code.md +46 -0
  10. package/skills/headless/connect/tools/codex.md +48 -0
  11. package/skills/headless/connect/tools/cursor.md +109 -0
  12. package/skills/headless/connect/tools/devin.md +50 -0
  13. package/skills/headless/connect/tools/vscode.md +57 -0
  14. package/skills/headless/report.schema.json +149 -0
  15. package/skills/headless/run-log.mjs +662 -0
  16. package/skills/headless/steps/01-check-state/SKILL.md +193 -0
  17. package/skills/headless/steps/02-refine/SKILL.md +206 -0
  18. package/skills/headless/steps/03-provision/SKILL.md +62 -0
  19. package/skills/headless/steps/03-provision/ecommerce/SKILL.md +133 -0
  20. package/skills/headless/steps/03-provision/webapp/SKILL.md +154 -0
  21. package/skills/headless/steps/03-provision/wordpress/SKILL.md +100 -0
  22. package/skills/headless/steps/04-content-model/SKILL.md +150 -0
  23. package/skills/headless/steps/04-content-model/ecommerce/SKILL.md +75 -0
  24. package/skills/headless/steps/04-content-model/wordpress/SKILL.md +178 -0
  25. package/skills/headless/steps/05-design/SKILL.md +455 -0
  26. package/skills/headless/steps/05-design/logo-kit.mjs +201 -0
  27. package/skills/headless/steps/06-frontend/SKILL.md +131 -0
  28. package/skills/headless/steps/07-validate/SKILL.md +53 -0
  29. package/skills/headless/steps/08-deploy/SKILL.md +171 -0
  30. package/skills/headless/steps/09-done/SKILL.md +151 -0
  31. package/src/core/skills.js +164 -24
  32. package/src/core/skills.ts +164 -24
  33. package/src/servers/agency-hosting.js +1 -1
  34. package/src/servers/agency-hosting.ts +1 -1
  35. package/src/servers/agent-readiness.js +1 -1
  36. package/src/servers/agent-readiness.ts +1 -1
  37. package/src/servers/all.js +1 -1
  38. package/src/servers/all.ts +1 -1
  39. package/src/servers/billing.js +1 -1
  40. package/src/servers/billing.ts +1 -1
  41. package/src/servers/dns.js +1 -1
  42. package/src/servers/dns.ts +1 -1
  43. package/src/servers/domains.js +1 -1
  44. package/src/servers/domains.ts +1 -1
  45. package/src/servers/ecommerce.js +1 -1
  46. package/src/servers/ecommerce.ts +1 -1
  47. package/src/servers/horizons.js +1 -1
  48. package/src/servers/horizons.ts +1 -1
  49. package/src/servers/hosting.js +1 -1
  50. package/src/servers/hosting.ts +1 -1
  51. package/src/servers/mail.js +1 -1
  52. package/src/servers/mail.ts +1 -1
  53. package/src/servers/reach.js +1 -1
  54. package/src/servers/reach.ts +1 -1
  55. package/src/servers/vps.js +1 -1
  56. package/src/servers/vps.ts +1 -1
  57. package/src/servers/wordpress.js +1 -1
  58. package/src/servers/wordpress.ts +1 -1
  59. package/skills/headless/entry/skill.md +0 -79
  60. package/skills/headless/references/DATABASE.md +0 -49
  61. package/skills/headless/references/DEPLOYMENT.md +0 -34
  62. package/skills/headless/references/SETUP.md +0 -35
  63. package/skills/headless/references/STORE.md +0 -52
  64. 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).