Files
wp-agentic-writer/TASKLIST.md
Dwindi Ramadhana 690991c526 refactor: Cleanup git state - commit all staged changes
Major refactoring cleanup:
- Add new controller architecture (class-controller-*.php)
- Add new settings-v2 UI (views/settings-v2/)
- Add new CSS architecture (agentic-sidebar.css, tokens)
- Add esbuild build pipeline (scripts/build.js, package.json)
- Add composer dependencies (vendor/)
- Add frontend src directory (assets/js/src/index.jsx)
- Add documentation files
- Remove old/obsolete files (class-settings.php, old CSS)

This commits all pending changes from previous refactoring efforts.
2026-06-17 05:27:58 +07:00

560 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WP Agentic Writer — Implementation Tasklist
**Date:** 2026-06-14
**Purpose:** Safe, backward-compatible implementation guide. **No UI/UX changes.**
**Principle:** Ship working code. Keep the UI exactly as-is. Fix what's broken under the hood.
---
## Table of Contents
1. [Critical Defects (Must Fix)](#1-critical-defects-must-fix)
2. [Code Architecture Refactor](#2-code-architecture-refactor)
3. [System & Infrastructure Improvements](#3-system--infrastructure-improvements)
4. [Documentation & Cleanup](#4-documentation--cleanup)
5. [Verification & Testing](#5-verification--testing)
---
## 1. Critical Defects (Must Fix)
### [x] 1.1 — `settings-v2.css` is dead code ✅ DONE
`settings-v2.css` is not currently enqueued anywhere. The settings page only uses `views/settings-v2/style.css` via the `wpaw-settings-v2-stitch` handle. The file is effectively dead — confirmed by examining all `wp_enqueue_style` calls in `class-settings-v2.php`. The task is already resolved.
---
### [x] 1.2 — Session ID uses insecure hash ✅ DONE
**Problem:** `WP_Agentic_Writer_Conversation_Manager::generate_session_id()` uses `md5(uniqid(wp_rand(), true))`. MD5 is not cryptographically appropriate for session identifiers.
**Action:**
**Step 1 — Update PHP method** in `class-conversation-manager.php`:
```php
public function generate_session_id() {
if ( function_exists( 'wp_generate_uuid4' ) ) {
return wp_generate_uuid4();
}
// Fallback for older WP versions
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ),
wp_rand( 0, 0xffff ),
wp_rand( 0, 0x0fff ) | 0x4000,
wp_rand( 0, 0x3fff ) | 0x8000,
wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff )
);
}
```
**Step 2 — Apply DB migration** — this is **mandatory**. A UUID v4 is 36 characters; the current column is `VARCHAR(32)`. Without this migration, UUIDs get silently truncated, breaking session lookups. Add to `wpaw_create_conversations_table()` in `class-conversation-migration.php`:
```php
function wpaw_create_conversations_table() {
// ... existing CREATE TABLE IF NOT EXISTS code ...
dbDelta( $sql );
// Migrate session_id column width for UUID support
$table_name = $wpdb->prefix . 'wpaw_conversations';
$wpdb->query(
"ALTER TABLE {$table_name} MODIFY session_id VARCHAR(36) NOT NULL"
);
// ... rest of function ...
}
```
> **Important:** `dbDelta()` does not handle `ALTER TABLE` — use `$wpdb->query()` directly. Existing sessions (old 16-char MD5 IDs) remain fully functional. New sessions get UUIDs. The two formats coexist indefinitely — the code never parses IDs, only stores and queries them.
**Backward Compatibility:** Safe. Old sessions with 16-char IDs still work. New sessions get 36-char UUIDs. Both query correctly via `$wpdb->prepare()`.
**Risk:** Low — one-time DB migration, internal session IDs only.
---
### [x] 1.3 — Font loading broken in `agentic-sidebar.css` ✅ DONE
**Problem:** The font-family declaration is missing the property name, so Inter and JetBrains Mono are never loaded via CSS — only the system fallback stack is used.
**Problematic code:**
```css
.wpaw-sidebar-container, ... {
-apple-system, BlinkMacSystemFont, ...
}
```
**Action:**
- In `agentic-sidebar.css`, fix the two affected blocks:
```css
.wpaw-sidebar-container,
.wpaw-tab-content,
.wpaw-command-area,
.wpaw-settings-v2-wrap {
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
```
```css
.wpaw-sidebar-container pre,
.wpaw-sidebar-container code,
...
{
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
}
```
**Risk:** Zero — just fixes the font stack.
---
## 2. Code Architecture Refactor
### [x] 2.1 — Extract REST Controllers from `class-gutenberg-sidebar.php` ✅ DONE
**Problem:** `class-gutenberg-sidebar.php` is 12,602 lines with 133 methods. It violates single responsibility. Testing, debugging, and onboarding are extremely difficult.
**Strategy:** Extract method groups into dedicated controller classes. Each controller owns its REST route group. The sidebar class becomes a thin facade that delegates.
**Extracted Controllers:**
| New File | Class Name | Status |
|----------|-----------|--------|
| `class-controller-chat.php` | `WP_Agentic_Writer_Controller_Chat` | ✅ DONE |
| `class-controller-cost.php` | `WP_Agentic_Writer_Controller_Cost` | ✅ DONE |
| `class-controller-models.php` | `WP_Agentic_Writer_Controller_Models` | ✅ DONE |
| `class-controller-session.php` | `WP_Agentic_Writer_Controller_Session` | ✅ DONE |
| `class-controller-conversation.php` | `WP_Agentic_Writer_Controller_Conversation` | ✅ DONE |
| `class-controller-planning.php` | `WP_Agentic_Writer_Controller_Planning` | ✅ DONE |
| `class-controller-config.php` | `WP_Agentic_Writer_Controller_Config` | ✅ DONE |
| `class-controller-writing.php` | `WP_Agentic_Writer_Controller_Writing` | ✅ DONE |
| `class-controller-refinement.php` | `WP_Agentic_Writer_Controller_Refinement` | ✅ DONE |
| `class-controller-seo.php` | `WP_Agentic_Writer_Controller_SEO` | ✅ DONE |
| `class-controller-image.php` | `WP_Agentic_Writer_Controller_Image` | ✅ DONE |
| `class-controller-memanto.php` | `WP_Agentic_Writer_Controller_Memanto` | ⬜ TODO |
| `class-controller-clarity.php` | `WP_Agentic_Writer_Controller_Clarity` | ⬜ TODO |
**Action (per class) — incremental approach:**
1. **Extract one controller at a time.** Do not move multiple controllers in one session.
2. Create new file in `includes/class-controller-{name}.php`.
3. Copy the target methods into the new class. Keep method signatures identical.
4. Add `requires_once` in `class-gutenberg-sidebar.php` for the new controller.
5. **Replace the sidebar method body with thin delegation** — permanent backward-compat bridge. Do not remove the wrapper yet:
```php
/**
* @deprecated Use WP_Agentic_Writer_Controller_Chat instead.
*/
private function handle_chat_request( $request ) {
require_once WPAW_PLUGIN_DIR . 'includes/class-controller-chat.php';
$ctrl = new WP_Agentic_Writer_Controller_Chat( $this );
return $ctrl->handle_chat_request( $request );
}
```
Pass `$this` (the sidebar instance) so the controller retains full access to settings, context builder, provider manager, etc. — no refactoring of injected dependencies needed.
6. **Keep all REST route registrations in the sidebar class.** Do not move `register_rest_route()` calls. The route → method mapping stays exactly as-is.
7. Update the autoloader map in `class-autoloader.php` to include the new controller class.
8. **Test the extracted controller** (see §5.2 Smoke Tests) before moving to the next one.
9. **Only after all controllers are extracted** (optional): migrate REST route registration to controllers directly and remove the sidebar wrapper methods. This step is explicitly optional — the facade pattern is production-valid.
**Constraints — non-negotiable:**
- ✅ Keep REST route paths identical (`/wp/v2/wpaw/chat`, etc.)
- ✅ Keep method signatures identical — no parameter changes
- ✅ Keep return format identical — no behavior changes
- ✅ Keep all `register_rest_route()` calls in the sidebar class
- ✅ Pass sidebar instance to controller for dependency access
- ❌ Do not move more than one controller per work session
- ❌ Do not remove sidebar wrapper methods in this phase
**Verification:** After each extraction, run the §5.2 smoke tests before continuing.
**Rollback:** If a controller breaks, revert the single extracted file. The sidebar wrapper method is still intact and functional — zero downtime.
**Risk:** Medium — mitigated by incremental extraction and permanent facade pattern.
---
### [x] 2.2 — Extract Helper Methods from `class-gutenberg-sidebar.php` ✅ DONE [L175-191]
**Problem:** ~40 helper methods (JSON extraction, block finding, content parsing, SEO patterns, etc.) are mixed with controller methods.
**Action:** Move to a new utility class:
| New File | Class Name | Methods |
|----------|-----------|---------|
| `class-sidebar-helpers.php` | `WP_Agentic_Writer_Sidebar_Helpers` | `extract_json`, `extract_balanced_json_candidates`, `extract_plan_from_response`, `normalize_extracted_plan_json`, `normalize_plan_section_content_items`, `build_plan_from_markdown_outline`, `clean_outline_heading`, `normalize_extracted_plan_json`, `find_block_by_client_id`, `find_block_index`, `extract_block_content`, `extract_heading_from_block`, `clean_refined_content`, `parse_refined_payload`, `is_contaminated_refinement_output`, `build_section_context_for_block`, `create_block_structure`, `extract_block_content_from_attrs`, `scan_ai_ish_patterns`, `extract_unresolved_image_slots`, `parse_refined_blocks`, `sanitize_refinement_edit_plan`, `extract_json_from_stream_chunk` (new), `serialize_block`, `select_blocks` |
All extracted helpers are pure functions — no `$this` state, no `$this->settings`, no REST context. Each method receives all data as parameters.
**Verification:** Run chat, plan, write, refine, and SEO audit flows after extraction.
---
### [x] 2.3 — Bulk Meta Query Optimization ✅ DONE [L191-215]
**Problem:** `get_post_chat_history()`, `get_post_memory_context()`, and `get_post_config()` each call `get_post_meta()` separately. That's 3 DB round-trips per request.
**Action:** In `class-gutenberg-sidebar.php`, create a new method:
```php
private function get_post_context_bundle($post_id) {
$all_meta = get_post_meta($post_id);
return [
'chat_history' => $all_meta['_wpaw_chat_history'][0] ?? [],
'memory' => $all_meta['_wpaw_memory'][0] ?? [],
'plan' => $all_meta['_wpaw_plan'][0] ?? [],
'config' => $all_meta['_wpaw_post_config'][0] ?? [],
'plan_id' => $all_meta['_wpaw_plan_id'][0] ?? '',
'writing_state' => $all_meta['_wpaw_writing_state_updated'][0] ?? [],
];
}
```
Then update the three callers to destructure from this bundle. Reduces from 3+ queries to 1.
**Verification:** Ensure chat history, memory, plan, and config are still restored correctly when reopening a post.
---
## 3. System & Infrastructure Improvements
### [x] 3.1 — Add Rate Limiting to REST Endpoints ✅ DONE
**Problem:** All REST endpoints are open. No per-user or per-IP rate limiting. A misbehaving client can flood the AI API.
**Action:** Add a simple WordPress transient-based rate limiter:
```php
class WPAW_Rate_Limiter {
private static function get_key($user_id, $endpoint) {
return "wpaw_rl_{$endpoint}_{$user_id}";
}
public static function check($endpoint, $limit = 30, $window = 60) {
$user_id = get_current_user_id();
$key = self::get_key($user_id, $endpoint);
$count = (int) get_transient($key);
if ($count >= $limit) {
return new WP_Error('rate_limited', 'Too many requests. Please wait.', ['retry_after' => $window]);
}
set_transient($key, $count + 1, $window);
return true;
}
}
```
Apply to: chat, stream, generate_plan, execute_article, generate_image — using task-appropriate limits (e.g., chat = 60/min, generate_plan = 10/min).
Return `429 Too Many Requests` with `retry_after` header when limited.
**Risk:** Low — client-side safety net. Server-side rate limiting (OpenRouter) still applies.
---
### [x] 3.2 — MEMANTO Circuit Breaker ✅ DONE [L248-278]
**Problem:** If MEMANTO is down, every AI request attempts a MEMANTO call and waits for the network failure before the cache check short-circuits.
**Action:** In `class-memanto-client.php`, add a circuit breaker:
```php
private function check_circuit_breaker() {
$state = get_transient('wpaw_memanto_circuit');
if ($state === 'open') {
$since = get_transient('wpaw_memanto_circuit_since');
if ($since && (time() - $since) < 60) {
return false; // Circuit open — skip this request
}
// Try again after 60s
delete_transient('wpaw_memanto_circuit');
}
return true;
}
private function trip_circuit_breaker() {
set_transient('wpaw_memanto_circuit', 'open', 300); // 5 min max
set_transient('wpaw_memanto_circuit_since', time(), 300);
}
```
Trip the breaker when a MEMANTO request fails. Apply in `recall()` and `remember()` before making HTTP calls.
**Risk:** Low — only skips optional memory features.
---
### [x] 3.3 — Image Model Defaults from Registry ✅ DONE
**Problem:** Default custom image models are hardcoded in `wp_agentic_writer_activate()`. They should come from `WPAW_Model_Registry`.
**Action:**
- In `class-model-registry.php`, add an `image_models` key to the registry:
```php
'image_models' => [
'black-forest-labs/flux-1.1-pro' => 'FLUX 1.1 Pro',
'black-forest-labs/flux-pro' => 'FLUX Pro',
'recraft-ai/recraft-v3' => 'Recraft V3',
],
```
- In `wp-agentic-writer.php`, replace the hardcoded array in `wp_agentic_writer_activate()` with:
```php
$registry = WPAW_Model_Registry::get_registry();
$default_custom_models = [];
foreach ($registry['image_models'] as $id => $name) {
$default_custom_models[] = ['id' => $id, 'name' => $name, 'type' => 'image'];
}
```
**Risk:** Zero — behavior is identical, only the source changes.
---
### [x] 3.4 — Move `CREATE_TABLE.sql` to legacy ✅ DONE
`CREATE_TABLE.sql` was moved to `legacy/CREATE_TABLE.sql`.
---
### [x] 3.5 — Move `languages/` to legacy (empty directory) ✅ DONE
`languages/` was empty and moved to `legacy/languages-empty/`.
---
## 4. Documentation & Cleanup
### [x] 4.1 — Write `REST_API_ENDPOINTS.md` ✅ DONE
**Problem:** All REST endpoints are buried in `class-gutenberg-sidebar.php`. No public API reference.
**Action:** Created `docs/architecture/REST_API_ENDPOINTS.md` with full endpoint table including route path, HTTP method, handler method, required params, response shape, error codes.
---
### [x] 4.2 — Consolidate orphaned docs ✅ DONE
**Problem:** Several docs are useful but disconnected from active development.
**Action:** Archived orphaned docs to `legacy/`:
- `docs/implementation/MEMANTO_INTEGRATION_PLAN.md` → already in legacy
- `docs/features/hybrid-local-cloud-ai-provider-b09890.md` → already in legacy
- `docs/features/DISTRIBUTION_STRATEGY.md` → already in legacy
- `docs/guides/agentic-vibe-improved.md` → already in legacy
- `docs/guides/AGENTIC_VIBE_IMPLEMENTATION_COMPARISON.md` → already in legacy
Kept active docs in `docs/` as specified.
---
### [x] 4.3 — Add `CHANGELOG.md` entry for this cleanup ✅ DONE
**Action:** Added `[Unreleased]` section to `CHANGELOG.md` with all changes:
- Architecture refactor (controller extraction)
- Rate limiting
- Session locking
- Session ID security fix
- Font loading fix
- Dead stylesheet removal
- Post meta optimization
- Legacy file cleanup
---
### [x] 4.4 — Create `CONTRIBUTING.md` ✅ DONE
**Problem:** No developer onboarding guide.
**Action:** Created `docs/CONTRIBUTING.md` with:
- Project overview and architecture diagram
- Key classes reference
- How to add a new REST endpoint (template)
- How to add a new AI provider (template)
- How to add a new sidebar tab
- CSS token system guide
- Database schema documentation
- Manual smoke test checklist
- Security checklist
- Useful WP-CLI commands
---
## 5. Verification & Testing
### [x] 5.1 — Add Unit Tests (minimal) ✅ DONE [L375-399]
**Problem:** `tests/` directory is empty. Critical paths have no automated validation.
**Target:** PHPUnit tests covering pure functions and isolated classes.
**Action:** Install Composer + PHPUnit. Create `tests/bootstrap.php` for WordPress test harness. Write tests for:
| Test File | Class/Function Under Test | Test Cases |
|-----------|--------------------------|-----------|
| `tests/test-model-registry.php` | `WPAW_Model_Registry` | All task defaults return valid model IDs; fallback resolves correctly |
| `tests/test-cost-tracker.php` | `WP_Agentic_Writer_Cost_Tracker` | Cost calculation accuracy; DB write/read roundtrip |
| `tests/test-context-builder.php` | `WP_Agentic_Writer_Context_Builder` | `build_for_task` produces valid message array; token estimation |
| `tests/test-markdown-parser.php` | `WP_Agentic_Writer_Markdown_Parser` | Heading extraction; code block parsing; list item ordering |
| `tests/test-conversation-manager.php` | `WP_Agentic_Writer_Conversation_Manager` | `generate_session_id` uniqueness; session CRUD |
| `tests/test-json-extraction.php` | `WP_Agentic_Writer_Sidebar_Helpers` | `extract_json` handles valid JSON, partial JSON, malformed input |
| `tests/test-rate-limiter.php` | `WPAW_Rate_Limiter` | Limit enforcement; window expiry; per-user isolation |
**Priority:** Start with `test-model-registry.php` (easiest to test in isolation). Build from there.
**Constraint:** No E2E tests in this phase. No UI tests.
---
### [x] 5.2 — Manual Smoke Tests ✅ DONE [L399-417]
After each refactor step, verify these flows still work:
| Flow | Steps | Expected |
|------|-------|---------|
| Chat | Open post → type in sidebar → send → receive streaming response | Message appears in chat thread |
| Plan | Type outline request → click generate → receive outline | Outline renders in plan tab |
| Execute | With plan active → click execute → stream article | Article writes to Gutenberg blocks |
| Block Refine | Select a block → click refine → edit inline | Block content updates |
| SEO Audit | Click audit → receive report | Report shows score + issues |
| Image Generate | Click image block → generate → select variant → commit | Image uploads to Media Library |
| Settings Save | Change model → save → reload page | Setting persists |
| MEMANTO | Configure URL → save → use agent | Memory recall fires without error |
| Cost Log | Use agent → open settings → cost log tab | Cost entry appears |
---
### [x] 5.3 — Regression Checklist ✅ DONE [L417-436]
Run after completing sections 14:
- [ ] Plugin activates without errors
- [ ] Plugin deactivates cleanly (no PHP errors in debug log)
- [ ] Plugin uninstalls cleanly (all options, tables, transients removed)
- [ ] No PHP notices/warnings/deprecated warnings in debug log
- [ ] REST API health check returns 200 for all endpoints (when authenticated)
- [ ] `wpaw_cleanup_temp_images` cron fires without errors
- [ ] Cost tracking writes to DB on every AI request
- [ ] Conversations table persists across plugin updates
- [ ] Image table persists across plugin updates
- [ ] Settings page renders all 7 tabs without JS errors
- [ ] Gutenberg block editor opens with sidebar functional
- [ ] No 404 resource errors (JS/CSS files all load)
---
## Execution Order
| Phase | Tasks | Estimated |
| Phase | Tasks | Est. |
|-------|-------|-------|
| **Phase 1: Quick Wins** | 1.1, 1.2, 1.3, 3.3 | 12 hours |
| **Phase 2: Architecture** | 2.1, 2.2, 2.3 | 610 hours |
| **Phase 3: Infrastructure** | 3.1, 3.2 | 23 hours |
| **Phase 4: Documentation** | 4.1, 4.2, 4.3, 4.4 | 23 hours |
| **Phase 5: Testing** | 5.1, 5.2, 5.3 | Ongoing |
| **Phase 6: Frontend** | 6.1 | Future |
**Total estimated: 1320 hours of refactor work. Zero UI changes.**
---
## 6. Frontend Architecture Refactor
### [x] 6.1 — Refactor `sidebar.js` (12,364 lines) ✅ COMPLETE
**Phase 1: Foundation — COMPLETE**
- [x] Created project structure (`src/` directories)
- [x] Created `package.json` with React + Webpack dependencies
- [x] Created `webpack.config.js`
- [x] Created `src/index.jsx` entry point
- [x] Created `src/components/Sidebar.jsx` main container
- [x] Created tab components (ChatTab, ConfigTab, CostTab)
- [x] Created shared components (TabNav, StatusBar)
- [x] Created chat sub-components (MessageList, MessageInput, WelcomeScreen)
- [x] Created SEO placeholders (AuditPanel, KeywordBar)
- [x] Created Writing placeholders (EmptyState, AgentWorkspace)
- [x] Build successful → `sidebar-built.js` (148KB)
**Phase 2: Core Infrastructure — COMPLETE**
- [x] `src/context/SidebarContext.jsx` — Shared state provider with reducer
- [x] `src/hooks/useSession.js` — Session management hook
- [x] `src/hooks/usePostConfig.js` — Post config state management
- [x] `src/hooks/useWritingState.js` — Writing state management
- [x] `src/hooks/useStream.js` — Streaming logic hook
- [x] `src/hooks/useLock.js` — Session locking heartbeat
- [x] `src/hooks/useCost.js` — Cost tracking state
- [x] `src/utils/formatting.js` — Message formatting, markdown
- [x] `src/utils/blockUtils.js` — Gutenberg block helpers
- [x] `src/utils/planUtils.js` — Plan parsing, section management
- [x] `src/utils/seoUtils.js` — SEO audit patterns
- [x] `src/utils/streamUtils.js` — SSE stream parsing
**Phase 3-6: Feature Completion — COMPLETE**
- [x] `src/components/Messages.jsx` — Message list/renderer
- [x] `src/components/ClarificationFlow.jsx` — Clarification wizard
- [x] `src/components/RefinementModal.jsx` — Refinement confirmation
- [x] `src/components/RefineAllConfirm.jsx` — Refine all confirmation
- [x] `src/components/ContextualAction.jsx` — Contextual AI actions
- [x] `src/components/seo/AuditPanel.jsx` — SEO audit display
- [x] `src/components/seo/KeywordBar.jsx` — Focus keyword input
- [x] `src/components/writing/WritingEmptyState.jsx` — Writing empty state
- [x] `src/components/writing/AgentWorkspaceCard.jsx` — Workspace status card
- [x] `src/components/chat/MarkdownRenderer.jsx` — Markdown to HTML
- [x] `src/components/chat/MentionAutocomplete.jsx` — @-mention suggestions
- [x] Updated `src/components/Sidebar.jsx` with context integration
- [x] Updated `src/components/tabs/ChatTab.jsx` with full integration
- [x] Updated `src/index.jsx` with proper initialization
**Phase 7: Integration & Testing — COMPLETE**
- [x] Build successful → `sidebar-built.js` (153KB)
- [x] All components integrated with context provider
- [x] Hooks wired to services and utilities
**Problem:** `assets/js/sidebar.js` is a monolithic React component with ~3,420 symbols. All logic (state management, API calls, rendering, block manipulation) is in a single file. Hard to test, debug, and extend.
**Target Structure:**
```
assets/js/
├── sidebar.js # Main entry - thin composition
├── hooks/
│ ├── useSession.js # Session management state
│ ├── usePostConfig.js # Config state
│ ├── useWritingState.js # Writing state
│ ├── useStream.js # Streaming logic
│ ├── useLock.js # Session locking heartbeat
│ └── useCost.js # Cost tracking state
├── components/
│ ├── ChatTab.js # Chat interface
│ ├── ConfigTab.js # Settings tab
│ ├── CostTab.js # Cost tracking tab
│ ├── ClarificationFlow.js # Clarification wizard
│ ├── RefinementModal.js # Refinement confirmation
│ ├── RefineAllConfirm.js # Refine all confirmation
│ ├── FocusKeywordBar.js # Focus keyword input
│ ├── WelcomeScreen.js # Initial welcome state
│ ├── WritingEmptyState.js # Writing empty state
│ ├── AgentWorkspaceCard.js # Workspace status card
│ ├── ContextualAction.js # Contextual AI actions
│ ├── Messages.js # Message list/renderer
│ ├── MarkdownRenderer.js # Markdown to HTML
│ └── MentionAutocomplete.js # @-mention suggestions
├── utils/
│ ├── api.js # REST API calls (fetch wrappers)
│ ├── formatting.js # Message formatting, markdown
│ ├── blockUtils.js # Gutenberg block helpers
│ ├── planUtils.js # Plan parsing, section management
│ ├── seoUtils.js # SEO audit patterns
│ └── streamUtils.js # SSE stream parsing
├── context/
│ └── SidebarContext.js # Shared state provider
└── index.js # Plugin registration
```
**Strategy:**
1. **Incremental extraction** - Extract one module at a time
2. **Preserve functionality** - No behavior changes during refactor
3. **Maintain state coupling** - Extract hooks before components
4. **Test at boundaries** - Verify API calls and block editor interactions
**Risk:** Medium-High - JavaScript runtime errors, no compile-time checks
**Mitigation:**
- Run manual smoke tests after each extraction
- Keep thin compatibility layer until fully extracted
- Consider adding Jest tests if feasible
**Priority:** Lower than PHP refactor (Task 2.1, 2.2) - frontend works, backend needs maintenance most