From dfb3c9372bca977716d6a60824d5e6ca9f083e03 Mon Sep 17 00:00:00 2001 From: SinachPat Date: Thu, 13 Aug 2026 18:57:12 +0100 Subject: [PATCH] =?UTF-8?q?docs:=20PRD=20v2.0,=20IMPLEMENTATION=20v2.0=20?= =?UTF-8?q?=E2=80=94=20non-technical-first=20pivot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete rewrite of both documents from engineer-first to non-technical-first: Architecture changed: - Desktop app (Tauri + Monaco) → Web app (React + Node.js) - Local Docker runtime → Cloud-hosted sandboxes (Wursor-hosted, VPS) - Code editor + terminal + diff panels → Chat interface + live preview + approve/reject - Tools for engineers (fs, git, wp-cli) → Playbooks for everyone (content, design, plugins) - BYO API key → Wursor-hosted model (Grok) Product changed: - Primary user: WordPress engineer → WordPress site owner (non-technical) - Interface: code editor with panels → single chat input + live preview - Safety model: permission tiers + file diffs → cloud sandbox + approve/reject - Deploy: Git-based → plugin-based with one-click rollback - Competitive frame: dev tools → WP management layer Key new sections: - Cloud sandbox orchestration with warm pool, mirroring, lazy media sync - WordPress plugin connector (REST API, deploy receiver, rollback) - Deploy mechanism (diff engine, pusher, verifier, rollback) - New playbook system (content, design, plugin, site build) - Error states for the web app + plugin paradigm - Non-technical personas, principles, and UX --- IMPLEMENTATION.md | 1567 ++++++++++++++++++++++++--------------------- PRD.md | 666 ++++++++++--------- README.md | 15 +- 3 files changed, 1193 insertions(+), 1055 deletions(-) diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index bcabd23..c52f825 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -1,7 +1,7 @@ -# Implementation Guide — Wursor v1 +# Implementation Guide — Wursor v2 -**Version:** 1.1 -**Source:** [PRD.md](./PRD.md) v1.3 +**Version:** 2.0 +**Source:** [PRD.md](./PRD.md) v2.0 **Method:** Test-driven development (TDD) — every module is written against its tests before its implementation. --- @@ -12,12 +12,12 @@ 2. [Project Structure](#2-project-structure) 3. [Build Phases](#3-build-phases) 4. [Phase 1 — Foundation (Weeks 1–8)](#4-phase-1--foundation-weeks-18) - - [Sprint 1: Tauri shell + Monaco Editor + project scaffold](#sprint-1) - - [Sprint 2: Rust tool bus](#sprint-2) - - [Sprint 3: wp-env runtime manager](#sprint-3) - - [Sprint 4: Agent chat + diff review](#sprint-4) - - [Sprint 5: Permission engine + WP-CLI](#sprint-5) - - [Sprint 6: P0 playbooks + first-run](#sprint-6) + - [Sprint 1: Web app scaffold + sandbox orchestration](#sprint-1) + - [Sprint 2: WordPress plugin connector](#sprint-2) + - [Sprint 3: Agent orchestrator + playbook runner](#sprint-3) + - [Sprint 4: Content playbooks](#sprint-4) + - [Sprint 5: Design playbooks](#sprint-5) + - [Sprint 6: Deploy + rollback](#sprint-6) - [Sprint 7: Integration + exit criteria](#sprint-7) - [Sprint 8: Polish + alpha readiness](#sprint-8) 5. [Phase 2 — Intelligence (Weeks 9–16)](#5-phase-2--intelligence-weeks-916) @@ -31,45 +31,72 @@ ``` ┌──────────────────────────────────────────────────────────┐ -│ Tauri Shell (Rust native process) │ -│ ┌──────────────────────┐ ┌───────────────────────────┐ │ -│ │ System Webview (TS) │ │ Rust Backend │ │ -│ │ ┌────────────────┐ │ │ ┌─────────────────────┐ │ │ -│ │ │ Monaco Editor │ │ │ │ Tool bus (spawn, │ │ │ -│ │ │ (editor core) │ │ │ │ exec, stream) │ │ │ -│ │ ├────────────────┤ │ │ ├─────────────────────┤ │ │ -│ │ │ Wursor panels │ │ │ │ Knowledge graph │ │ │ -│ │ │ - preview │ │ │ │ parser (PHP/JSON │ │ │ -│ │ │ - diff │ │ │ │ scan, 10k files │ │ │ -│ │ │ - state diff │ │ │ │ in ~200ms) │ │ │ -│ │ │ - chat │ │ │ ├─────────────────────┤ │ │ -│ │ │ - status bar │ │ │ │ Permission engine │ │ │ -│ │ └────────────────┘ │ │ │ + secret redaction │ │ │ -│ │ Web frontend │ │ ├─────────────────────┤ │ │ -│ │ (TypeScript/HTML) │ │ │ Runtime manager │ │ │ -│ └──────────────────────┘ │ │ (Docker socket, │ │ │ -│ │ │ wp-env lifecycle) │ │ │ -│ │ ├─────────────────────┤ │ │ -│ │ │ State Diff engine │ │ │ -│ │ │ (create, evaluate, │ │ │ -│ │ │ rollback, persist) │ │ │ -│ │ └─────────────────────┘ │ │ -│ └───────────────────────────┘ │ -└───────────────────────────────────────────────────────────┘ - │ - ▼ -┌──────────────────────────────────────────────────────────┐ -│ Site Runtime (wp-env / Docker) │ -│ WordPress + MySQL + WP-CLI │ +│ User's Browser (Wursor Web App) │ +│ ┌────────────────────┐ ┌─────────────────────────────┐ │ +│ │ Chat Panel │ │ Preview iframe │ │ +│ │ (React) │ │ (sandbox URL, interactive) │ │ +│ │ ┌────────────────┐ │ │ ┌─────────────────────────┐ │ │ +│ │ │ Message list │ │ │ │ Live preview of the │ │ │ +│ │ │ Input field │ │ │ │ sandbox site. User │ │ │ +│ │ │ Approve/Reject │ │ │ │ can click around, │ │ │ +│ │ │ buttons │ │ │ │ navigate pages. │ │ │ +│ │ └────────────────┘ │ │ └─────────────────────────┘ │ │ +│ └────────────────────┘ └─────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ Deploy History (timeline, one-click undo) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└──────────────────────┬───────────────────────────────────┘ + │ HTTPS / SSE +┌──────────────────────▼───────────────────────────────────┐ +│ API Server (Node.js + TypeScript) │ +│ ┌─────────────────────┐ ┌──────────────────────────────┐ │ +│ │ Session Manager │ │ Agent Orchestrator │ │ +│ │ ├─ Auth │ │ ├─ Route request → playbook │ │ +│ │ ├─ Site connection │ │ ├─ Build system prompt │ │ +│ │ └─ Session state │ │ ├─ Dispatch tool calls │ │ +│ │ │ │ └─ Stream results (SSE) │ │ +│ ├─────────────────────┤ ├──────────────────────────────┤ │ +│ │ Sandbox Manager │ │ Playbook Runner │ │ +│ │ ├─ Spin up/down │ │ ├─ Content playbook │ │ +│ │ ├─ Warm pool │ │ ├─ Design playbook │ │ +│ │ ├─ Site mirroring │ │ ├─ Plugin playbook │ │ +│ │ └─ GC │ │ └─ Site build playbook │ │ +│ ├─────────────────────┤ ├──────────────────────────────┤ │ +│ │ Deploy Manager │ │ Plugin API Client │ │ +│ │ ├─ Compute diff │ │ ├─ Site info (read) │ │ +│ │ ├─ Push changes │ │ ├─ File write │ │ +│ │ ├─ Verify deploy │ │ ├─ DB write │ │ +│ │ └─ Rollback │ │ └─ WP-CLI execute │ │ +│ └─────────────────────┘ └──────────────────────────────┘ │ +├──────────────────────────────────────────────────────────┤ +│ Infrastructure (Docker VPS) │ +│ ┌─────────────────────┐ ┌──────────────────────────────┐ │ +│ │ Warm Pool │ │ Active Sandboxes │ │ +│ │ (pre-booted WP imgs)│ │ (ephemeral containers) │ │ +│ │ ┌─────────────────┐ │ │ ┌──────────────────────────┐ │ │ +│ │ │ nginx + PHP 8.x │ │ │ │ WordPress + MySQL │ │ │ +│ │ │ + MySQL 8.x │ │ │ │ + user's theme/plugins │ │ │ +│ │ │ + WP-CLI │ │ │ │ + user's content/media │ │ │ +│ │ └─────────────────┘ │ │ └──────────────────────────┘ │ │ +│ └─────────────────────┘ └──────────────────────────────┘ │ +├──────────────────────────────────────────────────────────┤ +│ Internet │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ User's WordPress Site (their hosting) │ │ +│ │ ┌──────────────────────────────────────────────────┐ │ │ +│ │ │ Wursor Plugin (REST API, WP-CLI, deploy rx) │ │ │ +│ │ └──────────────────────────────────────────────────┘ │ │ +│ └──────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘ ``` -**Key structural decisions:** -- **Monorepo** with a `crates/` directory for Rust packages and a `webview/` directory for the TypeScript frontend -- Rust crates communicate via `tauri::command` IPC to the webview -- Each Rust crate has its own `tests/` directory (integration) and inline `#[cfg(test)]` unit tests -- Integration tests use fixture-based WordPress repos in CI -- E2E tests use Playwright against the Tauri webview +**Key stack decisions:** +- **Backend:** Node.js + TypeScript (fastest path to a working API server; the orchestration is I/O-bound, not CPU-bound) +- **Frontend:** React + TypeScript (chat interface, preview iframe, deploy history) +- **Sandbox:** Docker containers on VPS with pre-baked WordPress image +- **Plugin:** PHP WordPress plugin (standard WordPress plugin architecture) +- **Database:** PostgreSQL for Wursor's own data (users, sites, sessions, deploy history); MySQL inside sandboxes for WordPress +- **Queue:** Redis for SSE streaming, task queues, and cache --- @@ -77,139 +104,155 @@ ``` wursor/ -├── src-tauri/ # Tauri Rust backend +├── api/ # API server (Node.js + TypeScript) │ ├── src/ -│ │ ├── main.rs # Tauri app entry -│ │ ├── lib.rs # Plugin registration -│ │ ├── commands/ # #[tauri::command] IPC handlers -│ │ │ ├── fs.rs # File read/write -│ │ │ ├── project.rs # Project open/detect -│ │ │ ├── runtime.rs # wp-env start/stop/status -│ │ │ ├── tools.rs # Tool bus dispatch -│ │ │ ├── permissions.rs # Policy query -│ │ │ └── verify.rs # Screenshot/HTTP check -│ │ ├── tool-bus/ -│ │ │ ├── mod.rs # Tool registry -│ │ │ ├── registry.rs # Name → {schema, handler, category} -│ │ │ ├── tools/ -│ │ │ │ ├── fs.rs # fs.read, fs.write, fs.apply_patch -│ │ │ │ ├── wpcli.rs # wpcli.run (categorized) -│ │ │ │ ├── site.rs # site.browse, site.screenshot, site.request -│ │ │ │ ├── db.rs # db.query (read-only) -│ │ │ │ ├── lint.rs # lint.phpcs -│ │ │ │ ├── test_runner.rs# test.phpunit -│ │ │ │ └── index.rs # index.search, index.graph_lookup (stub) -│ │ │ └── executor.rs # Shell spawn, timeout, stream -│ │ ├── knowledge-index/ -│ │ │ ├── mod.rs -│ │ │ ├── scanner.rs # Static PHP/JSON scan (Rust-native parser) -│ │ │ ├── enricher.rs # WP-CLI runtime enrichment -│ │ │ ├── graph.rs # Node, edge, store -│ │ │ ├── freshness.rs # Staleness tracking -│ │ │ └── queries.rs # Graph query API -│ │ ├── state-diff/ -│ │ │ ├── mod.rs -│ │ │ ├── lifecycle.rs # create → review → stage → apply → verify → commit -│ │ │ ├── evaluator.rs # Blast radius, intent -│ │ │ ├── rollback.rs # Inverse generation -│ │ │ └── serializer.rs # .state-diff.json format -│ │ ├── runtime-manager/ -│ │ │ ├── mod.rs -│ │ │ ├── interface.rs # RuntimeAdapter trait -│ │ │ ├── adapters/ -│ │ │ │ └── wp_env.rs # wp-env adapter (v1 only) -│ │ │ ├── lifecycle.rs # State machine -│ │ │ └── logs.rs # Log tailing -│ │ ├── permission-engine/ -│ │ │ ├── mod.rs -│ │ │ ├── tiers.rs # Permission tiers enum -│ │ │ ├── evaluator.rs # Tool call → policy check -│ │ │ ├── redactor.rs # Secret redaction (.env, wp-config) -│ │ │ └── config.rs # User policy (.wursor/policy.json) -│ │ ├── verify/ -│ │ │ ├── mod.rs -│ │ │ ├── screenshot.rs # Screenshot capture -│ │ │ ├── http_check.rs # URL load + status + error sniff -│ │ │ ├── editor_check.rs # Block editor route check -│ │ │ └── reporter.rs # Result formatting -│ │ ├── agent-bridge/ -│ │ │ ├── mod.rs -│ │ │ ├── client.rs # Grok API client (BYO key) -│ │ │ ├── tool_schemas.rs # Tool schemas → Grok format -│ │ │ ├── context.rs # System prompt + WP context -│ │ │ └── fallback.rs # Error handling + retry -│ │ └── playbooks/ -│ │ ├── mod.rs -│ │ ├── registry.rs # Playbook registry -│ │ ├── dynamic_block.rs -│ │ ├── child_theme.rs -│ │ ├── cpt.rs -│ │ └── plugin.rs -│ ├── tests/ # Integration tests -│ │ ├── tool_bus_test.rs -│ │ ├── runtime_manager_test.rs -│ │ ├── knowledge_index_test.rs -│ │ ├── state_diff_test.rs -│ │ ├── permission_engine_test.rs -│ │ └── playbooks_test.rs -│ ├── fixtures/ # Test WP repos -│ │ ├── classic-theme/ -│ │ ├── block-theme/ -│ │ └── single-plugin/ -│ ├── Cargo.toml -│ └── tauri.conf.json +│ │ ├── index.ts # Express/Fastify server entry +│ │ ├── routes/ +│ │ │ ├── auth.ts # Sign-up, sign-in, session +│ │ │ ├── sites.ts # Site connection, plugin pairing +│ │ │ ├── sessions.ts # Chat session create/resume +│ │ │ ├── chat.ts # Chat message, SSE stream +│ │ │ ├── preview.ts # Preview URL, sandbox status +│ │ │ ├── deploy.ts # Approve, deploy, rollback +│ │ │ └── webhooks.ts # Plugin webhook receiver +│ │ ├── services/ +│ │ │ ├── session-manager.ts +│ │ │ ├── agent-orchestrator.ts +│ │ │ ├── playbook-runner.ts +│ │ │ ├── sandbox-manager.ts +│ │ │ ├── deploy-manager.ts +│ │ │ ├── plugin-client.ts +│ │ │ └── warm-pool.ts +│ │ ├── agents/ +│ │ │ ├── grok-client.ts # Grok API client +│ │ │ ├── prompt-builder.ts # System prompt per session +│ │ │ ├── tool-schemas.ts # Tool schemas → Grok format +│ │ │ └── fallback.ts # Error handling, retry +│ │ ├── playbooks/ +│ │ │ ├── registry.ts # Playbook registry +│ │ │ ├── content.ts # Content edit playbook +│ │ │ ├── design.ts # Design change playbook +│ │ │ ├── plugin.ts # Plugin install playbook +│ │ │ └── site-build.ts # Site build playbook (P0 limited) +│ │ ├── sandbox/ +│ │ │ ├── docker-client.ts # Docker API client +│ │ │ ├── image-manager.ts # Pre-baked image management +│ │ │ ├── mirror.ts # Site mirroring (content, themes, plugins) +│ │ │ ├── media-sync.ts # Lazy media sync +│ │ │ └── gc.ts # Garbage collection (idle, hard timeout) +│ │ ├── deploy/ +│ │ │ ├── diff-engine.ts # Compare sandbox → live site +│ │ │ ├── pusher.ts # Push changes via plugin API +│ │ │ ├── verifier.ts # Verify live site after deploy +│ │ │ └── rollback.ts # Snapshot-based rollback +│ │ ├── models/ +│ │ │ ├── user.ts +│ │ │ ├── site.ts +│ │ │ ├── session.ts +│ │ │ ├── deploy-log.ts +│ │ │ └── sandbox.ts +│ │ └── lib/ +│ │ ├── crypto.ts # Token generation, encryption +│ │ ├── sse.ts # Server-sent events +│ │ └── queue.ts # Redis queue +│ ├── __tests__/ +│ │ ├── services/ +│ │ │ ├── agent-orchestrator.test.ts +│ │ │ ├── sandbox-manager.test.ts +│ │ │ ├── deploy-manager.test.ts +│ │ │ └── playbook-runner.test.ts +│ │ ├── agents/ +│ │ │ ├── grok-client.test.ts +│ │ │ ├── prompt-builder.test.ts +│ │ │ └── tool-schemas.test.ts +│ │ ├── playbooks/ +│ │ │ ├── content.test.ts +│ │ │ ├── design.test.ts +│ │ │ └── plugin.test.ts +│ │ ├── sandbox/ +│ │ │ ├── mirror.test.ts +│ │ │ ├── media-sync.test.ts +│ │ │ └── gc.test.ts +│ │ └── deploy/ +│ │ ├── diff-engine.test.ts +│ │ ├── pusher.test.ts +│ │ └── rollback.test.ts +│ ├── package.json +│ └── tsconfig.json │ -├── webview/ # Tauri webview frontend (TypeScript) +├── web/ # Web frontend (React + TypeScript) │ ├── src/ -│ │ ├── main.tsx # App entry -│ │ ├── App.tsx # Root component +│ │ ├── main.tsx +│ │ ├── App.tsx +│ │ ├── pages/ +│ │ │ ├── Chat.tsx # Main chat + preview view +│ │ │ ├── SignIn.tsx +│ │ │ ├── SignUp.tsx +│ │ │ ├── ConnectSite.tsx # Plugin pairing flow +│ │ │ └── History.tsx # Deploy history timeline │ │ ├── components/ -│ │ │ ├── Editor.tsx # Monaco Editor wrapper -│ │ │ ├── Preview.tsx # Site preview iframe -│ │ │ ├── ChatPanel.tsx # Agent chat -│ │ │ ├── DiffPanel.tsx # Side-by-side file diff -│ │ │ ├── StateDiffPanel.tsx # State Diff lifecycle UI -│ │ │ ├── StatusBar.tsx # Site status, permissions -│ │ │ ├── Terminal.tsx # Integrated terminal -│ │ │ └── FirstRun.tsx # Onboarding wizard +│ │ │ ├── ChatPanel.tsx +│ │ │ ├── Preview.tsx +│ │ │ ├── ApproveBar.tsx +│ │ │ ├── DeployTimeline.tsx +│ │ │ ├── SiteConnector.tsx +│ │ │ └── WelcomeScreen.tsx │ │ ├── hooks/ -│ │ │ ├── useToolBus.ts # Invoke Rust commands -│ │ │ ├── useRuntime.ts # Runtime state -│ │ │ └── useAgent.ts # Agent chat state -│ │ ├── utils/ -│ │ │ ├── monaco-setup.ts # Monaco theme, WP stubs -│ │ │ └── tauri-api.ts # @tauri-apps/api wrappers +│ │ │ ├── useChat.ts +│ │ │ ├── usePreview.ts +│ │ │ ├── useSession.ts +│ │ │ └── useDeploy.ts │ │ └── styles/ │ │ └── global.css │ ├── __tests__/ -│ │ ├── App.test.tsx │ │ ├── ChatPanel.test.tsx -│ │ ├── DiffPanel.test.tsx -│ │ └── FirstRun.test.tsx +│ │ ├── Preview.test.tsx +│ │ ├── ApproveBar.test.tsx +│ │ └── SiteConnector.test.tsx │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ └── package.json │ -├── e2e/ # End-to-end tests -│ ├── tauri/ -│ │ ├── open-project.test.ts -│ │ ├── detect-wp.test.ts -│ │ ├── boot-preview.test.ts -│ │ ├── playbook-dynamic-block.test.ts -│ │ └── verify-preview.test.ts -│ ├── fixtures/ -│ │ ├── sample-theme/ -│ │ └── sample-plugin/ -│ └── playwright.config.ts +├── plugin/ # WordPress plugin (PHP) +│ ├── wursor.php # Plugin header, bootstrap +│ ├── src/ +│ │ ├── class-api.php # REST API handlers +│ │ ├── class-auth.php # Token auth, pairing +│ │ ├── class-deploy.php # Deploy receiver (files, DB, WP-CLI) +│ │ ├── class-rollback.php # Snapshot-based rollback +│ │ ├── class-site-info.php # Site info provider +│ │ └── class-admin.php # Admin settings page +│ ├── __tests__/ +│ │ ├── test-api.php +│ │ ├── test-auth.php +│ │ ├── test-deploy.php +│ │ └── test-rollback.php +│ └── readme.txt │ -├── Cargo.toml # Workspace root -├── package.json # Scripts, dev tooling +├── infrastructure/ # Infrastructure scripts +│ ├── docker/ +│ │ ├── Dockerfile.wordpress # Pre-baked WordPress image +│ │ └── docker-compose.yml # For local dev +│ ├── scripts/ +│ │ ├── warm-pool.ts # Warm pool manager +│ │ ├── gc.ts # Garbage collection cron +│ │ └── deploy.ts # Deploy hook +│ └── terraform/ +│ └── main.tf # VPS provisioning (v1: manual, v2: Terraform) +│ +├── e2e/ # End-to-end tests +│ ├── chat-flow.test.ts +│ ├── plugin-connection.test.ts +│ ├── deploy-flow.test.ts +│ └── rollback.test.ts +│ +├── package.json ├── pnpm-workspace.yaml -├── rust-toolchain.toml +├── tsconfig.base.json └── .github/workflows/ - ├── ci.yml # Rust tests + webview tests on PR - └── e2e.yml # E2E on release branch + ├── ci.yml + └── e2e.yml ``` --- @@ -218,10 +261,10 @@ wursor/ | Phase | Weeks | Output | Exit criteria | |-------|-------|--------|---------------| -| **Phase 1** | 1–8 | Tauri shell, Monaco Editor, Rust tool bus, wp-env, chat, playbooks, first-run | Clean machine → live preview ≤10 min; P0 playbook completes | -| **Phase 2** | 9–16 | Knowledge graph, State Diffs, quality gates, staging pull, closed alpha | All §11 baselines collected; State Diff lifecycle demoed | -| **Phase 3** | 17–28 | Block/FSE workshop, deploy connectors, team playbooks, paid beta | — | -| **Phase 4** | 29+ | WooCommerce, multisite, maintenance agents, ecosystem | — | +| **Phase 1** | 1–8 | Web app, chat, preview, sandbox, plugin, deploy, content+design playbooks | New user connects site, makes a content change, previews, approves ≤5 min | +| **Phase 2** | 9–16 | Plugin playbooks, site build, agent disambiguation, closed alpha | Non-technical user installs a plugin via chat | +| **Phase 3** | 17–28 | Multi-step workflows, design picker, SEO, performance, paid beta | — | +| **Phase 4** | 29+ | Multi-site, team, e-commerce, scheduled changes, marketplace | — | --- @@ -231,686 +274,728 @@ wursor/ --- -### Sprint 1: Tauri shell + Monaco Editor + project scaffold +### Sprint 1: Web app scaffold + sandbox orchestration -**Goal:** A working Tauri window with Monaco Editor that opens a folder and shows a Wursor sidebar. +**Goal:** A user can sign up, see a chat interface, and Wursor spins up a sandbox WordPress instance. #### TDD sequence -**Step 1 — Write the test that defines "done"** +**Step 1 — Write the integration test** ```typescript -// e2e/tauri/open-project.test.ts +// e2e/chat-flow.test.ts import { test, expect } from '@playwright/test'; -import { _electron as tauri } from 'tauri-playwright'; -test('opens a folder and shows Wursor sidebar', async () => { - const app = await tauri.launch({ - args: ['--project', '/path/to/fixtures/sample-theme'], +test('user signs up, starts a session, sandbox spins up', async ({ page }) => { + await page.goto('http://localhost:3000'); + await page.locator('.wursor-signup-button').click(); + await page.locator('input[name=email]').fill('test@example.com'); + await page.locator('input[name=password]').fill('password123'); + await page.locator('.wursor-submit').click(); + + // Should see the chat interface + await expect(page.locator('.wursor-chat-input')).toBeVisible(); + await expect(page.locator('.wursor-welcome')).toContainText('Describe what you'); + + // Type a request + await page.locator('.wursor-chat-input').fill('Change the homepage heading to "Hello World"'); + await page.locator('.wursor-chat-send').click(); + + // Agent should acknowledge and start working + await expect(page.locator('.wursor-message-agent')).toContainText('working on it', { timeout: 30000 }); +}); +``` + +**Step 2 — Write the unit tests** + +```typescript +// api/__tests__/sandbox/mirror.test.ts +describe('Mirror', () => { + it('connects to the live site and fetches site info', async () => { + const mirror = new Mirror({ siteUrl: 'https://example.com', token: 'test-token' }); + const info = await mirror.fetchSiteInfo(); + expect(info.theme).toBeDefined(); + expect(info.plugins.length).toBeGreaterThan(0); }); - const window = await app.webview(); - await expect(window.locator('.wursor-sidebar')).toBeVisible(); - await expect(window.locator('.monaco-editor')).toBeVisible(); - await app.close(); + + it('copies theme and active plugins to the sandbox', async () => { + const mirror = new Mirror({ sandboxId: 'sb-123' }); + await mirror.copyTheme('twentytwentyfour'); + await mirror.copyPlugins(['woocommerce', 'contact-form-7']); + // Verify files exist in the sandbox + expect(await mirror.sandboxFileExists('/wp-content/themes/twentytwentyfour')).toBe(true); + }); + + it('lazy-syncs media only when accessed', async () => { + // Media should not be synced during mirror, only on first access + }); +}); + +// api/__tests__/sandbox/docker-client.test.ts +describe('DockerClient', () => { + it('spins up a sandbox container from the pre-baked image', async () => { + const client = new DockerClient(); + const container = await client.createSandbox('wursor-base:latest'); + expect(container.id).toBeDefined(); + expect(container.status).toBe('running'); + }); + + it('destroys a sandbox container', async () => { + const client = new DockerClient(); + await client.destroySandbox('sb-123'); + const status = await client.getStatus('sb-123'); + expect(status).toBe('destroyed'); + }); +}); + +// api/__tests__/sandbox/gc.test.ts +describe('GarbageCollection', () => { + it('destroys sandboxes after 15 minutes of idle', async () => { /* ... */ }); + it('destroys sandboxes after 24 hours regardless', async () => { /* ... */ }); + it('does not destroy active sandboxes', async () => { /* ... */ }); }); ``` -**Step 2 — Write the Rust unit tests** +**Step 3 — Implement** -```rust -// src-tauri/src/commands/project.rs (tests module) -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_open_valid_project() { - let result = open_project("/path/to/fixtures/sample-theme"); - assert!(result.is_ok()); - assert_eq!(result.unwrap().project_type, "classic-theme"); - } - - #[test] - fn test_open_invalid_path() { - let result = open_project("/nonexistent"); - assert!(result.is_err()); - } -} -``` - -**Step 3 — Write the webview unit tests** - -```typescript -// webview/__tests__/App.test.tsx -describe('App', () => { - it('renders Monaco Editor after project open', () => { /* ... */ }); - it('shows Wursor sidebar', () => { /* ... */ }); - it('displays project name in the title bar', () => { /* ... */ }); -}); -``` - -**Step 4 — Implement** - -- **`src-tauri/src/main.rs`** — Tauri builder, register commands -- **`src-tauri/src/commands/project.rs`** — `open_project` command -- **`webview/src/App.tsx`** — Root layout: editor + sidebar -- **`webview/src/components/Editor.tsx`** — Monaco Editor wrapper (`@monaco-editor/react`) -- **`webview/src/components/ChatPanel.tsx`** — Sidebar (placeholder) -- **`webview/src/utils/monaco-setup.ts`** — Dark theme, PHP stubs, `block.json` schemas -- **`src-tauri/tauri.conf.json`** — Window config, permissions -- **`src-tauri/Cargo.toml`** — Dependencies: tauri, serde, tokio, etc. +- **`web/`** — React app with sign-up, sign-in, chat interface (placeholder) +- **`api/src/index.ts`** — Express server with auth routes +- **`api/src/routes/auth.ts`** — Sign-up, sign-in, session management +- **`api/src/routes/sessions.ts`** — Create session, stream SSE +- **`api/src/services/sandbox-manager.ts`** — Orchestrate sandbox lifecycle +- **`api/src/sandbox/docker-client.ts`** — Docker API client (dockerode) +- **`api/src/sandbox/image-manager.ts`** — Pre-baked image → Dockerfile +- **`api/src/sandbox/mirror.ts`** — Site mirroring (stub plugin client) +- **`api/src/sandbox/media-sync.ts`** — Lazy media sync (stub) +- **`api/src/sandbox/gc.ts`** — Garbage collection (idle timeout, hard timeout) +- **`infrastructure/docker/Dockerfile.wordpress`** — Pre-baked image +- **`infrastructure/scripts/warm-pool.ts`** — Warm pool manager #### Deliverables -- Tauri app that opens a folder and shows Monaco Editor + sidebar -- `e2e/tauri/open-project.test.ts` passing -- Rust unit tests for `open_project` command -- Webview unit tests for `App` component +- Web app with sign-up and chat interface +- Sandbox spin-up from pre-baked image +- `e2e/chat-flow.test.ts` passing (sign-up → sees chat) +- All unit tests passing --- -### Sprint 2: Rust tool bus +### Sprint 2: WordPress plugin connector -**Goal:** All tools from §8.3 are registered Rust commands with schemas, handlers, and shell execution. - -#### TDD sequence - -**Step 1 — Write the Rust unit tests** - -```rust -// src-tauri/src/tool-bus/registry.rs -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_register_tool() { - let mut registry = ToolRegistry::new(); - registry.register("fs.read", fs_schema(), fs_handler()); - assert!(registry.get("fs.read").is_some()); - } - - #[test] - fn test_duplicate_tool_name_errors() { - let mut registry = ToolRegistry::new(); - registry.register("fs.read", fs_schema(), fs_handler()); - assert!(registry.register("fs.read", fs_schema(), fs_handler()).is_err()); - } -} - -// src-tauri/src/tool-bus/tools/fs.rs -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_fs_read_workspace_file() { - let result = fs_read(FsReadArgs { path: "fixtures/sample.txt".into() }).await; - assert!(result.is_ok()); - assert_eq!(result.unwrap().content, "hello"); - } - - #[tokio::test] - async fn test_fs_read_rejects_path_traversal() { - let result = fs_read(FsReadArgs { path: "../../etc/passwd".into() }).await; - assert!(result.is_err()); - } - - #[tokio::test] - async fn test_fs_read_missing_file() { - let result = fs_read(FsReadArgs { path: "nonexistent.txt".into() }).await; - assert!(result.is_err()); - } -} - -// src-tauri/src/tool-bus/tools/wpcli.rs -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_wpcli_run_safe_command() { - let result = wpcli_run(WpCliArgs { - command: "wp option get blogname".into(), - category: "safe".into(), - }).await; - assert!(result.is_ok()); - assert_eq!(result.unwrap().exit_code, 0); - } - - #[tokio::test] - async fn test_wpcli_rejects_destructive_without_confirm() { - let result = wpcli_run(WpCliArgs { - command: "wp db drop".into(), - category: "destructive".into(), - }).await; - assert!(result.is_err()); - assert!(result.unwrap_err().contains("confirmation required")); - } -} - -// src-tauri/src/tool-bus/executor.rs -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_execute_shell_command() { - let output = execute("echo hello", None).await.unwrap(); - assert_eq!(output.stdout.trim(), "hello"); - } - - #[tokio::test] - async fn test_execute_timeout() { - let result = execute("sleep 60", Some(Duration::from_millis(100))).await; - assert!(result.is_err()); - } -} -``` - -**Step 2 — Implement** - -- **`src-tauri/src/tool-bus/registry.rs`** — `HashMap` with schema + handler -- **`src-tauri/src/tool-bus/tools/fs.rs`** — Path-scoped file operations -- **`src-tauri/src/tool-bus/tools/wpcli.rs`** — WP-CLI spawn with categorized allowlist -- **`src-tauri/src/tool-bus/tools/site.rs`** — HTTP fetch (reqwest) + screenshot via headless webview -- **`src-tauri/src/tool-bus/tools/db.rs`** — MySQL read-only query via mysql crate -- **`src-tauri/src/tool-bus/tools/lint.rs`** — PHPCS spawn -- **`src-tauri/src/tool-bus/tools/test_runner.rs`** — PHPUnit spawn -- **`src-tauri/src/tool-bus/tools/index.rs`** — Stub (throws "not implemented" until Phase 2) -- **`src-tauri/src/tool-bus/executor.rs`** — `tokio::process::Command` wrapper with timeout + streaming - -#### Deliverables - -- Rust tool registry with all 9 tools -- Each tool has unit tests for happy path, error path, and security boundary -- `cargo test --package tool-bus` passes - ---- - -### Sprint 3: wp-env runtime manager - -**Goal:** Start/stop/reset a WordPress site via wp-env from Rust, show status in the webview. +**Goal:** User installs the Wursor plugin on their site, pairs it with Wursor, and Wursor can read site info. #### TDD sequence **Step 1 — Write the tests** -```rust -// src-tauri/src/runtime-manager/lifecycle.rs -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_start_wp_env() { - let manager = RuntimeManager::new(WpEnvAdapter::new()); - let status = manager.start().await.unwrap(); - assert_eq!(status, RuntimeStatus::Running); +```php +// plugin/__tests__/test-auth.php +class WursorAuthTest extends WP_UnitTestCase { + public function test_generates_pairing_code() { + $auth = new Wursor_Auth(); + $code = $auth->generate_pairing_code(); + $this->assertEquals(6, strlen($code)); + $this->assertMatchesRegularExpression('/^[A-Z0-9]{6}$/', $code); } - #[tokio::test] - async fn test_stop_wp_env() { - let manager = RuntimeManager::new(WpEnvAdapter::new()); - manager.start().await.unwrap(); - let status = manager.stop().await.unwrap(); - assert_eq!(status, RuntimeStatus::Stopped); + public function test_verifies_valid_token() { + $auth = new Wursor_Auth(); + $token = $auth->generate_token(); + $this->assertTrue($auth->verify_token($token)); } - #[tokio::test] - async fn test_status_when_stopped() { - let manager = RuntimeManager::new(WpEnvAdapter::new()); - assert_eq!(manager.status().await, RuntimeStatus::Stopped); - } - - #[tokio::test] - async fn test_log_streaming() { - let manager = RuntimeManager::new(WpEnvAdapter::new()); - let mut logs = manager.stream_logs().await.unwrap(); - let entry = logs.next().await; - assert!(entry.is_some()); + public function test_rejects_invalid_token() { + $auth = new Wursor_Auth(); + $this->assertFalse($auth->verify_token('invalid')); } } -// src-tauri/src/runtime-manager/adapters/wp_env.rs -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_spawn_wp_env_start() { - let adapter = WpEnvAdapter::new(); - let result = adapter.start().await; - assert!(result.is_ok()); +// plugin/__tests__/test-api.php +class WursorApiTest extends WP_UnitTestCase { + public function test_returns_site_info() { + $api = new Wursor_API(); + $response = $api->get_site_info(); + $this->assertArrayHasKey('theme', $response); + $this->assertArrayHasKey('plugins', $response); + $this->assertArrayHasKey('wordpress_version', $response); + $this->assertArrayHasKey('php_version', $response); } - #[tokio::test] - async fn test_parse_wp_env_output() { - let output = "WordPress development site.\n\nℹ http://localhost:8888\n\n✔ Okay!"; - let parsed = WpEnvAdapter::parse_output(output); - assert_eq!(parsed.url, "http://localhost:8888"); + public function test_requires_auth() { + $api = new Wursor_API(); + $response = $api->handle_request('GET', '/site-info', []); + $this->assertEquals(401, $response['status']); } +} - #[tokio::test] - async fn test_handle_wp_env_not_found() { - let adapter = WpEnvAdapter::new(); - // Simulate missing wp-env by clearing PATH - let result = adapter.start().await; - assert!(result.is_err()); - assert!(result.unwrap_err().contains("wp-env not found")); +// plugin/__tests__/test-deploy.php (placeholder) +class WursorDeployTest extends WP_UnitTestCase { + public function test_receives_file_change() { + // Stub for Sprint 6 + $this->markTestSkipped('Deploy test in Sprint 6'); } } ``` ```typescript -// webview/__tests__/Preview.test.tsx -describe('Preview', () => { - it('shows a loading state while runtime starts', () => { /* ... */ }); - it('renders the site iframe when running', () => { /* ... */ }); - it('shows error state when runtime fails', () => { /* ... */ }); +// api/__tests__/services/plugin-client.test.ts +describe('PluginClient', () => { + it('connects to the plugin and fetches site info', async () => { + const client = new PluginClient({ siteUrl: 'https://example.com', token: 'valid-token' }); + const info = await client.getSiteInfo(); + expect(info.theme).toBeDefined(); + expect(info.plugins).toBeInstanceOf(Array); + }); + + it('throws on invalid token', async () => { + const client = new PluginClient({ siteUrl: 'https://example.com', token: 'invalid' }); + await expect(client.getSiteInfo()).rejects.toThrow('Authentication failed'); + }); + + it('handles unreachable site', async () => { + const client = new PluginClient({ siteUrl: 'https://nonexistent.example.com', token: 'token' }); + await expect(client.getSiteInfo()).rejects.toThrow('Site unreachable'); + }); +}); +``` + +```typescript +// web/__tests__/SiteConnector.test.tsx +describe('SiteConnector', () => { + it('shows the pairing code', () => { /* ... */ }); + it('polls for connection status', () => { /* ... */ }); + it('shows success state when connected', () => { /* ... */ }); + it('shows error state when connection fails', () => { /* ... */ }); }); ``` **Step 2 — Implement** -- **`src-tauri/src/runtime-manager/interface.rs`** — `RuntimeAdapter` trait -- **`src-tauri/src/runtime-manager/adapters/wp_env.rs`** — Spawns `npx wp-env start`, parses URL/creds -- **`src-tauri/src/runtime-manager/lifecycle.rs`** — State machine: Stopped → Starting → Running → Stopping → Stopped -- **`src-tauri/src/runtime-manager/logs.rs`** — Tail `wp-env logs` via tokio process -- **`src-tauri/src/commands/runtime.rs`** — `start_runtime`, `stop_runtime`, `runtime_status` IPC commands -- **`webview/src/components/Preview.tsx`** — iframe pointing to site URL -- **`webview/src/components/StatusBar.tsx`** — Runtime status indicator -- **`webview/src/hooks/useRuntime.ts`** — React hook subscribing to runtime state +- **`plugin/wursor.php`** — Plugin header, activation hook, bootstrap +- **`plugin/src/class-auth.php`** — Token generation, verification, pairing code +- **`plugin/src/class-api.php`** — REST API endpoints (site-info, files, DB, WP-CLI) +- **`plugin/src/class-site-info.php`** — Site info provider (theme, plugins, WP version, PHP version) +- **`plugin/src/class-admin.php`** — Admin settings page (pairing code display) +- **`api/src/services/plugin-client.ts`** — HTTP client for the plugin API +- **`api/src/routes/sites.ts`** — Site connection flow, pairing +- **`web/src/components/SiteConnector.tsx`** — Pairing UI (show code, wait for connection) +- **`web/src/pages/ConnectSite.tsx`** — Connection page #### Deliverables -- Rust runtime manager with wp-env adapter -- Preview panel showing the live site -- Status bar showing runtime state -- `cargo test --package runtime-manager` passing +- WordPress plugin with pairing and site-info API +- Plugin client in the API server +- Connection flow: user installs plugin → gets code → enters in Wursor → connected +- `plugin/__tests__/test-auth.php` and `test-api.php` passing +- `api/__tests__/services/plugin-client.test.ts` passing +- `web/__tests__/SiteConnector.test.tsx` passing --- -### Sprint 4: Agent chat + diff review +### Sprint 3: Agent orchestrator + playbook runner -**Goal:** Chat panel that sends tasks to Grok, receives tool calls, dispatches through the Rust tool bus, and shows diffs. +**Goal:** User types a request, the agent orchestrator routes it to a playbook, and the playbook executes in the sandbox. #### TDD sequence **Step 1 — Write the tests** -```rust -// src-tauri/src/agent-bridge/client.rs -#[cfg(test)] -mod tests { - use super::*; +```typescript +// api/__tests__/agents/grok-client.test.ts +describe('GrokClient', () => { + it('sends a message and returns a response', async () => { + const client = new GrokClient({ apiKey: 'test-key' }); + const response = await client.send('Change the homepage heading to "Hello"'); + expect(response.type).toBe('tool_call'); + }); - #[tokio::test] - async fn test_send_message_to_grok() { - let client = GrokClient::new("test-key"); - let response = client.send("Add a paragraph to index.php").await.unwrap(); - assert_eq!(response.message_type, "tool_call"); - } + it('handles API errors with a clear message', async () => { + const client = new GrokClient({ apiKey: 'invalid-key' }); + await expect(client.send('hello')).rejects.toThrow('API error'); + }); - #[tokio::test] - async fn test_handle_api_error() { - let client = GrokClient::new("invalid-key"); - let result = client.send("hello").await; - assert!(result.is_err()); - assert!(result.unwrap_err().contains("API error")); - } -} + it('handles rate limiting with retry', async () => { /* ... */ }); +}); -// src-tauri/src/agent-bridge/tool_schemas.rs -#[cfg(test)] -mod tests { - #[test] - fn test_converts_tool_registry_to_grok_format() { - let schemas = to_grok_format(®istry); - assert_eq!(schemas[0].name, "fs.read"); - assert!(schemas[0].input_schema.is_object()); - } -} +// api/__tests__/agents/prompt-builder.test.ts +describe('PromptBuilder', () => { + it('builds a system prompt with site context', async () => { + const builder = new PromptBuilder(); + const prompt = await builder.build({ + siteInfo: { theme: 'twentytwentyfour', plugins: ['woocommerce'] }, + userGoal: 'Change the homepage', + }); + expect(prompt).toContain('twentytwentyfour'); + expect(prompt).toContain('woocommerce'); + expect(prompt).toContain('never touch the live site'); + }); -// src-tauri/src/agent-bridge/context.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_builds_system_prompt_with_wp_semantics() { - let ctx = ContextBuilder::new() - .with_project_rules("fixtures/WORDPRESS.md") - .build(); - assert!(ctx.contains("WordPress")); - assert!(ctx.contains("template hierarchy")); - } -} + it('includes safety rules', async () => { + const builder = new PromptBuilder(); + const prompt = await builder.build({ siteInfo: {}, userGoal: '' }); + expect(prompt).toContain('sandbox'); + expect(prompt).toContain('approval'); + }); +}); + +// api/__tests__/agents/tool-schemas.test.ts +describe('ToolSchemas', () => { + it('generates tool schemas for the Grok API', () => { + const schemas = generateToolSchemas(); + expect(schemas.length).toBeGreaterThan(0); + expect(schemas[0].name).toBe('wp_cli'); + expect(schemas[0].parameters).toBeDefined(); + }); +}); + +// api/__tests__/services/agent-orchestrator.test.ts +describe('AgentOrchestrator', () => { + it('routes a content request to the content playbook', async () => { + const orchestrator = new AgentOrchestrator(); + const playbook = await orchestrator.route('Change the homepage heading to "Hello"'); + expect(playbook.name).toBe('content'); + }); + + it('routes a plugin request to the plugin playbook', async () => { + const orchestrator = new AgentOrchestrator(); + const playbook = await orchestrator.route('Install a contact form plugin'); + expect(playbook.name).toBe('plugin'); + }); + + it('streams updates to the frontend via SSE', async () => { + // Mock SSE connection, verify events are sent + }); +}); + +// api/__tests__/services/playbook-runner.test.ts +describe('PlaybookRunner', () => { + it('executes a content playbook and returns the result', async () => { + const runner = new PlaybookRunner({ sandboxId: 'sb-123' }); + const result = await runner.run('content', { + type: 'edit_text', + target: 'homepage', + changes: { heading: 'Hello World' }, + }); + expect(result.success).toBe(true); + expect(result.previewUrl).toBe('http://sb-123.wursor.dev'); + }); +}); ``` ```typescript -// webview/__tests__/ChatPanel.test.tsx +// web/__tests__/ChatPanel.test.tsx describe('ChatPanel', () => { - it('sends a message and displays the response', () => { /* ... */ }); - it('shows tool calls as expandable cards', () => { /* ... */ }); - it('shows diffs in a side-by-side view', () => { /* ... */ }); - it('shows error state when Grok is unreachable', () => { /* ... */ }); + it('sends a message and displays the agent response', () => { /* ... */ }); + it('shows typing indicator while agent works', () => { /* ... */ }); + it('shows the preview when ready', () => { /* ... */ }); + it('shows error state when agent fails', () => { /* ... */ }); }); ``` **Step 2 — Implement** -- **`src-tauri/src/agent-bridge/client.rs`** — Grok API client (messages API, tool use, streaming) -- **`src-tauri/src/agent-bridge/tool_schemas.rs`** — Convert tool registry → Grok `tools` array -- **`src-tauri/src/agent-bridge/context.rs`** — Build system prompt with WP semantics, project rules, graph state -- **`src-tauri/src/agent-bridge/fallback.rs`** — Error handling, retry with exponential backoff -- **`webview/src/components/ChatPanel.tsx`** — Chat UI (message list, input, tool call cards, streaming) -- **`webview/src/components/DiffPanel.tsx`** — Side-by-side diff view (Monaco diff editor) -- **`webview/src/hooks/useAgent.ts`** — React hook for agent state +- **`api/src/agents/grok-client.ts`** — Grok API client (messages API, tool use, streaming) +- **`api/src/agents/prompt-builder.ts`** — Build system prompt per session +- **`api/src/agents/tool-schemas.ts`** — Tool schemas → Grok format +- **`api/src/agents/fallback.ts`** — Error handling, retry +- **`api/src/services/agent-orchestrator.ts`** — Route requests, dispatch tools, stream results +- **`api/src/services/playbook-runner.ts`** — Execute playbook steps in sandbox +- **`api/src/playbooks/registry.ts`** — Playbook registry +- **`api/src/routes/chat.ts`** — Chat message endpoint, SSE stream +- **`web/src/components/ChatPanel.tsx`** — Chat UI with message list, input, typing indicator +- **`web/src/hooks/useChat.ts`** — SSE connection, message state #### Deliverables -- Working chat panel that sends to Grok and executes tool calls -- Diff view showing file changes -- `cargo test --package agent-bridge` passing -- Chat panel unit tests passing +- Agent orchestrator routing requests to playbooks +- Grok API client with tool-calling +- Chat panel streaming agent responses +- All unit tests passing --- -### Sprint 5: Permission engine + WP-CLI +### Sprint 4: Content playbooks -**Goal:** WP-CLI commands categorized and gated by permission tiers. Secrets redacted from agent context. +**Goal:** User can change text, images, and page content on their site via chat. #### TDD sequence **Step 1 — Write the tests** -```rust -// src-tauri/src/permission-engine/tiers.rs -#[cfg(test)] -mod tests { - #[test] - fn test_tiers_ordered() { - assert!(Tier::ReadFs < Tier::EditFs); - assert!(Tier::EditFs < Tier::WpCliSafe); - assert!(Tier::WpCliSafe < Tier::WpCliDestructive); - } +```typescript +// api/__tests__/playbooks/content.test.ts +describe('ContentPlaybook', () => { + it('finds and replaces text on a specific page', async () => { + const playbook = new ContentPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.editText({ + page: 'homepage', + target: 'Welcome to our site', + replacement: 'Welcome to My Business', + }); + expect(result.success).toBe(true); + // Verify the text was changed in the sandbox DB + const pageContent = await playbook.getPageContent('homepage'); + expect(pageContent).toContain('Welcome to My Business'); + expect(pageContent).not.toContain('Welcome to our site'); + }); - #[test] - fn test_all_tiers_defined() { - assert_eq!(Tier::variants().len(), 7); - } -} + it('updates a heading tag', async () => { + const playbook = new ContentPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.editHeading({ + page: 'homepage', + headingIndex: 0, + newText: 'New Heading', + }); + expect(result.success).toBe(true); + }); -// src-tauri/src/permission-engine/evaluator.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_allows_call_within_tier() { - let policy = Policy::default(); - assert!(policy.evaluate("fs.read", Tier::ReadFs).await.is_ok()); - } + it('replaces an image', async () => { + const playbook = new ContentPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.replaceImage({ + page: 'about', + imageSelector: '.hero-image', + imageUrl: 'https://example.com/new-image.jpg', + }); + expect(result.success).toBe(true); + }); - #[tokio::test] - async fn test_blocks_call_above_tier() { - let policy = Policy::default().with_tier(Tier::ReadFs); - assert!(policy.evaluate("wpcli.run", Tier::WpCliSafe).await.is_err()); - } + it('adds a new section to a page', async () => { + const playbook = new ContentPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.addSection({ + page: 'homepage', + sectionType: 'cta', + content: 'Call us today!', + position: 'after-hero', + }); + expect(result.success).toBe(true); + }); - #[tokio::test] - async fn test_requires_confirm_for_destructive() { - let policy = Policy::default().with_tier(Tier::WpCliDestructive); - let result = policy.evaluate("wpcli.run", Tier::WpCliDestructive).await.unwrap(); - assert!(result.confirmation_required); - } - - #[tokio::test] - async fn test_blocks_production_writes() { - let policy = Policy::default().with_environment(Environment::Production); - assert!(policy.evaluate("fs.write", Tier::EditFs).await.is_err()); - } -} - -// src-tauri/src/permission-engine/redactor.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_redacts_env_values() { - let text = "DB_PASSWORD=secret123"; - let secrets = vec!["secret123".to_string()]; - assert!(!redact(text, &secrets).contains("secret123")); - } - - #[tokio::test] - async fn test_redacts_wp_config_constants() { - let text = "define('DB_PASSWORD', 'secret123');"; - let result = redact(text, &[]).await; - assert!(result.contains("[REDACTED]")); - } - - #[tokio::test] - async fn test_preserves_variable_names() { - let text = "DB_PASSWORD=secret123"; - let result = redact(text, &["secret123".to_string()]); - assert!(result.contains("DB_PASSWORD")); - } -} + it('reverts changes on failure', async () => { + // If the change fails, the sandbox should be reset to the mirror state + }); +}); ``` **Step 2 — Implement** -- **`src-tauri/src/permission-engine/tiers.rs`** — Ordered enum, 7 tiers -- **`src-tauri/src/permission-engine/evaluator.rs`** — Policy evaluator: current tier, environment, confirmation flag -- **`src-tauri/src/permission-engine/redactor.rs`** — Scan text for secrets, redact before sending to agent -- **`src-tauri/src/permission-engine/config.rs`** — User-defined policy from `.wursor/policy.json` -- Wire into **`src-tauri/src/tool-bus/tools/wpcli.rs`** — check permission before executing +- **`api/src/playbooks/content.ts`** — Content playbook with tool calls + - `editText`: search DB for content → wp-cli `wp post update` or direct DB update + - `editHeading`: find heading in page HTML → update via WP-CLI or file edit + - `replaceImage`: upload new image → replace in content → verify + - `addSection`: create new content block → add to page → verify +- Each method uses the plugin client to execute WP-CLI commands or file operations in the sandbox +- After each change, the playbook triggers a preview refresh #### Deliverables -- Rust permission engine with all 7 tiers -- Secret redaction for `.env` and `wp-config.php` -- WP-CLI tool categorized and gated -- `cargo test --package permission-engine` passing +- Content playbook: edit text, edit headings, replace images, add sections +- All content playbook tests passing --- -### Sprint 6: P0 playbooks + first-run +### Sprint 5: Design playbooks -**Goal:** Four P0 playbooks executable from the chat panel. First-run experience guides project open. +**Goal:** User can change the theme, layout, colors, and fonts of their site via chat. #### TDD sequence **Step 1 — Write the tests** -```rust -// src-tauri/src/playbooks/dynamic_block.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_detects_build_setup() { - let playbook = DynamicBlockPlaybook::new(); - let config = playbook.detect("fixtures/sample-theme").await.unwrap(); - assert_eq!(config.build_tool, "@wordpress/scripts"); +```typescript +// api/__tests__/playbooks/design.test.ts +describe('DesignPlaybook', () => { + it('changes the active theme', async () => { + const playbook = new DesignPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.changeTheme('twentytwentyfour'); + expect(result.success).toBe(true); + // Verify the theme was activated + const activeTheme = await playbook.getActiveTheme(); + expect(activeTheme).toBe('twentytwentyfour'); + }); + + it('changes the site layout (single column → two columns)', async () => { + const playbook = new DesignPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.changeLayout('two-column'); + expect(result.success).toBe(true); + }); + + it('updates theme colors', async () => { + const playbook = new DesignPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.updateColors({ + primary: '#ff0000', + secondary: '#00ff00', + }); + expect(result.success).toBe(true); + }); + + it('updates typography', async () => { + const playbook = new DesignPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.updateTypography({ + headingFont: 'Inter', + bodyFont: 'Open Sans', + }); + expect(result.success).toBe(true); + }); + + it('fixes a mobile layout issue', async () => { + const playbook = new DesignPlaybook({ sandboxId: 'sb-123' }); + const result = await playbook.fixMobileLayout({ page: 'homepage' }); + expect(result.success).toBe(true); + }); +}); +``` + +**Step 2 — Implement** + +- **`api/src/playbooks/design.ts`** — Design playbook + - `changeTheme`: install theme via WP-CLI → activate → verify + - `changeLayout`: modify theme templates or page builder content → verify + - `updateColors`: update theme.json → regenerate CSS → verify + - `updateTypography`: update theme.json → verify + - `fixMobileLayout`: identify responsive CSS issues → fix → verify + +#### Deliverables + +- Design playbook: change theme, layout, colors, fonts, mobile fix +- All design playbook tests passing + +--- + +### Sprint 6: Deploy + rollback + +**Goal:** User approves the change, Wursor deploys to the live site, and can roll back. + +#### TDD sequence + +**Step 1 — Write the tests** + +```typescript +// api/__tests__/deploy/diff-engine.test.ts +describe('DiffEngine', () => { + it('computes file changes between sandbox and mirror', async () => { + const engine = new DiffEngine({ sandboxId: 'sb-123', mirrorId: 'mirror-123' }); + const diff = await engine.computeFileDiff(); + expect(diff.changedFiles).toContain('/wp-content/themes/twentytwentyfour/style.css'); + expect(diff.newFiles).toHaveLength(0); + }); + + it('computes database changes', async () => { + const engine = new DiffEngine({ sandboxId: 'sb-123', mirrorId: 'mirror-123' }); + const diff = await engine.computeDbDiff(); + expect(diff.changedTables).toContain('wp_options'); + expect(diff.changedRows).toBeGreaterThan(0); + }); + + it('computes plugin changes', async () => { + const engine = new DiffEngine({ sandboxId: 'sb-123', mirrorId: 'mirror-123' }); + const diff = await engine.computePluginDiff(); + expect(diff.installed).toContain('contact-form-7'); + }); +}); + +// api/__tests__/deploy/pusher.test.ts +describe('Pusher', () => { + it('pushes file changes to the live site', async () => { + const pusher = new Pusher({ siteUrl: 'https://example.com', token: 'valid-token' }); + const result = await pusher.pushFiles([ + { path: '/wp-content/themes/twentytwentyfour/style.css', content: '...' }, + ]); + expect(result.success).toBe(true); + }); + + it('pushes database changes', async () => { + const pusher = new Pusher({ siteUrl: 'https://example.com', token: 'valid-token' }); + const result = await pusher.pushDb([ + { table: 'wp_options', operation: 'UPDATE', where: { option_name: 'blogname' }, data: { option_value: 'My Site' } }, + ]); + expect(result.success).toBe(true); + }); + + it('pushes plugin installs', async () => { + const pusher = new Pusher({ siteUrl: 'https://example.com', token: 'valid-token' }); + const result = await pusher.pushPluginInstall('contact-form-7'); + expect(result.success).toBe(true); + }); + + it('handles partial failures', async () => { + // If some files fail but others succeed, what happens? Roll back the batch. + }); +}); + +// api/__tests__/deploy/verifier.test.ts +describe('Verifier', () => { + it('checks the home page returns 200', async () => { + const verifier = new Verifier({ siteUrl: 'https://example.com' }); + const result = await verifier.checkHomePage(); + expect(result.status).toBe(200); + }); + + it('checks for PHP errors', async () => { + const verifier = new Verifier({ siteUrl: 'https://example.com' }); + const result = await verifier.checkPhpErrors(); + expect(result.hasErrors).toBe(false); + }); + + it('checks the admin dashboard loads', async () => { + const verifier = new Verifier({ siteUrl: 'https://example.com' }); + const result = await verifier.checkAdmin(); + expect(result.status).toBe(200); + }); +}); + +// api/__tests__/deploy/rollback.test.ts +describe('Rollback', () => { + it('restores files from the snapshot', async () => { + const rollback = new Rollback({ siteUrl: 'https://example.com', token: 'valid-token' }); + const result = await rollback.restoreFiles('deploy-123'); + expect(result.success).toBe(true); + }); + + it('restores the database from the snapshot', async () => { + const rollback = new Rollback({ siteUrl: 'https://example.com', token: 'valid-token' }); + const result = await rollback.restoreDb('deploy-123'); + expect(result.success).toBe(true); + }); +}); + +// plugin/__tests__/test-deploy.php +class WursorDeployTest extends WP_UnitTestCase { + public function test_receives_file_change() { + $deploy = new Wursor_Deploy(); + $result = $deploy->apply_file_change('/wp-content/themes/twentytwentyfour/style.css', 'body { color: red; }'); + $this->assertTrue($result); + $this->assertEquals('body { color: red; }', file_get_contents(WP_CONTENT_DIR . '/themes/twentytwentyfour/style.css')); } - #[tokio::test] - async fn test_scaffolds_block_with_metadata() { - let playbook = DynamicBlockPlaybook::new(); - let result = playbook.scaffold("testimonial").await.unwrap(); - assert!(result.files.contains("block.json")); - assert!(result.files.contains("render.php")); + public function test_receives_db_change() { + $deploy = new Wursor_Deploy(); + $result = $deploy->apply_db_change('UPDATE wp_options SET option_value = "New Title" WHERE option_name = "blogname"'); + $this->assertTrue($result); + $this->assertEquals('New Title', get_option('blogname')); } - #[tokio::test] - async fn test_verifies_block_in_editor() { - let playbook = DynamicBlockPlaybook::new(); - assert!(playbook.verify("testimonial").await.unwrap()); - } -} - -// src-tauri/src/playbooks/child_theme.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_creates_style_css_with_template_header() { - let playbook = ChildThemePlaybook::new(); - let result = playbook.scaffold("twentytwentyfour", "my-child").await.unwrap(); - assert!(result.style_css.contains("Template: twentytwentyfour")); - } -} - -// src-tauri/src/playbooks/cpt.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_registers_cpt_with_rest_support() { - let playbook = CptPlaybook::new(); - let result = playbook.register("book", "books").await.unwrap(); - assert!(result.php.contains("show_in_rest")); + public function test_creates_snapshot_for_rollback() { + $deploy = new Wursor_Deploy(); + $snapshot = $deploy->create_snapshot(); + $this->assertArrayHasKey('files', $snapshot); + $this->assertArrayHasKey('db', $snapshot); } - #[tokio::test] - async fn test_flushes_rewrite_rules() { - let playbook = CptPlaybook::new(); - assert!(playbook.flush_rewrites().await.is_ok()); - } - - #[tokio::test] - async fn test_verifies_rest_endpoint() { - let playbook = CptPlaybook::new(); - let result = playbook.verify_rest("book").await.unwrap(); - assert!(result.status_ok); - } -} - -// src-tauri/src/playbooks/plugin.rs -#[cfg(test)] -mod tests { - #[tokio::test] - async fn test_creates_plugin_headers() { - let playbook = PluginPlaybook::new(); - let result = playbook.scaffold("my-plugin", "My Plugin").await.unwrap(); - assert!(result.main_php.contains("Plugin Name: My Plugin")); + public function test_restores_from_snapshot() { + $deploy = new Wursor_Deploy(); + $snapshot = $deploy->create_snapshot(); + // Make a change + update_option('blogname', 'Changed Title'); + // Restore + $deploy->restore_snapshot($snapshot); + $this->assertEquals('Original Title', get_option('blogname')); } } ``` ```typescript -// webview/__tests__/FirstRun.test.tsx -describe('FirstRun', () => { - it('shows three project paths on first launch', () => { /* ... */ }); - it('detects Docker and wp-env, guides install if missing', () => { /* ... */ }); - it('opens a project and shows the workspace within 10 minutes', () => { /* ... */ }); +// web/__tests__/ApproveBar.test.tsx +describe('ApproveBar', () => { + it('shows "Looks good → Apply" and "Not right → Reject" buttons', () => { /* ... */ }); + it('shows a confirmation dialog before apply', () => { /* ... */ }); + it('shows success state after deploy', () => { /* ... */ }); + it('shows the deploy history timeline', () => { /* ... */ }); + it('allows one-click undo on a deploy', () => { /* ... */ }); }); ``` **Step 2 — Implement** -- **`src-tauri/src/playbooks/registry.rs`** — Playbook registration -- **`src-tauri/src/playbooks/dynamic_block.rs`** — detect → scaffold → register → build → verify → diff -- **`src-tauri/src/playbooks/child_theme.rs`** — scaffold → enqueue → override → screenshot -- **`src-tauri/src/playbooks/cpt.rs`** — register → flush → seed → REST check → diff -- **`src-tauri/src/playbooks/plugin.rs`** — headers → optional Composer/PHPUnit -- **`webview/src/components/FirstRun.tsx`** — Onboarding wizard (3 paths: WP repo, folder, sample) -- **`src-tauri/src/commands/project.rs`** — Dependency check command +- **`plugin/src/class-deploy.php`** — Deploy receiver (file write, DB write, WP-CLI exec, snapshot) +- **`plugin/src/class-rollback.php`** — Snapshot-based rollback (files + DB) +- **`api/src/deploy/diff-engine.ts`** — Compare sandbox → live site +- **`api/src/deploy/pusher.ts`** — Push changes via plugin API +- **`api/src/deploy/verifier.ts`** — Verify live site after deploy +- **`api/src/deploy/rollback.ts`** — Snapshot-based rollback +- **`api/src/routes/deploy.ts`** — Approve, deploy, rollback endpoints +- **`web/src/components/ApproveBar.tsx`** — Approve/reject buttons, confirmation dialog +- **`web/src/components/DeployTimeline.tsx`** — Deploy history with one-click undo +- **`web/src/hooks/useDeploy.ts`** — Deploy state, polling #### Deliverables -- 4 P0 playbooks with Rust unit tests -- First-run dialog (3 paths) -- `cargo test --package playbooks` passing +- Deploy + rollback for files, DB, and plugins +- Approve/reject UI with confirmation dialog +- Deploy history timeline with one-click undo +- All unit tests passing --- ### Sprint 7: Integration + exit criteria -**Goal:** All Phase 1 pieces work together. Exit criteria tests pass end-to-end. +**Goal:** All Phase 1 pieces work together. Exit criteria test passes end-to-end. -#### Integration tests +#### Integration test ```typescript -// e2e/tauri/phase1-exit-criteria.test.ts +// e2e/phase1-exit-criteria.test.ts import { test, expect } from '@playwright/test'; -test('clean machine → live preview in ≤10 min', async () => { - const app = await tauri.launch({ args: [] }); - const window = await app.webview(); +test('new user connects site, makes a content change, previews, approves in ≤5 min', async ({ page }) => { const startTime = Date.now(); - // Follow first-run dialog → install Docker → install wp-env → open project → boot - await window.locator('.wursor-first-run-open-repo').click(); - await window.locator('.wursor-project-picker').fill('/path/to/fixtures/sample-theme'); - await window.locator('.wursor-confirm-open').click(); + // 1. Sign up + await page.goto('http://localhost:3000'); + await page.locator('.wursor-signup-button').click(); + await page.locator('input[name=email]').fill('test@example.com'); + await page.locator('input[name=password]').fill('password123'); + await page.locator('.wursor-submit').click(); + + // 2. Connect site (simulated plugin) + await expect(page.locator('.wursor-connect-site')).toBeVisible(); + await page.locator('.wursor-pairing-code-input').fill('ABC123'); + await page.locator('.wursor-connect-button').click(); + await expect(page.locator('.wursor-connected')).toBeVisible({ timeout: 10000 }); + + // 3. Make a change + await page.locator('.wursor-chat-input').fill('Change the homepage heading to "Welcome to My Business"'); + await page.locator('.wursor-chat-send').click(); + + // 4. See preview + await expect(page.locator('.wursor-preview-frame')).toBeVisible({ timeout: 60000 }); + + // 5. Approve + await page.locator('.wursor-approve-button').click(); + await page.locator('.wursor-confirm-apply').click(); + await expect(page.locator('.wursor-deploy-success')).toBeVisible({ timeout: 30000 }); - // Wait for runtime to boot - await expect(window.locator('.wursor-status-indicator')).toHaveText('running', { timeout: 600000 }); const elapsed = Date.now() - startTime; - expect(elapsed).toBeLessThan(10 * 60 * 1000); - await app.close(); + expect(elapsed).toBeLessThan(5 * 60 * 1000); }); - -test('P0 playbook completes with verified preview + accepted diff', async () => { - const app = await tauri.launch({ args: ['/path/to/fixtures/sample-theme'] }); - const window = await app.webview(); - - // Wait for runtime - await expect(window.locator('.wursor-status-indicator')).toHaveText('running', { timeout: 60000 }); - - // Run playbook - await window.locator('.wursor-chat-input').fill('Create a dynamic block named "testimonial"'); - await window.locator('.wursor-chat-send').click(); - - // Wait for diff - await expect(window.locator('.wursor-diff-view')).toBeVisible({ timeout: 120000 }); - - // Wait for verify - await expect(window.locator('.wursor-verify-result')).toBeVisible({ timeout: 60000 }); - - // Accept - await window.locator('.wursor-accept-diff').click(); - await expect(window.locator('.wursor-accepted-badge')).toBeVisible(); - await app.close(); -}); -``` - -```rust -// src-tauri/tests/tool_bus_test.rs -#[cfg(test)] -mod integration_tests { - #[tokio::test] - async fn test_tool_bus_integration() { - let registry = build_registry(); - let tool = registry.get("fs.read").unwrap(); - let result = (tool.handler)(serde_json::json!({"path": "fixtures/sample.txt"})).await; - assert!(result.is_ok()); - } -} ``` #### Deliverables -- Both exit criteria tests passing -- `cargo test` passing (all Rust unit + integration tests) -- `pnpm test:webview` passing (all webview tests) +- Exit criteria test passing +- All unit tests passing (`pnpm test`) +- All integration tests passing (`pnpm test:integration`) --- ### Sprint 8: Polish + alpha readiness -**Goal:** Error states from §8.5 handled, app packaging works, build ready for internal alpha. +**Goal:** Error states handled, app ready for internal alpha. #### Tasks -- **Error states** — Wire each state from PRD §8.5 into the webview UI -- **App packaging** — `tauri build` produces signed `.dmg` (macOS) and `.msi` (Windows) -- **Auto-update** — Tauri updater with GitHub releases -- **Telemetry** — Minimal events (preview load time, playbook run, verify result) with consent dialog +- **Error states** — Wire each state from §8.5 into the UI +- **Mobile responsive** — Chat collapses to full-screen on mobile, preview opens in new tab +- **Email auth** — Magic link or password reset flow +- **Plugin auto-update** — Plugin checks for updates from Wursor +- **Telemetry** — Minimal events (sign-up, connect, task start, task approve, task reject, deploy) with consent dialog - **Documentation** — `README.md` with install instructions and quickstart -- **Bug bash** — Internal team runs through first-run + playbook flow +- **Bug bash** — Internal team runs through the full flow #### Deliverables -- Signed app bundles for macOS + Windows -- Auto-update mechanism -- Error states all wired in the webview +- Web app deployed to staging +- Plugin packaged for WordPress plugin repo +- Error states all wired - Minimal telemetry with consent - `README.md` updated for alpha users @@ -918,37 +1003,28 @@ mod integration_tests { ## 5. Phase 2 — Intelligence (Weeks 9–16) -*High-level outline — full breakdown follows Phase 1 exit.* - -| Sprint | Focus | Crates | -|--------|-------|--------| -| 9 | Knowledge graph static scanner | `src-tauri/src/knowledge-index/scanner.rs` | -| 10 | Knowledge graph runtime enricher | `src-tauri/src/knowledge-index/enricher.rs` | -| 11 | Knowledge graph queries + UI | `src-tauri/src/knowledge-index/queries.rs`, tree view in webview | -| 12 | State Diff lifecycle | `src-tauri/src/state-diff/lifecycle.rs` | -| 13 | State Diff UI + rollback | `src-tauri/src/state-diff/rollback.rs`, StateDiffPanel | -| 14 | Quality gates (PHPCS, PHPUnit) | `src-tauri/src/tool-bus/tools/lint.rs`, `test_runner.rs` | -| 15 | Staging pull connector | `src-tauri/src/tool-bus/tools/staging.rs` | -| 16 | Closed alpha ship + baseline collection | Telemetry review, §11 baselines | +| Sprint | Focus | Files | +|--------|-------|-------| +| 9 | Plugin playbook (install, configure, fix conflicts) | `api/src/playbooks/plugin.ts` | +| 10 | Site build playbook (from scratch, limited) | `api/src/playbooks/site-build.ts` | +| 11 | Mobile-responsive preview | `web/src/components/Preview.tsx` | +| 12 | Agent clarifying questions | `api/src/services/agent-orchestrator.ts` | +| 13 | Visual design picker (theme gallery) | `api/src/playbooks/design.ts`, `web/src/components/DesignPicker.tsx` | +| 14 | Multi-step workflows (queue changes) | `api/src/services/playbook-runner.ts` | +| 15 | Closed alpha with 10–20 users | Telemetry review, baselines | +| 16 | Alpha feedback → Phase 2 exit review | All §11 baselines collected | --- ## 6. TDD Rules -These rules apply to every sprint: - 1. **Write the test first.** No implementation code is written without a failing test. 2. **One assertion per test.** Each test verifies exactly one behavior. -3. **Tests are deterministic.** No network calls in unit tests (mock Grok API, mock wp-env). -4. **Integration tests use fixtures.** Sample WP repos live in `src-tauri/fixtures/` and `e2e/fixtures/`. +3. **Tests are deterministic.** No network calls in unit tests (mock Grok API, mock plugin, mock Docker). +4. **Integration tests use real sandboxes in CI.** Pre-baked WordPress image in Docker on GitHub Actions. 5. **Red → Green → Refactor.** Write the failing test (red), make it pass (green), then clean up (refactor). -6. **Coverage floor.** Rust: `cargo-tarpaulin` enforces ≥ 90% line coverage. Webview: vitest enforces ≥ 90%. -7. **No skipped tests in main.** `#[ignore]` and `test.only` only in feature branches. - -### Test naming convention - -Rust: `{module}_{behavior}` (e.g. `fs_read_rejects_path_traversal`) -TypeScript: `{module}.{behavior}.test.ts` (e.g. `fs.read-workspace-file.test.ts`) +6. **Coverage floor.** TypeScript: vitest enforces ≥ 90%. PHP: phpunit with coverage ≥ 80%. +7. **No skipped tests in main.** `test.skip` and `test.only` only in feature branches. --- @@ -959,29 +1035,36 @@ TypeScript: `{module}.{behavior}.test.ts` (e.g. `fs.read-workspace-file.test.ts` name: CI on: [pull_request] jobs: - rust: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - run: cargo test --all-features - - run: cargo tarpaulin --out Xml --skip-clean - # Enforces 90% coverage floor - - webview: + api: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - run: pnpm install - - run: pnpm test -- --coverage # Enforces 90% coverage + - run: pnpm test:api + - run: pnpm test:api:coverage + + web: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + - run: pnpm install + - run: pnpm test:web + - run: pnpm test:web:coverage + + plugin: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: composer install + - run: ./vendor/bin/phpunit plugin/__tests__/ + - run: ./vendor/bin/phpunit --coverage-text lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - run: cargo clippy --all-targets -- -D warnings - uses: pnpm/action-setup@v2 - run: pnpm install && pnpm lint @@ -992,16 +1075,16 @@ on: branches: [release/*] jobs: e2e: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, windows-latest] + runs-on: ubuntu-latest + services: + docker: + image: docker:20.10 + options: --privileged steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - uses: pnpm/action-setup@v2 - run: pnpm install - - run: cargo build --release + - run: pnpm build - run: pnpm test:e2e ``` @@ -1011,14 +1094,14 @@ jobs: | Term | Definition | |------|------------| -| **Tool bus** | Rust registry and executor for all agent-callable tools (fs, wpcli, site, db, etc.) | -| **Runtime adapter** | Rust trait that abstracts wp-env (v1) behind a common interface for future backends | -| **Playbook** | A reusable, multi-step agent workflow (scaffold block, create CPT, etc.) | -| **State Diff** | A reviewable mutation plan for WP content/state (CLI commands, SQL, or migration) | -| **Permission tier** | A capability level (read FS → edit FS → WP-CLI safe → destructive → etc.) | -| **Verify** | Required proof step: screenshot, HTTP check, or editor route confirmation | -| **Knowledge graph** | Rust-indexed map of themes, plugins, blocks, hooks, and REST routes | +| **Sandbox** | An ephemeral, isolated copy of the user's WordPress site running in Wursor's cloud | +| **Playbook** | A structured, multi-step agent workflow for a specific task type | +| **Plugin connector** | The WordPress plugin that connects the user's site to Wursor | +| **Mirror** | The process of copying a site's theme, plugins, content, and settings into a sandbox | +| **Deploy** | The process of applying sandbox changes to the live site | +| **Warm pool** | Pre-booted WordPress containers ready to accept a mirror | +| **SSE** | Server-Sent Events — the protocol used to stream agent responses to the frontend | --- -*End of Implementation Guide v1.1 — Wursor* \ No newline at end of file +*End of Implementation Guide v2.0 — Wursor* \ No newline at end of file diff --git a/PRD.md b/PRD.md index c345f4e..48b923f 100644 --- a/PRD.md +++ b/PRD.md @@ -2,188 +2,156 @@ **Wursor** -The Agentic WordPress Development Environment +The Agentic WordPress Management Platform -*Where WordPress products get built — code, site, and shipping in one loop.* +*Just describe what you want. Wursor does the rest.* | Field | Value | | :--- | :--- | -| **Version** | 1.3 | +| **Version** | 2.0 | | **Date** | August 13, 2026 | | **Author** | Patrick (Product Lead) | -| **Status** | Draft — Internal (key decisions locked; Phase 0) | -| **Repo** | SinachPat/wursor (renamed from originmain) | +| **Status** | Draft — Internal (non-technical-first pivot; Phase 0) | +| **Repo** | SinachPat/wursor | | **Classification** | Confidential | -| **Supersedes** | v1.2 (shell → Tauri/Monaco; model → Grok) | +| **Supersedes** | v1.3 (engineer-first; desktop shell; Tauri + Monaco) | --- ## 1. Executive Summary -Wursor is a development environment built for people who ship on WordPress. It combines an AI agent that can plan and edit real project code with a live WordPress runtime, WP-CLI, database awareness, and preview — so building a theme, plugin, or block is not split across five apps and a hope that the model "knows WordPress." +Wursor is a web-based platform that lets anyone manage their WordPress site by simply describing what they want. No code, no wp-admin, no hosting jargon. The user types "make my homepage look more modern" or "add a booking form" or "change the site to a two-column layout" — and Wursor does it. -WordPress work is not generic app development. The product surface is a CMS platform with themes, plugins, hooks, a block editor, content in MySQL, and a long tail of agency and product workflows. Today's stack forces builders to keep that reality in their head while jumping between an editor, a local site tool, wp-admin, a terminal for WP-CLI, and a database client. +Under the hood, Wursor spins up a secure cloud sandbox (an isolated copy of the user's site), instructs an AI agent to make the changes, and shows the user a live preview. If the user likes it, Wursor deploys the changes to the real site. If they don't, it resets. -Wursor makes that reality the environment: +The product is a web app. The user installs one WordPress plugin to connect their site. Everything else happens in the browser. No Docker, no terminal, no filesystem access. -- A **site you can boot, browse, reset, and inspect** sits beside the code. -- The agent is taught **WordPress semantics** — template hierarchy, hooks, `block.json`, capabilities, text domains — not only PHP syntax. -- Changes show up as **reviewable code diffs** and, when content or options must move, as explicit **State Diffs** (WP-CLI / migration scripts), never silent database edits. -- **Blocks and block themes** are first-class: `theme.json`, patterns, template parts, and editor preview. -- **Environments matter**: local → staging → production, with write access gated by policy. - -**The opportunity:** become the default professional workspace for WordPress product and agency teams in an agent-assisted era — without pretending WordPress is "just another repo." +**The opportunity:** become the default way non-technical WordPress site owners make changes to their sites — replacing the agency phone call, the frustrating wp-admin search, and the fear of breaking something. --- ## 2. Problem Statement -### 2.1 WordPress is a platform, not a folder of PHP +### 2.1 WordPress is powerful, but it still requires technical skill -Competent general coding agents still miss what breaks real WP projects: +Millions of businesses run on WordPress. The owners are real estate agents, restaurant owners, consultants, dentists, e-commerce operators. They are not developers. They are not designers. They are people who need a website that works. -- The split between **code** (themes/plugins) and **content/state** (posts, options, post meta, transients). -- **Load order** and hook timing (`plugins_loaded` vs `init` vs `wp_enqueue_scripts`). -- **Child themes**, template hierarchy, and the dual world of classic vs block themes. -- **WP-CLI** as the practical automation layer. -- **Multisite**, capabilities, nonces, and auth patterns. -- **Block development** (`block.json`, `render.php`, editor scripts, `@wordpress/scripts`). +When they need to change something — update the layout, add a feature, fix a broken page — they have three options today: -The failure mode is confident patches that enqueue wrong, ignore APIs WordPress already provides, or "fix" a theme without ever loading the site. +1. **Learn wp-admin** — navigate a 20-year-old admin interface designed for content publishers, not business owners +2. **Call an agency** — wait days, pay hundreds, and hope the result matches what they described +3. **Use a hosted builder** — leave WordPress entirely for Wix/Squarespace, losing their SEO, content, and investment -### 2.2 The toolchain is fragmented +Each option is painful. None of them respects the user's time or expertise. -| Concern | Typical tool today | -| :--- | :--- | -| Edit code | General IDE / editor | -| Run site | Local WP, DDEV, Lando, wp-env, Docker | -| Admin / content | wp-admin in a browser | -| Automate | WP-CLI in a separate terminal | -| Database | phpMyAdmin / TablePlus | -| Assist | Chat tools with no live site context | -| Deploy | FTP, rsync, Git + host pipelines, site managers | +### 2.2 The toolchain is designed for engineers -Every hop drops context. Nothing in that chain can scaffold a block, flush rewrites, open the editor, and prove the front end in one continuous run. +The WordPress ecosystem tools — Local WP, wp-env, WP-CLI, Git, staging sites — are all built for developers. A business owner doesn't know what a staging site is. They don't want to know. They want to see their change and click "accept." -### 2.3 Agencies and product teams buy turnaround +### 2.3 The gap is trust, not technology -WordPress shops compete on speed and reliability. Friction is environment spin-up, safe changes across code and data, regression checks on real themes, and handoff between design, content, and engineering. Host "AI" features aimed at writing posts do not solve that. +The technology to have an AI agent edit a WordPress site exists today. The gap is safety: the user needs to trust that the agent won't break their site. That's why the core product is the sandbox + preview + approve loop — not the agent itself. The agent is invisible. The sandbox is the safety guarantee. -### 2.4 Blast radius is real +### 2.4 Fragmentation is the user's problem, not the tool's -WordPress sites are high-value targets. An agent that can edit `wp-config.php`, install arbitrary zips, or run unchecked SQL is a liability. **Safe-by-default permissions** are a core product requirement. +A business owner might use: +- A page builder (Elementor, Beaver Builder) +- Several plugins (WooCommerce, SEO, forms, booking) +- A custom theme +- Third-party services (Mailchimp, Stripe, Google Analytics) + +Today, changing any of these requires learning each tool's interface. Wursor abstracts all of them behind a single chat interface. The agent knows how to use them. --- ## 3. Vision & Opportunity -**Vision:** Open a WordPress project in Wursor and you get a workspace that already understands the shape of the project, can start the site, and can take a job like "add a pricing block that matches our patterns and verify it on /pricing" through edit → CLI → preview → review in one place. +**Vision:** Open Wursor, type what you want your site to do, preview it, approve it. That's it. WordPress becomes as easy as describing it. -Wursor sits at the intersection of: +Wursor is not a page builder. It's not a hosting platform. It's not a content editor. It's the **agentic layer** that sits on top of any WordPress site and lets you control it with natural language. -| Category | What exists | What Wursor adds | -| :--- | :--- | :--- | -| AI-assisted coding | General editors and agents | WP-native tools, playbooks, and site loop | -| Local WP environments | Local, DDEV, wp-env | Runtime embedded and controllable by the agent | -| In-admin AI helpers | Host and plugin copilots | Real engineering workspace (Git, diffs, tests), not post drafting | -| Block / theme tooling | `@wordpress/scripts`, theme.json editors | Unified with agent + live preview | +**The opportunity:** the entire WordPress ecosystem (43% of the web) has no native agentic interface. The closest alternatives are: +- AI assistants inside page builders (lock you into their builder) +- General AI coding tools (require technical skill) +- Agency retainer relationships (expensive, slow) -**Positioning:** Wursor is the agentic **WordPress workshop** — not a generic coding assistant with a WordPress sticker, and not an AI writing widget inside wp-admin. +Wursor is the first product that gives non-technical site owners a direct, safe, natural-language interface to their WordPress site — without requiring them to learn any tool. --- ## 4. Target Users & Personas -### 4.1 Primary — Agency WordPress Engineer +### 4.1 Primary — WordPress Site Owner (non-technical) -Ships custom themes/plugins for clients on deadline. Wants faster scaffolding, safer refactors, fewer context switches. Uses Git; distrusts mystery FTP deploys. +Runs a business on WordPress. Has admin access but doesn't know how to use it beyond basic post editing. Hires an agency for anything non-trivial. Wants to make changes without a phone call. Examples: dentist, real estate agent, restaurant owner, e-commerce store operator. -### 4.2 Primary — Plugin / Block Product Developer +**Out of scope for v1:** content-only users who mainly need AI to draft posts. Wursor is for *doing* — changing the site, not just writing. -Ships commercial or open-source plugins and block libraries. Needs scaffolding, wp-env, WPCS, tests, and release hygiene. Cares about headers, text domains, and build pipelines. +### 4.2 Secondary — Agency Client (delegator) -### 4.3 Secondary — Technical Founder / Solo Builder +Has an agency but wants to make small changes themselves without waiting for a ticket. Wursor becomes the "self-serve" layer on top of the agency-managed site. -Runs a business on WordPress (WooCommerce, membership, LMS). Wants senior-WP leverage without a full bench. +### 4.3 Tertiary — Technical WordPress Developer (future) -### 4.4 Secondary — Design Engineer on Block Themes - -Lives in `theme.json`, patterns, and template parts. Needs structured edits plus visual proof. - -### 4.5 Tertiary — Technical PM / Solutions Architect - -Scopes builds, reviews proposed changes, cares about migration plans and staging checks. - -**Out of scope for v1:** content-only users who mainly need AI inside wp-admin to draft posts. +The same product, with advanced features unlocked later. For now, the product is designed for non-technical users. Engineers can use it too, but they're not the target. --- ## 5. Product Principles -1. **Site is a runtime, not a folder** — If it cannot boot, browse, and assert against WordPress, it is guessing. -2. **Code and state are both first-class** — File diffs and explicit State Diffs; no silent DB mutation. -3. **WordPress semantics over generic PHP** — Prefer platform APIs, hooks, and patterns a senior WP engineer would choose. -4. **Safe by default** — Capability-scoped tools; production gated; secrets redacted; destructive ops require confirmation. -5. **Preview is proof** — The agent cannot mark a task "done" without a verify step (screenshot, HTTP check, or editor verification). Users may dismiss the proof; the agent may not skip producing it. -6. **Git records code; scripts record state** — Migrations and WP-CLI plans are reviewable artifacts. -7. **Opinionated for WordPress** — Defaults follow WPCS, wp-env, and block-era workflows; escape hatches exist but are not the center. +1. **The user describes what they want; the agent does the rest.** No settings screens, no toggles, no configuration. +2. **The live preview is the only proof.** The user never sees a diff, a terminal, or an error log. They see their site with the change applied. +3. **Safe by default.** The agent never touches the live site until the user explicitly approves. Sandbox isolation is non-negotiable. +4. **Every change is reversible.** If the user doesn't like the result, they reject it. The live site is unchanged. If they approved and regret it, one-click rollback. +5. **Speed is the UX.** Boot the sandbox fast, show the preview fast, deploy fast. The user waits seconds, not minutes. +6. **The agent knows WordPress.** The user doesn't need to know what a theme, plugin, hook, or shortcode is. The agent does. +7. **Non-technical first.** Every feature is designed for the person who doesn't know what a file is. Technical features are added later, not instead. --- ## 6. Core Concepts & Mental Model -### 6.1 Workspace = Project + Site +### 6.1 The loop -A **Workspace** binds: +The user's entire interaction with Wursor is a single loop: -- A Git project (theme, plugin, plugin monorepo, `wp-content` checkout, or Composer/Bedrock layout) -- A **site runtime** (wp-env by default; Docker / Local / DDEV import paths) -- Environment config (local / staging / production endpoints and a credentials vault) +``` +Describe → Preview → Approve +``` -> **v1 scope (locked):** wp-env is the *only* supported runtime in v1. Local / DDEV / Bedrock import is P1 (§7.2.6). The runtime manager is still abstraction-bound (§8.1) so adding those backends later does not require a redesign. +That's it. There is nothing else. No dashboard, no settings, no configuration. The user lands on a chat interface, types what they want, sees a preview of their site with the change, and clicks approve or reject. -### 6.2 WordPress Knowledge Graph +### 6.2 The sandbox -Indexed understanding of: +Every task gets an ephemeral, isolated copy of the user's WordPress site — a **sandbox**. The sandbox is a full WordPress instance running in Wursor's cloud infrastructure, pre-loaded with the user's active theme, plugins, and content. -- Themes / child themes / active theme -- Plugins (active, mu-plugins, drop-ins) -- CPTs, taxonomies, REST routes -- Hook registrations (best-effort from code) -- Block inventory (`block.json`) -- `theme.json` tokens and style variations -- Template hierarchy for key routes +The agent works inside the sandbox. It can install plugins, edit files, change settings, modify the database — anything. The live site is never touched. -**Build source (locked):** two passes. (1) *Static* — scan of `*.php`, `block.json`, `theme.json`, and plugin/theme headers at project open, refreshed on file-save and on git checkout. (2) *Runtime* — when the site is up, enrich via WP-CLI (`wp plugin list`, `wp theme list`, `wp post-type list`, `wp rewrite list`) with the *actual* active theme, active plugins, registered CPTs/taxonomies, and REST routes. +When the user approves, the sandbox changes are deployed to the live site via the Wursor plugin. When the user rejects, the sandbox is destroyed. Nothing persists. -**Freshness model:** every graph node carries a source stamp (static vs runtime) and timestamp. Both the agent context and the UI surface staleness explicitly (e.g., "active theme — static scan, site not loaded"). Full re-index runs on project open and on every `site.browse` boot; incremental updates follow file-save events. Runtime nodes are re-verified each time the site boots. +### 6.3 The plugin -### 6.3 The build loop +A lightweight WordPress plugin that the user installs once. It does two things: +1. **Connects the site** — provides a secure API for Wursor to read site info (themes, plugins, content) and deploy changes +2. **Receives deploys** — applies the sandbox changes (file updates, database changes, plugin installs) to the live site -Plan → edit files → run WP-CLI / tests → refresh preview → read logs → revise. Every step uses WordPress-aware tools. +The plugin is the only thing the user ever installs. It requires no configuration beyond the initial connection token. -### 6.4 State Diffs +### 6.4 Playbooks -When a task needs content or options changes, Wursor proposes a **State Diff**: WP-CLI commands and/or a migration script — never an invisible database tweak. The lifecycle is explicit: +Every user request maps to a **playbook** — a structured, multi-step agent workflow. The user doesn't know about playbooks. They just see "I'll work on that now." But internally, each request is routed to a specific playbook: -1. **Create** — the agent generates a candidate diff (WP-CLI commands, SQL statements, or a PHP migration), each step annotated with intent and blast radius. -2. **Review** — shown in the State tab; every step expands to full text and effect; nothing runs without review. -3. **Stage** — approved steps form a numbered plan; steps can be reordered or dropped. -4. **Apply** — executes against the local environment by default; each step streams output and marks pass/fail. -5. **Verify** — the agent re-checks the site (option read-back, URL load, screenshot) before the diff counts as applied. -6. **Commit** — migration-style state scripts commit to the repo as `db/` migrations; pure WP-CLI plans persist as reviewable `.state-diff.json` artifacts under `.wursor/state-diffs/`. +- **Content change** — edit text, images, pages +- **Design change** — modify theme, layout, colors, fonts +- **Feature add** — install and configure a plugin (e.g., booking form, SEO, analytics) +- **Plugin install** — find, install, and activate a plugin from the WordPress repo +- **Site build** — create a new site from scratch (theme + pages + content) +- **Fix** — diagnose and repair a broken page, layout issue, or plugin conflict -**Rollback (locked):** destructive steps must declare an inverse at create time (e.g., `wp option delete` paired with the prior value) or an explicit "manual backup required" acknowledgment; Wursor refuses to stage a destructive step without one. +### 6.5 Environments -### 6.5 Rules & Playbooks - -Project guidance lives in `WORDPRESS.md` / `.wursor/rules` (standards, banned patterns, deploy checklists). **Playbooks** are reusable workflows: scaffold a dynamic block, spin a child theme, register a CPT, harden a plugin release. - -### 6.6 Environments - -- **Local** — full control for the agent under user policy -- **Staging** — sync down and careful promote -- **Production** — read/observe by default; write only with explicit break-glass +- **Sandbox** — ephemeral, agent has full access, isolated from live site +- **Live site** — the user's real WordPress site, only touched by explicit deploy after approval --- @@ -193,93 +161,113 @@ Project guidance lives in `WORDPRESS.md` / `.wursor/rules` (standards, banned pa ### 7.1 P0 — Launch Blocking -#### 7.1.1 Agentic editing workspace -- Project-aware chat with file/symbol/doc context -- Inline rewrite of selections -- Multi-file agent runs with reviewable patches -- Integrated terminal -- Git status, diff review, commit assist -- Project rules (`WORDPRESS.md`, `.wursor/rules`) +#### 7.1.1 Chat interface +- Single text input, no buttons, no tabs +- User types what they want, in any language, any level of detail +- Agent responds conversationally: "I've changed your homepage to a two-column layout. Here's the preview." +- Agent can ask clarifying questions: "I see you have a contact form. Do you want me to keep it or replace it?" +- Follow-up turns refine the result: "Make the header blue instead" → new preview +- Mobile-responsive — the user can approve changes from their phone -#### 7.1.2 WordPress project intelligence -- Detect project shape: classic theme, block theme, single plugin, `wp-content` tree, Bedrock/Composer -- When the site is up: map active theme and plugins -- PHP + block JS support with WordPress stubs -- Template hierarchy and `block.json` awareness +#### 7.1.2 Live preview +- Embedded browser preview of the sandbox site +- Real-time — the preview updates as the agent works (streaming changes) +- The user can click around the preview to verify the change works on all pages +- Device toggle: desktop / tablet / mobile views +- The preview is the *only* verification — no diffs, no logs, no technical output -#### 7.1.3 Embedded local site runtime -- Start/stop/reset via **wp-env** — the only supported runtime in v1 (the emitted Docker compose file is for debugging, not an alternative surface) -- Embedded preview (front end + wp-admin) -- Log tail (PHP / web server; Query Monitor later) -- Runtime manager is abstraction-bound (§8.1); Local / DDEV import (P1) plugs in behind the same interface +#### 7.1.3 Approve / reject +- Big, clear buttons: "Looks good → Apply" and "Not right → Reject" +- Apply deploys the sandbox changes to the live site via the plugin +- Reject destroys the sandbox, live site unchanged +- Confirmation dialog before apply: "Apply changes to your live site?" +- One-click rollback: a history of deployed changes, with "Undo" for each -#### 7.1.4 WP-CLI as an agent tool -- Allowlisted WP-CLI runner -- Recipes: scaffold plugin/theme/block, rewrite flush, cache flush, activate plugins, local DB export/import -- Preview destructive commands before run +#### 7.1.4 WordPress plugin connector +- One-click install from wp-admin plugin directory +- Pairing flow: user copies a 6-character code from Wursor web app, pastes it into the plugin +- Plugin exposes: site info (theme, plugins, content), file system (read/write), database (read/write), WP-CLI (full access) +- All communication over HTTPS with token-based auth +- Plugin auto-updates; no user maintenance -#### 7.1.5 Permissions & safety -- Tiers: read FS, edit FS, WP-CLI safe, WP-CLI destructive, SQL read, SQL write, network install -- Production writes off by default -- Redact secrets from `.env` / `wp-config` in agent context; scan on apply +#### 7.1.5 Cloud sandbox orchestration +- Spin up a sandbox in ≤ 10 seconds (warm pool) +- Mirror the user's site: theme, plugins, content, media (lazy sync for media) +- Full network access (so the agent can install plugins from the WordPress repo) +- 15-minute idle timeout (auto-hibernate, resume on user interaction) +- 24-hour hard timeout (sandbox destroyed, no exceptions) +- Deploy: apply file changes, database changes, plugin installs/activations to the live site via the plugin -#### 7.1.6 Preview verification -- Verify runs by default on every agent task and is required before the agent marks a task "done" (Principle 5); users may dismiss the proof, the agent cannot skip producing it -- Verify step: load URLs, screenshot, HTTP status + basic error sniff (PHP error log, 500s) -- For block tasks: open editor routes and confirm the block can be inserted (lightweight P0) -- Failures surface explicitly — "verify failed: /pricing returned 500" with the log excerpt — never a silent retry +#### 7.1.6 Content playbooks +- **Edit text** — find and replace text on any page, update headings, rewrite paragraphs +- **Edit images** — replace, resize, reposition images +- **Edit pages** — add/remove sections, reorder content, change layouts +- **Import/export** — copy content from another page or site -#### 7.1.7 Scaffolding playbooks -- Plugin (headers, text domain, optional Composer/PHPUnit) -- Static / dynamic block (`@wordpress/scripts`) -- Child theme -- CPT + REST + minimal admin UI +#### 7.1.7 Design playbooks +- **Theme change** — switch to a new theme, migrate content +- **Layout change** — single column → two columns, sidebar position, full-width sections +- **Color/font change** — update theme colors, typography, spacing +- **Mobile fix** — fix a layout that breaks on mobile -#### 7.1.8 First-run experience -- Install: single signed app bundle (macOS + Windows; Linux best-effort); no Docker prompt before first project open -- First open: guided "open a project" with three paths — a WordPress repo (auto-detects wp-env config), a plain theme/plugin folder, or a built-in sample project -- Dependency check: Docker / wp-env detection with one-click install guidance and a diagnostic panel — a dead end is not an option -- First preview target: ≤ 10 minutes p50 from install to a live preview (§11) +#### 7.1.8 Plugin playbooks +- **Install plugin** — find a plugin in the WordPress repo, install, activate, configure +- **Configure plugin** — change settings for an existing plugin (e.g., "set up WooCommerce shipping") +- **Plugin conflict fix** — diagnose and resolve a plugin conflict + +#### 7.1.9 Site build playbook (P0 limited) +- **From scratch** — user describes their business type; Wursor selects a theme, installs it, creates pages, sets up plugins +- **From existing** — take the user's current site and apply a new design direction +- Limited to simple sites in v1 (5 pages, standard plugins). Complex builds are P1. + +#### 7.1.10 Safety & trust +- Every change is previewed before apply — no "apply now, preview later" +- Agent has a "no-surprise" rule: it must surface any action that costs money (e.g., a paid plugin) or affects SEO (e.g., URL changes) +- Agent role: "I changed your homepage layout. It also removed your sidebar widget. Is that OK?" +- Deploy history: a timeline of all changes, with one-click undo per change +- Undo reverts the last deploy (not individual file changes — the user sees "your site has been restored to before that change") ### 7.2 P1 — Follow-on -#### 7.2.1 Database & options introspection -- Read-only schema explorer and options search -- Explain an options row with link-back to plugin code when possible -- State Diff generation for options / post meta +#### 7.2.1 Multi-step workflows +- User can queue multiple changes: "Change the homepage layout, add a booking form, and update the footer" +- Agent works through them in order, previews the combined result +- User approves all at once or rejects individual changes -#### 7.2.2 Block & FSE workshop -- Structured `theme.json` editing + agent tools -- Pattern export/import as files -- Best-effort jump from preview selection → template part / block source +#### 7.2.2 Visual design picker +- Instead of describing a design, the user picks from a gallery of design templates +- "Show me my site with this theme" — the agent changes the theme, previews it +- The user can cycle through options without committing -#### 7.2.3 Quality gates -- PHPCS + WordPress Coding Standards -- PHPUnit / e2e hooks (Playwright or wp-env-based) -- Agent runs configured gates before marking work done +#### 7.2.3 SEO optimization +- "Make my site rank better" — agent analyzes the site, suggests changes, applies them with approval +- Meta descriptions, title tags, alt text, heading structure, schema markup -#### 7.2.4 Staging connectors -- Explicit, logged pull of DB/media from staging -- Deploy via GitHub Actions / host APIs -- Production DB pull requires double confirm + scrubbing warnings +#### 7.2.4 Performance optimization +- "Make my site faster" — agent analyzes performance, suggests fixes (image optimization, caching, plugin cleanup) +- Applies changes with approval -#### 7.2.5 Hook & REST introspection -- Live REST route list from the site -- Assist for `add_action` / `add_filter` discovery +#### 7.2.5 Content migration +- "Move my site from Wix to WordPress" — agent imports content, maps pages, sets up redirects +- Complex, but the agent does the heavy lifting -#### 7.2.6 Import paths -- Open Local WP / DDEV projects -- Zero-config open for existing wp-env repos +#### 7.2.6 Multi-site management +- User connects multiple WordPress sites to one Wursor account +- Switch between sites, apply changes across sites, bulk operations + +#### 7.2.7 Team / agency mode +- Multiple users can access the same site with different permission levels +- Agency team can manage client sites from a single Wursor account +- Client approves changes, agency makes them ### 7.3 P2 — Strategic -- WooCommerce-oriented skills -- Multisite tools -- Host integrations (WP Cloud, SpinupWP, Rocket.net, etc.) -- Team workspaces and shared playbooks for agencies -- Sandboxed maintenance agent (updates, conflict triage) -- Optional design intake (e.g. Figma → patterns) -- Headless / hybrid (Faust, Next) workspaces +- E-commerce operations (WooCommerce: product updates, inventory, pricing, shipping) +- Scheduled changes (e.g., "update the site for the holiday sale on December 1") +- Custom code agent (for users who want to add custom CSS/JS — still via chat, no code editor) +- A/B testing (agent creates two versions, measures performance, picks the winner) +- Monitoring agent (watches the site, suggests fixes proactively) +- Marketplace (playbooks built by third parties, shared with the community) --- @@ -287,74 +275,125 @@ Project guidance lives in `WORDPRESS.md` / `.wursor/rules` (standards, banned pa ### 8.1 Layers -> **Shell decision (locked):** Native desktop app built on **Tauri + Monaco Editor**. Tauri (Rust shell, system webview) gives fast startup (~0.3–0.8s), low memory (100–200MB), and native filesystem/Docker/process access. Monaco Editor is the same editor component that powers VS Code — editing, language services, and diff views without Electron's Chromium overhead. Rust owns the tool bus, knowledge graph parser, permission engine, and runtime manager; the webview renders the editor and Wursor panels (TypeScript/HTML). -> -> **Why not Electron + Code-OSS:** Electron ships an entire Chromium per app (500MB–1GB memory, 2–6s startup) — a constant quality-of-life cost for a daily-driver dev tool. Tauri uses the OS webview, which is already resident. -> -> **Why not Zed:** immature ecosystem, no Monaco/VS Code editor quality, and its agent story is not Claude/Grok-style tool-calling. The product is the WP+agent loop, not the editor. +``` +┌─────────────────────────────────────────────────────┐ +│ Web Frontend (Wursor Web App) │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Chat UI (React) │ │ +│ │ Preview iframe (sandbox URL) │ │ +│ │ Approve / Reject buttons │ │ +│ │ Deploy history timeline │ │ +│ └─────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────┤ +│ API Server (Backend) │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Session Manager │ │ +│ │ ├─ Create session (auth, site info, context) │ │ +│ │ └─ Resume session (reconnect to sandbox) │ │ +│ ├─────────────────────────────────────────────────┤ │ +│ │ Agent Orchestrator │ │ +│ │ ├─ Route user request to playbook │ │ +│ │ ├─ Build system prompt (site context, rules) │ │ +│ │ ├─ Dispatch tool calls to sandbox │ │ +│ │ └─ Stream results to frontend (SSE) │ │ +│ ├─────────────────────────────────────────────────┤ │ +│ │ Playbook Runner │ │ +│ │ ├─ Content playbook (text, images, pages) │ │ +│ │ ├─ Design playbook (theme, layout, colors) │ │ +│ │ ├─ Plugin playbook (install, configure) │ │ +│ │ └─ Site build playbook (scaffold, populate) │ │ +│ ├─────────────────────────────────────────────────┤ │ +│ │ Sandbox Manager │ │ +│ │ ├─ Spin up/down containers (Docker + k8s) │ │ +│ │ ├─ Warm pool (pre-booted base images) │ │ +│ │ ├─ Mirror user site (plugin, content, media) │ │ +│ │ └─ Garbage collection (idle, hard timeout) │ │ +│ ├─────────────────────────────────────────────────┤ │ +│ │ Deploy Manager │ │ +│ │ ├─ Compute diff from sandbox → live site │ │ +│ │ ├─ Push changes via plugin API (files, DB) │ │ +│ │ └─ Rollback (revert last deploy) │ │ +│ └─────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────┤ +│ WordPress Plugin (on user's hosting) │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ REST API (site info, read/write files, DB, │ │ +│ │ WP-CLI execution, deploy receiver) │ │ +│ └─────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────┤ +│ Infrastructure │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ Docker + orchestration on raw VPS (v1) │ │ +│ │ → Pre-baked WordPress image │ │ +│ │ → Warm pool for instant spin-up │ │ +│ │ → Lazy media sync (only what the task needs) │ │ +│ └─────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` -| Layer | Responsibility | -| :--- | :--- | -| **Workspace shell** | Tauri window, Monaco Editor, Wursor panels (preview, diff, state, chat), terminal | -| **WP language services** | PHP/JS, stubs, `block.json`, `theme.json` schemas (Monaco language services + WP stubs) | -| **Site runtime manager** | wp-env/Docker lifecycle, ports, credentials (Rust; abstraction-bound for future backends) | -| **Agent tool bus** | Files, WP-CLI, HTTP preview, DB read, linters (Rust; one tool schema per tool) | -| **Knowledge index** | Code index + WP graph (Rust parser; static scan + runtime enrichment) | -| **Policy engine** | Permissions, environment gates, secret redaction (Rust) | -| **Preview / verify** | Embedded webview, screenshots, HTTP checks, error sniff | -| **Connectors** | GitHub, staging hosts, optional design tools | +### 8.2 Agent substrate (locked) -### 8.1.1 Agent substrate (locked) +- **Model:** Grok (xAI) — strong agentic capabilities, tool-calling, multi-step reasoning +- **Routing:** Wursor-hosted (users do not need their own API key) +- **Tool-calling:** Each playbook step is a tool call against the sandbox. The agent orchestrates the sequence; the API server dispatches. +- **System prompt:** Built per session from: + - Site info (theme, active plugins, WordPress version, PHP version) + - User's goal (parsed from the chat message) + - Safety rules (never touch the live site, never ask for money, never hide changes) + - Playbook-specific instructions -- **Model:** Grok (xAI) — agentic coding model; BYO API key at launch -- **Routing:** All agent traffic goes through the user's own API key — no Wursor-hosted model tier in v1 -- **Tool-calling protocol:** Every agent tool (§8.3) is a single tool schema, not a prompt chain. The agent calls tools; the tool bus executes against the local environment -- **Fallback:** If the model is unreachable or returns an error, the agent panel shows a clear "Model unavailable" state with the raw error, logs, and a retry button. The workspace shell (editing, terminal, preview) remains fully functional -- **P1 upsell:** Optional Wursor-hosted routing tier for users who prefer a managed key or bundled tokens +### 8.3 Sandbox infrastructure -### 8.2 Default local stack -- **wp-env** for local + CI parity (sole runtime in v1; runtime manager abstraction-bound for future backends) -- MySQL as default; optional ultralight SQLite path for demos only -- Node LTS for block builds +- **Base image:** WordPress + nginx + PHP 8.x + MySQL 8.x + WP-CLI + Redis +- **Warm pool:** 5–10 pre-booted containers per region, ready to accept a mirror +- **Site mirroring:** + - Plugin list and active theme → installed immediately + - Content (posts, pages, options) → pulled from the live site via the plugin API + - Media files → lazy sync; only pulled when the preview or agent accesses them +- **Networking:** Sandboxes have full outbound internet access (for plugin installs, API calls). No inbound access except from the Wursor API server. +- **Idle timeout:** 15 minutes. User typing or viewing the preview resets the timer. +- **Hard timeout:** 24 hours. Sandbox is destroyed regardless of state. +- **Cost per sandbox:** ~$0.01–0.02/hour in raw compute (VPS-backed). A typical 1.5-hour task costs ~$0.02–0.03. -### 8.3 Initial agent tools -- `fs.read` / `fs.write` / `fs.apply_patch` -- `wpcli.run` (categorized) -- `site.browse` / `site.screenshot` -- `site.request` (front / REST) -- `db.query` (read-only default) -- `lint.phpcs` / `test.phpunit` -- `index.search` / `wp.graph.lookup` +### 8.4 Deploy mechanism -### 8.4 Example flow -1. "Add an FAQ accordion block and show it on /pricing." -2. Detect theme type, build setup, existing patterns. -3. Scaffold and register the block; wire pattern or template. -4. Build assets; flush as needed via WP-CLI. -5. Load /pricing and editor insert path; capture proof. -6. Present file diffs (+ State Diff if any); user accepts. +When the user approves: + +1. **Compute diff** — compare the sandbox's file system and database to the mirror snapshot taken at spin-up +2. **File changes** — send changed files to the plugin's deploy API +3. **Database changes** — send SQL migration to the plugin's deploy API (or WP-CLI commands) +4. **Plugin changes** — plugin installs/activations sent as WP-CLI commands +5. **Verify** — plugin confirms the live site is functional after changes +6. **Snapshot** — deploy snapshot stored for rollback (files + DB state) + +Rollback restores the files and database from the snapshot. ### 8.5 Error & offline states | State | What Wursor does | | :--- | :--- | -| **Docker not installed** | Detect at project open; show diagnostic panel with one-click install guide; app remains usable for file editing and git | -| **wp-env not found** | Offer to install via npm; fall back to npx | -| **Site won't boot** | Stream logs live; highlight the first error; offer "reset" and "last known good config" | -| **Model unreachable** | Show raw error + retry; workspace shell stays fully functional | -| **API key invalid / expired** | Prompt for key update inline; no data loss | -| **Network offline** | Cache last-known graph state; agent panel shows "offline" warning; local site and editing unaffected | -| **File permission denied** | Surface the OS-level error; no silent fallback to a different path | +| **Plugin not installed** | Show the pairing code and a link to install the plugin; wait for connection | +| **Plugin unreachable** | Show "Wursor can't reach your site" with troubleshooting steps (check if site is down, plugin is active) | +| **Sandbox spin-up fails** | Retry with a fresh container; if persistent, show "We're having trouble starting a preview" with support link | +| **Agent encounters an error** | Surface in chat: "I ran into an issue. Here's what happened and what I can try next." | +| **Deploy fails** | Show the error with a retry button; sandbox is kept alive so the user can retry or contact support | +| **Deploy results in broken site** | Plugin detects a 500 error or critical failure; automatically rolls back and reports to the user | +| **Network offline (user)** | Show "You're offline" message; session resumes when connection returns | +| **Rate limit / API error** | Retry with exponential backoff; surface persistent failures in chat | --- ## 9. UX Notes -- Dark-first, dense workshop UI; calm over theatrical -- **Site status bar:** environment, WP version, active theme, permission mode -- **Diff view:** Files tab + State tab -- **Preview:** dockable; device widths; view-as role (Admin / Editor / Customer) -- Keyboard-complete for agent flows; escape hatch from preview focus +- **Landing page is the chat.** No dashboard, no navigation. The user signs in and sees a chat input. That's it. +- **First-time user:** "Welcome to Wursor. Describe what you'd like to change on your site." +- **Empty state:** "Your site is connected. Try: 'Make my homepage look more modern' or 'Add a contact form.'" +- **Preview opens in a split view.** Chat on the left, preview on the right. The user can resize the split. +- **Preview is interactive.** The user can click around the preview, navigate pages, test forms. It's a real browser. +- **Approve/reject buttons are persistent.** They stay at the bottom of the chat as long as there's an unapproved change. +- **Deploy history is a simple list.** "Homepage redesign — applied 2 hours ago — Undo" with a one-click undo on each entry. +- **The agent has a name and personality.** Warm, competent, transparent. "I've updated your homepage. Here's what I changed: I updated the hero section, added a call-to-action button, and fixed the mobile layout." +- **Mobile:** The preview collapses to a full-screen chat with a "Show preview" button that opens the preview in a new tab. --- @@ -362,14 +401,14 @@ Project guidance lives in `WORDPRESS.md` / `.wursor/rules` (standards, banned pa | Product type | Strength | Gap Wursor fills | | :--- | :--- | :--- | -| General AI code editors | Strong general coding agents | No WordPress runtime loop or WP semantics | -| Classic PHP IDEs | Deep PHP tooling | Weak agent-native site loop | -| Local WP apps | Easy site spin-up | Not an engineering agent workspace | -| wp-env / DDEV | Solid runtimes | CLI-centric; no integrated agent UX | -| Host / plugin AI | Handy in wp-admin | Content-oriented; not Git/theme/plugin shipping | -| Page builders | Fast visual pages | Different paradigm; not Wursor's v1 center | +| Page builders (Elementor, etc.) | Visual editing | Require learning the builder; agent does it for you | +| AI content assistants (Jetpack AI, etc.) | Writing posts | Can't change layout, install plugins, or modify design | +| General AI coding tools | Code-level changes | Require technical skill; no visual preview | +| Wix / Squarespace | Simple, integrated | Not WordPress; don't own your site | +| Agency / freelancer | Human expertise | Slow, expensive, per-task | +| Host copilot features | In-context help | Limited to what the host built; no agentic autonomy | -**Moat:** WP knowledge graph + controllable runtime + policy-aware tools + verify-via-preview, packaged as playbooks agencies and plugin teams repeat weekly. +**Moat:** Cloud sandbox + agentic orchestration + universal plugin connector. The user gets a safe, ephemeral copy of their site, an agent that can do anything a WordPress developer can do, and a one-click deploy back to the live site. No other product combines all three. --- @@ -377,53 +416,58 @@ Project guidance lives in `WORDPRESS.md` / `.wursor/rules` (standards, banned pa | Metric | Baseline | 6-month target | Owner | How we measure | | :--- | :--- | :--- | :--- | :--- | -| Time to first local preview from new workspace | TBD (Phase 0 spike) | ≤ 10 min p50 | Eng lead | In-app timer from project open to first rendered preview | -| Accepted agent runs on P0 playbooks (little rework) | TBD (alpha 1) | ≥ 60% | PM | Per-playbook accept/reject event, tagged by playbook | -| Verify step catches issues before accept | TBD (alpha 1) | ≥ 30% of failing tasks | PM | Verify-fail event before accept, per task | -| Trial → weekly habit by week 4 | TBD | ≥ 40% | PM | Weekly active usage per trial cohort | -| Paying seats | n/a | TBD with pricing | GTM | Billing records | +| Time from sign-up to first deployed change | TBD (alpha 1) | ≤ 5 min | PM | In-app timer from sign-up to first approve | +| Task completion rate (user describes → change deployed) | TBD (alpha 1) | ≥ 60% | PM | Per-task: started → approved | +| User claps back (reject → re-describe → approve) | TBD | ≤ 20% of tasks | PM | Reject events per session | +| Sandbox spin-up time (p50) | TBD | ≤ 5s | Eng | Server-side timer | +| Verify step catches issues before approve | TBD (alpha 1) | ≥ 30% of failing tasks | PM | Verify-fail before approve, per task | +| Weekly active users as % of sign-ups | TBD | ≥ 40% | PM | Weekly active per cohort | +| Paid conversion | TBD | ≥ 5% of trial users | GTM | Billing records | -**Measurement plan:** all metrics instrumented from first alpha build (Phase 2). Every metric is a dashboarded event, not a manual tally. Baselines are collected during closed alpha (10–20 agencies / plugin teams) and reviewed as Phase 2 exit criteria. - -Qualitative bar: experienced WordPress engineers say it behaves like someone who has shipped WP for years. +**Measurement plan:** all metrics instrumented from first alpha build. Every metric is a dashboarded event. Baselines from closed alpha (Phase 1) inform Phase 2 targets. --- ## 12. Phased Roadmap ### Phase 0 — Pivot & spec (now) -- Clear prior product codebase -- PRD + naming -- Spike: wp-env control plane + agent tool bus +- Rewrite PRD for non-technical-first +- Spike: cloud sandbox orchestration (WordPress in Docker, warm pool, site mirroring) +- Spike: WordPress plugin (REST API, file read/write, DB access, WP-CLI) +- Spike: basic chat + preview web app ### Phase 1 — Foundation (weeks 1–8) -- Ship Tauri shell on Monaco Editor (reused editor component; no greenfield chrome) -- Project open + WP detection -- wp-env lifecycle + preview -- Agent chat + diffs + rules -- WP-CLI tool + permission engine -- P0 playbooks +- Web app: sign-up, site connection (plugin auth), chat, preview, approve/reject +- Sandbox infrastructure: warm pool, site mirroring, idle timeout, GC +- WordPress plugin: site info API, deploy receiver, rollback, auto-update +- Playbooks: content edit (text, images, pages), design change (layout, colors) +- Deploy history: timeline, one-click undo -**Exit criteria:** a new user on a clean machine (no Docker, no wp-env) reaches a live preview of a WordPress repo in ≤ 10 minutes, and a P0 playbook (dynamic block) completes with a verified preview + accepted diff. +**Exit criteria:** a new user signs up, connects their WordPress site, types "change my homepage heading to 'Welcome to My Business'", sees a preview with the change, and approves it — all in under 5 minutes. ### Phase 2 — Intelligence (weeks 9–16) -- Knowledge graph v1 (static + runtime passes) -- WPCS / tests in the loop -- State Diffs + read-only DB introspection -- Careful staging pull -- Closed alpha (10–20 agencies / plugin teams) +- Plugin playbooks (install, configure, fix conflicts) +- Site build playbook (from scratch, from design direction) +- Mobile-responsive preview +- Agent clarifying questions (disambiguation) +- Closed alpha (10–20 site owners) -**Exit criteria:** all §11 baselines collected and reviewed; knowledge graph staleness surfaced in UI; State Diff create→rollback loop demoed on a destructive option change. +**Exit criteria:** all §11 baselines collected and reviewed; a non-technical user can install a plugin via chat and see it working on their site. ### Phase 3 — Professional (weeks 17–28) -- Block / FSE workshop -- Role-based preview -- Host deploy connectors -- Shared team playbooks -- Paid beta +- Multi-step workflows (queue changes) +- Visual design picker (theme gallery) +- Site build playbook (complex, multi-page) +- SEO optimization playbook +- Performance optimization playbook +- Paid beta ### Phase 4 — Platform -- WooCommerce, multisite, maintenance agents, ecosystem connectors +- Multi-site management +- Team / agency mode +- E-commerce operations (WooCommerce) +- Scheduled changes +- Marketplace (community playbooks) --- @@ -431,15 +475,14 @@ Qualitative bar: experienced WordPress engineers say it behaves like someone who | Risk | Impact | Mitigation | | :--- | :--- | :--- | -| Building a full workspace is large | High | Tauri + Monaco (reused editor component, no greenfield editor); WP runtime + tools get the focus | -| Local Docker/wp-env pain (esp. Windows) | High | Diagnostics-first first-run; installer guides; early Local/DDEV import (P1) | -| Agent harms a site | High | Permission tiers; local-default; production lock; State Diffs with rollback | -| "Prompts in my current editor are enough" | Medium | Demo the site loop and playbooks general setups fail | -| Legacy PHP / chaotic themes | Medium | Stubs, WPCS, honest limits; playbooks for clean paths first | -| Repo still named originmain | Resolved | Repo renamed to SinachPat/wursor | -| Trademark / "WordPress" in marketing | Medium | Follow WordPress Foundation trademark rules | -| LLM provider outage / model churn | Medium | BYO-key model; workspace shell stays usable offline; P1 hosted routing tier | -| Docker Desktop licensing for commercial use | Low | Document; wp-env alternatives; Rancher Desktop path | +| Agent breaks the sandbox site | Medium | Sandbox is ephemeral; worst case, GC and start fresh. Live site never touched. | +| Agent installs a malicious plugin | Medium | Plugin repo is reviewed; sandbox is isolated; no data leaks to the live site. | +| Deploy to live site fails | High | Plugin detects failure, rolls back automatically, sandbox stays alive for retry. | +| Mirroring a large site is slow | High | Lazy sync for media; incremental content sync; warm pool absorbs the variance. | +| User can't describe what they want | Medium | Agent asks clarifying questions; suggests options ("Would you like a modern look or a classic look?"). | +| Plugin compatibility (old WordPress, old PHP) | Medium | Detect at connection time; warn the user; support the top 90% of versions. | +| Grok model quality for agentic tasks | Medium | Evaluate in Phase 0 spike; have a fallback model path (switch to Claude or GPT-4o). | +| Sandbox cost scales with usage | Low | ~$0.02/task at v1 volume; even at 100k tasks/month, < $5k. Agent API calls are the higher cost. | --- @@ -447,57 +490,58 @@ Qualitative bar: experienced WordPress engineers say it behaves like someone who ### Resolved (locked) -1. **Shell:** Native desktop app on Tauri (Rust shell, system webview) + Monaco Editor (the editor core that powers VS Code). Fast startup (~0.3–0.8s), low memory (100–200MB), native filesystem/Docker/process access, offline-capable. Rust backend; webview UI in TypeScript. -2. **Name:** Wursor (locked in v1.2; no further rename planned). -3. **Repo:** renamed to `SinachPat/wursor`. -4. **Pricing:** seat-based ($X/dev/month, free tier with per-seat limits); agency teams primary. Final $X set during Phase 3 paid beta. -5. **Roots/Bedrock/Trellis support:** P1 (not v1). wp-env covers the launch segment; runtime manager is abstraction-bound for later import. -6. **Models:** Grok (xAI) via BYO API key (v1); optional Wursor-hosted routing tier (P1 upsell). No local model support in v1. -7. **Runtime backends:** wp-env only in v1; Local / DDEV import is P1. +1. **Shell:** Web app. No desktop app. No code editor. The chat interface is the entire product surface. +2. **Architecture:** Web app → API server → cloud sandbox → WordPress plugin on user's hosting. +3. **Sandbox hosting:** Wursor-hosted (cloud VPS with Docker). User installs nothing beyond the plugin. +4. **Model:** Grok (xAI). Wursor-hosted. Users do not need their own API key. +5. **Plugin connector:** Required for all users. Single install, one-time pairing code. +6. **Pricing:** Seat-based (free tier with limited tasks/month, paid tier for unlimited). Free tier supports the "try before you trust" loop. +7. **Non-technical first:** All features are designed for the person who has never seen a terminal. Advanced features are added later. ### Remaining (genuinely open) -All Phase 0 questions are resolved above. New questions will be documented per phase and resolved before the next phase begins. +1. **Free tier limits:** How many tasks per month before asking for payment? Set during Phase 3 beta. +2. **Pricing:** Final $X and free tier limits set during Phase 3 paid beta. +3. **Media library handling:** Lazy sync is the plan, but large media libraries (20GB+) need specific design. Phase 1 spike. +4. **Plugin compatibility:** Which WordPress + PHP versions are we guaranteeing? Phase 0 spike. --- ## 15. Appendices ### A. Glossary -- **Workspace** — Project + site runtime + environment config bound together -- **State Diff** — Reviewable WP-CLI / SQL / content mutation plan with a create→review→apply→rollback lifecycle -- **Playbook** — Reusable agent workflow with tools and checks -- **WP Knowledge Graph** — Map of themes, plugins, blocks, hooks, REST (static scan + runtime enrichment) -- **Runtime** — The site execution environment (wp-env in v1) -- **Environment** — A target (local / staging / production) with endpoints and policy -- **Verify** — The proof step (screenshot / HTTP check / editor confirmation) required before a task is "done" -- **FSE** — Full Site Editing (block themes) -- **wp-env** — `@wordpress/env` local environment +- **Sandbox** — An ephemeral, isolated copy of the user's WordPress site running in Wursor's cloud +- **Playbook** — A structured, multi-step agent workflow for a specific task type +- **Plugin connector** — The WordPress plugin that connects the user's site to Wursor +- **Mirror** — The process of copying a site's theme, plugins, content, and settings into a sandbox +- **Deploy** — The process of applying sandbox changes to the live site +- **Warm pool** — Pre-booted WordPress containers ready to accept a mirror, reducing spin-up time ### B. P0 playbook sketches -1. **Dynamic block** — detect build → scaffold → register → build → verify in editor → diff -2. **Child theme** — scaffold → enqueue parent → override template → screenshot home -3. **CPT** — register → flush rewrites → seed via WP-CLI → REST check → diff +1. **Edit text** — parse user request → find content in DB → update → verify page loads → show preview +2. **Change layout** — parse user request → identify theme → modify template or page builder content → verify → preview +3. **Install plugin** — parse user request → search WP repo → install via WP-CLI → activate → configure defaults → verify → preview ### C. Non-goals (v1) -- Replacing wp-admin for authors -- Unattended production hotfixes -- Competing with Elementor-class page builders as the core offer -- Equal-class support for every legacy builder shortcode ecosystem on day one -- A public extension/plugin API — connectors are internal; third-party integration ships after platform phase -- Local / DDEV / Bedrock imports — P1 (§7.2.6) -- Managed/hosted model tier — P1 upsell -- **Accessibility certification (WCAG) or i18n / localization** — v1 is English-only with no formal accessibility conformance target. Basic keyboard navigation and screen reader support come from Monaco and the webview's standard web accessibility practices; custom Wursor panels will not be audited until Phase 3. +- A code editor or terminal +- Git integration or file diffs +- Local development workflows (Docker, wp-env, Local WP) +- AI content writing (blog posts, copywriting — Wursor is for *doing*, not writing) +- Replacing wp-admin entirely for users who want it (the plugin coexists) +- Hosting or infrastructure management (Wursor is not a hosting platform) +- Extension API or marketplace (Phase 4) +- Multi-user or team features (Phase 4) +- **Accessibility certification (WCAG) or i18n / localization** — v1 is English-only with no formal accessibility conformance target. The web app targets standard web accessibility practices but will not be audited until Phase 3. -### C.1 Wursor's own test strategy -- **Unit + integration tests** for the agent tool bus (each tool schema), the permission engine, and the State Diff lifecycle -- **Fixture-based WP repos** in CI (wp-env in GitHub Actions) to test detection, indexing, and playbooks without a live install -- **E2E smoke** on the Tauri shell: open → detect → boot → preview → verify -- **Release gates:** CI runs Rust + webview tests on every PR; e2e before each release +### D. Wursor's own test strategy +- **Unit tests** for the agent orchestrator, playbook runner, sandbox manager, and deploy manager +- **Integration tests** with real WordPress sandbox instances in CI (Docker on GitHub Actions, pre-baked image) +- **E2E tests** with Playwright against the web app, connected to a real sandbox + plugin +- **Release gates:** CI runs unit + integration on every PR; e2e before each release -### D. One-liner -**Wursor is the agentic workshop for WordPress — code, WP-CLI, data, and a live site in one loop.** +### E. One-liner +**Wursor is the agentic layer for WordPress — describe what you want, see it live, approve it.** --- -*End of PRD v1.3 — Wursor* +*End of PRD v2.0 — Wursor* \ No newline at end of file diff --git a/README.md b/README.md index 5a2368c..74a8f52 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,25 @@ # Wursor -**Working title.** Agentic WordPress development environment — code, live site, WP-CLI, and shipping in one loop. +**The Agentic WordPress Management Platform** -> This repository previously contained **Originmain** (AI design-engineering canvas). That codebase was removed in the product pivot of August 2026. +Just describe what you want. Wursor does the rest. + +> This repository previously contained **Originmain** (AI design-engineering canvas) and then pivoted to **Wursor** (engineer-first WordPress desktop tool). The current version is **Wursor v2** — a web-based platform for non-technical WordPress site owners. ## Start here - Full product spec: [PRD.md](./PRD.md) - Build guide (TDD, sprints, CI): [IMPLEMENTATION.md](./IMPLEMENTATION.md) +## What it does + +1. Connect your WordPress site (one-click plugin install) +2. Describe what you want to change — "make my homepage look more modern" or "add a contact form" +3. Wursor spins up a safe copy of your site, makes the changes, and shows you a live preview +4. Like it? Click approve. Don't like it? Click reject — your site is untouched. + +No code. No terminals. No wp-admin. + ## Status Spec / Phase 0. Implementation has not started. \ No newline at end of file