wp-studio 1.7.10 → 1.7.11-beta1
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/cli/{_events-BcapW3eh.mjs → _events-D_POEBYs.mjs} +4 -5
- package/dist/cli/appdata-D-luHxJU.mjs +19 -0
- package/dist/cli/{certificate-manager-SVYcCL_i.mjs → certificate-manager-v-yNLDFJ.mjs} +134 -15
- package/dist/cli/{delete-D1924O3o.mjs → delete-C4R7DV_E.mjs} +3 -3
- package/dist/cli/{helpers-oQuItT8n.mjs → helpers-CBl4GQzX.mjs} +2 -2
- package/dist/cli/{index-4lan3TI_.mjs → index-Bej4fL6n.mjs} +31 -4
- package/dist/cli/{index-DRQnCQvM.mjs → index-DYlrlauo.mjs} +1308 -1109
- package/dist/cli/{index-BjzOJKPi.mjs → index-jpyzVGrl.mjs} +90 -52
- package/dist/cli/{list-DOFyyV1f.mjs → list-BRllR-8H.mjs} +3 -3
- package/dist/cli/{login-BtPZeZ4G.mjs → login-CDVrFvfk.mjs} +3 -3
- package/dist/cli/{logout-Cr631QzG.mjs → logout-DLAXkbrZ.mjs} +2 -3
- package/dist/cli/main.mjs +2 -2
- package/dist/cli/{paths-CqXGLB7R.mjs → paths-D7DniT1Q.mjs} +7 -6
- package/dist/cli/plugin/skills/rank-me-up/SKILL.md +166 -0
- package/dist/cli/plugin/skills/site-spec/SKILL.md +4 -0
- package/dist/cli/process-manager-daemon.mjs +29 -5
- package/dist/cli/{process-manager-ipc-BisO0qtU.mjs → process-manager-ipc-GCdebuBH.mjs} +4 -1
- package/dist/cli/proxy-daemon.mjs +1 -1
- package/dist/cli/{prune-pm-logs-COryxqeo.mjs → prune-pm-logs-Dm_Bwi7l.mjs} +1 -1
- package/dist/cli/{resume-BwDwdJtq.mjs → resume-B8M2e-Ii.mjs} +4 -15
- package/dist/cli/{rewrite-wp-cli-post-content-2zlfFnKT.mjs → rewrite-wp-cli-post-content-Beo5_Ojo.mjs} +32 -531
- package/dist/cli/{set-D5eeqHbp.mjs → set-CizYT2gE.mjs} +2 -3
- package/dist/cli/{set-DYnzUz_G.mjs → set-_5ABvjxt.mjs} +4 -5
- package/dist/cli/{status-DNvMZBqD.mjs → status-D6Huwi6x.mjs} +2 -2
- package/dist/cli/well-known-paths-QcSJNi_l.mjs +95 -0
- package/dist/cli/wordpress-server-child.mjs +5 -3
- package/dist/cli/{wp-DD2-QiiP.mjs → wp-BevvZcM1.mjs} +2 -2
- package/dist/cli/wp-files/latest/available-site-translations.json +1 -1
- package/dist/cli/wp-files/sqlite-database-integration/admin-page.php +1 -2
- package/dist/cli/wp-files/sqlite-database-integration/constants.php +0 -5
- package/dist/cli/wp-files/sqlite-database-integration/load.php +1 -1
- package/dist/cli/wp-files/sqlite-database-integration/readme.txt +6 -3
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/database/sqlite/class-wp-pdo-mysql-on-sqlite.php +22 -3
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/database/version.php +1 -1
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php +41 -89
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/db.php +2 -24
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/install-functions.php +7 -16
- package/package.json +2 -1
- package/dist/cli/well-known-paths-BYA1Bw5o.mjs +0 -214
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-lexer.php +0 -2575
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php +0 -899
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-query-rewriter.php +0 -343
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-token.php +0 -327
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-translator.php +0 -4543
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/php-polyfills.php +0 -68
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { b as getAiSessionsDirectoryForDate, c as buildAiSessionFileName, l as listAiSessions, g as getAiSessionsRootDirectory } from "./paths-
|
|
2
|
-
import { S as STUDIO_SITES_ROOT, c as createRemoteSiteTools, a as createStudioTools, r as readAuthToken, b as getSnapshotsFromConfig, i as isSnapshotExpired, d as captureCommandOutput, e as runCommand$3, f as runCommand$4, h as chalk, j as getSitesRunningStatus, k as getWpComSites, l as isSiteRunning, o as openBrowser, m as getSiteUrl, n as closeSharedBrowser } from "./index-
|
|
1
|
+
import { b as getAiSessionsDirectoryForDate, c as buildAiSessionFileName, l as listAiSessions, g as getAiSessionsRootDirectory } from "./paths-D7DniT1Q.mjs";
|
|
2
|
+
import { S as STUDIO_SITES_ROOT, c as createRemoteSiteTools, a as createStudioTools, r as readAuthToken, b as getSnapshotsFromConfig, i as isSnapshotExpired, d as captureCommandOutput, e as runCommand$3, f as runCommand$4, h as chalk, j as getSitesRunningStatus, k as getWpComSites, l as isSiteRunning, o as openBrowser, m as getSiteUrl, n as closeSharedBrowser } from "./index-DYlrlauo.mjs";
|
|
3
3
|
import { __, sprintf, _n } from "@wordpress/i18n";
|
|
4
4
|
import fs__default from "fs";
|
|
5
5
|
import path__default from "path";
|
|
6
6
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
7
7
|
import os from "os";
|
|
8
|
-
import { l as lockCliConfig, r as readCliConfig, s as saveCliConfig, u as unlockCliConfig,
|
|
8
|
+
import { l as lockCliConfig, r as readCliConfig, s as saveCliConfig, u as unlockCliConfig, L as LoggerError, m as updateCliConfigWithPartial, c as Logger, q as setProgressCallback } from "./certificate-manager-v-yNLDFJ.mjs";
|
|
9
9
|
import { password } from "@inquirer/prompts";
|
|
10
|
-
import { L as LoggerError, d as Logger, s as setProgressCallback } from "./well-known-paths-BYA1Bw5o.mjs";
|
|
11
10
|
import crypto from "crypto";
|
|
12
11
|
import fs from "fs/promises";
|
|
13
|
-
import { runCommand as runCommand$1 } from "./login-
|
|
14
|
-
import { runCommand as runCommand$2 } from "./logout-
|
|
12
|
+
import { runCommand as runCommand$1 } from "./login-CDVrFvfk.mjs";
|
|
13
|
+
import { runCommand as runCommand$2 } from "./logout-DLAXkbrZ.mjs";
|
|
15
14
|
import { ProcessTerminal, TUI, Container, Loader, CombinedAutocompleteProvider, isKeyRelease, matchesKey, Text, SelectList, Input, Markdown, Editor, visibleWidth, CURSOR_MARKER, truncateToWidth } from "@mariozechner/pi-tui";
|
|
16
15
|
const AI_MODELS = {
|
|
17
16
|
"claude-sonnet-4-6": "Sonnet 4.6",
|
|
@@ -357,6 +356,17 @@ Then continue with:
|
|
|
357
356
|
5. **Check the misuse of HTML blocks**: Verify if HTML blocks were used as sections or not. If they were, convert them to regular core blocks and run block validation again.
|
|
358
357
|
6. **Check the result**: Use take_screenshot to capture the site's landing page on desktop and mobile and verify the design visually on both viewports, check for wrong spacing, alignment, colors, contrast, borders, hover styles and other visual issues. Fix any issues found. Pay particular attention to the navigation menu and the CTA buttons. The design needs to match your original expectations.
|
|
359
358
|
|
|
359
|
+
## Working cadence
|
|
360
|
+
|
|
361
|
+
One \`Write\` or \`Edit\` per turn (read-only \`site_info\`, \`site_list\`, \`wp_cli\` queries may be combined). Short prose between tools — no long design-plan essays. The CLI only renders complete assistant messages, so a turn that batches files or emits >~200 lines spins silently for minutes and can hit gateway timeouts. Cadence is also a quality lever: the screenshot-fix loop only works after small visible increments.
|
|
362
|
+
|
|
363
|
+
**After \`site_create\`** (or "redesign"/"rebuild"/"start over" triggers), the next turn MUST be small: \`site_info\` or a single ≤50-line \`Write\`. Never scaffold a whole theme in one turn.
|
|
364
|
+
|
|
365
|
+
**Long files (>~200 lines): skeleton first, then fill across Edits.**
|
|
366
|
+
|
|
367
|
+
- \`style.css\`: skeleton = \`:root { ... }\` custom properties + 6–10 anchor comments \`/* === <concern> === */\` (e.g. \`reset\`, \`typography\`, \`hero\`, \`features\`, \`cta\`, \`footer\`, \`responsive\`), <2KB total. Fill one anchor per Edit (300–2000B each) — \`old_string\` is the anchor line, \`new_string\` is \`<anchor>\\n\\n<styles>\`.
|
|
368
|
+
- Page content: create the page empty (\`wp_cli post create --post_content=""\`), write \`<theme>/page-content.html\` with \`<!-- section:<concern> -->\` anchors (<1KB), fill one anchor per Edit using only core blocks (never wrap in \`core/html\`), then apply once with \`wp_cli post update <id> --post_content-file=<absolute path>\`.
|
|
369
|
+
|
|
360
370
|
## Available Studio Tools (prefixed with mcp__studio__)
|
|
361
371
|
|
|
362
372
|
- site_create: Create a new WordPress site (name only — handles everything automatically)
|
|
@@ -373,6 +383,7 @@ Then continue with:
|
|
|
373
383
|
- validate_blocks: Validate block content for correctness on a running site (runs each block through its save() function in a real browser). Requires a site name or path. Call after every file write/edit that contains block content.
|
|
374
384
|
- take_screenshot: Take a full-page screenshot of a URL (supports desktop and mobile viewports). Use this to visually check the site after building it.
|
|
375
385
|
- need_for_speed: Measure frontend performance metrics (TTFB, FCP, LCP, CLS, page weight, DOM size, JS/CSS/image/font asset breakdown) for a running site. Use this to identify performance bottlenecks and guide optimization.
|
|
386
|
+
- rank_me_up: Run an on-page SEO audit (title/meta tags, headings, image alt text, OpenGraph/Twitter cards, JSON-LD structured data, robots.txt and sitemap.xml availability) for a running site. Use this to identify on-page SEO issues and guide fixes.
|
|
376
387
|
- site_push: Push a local site to a WordPress.com site. Requires authentication (studio auth login). Specify the remote site URL or ID and sync options (all, sqls, uploads, plugins, themes, contents).
|
|
377
388
|
- site_pull: Pull a WordPress.com site to a local site. Requires authentication. Specify the remote site URL or ID and sync options.
|
|
378
389
|
- site_import: Import a backup file (.zip, .tar.gz, .sql, .wpress) into a local site.
|
|
@@ -464,7 +475,7 @@ function startAiAgent(config) {
|
|
|
464
475
|
prompt,
|
|
465
476
|
env,
|
|
466
477
|
model = DEFAULT_MODEL,
|
|
467
|
-
maxTurns =
|
|
478
|
+
maxTurns = 75,
|
|
468
479
|
resume,
|
|
469
480
|
autoApprove,
|
|
470
481
|
activeSite,
|
|
@@ -579,6 +590,7 @@ function createBaseEnvironment() {
|
|
|
579
590
|
delete env.ANTHROPIC_BASE_URL;
|
|
580
591
|
delete env.ANTHROPIC_CUSTOM_HEADERS;
|
|
581
592
|
delete env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS;
|
|
593
|
+
delete env.CLAUDE_CODE_MAX_RETRIES;
|
|
582
594
|
if (!env.CLAUDE_CODE_MAX_RETRIES) {
|
|
583
595
|
env.CLAUDE_CODE_MAX_RETRIES = "1";
|
|
584
596
|
}
|
|
@@ -596,7 +608,7 @@ const AI_PROVIDER_DEFINITIONS = {
|
|
|
596
608
|
}
|
|
597
609
|
throw new LoggerError(__("WordPress.com login required. Use /login to authenticate."));
|
|
598
610
|
},
|
|
599
|
-
resolveEnv: async () => {
|
|
611
|
+
resolveEnv: async (options) => {
|
|
600
612
|
const inlineToken = readInlineWpcomToken();
|
|
601
613
|
const accessToken = inlineToken ?? (await readAuthToken())?.accessToken;
|
|
602
614
|
if (!accessToken) {
|
|
@@ -605,10 +617,15 @@ const AI_PROVIDER_DEFINITIONS = {
|
|
|
605
617
|
const env = createBaseEnvironment();
|
|
606
618
|
env.ANTHROPIC_BASE_URL = getWpcomAiGatewayBaseUrl();
|
|
607
619
|
env.ANTHROPIC_AUTH_TOKEN = accessToken;
|
|
608
|
-
|
|
620
|
+
const customHeaders = {
|
|
609
621
|
"X-WPCOM-AI-Feature": WPCOM_AI_FEATURE_HEADER
|
|
610
|
-
}
|
|
622
|
+
};
|
|
623
|
+
if (options?.sessionId) {
|
|
624
|
+
customHeaders["X-WPCOM-Session-ID"] = options.sessionId;
|
|
625
|
+
}
|
|
626
|
+
env.ANTHROPIC_CUSTOM_HEADERS = buildAnthropicCustomHeaders(customHeaders);
|
|
611
627
|
env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1";
|
|
628
|
+
env.CLAUDE_CODE_MAX_RETRIES = "0";
|
|
612
629
|
return env;
|
|
613
630
|
}
|
|
614
631
|
},
|
|
@@ -698,8 +715,8 @@ async function saveSelectedAiProvider(provider) {
|
|
|
698
715
|
async function prepareAiProvider(provider, options) {
|
|
699
716
|
await getAiProviderDefinition(provider).prepare(options);
|
|
700
717
|
}
|
|
701
|
-
async function resolveAiEnvironment(provider) {
|
|
702
|
-
return getAiProviderDefinition(provider).resolveEnv();
|
|
718
|
+
async function resolveAiEnvironment(provider, options) {
|
|
719
|
+
return getAiProviderDefinition(provider).resolveEnv(options);
|
|
703
720
|
}
|
|
704
721
|
function emitEvent(event) {
|
|
705
722
|
if (typeof process.send === "function") {
|
|
@@ -938,15 +955,6 @@ class AiSessionRecorder {
|
|
|
938
955
|
wpcomSiteId: site.wpcomSiteId
|
|
939
956
|
});
|
|
940
957
|
}
|
|
941
|
-
async recordEnvironmentSelected(payload) {
|
|
942
|
-
await this.appendEvent({
|
|
943
|
-
type: "environment.selected",
|
|
944
|
-
timestamp: toIsoTimestamp(),
|
|
945
|
-
environment: payload.environment,
|
|
946
|
-
url: payload.url,
|
|
947
|
-
wpcomSiteId: payload.wpcomSiteId
|
|
948
|
-
});
|
|
949
|
-
}
|
|
950
958
|
async recordUserMessage(options) {
|
|
951
959
|
await this.appendEvent({
|
|
952
960
|
type: "user.message",
|
|
@@ -1033,25 +1041,6 @@ function replaySessionHistory(ui, events) {
|
|
|
1033
1041
|
);
|
|
1034
1042
|
continue;
|
|
1035
1043
|
}
|
|
1036
|
-
if (event.type === "environment.selected") {
|
|
1037
|
-
const current = ui.activeSite;
|
|
1038
|
-
if (!current) {
|
|
1039
|
-
continue;
|
|
1040
|
-
}
|
|
1041
|
-
const isLive = event.environment === "live";
|
|
1042
|
-
ui.setActiveSite(
|
|
1043
|
-
{
|
|
1044
|
-
name: current.name,
|
|
1045
|
-
path: current.path,
|
|
1046
|
-
running: current.running,
|
|
1047
|
-
remote: isLive,
|
|
1048
|
-
url: isLive ? event.url : void 0,
|
|
1049
|
-
wpcomSiteId: isLive ? event.wpcomSiteId : void 0
|
|
1050
|
-
},
|
|
1051
|
-
{ announce: true, emitEvent: false }
|
|
1052
|
-
);
|
|
1053
|
-
continue;
|
|
1054
|
-
}
|
|
1055
1044
|
if (event.type === "user.message") {
|
|
1056
1045
|
if (!isVisibleUserMessage(event)) {
|
|
1057
1046
|
continue;
|
|
@@ -1328,7 +1317,8 @@ const AI_CHAT_SLASH_COMMANDS = [
|
|
|
1328
1317
|
handler: async () => "break"
|
|
1329
1318
|
},
|
|
1330
1319
|
{ name: "taxonomist", description: __("Optimize category taxonomy with AI") },
|
|
1331
|
-
{ name: "need-for-speed", description: __("Run a performance audit on a site") }
|
|
1320
|
+
{ name: "need-for-speed", description: __("Run a performance audit on a site") },
|
|
1321
|
+
{ name: "rank-me-up", description: __("Run an on-page SEO audit on a site") }
|
|
1332
1322
|
];
|
|
1333
1323
|
const THINKING_MESSAGES = [
|
|
1334
1324
|
"Thinking…",
|
|
@@ -1792,6 +1782,7 @@ class AiChatUI {
|
|
|
1792
1782
|
this.editorVisible = false;
|
|
1793
1783
|
this.interruptCallback = null;
|
|
1794
1784
|
this.wasInterrupted = false;
|
|
1785
|
+
this.usageCapReached = false;
|
|
1795
1786
|
this.hasShownResponseMarker = false;
|
|
1796
1787
|
this.turnStartTime = 0;
|
|
1797
1788
|
this.toolStartTime = null;
|
|
@@ -2217,15 +2208,16 @@ ${chalk.dim(message)}
|
|
|
2217
2208
|
this.refreshPromptChrome();
|
|
2218
2209
|
const label = site.remote ? sprintf(
|
|
2219
2210
|
/* translators: %s: site name */
|
|
2220
|
-
__("
|
|
2211
|
+
__(" Selected site: %s (WordPress.com)"),
|
|
2221
2212
|
site.name
|
|
2222
2213
|
) : sprintf(
|
|
2223
2214
|
/* translators: %s: site name */
|
|
2224
|
-
__("
|
|
2215
|
+
__(" Selected site: %s"),
|
|
2225
2216
|
site.name
|
|
2226
2217
|
);
|
|
2227
2218
|
if (announce) {
|
|
2228
|
-
this.messages.addChild(new Text(
|
|
2219
|
+
this.messages.addChild(new Text(`
|
|
2220
|
+
${chalk.hex("#8839ef")(label)}
|
|
2229
2221
|
`, 0, 0));
|
|
2230
2222
|
}
|
|
2231
2223
|
if (emitEvent2) {
|
|
@@ -2448,7 +2440,7 @@ ${chalk.dim(message)}
|
|
|
2448
2440
|
this.tui.start();
|
|
2449
2441
|
}
|
|
2450
2442
|
showWelcome() {
|
|
2451
|
-
const version = "1.7.
|
|
2443
|
+
const version = "1.7.11-beta1";
|
|
2452
2444
|
const cwd = process.cwd();
|
|
2453
2445
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
2454
2446
|
const displayCwd = home && cwd.startsWith(home) ? "~" + cwd.slice(home.length) : cwd;
|
|
@@ -2622,6 +2614,7 @@ ${chalk.dim(message)}
|
|
|
2622
2614
|
this.currentResponseText = "";
|
|
2623
2615
|
this.hasShownResponseMarker = false;
|
|
2624
2616
|
this.wasInterrupted = false;
|
|
2617
|
+
this.usageCapReached = false;
|
|
2625
2618
|
this.turnStartTime = this.nowMs();
|
|
2626
2619
|
this.todoSnapshot = [];
|
|
2627
2620
|
this.latestTodoSnapshot = [];
|
|
@@ -2648,6 +2641,15 @@ ${chalk.dim(message)}
|
|
|
2648
2641
|
this.pendingTodoRenders.clear();
|
|
2649
2642
|
this.pendingTodoRenderOrder = [];
|
|
2650
2643
|
}
|
|
2644
|
+
/**
|
|
2645
|
+
* Returns true when the current/last turn surfaced the AI usage cap
|
|
2646
|
+
* message to the user. Lets callers suppress redundant downstream
|
|
2647
|
+
* errors (e.g. the SDK's "process exited with code 1" that follows
|
|
2648
|
+
* the upstream 429).
|
|
2649
|
+
*/
|
|
2650
|
+
hasErrorBeenSurfaced() {
|
|
2651
|
+
return this.usageCapReached;
|
|
2652
|
+
}
|
|
2651
2653
|
showOnboarding() {
|
|
2652
2654
|
const text = " " + chalk.blue("⏺") + " " + sprintf(
|
|
2653
2655
|
/* translators: %s: product name (WordPress Studio) */
|
|
@@ -3113,6 +3115,30 @@ ${chalk.dim(message)}
|
|
|
3113
3115
|
handleMessage(message) {
|
|
3114
3116
|
switch (message.type) {
|
|
3115
3117
|
case "assistant": {
|
|
3118
|
+
let isCapMessage = false;
|
|
3119
|
+
if (message.error && this.currentProvider === "wpcom") {
|
|
3120
|
+
for (const block of message.message.content) {
|
|
3121
|
+
if (block.type === "text" && /API Error:\s*429/i.test(block.text ?? "")) {
|
|
3122
|
+
isCapMessage = true;
|
|
3123
|
+
break;
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
if (isCapMessage) {
|
|
3128
|
+
this.hideLoader();
|
|
3129
|
+
this.usageCapReached = true;
|
|
3130
|
+
this.showError(
|
|
3131
|
+
__(
|
|
3132
|
+
"AI usage cap reached. You can continue using Studio Code by switching to your own Anthropic API key."
|
|
3133
|
+
)
|
|
3134
|
+
);
|
|
3135
|
+
this.showInfo(
|
|
3136
|
+
__("Use /provider to switch to Anthropic · API key, or try again later.")
|
|
3137
|
+
);
|
|
3138
|
+
this.currentMarkdown = null;
|
|
3139
|
+
this.currentResponseText = "";
|
|
3140
|
+
return void 0;
|
|
3141
|
+
}
|
|
3116
3142
|
for (const block of message.message.content) {
|
|
3117
3143
|
if (block.type === "text") {
|
|
3118
3144
|
this.hideLoader();
|
|
@@ -3195,6 +3221,9 @@ ${chalk.dim(message)}
|
|
|
3195
3221
|
numTurns: message.num_turns
|
|
3196
3222
|
};
|
|
3197
3223
|
}
|
|
3224
|
+
if (this.usageCapReached) {
|
|
3225
|
+
return { type: "result", sessionId: message.session_id, success: false };
|
|
3226
|
+
}
|
|
3198
3227
|
if (this.wasInterrupted) {
|
|
3199
3228
|
const thinkingSec2 = Math.round((this.nowMs() - this.turnStartTime) / 1e3);
|
|
3200
3229
|
this.messages.addChild(
|
|
@@ -3466,6 +3495,9 @@ async function runCommand(options) {
|
|
|
3466
3495
|
}
|
|
3467
3496
|
function handleAgentTurnError(error) {
|
|
3468
3497
|
sessionId = void 0;
|
|
3498
|
+
if (ui instanceof AiChatUI && ui.hasErrorBeenSurfaced()) {
|
|
3499
|
+
return;
|
|
3500
|
+
}
|
|
3469
3501
|
if (error instanceof LoggerError) {
|
|
3470
3502
|
ui.showError(error.message);
|
|
3471
3503
|
} else if (error instanceof Error) {
|
|
@@ -3588,7 +3620,10 @@ async function runCommand(options) {
|
|
|
3588
3620
|
const MAX_RETRY_ATTEMPTS = 4;
|
|
3589
3621
|
async function runAgentTurn(prompt, retryAttempt = 0) {
|
|
3590
3622
|
await maybeAutoSwitchProvider();
|
|
3591
|
-
const
|
|
3623
|
+
const recorder = await ensureSessionRecorder();
|
|
3624
|
+
const env = await resolveAiEnvironment(currentProvider, {
|
|
3625
|
+
sessionId: recorder?.sessionId
|
|
3626
|
+
});
|
|
3592
3627
|
ui.beginAgentTurn();
|
|
3593
3628
|
let enrichedPrompt = prompt;
|
|
3594
3629
|
const site = ui.activeSite;
|
|
@@ -3608,7 +3643,7 @@ ${prompt}`;
|
|
|
3608
3643
|
}
|
|
3609
3644
|
await persistSessionContext();
|
|
3610
3645
|
await persist(
|
|
3611
|
-
(
|
|
3646
|
+
(recorder2) => recorder2.recordUserMessage({
|
|
3612
3647
|
text: prompt,
|
|
3613
3648
|
source: "prompt",
|
|
3614
3649
|
sitePath: site?.path
|
|
@@ -3633,10 +3668,10 @@ ${prompt}`;
|
|
|
3633
3668
|
for await (const message of agentQuery) {
|
|
3634
3669
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3635
3670
|
const result = ui.handleMessage(message);
|
|
3636
|
-
await persist((
|
|
3671
|
+
await persist((recorder2) => recorder2.recordSdkMessage(message, timestamp));
|
|
3637
3672
|
if (result) {
|
|
3638
3673
|
sessionId = result.sessionId;
|
|
3639
|
-
await persist((
|
|
3674
|
+
await persist((recorder2) => recorder2.recordAgentSessionId(result.sessionId));
|
|
3640
3675
|
if (result.type === "max_turns") {
|
|
3641
3676
|
maxTurnsResult = {
|
|
3642
3677
|
numTurns: result.numTurns
|
|
@@ -3654,9 +3689,11 @@ ${prompt}`;
|
|
|
3654
3689
|
if (isJsonMode) {
|
|
3655
3690
|
throw error;
|
|
3656
3691
|
}
|
|
3657
|
-
ui.
|
|
3692
|
+
if (!(ui instanceof AiChatUI && ui.hasErrorBeenSurfaced())) {
|
|
3693
|
+
ui.showError(getErrorMessage(error));
|
|
3694
|
+
}
|
|
3658
3695
|
} finally {
|
|
3659
|
-
await persist((
|
|
3696
|
+
await persist((recorder2) => recorder2.recordTurnClosed(turnStatus));
|
|
3660
3697
|
ui.endAgentTurn();
|
|
3661
3698
|
}
|
|
3662
3699
|
if (maxTurnsResult) {
|
|
@@ -3682,7 +3719,8 @@ ${prompt}`;
|
|
|
3682
3719
|
return runAgentTurn("Continue from where you left off.");
|
|
3683
3720
|
}
|
|
3684
3721
|
}
|
|
3685
|
-
|
|
3722
|
+
const hasTerminalError = ui instanceof AiChatUI && ui.hasErrorBeenSurfaced();
|
|
3723
|
+
if (turnStatus === "error" && !isJsonMode && !hasTerminalError) {
|
|
3686
3724
|
if (retryAttempt >= MAX_RETRY_ATTEMPTS) {
|
|
3687
3725
|
ui.showInfo(
|
|
3688
3726
|
__("The server has not recovered after multiple attempts. Please try again later.")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { l as listAiSessions, g as getAiSessionsRootDirectory } from "./paths-
|
|
1
|
+
import { l as listAiSessions, g as getAiSessionsRootDirectory } from "./paths-D7DniT1Q.mjs";
|
|
2
2
|
import { __ } from "@wordpress/i18n";
|
|
3
|
-
import { d as displaySessionsCompact } from "./helpers-
|
|
4
|
-
import { L as LoggerError,
|
|
3
|
+
import { d as displaySessionsCompact } from "./helpers-CBl4GQzX.mjs";
|
|
4
|
+
import { L as LoggerError, c as Logger } from "./certificate-manager-v-yNLDFJ.mjs";
|
|
5
5
|
const logger = new Logger();
|
|
6
6
|
async function runCommand(format) {
|
|
7
7
|
const sessions = await listAiSessions(getAiSessionsRootDirectory());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { input } from "@inquirer/prompts";
|
|
2
|
-
import {
|
|
3
|
-
import { A as AUTH_EVENTS } from "./certificate-manager-
|
|
4
|
-
import { r as readAuthToken, A as AuthCommandLoggerAction, p as getAppLocale, o as openBrowser, g as getUserInfo, u as updateSharedConfig, q as emitCliEvent } from "./index-
|
|
2
|
+
import { s as PROTOCOL_PREFIX, t as CLIENT_ID, u as DEFAULT_TOKEN_LIFETIME_MS } from "./well-known-paths-QcSJNi_l.mjs";
|
|
3
|
+
import { c as Logger, L as LoggerError, A as AUTH_EVENTS } from "./certificate-manager-v-yNLDFJ.mjs";
|
|
4
|
+
import { r as readAuthToken, A as AuthCommandLoggerAction, p as getAppLocale, o as openBrowser, g as getUserInfo, u as updateSharedConfig, q as emitCliEvent } from "./index-DYlrlauo.mjs";
|
|
5
5
|
import { __, sprintf } from "@wordpress/i18n";
|
|
6
6
|
const SCOPES = "global";
|
|
7
7
|
const REDIRECT_URI = `${PROTOCOL_PREFIX}://auth`;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { A as AUTH_EVENTS } from "./certificate-manager-
|
|
2
|
-
import { A as AuthCommandLoggerAction, r as readAuthToken, s as revokeAuthToken, u as updateSharedConfig, q as emitCliEvent } from "./index-
|
|
1
|
+
import { c as Logger, L as LoggerError, A as AUTH_EVENTS } from "./certificate-manager-v-yNLDFJ.mjs";
|
|
2
|
+
import { A as AuthCommandLoggerAction, r as readAuthToken, s as revokeAuthToken, u as updateSharedConfig, q as emitCliEvent } from "./index-DYlrlauo.mjs";
|
|
3
3
|
import { __ } from "@wordpress/i18n";
|
|
4
|
-
import { d as Logger, L as LoggerError } from "./well-known-paths-BYA1Bw5o.mjs";
|
|
5
4
|
async function runCommand() {
|
|
6
5
|
const logger = new Logger();
|
|
7
6
|
logger.reportStart(AuthCommandLoggerAction.LOGOUT, __("Logging out…"));
|
package/dist/cli/main.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "node:path";
|
|
3
|
-
import "./index-
|
|
3
|
+
import "./index-DYlrlauo.mjs";
|
|
4
4
|
import "@wordpress/i18n";
|
|
5
5
|
import "semver";
|
|
6
6
|
import "yargs";
|
|
7
|
-
import "./certificate-manager-
|
|
7
|
+
import "./certificate-manager-v-yNLDFJ.mjs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "crypto";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path__default from "path";
|
|
4
|
-
import {
|
|
4
|
+
import { g as getAppdataDirectory } from "./appdata-D-luHxJU.mjs";
|
|
5
5
|
const SESSION_FILE_EXTENSION = ".jsonl";
|
|
6
6
|
const SESSION_ID_REGEX = /\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b/i;
|
|
7
7
|
function buildAiSessionFileName(startedAt, sessionId) {
|
|
@@ -35,6 +35,7 @@ async function readAiSessionSummaryFromEvents(filePath, events) {
|
|
|
35
35
|
let ownerSiteName;
|
|
36
36
|
let selectedSiteName;
|
|
37
37
|
let activeEnvironment = "local";
|
|
38
|
+
let lastSelectedWpcomSiteId;
|
|
38
39
|
let endReason;
|
|
39
40
|
let eventCount = 0;
|
|
40
41
|
for (const event of events) {
|
|
@@ -51,14 +52,13 @@ async function readAiSessionSummaryFromEvents(filePath, events) {
|
|
|
51
52
|
}
|
|
52
53
|
if (event.type === "site.selected") {
|
|
53
54
|
selectedSiteName = event.siteName;
|
|
54
|
-
|
|
55
|
+
const isLive = event.remote === true;
|
|
56
|
+
activeEnvironment = isLive ? "live" : "local";
|
|
57
|
+
lastSelectedWpcomSiteId = isLive ? event.wpcomSiteId : void 0;
|
|
58
|
+
if (ownerSitePath === void 0 && !isLive) {
|
|
55
59
|
ownerSitePath = event.sitePath;
|
|
56
60
|
ownerSiteName = event.siteName;
|
|
57
61
|
}
|
|
58
|
-
activeEnvironment = event.remote === true ? "live" : "local";
|
|
59
|
-
}
|
|
60
|
-
if (event.type === "environment.selected") {
|
|
61
|
-
activeEnvironment = event.environment;
|
|
62
62
|
}
|
|
63
63
|
if (event.type === "user.message" && event.source === "prompt" && !firstPrompt) {
|
|
64
64
|
firstPrompt = event.text;
|
|
@@ -85,6 +85,7 @@ async function readAiSessionSummaryFromEvents(filePath, events) {
|
|
|
85
85
|
ownerSiteName,
|
|
86
86
|
selectedSiteName,
|
|
87
87
|
activeEnvironment,
|
|
88
|
+
lastSelectedWpcomSiteId,
|
|
88
89
|
endReason,
|
|
89
90
|
eventCount
|
|
90
91
|
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rank-me-up
|
|
3
|
+
description: Run an on-page SEO audit on a WordPress site and get actionable recommendations to improve search visibility.
|
|
4
|
+
user-invokable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SEO Audit
|
|
8
|
+
|
|
9
|
+
Run an on-page SEO audit on a WordPress site to surface missing meta tags, broken heading structure, alt-text gaps, missing structured data, and indexing issues — then provide concrete, ordered fixes.
|
|
10
|
+
|
|
11
|
+
## How to Run
|
|
12
|
+
|
|
13
|
+
1. Determine which site to audit. If the user hasn't specified, ask them or use the site from the current context.
|
|
14
|
+
2. Ensure the site is running (use `site_start` if needed).
|
|
15
|
+
3. Call `rank_me_up` with the site name and path (defaults to `/`).
|
|
16
|
+
4. **Inspect the SEO toolchain already on the site** (mandatory before suggesting plugin installs — see "Check Active Plugins First" below).
|
|
17
|
+
5. Analyze the results using the interpretation guide.
|
|
18
|
+
6. Present a prioritized list of fixes — most impactful first — with the specific change to make, routed through whatever plugin is already active.
|
|
19
|
+
|
|
20
|
+
## Check Active Plugins First
|
|
21
|
+
|
|
22
|
+
The audit only sees rendered HTML — it can't tell *why* a tag is missing. Before recommending an install, find out what's already wired up. Skipping this step leads to dumb suggestions like "install Jetpack" when Jetpack is already active and just needs a module enabled.
|
|
23
|
+
|
|
24
|
+
**Step 1 — list active plugins:**
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
wp_cli: plugin list --status=active --field=name
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Look for known SEO providers:
|
|
31
|
+
|
|
32
|
+
| Slug | Plugin |
|
|
33
|
+
|------|--------|
|
|
34
|
+
| `jetpack` | Jetpack (preferred — see below) |
|
|
35
|
+
| `wordpress-seo` | Yoast SEO |
|
|
36
|
+
| `seo-by-rank-math` | Rank Math |
|
|
37
|
+
| `all-in-one-seo-pack` | All in One SEO |
|
|
38
|
+
| `wp-seopress` | SEOPress |
|
|
39
|
+
| `the-seo-framework` | The SEO Framework |
|
|
40
|
+
|
|
41
|
+
**Step 2 — if Jetpack is active, list its enabled modules** (Jetpack is modular; SEO features live in the `seo-tools` module and don't run unless that specific module is on):
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
wp_cli: jetpack module list
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then map the audit findings:
|
|
48
|
+
|
|
49
|
+
- **Jetpack active + `seo-tools` module active** → don't install anything. Tell the user the issue is in *configuration* (e.g. empty default title format, missing per-post meta description). Point them at **Jetpack → Settings → Traffic → SEO Tools** in `wp-admin`.
|
|
50
|
+
- **Jetpack active + `seo-tools` module inactive** → enable the module instead of installing a new plugin:
|
|
51
|
+
```
|
|
52
|
+
wp_cli: jetpack module activate seo-tools
|
|
53
|
+
```
|
|
54
|
+
Then re-run `rank_me_up` to confirm the tags appear.
|
|
55
|
+
- **Another SEO plugin active** → don't recommend installing Jetpack on top of it (two SEO plugins fight over the same `<head>` tags). Surface the *settings page* for the active plugin instead — e.g. for Yoast: **SEO → Search Appearance**; for Rank Math: **Rank Math → Titles & Meta**.
|
|
56
|
+
- **No SEO plugin active** → recommend installing Jetpack. See "Recommended SEO Plugin" below.
|
|
57
|
+
|
|
58
|
+
**Also worth checking for non-plugin fixes:**
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
wp_cli: option get blog_public
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If it returns `0`, the site has "Discourage search engines" enabled — no plugin can override that. Fix with `wp option update blog_public 1`.
|
|
65
|
+
|
|
66
|
+
## Interpreting Results
|
|
67
|
+
|
|
68
|
+
### Title and Meta Description
|
|
69
|
+
|
|
70
|
+
| Field | Recommended | Issue |
|
|
71
|
+
|-------|-------------|-------|
|
|
72
|
+
| `title` | 30–60 chars | Missing, too short (< 20), or too long (> 70) gets truncated in SERPs |
|
|
73
|
+
| `description` | 70–160 chars | Missing means Google generates one for you, often poorly |
|
|
74
|
+
| `canonical` | Self-referencing absolute URL | Missing causes duplicate-content risk |
|
|
75
|
+
| `robots` | absent or `index, follow` | `noindex` blocks the page from search results |
|
|
76
|
+
| `viewport` | `width=device-width, initial-scale=1` | Missing breaks mobile usability ranking signal |
|
|
77
|
+
| `htmlLang` | Present (e.g. `en-US`) | Missing hurts internationalization and accessibility |
|
|
78
|
+
|
|
79
|
+
### Headings
|
|
80
|
+
|
|
81
|
+
- **Exactly 1 `h1`** per page. 0 means the page has no clear topic; 2+ dilutes the signal.
|
|
82
|
+
- **No skipped levels** (e.g. `h2 → h4`). Skipped levels confuse screen readers and weaken topical structure.
|
|
83
|
+
- The `h1` text should reflect the page's primary search intent.
|
|
84
|
+
|
|
85
|
+
### Images
|
|
86
|
+
|
|
87
|
+
- Every `img` should have an `alt` attribute. `withoutAlt` (missing entirely) is worse than `emptyAlt` (decorative).
|
|
88
|
+
- Empty `alt=""` is correct only for decorative images. Content images need descriptive alt text.
|
|
89
|
+
- Use the `missingAltUrls` list to identify which images to fix first.
|
|
90
|
+
|
|
91
|
+
### Social Tags (Open Graph & Twitter)
|
|
92
|
+
|
|
93
|
+
Required for good link previews on social media:
|
|
94
|
+
- **Open Graph**: `og:title`, `og:description`, `og:image`, `og:url`, `og:type`
|
|
95
|
+
- **Twitter**: `twitter:card` (typically `summary_large_image`), `twitter:title`, `twitter:description`, `twitter:image`
|
|
96
|
+
|
|
97
|
+
Missing OG tags don't hurt rankings directly but kill click-through from social shares.
|
|
98
|
+
|
|
99
|
+
### Structured Data (JSON-LD)
|
|
100
|
+
|
|
101
|
+
- `jsonLdCount: 0` means no rich-result eligibility. Add at minimum `WebSite` and `Organization` schema sitewide.
|
|
102
|
+
- For specific page types, add `Article`, `Product`, `FAQPage`, `BreadcrumbList`, `LocalBusiness`, etc.
|
|
103
|
+
|
|
104
|
+
### Resources
|
|
105
|
+
|
|
106
|
+
- **`robotsTxtFound: false`**: WordPress generates a virtual one — usually fine, but a real file gives more control.
|
|
107
|
+
- **`robotsTxtBlocksAll: true`**: CRITICAL — `Disallow: /` blocks all crawlers. Often a forgotten "Discourage search engines" setting in WP Admin → Reading.
|
|
108
|
+
- **`sitemapFound: false`**: Install Jetpack and enable its SEO Tools module, or enable WP core's sitemap (`/wp-sitemap.xml`). See "Recommended SEO Plugin" below.
|
|
109
|
+
|
|
110
|
+
### Links
|
|
111
|
+
|
|
112
|
+
- Many `emptyText` links (anchors with no visible text, no `aria-label`, no alt-text image) hurt accessibility and SEO. Use the `emptyTextHrefs` list to fix.
|
|
113
|
+
- A high `nofollow` count on internal links wastes link equity — internal links should generally be `dofollow`.
|
|
114
|
+
|
|
115
|
+
## Recommended SEO Plugin: Jetpack
|
|
116
|
+
|
|
117
|
+
**Only reach this section after running the "Check Active Plugins First" step and confirming no SEO plugin is active.**
|
|
118
|
+
|
|
119
|
+
**Install, activate, and enable the SEO module:**
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
wp_cli: plugin install jetpack --activate
|
|
123
|
+
wp_cli: jetpack module activate seo-tools
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**What the `seo-tools` module covers:**
|
|
127
|
+
|
|
128
|
+
- Custom page title formats (front page, post, page, archive, etc.)
|
|
129
|
+
- Custom meta descriptions per post/page
|
|
130
|
+
- Open Graph + Twitter Card tags (including `twitter:card`, `og:image`, etc.)
|
|
131
|
+
- XML sitemap at `/sitemap.xml` (auto-submitted to WordPress.com)
|
|
132
|
+
- Verification tag support (Google, Bing, Pinterest, Yandex)
|
|
133
|
+
|
|
134
|
+
**Plan note:** Jetpack's *Advanced SEO Tools* (custom title formats + per-post meta descriptions) require a paid Jetpack plan (Security, Complete, or the standalone Jetpack SEO add-on). Basic social meta + sitemap are available on the free tier. If the user is on free and needs per-post meta descriptions, tell them upfront so they can decide whether to upgrade.
|
|
135
|
+
|
|
136
|
+
**Fallback:** Only suggest alternatives (Yoast SEO, Rank Math, AIOSEO) if the user explicitly rejects Jetpack.
|
|
137
|
+
|
|
138
|
+
## Common WordPress Recommendations
|
|
139
|
+
|
|
140
|
+
Based on the metrics, suggest specific actions. Route each fix through whatever you discovered in "Check Active Plugins First" — don't recommend installing a plugin that's already active or telling the user to configure one that isn't.
|
|
141
|
+
|
|
142
|
+
- **Missing title/description**:
|
|
143
|
+
- *Jetpack active + `seo-tools` on*: user needs to *configure* title formats at **Jetpack → Settings → Traffic → SEO Tools** and fill meta descriptions per-post (paid plan required for per-post).
|
|
144
|
+
- *Jetpack active + `seo-tools` off*: `wp_cli: jetpack module activate seo-tools`.
|
|
145
|
+
- *Another SEO plugin active*: point to its title/meta settings page — don't install Jetpack on top.
|
|
146
|
+
- *Nothing active*: install Jetpack (see "Recommended SEO Plugin" above).
|
|
147
|
+
- *Plugins refused*: set the title via theme `wp_title()` and description via a `<meta>` tag in `header.php` / a block theme template part.
|
|
148
|
+
- **Missing canonical**: Any SEO plugin (Jetpack, Yoast, Rank Math, AIOSEO) handles this automatically once active. Without one, add `<link rel="canonical" href="<?php echo esc_url( wp_get_canonical_url() ); ?>">` to the head.
|
|
149
|
+
- **Wrong h1 count**: Audit the active block theme's templates — site title is often wrapped in an `h1` on every page. Use `h2` for the site title on inner pages, reserve `h1` for the page title. This is a theme fix; no SEO plugin will correct it.
|
|
150
|
+
- **Missing alt text**: Content fix, not a plugin fix. Run `wp media list --field=ID` and update via `wp post update`, or train content editors to add alt text on upload.
|
|
151
|
+
- **Missing OG / Twitter tags**:
|
|
152
|
+
- *Jetpack active + `seo-tools` on*: OG/Twitter should already be emitted — re-check the HTML; if still missing, inspect for a conflicting plugin stripping them.
|
|
153
|
+
- *Jetpack active + `seo-tools` off*: enable the module.
|
|
154
|
+
- *Another SEO plugin active*: enable its social-meta feature (Yoast: **SEO → Social**; Rank Math: **Rank Math → Titles & Meta → Social Meta**).
|
|
155
|
+
- *Nothing active*: install Jetpack, or hook into `wp_head` to emit tags manually.
|
|
156
|
+
- **No JSON-LD**: WordPress core does not emit JSON-LD. Jetpack adds basic `WebSite` / `SearchAction` markup when the `seo-tools` module is on. For richer schemas (Product, FAQ, Article, BreadcrumbList) use a dedicated schema plugin (e.g. Yoast/Rank Math handle these) or custom `wp_head` output.
|
|
157
|
+
- **`robotsTxtBlocksAll: true`**: Run `wp_cli: option update blog_public 1` to undo the "Discourage search engines" setting. No plugin can override this.
|
|
158
|
+
- **Missing sitemap**: Verify `blog_public` is `1` first. Then check `/wp-sitemap.xml` (WP core since 5.5) or `/sitemap.xml` (Jetpack). If Jetpack is active but the sitemap is missing, enable the `seo-tools` module. If another SEO plugin is active, it may have disabled core sitemaps in favor of its own — check its sitemap settings.
|
|
159
|
+
- **Missing viewport meta**: Theme bug — add `<meta name="viewport" content="width=device-width, initial-scale=1">` in the `<head>`. Not a plugin concern.
|
|
160
|
+
- **Missing `htmlLang`**: Theme should output `<html <?php language_attributes(); ?>>` (block themes do this automatically; classic themes need it added). Not a plugin concern.
|
|
161
|
+
|
|
162
|
+
## Important Notes
|
|
163
|
+
|
|
164
|
+
- This is an **on-page audit of a local dev site**. It does NOT measure rankings, traffic, backlinks, or Core Web Vitals (use `need_for_speed` for performance signals).
|
|
165
|
+
- Search engines won't crawl a local Studio site, so this is purely about preparing the site for production. Pair fixes with `site_push` or `preview_create` to validate on a public URL.
|
|
166
|
+
- For content-quality SEO (keyword targeting, search intent, content depth), the audit can only flag structural gaps — not whether the content is *good*. Combine with editorial review.
|
|
@@ -27,6 +27,10 @@ After the user provides the name, use AskUserQuestion for:
|
|
|
27
27
|
|
|
28
28
|
Call `site_create` with the provided name and use the layout preference to guide all subsequent design decisions.
|
|
29
29
|
|
|
30
|
+
## After site_create returns
|
|
31
|
+
|
|
32
|
+
The turn immediately after `site_create` is the biggest source of perceived hangs. Acknowledge the site in ≤2 lines of prose, then make your next tool call a small one — `site_info`, or a single ≤50-line first `Write`. Do NOT scaffold the theme, chain multiple Writes, or write a long design-plan essay in this turn. Grow the build across many small turns (see the "Working cadence" section of the system prompt).
|
|
33
|
+
|
|
30
34
|
## When to Skip Discovery
|
|
31
35
|
|
|
32
36
|
Do NOT ask questions if:
|