@awesomate/hosting-mcp 0.19.0 → 0.19.2
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 +78 -13
- package/package.json +1 -1
- package/skill/CHANGELOG.json +13 -0
- package/skill/awesomate-app-builder/SKILL.md +53 -3
- package/skill/awesomate-app-builder/references/stack-decision.md +36 -9
- package/skill/awesomate-app-builder/references/wp-migrate.md +4 -2
- package/skill/awesomate-credentials/SKILL.md +41 -4
- package/skill/awesomate-database/SKILL.md +60 -15
- package/skill/awesomate-github/SKILL.md +49 -8
- package/skill/awesomate-github/scripts/github-setup.mjs +4 -1
- package/skill/awesomate-hosting/SKILL.md +131 -20
- package/skill/awesomate-hosting/references/voice.md +1 -1
- package/skill/awesomate-knowledge/SKILL.md +14 -4
- package/skill/awesomate-n8n/SKILL.md +37 -11
- package/skill/awesomate-n8n/references/error-handling.md +3 -1
- package/skill/awesomate-n8n/references/platform-notes.md +3 -1
- package/skill/awesomate-n8n/references/recurring-reports.md +13 -6
- package/skill/awesomate-n8n/references/rest-fallback.md +7 -2
- package/skill/awesomate-n8n/references/troubleshooting.md +31 -1
- package/skill/awesomate-seo/SKILL.md +109 -20
- package/skill/awesomate-support/SKILL.md +59 -12
package/dist/index.js
CHANGED
|
@@ -39897,7 +39897,7 @@ async function hubRequest(config3, method, path, jsonBody, opts = {}) {
|
|
|
39897
39897
|
if (res.status === 401) {
|
|
39898
39898
|
hint = " Your access token is invalid or expired \u2014 ask the user to open the hub Sites page (hub.awesomate.ai/sites), generate a fresh Connect prompt, and re-run the bootstrap.";
|
|
39899
39899
|
} else if (res.status === 403 && code === "consent_required") {
|
|
39900
|
-
const settingsUrl = body && typeof body === "object" && "settingsUrl" in body && typeof body.settingsUrl === "string" ? body.settingsUrl : "https://hub.awesomate.ai/settings
|
|
39900
|
+
const settingsUrl = body && typeof body === "object" && "settingsUrl" in body && typeof body.settingsUrl === "string" ? body.settingsUrl : "https://hub.awesomate.ai/n8n/settings";
|
|
39901
39901
|
hint = ` This needs a privacy toggle the user must flip themselves \u2014 send them to ${settingsUrl}, wait for them to confirm, then retry. Never suggest a plan upgrade for a consent denial.`;
|
|
39902
39902
|
} else if (res.status === 403) {
|
|
39903
39903
|
const missing = body && typeof body === "object" && "missingScopes" in body ? ` (missing: ${JSON.stringify(body.missingScopes)})` : "";
|
|
@@ -39906,6 +39906,10 @@ async function hubRequest(config3, method, path, jsonBody, opts = {}) {
|
|
|
39906
39906
|
hint = " Payment/allowance required \u2014 NOTHING was purchased. State the cost plainly and let the user decide in the hub; this tool never spends money.";
|
|
39907
39907
|
} else if (res.status === 404) {
|
|
39908
39908
|
hint = " Not found \u2014 usually the resource does not exist for THIS account. Re-check the id/domain before retrying; do not invent one.";
|
|
39909
|
+
} else if (code === "n8n_credential_invalid") {
|
|
39910
|
+
const settingsUrl = body && typeof body === "object" && typeof body.settingsUrl === "string" ? body.settingsUrl : "https://hub.awesomate.ai/n8n/settings";
|
|
39911
|
+
const instanceUrl = body && typeof body === "object" && typeof body.instanceUrl === "string" ? body.instanceUrl : null;
|
|
39912
|
+
hint = ` THIS IS NOT A HUB OUTAGE and not your workflow \u2014 the user's own n8n rejected the API key Awesomate has stored. Tell the user to fix it in two steps: (1) in their n8n${instanceUrl ? ` at ${instanceUrl}` : ""}, open Settings \u2192 n8n API and create a new API key; (2) paste it into their Awesomate account at ${settingsUrl} under "New n8n API Key" and save. Then retry. Do not retry before they confirm, and do not investigate hub/Cloudflare status \u2014 reads and validation keep working because those never touch their instance.`;
|
|
39909
39913
|
} else if (res.status === 409) {
|
|
39910
39914
|
hint = " Conflict \u2014 the payload carries the specifics (a limit reached, or a prerequisite missing). Read its code field; do not blind-retry.";
|
|
39911
39915
|
} else if (res.status === 422) {
|
|
@@ -39915,7 +39919,8 @@ async function hubRequest(config3, method, path, jsonBody, opts = {}) {
|
|
|
39915
39919
|
} else if (res.status === 503) {
|
|
39916
39920
|
hint = " The hub is temporarily unable to serve this \u2014 retry shortly.";
|
|
39917
39921
|
}
|
|
39918
|
-
|
|
39922
|
+
const stem = /[.!?]$/.test(serverMsg) ? serverMsg : `${serverMsg}.`;
|
|
39923
|
+
throw new HubApiError(`${stem}${hint}`, res.status, body);
|
|
39919
39924
|
}
|
|
39920
39925
|
return body;
|
|
39921
39926
|
}
|
|
@@ -40569,6 +40574,36 @@ var SKILL_DRIFT = (() => {
|
|
|
40569
40574
|
if (!info.updateAvailable) return null;
|
|
40570
40575
|
return `${info.installedSkillVersion ?? "unversioned"} \u2192 ${SERVER_VERSION}`;
|
|
40571
40576
|
})();
|
|
40577
|
+
function restartGuidance() {
|
|
40578
|
+
const entry = (process.env.CLAUDE_CODE_ENTRYPOINT ?? "").toLowerCase();
|
|
40579
|
+
const inVsCode = entry.includes("vscode") || Boolean(process.env.VSCODE_IPC_HOOK || process.env.VSCODE_PID);
|
|
40580
|
+
if (inVsCode) {
|
|
40581
|
+
return {
|
|
40582
|
+
surface: "vscode",
|
|
40583
|
+
instruction: 'Open the Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows/Linux) and run "Developer: Reload Window".',
|
|
40584
|
+
certain: true
|
|
40585
|
+
};
|
|
40586
|
+
}
|
|
40587
|
+
if (entry.includes("desktop") || entry.includes("app")) {
|
|
40588
|
+
return {
|
|
40589
|
+
surface: "desktop-app",
|
|
40590
|
+
instruction: "Quit the Claude app completely and open it again, then reopen this project.",
|
|
40591
|
+
certain: true
|
|
40592
|
+
};
|
|
40593
|
+
}
|
|
40594
|
+
if (entry === "cli" || entry.includes("cli")) {
|
|
40595
|
+
return {
|
|
40596
|
+
surface: "terminal",
|
|
40597
|
+
instruction: "Type `exit` (or press Ctrl+C twice) to leave Claude Code, then run `claude` again.",
|
|
40598
|
+
certain: true
|
|
40599
|
+
};
|
|
40600
|
+
}
|
|
40601
|
+
return {
|
|
40602
|
+
surface: entry || "unknown",
|
|
40603
|
+
instruction: "Start a fresh Claude Code session \u2014 however you normally start one (close this session and open a new one). Ask the user how they run Claude Code rather than guessing.",
|
|
40604
|
+
certain: false
|
|
40605
|
+
};
|
|
40606
|
+
}
|
|
40572
40607
|
var SERVER_INSTRUCTIONS = [
|
|
40573
40608
|
"Awesomate connects this account's WordPress hosting, n8n automations, apps and Knowledge Base to Claude.",
|
|
40574
40609
|
"Entry point: call awesomate_get_context once per session FIRST \u2014 it returns the account, plan, limits, an `attention` digest (unread notifications, erroring workflows, token expiry) and skill freshness. The domain contexts (awesomate_n8n_context, awesomate_app_context, awesomate_knowledge_status) come after it, only for their domain.",
|
|
@@ -40677,7 +40712,7 @@ server.registerTool(
|
|
|
40677
40712
|
"awesomate_n8n_deploy",
|
|
40678
40713
|
{
|
|
40679
40714
|
annotations: DESTRUCTIVE,
|
|
40680
|
-
description: "Workflow lifecycle writes on the client's n8n, all consent-gated and audited server-side. Actions: 'validate' (structural + n8n-mcp check of workflowJson \u2014 ALWAYS validate before create_draft), 'create_draft' (creates an INACTIVE '[CLI] ' workflow tagged awm:client-cli; returns its webhook URLs), 'activate'/'deactivate' (activation strips the [CLI] prefix; production webhooks respond only while active; agency-managed workflows are refused), 'promote' (swaps a TESTED draft into the live workflow IN PLACE \u2014 live id + webhookIds preserved so external callers keep working; pass workflowId=the LIVE id and draftId=the tested draft; the draft is archived '[promoted <date>]'; response includes operationId for rollback), 'rollback' (restore a promote's pre-swap state \u2014 pass operationId), 'update_draft' (replace an INACTIVE self-built draft's full JSON in place \u2014 pass workflowId + workflowJson; live workflows are refused, use the promote path), 'delete_draft' (inactive self-built drafts only). Get explicit user approval before activate, promote, rollback, and delete_draft. 429 quota_exceeded = daily plan limit; 403 consent_required \u2192 send user to settingsUrl.",
|
|
40715
|
+
description: "Workflow lifecycle writes on the client's n8n, all consent-gated and audited server-side. Actions: 'validate' (structural + n8n-mcp check of workflowJson \u2014 ALWAYS validate before create_draft), 'create_draft' (creates an INACTIVE '[CLI] ' workflow tagged awm:client-cli; returns its webhook URLs), 'activate'/'deactivate' (activation strips the [CLI] prefix; production webhooks respond only while active; agency-managed workflows are refused), 'promote' (swaps a TESTED draft into the live workflow IN PLACE \u2014 live id + webhookIds preserved so external callers keep working; pass workflowId=the LIVE id and draftId=the tested draft; the draft is archived '[promoted <date>]'; response includes operationId for rollback), 'rollback' (restore a promote's pre-swap state \u2014 pass operationId), 'update_draft' (replace an INACTIVE self-built draft's full JSON in place \u2014 pass workflowId + workflowJson; live workflows are refused, use the promote path), 'delete_draft' (inactive self-built drafts only). Get explicit user approval before activate, promote, rollback, and delete_draft. 429 quota_exceeded = daily plan limit; 403 consent_required \u2192 send user to settingsUrl; 409 n8n_credential_invalid = the user's OWN n8n rejected the stored API key (NOT a hub outage \u2014 reads and validate keep working because they never touch their instance): tell them to create a new API key in their n8n under Settings \u2192 n8n API, paste it into their Awesomate account at hub.awesomate.ai/n8n/settings, then retry.",
|
|
40681
40716
|
inputSchema: {
|
|
40682
40717
|
action: external_exports.enum(["validate", "create_draft", "update_draft", "activate", "deactivate", "promote", "rollback", "delete_draft"]),
|
|
40683
40718
|
workflowJson: external_exports.record(external_exports.unknown()).optional().describe("For validate: the full workflow JSON. For create_draft: must contain name, nodes, connections (settings optional)."),
|
|
@@ -40912,12 +40947,18 @@ server.registerTool(
|
|
|
40912
40947
|
...result,
|
|
40913
40948
|
changed: changed.map((u) => `${u.name}: ${u.from ?? "unversioned"} \u2192 ${u.to}`),
|
|
40914
40949
|
whatsNew: changelogSince(changed[0]?.from ?? null).flatMap((e) => e.highlights),
|
|
40915
|
-
restart: {
|
|
40916
|
-
|
|
40917
|
-
|
|
40918
|
-
|
|
40919
|
-
|
|
40920
|
-
|
|
40950
|
+
restart: (() => {
|
|
40951
|
+
const g = restartGuidance();
|
|
40952
|
+
return {
|
|
40953
|
+
note: "Updated skill files load in the next Claude Code session \u2014 one restart lands both the skills and the (self-updating) server.",
|
|
40954
|
+
// Detected from this session's own environment. Give the user THIS
|
|
40955
|
+
// instruction only — do not read out a list of surfaces.
|
|
40956
|
+
detectedSurface: g.surface,
|
|
40957
|
+
instruction: g.instruction,
|
|
40958
|
+
...g.certain ? {} : { note2: "Surface could not be detected \u2014 ask the user how they run Claude Code instead of guessing steps." },
|
|
40959
|
+
verify: "After restarting, awesomate_get_context should report skill.updateAvailable: false."
|
|
40960
|
+
};
|
|
40961
|
+
})()
|
|
40921
40962
|
});
|
|
40922
40963
|
} catch (err) {
|
|
40923
40964
|
return errorResult(err);
|
|
@@ -41010,7 +41051,7 @@ server.registerTool(
|
|
|
41010
41051
|
server.registerTool(
|
|
41011
41052
|
"awesomate_run_wp_cli",
|
|
41012
41053
|
{
|
|
41013
|
-
description: "Run an allowlisted WP-CLI command on one of the user's WordPress sites (plugin/theme list+activate+update, cache flush, option get/update, post/media/menu/comment/user list). Installs accept wp.org SLUGS only \u2014 never URLs. args is the command as an array, e.g. ['plugin','list'] or ['plugin','install','wordpress-seo','--activate']. A 400 wp_cli_not_allowed means that command isn't permitted; a 502 wp_cli_unavailable is a temporary server-side issue, not your command.",
|
|
41054
|
+
description: "Run an allowlisted WP-CLI command on one of the user's WordPress sites (plugin/theme list+activate+update, cache flush, option get/update, post/media/menu/comment/user list). ARGUMENTS CANNOT CONTAIN SPACES (letters, digits and -_./=:@+, only) \u2014 so a site title, tagline or any multi-word value is impossible here: use awesomate_wp_settings for those, and awesomate_wp_post for post/page content. Installs accept wp.org SLUGS only \u2014 never URLs. args is the command as an array, e.g. ['plugin','list'] or ['plugin','install','wordpress-seo','--activate']. A 400 wp_cli_not_allowed means that command isn't permitted; a 502 wp_cli_unavailable is a temporary server-side issue, not your command.",
|
|
41014
41055
|
inputSchema: {
|
|
41015
41056
|
domain: external_exports.string().describe("The site domain"),
|
|
41016
41057
|
args: external_exports.array(external_exports.string()).describe("WP-CLI args, e.g. ['plugin','list']")
|
|
@@ -41297,7 +41338,6 @@ server.registerTool(
|
|
|
41297
41338
|
server.registerTool(
|
|
41298
41339
|
"awesomate_app_deploy_info",
|
|
41299
41340
|
{
|
|
41300
|
-
annotations: READ_ONLY,
|
|
41301
41341
|
description: "READ-ONLY deploy briefing \u2014 this never deploys anything (deploys happen via git push). Reports how to deploy an app, its per-env targets, last-deploy/health state, and how to promote (dev\u2192staging\u2192main) or roll back (git revert + push). Node apps deploy via git push (dev/staging/main \u2192 GitHub Actions \u2192 cPanel). Use the awesomate-github skill to wire push-to-deploy the first time.",
|
|
41302
41342
|
inputSchema: { appId: external_exports.number().int().positive().describe("The app id") }
|
|
41303
41343
|
},
|
|
@@ -41651,7 +41691,7 @@ server.registerTool(
|
|
|
41651
41691
|
"awesomate_n8n_findings",
|
|
41652
41692
|
{
|
|
41653
41693
|
annotations: READ_ONLY,
|
|
41654
|
-
description: "Findings from Awesomate's automated error analyzer for THIS account (Pro/Embedded + the 'AI
|
|
41694
|
+
description: "Findings from Awesomate's automated error analyzer for THIS account (Pro/Embedded + the 'Enable AI Error Diagnosis' privacy toggle (n8n \u2192 Settings \u2192 Privacy)): severity, workflow, occurrences, a plain-language clientSummary, needsClientAction (something only the user can fix \u2014 expired logins, third-party quotas), and fixReady (a reviewed fix is prepared \u2014 raise it with awesomate_support to have it applied). Read-only. An empty list on a healthy instance is the good state, not an error.",
|
|
41655
41695
|
inputSchema: { limit: external_exports.number().int().min(1).max(100).optional() }
|
|
41656
41696
|
},
|
|
41657
41697
|
async ({ limit }) => {
|
|
@@ -41685,7 +41725,7 @@ readTool(
|
|
|
41685
41725
|
);
|
|
41686
41726
|
readTool(
|
|
41687
41727
|
"awesomate_privacy_settings",
|
|
41688
|
-
"READ which privacy/consent toggles are on or off for this account \u2014 call it when a tool returns 403 consent_required so you can name the exact toggle instead of guessing. Toggles are changed ONLY by the user in the hub (Settings \u2192 Privacy, hub.awesomate.ai/settings
|
|
41728
|
+
"READ which privacy/consent toggles are on or off for this account \u2014 call it when a tool returns 403 consent_required so you can name the exact toggle instead of guessing. Toggles are changed ONLY by the user in the hub (n8n \u2192 Settings \u2192 Privacy, hub.awesomate.ai/n8n/settings); there is deliberately no write here.",
|
|
41689
41729
|
"/api/client-settings/privacy"
|
|
41690
41730
|
);
|
|
41691
41731
|
server.registerTool(
|
|
@@ -41777,6 +41817,31 @@ server.registerTool(
|
|
|
41777
41817
|
}
|
|
41778
41818
|
}
|
|
41779
41819
|
);
|
|
41820
|
+
server.registerTool(
|
|
41821
|
+
"awesomate_wp_settings",
|
|
41822
|
+
{
|
|
41823
|
+
description: `Change a WordPress site's core settings \u2014 THE tool for "change my site title" / tagline. Use this, never awesomate_run_wp_cli, for any value containing spaces: that tool's argument gate rejects spaces outright, so \`option update blogname "My Business Name"\` cannot work there. Fields (send only what you're changing): title, tagline, timezone (IANA, e.g. Australia/Sydney), dateFormat, timeFormat, postsPerPage (1-100), searchEngineVisible (false hides the site from search engines \u2014 confirm before setting it). Support Plus+ and audited; flushes the object cache so the change shows. Snapshot first with awesomate_snapshot_site if this is the session's first change to a live site.`,
|
|
41824
|
+
inputSchema: {
|
|
41825
|
+
domain: external_exports.string().min(3).max(253).describe("The site domain, e.g. mybusiness.awesomate.site"),
|
|
41826
|
+
title: external_exports.string().min(1).max(200).optional().describe("Site title (blogname)"),
|
|
41827
|
+
tagline: external_exports.string().max(300).optional().describe("Tagline (blogdescription)"),
|
|
41828
|
+
timezone: external_exports.string().max(64).optional(),
|
|
41829
|
+
dateFormat: external_exports.string().max(40).optional(),
|
|
41830
|
+
timeFormat: external_exports.string().max(40).optional(),
|
|
41831
|
+
postsPerPage: external_exports.number().int().min(1).max(100).optional(),
|
|
41832
|
+
searchEngineVisible: external_exports.boolean().optional()
|
|
41833
|
+
}
|
|
41834
|
+
},
|
|
41835
|
+
async ({ domain, ...fields }) => {
|
|
41836
|
+
try {
|
|
41837
|
+
const dom = encodeURIComponent(domain.toLowerCase());
|
|
41838
|
+
const payload = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== void 0));
|
|
41839
|
+
return textResult(await hubPatch(requireConfig(), `/api/client-hosting/sites/${dom}/settings`, payload));
|
|
41840
|
+
} catch (err) {
|
|
41841
|
+
return errorResult(err);
|
|
41842
|
+
}
|
|
41843
|
+
}
|
|
41844
|
+
);
|
|
41780
41845
|
server.registerPrompt(
|
|
41781
41846
|
"awesomate-status",
|
|
41782
41847
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awesomate/hosting-mcp",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
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",
|
package/skill/CHANGELOG.json
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"versions": [
|
|
3
|
+
{
|
|
4
|
+
"version": "0.19.2",
|
|
5
|
+
"highlights": [
|
|
6
|
+
"Change your site title or tagline just by asking - multi-word names now work",
|
|
7
|
+
"Claude tells you how to restart in the way that matches how you actually run it, instead of listing every option"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.19.1",
|
|
12
|
+
"highlights": [
|
|
13
|
+
"When your n8n API key stops working, Claude now tells you exactly how to fix it instead of reporting a confusing server error"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
3
16
|
{
|
|
4
17
|
"version": "0.19.0",
|
|
5
18
|
"highlights": [
|
|
@@ -45,7 +45,24 @@ decision brain. The short version, matched to what they say:
|
|
|
45
45
|
- **Landing page / capture leads / one-pager / "just needs to look good and
|
|
46
46
|
be found"** → **static site** (`kind: 'static'`). Fastest possible load,
|
|
47
47
|
no database. This is the right default for marketing pages — don't reach for
|
|
48
|
-
a full app.
|
|
48
|
+
a full app. Know what you give up, and check it against the job BEFORE
|
|
49
|
+
creating, because the stack can't be changed afterwards:
|
|
50
|
+
- **No server-side environment.** `awesomate_app_set_env` returns 400 for
|
|
51
|
+
a static app — there is nowhere to put a secret. Anything the page must
|
|
52
|
+
keep private (an API key, a webhook URL you don't want public) rules
|
|
53
|
+
static out.
|
|
54
|
+
- **`awesomate_n8n_attach_to_app` is Node-only.** So the "form posts to
|
|
55
|
+
their n8n" pattern needs a Node app, unless the webhook URL being public
|
|
56
|
+
is genuinely acceptable.
|
|
57
|
+
- **No separate dev site.** All three environment URLs point at the same
|
|
58
|
+
single subdomain — there's no dev copy to review on, so every change is
|
|
59
|
+
live the moment it's published.
|
|
60
|
+
- **No deploy workflow.** The static template is just the page files, so
|
|
61
|
+
pushing to GitHub does not publish it (see the awesomate-github skill's
|
|
62
|
+
§2 — deploy wiring needs Awesomate support).
|
|
63
|
+
|
|
64
|
+
If the page needs a webhook URL, a secret, or a place to test before it
|
|
65
|
+
goes live, it's a **Node app**. Say that in one line and move on.
|
|
49
66
|
- **App with logins / custom logic / a dashboard / an API** → **Node app**
|
|
50
67
|
(`kind: 'node'`). Pick **postgres** when the data is relational / needs
|
|
51
68
|
concurrency / JSON / analytics; **mysql** for simple cases.
|
|
@@ -58,7 +75,14 @@ If the app needs to **send an email, look something up in their apps (Sheets,
|
|
|
58
75
|
CRM, a database), or use AI** — their **own n8n** is often the fastest path,
|
|
59
76
|
because their credentials are already connected there. Check the credential
|
|
60
77
|
inventory (`awesomate_n8n_inspect {what:'credentials'}`) and say e.g. *"you already
|
|
61
|
-
have Gmail connected in n8n — want the form to email you through that?"*
|
|
78
|
+
have Gmail connected in n8n — want the form to email you through that?"*
|
|
79
|
+
That read is **consent-gated**: it needs the **"Allow Claude Code to Build
|
|
80
|
+
n8n Workflows"** toggle (n8n → Settings → Privacy,
|
|
81
|
+
hub.awesomate.ai/n8n/settings; also on hub.awesomate.ai/n8n/settings),
|
|
82
|
+
which is **off by default** — so on a fresh account it comes back 403
|
|
83
|
+
`consent_required`. That's not a failure to work around: call
|
|
84
|
+
`awesomate_privacy_settings` to name the exact toggle, ask the user to flip
|
|
85
|
+
it, re-check, and meanwhile design as if you don't know what's connected. Use
|
|
62
86
|
the **awesomate-n8n** skill to build the workflow, then store its webhook URL
|
|
63
87
|
in the app (see the awesomate-credentials skill) and call it from the app.
|
|
64
88
|
Don't reach for n8n for pure in-app logic with no external app/credential — a
|
|
@@ -108,6 +132,29 @@ target; never touch prod without an explicit ask.**
|
|
|
108
132
|
by policy), then `awesomate_app_health` to confirm it came up (Node:
|
|
109
133
|
`/api/ready`). Show the user the live dev URL and let them try it. Iterate
|
|
110
134
|
on dev until they're happy.
|
|
135
|
+
**That dev address is Node-only.** A static app has ONE address —
|
|
136
|
+
`{app}.{slug}.awesomate.app` — and `awesomate_app_get` reports it for dev,
|
|
137
|
+
staging and prod alike. There is no separate copy to review on, so with a
|
|
138
|
+
static site "post it to dev first" isn't an option you can honestly offer:
|
|
139
|
+
say the change goes live, and get the yes before publishing. Read the real
|
|
140
|
+
addresses from `awesomate_app_get` rather than constructing them — an
|
|
141
|
+
account without the branded domains falls back to subdomains of its own
|
|
142
|
+
primary domain (`dev-{app}.{domain}`).
|
|
143
|
+
**Publishing to their WordPress site** (as opposed to an app) has its own
|
|
144
|
+
tools, and `awesomate_run_wp_cli` is the wrong one for content: its
|
|
145
|
+
argument gate rejects spaces outright, so a title or tagline can't go
|
|
146
|
+
through it.
|
|
147
|
+
- `awesomate_wp_post` — create/update/read a post or page, with real
|
|
148
|
+
titles and HTML. It lands as a **DRAFT** unless you explicitly pass
|
|
149
|
+
`status:'publish'`; never publish something the user hasn't read.
|
|
150
|
+
- `awesomate_wp_media_import` — pull one image in by https URL (it cannot
|
|
151
|
+
read local files) and get back an `attachmentId` to reference.
|
|
152
|
+
- `awesomate_wp_settings` — site title, tagline, timezone, date/time
|
|
153
|
+
format, posts per page, search-engine visibility.
|
|
154
|
+
|
|
155
|
+
Snapshot the site (`awesomate_snapshot_site`) before the session's first
|
|
156
|
+
change to a live WordPress site.
|
|
157
|
+
|
|
111
158
|
7. **Wrap-up / promote** — only when the user approves, promote **dev → staging
|
|
112
159
|
→ main** (merge + push per branch). `awesomate_app_deploy_info` reports the
|
|
113
160
|
branch→env map + last-deploy state (it never deploys — `git push` does). Prod is `main`. **Before merging to
|
|
@@ -141,7 +188,10 @@ app with a UI, an n8n data table (awesomate-n8n skill) beats building an app
|
|
|
141
188
|
- A `409` with code `app_limit` = the plan's app allowance is full (Support
|
|
142
189
|
Plus 5, Pro 20 — `awesomate_app_context.limits` has the live numbers).
|
|
143
190
|
Don't retry; tell the user and surface the `recommendedPlan`/`deepLink`
|
|
144
|
-
from the error.
|
|
191
|
+
from the error. **There is no delete-app tool or route** — don't offer
|
|
192
|
+
"delete an old one to free a slot", because neither you nor the user can.
|
|
193
|
+
The honest options are an upgrade or asking support (awesomate-support
|
|
194
|
+
skill) to remove an app.
|
|
145
195
|
- Never invent that a capability exists — read `awesomate_app_context` first.
|
|
146
196
|
- Never print a secret or commit a `.env` (the credentials + github skills
|
|
147
197
|
enforce this).
|
|
@@ -10,7 +10,22 @@ confirm before building. **Never default to WordPress.**
|
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| "I already have a WordPress site" / a blog / publishes content regularly / **wants to rank on Google & AI** | **WordPress** | Best CMS for indexable content, sitemaps, schema. Hand to the awesomate-hosting skill to provision, then run **awesomate-seo**. Existing WP elsewhere → migrate it, don't rebuild: [wp-migrate.md](wp-migrate.md) (also covers WP ↔ app coexistence on subdomains). |
|
|
12
12
|
| Sell products / online shop | **WordPress + WooCommerce** | Provision via the awesomate-hosting skill, install WooCommerce over WP-CLI, then run **awesomate-seo** (product pages need `Product` JSON-LD + real product data). |
|
|
13
|
-
| A landing page / capture leads / a one-pager / "just needs to look good and be found" | **static site** (`kind: 'static'`) | Served straight from the cPanel docroot — no database, no server process, fastest load. Add SEO + a lead form (below). The right default for marketing pages. |
|
|
13
|
+
| A landing page / capture leads / a one-pager / "just needs to look good and be found" | **static site** (`kind: 'static'`) | Served straight from the cPanel docroot — no database, no server process, fastest load. Add SEO + a lead form (below). The right default for marketing pages — but see the three limits under the table before you commit to it. |
|
|
14
|
+
|
|
15
|
+
**What a static site cannot do** (the stack can't be changed later, so check
|
|
16
|
+
this against the job first):
|
|
17
|
+
|
|
18
|
+
- **No server-side environment.** `awesomate_app_set_env` returns 400 for a
|
|
19
|
+
static app. Anything the page must keep secret has nowhere to live — it
|
|
20
|
+
would be in the page source, readable by anyone.
|
|
21
|
+
- **`awesomate_n8n_attach_to_app` is Node-only.** A static lead form can only
|
|
22
|
+
POST to an n8n webhook URL that is **hardcoded in the page and therefore
|
|
23
|
+
public**. Sometimes that's acceptable (a plain lead capture behind n8n-side
|
|
24
|
+
validation); when it isn't, it's a Node app.
|
|
25
|
+
- **No separate dev site.** All three environment URLs are the same single
|
|
26
|
+
subdomain, so there's nowhere to review a change before it's live.
|
|
27
|
+
- **No deploy workflow.** The static template is just the page files —
|
|
28
|
+
pushing to GitHub does not publish it (awesomate-github §2).
|
|
14
29
|
| A tool/app with **logins, custom logic, a dashboard, or an API** | **Node app** (`kind: 'node'`) | A real backend + database. |
|
|
15
30
|
|
|
16
31
|
### Within a Node app: which starter template?
|
|
@@ -47,18 +62,30 @@ If the ask includes **email someone, look something up in their apps, save a
|
|
|
47
62
|
lead somewhere, or use AI**, that's usually a job for their **own n8n** — their
|
|
48
63
|
credentials are already connected there, so there's nothing new to set up.
|
|
49
64
|
|
|
50
|
-
- Check
|
|
51
|
-
they already have: *"You've got Gmail +
|
|
52
|
-
form to email you and add the lead to a
|
|
65
|
+
- Check what's connected with `awesomate_n8n_inspect {what:'credentials'}`
|
|
66
|
+
(names/types only) and offer what they already have: *"You've got Gmail +
|
|
67
|
+
Google Sheets connected — want the form to email you and add the lead to a
|
|
68
|
+
sheet?"* That read needs the **"Allow Claude Code to Build n8n Workflows"**
|
|
69
|
+
privacy toggle, which is off by default — a 403 `consent_required` means
|
|
70
|
+
call `awesomate_privacy_settings`, name the toggle, and ask the user to flip
|
|
71
|
+
it at hub.awesomate.ai/n8n/settings.
|
|
53
72
|
- Build the workflow with the **awesomate-n8n** skill (webhook trigger →
|
|
54
|
-
action), take its **webhook URL**, and
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
action), take its **webhook URL**, and wire it into the app.
|
|
74
|
+
- **Node app:** `awesomate_n8n_attach_to_app` — it stores
|
|
75
|
+
`N8N_WEBHOOK_URL` plus a generated `N8N_WEBHOOK_SECRET` (encrypted +
|
|
76
|
+
injected) and returns the secret so you can add the matching
|
|
77
|
+
`X-Awesomate-Webhook-Secret` check on the n8n side. `src/lib/n8n.ts`
|
|
78
|
+
POSTs there.
|
|
79
|
+
- **Static site:** there is no app environment, so the URL sits in the page
|
|
80
|
+
source, public. Tell the user that plainly, and defend the workflow on the
|
|
81
|
+
n8n side instead (validation, rate limiting) — or use a Node app.
|
|
57
82
|
- Skip n8n for pure in-app logic with no external app/credential/AI — a webhook
|
|
58
83
|
round-trip just adds latency and a failure point.
|
|
59
84
|
|
|
60
85
|
## Always, for any public site
|
|
61
86
|
Offer **awesomate-seo** (meta/OG tags, `sitemap.xml`, `robots.txt`, JSON-LD, a
|
|
62
87
|
general `llms.txt`) so it's findable by search engines *and* AI assistants — and
|
|
63
|
-
**awesomate-github** so their work is version-controlled
|
|
64
|
-
|
|
88
|
+
**awesomate-github** so their work is version-controlled from day one.
|
|
89
|
+
Push-to-deploy is a second step and not one you can finish alone: the Actions
|
|
90
|
+
workflow needs SSH repo secrets no client-side tool returns, so set up the repo
|
|
91
|
+
and hand the deploy wiring to Awesomate support (awesomate-github §2).
|
|
@@ -26,8 +26,10 @@ until you're happy."*
|
|
|
26
26
|
installed on the new site.
|
|
27
27
|
4. **Verify before DNS.** Browse the copy on its Awesomate URL: pages,
|
|
28
28
|
images, forms, admin login, HTTPS. Run **awesomate-seo** (sitemap,
|
|
29
|
-
robots, meta)
|
|
30
|
-
|
|
29
|
+
robots, meta). A migrated site brings its old plugins with it and stale
|
|
30
|
+
plugins are the top WP risk, so check for outdated ones
|
|
31
|
+
(`awesomate_run_wp_cli ['plugin','list']`), update what's safe to update,
|
|
32
|
+
and flag anything abandoned to the user.
|
|
31
33
|
5. **Cut over DNS** — point the domain at Awesomate (add it as a custom
|
|
32
34
|
domain first; plan limits apply). Old site stays live as the fallback
|
|
33
35
|
until the user confirms; propagation can take up to a day and both
|
|
@@ -47,8 +47,13 @@ value goes browser → destination directly; you only ever see the key NAMES.
|
|
|
47
47
|
node ~/.claude/skills/awesomate-credentials/scripts/secret-drop.mjs \
|
|
48
48
|
--hub-app <appId> --env dev --keys STRIPE_SECRET_KEY --port <port> --token <hex>
|
|
49
49
|
```
|
|
50
|
-
Get `<appId>` from `awesomate_app_list`. Keys
|
|
51
|
-
|
|
50
|
+
Get `<appId>` from `awesomate_app_list`. Keys must be UPPER_SNAKE: the
|
|
51
|
+
drop script uppercases what you pass it in hub mode, but the
|
|
52
|
+
`awesomate_app_set_env` TOOL does not — it hard-rejects anything that
|
|
53
|
+
isn't `^[A-Z][A-Z0-9_]{0,63}$`, so a lowercase name or a leading
|
|
54
|
+
underscore is a validation error there, not a tidy-up. Pick the final
|
|
55
|
+
uppercase name once and use it in both places. Node apps only — static
|
|
56
|
+
sites have no server env (`set_env` returns 400 for them). The
|
|
52
57
|
script posts each value to the hub with the user's own PAT
|
|
53
58
|
(`~/.awesomate/credentials.json`) — the same route `awesomate_app_set_env`
|
|
54
59
|
uses, so encryption, `.env` injection and restart all behave identically.
|
|
@@ -71,6 +76,26 @@ value goes browser → destination directly; you only ever see the key NAMES.
|
|
|
71
76
|
6. Confirm with key names only. No rotation warning needed — the value never
|
|
72
77
|
entered the conversation.
|
|
73
78
|
|
|
79
|
+
### What the drop link can't do
|
|
80
|
+
|
|
81
|
+
- **The link is short-lived and single-use.** It expires after 15 minutes
|
|
82
|
+
(`--ttl <minutes>` to change it) and the server exits after the FIRST
|
|
83
|
+
successful save unless you passed `--stay`. If the user comes back later
|
|
84
|
+
and the page is dead, that's expected — start a fresh one on a new port
|
|
85
|
+
with a new token rather than telling them to retry the old link.
|
|
86
|
+
- **It cannot carry a MULTI-LINE value.** The bulk box is parsed line by
|
|
87
|
+
line, splitting on the first `=`, so a PEM private key or a Google
|
|
88
|
+
service-account JSON loses everything after line 1 — silently, and those
|
|
89
|
+
are two of the commonest secrets people have. The named field is a
|
|
90
|
+
single-line input, so it can't hold one either. For a multi-line secret,
|
|
91
|
+
either:
|
|
92
|
+
- **base64 it into one line** — `base64 -i key.pem | tr -d '\n'` — drop
|
|
93
|
+
that through the link as e.g. `GOOGLE_SA_JSON_B64`, and decode it in the
|
|
94
|
+
app at startup; or
|
|
95
|
+
- have the user **save it to a file** on their machine and tell you the
|
|
96
|
+
path, then read and store it — being honest that reading it puts the
|
|
97
|
+
value in the conversation record, so the rotation nudge applies.
|
|
98
|
+
|
|
74
99
|
## Detect and offer
|
|
75
100
|
|
|
76
101
|
If the user's message contains something shaped like a secret — `sk-…`,
|
|
@@ -94,14 +119,26 @@ full map. Short version:
|
|
|
94
119
|
the file-mode link; add a rotation note if it came through the chat.
|
|
95
120
|
- **Never** a tracked file, a commit, or the chat.
|
|
96
121
|
|
|
122
|
+
A secret for an n8n workflow belongs on the n8n instance (awesomate-n8n
|
|
123
|
+
skill), never in an app `.env`. Those tools are consent-gated: if one comes
|
|
124
|
+
back 403 `consent_required`, call `awesomate_privacy_settings` — it names
|
|
125
|
+
the exact toggle that's off, so you can send the user to the right switch at
|
|
126
|
+
hub.awesomate.ai/n8n/settings instead of guessing. You can't flip it
|
|
127
|
+
for them.
|
|
128
|
+
|
|
97
129
|
## Fallback: the file-drop flow
|
|
98
130
|
|
|
99
131
|
Only when the link flow can't work (e.g. Claude is running on a remote machine
|
|
100
132
|
the user's browser can't reach as 127.0.0.1):
|
|
101
133
|
|
|
102
134
|
1. Tell them: *"Create a file `.awesomate/secret-inbox.txt` in your project and
|
|
103
|
-
paste the value there — I'll grab it and wipe the file."*
|
|
104
|
-
|
|
135
|
+
paste the value there — I'll grab it and wipe the file."*
|
|
136
|
+
**Verify `.gitignore` yourself before any commit.** Don't assume the
|
|
137
|
+
folder is ignored: the github skill's setup script adds `.awesomate/` only
|
|
138
|
+
when it has actually been run in this project, and older installed copies
|
|
139
|
+
of that script didn't list it at all. `grep -q '^\.awesomate/' .gitignore`
|
|
140
|
+
— add the line if it's missing, before the file exists, and never commit
|
|
141
|
+
while it does.
|
|
105
142
|
2. Read the file, store via `awesomate_app_set_env`, then **scrub the file**:
|
|
106
143
|
`node ~/.claude/skills/awesomate-credentials/scripts/capture-secret.mjs .awesomate/secret-inbox.txt`
|
|
107
144
|
3. Be honest with the user: reading the file put the value in the conversation
|
|
@@ -26,6 +26,14 @@ co-located with the workflow that uses it. No hosting slot consumed, and
|
|
|
26
26
|
workflows query it directly — this is the default answer for anything
|
|
27
27
|
automation-shaped. Inspect what exists with
|
|
28
28
|
`awesomate_n8n_inspect {what:'datatables'}`.
|
|
29
|
+
**Both the write AND that read are gated:** Support Plus or above, plus the
|
|
30
|
+
privacy toggle **"Allow Claude Code to Build n8n Workflows"** — n8n →
|
|
31
|
+
Settings → Privacy (hub.awesomate.ai/n8n/settings, or the same toggles at
|
|
32
|
+
hub.awesomate.ai/n8n/settings, which is what the 403's
|
|
33
|
+
`settingsUrl` points at). It is **off by default**, so on a fresh account
|
|
34
|
+
both the read and the write come back 403 `consent_required`. Call
|
|
35
|
+
`awesomate_privacy_settings` to name the exact toggle that's off, send the
|
|
36
|
+
user to flip it, then re-check. You cannot flip it for them.
|
|
29
37
|
|
|
30
38
|
**(b) Real APP data** — logins, dashboards, anything relational that an
|
|
31
39
|
app serves to people. → the app's own Postgres.
|
|
@@ -38,7 +46,23 @@ app serves to people. → the app's own Postgres.
|
|
|
38
46
|
**(c) A WORKFLOW that needs raw SQL against its own Postgres** — real
|
|
39
47
|
joins, aggregates, or volumes a data table shouldn't carry. →
|
|
40
48
|
`awesomate_n8n_provision_pg`. One step creates the database AND the n8n
|
|
41
|
-
credential, so the workflow can query it immediately.
|
|
49
|
+
credential, so the workflow can query it immediately. It has the most gates
|
|
50
|
+
of anything here — check them BEFORE offering it, so the user doesn't hit a
|
|
51
|
+
wall halfway:
|
|
52
|
+
- Support Plus or above.
|
|
53
|
+
- The **"Allow Claude Code to Build n8n Workflows"** toggle (off by
|
|
54
|
+
default) — same one as branch (a).
|
|
55
|
+
- The **direct database writes** consent (`allow_pg_writes`). It's on by
|
|
56
|
+
default, so this is rare — but it has **no client-facing toggle in the
|
|
57
|
+
hub**, so if it's off, don't send the user hunting for a switch that
|
|
58
|
+
doesn't exist: route it to support via the awesomate-support skill.
|
|
59
|
+
- An Awesomate cPanel hosting account. Without one it returns 409
|
|
60
|
+
`hosting_required`; hosting comes first (awesomate-hosting skill).
|
|
61
|
+
- Capped at **2 a day**.
|
|
62
|
+
|
|
63
|
+
On any 403 here, call `awesomate_privacy_settings` first — it names the
|
|
64
|
+
exact toggle that's blocking, which decides whether this is a "flip this
|
|
65
|
+
switch" or a "raise it with support" answer.
|
|
42
66
|
|
|
43
67
|
**(d) Analytics/BI or data shared with other systems** — reporting
|
|
44
68
|
warehouses, a spreadsheet the whole team lives in, a CRM. → recommend
|
|
@@ -69,12 +93,19 @@ reach), ask "live or dev?" before provisioning against it.
|
|
|
69
93
|
|
|
70
94
|
## 2. Safety rules
|
|
71
95
|
|
|
72
|
-
- **Passwords
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
- **Passwords land where they belong, and the two branches differ.**
|
|
97
|
+
`awesomate_app_provision_db` NEVER returns a password: it's set
|
|
98
|
+
server-side, stored encrypted, and injected as the app's `DATABASE_URL`
|
|
99
|
+
— there is nothing to show, so don't promise the user a password.
|
|
100
|
+
`awesomate_n8n_provision_pg` is the one that DOES return a password, once,
|
|
101
|
+
because it lives in the n8n credential and Awesomate never stores it. The
|
|
102
|
+
workflow doesn't need you to repeat it — the credential already holds it —
|
|
103
|
+
so say that, and only offer the value if the user actually needs it
|
|
104
|
+
elsewhere, warning that it has now passed through the transcript and
|
|
105
|
+
should be rotated. Never put a `DATABASE_URL` in a message. Tell the user where
|
|
106
|
+
it lives instead: "the app already has its connection — nothing for you
|
|
107
|
+
to copy." (After an app database is provisioned, the app needs a restart
|
|
108
|
+
or redeploy to pick it up.)
|
|
78
109
|
- Secrets the user's own app code needs (API keys, tokens) go through
|
|
79
110
|
the awesomate-credentials skill, never pasted into files or chat.
|
|
80
111
|
- **Deleting data requires an explicit filter and an explicit yes.**
|
|
@@ -84,14 +115,28 @@ reach), ask "live or dev?" before provisioning against it.
|
|
|
84
115
|
|
|
85
116
|
## 3. Limits, honestly
|
|
86
117
|
|
|
87
|
-
|
|
88
|
-
(`awesomate_app_provision_db`)
|
|
89
|
-
(`awesomate_n8n_provision_pg`)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
118
|
+
Everything that provisions here needs **Support Plus or above** — app
|
|
119
|
+
databases (`awesomate_app_provision_db`), workflow Postgres
|
|
120
|
+
(`awesomate_n8n_provision_pg`) and data tables alike. Data tables are NOT
|
|
121
|
+
"any plan with n8n access": Essentials gets 0 creations a day, and both the
|
|
122
|
+
table read and the write also need the "Allow Claude Code to Build n8n
|
|
123
|
+
Workflows" toggle (§0(a)).
|
|
124
|
+
|
|
125
|
+
Only some of it is capped per day:
|
|
126
|
+
- `awesomate_n8n_provision_pg` — **2 a day**, flat; no plan raises it, so
|
|
127
|
+
don't pitch an upgrade for it.
|
|
128
|
+
- Data table creations — **10 a day** on Support Plus and Pro (20 on
|
|
129
|
+
Embedded), 0 on Essentials. An instance also tops out at 50 data tables.
|
|
130
|
+
- `awesomate_app_provision_db` — **no daily cap**. It's one database per
|
|
131
|
+
environment and refuses (409 `already_has_db`) if the app has one, so
|
|
132
|
+
there's nothing to ration.
|
|
133
|
+
|
|
134
|
+
If the user's plan doesn't cover what branch (b) or (c) needs, say it once
|
|
135
|
+
— "app databases are on Support Plus" — point them at
|
|
136
|
+
hub.awesomate.ai/billing to upgrade, then help fully within what their plan
|
|
137
|
+
allows. On Essentials that means none of (a), (b) or (c) is available —
|
|
138
|
+
don't dangle a data table as the consolation prize; say so once and look at
|
|
139
|
+
(d), keeping the data where it already lives and connecting to it. If a
|
|
95
140
|
daily quota is hit, say when it resets and what can proceed meanwhile.
|
|
96
141
|
|
|
97
142
|
Report every result as a business outcome: "your workflow now remembers
|
|
@@ -47,19 +47,48 @@ Ask only: **"What should we call this project?"** (repo name) and, if relevant,
|
|
|
47
47
|
**"Keep it private?"** (default yes). Derive a sensible kebab-case repo name
|
|
48
48
|
from what they tell you.
|
|
49
49
|
|
|
50
|
+
**The script's first push goes to `main`.** It runs `git init -b main` and
|
|
51
|
+
pushes the current branch, so on a fresh repo `main` — the PRODUCTION branch
|
|
52
|
+
for Node apps — is what gets created. That's fine for a repo that's just a
|
|
53
|
+
backup, but if the code isn't ready to be live, say so before running it, or
|
|
54
|
+
create and check out `dev` first. There is **no `dev` branch until you make
|
|
55
|
+
one**: `git push origin dev` on a fresh repo fails. Create it, then push:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
git checkout -b dev
|
|
59
|
+
git push -u origin dev
|
|
60
|
+
```
|
|
61
|
+
|
|
50
62
|
## 2. Push-to-deploy (for apps built with awesomate-app-builder)
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
Node apps from the app builder ship with a GitHub Actions workflow that
|
|
65
|
+
deploys on a push to `dev` / `staging` / `main` (dev → dev, main → prod).
|
|
66
|
+
Once the branches exist, explain it as *"save your work = it goes live"* —
|
|
67
|
+
don't lecture on branches.
|
|
68
|
+
|
|
69
|
+
**But you cannot finish the wiring yourself, and you should say so.** That
|
|
70
|
+
workflow needs `SSH_HOST`, `SSH_USER` and `SSH_PRIVATE_KEY` as repo secrets,
|
|
71
|
+
and **no client-reachable tool or route returns those values** — they're
|
|
72
|
+
admin-only. `github-setup.mjs --deploy-secrets` only copies them from your
|
|
73
|
+
environment, so with nothing there it prints `skip deploy secret …` and the
|
|
74
|
+
first deploy run goes red. So:
|
|
75
|
+
|
|
76
|
+
1. Set up the repo, the branches and the commits (§1) — all of that works.
|
|
77
|
+
2. Tell the user plainly: *"your code is backed up on GitHub; the automatic
|
|
78
|
+
deploy still needs Awesomate to connect it — I'll raise that for you."*
|
|
79
|
+
Raise it via the **awesomate-support** skill.
|
|
80
|
+
3. **Do not loop on the failing pipeline.** Re-running it, rewriting the
|
|
81
|
+
workflow, or pushing again won't produce the missing secrets.
|
|
82
|
+
|
|
83
|
+
Static sites have no deploy workflow at all — the template is just the page
|
|
84
|
+
files — so pushing does not publish them either; same handoff.
|
|
58
85
|
|
|
59
86
|
## 3. Everyday use (plain language)
|
|
60
87
|
|
|
61
88
|
- "Save my work" → stage, commit with a short message you write for them, push.
|
|
62
|
-
- "Put it live" → push to the right branch (dev/staging/main) and tell them the
|
|
89
|
+
- "Put it live" → push to the right branch (dev/staging/main) and tell them the
|
|
90
|
+
URL. If the branch doesn't exist yet, create it (`git checkout -b <name>`)
|
|
91
|
+
before pushing — only `main` exists after first-time setup.
|
|
63
92
|
- Always write clear commit messages **for** them; don't ask them to.
|
|
64
93
|
|
|
65
94
|
## 4. Be the version-control guardian (proactive, not reactive)
|
|
@@ -78,6 +107,13 @@ control — they should never have to think about it:
|
|
|
78
107
|
aren't backed up anywhere — want me to set that up? Takes a minute."*
|
|
79
108
|
`env_file_tracked` is urgent: untrack it, .gitignore it, and route the
|
|
80
109
|
secrets through awesomate-credentials.
|
|
110
|
+
**`last_deploy_failed` that never clears is usually the unwired deploy
|
|
111
|
+
secrets (§2), not a bug you can fix.** Check whether `SSH_HOST` /
|
|
112
|
+
`SSH_USER` / `SSH_PRIVATE_KEY` are set on the repo (`gh secret list`)
|
|
113
|
+
before touching anything: if they're missing, stop, tell the user the
|
|
114
|
+
deploy connection is pending with Awesomate, and raise it via the
|
|
115
|
+
awesomate-support skill. Don't retry the run, and don't start editing the
|
|
116
|
+
workflow file.
|
|
81
117
|
- **Auto-checkpoint.** After every meaningful working change (feature works,
|
|
82
118
|
bug fixed, content updated), commit with a plain-English message — don't
|
|
83
119
|
wait for "save my work", and never ask them to write a message. Push at
|
|
@@ -90,7 +126,12 @@ control — they should never have to think about it:
|
|
|
90
126
|
|
|
91
127
|
## Hard rules
|
|
92
128
|
- Never commit a secret or a `.env`. The script's secret guard is a backstop,
|
|
93
|
-
not a licence to skip checking.
|
|
129
|
+
not a licence to skip checking. The script's `.gitignore` list — `.env`,
|
|
130
|
+
`.env.local`, `.awesomate/`, `*.log`, `.DS_Store`, `node_modules/` — is only
|
|
131
|
+
applied when the script has been run in that project, so on a repo somebody
|
|
132
|
+
else set up, read `.gitignore` before committing rather than assuming.
|
|
133
|
+
`.awesomate/` matters especially: the credentials skill's file-drop fallback
|
|
134
|
+
puts a live secret there. If you spot a secret, route it through the
|
|
94
135
|
awesomate-credentials skill instead.
|
|
95
136
|
- Never ask the user for a GitHub token or password; the device-flow is the
|
|
96
137
|
only auth path.
|