From 8afea22160eeaa0ad4750a73b83a508cf8dc9cb6 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 20 Feb 2026 14:30:32 +0100 Subject: [PATCH 01/12] Add `studio ai` CLI command with interactive AI agent Introduces an AI-powered WordPress assistant accessible via `studio ai`. Uses the Claude Agent SDK with a TUI (pi-tui) for interactive conversations. Features: - REPL chat interface with markdown rendering and bordered editor - MCP tools for site management (list, info, start, stop, WP-CLI) - WordPress development skills via SDK plugins (blocks, themes, patterns) - BYOK authentication with saved API key - Session resume across conversation turns - Escape to interrupt, Ctrl+C to exit - Human-readable tool status in spinner - Tool result output display Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/agent.ts | 89 + apps/cli/ai/plugin/.claude-plugin/plugin.json | 4 + .../ai/plugin/skills/creating-blocks/SKILL.md | 34 + .../references/dynamic-vs-static.md | 17 + .../references/file-templates.md | 77 + .../references/inner-blocks.md | 13 + .../ai/plugin/skills/creating-themes/SKILL.md | 122 + .../references/card-layouts.md | 39 + .../ai/plugin/skills/editing-blocks/SKILL.md | 26 + .../editing-blocks/references/inner-blocks.md | 13 + .../ai/plugin/skills/editing-themes/SKILL.md | 20 + .../skills/generating-patterns/SKILL.md | 141 ++ apps/cli/ai/system-prompt.ts | 20 + apps/cli/ai/tools.ts | 228 ++ apps/cli/ai/ui.ts | 413 ++++ apps/cli/commands/ai.ts | 106 + apps/cli/index.ts | 3 + apps/cli/lib/appdata.ts | 24 +- apps/cli/package.json | 3 + apps/cli/vite.config.ts | 10 + package-lock.json | 2182 +++++++++-------- tools/common/logger-actions.ts | 5 + 22 files changed, 2606 insertions(+), 983 deletions(-) create mode 100644 apps/cli/ai/agent.ts create mode 100644 apps/cli/ai/plugin/.claude-plugin/plugin.json create mode 100644 apps/cli/ai/plugin/skills/creating-blocks/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md create mode 100644 apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md create mode 100644 apps/cli/ai/plugin/skills/creating-blocks/references/inner-blocks.md create mode 100644 apps/cli/ai/plugin/skills/creating-themes/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md create mode 100644 apps/cli/ai/plugin/skills/editing-blocks/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md create mode 100644 apps/cli/ai/plugin/skills/editing-themes/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/generating-patterns/SKILL.md create mode 100644 apps/cli/ai/system-prompt.ts create mode 100644 apps/cli/ai/tools.ts create mode 100644 apps/cli/ai/ui.ts create mode 100644 apps/cli/commands/ai.ts diff --git a/apps/cli/ai/agent.ts b/apps/cli/ai/agent.ts new file mode 100644 index 0000000000..3639747194 --- /dev/null +++ b/apps/cli/ai/agent.ts @@ -0,0 +1,89 @@ +import path from 'path'; +import { query, type Query } from '@anthropic-ai/claude-agent-sdk'; +import { buildSystemPrompt } from 'cli/ai/system-prompt'; +import { createStudioTools } from 'cli/ai/tools'; + +export interface AskUserQuestion { + question: string; + options: { label: string; description: string }[]; +} + +export interface AiAgentConfig { + prompt: string; + apiKey: string; + maxTurns?: number; + resume?: string; + onAskUser?: ( questions: AskUserQuestion[] ) => Promise< Record< string, string > >; +} + +export interface AiAgentResult { + sessionId: string; + success: boolean; +} + +function buildEnv( apiKey: string ): Record< string, string > { + const env: Record< string, string > = {}; + for ( const [ key, value ] of Object.entries( process.env ) ) { + if ( value !== undefined ) { + env[ key ] = value; + } + } + env.ANTHROPIC_API_KEY = apiKey; + return env; +} + +/** + * Start the AI agent and return the Query object. + * Caller can iterate messages with `for await` and call `interrupt()` to stop. + */ +export function startAiAgent( config: AiAgentConfig ): Query { + const { prompt, apiKey, maxTurns = 10, resume, onAskUser } = config; + + const pluginPath = path.resolve( __dirname, 'plugin' ); + + return query( { + prompt, + options: { + env: buildEnv( apiKey ), + systemPrompt: { + type: 'preset', + preset: 'claude_code', + append: buildSystemPrompt(), + }, + mcpServers: { + studio: createStudioTools(), + }, + plugins: [ { type: 'local', path: pluginPath } ], + maxTurns, + cwd: process.cwd(), + permissionMode: 'default', + canUseTool: async ( toolName, input ) => { + if ( toolName === 'AskUserQuestion' && onAskUser ) { + const typedInput = input as { + questions?: AskUserQuestion[]; + answers?: Record< string, string >; + }; + const questions = typedInput.questions ?? []; + const answers = await onAskUser( questions ); + return { + behavior: 'allow' as const, + updatedInput: { ...input, answers }, + }; + } + return { behavior: 'allow' as const, updatedInput: input }; + }, + allowedTools: [ + 'mcp__studio__*', + 'Skill', + 'Read', + 'Write', + 'Edit', + 'Bash', + 'Glob', + 'Grep', + ], + model: 'claude-opus-4-6', + resume, + }, + } ); +} diff --git a/apps/cli/ai/plugin/.claude-plugin/plugin.json b/apps/cli/ai/plugin/.claude-plugin/plugin.json new file mode 100644 index 0000000000..5874f8fe91 --- /dev/null +++ b/apps/cli/ai/plugin/.claude-plugin/plugin.json @@ -0,0 +1,4 @@ +{ + "name": "studio-wp", + "description": "WordPress development skills for Studio AI" +} diff --git a/apps/cli/ai/plugin/skills/creating-blocks/SKILL.md b/apps/cli/ai/plugin/skills/creating-blocks/SKILL.md new file mode 100644 index 0000000000..6c75af501a --- /dev/null +++ b/apps/cli/ai/plugin/skills/creating-blocks/SKILL.md @@ -0,0 +1,34 @@ +--- +name: creating-blocks +description: Templates and guidelines for creating new WordPress blocks from scratch — load this before generating block files +--- + +## When to use me + +Use this skill when creating a new block from scratch. +Do not use this skill when modifying an existing block. + +## Generating Blocks Instructions + +- Always use plain JS for the view.js file +- Always use the current folder to generate the block +- Always remove space before the PHP opening tag, nor leave more than one empty new line at the end of any file +- Always use block props to make sure the block is selectable in the canvas +- Always use the lowercase for block name slugs +- Never close the last PHP opening tag, you always leave it open +- Never suggest or generate code outside the specification, such as REST controllers, PHP classes, or other code that is not part of a standard WordPress block +- Prefer building blocks that have the same behaviour on the frontend and the backend, unless the user asks for a block that is specifically different on the frontend +- Do not opt for placeholders in the editor +- Be proactive in adding inspector or block toolbar controls that can make the block more interactive and customizable +- Never redeclare functions in PHP and always guard with function_exists +- Do not create additional markdown files with documentation or installation instructions +- Make sure the block is registered correctly so that it renders on the front end +- Use the InnerBlocks component from @wordpress/block-editor as much as possible. + +## Reference Files + +Before generating block files, read the relevant references from the `references/` directory next to this skill file. + +- **`references/file-templates.md`** — REQUIRED: read this before generating any block files. Contains templates for all 11 block file types (block.json, edit.js, save.js, render.php, etc.) +- **`references/inner-blocks.md`** — read this if the block uses InnerBlocks or child blocks +- **`references/dynamic-vs-static.md`** — read this if you need to decide between a dynamic or static rendering approach diff --git a/apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md b/apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md new file mode 100644 index 0000000000..f30828fb83 --- /dev/null +++ b/apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md @@ -0,0 +1,17 @@ +# Dynamic vs Static Blocks + +A block can be either dynamic or static: + +**Static Block:** +- Output saved in post-content +- Uses save.js +- No render.php +- "render" not in block.json +- Best for fixed content + +**Dynamic Block:** +- Output rendered by PHP at runtime +- Uses render.php +- No save.js +- "render": "file:./render.php" in block.json +- Best for live or database-driven content diff --git a/apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md b/apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md new file mode 100644 index 0000000000..2fb943e113 --- /dev/null +++ b/apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md @@ -0,0 +1,77 @@ +# Block File Templates + +### readme.md +Purpose: WordPress plugin readme with metadata and documentation. +- Follow WordPress plugin readme format +- Include the plugin header with Contributors, Tags, License +- Provide Description, Installation, FAQ, Changelog sections + +### block-slug.php +Purpose: Main WordPress plugin file that registers the block. +- Include WordPress plugin header +- Guard with ABSPATH check +- Register a block with register_block_type() +- Always guard functions with function_exists() +- Never close the final PHP tag +- No spaces before `` +- Use the allowedBlocks prop to restrict which blocks can be inserted as children +- Set orientation="horizontal" when inner blocks are displayed horizontally +- Use template prop to define a pre-filled block structure +- Combine template with templateLock="all" to prevent any changes, or templateLock="insert" to prevent additions but allow reordering +- Use defaultBlock with directInsert={true} to auto-insert a specific block type when the appender is clicked +- Define block relationships in block.json: use parent for direct descendants only, ancestor for any level +- For advanced cases, consider useInnerBlocksProps hook instead of the component diff --git a/apps/cli/ai/plugin/skills/creating-themes/SKILL.md b/apps/cli/ai/plugin/skills/creating-themes/SKILL.md new file mode 100644 index 0000000000..40bd915a6b --- /dev/null +++ b/apps/cli/ai/plugin/skills/creating-themes/SKILL.md @@ -0,0 +1,122 @@ +--- +name: creating-themes +description: Guidelines for creating new WordPress block themes from scratch — load this before generating theme files +--- + +## When to use me + +Use this skill when creating a new theme from scratch. +Do not use this skill when modifying an existing theme. + +## General Rules + +Follow these rules carefully unless the user explicitly requests otherwise: + +- **Focus on the home page first**: Create a beautiful, image-rich home page as the centerpiece of the initial theme. The home page should showcase the theme's aesthetic vision with compelling visuals, strong typography, and engaging layout. Put your creative energy here. +- **Minimal template set**: On initial theme creation, only create `index.html`. Avoid creating additional templates like `single.html`, `page.html`, `archive.html`, etc. unless the user specifically requests them. Keep the initial scope focused. +- **Prefer templates over patterns**: Default to creating templates rather than patterns. Templates are easier for users to find, preview, and manipulate in the Site Editor. Only create patterns when the user explicitly requests pattern creation. +- **Pattern visibility rule**: If you must create a pattern (because the user requested it), always include that pattern's content in a new or existing template so the user can immediately preview it. Patterns that exist only in the `patterns/` directory without being used in any template are hard to discover. + +## Generating Theme Instructions + +- Always use the current working directory — do not create a subdirectory for the theme +- Always include style.css with the required WordPress theme header comment +- Always create a valid theme.json (version 3) as the central configuration +- Always create block templates in templates/ and template parts in parts/ +- Never use emojis in any generated content — not in headings, paragraphs, button text, or any text +- Never close the final PHP tag in functions.php +- Guard functions with function_exists() checks +- Use `enqueue_block_assets` hook for fonts (not `wp_enqueue_scripts`) to ensure they load in both front-end and editor +- Define colors, typography, and spacing in theme.json — not in CSS where possible +- Make the theme FSE-compatible (Full Site Editing) +- Avoid patterns unless the user explicitly asks to create them — prefer templates instead +- Keep functions.php minimal — enqueuing assets, registering patterns, and adding theme support + +## Theme File Structure + +``` +style.css # Required — theme header comment and base styles +theme.json # Central configuration (colors, typography, spacing, layout) +functions.php # Minimal — enqueue assets, register patterns, add theme support +templates/ # Block templates + index.html # Required — fallback template + single.html # Single post + page.html # Page + archive.html # Archive + 404.html # Not found + home.html # Blog home (optional) + search.html # Search results (optional) +parts/ # Reusable template parts + header.html # Site header + footer.html # Site footer +patterns/ # Block patterns for reusable sections +assets/ # Static assets (images, local fonts) +``` + +## style.css Header (required) + +```css +/* +Theme Name: Theme Name +Theme URI: https://example.com +Author: Author Name +Description: Theme description +Version: 1.0.0 +Requires at least: 6.0 +Tested up to: 6.5 +Requires PHP: 8.0 +License: GNU General Public License v2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Text Domain: theme-slug +*/ +``` + +## theme.json Essentials + +- Use version 3 +- Define settings: colors (palette), typography (fontFamilies, fontSizes), spacing (units, spacingSizes), layout (contentSize, wideSize) +- Define styles: global styles and block-specific overrides +- Use appearanceTools: true to enable border, typography, spacing, and color controls +- Register custom template parts with their area (header, footer, uncategorized) +- Use style variations to offer different design options +- Add custom block styles when needed to extend core blocks + +## Google Fonts + +ALWAYS use `enqueue_block_assets` hook (not `wp_enqueue_scripts`) to ensure fonts load in BOTH the front-end AND block editor: + +```php +function theme_fonts() { + wp_enqueue_style( + 'theme-fonts', + 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap', + array(), + null + ); +} +add_action( 'enqueue_block_assets', 'theme_fonts' ); +``` + +## Generating a landing page + +- **YOU DECIDE** which sections/blocks best serve this specific site. Do not follow a rigid template. + Consider the user site type and audience to determine the optimal page structure. + Examples: + - A portfolio might need a full-bleed project gallery + - A SaaS might need feature grids + - A restaurant might need a reservations widget + - An agency might need case study cards +- **Always include a header and footer**, but the sections in between are your creative choice. +- Treat every block as a **self-contained section** (one dominant semantic wrapper) whose markup will live inside its own file. +- Keep copy **realistic but placeholder-friendly**; never reference real clients or brands. +- Favor **semantic HTML**, class-based styling hooks, and **shallow DOM trees** that respond well on mobile. +- **Section margin reset**: Add `"style":{"spacing":{"margin":{"top":"0"}}}` to every top-level Group block that wraps a landing page section. This overrides WordPress's default top margin on direct children of `.wp-site-blocks` and can be easily adjusted by users in the editor. +- **Section layout widths**: Hero sections, header groups, cover blocks, and feature grids should use `"align":"wide"` or `"align":"full"` rather than defaulting to narrow content width. Only use default (content) alignment for text-heavy reading sections. +- Do **not** use ``; output the full expanded markup inside each block. +- **No decorative HTML comments**: Never insert section-labeling comments like `` or `` in templates, template parts, or patterns. Only WordPress block comments (``) are allowed. + +## Reference Files + +Before generating theme files, read the relevant references from the `references/` directory next to this skill file. + +- **`references/card-layouts.md`** — read this if the theme uses card-based layouts (equal-height columns, grid cards with CTAs) diff --git a/apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md b/apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md new file mode 100644 index 0000000000..198e6b4b92 --- /dev/null +++ b/apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md @@ -0,0 +1,39 @@ +# Card Layouts in Rows + +For equal-height, equal-width cards (with optional bottom-aligned CTAs), use this structure unless the user specifies otherwise: + +``` +Columns (className: "equal-cards") + └── Column + verticalAlignment: "stretch" + width: "X%" where X = 100 / number_of_cards (e.g., 2 cards = 50%, 3 cards = 33.33%, 4 cards = 25%) + └── Group [card wrapper] + └── [content: headings, paragraphs, images*, lists] + └── (optional) Buttons (className: "cta-bottom") +``` + +**Width rule**: All cards in a row MUST have equal width. Calculate each column's width as `100% / number_of_cards` (e.g., 3 cards = 33.33% each). The sum of all column widths must equal exactly 100% - never exceed the parent element width. + +*Images in cards: `style="height:200px;object-fit:cover;width:100%"` + +**Required CSS** (style.css): +```css +.equal-cards > .wp-block-column { + display: flex; + flex-direction: column; + flex-grow: 0; + flex-shrink: 0; +} +.equal-cards > .wp-block-column > .wp-block-group { + display: flex; + flex-direction: column; + flex-grow: 1; +} +``` +If present, ensure bottom-aligned CTAs unless otherwise specified: +```css +.equal-cards .cta-bottom { + margin-top: auto; + justify-content: center; +} +``` diff --git a/apps/cli/ai/plugin/skills/editing-blocks/SKILL.md b/apps/cli/ai/plugin/skills/editing-blocks/SKILL.md new file mode 100644 index 0000000000..552822185a --- /dev/null +++ b/apps/cli/ai/plugin/skills/editing-blocks/SKILL.md @@ -0,0 +1,26 @@ +--- +name: editing-blocks +description: Guidelines for modifying existing WordPress blocks — load this before editing block files +--- + +## When to use me + +Use this skill when modifying an existing block (files already exist in the workspace). +Do not use this skill when creating a new block from scratch. + +## Editing Guidelines + +- Read all existing block files before making changes — understand the current architecture, attributes, and rendering approach +- Make minimal, targeted changes — only modify what the user requested +- Only touch files that need to change — do not rewrite unrelated files +- Use the `edit` tool for targeted changes; only use `write` when replacing more than 50% of a file +- Do not convert between static and dynamic blocks, or change the block name/slug, unless the user explicitly asks +- When adding or modifying attributes, update all relevant files (block.json, edit.js, and save.js or render.php) +- After changes, verify the block is still registered correctly and renders on the front end +- Use the InnerBlocks component from @wordpress/block-editor as much as possible. + +## Reference Files + +Before editing block files, read the relevant references from the `references/` directory next to this skill file. + +- **`references/inner-blocks.md`** — read this if the block uses InnerBlocks or child blocks diff --git a/apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md b/apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md new file mode 100644 index 0000000000..f90cc9ab92 --- /dev/null +++ b/apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md @@ -0,0 +1,13 @@ +# Inner Blocks + +Use the InnerBlocks component from @wordpress/block-editor as much as possible. +- Import InnerBlocks and useBlockProps from @wordpress/block-editor +- A block can only contain ONE InnerBlocks component +- Always wrap InnerBlocks in a div with blockProps spread: `
` +- Use the allowedBlocks prop to restrict which blocks can be inserted as children +- Set orientation="horizontal" when inner blocks are displayed horizontally +- Use template prop to define a pre-filled block structure +- Combine template with templateLock="all" to prevent any changes, or templateLock="insert" to prevent additions but allow reordering +- Use defaultBlock with directInsert={true} to auto-insert a specific block type when the appender is clicked +- Define block relationships in block.json: use parent for direct descendants only, ancestor for any level +- For advanced cases, consider useInnerBlocksProps hook instead of the component diff --git a/apps/cli/ai/plugin/skills/editing-themes/SKILL.md b/apps/cli/ai/plugin/skills/editing-themes/SKILL.md new file mode 100644 index 0000000000..231f38234b --- /dev/null +++ b/apps/cli/ai/plugin/skills/editing-themes/SKILL.md @@ -0,0 +1,20 @@ +--- +name: editing-themes +description: Guidelines for modifying existing WordPress block themes — load this before editing theme files +--- + +## When to use me + +Use this skill when modifying an existing theme (files already exist in the workspace). +Do not use this skill when creating a new theme from scratch. + +## Editing Guidelines + +- Read theme.json, style.css, and functions.php before making changes — understand the current configuration, design tokens, and registered assets +- Make minimal, targeted changes — only modify what the user requested +- Only touch files that need to change — do not rewrite unrelated templates or patterns +- Use the `edit` tool for targeted changes; only use `write` when replacing more than 50% of a file +- When modifying colors, typography, or spacing, update theme.json — not CSS — unless the change cannot be expressed in theme.json +- When adding or modifying template parts, ensure they are registered in theme.json if needed +- Preserve the theme's existing design direction and aesthetic unless the user explicitly asks to change it +- After changes, verify templates still reference valid template parts and patterns diff --git a/apps/cli/ai/plugin/skills/generating-patterns/SKILL.md b/apps/cli/ai/plugin/skills/generating-patterns/SKILL.md new file mode 100644 index 0000000000..bcb6670000 --- /dev/null +++ b/apps/cli/ai/plugin/skills/generating-patterns/SKILL.md @@ -0,0 +1,141 @@ +--- +name: generating-patterns +description: Guidelines and examples for generating WordPress block patterns — load this when creating patterns for themes +--- + +## When to use me + +Use this skill when generating block patterns for a theme. +Load this skill alongside `creating-themes` or `editing-themes` when patterns are part of the work. + +## Pattern Generation Rules + +- Place pattern files in the `patterns/` directory +- Each pattern file must start with a PHP comment header registering the pattern +- Use descriptive, kebab-case filenames (e.g., `hero-split.php`, `faq-accordion.php`) +- Never use emojis in any pattern content +- Keep copy realistic but placeholder-friendly — never reference real clients or brands +- Alternate between light and dark section backgrounds to create visual rhythm +- Patterns must be self-contained — do not use `` or assume external context + +## Pattern File Header (required) + +Every pattern file must begin with this PHP comment block: + +```php + +``` + +- `Title`: Human-readable name shown in the inserter +- `Slug`: Must be `theme-slug/pattern-name` using the theme's text domain +- `Categories`: Comma-separated list (use WordPress defaults: `featured`, `banner`, `text`, `gallery`, `call-to-action`, `about`, `team`, `testimonials`, `contact`, `footer`, `header`) +- `Keywords`: Comma-separated search terms for discoverability +- `Block Types`: Optional — restricts where the pattern appears + +## Pattern Set for Landing Pages + +When generating patterns for a landing page theme, include a varied set that covers the full page flow. Choose patterns that match the site type and audience — not every site needs the same set. + +Recommended baseline (adapt per site type): + +1. **Hero section** — the first thing visitors see; must set the tone and include a primary CTA +2. **Social proof / logos** — build trust early with client logos or partner badges +3. **Feature grid** — highlight key offerings or services +4. **Content with media** — pair text with imagery to explain the value proposition +5. **Testimonials or case studies** — reinforce credibility +6. **FAQ** — address common objections +7. **Final CTA** — closing section with a clear conversion action + +## CTA Guidelines + +- Every landing page must have at least two CTA patterns: one in the hero, one as a closing section +- Use `wp:buttons` with clear, action-oriented text ("Get Started", "Book a Demo", "View Portfolio") +- Style CTAs to stand out — use the theme's accent color, generous padding, and prominent placement +- Avoid vague labels like "Click Here" or "Learn More" when a specific action is available + +## Layout Examples + +### Hero with left text / right image + +Two-column split: heading + paragraph + CTA button on the left, full-height image on the right. Use `wp:columns` with a 55/45 or 60/40 split. The text column gets vertical centering; the image column uses `object-fit: cover` at full height. + +``` +Columns (align: full) + └── Column (width: 55%, verticalAlignment: center) + │ └── Heading (h1) + │ └── Paragraph + │ └── Buttons + └── Column (width: 45%) + └── Image (style: height 100%, object-fit: cover) +``` + +### Z-pattern layout + +Alternate the position of text and image across consecutive sections to create a natural Z reading flow. Odd sections place text left / image right; even sections flip to image left / text right. + +``` +Section 1 — Columns (align: wide) + └── Column (text) | Column (image) + +Section 2 — Columns (align: wide) + └── Column (image) | Column (text) + +Section 3 — Columns (align: wide) + └── Column (text) | Column (image) +``` + +Use alternating background colors (light/dark) between sections to reinforce separation. + +### 3-column feature grid + +Three equal-width columns, each containing an icon or image, a heading, a paragraph, and an optional CTA. Use the `equal-cards` pattern from the card-layouts reference. + +``` +Columns (className: "equal-cards", align: wide) + └── Column (width: 33.33%, verticalAlignment: stretch) + │ └── Group + │ └── Image (icon or illustration) + │ └── Heading (h3) + │ └── Paragraph + │ └── Buttons (className: "cta-bottom") [optional] + └── Column (width: 33.33%, verticalAlignment: stretch) + │ └── Group + │ └── ... + └── Column (width: 33.33%, verticalAlignment: stretch) + └── Group + └── ... +``` + +### Accordion FAQ + +Use the `wp:details` block for collapsible FAQ items inside a constrained-width Group. + +``` +Group (align: wide, layout: constrained) + └── Heading (h2, "Frequently Asked Questions") + └── Details (summary: "Question one?") + │ └── Paragraph (answer) + └── Details (summary: "Question two?") + │ └── Paragraph (answer) + └── Details (summary: "Question three?") + └── Paragraph (answer) +``` + +## Site-Type Pattern Suggestions + +Not every site needs the same patterns. Adapt the set to the context: + +- **Portfolio**: Hero with full-bleed image, project gallery grid, about/bio section, contact CTA +- **SaaS / Product**: Hero split, logo bar, feature grid, pricing table, testimonials, FAQ, final CTA +- **Restaurant / Local**: Hero with cover image, menu highlights, hours/location, reservation CTA, gallery +- **Agency / Studio**: Hero with reel or case study, services grid, case study cards, team, contact +- **Blog / Magazine**: Hero with featured post, category grid, newsletter signup, recent posts +- **E-commerce**: Hero with product showcase, category grid, bestsellers, testimonials, promo banner diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts new file mode 100644 index 0000000000..6636d57f51 --- /dev/null +++ b/apps/cli/ai/system-prompt.ts @@ -0,0 +1,20 @@ +export function buildSystemPrompt(): string { + return `You are a WordPress development assistant integrated with WordPress Studio, a local WordPress development environment. + +You have access to Studio-specific tools to manage WordPress sites: +- studio_list_sites: List all local WordPress sites with their status +- studio_get_site_info: Get details about a specific site by name or path +- studio_start_site: Start a WordPress site +- studio_stop_site: Stop a WordPress site +- studio_run_wp_cli: Run WP-CLI commands on a site (install plugins, manage options, query the database, etc.) + +You also have standard file system and shell tools for working with WordPress code. + +Guidelines: +- When working with WordPress sites, always check which sites exist first using studio_list_sites. +- Before running WP-CLI commands, ensure the target site is running using studio_start_site if needed. +- For file operations on WordPress sites, use the site's path from studio_get_site_info. +- Do NOT modify WordPress core files directly. Use WP-CLI or the Studio tools instead. +- When creating themes or plugins, follow WordPress coding standards. +- Be concise in your responses and focus on actionable results.`; +} diff --git a/apps/cli/ai/tools.ts b/apps/cli/ai/tools.ts new file mode 100644 index 0000000000..9219565908 --- /dev/null +++ b/apps/cli/ai/tools.ts @@ -0,0 +1,228 @@ +import { tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; +import { z } from 'zod'; +import { readAppdata, getSiteByFolder, getSiteUrl, type SiteData } from 'cli/lib/appdata'; +import { connect, disconnect } from 'cli/lib/pm2-manager'; +import { isSiteRunning } from 'cli/lib/site-utils'; +import { keepSqliteIntegrationUpdated } from 'cli/lib/sqlite-integration'; +import { + isServerRunning, + startWordPressServer, + stopWordPressServer, + sendWpCliCommand, +} from 'cli/lib/wordpress-server-manager'; +import { Logger } from 'cli/logger'; + +async function findSiteByName( name: string ): Promise< SiteData | undefined > { + const appdata = await readAppdata(); + return appdata.sites.find( ( site ) => site.name.toLowerCase() === name.toLowerCase() ); +} + +async function resolveSite( nameOrPath: string ): Promise< SiteData > { + const siteByName = await findSiteByName( nameOrPath ); + if ( siteByName ) { + return siteByName; + } + return getSiteByFolder( nameOrPath ); +} + +function errorResult( message: string ) { + return { + content: [ { type: 'text' as const, text: message } ], + isError: true, + }; +} + +function textResult( text: string ) { + return { + content: [ { type: 'text' as const, text } ], + }; +} + +const listSitesTool = tool( + 'site_list', + 'Lists all WordPress sites managed by Studio with their name, path, URL, PHP version, and running status.', + z.object( {} ), + async () => { + try { + const appdata = await readAppdata(); + if ( appdata.sites.length === 0 ) { + return textResult( 'No sites found.' ); + } + + try { + await connect(); + const sites = []; + for ( const site of appdata.sites ) { + const running = await isSiteRunning( site ); + sites.push( { + name: site.name, + path: site.path, + url: getSiteUrl( site ), + phpVersion: site.phpVersion, + running, + } ); + } + return textResult( JSON.stringify( sites, null, 2 ) ); + } finally { + await disconnect(); + } + } catch ( error ) { + return errorResult( `Failed to list sites: ${ error instanceof Error ? error.message : String( error ) }` ); + } + } +); + +const getSiteInfoTool = tool( + 'site_info', + 'Gets detailed information about a specific WordPress site by name or path, including its running status, URL, PHP version, and custom domain.', + z.object( { + nameOrPath: z.string().describe( 'The site name or file system path to the site' ), + } ), + async ( args ) => { + try { + const site = await resolveSite( args.nameOrPath ); + let running = false; + + try { + await connect(); + running = await isSiteRunning( site ); + } finally { + await disconnect(); + } + + const info = { + name: site.name, + path: site.path, + url: getSiteUrl( site ), + phpVersion: site.phpVersion, + running, + customDomain: site.customDomain || null, + enableHttps: site.enableHttps || false, + }; + + return textResult( JSON.stringify( info, null, 2 ) ); + } catch ( error ) { + return errorResult( `Failed to get site info: ${ error instanceof Error ? error.message : String( error ) }` ); + } + } +); + +const startSiteTool = tool( + 'site_start', + 'Starts a WordPress site by name or path. The site must already exist in Studio.', + z.object( { + nameOrPath: z.string().describe( 'The site name or file system path to the site' ), + } ), + async ( args ) => { + try { + const site = await resolveSite( args.nameOrPath ); + + try { + await connect(); + + const runningProcess = await isServerRunning( site.id ); + if ( runningProcess ) { + return textResult( `Site "${ site.name }" is already running at ${ getSiteUrl( site ) }` ); + } + + await keepSqliteIntegrationUpdated( site.path ); + + const logger = new Logger< string >(); + await startWordPressServer( site, logger ); + + return textResult( `Site "${ site.name }" started at ${ getSiteUrl( site ) }` ); + } finally { + await disconnect(); + } + } catch ( error ) { + return errorResult( `Failed to start site: ${ error instanceof Error ? error.message : String( error ) }` ); + } + } +); + +const stopSiteTool = tool( + 'site_stop', + 'Stops a running WordPress site by name or path.', + z.object( { + nameOrPath: z.string().describe( 'The site name or file system path to the site' ), + } ), + async ( args ) => { + try { + const site = await resolveSite( args.nameOrPath ); + + try { + await connect(); + + const runningProcess = await isServerRunning( site.id ); + if ( ! runningProcess ) { + return textResult( `Site "${ site.name }" is not running.` ); + } + + await stopWordPressServer( site.id ); + return textResult( `Site "${ site.name }" stopped.` ); + } finally { + await disconnect(); + } + } catch ( error ) { + return errorResult( `Failed to stop site: ${ error instanceof Error ? error.message : String( error ) }` ); + } + } +); + +const runWpCliTool = tool( + 'wp_cli', + 'Runs a WP-CLI command on a specific WordPress site. The site must be running. Examples: "plugin install woocommerce --activate", "option get blogname", "user list".', + z.object( { + nameOrPath: z.string().describe( 'The site name or file system path to the site' ), + command: z + .string() + .describe( + 'The WP-CLI command to run (without the "wp" prefix). Example: "plugin list --status=active"' + ), + } ), + async ( args ) => { + try { + const site = await resolveSite( args.nameOrPath ); + + try { + await connect(); + + const runningProcess = await isServerRunning( site.id ); + if ( ! runningProcess ) { + return errorResult( `Site "${ site.name }" is not running. Start it first using site_start.` ); + } + + const wpCliArgs = args.command.split( /\s+/ ); + const result = await sendWpCliCommand( site.id, wpCliArgs ); + + let output = ''; + if ( result.stdout ) { + output += result.stdout; + } + if ( result.stderr ) { + output += ( output ? '\n' : '' ) + `stderr: ${ result.stderr }`; + } + if ( result.exitCode !== 0 ) { + output += `\nExit code: ${ result.exitCode }`; + } + + return { + content: [ { type: 'text' as const, text: output || 'Command completed with no output.' } ], + isError: result.exitCode !== 0, + }; + } finally { + await disconnect(); + } + } catch ( error ) { + return errorResult( `Failed to run WP-CLI command: ${ error instanceof Error ? error.message : String( error ) }` ); + } + } +); + +export function createStudioTools() { + return createSdkMcpServer( { + name: 'studio', + version: '1.0.0', + tools: [ listSitesTool, getSiteInfoTool, startSiteTool, stopSiteTool, runWpCliTool ], + } ); +} diff --git a/apps/cli/ai/ui.ts b/apps/cli/ai/ui.ts new file mode 100644 index 0000000000..e62fd98f43 --- /dev/null +++ b/apps/cli/ai/ui.ts @@ -0,0 +1,413 @@ +import chalk from 'chalk'; +import { + TUI, + ProcessTerminal, + Editor, + Markdown, + Text, + Loader, + Container, + type Component, + type Focusable, + type EditorTheme, + type EditorOptions, + type MarkdownTheme, +} from '@mariozechner/pi-tui'; +import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; +import type { AskUserQuestion } from 'cli/ai/agent'; + +/** + * Wraps the Editor component with left/right borders and rounded corners. + * The Editor only renders top/bottom horizontal lines — this adds │ on the sides + * and ╭╮╰╯ corners for a complete box. + */ +class BorderedEditor implements Component, Focusable { + private editor: Editor; + private borderColorFn: ( text: string ) => string; + private _focused = false; + + get focused(): boolean { + return this._focused; + } + set focused( value: boolean ) { + this._focused = value; + this.editor.focused = value; + } + + set onSubmit( fn: ( ( text: string ) => void ) | undefined ) { + this.editor.onSubmit = fn; + } + + constructor( tui: TUI, theme: EditorTheme, options?: EditorOptions ) { + this.editor = new Editor( tui, theme, { ...options, paddingX: options?.paddingX ?? 1 } ); + this.borderColorFn = theme.borderColor; + } + + setText( text: string ): void { + this.editor.setText( text ); + } + + handleInput( data: string ): void { + this.editor.handleInput( data ); + } + + invalidate(): void { + this.editor.invalidate(); + } + + render( width: number ): string[] { + const innerWidth = Math.max( 1, width - 2 ); + const lines = this.editor.render( innerWidth ); + const bc = this.borderColorFn; + + return lines.map( ( line, i ) => { + if ( i === 0 ) { + return bc( '┌' ) + line + bc( '┐' ); + } + if ( i === lines.length - 1 ) { + return bc( '└' ) + line + bc( '┘' ); + } + return bc( '│' ) + line + bc( '│' ); + } ); + } +} + +const markdownTheme: MarkdownTheme = { + heading: ( text ) => chalk.bold( text ), + link: ( text ) => chalk.cyan.underline( text ), + linkUrl: ( text ) => chalk.dim( text ), + code: ( text ) => chalk.yellow( text ), + codeBlock: ( text ) => text, + codeBlockBorder: ( text ) => chalk.dim( text ), + quote: ( text ) => chalk.italic( text ), + quoteBorder: ( text ) => chalk.dim( text ), + hr: ( text ) => chalk.dim( text ), + listBullet: ( text ) => chalk.cyan( text ), + bold: ( text ) => chalk.bold( text ), + italic: ( text ) => chalk.italic( text ), + strikethrough: ( text ) => chalk.strikethrough( text ), + underline: ( text ) => chalk.underline( text ), +}; + +const editorTheme: EditorTheme = { + borderColor: ( text ) => chalk.cyan( text ), + selectList: { + selectedItem: ( text ) => chalk.inverse( text ), + item: ( text ) => text, + border: ( text ) => chalk.dim( text ), + }, +}; + +const toolDisplayNames: Record< string, string > = { + mcp__studio__site_list: 'Listing sites', + mcp__studio__site_info: 'Getting site info', + mcp__studio__site_start: 'Starting site', + mcp__studio__site_stop: 'Stopping site', + mcp__studio__wp_cli: 'Running WP-CLI', + Read: 'Reading file', + Write: 'Writing file', + Edit: 'Editing file', + Bash: 'Running command', + Glob: 'Searching files', + Grep: 'Searching code', + Skill: 'Loading skill', + Task: 'Running task', +}; + +function formatToolName( name: string ): string { + return toolDisplayNames[ name ] ?? name; +} + +export class AiChatUI { + private tui: TUI; + private editor: BorderedEditor; + private loader: Loader; + private messages: Container; + private currentResponseText = ''; + private currentMarkdown: Markdown | null = null; + private submitResolve: ( ( text: string ) => void ) | null = null; + private loaderVisible = false; + private editorVisible = false; + private interruptCallback: ( () => void ) | null = null; + + constructor() { + const terminal = new ProcessTerminal(); + this.tui = new TUI( terminal ); + + this.messages = new Container(); + this.tui.addChild( this.messages ); + + this.loader = new Loader( + this.tui, + ( str ) => chalk.cyan( str ), + ( str ) => chalk.dim( str ), + 'Thinking…' + ); + + this.editor = new BorderedEditor( this.tui, editorTheme ); + this.editor.onSubmit = ( text ) => { + const trimmed = text.trim(); + if ( trimmed && this.submitResolve ) { + const resolve = this.submitResolve; + this.submitResolve = null; + resolve( trimmed ); + } + }; + + // Ctrl+C to exit, Escape to interrupt agent + this.tui.addInputListener( ( data ) => { + if ( data === '\x03' ) { + this.stop(); + process.exit( 0 ); + } + if ( data === '\x1b' && this.interruptCallback ) { + this.interruptCallback(); + } + return undefined; + } ); + } + + start(): void { + this.tui.start(); + } + + set onInterrupt( fn: ( () => void ) | null ) { + this.interruptCallback = fn; + } + + stop(): void { + this.loader.stop(); + this.tui.stop(); + } + + waitForInput(): Promise< string > { + this.editor.setText( '' ); + this.hideLoader(); + this.showEditor(); + return new Promise( ( resolve ) => { + this.submitResolve = resolve; + } ); + } + + private addSpacer(): void { + this.messages.addChild( new Text( '', 0, 0 ) ); + } + + addUserMessage( text: string ): void { + this.addSpacer(); + this.messages.addChild( new Text( chalk.bold.cyan( '> ' ) + text, 0, 0 ) ); + this.tui.requestRender(); + } + + private showLoader(): void { + if ( ! this.loaderVisible ) { + this.tui.addChild( this.loader ); + this.loader.start(); + this.loaderVisible = true; + this.tui.requestRender(); + } + } + + private hideLoader(): void { + if ( this.loaderVisible ) { + this.loader.stop(); + this.tui.removeChild( this.loader ); + this.loaderVisible = false; + this.tui.requestRender(); + } + } + + private showEditor(): void { + if ( ! this.editorVisible ) { + this.tui.addChild( this.editor ); + this.tui.setFocus( this.editor ); + this.editorVisible = true; + this.tui.requestRender(); + } + } + + private hideEditor(): void { + if ( this.editorVisible ) { + this.tui.removeChild( this.editor ); + this.editorVisible = false; + this.tui.requestRender(); + } + } + + /** + * Begin an agent turn: hide editor, show loader, prepare response area. + */ + beginAgentTurn(): void { + this.hideEditor(); + this.showLoader(); + this.currentResponseText = ''; + this.addSpacer(); + this.currentMarkdown = new Markdown( '', 1, 0, markdownTheme ); + this.messages.addChild( this.currentMarkdown ); + } + + /** + * End an agent turn: hide loader, clean up response state. + */ + endAgentTurn(): void { + this.hideLoader(); + this.interruptCallback = null; + this.currentMarkdown = null; + this.currentResponseText = ''; + } + + showError( message: string ): void { + this.messages.addChild( new Text( chalk.red( message ), 0, 0 ) ); + this.tui.requestRender(); + } + + showInfo( message: string ): void { + this.messages.addChild( new Text( chalk.dim( message ), 0, 0 ) ); + this.tui.requestRender(); + } + + private showToolResult( message: SDKMessage & { type: 'user' } ): void { + const result = message.tool_use_result; + if ( ! result || typeof result !== 'object' ) { + return; + } + const typedResult = result as { + content?: Array< { type: string; text?: string } >; + isError?: boolean; + }; + const content = typedResult.content; + if ( ! Array.isArray( content ) ) { + return; + } + const text = content + .filter( ( block ) => block.type === 'text' && block.text ) + .map( ( block ) => block.text ) + .join( '\n' ); + if ( ! text ) { + return; + } + const maxLength = 500; + const truncated = text.length > maxLength ? text.slice( 0, maxLength ) + '…' : text; + const prefix = typedResult.isError ? chalk.red( '✗ ' ) : chalk.dim( '↳ ' ); + this.messages.addChild( new Text( prefix + chalk.dim( truncated ), 1, 0 ) ); + this.tui.requestRender(); + } + + /** + * Display questions from the agent and collect user answers. + * Called via canUseTool when the agent uses AskUserQuestion. + */ + async askUser( questions: AskUserQuestion[] ): Promise< Record< string, string > > { + this.hideLoader(); + + // Close off the current markdown block so questions appear after the text so far + this.currentMarkdown = null; + this.currentResponseText = ''; + + const answers: Record< string, string > = {}; + + for ( const q of questions ) { + // Display the question and options + this.addSpacer(); + let questionText = chalk.bold.yellow( '? ' ) + chalk.bold( q.question ); + if ( q.options.length > 0 ) { + questionText += '\n'; + questionText += q.options + .map( ( opt, i ) => chalk.dim( ` ${ i + 1 }. ` ) + opt.label + chalk.dim( ` — ${ opt.description }` ) ) + .join( '\n' ); + } + this.messages.addChild( new Text( questionText, 0, 0 ) ); + this.tui.requestRender(); + + // Collect answer via the editor + const answer = await this.waitForInput(); + this.hideEditor(); + + // Show the user's answer + this.messages.addChild( new Text( chalk.bold.cyan( '> ' ) + answer, 0, 0 ) ); + + // If user typed a number, map to option label + const num = parseInt( answer, 10 ); + if ( ! isNaN( num ) && num >= 1 && num <= q.options.length ) { + answers[ q.question ] = q.options[ num - 1 ].label; + } else { + answers[ q.question ] = answer; + } + } + + // Resume the agent turn with a fresh markdown block for subsequent text + this.showLoader(); + return answers; + } + + /** + * Process an SDK message and update the UI. + * Returns session result when the agent turn is complete. + */ + handleMessage( message: SDKMessage ): { sessionId: string; success: boolean } | undefined { + switch ( message.type ) { + case 'assistant': { + for ( const block of message.message.content ) { + if ( block.type === 'text' ) { + this.hideLoader(); + // Lazily create a new markdown block if needed (e.g. after askUser closed the previous one) + if ( ! this.currentMarkdown ) { + this.currentResponseText = ''; + this.currentMarkdown = new Markdown( '', 1, 0, markdownTheme ); + this.messages.addChild( this.currentMarkdown ); + } + // Add a line break between consecutive assistant messages + if ( this.currentResponseText && ! this.currentResponseText.endsWith( '\n' ) ) { + this.currentResponseText += '\n'; + } + this.currentResponseText += block.text; + this.currentMarkdown.setText( this.currentResponseText ); + this.tui.requestRender(); + } else if ( block.type === 'tool_use' ) { + this.showLoader(); + this.loader.setMessage( formatToolName( block.name ) ); + } + } + // Always show the loader after processing — the agent turn is still active + // and more messages are coming (next API call, tool execution, etc.) + if ( ! this.loaderVisible ) { + this.showLoader(); + this.loader.setMessage( 'Thinking…' ); + } + return undefined; + } + case 'user': { + this.showToolResult( message ); + return undefined; + } + case 'result': { + this.hideLoader(); + if ( message.subtype === 'success' ) { + this.showInfo( + `${ message.num_turns } turns · $${ message.total_cost_usd.toFixed( 4 ) }` + ); + return { sessionId: message.session_id, success: true }; + } + + // Build detailed error message + const parts: string[] = []; + if ( 'errors' in message && message.errors?.length ) { + parts.push( ...message.errors ); + } + if ( message.subtype === 'error_max_turns' ) { + parts.push( 'Reached the maximum number of turns. Use --max-turns to increase the limit.' ); + } else if ( message.subtype ) { + parts.push( `(${ message.subtype })` ); + } + if ( 'permission_denials' in message && message.permission_denials?.length ) { + for ( const denial of message.permission_denials ) { + parts.push( `Permission denied: ${ denial.tool_name }` ); + } + } + this.showError( parts.length > 0 ? parts.join( '\n' ) : 'Unknown error' ); + return { sessionId: message.session_id, success: false }; + } + } + return undefined; + } +} diff --git a/apps/cli/commands/ai.ts b/apps/cli/commands/ai.ts new file mode 100644 index 0000000000..8ab02b82e7 --- /dev/null +++ b/apps/cli/commands/ai.ts @@ -0,0 +1,106 @@ +import { password } from '@inquirer/prompts'; +import { __ } from '@wordpress/i18n'; +import { AiCommandLoggerAction as LoggerAction } from '@studio/common/logger-actions'; +import { startAiAgent } from 'cli/ai/agent'; +import { getAnthropicApiKey, saveAnthropicApiKey } from 'cli/lib/appdata'; +import { AiChatUI } from 'cli/ai/ui'; +import { Logger, LoggerError } from 'cli/logger'; +import { StudioArgv } from 'cli/types'; + +const logger = new Logger< LoggerAction >(); + +async function resolveApiKey(): Promise< string > { + const savedKey = await getAnthropicApiKey(); + if ( savedKey ) { + return savedKey; + } + + const apiKey = await password( { + message: __( 'Enter your Anthropic API key (will be saved for future use):' ), + mask: '*', + validate: ( value ) => { + if ( ! value.trim() ) { + return __( 'API key is required' ); + } + return true; + }, + } ); + + await saveAnthropicApiKey( apiKey ); + return apiKey; +} + +export async function runCommand( options: { maxTurns?: number } ): Promise< void > { + const apiKey = await resolveApiKey(); + + const ui = new AiChatUI(); + ui.start(); + + let sessionId: string | undefined; + + try { + // eslint-disable-next-line no-constant-condition + while ( true ) { + const prompt = await ui.waitForInput(); + + ui.addUserMessage( prompt ); + ui.beginAgentTurn(); + + const agentQuery = startAiAgent( { + prompt, + apiKey, + maxTurns: options.maxTurns, + resume: sessionId, + onAskUser: ( questions ) => ui.askUser( questions ), + } ); + + // Escape key interrupts the current agent turn + ui.onInterrupt = () => { + agentQuery.interrupt(); + }; + + for await ( const message of agentQuery ) { + const result = ui.handleMessage( message ); + if ( result ) { + sessionId = result.sessionId; + } + } + + ui.endAgentTurn(); + } + } finally { + ui.stop(); + } +} + +export const registerCommand = ( yargs: StudioArgv ) => { + return yargs.command( { + command: 'ai', + describe: __( 'AI-powered WordPress assistant' ), + builder: ( yargs ) => { + return yargs + .option( 'max-turns', { + type: 'number', + describe: __( 'Maximum conversation turns' ), + default: 50, + } ) + .option( 'path', { + hidden: true, + } ); + }, + handler: async ( argv ) => { + try { + await runCommand( { + maxTurns: argv.maxTurns, + } ); + } catch ( error ) { + if ( error instanceof LoggerError ) { + logger.reportError( error ); + } else { + const loggerError = new LoggerError( __( 'AI agent failed' ), error ); + logger.reportError( loggerError ); + } + } + }, + } ); +}; diff --git a/apps/cli/index.ts b/apps/cli/index.ts index 36273a003e..df1481da62 100644 --- a/apps/cli/index.ts +++ b/apps/cli/index.ts @@ -9,6 +9,7 @@ import { StatsGroup, StatsMetric } from '@studio/common/types/stats'; import { __ } from '@wordpress/i18n'; import yargs from 'yargs'; import { commandHandler as eventsCommandHandler } from 'cli/commands/_events'; +import { registerCommand as registerAiCommand } from 'cli/commands/ai'; import { registerCommand as registerAuthLoginCommand } from 'cli/commands/auth/login'; import { registerCommand as registerAuthLogoutCommand } from 'cli/commands/auth/logout'; import { registerCommand as registerAuthStatusCommand } from 'cli/commands/auth/status'; @@ -128,6 +129,8 @@ async function main() { .demandCommand( 1, __( 'You must provide a valid command' ) ) .strict(); + registerAiCommand( studioArgv ); + await studioArgv.argv; } diff --git a/apps/cli/lib/appdata.ts b/apps/cli/lib/appdata.ts index 202482af65..9985c7d93b 100644 --- a/apps/cli/lib/appdata.ts +++ b/apps/cli/lib/appdata.ts @@ -48,7 +48,9 @@ const userDataSchema = z } ) .loose(); -type UserData = z.infer< typeof userDataSchema >; +type UserData = z.infer< typeof userDataSchema > & { + anthropicApiKey?: string; +}; export type SiteData = z.infer< typeof siteSchema >; export type ValidatedAuthToken = Required< NonNullable< UserData[ 'authToken' ] > >; @@ -241,6 +243,26 @@ export async function updateSiteAutoStart( siteId: string, autoStart: boolean ): } } +export async function getAnthropicApiKey(): Promise< string | undefined > { + try { + const userData = await readAppdata(); + return userData.anthropicApiKey; + } catch { + return undefined; + } +} + +export async function saveAnthropicApiKey( apiKey: string ): Promise< void > { + try { + await lockAppdata(); + const userData = await readAppdata(); + userData.anthropicApiKey = apiKey; + await saveAppdata( userData ); + } finally { + await unlockAppdata(); + } +} + export async function removeSiteFromAppdata( siteId: string ): Promise< void > { try { await lockAppdata(); diff --git a/apps/cli/package.json b/apps/cli/package.json index 79d6b5f080..6173220029 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -7,6 +7,8 @@ "license": "GPL-2.0-or-later", "main": "index.js", "dependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.45", + "@mariozechner/pi-tui": "^0.54.0", "@php-wasm/universal": "3.1.2", "@studio/common": "file:../../tools/common", "@vscode/sudo-prompt": "^9.3.2", @@ -14,6 +16,7 @@ "@wp-playground/cli": "3.1.2", "@wp-playground/common": "3.1.2", "@wp-playground/storage": "3.1.2", + "chalk": "^5.6.2", "cli-table3": "^0.6.5", "http-proxy": "^1.18.1", "node-forge": "^1.3.3", diff --git a/apps/cli/vite.config.ts b/apps/cli/vite.config.ts index 1d9d4756b4..80156f0c19 100644 --- a/apps/cli/vite.config.ts +++ b/apps/cli/vite.config.ts @@ -56,6 +56,14 @@ export default defineConfig( { }, ] : [] ), + viteStaticCopy( { + targets: [ + { + src: 'ai/plugin', + dest: 'ai', + }, + ], + } ), ], build: { lib: { @@ -87,6 +95,8 @@ export default defineConfig( { '@wp-playground/cli', '@wp-playground/blueprints', '@wp-playground/wordpress', + '@anthropic-ai/claude-agent-sdk', + 'koffi', ], output: { format: 'cjs', diff --git a/package-lock.json b/package-lock.json index 26db054a6d..18a62e5afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,6 @@ { "name": "studio", + "version": "1.7.4", "lockfileVersion": 3, "requires": true, "packages": { @@ -52,6 +53,8 @@ "name": "studio-cli", "license": "GPL-2.0-or-later", "dependencies": { + "@anthropic-ai/claude-agent-sdk": "^0.2.45", + "@mariozechner/pi-tui": "^0.54.0", "@php-wasm/universal": "3.1.2", "@studio/common": "file:../../tools/common", "@vscode/sudo-prompt": "^9.3.2", @@ -59,6 +62,7 @@ "@wp-playground/cli": "3.1.2", "@wp-playground/common": "3.1.2", "@wp-playground/storage": "3.1.2", + "chalk": "^5.6.2", "cli-table3": "^0.6.5", "http-proxy": "^1.18.1", "node-forge": "^1.3.3", @@ -175,226 +179,6 @@ "appdmg": "^0.6.6" } }, - "apps/studio/node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "apps/studio/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "apps/studio/node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "apps/studio/node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "apps/studio/node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "apps/studio/node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "apps/studio/node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "apps/studio/node_modules/@inquirer/ansi": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz", @@ -723,49 +507,12 @@ } } }, - "apps/studio/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "apps/studio/node_modules/@vitejs/plugin-react": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", - "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, "apps/studio/node_modules/chardet": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "license": "MIT" }, - "apps/studio/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "apps/studio/node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -775,6 +522,15 @@ "node": ">= 12" } }, + "apps/studio/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, "apps/studio/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -830,15 +586,6 @@ "url": "https://opencollective.com/express" } }, - "apps/studio/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, "apps/studio/node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -855,27 +602,6 @@ "url": "https://opencollective.com/express" } }, - "apps/studio/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "apps/studio/node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, "apps/studio/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -923,31 +649,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "apps/studio/node_modules/tar": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz", - "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/studio/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "dev": true, @@ -964,6 +665,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@anthropic-ai/claude-agent-sdk": { + "version": "0.2.45", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.45.tgz", + "integrity": "sha512-AKH2hKoJNyjLf9ThAttKqbmCjUFg7qs/8+LR/UTVX20fCLn359YH9WrQc6dAiAfi8RYNA+mWwrNYCAq+Sdo5Ag==", + "license": "SEE LICENSE IN README.md", + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "^0.33.5", + "@img/sharp-darwin-x64": "^0.33.5", + "@img/sharp-linux-arm": "^0.33.5", + "@img/sharp-linux-arm64": "^0.33.5", + "@img/sharp-linux-x64": "^0.33.5", + "@img/sharp-linuxmusl-arm64": "^0.33.5", + "@img/sharp-linuxmusl-x64": "^0.33.5", + "@img/sharp-win32-x64": "^0.33.5" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, "node_modules/@ariakit/core": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.18.tgz", @@ -1215,10 +938,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -1227,7 +952,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "engines": { @@ -1235,19 +962,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -1272,11 +1001,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -1297,11 +1028,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -1406,24 +1139,28 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1531,22 +1268,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -2743,27 +2484,31 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -2771,7 +2516,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -3209,6 +2956,23 @@ "node": ">=18" } }, + "node_modules/@electron-forge/cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@electron-forge/cli/node_modules/cli-width": { "version": "4.1.0", "dev": true, @@ -3347,6 +3111,23 @@ "node": ">= 16.4.0" } }, + "node_modules/@electron-forge/core-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@electron-forge/core-utils/node_modules/fs-extra": { "version": "10.1.0", "dev": true, @@ -3360,6 +3141,23 @@ "node": ">=12" } }, + "node_modules/@electron-forge/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@electron-forge/core/node_modules/fs-extra": { "version": "10.1.0", "dev": true, @@ -4033,6 +3831,23 @@ "node": ">=12.13.0" } }, + "node_modules/@electron/rebuild/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@electron/rebuild/node_modules/cli-cursor": { "version": "3.1.0", "dev": true, @@ -4445,9 +4260,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", "cpu": [ "ppc64" ], @@ -4462,9 +4277,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", "cpu": [ "arm" ], @@ -4479,9 +4294,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", "cpu": [ "arm64" ], @@ -4496,9 +4311,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", "cpu": [ "x64" ], @@ -4528,9 +4343,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", "cpu": [ "x64" ], @@ -4545,9 +4360,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", "cpu": [ "arm64" ], @@ -4562,9 +4377,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", "cpu": [ "x64" ], @@ -4579,9 +4394,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", "cpu": [ "arm" ], @@ -4596,9 +4411,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", "cpu": [ "arm64" ], @@ -4613,9 +4428,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", "cpu": [ "ia32" ], @@ -4630,9 +4445,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", "cpu": [ "loong64" ], @@ -4647,9 +4462,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", "cpu": [ "mips64el" ], @@ -4664,9 +4479,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", "cpu": [ "ppc64" ], @@ -4681,9 +4496,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", "cpu": [ "riscv64" ], @@ -4698,9 +4513,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", "cpu": [ "s390x" ], @@ -4715,9 +4530,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", "cpu": [ "x64" ], @@ -4732,9 +4547,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -4749,9 +4564,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", "cpu": [ "x64" ], @@ -4766,9 +4581,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -4783,9 +4598,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", "cpu": [ "x64" ], @@ -4817,9 +4632,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], @@ -4834,9 +4649,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ "arm64" ], @@ -4851,9 +4666,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ "ia32" ], @@ -4868,9 +4683,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ "x64" ], @@ -5052,9 +4867,9 @@ } }, "node_modules/@fastify/otel/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -5174,22 +4989,307 @@ "version": "1.0.1", "license": "Apache-2.0", "engines": { - "node": ">=12.22" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "license": "Apache-2.0", + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=18.18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://opencollective.com/libvips" } }, "node_modules/@inquirer/ansi": { @@ -5567,9 +5667,7 @@ } }, "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "version": "5.0.0", "dev": true, "license": "MIT", "dependencies": { @@ -5581,8 +5679,6 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", "dependencies": { @@ -5598,9 +5694,7 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "version": "6.2.1", "dev": true, "license": "MIT", "engines": { @@ -5612,15 +5706,11 @@ }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { @@ -5637,8 +5727,6 @@ }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5784,6 +5872,48 @@ "node": ">= 12.13.0" } }, + "node_modules/@mariozechner/pi-tui": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.54.0.tgz", + "integrity": "sha512-bvFlUohdxDvKcFeQM2xsd5twCGKWxVaYSlHCFljIW0KqMC4vU+/Ts4A1i9iDnm6Xe/MlueKvC0V09YeC8fLIHA==", + "license": "MIT", + "dependencies": { + "@types/mime-types": "^2.1.4", + "chalk": "^5.5.0", + "get-east-asian-width": "^1.3.0", + "koffi": "^2.9.0", + "marked": "^15.0.12", + "mime-types": "^3.0.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mariozechner/pi-tui/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -5882,6 +6012,8 @@ }, "node_modules/@octokit/app": { "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz", + "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==", "license": "MIT", "dependencies": { "@octokit/auth-app": "^6.0.0", @@ -5898,6 +6030,8 @@ }, "node_modules/@octokit/auth-app": { "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz", + "integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-app": "^7.1.0", @@ -5916,10 +6050,14 @@ }, "node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/auth-app/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -5928,6 +6066,8 @@ "node_modules/@octokit/auth-app/node_modules/lru-cache": { "name": "@wolfy1339/lru-cache", "version": "11.0.2-patch.1", + "resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz", + "integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==", "license": "ISC", "engines": { "node": "18 >=18.20 || 20 || >=22" @@ -5935,6 +6075,8 @@ }, "node_modules/@octokit/auth-oauth-app": { "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz", + "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-device": "^6.1.0", @@ -5951,10 +6093,14 @@ }, "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -5962,6 +6108,8 @@ }, "node_modules/@octokit/auth-oauth-device": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz", + "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==", "license": "MIT", "dependencies": { "@octokit/oauth-methods": "^4.1.0", @@ -5975,10 +6123,14 @@ }, "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -5986,6 +6138,8 @@ }, "node_modules/@octokit/auth-oauth-user": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz", + "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-device": "^6.1.0", @@ -6001,10 +6155,14 @@ }, "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6012,6 +6170,8 @@ }, "node_modules/@octokit/auth-token": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "license": "MIT", "engines": { "node": ">= 18" @@ -6019,6 +6179,8 @@ }, "node_modules/@octokit/auth-unauthenticated": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz", + "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==", "license": "MIT", "dependencies": { "@octokit/request-error": "^5.0.0", @@ -6030,6 +6192,8 @@ }, "node_modules/@octokit/core": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^4.0.0", @@ -6046,10 +6210,14 @@ }, "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/core/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6057,6 +6225,8 @@ }, "node_modules/@octokit/endpoint": { "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", @@ -6068,10 +6238,14 @@ }, "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/endpoint/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6079,6 +6253,8 @@ }, "node_modules/@octokit/graphql": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", "license": "MIT", "dependencies": { "@octokit/request": "^8.4.1", @@ -6091,10 +6267,14 @@ }, "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/graphql/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6102,6 +6282,8 @@ }, "node_modules/@octokit/oauth-app": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz", + "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-app": "^7.0.0", @@ -6119,6 +6301,8 @@ }, "node_modules/@octokit/oauth-authorization-url": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz", + "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==", "license": "MIT", "engines": { "node": ">= 18" @@ -6126,6 +6310,8 @@ }, "node_modules/@octokit/oauth-methods": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz", + "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==", "license": "MIT", "dependencies": { "@octokit/oauth-authorization-url": "^6.0.2", @@ -6140,10 +6326,14 @@ }, "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6151,10 +6341,14 @@ }, "node_modules/@octokit/openapi-types": { "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", "license": "MIT" }, "node_modules/@octokit/plugin-paginate-graphql": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.1.tgz", + "integrity": "sha512-R8ZQNmrIKKpHWC6V2gum4x9LG2qF1RxRjo27gjQcG3j+vf2tLsEfE7I/wRWEPzYMaenr1M+qDAtNcwZve1ce1A==", "license": "MIT", "engines": { "node": ">= 18" @@ -6165,6 +6359,8 @@ }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" @@ -6178,6 +6374,8 @@ }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" @@ -6191,6 +6389,8 @@ }, "node_modules/@octokit/plugin-retry": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", "license": "MIT", "dependencies": { "@octokit/request-error": "^5.0.0", @@ -6206,10 +6406,14 @@ }, "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6217,6 +6421,8 @@ }, "node_modules/@octokit/plugin-throttling": { "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", "license": "MIT", "dependencies": { "@octokit/types": "^12.2.0", @@ -6231,6 +6437,8 @@ }, "node_modules/@octokit/request": { "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", "license": "MIT", "dependencies": { "@octokit/endpoint": "^9.0.6", @@ -6244,6 +6452,8 @@ }, "node_modules/@octokit/request-error": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", @@ -6256,10 +6466,14 @@ }, "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/request-error/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6267,10 +6481,14 @@ }, "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/request/node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -6278,6 +6496,8 @@ }, "node_modules/@octokit/types": { "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" @@ -6285,6 +6505,8 @@ }, "node_modules/@octokit/webhooks": { "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.3.2.tgz", + "integrity": "sha512-exj1MzVXoP7xnAcAB3jZ97pTvVPkQF9y6GA/dvYC47HV7vLv+24XRS6b/v/XnyikpEuvMhugEXdGtAlU086WkQ==", "license": "MIT", "dependencies": { "@octokit/request-error": "^5.0.0", @@ -6298,6 +6520,8 @@ }, "node_modules/@octokit/webhooks-methods": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz", + "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==", "license": "MIT", "engines": { "node": ">= 18" @@ -6305,6 +6529,8 @@ }, "node_modules/@octokit/webhooks-types": { "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.6.1.tgz", + "integrity": "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw==", "license": "MIT" }, "node_modules/@opentelemetry/api": { @@ -7644,8 +7870,6 @@ }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", "optional": true, @@ -8070,6 +8294,13 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/plugin-virtual": { "version": "3.0.2", "dev": true, @@ -8087,9 +8318,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", + "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", "cpu": [ "arm" ], @@ -8101,9 +8332,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", + "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", "cpu": [ "arm64" ], @@ -8115,9 +8346,7 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "version": "4.50.2", "cpu": [ "arm64" ], @@ -8129,9 +8358,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", + "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", "cpu": [ "x64" ], @@ -8143,9 +8372,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", + "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", "cpu": [ "arm64" ], @@ -8157,9 +8386,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", + "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", "cpu": [ "x64" ], @@ -8171,9 +8400,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", + "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", "cpu": [ "arm" ], @@ -8185,9 +8414,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", + "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", "cpu": [ "arm" ], @@ -8199,9 +8428,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", + "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", "cpu": [ "arm64" ], @@ -8213,9 +8442,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", + "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", "cpu": [ "arm64" ], @@ -8227,23 +8456,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", + "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", "cpu": [ "loong64" ], @@ -8255,23 +8470,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", + "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", "cpu": [ "ppc64" ], @@ -8283,9 +8484,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", + "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", "cpu": [ "riscv64" ], @@ -8297,9 +8498,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", + "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", "cpu": [ "riscv64" ], @@ -8311,9 +8512,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", + "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", "cpu": [ "s390x" ], @@ -8325,9 +8526,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", "cpu": [ "x64" ], @@ -8338,9 +8539,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", + "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", "cpu": [ "x64" ], @@ -8351,24 +8552,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", + "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", "cpu": [ "arm64" ], @@ -8380,9 +8567,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", + "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", "cpu": [ "arm64" ], @@ -8394,9 +8581,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", + "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", "cpu": [ "ia32" ], @@ -8407,24 +8594,10 @@ "win32" ] }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", "cpu": [ "x64" ], @@ -8545,9 +8718,9 @@ } }, "node_modules/@sentry/bundler-plugin-core/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { @@ -9566,6 +9739,8 @@ }, "node_modules/@types/btoa-lite": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", + "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==", "license": "MIT" }, "node_modules/@types/cacheable-request": { @@ -9652,8 +9827,6 @@ }, "node_modules/@types/fs-extra": { "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", - "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9702,8 +9875,6 @@ }, "node_modules/@types/jsonfile": { "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", - "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9712,6 +9883,8 @@ }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { "@types/ms": "*", @@ -9738,6 +9911,12 @@ "@types/unist": "*" } }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "license": "MIT" + }, "node_modules/@types/mousetrap": { "version": "1.6.15", "license": "MIT" @@ -9774,8 +9953,6 @@ }, "node_modules/@types/node-forge": { "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", - "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", "dev": true, "license": "MIT", "dependencies": { @@ -10427,6 +10604,27 @@ "react": ">= 16.8.0" } }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, "node_modules/@vitest/expect": { "version": "4.0.18", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", @@ -10787,9 +10985,7 @@ } }, "node_modules/@wordpress/components/node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "version": "6.2.1", "license": "MIT" }, "node_modules/@wordpress/compose": { @@ -11291,8 +11487,6 @@ }, "node_modules/@wp-playground/blueprints/node_modules/ajv": { "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11307,8 +11501,6 @@ }, "node_modules/@wp-playground/blueprints/node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -11321,8 +11513,6 @@ }, "node_modules/@wp-playground/blueprints/node_modules/ini": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", - "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -11330,20 +11520,14 @@ }, "node_modules/@wp-playground/blueprints/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/@wp-playground/blueprints/node_modules/pako": { "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", "license": "(MIT AND Zlib)" }, "node_modules/@wp-playground/blueprints/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11354,8 +11538,6 @@ }, "node_modules/@wp-playground/blueprints/node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -11372,8 +11554,6 @@ }, "node_modules/@wp-playground/blueprints/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "engines": { "node": ">=12" @@ -11891,9 +12071,9 @@ "license": "Apache-2.0" }, "node_modules/@yao-pkg/pkg": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.13.1.tgz", - "integrity": "sha512-HZGrjKngYUpCAHUjBHb9VMb0V7FsJtoMHtw0tfSS4GeS/Ur8ZK/EGDWtdj+apGERyScc3QLOyg1mXYuv01gqvw==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.14.1.tgz", + "integrity": "sha512-kTtxr+r9/pi+POcuuJN9q2YRs1Ekp7iwsgU3du1XO6ozwjlp+F31ZF19WS2ZDIloWYGCvfbURrrXMgQJpWZb+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -11902,8 +12082,8 @@ "@babel/traverse": "^7.23.0", "@babel/types": "^7.23.0", "@yao-pkg/pkg-fetch": "3.5.32", - "esbuild": "^0.24.0", - "into-stream": "^6.0.0", + "esbuild": "^0.27.3", + "into-stream": "^9.1.0", "minimist": "^1.2.6", "multistream": "^4.1.0", "picocolors": "^1.1.0", @@ -11912,7 +12092,7 @@ "resolve": "^1.22.10", "resolve.exports": "^2.0.3", "stream-meter": "^1.0.4", - "tar": "^7.5.6", + "tar": "^7.5.7", "tinyglobby": "^0.2.11", "unzipper": "^0.12.3" }, @@ -11920,7 +12100,7 @@ "pkg": "lib-es5/bin.js" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@yao-pkg/pkg-fetch": { @@ -11997,9 +12177,9 @@ } }, "node_modules/@yao-pkg/pkg/node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", "cpu": [ "arm64" ], @@ -12013,10 +12193,61 @@ "node": ">=18" } }, + "node_modules/@yao-pkg/pkg/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@yao-pkg/pkg/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@yao-pkg/pkg/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@yao-pkg/pkg/node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -12027,31 +12258,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" } }, "node_modules/@yao-pkg/pkg/node_modules/picomatch": { @@ -12074,6 +12306,8 @@ }, "node_modules/@zip.js/zip.js": { "version": "2.7.57", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", "license": "BSD-3-Clause", "engines": { "bun": ">=0.7.0", @@ -12578,6 +12812,8 @@ }, "node_modules/async-lock": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", "license": "MIT" }, "node_modules/asynckit": { @@ -12711,9 +12947,9 @@ } }, "node_modules/bare-fs": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.4.tgz", - "integrity": "sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.3.tgz", + "integrity": "sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -12828,9 +13064,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", - "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -12838,6 +13074,8 @@ }, "node_modules/before-after-hook": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "license": "Apache-2.0" }, "node_modules/benchmark-site-editor": { @@ -12873,23 +13111,21 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "version": "1.20.3", "license": "MIT", "dependencies": { - "bytes": "~3.1.2", + "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", - "unpipe": "~1.0.0" + "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8", @@ -12903,37 +13139,21 @@ "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "license": "MIT" }, - "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, "engines": { - "node": ">= 0.8" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/boolean": { @@ -12944,6 +13164,8 @@ }, "node_modules/bottleneck": { "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "license": "MIT" }, "node_modules/bplist-creator": { @@ -13006,6 +13228,8 @@ }, "node_modules/btoa-lite": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==", "license": "MIT" }, "node_modules/buffer": { @@ -13040,6 +13264,8 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, "node_modules/buffer-from": { @@ -13370,14 +13596,12 @@ } }, "node_modules/chalk": { - "version": "4.1.2", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -13525,6 +13749,8 @@ }, "node_modules/clean-git-ref": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", "license": "Apache-2.0" }, "node_modules/clean-stack": { @@ -14365,6 +14591,8 @@ }, "node_modules/deprecation": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "license": "ISC" }, "node_modules/dequal": { @@ -14421,9 +14649,7 @@ "license": "Apache-2.0" }, "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "version": "4.0.2", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -14546,6 +14772,8 @@ }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -14816,9 +15044,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.302", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", - "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "dev": true, "license": "ISC" }, @@ -15177,6 +15405,8 @@ }, "node_modules/equivalent-key-map": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz", + "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==", "license": "MIT" }, "node_modules/err-code": { @@ -15630,44 +15860,10 @@ "node": ">=18" } }, - "node_modules/esbuild/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/esbuild/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -15675,24 +15871,24 @@ "license": "MIT", "optional": true, "os": [ - "netbsd" + "linux" ], "engines": { "node": ">=18" } }, - "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": { + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "openbsd" + "netbsd" ], "engines": { "node": ">=18" @@ -16170,6 +16366,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/eslint/node_modules/eslint-scope": { "version": "8.4.0", "license": "BSD-2-Clause", @@ -16430,8 +16642,6 @@ }, "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -16491,8 +16701,6 @@ }, "node_modules/express/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -16500,8 +16708,6 @@ }, "node_modules/express/node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -16509,14 +16715,10 @@ }, "node_modules/express/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -16708,10 +16910,22 @@ "fast-string-width": "^3.0.2" } }, + "node_modules/fast-xml-builder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz", + "integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/fast-xml-parser": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.7.tgz", - "integrity": "sha512-JzVLro9NQv92pOM/jTCR6mHlJh2FGwtomH8ZQjhFj/R29P2Fnj38OgPJVtcvYw6SuKClhgYuwUZf5b3rd8u2mA==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", + "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", "funding": [ { "type": "github", @@ -16720,6 +16934,7 @@ ], "license": "MIT", "dependencies": { + "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { @@ -17065,50 +17280,6 @@ "node": ">= 0.6" } }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/from2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/from2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/from2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -17878,6 +18049,8 @@ }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" @@ -18347,32 +18520,18 @@ } }, "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-9.1.0.tgz", + "integrity": "sha512-DRsRnQrbzdFjaQ1oe4C6/EIUymIOEix1qROEJTF9dbMq+M4Zrm6VaLp6SD/B9IsiEjPZuBSnWWFN+udajugdWA==", "dev": true, "license": "MIT", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/into-stream/node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ip-address": { "version": "10.1.0", "license": "MIT", @@ -18959,14 +19118,15 @@ } }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "2.3.6", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": ">=14" + }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -19019,9 +19179,7 @@ } }, "node_modules/jsdom": { - "version": "24.1.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz", - "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==", + "version": "24.0.0", "dev": true, "license": "MIT", "dependencies": { @@ -19030,21 +19188,21 @@ "decimal.js": "^10.4.3", "form-data": "^4.0.0", "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", + "nwsapi": "^2.2.7", "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", + "rrweb-cssom": "^0.6.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.4", + "tough-cookie": "^4.1.3", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0", - "ws": "^8.18.0", + "ws": "^8.16.0", "xml-name-validator": "^5.0.0" }, "engines": { @@ -19184,6 +19342,8 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { "jws": "^4.0.1", @@ -19250,6 +19410,8 @@ }, "node_modules/jwa": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -19259,6 +19421,8 @@ }, "node_modules/jws": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { "jwa": "^2.0.1", @@ -19280,6 +19444,16 @@ "graceful-fs": "^4.1.11" } }, + "node_modules/koffi": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.15.1.tgz", + "integrity": "sha512-mnc0C0crx/xMSljb5s9QbnLrlFHprioFO1hkXyuSuO/QtbpLDa0l/uM21944UfQunMKmp3/r789DTDxVyyH6aA==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, "node_modules/lazystream": { "version": "1.0.1", "license": "MIT", @@ -19480,9 +19654,7 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.17.21", "license": "MIT" }, "node_modules/lodash._baseiteratee": { @@ -19535,26 +19707,38 @@ }, "node_modules/lodash.includes": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.merge": { @@ -19568,6 +19752,8 @@ }, "node_modules/lodash.once": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/lodash.throttle": { @@ -19597,6 +19783,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/log-update": { "version": "5.0.1", "dev": true, @@ -19817,6 +20020,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/matcher": { "version": "3.0.0", "dev": true, @@ -20732,6 +20947,8 @@ }, "node_modules/minimisted": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", "license": "MIT", "dependencies": { "minimist": "^1.2.5" @@ -20849,10 +21066,14 @@ }, "node_modules/module-details-from-path": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", "license": "MIT" }, "node_modules/moment": { "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "license": "MIT", "engines": { "node": "*" @@ -20860,6 +21081,8 @@ }, "node_modules/moment-timezone": { "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", "license": "MIT", "dependencies": { "moment": "^2.29.4" @@ -21381,6 +21604,8 @@ }, "node_modules/octokit": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz", + "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==", "license": "MIT", "dependencies": { "@octokit/app": "^14.0.2", @@ -21471,16 +21696,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/chalk": { - "version": "5.4.1", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/ora/node_modules/cli-cursor": { "version": "5.0.0", "license": "MIT", @@ -21886,8 +22101,6 @@ }, "node_modules/patch-package": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", - "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "dev": true, "license": "MIT", "dependencies": { @@ -21914,6 +22127,23 @@ "npm": ">5" } }, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/patch-package/node_modules/fs-extra": { "version": "10.1.0", "dev": true, @@ -22908,6 +23138,8 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/ps-man": { @@ -22943,9 +23175,7 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.14.0", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -23015,45 +23245,14 @@ } }, "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "version": "2.5.2", "license": "MIT", "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -23157,6 +23356,8 @@ }, "node_modules/react-is": { "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "node_modules/react-markdown": { @@ -23592,6 +23793,8 @@ }, "node_modules/rememo": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz", + "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==", "license": "MIT" }, "node_modules/remove-accents": { @@ -23608,6 +23811,8 @@ }, "node_modules/requestidlecallback": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", + "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==", "license": "MIT" }, "node_modules/require-directory": { @@ -23864,9 +24069,7 @@ } }, "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "version": "4.50.2", "dev": true, "license": "MIT", "dependencies": { @@ -23880,38 +24083,60 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", + "@rollup/rollup-android-arm-eabi": "4.50.2", + "@rollup/rollup-android-arm64": "4.50.2", + "@rollup/rollup-darwin-arm64": "4.50.2", + "@rollup/rollup-darwin-x64": "4.50.2", + "@rollup/rollup-freebsd-arm64": "4.50.2", + "@rollup/rollup-freebsd-x64": "4.50.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", + "@rollup/rollup-linux-arm-musleabihf": "4.50.2", + "@rollup/rollup-linux-arm64-gnu": "4.50.2", + "@rollup/rollup-linux-arm64-musl": "4.50.2", + "@rollup/rollup-linux-loong64-gnu": "4.50.2", + "@rollup/rollup-linux-ppc64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-musl": "4.50.2", + "@rollup/rollup-linux-s390x-gnu": "4.50.2", + "@rollup/rollup-linux-x64-gnu": "4.50.2", + "@rollup/rollup-linux-x64-musl": "4.50.2", + "@rollup/rollup-openharmony-arm64": "4.50.2", + "@rollup/rollup-win32-arm64-msvc": "4.50.2", + "@rollup/rollup-win32-ia32-msvc": "4.50.2", + "@rollup/rollup-win32-x64-msvc": "4.50.2", "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", + "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", + "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "version": "0.6.0", "dev": true, "license": "MIT" }, @@ -24281,6 +24506,8 @@ }, "node_modules/sha.js": { "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.4", @@ -24299,6 +24526,8 @@ }, "node_modules/sha.js/node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -24344,6 +24573,8 @@ }, "node_modules/shimmer": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", "license": "BSD-2-Clause" }, "node_modules/side-channel": { @@ -24412,8 +24643,6 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, @@ -24463,9 +24692,9 @@ } }, "node_modules/simple-git": { - "version": "3.30.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.30.0.tgz", - "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", + "version": "3.32.3", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.32.3.tgz", + "integrity": "sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw==", "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", @@ -24666,8 +24895,6 @@ }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, @@ -24680,8 +24907,6 @@ }, "node_modules/std-env": { "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, "license": "MIT" }, @@ -24809,8 +25034,6 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -24824,8 +25047,6 @@ }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -24926,8 +25147,6 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -25071,8 +25290,6 @@ }, "node_modules/sucrase/node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -25088,31 +25305,28 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "version": "10.3.10", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.3", "dev": true, "license": "ISC", "dependencies": { @@ -25126,9 +25340,7 @@ } }, "node_modules/sucrase/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.0.4", "dev": true, "license": "ISC", "engines": { @@ -25176,6 +25388,8 @@ }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -25216,9 +25430,9 @@ } }, "node_modules/systeminformation": { - "version": "5.30.7", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.7.tgz", - "integrity": "sha512-33B/cftpaWdpvH+Ho9U1b08ss8GQuLxrWHelbJT1yw4M48Taj8W3ezcPuaLoIHZz5V6tVHuQPr5BprEfnBLBMw==", + "version": "5.31.1", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.1.tgz", + "integrity": "sha512-6pRwxoGeV/roJYpsfcP6tN9mep6pPeCtXbUOCdVa0nme05Brwcwdge/fVNhIZn2wuUitAKZm4IYa7QjnRIa9zA==", "license": "MIT", "optional": true, "os": [ @@ -25304,10 +25518,9 @@ } }, "node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", - "dev": true, + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz", + "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -25346,7 +25559,6 @@ }, "node_modules/tar/node_modules/chownr": { "version": "3.0.0", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -25354,7 +25566,6 @@ }, "node_modules/tar/node_modules/minipass": { "version": "7.1.2", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -25362,7 +25573,6 @@ }, "node_modules/tar/node_modules/minizlib": { "version": "3.1.0", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -25373,7 +25583,6 @@ }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -25542,8 +25751,6 @@ }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, @@ -25636,6 +25843,8 @@ }, "node_modules/to-buffer": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", "license": "MIT", "dependencies": { "isarray": "^2.0.5", @@ -25648,6 +25857,8 @@ }, "node_modules/to-buffer/node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -25728,9 +25939,9 @@ } }, "node_modules/trash": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/trash/-/trash-10.1.0.tgz", - "integrity": "sha512-gOs9Hd1XMiJfORccP8KJNDmrSJ7YqO1CNt9lGOiBiydyBJab7Eaefkc/wj50b8lTtpB/4/VgezREs9NULOm42A==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/trash/-/trash-10.1.1.tgz", + "integrity": "sha512-L/mu8sfblMwaS+exj1MxpmihlIRwVQyB6ieKuTTmBJG0lXWBPfx3pMGQG8i3NT/S8vvNZrflDUOp+j0o7Cnxzw==", "license": "MIT", "dependencies": { "@stroncium/procfs": "^1.2.1", @@ -25815,8 +26026,6 @@ }, "node_modules/ts-node": { "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -26274,6 +26483,8 @@ }, "node_modules/universal-github-app-jwt": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz", + "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==", "license": "MIT", "dependencies": { "@types/jsonwebtoken": "^9.0.0", @@ -26282,6 +26493,8 @@ }, "node_modules/universal-user-agent": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", "license": "ISC" }, "node_modules/universalify": { @@ -27350,6 +27563,8 @@ }, "node_modules/wasm-feature-detect": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", "license": "Apache-2.0" }, "node_modules/watchpack": { @@ -27607,8 +27822,6 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -27724,8 +27937,6 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { @@ -27742,8 +27953,6 @@ }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -27803,9 +28012,9 @@ } }, "node_modules/wsl-utils/node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" @@ -28023,9 +28232,7 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.3.5", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -28039,6 +28246,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "packages/eslint-plugin-studio": { + "version": "1.0.0", + "extraneous": true, + "dependencies": { + "eslint": "^9.0.0" + } + }, "tools/benchmark-site-editor": { "version": "0.0.1", "license": "GPLv2", @@ -28063,18 +28277,6 @@ "undici-types": "~7.18.0" } }, - "tools/benchmark-site-editor/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "tools/benchmark-site-editor/node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", @@ -28130,6 +28332,22 @@ "undici-types": "~7.18.0" } }, + "tools/compare-perf/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "tools/compare-perf/node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", diff --git a/tools/common/logger-actions.ts b/tools/common/logger-actions.ts index c2e6868d20..d14f51cc0c 100644 --- a/tools/common/logger-actions.ts +++ b/tools/common/logger-actions.ts @@ -40,3 +40,8 @@ export enum SiteCommandLoggerAction { DELETE_PREVIEW_SITES = 'deletePreviewSites', DELETE_FILES = 'deleteFiles', } + +export enum AiCommandLoggerAction { + AGENT_START = 'agentStart', + AGENT_RUN = 'agentRun', +} From ec996ab92f8b59ecd377ae0469f253260a138d5c Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Fri, 20 Feb 2026 16:49:53 +0100 Subject: [PATCH 02/12] Update package-lock.json after npm install Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 18a62e5afc..0b59448c6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9695,9 +9695,9 @@ "peer": true }, "node_modules/@types/aws-lambda": { - "version": "8.10.161", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.161.tgz", - "integrity": "sha512-rUYdp+MQwSFocxIOcSsYSF3YYYC/uUpMbCY/mbO21vGqfrEYvNSoPyKYDj6RhXXpPfS0KstW9RwG3qXh9sL7FQ==", + "version": "8.10.160", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.160.tgz", + "integrity": "sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==", "license": "MIT" }, "node_modules/@types/babel__core": { @@ -11487,6 +11487,8 @@ }, "node_modules/@wp-playground/blueprints/node_modules/ajv": { "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -11501,6 +11503,8 @@ }, "node_modules/@wp-playground/blueprints/node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -11513,6 +11517,8 @@ }, "node_modules/@wp-playground/blueprints/node_modules/ini": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -11520,14 +11526,20 @@ }, "node_modules/@wp-playground/blueprints/node_modules/json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/@wp-playground/blueprints/node_modules/pako": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", "license": "(MIT AND Zlib)" }, "node_modules/@wp-playground/blueprints/node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11538,6 +11550,8 @@ }, "node_modules/@wp-playground/blueprints/node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -11554,6 +11568,8 @@ }, "node_modules/@wp-playground/blueprints/node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "engines": { "node": ">=12" @@ -13064,9 +13080,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", - "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", + "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -24692,9 +24708,9 @@ } }, "node_modules/simple-git": { - "version": "3.32.3", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.32.3.tgz", - "integrity": "sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.31.1.tgz", + "integrity": "sha512-oiWP4Q9+kO8q9hHqkX35uuHmxiEbZNTrZ5IPxgMGrJwN76pzjm/jabkZO0ItEcqxAincqGAzL3QHSaHt4+knBg==", "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", @@ -25939,9 +25955,9 @@ } }, "node_modules/trash": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/trash/-/trash-10.1.1.tgz", - "integrity": "sha512-L/mu8sfblMwaS+exj1MxpmihlIRwVQyB6ieKuTTmBJG0lXWBPfx3pMGQG8i3NT/S8vvNZrflDUOp+j0o7Cnxzw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/trash/-/trash-10.1.0.tgz", + "integrity": "sha512-gOs9Hd1XMiJfORccP8KJNDmrSJ7YqO1CNt9lGOiBiydyBJab7Eaefkc/wj50b8lTtpB/4/VgezREs9NULOm42A==", "license": "MIT", "dependencies": { "@stroncium/procfs": "^1.2.1", From 1a0947e3fe9d98ed2416a6782784cd3ecd333335 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Sat, 21 Feb 2026 00:02:36 +0100 Subject: [PATCH 03/12] Improve studio ai agent: new skills, progress forwarding, and PM2 fix - Switch to Sonnet model with bypassPermissions mode to prevent tool hanging - Replace old skills (creating-themes, editing-blocks, editing-themes, generating-patterns) with new ones (frontend-design, wordpress-block-theming, wp-interactivity-api) - Forward Logger progress messages to TUI spinner instead of fighting with ora - Add directive workflow in system prompt to ensure site creation vs reuse - Add block markup rules: no HTML blocks, no decorative comments, no emojis - Fix stale PM2 bus cache causing "SUB connection already being closed" errors after site creation by clearing pm2Bus on disconnect Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/agent.ts | 16 +- .../ai/plugin/skills/creating-themes/SKILL.md | 122 ---- .../references/card-layouts.md | 39 - .../ai/plugin/skills/editing-blocks/SKILL.md | 26 - .../editing-blocks/references/inner-blocks.md | 13 - .../ai/plugin/skills/editing-themes/SKILL.md | 20 - .../ai/plugin/skills/frontend-design/SKILL.md | 82 +++ .../skills/generating-patterns/SKILL.md | 141 ---- .../skills/wordpress-block-theming/SKILL.md | 687 ++++++++++++++++++ .../skills/wp-interactivity-api/SKILL.md | 183 +++++ .../references/debugging.md | 28 + .../references/directives-quickref.md | 29 + .../references/server-side-rendering.md | 310 ++++++++ apps/cli/ai/system-prompt.ts | 60 +- apps/cli/ai/tools.ts | 225 +++--- apps/cli/ai/ui.ts | 69 +- apps/cli/commands/ai.ts | 2 + apps/cli/lib/pm2-manager.ts | 1 + apps/cli/logger.ts | 28 + 19 files changed, 1591 insertions(+), 490 deletions(-) delete mode 100644 apps/cli/ai/plugin/skills/creating-themes/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md delete mode 100644 apps/cli/ai/plugin/skills/editing-blocks/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md delete mode 100644 apps/cli/ai/plugin/skills/editing-themes/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/frontend-design/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/generating-patterns/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md create mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md create mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md create mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/references/server-side-rendering.md diff --git a/apps/cli/ai/agent.ts b/apps/cli/ai/agent.ts index 3639747194..27e4bae839 100644 --- a/apps/cli/ai/agent.ts +++ b/apps/cli/ai/agent.ts @@ -56,7 +56,8 @@ export function startAiAgent( config: AiAgentConfig ): Query { plugins: [ { type: 'local', path: pluginPath } ], maxTurns, cwd: process.cwd(), - permissionMode: 'default', + permissionMode: 'bypassPermissions', + allowDangerouslySkipPermissions: true, canUseTool: async ( toolName, input ) => { if ( toolName === 'AskUserQuestion' && onAskUser ) { const typedInput = input as { @@ -72,17 +73,8 @@ export function startAiAgent( config: AiAgentConfig ): Query { } return { behavior: 'allow' as const, updatedInput: input }; }, - allowedTools: [ - 'mcp__studio__*', - 'Skill', - 'Read', - 'Write', - 'Edit', - 'Bash', - 'Glob', - 'Grep', - ], - model: 'claude-opus-4-6', + allowedTools: [ 'mcp__studio__*', 'Skill', 'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep' ], + model: 'claude-sonnet-4-6', resume, }, } ); diff --git a/apps/cli/ai/plugin/skills/creating-themes/SKILL.md b/apps/cli/ai/plugin/skills/creating-themes/SKILL.md deleted file mode 100644 index 40bd915a6b..0000000000 --- a/apps/cli/ai/plugin/skills/creating-themes/SKILL.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -name: creating-themes -description: Guidelines for creating new WordPress block themes from scratch — load this before generating theme files ---- - -## When to use me - -Use this skill when creating a new theme from scratch. -Do not use this skill when modifying an existing theme. - -## General Rules - -Follow these rules carefully unless the user explicitly requests otherwise: - -- **Focus on the home page first**: Create a beautiful, image-rich home page as the centerpiece of the initial theme. The home page should showcase the theme's aesthetic vision with compelling visuals, strong typography, and engaging layout. Put your creative energy here. -- **Minimal template set**: On initial theme creation, only create `index.html`. Avoid creating additional templates like `single.html`, `page.html`, `archive.html`, etc. unless the user specifically requests them. Keep the initial scope focused. -- **Prefer templates over patterns**: Default to creating templates rather than patterns. Templates are easier for users to find, preview, and manipulate in the Site Editor. Only create patterns when the user explicitly requests pattern creation. -- **Pattern visibility rule**: If you must create a pattern (because the user requested it), always include that pattern's content in a new or existing template so the user can immediately preview it. Patterns that exist only in the `patterns/` directory without being used in any template are hard to discover. - -## Generating Theme Instructions - -- Always use the current working directory — do not create a subdirectory for the theme -- Always include style.css with the required WordPress theme header comment -- Always create a valid theme.json (version 3) as the central configuration -- Always create block templates in templates/ and template parts in parts/ -- Never use emojis in any generated content — not in headings, paragraphs, button text, or any text -- Never close the final PHP tag in functions.php -- Guard functions with function_exists() checks -- Use `enqueue_block_assets` hook for fonts (not `wp_enqueue_scripts`) to ensure they load in both front-end and editor -- Define colors, typography, and spacing in theme.json — not in CSS where possible -- Make the theme FSE-compatible (Full Site Editing) -- Avoid patterns unless the user explicitly asks to create them — prefer templates instead -- Keep functions.php minimal — enqueuing assets, registering patterns, and adding theme support - -## Theme File Structure - -``` -style.css # Required — theme header comment and base styles -theme.json # Central configuration (colors, typography, spacing, layout) -functions.php # Minimal — enqueue assets, register patterns, add theme support -templates/ # Block templates - index.html # Required — fallback template - single.html # Single post - page.html # Page - archive.html # Archive - 404.html # Not found - home.html # Blog home (optional) - search.html # Search results (optional) -parts/ # Reusable template parts - header.html # Site header - footer.html # Site footer -patterns/ # Block patterns for reusable sections -assets/ # Static assets (images, local fonts) -``` - -## style.css Header (required) - -```css -/* -Theme Name: Theme Name -Theme URI: https://example.com -Author: Author Name -Description: Theme description -Version: 1.0.0 -Requires at least: 6.0 -Tested up to: 6.5 -Requires PHP: 8.0 -License: GNU General Public License v2 or later -License URI: http://www.gnu.org/licenses/gpl-2.0.html -Text Domain: theme-slug -*/ -``` - -## theme.json Essentials - -- Use version 3 -- Define settings: colors (palette), typography (fontFamilies, fontSizes), spacing (units, spacingSizes), layout (contentSize, wideSize) -- Define styles: global styles and block-specific overrides -- Use appearanceTools: true to enable border, typography, spacing, and color controls -- Register custom template parts with their area (header, footer, uncategorized) -- Use style variations to offer different design options -- Add custom block styles when needed to extend core blocks - -## Google Fonts - -ALWAYS use `enqueue_block_assets` hook (not `wp_enqueue_scripts`) to ensure fonts load in BOTH the front-end AND block editor: - -```php -function theme_fonts() { - wp_enqueue_style( - 'theme-fonts', - 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap', - array(), - null - ); -} -add_action( 'enqueue_block_assets', 'theme_fonts' ); -``` - -## Generating a landing page - -- **YOU DECIDE** which sections/blocks best serve this specific site. Do not follow a rigid template. - Consider the user site type and audience to determine the optimal page structure. - Examples: - - A portfolio might need a full-bleed project gallery - - A SaaS might need feature grids - - A restaurant might need a reservations widget - - An agency might need case study cards -- **Always include a header and footer**, but the sections in between are your creative choice. -- Treat every block as a **self-contained section** (one dominant semantic wrapper) whose markup will live inside its own file. -- Keep copy **realistic but placeholder-friendly**; never reference real clients or brands. -- Favor **semantic HTML**, class-based styling hooks, and **shallow DOM trees** that respond well on mobile. -- **Section margin reset**: Add `"style":{"spacing":{"margin":{"top":"0"}}}` to every top-level Group block that wraps a landing page section. This overrides WordPress's default top margin on direct children of `.wp-site-blocks` and can be easily adjusted by users in the editor. -- **Section layout widths**: Hero sections, header groups, cover blocks, and feature grids should use `"align":"wide"` or `"align":"full"` rather than defaulting to narrow content width. Only use default (content) alignment for text-heavy reading sections. -- Do **not** use ``; output the full expanded markup inside each block. -- **No decorative HTML comments**: Never insert section-labeling comments like `` or `` in templates, template parts, or patterns. Only WordPress block comments (``) are allowed. - -## Reference Files - -Before generating theme files, read the relevant references from the `references/` directory next to this skill file. - -- **`references/card-layouts.md`** — read this if the theme uses card-based layouts (equal-height columns, grid cards with CTAs) diff --git a/apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md b/apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md deleted file mode 100644 index 198e6b4b92..0000000000 --- a/apps/cli/ai/plugin/skills/creating-themes/references/card-layouts.md +++ /dev/null @@ -1,39 +0,0 @@ -# Card Layouts in Rows - -For equal-height, equal-width cards (with optional bottom-aligned CTAs), use this structure unless the user specifies otherwise: - -``` -Columns (className: "equal-cards") - └── Column - verticalAlignment: "stretch" - width: "X%" where X = 100 / number_of_cards (e.g., 2 cards = 50%, 3 cards = 33.33%, 4 cards = 25%) - └── Group [card wrapper] - └── [content: headings, paragraphs, images*, lists] - └── (optional) Buttons (className: "cta-bottom") -``` - -**Width rule**: All cards in a row MUST have equal width. Calculate each column's width as `100% / number_of_cards` (e.g., 3 cards = 33.33% each). The sum of all column widths must equal exactly 100% - never exceed the parent element width. - -*Images in cards: `style="height:200px;object-fit:cover;width:100%"` - -**Required CSS** (style.css): -```css -.equal-cards > .wp-block-column { - display: flex; - flex-direction: column; - flex-grow: 0; - flex-shrink: 0; -} -.equal-cards > .wp-block-column > .wp-block-group { - display: flex; - flex-direction: column; - flex-grow: 1; -} -``` -If present, ensure bottom-aligned CTAs unless otherwise specified: -```css -.equal-cards .cta-bottom { - margin-top: auto; - justify-content: center; -} -``` diff --git a/apps/cli/ai/plugin/skills/editing-blocks/SKILL.md b/apps/cli/ai/plugin/skills/editing-blocks/SKILL.md deleted file mode 100644 index 552822185a..0000000000 --- a/apps/cli/ai/plugin/skills/editing-blocks/SKILL.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -name: editing-blocks -description: Guidelines for modifying existing WordPress blocks — load this before editing block files ---- - -## When to use me - -Use this skill when modifying an existing block (files already exist in the workspace). -Do not use this skill when creating a new block from scratch. - -## Editing Guidelines - -- Read all existing block files before making changes — understand the current architecture, attributes, and rendering approach -- Make minimal, targeted changes — only modify what the user requested -- Only touch files that need to change — do not rewrite unrelated files -- Use the `edit` tool for targeted changes; only use `write` when replacing more than 50% of a file -- Do not convert between static and dynamic blocks, or change the block name/slug, unless the user explicitly asks -- When adding or modifying attributes, update all relevant files (block.json, edit.js, and save.js or render.php) -- After changes, verify the block is still registered correctly and renders on the front end -- Use the InnerBlocks component from @wordpress/block-editor as much as possible. - -## Reference Files - -Before editing block files, read the relevant references from the `references/` directory next to this skill file. - -- **`references/inner-blocks.md`** — read this if the block uses InnerBlocks or child blocks diff --git a/apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md b/apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md deleted file mode 100644 index f90cc9ab92..0000000000 --- a/apps/cli/ai/plugin/skills/editing-blocks/references/inner-blocks.md +++ /dev/null @@ -1,13 +0,0 @@ -# Inner Blocks - -Use the InnerBlocks component from @wordpress/block-editor as much as possible. -- Import InnerBlocks and useBlockProps from @wordpress/block-editor -- A block can only contain ONE InnerBlocks component -- Always wrap InnerBlocks in a div with blockProps spread: `
` -- Use the allowedBlocks prop to restrict which blocks can be inserted as children -- Set orientation="horizontal" when inner blocks are displayed horizontally -- Use template prop to define a pre-filled block structure -- Combine template with templateLock="all" to prevent any changes, or templateLock="insert" to prevent additions but allow reordering -- Use defaultBlock with directInsert={true} to auto-insert a specific block type when the appender is clicked -- Define block relationships in block.json: use parent for direct descendants only, ancestor for any level -- For advanced cases, consider useInnerBlocksProps hook instead of the component diff --git a/apps/cli/ai/plugin/skills/editing-themes/SKILL.md b/apps/cli/ai/plugin/skills/editing-themes/SKILL.md deleted file mode 100644 index 231f38234b..0000000000 --- a/apps/cli/ai/plugin/skills/editing-themes/SKILL.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: editing-themes -description: Guidelines for modifying existing WordPress block themes — load this before editing theme files ---- - -## When to use me - -Use this skill when modifying an existing theme (files already exist in the workspace). -Do not use this skill when creating a new theme from scratch. - -## Editing Guidelines - -- Read theme.json, style.css, and functions.php before making changes — understand the current configuration, design tokens, and registered assets -- Make minimal, targeted changes — only modify what the user requested -- Only touch files that need to change — do not rewrite unrelated templates or patterns -- Use the `edit` tool for targeted changes; only use `write` when replacing more than 50% of a file -- When modifying colors, typography, or spacing, update theme.json — not CSS — unless the change cannot be expressed in theme.json -- When adding or modifying template parts, ensure they are registered in theme.json if needed -- Preserve the theme's existing design direction and aesthetic unless the user explicitly asks to change it -- After changes, verify templates still reference valid template parts and patterns diff --git a/apps/cli/ai/plugin/skills/frontend-design/SKILL.md b/apps/cli/ai/plugin/skills/frontend-design/SKILL.md new file mode 100644 index 0000000000..aa1a06da6a --- /dev/null +++ b/apps/cli/ai/plugin/skills/frontend-design/SKILL.md @@ -0,0 +1,82 @@ +--- +name: frontend-design +description: "Use when generating any user-facing HTML, CSS, or WordPress markup. Enforces design excellence: purposeful layouts, sophisticated typography, meaningful whitespace, and visual hierarchy. Prevents generic 'AI-generated' aesthetics." +--- + +# Frontend Design Excellence + +## When to use + +Apply this skill to ALL visual output — landing pages, themes, patterns, single-page layouts, any HTML/CSS generation. This skill defines the quality bar. + +## Related Skills + +- **Load `wordpress-block-theming`** for WordPress FSE implementation details — theme.json, block templates, patterns, and the className pattern for connecting CSS animations to WordPress blocks. +- **Load `wp-interactivity-api`** when implementing scroll-triggered animations, dynamic state, or any JavaScript-driven interactivity in WordPress. It's the native way to add motion and behavior to block themes. + +## Core Principles + +### 1. No AI Slop + +AI-generated designs share recognizable patterns that immediately signal "template." Avoid all of these: + +- **Generic hero patterns**: Big heading + subtitle + two buttons centered on a gradient. Instead, design heroes that match the brand's personality — asymmetric layouts, bold typography treatments, unexpected compositions. +- **Emoji headings**: Never prefix section headings with emoji. Use typography, color, and spacing to create visual hierarchy. +- **Uniform card grids**: Three identical cards with icon + heading + paragraph is the hallmark of AI design. Vary card sizes, use asymmetric grids, feature one item prominently, or use entirely different layouts. If equal cards are genuinely needed (pricing tiers, feature comparison), vary the visual treatment — different background colors, featured/highlighted card, varied imagery. +- **Stock phrases**: "Elevate your experience," "Unlock the power of," "Transform your workflow." Write copy that is specific to the actual product/service. +- **Decorative filler sections**: Every section must earn its place. If it doesn't serve the user's goal, remove it. +- **Rainbow gradients and generic blobs**: Use intentional color that reinforces brand identity. + +### 2. Visual Hierarchy + +Every page needs a clear reading order: + +- **One dominant element per viewport**: The eye needs a starting point. Usually the hero heading or a striking image. +- **Size contrast**: If everything is the same size, nothing stands out. Create drama with scale differences — a massive heading paired with small body text, a full-bleed image next to a narrow text column. +- **Whitespace is structure**: Generous spacing between sections creates rhythm. Tight spacing within groups creates unity. Use spacing intentionally, not uniformly. +- **Color as signal**: Reserve your accent/primary color for CTAs and key interactive elements. Using it everywhere dilutes its impact. + +### 3. Typography as Design + +Typography is the single most impactful design tool: + +- **Choose distinctive fonts**: Skip Arial, Inter, Open Sans. Use fonts with character — Clash Display, Cabinet Grotesk, Instrument Serif, Space Grotesk, Fraunces, Playfair Display. Pair a distinctive display font with a refined body font. +- **Size with intention**: Body text at 1rem. Headings should create a clear scale. Hero headings can be large (clamp-based), but avoid sizes above 4rem — they rarely improve design. +- **Weight and style variation**: Use bold, italic, uppercase tracking, and font-weight differences to create typographic hierarchy without relying solely on size. +- **Line height matters**: Body text 1.5-1.65. Headings 1.1-1.3. Tight leading on large display text creates visual density and impact. + +### 4. Color with Purpose + +- **Start with a limited palette**: 2-3 colors maximum for most designs. Expand with tints/shades, not new hues. +- **Dark backgrounds create drama**: Don't default to white backgrounds. Dark sections with light text create visual weight and break up page monotony. +- **Contrast ratios**: Ensure text meets WCAG AA (4.5:1 for body text, 3:1 for large text). This is non-negotiable. +- **Background alternation**: Create visual rhythm by alternating section backgrounds — but make each transition feel intentional, not mechanical. + +### 5. Layout Composition + +- **Break the grid occasionally**: A mostly-constrained layout with one full-bleed element creates visual interest. Predictable grids feel static. +- **Asymmetry over symmetry**: Slightly off-center compositions feel more dynamic and designed. Centered layouts are safe but rarely exciting. +- **Edge-to-edge sections**: Major page sections should be full-width (`alignfull`) with constrained content inside. Narrow pages feel like documents, not designs. +- **Vary section density**: Alternate between dense, content-rich sections and spacious, breathing-room sections. + +### 6. Motion and Interaction + +- **Scroll-triggered reveals**: Sections fading up as the user scrolls is the single most impactful animation. For WordPress block themes, see the `wordpress-block-theming` skill for the CSS className pattern and `wp-interactivity-api` for advanced directive-driven animations. +- **Hover states on interactive elements**: Cards, buttons, and links should respond to hover with subtle transforms or shadow changes. +- **Staggered animations**: When revealing a group of elements (cards, features), stagger their entrance for a cascading effect. +- **Restraint**: Animate section entrances and interactive elements. Don't animate every heading and paragraph — visual noise degrades the experience. +- **Respect `prefers-reduced-motion`**: Always include the media query to disable animations for users who prefer it. + +## Quality Checklist + +Before considering any design complete: + +- [ ] Does the hero section feel unique to this brand, not generic? +- [ ] Is there clear visual hierarchy — can you trace the reading order? +- [ ] Are fonts distinctive and well-paired? +- [ ] Does the color palette feel intentional and limited? +- [ ] Is there rhythm in section backgrounds and spacing? +- [ ] Do interactive elements have hover/focus states? +- [ ] Are scroll animations present but restrained? +- [ ] Is the design accessible (contrast, focus states, reduced-motion)? +- [ ] Could you tell this was hand-designed, not AI-generated? diff --git a/apps/cli/ai/plugin/skills/generating-patterns/SKILL.md b/apps/cli/ai/plugin/skills/generating-patterns/SKILL.md deleted file mode 100644 index bcb6670000..0000000000 --- a/apps/cli/ai/plugin/skills/generating-patterns/SKILL.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -name: generating-patterns -description: Guidelines and examples for generating WordPress block patterns — load this when creating patterns for themes ---- - -## When to use me - -Use this skill when generating block patterns for a theme. -Load this skill alongside `creating-themes` or `editing-themes` when patterns are part of the work. - -## Pattern Generation Rules - -- Place pattern files in the `patterns/` directory -- Each pattern file must start with a PHP comment header registering the pattern -- Use descriptive, kebab-case filenames (e.g., `hero-split.php`, `faq-accordion.php`) -- Never use emojis in any pattern content -- Keep copy realistic but placeholder-friendly — never reference real clients or brands -- Alternate between light and dark section backgrounds to create visual rhythm -- Patterns must be self-contained — do not use `` or assume external context - -## Pattern File Header (required) - -Every pattern file must begin with this PHP comment block: - -```php - -``` - -- `Title`: Human-readable name shown in the inserter -- `Slug`: Must be `theme-slug/pattern-name` using the theme's text domain -- `Categories`: Comma-separated list (use WordPress defaults: `featured`, `banner`, `text`, `gallery`, `call-to-action`, `about`, `team`, `testimonials`, `contact`, `footer`, `header`) -- `Keywords`: Comma-separated search terms for discoverability -- `Block Types`: Optional — restricts where the pattern appears - -## Pattern Set for Landing Pages - -When generating patterns for a landing page theme, include a varied set that covers the full page flow. Choose patterns that match the site type and audience — not every site needs the same set. - -Recommended baseline (adapt per site type): - -1. **Hero section** — the first thing visitors see; must set the tone and include a primary CTA -2. **Social proof / logos** — build trust early with client logos or partner badges -3. **Feature grid** — highlight key offerings or services -4. **Content with media** — pair text with imagery to explain the value proposition -5. **Testimonials or case studies** — reinforce credibility -6. **FAQ** — address common objections -7. **Final CTA** — closing section with a clear conversion action - -## CTA Guidelines - -- Every landing page must have at least two CTA patterns: one in the hero, one as a closing section -- Use `wp:buttons` with clear, action-oriented text ("Get Started", "Book a Demo", "View Portfolio") -- Style CTAs to stand out — use the theme's accent color, generous padding, and prominent placement -- Avoid vague labels like "Click Here" or "Learn More" when a specific action is available - -## Layout Examples - -### Hero with left text / right image - -Two-column split: heading + paragraph + CTA button on the left, full-height image on the right. Use `wp:columns` with a 55/45 or 60/40 split. The text column gets vertical centering; the image column uses `object-fit: cover` at full height. - -``` -Columns (align: full) - └── Column (width: 55%, verticalAlignment: center) - │ └── Heading (h1) - │ └── Paragraph - │ └── Buttons - └── Column (width: 45%) - └── Image (style: height 100%, object-fit: cover) -``` - -### Z-pattern layout - -Alternate the position of text and image across consecutive sections to create a natural Z reading flow. Odd sections place text left / image right; even sections flip to image left / text right. - -``` -Section 1 — Columns (align: wide) - └── Column (text) | Column (image) - -Section 2 — Columns (align: wide) - └── Column (image) | Column (text) - -Section 3 — Columns (align: wide) - └── Column (text) | Column (image) -``` - -Use alternating background colors (light/dark) between sections to reinforce separation. - -### 3-column feature grid - -Three equal-width columns, each containing an icon or image, a heading, a paragraph, and an optional CTA. Use the `equal-cards` pattern from the card-layouts reference. - -``` -Columns (className: "equal-cards", align: wide) - └── Column (width: 33.33%, verticalAlignment: stretch) - │ └── Group - │ └── Image (icon or illustration) - │ └── Heading (h3) - │ └── Paragraph - │ └── Buttons (className: "cta-bottom") [optional] - └── Column (width: 33.33%, verticalAlignment: stretch) - │ └── Group - │ └── ... - └── Column (width: 33.33%, verticalAlignment: stretch) - └── Group - └── ... -``` - -### Accordion FAQ - -Use the `wp:details` block for collapsible FAQ items inside a constrained-width Group. - -``` -Group (align: wide, layout: constrained) - └── Heading (h2, "Frequently Asked Questions") - └── Details (summary: "Question one?") - │ └── Paragraph (answer) - └── Details (summary: "Question two?") - │ └── Paragraph (answer) - └── Details (summary: "Question three?") - └── Paragraph (answer) -``` - -## Site-Type Pattern Suggestions - -Not every site needs the same patterns. Adapt the set to the context: - -- **Portfolio**: Hero with full-bleed image, project gallery grid, about/bio section, contact CTA -- **SaaS / Product**: Hero split, logo bar, feature grid, pricing table, testimonials, FAQ, final CTA -- **Restaurant / Local**: Hero with cover image, menu highlights, hours/location, reservation CTA, gallery -- **Agency / Studio**: Hero with reel or case study, services grid, case study cards, team, contact -- **Blog / Magazine**: Hero with featured post, category grid, newsletter signup, recent posts -- **E-commerce**: Hero with product showcase, category grid, bestsellers, testimonials, promo banner diff --git a/apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md b/apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md new file mode 100644 index 0000000000..7dfbdaf8ed --- /dev/null +++ b/apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md @@ -0,0 +1,687 @@ +--- +name: wordpress-block-theming +description: WordPress Full Site Editing (FSE) theme architecture. Use when generating theme.json, block templates, template parts, patterns, and functions.php for WordPress block themes. +--- + +# WordPress Block Theming Skill + +Comprehensive knowledge for building WordPress block themes using Full Site Editing (FSE) architecture. + +## Related Skills + +- **Always load `frontend-design`** alongside this skill. It defines the design quality bar — visual hierarchy, typography pairing, anti-AI-slop rules — that should govern all theme output. +- **Load `wp-interactivity-api`** when the design calls for scroll-triggered animations, dynamic state changes, or any interactivity beyond CSS. The Interactivity API is the WordPress-native way to add JavaScript-driven behavior to block themes. + +## Absolute Rules + +- **NO HTML BLOCKS**: Never use `` (the `core/html` block). HTML blocks are opaque blobs in the block editor — users cannot select, style, or rearrange individual elements inside them. Every piece of content MUST use a proper core block (`wp:group`, `wp:heading`, `wp:paragraph`, `wp:columns`, etc.). If you find yourself reaching for `wp:html`, stop and decompose the content into the correct core blocks with `className` attributes and CSS instead. +- **NO DECORATIVE HTML COMMENTS**: Never insert non-block HTML comments like `` or `` in templates, template parts, or patterns. The only HTML comments allowed are WordPress block delimiters (`` / ``). +- **NO EMOJIS**: Never use emojis anywhere in generated content - not in headings, paragraphs, button text, or any other text. This applies to all templates, patterns, and content. + +## Theme Architecture + +### Directory Structure + +``` +theme-slug/ +├── theme.json # Central configuration file +├── style.css # Theme metadata + custom CSS +├── functions.php # Asset enqueuing, pattern registration +├── templates/ # Block templates +│ ├── index.html # Main/fallback template +│ ├── single.html # Single post +│ ├── page.html # Single page +│ ├── archive.html # Archive listings +│ ├── search.html # Search results +│ └── 404.html # Not found +├── parts/ # Reusable template parts +│ ├── header.html # Site header +│ └── footer.html # Site footer +└── patterns/ # Block patterns + ├── hero.php + ├── features.php + └── cta.php +``` + +## theme.json Configuration + +The `theme.json` file is the central configuration for block themes. It defines: + +### Schema and Version + +```json +{ + "$schema": "https://schemas.wp.org/trunk/theme.json", + "version": 3 +} +``` + +### Settings + +Define available options for the editor: + +```json +{ + "settings": { + "appearanceTools": true, + "layout": { "contentSize": "800px", "wideSize": "1280px" }, + "color": { + "palette": [ /* 5 colors: primary, secondary, accent, light, dark */ ], + "defaultPalette": false, + "defaultGradients": false + }, + "typography": { + "fontFamilies": [ /* heading + body font families */ ], + "fontSizes": [ /* 5-6 step scale: small through huge */ ] + }, + "spacing": { + "units": ["px", "em", "rem", "%", "vw", "vh"], + "spacingSizes": [ /* 6 steps from compact to spacious */ ] + } + } +} +``` + +### Styles + +Define default styles for the site and blocks: + +```json +{ + "styles": { + "color": { /* background + text from palette */ }, + "typography": { /* body font family, medium size, line-height 1.5-1.65 */ }, + "elements": { + "heading": { /* heading font family, appropriate weight, line-height 1.1-1.3 */ }, + "link": { /* accent color */ }, + "button": { /* accent background, light text, border-radius */ } + } + } +} +``` + +## Typography + +See `frontend-design` skill for font selection and sizing principles. WordPress-specific implementation: + +- Register font families in `settings.typography.fontFamilies` in theme.json. Use Google Fonts enqueued via `functions.php`. +- Define a 6-step font size scale in `settings.typography.fontSizes`. A good scale: 0.875rem / 1rem / 1.25rem / 1.75rem / 2.25rem / clamp(2.5rem, 4vw, 3.5rem). +- **Line height**: Body text: 1.5–1.65. Headings: 1.1–1.3. Never go below 1.0 for any text. Apply via `styles.typography.lineHeight` and `styles.elements.heading.typography.lineHeight` in theme.json. + +## Block Templates + +Templates use WordPress block markup (HTML comments with JSON attributes). + +### Template Structure + +```html + + + +
+ +
+ + + +``` + +## Template Parts + +### Header Requirements +- Constrained layout group with site-appropriate background +- Flex row: `site-title` (level:0 — renders `

` not `

`) + `navigation` +- Appropriate padding using spacing presets + +### Footer Requirements +- Constrained layout group, matching or complementing header style +- Content varies by site type (copyright, social links, contact info, etc.) +- Include footer margin reset in style.css + +### Page Title +A page title template should be included by default, and should reflect in some way the design of the main landing page. If each page added shares the same page title template then the site will feel more cohesive and polished. It should use the `` block so the page title is dynamic and reflects the actual page/post title. + +## Block Patterns + +Patterns are PHP files that register reusable block content. + +### Pattern Registration + +```php + + +... + +``` + +## functions.php + +Keep functions.php minimal. Primary uses: + +### Google Fonts Enqueuing + +**IMPORTANT:** Always use `enqueue_block_assets` hook (not `wp_enqueue_scripts`) to ensure fonts load in BOTH the front-end AND block editor. + +```php +get( 'Version' ) + ); +} +add_action( 'enqueue_block_assets', 'theme_slug_enqueue_assets' ); + +// Register block patterns +function theme_slug_register_patterns() { + register_block_pattern_category( + 'theme-slug', + array( 'label' => __( 'Theme Patterns', 'theme-slug' ) ) + ); +} +add_action( 'init', 'theme_slug_register_patterns' ); +``` + +## Security in Generated Code + +- When `functions.php` outputs any user-derived value, use WordPress escaping functions: + - HTML context: `esc_html()` + - Attribute context: `esc_attr()` + - URL context: `esc_url()` +- Never use `eval()`, `create_function()`, `shell_exec()`, `exec()`, or `system()` in generated theme code +- Static block themes with hardcoded content (the default) do not need escaping — WordPress core blocks handle this. Escaping matters only if generating PHP that renders dynamic data. + +## style.css + +The style.css file contains theme metadata and custom CSS. +**Important**: Always bring across custom CSS from the design into style.css that is not achievable via theme.json, especially for layout and composition techniques that are critical to the design's aesthetics such as animation/motion. + +```css +/* +Theme Name: Theme Name +Theme URI: https://example.com +Author: Author Name +Author URI: https://example.com +Description: A beautiful WordPress block theme +Version: 1.0.0 +Requires at least: 6.0 +Tested up to: 6.7 +Requires PHP: 7.4 +License: GNU General Public License v2 or later +License URI: https://www.gnu.org/licenses/gpl-2.0.html +Text Domain: theme-slug +*/ +``` + +## Animation & Motion in Block Themes + +Animation brings life to block themes, but WordPress block markup requires a specific pattern to connect CSS animations to blocks. + +### The className Pattern + +Add animation classes to blocks via the `className` JSON attribute. WordPress renders this as a class on the wrapper div: + +```html + +
+ +
+ +``` + +This works on any block — groups, columns, headings, paragraphs, buttons, images: + +```html + +

Features

+ + + +
+ ... +
+ +``` + +### Animation Classes in style.css + +Generate and adapt these classes (these are examples only, do not limit yourself to these) in each theme's `style.css`: + +**Entrance animations:** +```css +.fade-up { + opacity: 0; + transform: translateY(30px); + animation: fadeUp 0.6s ease forwards; +} +.fade-in { + opacity: 0; + animation: fadeIn 0.6s ease forwards; +} +.slide-in-left { + opacity: 0; + transform: translateX(-40px); + animation: slideIn 0.7s ease forwards; +} +.slide-in-right { + opacity: 0; + transform: translateX(40px); + animation: slideIn 0.7s ease forwards; +} + +@keyframes fadeUp { to { opacity: 1; transform: translateY(0); } } +@keyframes fadeIn { to { opacity: 1; } } +@keyframes slideIn { to { opacity: 1; transform: translateX(0); } } +``` + +**Staggered children** — delays applied via nth-child: +```css +.stagger-children > * { + opacity: 0; + transform: translateY(20px); + animation: fadeUp 0.5s ease forwards; +} +.stagger-children > *:nth-child(1) { animation-delay: 0.1s; } +.stagger-children > *:nth-child(2) { animation-delay: 0.2s; } +.stagger-children > *:nth-child(3) { animation-delay: 0.3s; } +.stagger-children > *:nth-child(4) { animation-delay: 0.4s; } +``` + +**Interactive transitions:** +```css +.hover-lift { + transition: transform 0.2s ease, box-shadow 0.2s ease; +} +.hover-lift:hover { + transform: translateY(-4px); + box-shadow: 0 12px 24px rgba(0,0,0,0.15); +} +.hover-glow { + transition: box-shadow 0.3s ease; +} +.hover-glow:hover { + box-shadow: 0 0 20px rgba(var(--wp--preset--color--accent-rgb, 0,0,0), 0.3); +} +``` + +**Continuous ambient motion:** +```css +.float { + animation: float 3s ease-in-out infinite; +} +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} +.pulse-subtle { + animation: pulse 2s ease-in-out infinite; +} +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.7; } +} +``` + +### Scroll-Triggered Reveals via functions.php + +> For advanced scroll-driven interactivity (parallax, counters, dynamic content loading, state-driven animations), use the **`wp-interactivity-api` skill** instead of raw IntersectionObserver. The pattern below is a lightweight CSS-only approach suitable for simple reveal animations. + +The most impactful animation pattern — sections revealing as the user scrolls — requires a small IntersectionObserver script. Add this to `functions.php`: + +```php +// Scroll animation observer — outputs inline script +function theme_slug_scroll_animations() { + ?> + + +
+ +
+ +``` + +### prefers-reduced-motion (Required) + +Every theme MUST include this in `style.css`: + +```css +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + transition-duration: 0.01ms !important; + } + /* Elements that start hidden for entrance animations must become visible */ + .animate-on-scroll, + .fade-up, + .fade-in, + .slide-in-left, + .slide-in-right, + .scale-up { + opacity: 1 !important; + transform: none !important; + } +} +``` + +### Editor Visibility (Required) + +The theme's `style.css` loads in both the front-end and the WordPress block editor (via `enqueue_block_assets`). However, the IntersectionObserver script that triggers scroll reveals runs only on the front-end (`wp_footer`), not inside the editor iframe. This means any block with an entrance animation class that sets `opacity: 0` will be **invisible in the block editor**. + +WordPress wraps all editor content inside `
`. This class does not exist on the front-end, so it is the correct selector for editor-only overrides. + +Every theme MUST include a block like this in `style.css`, covering **every** entrance animation class used in the theme: + +```css +/* === Editor: keep animated content visible while editing === */ +.editor-styles-wrapper .fade-up, +.editor-styles-wrapper .fade-in, +.editor-styles-wrapper .slide-in-left, +.editor-styles-wrapper .slide-in-right, +.editor-styles-wrapper .scale-up, +.editor-styles-wrapper .animate-on-scroll, +.editor-styles-wrapper .stagger-children > * { + opacity: 1 !important; + transform: none !important; + animation: none !important; + transition: none !important; +} +``` + +If the theme uses any custom animation class that sets `opacity: 0` or uses `transform` as an initial hidden state, add a corresponding `.editor-styles-wrapper` selector to this block. The rule is simple: every class that hides content for animation purposes needs an editor override that makes it visible. + +### How Much Animation + +Not every element needs animation. Prioritize: +- **Hero section entrance** — the first impression (fade-up, scale, or slide) +- **Section reveals on scroll** — major content blocks with `animate-on-scroll` +- **Interactive elements** — cards with `hover-lift`, buttons with transitions +- **1-2 decorative ambient animations** — a floating shape, gradient shift, or pulsing accent + +Avoid animating every heading, paragraph, and button individually — it creates visual noise rather than delight. + +## Card Layouts in Rows + +For equal-height, equal-width cards (with optional bottom-aligned CTAs), use this structure unless the user specifies otherwise: + +``` +Columns (className: "equal-cards") + └── Column + verticalAlignment: "stretch" + width: "X%" where X = 100 / number_of_cards (e.g., 2 cards = 50%, 3 cards = 33.33%, 4 cards = 25%) + └── Group [card wrapper] + └── [content: headings, paragraphs, images*, lists] + └── (optional) Buttons (className: "cta-bottom") +``` + +**Width rule**: All cards in a row MUST have equal width. Calculate each column's width as `100% / number_of_cards` (e.g., 3 cards = 33.33% each). The sum of all column widths must equal exactly 100% - never exceed the parent element width. + +*Images in cards: `style="height:200px;object-fit:cover;width:100%"` + +**Required CSS** (style.css): +```css +.equal-cards > .wp-block-column { + display: flex; + flex-direction: column; + flex-grow: 0; +} +.equal-cards > .wp-block-column > .wp-block-group { + display: flex; + flex-direction: column; + flex-grow: 1; +} +``` +If present, ensure bottom-aligned CTAs unless otherwise specified: +```css +.equal-cards .cta-bottom { + margin-top: auto; + justify-content: center; +} +``` +Always add the following CSS to reset the footer top margin: +```css +.wp-site-blocks > footer { + margin-block-start: 0; +} +``` + +## Landing Page Composition + +When generating homepage block markup, think like a landing page designer, not a template assembler. Every section should be a visually distinct, full-width band that creates rhythm and visual impact as the user scrolls. + +### Section Architecture + +- **Section margin reset**: Add `"style":{"spacing":{"margin":{"top":"0"}}}` to every top-level Group block that wraps a landing page section. This overrides WordPress's default top margin on direct children of `.wp-site-blocks` and can be easily adjusted by users in the editor. +- **Section layout widths**: Hero sections, header groups, cover blocks, and feature grids should use `"align":"wide"` or `"align":"full"` rather than defaulting to narrow content width. Only use default (content) alignment for text-heavy reading sections. +- Do **not** use ``; output the full expanded markup inside each block. +- **Columns alignment**: ALWAYS set `"align":"wide"` on `wp:columns` blocks (and add the `alignwide` class on the wrapper div) unless specifically instructed otherwise. +- **No decorative HTML comments**: Never insert section-labeling comments like `` or `` in templates, template parts, or patterns. Only WordPress block comments (``) are allowed. +- **No HTML blocks**: Never use ``. See Common Block Mistakes below. + +### Common Block Mistakes + +These are the most frequent errors when generating block markup. Every example shows the **wrong** approach and the **correct** decomposition into core blocks. + +**1. Complex layout dumped into an HTML block** + +WRONG — opaque blob, not editable in the block editor: +```html + +
+
+

Fast

+

Lightning-quick load times.

+
+
+

Secure

+

Enterprise-grade security.

+
+
+ +``` + +RIGHT — each element is a selectable, editable core block: +```html + +
+ +
+ +

Fast

+ + +

Lightning-quick load times.

+ +
+ + +
+ +

Secure

+ + +

Enterprise-grade security.

+ +
+ +
+ +``` + +**2. Styled heading in an HTML block** + +WRONG: +```html + +

Our Services

+ +``` + +RIGHT — use `className` on the core heading block, define `.gradient-text` and `.section-title` in `style.css`: +```html + +

Our Services

+ +``` + +**3. Entire styled section in an HTML block** + +WRONG: +```html + +
+

Ready to get started?

+

Join thousands of happy customers.

+ Sign Up Free +
+ +``` + +RIGHT — use `wp:group` with `className`, define `.cta-band` styles in `style.css`: +```html + +
+ +

Ready to get started?

+ + +

Join thousands of happy customers.

+ + +
+ + + +
+ +
+ +``` + +**4. Decorative HTML comments** + +WRONG: +```html + + +... + + + + +... + +``` + +RIGHT — remove the non-block comments entirely: +```html + +... + + + +... + +``` + +**Every major homepage section must be `alignfull`** — edge-to-edge across the viewport. Content inside can be constrained, but the section wrapper fills the screen width. This is the **full-bleed wrapper, constrained content** pattern: + +`` + +**Never use bare `{"layout":{"type":"constrained"}}` without `"align":"full"` for homepage sections.** Without `alignfull`, sections render at `contentSize` (800px) and the page looks narrow and lifeless. + +**YOU DECIDE** which sections best serve this specific site. Do not follow a rigid template. Consider the site type, audience, and primary goal: + +- A portfolio needs a full-bleed project gallery +- A SaaS needs feature grids with clear value props +- A restaurant needs appetizing imagery and menu sections +- An agency needs case study cards and social proof +- An escape room needs atmosphere and immersion + +### Visual Rhythm + +Alternate between visual treatments to create rhythm as the user scrolls: + +| Technique | WordPress Implementation | +|-----------|------------------------| +| Alternating backgrounds | Alternate `backgroundColor` between `background` and `surface` (or `primary`/`secondary` for bold sections) | +| Full-bleed imagery | Cover blocks with `"align":"full"` and `overlayColor` from the brand palette | +| Edge-to-edge media-text | `wp:media-text` with `"align":"full"` for alternating image/content sides | +| Bold CTA bands | Full-width group with `primary` or `accent` background, centered text | +| Spacer breaks | `wp:spacer` between sections for breathing room | + +Every section should feel visually distinct from its neighbors. If two adjacent sections have the same background color and layout pattern, the page feels monotonous — change the background, flip the image side, switch from grid to single-column, or add a cover block break. + +## Image Handling + +ONLY add user provided images/image URLs to the initial site build. Stock image urls often fail to load in the block editor and break the design. +Look at any user supplied images carefully and include them in the design if appropriate, but do not force them in if they do not fit the design. + +### Creating Visual Richness Without Images + +Since only user provided images/image URLs can be used, if none are available convey atmosphere and visual interest through: + +- **CSS Gradients**: Linear, radial, and conic gradients for depth and color +- **Color Blocks**: Bold use of background colors to create visual hierarchy +- **Typography as Design**: Large, distinctive headings; creative font pairing; varied text sizes and weights +- **CSS Patterns**: Repeating backgrounds using CSS gradients (stripes, dots, grids) +- **Shadows & Depth**: Box-shadow, text-shadow, and drop-shadow for dimension +- **Borders & Frames**: Creative use of borders, outlines, and decorative frames +- **Spacing & Layout**: Generous whitespace or controlled density to create mood +- **CSS Pseudo-elements**: ::before and ::after for decorative visual elements +- **Color Overlays**: Layered divs with transparency for atmospheric effects diff --git a/apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md b/apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md new file mode 100644 index 0000000000..32706d5ca6 --- /dev/null +++ b/apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md @@ -0,0 +1,183 @@ +--- +name: wp-interactivity-api +description: "Use when building or debugging WordPress Interactivity API features (data-wp-* directives, @wordpress/interactivity store/state/actions, block viewScriptModule integration, wp_interactivity_*()) including performance, hydration, and directive behavior." +compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+). Filesystem-based agent with bash + node. Some workflows require WP-CLI." +--- + +# WP Interactivity API + +## When to use + +Use this skill when the user mentions: + +- Interactivity API, `@wordpress/interactivity`, +- `data-wp-interactive`, `data-wp-on--*`, `data-wp-bind--*`, `data-wp-context`, +- block `viewScriptModule` / module-based view scripts, +- hydration issues or "directives don't fire", +- scroll-triggered animations, parallax, counters, or dynamic state changes in WordPress themes/blocks. + +## Related Skills + +- **Load `wordpress-block-theming`** when building a full block theme that includes interactivity. It covers theme.json, templates, patterns, and CSS animation classes that complement Interactivity API directives. +- **Load `frontend-design`** to ensure interactive features serve a good design — visual hierarchy, restraint in animation, and purposeful motion rather than decorative noise. + +## Inputs required + +- Which block/theme/plugin surfaces are affected (frontend, editor, both). +- Any constraints: WP version, whether modules are supported in the build. + +## Procedure + +### 1) Detect existing usage + integration style + +Search for: + +- `data-wp-interactive` +- `@wordpress/interactivity` +- `viewScriptModule` + +Decide: + +- Is this a block providing interactivity via `block.json` view script module? +- Is this theme-level interactivity? +- Is this plugin-side "enhance existing markup" usage? + +If you're creating a new interactive block (not just debugging), prefer the official scaffold template: + +- `@wordpress/create-block-interactive-template` (via `@wordpress/create-block`) + +### 2) Identify the store(s) + +Locate store definitions and confirm: + +- state shape, +- actions (mutations), +- callbacks/event handlers used by `data-wp-on--*`. + +### 3) Server-side rendering (best practice) + +**Pre-render HTML on the server** before outputting to ensure: + +- Correct initial state in the HTML before JavaScript loads (no layout shift). +- SEO benefits and faster perceived load time. +- Seamless hydration when the client-side JavaScript takes over. + +#### Enable server directive processing + +For components using `block.json`, add `supports.interactivity`: + +```json +{ + "supports": { + "interactivity": true + } +} +``` + +For themes/plugins without `block.json`, use `wp_interactivity_process_directives()` to process directives. + +#### Initialize state/context in PHP + +Use `wp_interactivity_state()` to define initial global state: + +```php +wp_interactivity_state( 'myPlugin', array( + 'items' => array( 'Apple', 'Banana', 'Cherry' ), + 'hasItems' => true, +)); +``` + +For local context, use `wp_interactivity_data_wp_context()`: + +```php + false ); +?> +
> + ... +
+``` + +#### Define derived state in PHP + +When derived state affects initial HTML rendering, replicate the logic in PHP: + +```php +wp_interactivity_state( 'myPlugin', array( + 'items' => array( 'Apple', 'Banana' ), + 'hasItems' => function() { + $state = wp_interactivity_state(); + return count( $state['items'] ) > 0; + } +)); +``` + +This ensures directives like `data-wp-bind--hidden="!state.hasItems"` render correctly on first load. + +For detailed examples and patterns, see `references/server-side-rendering.md`. + +### 4) Implement or change directives safely + +When touching markup directives: + +- keep directive usage minimal and scoped, +- prefer stable data attributes that map clearly to store state, +- ensure server-rendered markup + client hydration align. + +**WordPress 6.9 changes:** + +- **`data-wp-ignore` is deprecated** and will be removed in future versions. +- **Unique directive IDs**: Multiple directives of the same type can now exist on one element using the `---` separator (e.g., `data-wp-on--click---plugin-a="..."` and `data-wp-on--click---plugin-b="..."`). +- **New TypeScript types**: `AsyncAction` and `TypeYield` help with async action typing. + +For quick directive reminders, see `references/directives-quickref.md`. + +### 5) Build/tooling alignment + +Verify the repo supports the required module build path: + +- if it uses `@wordpress/scripts`, prefer its conventions. +- if it uses custom bundling, confirm module output is supported. + +### 6) Debug common failure modes + +If "nothing happens" on interaction: + +- confirm the `viewScriptModule` is enqueued/loaded, +- confirm the DOM element has `data-wp-interactive`, +- confirm the store namespace matches the directive's value, +- confirm there are no JS errors before hydration. + +See `references/debugging.md`. + +## Verification + +- Manual smoke test: directive triggers and state updates as expected. +- If tests exist: add/extend Playwright E2E around the interaction path. + +## Failure modes / debugging + +- Directives present but inert: + - view script not loading, wrong module entrypoint, or missing `data-wp-interactive`. +- Hydration mismatch / flicker: + - server markup differs from client expectations; simplify or align initial state. + - derived state not defined in PHP: use `wp_interactivity_state()` with closures. +- Initial content missing or wrong: + - `supports.interactivity` not set in `block.json` (for blocks). + - `wp_interactivity_process_directives()` not called (for themes/plugins). + - state/context not initialized in PHP before render. +- Layout shift on load: + - derived state like `state.hasItems` missing on server, causing `hidden` attribute to be absent. +- Performance regressions: + - overly broad interactive roots; scope interactivity to smaller subtrees. +- Client-side navigation issues (WordPress 6.9): + - `getServerState()` and `getServerContext()` now reset between page transitions. + - Router regions now support `attachTo` for rendering overlays dynamically. + +## Escalation + +- If repo build constraints are unclear, ask: "Is this using `@wordpress/scripts` or a custom bundler (webpack/vite)?" +- Consult: + - `references/server-side-rendering.md` + - `references/directives-quickref.md` + - `references/debugging.md` diff --git a/apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md b/apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md new file mode 100644 index 0000000000..688a1f65d1 --- /dev/null +++ b/apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md @@ -0,0 +1,28 @@ +# Debugging checklist + +1. Confirm the interactive root exists in the rendered HTML (`data-wp-interactive`). +2. Confirm the view script module is loaded (network + source maps). +3. Confirm store namespace matches what markup expects. +4. Check console for errors before any interaction. +5. Reduce scope: + - temporarily remove directives to isolate which directive/store path breaks. +6. If hydration mismatch occurs: + - ensure initial state/context matches server markup. + +## WordPress 6.9 specific issues + +**State not persisting across navigation:** +- `getServerState()` and `getServerContext()` now reset between client-side page transitions. +- If you relied on stale values persisting, refactor to use the store's reactive state instead. + +**Multiple plugins conflicting on same element:** +- Use unique directive IDs with the `---` separator to avoid attribute collisions. +- Example: `data-wp-on--click---my-plugin="actions.handle"` + +**`data-wp-ignore` not working:** +- This directive is deprecated in 6.9 and will be removed. It caused context inheritance and navigation bugs. +- Find an alternative approach (conditional rendering, separate interactive regions). + +**Router regions / overlays not rendering:** +- WordPress 6.9 adds `attachTo` property for router regions to render overlays anywhere on the page. +- Ensure nested router regions are properly structured. diff --git a/apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md b/apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md new file mode 100644 index 0000000000..34a94d2dd7 --- /dev/null +++ b/apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md @@ -0,0 +1,29 @@ +# Directives quick reference (high level) + +Common directives to recognize in markup: + +- `data-wp-interactive`: declares an interactive region (and often a store namespace). +- `data-wp-context`: provides server-rendered context/state. +- `data-wp-on--event`: attaches event handlers (e.g. `click`, `submit`). +- `data-wp-on-async--event`: async event handlers (preferred for most actions). +- `data-wp-bind--attr`: binds DOM attributes to state. +- `data-wp-class--name`: toggles CSS classes based on state. + +Use these as search anchors when triaging bugs. + +## Unique directive IDs (WordPress 6.9+) + +HTML doesn't allow duplicate attributes. To attach multiple handlers of the same type from different plugins, use the `---` separator: + +```html + +
+ Content for +
+
+``` + +## Derived State on the Server + +When derived state affects the initial HTML, define it in PHP to avoid layout shifts. + +### Static Derived State + +When the derived value is known at render time: + +```php +$fruits = array( 'Apple', 'Banana', 'Cherry' ); +$hasFruits = count( $fruits ) > 0; + +wp_interactivity_state( 'myPlugin', array( + 'fruits' => $fruits, + 'hasFruits' => $hasFruits, +)); +``` + +### Dynamic Derived State (using closures) + +When the value depends on context (e.g., inside `data-wp-each` loops): + +```php +wp_interactivity_state( 'myPlugin', array( + 'fruits' => array( 'apple', 'banana', 'cherry' ), + 'shoppingList' => array( 'apple', 'cherry' ), + 'onShoppingList' => function() { + $state = wp_interactivity_state(); + $context = wp_interactivity_get_context(); + return in_array( $context['item'], $state['shoppingList'] ) ? 'Yes' : 'No'; + }, +)); +``` + +The closure is evaluated during directive processing for each element. + +## Complete Example: List with Server Rendering + +### PHP (render callback or template) + +```php + $fruits, + 'hasFruits' => count( $fruits ) > 0, + 'mango' => __( 'Mango' ), +)); +?> + +
+ + + +
    + +
+ +

+ +

+
+``` + +### JavaScript (view.js) + +```javascript +import { store, getContext } from '@wordpress/interactivity'; + +const { state } = store( 'myFruitPlugin', { + state: { + get hasFruits() { + return state.fruits.length > 0; + }, + }, + actions: { + addMango() { + state.fruits.push( state.mango ); + }, + clearAll() { + state.fruits = []; + }, + }, +}); +``` + +### Rendered Output (initial HTML) + +```html +
+ + + +
    +
  • Apple
  • +
  • Banana
  • +
  • Cherry
  • +
+ + +
+``` + +The `hidden` attribute is added server-side because `state.hasFruits` is true. + +## Serializing Values for Client Use + +Use `wp_interactivity_state()` to pass server values to client JavaScript: + +### Translations + +```php +wp_interactivity_state( 'myPlugin', array( + 'labels' => array( + 'add' => __( 'Add Item', 'textdomain' ), + 'remove' => __( 'Remove Item', 'textdomain' ), + 'empty' => __( 'No items found', 'textdomain' ), + ), +)); +``` + +### Ajax URLs and Nonces + +```php +wp_interactivity_state( 'myPlugin', array( + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'myPlugin_nonce' ), + 'restUrl' => rest_url( 'myPlugin/v1/' ), +)); +``` + +### Client Usage + +```javascript +const { state } = store( 'myPlugin', { + actions: { + *fetchData() { + const formData = new FormData(); + formData.append( 'action', 'my_action' ); + formData.append( '_ajax_nonce', state.nonce ); + + const response = yield fetch( state.ajaxUrl, { + method: 'POST', + body: formData, + }); + return yield response.json(); + }, + }, +}); +``` + +## Themes and Plugins without block.json + +For themes or plugins not using `block.json`, use `wp_interactivity_process_directives()`: + +```php + false, +)); + +ob_start(); +?> + + + + +

No fruits

+``` + +### State Not Matching Client Expectations + +Ensure PHP and JavaScript derived state logic matches: + +```php +// PHP +'hasFruits' => count( $fruits ) > 0, +``` + +```javascript +// JavaScript - must match PHP logic +get hasFruits() { + return state.fruits.length > 0; +} +``` + +## External References + +- [WordPress: Server-side rendering](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/server-side-rendering/) +- [WordPress: Understanding global state, local context and derived state](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state/) +- [WordPress: Interactivity API Reference](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/) diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index 6636d57f51..4c7d0b7635 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -1,20 +1,46 @@ export function buildSystemPrompt(): string { - return `You are a WordPress development assistant integrated with WordPress Studio, a local WordPress development environment. - -You have access to Studio-specific tools to manage WordPress sites: -- studio_list_sites: List all local WordPress sites with their status -- studio_get_site_info: Get details about a specific site by name or path -- studio_start_site: Start a WordPress site -- studio_stop_site: Stop a WordPress site -- studio_run_wp_cli: Run WP-CLI commands on a site (install plugins, manage options, query the database, etc.) - -You also have standard file system and shell tools for working with WordPress code. - -Guidelines: -- When working with WordPress sites, always check which sites exist first using studio_list_sites. -- Before running WP-CLI commands, ensure the target site is running using studio_start_site if needed. -- For file operations on WordPress sites, use the site's path from studio_get_site_info. -- Do NOT modify WordPress core files directly. Use WP-CLI or the Studio tools instead. -- When creating themes or plugins, follow WordPress coding standards. + return `You are the AI assistant built into WordPress Studio CLI. You manage and modify local WordPress sites using your Studio tools. + +IMPORTANT: You MUST use your mcp__studio__ tools to manage WordPress sites. Never create, start, or stop sites using Bash commands, shell scripts, or manual file operations. The Studio tools handle all server management, database setup, and WordPress provisioning automatically. + +## Workflow + +For any request that involves a WordPress site, you MUST first determine which site to use: + +- **"Create" / "build" / "make" a site**: Call site_create with a name as your FIRST tool call. Do NOT call site_list first. Do NOT reuse or repurpose any existing site. Every new project gets a fresh site. +- **User names a specific existing site**: Call site_list to find it. +- **User doesn't specify**: Ask the user whether to create a new site or use an existing one. + +Then continue with: + +1. **Get site details**: Use site_info to get the site path, URL, and credentials. +2. **Write theme/plugin files**: Use Write and Edit to create files under the site's wp-content/themes/ or wp-content/plugins/ directory. +3. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. + +## Available Studio Tools (prefixed with mcp__studio__) + +- site_create: Create a new WordPress site (name only — handles everything automatically) +- site_list: List all local WordPress sites with their status +- site_info: Get details about a specific site (path, URL, credentials, running status) +- site_start: Start a stopped site +- site_stop: Stop a running site +- wp_cli: Run WP-CLI commands on a running site + +## Skills + +You have access to specialized skills via the Skill tool. ALWAYS load relevant skills BEFORE writing any code: + +- **wordpress-block-theming**: Load when creating or modifying themes. ALL themes MUST be block themes (Full Site Editing). Never create classic themes. +- **frontend-design**: Load when building any user-facing design. Defines visual quality standards. +- **wp-interactivity-api**: Load when adding animations, scroll effects, or dynamic behavior. + +Load skills early in your workflow — they contain critical rules and patterns you must follow. + +## Rules + +- Do NOT modify WordPress core files. Only work within wp-content/. +- Before running wp_cli, ensure the site is running (site_start if needed). +- NEVER use \`\` blocks. Use native WordPress blocks only. +- NEVER insert raw HTML comments like \`\` or \`\` in templates or patterns. The ONLY comments allowed are WordPress block comments (\`\` / \`\`). - Be concise in your responses and focus on actionable results.`; } diff --git a/apps/cli/ai/tools.ts b/apps/cli/ai/tools.ts index 9219565908..28898992d0 100644 --- a/apps/cli/ai/tools.ts +++ b/apps/cli/ai/tools.ts @@ -1,16 +1,22 @@ +import path from 'path'; import { tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; +import { DEFAULT_PHP_VERSION } from '@studio/common/constants'; import { z } from 'zod'; -import { readAppdata, getSiteByFolder, getSiteUrl, type SiteData } from 'cli/lib/appdata'; +import { runCommand as runCreateSiteCommand } from 'cli/commands/site/create'; +import { runCommand as runListSitesCommand } from 'cli/commands/site/list'; +import { runCommand as runStartSiteCommand } from 'cli/commands/site/start'; +import { runCommand as runStatusCommand } from 'cli/commands/site/status'; +import { runCommand as runStopSiteCommand, Mode as StopMode } from 'cli/commands/site/stop'; +import { getSiteByFolder, getSiteUrl, readAppdata, type SiteData } from 'cli/lib/appdata'; +import { setProgressCallback } from 'cli/logger'; import { connect, disconnect } from 'cli/lib/pm2-manager'; -import { isSiteRunning } from 'cli/lib/site-utils'; -import { keepSqliteIntegrationUpdated } from 'cli/lib/sqlite-integration'; -import { - isServerRunning, - startWordPressServer, - stopWordPressServer, - sendWpCliCommand, -} from 'cli/lib/wordpress-server-manager'; -import { Logger } from 'cli/logger'; +import { isServerRunning, sendWpCliCommand } from 'cli/lib/wordpress-server-manager'; + +export function setToolProgressHandler( handler: ( message: string ) => void ): void { + setProgressCallback( handler ); +} + +const SITES_ROOT = path.join( process.env.HOME || '~', 'Studio' ); async function findSiteByName( name: string ): Promise< SiteData | undefined > { const appdata = await readAppdata(); @@ -38,71 +44,110 @@ function textResult( text: string ) { }; } -const listSitesTool = tool( - 'site_list', - 'Lists all WordPress sites managed by Studio with their name, path, URL, PHP version, and running status.', - z.object( {} ), - async () => { +/** + * Captures console.log output during a function call. + * Used for commands (list, status) that print JSON to console instead of returning data. + */ +async function captureConsoleOutput( fn: () => Promise< void > ): Promise< string > { + let captured = ''; + const origLog = console.log; + const origTable = console.table; + console.log = ( ...args: unknown[] ) => { + captured += args.map( String ).join( ' ' ) + '\n'; + }; + console.table = ( ...args: unknown[] ) => { + captured += args.map( String ).join( ' ' ) + '\n'; + }; + try { + await fn(); + } finally { + console.log = origLog; + console.table = origTable; + } + return captured.trim(); +} + +const createSiteTool = tool( + 'site_create', + 'Creates a new WordPress site with the latest WordPress version. Automatically sets up the site directory, installs WordPress, registers the site, and starts the server. Returns the site URL and credentials.', + z.object( { + name: z.string().describe( 'The name for the new site (e.g., "My Coffee Shop")' ), + } ), + async ( args ) => { try { - const appdata = await readAppdata(); - if ( appdata.sites.length === 0 ) { - return textResult( 'No sites found.' ); - } + const slug = args.name + .toLowerCase() + .replace( /[^a-z0-9]+/g, '-' ) + .replace( /^-|-$/g, '' ); + const sitePath = path.join( SITES_ROOT, slug ); - try { - await connect(); - const sites = []; - for ( const site of appdata.sites ) { - const running = await isSiteRunning( site ); - sites.push( { + await runCreateSiteCommand( sitePath, { + name: args.name, + wpVersion: 'latest', + phpVersion: DEFAULT_PHP_VERSION, + enableHttps: false, + noStart: false, + skipBrowser: true, + skipLogDetails: true, + } ); + + // Read back the created site to return its details + const site = await resolveSite( args.name ); + const url = getSiteUrl( site ); + return textResult( + JSON.stringify( + { name: site.name, path: site.path, - url: getSiteUrl( site ), + url, + adminUrl: `${ url }/wp-admin`, + username: 'admin', + password: site.adminPassword, phpVersion: site.phpVersion, - running, - } ); - } - return textResult( JSON.stringify( sites, null, 2 ) ); - } finally { - await disconnect(); - } + }, + null, + 2 + ) + ); } catch ( error ) { - return errorResult( `Failed to list sites: ${ error instanceof Error ? error.message : String( error ) }` ); + return errorResult( + `Failed to create site: ${ error instanceof Error ? error.message : String( error ) }` + ); + } + } +); + +const listSitesTool = tool( + 'site_list', + 'Lists all WordPress sites managed by Studio with their name, path, URL, and running status.', + z.object( {} ), + async () => { + try { + const output = await captureConsoleOutput( () => runListSitesCommand( 'json' ) ); + return textResult( output || 'No sites found.' ); + } catch ( error ) { + return errorResult( + `Failed to list sites: ${ error instanceof Error ? error.message : String( error ) }` + ); } } ); const getSiteInfoTool = tool( 'site_info', - 'Gets detailed information about a specific WordPress site by name or path, including its running status, URL, PHP version, and custom domain.', + 'Gets detailed information about a specific WordPress site by name or path, including its running status, URL, PHP version, and admin credentials.', z.object( { nameOrPath: z.string().describe( 'The site name or file system path to the site' ), } ), async ( args ) => { try { const site = await resolveSite( args.nameOrPath ); - let running = false; - - try { - await connect(); - running = await isSiteRunning( site ); - } finally { - await disconnect(); - } - - const info = { - name: site.name, - path: site.path, - url: getSiteUrl( site ), - phpVersion: site.phpVersion, - running, - customDomain: site.customDomain || null, - enableHttps: site.enableHttps || false, - }; - - return textResult( JSON.stringify( info, null, 2 ) ); + const output = await captureConsoleOutput( () => runStatusCommand( site.path, 'json' ) ); + return textResult( output || 'No site info available.' ); } catch ( error ) { - return errorResult( `Failed to get site info: ${ error instanceof Error ? error.message : String( error ) }` ); + return errorResult( + `Failed to get site info: ${ error instanceof Error ? error.message : String( error ) }` + ); } } ); @@ -116,26 +161,12 @@ const startSiteTool = tool( async ( args ) => { try { const site = await resolveSite( args.nameOrPath ); - - try { - await connect(); - - const runningProcess = await isServerRunning( site.id ); - if ( runningProcess ) { - return textResult( `Site "${ site.name }" is already running at ${ getSiteUrl( site ) }` ); - } - - await keepSqliteIntegrationUpdated( site.path ); - - const logger = new Logger< string >(); - await startWordPressServer( site, logger ); - - return textResult( `Site "${ site.name }" started at ${ getSiteUrl( site ) }` ); - } finally { - await disconnect(); - } + await runStartSiteCommand( site.path, true, true ); + return textResult( `Site "${ site.name }" started at ${ getSiteUrl( site ) }` ); } catch ( error ) { - return errorResult( `Failed to start site: ${ error instanceof Error ? error.message : String( error ) }` ); + return errorResult( + `Failed to start site: ${ error instanceof Error ? error.message : String( error ) }` + ); } } ); @@ -149,26 +180,17 @@ const stopSiteTool = tool( async ( args ) => { try { const site = await resolveSite( args.nameOrPath ); - - try { - await connect(); - - const runningProcess = await isServerRunning( site.id ); - if ( ! runningProcess ) { - return textResult( `Site "${ site.name }" is not running.` ); - } - - await stopWordPressServer( site.id ); - return textResult( `Site "${ site.name }" stopped.` ); - } finally { - await disconnect(); - } + await runStopSiteCommand( StopMode.STOP_SINGLE_SITE, site.path, false ); + return textResult( `Site "${ site.name }" stopped.` ); } catch ( error ) { - return errorResult( `Failed to stop site: ${ error instanceof Error ? error.message : String( error ) }` ); + return errorResult( + `Failed to stop site: ${ error instanceof Error ? error.message : String( error ) }` + ); } } ); +// Note: wp.ts runCommand calls process.exit(), so we use the lower-level sendWpCliCommand directly. const runWpCliTool = tool( 'wp_cli', 'Runs a WP-CLI command on a specific WordPress site. The site must be running. Examples: "plugin install woocommerce --activate", "option get blogname", "user list".', @@ -189,7 +211,9 @@ const runWpCliTool = tool( const runningProcess = await isServerRunning( site.id ); if ( ! runningProcess ) { - return errorResult( `Site "${ site.name }" is not running. Start it first using site_start.` ); + return errorResult( + `Site "${ site.name }" is not running. Start it first using site_start.` + ); } const wpCliArgs = args.command.split( /\s+/ ); @@ -207,14 +231,20 @@ const runWpCliTool = tool( } return { - content: [ { type: 'text' as const, text: output || 'Command completed with no output.' } ], + content: [ + { type: 'text' as const, text: output || 'Command completed with no output.' }, + ], isError: result.exitCode !== 0, }; } finally { await disconnect(); } } catch ( error ) { - return errorResult( `Failed to run WP-CLI command: ${ error instanceof Error ? error.message : String( error ) }` ); + return errorResult( + `Failed to run WP-CLI command: ${ + error instanceof Error ? error.message : String( error ) + }` + ); } } ); @@ -223,6 +253,13 @@ export function createStudioTools() { return createSdkMcpServer( { name: 'studio', version: '1.0.0', - tools: [ listSitesTool, getSiteInfoTool, startSiteTool, stopSiteTool, runWpCliTool ], + tools: [ + createSiteTool, + listSitesTool, + getSiteInfoTool, + startSiteTool, + stopSiteTool, + runWpCliTool, + ], } ); } diff --git a/apps/cli/ai/ui.ts b/apps/cli/ai/ui.ts index e62fd98f43..db00e4f918 100644 --- a/apps/cli/ai/ui.ts +++ b/apps/cli/ai/ui.ts @@ -1,4 +1,3 @@ -import chalk from 'chalk'; import { TUI, ProcessTerminal, @@ -13,6 +12,7 @@ import { type EditorOptions, type MarkdownTheme, } from '@mariozechner/pi-tui'; +import chalk from 'chalk'; import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; import type { AskUserQuestion } from 'cli/ai/agent'; @@ -99,6 +99,7 @@ const editorTheme: EditorTheme = { }; const toolDisplayNames: Record< string, string > = { + mcp__studio__site_create: 'Creating site', mcp__studio__site_list: 'Listing sites', mcp__studio__site_info: 'Getting site info', mcp__studio__site_start: 'Starting site', @@ -114,8 +115,52 @@ const toolDisplayNames: Record< string, string > = { Task: 'Running task', }; -function formatToolName( name: string ): string { - return toolDisplayNames[ name ] ?? name; +function getToolDetail( name: string, input: Record< string, unknown > ): string { + switch ( name ) { + case 'mcp__studio__site_create': + return typeof input.name === 'string' ? input.name : ''; + case 'mcp__studio__site_info': + case 'mcp__studio__site_start': + case 'mcp__studio__site_stop': + return typeof input.nameOrPath === 'string' ? input.nameOrPath : ''; + case 'mcp__studio__wp_cli': + return typeof input.command === 'string' ? `wp ${ input.command }` : ''; + case 'Read': + case 'Write': + case 'Edit': { + const filePath = input.file_path ?? input.path; + if ( typeof filePath === 'string' ) { + const parts = filePath.split( '/' ); + return parts.slice( -2 ).join( '/' ); + } + return ''; + } + case 'Bash': + return typeof input.command === 'string' + ? input.command.length > 60 + ? input.command.slice( 0, 57 ) + '…' + : input.command + : ''; + case 'Skill': + return typeof input.skill === 'string' ? input.skill : ''; + case 'Grep': + return typeof input.pattern === 'string' ? input.pattern : ''; + case 'Glob': + return typeof input.pattern === 'string' ? input.pattern : ''; + default: + return ''; + } +} + +function formatToolName( name: string, input?: Record< string, unknown > ): string { + const displayName = toolDisplayNames[ name ] ?? name; + if ( input ) { + const detail = getToolDetail( name, input ); + if ( detail ) { + return `${ displayName }: ${ detail }`; + } + } + return displayName; } export class AiChatUI { @@ -199,6 +244,12 @@ export class AiChatUI { this.tui.requestRender(); } + setLoaderMessage( message: string ): void { + if ( this.loaderVisible ) { + this.loader.setMessage( message ); + } + } + private showLoader(): void { if ( ! this.loaderVisible ) { this.tui.addChild( this.loader ); @@ -313,7 +364,10 @@ export class AiChatUI { if ( q.options.length > 0 ) { questionText += '\n'; questionText += q.options - .map( ( opt, i ) => chalk.dim( ` ${ i + 1 }. ` ) + opt.label + chalk.dim( ` — ${ opt.description }` ) ) + .map( + ( opt, i ) => + chalk.dim( ` ${ i + 1 }. ` ) + opt.label + chalk.dim( ` — ${ opt.description }` ) + ) .join( '\n' ); } this.messages.addChild( new Text( questionText, 0, 0 ) ); @@ -365,7 +419,8 @@ export class AiChatUI { this.tui.requestRender(); } else if ( block.type === 'tool_use' ) { this.showLoader(); - this.loader.setMessage( formatToolName( block.name ) ); + const input = ( block as { input?: Record< string, unknown > } ).input; + this.loader.setMessage( formatToolName( block.name, input ) ); } } // Always show the loader after processing — the agent turn is still active @@ -395,7 +450,9 @@ export class AiChatUI { parts.push( ...message.errors ); } if ( message.subtype === 'error_max_turns' ) { - parts.push( 'Reached the maximum number of turns. Use --max-turns to increase the limit.' ); + parts.push( + 'Reached the maximum number of turns. Use --max-turns to increase the limit.' + ); } else if ( message.subtype ) { parts.push( `(${ message.subtype })` ); } diff --git a/apps/cli/commands/ai.ts b/apps/cli/commands/ai.ts index 8ab02b82e7..51c80fbb76 100644 --- a/apps/cli/commands/ai.ts +++ b/apps/cli/commands/ai.ts @@ -2,6 +2,7 @@ import { password } from '@inquirer/prompts'; import { __ } from '@wordpress/i18n'; import { AiCommandLoggerAction as LoggerAction } from '@studio/common/logger-actions'; import { startAiAgent } from 'cli/ai/agent'; +import { setToolProgressHandler } from 'cli/ai/tools'; import { getAnthropicApiKey, saveAnthropicApiKey } from 'cli/lib/appdata'; import { AiChatUI } from 'cli/ai/ui'; import { Logger, LoggerError } from 'cli/logger'; @@ -34,6 +35,7 @@ export async function runCommand( options: { maxTurns?: number } ): Promise< voi const apiKey = await resolveApiKey(); const ui = new AiChatUI(); + setToolProgressHandler( ( message ) => ui.setLoaderMessage( message ) ); ui.start(); let sessionId: string | undefined; diff --git a/apps/cli/lib/pm2-manager.ts b/apps/cli/lib/pm2-manager.ts index 77e364b8d2..da826a10c4 100644 --- a/apps/cli/lib/pm2-manager.ts +++ b/apps/cli/lib/pm2-manager.ts @@ -92,6 +92,7 @@ export async function disconnect(): Promise< void > { return; } isConnected = false; + pm2Bus = null; resolve(); } ); } ); diff --git a/apps/cli/logger.ts b/apps/cli/logger.ts index 566d3a830f..0197dd58c4 100644 --- a/apps/cli/logger.ts +++ b/apps/cli/logger.ts @@ -2,10 +2,21 @@ import ora, { Ora } from 'ora'; const isIpcMode = Boolean( process.send ); +type ProgressCallback = ( message: string ) => void; +let progressCallback: ProgressCallback | null = null; + +export function setProgressCallback( callback: ProgressCallback | null ): void { + progressCallback = callback; +} + function canSend(): boolean { return isIpcMode && !! process.send && process.connected; } +function hasCallback(): boolean { + return progressCallback !== null; +} + export class LoggerError extends Error { previousError?: Error; private errorMessage: string; @@ -44,6 +55,10 @@ export class Logger< T extends string > { process.send!( { action, status: 'inprogress', message } ); return; } + if ( hasCallback() ) { + progressCallback!( message ); + return; + } this.spinner.start( message ); } @@ -53,6 +68,11 @@ export class Logger< T extends string > { return; } + if ( hasCallback() ) { + progressCallback!( message ); + return; + } + // Update the spinner text and force render this.spinner.text = message; if ( ! this.spinner.isSpinning ) { @@ -65,6 +85,8 @@ export class Logger< T extends string > { public reportSuccess( message: string, shouldClearSpinner = false ) { if ( canSend() ) { process.send!( { action: this.currentAction, status: 'success', message } ); + } else if ( hasCallback() ) { + progressCallback!( message ); } else if ( shouldClearSpinner ) { this.spinner.clear(); } else { @@ -79,6 +101,10 @@ export class Logger< T extends string > { process.send!( { action: this.currentAction, status: 'warning', message } ); return; } + if ( hasCallback() ) { + progressCallback!( message ); + return; + } this.spinner.warn( message ); } @@ -89,6 +115,8 @@ export class Logger< T extends string > { if ( canSend() ) { process.send!( { action: this.currentAction, status: 'fail', message: error.message } ); + } else if ( hasCallback() ) { + progressCallback!( error.message ); } else { this.spinner.fail( error.message ); } From 24cdb93ebdef9abadfbe0aabeef4e544920c9727 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 24 Feb 2026 23:33:09 +0100 Subject: [PATCH 04/12] Simplify studio ai agent: remove skills plugin, inline rules in system prompt Remove the SDK plugin/skills system and move block content rules directly into the system prompt. Switch to Sonnet 4.6, add PM2 keepAlive to prevent stale bus connections, fix WP-CLI quoted argument parsing, and fix lint issues. Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/agent.ts | 6 +- apps/cli/ai/plugin/.claude-plugin/plugin.json | 4 - .../ai/plugin/skills/creating-blocks/SKILL.md | 34 - .../references/dynamic-vs-static.md | 17 - .../references/file-templates.md | 77 -- .../references/inner-blocks.md | 13 - .../ai/plugin/skills/frontend-design/SKILL.md | 82 --- .../skills/wordpress-block-theming/SKILL.md | 687 ------------------ .../skills/wp-interactivity-api/SKILL.md | 183 ----- .../references/debugging.md | 28 - .../references/directives-quickref.md | 29 - .../references/server-side-rendering.md | 310 -------- apps/cli/ai/system-prompt.ts | 29 +- apps/cli/ai/tools.ts | 49 +- apps/cli/commands/ai.ts | 10 +- apps/cli/lib/pm2-manager.ts | 12 +- apps/cli/vite.config.ts | 8 - 17 files changed, 77 insertions(+), 1501 deletions(-) delete mode 100644 apps/cli/ai/plugin/.claude-plugin/plugin.json delete mode 100644 apps/cli/ai/plugin/skills/creating-blocks/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md delete mode 100644 apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md delete mode 100644 apps/cli/ai/plugin/skills/creating-blocks/references/inner-blocks.md delete mode 100644 apps/cli/ai/plugin/skills/frontend-design/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md delete mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md delete mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md delete mode 100644 apps/cli/ai/plugin/skills/wp-interactivity-api/references/server-side-rendering.md diff --git a/apps/cli/ai/agent.ts b/apps/cli/ai/agent.ts index 27e4bae839..25a410b84b 100644 --- a/apps/cli/ai/agent.ts +++ b/apps/cli/ai/agent.ts @@ -1,4 +1,3 @@ -import path from 'path'; import { query, type Query } from '@anthropic-ai/claude-agent-sdk'; import { buildSystemPrompt } from 'cli/ai/system-prompt'; import { createStudioTools } from 'cli/ai/tools'; @@ -39,8 +38,6 @@ function buildEnv( apiKey: string ): Record< string, string > { export function startAiAgent( config: AiAgentConfig ): Query { const { prompt, apiKey, maxTurns = 10, resume, onAskUser } = config; - const pluginPath = path.resolve( __dirname, 'plugin' ); - return query( { prompt, options: { @@ -53,7 +50,6 @@ export function startAiAgent( config: AiAgentConfig ): Query { mcpServers: { studio: createStudioTools(), }, - plugins: [ { type: 'local', path: pluginPath } ], maxTurns, cwd: process.cwd(), permissionMode: 'bypassPermissions', @@ -73,7 +69,7 @@ export function startAiAgent( config: AiAgentConfig ): Query { } return { behavior: 'allow' as const, updatedInput: input }; }, - allowedTools: [ 'mcp__studio__*', 'Skill', 'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep' ], + allowedTools: [ 'mcp__studio__*', 'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep' ], model: 'claude-sonnet-4-6', resume, }, diff --git a/apps/cli/ai/plugin/.claude-plugin/plugin.json b/apps/cli/ai/plugin/.claude-plugin/plugin.json deleted file mode 100644 index 5874f8fe91..0000000000 --- a/apps/cli/ai/plugin/.claude-plugin/plugin.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "studio-wp", - "description": "WordPress development skills for Studio AI" -} diff --git a/apps/cli/ai/plugin/skills/creating-blocks/SKILL.md b/apps/cli/ai/plugin/skills/creating-blocks/SKILL.md deleted file mode 100644 index 6c75af501a..0000000000 --- a/apps/cli/ai/plugin/skills/creating-blocks/SKILL.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: creating-blocks -description: Templates and guidelines for creating new WordPress blocks from scratch — load this before generating block files ---- - -## When to use me - -Use this skill when creating a new block from scratch. -Do not use this skill when modifying an existing block. - -## Generating Blocks Instructions - -- Always use plain JS for the view.js file -- Always use the current folder to generate the block -- Always remove space before the PHP opening tag, nor leave more than one empty new line at the end of any file -- Always use block props to make sure the block is selectable in the canvas -- Always use the lowercase for block name slugs -- Never close the last PHP opening tag, you always leave it open -- Never suggest or generate code outside the specification, such as REST controllers, PHP classes, or other code that is not part of a standard WordPress block -- Prefer building blocks that have the same behaviour on the frontend and the backend, unless the user asks for a block that is specifically different on the frontend -- Do not opt for placeholders in the editor -- Be proactive in adding inspector or block toolbar controls that can make the block more interactive and customizable -- Never redeclare functions in PHP and always guard with function_exists -- Do not create additional markdown files with documentation or installation instructions -- Make sure the block is registered correctly so that it renders on the front end -- Use the InnerBlocks component from @wordpress/block-editor as much as possible. - -## Reference Files - -Before generating block files, read the relevant references from the `references/` directory next to this skill file. - -- **`references/file-templates.md`** — REQUIRED: read this before generating any block files. Contains templates for all 11 block file types (block.json, edit.js, save.js, render.php, etc.) -- **`references/inner-blocks.md`** — read this if the block uses InnerBlocks or child blocks -- **`references/dynamic-vs-static.md`** — read this if you need to decide between a dynamic or static rendering approach diff --git a/apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md b/apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md deleted file mode 100644 index f30828fb83..0000000000 --- a/apps/cli/ai/plugin/skills/creating-blocks/references/dynamic-vs-static.md +++ /dev/null @@ -1,17 +0,0 @@ -# Dynamic vs Static Blocks - -A block can be either dynamic or static: - -**Static Block:** -- Output saved in post-content -- Uses save.js -- No render.php -- "render" not in block.json -- Best for fixed content - -**Dynamic Block:** -- Output rendered by PHP at runtime -- Uses render.php -- No save.js -- "render": "file:./render.php" in block.json -- Best for live or database-driven content diff --git a/apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md b/apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md deleted file mode 100644 index 2fb943e113..0000000000 --- a/apps/cli/ai/plugin/skills/creating-blocks/references/file-templates.md +++ /dev/null @@ -1,77 +0,0 @@ -# Block File Templates - -### readme.md -Purpose: WordPress plugin readme with metadata and documentation. -- Follow WordPress plugin readme format -- Include the plugin header with Contributors, Tags, License -- Provide Description, Installation, FAQ, Changelog sections - -### block-slug.php -Purpose: Main WordPress plugin file that registers the block. -- Include WordPress plugin header -- Guard with ABSPATH check -- Register a block with register_block_type() -- Always guard functions with function_exists() -- Never close the final PHP tag -- No spaces before `` -- Use the allowedBlocks prop to restrict which blocks can be inserted as children -- Set orientation="horizontal" when inner blocks are displayed horizontally -- Use template prop to define a pre-filled block structure -- Combine template with templateLock="all" to prevent any changes, or templateLock="insert" to prevent additions but allow reordering -- Use defaultBlock with directInsert={true} to auto-insert a specific block type when the appender is clicked -- Define block relationships in block.json: use parent for direct descendants only, ancestor for any level -- For advanced cases, consider useInnerBlocksProps hook instead of the component diff --git a/apps/cli/ai/plugin/skills/frontend-design/SKILL.md b/apps/cli/ai/plugin/skills/frontend-design/SKILL.md deleted file mode 100644 index aa1a06da6a..0000000000 --- a/apps/cli/ai/plugin/skills/frontend-design/SKILL.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -name: frontend-design -description: "Use when generating any user-facing HTML, CSS, or WordPress markup. Enforces design excellence: purposeful layouts, sophisticated typography, meaningful whitespace, and visual hierarchy. Prevents generic 'AI-generated' aesthetics." ---- - -# Frontend Design Excellence - -## When to use - -Apply this skill to ALL visual output — landing pages, themes, patterns, single-page layouts, any HTML/CSS generation. This skill defines the quality bar. - -## Related Skills - -- **Load `wordpress-block-theming`** for WordPress FSE implementation details — theme.json, block templates, patterns, and the className pattern for connecting CSS animations to WordPress blocks. -- **Load `wp-interactivity-api`** when implementing scroll-triggered animations, dynamic state, or any JavaScript-driven interactivity in WordPress. It's the native way to add motion and behavior to block themes. - -## Core Principles - -### 1. No AI Slop - -AI-generated designs share recognizable patterns that immediately signal "template." Avoid all of these: - -- **Generic hero patterns**: Big heading + subtitle + two buttons centered on a gradient. Instead, design heroes that match the brand's personality — asymmetric layouts, bold typography treatments, unexpected compositions. -- **Emoji headings**: Never prefix section headings with emoji. Use typography, color, and spacing to create visual hierarchy. -- **Uniform card grids**: Three identical cards with icon + heading + paragraph is the hallmark of AI design. Vary card sizes, use asymmetric grids, feature one item prominently, or use entirely different layouts. If equal cards are genuinely needed (pricing tiers, feature comparison), vary the visual treatment — different background colors, featured/highlighted card, varied imagery. -- **Stock phrases**: "Elevate your experience," "Unlock the power of," "Transform your workflow." Write copy that is specific to the actual product/service. -- **Decorative filler sections**: Every section must earn its place. If it doesn't serve the user's goal, remove it. -- **Rainbow gradients and generic blobs**: Use intentional color that reinforces brand identity. - -### 2. Visual Hierarchy - -Every page needs a clear reading order: - -- **One dominant element per viewport**: The eye needs a starting point. Usually the hero heading or a striking image. -- **Size contrast**: If everything is the same size, nothing stands out. Create drama with scale differences — a massive heading paired with small body text, a full-bleed image next to a narrow text column. -- **Whitespace is structure**: Generous spacing between sections creates rhythm. Tight spacing within groups creates unity. Use spacing intentionally, not uniformly. -- **Color as signal**: Reserve your accent/primary color for CTAs and key interactive elements. Using it everywhere dilutes its impact. - -### 3. Typography as Design - -Typography is the single most impactful design tool: - -- **Choose distinctive fonts**: Skip Arial, Inter, Open Sans. Use fonts with character — Clash Display, Cabinet Grotesk, Instrument Serif, Space Grotesk, Fraunces, Playfair Display. Pair a distinctive display font with a refined body font. -- **Size with intention**: Body text at 1rem. Headings should create a clear scale. Hero headings can be large (clamp-based), but avoid sizes above 4rem — they rarely improve design. -- **Weight and style variation**: Use bold, italic, uppercase tracking, and font-weight differences to create typographic hierarchy without relying solely on size. -- **Line height matters**: Body text 1.5-1.65. Headings 1.1-1.3. Tight leading on large display text creates visual density and impact. - -### 4. Color with Purpose - -- **Start with a limited palette**: 2-3 colors maximum for most designs. Expand with tints/shades, not new hues. -- **Dark backgrounds create drama**: Don't default to white backgrounds. Dark sections with light text create visual weight and break up page monotony. -- **Contrast ratios**: Ensure text meets WCAG AA (4.5:1 for body text, 3:1 for large text). This is non-negotiable. -- **Background alternation**: Create visual rhythm by alternating section backgrounds — but make each transition feel intentional, not mechanical. - -### 5. Layout Composition - -- **Break the grid occasionally**: A mostly-constrained layout with one full-bleed element creates visual interest. Predictable grids feel static. -- **Asymmetry over symmetry**: Slightly off-center compositions feel more dynamic and designed. Centered layouts are safe but rarely exciting. -- **Edge-to-edge sections**: Major page sections should be full-width (`alignfull`) with constrained content inside. Narrow pages feel like documents, not designs. -- **Vary section density**: Alternate between dense, content-rich sections and spacious, breathing-room sections. - -### 6. Motion and Interaction - -- **Scroll-triggered reveals**: Sections fading up as the user scrolls is the single most impactful animation. For WordPress block themes, see the `wordpress-block-theming` skill for the CSS className pattern and `wp-interactivity-api` for advanced directive-driven animations. -- **Hover states on interactive elements**: Cards, buttons, and links should respond to hover with subtle transforms or shadow changes. -- **Staggered animations**: When revealing a group of elements (cards, features), stagger their entrance for a cascading effect. -- **Restraint**: Animate section entrances and interactive elements. Don't animate every heading and paragraph — visual noise degrades the experience. -- **Respect `prefers-reduced-motion`**: Always include the media query to disable animations for users who prefer it. - -## Quality Checklist - -Before considering any design complete: - -- [ ] Does the hero section feel unique to this brand, not generic? -- [ ] Is there clear visual hierarchy — can you trace the reading order? -- [ ] Are fonts distinctive and well-paired? -- [ ] Does the color palette feel intentional and limited? -- [ ] Is there rhythm in section backgrounds and spacing? -- [ ] Do interactive elements have hover/focus states? -- [ ] Are scroll animations present but restrained? -- [ ] Is the design accessible (contrast, focus states, reduced-motion)? -- [ ] Could you tell this was hand-designed, not AI-generated? diff --git a/apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md b/apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md deleted file mode 100644 index 7dfbdaf8ed..0000000000 --- a/apps/cli/ai/plugin/skills/wordpress-block-theming/SKILL.md +++ /dev/null @@ -1,687 +0,0 @@ ---- -name: wordpress-block-theming -description: WordPress Full Site Editing (FSE) theme architecture. Use when generating theme.json, block templates, template parts, patterns, and functions.php for WordPress block themes. ---- - -# WordPress Block Theming Skill - -Comprehensive knowledge for building WordPress block themes using Full Site Editing (FSE) architecture. - -## Related Skills - -- **Always load `frontend-design`** alongside this skill. It defines the design quality bar — visual hierarchy, typography pairing, anti-AI-slop rules — that should govern all theme output. -- **Load `wp-interactivity-api`** when the design calls for scroll-triggered animations, dynamic state changes, or any interactivity beyond CSS. The Interactivity API is the WordPress-native way to add JavaScript-driven behavior to block themes. - -## Absolute Rules - -- **NO HTML BLOCKS**: Never use `` (the `core/html` block). HTML blocks are opaque blobs in the block editor — users cannot select, style, or rearrange individual elements inside them. Every piece of content MUST use a proper core block (`wp:group`, `wp:heading`, `wp:paragraph`, `wp:columns`, etc.). If you find yourself reaching for `wp:html`, stop and decompose the content into the correct core blocks with `className` attributes and CSS instead. -- **NO DECORATIVE HTML COMMENTS**: Never insert non-block HTML comments like `` or `` in templates, template parts, or patterns. The only HTML comments allowed are WordPress block delimiters (`` / ``). -- **NO EMOJIS**: Never use emojis anywhere in generated content - not in headings, paragraphs, button text, or any other text. This applies to all templates, patterns, and content. - -## Theme Architecture - -### Directory Structure - -``` -theme-slug/ -├── theme.json # Central configuration file -├── style.css # Theme metadata + custom CSS -├── functions.php # Asset enqueuing, pattern registration -├── templates/ # Block templates -│ ├── index.html # Main/fallback template -│ ├── single.html # Single post -│ ├── page.html # Single page -│ ├── archive.html # Archive listings -│ ├── search.html # Search results -│ └── 404.html # Not found -├── parts/ # Reusable template parts -│ ├── header.html # Site header -│ └── footer.html # Site footer -└── patterns/ # Block patterns - ├── hero.php - ├── features.php - └── cta.php -``` - -## theme.json Configuration - -The `theme.json` file is the central configuration for block themes. It defines: - -### Schema and Version - -```json -{ - "$schema": "https://schemas.wp.org/trunk/theme.json", - "version": 3 -} -``` - -### Settings - -Define available options for the editor: - -```json -{ - "settings": { - "appearanceTools": true, - "layout": { "contentSize": "800px", "wideSize": "1280px" }, - "color": { - "palette": [ /* 5 colors: primary, secondary, accent, light, dark */ ], - "defaultPalette": false, - "defaultGradients": false - }, - "typography": { - "fontFamilies": [ /* heading + body font families */ ], - "fontSizes": [ /* 5-6 step scale: small through huge */ ] - }, - "spacing": { - "units": ["px", "em", "rem", "%", "vw", "vh"], - "spacingSizes": [ /* 6 steps from compact to spacious */ ] - } - } -} -``` - -### Styles - -Define default styles for the site and blocks: - -```json -{ - "styles": { - "color": { /* background + text from palette */ }, - "typography": { /* body font family, medium size, line-height 1.5-1.65 */ }, - "elements": { - "heading": { /* heading font family, appropriate weight, line-height 1.1-1.3 */ }, - "link": { /* accent color */ }, - "button": { /* accent background, light text, border-radius */ } - } - } -} -``` - -## Typography - -See `frontend-design` skill for font selection and sizing principles. WordPress-specific implementation: - -- Register font families in `settings.typography.fontFamilies` in theme.json. Use Google Fonts enqueued via `functions.php`. -- Define a 6-step font size scale in `settings.typography.fontSizes`. A good scale: 0.875rem / 1rem / 1.25rem / 1.75rem / 2.25rem / clamp(2.5rem, 4vw, 3.5rem). -- **Line height**: Body text: 1.5–1.65. Headings: 1.1–1.3. Never go below 1.0 for any text. Apply via `styles.typography.lineHeight` and `styles.elements.heading.typography.lineHeight` in theme.json. - -## Block Templates - -Templates use WordPress block markup (HTML comments with JSON attributes). - -### Template Structure - -```html - - - -
- -
- - - -``` - -## Template Parts - -### Header Requirements -- Constrained layout group with site-appropriate background -- Flex row: `site-title` (level:0 — renders `

` not `

`) + `navigation` -- Appropriate padding using spacing presets - -### Footer Requirements -- Constrained layout group, matching or complementing header style -- Content varies by site type (copyright, social links, contact info, etc.) -- Include footer margin reset in style.css - -### Page Title -A page title template should be included by default, and should reflect in some way the design of the main landing page. If each page added shares the same page title template then the site will feel more cohesive and polished. It should use the `` block so the page title is dynamic and reflects the actual page/post title. - -## Block Patterns - -Patterns are PHP files that register reusable block content. - -### Pattern Registration - -```php - - -... - -``` - -## functions.php - -Keep functions.php minimal. Primary uses: - -### Google Fonts Enqueuing - -**IMPORTANT:** Always use `enqueue_block_assets` hook (not `wp_enqueue_scripts`) to ensure fonts load in BOTH the front-end AND block editor. - -```php -get( 'Version' ) - ); -} -add_action( 'enqueue_block_assets', 'theme_slug_enqueue_assets' ); - -// Register block patterns -function theme_slug_register_patterns() { - register_block_pattern_category( - 'theme-slug', - array( 'label' => __( 'Theme Patterns', 'theme-slug' ) ) - ); -} -add_action( 'init', 'theme_slug_register_patterns' ); -``` - -## Security in Generated Code - -- When `functions.php` outputs any user-derived value, use WordPress escaping functions: - - HTML context: `esc_html()` - - Attribute context: `esc_attr()` - - URL context: `esc_url()` -- Never use `eval()`, `create_function()`, `shell_exec()`, `exec()`, or `system()` in generated theme code -- Static block themes with hardcoded content (the default) do not need escaping — WordPress core blocks handle this. Escaping matters only if generating PHP that renders dynamic data. - -## style.css - -The style.css file contains theme metadata and custom CSS. -**Important**: Always bring across custom CSS from the design into style.css that is not achievable via theme.json, especially for layout and composition techniques that are critical to the design's aesthetics such as animation/motion. - -```css -/* -Theme Name: Theme Name -Theme URI: https://example.com -Author: Author Name -Author URI: https://example.com -Description: A beautiful WordPress block theme -Version: 1.0.0 -Requires at least: 6.0 -Tested up to: 6.7 -Requires PHP: 7.4 -License: GNU General Public License v2 or later -License URI: https://www.gnu.org/licenses/gpl-2.0.html -Text Domain: theme-slug -*/ -``` - -## Animation & Motion in Block Themes - -Animation brings life to block themes, but WordPress block markup requires a specific pattern to connect CSS animations to blocks. - -### The className Pattern - -Add animation classes to blocks via the `className` JSON attribute. WordPress renders this as a class on the wrapper div: - -```html - -
- -
- -``` - -This works on any block — groups, columns, headings, paragraphs, buttons, images: - -```html - -

Features

- - - -
- ... -
- -``` - -### Animation Classes in style.css - -Generate and adapt these classes (these are examples only, do not limit yourself to these) in each theme's `style.css`: - -**Entrance animations:** -```css -.fade-up { - opacity: 0; - transform: translateY(30px); - animation: fadeUp 0.6s ease forwards; -} -.fade-in { - opacity: 0; - animation: fadeIn 0.6s ease forwards; -} -.slide-in-left { - opacity: 0; - transform: translateX(-40px); - animation: slideIn 0.7s ease forwards; -} -.slide-in-right { - opacity: 0; - transform: translateX(40px); - animation: slideIn 0.7s ease forwards; -} - -@keyframes fadeUp { to { opacity: 1; transform: translateY(0); } } -@keyframes fadeIn { to { opacity: 1; } } -@keyframes slideIn { to { opacity: 1; transform: translateX(0); } } -``` - -**Staggered children** — delays applied via nth-child: -```css -.stagger-children > * { - opacity: 0; - transform: translateY(20px); - animation: fadeUp 0.5s ease forwards; -} -.stagger-children > *:nth-child(1) { animation-delay: 0.1s; } -.stagger-children > *:nth-child(2) { animation-delay: 0.2s; } -.stagger-children > *:nth-child(3) { animation-delay: 0.3s; } -.stagger-children > *:nth-child(4) { animation-delay: 0.4s; } -``` - -**Interactive transitions:** -```css -.hover-lift { - transition: transform 0.2s ease, box-shadow 0.2s ease; -} -.hover-lift:hover { - transform: translateY(-4px); - box-shadow: 0 12px 24px rgba(0,0,0,0.15); -} -.hover-glow { - transition: box-shadow 0.3s ease; -} -.hover-glow:hover { - box-shadow: 0 0 20px rgba(var(--wp--preset--color--accent-rgb, 0,0,0), 0.3); -} -``` - -**Continuous ambient motion:** -```css -.float { - animation: float 3s ease-in-out infinite; -} -@keyframes float { - 0%, 100% { transform: translateY(0); } - 50% { transform: translateY(-10px); } -} -.pulse-subtle { - animation: pulse 2s ease-in-out infinite; -} -@keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.7; } -} -``` - -### Scroll-Triggered Reveals via functions.php - -> For advanced scroll-driven interactivity (parallax, counters, dynamic content loading, state-driven animations), use the **`wp-interactivity-api` skill** instead of raw IntersectionObserver. The pattern below is a lightweight CSS-only approach suitable for simple reveal animations. - -The most impactful animation pattern — sections revealing as the user scrolls — requires a small IntersectionObserver script. Add this to `functions.php`: - -```php -// Scroll animation observer — outputs inline script -function theme_slug_scroll_animations() { - ?> - - -
- -
- -``` - -### prefers-reduced-motion (Required) - -Every theme MUST include this in `style.css`: - -```css -@media (prefers-reduced-motion: reduce) { - *, *::before, *::after { - animation-duration: 0.01ms !important; - transition-duration: 0.01ms !important; - } - /* Elements that start hidden for entrance animations must become visible */ - .animate-on-scroll, - .fade-up, - .fade-in, - .slide-in-left, - .slide-in-right, - .scale-up { - opacity: 1 !important; - transform: none !important; - } -} -``` - -### Editor Visibility (Required) - -The theme's `style.css` loads in both the front-end and the WordPress block editor (via `enqueue_block_assets`). However, the IntersectionObserver script that triggers scroll reveals runs only on the front-end (`wp_footer`), not inside the editor iframe. This means any block with an entrance animation class that sets `opacity: 0` will be **invisible in the block editor**. - -WordPress wraps all editor content inside `
`. This class does not exist on the front-end, so it is the correct selector for editor-only overrides. - -Every theme MUST include a block like this in `style.css`, covering **every** entrance animation class used in the theme: - -```css -/* === Editor: keep animated content visible while editing === */ -.editor-styles-wrapper .fade-up, -.editor-styles-wrapper .fade-in, -.editor-styles-wrapper .slide-in-left, -.editor-styles-wrapper .slide-in-right, -.editor-styles-wrapper .scale-up, -.editor-styles-wrapper .animate-on-scroll, -.editor-styles-wrapper .stagger-children > * { - opacity: 1 !important; - transform: none !important; - animation: none !important; - transition: none !important; -} -``` - -If the theme uses any custom animation class that sets `opacity: 0` or uses `transform` as an initial hidden state, add a corresponding `.editor-styles-wrapper` selector to this block. The rule is simple: every class that hides content for animation purposes needs an editor override that makes it visible. - -### How Much Animation - -Not every element needs animation. Prioritize: -- **Hero section entrance** — the first impression (fade-up, scale, or slide) -- **Section reveals on scroll** — major content blocks with `animate-on-scroll` -- **Interactive elements** — cards with `hover-lift`, buttons with transitions -- **1-2 decorative ambient animations** — a floating shape, gradient shift, or pulsing accent - -Avoid animating every heading, paragraph, and button individually — it creates visual noise rather than delight. - -## Card Layouts in Rows - -For equal-height, equal-width cards (with optional bottom-aligned CTAs), use this structure unless the user specifies otherwise: - -``` -Columns (className: "equal-cards") - └── Column - verticalAlignment: "stretch" - width: "X%" where X = 100 / number_of_cards (e.g., 2 cards = 50%, 3 cards = 33.33%, 4 cards = 25%) - └── Group [card wrapper] - └── [content: headings, paragraphs, images*, lists] - └── (optional) Buttons (className: "cta-bottom") -``` - -**Width rule**: All cards in a row MUST have equal width. Calculate each column's width as `100% / number_of_cards` (e.g., 3 cards = 33.33% each). The sum of all column widths must equal exactly 100% - never exceed the parent element width. - -*Images in cards: `style="height:200px;object-fit:cover;width:100%"` - -**Required CSS** (style.css): -```css -.equal-cards > .wp-block-column { - display: flex; - flex-direction: column; - flex-grow: 0; -} -.equal-cards > .wp-block-column > .wp-block-group { - display: flex; - flex-direction: column; - flex-grow: 1; -} -``` -If present, ensure bottom-aligned CTAs unless otherwise specified: -```css -.equal-cards .cta-bottom { - margin-top: auto; - justify-content: center; -} -``` -Always add the following CSS to reset the footer top margin: -```css -.wp-site-blocks > footer { - margin-block-start: 0; -} -``` - -## Landing Page Composition - -When generating homepage block markup, think like a landing page designer, not a template assembler. Every section should be a visually distinct, full-width band that creates rhythm and visual impact as the user scrolls. - -### Section Architecture - -- **Section margin reset**: Add `"style":{"spacing":{"margin":{"top":"0"}}}` to every top-level Group block that wraps a landing page section. This overrides WordPress's default top margin on direct children of `.wp-site-blocks` and can be easily adjusted by users in the editor. -- **Section layout widths**: Hero sections, header groups, cover blocks, and feature grids should use `"align":"wide"` or `"align":"full"` rather than defaulting to narrow content width. Only use default (content) alignment for text-heavy reading sections. -- Do **not** use ``; output the full expanded markup inside each block. -- **Columns alignment**: ALWAYS set `"align":"wide"` on `wp:columns` blocks (and add the `alignwide` class on the wrapper div) unless specifically instructed otherwise. -- **No decorative HTML comments**: Never insert section-labeling comments like `` or `` in templates, template parts, or patterns. Only WordPress block comments (``) are allowed. -- **No HTML blocks**: Never use ``. See Common Block Mistakes below. - -### Common Block Mistakes - -These are the most frequent errors when generating block markup. Every example shows the **wrong** approach and the **correct** decomposition into core blocks. - -**1. Complex layout dumped into an HTML block** - -WRONG — opaque blob, not editable in the block editor: -```html - -
-
-

Fast

-

Lightning-quick load times.

-
-
-

Secure

-

Enterprise-grade security.

-
-
- -``` - -RIGHT — each element is a selectable, editable core block: -```html - -
- -
- -

Fast

- - -

Lightning-quick load times.

- -
- - -
- -

Secure

- - -

Enterprise-grade security.

- -
- -
- -``` - -**2. Styled heading in an HTML block** - -WRONG: -```html - -

Our Services

- -``` - -RIGHT — use `className` on the core heading block, define `.gradient-text` and `.section-title` in `style.css`: -```html - -

Our Services

- -``` - -**3. Entire styled section in an HTML block** - -WRONG: -```html - -
-

Ready to get started?

-

Join thousands of happy customers.

- Sign Up Free -
- -``` - -RIGHT — use `wp:group` with `className`, define `.cta-band` styles in `style.css`: -```html - -
- -

Ready to get started?

- - -

Join thousands of happy customers.

- - -
- - - -
- -
- -``` - -**4. Decorative HTML comments** - -WRONG: -```html - - -... - - - - -... - -``` - -RIGHT — remove the non-block comments entirely: -```html - -... - - - -... - -``` - -**Every major homepage section must be `alignfull`** — edge-to-edge across the viewport. Content inside can be constrained, but the section wrapper fills the screen width. This is the **full-bleed wrapper, constrained content** pattern: - -`` - -**Never use bare `{"layout":{"type":"constrained"}}` without `"align":"full"` for homepage sections.** Without `alignfull`, sections render at `contentSize` (800px) and the page looks narrow and lifeless. - -**YOU DECIDE** which sections best serve this specific site. Do not follow a rigid template. Consider the site type, audience, and primary goal: - -- A portfolio needs a full-bleed project gallery -- A SaaS needs feature grids with clear value props -- A restaurant needs appetizing imagery and menu sections -- An agency needs case study cards and social proof -- An escape room needs atmosphere and immersion - -### Visual Rhythm - -Alternate between visual treatments to create rhythm as the user scrolls: - -| Technique | WordPress Implementation | -|-----------|------------------------| -| Alternating backgrounds | Alternate `backgroundColor` between `background` and `surface` (or `primary`/`secondary` for bold sections) | -| Full-bleed imagery | Cover blocks with `"align":"full"` and `overlayColor` from the brand palette | -| Edge-to-edge media-text | `wp:media-text` with `"align":"full"` for alternating image/content sides | -| Bold CTA bands | Full-width group with `primary` or `accent` background, centered text | -| Spacer breaks | `wp:spacer` between sections for breathing room | - -Every section should feel visually distinct from its neighbors. If two adjacent sections have the same background color and layout pattern, the page feels monotonous — change the background, flip the image side, switch from grid to single-column, or add a cover block break. - -## Image Handling - -ONLY add user provided images/image URLs to the initial site build. Stock image urls often fail to load in the block editor and break the design. -Look at any user supplied images carefully and include them in the design if appropriate, but do not force them in if they do not fit the design. - -### Creating Visual Richness Without Images - -Since only user provided images/image URLs can be used, if none are available convey atmosphere and visual interest through: - -- **CSS Gradients**: Linear, radial, and conic gradients for depth and color -- **Color Blocks**: Bold use of background colors to create visual hierarchy -- **Typography as Design**: Large, distinctive headings; creative font pairing; varied text sizes and weights -- **CSS Patterns**: Repeating backgrounds using CSS gradients (stripes, dots, grids) -- **Shadows & Depth**: Box-shadow, text-shadow, and drop-shadow for dimension -- **Borders & Frames**: Creative use of borders, outlines, and decorative frames -- **Spacing & Layout**: Generous whitespace or controlled density to create mood -- **CSS Pseudo-elements**: ::before and ::after for decorative visual elements -- **Color Overlays**: Layered divs with transparency for atmospheric effects diff --git a/apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md b/apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md deleted file mode 100644 index 32706d5ca6..0000000000 --- a/apps/cli/ai/plugin/skills/wp-interactivity-api/SKILL.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -name: wp-interactivity-api -description: "Use when building or debugging WordPress Interactivity API features (data-wp-* directives, @wordpress/interactivity store/state/actions, block viewScriptModule integration, wp_interactivity_*()) including performance, hydration, and directive behavior." -compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+). Filesystem-based agent with bash + node. Some workflows require WP-CLI." ---- - -# WP Interactivity API - -## When to use - -Use this skill when the user mentions: - -- Interactivity API, `@wordpress/interactivity`, -- `data-wp-interactive`, `data-wp-on--*`, `data-wp-bind--*`, `data-wp-context`, -- block `viewScriptModule` / module-based view scripts, -- hydration issues or "directives don't fire", -- scroll-triggered animations, parallax, counters, or dynamic state changes in WordPress themes/blocks. - -## Related Skills - -- **Load `wordpress-block-theming`** when building a full block theme that includes interactivity. It covers theme.json, templates, patterns, and CSS animation classes that complement Interactivity API directives. -- **Load `frontend-design`** to ensure interactive features serve a good design — visual hierarchy, restraint in animation, and purposeful motion rather than decorative noise. - -## Inputs required - -- Which block/theme/plugin surfaces are affected (frontend, editor, both). -- Any constraints: WP version, whether modules are supported in the build. - -## Procedure - -### 1) Detect existing usage + integration style - -Search for: - -- `data-wp-interactive` -- `@wordpress/interactivity` -- `viewScriptModule` - -Decide: - -- Is this a block providing interactivity via `block.json` view script module? -- Is this theme-level interactivity? -- Is this plugin-side "enhance existing markup" usage? - -If you're creating a new interactive block (not just debugging), prefer the official scaffold template: - -- `@wordpress/create-block-interactive-template` (via `@wordpress/create-block`) - -### 2) Identify the store(s) - -Locate store definitions and confirm: - -- state shape, -- actions (mutations), -- callbacks/event handlers used by `data-wp-on--*`. - -### 3) Server-side rendering (best practice) - -**Pre-render HTML on the server** before outputting to ensure: - -- Correct initial state in the HTML before JavaScript loads (no layout shift). -- SEO benefits and faster perceived load time. -- Seamless hydration when the client-side JavaScript takes over. - -#### Enable server directive processing - -For components using `block.json`, add `supports.interactivity`: - -```json -{ - "supports": { - "interactivity": true - } -} -``` - -For themes/plugins without `block.json`, use `wp_interactivity_process_directives()` to process directives. - -#### Initialize state/context in PHP - -Use `wp_interactivity_state()` to define initial global state: - -```php -wp_interactivity_state( 'myPlugin', array( - 'items' => array( 'Apple', 'Banana', 'Cherry' ), - 'hasItems' => true, -)); -``` - -For local context, use `wp_interactivity_data_wp_context()`: - -```php - false ); -?> -
> - ... -
-``` - -#### Define derived state in PHP - -When derived state affects initial HTML rendering, replicate the logic in PHP: - -```php -wp_interactivity_state( 'myPlugin', array( - 'items' => array( 'Apple', 'Banana' ), - 'hasItems' => function() { - $state = wp_interactivity_state(); - return count( $state['items'] ) > 0; - } -)); -``` - -This ensures directives like `data-wp-bind--hidden="!state.hasItems"` render correctly on first load. - -For detailed examples and patterns, see `references/server-side-rendering.md`. - -### 4) Implement or change directives safely - -When touching markup directives: - -- keep directive usage minimal and scoped, -- prefer stable data attributes that map clearly to store state, -- ensure server-rendered markup + client hydration align. - -**WordPress 6.9 changes:** - -- **`data-wp-ignore` is deprecated** and will be removed in future versions. -- **Unique directive IDs**: Multiple directives of the same type can now exist on one element using the `---` separator (e.g., `data-wp-on--click---plugin-a="..."` and `data-wp-on--click---plugin-b="..."`). -- **New TypeScript types**: `AsyncAction` and `TypeYield` help with async action typing. - -For quick directive reminders, see `references/directives-quickref.md`. - -### 5) Build/tooling alignment - -Verify the repo supports the required module build path: - -- if it uses `@wordpress/scripts`, prefer its conventions. -- if it uses custom bundling, confirm module output is supported. - -### 6) Debug common failure modes - -If "nothing happens" on interaction: - -- confirm the `viewScriptModule` is enqueued/loaded, -- confirm the DOM element has `data-wp-interactive`, -- confirm the store namespace matches the directive's value, -- confirm there are no JS errors before hydration. - -See `references/debugging.md`. - -## Verification - -- Manual smoke test: directive triggers and state updates as expected. -- If tests exist: add/extend Playwright E2E around the interaction path. - -## Failure modes / debugging - -- Directives present but inert: - - view script not loading, wrong module entrypoint, or missing `data-wp-interactive`. -- Hydration mismatch / flicker: - - server markup differs from client expectations; simplify or align initial state. - - derived state not defined in PHP: use `wp_interactivity_state()` with closures. -- Initial content missing or wrong: - - `supports.interactivity` not set in `block.json` (for blocks). - - `wp_interactivity_process_directives()` not called (for themes/plugins). - - state/context not initialized in PHP before render. -- Layout shift on load: - - derived state like `state.hasItems` missing on server, causing `hidden` attribute to be absent. -- Performance regressions: - - overly broad interactive roots; scope interactivity to smaller subtrees. -- Client-side navigation issues (WordPress 6.9): - - `getServerState()` and `getServerContext()` now reset between page transitions. - - Router regions now support `attachTo` for rendering overlays dynamically. - -## Escalation - -- If repo build constraints are unclear, ask: "Is this using `@wordpress/scripts` or a custom bundler (webpack/vite)?" -- Consult: - - `references/server-side-rendering.md` - - `references/directives-quickref.md` - - `references/debugging.md` diff --git a/apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md b/apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md deleted file mode 100644 index 688a1f65d1..0000000000 --- a/apps/cli/ai/plugin/skills/wp-interactivity-api/references/debugging.md +++ /dev/null @@ -1,28 +0,0 @@ -# Debugging checklist - -1. Confirm the interactive root exists in the rendered HTML (`data-wp-interactive`). -2. Confirm the view script module is loaded (network + source maps). -3. Confirm store namespace matches what markup expects. -4. Check console for errors before any interaction. -5. Reduce scope: - - temporarily remove directives to isolate which directive/store path breaks. -6. If hydration mismatch occurs: - - ensure initial state/context matches server markup. - -## WordPress 6.9 specific issues - -**State not persisting across navigation:** -- `getServerState()` and `getServerContext()` now reset between client-side page transitions. -- If you relied on stale values persisting, refactor to use the store's reactive state instead. - -**Multiple plugins conflicting on same element:** -- Use unique directive IDs with the `---` separator to avoid attribute collisions. -- Example: `data-wp-on--click---my-plugin="actions.handle"` - -**`data-wp-ignore` not working:** -- This directive is deprecated in 6.9 and will be removed. It caused context inheritance and navigation bugs. -- Find an alternative approach (conditional rendering, separate interactive regions). - -**Router regions / overlays not rendering:** -- WordPress 6.9 adds `attachTo` property for router regions to render overlays anywhere on the page. -- Ensure nested router regions are properly structured. diff --git a/apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md b/apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md deleted file mode 100644 index 34a94d2dd7..0000000000 --- a/apps/cli/ai/plugin/skills/wp-interactivity-api/references/directives-quickref.md +++ /dev/null @@ -1,29 +0,0 @@ -# Directives quick reference (high level) - -Common directives to recognize in markup: - -- `data-wp-interactive`: declares an interactive region (and often a store namespace). -- `data-wp-context`: provides server-rendered context/state. -- `data-wp-on--event`: attaches event handlers (e.g. `click`, `submit`). -- `data-wp-on-async--event`: async event handlers (preferred for most actions). -- `data-wp-bind--attr`: binds DOM attributes to state. -- `data-wp-class--name`: toggles CSS classes based on state. - -Use these as search anchors when triaging bugs. - -## Unique directive IDs (WordPress 6.9+) - -HTML doesn't allow duplicate attributes. To attach multiple handlers of the same type from different plugins, use the `---` separator: - -```html - -
- Content for -
-
-``` - -## Derived State on the Server - -When derived state affects the initial HTML, define it in PHP to avoid layout shifts. - -### Static Derived State - -When the derived value is known at render time: - -```php -$fruits = array( 'Apple', 'Banana', 'Cherry' ); -$hasFruits = count( $fruits ) > 0; - -wp_interactivity_state( 'myPlugin', array( - 'fruits' => $fruits, - 'hasFruits' => $hasFruits, -)); -``` - -### Dynamic Derived State (using closures) - -When the value depends on context (e.g., inside `data-wp-each` loops): - -```php -wp_interactivity_state( 'myPlugin', array( - 'fruits' => array( 'apple', 'banana', 'cherry' ), - 'shoppingList' => array( 'apple', 'cherry' ), - 'onShoppingList' => function() { - $state = wp_interactivity_state(); - $context = wp_interactivity_get_context(); - return in_array( $context['item'], $state['shoppingList'] ) ? 'Yes' : 'No'; - }, -)); -``` - -The closure is evaluated during directive processing for each element. - -## Complete Example: List with Server Rendering - -### PHP (render callback or template) - -```php - $fruits, - 'hasFruits' => count( $fruits ) > 0, - 'mango' => __( 'Mango' ), -)); -?> - -
- - - -
    - -
- -

- -

-
-``` - -### JavaScript (view.js) - -```javascript -import { store, getContext } from '@wordpress/interactivity'; - -const { state } = store( 'myFruitPlugin', { - state: { - get hasFruits() { - return state.fruits.length > 0; - }, - }, - actions: { - addMango() { - state.fruits.push( state.mango ); - }, - clearAll() { - state.fruits = []; - }, - }, -}); -``` - -### Rendered Output (initial HTML) - -```html -
- - - -
    -
  • Apple
  • -
  • Banana
  • -
  • Cherry
  • -
- - -
-``` - -The `hidden` attribute is added server-side because `state.hasFruits` is true. - -## Serializing Values for Client Use - -Use `wp_interactivity_state()` to pass server values to client JavaScript: - -### Translations - -```php -wp_interactivity_state( 'myPlugin', array( - 'labels' => array( - 'add' => __( 'Add Item', 'textdomain' ), - 'remove' => __( 'Remove Item', 'textdomain' ), - 'empty' => __( 'No items found', 'textdomain' ), - ), -)); -``` - -### Ajax URLs and Nonces - -```php -wp_interactivity_state( 'myPlugin', array( - 'ajaxUrl' => admin_url( 'admin-ajax.php' ), - 'nonce' => wp_create_nonce( 'myPlugin_nonce' ), - 'restUrl' => rest_url( 'myPlugin/v1/' ), -)); -``` - -### Client Usage - -```javascript -const { state } = store( 'myPlugin', { - actions: { - *fetchData() { - const formData = new FormData(); - formData.append( 'action', 'my_action' ); - formData.append( '_ajax_nonce', state.nonce ); - - const response = yield fetch( state.ajaxUrl, { - method: 'POST', - body: formData, - }); - return yield response.json(); - }, - }, -}); -``` - -## Themes and Plugins without block.json - -For themes or plugins not using `block.json`, use `wp_interactivity_process_directives()`: - -```php - false, -)); - -ob_start(); -?> - - - - -

No fruits

-``` - -### State Not Matching Client Expectations - -Ensure PHP and JavaScript derived state logic matches: - -```php -// PHP -'hasFruits' => count( $fruits ) > 0, -``` - -```javascript -// JavaScript - must match PHP logic -get hasFruits() { - return state.fruits.length > 0; -} -``` - -## External References - -- [WordPress: Server-side rendering](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/server-side-rendering/) -- [WordPress: Understanding global state, local context and derived state](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state/) -- [WordPress: Interactivity API Reference](https://developer.wordpress.org/block-editor/reference-guides/interactivity-api/api-reference/) diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index 4c7d0b7635..f932f99c06 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -15,7 +15,8 @@ Then continue with: 1. **Get site details**: Use site_info to get the site path, URL, and credentials. 2. **Write theme/plugin files**: Use Write and Edit to create files under the site's wp-content/themes/ or wp-content/plugins/ directory. -3. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. +3. **Validation** For each generated file with block content, ensure the validation rules defined a bit further down are followed and fix the content if not. +4. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. ## Available Studio Tools (prefixed with mcp__studio__) @@ -26,21 +27,19 @@ Then continue with: - site_stop: Stop a running site - wp_cli: Run WP-CLI commands on a running site -## Skills - -You have access to specialized skills via the Skill tool. ALWAYS load relevant skills BEFORE writing any code: - -- **wordpress-block-theming**: Load when creating or modifying themes. ALL themes MUST be block themes (Full Site Editing). Never create classic themes. -- **frontend-design**: Load when building any user-facing design. Defines visual quality standards. -- **wp-interactivity-api**: Load when adding animations, scroll effects, or dynamic behavior. - -Load skills early in your workflow — they contain critical rules and patterns you must follow. - -## Rules +## MUST follow rules - Do NOT modify WordPress core files. Only work within wp-content/. - Before running wp_cli, ensure the site is running (site_start if needed). -- NEVER use \`\` blocks. Use native WordPress blocks only. -- NEVER insert raw HTML comments like \`\` or \`\` in templates or patterns. The ONLY comments allowed are WordPress block comments (\`\` / \`\`). -- Be concise in your responses and focus on actionable results.`; +- when building themes, always build block themes (NO CLASSIC THEMES). +- Always add the style.css as editor styles in the functions.php of the theme to make the editor match the frontend. +- For theme and page content custom CSS, put the styles in the main style.css of the theme. No custom stylesheets. +- Avoid html blocks: Avoid using \`\` (the \`core/html\` block) when there are core block types that can achieve the same result. +- You MUST NEVER add decorative or random HTML comments: Never insert non-block HTML comments like \`\` or \`\` or \`\` or similar. +- The only HTML comments allowed are the block comments that define blocks (e.g. \`\`). +- You MUST NEVER use custom class names in inner DOM elements that are not block wrappers. No matter the block type, button, image or else. +- Avoid using the "core/group" block for empty divs that only have a styling purpose. You MUST use the "core/spacer" block for that instead. +- Do not use inline style or style block attribute: To add custom CSS and styles to blocks, you MUST use the \`className\` attribute and define styles in \`style.css\`. The custom classname should also be added to the outermost wrapper of the block (the element that corresponds to the block comment) and not to inner elements, as the block editor compares inner HTML against its expected output and any difference causes "invalid block" errors. +- Use patterns for complex block structures: For complex sections with multiple nested blocks, create a reusable pattern in the WordPress admin and then use the \`pattern\` attribute in your block comments to insert it. This ensures the content is editable and maintainable for users. +- If use have a group block with a style.background property in the block attributes, you MUST add a has-background class to the block's wrapper element classes.`; } diff --git a/apps/cli/ai/tools.ts b/apps/cli/ai/tools.ts index 28898992d0..55fab701b4 100644 --- a/apps/cli/ai/tools.ts +++ b/apps/cli/ai/tools.ts @@ -8,16 +8,59 @@ import { runCommand as runStartSiteCommand } from 'cli/commands/site/start'; import { runCommand as runStatusCommand } from 'cli/commands/site/status'; import { runCommand as runStopSiteCommand, Mode as StopMode } from 'cli/commands/site/stop'; import { getSiteByFolder, getSiteUrl, readAppdata, type SiteData } from 'cli/lib/appdata'; -import { setProgressCallback } from 'cli/logger'; -import { connect, disconnect } from 'cli/lib/pm2-manager'; +import { connect, disconnect, setKeepAlive } from 'cli/lib/pm2-manager'; import { isServerRunning, sendWpCliCommand } from 'cli/lib/wordpress-server-manager'; +import { setProgressCallback } from 'cli/logger'; export function setToolProgressHandler( handler: ( message: string ) => void ): void { setProgressCallback( handler ); } +export function enablePm2KeepAlive(): void { + setKeepAlive( true ); +} + const SITES_ROOT = path.join( process.env.HOME || '~', 'Studio' ); +/** + * Splits a command string into arguments, respecting quoted strings. + * Handles both single and double quotes, e.g.: + * post create --post_title="Ember & Oak" --post_type=page + * → ['post', 'create', '--post_title=Ember & Oak', '--post_type=page'] + */ +function splitCommandArgs( command: string ): string[] { + const args: string[] = []; + let current = ''; + let inQuote: string | null = null; + + for ( let i = 0; i < command.length; i++ ) { + const char = command[ i ]; + + if ( inQuote ) { + if ( char === inQuote ) { + inQuote = null; + } else { + current += char; + } + } else if ( char === '"' || char === "'" ) { + inQuote = char; + } else if ( /\s/.test( char ) ) { + if ( current ) { + args.push( current ); + current = ''; + } + } else { + current += char; + } + } + + if ( current ) { + args.push( current ); + } + + return args; +} + async function findSiteByName( name: string ): Promise< SiteData | undefined > { const appdata = await readAppdata(); return appdata.sites.find( ( site ) => site.name.toLowerCase() === name.toLowerCase() ); @@ -216,7 +259,7 @@ const runWpCliTool = tool( ); } - const wpCliArgs = args.command.split( /\s+/ ); + const wpCliArgs = splitCommandArgs( args.command ); const result = await sendWpCliCommand( site.id, wpCliArgs ); let output = ''; diff --git a/apps/cli/commands/ai.ts b/apps/cli/commands/ai.ts index 51c80fbb76..e59d1dd592 100644 --- a/apps/cli/commands/ai.ts +++ b/apps/cli/commands/ai.ts @@ -1,10 +1,10 @@ import { password } from '@inquirer/prompts'; -import { __ } from '@wordpress/i18n'; import { AiCommandLoggerAction as LoggerAction } from '@studio/common/logger-actions'; +import { __ } from '@wordpress/i18n'; import { startAiAgent } from 'cli/ai/agent'; -import { setToolProgressHandler } from 'cli/ai/tools'; -import { getAnthropicApiKey, saveAnthropicApiKey } from 'cli/lib/appdata'; +import { enablePm2KeepAlive, setToolProgressHandler } from 'cli/ai/tools'; import { AiChatUI } from 'cli/ai/ui'; +import { getAnthropicApiKey, saveAnthropicApiKey } from 'cli/lib/appdata'; import { Logger, LoggerError } from 'cli/logger'; import { StudioArgv } from 'cli/types'; @@ -36,12 +36,12 @@ export async function runCommand( options: { maxTurns?: number } ): Promise< voi const ui = new AiChatUI(); setToolProgressHandler( ( message ) => ui.setLoaderMessage( message ) ); + enablePm2KeepAlive(); ui.start(); let sessionId: string | undefined; try { - // eslint-disable-next-line no-constant-condition while ( true ) { const prompt = await ui.waitForInput(); @@ -58,7 +58,7 @@ export async function runCommand( options: { maxTurns?: number } ): Promise< voi // Escape key interrupts the current agent turn ui.onInterrupt = () => { - agentQuery.interrupt(); + void agentQuery.interrupt(); }; for await ( const message of agentQuery ) { diff --git a/apps/cli/lib/pm2-manager.ts b/apps/cli/lib/pm2-manager.ts index da826a10c4..3abbf79793 100644 --- a/apps/cli/lib/pm2-manager.ts +++ b/apps/cli/lib/pm2-manager.ts @@ -41,6 +41,16 @@ export interface ProcessEventData { const pm2 = new PM2( { pm2_home: STUDIO_PM2_HOME } ); let isConnected = false; +let keepAlive = false; + +/** + * When enabled, `disconnect()` becomes a no-op so the PM2 connection stays open + * for the lifetime of the process. Used by the AI agent to avoid reconnection + * issues when multiple tools call connect/disconnect in sequence. + */ +export function setKeepAlive( value: boolean ): void { + keepAlive = value; +} export async function connect(): Promise< void > { if ( isConnected ) { @@ -76,7 +86,7 @@ export async function connect(): Promise< void > { } export async function disconnect(): Promise< void > { - if ( ! isConnected ) { + if ( ! isConnected || keepAlive ) { return; } diff --git a/apps/cli/vite.config.ts b/apps/cli/vite.config.ts index 80156f0c19..6a925a77c8 100644 --- a/apps/cli/vite.config.ts +++ b/apps/cli/vite.config.ts @@ -56,14 +56,6 @@ export default defineConfig( { }, ] : [] ), - viteStaticCopy( { - targets: [ - { - src: 'ai/plugin', - dest: 'ai', - }, - ], - } ), ], build: { lib: { From d4cc9e9d75f08057489c59bafb4e82287c117606 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 24 Feb 2026 23:55:30 +0100 Subject: [PATCH 05/12] Add rule to avoid style.backgroundColor and style.textColor block attributes Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/system-prompt.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index f932f99c06..e08b7a835c 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -41,5 +41,6 @@ Then continue with: - Avoid using the "core/group" block for empty divs that only have a styling purpose. You MUST use the "core/spacer" block for that instead. - Do not use inline style or style block attribute: To add custom CSS and styles to blocks, you MUST use the \`className\` attribute and define styles in \`style.css\`. The custom classname should also be added to the outermost wrapper of the block (the element that corresponds to the block comment) and not to inner elements, as the block editor compares inner HTML against its expected output and any difference causes "invalid block" errors. - Use patterns for complex block structures: For complex sections with multiple nested blocks, create a reusable pattern in the WordPress admin and then use the \`pattern\` attribute in your block comments to insert it. This ensures the content is editable and maintainable for users. -- If use have a group block with a style.background property in the block attributes, you MUST add a has-background class to the block's wrapper element classes.`; +- If use have a group block with a style.background property in the block attributes, you MUST add a has-background class to the block's wrapper element classes. +- Do not use the style.backgroundColor or style.textColor block attributes.`; } From 15c2a3a85ec8fbd88e6f623e556441da71f00a50 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 25 Feb 2026 00:57:50 +0100 Subject: [PATCH 06/12] Add rule to avoid emojis in generated content Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/system-prompt.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index e08b7a835c..fc874b809b 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -42,5 +42,7 @@ Then continue with: - Do not use inline style or style block attribute: To add custom CSS and styles to blocks, you MUST use the \`className\` attribute and define styles in \`style.css\`. The custom classname should also be added to the outermost wrapper of the block (the element that corresponds to the block comment) and not to inner elements, as the block editor compares inner HTML against its expected output and any difference causes "invalid block" errors. - Use patterns for complex block structures: For complex sections with multiple nested blocks, create a reusable pattern in the WordPress admin and then use the \`pattern\` attribute in your block comments to insert it. This ensures the content is editable and maintainable for users. - If use have a group block with a style.background property in the block attributes, you MUST add a has-background class to the block's wrapper element classes. -- Do not use the style.backgroundColor or style.textColor block attributes.`; +- Do not use the style.backgroundColor or style.textColor block attributes. +- Avoid emojis: Never use emojis anywhere in generated content, prefer using engaging images. +`; } From 372c939587dfa8da4a3ad1bc8497ffeda777347a Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 25 Feb 2026 11:37:24 +0100 Subject: [PATCH 07/12] Add block validation tool for the AI agent Validates WordPress block content against registered save() functions using @wordpress/blocks + @wordpress/block-library with jsdom as DOM polyfill. The agent must now validate every file with block content and loop until 0 invalid blocks remain. Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/block-validator.ts | 294 ++++ apps/cli/ai/system-prompt.ts | 29 +- apps/cli/ai/tools.ts | 65 + apps/cli/ai/ui.ts | 6 + apps/cli/package.json | 3 + apps/cli/vite.config.ts | 8 + package-lock.json | 2453 ++++++++++++++++++++++++++++---- 7 files changed, 2591 insertions(+), 267 deletions(-) create mode 100644 apps/cli/ai/block-validator.ts diff --git a/apps/cli/ai/block-validator.ts b/apps/cli/ai/block-validator.ts new file mode 100644 index 0000000000..1c2eca817e --- /dev/null +++ b/apps/cli/ai/block-validator.ts @@ -0,0 +1,294 @@ +let initialized = false; +let initError: string | null = null; + +interface BlockValidationResult { + blockName: string; + isValid: boolean; + issues: string[]; + originalContent: string; + expectedContent?: string; +} + +export interface ValidationReport { + totalBlocks: number; + validBlocks: number; + invalidBlocks: number; + results: BlockValidationResult[]; + error?: string; +} + +/** + * Set up jsdom globals so WordPress packages (especially hpq) can use DOM APIs. + * Must be called before importing any @wordpress/* packages. + */ +function setupDomEnvironment(): void { + const { JSDOM } = require( 'jsdom' ); + const dom = new JSDOM( '', { + // Suppress CSS parsing errors from @wordpress/ui CSS modules + pretendToBeVisual: false, + } ); + + const g = global as Record< string, unknown >; + + // Copy all window properties to global (includes MutationObserver, etc.) + g.window = dom.window; + g.document = dom.window.document; + g.navigator = dom.window.navigator; + g.MutationObserver = dom.window.MutationObserver; + g.Node = dom.window.Node; + g.HTMLElement = dom.window.HTMLElement; + g.CustomEvent = dom.window.CustomEvent; + g.URL = dom.window.URL; + g.Element = dom.window.Element; + + // Mock matchMedia (used by @wordpress/components and others) + if ( ! dom.window.matchMedia ) { + dom.window.matchMedia = () => + ( { + matches: false, + media: '', + onchange: null, + addListener: () => {}, + removeListener: () => {}, + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => false, + } ) as unknown as MediaQueryList; + } + + // Mock requestAnimationFrame / requestIdleCallback + if ( ! g.requestAnimationFrame ) { + g.requestAnimationFrame = ( cb: () => void ) => setTimeout( cb, 0 ); + g.cancelAnimationFrame = ( id: number ) => clearTimeout( id ); + } + if ( ! g.requestIdleCallback ) { + g.requestIdleCallback = ( cb: ( deadline: { timeRemaining: () => number } ) => void ) => + setTimeout( () => cb( { timeRemaining: () => 50 } ), 0 ); + g.cancelIdleCallback = ( id: number ) => clearTimeout( id ); + } + + // Some WP packages check for ResizeObserver + if ( ! g.ResizeObserver ) { + g.ResizeObserver = class { + observe() {} + unobserve() {} + disconnect() {} + }; + } + + // IntersectionObserver mock + if ( ! g.IntersectionObserver ) { + g.IntersectionObserver = class { + observe() {} + unobserve() {} + disconnect() {} + }; + } +} + +/** + * Lazy initialization: set up DOM environment and register blocks on first use. + */ +function ensureInitialized(): void { + if ( initialized ) { + return; + } + + try { + setupDomEnvironment(); + } catch ( error ) { + initError = `Failed to set up DOM environment: ${ + error instanceof Error ? error.message : String( error ) + }. Make sure 'jsdom' is installed.`; + initialized = true; + return; + } + + // Suppress jsdom CSS parsing errors during @wordpress/block-library import + // (jsdom can't parse CSS modules used by @wordpress/ui and @wordpress/components) + const origError = console.error; + console.error = ( ...args: unknown[] ) => { + const msg = String( args[ 0 ] ); + if ( msg.includes( 'Could not parse CSS stylesheet' ) ) { + return; + } + origError( ...args ); + }; + + try { + const { registerCoreBlocks } = require( '@wordpress/block-library' ); + registerCoreBlocks(); + } catch ( error ) { + initError = `Failed to register core blocks: ${ + error instanceof Error ? error.message : String( error ) + }. Make sure '@wordpress/block-library' is installed.`; + } finally { + console.error = origError; + } + + // Verify that blocks were actually registered + if ( ! initError ) { + try { + const { getBlockTypes } = require( '@wordpress/blocks' ); + const types = getBlockTypes(); + if ( ! types || types.length === 0 ) { + initError = 'No block types were registered. Block validation will not work correctly.'; + } + } catch { + // If we can't even check, something is very wrong + initError = 'Failed to verify block registration.'; + } + } + + initialized = true; +} + +function formatLogItem( item: { args?: unknown[] } ): string { + if ( ! item.args || item.args.length === 0 ) { + return ''; + } + + const formatStr = String( item.args[ 0 ] ); + + // Skip the verbose "Block validation failed for `name` (blockType)" message — + // we already show the block name and the per-attribute issues are more useful. + if ( formatStr.startsWith( 'Block validation failed' ) ) { + return ''; + } + + let message = formatStr; + let argIndex = 1; + message = message.replace( /%[so]/g, () => { + if ( argIndex < ( item.args?.length ?? 0 ) ) { + const val = item.args![ argIndex++ ]; + if ( typeof val === 'object' ) { + return JSON.stringify( val ).slice( 0, 200 ); + } + return String( val ).slice( 0, 500 ); + } + return ''; + } ); + + return message; +} + +function tryGetSaveContent( block: { + name: string; + attributes: Record< string, unknown >; +} ): string | undefined { + try { + const { getSaveContent, getBlockType } = require( '@wordpress/blocks' ); + const blockType = getBlockType( block.name ); + if ( blockType ) { + return getSaveContent( blockType, block.attributes ); + } + } catch { + // If save content generation fails, skip + } + return undefined; +} + +interface ParsedBlock { + name: string | null; + attributes: Record< string, unknown >; + originalContent?: string; + innerBlocks?: ParsedBlock[]; +} + +function validateBlockList( blocks: ParsedBlock[], results: BlockValidationResult[] ): void { + const { validateBlock } = require( '@wordpress/blocks' ); + + for ( const block of blocks ) { + // Skip freeform blocks (raw HTML) and missing blocks (unregistered/PHP content) + if ( ! block.name || block.name === 'core/freeform' || block.name === 'core/missing' ) { + continue; + } + + try { + const [ isValid, logItems ] = validateBlock( block ) as [ + boolean, + Array< { args?: unknown[] } >, + ]; + + const issues: string[] = []; + for ( const item of logItems ) { + const msg = formatLogItem( item ); + if ( msg ) { + issues.push( msg ); + } + } + + results.push( { + blockName: block.name, + isValid, + issues, + originalContent: block.originalContent || '', + expectedContent: isValid + ? undefined + : tryGetSaveContent( block as { name: string; attributes: Record< string, unknown > } ), + } ); + } catch ( error ) { + results.push( { + blockName: block.name, + isValid: false, + issues: [ + `Validation error: ${ error instanceof Error ? error.message : String( error ) }`, + ], + originalContent: block.originalContent || '', + } ); + } + + // Recursively validate inner blocks + if ( block.innerBlocks && block.innerBlocks.length > 0 ) { + validateBlockList( block.innerBlocks, results ); + } + } +} + +/** + * Validate WordPress block content. + * Parses the content into blocks and validates each one against its registered save() function. + */ +export function validateBlocks( content: string ): ValidationReport { + ensureInitialized(); + + if ( initError ) { + return { + totalBlocks: 0, + validBlocks: 0, + invalidBlocks: 0, + results: [], + error: initError, + }; + } + + // Suppress WordPress's verbose console output during parse/validation + // (e.g., "Updated Block:", "Block successfully updated for ..." messages) + const origLog = console.log; + const origInfo = console.info; + const origWarn = console.warn; + console.log = () => {}; + console.info = () => {}; + console.warn = () => {}; + + try { + const { parse } = require( '@wordpress/blocks' ); + const blocks = parse( content ) as ParsedBlock[]; + const results: BlockValidationResult[] = []; + + validateBlockList( blocks, results ); + + const validCount = results.filter( ( r ) => r.isValid ).length; + + return { + totalBlocks: results.length, + validBlocks: validCount, + invalidBlocks: results.length - validCount, + results, + }; + } finally { + console.log = origLog; + console.info = origInfo; + console.warn = origWarn; + } +} diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index fc874b809b..2ddc9df634 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -15,7 +15,20 @@ Then continue with: 1. **Get site details**: Use site_info to get the site path, URL, and credentials. 2. **Write theme/plugin files**: Use Write and Edit to create files under the site's wp-content/themes/ or wp-content/plugins/ directory. -3. **Validation** For each generated file with block content, ensure the validation rules defined a bit further down are followed and fix the content if not. +3. **Validation loop** (MANDATORY for every file with block content): + a. Before calling validate_blocks, review the file against this block content checklist: + - No \`\` blocks when a core block type can achieve the same result. + - No decorative HTML comments (e.g. \`\`, \`\`). Only block delimiter comments are allowed. + - No custom class names on inner DOM elements — only on the outermost block wrapper via the \`className\` attribute. + - No inline \`style\` or \`style\` block attributes for styling. Use \`className\` + \`style.css\` instead. + - No \`style.backgroundColor\` or \`style.textColor\` block attributes. + - Use \`core/spacer\` for empty spacing divs, not \`core/group\`. + - No emojis anywhere in generated content. + b. Call validate_blocks with the file path. + c. If validate_blocks reports ANY invalid blocks, fix them in the file. (Ensure design doesn't regress — adapt CSS or markup as needed.) + d. After fixing, call validate_blocks AGAIN on the same file. Repeat steps c–d until validate_blocks reports 0 invalid blocks. + e. Only proceed to the next step once every file with block content passes validation with 0 invalid blocks. + f. NEVER skip re-validation after a fix — the fix itself may introduce new issues. 4. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. ## Available Studio Tools (prefixed with mcp__studio__) @@ -26,23 +39,15 @@ Then continue with: - site_start: Start a stopped site - site_stop: Stop a running site - wp_cli: Run WP-CLI commands on a running site +- validate_blocks: Validate WordPress block content for correctness (checks block markup matches expected save output). MUST be called after every file write/edit that contains block content, and repeated until 0 invalid blocks remain. -## MUST follow rules +## General rules - Do NOT modify WordPress core files. Only work within wp-content/. - Before running wp_cli, ensure the site is running (site_start if needed). -- when building themes, always build block themes (NO CLASSIC THEMES). +- When building themes, always build block themes (NO CLASSIC THEMES). - Always add the style.css as editor styles in the functions.php of the theme to make the editor match the frontend. - For theme and page content custom CSS, put the styles in the main style.css of the theme. No custom stylesheets. -- Avoid html blocks: Avoid using \`\` (the \`core/html\` block) when there are core block types that can achieve the same result. -- You MUST NEVER add decorative or random HTML comments: Never insert non-block HTML comments like \`\` or \`\` or \`\` or similar. -- The only HTML comments allowed are the block comments that define blocks (e.g. \`\`). -- You MUST NEVER use custom class names in inner DOM elements that are not block wrappers. No matter the block type, button, image or else. -- Avoid using the "core/group" block for empty divs that only have a styling purpose. You MUST use the "core/spacer" block for that instead. -- Do not use inline style or style block attribute: To add custom CSS and styles to blocks, you MUST use the \`className\` attribute and define styles in \`style.css\`. The custom classname should also be added to the outermost wrapper of the block (the element that corresponds to the block comment) and not to inner elements, as the block editor compares inner HTML against its expected output and any difference causes "invalid block" errors. - Use patterns for complex block structures: For complex sections with multiple nested blocks, create a reusable pattern in the WordPress admin and then use the \`pattern\` attribute in your block comments to insert it. This ensures the content is editable and maintainable for users. -- If use have a group block with a style.background property in the block attributes, you MUST add a has-background class to the block's wrapper element classes. -- Do not use the style.backgroundColor or style.textColor block attributes. -- Avoid emojis: Never use emojis anywhere in generated content, prefer using engaging images. `; } diff --git a/apps/cli/ai/tools.ts b/apps/cli/ai/tools.ts index 55fab701b4..c73312de4c 100644 --- a/apps/cli/ai/tools.ts +++ b/apps/cli/ai/tools.ts @@ -1,7 +1,9 @@ +import { readFile } from 'fs/promises'; import path from 'path'; import { tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; import { DEFAULT_PHP_VERSION } from '@studio/common/constants'; import { z } from 'zod'; +import { validateBlocks } from 'cli/ai/block-validator'; import { runCommand as runCreateSiteCommand } from 'cli/commands/site/create'; import { runCommand as runListSitesCommand } from 'cli/commands/site/list'; import { runCommand as runStartSiteCommand } from 'cli/commands/site/start'; @@ -292,6 +294,68 @@ const runWpCliTool = tool( } ); +const validateBlocksTool = tool( + 'validate_blocks', + 'Validates WordPress block content by parsing it and checking each block against its registered save() function. ' + + 'Returns per-block validation results showing which blocks are valid and which have issues, ' + + 'along with the expected HTML for invalid blocks so you can fix them.', + z.object( { + filePath: z + .string() + .optional() + .describe( 'Path to a file containing WordPress block content to validate' ), + content: z + .string() + .optional() + .describe( 'Raw WordPress block content (HTML with block comments) to validate' ), + } ), + async ( args ) => { + try { + let blockContent: string; + + if ( args.filePath ) { + blockContent = await readFile( args.filePath, 'utf-8' ); + } else if ( args.content ) { + blockContent = args.content; + } else { + return errorResult( 'Either content or filePath must be provided.' ); + } + + const report = validateBlocks( blockContent ); + + if ( report.error ) { + return errorResult( `Block validator initialization failed: ${ report.error }` ); + } + + const lines: string[] = []; + lines.push( `Validation: ${ report.validBlocks }/${ report.totalBlocks } blocks valid` ); + + if ( report.invalidBlocks > 0 ) { + lines.push( '' ); + lines.push( 'Invalid blocks:' ); + for ( const result of report.results ) { + if ( ! result.isValid ) { + lines.push( ` - ${ result.blockName }` ); + for ( const issue of result.issues ) { + lines.push( ` ${ issue }` ); + } + if ( result.expectedContent !== undefined ) { + lines.push( ` Expected: ${ result.expectedContent }` ); + lines.push( ` Actual: ${ result.originalContent }` ); + } + } + } + } + + return textResult( lines.join( '\n' ) ); + } catch ( error ) { + return errorResult( + `Block validation failed: ${ error instanceof Error ? error.message : String( error ) }` + ); + } + } +); + export function createStudioTools() { return createSdkMcpServer( { name: 'studio', @@ -303,6 +367,7 @@ export function createStudioTools() { startSiteTool, stopSiteTool, runWpCliTool, + validateBlocksTool, ], } ); } diff --git a/apps/cli/ai/ui.ts b/apps/cli/ai/ui.ts index db00e4f918..b3373df453 100644 --- a/apps/cli/ai/ui.ts +++ b/apps/cli/ai/ui.ts @@ -105,6 +105,7 @@ const toolDisplayNames: Record< string, string > = { mcp__studio__site_start: 'Starting site', mcp__studio__site_stop: 'Stopping site', mcp__studio__wp_cli: 'Running WP-CLI', + mcp__studio__validate_blocks: 'Validating blocks', Read: 'Reading file', Write: 'Writing file', Edit: 'Editing file', @@ -125,6 +126,11 @@ function getToolDetail( name: string, input: Record< string, unknown > ): string return typeof input.nameOrPath === 'string' ? input.nameOrPath : ''; case 'mcp__studio__wp_cli': return typeof input.command === 'string' ? `wp ${ input.command }` : ''; + case 'mcp__studio__validate_blocks': + if ( typeof input.filePath === 'string' ) { + return input.filePath.split( '/' ).slice( -2 ).join( '/' ); + } + return 'inline content'; case 'Read': case 'Write': case 'Edit': { diff --git a/apps/cli/package.json b/apps/cli/package.json index 6173220029..232a2ad90c 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -23,6 +23,9 @@ "pm2": "^6.0.14", "pm2-axon": "^4.0.1", "trash": "^10.0.1", + "@wordpress/block-library": "^9.40.0", + "@wordpress/blocks": "^15.13.0", + "jsdom": "^24.0.0", "yargs": "^18.0.0", "yargs-parser": "^22.0.0" }, diff --git a/apps/cli/vite.config.ts b/apps/cli/vite.config.ts index 6a925a77c8..4b9934afc8 100644 --- a/apps/cli/vite.config.ts +++ b/apps/cli/vite.config.ts @@ -89,6 +89,14 @@ export default defineConfig( { '@wp-playground/wordpress', '@anthropic-ai/claude-agent-sdk', 'koffi', + // WordPress block validation packages (loaded at runtime from node_modules) + /^@wordpress\//, + 'jsdom', + 'hpq', + 'simple-html-tokenizer', + 'react', + 'react-dom', + 'react-is', ], output: { format: 'cjs', diff --git a/package-lock.json b/package-lock.json index 0b59448c6a..35b90bd59a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,6 +58,8 @@ "@php-wasm/universal": "3.1.2", "@studio/common": "file:../../tools/common", "@vscode/sudo-prompt": "^9.3.2", + "@wordpress/block-library": "^9.40.0", + "@wordpress/blocks": "^15.13.0", "@wp-playground/blueprints": "3.1.2", "@wp-playground/cli": "3.1.2", "@wp-playground/common": "3.1.2", @@ -65,6 +67,7 @@ "chalk": "^5.6.2", "cli-table3": "^0.6.5", "http-proxy": "^1.18.1", + "jsdom": "^24.0.0", "node-forge": "^1.3.3", "pm2": "^6.0.14", "pm2-axon": "^4.0.1", @@ -727,7 +730,6 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "3.2.0", - "dev": true, "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.3", @@ -739,7 +741,6 @@ }, "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { "version": "10.4.3", - "dev": true, "license": "ISC" }, "node_modules/@automattic/babel-plugin-i18n-calypso": { @@ -2622,7 +2623,6 @@ }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", - "dev": true, "funding": [ { "type": "github", @@ -2640,7 +2640,6 @@ }, "node_modules/@csstools/css-calc": { "version": "2.1.4", - "dev": true, "funding": [ { "type": "github", @@ -2662,7 +2661,6 @@ }, "node_modules/@csstools/css-color-parser": { "version": "3.1.0", - "dev": true, "funding": [ { "type": "github", @@ -2688,7 +2686,6 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "3.0.5", - "dev": true, "funding": [ { "type": "github", @@ -2709,7 +2706,6 @@ }, "node_modules/@csstools/css-tokenizer": { "version": "3.0.4", - "dev": true, "funding": [ { "type": "github", @@ -8215,6 +8211,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@preact/signals": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@preact/signals/-/signals-1.3.4.tgz", + "integrity": "sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g==", + "license": "MIT", + "dependencies": { + "@preact/signals-core": "^1.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact": "10.x" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.13.0.tgz", + "integrity": "sha512-slT6XeTCAbdql61GVLlGU4x7XHI7kCZV5Um5uhE4zLX4ApgiiXc0UYFvVOKq06xcovzp7p+61l68oPi563ARKg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/@prisma/instrumentation": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.2.0.tgz", @@ -8256,170 +8278,683 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@reduxjs/toolkit": { - "version": "2.11.2", + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@standard-schema/utils": "^0.3.0", - "immer": "^11.0.0", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "react": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { "optional": true }, - "react-redux": { + "@types/react-dom": { "optional": true } } }, - "node_modules/@rive-app/canvas": { - "version": "2.18.0", - "license": "MIT" - }, - "node_modules/@rive-app/react-canvas": { - "version": "4.12.0", + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@rive-app/canvas": "2.18.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/plugin-virtual": { - "version": "3.0.2", - "dev": true, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", - "engines": { - "node": ">=14.0.0" + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "rollup": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { "optional": true } } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", - "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", - "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.2", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", - "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", - "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", - "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", - "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", - "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", - "cpu": [ - "arm" - ], + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-spring/animated": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz", + "integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==", + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.5.tgz", + "integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz", + "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==", + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz", + "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==", + "license": "MIT", + "dependencies": { + "@react-spring/rafz": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz", + "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==", + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", + "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/core": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@rive-app/canvas": { + "version": "2.18.0", + "license": "MIT" + }, + "node_modules/@rive-app/react-canvas": { + "version": "4.12.0", + "license": "MIT", + "dependencies": { + "@rive-app/canvas": "2.18.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-virtual": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", + "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", + "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.50.2", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", + "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", + "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", + "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", + "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", + "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, @@ -10886,37 +11421,284 @@ "@webassemblyjs/utf8": "1.13.2" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "dev": true, - "license": "MIT", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@wordpress/a11y": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.40.0.tgz", + "integrity": "sha512-WhBuBgJTvanbBMNeflgCvwQLOU9ToITdYSzOvWg0kzz1i/e138NlCxrVpcXGUc6MQulduKhOWOtjizSdotaQRA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/dom-ready": "^4.40.0", + "@wordpress/i18n": "^6.13.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/api-fetch": { + "version": "7.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-7.40.0.tgz", + "integrity": "sha512-u/PjrmuHlVo93u1FrUGJQNokMyc8RvC9o0mQboU8sLe9Hz288XSShdvY7hyZfroYtXGu81s/3KUHxUsTK4GGrA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/i18n": "^6.13.0", + "@wordpress/url": "^4.40.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/autop": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-4.40.0.tgz", + "integrity": "sha512-sAWp7WFtwZni5QtoxX1O5U9zFnpmm42k3e+70fSOK8HcXYzW9EgVp1029oIlFihhiDU6Tey3yLzUvnkH+26hEw==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/base-styles": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-6.16.0.tgz", + "integrity": "sha512-g8eZCTULM9rdQMTYfp3U+bHjT6wTtyuo8BFE2PCwJmH60Lp6P4qjnaez1PDW2M3yujCPwDdQBIR8tPXrTAlC/A==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/blob": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-4.40.0.tgz", + "integrity": "sha512-25NNb+xCRudku6xtslOkwpAySRJyOFdFDDn1J3KUeAI7B9vsUppwRn1xPd4rcZuJ30DVuPZnvSRR9IXXjm3cIg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/block-editor": { + "version": "15.13.1", + "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-15.13.1.tgz", + "integrity": "sha512-7/APQjjuRYzfUUAvCZfU2lRYSZA35wAz2gUE6QK97AFhmDaAzOBdRCf8GdftiYLhRq+uuKdNzh1vyZ7ewKXF8A==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@react-spring/web": "^9.4.5", + "@wordpress/a11y": "^4.40.0", + "@wordpress/api-fetch": "^7.40.0", + "@wordpress/base-styles": "^6.16.0", + "@wordpress/blob": "^4.40.0", + "@wordpress/block-serialization-default-parser": "^5.40.0", + "@wordpress/blocks": "^15.13.0", + "@wordpress/commands": "^1.40.0", + "@wordpress/components": "^32.2.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/dataviews": "^12.0.0", + "@wordpress/date": "^5.40.0", + "@wordpress/deprecated": "^4.40.0", + "@wordpress/dom": "^4.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/escape-html": "^3.40.0", + "@wordpress/global-styles-engine": "^1.7.0", + "@wordpress/hooks": "^4.40.0", + "@wordpress/html-entities": "^4.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/icons": "^11.7.0", + "@wordpress/image-cropper": "^1.4.0", + "@wordpress/interactivity": "^6.40.0", + "@wordpress/is-shallow-equal": "^5.40.0", + "@wordpress/keyboard-shortcuts": "^5.40.0", + "@wordpress/keycodes": "^4.40.0", + "@wordpress/notices": "^5.40.0", + "@wordpress/preferences": "^4.40.0", + "@wordpress/priority-queue": "^3.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/rich-text": "^7.40.0", + "@wordpress/style-engine": "^2.40.0", + "@wordpress/token-list": "^3.40.0", + "@wordpress/upload-media": "^0.25.1", + "@wordpress/url": "^4.40.0", + "@wordpress/warning": "^3.40.0", + "@wordpress/wordcount": "^4.40.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "deepmerge": "^4.3.0", + "diff": "^4.0.2", + "fast-deep-equal": "^3.1.3", + "memize": "^2.1.0", + "parsel-js": "^1.1.2", + "postcss": "^8.4.21", + "postcss-prefix-selector": "^1.16.0", + "postcss-urlrebase": "^1.4.0", + "react-autosize-textarea": "^7.1.0", + "react-easy-crop": "^5.0.6", + "remove-accents": "^0.5.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/block-library": { + "version": "9.40.1", + "resolved": "https://registry.npmjs.org/@wordpress/block-library/-/block-library-9.40.1.tgz", + "integrity": "sha512-euJSw8qS4T2RBuWwTM5VpZojNZyAGQb6yQyK4BFlFCDhLiJrER4Mbm9H7WvEVEiG7/YuBcouc6/aq0Odstbdzg==", + "license": "GPL-2.0-or-later", "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" + "@wordpress/a11y": "^4.40.0", + "@wordpress/api-fetch": "^7.40.0", + "@wordpress/autop": "^4.40.0", + "@wordpress/base-styles": "^6.16.0", + "@wordpress/blob": "^4.40.0", + "@wordpress/block-editor": "^15.13.1", + "@wordpress/blocks": "^15.13.0", + "@wordpress/components": "^32.2.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/core-data": "^7.40.1", + "@wordpress/data": "^10.40.0", + "@wordpress/date": "^5.40.0", + "@wordpress/deprecated": "^4.40.0", + "@wordpress/dom": "^4.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/escape-html": "^3.40.0", + "@wordpress/hooks": "^4.40.0", + "@wordpress/html-entities": "^4.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/icons": "^11.7.0", + "@wordpress/interactivity": "^6.40.0", + "@wordpress/interactivity-router": "^2.40.0", + "@wordpress/keyboard-shortcuts": "^5.40.0", + "@wordpress/keycodes": "^4.40.0", + "@wordpress/latex-to-mathml": "^1.8.0", + "@wordpress/notices": "^5.40.0", + "@wordpress/patterns": "^2.40.1", + "@wordpress/primitives": "^4.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/reusable-blocks": "^5.40.1", + "@wordpress/rich-text": "^7.40.0", + "@wordpress/server-side-render": "^6.16.0", + "@wordpress/upload-media": "^0.25.1", + "@wordpress/url": "^4.40.0", + "@wordpress/viewport": "^6.40.0", + "@wordpress/wordcount": "^4.40.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "fast-average-color": "^9.1.1", + "fast-deep-equal": "^3.1.3", + "html-react-parser": "5.2.11", + "memize": "^2.1.0", + "remove-accents": "^0.5.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/a11y": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.40.0.tgz", - "integrity": "sha512-WhBuBgJTvanbBMNeflgCvwQLOU9ToITdYSzOvWg0kzz1i/e138NlCxrVpcXGUc6MQulduKhOWOtjizSdotaQRA==", + "node_modules/@wordpress/block-serialization-default-parser": { + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-5.40.0.tgz", + "integrity": "sha512-aAkE883BgNsV/sIua7VY0ifpbgUkDD/b98naWGCKnHCw2YIh1vWLNrjKlozsMyLVutuyW3w3agnYMKtXQc2uxg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/blocks": { + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-15.13.0.tgz", + "integrity": "sha512-e1OEv472ZGi5zL154TWASO/wYxbH5845C42thbp9sBis1zB31bkUriIxpn2vqmJV22uFnh0L31uBLTkQAp5BiQ==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/dom-ready": "^4.40.0", - "@wordpress/i18n": "^6.13.0" + "@wordpress/autop": "^4.40.0", + "@wordpress/blob": "^4.40.0", + "@wordpress/block-serialization-default-parser": "^5.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/deprecated": "^4.40.0", + "@wordpress/dom": "^4.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/hooks": "^4.40.0", + "@wordpress/html-entities": "^4.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/is-shallow-equal": "^5.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/rich-text": "^7.40.0", + "@wordpress/shortcode": "^4.40.0", + "@wordpress/warning": "^3.40.0", + "change-case": "^4.1.2", + "colord": "^2.7.0", + "fast-deep-equal": "^3.1.3", + "hpq": "^1.3.0", + "is-plain-object": "^5.0.0", + "memize": "^2.1.0", + "react-is": "^18.3.0", + "remove-accents": "^0.5.0", + "showdown": "^1.9.1", + "simple-html-tokenizer": "^0.5.7", + "uuid": "^9.0.1" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" } }, - "node_modules/@wordpress/base-styles": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-6.16.0.tgz", - "integrity": "sha512-g8eZCTULM9rdQMTYfp3U+bHjT6wTtyuo8BFE2PCwJmH60Lp6P4qjnaez1PDW2M3yujCPwDdQBIR8tPXrTAlC/A==", + "node_modules/@wordpress/blocks/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/@wordpress/commands": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-1.40.0.tgz", + "integrity": "sha512-hqkXJoV/9NNctGZCO9VjyuXnT0yv0OaC8/XcW+Q3GX55laCEa2MXOgo3NdW5zqNY3PJqGdyO84RO9cG+lCtdiQ==", "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/base-styles": "^6.16.0", + "@wordpress/components": "^32.2.0", + "@wordpress/data": "^10.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/icons": "^11.7.0", + "@wordpress/keyboard-shortcuts": "^5.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/warning": "^3.40.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@wordpress/components": { @@ -11015,6 +11797,43 @@ "react": "^18.0.0" } }, + "node_modules/@wordpress/core-data": { + "version": "7.40.1", + "resolved": "https://registry.npmjs.org/@wordpress/core-data/-/core-data-7.40.1.tgz", + "integrity": "sha512-lpI/ZyMe8f4rQnkXYBZNm3pn3ybV75vzYwbx8XiZlCqmVcXZQU5m6bEfcg/oUTReqzKjAslGYrlwebhAq4Rvbg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.40.0", + "@wordpress/block-editor": "^15.13.1", + "@wordpress/blocks": "^15.13.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/deprecated": "^4.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/html-entities": "^4.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/is-shallow-equal": "^5.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/rich-text": "^7.40.0", + "@wordpress/sync": "^1.40.0", + "@wordpress/undo-manager": "^1.40.0", + "@wordpress/url": "^4.40.0", + "@wordpress/warning": "^3.40.0", + "change-case": "^4.1.2", + "equivalent-key-map": "^0.2.2", + "fast-deep-equal": "^3.1.3", + "memize": "^2.1.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@wordpress/data": { "version": "10.40.0", "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.40.0.tgz", @@ -11133,94 +11952,239 @@ "npm": ">=8.19.2" } }, - "node_modules/@wordpress/dom-ready": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.40.0.tgz", - "integrity": "sha512-mHVy4P6yc0XLmGgnccxptMKg83TwcbYKfYrQH8pTcIu43P24zONTd44eZFjkfz7c/b+RLJg1Kj+d5mKh1xqH1A==", + "node_modules/@wordpress/dom-ready": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.40.0.tgz", + "integrity": "sha512-mHVy4P6yc0XLmGgnccxptMKg83TwcbYKfYrQH8pTcIu43P24zONTd44eZFjkfz7c/b+RLJg1Kj+d5mKh1xqH1A==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/element": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.40.0.tgz", + "integrity": "sha512-OhU8B2xEGg7c41rh/VRiJLOz6TnM/r5r8sraAg5ISc2bF7s2oAFqLwvlR0/U6ervyYwbK644osWZGQxFyL3huA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.1", + "@wordpress/escape-html": "^3.40.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.0", + "react-dom": "^18.3.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/escape-html": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.40.0.tgz", + "integrity": "sha512-DD6xWVbnw4fGGgO6DFDTJiLj52om0OG4cYHLz7ZhuipmOlEUGljPYOcrj8uxtlh5EFrqHCIPkOya+qQXUHUSBw==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/global-styles-engine": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/global-styles-engine/-/global-styles-engine-1.7.0.tgz", + "integrity": "sha512-CGtsgrca3D7oeBWwZDfMh7v7vo31QYFg5HSrrydzF0rUEjr2qJnaTD8RtStqWd8ApbQ4cL1XsHL3r+xrxLvhUg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/blocks": "^15.13.0", + "@wordpress/data": "^10.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/style-engine": "^2.40.0", + "colord": "^2.9.2", + "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", + "is-plain-object": "^5.0.0", + "memize": "^2.1.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/hooks": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.40.0.tgz", + "integrity": "sha512-Lz89uHQaMKM2TAdwafCPJr6px5qodZt/wdLmRrGkrItvtbikLdf9l29BrjpSMmRbJY6jiYtOTVF4sg5rwJv2Pw==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/html-entities": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.40.0.tgz", + "integrity": "sha512-bsJrwZk22On8gNhUd84yyWKt/nrNZtACNZpXmkpyue/oTlFqNenLfhqRkvTKJzjbLxrrcUPsXlskbPcS7mxwTQ==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/i18n": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz", + "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@tannin/sprintf": "^1.3.2", + "@wordpress/hooks": "^4.40.0", + "gettext-parser": "^1.3.1", + "memize": "^2.1.0", + "tannin": "^1.2.0" + }, + "bin": { + "pot-to-php": "tools/pot-to-php.js" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/icons": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-11.7.0.tgz", + "integrity": "sha512-t+z65fn98A/Y4x+nynMQuJfz2v0sCfpsxa/+xopmOne/4Yt7H5/224sUc6zWV0NrIlWTDscD0QepUZ5j1qFM0Q==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^6.40.0", + "@wordpress/primitives": "^4.40.0", + "change-case": "4.1.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/image-cropper": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@wordpress/image-cropper/-/image-cropper-1.4.0.tgz", + "integrity": "sha512-4Aedd2+eGwrxcVgSEK2GL1zHJVoSCQCKqQogYpnL3SGws8McuKrTpawLzbxCgQepwBL64UuNsZt39IKPtk/m4g==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/components": "^32.2.0", + "@wordpress/element": "^6.40.0", + "@wordpress/i18n": "^6.13.0", + "clsx": "^2.1.1", + "dequal": "^2.0.3", + "react-easy-crop": "^5.4.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/interactivity": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/interactivity/-/interactivity-6.40.0.tgz", + "integrity": "sha512-VYHZMKzg3w7pRG58aD+M1ZxyicDK9or6WJ3pcVXyp7WaGJrleJqd/jIFj4csIqLGW4kKozNq1NBaqjqVHOnIqA==", "license": "GPL-2.0-or-later", + "dependencies": { + "@preact/signals": "^1.3.0", + "preact": "^10.24.2" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/element": { - "version": "6.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.40.0.tgz", - "integrity": "sha512-OhU8B2xEGg7c41rh/VRiJLOz6TnM/r5r8sraAg5ISc2bF7s2oAFqLwvlR0/U6ervyYwbK644osWZGQxFyL3huA==", + "node_modules/@wordpress/interactivity-router": { + "version": "2.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/interactivity-router/-/interactivity-router-2.40.0.tgz", + "integrity": "sha512-d1pl9ln/f3VCpHE+yyCwmAPHN4Eiz5R9jdUfJfwHZkjceM0T/N1XTNCnsyrOhJW80uUGH4a3Nd/anOo+R5jp6A==", "license": "GPL-2.0-or-later", "dependencies": { - "@types/react": "^18.3.27", - "@types/react-dom": "^18.3.1", - "@wordpress/escape-html": "^3.40.0", - "change-case": "^4.1.2", - "is-plain-object": "^5.0.0", - "react": "^18.3.0", - "react-dom": "^18.3.0" + "@wordpress/a11y": "^4.40.0", + "@wordpress/interactivity": "^6.40.0", + "es-module-lexer": "^1.5.4" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/escape-html": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.40.0.tgz", - "integrity": "sha512-DD6xWVbnw4fGGgO6DFDTJiLj52om0OG4cYHLz7ZhuipmOlEUGljPYOcrj8uxtlh5EFrqHCIPkOya+qQXUHUSBw==", + "node_modules/@wordpress/is-shallow-equal": { + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.40.0.tgz", + "integrity": "sha512-IU11xOcHIGqDLxx9X+8RIk4WFo0qqba0bpeLqrVKsQXNGjP7tXSo2ufylxE9K9CEYXFMF0C65k83XpRZtEkA8g==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/hooks": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.40.0.tgz", - "integrity": "sha512-Lz89uHQaMKM2TAdwafCPJr6px5qodZt/wdLmRrGkrItvtbikLdf9l29BrjpSMmRbJY6jiYtOTVF4sg5rwJv2Pw==", + "node_modules/@wordpress/keyboard-shortcuts": { + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-5.40.0.tgz", + "integrity": "sha512-E9EjZa1Dibo5YiRz6hoFx+ihlj5nqGkMc4ZF8LwpTbZLqsN8fG/SEdOwbkxFXqVQIBau6Csq484Ld2xtQ9wDHw==", "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/data": "^10.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/keycodes": "^4.40.0" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" } }, - "node_modules/@wordpress/html-entities": { + "node_modules/@wordpress/keycodes": { "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.40.0.tgz", - "integrity": "sha512-bsJrwZk22On8gNhUd84yyWKt/nrNZtACNZpXmkpyue/oTlFqNenLfhqRkvTKJzjbLxrrcUPsXlskbPcS7mxwTQ==", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.40.0.tgz", + "integrity": "sha512-laLkfjwkhMdreCl/KQdHucBIQAYwSjkyk3BToq/PCrcxFJBwWK2NgEtSl/t1CEw2HJwe0H2ne3FEWtipY4iDrA==", "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/i18n": "^6.13.0" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/i18n": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.13.0.tgz", - "integrity": "sha512-Yx882uFxcg6QpB13fv8UhvM6k5NwMQGfNXKB9SVSNL/APvDWn2m/n4n+5GZYi+wOV+KJLojQZbdRpHWCnX/jFg==", + "node_modules/@wordpress/latex-to-mathml": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@wordpress/latex-to-mathml/-/latex-to-mathml-1.8.0.tgz", + "integrity": "sha512-GfUKLJ48QUT+dmcUDZEhdG6rCakj4UXaNmUjnhUgIv0tWp+yu1cFXPFS/ojlqFP3H8t3OjJWKouPW8JQPyBlKw==", "license": "GPL-2.0-or-later", "dependencies": { - "@tannin/sprintf": "^1.3.2", - "@wordpress/hooks": "^4.40.0", - "gettext-parser": "^1.3.1", - "memize": "^2.1.0", - "tannin": "^1.2.0" - }, - "bin": { - "pot-to-php": "tools/pot-to-php.js" + "temml": "^0.10.33" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" } }, - "node_modules/@wordpress/icons": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-11.7.0.tgz", - "integrity": "sha512-t+z65fn98A/Y4x+nynMQuJfz2v0sCfpsxa/+xopmOne/4Yt7H5/224sUc6zWV0NrIlWTDscD0QepUZ5j1qFM0Q==", + "node_modules/@wordpress/notices": { + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-5.40.0.tgz", + "integrity": "sha512-hn54Pa5kDk7sZZ0RihALYrxJ5RAOxANyfMqrPiGX7Yi5U+K+kWTio+WhPB+j6iq1+G9BXJS3dkouJk11RtbcKw==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/element": "^6.40.0", - "@wordpress/primitives": "^4.40.0", - "change-case": "4.1.2" + "@wordpress/a11y": "^4.40.0", + "@wordpress/components": "^32.2.0", + "@wordpress/data": "^10.40.0", + "clsx": "^2.1.1" }, "engines": { "node": ">=18.12.0", @@ -11230,27 +12194,62 @@ "react": "^18.0.0" } }, - "node_modules/@wordpress/is-shallow-equal": { - "version": "5.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.40.0.tgz", - "integrity": "sha512-IU11xOcHIGqDLxx9X+8RIk4WFo0qqba0bpeLqrVKsQXNGjP7tXSo2ufylxE9K9CEYXFMF0C65k83XpRZtEkA8g==", + "node_modules/@wordpress/patterns": { + "version": "2.40.1", + "resolved": "https://registry.npmjs.org/@wordpress/patterns/-/patterns-2.40.1.tgz", + "integrity": "sha512-g/5Bx96LazWEda3zWTvVJ3bwcRqaUV0csgVNzqxCyNObcsDoKBU0LgC9Wk6ze4W93BSy5mhEPET48wWOX+MPmw==", "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.40.0", + "@wordpress/base-styles": "^6.16.0", + "@wordpress/block-editor": "^15.13.1", + "@wordpress/blocks": "^15.13.0", + "@wordpress/components": "^32.2.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/core-data": "^7.40.1", + "@wordpress/data": "^10.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/html-entities": "^4.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/icons": "^11.7.0", + "@wordpress/notices": "^5.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/url": "^4.40.0" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/keycodes": { + "node_modules/@wordpress/preferences": { "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.40.0.tgz", - "integrity": "sha512-laLkfjwkhMdreCl/KQdHucBIQAYwSjkyk3BToq/PCrcxFJBwWK2NgEtSl/t1CEw2HJwe0H2ne3FEWtipY4iDrA==", + "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-4.40.0.tgz", + "integrity": "sha512-vs6p0jEFVJtA3K6YI8Wm2C1zOYYqcYYS1cJVApat/95VBORFcu7i8GZ1bg59tuxX1OFmxevrdIL8YnG8W9ZoLQ==", "license": "GPL-2.0-or-later", "dependencies": { - "@wordpress/i18n": "^6.13.0" + "@wordpress/a11y": "^4.40.0", + "@wordpress/base-styles": "^6.16.0", + "@wordpress/components": "^32.2.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/deprecated": "^4.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/icons": "^11.7.0", + "@wordpress/private-apis": "^1.40.0", + "clsx": "^2.1.1" }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, "node_modules/@wordpress/primitives": { @@ -11326,6 +12325,34 @@ "redux": ">=4" } }, + "node_modules/@wordpress/reusable-blocks": { + "version": "5.40.1", + "resolved": "https://registry.npmjs.org/@wordpress/reusable-blocks/-/reusable-blocks-5.40.1.tgz", + "integrity": "sha512-cFLo2Esf/RzFsfamSkHpwx7395sHDqWb/Cb2iHqAGpI8JWFPodijQmp3d/680oEDFwD9qtljMLZ08tigZVy6QQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/base-styles": "^6.16.0", + "@wordpress/block-editor": "^15.13.1", + "@wordpress/blocks": "^15.13.0", + "@wordpress/components": "^32.2.0", + "@wordpress/core-data": "^7.40.1", + "@wordpress/data": "^10.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/icons": "^11.7.0", + "@wordpress/notices": "^5.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/url": "^4.40.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@wordpress/rich-text": { "version": "7.40.0", "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.40.0.tgz", @@ -11353,6 +12380,87 @@ "react": "^18.0.0" } }, + "node_modules/@wordpress/server-side-render": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/server-side-render/-/server-side-render-6.16.0.tgz", + "integrity": "sha512-TUfIsOdreTShBPIaqkdx3nxLrtdcRUYgbt0sH0jFlPWXf8o7X+X1gbFIxQcjUsAQIXR889412InX/8fJmOT0VA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.40.0", + "@wordpress/blocks": "^15.13.0", + "@wordpress/components": "^32.2.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/deprecated": "^4.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/url": "^4.40.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/shortcode": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-4.40.0.tgz", + "integrity": "sha512-Cf5aE15kflXL1JV/twK3awjhfrYe0opZbaNS/PtAgDVWnI6TPXfEwwaOXBy+Y6+rAVWV6YTYnv7CNPvGVlZ1YQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "memize": "^2.0.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/style-engine": { + "version": "2.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-2.40.0.tgz", + "integrity": "sha512-/xV3VjWo4sq3YR6T/Xo/6DCqILWzD8otzz2xVFAB9kKVfD8fknblkIs5c9Nuv39ZDIqQFJ91YF7Bu6Zw6K2mhg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "change-case": "^4.1.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/sync": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/sync/-/sync-1.40.0.tgz", + "integrity": "sha512-kL5br12NC0OOG7x4qZVjJnFc9LJhAAWhnK7QS2gYfM4IoYovTTt1DDK8dz6ww0mlzw3QV/PWWXpFbGuS7mqvNg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.40.0", + "@wordpress/hooks": "^4.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/undo-manager": "^1.40.0", + "diff": "^8.0.3", + "fast-deep-equal": "^3.1.3", + "lib0": "0.2.99", + "y-protocols": "^1.0.7", + "yjs": "13.6.29" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/sync/node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/@wordpress/theme": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@wordpress/theme/-/theme-0.7.0.tgz", @@ -11379,6 +12487,16 @@ } } }, + "node_modules/@wordpress/token-list": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-3.40.0.tgz", + "integrity": "sha512-J9HXmpv0zWgRS8oawSLXaANstZ29pb353rjOYH3RFhawtJd3Z4r6alLy4rLXGEA6CElIACF2PiPCQj+y/iKI+g==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/ui": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@wordpress/ui/-/ui-0.7.0.tgz", @@ -11419,6 +12537,33 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/upload-media": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@wordpress/upload-media/-/upload-media-0.25.1.tgz", + "integrity": "sha512-OGr1WMJDeTsQnjZ9RHJ/GGBaUAwdqTIQt/NjABEA3EdQdWXhxzquvfOviV2vQiX3gCrPdHYYf0w9L1i1hiM/+Q==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.40.0", + "@wordpress/blob": "^4.40.0", + "@wordpress/compose": "^7.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/element": "^6.40.0", + "@wordpress/i18n": "^6.13.0", + "@wordpress/preferences": "^4.40.0", + "@wordpress/private-apis": "^1.40.0", + "@wordpress/url": "^4.40.0", + "@wordpress/vips": "^1.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@wordpress/url": { "version": "4.40.0", "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.40.0.tgz", @@ -11432,6 +12577,38 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/viewport": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/viewport/-/viewport-6.40.0.tgz", + "integrity": "sha512-BY0S7UbyWlEkYXc4jCSrLImOR21tEk3kk1WpaNr+NVEb0P/HYA63CJqysQFVb+r1MAXOXDGylCNmPDehbt0kwQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/compose": "^7.40.0", + "@wordpress/data": "^10.40.0", + "@wordpress/element": "^6.40.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/vips": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/vips/-/vips-1.0.0.tgz", + "integrity": "sha512-YdSpJ3Gl/LBzLwtMG6mZJkJ5lzotnx+iOzI0emDrTiw8yuN7LWDtj2sx2FAE5Me0dE0dQC5nmm2OQUZVab5PGQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/worker-threads": "^1.0.0", + "wasm-vips": "^0.0.16" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/warning": { "version": "3.40.0", "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.40.0.tgz", @@ -11442,6 +12619,29 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/wordcount": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-4.40.0.tgz", + "integrity": "sha512-pVL1CURIYNIc0/9l1YncwYvRwm1JoQ2RUy+++3d9oTX7LfAQwbx1IvJEH2S8GpV9/4NrnorRnYKGw8tzxCBtkQ==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/worker-threads": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/worker-threads/-/worker-threads-1.0.0.tgz", + "integrity": "sha512-q/dJ9EQMyA+QQPmQ3oiboLUFn/tT1+B9oPDnmcoWem+dov2bfFKp2NJw1+CBKnA4Q8VBSpPfM2WLmrgM3n/1gw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "comctx": "^1.4.3" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wp-playground/blueprints": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@wp-playground/blueprints/-/blueprints-3.1.2.tgz", @@ -12674,6 +13874,18 @@ "version": "2.0.1", "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "dev": true, @@ -12860,6 +14072,12 @@ "node": ">=0.8" } }, + "node_modules/autosize": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.4.tgz", + "integrity": "sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "license": "MIT", @@ -13557,6 +14775,15 @@ "tslib": "^2.0.3" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "dev": true, @@ -13968,6 +15195,22 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/color-convert": { "version": "2.0.1", "license": "MIT", @@ -14018,6 +15261,12 @@ "node": ">= 0.8" } }, + "node_modules/comctx": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/comctx/-/comctx-1.6.1.tgz", + "integrity": "sha512-ZMRGAYASYRdVfEoB7oxH8Nqu5Ay8I+YvAsQni+td0pYV9eww/PrtSFVyvc2JkNQyHXGDknCB4wJfxFYP6fuqZg==", + "license": "MIT" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "license": "MIT", @@ -14128,6 +15377,11 @@ ], "license": "MIT" }, + "node_modules/computed-style": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/computed-style/-/computed-style-0.1.4.tgz", + "integrity": "sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==" + }, "node_modules/concat-map": { "version": "0.0.1", "license": "MIT" @@ -14332,7 +15586,6 @@ }, "node_modules/cssstyle": { "version": "4.6.0", - "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^3.2.0", @@ -14344,7 +15597,6 @@ }, "node_modules/cssstyle/node_modules/rrweb-cssom": { "version": "0.8.0", - "dev": true, "license": "MIT" }, "node_modules/csstype": { @@ -14372,7 +15624,6 @@ }, "node_modules/data-urls": { "version": "5.0.0", - "dev": true, "license": "MIT", "dependencies": { "whatwg-mimetype": "^4.0.0", @@ -14463,9 +15714,17 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decimal.js": { "version": "10.6.0", - "dev": true, "license": "MIT" }, "node_modules/decode-named-character-reference": { @@ -14640,6 +15899,12 @@ "license": "MIT", "optional": true }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "license": "MIT", @@ -14695,6 +15960,73 @@ "license": "MIT", "peer": true }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dot-case": { "version": "3.0.4", "license": "MIT", @@ -15522,7 +16854,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -16828,6 +18159,15 @@ "follow-redirects": "^1.14.0" } }, + "node_modules/fast-average-color": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.5.0.tgz", + "integrity": "sha512-nC6x2YIlJ9xxgkMFMd1BNoM1ctMjNoRKfRliPmiEWW3S6rLTHiQcy9g3pt/xiKv/D0NAAkhb9VyV+WJFvTqMGg==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "license": "MIT" @@ -17523,6 +18863,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-info": { "version": "1.0.0", "dev": true, @@ -18084,9 +19433,24 @@ "node": ">=14" } }, + "node_modules/hpq": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/hpq/-/hpq-1.4.0.tgz", + "integrity": "sha512-ycJQMRaRPBcfnoT1gS5I1XCvbbw9KO94Y0vkwksuOjcJMqNZtb03MF2tCItLI2mQbkZWSSeFinoRDPmjzv4tKg==", + "license": "MIT" + }, + "node_modules/html-dom-parser": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.1.2.tgz", + "integrity": "sha512-9nD3Rj3/FuQt83AgIa1Y3ruzspwFFA54AJbQnohXN+K6fL1/bhcDQJJY5Ne4L4A163ADQFVESd/0TLyNoV0mfg==", + "license": "MIT", + "dependencies": { + "domhandler": "5.0.3", + "htmlparser2": "10.0.0" + } + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", - "dev": true, "license": "MIT", "dependencies": { "whatwg-encoding": "^3.1.1" @@ -18095,6 +19459,27 @@ "node": ">=18" } }, + "node_modules/html-react-parser": { + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.2.11.tgz", + "integrity": "sha512-WnSQVn/D1UTj64nSz5y8MriL+MrbsZH80Ytr1oqKqs8DGZnphWY1R1pl3t7TY3rpqTSu+FHA21P80lrsmrdNBA==", + "license": "MIT", + "dependencies": { + "domhandler": "5.0.3", + "html-dom-parser": "5.1.2", + "react-property": "2.0.2", + "style-to-js": "1.1.21" + }, + "peerDependencies": { + "@types/react": "0.14 || 15 || 16 || 17 || 18 || 19", + "react": "0.14 || 15 || 16 || 17 || 18 || 19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/html-url-attributes": { "version": "3.0.0", "license": "MIT", @@ -18111,6 +19496,25 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "dev": true, @@ -18321,7 +19725,9 @@ } }, "node_modules/inline-style-parser": { - "version": "0.2.3", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, "node_modules/inquirer": { @@ -18935,7 +20341,6 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "dev": true, "license": "MIT" }, "node_modules/is-promise": { @@ -19133,6 +20538,16 @@ "whatwg-fetch": "^3.4.1" } }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/jackspeak": { "version": "2.3.6", "dev": true, @@ -19196,7 +20611,6 @@ }, "node_modules/jsdom": { "version": "24.0.0", - "dev": true, "license": "MIT", "dependencies": { "cssstyle": "^4.0.1", @@ -19235,7 +20649,6 @@ }, "node_modules/jsdom/node_modules/agent-base": { "version": "7.1.4", - "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -19243,7 +20656,6 @@ }, "node_modules/jsdom/node_modules/http-proxy-agent": { "version": "7.0.2", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -19255,7 +20667,6 @@ }, "node_modules/jsdom/node_modules/https-proxy-agent": { "version": "7.0.6", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -19515,6 +20926,27 @@ "node": ">= 0.8.0" } }, + "node_modules/lib0": { + "version": "0.2.99", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.99.tgz", + "integrity": "sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==", + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/lie": { "version": "3.3.0", "dev": true, @@ -19531,6 +20963,18 @@ "node": ">=10" } }, + "node_modules/line-height": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/line-height/-/line-height-0.3.1.tgz", + "integrity": "sha512-YExecgqPwnp5gplD2+Y8e8A5+jKpr25+DzMbFdI1/1UAr0FJrTFv4VkHLf8/6B590i1wUPJWMKKldkd/bdQ//w==", + "license": "MIT", + "dependencies": { + "computed-style": "~0.1.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "license": "MIT" @@ -21241,7 +22685,6 @@ }, "node_modules/nanoid": { "version": "3.3.11", - "dev": true, "funding": [ { "type": "github", @@ -21483,6 +22926,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", + "license": "BSD-3-Clause" + }, "node_modules/npm-run-path": { "version": "2.0.2", "devOptional": true, @@ -21504,12 +22953,10 @@ }, "node_modules/nwsapi": { "version": "2.2.23", - "dev": true, "license": "MIT" }, "node_modules/object-assign": { "version": "4.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -21919,6 +23366,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", @@ -22100,6 +23556,22 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parsel-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parsel-js/-/parsel-js-1.2.2.tgz", + "integrity": "sha512-AVJMlwQ4bL2Y0VvYJGk+Fp7eX4SCH2uFoNApmn4yKWACUewZ+alwW3tyoe1r5Z3aLYQTuAuPZIyGghMfO/Tlxw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/LeaVerou" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/leaverou" + } + ], + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "license": "MIT", @@ -22653,7 +24125,6 @@ }, "node_modules/postcss": { "version": "8.5.6", - "dev": true, "funding": [ { "type": "opencollective", @@ -22772,6 +24243,15 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-prefix-selector": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz", + "integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==", + "license": "MIT", + "peerDependencies": { + "postcss": ">4 <9" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.15", "dev": true, @@ -22784,9 +24264,20 @@ "node": ">=4" } }, + "node_modules/postcss-urlrebase": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/postcss-urlrebase/-/postcss-urlrebase-1.4.0.tgz", + "integrity": "sha512-rRaxMmWvXrn8Rk1PqsxmaJwldRHsr0WbbASKKCZYxXwotHkM/5X/6IrwaEe8pdzpbNGCEY86yhYMN0MhgOkADA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.3.0" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", - "dev": true, "license": "MIT" }, "node_modules/postgres-array": { @@ -22862,6 +24353,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/preact": { + "version": "10.28.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.4.tgz", + "integrity": "sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -23039,6 +24540,17 @@ "read": "^1.0.4" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/propagate": { "version": "2.0.1", "dev": true, @@ -23166,7 +24678,6 @@ }, "node_modules/psl": { "version": "1.15.0", - "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -23324,6 +24835,21 @@ "node": ">=0.10.0" } }, + "node_modules/react-autosize-textarea": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/react-autosize-textarea/-/react-autosize-textarea-7.1.0.tgz", + "integrity": "sha512-BHpjCDkuOlllZn3nLazY2F8oYO1tS2jHnWhcjTWQdcKiiMU6gHLNt/fzmqMSyerR0eTdKtfSIqtSeTtghNwS+g==", + "license": "MIT", + "dependencies": { + "autosize": "^4.0.2", + "line-height": "^0.3.1", + "prop-types": "^15.5.6" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/react-colorful": { "version": "5.6.1", "license": "MIT", @@ -23370,6 +24896,20 @@ "react": "^18.3.1" } }, + "node_modules/react-easy-crop": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.6.tgz", + "integrity": "sha512-Jw3/ozs8uXj3NpL511Suc4AHY+mLRO23rUgipXvNYKqezcFSYHxe4QXibBymkOoY6oOtLVMPO2HNPRHYvMPyTw==", + "license": "MIT", + "dependencies": { + "normalize-wheel": "^1.0.1", + "tslib": "^2.0.1" + }, + "peerDependencies": { + "react": ">=16.4.0", + "react-dom": ">=16.4.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -23391,42 +24931,117 @@ "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==", + "license": "MIT" + }, + "node_modules/react-redux": { + "version": "9.2.0", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" }, "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/react-redux": { - "version": "9.2.0", + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", "dependencies": { - "@types/use-sync-external-store": "^0.0.6", - "use-sync-external-store": "^1.4.0" + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" }, "peerDependencies": { - "@types/react": "^18.2.25 || ^19", - "react": "^18.0 || ^19", - "redux": "^5.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "redux": { - "optional": true } } }, - "node_modules/react-refresh": { - "version": "0.18.0", - "dev": true, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/read": { @@ -23858,6 +25473,12 @@ "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/requireindex": { "version": "1.2.0", "dev": true, @@ -24153,7 +25774,6 @@ }, "node_modules/rrweb-cssom": { "version": "0.6.0", - "dev": true, "license": "MIT" }, "node_modules/run-async": { @@ -24287,7 +25907,6 @@ }, "node_modules/saxes": { "version": "6.0.0", - "dev": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -24469,6 +26088,12 @@ "node": ">= 0.8" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "license": "MIT", @@ -24593,6 +26218,216 @@ "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", "license": "BSD-2-Clause" }, + "node_modules/showdown": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==", + "license": "BSD-3-Clause", + "dependencies": { + "yargs": "^14.2" + }, + "bin": { + "showdown": "bin/showdown.js" + } + }, + "node_modules/showdown/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/showdown/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/showdown/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/showdown/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/showdown/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "license": "MIT" + }, + "node_modules/showdown/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/showdown/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/showdown/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/showdown/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/showdown/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/showdown/node_modules/yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "node_modules/showdown/node_modules/yargs-parser": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.3.tgz", + "integrity": "sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, "node_modules/side-channel": { "version": "1.1.0", "license": "MIT", @@ -24722,6 +26557,12 @@ "url": "https://github.com/steveukx/git-js?sponsor=1" } }, + "node_modules/simple-html-tokenizer": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz", + "integrity": "sha512-C2WEK/Z3HoSFbYq8tI7ni3eOo/NneSPRoPpcM7WdLjFOArFuyXEjAoCdOC3DgMfRyziZQ1hCNR4mrNdWEvD0og==", + "license": "MIT" + }, "node_modules/sirv": { "version": "3.0.2", "dev": true, @@ -24834,7 +26675,6 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -25270,11 +27110,22 @@ "resolved": "apps/cli", "link": true }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, "node_modules/style-to-object": { - "version": "1.0.6", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", "license": "MIT", "dependencies": { - "inline-style-parser": "0.2.3" + "inline-style-parser": "0.2.7" } }, "node_modules/stylis": { @@ -25426,7 +27277,6 @@ }, "node_modules/symbol-tree": { "version": "3.2.4", - "dev": true, "license": "MIT" }, "node_modules/synckit": { @@ -25604,6 +27454,15 @@ "node": ">=18" } }, + "node_modules/temml": { + "version": "0.10.34", + "resolved": "https://registry.npmjs.org/temml/-/temml-0.10.34.tgz", + "integrity": "sha512-f3b5CaPwPvMviA+CtHy0qoIGWvzpRrNpXmGRc/Y1jc9gAYy+xOlndJFyn7Vfcz7cBcS8QRvv8z0EEH59sHCQxg==", + "license": "MIT", + "engines": { + "node": ">=18.13.0" + } + }, "node_modules/temp": { "version": "0.9.4", "dev": true, @@ -25923,7 +27782,6 @@ }, "node_modules/tough-cookie": { "version": "4.1.4", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", @@ -25937,7 +27795,6 @@ }, "node_modules/tough-cookie/node_modules/universalify": { "version": "0.2.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 4.0.0" @@ -25945,7 +27802,6 @@ }, "node_modules/tr46": { "version": "5.1.1", - "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -26675,6 +28531,27 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-memo-one": { "version": "1.1.3", "license": "MIT", @@ -26682,6 +28559,28 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "license": "MIT", @@ -27568,7 +29467,6 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", - "dev": true, "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -27583,6 +29481,15 @@ "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", "license": "Apache-2.0" }, + "node_modules/wasm-vips": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/wasm-vips/-/wasm-vips-0.0.16.tgz", + "integrity": "sha512-4/bEq8noAFt7DX3VT+Vt5AgNtnnOLwvmrDbduWfiv9AV+VYkbUU4f9Dam9e6khRqPinyClFHCqiwATTTJEiGwA==", + "license": "MIT", + "engines": { + "node": ">=16.4.0" + } + }, "node_modules/watchpack": { "version": "2.5.1", "dev": true, @@ -27626,7 +29533,6 @@ }, "node_modules/webidl-conversions": { "version": "7.0.0", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -27694,7 +29600,6 @@ }, "node_modules/whatwg-encoding": { "version": "3.1.1", - "dev": true, "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" @@ -27705,7 +29610,6 @@ }, "node_modules/whatwg-encoding/node_modules/iconv-lite": { "version": "0.6.3", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -27721,7 +29625,6 @@ }, "node_modules/whatwg-mimetype": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -27729,7 +29632,6 @@ }, "node_modules/whatwg-url": { "version": "14.2.0", - "dev": true, "license": "MIT", "dependencies": { "tr46": "^5.1.0", @@ -27817,6 +29719,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.19", "license": "MIT", @@ -28080,7 +29988,6 @@ }, "node_modules/xml-name-validator": { "version": "5.0.0", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18" @@ -28118,7 +30025,6 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "dev": true, "license": "MIT" }, "node_modules/xtend": { @@ -28128,6 +30034,26 @@ "node": ">=0.4" } }, + "node_modules/y-protocols": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.7.tgz", + "integrity": "sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.85" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^13.0.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "license": "ISC", @@ -28208,6 +30134,23 @@ "node": ">=12" } }, + "node_modules/yjs": { + "version": "13.6.29", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.29.tgz", + "integrity": "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/yn": { "version": "3.1.1", "license": "MIT", From c268249ca5ce9234d94c4110bd9b510dbfd5c8d7 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 25 Feb 2026 14:14:56 +0100 Subject: [PATCH 08/12] Improve block validation, fix Node 20 JSPI flag, increase max turns - Suppress console.error during block validation to eliminate verbose block type object dumps from WordPress's validation logging - Auto-validate post content in wp_cli post create/update commands, rejecting invalid blocks before execution - Add progress messages to validate_blocks showing file and result status - Track last tool name in UI for context-aware result display - Increase validation result truncation limit to 2000 chars - Increase default max turns from 10 to 30 - Make --experimental-wasm-jspi conditional on Node 22+ to fix site startup on Node 20 - Add emitProgress helper to logger - Update system prompt: restructure validation steps, add block content checklist, note wp_cli content validation Co-Authored-By: Claude Opus 4.6 --- apps/cli/ai/agent.ts | 2 +- apps/cli/ai/block-validator.ts | 52 ++++++++++++++++------ apps/cli/ai/system-prompt.ts | 17 +++----- apps/cli/ai/tools.ts | 79 +++++++++++++++++++++++++++++++++- apps/cli/ai/ui.ts | 10 +++-- apps/cli/lib/pm2-manager.ts | 5 ++- apps/cli/logger.ts | 4 ++ 7 files changed, 137 insertions(+), 32 deletions(-) diff --git a/apps/cli/ai/agent.ts b/apps/cli/ai/agent.ts index 25a410b84b..1071a1b08a 100644 --- a/apps/cli/ai/agent.ts +++ b/apps/cli/ai/agent.ts @@ -36,7 +36,7 @@ function buildEnv( apiKey: string ): Record< string, string > { * Caller can iterate messages with `for await` and call `interrupt()` to stop. */ export function startAiAgent( config: AiAgentConfig ): Query { - const { prompt, apiKey, maxTurns = 10, resume, onAskUser } = config; + const { prompt, apiKey, maxTurns = 30, resume, onAskUser } = config; return query( { prompt, diff --git a/apps/cli/ai/block-validator.ts b/apps/cli/ai/block-validator.ts index 1c2eca817e..67df1b37e6 100644 --- a/apps/cli/ai/block-validator.ts +++ b/apps/cli/ai/block-validator.ts @@ -30,16 +30,30 @@ function setupDomEnvironment(): void { const g = global as Record< string, unknown >; - // Copy all window properties to global (includes MutationObserver, etc.) - g.window = dom.window; - g.document = dom.window.document; - g.navigator = dom.window.navigator; - g.MutationObserver = dom.window.MutationObserver; - g.Node = dom.window.Node; - g.HTMLElement = dom.window.HTMLElement; - g.CustomEvent = dom.window.CustomEvent; - g.URL = dom.window.URL; - g.Element = dom.window.Element; + // Helper to set global properties, using Object.defineProperty for read-only ones + // (e.g., `navigator` is getter-only in Node.js 21+) + function setGlobal( key: string, value: unknown ): void { + try { + g[ key ] = value; + } catch { + Object.defineProperty( globalThis, key, { + value, + writable: true, + configurable: true, + } ); + } + } + + // Copy DOM globals needed by WordPress packages + setGlobal( 'window', dom.window ); + setGlobal( 'document', dom.window.document ); + setGlobal( 'navigator', dom.window.navigator ); + setGlobal( 'MutationObserver', dom.window.MutationObserver ); + setGlobal( 'Node', dom.window.Node ); + setGlobal( 'HTMLElement', dom.window.HTMLElement ); + setGlobal( 'CustomEvent', dom.window.CustomEvent ); + setGlobal( 'URL', dom.window.URL ); + setGlobal( 'Element', dom.window.Element ); // Mock matchMedia (used by @wordpress/components and others) if ( ! dom.window.matchMedia ) { @@ -98,8 +112,9 @@ function ensureInitialized(): void { setupDomEnvironment(); } catch ( error ) { initError = `Failed to set up DOM environment: ${ - error instanceof Error ? error.message : String( error ) + error instanceof Error ? error.stack || error.message : String( error ) }. Make sure 'jsdom' is installed.`; + // Error stored in initError, returned via ValidationReport.error initialized = true; return; } @@ -120,8 +135,9 @@ function ensureInitialized(): void { registerCoreBlocks(); } catch ( error ) { initError = `Failed to register core blocks: ${ - error instanceof Error ? error.message : String( error ) + error instanceof Error ? error.stack || error.message : String( error ) }. Make sure '@wordpress/block-library' is installed.`; + // Error stored in initError, returned via ValidationReport.error } finally { console.error = origError; } @@ -133,10 +149,14 @@ function ensureInitialized(): void { const types = getBlockTypes(); if ( ! types || types.length === 0 ) { initError = 'No block types were registered. Block validation will not work correctly.'; + // Error stored in initError, returned via ValidationReport.error + } else { + // Blocks registered successfully } } catch { // If we can't even check, something is very wrong initError = 'Failed to verify block registration.'; + // Error stored in initError, returned via ValidationReport.error } } @@ -262,14 +282,17 @@ export function validateBlocks( content: string ): ValidationReport { }; } - // Suppress WordPress's verbose console output during parse/validation - // (e.g., "Updated Block:", "Block successfully updated for ..." messages) + // Suppress ALL console output during parse/validation. + // WordPress logs verbose messages (e.g., "Updated Block:", block type object dumps) + // via console.log/info/warn/error that would pollute the agent output. const origLog = console.log; const origInfo = console.info; const origWarn = console.warn; + const origError = console.error; console.log = () => {}; console.info = () => {}; console.warn = () => {}; + console.error = () => {}; try { const { parse } = require( '@wordpress/blocks' ); @@ -290,5 +313,6 @@ export function validateBlocks( content: string ): ValidationReport { console.log = origLog; console.info = origInfo; console.warn = origWarn; + console.error = origError; } } diff --git a/apps/cli/ai/system-prompt.ts b/apps/cli/ai/system-prompt.ts index 2ddc9df634..96fc73f5b9 100644 --- a/apps/cli/ai/system-prompt.ts +++ b/apps/cli/ai/system-prompt.ts @@ -14,22 +14,17 @@ For any request that involves a WordPress site, you MUST first determine which s Then continue with: 1. **Get site details**: Use site_info to get the site path, URL, and credentials. -2. **Write theme/plugin files**: Use Write and Edit to create files under the site's wp-content/themes/ or wp-content/plugins/ directory. -3. **Validation loop** (MANDATORY for every file with block content): - a. Before calling validate_blocks, review the file against this block content checklist: - - No \`\` blocks when a core block type can achieve the same result. +2. **Block content checklist** — before writing any file with block content, or creating any page/post with block content, review the content against: + - Never use HTML (\`\`) blocks. Only allow it when wrapping SVGs or markup that no primitive core block support. Never use an HTML block for a big section. - No decorative HTML comments (e.g. \`\`, \`\`). Only block delimiter comments are allowed. - No custom class names on inner DOM elements — only on the outermost block wrapper via the \`className\` attribute. - No inline \`style\` or \`style\` block attributes for styling. Use \`className\` + \`style.css\` instead. - No \`style.backgroundColor\` or \`style.textColor\` block attributes. - Use \`core/spacer\` for empty spacing divs, not \`core/group\`. - No emojis anywhere in generated content. - b. Call validate_blocks with the file path. - c. If validate_blocks reports ANY invalid blocks, fix them in the file. (Ensure design doesn't regress — adapt CSS or markup as needed.) - d. After fixing, call validate_blocks AGAIN on the same file. Repeat steps c–d until validate_blocks reports 0 invalid blocks. - e. Only proceed to the next step once every file with block content passes validation with 0 invalid blocks. - f. NEVER skip re-validation after a fix — the fix itself may introduce new issues. -4. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. +3. **Write theme/plugin files**: Use Write and Edit to create files under the site's wp-content/themes/ or wp-content/plugins/ directory. +4. **Per-file validation** — after writing EACH file with block content (templates, template parts, patterns), call validate_blocks with the file path. If it reports invalid blocks, fix them and re-validate until 0 invalid blocks remain. NEVER skip re-validation after a fix. +6. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. Note: post content passed via \`wp post create\` or \`wp post update --post_content=...\` need to be validated with the validate_blocks tool and adhere to the block content guidelines above as well. ## Available Studio Tools (prefixed with mcp__studio__) @@ -39,7 +34,7 @@ Then continue with: - site_start: Start a stopped site - site_stop: Stop a running site - wp_cli: Run WP-CLI commands on a running site -- validate_blocks: Validate WordPress block content for correctness (checks block markup matches expected save output). MUST be called after every file write/edit that contains block content, and repeated until 0 invalid blocks remain. +- validate_blocks: Validate a single file's block content for correctness (checks block markup matches expected save output). Call after every file write/edit that contains block content. ## General rules diff --git a/apps/cli/ai/tools.ts b/apps/cli/ai/tools.ts index c73312de4c..e3da5d2a67 100644 --- a/apps/cli/ai/tools.ts +++ b/apps/cli/ai/tools.ts @@ -12,7 +12,7 @@ import { runCommand as runStopSiteCommand, Mode as StopMode } from 'cli/commands import { getSiteByFolder, getSiteUrl, readAppdata, type SiteData } from 'cli/lib/appdata'; import { connect, disconnect, setKeepAlive } from 'cli/lib/pm2-manager'; import { isServerRunning, sendWpCliCommand } from 'cli/lib/wordpress-server-manager'; -import { setProgressCallback } from 'cli/logger'; +import { emitProgress, setProgressCallback } from 'cli/logger'; export function setToolProgressHandler( handler: ( message: string ) => void ): void { setProgressCallback( handler ); @@ -235,10 +235,39 @@ const stopSiteTool = tool( } ); +const BLOCK_COMMENT_PATTERN = /\`) blocks. Only allow it when wrapping SVGs or markup that no primitive core block support. Never use an HTML block for a big section. - - No decorative HTML comments (e.g. \`\`, \`\`). Only block delimiter comments are allowed. - - No custom class names on inner DOM elements — only on the outermost block wrapper via the \`className\` attribute. - - No inline \`style\` or \`style\` block attributes for styling. Use \`className\` + \`style.css\` instead. - - No \`style.backgroundColor\` or \`style.textColor\` block attributes. - - Use \`core/spacer\` for empty spacing divs, not \`core/group\`. - - No emojis anywhere in generated content. +2. **Plan the design**: Before writing any code, read the Design Guidelines below and plan the visual direction — layout, colors, typography, spacing. 3. **Write theme/plugin files**: Use Write and Edit to create files under the site's wp-content/themes/ or wp-content/plugins/ directory. -4. **Per-file validation** — after writing EACH file with block content (templates, template parts, patterns), call validate_blocks with the file path. If it reports invalid blocks, fix them and re-validate until 0 invalid blocks remain. NEVER skip re-validation after a fix. -6. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. Note: post content passed via \`wp post create\` or \`wp post update --post_content=...\` need to be validated with the validate_blocks tool and adhere to the block content guidelines above as well. -7. **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 and other visual issues. Fix any issues found. +4. **Configure WordPress**: Use wp_cli to activate themes, install plugins, manage options, create posts and pages, edit and import content. The site must be running. Note: post content passed via \`wp post create\` or \`wp post update --post_content=...\` need to be pre-validated for editability and also validated using validate_blocks tool and adhere to the block content guidelines above as well. +5. **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 and other visual issues. Fix any issues found. ## Available Studio Tools (prefixed with mcp__studio__) @@ -40,11 +37,59 @@ Then continue with: ## General rules +- Design quality and visual ambition are not in conflict with using core blocks. Custom CSS targeting block classNames can achieve any visual design. The block structure is for editability; the CSS is for aesthetics. - Do NOT modify WordPress core files. Only work within wp-content/. - Before running wp_cli, ensure the site is running (site_start if needed). - When building themes, always build block themes (NO CLASSIC THEMES). - Always add the style.css as editor styles in the functions.php of the theme to make the editor match the frontend. - For theme and page content custom CSS, put the styles in the main style.css of the theme. No custom stylesheets. -- Use patterns for complex block structures: For complex sections with multiple nested blocks, create a reusable pattern in the WordPress admin and then use the \`pattern\` attribute in your block comments to insert it. This ensures the content is editable and maintainable for users. + +## Block content guidelines + +- Only use \`core/html\` blocks for: + - Inline SVGs + - \`
\` elements and interactive inputs + - Animation/interaction markup with no block equivalent (marquee, cursor) + - A single \`