@awesomate/hosting-mcp 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -40111,29 +40111,43 @@ readTool(
40111
40111
  "Usage vs plan limits across every dimension (sites, custom domains, apps, database provisions today, workflow credits, AI-editor credits) plus a nudges[] array. Call BEFORE any create action; when a nudge has severity 'approaching' or 'exceeded', surface it to the user with the recommended plan \u2014 never execute an upgrade without preview + explicit confirmation (upgrade tools ship in a later release; for now deep-link to the hub billing page). pgProvisionsToday is a flat daily cap that resets within 24h \u2014 no plan raises it, so don't pitch an upgrade for it.",
40112
40112
  "/api/client-hosting/limits"
40113
40113
  );
40114
+ var PLAN_LADDER_FALLBACK = {
40115
+ note: "Live per-account limits come from awesomate_get_limits \u2014 this is the plan ladder reference (static fallback).",
40116
+ plans: [
40117
+ { name: "Essentials", purchasable: false, monthlyPriceUsd: 75, maxSites: 1, maxCustomDomains: 1, maxApps: 0, cpanelAccess: true, shellAccess: false, appBuilder: false },
40118
+ { name: "Support Plus", purchasable: true, monthlyPriceUsd: 175, maxSites: 2, maxCustomDomains: 5, maxApps: 5, cpanelAccess: true, shellAccess: true, appBuilder: true },
40119
+ { name: "Pro", purchasable: true, monthlyPriceUsd: 375, maxSites: 10, maxCustomDomains: 20, maxApps: 20, cpanelAccess: true, shellAccess: true, appBuilder: true },
40120
+ { name: "Embedded", purchasable: false, monthlyPriceUsd: null, maxSites: 100, maxCustomDomains: 100, maxApps: 100, cpanelAccess: true, shellAccess: true, appBuilder: true, note: "Not generally available yet" }
40121
+ ],
40122
+ upgradePath: "Essentials \u2192 Support Plus \u2192 Pro \u2192 Embedded; upgrades at hub.awesomate.ai/billing (only Support Plus and Pro are purchasable)"
40123
+ };
40114
40124
  server.registerTool(
40115
40125
  "awesomate_get_plan_features",
40116
40126
  {
40117
- description: "Static reference: what each Awesomate plan includes for hosting (sites, custom domains, cPanel, shell/Claude Code access) and monthly pricing. Use to explain what an upgrade unlocks.",
40127
+ description: "The Awesomate plan ladder: what each plan includes for hosting (WordPress sites, custom domains, hosted apps, cPanel, shell/Claude Code access) and which plans are purchasable. Fetched live from the hub (single source of truth) with a static fallback. Use to explain what an upgrade unlocks; live per-account usage comes from awesomate_get_limits.",
40118
40128
  inputSchema: {}
40119
40129
  },
40120
- async () => textResult({
40121
- note: "Live per-account limits come from awesomate_get_limits \u2014 this is the plan ladder reference.",
40122
- plans: [
40123
- { name: "Essentials", monthlyPriceUsd: 75, maxSites: 1, maxCustomDomains: 0, cpanelAccess: true, shellAccess: false },
40124
- { name: "Support Plus", monthlyPriceUsd: 175, maxSites: 1, maxCustomDomains: 1, cpanelAccess: true, shellAccess: true },
40125
- { name: "Pro", monthlyPriceUsd: 375, maxSites: 3, maxCustomDomains: 3, cpanelAccess: true, shellAccess: true },
40126
- { name: "Embedded", monthlyPriceUsd: null, maxSites: null, maxCustomDomains: null, cpanelAccess: true, shellAccess: true, note: "Unlimited sites/domains; not generally available yet" }
40127
- ],
40128
- upgradePath: "Essentials \u2192 Support Plus \u2192 Pro \u2192 Embedded; upgrades at hub.awesomate.ai/billing"
40129
- })
40130
+ async () => {
40131
+ try {
40132
+ const live = await hubGet(requireConfig(), "/api/client-hosting/plan-ladder");
40133
+ if (Array.isArray(live?.plans) && live.plans.length > 0) {
40134
+ return textResult({
40135
+ note: "Live plan ladder from the hub. Per-account usage comes from awesomate_get_limits.",
40136
+ ...live,
40137
+ upgradePath: PLAN_LADDER_FALLBACK.upgradePath
40138
+ });
40139
+ }
40140
+ } catch {
40141
+ }
40142
+ return textResult(PLAN_LADDER_FALLBACK);
40143
+ }
40130
40144
  );
40131
40145
  server.registerTool(
40132
40146
  "awesomate_snapshot_site",
40133
40147
  {
40134
40148
  description: "Snapshot a WordPress site (files + database) BEFORE any risky change \u2014 an AI edit, a deploy, a plugin/theme/core update. Returns a snapshotId you can roll back to. ALWAYS snapshot before mutating a live site. Requires shell access (Support Plus+).",
40135
40149
  inputSchema: {
40136
- domain: external_exports.string().describe("The site domain (as shown by awesomate_list_sites), e.g. mysite.awesomate.io"),
40150
+ domain: external_exports.string().describe("The site domain (as shown by awesomate_list_sites), e.g. mysite.awesomate.site"),
40137
40151
  reason: external_exports.string().optional().describe('Why (stored in the snapshot list), e.g. "before deploy"')
40138
40152
  }
40139
40153
  },
@@ -40180,6 +40194,60 @@ server.registerTool(
40180
40194
  }
40181
40195
  }
40182
40196
  );
40197
+ server.registerTool(
40198
+ "awesomate_site_staging_create",
40199
+ {
40200
+ description: "Create a staging copy of a WordPress site on the client's private awesomate.dev address (files + database cloned, URLs rewritten). Use this BEFORE making user-facing changes so the user can review at the staging URL first \u2014 post to dev, review, then awesomate_site_staging_promote. The staging site is hidden from search engines and AI crawlers by policy; anyone with the link can view it. One staging copy per site \u2014 a 409 with code 'staging_exists' means promote or discard the existing one first. Cloning can take a few minutes on large sites. Requires shell access (Support Plus+).",
40201
+ inputSchema: {
40202
+ domain: external_exports.string().describe("The LIVE site domain (as shown by awesomate_list_sites), e.g. mysite.awesomate.site")
40203
+ }
40204
+ },
40205
+ async ({ domain }) => {
40206
+ try {
40207
+ return textResult(
40208
+ await hubPost(requireConfig(), `/api/hosting-access/sites/${encodeURIComponent(domain)}/staging`, {})
40209
+ );
40210
+ } catch (err) {
40211
+ return errorResult(err);
40212
+ }
40213
+ }
40214
+ );
40215
+ server.registerTool(
40216
+ "awesomate_site_staging_promote",
40217
+ {
40218
+ description: "Publish the staging copy to the LIVE site (overwrites live files + database with staging). The live site is auto-snapshotted first \u2014 the result includes preSnapshotId, which awesomate_rollback_site can restore if anything looks wrong. **Confirm with the user before promoting \u2014 it replaces the live site.** Requires shell access (Support Plus+).",
40219
+ inputSchema: {
40220
+ domain: external_exports.string().describe("The LIVE site domain whose staging copy should go live")
40221
+ }
40222
+ },
40223
+ async ({ domain }) => {
40224
+ try {
40225
+ return textResult(
40226
+ await hubPost(requireConfig(), `/api/hosting-access/sites/${encodeURIComponent(domain)}/staging/promote`, {})
40227
+ );
40228
+ } catch (err) {
40229
+ return errorResult(err);
40230
+ }
40231
+ }
40232
+ );
40233
+ server.registerTool(
40234
+ "awesomate_site_staging_discard",
40235
+ {
40236
+ description: "Delete the staging copy of a site (staging WP install + its awesomate.dev address; the live site is untouched). **Confirm with the user before discarding \u2014 unpromoted staging changes are lost.** Requires shell access (Support Plus+).",
40237
+ inputSchema: {
40238
+ domain: external_exports.string().describe("The LIVE site domain whose staging copy should be discarded")
40239
+ }
40240
+ },
40241
+ async ({ domain }) => {
40242
+ try {
40243
+ return textResult(
40244
+ await hubDelete(requireConfig(), `/api/hosting-access/sites/${encodeURIComponent(domain)}/staging`)
40245
+ );
40246
+ } catch (err) {
40247
+ return errorResult(err);
40248
+ }
40249
+ }
40250
+ );
40183
40251
  readTool(
40184
40252
  "awesomate_app_context",
40185
40253
  "Call FIRST before any app-builder work. Returns the connected account's plan, whether the app builder is unlocked (capability.appBuilder \u2014 Support Plus+), whether they have a cPanel hosting account yet, their existing apps, firstRun, and capability-aware next-step suggestions to offer the user. If appBuilder is false, relay the upgrade suggestion rather than attempting to build.",
@@ -40207,7 +40275,7 @@ server.registerTool(
40207
40275
  server.registerTool(
40208
40276
  "awesomate_app_create",
40209
40277
  {
40210
- description: "Provision a new app on the client's cPanel account. Choose the stack deliberately (see the awesomate-app-builder skill): kind 'static' = a single fast landing/lead page served straight from the docroot (no DB, no server process \u2014 pick this for brochure/landing/lead-capture); kind 'node' = a dynamic app with a backend + database (logins, custom logic, an API). Pick the template by job \u2014 'node-auth-sync' (user accounts/logins, MySQL), 'node-crud-postgres' (structured data without logins: trackers/dashboards \u2014 ready-made CRUD + UI, Postgres), 'node-api-only' (webhooks/integrations/glue, no DB wiring), 'static-landing' (marketing/lead page). If dbEngine is omitted the template's native engine is used (node-crud-postgres \u2192 postgres). Returns 202 immediately with an appId + subdomain(s); poll awesomate_app_get until status=active, then fetch the starter files with awesomate_app_scaffold. Requires apps:write (Support Plus+) \u2014 a 403 means offer an upgrade. Get the user's confirmation on the stack + name before calling.",
40278
+ description: "Provision a new app on the client's cPanel account. Choose the stack deliberately (see the awesomate-app-builder skill): kind 'static' = a single fast landing/lead page served straight from the docroot (no DB, no server process \u2014 pick this for brochure/landing/lead-capture); kind 'node' = a dynamic app with a backend + database (logins, custom logic, an API). Pick the template by job \u2014 'node-auth-sync' (user accounts/logins, MySQL), 'node-crud-postgres' (structured data without logins: trackers/dashboards \u2014 ready-made CRUD + UI, Postgres), 'node-api-only' (webhooks/integrations/glue, no DB wiring), 'static-landing' (marketing/lead page). If dbEngine is omitted the template's native engine is used (node-crud-postgres \u2192 postgres). Returns 202 immediately with an appId + subdomain(s); poll awesomate_app_get until status=active, then fetch the starter files with awesomate_app_scaffold. Requires apps:write (Support Plus+) \u2014 a 403 means offer an upgrade. Plans also cap the NUMBER of apps (Support Plus 5, Pro 20 \u2014 check awesomate_get_limits.dimensions.apps first): a 409 with code 'app_limit' means the cap is reached \u2014 do NOT retry; tell the user their allowance is full and surface the recommendedPlan/deepLink from the error. Get the user's confirmation on the stack + name before calling.",
40211
40279
  inputSchema: {
40212
40280
  appSlug: external_exports.string().regex(/^[a-z][a-z0-9]{1,15}$/).describe("2-16 chars, starts with a letter, lowercase alphanumeric \u2014 used for subdomains + db names"),
40213
40281
  kind: external_exports.enum(["node", "static"]).default("node").describe("'static' for a landing/lead page (no DB); 'node' for a dynamic app"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -87,12 +87,22 @@ target; never touch prod without an explicit ask.**
87
87
  [references/node-recipes.md](references/node-recipes.md) has the recipes
88
88
  (add a table/endpoint, env vars, connection strings, promote/rollback).
89
89
  Handle **secrets** only via the **awesomate-credentials** skill
90
- (encrypted + injected, never committed/echoed). Wire **version control**
91
- from the first commit via **awesomate-github**. If it needs email/lookup/AI,
92
- attach n8n (§"when to use n8n").
93
- 6. **Testing** deploy to **dev** (`git push` to `dev`), then
94
- `awesomate_app_health` to confirm it came up (Node: `/api/ready`). Show the
95
- user the live dev URL and let them try it. Iterate on dev until they're happy.
90
+ (encrypted + injected, never committed/echoed). **Version control is not
91
+ optional**: as soon as the scaffold exists, run the awesomate-github setup
92
+ (repo + first commit + push-to-deploy) ask only the project name and
93
+ private-y/n, in plain words ("I'll back your work up as we go"). From then
94
+ on, **auto-checkpoint**: commit after every working change with a message
95
+ you write, and run the github skill's `vc-healthcheck.mjs` at session start
96
+ and before every deploy. If it needs email/lookup/AI, attach n8n (§"when to
97
+ use n8n").
98
+ 6. **Testing — always ask "live or dev?" before posting anything.** When the
99
+ user asks to publish/change something user-facing, ask: *"Want this on your
100
+ live site, or on your dev site (awesomate.dev) to review and test first?"*
101
+ Recommend dev. Deploy to **dev** (`git push` to `dev` — the dev site lives
102
+ at `{app}.{slug}.awesomate.dev`, hidden from search engines and AI crawlers
103
+ by policy), then `awesomate_app_health` to confirm it came up (Node:
104
+ `/api/ready`). Show the user the live dev URL and let them try it. Iterate
105
+ on dev until they're happy.
96
106
  7. **Wrap-up / promote** — only when the user approves, promote **dev → staging
97
107
  → main** (merge + push per branch). `awesomate_app_deploy` reports the
98
108
  branch→env map + last-deploy state. Prod is `main`. **Before merging to
@@ -113,8 +123,16 @@ files directly (no build step).
113
123
  - Non-technical user: explain in plain words, one or two short questions at a
114
124
  time, no unexplained jargon. Recommend; don't quiz them on architecture.
115
125
  - Confirm the stack + name before `awesomate_app_create`. Default the target
116
- to **dev**; never push to prod without an explicit ask.
126
+ to **dev**; never push to prod without an explicit ask — and when they ask
127
+ to "post"/"publish" something, ask **"live or dev?"** first.
128
+ - You are the user's version-control expert (they don't know what a repo is):
129
+ set up GitHub right after the first build, auto-commit as you go, and run
130
+ the github skill's vc-healthcheck at session start + before deploys.
117
131
  - A `403` on a write = plan gate (Support Plus+). Offer an upgrade, don't retry.
132
+ - A `409` with code `app_limit` = the plan's app allowance is full (Support
133
+ Plus 5, Pro 20 — `awesomate_app_context.limits` has the live numbers).
134
+ Don't retry; tell the user and surface the `recommendedPlan`/`deepLink`
135
+ from the error. Deleting an unused app also frees a slot.
118
136
  - Never invent that a capability exists — read `awesomate_app_context` first.
119
137
  - Never print a secret or commit a `.env` (the credentials + github skills
120
138
  enforce this).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: awesomate-github
3
- description: Set up GitHub version control for the user's app with zero Git knowledge required — connect their GitHub once, then create the repo, .gitignore, first commit and push-to-deploy for them. Use when the user says "connect GitHub", "back up my app", "save my work", "set up version control", "put this on GitHub", or after building an app when it should be version-controlled. Companion to awesomate-app-builder.
3
+ description: Set up GitHub version control for the user's app with zero Git knowledge required — connect their GitHub once, then create the repo, .gitignore, first commit and push-to-deploy for them. Use when the user says "connect GitHub", "back up my app", "save my work", "set up version control", "put this on GitHub", or after building an app when it should be version-controlled — and proactively: run its vc-healthcheck at session start and before deploys to keep commits/pushes/deploys healthy for users who do not know Git. Companion to awesomate-app-builder.
4
4
  ---
5
5
 
6
6
  # Awesomate GitHub — version control for people who don't know Git
@@ -62,6 +62,32 @@ Explain it as *"save your work = it goes live"* — don't lecture on branches.
62
62
  - "Put it live" → push to the right branch (dev/staging/main) and tell them the URL.
63
63
  - Always write clear commit messages **for** them; don't ask them to.
64
64
 
65
+ ## 4. Be the version-control guardian (proactive, not reactive)
66
+
67
+ The user is ~99% likely to have no idea what a repo is. YOU own their version
68
+ control — they should never have to think about it:
69
+
70
+ - **Health check at session start.** Before touching an app folder, run:
71
+ ```
72
+ node ~/.claude/skills/awesomate-github/scripts/vc-healthcheck.mjs <project-dir>
73
+ ```
74
+ It prints JSON (`problems[]`: `no_repo`, `no_remote`, `uncommitted_changes`,
75
+ `unpushed_commits`, `env_file_tracked`, `last_deploy_failed`, …). Fix what
76
+ you can silently (commit + push loose work with a message you write); for
77
+ `no_repo`/`no_remote`, offer setup (§1) in plain words: *"your last changes
78
+ aren't backed up anywhere — want me to set that up? Takes a minute."*
79
+ `env_file_tracked` is urgent: untrack it, .gitignore it, and route the
80
+ secrets through awesomate-credentials.
81
+ - **Auto-checkpoint.** After every meaningful working change (feature works,
82
+ bug fixed, content updated), commit with a plain-English message — don't
83
+ wait for "save my work", and never ask them to write a message. Push at
84
+ natural pauses so their backup is never behind.
85
+ - **Health check before every deploy/publish.** Re-run the script; never push
86
+ to a deploy branch with a failing prior run you haven't explained.
87
+ - **Report in their language.** "Your work is backed up" / "your last 3
88
+ changes were never saved — I've saved them now" — never "you're 2 commits
89
+ ahead of origin/main".
90
+
65
91
  ## Hard rules
66
92
  - Never commit a secret or a `.env`. The script's secret guard is a backstop,
67
93
  not a licence to skip checking. If you spot a secret, route it through the
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Version-control health check for an Awesomate app folder.
4
+ *
5
+ * Run from (or pass) the project directory. Prints a JSON report Claude uses
6
+ * to keep the user's version control healthy WITHOUT them knowing Git:
7
+ * - repo/remote wiring, uncommitted + unpushed work
8
+ * - branch → deploy-env mapping (dev → awesomate.dev, main → live)
9
+ * - last GitHub Actions deploy run (when `gh` is authed)
10
+ *
11
+ * Exit code 0 always (reporting tool, not a gate) — Claude decides what to
12
+ * fix based on the JSON.
13
+ */
14
+ import { execSync } from 'node:child_process';
15
+ import { existsSync } from 'node:fs';
16
+ import { resolve } from 'node:path';
17
+
18
+ const dir = resolve(process.argv[2] ?? '.');
19
+ const report = {
20
+ dir,
21
+ gitInstalled: false,
22
+ repoInitialized: false,
23
+ remote: null,
24
+ branch: null,
25
+ uncommittedFiles: 0,
26
+ unpushedCommits: null, // null = unknown (no upstream)
27
+ hasUpstream: false,
28
+ envTracked: false,
29
+ ghAuthed: false,
30
+ lastDeployRun: null,
31
+ problems: [],
32
+ };
33
+
34
+ function sh(cmd) {
35
+ return execSync(cmd, { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 })
36
+ .toString()
37
+ .trim();
38
+ }
39
+ function trySh(cmd) {
40
+ try {
41
+ return sh(cmd);
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+
47
+ if (trySh('git --version') === null) {
48
+ report.problems.push('git_not_installed');
49
+ } else {
50
+ report.gitInstalled = true;
51
+ report.repoInitialized = trySh('git rev-parse --is-inside-work-tree') === 'true';
52
+ if (!report.repoInitialized) {
53
+ report.problems.push('no_repo');
54
+ } else {
55
+ report.branch = trySh('git branch --show-current') || '(detached)';
56
+ report.remote = trySh('git remote get-url origin');
57
+ if (!report.remote) report.problems.push('no_remote');
58
+
59
+ const status = trySh('git status --porcelain') ?? '';
60
+ report.uncommittedFiles = status ? status.split('\n').length : 0;
61
+ if (report.uncommittedFiles > 0) report.problems.push('uncommitted_changes');
62
+
63
+ // .env must never be tracked — the single most dangerous drift.
64
+ const tracked = trySh('git ls-files .env */.env') ?? '';
65
+ report.envTracked = tracked.length > 0;
66
+ if (report.envTracked) report.problems.push('env_file_tracked');
67
+
68
+ const upstream = trySh('git rev-parse --abbrev-ref @{upstream}');
69
+ report.hasUpstream = !!upstream;
70
+ if (upstream) {
71
+ const ahead = trySh('git rev-list --count @{upstream}..HEAD');
72
+ report.unpushedCommits = ahead === null ? null : Number(ahead);
73
+ if (report.unpushedCommits > 0) report.problems.push('unpushed_commits');
74
+ } else if (report.remote) {
75
+ report.problems.push('branch_not_pushed');
76
+ }
77
+
78
+ if (report.remote && trySh('gh auth status') !== null) {
79
+ report.ghAuthed = true;
80
+ const runs = trySh(
81
+ 'gh run list --limit 1 --json displayTitle,conclusion,headBranch,updatedAt',
82
+ );
83
+ try {
84
+ const parsed = JSON.parse(runs ?? '[]');
85
+ if (parsed[0]) {
86
+ report.lastDeployRun = parsed[0];
87
+ if (parsed[0].conclusion && parsed[0].conclusion !== 'success') {
88
+ report.problems.push('last_deploy_failed');
89
+ }
90
+ }
91
+ } catch {
92
+ /* no runs / not parseable — fine */
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ if (!existsSync(resolve(dir, '.gitignore')) && report.repoInitialized) {
99
+ report.problems.push('no_gitignore');
100
+ }
101
+
102
+ console.log(JSON.stringify(report, null, 2));
@@ -3,7 +3,7 @@ name: awesomate-hosting
3
3
  description: >
4
4
  Manage, build, and grow your Awesomate WordPress hosting from Claude. Use when
5
5
  the user mentions their Awesomate site or hosting, cPanel, a site on
6
- *.awesomate.io or a custom domain hosted with Awesomate, WordPress admin,
6
+ *.awesomate.site (or legacy *.site.awesomate.io) or a custom domain hosted with Awesomate, WordPress admin,
7
7
  installing a plugin/theme, running WP-CLI, "my site is slow / out of space /
8
8
  broke", snapshotting or rolling back a site, deploying from a local WordPress
9
9
  Studio site to live, checking hosting stats or how close they are to a plan
@@ -11,7 +11,9 @@ description: >
11
11
  plan. Trigger phrases: "my awesomate site", "connect my hosting", "spin up a
12
12
  wordpress site", "install wordpress", "add my domain", "why is my site slow",
13
13
  "am I near my limit", "snapshot my site", "roll back my site", "deploy to
14
- live", "run wp-cli", "upgrade my plan", "how many sites can I have".
14
+ live", "run wp-cli", "upgrade my plan", "how many sites can I have",
15
+ "create a staging site", "test changes before going live", "publish staging",
16
+ "post this to my dev site".
15
17
  ---
16
18
 
17
19
  # Awesomate Hosting
@@ -148,15 +150,18 @@ natural break so the richer MCP integration loads.
148
150
 
149
151
  ## 1. The plan model (so your nudges are accurate)
150
152
 
151
- | Plan | WP sites | Custom domains | Shell / Claude Code | Notes |
152
- |---|---|---|---|---|
153
- | **Essentials** | 1 | 0 | **No** | read/status + upgrade nudges only |
154
- | **Support Plus** | (per plan) | ≥1 | **Yes** | shell, WP-CLI, snapshot/rollback, deploy |
155
- | **Pro** | more | more | **Yes** | + higher limits |
156
- | **Embedded** | | | **Yes** | agency tier |
153
+ | Plan | WP sites | Hosted apps | Custom domains | Shell / Claude Code | Notes |
154
+ |---|---|---|---|---|---|
155
+ | **Essentials** | 1 | 0 | 0 | **No** | read/status + upgrade nudges only |
156
+ | **Support Plus** | 2 | 5 | 3 | **Yes** | shell, WP-CLI, snapshot/rollback, deploy, app builder |
157
+ | **Pro** | 10 | 20 | 10 | **Yes** | + higher limits |
158
+ | **Embedded** | 100 | 100 | 100 | **Yes** | agency tier, not generally available |
157
159
 
158
160
  Authoritative limits are always what `awesomate_get_limits` returns for THIS
159
- account never quote the table above as fact; use it only to explain upgrades.
161
+ account (and `awesomate_get_plan_features` fetches the live ladder from the
162
+ hub) — never quote the table above as fact; use it only to explain upgrades.
163
+ App creation past the cap returns a 409 with code `app_limit` — don't retry;
164
+ surface the recommended plan instead.
160
165
 
161
166
  ## 2. Branch by capability
162
167
 
@@ -167,25 +172,34 @@ Allowed: `awesomate_get_context`, `_get_hosting_status`, `_get_hosting_account`,
167
172
  `_get_hosting_stats`, `_get_grafana_url`, `_get_credit_balance`, the billing
168
173
  preview/deep-link tools, and 1-click WP-admin / cPanel SSO links.
169
174
 
170
- Never attempt SSH, WP-CLI, snapshot/rollback, deploy, multi-site, or custom
171
- domains — those tools will 403. Instead, surface the specific limit and what
175
+ Never attempt SSH, WP-CLI, snapshot/rollback, staging, deploy, multi-site
176
+ those tools will 403. Instead, surface the specific limit and what
172
177
  Support Plus/Pro unlocks (see §4), and offer to preview the upgrade.
173
178
 
174
179
  ### Support Plus and above (shell) — full workflow
175
180
 
176
181
  You additionally have: jailed SSH to the client's own cPanel account, WP-CLI
177
182
  (`awesomate_run_wp_cli` or raw SSH via `scripts/ssh-connect.sh`),
178
- snapshot/rollback, and the local-first deploy flow 5). Golden rule, always:
183
+ snapshot/rollback, **WP staging on awesomate.dev**5a), and the local-first
184
+ deploy flow (§5). Golden rule, always:
179
185
 
180
186
  > **Local first, live never — and snapshot before you touch live.**
181
187
 
188
+ **Ask "live or dev?" before posting anything user-facing.** When the user asks
189
+ you to change/publish something on their WordPress site, ask: *"Want me to put
190
+ this on your live site, or on your private dev copy (awesomate.dev) so you can
191
+ review it first?"* Default suggestion: dev first, publish after they've looked
192
+ at it. Their dev copy is invisible to Google and AI crawlers by policy.
193
+
182
194
  ## 3. Safety rules (non-negotiable)
183
195
 
184
196
  - **Before ANY change to a live site** — plugin/theme/core update, `search-replace`,
185
197
  bulk edit, deploy — call **`awesomate_snapshot_site`** first and tell the user the
186
198
  `snapshotId`. If it goes wrong, `awesomate_rollback_site` restores files + DB.
187
199
  - **Confirm before destructive actions**: `awesomate_uninstall_site`,
188
- `awesomate_rollback_site`, dropping tables, `wp db reset`, deleting content.
200
+ `awesomate_rollback_site`, `awesomate_site_staging_promote` (replaces live),
201
+ `awesomate_site_staging_discard` (loses unpublished staging work), dropping
202
+ tables, `wp db reset`, deleting content.
189
203
  State exactly what will be lost and wait for an explicit "yes".
190
204
  - Prefer building/testing in **WordPress Studio locally**, then deploy (§5).
191
205
  - Dry-run risky WP-CLI where the command supports it before the real run.
@@ -211,8 +225,8 @@ one command — `scripts/deploy.sh`, which **snapshots live first** (so a bad
211
225
  deploy is one rollback away), pushes files, then optionally the DB:
212
226
 
213
227
  ```
214
- scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io # files only
215
- scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io --with-db # + database
228
+ scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site # files only
229
+ scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site --with-db # + database
216
230
  ```
217
231
 
218
232
  - **Always files-first is the safe default.** `--with-db` also exports the
@@ -227,6 +241,26 @@ scripts/deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io --with-db
227
241
  Never deploy without confirming with the user first, and always report the
228
242
  snapshot id and the verified live status.
229
243
 
244
+ ## 5a. Staging on awesomate.dev (Support Plus+)
245
+
246
+ The middle tier between "edit live" and "local Studio": a private full copy of
247
+ the site at `{name}.{slug}.awesomate.dev`, noindexed at the edge (no Google, no
248
+ AI crawlers — anyone with the link can view, perfect for client review).
249
+
250
+ - `awesomate_site_staging_create <live domain>` — clones files + DB, rewrites
251
+ URLs. One staging copy per site (409 `staging_exists` → promote or discard
252
+ first). Takes a few minutes on large sites.
253
+ - Make the changes ON the staging domain (WP-CLI / wp-admin / deploy.sh with
254
+ `--domain <staging domain>`), send the user the staging URL to review.
255
+ - `awesomate_site_staging_promote <live domain>` — publishes staging over live.
256
+ Live is auto-snapshotted first; report the returned `preSnapshotId` as the
257
+ undo handle (`awesomate_rollback_site`). **Confirm before promoting.**
258
+ - `awesomate_site_staging_discard <live domain>` — deletes the staging copy;
259
+ live untouched. **Confirm — unpublished staging changes are lost.**
260
+
261
+ Prefer this flow over editing live whenever the change is user-visible
262
+ (theme/layout/content restructures, plugin experiments, redesigns).
263
+
230
264
  ## 6. Version control with GitHub (optional)
231
265
 
232
266
  Awesomate's snapshots are your safety net, so Git isn't required — but for real
@@ -4,7 +4,7 @@
4
4
  # Awesomate site. SNAPSHOTS LIVE FIRST via the hub (so a bad deploy is one
5
5
  # rollback away), then pushes files, then optionally the database.
6
6
  #
7
- # deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.io [options]
7
+ # deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site [options]
8
8
  #
9
9
  # Options:
10
10
  # --docroot PATH Live docroot (default: /home/<cpanel-user>/public_html)
@@ -4,7 +4,7 @@
4
4
  # from an existing live site). Read-only against live — it never changes the
5
5
  # live site. Copies files + exports the live DB.
6
6
  #
7
- # pull-live.sh --domain mysite.awesomate.io --to ~/Studio/mysite [--docroot PATH]
7
+ # pull-live.sh --domain mysite.awesomate.site --to ~/Studio/mysite [--docroot PATH]
8
8
  #
9
9
  # Then import the files into a new WordPress Studio site and load db.sql.
10
10
  # Client-side deps: bash, ssh, rsync, scp.