Skip to content

Commit 1566f4a

Browse files
author
Kateryna Kodonenko
committed
Merge trunk into fix/refactor-auth-provider-in-redux-slice
Port removal of noisy Sentry.captureException calls from auth-provider.tsx to auth-slice.ts (handleInvalidToken, authLogout token revoke, authLogout clearAuthenticationToken), keeping the intentional deletion of auth-provider.tsx. Made-with: Cursor
2 parents 28cc95a + 67e9625 commit 1566f4a

File tree

351 files changed

+13242
-6485
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

351 files changed

+13242
-6485
lines changed

.github/dependabot.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ updates:
112112
- dependency-name: "ora"
113113
- dependency-name: "cross-port-killer"
114114
- dependency-name: "fast-deep-equal"
115+
- dependency-name: "simple-git"
115116

116117
# Markdown & content processing
117118
- dependency-name: "react-markdown"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ artifacts
125125
.cursor/
126126
.claude/
127127
CLAUDE.md
128+
!skills/CLAUDE.md
128129

129130
# Bundled Node binary (downloaded during build)
130131
apps/studio/bin/node

.xcode-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
26.2
1+
26.3

Gemfile.lock

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ GEM
33
specs:
44
CFPropertyList (3.0.8)
55
abbrev (0.1.2)
6-
activesupport (7.2.3)
6+
activesupport (7.2.3.1)
77
base64
88
benchmark (>= 0.3)
99
bigdecimal
@@ -12,7 +12,7 @@ GEM
1212
drb
1313
i18n (>= 1.6, < 2)
1414
logger (>= 1.4.2)
15-
minitest (>= 5.1)
15+
minitest (>= 5.1, < 6)
1616
securerandom (>= 0.3)
1717
tzinfo (~> 2.0, >= 2.0.5)
1818
addressable (2.8.9)
@@ -225,7 +225,7 @@ GEM
225225
concurrent-ruby (~> 1.0)
226226
java-properties (0.3.0)
227227
jmespath (1.6.2)
228-
json (2.19.1)
228+
json (2.19.2)
229229
jwt (2.10.2)
230230
base64
231231
language_server-protocol (3.17.0.5)
@@ -235,9 +235,7 @@ GEM
235235
logger (1.7.0)
236236
mini_magick (4.13.2)
237237
mini_mime (1.1.5)
238-
minitest (6.0.2)
239-
drb (~> 2.0)
240-
prism (~> 1.5)
238+
minitest (5.27.0)
241239
multi_json (1.19.1)
242240
multipart-post (2.4.1)
243241
mutex_m (0.3.0)

RELEASE-NOTES.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
Unreleased
22
==========
33

4+
1.7.7
5+
=====
6+
* Added dark mode support #2652 #2865 #2857 #2866
7+
* Added phpMyAdmin support #2834
8+
* Ship AI instructions with every newly created site #2886 #2889
9+
* Fixed issue where site status wouldn't show up correctly in app #2847
10+
* Rename 'Open site' to 'Open local site' in header #2856
11+
* Improve Playground CLI child process error handling #2844
12+
* Move login into Account tab and rename settings tabs #2854
13+
* Migrated config files, HTTPS certificates, and WordPress dependencies `~/.studio` #2793 #2871 #2873
14+
* Updated multiple dependencies, including WordPress packages, and PHP-WASM
15+
416
1.7.6
517
=====
618
* Added enableMultisite Blueprint step support #2470

apps/cli/README.md

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,84 @@
11
# WordPress Studio CLI
22

3-
> [!WARNING]
4-
> Please note that this is still alpha software. It requires the Studio desktop app to be installed.
3+
`wp-studio` is the standalone, CLI-only version of [WordPress Studio](https://developer.wordpress.com/studio/) – a fast, free, open source tool for local WordPress development all powered by WordPress Playground and WordPress.com.
54

6-
The standalone, CLI-only version of [WordPress Studio](https://developer.wordpress.com/studio/).
5+
If you already have Studio installed, then the easiest way to use the CLI is to open Studio, go to the settings modal and ensure that the "Studio CLI" toggle is enabled.
6+
7+
The Studio CLI lets you:
78

89
- Create, run, and manage local WordPress sites from the terminal.
910
- Run WP-CLI commands.
1011
- Publish ephemeral preview sites to share (requires WordPress.com login).
12+
- Integrate with AI coding agents. Every site comes with an `AGENTS.md` file.
13+
14+
# Table of contents
15+
16+
- [Requirements](#requirements)
17+
- [Installation](#installation)
18+
- [Quick start](#quick-start)
19+
- [Usage](#usage)
20+
- [Preview sites](#preview-sites)
21+
22+
## Requirements
1123

12-
## Install
24+
`wp-studio` runs best on Node.js 24 or higher, which supports more recent V8 WASM APIs. Node.js 22 or higher is required. You can download the appropriate version from the [Node.js website](https://nodejs.org/en/download).
25+
26+
## Installation
1327

1428
Run without installing:
1529

1630
```bash
17-
npx @automattic/wp-studio@latest --help
31+
npx wp-studio@latest --help
1832
```
1933

2034
Install globally:
2135

2236
```bash
23-
npm install -g @automattic/wp-studio
37+
npm install -g wp-studio
2438
studio --help
2539
```
2640

2741
## Quick start
2842

29-
Create a site (with a step-by-step guide):
43+
From anywhere on your system, run the following command to create a new WordPress site (with a step-by-step guide):
3044

3145
```bash
3246
studio site create
3347
```
3448

35-
List sites:
49+
## Usage
50+
51+
The Studio CLI integrates with Studio and uses the same list of sites. Similarly to Studio, the Studio CLI also runs sites in the background. To see the list of sites under management by Studio and their current status, run the command:
3652

3753
```bash
3854
studio site list
3955
```
4056

41-
Start / stop a site:
57+
To start and stop sites, run these commands:
4258

4359
```bash
4460
studio site start --path ~/Studio/my-site
4561
studio site stop --path ~/Studio/my-site
4662
```
4763

48-
Run WP-CLI in a site:
64+
Run WP-CLI commands in a site:
4965

5066
```bash
5167
studio wp plugin list --path ~/Studio/my-site
5268
studio wp option get home --path ~/Studio/my-site
5369
```
5470

71+
## Preview sites
72+
73+
The Studio CLI lets you share your work through preview sites. These are powered by WordPress.com on a temporary domain (wp.build), and they allow you to share snapshots of your local sites with clients or team members.
74+
5575
To publish preview sites, you need to first authenticate with WordPress.com:
5676

5777
```bash
5878
studio auth login
5979
```
6080

61-
Publish a preview site:
81+
Publish a preview with this command:
6282

6383
```bash
6484
studio preview create --path ~/Studio/my-site

apps/cli/__mocks__/lib/daemon-client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { vi } from 'vitest';
22

33
export const connectToDaemon = vi.fn().mockResolvedValue( undefined );
44
export const disconnectFromDaemon = vi.fn().mockResolvedValue( undefined );
5-
export const emitSiteEvent = vi.fn().mockResolvedValue( undefined );
5+
export const emitCliEvent = vi.fn().mockResolvedValue( undefined );
66
export const killDaemonAndChildren = vi.fn().mockResolvedValue( undefined );
77
export const listProcesses = vi.fn().mockResolvedValue( [] );
88
export const getDaemonBus = vi.fn().mockResolvedValue( {} );

apps/cli/ai/auth.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
getAiProviderDefinition,
55
type AiProviderId,
66
} from 'cli/ai/providers';
7-
import { getAiProvider, saveAiProvider } from 'cli/lib/appdata';
7+
import { readCliConfig, updateCliConfigWithPartial } from 'cli/lib/cli-config/core';
88

99
async function getPreferredReadyProvider(
1010
exclude?: AiProviderId
@@ -49,7 +49,7 @@ export async function resolveUnavailableAiProvider(
4949
}
5050

5151
export async function resolveInitialAiProvider(): Promise< AiProviderId > {
52-
const savedProvider = await getAiProvider();
52+
const { aiProvider: savedProvider } = await readCliConfig();
5353
if ( savedProvider ) {
5454
const definition = getAiProviderDefinition( savedProvider );
5555
if (
@@ -73,7 +73,7 @@ export async function resolveInitialAiProvider(): Promise< AiProviderId > {
7373
}
7474

7575
export async function saveSelectedAiProvider( provider: AiProviderId ): Promise< void > {
76-
await saveAiProvider( provider );
76+
await updateCliConfigWithPartial( { aiProvider: provider } );
7777
}
7878

7979
export async function prepareAiProvider(
Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,35 @@
11
---
22
name: site-spec
3-
description: Gather user preferences before building a WordPress site. Asks about purpose, audience, brand personality, aesthetic direction, colors, and content structure. Run this before creating any new site.
3+
description: Gather the site name and layout preference before building a WordPress site. Run this before creating any new site.
44
user-invokable: true
55
---
66

77
# Site Spec Discovery
88

9-
Before creating a new WordPress site, gather the user's preferences through a short interactive discovery phase. This produces a **Site Spec** that guides all subsequent design and development decisions.
9+
Before creating a new WordPress site, gather the user's basic preferences through a short interactive discovery phase. This produces a **Site Spec** that guides all subsequent design and development decisions.
1010

1111
## How to Run
1212

13-
Gather preferences through 3-4 rounds, building on previous answers. Keep it concise — no more than 6-7 questions total.
13+
Gather preferences through 2 rounds. Keep it concise.
1414

1515
**AskUserQuestion constraints**: Each call supports 1-4 questions, each with 2-4 options. An "Other" free-form option is automatically provided by the system — do NOT add one yourself. Keep option labels short (1-5 words). Only use AskUserQuestion for questions that have meaningful predefined options. For open-ended questions (like asking for a name), just ask in your text output — the user will type their answer in the prompt.
1616

1717
### Round 1 — Name
1818

1919
Ask the user for their business/site name in your text output. **Stop here and wait for their reply** — do NOT call any tools or continue to the next round. The user needs a chance to type their answer in the prompt.
2020

21-
### Round 2 — Purpose & Audience
21+
### Round 2 — Layout
2222

2323
After the user provides the name, use AskUserQuestion for:
24-
- What is this site for? (e.g., business type, portfolio, blog, community, e-commerce, agency, restaurant, etc.)
25-
- Who is the target audience? (e.g., professionals, consumers, creatives, developers, local community, etc.)
26-
27-
### Round 3 — Brand & Aesthetic Direction
28-
29-
Adapt based on previous answers. Ask about:
30-
- What tone or personality should the site convey? (e.g., professional, playful, minimalist, bold, luxurious, raw, editorial, retro, organic, etc.)
31-
- Any reference sites or brands you admire? Or styles you want to avoid?
32-
33-
### Round 4 — Visual & Structural Preferences
34-
35-
Adapt based on previous answers. Ask about:
36-
- Color preferences or existing brand colors? (or let the user say "surprise me")
3724
- One-page site or multi-page site? (e.g., single scrollable page with sections vs. separate pages for each area)
38-
- What pages/sections do you need? (e.g., homepage, about, services, blog, contact, shop, gallery, etc.) — adapt the phrasing based on the one-page vs. multi-page answer
39-
40-
## Synthesize the Site Spec
4125

42-
After gathering answers, produce a concise **Site Spec** document:
26+
## After Gathering Answers
4327

44-
```
45-
Site Spec: [Site Name]
46-
- Purpose: [what the site is for]
47-
- Audience: [who it's for]
48-
- Tone: [personality/voice]
49-
- Aesthetic direction: [visual style, references]
50-
- Color palette: [colors or direction]
51-
- Layout: [one-page or multi-page]
52-
- Pages/Structure: [list of pages or sections and key features]
53-
- Key differentiator: [the one thing that makes this site memorable]
54-
```
55-
56-
Show the spec to the user, then use AskUserQuestion to ask for confirmation (e.g., "Ready to build this site?" with options like "Yes, let's go" / "I'd like to adjust something"). Do NOT just print a text question and wait — always use the AskUserQuestion tool for confirmation so the user gets interactive options.
57-
58-
## After Confirmation
59-
60-
1. Call `site_create` with an appropriate name derived from the spec.
61-
2. Save the site spec to `{site_path}/.agents/site-spec.md` using the Write tool, so it persists for future sessions.
62-
3. Use the spec to guide ALL subsequent design decisions — theme, typography, colors, layout, content tone, and page structure.
28+
Call `site_create` with the provided name and use the layout preference to guide all subsequent design decisions.
6329

6430
## When to Skip Discovery
6531

6632
Do NOT ask questions if:
67-
- The user already provided an answer for that specific question in the initial prompt. (e.g., "build me a dark minimalist portfolio for a photographer with these 5 pages using navy and gold colors"). Instead, synthesize the spec directly from their request and confirm.
33+
- The user already provided the name and layout preference in the initial prompt. Proceed directly with site creation.
6834
- The user says "just build something" or "surprise me". Pick a bold creative direction yourself and proceed.
6935
- The user explicitly asks to skip the setup or says they don't want questions.
70-
71-
In all skip cases, still synthesize and show a Site Spec before creating the site.

apps/cli/ai/providers.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import childProcess from 'child_process';
22
import { password } from '@inquirer/prompts';
3+
import { readAuthToken } from '@studio/common/lib/shared-config';
34
import { __ } from '@wordpress/i18n';
4-
import { z } from 'zod';
5-
import { getAnthropicApiKey, getAuthToken, saveAnthropicApiKey } from 'cli/lib/appdata';
5+
import { readCliConfig, updateCliConfigWithPartial } from 'cli/lib/cli-config/core';
66
import { LoggerError } from 'cli/logger';
77

88
export const AI_PROVIDERS = {
@@ -13,7 +13,6 @@ export const AI_PROVIDERS = {
1313

1414
export type AiProviderId = keyof typeof AI_PROVIDERS;
1515

16-
export const aiProviderSchema = z.enum( [ 'wpcom', 'anthropic-claude', 'anthropic-api-key' ] );
1716
export const DEFAULT_AI_PROVIDER: AiProviderId = 'anthropic-api-key';
1817
export const AI_PROVIDER_PRIORITY: AiProviderId[] = [
1918
'wpcom',
@@ -51,7 +50,7 @@ export function hasClaudeCodeAuth(): boolean {
5150
async function resolveAnthropicApiKey( options?: {
5251
force?: boolean;
5352
} ): Promise< string | undefined > {
54-
const savedKey = await getAnthropicApiKey();
53+
const { anthropicApiKey: savedKey } = await readCliConfig();
5554
if ( savedKey && ! options?.force ) {
5655
return savedKey;
5756
}
@@ -67,7 +66,7 @@ async function resolveAnthropicApiKey( options?: {
6766
},
6867
} );
6968

70-
await saveAnthropicApiKey( apiKey );
69+
await updateCliConfigWithPartial( { anthropicApiKey: apiKey } );
7170
return apiKey;
7271
}
7372

@@ -83,12 +82,8 @@ function getWpcomAiGatewayBaseUrl(): string {
8382
}
8483

8584
async function hasValidWpcomAuth(): Promise< boolean > {
86-
try {
87-
await getAuthToken();
88-
return true;
89-
} catch {
90-
return false;
91-
}
85+
const token = await readAuthToken();
86+
return token !== null;
9287
}
9388

9489
function createBaseEnvironment(): Record< string, string > {
@@ -117,7 +112,10 @@ const AI_PROVIDER_DEFINITIONS: Record< AiProviderId, AiProviderDefinition > = {
117112
throw new LoggerError( __( 'WordPress.com login required. Use /login to authenticate.' ) );
118113
},
119114
resolveEnv: async () => {
120-
const token = await getAuthToken();
115+
const token = await readAuthToken();
116+
if ( ! token ) {
117+
throw new LoggerError( __( 'WordPress.com login required. Use /login to authenticate.' ) );
118+
}
121119
const env = createBaseEnvironment();
122120
env.ANTHROPIC_BASE_URL = getWpcomAiGatewayBaseUrl();
123121
env.ANTHROPIC_AUTH_TOKEN = token.accessToken;
@@ -156,12 +154,15 @@ const AI_PROVIDER_DEFINITIONS: Record< AiProviderId, AiProviderDefinition > = {
156154
id: 'anthropic-api-key',
157155
autoFallbackWhenUnavailable: false,
158156
isVisible: async () => true,
159-
isReady: async () => Boolean( await getAnthropicApiKey() ),
157+
isReady: async () => {
158+
const { anthropicApiKey } = await readCliConfig();
159+
return Boolean( anthropicApiKey );
160+
},
160161
prepare: async ( options ) => {
161162
await resolveAnthropicApiKey( options );
162163
},
163164
resolveEnv: async () => {
164-
const apiKey = await getAnthropicApiKey();
165+
const { anthropicApiKey: apiKey } = await readCliConfig();
165166
if ( ! apiKey ) {
166167
throw new LoggerError(
167168
__(

0 commit comments

Comments
 (0)