hostinger-api-mcp 3.23.2 → 3.24.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 CHANGED
@@ -4,6 +4,20 @@ Generated at release time from the diff between the previous and current
4
4
  build. Tool changes are listed individually; everything else is summarised
5
5
  by area.
6
6
 
7
+ ## v3.24.1 — 2026-10-30
8
+
9
+ ### Also in this release
10
+
11
+ - Bundled agent skills: `skills/connect-domain/SKILL.md`
12
+ - Other files: `src/core/skills.js`, `src/core/skills.ts`
13
+
14
+ ## v3.24.0 — 2026-10-30
15
+
16
+ ### Also in this release
17
+
18
+ - Bundled agent skills: `skills/headless/steps/09-done/SKILL.md`
19
+ - Other files: `src/core/skills.js`, `src/core/skills.ts`
20
+
7
21
  ## v3.23.2 — 2026-10-30
8
22
 
9
23
  ### Also in this release
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostinger-api-mcp",
3
- "version": "3.23.2",
3
+ "version": "3.24.1",
4
4
  "description": "Hostinger API — manage domains, DNS, hosting, VPS, billing and more from Gemini CLI.",
5
5
  "mcpServers": {
6
6
  "hostinger": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostinger-api-mcp",
3
- "version": "3.23.2",
3
+ "version": "3.24.1",
4
4
  "mcpName": "io.github.hostinger/hostinger-api-mcp",
5
5
  "description": "MCP server for Hostinger API",
6
6
  "repository": {
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/hostinger/api-mcp-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "3.23.2",
9
+ "version": "3.24.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@hostinger/mcp",
14
- "version": "3.23.2",
14
+ "version": "3.24.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -40,13 +40,13 @@ Agency Plan:
40
40
 
41
41
  ## 3. Point DNS at Hostinger
42
42
 
43
- **DNS already at Hostinger** (nameservers end in `dns-parking.com`): creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it.
43
+ **DNS already at Hostinger** (nameservers end in `dns-parking.com`): keep the domain's existing `*.dns-parking.com` nameserver pair and do not change its nameservers. Creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it.
44
44
 
45
45
  `dns_records_update` defaults to `overwrite: true`, which deletes every existing record with the same name and type. Adding a `TXT` at `@` that way wipes SPF and verification records. Send `overwrite: false` when adding; use `true` only to replace one record set on purpose, and run `dns_records_validate` on the same payload first.
46
46
 
47
47
  **DNS elsewhere** (registered at Hostinger with other nameservers, or registered elsewhere). Two options — let the user choose:
48
48
 
49
- - **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains (`ns1`/`ns2.dns-parking.com`, `solar`/`lunar.dns-parking.com` and others), so use the pair hPanel shows for this domain, never a guessed one. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar.
49
+ - **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains, and DNS resolves only on the pair the domain belongs to. Use the pair hPanel shows for the domain (Nameservers page → "Use Hostinger nameservers"), or ask the user for it. Never guess nameservers, and never use `ns1`/`ns2.dns-parking.com`. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar.
50
50
  - **Keep the current DNS provider** and change only the web records: `A` for `@` and `CNAME` (or `A`) for `www`. Agency sites point at `details.ipv4`. Shared and Cloud sites point at the targets in the Hostinger zone for the domain (`dns_records_list`) or, when that zone does not exist, the IP hPanel shows for the website. The user makes this change at their DNS provider.
51
51
 
52
52
  ## 4. SSL and HTTPS
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: headless-step-done
3
- description: "Step 9 (done) of the Hostinger Headless flow. Verifies the live site, then hands over the URLs (hPanel success page, main site, WordPress/store admin, dashboard), opens the success page in the browser on a successful run, and gives a summary of what was built and what the user can do next. Its final report format is also used whenever a run stops early."
3
+ description: "Step 9 (done) of the Hostinger Headless flow. Verifies the live site, then hands over the URLs (hPanel success page, main site, WordPress/store admin, dashboard), opens the success page in the browser on a successful run, and gives a summary of what was built plus explicit next steps — how to log in to wp-admin and reset the password, how to manage content, and how to finish WooCommerce/store setup so it can take orders. Its final report format is also used whenever a run stops early."
4
4
  ---
5
5
 
6
6
  # headless-step-done
@@ -52,14 +52,17 @@ Write it in this order:
52
52
  - **WordPress admin (one-click auto-login):** for a WordPress backend, **mint and hand over the
53
53
  auto-login redirect URL** — `wordpress_login_create-links { username, software: <site.json
54
54
  wordpress.install_id> }` returns a temporary link that logs the user straight into wp-admin. Give
55
- that URL as the admin link (it lands on `/cms/wp-admin`), so the user never needs a password. Also
56
- note the plain `urls.wp_admin` as a fallback. **Never print or store a password.** For a
57
- **WordPress + WooCommerce** store, this *same* wp-admin login is the store admin (wp-admin →
58
- WooCommerce → Products/Orders) — do **not** use the Ecommerce `store_admin` path. **Call out the one
59
- setup step under *Gaps / follow-ups*:** when `site.json → woocommerce.payment_enabled` is `false`,
60
- the owner must enable a payment method + a shipping zone in WooCommerce → Settings before checkout
61
- works (the catalog and cart already work). For a Hostinger **Ecommerce** store, link the store admin
62
- from `urls.store_admin` instead.
55
+ that URL as the admin link (it lands on `/cms/wp-admin`), so the user never needs a password.
56
+ Alongside it, give the two ways back in that don't expire: the **login URL `{urls.wp_admin}`**
57
+ (`https://<domain>/cms/wp-admin` — the headless WordPress lives under `/cms`), and the **WP Admin
58
+ button in hPanel** (hPanel → the website → *WP Admin*). Note how to **reset the password**: on the
59
+ `/cms/wp-admin` login screen use **"Lost your password?"**, and WordPress emails a reset link to the
60
+ **WordPress admin email** (the address set at install, which the owner already has) — see *Next
61
+ steps* for an optional deliverability tip.
62
+ **Never print or store a password.** For a **WordPress + WooCommerce** store, this *same* wp-admin
63
+ login is the store admin (wp-admin → WooCommerce → Products/Orders) — do **not** use the Ecommerce
64
+ `store_admin` path; the store setup the owner still needs is spelled out under *Next steps*. For a
65
+ Hostinger **Ecommerce** store, link the store admin from `urls.store_admin` instead.
63
66
  - **hPanel site dashboard:** `https://hpanel.hostinger.com/websites/<domain>` (the headless website
64
67
  is installed by now).
65
68
  - **Onboarding (resume):** `https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/`
@@ -69,10 +72,34 @@ Write it in this order:
69
72
  > release. If a built URL 404s, retry the same path without the `/<username>` segment before
70
73
  > reporting it as broken.
71
74
 
72
- 4. **Where state lives** — the workspace path `~/.hostinger/<domain>/`, the `stage` that a
75
+ 4. **Next steps** — what the user does now to actually use the site. Required on a successful run;
76
+ tailor the bullets to the backend and keep them concrete. This is the hand-holding the hPanel
77
+ success page can't give: don't just link wp-admin, tell them what to do once they're in.
78
+
79
+ - **Manage content (WordPress):** in wp-admin (the one-click link, the `/cms/wp-admin` login URL, or
80
+ hPanel → *WP Admin*) → **Posts**, **Pages**, **Media**, **Appearance → Menus**. Say how edits reach
81
+ the live site for *this* build: an **SSR** frontend re-reads the REST API each request, so changes
82
+ show up with no redeploy; an **SSG/static** build must be redeployed to pick up new content (read
83
+ `state.json → frontend.rendering`).
84
+ - **Log in later & reset the password:** repeat the two non-expiring routes (the `/cms/wp-admin` login
85
+ URL and hPanel's *WP Admin* button) and the **"Lost your password?"** reset. The reset link goes to
86
+ the **WordPress admin email** — the address set at install, which the owner already has — so nothing
87
+ extra is needed to receive it. Optionally, for more reliable delivery, they can create a mailbox on
88
+ the domain (hPanel → *Emails*) and set it as the admin address in **wp-admin → Settings → General**.
89
+ - **WordPress + WooCommerce — finish store setup so it can take orders.** The catalog and cart already
90
+ work, but **checkout stays off until the owner configures it in wp-admin**: WooCommerce → Settings →
91
+ **Payments** (enable at least one method) and → **Shipping** (add a zone with a rate), then confirm
92
+ products/prices and place a test order. When `site.json → woocommerce.payment_enabled` is `false`,
93
+ state plainly that this step is **required before any purchase works** — this is the gap owners hit.
94
+ - **Hostinger Ecommerce store:** when `site.json → ecommerce.payment_enabled` is `false`, finish
95
+ payment setup in the store admin (`urls.store_admin`) before the storefront can sell; otherwise it's
96
+ ready to take orders.
97
+ - **Webapp:** manage the app from hPanel → the website (environment variables, deployments, logs).
98
+
99
+ 5. **Where state lives** — the workspace path `~/.hostinger/<domain>/`, the `stage` that a
73
100
  resumed run will start from, and the build report `~/.hostinger/<domain>/report.html`.
74
101
 
75
- 5. **Gaps / follow-ups** (only if any) — a short numbered list of anything skipped, approximated or
102
+ 6. **Gaps / follow-ups** (only if any) — a short numbered list of anything skipped, approximated or
76
103
  left for the user (e.g. no images generated, default WordPress content left in place).
77
104
 
78
105
  Never include passwords, JWTs or other secrets in the report. For wp-admin access, give the URL and
@@ -25,10 +25,10 @@ export default [
25
25
  "resources": [
26
26
  {
27
27
  "uri": "skill://connect-domain/SKILL.md",
28
- "digest": "sha256:f864ecc8a8aaf67408d3838b1e6962a7e5d717a52febea04553a9692709cfed4",
29
- "size": 6993,
28
+ "digest": "sha256:500ef355966547e908662760ce834d463987f4eaee8a52ea101dcccc26f11e1a",
29
+ "size": 7192,
30
30
  "mimeType": "text/markdown",
31
- "text": "---\nname: connect-domain\ndescription: \"Connect a custom domain to a website on Hostinger web hosting (Shared, Cloud or Agency plans) end to end: attach the domain to the site, point DNS at Hostinger without breaking existing email or verification records, install SSL, turn on the HTTPS redirect, and verify it resolves. Handles domains registered at Hostinger or elsewhere, moving a site off its free *.hostingersite.com subdomain, subdomains and aliases. Triggers: connect my domain, point my domain to my site, use my own domain, move off the free subdomain, add a subdomain, park a domain, add a domain alias, my domain is not working.\"\n---\n\n# Connect a domain\n\nGet a domain serving a Hostinger website over HTTPS. The one rule that matters most: records that already work — email (`MX`), SPF, DKIM and verification `TXT` records — must survive every DNS change.\n\n## Calling the operations\n\n- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. \"dns records\").\n- Batch reads with `multi-execute`; values pass between steps as `$steps.<i>.<path>`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `agency-hosting`, `domains` and `dns` as separate servers. When `search` cannot find an operation, ask the user to enable that product group in the Connector.\n- Every write here changes what the public sees. State the change and get a yes before each one.\n\n## 1. Gather the facts (read-only)\n\n- **Website.** `hosting_websites_list` with `domain` (Shared and Cloud; substring match, so take the exact entry) and `agency-hosting_websites_list-plan` with `domain` (Agency). Keep `username` and `order_id`, or `details.uid` and `details.ipv4`. When the domain has no website yet, find the site the user means — often a `*.hostingersite.com` one — or the order to create it on (`hosting_orders_list`, `agency-hosting_orders_list`).\n- **Registration.** `domains_portfolio_get` succeeds only for domains registered at Hostinger; its `name_servers` show who runs DNS. Hostinger's own nameservers end in `dns-parking.com`.\n- **Live DNS**, to know what must be kept:\n\n```bash\ndig +short NS DOMAIN; dig +short A DOMAIN; dig +short CNAME www.DOMAIN\ndig +short MX DOMAIN; dig +short TXT DOMAIN; dig +short CAA DOMAIN\n```\n\n## 2. Attach the domain to hosting\n\nShared and Cloud:\n\n- **New website on the domain.** For a domain outside this account run `hosting_domains_verify-ownership` first. When `is_accessible` is false, give the user the `TXT` record it returns to add next to the existing ones, then verify again (propagation takes up to ~10 minutes). Create with `hosting_websites_create` (`domain` without `www.`, `order_id`), then poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed`.\n- **Moving a site off its free subdomain.** Shared and Cloud websites cannot be renamed through the API. Either create a website on the real domain and redeploy the project there (the `deploy-to-hosting` skill) — required for WordPress, which keeps its URL in the database — or, for static sites, park the domain on the existing site with `hosting_domains_create-website-parked`.\n- **Alias** (a second domain showing the same site): `hosting_domains_create-website-parked`.\n- **Subdomain** (`shop.example.com`): `hosting_domains_create-website-subdomain`. `www` belongs to the main domain — do not create it as a subdomain.\n\nAgency Plan:\n\n- `agency-hosting_domains_link-to-website` adds a domain to the site.\n- `agency-hosting_domains_change-website` replaces the primary domain, for example the free subdomain. The old name stops serving at once — confirm first.\n\n## 3. Point DNS at Hostinger\n\n**DNS already at Hostinger** (nameservers end in `dns-parking.com`): creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it.\n\n`dns_records_update` defaults to `overwrite: true`, which deletes every existing record with the same name and type. Adding a `TXT` at `@` that way wipes SPF and verification records. Send `overwrite: false` when adding; use `true` only to replace one record set on purpose, and run `dns_records_validate` on the same payload first.\n\n**DNS elsewhere** (registered at Hostinger with other nameservers, or registered elsewhere). Two options — let the user choose:\n\n- **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains (`ns1`/`ns2.dns-parking.com`, `solar`/`lunar.dns-parking.com` and others), so use the pair hPanel shows for this domain, never a guessed one. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar.\n- **Keep the current DNS provider** and change only the web records: `A` for `@` and `CNAME` (or `A`) for `www`. Agency sites point at `details.ipv4`. Shared and Cloud sites point at the targets in the Hostinger zone for the domain (`dns_records_list`) or, when that zone does not exist, the IP hPanel shows for the website. The user makes this change at their DNS provider.\n\n## 4. SSL and HTTPS\n\nStart only once `dig` shows the domain resolving to Hostinger — the certificate authority checks the domain against the server.\n\n- Shared and Cloud: `ssl` in `hosting_websites_get-settings`. When `ssl.status` is not `active`, call `hosting_ssl_install` and poll `hosting_websites_get-settings` until it is `active` (or `failed` with `last_error`), then `hosting_websites_update-settings` with `httpsRedirect: true` (it changes nothing until the certificate is `active`).\n- Agency: `agency-hosting_ssl_install-website` for each domain, then poll `agency-hosting_ssl_website-status`. A domain allows three setups per seven days and one request a minute — install once DNS is right and never loop.\n- A `CAA` record at the apex that does not allow `letsencrypt.org` blocks issuance; the user adds `0 issue \"letsencrypt.org\"` beside the existing ones.\n\n## 5. Verify\n\n```bash\ndig +short A DOMAIN; dig +short A www.DOMAIN\ncurl -sSI https://DOMAIN/ | grep -i -E \"^(HTTP|platform|location)\"\ncurl -sSI http://DOMAIN/ | grep -i -E \"^(HTTP|location)\"\ndig +short MX DOMAIN\n```\n\nDone when both names resolve to Hostinger, HTTPS answers with `platform: hostinger`, HTTP redirects to HTTPS, and `MX` still matches step 1. Nameserver changes can take up to 24–48 hours to reach every resolver; when records are still old, report what is pending and the command to re-check instead of polling for hours.\n"
31
+ "text": "---\nname: connect-domain\ndescription: \"Connect a custom domain to a website on Hostinger web hosting (Shared, Cloud or Agency plans) end to end: attach the domain to the site, point DNS at Hostinger without breaking existing email or verification records, install SSL, turn on the HTTPS redirect, and verify it resolves. Handles domains registered at Hostinger or elsewhere, moving a site off its free *.hostingersite.com subdomain, subdomains and aliases. Triggers: connect my domain, point my domain to my site, use my own domain, move off the free subdomain, add a subdomain, park a domain, add a domain alias, my domain is not working.\"\n---\n\n# Connect a domain\n\nGet a domain serving a Hostinger website over HTTPS. The one rule that matters most: records that already work — email (`MX`), SPF, DKIM and verification `TXT` records — must survive every DNS change.\n\n## Calling the operations\n\n- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. \"dns records\").\n- Batch reads with `multi-execute`; values pass between steps as `$steps.<i>.<path>`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `agency-hosting`, `domains` and `dns` as separate servers. When `search` cannot find an operation, ask the user to enable that product group in the Connector.\n- Every write here changes what the public sees. State the change and get a yes before each one.\n\n## 1. Gather the facts (read-only)\n\n- **Website.** `hosting_websites_list` with `domain` (Shared and Cloud; substring match, so take the exact entry) and `agency-hosting_websites_list-plan` with `domain` (Agency). Keep `username` and `order_id`, or `details.uid` and `details.ipv4`. When the domain has no website yet, find the site the user means — often a `*.hostingersite.com` one — or the order to create it on (`hosting_orders_list`, `agency-hosting_orders_list`).\n- **Registration.** `domains_portfolio_get` succeeds only for domains registered at Hostinger; its `name_servers` show who runs DNS. Hostinger's own nameservers end in `dns-parking.com`.\n- **Live DNS**, to know what must be kept:\n\n```bash\ndig +short NS DOMAIN; dig +short A DOMAIN; dig +short CNAME www.DOMAIN\ndig +short MX DOMAIN; dig +short TXT DOMAIN; dig +short CAA DOMAIN\n```\n\n## 2. Attach the domain to hosting\n\nShared and Cloud:\n\n- **New website on the domain.** For a domain outside this account run `hosting_domains_verify-ownership` first. When `is_accessible` is false, give the user the `TXT` record it returns to add next to the existing ones, then verify again (propagation takes up to ~10 minutes). Create with `hosting_websites_create` (`domain` without `www.`, `order_id`), then poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed`.\n- **Moving a site off its free subdomain.** Shared and Cloud websites cannot be renamed through the API. Either create a website on the real domain and redeploy the project there (the `deploy-to-hosting` skill) — required for WordPress, which keeps its URL in the database — or, for static sites, park the domain on the existing site with `hosting_domains_create-website-parked`.\n- **Alias** (a second domain showing the same site): `hosting_domains_create-website-parked`.\n- **Subdomain** (`shop.example.com`): `hosting_domains_create-website-subdomain`. `www` belongs to the main domain — do not create it as a subdomain.\n\nAgency Plan:\n\n- `agency-hosting_domains_link-to-website` adds a domain to the site.\n- `agency-hosting_domains_change-website` replaces the primary domain, for example the free subdomain. The old name stops serving at once — confirm first.\n\n## 3. Point DNS at Hostinger\n\n**DNS already at Hostinger** (nameservers end in `dns-parking.com`): keep the domain's existing `*.dns-parking.com` nameserver pair and do not change its nameservers. Creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it.\n\n`dns_records_update` defaults to `overwrite: true`, which deletes every existing record with the same name and type. Adding a `TXT` at `@` that way wipes SPF and verification records. Send `overwrite: false` when adding; use `true` only to replace one record set on purpose, and run `dns_records_validate` on the same payload first.\n\n**DNS elsewhere** (registered at Hostinger with other nameservers, or registered elsewhere). Two options — let the user choose:\n\n- **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains, and DNS resolves only on the pair the domain belongs to. Use the pair hPanel shows for the domain (Nameservers page → \"Use Hostinger nameservers\"), or ask the user for it. Never guess nameservers, and never use `ns1`/`ns2.dns-parking.com`. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar.\n- **Keep the current DNS provider** and change only the web records: `A` for `@` and `CNAME` (or `A`) for `www`. Agency sites point at `details.ipv4`. Shared and Cloud sites point at the targets in the Hostinger zone for the domain (`dns_records_list`) or, when that zone does not exist, the IP hPanel shows for the website. The user makes this change at their DNS provider.\n\n## 4. SSL and HTTPS\n\nStart only once `dig` shows the domain resolving to Hostinger — the certificate authority checks the domain against the server.\n\n- Shared and Cloud: `ssl` in `hosting_websites_get-settings`. When `ssl.status` is not `active`, call `hosting_ssl_install` and poll `hosting_websites_get-settings` until it is `active` (or `failed` with `last_error`), then `hosting_websites_update-settings` with `httpsRedirect: true` (it changes nothing until the certificate is `active`).\n- Agency: `agency-hosting_ssl_install-website` for each domain, then poll `agency-hosting_ssl_website-status`. A domain allows three setups per seven days and one request a minute — install once DNS is right and never loop.\n- A `CAA` record at the apex that does not allow `letsencrypt.org` blocks issuance; the user adds `0 issue \"letsencrypt.org\"` beside the existing ones.\n\n## 5. Verify\n\n```bash\ndig +short A DOMAIN; dig +short A www.DOMAIN\ncurl -sSI https://DOMAIN/ | grep -i -E \"^(HTTP|platform|location)\"\ncurl -sSI http://DOMAIN/ | grep -i -E \"^(HTTP|location)\"\ndig +short MX DOMAIN\n```\n\nDone when both names resolve to Hostinger, HTTPS answers with `platform: hostinger`, HTTP redirects to HTTPS, and `MX` still matches step 1. Nameserver changes can take up to 24–48 hours to reach every resolver; when records are still old, report what is pending and the command to re-check instead of polling for hours.\n"
32
32
  }
33
33
  ]
34
34
  },
@@ -253,10 +253,10 @@ export default [
253
253
  },
254
254
  {
255
255
  "uri": "skill://hostinger-headless/steps/09-done/SKILL.md",
256
- "digest": "sha256:e1e5c09f0d6f01f1d911455a8e49667da715a37cca140105d08fc5e8a73e435d",
257
- "size": 11209,
256
+ "digest": "sha256:896b76e2a90868df56a36d59010b18c4944cfca93233847b3e041c436e154bfa",
257
+ "size": 13831,
258
258
  "mimeType": "text/markdown",
259
- "text": "---\nname: headless-step-done\ndescription: \"Step 9 (done) of the Hostinger Headless flow. Verifies the live site, then hands over the URLs (hPanel success page, main site, WordPress/store admin, dashboard), opens the success page in the browser on a successful run, and gives a summary of what was built and what the user can do next. Its final report format is also used whenever a run stops early.\"\n---\n\n# headless-step-done\n\nVerify the live site, then hand over the URLs (success page, main site, WordPress admin, dashboard), a\nsummary of what was built, and what the user can do next. **On a successful run, open the success page\nfor the user** (see *Open the success page*).\n\n## Final report\n\nEvery run ends with this report, whether it finishes here or stops at an earlier stage (a dummy or\nmissing step, a failed provision, the user pausing). On a run that deployed this session, run the\nchecks in *Verify the live site* below first. Build the report from `state.json`, `site.json`,\n`deploy.json` and what you actually did and checked in this run. Report only what you verified. Don't\nclaim a stage passed if you didn't check it.\n\nWrite it in this order:\n\n1. **One-paragraph outcome.** What was created and on which domain (bold it). If the run stopped\n early, name the stage it stopped at, why, and what the user will see right now (e.g. \"the site\n root returns 403 until the frontend is deployed\").\n\n2. **Steps completed.** This table is required on every run, including early stops: never replace\n it with prose. Give one row per stage that ran, using the stage numbers and names from the\n orchestrator's flow table. Leave out stages that didn't run and name them in the outcome\n paragraph instead. A finished WordPress blog build looks like this:\n\n | Stage | Result |\n |---|---|\n | connect | Hostinger connector reached over MCP (`@hostinger/mcp` 1.9.2); signed in through the browser login. |\n | 1 check state | New build for `harbourlight.blog`; no earlier workspace. |\n | 2 refine | Personal travel blog about coastal towns in the UK; backend: WordPress. |\n | 3 provision | Business plan, order 41875230; setup took 6 min; domain, SSL and WordPress checks passed. |\n | 4 content model | 5 pages, 8 posts, 6 categories, 1 menu, 14 images (Hostinger AI), SEO titles on every page; counts re-read from the REST API. |\n | 5 design | \"Salt & Ink\" direction: serif headings, sea-blue accents; `design.md` and logo written, logo check passed. |\n | 6 frontend | Next.js SSR with home, blog, post, category and contact pages wired to the REST API; local build passed. |\n | 7 validate | Instance setup completed; REST API returned the 8 pushed posts; no preview (nobody signing off). |\n | 8 deploy | Source uploaded, build finished in 3 min; site root returns 200. |\n | 9 done | Main site, wp-admin and REST API re-checked live: all returned 200. |\n\n3. **Links** — every URL that exists at this point. Build the hPanel URLs from `site.json`\n (`order_id`, `username`, `domain`) and the onboarding slug; take the rest from `site.json` /\n `deploy.json`. Lead with the one the user cares about most — the **main site** — then the admin\n and dashboards. **Don't list the success page here:** it opens on its own (see *Open the success\n page*), so printing its URL in the report is just noise — the only exception is when a browser\n can't be opened (handled in that section):\n\n - **Main site (live):** `urls.site` (`https://<domain>`) — only once deployed.\n - **WordPress admin (one-click auto-login):** for a WordPress backend, **mint and hand over the\n auto-login redirect URL** — `wordpress_login_create-links { username, software: <site.json\n wordpress.install_id> }` returns a temporary link that logs the user straight into wp-admin. Give\n that URL as the admin link (it lands on `/cms/wp-admin`), so the user never needs a password. Also\n note the plain `urls.wp_admin` as a fallback. **Never print or store a password.** For a\n **WordPress + WooCommerce** store, this *same* wp-admin login is the store admin (wp-admin →\n WooCommerce → Products/Orders) — do **not** use the Ecommerce `store_admin` path. **Call out the one\n setup step under *Gaps / follow-ups*:** when `site.json → woocommerce.payment_enabled` is `false`,\n the owner must enable a payment method + a shipping zone in WooCommerce → Settings before checkout\n works (the catalog and cart already work). For a Hostinger **Ecommerce** store, link the store admin\n from `urls.store_admin` instead.\n - **hPanel site dashboard:** `https://hpanel.hostinger.com/websites/<domain>` (the headless website\n is installed by now).\n - **Onboarding (resume):** `https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/`\n - **REST API** (for developers): `urls.rest_api`.\n\n > URL shape note: `<username>` may be dropped from the onboarding/success URLs in a future hPanel\n > release. If a built URL 404s, retry the same path without the `/<username>` segment before\n > reporting it as broken.\n\n4. **Where state lives** — the workspace path `~/.hostinger/<domain>/`, the `stage` that a\n resumed run will start from, and the build report `~/.hostinger/<domain>/report.html`.\n\n5. **Gaps / follow-ups** (only if any) — a short numbered list of anything skipped, approximated or\n left for the user (e.g. no images generated, default WordPress content left in place).\n\nNever include passwords, JWTs or other secrets in the report. For wp-admin access, give the URL and\noffer a one-click login link (`wordpress_login_create-links`), not credentials.\n\n## Verify the live site (brief)\n\nOn a run that deployed this session, re-confirm what you're about to hand over actually works (deploy\nalready checked this — this is a quick re-confirm, not a full re-run):\n\n- **Main site:** `GET https://<domain>/` → 200 (Next.js SSR: `x-powered-by: Next.js`; Astro SSG: static\n HTML with real content).\n- **WordPress admin:** `GET {urls.wp_admin}` → reachable (a login page, 200/302), not 404.\n- **Backend API:** `GET {urls.rest_api}/posts?per_page=1` → 200 (WordPress); for a **WooCommerce** store\n (`site.json → woocommerce.enabled`) also `GET {urls.store_api}/products?per_page=1` → 200; or the\n storefront API for the sales channel → 200 (Ecommerce).\n- **Contact form** (`site.json → contact_form.enabled`): validate already sent a test submission, so\n don't send another here. In the handover, tell the owner the contact page is live and enquiries email\n the **WordPress admin address** (via `wp_mail`) — and that for the most reliable delivery that address\n should be a Hostinger mailbox on the domain. If validate reported `mail_failed`, say so plainly instead\n of claiming the form sends.\n\nIf a check fails, say so in the outcome paragraph and don't claim the site is live.\n\n## Open the success page\n\nOn a **successful** run (the deploy succeeded and the live checks passed), open the success page in the\nuser's **main / system browser** right after presenting the report, so they land on the hPanel\ncelebration/next-steps screen for their new site. This is a side effect only — **don't print the\nsuccess URL in the report output** (it just opens); the one exception is the can't-open-a-browser\nfallback below. Build the URL with `<tool>` as the running tool's onboarding slug (see *State and\nartifacts* in the top-level `SKILL.md`):\n\n```bash\n# Build the URL first, then open it with the OS default (main) browser — not an embedded one.\n# <tool> = the onboarding slug\nURL=\"https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/success/<domain>/<tool>\"\nopen \"$URL\" # macOS\n# xdg-open \"$URL\" # Linux\n# start \"\" \"$URL\" # Windows\n```\n\n- **Main browser only.** Use the OS opener above (`open` / `xdg-open` / `start`) so the page lands in\n Chrome, Safari, Edge, or whatever the user actually uses. **Do not** open it in an embedded or\n sandboxed browser (Cursor IDE browser / `cursor-ide-browser`, Playwright, Puppeteer, WebView, or\n any in-agent browser tab). Those stay inside the tool session and the user won't see the\n celebration page where they expect it.\n- Open it **once**, and only on a successful run — never on an early stop or a failed deploy.\n- If your tool can't open a browser (the command isn't available, or `open`/`xdg-open`/`start`\n fails), don't treat it as a failure and **don't fall back to an embedded browser**. This is the\n **one** case where you print the success URL — show it once in the report and tell the user to\n open it themselves in their main browser.\n\n## Build report\n\nRight after the final report, save the build report for this run. It turns the run log (stage times\nfrom `run-log.json`, events from `events.jsonl`) into `reports/<run>.json` and `.html`, and copies the\nHTML to `report.html` in the workspace:\n\n```bash\nnode <skill>/run-log.mjs <domain> event good '{\"text\":\"<something that went well>\"}' # one per thing, if not logged yet\nnode <skill>/run-log.mjs <domain> stage-end done ok\nnode <skill>/run-log.mjs <domain> report --outcome <live|stopped|failed> [--tokens '<json>']\n```\n\nThen send it to Hostinger: pass `reports/<run>.json` unchanged to `hosting_headless_submit-build-report`\nand run `report-sent <run>` (or `report-rejected <run>` on a 422). The `report` command prints the\nexact path and run id. Don't ask first; see *Run log and build report* in the top-level `SKILL.md`\nfor failures. Then tell the user in one line: \"Sent the build report to Hostinger to help improve\nthe flow.\" On a 422 say it's saved locally only; on any other failure say it'll be sent next session.\n\nIf the user asks for more changes after this report in the same session, call\n`run-log.mjs <domain> run-resume --reason \"<what they asked for>\"` before the work. Then log as\nusual and run `report` again when it's done. It updates this run's report in place, and the report\nlists the follow-up. Send the updated report the same way.\n\n- **Outcome:** `live` when the deploy succeeded this run, `failed` when a stage failed and you\n couldn't recover, `stopped` for everything else (the user paused, a dummy or missing step). Leave\n `--outcome` out and the script decides from the log.\n- **Before you write it,** check that every issue, correction and skill gap from this run is in\n `events.jsonl`. Log any that are missing now; the report only shows what was logged.\n- **After you write it,** read the `warning:` lines `report` prints. They list logging gaps: a stage\n that never ended, a start that was estimated, a stage logged with no duration, or (on a live build)\n a stage that was never logged. Fix what you can: `stage-end` an open stage, or correct a start with\n `stage-fix <stage> --started-ago <time>`, then run `report` again. What you can't fix stays in the report's\n *Logging gaps* section.\n- **Tokens:** only what your tool tells you about your own usage (see *Run log and build report* in\n the top-level `SKILL.md`).\n- In the final report's *Where state lives*, add the path to `report.html` so the user can open it.\n\nDo this on every run, including the early stops: those reports are the most useful ones.\n"
259
+ "text": "---\nname: headless-step-done\ndescription: \"Step 9 (done) of the Hostinger Headless flow. Verifies the live site, then hands over the URLs (hPanel success page, main site, WordPress/store admin, dashboard), opens the success page in the browser on a successful run, and gives a summary of what was built plus explicit next steps — how to log in to wp-admin and reset the password, how to manage content, and how to finish WooCommerce/store setup so it can take orders. Its final report format is also used whenever a run stops early.\"\n---\n\n# headless-step-done\n\nVerify the live site, then hand over the URLs (success page, main site, WordPress admin, dashboard), a\nsummary of what was built, and what the user can do next. **On a successful run, open the success page\nfor the user** (see *Open the success page*).\n\n## Final report\n\nEvery run ends with this report, whether it finishes here or stops at an earlier stage (a dummy or\nmissing step, a failed provision, the user pausing). On a run that deployed this session, run the\nchecks in *Verify the live site* below first. Build the report from `state.json`, `site.json`,\n`deploy.json` and what you actually did and checked in this run. Report only what you verified. Don't\nclaim a stage passed if you didn't check it.\n\nWrite it in this order:\n\n1. **One-paragraph outcome.** What was created and on which domain (bold it). If the run stopped\n early, name the stage it stopped at, why, and what the user will see right now (e.g. \"the site\n root returns 403 until the frontend is deployed\").\n\n2. **Steps completed.** This table is required on every run, including early stops: never replace\n it with prose. Give one row per stage that ran, using the stage numbers and names from the\n orchestrator's flow table. Leave out stages that didn't run and name them in the outcome\n paragraph instead. A finished WordPress blog build looks like this:\n\n | Stage | Result |\n |---|---|\n | connect | Hostinger connector reached over MCP (`@hostinger/mcp` 1.9.2); signed in through the browser login. |\n | 1 check state | New build for `harbourlight.blog`; no earlier workspace. |\n | 2 refine | Personal travel blog about coastal towns in the UK; backend: WordPress. |\n | 3 provision | Business plan, order 41875230; setup took 6 min; domain, SSL and WordPress checks passed. |\n | 4 content model | 5 pages, 8 posts, 6 categories, 1 menu, 14 images (Hostinger AI), SEO titles on every page; counts re-read from the REST API. |\n | 5 design | \"Salt & Ink\" direction: serif headings, sea-blue accents; `design.md` and logo written, logo check passed. |\n | 6 frontend | Next.js SSR with home, blog, post, category and contact pages wired to the REST API; local build passed. |\n | 7 validate | Instance setup completed; REST API returned the 8 pushed posts; no preview (nobody signing off). |\n | 8 deploy | Source uploaded, build finished in 3 min; site root returns 200. |\n | 9 done | Main site, wp-admin and REST API re-checked live: all returned 200. |\n\n3. **Links** — every URL that exists at this point. Build the hPanel URLs from `site.json`\n (`order_id`, `username`, `domain`) and the onboarding slug; take the rest from `site.json` /\n `deploy.json`. Lead with the one the user cares about most — the **main site** — then the admin\n and dashboards. **Don't list the success page here:** it opens on its own (see *Open the success\n page*), so printing its URL in the report is just noise — the only exception is when a browser\n can't be opened (handled in that section):\n\n - **Main site (live):** `urls.site` (`https://<domain>`) — only once deployed.\n - **WordPress admin (one-click auto-login):** for a WordPress backend, **mint and hand over the\n auto-login redirect URL** — `wordpress_login_create-links { username, software: <site.json\n wordpress.install_id> }` returns a temporary link that logs the user straight into wp-admin. Give\n that URL as the admin link (it lands on `/cms/wp-admin`), so the user never needs a password.\n Alongside it, give the two ways back in that don't expire: the **login URL `{urls.wp_admin}`**\n (`https://<domain>/cms/wp-admin` — the headless WordPress lives under `/cms`), and the **WP Admin\n button in hPanel** (hPanel → the website → *WP Admin*). Note how to **reset the password**: on the\n `/cms/wp-admin` login screen use **\"Lost your password?\"**, and WordPress emails a reset link to the\n **WordPress admin email** (the address set at install, which the owner already has) — see *Next\n steps* for an optional deliverability tip.\n **Never print or store a password.** For a **WordPress + WooCommerce** store, this *same* wp-admin\n login is the store admin (wp-admin → WooCommerce → Products/Orders) — do **not** use the Ecommerce\n `store_admin` path; the store setup the owner still needs is spelled out under *Next steps*. For a\n Hostinger **Ecommerce** store, link the store admin from `urls.store_admin` instead.\n - **hPanel site dashboard:** `https://hpanel.hostinger.com/websites/<domain>` (the headless website\n is installed by now).\n - **Onboarding (resume):** `https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/`\n - **REST API** (for developers): `urls.rest_api`.\n\n > URL shape note: `<username>` may be dropped from the onboarding/success URLs in a future hPanel\n > release. If a built URL 404s, retry the same path without the `/<username>` segment before\n > reporting it as broken.\n\n4. **Next steps** — what the user does now to actually use the site. Required on a successful run;\n tailor the bullets to the backend and keep them concrete. This is the hand-holding the hPanel\n success page can't give: don't just link wp-admin, tell them what to do once they're in.\n\n - **Manage content (WordPress):** in wp-admin (the one-click link, the `/cms/wp-admin` login URL, or\n hPanel → *WP Admin*) → **Posts**, **Pages**, **Media**, **Appearance → Menus**. Say how edits reach\n the live site for *this* build: an **SSR** frontend re-reads the REST API each request, so changes\n show up with no redeploy; an **SSG/static** build must be redeployed to pick up new content (read\n `state.json → frontend.rendering`).\n - **Log in later & reset the password:** repeat the two non-expiring routes (the `/cms/wp-admin` login\n URL and hPanel's *WP Admin* button) and the **\"Lost your password?\"** reset. The reset link goes to\n the **WordPress admin email** — the address set at install, which the owner already has — so nothing\n extra is needed to receive it. Optionally, for more reliable delivery, they can create a mailbox on\n the domain (hPanel → *Emails*) and set it as the admin address in **wp-admin → Settings → General**.\n - **WordPress + WooCommerce — finish store setup so it can take orders.** The catalog and cart already\n work, but **checkout stays off until the owner configures it in wp-admin**: WooCommerce → Settings →\n **Payments** (enable at least one method) and → **Shipping** (add a zone with a rate), then confirm\n products/prices and place a test order. When `site.json → woocommerce.payment_enabled` is `false`,\n state plainly that this step is **required before any purchase works** — this is the gap owners hit.\n - **Hostinger Ecommerce store:** when `site.json → ecommerce.payment_enabled` is `false`, finish\n payment setup in the store admin (`urls.store_admin`) before the storefront can sell; otherwise it's\n ready to take orders.\n - **Webapp:** manage the app from hPanel → the website (environment variables, deployments, logs).\n\n5. **Where state lives** — the workspace path `~/.hostinger/<domain>/`, the `stage` that a\n resumed run will start from, and the build report `~/.hostinger/<domain>/report.html`.\n\n6. **Gaps / follow-ups** (only if any) — a short numbered list of anything skipped, approximated or\n left for the user (e.g. no images generated, default WordPress content left in place).\n\nNever include passwords, JWTs or other secrets in the report. For wp-admin access, give the URL and\noffer a one-click login link (`wordpress_login_create-links`), not credentials.\n\n## Verify the live site (brief)\n\nOn a run that deployed this session, re-confirm what you're about to hand over actually works (deploy\nalready checked this — this is a quick re-confirm, not a full re-run):\n\n- **Main site:** `GET https://<domain>/` → 200 (Next.js SSR: `x-powered-by: Next.js`; Astro SSG: static\n HTML with real content).\n- **WordPress admin:** `GET {urls.wp_admin}` → reachable (a login page, 200/302), not 404.\n- **Backend API:** `GET {urls.rest_api}/posts?per_page=1` → 200 (WordPress); for a **WooCommerce** store\n (`site.json → woocommerce.enabled`) also `GET {urls.store_api}/products?per_page=1` → 200; or the\n storefront API for the sales channel → 200 (Ecommerce).\n- **Contact form** (`site.json → contact_form.enabled`): validate already sent a test submission, so\n don't send another here. In the handover, tell the owner the contact page is live and enquiries email\n the **WordPress admin address** (via `wp_mail`) — and that for the most reliable delivery that address\n should be a Hostinger mailbox on the domain. If validate reported `mail_failed`, say so plainly instead\n of claiming the form sends.\n\nIf a check fails, say so in the outcome paragraph and don't claim the site is live.\n\n## Open the success page\n\nOn a **successful** run (the deploy succeeded and the live checks passed), open the success page in the\nuser's **main / system browser** right after presenting the report, so they land on the hPanel\ncelebration/next-steps screen for their new site. This is a side effect only — **don't print the\nsuccess URL in the report output** (it just opens); the one exception is the can't-open-a-browser\nfallback below. Build the URL with `<tool>` as the running tool's onboarding slug (see *State and\nartifacts* in the top-level `SKILL.md`):\n\n```bash\n# Build the URL first, then open it with the OS default (main) browser — not an embedded one.\n# <tool> = the onboarding slug\nURL=\"https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/success/<domain>/<tool>\"\nopen \"$URL\" # macOS\n# xdg-open \"$URL\" # Linux\n# start \"\" \"$URL\" # Windows\n```\n\n- **Main browser only.** Use the OS opener above (`open` / `xdg-open` / `start`) so the page lands in\n Chrome, Safari, Edge, or whatever the user actually uses. **Do not** open it in an embedded or\n sandboxed browser (Cursor IDE browser / `cursor-ide-browser`, Playwright, Puppeteer, WebView, or\n any in-agent browser tab). Those stay inside the tool session and the user won't see the\n celebration page where they expect it.\n- Open it **once**, and only on a successful run — never on an early stop or a failed deploy.\n- If your tool can't open a browser (the command isn't available, or `open`/`xdg-open`/`start`\n fails), don't treat it as a failure and **don't fall back to an embedded browser**. This is the\n **one** case where you print the success URL — show it once in the report and tell the user to\n open it themselves in their main browser.\n\n## Build report\n\nRight after the final report, save the build report for this run. It turns the run log (stage times\nfrom `run-log.json`, events from `events.jsonl`) into `reports/<run>.json` and `.html`, and copies the\nHTML to `report.html` in the workspace:\n\n```bash\nnode <skill>/run-log.mjs <domain> event good '{\"text\":\"<something that went well>\"}' # one per thing, if not logged yet\nnode <skill>/run-log.mjs <domain> stage-end done ok\nnode <skill>/run-log.mjs <domain> report --outcome <live|stopped|failed> [--tokens '<json>']\n```\n\nThen send it to Hostinger: pass `reports/<run>.json` unchanged to `hosting_headless_submit-build-report`\nand run `report-sent <run>` (or `report-rejected <run>` on a 422). The `report` command prints the\nexact path and run id. Don't ask first; see *Run log and build report* in the top-level `SKILL.md`\nfor failures. Then tell the user in one line: \"Sent the build report to Hostinger to help improve\nthe flow.\" On a 422 say it's saved locally only; on any other failure say it'll be sent next session.\n\nIf the user asks for more changes after this report in the same session, call\n`run-log.mjs <domain> run-resume --reason \"<what they asked for>\"` before the work. Then log as\nusual and run `report` again when it's done. It updates this run's report in place, and the report\nlists the follow-up. Send the updated report the same way.\n\n- **Outcome:** `live` when the deploy succeeded this run, `failed` when a stage failed and you\n couldn't recover, `stopped` for everything else (the user paused, a dummy or missing step). Leave\n `--outcome` out and the script decides from the log.\n- **Before you write it,** check that every issue, correction and skill gap from this run is in\n `events.jsonl`. Log any that are missing now; the report only shows what was logged.\n- **After you write it,** read the `warning:` lines `report` prints. They list logging gaps: a stage\n that never ended, a start that was estimated, a stage logged with no duration, or (on a live build)\n a stage that was never logged. Fix what you can: `stage-end` an open stage, or correct a start with\n `stage-fix <stage> --started-ago <time>`, then run `report` again. What you can't fix stays in the report's\n *Logging gaps* section.\n- **Tokens:** only what your tool tells you about your own usage (see *Run log and build report* in\n the top-level `SKILL.md`).\n- In the final report's *Where state lives*, add the path to `report.html` so the user can open it.\n\nDo this on every run, including the early stops: those reports are the most useful ones.\n"
260
260
  }
261
261
  ]
262
262
  },
@@ -39,10 +39,10 @@ const skills: Skill[] = [
39
39
  "resources": [
40
40
  {
41
41
  "uri": "skill://connect-domain/SKILL.md",
42
- "digest": "sha256:f864ecc8a8aaf67408d3838b1e6962a7e5d717a52febea04553a9692709cfed4",
43
- "size": 6993,
42
+ "digest": "sha256:500ef355966547e908662760ce834d463987f4eaee8a52ea101dcccc26f11e1a",
43
+ "size": 7192,
44
44
  "mimeType": "text/markdown",
45
- "text": "---\nname: connect-domain\ndescription: \"Connect a custom domain to a website on Hostinger web hosting (Shared, Cloud or Agency plans) end to end: attach the domain to the site, point DNS at Hostinger without breaking existing email or verification records, install SSL, turn on the HTTPS redirect, and verify it resolves. Handles domains registered at Hostinger or elsewhere, moving a site off its free *.hostingersite.com subdomain, subdomains and aliases. Triggers: connect my domain, point my domain to my site, use my own domain, move off the free subdomain, add a subdomain, park a domain, add a domain alias, my domain is not working.\"\n---\n\n# Connect a domain\n\nGet a domain serving a Hostinger website over HTTPS. The one rule that matters most: records that already work — email (`MX`), SPF, DKIM and verification `TXT` records — must survive every DNS change.\n\n## Calling the operations\n\n- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. \"dns records\").\n- Batch reads with `multi-execute`; values pass between steps as `$steps.<i>.<path>`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `agency-hosting`, `domains` and `dns` as separate servers. When `search` cannot find an operation, ask the user to enable that product group in the Connector.\n- Every write here changes what the public sees. State the change and get a yes before each one.\n\n## 1. Gather the facts (read-only)\n\n- **Website.** `hosting_websites_list` with `domain` (Shared and Cloud; substring match, so take the exact entry) and `agency-hosting_websites_list-plan` with `domain` (Agency). Keep `username` and `order_id`, or `details.uid` and `details.ipv4`. When the domain has no website yet, find the site the user means — often a `*.hostingersite.com` one — or the order to create it on (`hosting_orders_list`, `agency-hosting_orders_list`).\n- **Registration.** `domains_portfolio_get` succeeds only for domains registered at Hostinger; its `name_servers` show who runs DNS. Hostinger's own nameservers end in `dns-parking.com`.\n- **Live DNS**, to know what must be kept:\n\n```bash\ndig +short NS DOMAIN; dig +short A DOMAIN; dig +short CNAME www.DOMAIN\ndig +short MX DOMAIN; dig +short TXT DOMAIN; dig +short CAA DOMAIN\n```\n\n## 2. Attach the domain to hosting\n\nShared and Cloud:\n\n- **New website on the domain.** For a domain outside this account run `hosting_domains_verify-ownership` first. When `is_accessible` is false, give the user the `TXT` record it returns to add next to the existing ones, then verify again (propagation takes up to ~10 minutes). Create with `hosting_websites_create` (`domain` without `www.`, `order_id`), then poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed`.\n- **Moving a site off its free subdomain.** Shared and Cloud websites cannot be renamed through the API. Either create a website on the real domain and redeploy the project there (the `deploy-to-hosting` skill) — required for WordPress, which keeps its URL in the database — or, for static sites, park the domain on the existing site with `hosting_domains_create-website-parked`.\n- **Alias** (a second domain showing the same site): `hosting_domains_create-website-parked`.\n- **Subdomain** (`shop.example.com`): `hosting_domains_create-website-subdomain`. `www` belongs to the main domain — do not create it as a subdomain.\n\nAgency Plan:\n\n- `agency-hosting_domains_link-to-website` adds a domain to the site.\n- `agency-hosting_domains_change-website` replaces the primary domain, for example the free subdomain. The old name stops serving at once — confirm first.\n\n## 3. Point DNS at Hostinger\n\n**DNS already at Hostinger** (nameservers end in `dns-parking.com`): creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it.\n\n`dns_records_update` defaults to `overwrite: true`, which deletes every existing record with the same name and type. Adding a `TXT` at `@` that way wipes SPF and verification records. Send `overwrite: false` when adding; use `true` only to replace one record set on purpose, and run `dns_records_validate` on the same payload first.\n\n**DNS elsewhere** (registered at Hostinger with other nameservers, or registered elsewhere). Two options — let the user choose:\n\n- **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains (`ns1`/`ns2.dns-parking.com`, `solar`/`lunar.dns-parking.com` and others), so use the pair hPanel shows for this domain, never a guessed one. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar.\n- **Keep the current DNS provider** and change only the web records: `A` for `@` and `CNAME` (or `A`) for `www`. Agency sites point at `details.ipv4`. Shared and Cloud sites point at the targets in the Hostinger zone for the domain (`dns_records_list`) or, when that zone does not exist, the IP hPanel shows for the website. The user makes this change at their DNS provider.\n\n## 4. SSL and HTTPS\n\nStart only once `dig` shows the domain resolving to Hostinger — the certificate authority checks the domain against the server.\n\n- Shared and Cloud: `ssl` in `hosting_websites_get-settings`. When `ssl.status` is not `active`, call `hosting_ssl_install` and poll `hosting_websites_get-settings` until it is `active` (or `failed` with `last_error`), then `hosting_websites_update-settings` with `httpsRedirect: true` (it changes nothing until the certificate is `active`).\n- Agency: `agency-hosting_ssl_install-website` for each domain, then poll `agency-hosting_ssl_website-status`. A domain allows three setups per seven days and one request a minute — install once DNS is right and never loop.\n- A `CAA` record at the apex that does not allow `letsencrypt.org` blocks issuance; the user adds `0 issue \"letsencrypt.org\"` beside the existing ones.\n\n## 5. Verify\n\n```bash\ndig +short A DOMAIN; dig +short A www.DOMAIN\ncurl -sSI https://DOMAIN/ | grep -i -E \"^(HTTP|platform|location)\"\ncurl -sSI http://DOMAIN/ | grep -i -E \"^(HTTP|location)\"\ndig +short MX DOMAIN\n```\n\nDone when both names resolve to Hostinger, HTTPS answers with `platform: hostinger`, HTTP redirects to HTTPS, and `MX` still matches step 1. Nameserver changes can take up to 24–48 hours to reach every resolver; when records are still old, report what is pending and the command to re-check instead of polling for hours.\n"
45
+ "text": "---\nname: connect-domain\ndescription: \"Connect a custom domain to a website on Hostinger web hosting (Shared, Cloud or Agency plans) end to end: attach the domain to the site, point DNS at Hostinger without breaking existing email or verification records, install SSL, turn on the HTTPS redirect, and verify it resolves. Handles domains registered at Hostinger or elsewhere, moving a site off its free *.hostingersite.com subdomain, subdomains and aliases. Triggers: connect my domain, point my domain to my site, use my own domain, move off the free subdomain, add a subdomain, park a domain, add a domain alias, my domain is not working.\"\n---\n\n# Connect a domain\n\nGet a domain serving a Hostinger website over HTTPS. The one rule that matters most: records that already work — email (`MX`), SPF, DKIM and verification `TXT` records — must survive every DNS change.\n\n## Calling the operations\n\n- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. \"dns records\").\n- Batch reads with `multi-execute`; values pass between steps as `$steps.<i>.<path>`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `agency-hosting`, `domains` and `dns` as separate servers. When `search` cannot find an operation, ask the user to enable that product group in the Connector.\n- Every write here changes what the public sees. State the change and get a yes before each one.\n\n## 1. Gather the facts (read-only)\n\n- **Website.** `hosting_websites_list` with `domain` (Shared and Cloud; substring match, so take the exact entry) and `agency-hosting_websites_list-plan` with `domain` (Agency). Keep `username` and `order_id`, or `details.uid` and `details.ipv4`. When the domain has no website yet, find the site the user means — often a `*.hostingersite.com` one — or the order to create it on (`hosting_orders_list`, `agency-hosting_orders_list`).\n- **Registration.** `domains_portfolio_get` succeeds only for domains registered at Hostinger; its `name_servers` show who runs DNS. Hostinger's own nameservers end in `dns-parking.com`.\n- **Live DNS**, to know what must be kept:\n\n```bash\ndig +short NS DOMAIN; dig +short A DOMAIN; dig +short CNAME www.DOMAIN\ndig +short MX DOMAIN; dig +short TXT DOMAIN; dig +short CAA DOMAIN\n```\n\n## 2. Attach the domain to hosting\n\nShared and Cloud:\n\n- **New website on the domain.** For a domain outside this account run `hosting_domains_verify-ownership` first. When `is_accessible` is false, give the user the `TXT` record it returns to add next to the existing ones, then verify again (propagation takes up to ~10 minutes). Create with `hosting_websites_create` (`domain` without `www.`, `order_id`), then poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed`.\n- **Moving a site off its free subdomain.** Shared and Cloud websites cannot be renamed through the API. Either create a website on the real domain and redeploy the project there (the `deploy-to-hosting` skill) — required for WordPress, which keeps its URL in the database — or, for static sites, park the domain on the existing site with `hosting_domains_create-website-parked`.\n- **Alias** (a second domain showing the same site): `hosting_domains_create-website-parked`.\n- **Subdomain** (`shop.example.com`): `hosting_domains_create-website-subdomain`. `www` belongs to the main domain — do not create it as a subdomain.\n\nAgency Plan:\n\n- `agency-hosting_domains_link-to-website` adds a domain to the site.\n- `agency-hosting_domains_change-website` replaces the primary domain, for example the free subdomain. The old name stops serving at once — confirm first.\n\n## 3. Point DNS at Hostinger\n\n**DNS already at Hostinger** (nameservers end in `dns-parking.com`): keep the domain's existing `*.dns-parking.com` nameserver pair and do not change its nameservers. Creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it.\n\n`dns_records_update` defaults to `overwrite: true`, which deletes every existing record with the same name and type. Adding a `TXT` at `@` that way wipes SPF and verification records. Send `overwrite: false` when adding; use `true` only to replace one record set on purpose, and run `dns_records_validate` on the same payload first.\n\n**DNS elsewhere** (registered at Hostinger with other nameservers, or registered elsewhere). Two options — let the user choose:\n\n- **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains, and DNS resolves only on the pair the domain belongs to. Use the pair hPanel shows for the domain (Nameservers page → \"Use Hostinger nameservers\"), or ask the user for it. Never guess nameservers, and never use `ns1`/`ns2.dns-parking.com`. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar.\n- **Keep the current DNS provider** and change only the web records: `A` for `@` and `CNAME` (or `A`) for `www`. Agency sites point at `details.ipv4`. Shared and Cloud sites point at the targets in the Hostinger zone for the domain (`dns_records_list`) or, when that zone does not exist, the IP hPanel shows for the website. The user makes this change at their DNS provider.\n\n## 4. SSL and HTTPS\n\nStart only once `dig` shows the domain resolving to Hostinger — the certificate authority checks the domain against the server.\n\n- Shared and Cloud: `ssl` in `hosting_websites_get-settings`. When `ssl.status` is not `active`, call `hosting_ssl_install` and poll `hosting_websites_get-settings` until it is `active` (or `failed` with `last_error`), then `hosting_websites_update-settings` with `httpsRedirect: true` (it changes nothing until the certificate is `active`).\n- Agency: `agency-hosting_ssl_install-website` for each domain, then poll `agency-hosting_ssl_website-status`. A domain allows three setups per seven days and one request a minute — install once DNS is right and never loop.\n- A `CAA` record at the apex that does not allow `letsencrypt.org` blocks issuance; the user adds `0 issue \"letsencrypt.org\"` beside the existing ones.\n\n## 5. Verify\n\n```bash\ndig +short A DOMAIN; dig +short A www.DOMAIN\ncurl -sSI https://DOMAIN/ | grep -i -E \"^(HTTP|platform|location)\"\ncurl -sSI http://DOMAIN/ | grep -i -E \"^(HTTP|location)\"\ndig +short MX DOMAIN\n```\n\nDone when both names resolve to Hostinger, HTTPS answers with `platform: hostinger`, HTTP redirects to HTTPS, and `MX` still matches step 1. Nameserver changes can take up to 24–48 hours to reach every resolver; when records are still old, report what is pending and the command to re-check instead of polling for hours.\n"
46
46
  }
47
47
  ]
48
48
  },
@@ -267,10 +267,10 @@ const skills: Skill[] = [
267
267
  },
268
268
  {
269
269
  "uri": "skill://hostinger-headless/steps/09-done/SKILL.md",
270
- "digest": "sha256:e1e5c09f0d6f01f1d911455a8e49667da715a37cca140105d08fc5e8a73e435d",
271
- "size": 11209,
270
+ "digest": "sha256:896b76e2a90868df56a36d59010b18c4944cfca93233847b3e041c436e154bfa",
271
+ "size": 13831,
272
272
  "mimeType": "text/markdown",
273
- "text": "---\nname: headless-step-done\ndescription: \"Step 9 (done) of the Hostinger Headless flow. Verifies the live site, then hands over the URLs (hPanel success page, main site, WordPress/store admin, dashboard), opens the success page in the browser on a successful run, and gives a summary of what was built and what the user can do next. Its final report format is also used whenever a run stops early.\"\n---\n\n# headless-step-done\n\nVerify the live site, then hand over the URLs (success page, main site, WordPress admin, dashboard), a\nsummary of what was built, and what the user can do next. **On a successful run, open the success page\nfor the user** (see *Open the success page*).\n\n## Final report\n\nEvery run ends with this report, whether it finishes here or stops at an earlier stage (a dummy or\nmissing step, a failed provision, the user pausing). On a run that deployed this session, run the\nchecks in *Verify the live site* below first. Build the report from `state.json`, `site.json`,\n`deploy.json` and what you actually did and checked in this run. Report only what you verified. Don't\nclaim a stage passed if you didn't check it.\n\nWrite it in this order:\n\n1. **One-paragraph outcome.** What was created and on which domain (bold it). If the run stopped\n early, name the stage it stopped at, why, and what the user will see right now (e.g. \"the site\n root returns 403 until the frontend is deployed\").\n\n2. **Steps completed.** This table is required on every run, including early stops: never replace\n it with prose. Give one row per stage that ran, using the stage numbers and names from the\n orchestrator's flow table. Leave out stages that didn't run and name them in the outcome\n paragraph instead. A finished WordPress blog build looks like this:\n\n | Stage | Result |\n |---|---|\n | connect | Hostinger connector reached over MCP (`@hostinger/mcp` 1.9.2); signed in through the browser login. |\n | 1 check state | New build for `harbourlight.blog`; no earlier workspace. |\n | 2 refine | Personal travel blog about coastal towns in the UK; backend: WordPress. |\n | 3 provision | Business plan, order 41875230; setup took 6 min; domain, SSL and WordPress checks passed. |\n | 4 content model | 5 pages, 8 posts, 6 categories, 1 menu, 14 images (Hostinger AI), SEO titles on every page; counts re-read from the REST API. |\n | 5 design | \"Salt & Ink\" direction: serif headings, sea-blue accents; `design.md` and logo written, logo check passed. |\n | 6 frontend | Next.js SSR with home, blog, post, category and contact pages wired to the REST API; local build passed. |\n | 7 validate | Instance setup completed; REST API returned the 8 pushed posts; no preview (nobody signing off). |\n | 8 deploy | Source uploaded, build finished in 3 min; site root returns 200. |\n | 9 done | Main site, wp-admin and REST API re-checked live: all returned 200. |\n\n3. **Links** — every URL that exists at this point. Build the hPanel URLs from `site.json`\n (`order_id`, `username`, `domain`) and the onboarding slug; take the rest from `site.json` /\n `deploy.json`. Lead with the one the user cares about most — the **main site** — then the admin\n and dashboards. **Don't list the success page here:** it opens on its own (see *Open the success\n page*), so printing its URL in the report is just noise — the only exception is when a browser\n can't be opened (handled in that section):\n\n - **Main site (live):** `urls.site` (`https://<domain>`) — only once deployed.\n - **WordPress admin (one-click auto-login):** for a WordPress backend, **mint and hand over the\n auto-login redirect URL** — `wordpress_login_create-links { username, software: <site.json\n wordpress.install_id> }` returns a temporary link that logs the user straight into wp-admin. Give\n that URL as the admin link (it lands on `/cms/wp-admin`), so the user never needs a password. Also\n note the plain `urls.wp_admin` as a fallback. **Never print or store a password.** For a\n **WordPress + WooCommerce** store, this *same* wp-admin login is the store admin (wp-admin →\n WooCommerce → Products/Orders) — do **not** use the Ecommerce `store_admin` path. **Call out the one\n setup step under *Gaps / follow-ups*:** when `site.json → woocommerce.payment_enabled` is `false`,\n the owner must enable a payment method + a shipping zone in WooCommerce → Settings before checkout\n works (the catalog and cart already work). For a Hostinger **Ecommerce** store, link the store admin\n from `urls.store_admin` instead.\n - **hPanel site dashboard:** `https://hpanel.hostinger.com/websites/<domain>` (the headless website\n is installed by now).\n - **Onboarding (resume):** `https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/`\n - **REST API** (for developers): `urls.rest_api`.\n\n > URL shape note: `<username>` may be dropped from the onboarding/success URLs in a future hPanel\n > release. If a built URL 404s, retry the same path without the `/<username>` segment before\n > reporting it as broken.\n\n4. **Where state lives** — the workspace path `~/.hostinger/<domain>/`, the `stage` that a\n resumed run will start from, and the build report `~/.hostinger/<domain>/report.html`.\n\n5. **Gaps / follow-ups** (only if any) — a short numbered list of anything skipped, approximated or\n left for the user (e.g. no images generated, default WordPress content left in place).\n\nNever include passwords, JWTs or other secrets in the report. For wp-admin access, give the URL and\noffer a one-click login link (`wordpress_login_create-links`), not credentials.\n\n## Verify the live site (brief)\n\nOn a run that deployed this session, re-confirm what you're about to hand over actually works (deploy\nalready checked this — this is a quick re-confirm, not a full re-run):\n\n- **Main site:** `GET https://<domain>/` → 200 (Next.js SSR: `x-powered-by: Next.js`; Astro SSG: static\n HTML with real content).\n- **WordPress admin:** `GET {urls.wp_admin}` → reachable (a login page, 200/302), not 404.\n- **Backend API:** `GET {urls.rest_api}/posts?per_page=1` → 200 (WordPress); for a **WooCommerce** store\n (`site.json → woocommerce.enabled`) also `GET {urls.store_api}/products?per_page=1` → 200; or the\n storefront API for the sales channel → 200 (Ecommerce).\n- **Contact form** (`site.json → contact_form.enabled`): validate already sent a test submission, so\n don't send another here. In the handover, tell the owner the contact page is live and enquiries email\n the **WordPress admin address** (via `wp_mail`) — and that for the most reliable delivery that address\n should be a Hostinger mailbox on the domain. If validate reported `mail_failed`, say so plainly instead\n of claiming the form sends.\n\nIf a check fails, say so in the outcome paragraph and don't claim the site is live.\n\n## Open the success page\n\nOn a **successful** run (the deploy succeeded and the live checks passed), open the success page in the\nuser's **main / system browser** right after presenting the report, so they land on the hPanel\ncelebration/next-steps screen for their new site. This is a side effect only — **don't print the\nsuccess URL in the report output** (it just opens); the one exception is the can't-open-a-browser\nfallback below. Build the URL with `<tool>` as the running tool's onboarding slug (see *State and\nartifacts* in the top-level `SKILL.md`):\n\n```bash\n# Build the URL first, then open it with the OS default (main) browser — not an embedded one.\n# <tool> = the onboarding slug\nURL=\"https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/success/<domain>/<tool>\"\nopen \"$URL\" # macOS\n# xdg-open \"$URL\" # Linux\n# start \"\" \"$URL\" # Windows\n```\n\n- **Main browser only.** Use the OS opener above (`open` / `xdg-open` / `start`) so the page lands in\n Chrome, Safari, Edge, or whatever the user actually uses. **Do not** open it in an embedded or\n sandboxed browser (Cursor IDE browser / `cursor-ide-browser`, Playwright, Puppeteer, WebView, or\n any in-agent browser tab). Those stay inside the tool session and the user won't see the\n celebration page where they expect it.\n- Open it **once**, and only on a successful run — never on an early stop or a failed deploy.\n- If your tool can't open a browser (the command isn't available, or `open`/`xdg-open`/`start`\n fails), don't treat it as a failure and **don't fall back to an embedded browser**. This is the\n **one** case where you print the success URL — show it once in the report and tell the user to\n open it themselves in their main browser.\n\n## Build report\n\nRight after the final report, save the build report for this run. It turns the run log (stage times\nfrom `run-log.json`, events from `events.jsonl`) into `reports/<run>.json` and `.html`, and copies the\nHTML to `report.html` in the workspace:\n\n```bash\nnode <skill>/run-log.mjs <domain> event good '{\"text\":\"<something that went well>\"}' # one per thing, if not logged yet\nnode <skill>/run-log.mjs <domain> stage-end done ok\nnode <skill>/run-log.mjs <domain> report --outcome <live|stopped|failed> [--tokens '<json>']\n```\n\nThen send it to Hostinger: pass `reports/<run>.json` unchanged to `hosting_headless_submit-build-report`\nand run `report-sent <run>` (or `report-rejected <run>` on a 422). The `report` command prints the\nexact path and run id. Don't ask first; see *Run log and build report* in the top-level `SKILL.md`\nfor failures. Then tell the user in one line: \"Sent the build report to Hostinger to help improve\nthe flow.\" On a 422 say it's saved locally only; on any other failure say it'll be sent next session.\n\nIf the user asks for more changes after this report in the same session, call\n`run-log.mjs <domain> run-resume --reason \"<what they asked for>\"` before the work. Then log as\nusual and run `report` again when it's done. It updates this run's report in place, and the report\nlists the follow-up. Send the updated report the same way.\n\n- **Outcome:** `live` when the deploy succeeded this run, `failed` when a stage failed and you\n couldn't recover, `stopped` for everything else (the user paused, a dummy or missing step). Leave\n `--outcome` out and the script decides from the log.\n- **Before you write it,** check that every issue, correction and skill gap from this run is in\n `events.jsonl`. Log any that are missing now; the report only shows what was logged.\n- **After you write it,** read the `warning:` lines `report` prints. They list logging gaps: a stage\n that never ended, a start that was estimated, a stage logged with no duration, or (on a live build)\n a stage that was never logged. Fix what you can: `stage-end` an open stage, or correct a start with\n `stage-fix <stage> --started-ago <time>`, then run `report` again. What you can't fix stays in the report's\n *Logging gaps* section.\n- **Tokens:** only what your tool tells you about your own usage (see *Run log and build report* in\n the top-level `SKILL.md`).\n- In the final report's *Where state lives*, add the path to `report.html` so the user can open it.\n\nDo this on every run, including the early stops: those reports are the most useful ones.\n"
273
+ "text": "---\nname: headless-step-done\ndescription: \"Step 9 (done) of the Hostinger Headless flow. Verifies the live site, then hands over the URLs (hPanel success page, main site, WordPress/store admin, dashboard), opens the success page in the browser on a successful run, and gives a summary of what was built plus explicit next steps — how to log in to wp-admin and reset the password, how to manage content, and how to finish WooCommerce/store setup so it can take orders. Its final report format is also used whenever a run stops early.\"\n---\n\n# headless-step-done\n\nVerify the live site, then hand over the URLs (success page, main site, WordPress admin, dashboard), a\nsummary of what was built, and what the user can do next. **On a successful run, open the success page\nfor the user** (see *Open the success page*).\n\n## Final report\n\nEvery run ends with this report, whether it finishes here or stops at an earlier stage (a dummy or\nmissing step, a failed provision, the user pausing). On a run that deployed this session, run the\nchecks in *Verify the live site* below first. Build the report from `state.json`, `site.json`,\n`deploy.json` and what you actually did and checked in this run. Report only what you verified. Don't\nclaim a stage passed if you didn't check it.\n\nWrite it in this order:\n\n1. **One-paragraph outcome.** What was created and on which domain (bold it). If the run stopped\n early, name the stage it stopped at, why, and what the user will see right now (e.g. \"the site\n root returns 403 until the frontend is deployed\").\n\n2. **Steps completed.** This table is required on every run, including early stops: never replace\n it with prose. Give one row per stage that ran, using the stage numbers and names from the\n orchestrator's flow table. Leave out stages that didn't run and name them in the outcome\n paragraph instead. A finished WordPress blog build looks like this:\n\n | Stage | Result |\n |---|---|\n | connect | Hostinger connector reached over MCP (`@hostinger/mcp` 1.9.2); signed in through the browser login. |\n | 1 check state | New build for `harbourlight.blog`; no earlier workspace. |\n | 2 refine | Personal travel blog about coastal towns in the UK; backend: WordPress. |\n | 3 provision | Business plan, order 41875230; setup took 6 min; domain, SSL and WordPress checks passed. |\n | 4 content model | 5 pages, 8 posts, 6 categories, 1 menu, 14 images (Hostinger AI), SEO titles on every page; counts re-read from the REST API. |\n | 5 design | \"Salt & Ink\" direction: serif headings, sea-blue accents; `design.md` and logo written, logo check passed. |\n | 6 frontend | Next.js SSR with home, blog, post, category and contact pages wired to the REST API; local build passed. |\n | 7 validate | Instance setup completed; REST API returned the 8 pushed posts; no preview (nobody signing off). |\n | 8 deploy | Source uploaded, build finished in 3 min; site root returns 200. |\n | 9 done | Main site, wp-admin and REST API re-checked live: all returned 200. |\n\n3. **Links** — every URL that exists at this point. Build the hPanel URLs from `site.json`\n (`order_id`, `username`, `domain`) and the onboarding slug; take the rest from `site.json` /\n `deploy.json`. Lead with the one the user cares about most — the **main site** — then the admin\n and dashboards. **Don't list the success page here:** it opens on its own (see *Open the success\n page*), so printing its URL in the report is just noise — the only exception is when a browser\n can't be opened (handled in that section):\n\n - **Main site (live):** `urls.site` (`https://<domain>`) — only once deployed.\n - **WordPress admin (one-click auto-login):** for a WordPress backend, **mint and hand over the\n auto-login redirect URL** — `wordpress_login_create-links { username, software: <site.json\n wordpress.install_id> }` returns a temporary link that logs the user straight into wp-admin. Give\n that URL as the admin link (it lands on `/cms/wp-admin`), so the user never needs a password.\n Alongside it, give the two ways back in that don't expire: the **login URL `{urls.wp_admin}`**\n (`https://<domain>/cms/wp-admin` — the headless WordPress lives under `/cms`), and the **WP Admin\n button in hPanel** (hPanel → the website → *WP Admin*). Note how to **reset the password**: on the\n `/cms/wp-admin` login screen use **\"Lost your password?\"**, and WordPress emails a reset link to the\n **WordPress admin email** (the address set at install, which the owner already has) — see *Next\n steps* for an optional deliverability tip.\n **Never print or store a password.** For a **WordPress + WooCommerce** store, this *same* wp-admin\n login is the store admin (wp-admin → WooCommerce → Products/Orders) — do **not** use the Ecommerce\n `store_admin` path; the store setup the owner still needs is spelled out under *Next steps*. For a\n Hostinger **Ecommerce** store, link the store admin from `urls.store_admin` instead.\n - **hPanel site dashboard:** `https://hpanel.hostinger.com/websites/<domain>` (the headless website\n is installed by now).\n - **Onboarding (resume):** `https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/`\n - **REST API** (for developers): `urls.rest_api`.\n\n > URL shape note: `<username>` may be dropped from the onboarding/success URLs in a future hPanel\n > release. If a built URL 404s, retry the same path without the `/<username>` segment before\n > reporting it as broken.\n\n4. **Next steps** — what the user does now to actually use the site. Required on a successful run;\n tailor the bullets to the backend and keep them concrete. This is the hand-holding the hPanel\n success page can't give: don't just link wp-admin, tell them what to do once they're in.\n\n - **Manage content (WordPress):** in wp-admin (the one-click link, the `/cms/wp-admin` login URL, or\n hPanel → *WP Admin*) → **Posts**, **Pages**, **Media**, **Appearance → Menus**. Say how edits reach\n the live site for *this* build: an **SSR** frontend re-reads the REST API each request, so changes\n show up with no redeploy; an **SSG/static** build must be redeployed to pick up new content (read\n `state.json → frontend.rendering`).\n - **Log in later & reset the password:** repeat the two non-expiring routes (the `/cms/wp-admin` login\n URL and hPanel's *WP Admin* button) and the **\"Lost your password?\"** reset. The reset link goes to\n the **WordPress admin email** — the address set at install, which the owner already has — so nothing\n extra is needed to receive it. Optionally, for more reliable delivery, they can create a mailbox on\n the domain (hPanel → *Emails*) and set it as the admin address in **wp-admin → Settings → General**.\n - **WordPress + WooCommerce — finish store setup so it can take orders.** The catalog and cart already\n work, but **checkout stays off until the owner configures it in wp-admin**: WooCommerce → Settings →\n **Payments** (enable at least one method) and → **Shipping** (add a zone with a rate), then confirm\n products/prices and place a test order. When `site.json → woocommerce.payment_enabled` is `false`,\n state plainly that this step is **required before any purchase works** — this is the gap owners hit.\n - **Hostinger Ecommerce store:** when `site.json → ecommerce.payment_enabled` is `false`, finish\n payment setup in the store admin (`urls.store_admin`) before the storefront can sell; otherwise it's\n ready to take orders.\n - **Webapp:** manage the app from hPanel → the website (environment variables, deployments, logs).\n\n5. **Where state lives** — the workspace path `~/.hostinger/<domain>/`, the `stage` that a\n resumed run will start from, and the build report `~/.hostinger/<domain>/report.html`.\n\n6. **Gaps / follow-ups** (only if any) — a short numbered list of anything skipped, approximated or\n left for the user (e.g. no images generated, default WordPress content left in place).\n\nNever include passwords, JWTs or other secrets in the report. For wp-admin access, give the URL and\noffer a one-click login link (`wordpress_login_create-links`), not credentials.\n\n## Verify the live site (brief)\n\nOn a run that deployed this session, re-confirm what you're about to hand over actually works (deploy\nalready checked this — this is a quick re-confirm, not a full re-run):\n\n- **Main site:** `GET https://<domain>/` → 200 (Next.js SSR: `x-powered-by: Next.js`; Astro SSG: static\n HTML with real content).\n- **WordPress admin:** `GET {urls.wp_admin}` → reachable (a login page, 200/302), not 404.\n- **Backend API:** `GET {urls.rest_api}/posts?per_page=1` → 200 (WordPress); for a **WooCommerce** store\n (`site.json → woocommerce.enabled`) also `GET {urls.store_api}/products?per_page=1` → 200; or the\n storefront API for the sales channel → 200 (Ecommerce).\n- **Contact form** (`site.json → contact_form.enabled`): validate already sent a test submission, so\n don't send another here. In the handover, tell the owner the contact page is live and enquiries email\n the **WordPress admin address** (via `wp_mail`) — and that for the most reliable delivery that address\n should be a Hostinger mailbox on the domain. If validate reported `mail_failed`, say so plainly instead\n of claiming the form sends.\n\nIf a check fails, say so in the outcome paragraph and don't claim the site is live.\n\n## Open the success page\n\nOn a **successful** run (the deploy succeeded and the live checks passed), open the success page in the\nuser's **main / system browser** right after presenting the report, so they land on the hPanel\ncelebration/next-steps screen for their new site. This is a side effect only — **don't print the\nsuccess URL in the report output** (it just opens); the one exception is the can't-open-a-browser\nfallback below. Build the URL with `<tool>` as the running tool's onboarding slug (see *State and\nartifacts* in the top-level `SKILL.md`):\n\n```bash\n# Build the URL first, then open it with the OS default (main) browser — not an embedded one.\n# <tool> = the onboarding slug\nURL=\"https://hpanel.hostinger.com/headless-onboarding/<order_id>/<username>/success/<domain>/<tool>\"\nopen \"$URL\" # macOS\n# xdg-open \"$URL\" # Linux\n# start \"\" \"$URL\" # Windows\n```\n\n- **Main browser only.** Use the OS opener above (`open` / `xdg-open` / `start`) so the page lands in\n Chrome, Safari, Edge, or whatever the user actually uses. **Do not** open it in an embedded or\n sandboxed browser (Cursor IDE browser / `cursor-ide-browser`, Playwright, Puppeteer, WebView, or\n any in-agent browser tab). Those stay inside the tool session and the user won't see the\n celebration page where they expect it.\n- Open it **once**, and only on a successful run — never on an early stop or a failed deploy.\n- If your tool can't open a browser (the command isn't available, or `open`/`xdg-open`/`start`\n fails), don't treat it as a failure and **don't fall back to an embedded browser**. This is the\n **one** case where you print the success URL — show it once in the report and tell the user to\n open it themselves in their main browser.\n\n## Build report\n\nRight after the final report, save the build report for this run. It turns the run log (stage times\nfrom `run-log.json`, events from `events.jsonl`) into `reports/<run>.json` and `.html`, and copies the\nHTML to `report.html` in the workspace:\n\n```bash\nnode <skill>/run-log.mjs <domain> event good '{\"text\":\"<something that went well>\"}' # one per thing, if not logged yet\nnode <skill>/run-log.mjs <domain> stage-end done ok\nnode <skill>/run-log.mjs <domain> report --outcome <live|stopped|failed> [--tokens '<json>']\n```\n\nThen send it to Hostinger: pass `reports/<run>.json` unchanged to `hosting_headless_submit-build-report`\nand run `report-sent <run>` (or `report-rejected <run>` on a 422). The `report` command prints the\nexact path and run id. Don't ask first; see *Run log and build report* in the top-level `SKILL.md`\nfor failures. Then tell the user in one line: \"Sent the build report to Hostinger to help improve\nthe flow.\" On a 422 say it's saved locally only; on any other failure say it'll be sent next session.\n\nIf the user asks for more changes after this report in the same session, call\n`run-log.mjs <domain> run-resume --reason \"<what they asked for>\"` before the work. Then log as\nusual and run `report` again when it's done. It updates this run's report in place, and the report\nlists the follow-up. Send the updated report the same way.\n\n- **Outcome:** `live` when the deploy succeeded this run, `failed` when a stage failed and you\n couldn't recover, `stopped` for everything else (the user paused, a dummy or missing step). Leave\n `--outcome` out and the script decides from the log.\n- **Before you write it,** check that every issue, correction and skill gap from this run is in\n `events.jsonl`. Log any that are missing now; the report only shows what was logged.\n- **After you write it,** read the `warning:` lines `report` prints. They list logging gaps: a stage\n that never ended, a start that was estimated, a stage logged with no duration, or (on a live build)\n a stage that was never logged. Fix what you can: `stage-end` an open stage, or correct a start with\n `stage-fix <stage> --started-ago <time>`, then run `report` again. What you can't fix stays in the report's\n *Logging gaps* section.\n- **Tokens:** only what your tool tells you about your own usage (see *Run log and build report* in\n the top-level `SKILL.md`).\n- In the final report's *Where state lives*, add the path to `report.html` so the user can open it.\n\nDo this on every run, including the early stops: those reports are the most useful ones.\n"
274
274
  }
275
275
  ]
276
276
  },
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agency-hosting.js';
5
5
  import instructions from '../core/instructions.agency-hosting.js';
6
6
 
7
- startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.23.2', tools, instructions });
7
+ startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.24.1', tools, instructions });
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agency-hosting.js';
5
5
  import instructions from '../core/instructions.agency-hosting.js';
6
6
 
7
- startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.23.2', tools, instructions });
7
+ startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.24.1', tools, instructions });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agent-readiness.js';
5
5
 
6
- startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agent-readiness.js';
5
5
 
6
- startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.24.1', tools });
@@ -5,4 +5,4 @@ import tools from '../core/tools/all.js';
5
5
  import instructions from '../core/instructions.js';
6
6
  import skills from '../core/skills.js';
7
7
 
8
- startServer({ name: 'hostinger-api-mcp', version: '3.23.2', tools, instructions, skills });
8
+ startServer({ name: 'hostinger-api-mcp', version: '3.24.1', tools, instructions, skills });
@@ -5,4 +5,4 @@ import tools from '../core/tools/all.js';
5
5
  import instructions from '../core/instructions.js';
6
6
  import skills from '../core/skills.js';
7
7
 
8
- startServer({ name: 'hostinger-api-mcp', version: '3.23.2', tools, instructions, skills });
8
+ startServer({ name: 'hostinger-api-mcp', version: '3.24.1', tools, instructions, skills });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/billing.js';
5
5
 
6
- startServer({ name: 'hostinger-billing-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-billing-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/billing.js';
5
5
 
6
- startServer({ name: 'hostinger-billing-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-billing-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/dns.js';
5
5
 
6
- startServer({ name: 'hostinger-dns-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-dns-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/dns.js';
5
5
 
6
- startServer({ name: 'hostinger-dns-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-dns-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/domains.js';
5
5
 
6
- startServer({ name: 'hostinger-domains-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-domains-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/domains.js';
5
5
 
6
- startServer({ name: 'hostinger-domains-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-domains-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/ecommerce.js';
5
5
 
6
- startServer({ name: 'hostinger-ecommerce-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-ecommerce-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/ecommerce.js';
5
5
 
6
- startServer({ name: 'hostinger-ecommerce-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-ecommerce-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/horizons.js';
5
5
 
6
- startServer({ name: 'hostinger-horizons-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-horizons-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/horizons.js';
5
5
 
6
- startServer({ name: 'hostinger-horizons-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-horizons-mcp', version: '3.24.1', tools });
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/hosting.js';
5
5
  import instructions from '../core/instructions.js';
6
6
 
7
- startServer({ name: 'hostinger-hosting-mcp', version: '3.23.2', tools, instructions });
7
+ startServer({ name: 'hostinger-hosting-mcp', version: '3.24.1', tools, instructions });
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/hosting.js';
5
5
  import instructions from '../core/instructions.js';
6
6
 
7
- startServer({ name: 'hostinger-hosting-mcp', version: '3.23.2', tools, instructions });
7
+ startServer({ name: 'hostinger-hosting-mcp', version: '3.24.1', tools, instructions });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/mail.js';
5
5
 
6
- startServer({ name: 'hostinger-mail-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-mail-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/mail.js';
5
5
 
6
- startServer({ name: 'hostinger-mail-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-mail-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/reach.js';
5
5
 
6
- startServer({ name: 'hostinger-reach-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-reach-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/reach.js';
5
5
 
6
- startServer({ name: 'hostinger-reach-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-reach-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/vps.js';
5
5
 
6
- startServer({ name: 'hostinger-vps-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-vps-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/vps.js';
5
5
 
6
- startServer({ name: 'hostinger-vps-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-vps-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/wordpress.js';
5
5
 
6
- startServer({ name: 'hostinger-wordpress-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-wordpress-mcp', version: '3.24.1', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/wordpress.js';
5
5
 
6
- startServer({ name: 'hostinger-wordpress-mcp', version: '3.23.2', tools });
6
+ startServer({ name: 'hostinger-wordpress-mcp', version: '3.24.1', tools });