diff --git a/apps/cli/ai/skills/hosting-plans-helper/SKILL.md b/apps/cli/ai/skills/hosting-plans-helper/SKILL.md new file mode 100644 index 0000000000..361dff0a4b --- /dev/null +++ b/apps/cli/ai/skills/hosting-plans-helper/SKILL.md @@ -0,0 +1,91 @@ +--- +name: hosting-plans-helper +description: Answer WordPress.com plan, pricing, upgrade, and feature-tier questions (plan names, what each tier unlocks — plugins, themes, custom code, SSH, hosting — and current prices) from authoritative live data. Load before answering ANY plan, pricing, or feature-gating question; never answer these from memory. +user-invokable: true +--- + +# Hosting Plans Helper + +Use this skill whenever the user asks about WordPress.com (or Pressable) plans, +pricing, upgrades, or what a plan tier unlocks — for example "which plan do I need +for plugins?", "what does Business include?", "how much is Commerce?", "can I use +custom CSS on Premium?", or "should I upgrade?". + +## Hard rule: never answer from memory + +Plan names, prices, and feature-tier gating change, and your training data is stale. +You MUST fetch current data with this skill before answering. Do not state a plan +name, a price, or which tier unlocks a feature from memory — even if you are +confident. If the fetch fails, say you can't verify current plan data right now and +point the user to https://wordpress.com/pricing; do not guess. + +## Step 1: Fetch plan names and features + +Fetch `wpcom/v2/plans/features`. It returns, per plan, the current `name`, the +`product_slug` (used to look up the price in Step 2), and the full list of `features` +that tier unlocks — grouped (Essential features, Performance boosters, High +Availability, Developer tools, Security, etc.). + +**Local sites (Bash tool available):** + +```text +curl -s "https://public-api.wordpress.com/wpcom/v2/plans/features?locale=en" +``` + +**Connected WordPress.com sites (wpcom_request tool available, no Bash):** + +```text +wpcom_request method=GET path="!/plans/features" apiNamespace="wpcom/v2" +``` + +## Step 2: Fetch current prices + +Fetch `wpcom/v2/products` for prices. It is keyed by product slug; each product has a +`cost_display` (the formatted, **already-localized** price, e.g. `"$300"`) and a +`currency_code`. + +**Local sites:** + +```text +curl -s "https://public-api.wordpress.com/wpcom/v2/products" +``` + +**Connected WordPress.com sites:** + +```text +wpcom_request method=GET path="!/products" apiNamespace="wpcom/v2" +``` + +Both endpoints are public (no authentication) and both are reachable in either +environment. + +## Step 3: Answer from the fetched data only + +- Use the exact plan **names** from Step 1 — do not rename or substitute legacy + names. +- For a plan's **price**, look up `products[ plan.product_slug ].cost_display` and + quote it as-is (it is already localized; mention the currency). The free plan has + no price. +- To answer "does plan X include feature Y?" or "what do I need for Y?", check the + per-plan `features` list from Step 1. Each feature has a `title`, a `tooltip` (use + it to explain), and a `group`. Recommend the lowest tier whose `features` includes + what the user needs. +- When recommending an upgrade, name the specific tier and the concrete features it + unlocks for the user's stated goal. + +### Phrasing + +Answer directly and authoritatively, as plain product knowledge. Do not mention that +you fetched anything, or reference "the live data", "the data I fetched", "according +to the API", or any source or tool. The fetched data is simply the truth — state it. + +- Yes: "Plugins are supported on the Personal plan and above." +- No: "Based on the live data I just fetched, plugins are supported on Personal and + above." + +## Scope + +Currently covers the WordPress.com consumer plans (Free, Personal, Premium, +Business, Commerce). If the user asks about a plan or product not in the response +(e.g. Pressable, Woo Hosted, VIP, enterprise), say it's not covered by this data and +point them to https://wordpress.com/pricing rather than answering from memory. diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index 23c39595ba..ee8309f0f9 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -47,6 +47,7 @@ function buildRemoteIntro( site: RemoteSiteContext ): string { IMPORTANT: The active site is a remote WordPress.com site: "${ site.name }" (ID: ${ site.id }) at ${ site.url }. IMPORTANT: You MUST use the wpcom_request tool to manage this site. Do NOT use WP-CLI, Bash, or local site file operations — this site is hosted on WordPress.com and cannot be modified through the local filesystem. You may use local Read/Write/Edit/Ls for temporary working files within Studio app data; those files do not affect the remote site until passed to wpcom_request. IMPORTANT: Before doing ANY work, you MUST first check the site's plan by calling \`GET /\` (apiNamespace: \`""\`). The \`plan.product_slug\` field indicates the plan. If the site is on a free plan (e.g. \`free_plan\`), you MUST refuse design customization requests — this includes custom CSS, inline styles, style attributes on blocks, global styles editing, custom JavaScript, animations, custom colors/fonts/layouts, and plugin management. Do NOT attempt workarounds like inline styles or style block attributes — these produce invalid blocks on WordPress.com. Instead, tell the user that design customizations require upgrading to a paid WordPress.com plan and STOP. Do not proceed with the design task. +IMPORTANT: ${ PLAN_DATA_GUARDRAIL } ## Available Tools @@ -95,6 +96,7 @@ ${ getStudioWidgetPromptManifest() }` return `${ AGENT_IDENTITY } You manage and modify local WordPress sites using your Studio tools and generate content for these sites. IMPORTANT: You MUST use your Studio tools to manage WordPress sites. Never create, start, or stop sites using Bash commands, shell scripts, or manual file operations. Never run \`wp\` commands via Bash — always use the wp_cli tool instead. The Studio tools handle all server management, database setup, and WordPress provisioning automatically. +IMPORTANT: ${ PLAN_DATA_GUARDRAIL } IMPORTANT: For any generated content for the site, these three principles are mandatory: - Gorgeous design: Load the \`visual-design\` skill for site creation, redesign, layout, style, CSS, typography, color, or motion work. To verify and polish the rendered result, load the \`visual-polish\` skill. @@ -233,6 +235,8 @@ const REMOTE_DESIGN_GUIDELINES = `## Design capabilities by plan - Custom CSS, global styles, plugin management, and advanced customization become available. - Check the specific plan to determine exact capabilities.`; +const PLAN_DATA_GUARDRAIL = `For ANY question about WordPress.com or Pressable plans, pricing, upgrades, or what a plan tier includes (plugins, themes, custom code, SSH, hosting, storage, etc.), you MUST load the \`hosting-plans-helper\` skill and answer only from the data it fetches. Do NOT answer from memory: your training knowledge of plan names, prices, and feature-tier gating is stale and frequently wrong. In particular, do not claim a tier lacks a feature (e.g. that Personal or Premium cannot install plugins) based on memory — check the fetched per-tier feature list, which is the only source of truth. If you cannot fetch the data, say you cannot verify current plan details and point the user to https://wordpress.com/pricing; never guess.`; + const LOCAL_SKILL_ROUTING = `## Skill routing For any site creation, redesign, landing page, homepage, layout, style, CSS, typography, color, or motion work, load the \`visual-design\` skill before writing design files or block markup. diff --git a/apps/cli/ai/tests/system-prompt.test.ts b/apps/cli/ai/tests/system-prompt.test.ts index 1abe61370b..430671f167 100644 --- a/apps/cli/ai/tests/system-prompt.test.ts +++ b/apps/cli/ai/tests/system-prompt.test.ts @@ -75,6 +75,22 @@ describe( 'buildSystemPrompt', () => { expect( prompt ).not.toContain( '## Common wp/v2 Endpoints' ); } ); + it( 'guards plan/pricing/feature answers behind the hosting-plans-helper skill (local)', () => { + const prompt = buildSystemPrompt( { chatArtifactsEnabled: true } ); + + expect( prompt ).toContain( '`hosting-plans-helper` skill' ); + expect( prompt ).toContain( 'Do NOT answer from memory' ); + expect( prompt ).toContain( 'Personal or Premium cannot install plugins' ); + } ); + + it( 'guards plan/pricing/feature answers behind the hosting-plans-helper skill (remote)', () => { + const prompt = buildSystemPrompt( { remoteSite } ); + + expect( prompt ).toContain( '`hosting-plans-helper` skill' ); + expect( prompt ).toContain( 'Do NOT answer from memory' ); + expect( prompt ).toContain( 'Personal or Premium cannot install plugins' ); + } ); + it( 'references only bundled skills', () => { const prompts = [ buildSystemPrompt( { chatArtifactsEnabled: true } ),