@hostinger/mcp 3.2.2 → 3.3.1
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 +14 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/headless/SKILL.md +245 -36
- package/skills/headless/connect/SKILL.md +98 -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 +209 -0
- package/skills/headless/steps/02-refine/SKILL.md +273 -0
- package/skills/headless/steps/03-provision/SKILL.md +117 -0
- package/skills/headless/steps/03-provision/ecommerce/SKILL.md +142 -0
- package/skills/headless/steps/03-provision/webapp/SKILL.md +154 -0
- package/skills/headless/steps/03-provision/wordpress/SKILL.md +249 -0
- package/skills/headless/steps/04-content-model/SKILL.md +228 -0
- package/skills/headless/steps/04-content-model/ecommerce/SKILL.md +75 -0
- package/skills/headless/steps/04-content-model/wordpress/SKILL.md +223 -0
- package/skills/headless/steps/04-content-model/wordpress/woocommerce.md +153 -0
- package/skills/headless/steps/05-design/SKILL.md +464 -0
- package/skills/headless/steps/05-design/logo-kit.mjs +201 -0
- package/skills/headless/steps/06-frontend/SKILL.md +160 -0
- package/skills/headless/steps/07-validate/SKILL.md +56 -0
- package/skills/headless/steps/08-deploy/SKILL.md +179 -0
- package/skills/headless/steps/09-done/SKILL.md +157 -0
- package/src/core/skills.js +171 -24
- package/src/core/skills.ts +171 -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,209 @@
|
|
|
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/headless/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
|
+
- **Is it an empty headless shell from onboarding?** A `headless` site whose setup is `completed` is
|
|
120
|
+
**not necessarily finished** — onboarding now creates the website *before* the build runs, as an
|
|
121
|
+
**empty headless shell** (`hosting_websites_start-setup { type: "headless_empty" }`): `website_type`
|
|
122
|
+
is `headless` but **no backend is installed** (no WordPress at `/cms`, no store). Tell a shell from a
|
|
123
|
+
real backend the same way provision does: within 24 h the setup `type` is `headless_empty`; otherwise
|
|
124
|
+
`wordpress_installations_list { username, domain, ownership: "all" }` returns **no** `/cms` row and
|
|
125
|
+
there is no `site.json` recording a backend. An empty shell means **provision has not chosen or
|
|
126
|
+
installed a backend yet** — this is a **new build** that happens to already own its website, not a
|
|
127
|
+
finished or resumable one. Route it to **refine** (choose the backend), then **provision** adopts the
|
|
128
|
+
shell and swaps it to that backend (see the provision router's *Onboarding handoff*). Never create a
|
|
129
|
+
second site, and never skip provision for it.
|
|
130
|
+
|
|
131
|
+
Reconcile with the local state. **When they disagree, reality wins** and the local `stage` is
|
|
132
|
+
corrected.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Step 4 — Determine the position
|
|
137
|
+
|
|
138
|
+
Connect always writes `state.json` (with `stage: check_state`) before this step runs, so local
|
|
139
|
+
state is never truly empty. **"Local state: none" here means only connect artifacts exist** —
|
|
140
|
+
`connect.json` + a `state.json` still pointing at `check_state`/`refine`, and **no build artifacts
|
|
141
|
+
yet** (`brief.md`, `site.json`, …).
|
|
142
|
+
|
|
143
|
+
| Local state | Reality (domain) | Position |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| none (connect only) | free | **new build** — start from refine |
|
|
146
|
+
| none (connect only) | empty headless shell (onboarding) | **new build on an existing shell** — start from refine; provision adopts the shell and swaps it to the chosen backend. Never create a second site, never skip provision |
|
|
147
|
+
| none (connect only) | site exists with a real backend | **adopt / resume** — never create a second site; route to the first unfinished stage |
|
|
148
|
+
| build artifacts present, incomplete | matches | **resume** at `state.json`'s `stage` |
|
|
149
|
+
| build artifacts present | disagrees | **reality wins** — recompute the stage from what actually exists |
|
|
150
|
+
| build artifacts present, all done | live site exists | **finished** — this is now an *iterate* request, not a build |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Step 5 — Guide the user
|
|
155
|
+
|
|
156
|
+
The question to ask depends on whether there is actually **something to resume** — i.e. **build
|
|
157
|
+
artifacts** (`brief.md`, `site.json`, …) or a **live site** for this domain. A stray `.lock` or
|
|
158
|
+
connect-only files are *not* something to resume.
|
|
159
|
+
|
|
160
|
+
- **New build** (no build artifacts and either the domain is free **or** it holds only an empty
|
|
161
|
+
headless shell from onboarding) → just proceed to refine. **Don't ask resume vs start over** —
|
|
162
|
+
there's nothing to resume; both would land in the same place. An empty headless shell is not
|
|
163
|
+
"something to resume": it has no backend and no content yet, so it's a fresh build that already owns
|
|
164
|
+
its website — provision will adopt and swap it. Mention it in one line ("Using the headless site
|
|
165
|
+
onboarding already created for this domain."). If a leftover `.lock` is lying around, silently take
|
|
166
|
+
it over and continue (optionally mention it in one line).
|
|
167
|
+
- **Something to resume** (build artifacts exist, or a site exists for the domain **with a real backend
|
|
168
|
+
installed** — WordPress at `/cms`, a store, or a deployed app) → tell the user plainly what's there
|
|
169
|
+
and **ask them to choose**, regardless of how old it is (don't guess from timestamps):
|
|
170
|
+
|
|
171
|
+
- **Resume** — pick up at the computed stage. (If a `.lock` is present in this case, also confirm no
|
|
172
|
+
other window is actively building this domain right now.)
|
|
173
|
+
- **Start over / redo a step** — only safe **before anything is provisioned**. If the site is already
|
|
174
|
+
provisioned or live, you **cannot** re-create it: that is forward-only *iterate* (change content or
|
|
175
|
+
design and redeploy), not a fresh build. Never re-provision or overwrite a live site; if the user
|
|
176
|
+
insists on a clean slate for a live domain, explain the limitation and point them at hPanel.
|
|
177
|
+
- **Switch domain** — they want a different domain. A new domain is a **new workspace**
|
|
178
|
+
(`~/.hostinger/<other-domain>/`, with its own `src/`). Hand off to start that domain's run.
|
|
179
|
+
- **Buy a domain** — they want a custom domain instead of the temporary one. Route to the domain
|
|
180
|
+
purchase / `connect-domain` flow, then continue with the chosen domain.
|
|
181
|
+
|
|
182
|
+
When the intent is ambiguous, **ask** — don't guess.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Step 6 — Record and route
|
|
187
|
+
|
|
188
|
+
Write `~/.hostinger/<domain>/state.json` with the agreed next `stage` and a fresh timestamp. If the
|
|
189
|
+
route isn't a new build, record it with `run-log.mjs <domain> run-update --kind resume` (or
|
|
190
|
+
`redesign` / `iterate`), then end the stage with `stage-end check_state ok --note "<the route>"`.
|
|
191
|
+
Then:
|
|
192
|
+
|
|
193
|
+
- **Handing straight to the next stage in this same session?** Keep the `.lock` (you are still
|
|
194
|
+
actively running) and continue.
|
|
195
|
+
- **Stopping and returning control to the user?** (e.g. the next stage isn't built yet, or you are
|
|
196
|
+
waiting on them.) **Delete the `.lock`** — the session is no longer active, and a leftover lock just
|
|
197
|
+
forces an unnecessary "is another window running?" question on the next run. Always release the lock
|
|
198
|
+
on a clean stop; it exists only to mark an actively-running session. A stop is still the end of a
|
|
199
|
+
run: give the final report and save the build report (`09-done`).
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Invariants
|
|
204
|
+
|
|
205
|
+
- **One directory per domain.** The domain is the key; state files never repeat it.
|
|
206
|
+
- **Never create a second site** on a domain that already has one.
|
|
207
|
+
- **Never re-provision or overwrite a live site.** Recovery is forward-only.
|
|
208
|
+
- **Reality beats local state**, always.
|
|
209
|
+
- **When ambiguous, ask.**
|
|
@@ -0,0 +1,273 @@
|
|
|
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), then *infers* the backend that fits (WordPress, Ecommerce, WordPress + Ecommerce, WordPress + WooCommerce for a one-site content+store, or Webapp for a custom app/SPA) — it never asks the user to pick a backend or a technology; the user confirms the plan in plain language, then it writes brief.md. Hostinger Ecommerce stays the default for selling; WooCommerce is chosen only when the user explicitly asks for it or wants content and a store on one WordPress site."
|
|
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
|
+
> **Never ask the user to choose a backend or a technology.** Don't ask "WordPress or Ecommerce?",
|
|
17
|
+
> don't present WordPress/Ecommerce/WooCommerce/Webapp as options, and don't surface the backend in a
|
|
18
|
+
> multiple-choice UI. Open with **"What do you want to build?"** and infer the backend yourself in
|
|
19
|
+
> Step 4 from what they describe (and whether they sell). The backend is a technical decision the flow
|
|
20
|
+
> makes *for* the user — they confirm the **plan** (what the site is and does), not the plumbing. The
|
|
21
|
+
> only exception is when the user *volunteers* a backend ("build it on WordPress", "use WooCommerce"):
|
|
22
|
+
> then honour their choice and note it in the brief.
|
|
23
|
+
|
|
24
|
+
**In:** the domain (the workspace directory name), plus anything the user already shared.
|
|
25
|
+
**Out:** `~/.hostinger/<domain>/brief.md`.
|
|
26
|
+
|
|
27
|
+
Nothing in this step touches Hostinger. It only asks, decides and writes.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Step 1 — Read the workspace, then the resume check
|
|
32
|
+
|
|
33
|
+
Read everything already in `~/.hostinger/<domain>/` (`state.json`, `connect.json`, any notes, images
|
|
34
|
+
or files the user dropped there) — it all counts as input.
|
|
35
|
+
|
|
36
|
+
If `brief.md` already exists for this domain, **don't overwrite it**. Show the user a three-line
|
|
37
|
+
summary (what the site is, its pages, the suggested backend) and ask: *keep it, or change something?*
|
|
38
|
+
Keep → go to Step 6. Change → edit only what they asked for, then re-confirm (Step 5).
|
|
39
|
+
|
|
40
|
+
If the site is already provisioned (`site.json` exists), the backend is **fixed** — never change the
|
|
41
|
+
Suggested backend on a provisioned site. Explain that switching backends means a new domain.
|
|
42
|
+
|
|
43
|
+
## Step 2 — Gather what you already have
|
|
44
|
+
|
|
45
|
+
Before asking anything, collect what the user has already given you, so you don't ask for it again:
|
|
46
|
+
|
|
47
|
+
- **Inspect the directory the command was run in** — this is the biggest signal for the build mode
|
|
48
|
+
(Step 3). Look for an existing **frontend / webapp / commerce project**: a `package.json`, a
|
|
49
|
+
framework config (`next.config.*`, `astro.config.*`, `vite.config.*`, `svelte.config.*`, `nuxt.*`),
|
|
50
|
+
a `src/` or `app/` tree, `index.html`, a storefront/cart, etc. A real project here usually means the
|
|
51
|
+
user **already has their own frontend** and mainly needs a Hostinger backend to wire it to — lean
|
|
52
|
+
toward "bring your own frontend" in Step 3. If the folder has nothing site-like, assume they don't
|
|
53
|
+
and lean toward "agent builds it".
|
|
54
|
+
- Anything in the pasted prompt beyond the domain (a sentence, a link, a screenshot).
|
|
55
|
+
- Files in the current project that look like site material (copy, logos, product lists, images).
|
|
56
|
+
- What the agent already knows about the user from local memory or this conversation (a prior build,
|
|
57
|
+
stack preferences, a frontend they always use).
|
|
58
|
+
- The domain name itself — a hint only (`coral-pottery.com` suggests pottery), never a conclusion.
|
|
59
|
+
|
|
60
|
+
## Step 3 — Ask
|
|
61
|
+
|
|
62
|
+
If Step 2 already answers *what are you building* and *will they sell*, skip the Round 1–3 questions
|
|
63
|
+
below, but still ask **Build mode** before moving on.
|
|
64
|
+
|
|
65
|
+
Otherwise open with one line and **wait for the user's answer**: *"Tell me what you want to build — a
|
|
66
|
+
sentence is enough. Links, screenshots or files help too. Skip anything you don't want to answer."*
|
|
67
|
+
|
|
68
|
+
> **"What do you want to build?" is an OPEN question — ask it in plain prose and wait.** Never turn it
|
|
69
|
+
> into a multiple-choice list of pre-baked site ideas ("Coffee shop / Travel blog / Portfolio …"):
|
|
70
|
+
> that puts words in the user's mouth and defeats the point of asking. Let them describe it in their
|
|
71
|
+
> own words. The structured question UI is only for genuinely *choice-shaped* follow-ups (selling
|
|
72
|
+
> yes/no, which pages, tone adjectives, the build mode) — not for the opening question, and never for
|
|
73
|
+
> the backend.
|
|
74
|
+
|
|
75
|
+
Ask in **short rounds of 2–3 questions**, and adapt each round to the previous answers. If your tool
|
|
76
|
+
has a structured question UI (multiple choice with an "other" field), use it for the choice-shaped
|
|
77
|
+
follow-up questions (pages, selling yes/no, tone) — **never for the opening "what do you want to
|
|
78
|
+
build?" and never for the backend** (that's your decision, not theirs). **At most 3 rounds**, and stop early as soon as you can guess the backend and the user
|
|
79
|
+
seems done — "just build it", "you decide", or a skipped question means *use defaults*, not *ask
|
|
80
|
+
again*.
|
|
81
|
+
|
|
82
|
+
**Round 1 — what it is (the only required round):**
|
|
83
|
+
|
|
84
|
+
1. **What are you building?** The business or project, and what the site is for.
|
|
85
|
+
2. **Will you sell anything on it?** Physical products, digital products, services with checkout —
|
|
86
|
+
or nothing. *This decides the backend.* Skip it only when the answer is already unmistakable
|
|
87
|
+
("an online shop for my candles", "my personal blog"); when in doubt, ask.
|
|
88
|
+
3. **Who is it for?** The audience, and how they'll find the site. *(skippable)*
|
|
89
|
+
|
|
90
|
+
**Round 2 — what goes on it (optional, adapt to Round 1):**
|
|
91
|
+
|
|
92
|
+
- **Content they'll publish over time** — blog posts, articles, news, recipes, portfolio pieces,
|
|
93
|
+
case studies? How often? Or is it a handful of pages that rarely change?
|
|
94
|
+
- **Pages** — propose a page list from what you know and ask them to add or remove.
|
|
95
|
+
- If they sell: **what and how many** — a rough product count, categories, whether products have
|
|
96
|
+
variants (size, colour), and the shipping regions.
|
|
97
|
+
|
|
98
|
+
**Round 3 — how it should feel (optional, skip what you already know):**
|
|
99
|
+
|
|
100
|
+
This is the round that captures **all design input** — the design step reads it from `brief.md` to derive
|
|
101
|
+
the design directions, so it is **not** asked again later; gather it here:
|
|
102
|
+
|
|
103
|
+
- **Reference & look** — a **site they like (URL)** and what to take from it, plus a few adjectives for
|
|
104
|
+
the vibe. Screenshots welcome.
|
|
105
|
+
- **Colours & brand** — brand/accent colours, logo, fonts, existing copy, photos. Note where they live.
|
|
106
|
+
- **Must-haves and no-gos** — contact form, newsletter, bookings, languages, things to avoid.
|
|
107
|
+
|
|
108
|
+
If the user asks for something outside the scope (user accounts, member areas, bookings with a
|
|
109
|
+
calendar backend, user-generated content), say plainly that the headless flow doesn't support it yet
|
|
110
|
+
and record it under **Out of scope** — don't promise it.
|
|
111
|
+
|
|
112
|
+
### Build mode — ask once (this one *is* a user choice)
|
|
113
|
+
|
|
114
|
+
The backend is your call, but **how the frontend gets built is the user's** — because most of the time
|
|
115
|
+
the user already has their own frontend and just needs a Hostinger backend to wire it to. After the
|
|
116
|
+
folder + memory inspection (Step 2), ask **one** question (a structured choice is ideal here), seeding
|
|
117
|
+
the default from what you found:
|
|
118
|
+
|
|
119
|
+
- **Bring your own frontend** *(default when Step 2 found a real project in the folder)* — "I already
|
|
120
|
+
have a frontend / I'll drive the design and we vibe-code it together." The agent **uses the user's
|
|
121
|
+
existing project in the current folder as the source**, collaborates on design/code at the user's
|
|
122
|
+
direction, and wires it to the provisioned backend. There is **no agent-generated design** — the
|
|
123
|
+
design step defers to the user (see the Build mode box at the top of the design step), and the frontend step builds on their project
|
|
124
|
+
rather than scaffolding a new one.
|
|
125
|
+
- **Agent designs and builds it** *(default when the folder has nothing site-like)* — "Give me ideas
|
|
126
|
+
— you design and code it." Today's flow: the design step proposes directions and the frontend step
|
|
127
|
+
scaffolds and implements them.
|
|
128
|
+
|
|
129
|
+
Record the answer as the brief's **Build mode**. This is the one frontend/technology question you *do*
|
|
130
|
+
put to the user; everything else about the stack (framework, SSR/SSG) is still inferred. The deliverable
|
|
131
|
+
is the same either way — a working site on the backend; only *who drives the frontend* changes.
|
|
132
|
+
|
|
133
|
+
## Step 4 — Decide the backend (your call, from the brief — not a question)
|
|
134
|
+
|
|
135
|
+
From what the user described, **you** pick the backend that fits — this is an internal decision, never
|
|
136
|
+
a question put back to the user. Pick the backend that fits what the user told you:
|
|
137
|
+
|
|
138
|
+
| Backend | Choose when | What it gives the owner |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| **WordPress** | No selling. The owner publishes and edits content: pages, blog, articles, portfolio. | Headless WordPress at `/cms` — write and edit content in wp-admin. |
|
|
141
|
+
| **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. |
|
|
142
|
+
| **WordPress + Ecommerce** | Selling **and** regular editorial content (a blog, guides, lookbooks, recipes) the owner keeps adding to, run as **two** backends (WordPress site + separate Hostinger Ecommerce store). | Both: WordPress for content, the Ecommerce store for products and checkout. |
|
|
143
|
+
| **WordPress + WooCommerce** | **Only** when the user explicitly names WooCommerce, or explicitly wants content **and** a store on **one** WordPress site (one admin, one domain). The one-site alternative to WordPress + Ecommerce. | A single headless WordPress at `/cms` with the WooCommerce plugin — products, cart, coupons and checkout (Cash on Delivery) over the WooCommerce Store API, plus WP posts/pages on the same site. |
|
|
144
|
+
| **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. |
|
|
145
|
+
|
|
146
|
+
Rules of thumb:
|
|
147
|
+
|
|
148
|
+
- **Is it an app, or a site?** A custom application the user builds/brings (its own logic, its own data,
|
|
149
|
+
maybe a database) → **Webapp**. A content site or a shop → one of the three above. When the user says
|
|
150
|
+
"my app / SPA / dashboard / tool", or hands you an existing app project to deploy, that's **Webapp**.
|
|
151
|
+
- **Selling decides first (for sites).** No selling → WordPress. Selling → Ecommerce or
|
|
152
|
+
WordPress + Ecommerce.
|
|
153
|
+
- **Then the content.** Selling plus only a few fixed pages → Ecommerce. Selling plus content the
|
|
154
|
+
owner will keep publishing → WordPress + Ecommerce.
|
|
155
|
+
- **Hostinger Ecommerce is the default for selling — WooCommerce is never the default and is never
|
|
156
|
+
recommended on its own.** Choose **WordPress + WooCommerce** only when the user explicitly names
|
|
157
|
+
WooCommerce, or explicitly wants content **and** a store on one WordPress site (one admin, one
|
|
158
|
+
domain — the one-site alternative to WordPress + Ecommerce). For every other selling case, keep
|
|
159
|
+
Hostinger Ecommerce. If the user wants both content and a store but hasn't said "one site" or
|
|
160
|
+
"WooCommerce", offer it as an option — don't switch to it silently.
|
|
161
|
+
- **Don't add WordPress "just in case".** A second backend is a second admin to learn. Recommend the
|
|
162
|
+
combination only when the user described ongoing content.
|
|
163
|
+
- Selling a single service by enquiry (no checkout, no cart) is **not** commerce → WordPress with a
|
|
164
|
+
contact page.
|
|
165
|
+
- **Webapp never uses WordPress or a store.** If the user wants a custom app *and* a CMS/shop, that's two
|
|
166
|
+
different sites — build the app as a Webapp and tell them the content/commerce backend is a separate
|
|
167
|
+
domain.
|
|
168
|
+
|
|
169
|
+
**Then set rendering** (write it in the brief's **Rendering** section): **Ecommerce → SSR required**
|
|
170
|
+
(carts/search need per-request rendering — any SSR framework works; Next.js is the default, not a
|
|
171
|
+
requirement); **WordPress + WooCommerce → SSR required** (same reason — cart/checkout need per-request
|
|
172
|
+
rendering); **WordPress → SSG (Astro)** if the site is mostly static and the owner is fine rebuilding
|
|
173
|
+
to publish, otherwise **SSR**; **Webapp → whatever the app is** (SSR for a server app, SPA for a
|
|
174
|
+
client-only app) — it deploys as a Node.js app either way. When unsure, default to **Next.js SSR**.
|
|
175
|
+
|
|
176
|
+
## Step 5 — Confirm the plan in one message
|
|
177
|
+
|
|
178
|
+
Send **one** message that confirms **what you'll build** — the plan, not the plumbing. Lead with the
|
|
179
|
+
site, not the backend:
|
|
180
|
+
|
|
181
|
+
- **The plan** — what it is, audience, pages, products (if any), tone. Mark anything you defaulted
|
|
182
|
+
(*"assumed: warm, minimal look"*) so the user sees what they skipped.
|
|
183
|
+
- **How it's built** — state the backend as a *consequence* in one plain line, focused on what it
|
|
184
|
+
means for them day to day ("I'll build this on WordPress, so you edit pages and posts in a normal
|
|
185
|
+
WordPress admin"), **not** as a choice you're asking them to make. Don't ask "is WordPress OK?" or
|
|
186
|
+
offer alternatives unless it was a genuine close call *and* the difference matters to the user.
|
|
187
|
+
|
|
188
|
+
End with *"Good to go, or anything to change?"* A yes (or silence-equivalent like "go") → write
|
|
189
|
+
`brief.md`. Changes → apply them and write it; don't loop back into questions. If the user
|
|
190
|
+
*volunteers* a different backend, use theirs and note it in the brief.
|
|
191
|
+
|
|
192
|
+
## Step 6 — Write `brief.md` and hand off
|
|
193
|
+
|
|
194
|
+
Write `brief.md` from the template below. Fill every section from the conversation, defaulting what
|
|
195
|
+
the user skipped and tagging it `(assumed)`; leave out **Products** when nothing is sold. Use the
|
|
196
|
+
user's own words where you can. Then update `state.json`:
|
|
197
|
+
`"stage": "provision"`, fresh `updated_at`.
|
|
198
|
+
|
|
199
|
+
The **Suggested backend** section is the contract with `03-provision/SKILL.md`, which routes on it.
|
|
200
|
+
Its first line must be exactly one of `**WordPress**`, `**Ecommerce**`, `**WordPress + Ecommerce**`,
|
|
201
|
+
`**WordPress + WooCommerce**` or `**Webapp**`, followed by an em dash and the one-line reason.
|
|
202
|
+
`**WordPress + WooCommerce**` is the WordPress backend with the WooCommerce store turned on — provision
|
|
203
|
+
routes it to the `wordpress` subfolder with the store enabled (it is **not** a separate backend).
|
|
204
|
+
|
|
205
|
+
## `brief.md` template
|
|
206
|
+
|
|
207
|
+
```markdown
|
|
208
|
+
# Brief — <domain>
|
|
209
|
+
|
|
210
|
+
> Brief agreed with the user on <today>.
|
|
211
|
+
|
|
212
|
+
## Purpose
|
|
213
|
+
<what the site is, who runs it, what it's for — 2–3 sentences>
|
|
214
|
+
|
|
215
|
+
## Audience
|
|
216
|
+
<who visits, how they arrive, what device they mostly use>
|
|
217
|
+
|
|
218
|
+
## Tone
|
|
219
|
+
<voice in a sentence or two>
|
|
220
|
+
|
|
221
|
+
## Design direction
|
|
222
|
+
<look & feel in a few adjectives; **reference site(s)** the user named (URLs) and what to take from them
|
|
223
|
+
(layout, structure, mood); colours / brand palette; any screenshots. This is where ALL design questions
|
|
224
|
+
land — the frontend step reads this and does not re-ask. Write "Agent's choice: <short style>" when the
|
|
225
|
+
user didn't specify.>
|
|
226
|
+
|
|
227
|
+
## Pages
|
|
228
|
+
- **<Page>** — <one-line purpose>
|
|
229
|
+
- …
|
|
230
|
+
|
|
231
|
+
## Products
|
|
232
|
+
<only when selling: what is sold, rough count, categories, variants, shipping regions, payments>
|
|
233
|
+
|
|
234
|
+
## Content
|
|
235
|
+
<what the owner will publish over time and how often — or "A few fixed pages; no ongoing content.">
|
|
236
|
+
|
|
237
|
+
## Must-haves
|
|
238
|
+
- …
|
|
239
|
+
|
|
240
|
+
## Out of scope
|
|
241
|
+
- …
|
|
242
|
+
|
|
243
|
+
## Assets
|
|
244
|
+
<logo, colours, photos, copy the user provided and where they are; "None yet" if none>
|
|
245
|
+
|
|
246
|
+
## Suggested backend
|
|
247
|
+
**<WordPress | Ecommerce | WordPress + Ecommerce | WordPress + WooCommerce | Webapp>** — <one-line reason>.
|
|
248
|
+
<"Confirmed by the user." or "User chose this over the recommended <X> because <reason>.">
|
|
249
|
+
|
|
250
|
+
## Build mode
|
|
251
|
+
**<Bring your own frontend | Agent designs and builds>** — <one line>. <When "Bring your own frontend":
|
|
252
|
+
the path to the user's existing project in the current folder (the frontend source), and how they want
|
|
253
|
+
to drive design/code. When "Agent designs and builds": leave the design + frontend steps to run as
|
|
254
|
+
normal.> The design step and the frontend step both read this: **Bring your own frontend** → design
|
|
255
|
+
defers to the user and frontend builds on their project; **Agent designs and builds** → both run as normal.
|
|
256
|
+
|
|
257
|
+
## Rendering
|
|
258
|
+
**<SSR | SSG>** — <reason>. Rules: **Ecommerce requires SSR** and **WordPress + WooCommerce requires
|
|
259
|
+
SSR** (carts/checkout need per-request rendering — any SSR framework works; Next.js is the default, not
|
|
260
|
+
a requirement); **WordPress may be SSG (Astro)** when the site is mostly static and rarely changes,
|
|
261
|
+
otherwise **SSR**. Default when unsure: **Next.js SSR**.
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Example — WordPress + Ecommerce
|
|
265
|
+
|
|
266
|
+
> A ceramicist sells around 30 handmade pieces and writes a monthly studio journal.
|
|
267
|
+
|
|
268
|
+
```markdown
|
|
269
|
+
## Suggested backend
|
|
270
|
+
**WordPress + Ecommerce** — sells ~30 products with checkout, and the owner publishes a monthly
|
|
271
|
+
studio journal they want to edit themselves.
|
|
272
|
+
Confirmed by the user.
|
|
273
|
+
```
|
|
@@ -0,0 +1,117 @@
|
|
|
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. WordPress + WooCommerce routes to the wordpress subfolder with the WooCommerce store turned on — it is not a separate backend."
|
|
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
|
+
| WordPress + WooCommerce | `wordpress/SKILL.md` **with the WooCommerce store on** (see below) |
|
|
29
|
+
| Ecommerce | `ecommerce/SKILL.md` |
|
|
30
|
+
| Webapp | `webapp/SKILL.md` |
|
|
31
|
+
|
|
32
|
+
If the section is missing or names something else, go back to **refine** — don't pick one here.
|
|
33
|
+
Tell the user the backend and its one-line reason before provisioning.
|
|
34
|
+
|
|
35
|
+
> **WordPress + WooCommerce is the WordPress backend with a store — not a new backend.** Route it to
|
|
36
|
+
> `wordpress/SKILL.md` and follow that recipe's **optional WooCommerce tail** (install + activate the
|
|
37
|
+
> `woocommerce` plugin and verify the store endpoints; the catalog is seeded later through the per-site WP
|
|
38
|
+
> MCP's native `wc-*` abilities — no custom plugin, no consumer key). `site.json → backend` stays
|
|
39
|
+
> `"wordpress"`; the store is recorded under `site.json → woocommerce` (`enabled: true`). Everything else
|
|
40
|
+
> downstream that routes on
|
|
41
|
+
> `wordpress` (content-model push, deploy's `/cms` preservation, done's wp-admin login) then works
|
|
42
|
+
> unchanged.
|
|
43
|
+
|
|
44
|
+
> **Supported backends: `wordpress` (optionally with a WooCommerce store), `ecommerce` and `webapp`.**
|
|
45
|
+
> `webapp` is a plain Node.js app host (an empty website — no CMS, no store, no `/cms`) for any app/SPA
|
|
46
|
+
> that brings its own code and data; it never installs WordPress. PocketBase is not available in this
|
|
47
|
+
> version — refine never proposes it, and there is no `pocketbase/` subfolder; if a brief somehow names
|
|
48
|
+
> it, stop and tell the user rather than improvising it. A **WordPress + Ecommerce** brief (two separate
|
|
49
|
+
> backends — a WordPress site **and** a Hostinger Ecommerce store) has no single combined `start-setup`
|
|
50
|
+
> type yet: stop, tell the user it isn't supported as one site, and offer either to continue with just
|
|
51
|
+
> WordPress or just Ecommerce (route to that one subfolder), **or**, if they want content and a store on
|
|
52
|
+
> **one** site, to switch the brief to **WordPress + WooCommerce** — the one-site path, which routes to
|
|
53
|
+
> `wordpress/SKILL.md` with the store on.
|
|
54
|
+
|
|
55
|
+
## Onboarding handoff — swap an existing empty headless shell (do this before creating anything)
|
|
56
|
+
|
|
57
|
+
Onboarding can create the website **before** you ever run, then paste the build prompt into this agent.
|
|
58
|
+
When it does, the domain already has an **empty headless shell**: a website set up with
|
|
59
|
+
`hosting_websites_start-setup { type: "headless_empty" }` — `website_type` is `headless`, but **nothing
|
|
60
|
+
is installed** (no WordPress at `/cms`, no store). It is the hosting slot only, waiting for a backend.
|
|
61
|
+
|
|
62
|
+
So provision has **two entry shapes**, and each subfolder's *detect* step picks between them:
|
|
63
|
+
|
|
64
|
+
- **An empty headless shell already exists for the domain → adopt it and *swap* it to the chosen
|
|
65
|
+
backend. Never call `start-setup` again, never create a second site.** The swap is backend-specific:
|
|
66
|
+
- **WordPress** → install WordPress **into the existing shell's `/cms`** with
|
|
67
|
+
`wordpress_installations_install { type: "headless" }` (it rewrites the root `.htaccess` and marks
|
|
68
|
+
the site headless-WordPress). Do **not** use `start-setup { type: "headless_wordpress" }` — that is
|
|
69
|
+
only for creating a fresh site.
|
|
70
|
+
- **Ecommerce** → the empty headless shell **is** the storefront host already (a `headless_ecommerce`
|
|
71
|
+
website installs no store engine either). Adopt it as the frontend host and go straight to creating
|
|
72
|
+
the store + custom sales channel.
|
|
73
|
+
- **No site exists for the domain at all → create it fresh, exactly as each subfolder defines today**
|
|
74
|
+
(`start-setup` with the backend's own `type`).
|
|
75
|
+
|
|
76
|
+
> **Webapp is not part of this handoff at all.** `webapp` is **not** reached on the usual onboarding
|
|
77
|
+
> flow — it is only triggered by experimental actions — so it **ignores headless instances entirely**:
|
|
78
|
+
> it never detects, adopts or swaps an empty headless shell, and provisions its own **Node.js app host**
|
|
79
|
+
> exactly as `webapp/SKILL.md` defines today. The swap below is **WordPress and Ecommerce only**.
|
|
80
|
+
|
|
81
|
+
**How to tell an empty shell from an already-provisioned backend** (both read `website_type: headless`):
|
|
82
|
+
|
|
83
|
+
1. Exact-match the domain row in `hosting_websites_list` — if there is **no** row, nothing exists → create fresh.
|
|
84
|
+
2. Within 24 h of onboarding, `hosting_websites_list-setups` still shows the setup `type`
|
|
85
|
+
(`headless_empty` = empty shell; `headless_wordpress` = WordPress already; `headless_ecommerce` = an
|
|
86
|
+
ecommerce host). Beyond 24 h the setup drops off — fall back to the backend probe:
|
|
87
|
+
3. `wordpress_installations_list { username, domain, ownership: "all" }` — a row with `directory: "cms"`
|
|
88
|
+
and `is_valid: true` means WordPress is **already** installed (adopt, don't swap); **no** such row on
|
|
89
|
+
a `headless` site means the shell is still empty and WordPress must be swapped in. A local `site.json`
|
|
90
|
+
with a recorded backend means a previous run already provisioned — trust reality over it, but it tells
|
|
91
|
+
you not to expect an empty shell.
|
|
92
|
+
|
|
93
|
+
This only changes *detect*. Everything after the swap (verify, `site.json`, the WooCommerce tail, the
|
|
94
|
+
background fire/join split) is identical to a fresh provision. A swap that installs WordPress is still
|
|
95
|
+
asynchronous — fire it, record `provision.in_progress`, and join at the barrier just like a fresh create.
|
|
96
|
+
|
|
97
|
+
## Step 2 — Fire (don't block)
|
|
98
|
+
|
|
99
|
+
Follow the chosen subfolder's `SKILL.md` through **detect + create** (its "start-setup" call). The
|
|
100
|
+
moment the create is accepted and queued, record it in `state.json` and **return** — do not sit on the
|
|
101
|
+
readiness poll:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{ "stage": "content_model",
|
|
105
|
+
"provision": { "status": "in_progress", "domain": "<domain>", "started_at": "<now>" } }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Now go run the prepare work that doesn't need the backend (content-model prepare, frontend
|
|
109
|
+
shell). The subfolder's readiness poll, verification and `site.json` are the **join** half below.
|
|
110
|
+
|
|
111
|
+
## Step 3 — Join (at the instance barrier)
|
|
112
|
+
|
|
113
|
+
When an instance-barrier step is about to run (content-model push, frontend data wiring, deploy) — or
|
|
114
|
+
right away, if the agent can't interleave — finish the subfolder's `SKILL.md`: **poll to `completed`**,
|
|
115
|
+
run its verification, and write `site.json` (which records the chosen `backend`). Then set
|
|
116
|
+
`provision.status` to `"completed"` in `state.json` with a fresh `updated_at`. On resume, treat a
|
|
117
|
+
stored `in_progress` as a hint only — re-poll `hosting_websites_list-setups` for the truth first.
|