diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule
index 9f71c945..883a4257 100644
Binary files a/backend/celerybeat-schedule and b/backend/celerybeat-schedule differ
diff --git a/igny8-wp-integration-plugin/.gitattributes b/igny8-wp-integration-plugin/.gitattributes
deleted file mode 100644
index dfe07704..00000000
--- a/igny8-wp-integration-plugin/.gitattributes
+++ /dev/null
@@ -1,2 +0,0 @@
-# Auto detect text files and perform LF normalization
-* text=auto
diff --git a/igny8-wp-integration-plugin/README.md b/igny8-wp-integration-plugin/README.md
deleted file mode 100644
index fa7604b8..00000000
--- a/igny8-wp-integration-plugin/README.md
+++ /dev/null
@@ -1,396 +0,0 @@
-# IGNY8 WordPress Bridge Plugin
-
-**Version**: 1.0.0
-**Last Updated**: 2025-10-17
-**Requires**: WordPress 5.0+, PHP 7.4+
-
----
-
-## Overview
-
-The IGNY8 WordPress Bridge Plugin is a **lightweight synchronization interface** that connects WordPress sites to the IGNY8 API. This plugin acts as a bridge, not a content management system, using WordPress native structures (taxonomies, post meta) to sync data bidirectionally with IGNY8.
-
-### Key Principles
-
-- ✅ **No Custom Database Tables** - Uses WordPress native taxonomies and post meta
-- ✅ **Lightweight Bridge** - Minimal code, maximum efficiency
-- ✅ **Two-Way Sync** - WordPress ↔ IGNY8 API synchronization
-- ✅ **WordPress Native** - Leverages existing WordPress structures
-- ✅ **API-First** - IGNY8 API is the source of truth
-
----
-
-## Features
-
-### Core Functionality
-
-1. **API Authentication**
- - Secure token management
- - Automatic token refresh
- - Encrypted credential storage
-
-2. **Two-Way Synchronization**
- - WordPress → IGNY8: Post status changes sync to IGNY8 tasks
- - IGNY8 → WordPress: Content published from IGNY8 creates WordPress posts
-
-3. **Taxonomy Mapping**
- - WordPress taxonomies → IGNY8 Sectors/Clusters
- - Hierarchical taxonomies map to IGNY8 Sectors
- - Taxonomy terms map to IGNY8 Clusters
-
-4. **Post Meta Integration**
- - `_igny8_task_id` - Links WordPress posts to IGNY8 tasks
- - `_igny8_cluster_id` - Links posts to IGNY8 clusters
- - `_igny8_sector_id` - Links posts to IGNY8 sectors
- - `_igny8_keyword_ids` - Links posts to IGNY8 keywords
-
-5. **Site Data Collection**
- - Automatic collection of WordPress posts, taxonomies, products
- - Semantic mapping to IGNY8 structure
- - WooCommerce integration support
-
-6. **Status Mapping**
- - WordPress post status → IGNY8 task status
- - Automatic sync on post save/publish/status change
-
----
-
-## Installation
-
-### Requirements
-
-- WordPress 5.0 or higher
-- PHP 7.4 or higher
-- WordPress REST API enabled
-- IGNY8 API account credentials
-
-### Installation Steps
-
-1. **Download/Clone Plugin**
- ```bash
- git clone [repository-url]
- cd igny8-ai-os
- ```
-
-2. **Install in WordPress**
- - Copy the `igny8-ai-os` folder to `/wp-content/plugins/`
- - Or create a symlink for development
-
-3. **Activate Plugin**
- - Go to WordPress Admin → Plugins
- - Activate "IGNY8 WordPress Bridge"
-
-4. **Configure API Connection**
- - Go to Settings → IGNY8 API
- - Enter your IGNY8 email and password
- - Click "Connect to IGNY8"
-
----
-
-## Configuration
-
-### API Settings
-
-Navigate to **Settings → IGNY8 API** to configure:
-
-- **Email**: Your IGNY8 account email
-- **Password**: Your IGNY8 account password
-- **Site ID**: Your IGNY8 site ID (auto-detected after connection)
-
-### WordPress Integration
-
-The plugin automatically:
-
-1. **Registers Taxonomies** (if needed):
- - `sectors` - Maps to IGNY8 Sectors
- - `clusters` - Maps to IGNY8 Clusters
-
-2. **Registers Post Meta Fields**:
- - `_igny8_task_id`
- - `_igny8_cluster_id`
- - `_igny8_sector_id`
- - `_igny8_keyword_ids`
- - `_igny8_content_id`
-
-3. **Sets Up WordPress Hooks**:
- - `save_post` - Syncs post changes to IGNY8
- - `publish_post` - Updates keywords on publish
- - `transition_post_status` - Handles status changes
-
----
-
-## Usage
-
-### Basic Workflow
-
-#### 1. Connect to IGNY8 API
-
-```php
-// Automatically handled via Settings page
-// Or programmatically:
-$api = new Igny8API();
-$api->login('your@email.com', 'password');
-```
-
-#### 2. Sync WordPress Site Data
-
-```php
-// Collect and send site data to IGNY8
-$site_id = get_option('igny8_site_id');
-igny8_send_site_data_to_igny8($site_id);
-```
-
-#### 3. WordPress → IGNY8 Sync
-
-When you save/publish a WordPress post:
-
-1. Plugin checks for `_igny8_task_id` in post meta
-2. If found, syncs post status to IGNY8 task
-3. If published, updates related keywords to 'mapped' status
-
-#### 4. IGNY8 → WordPress Sync
-
-When content is published from IGNY8:
-
-1. IGNY8 triggers webhook (or scheduled sync)
-2. Plugin creates WordPress post via `wp_insert_post()`
-3. Post meta saved with `_igny8_task_id`
-4. IGNY8 task updated with WordPress post ID
-
----
-
-## WordPress Structures Used
-
-### Taxonomies
-
-- **`sectors`** (hierarchical)
- - Maps to IGNY8 Sectors
- - Can be created manually or synced from IGNY8
-
-- **`clusters`** (hierarchical)
- - Maps to IGNY8 Clusters
- - Can be created manually or synced from IGNY8
-
-- **Native Taxonomies**
- - `category` - Can map to IGNY8 Sectors
- - `post_tag` - Can be used for keyword extraction
-
-### Post Meta Fields
-
-All stored in WordPress `wp_postmeta` table:
-
-- `_igny8_task_id` (integer) - IGNY8 task ID
-- `_igny8_cluster_id` (integer) - IGNY8 cluster ID
-- `_igny8_sector_id` (integer) - IGNY8 sector ID
-- `_igny8_keyword_ids` (array) - Array of IGNY8 keyword IDs
-- `_igny8_content_id` (integer) - IGNY8 content ID
-- `_igny8_last_synced` (datetime) - Last sync timestamp
-
-### Post Status Mapping
-
-| WordPress Status | IGNY8 Task Status |
-|------------------|-------------------|
-| `publish` | `completed` |
-| `draft` | `draft` |
-| `pending` | `pending` |
-| `private` | `completed` |
-| `trash` | `archived` |
-| `future` | `scheduled` |
-
----
-
-## API Reference
-
-### Main Classes
-
-#### `Igny8API`
-
-Main API client class for all IGNY8 API interactions.
-
-```php
-$api = new Igny8API();
-
-// Login
-$api->login('email@example.com', 'password');
-
-// Get keywords
-$response = $api->get('/planner/keywords/');
-
-// Create task
-$response = $api->post('/writer/tasks/', $data);
-
-// Update task
-$response = $api->put('/writer/tasks/123/', $data);
-```
-
-#### `Igny8WordPressSync`
-
-Handles two-way synchronization between WordPress and IGNY8.
-
-```php
-$sync = new Igny8WordPressSync();
-// Automatically hooks into WordPress post actions
-```
-
-#### `Igny8SiteIntegration`
-
-Manages site data collection and semantic mapping.
-
-```php
-$integration = new Igny8SiteIntegration($site_id);
-$result = $integration->full_site_scan();
-```
-
-### Main Functions
-
-- `igny8_login($email, $password)` - Authenticate with IGNY8
-- `igny8_sync_post_status_to_igny8($post_id, $post, $update)` - Sync post to IGNY8
-- `igny8_collect_site_data()` - Collect all WordPress site data
-- `igny8_send_site_data_to_igny8($site_id)` - Send site data to IGNY8
-- `igny8_map_site_to_semantic_strategy($site_id, $site_data)` - Map to semantic structure
-
-### Site Metadata Endpoint (Plugin)
-
-The plugin exposes a discovery endpoint that your IGNY8 app can call to learn which WordPress post types and taxonomies exist and how many items each contains.
-
-- Endpoint: `GET /wp-json/igny8/v1/site-metadata/`
-- Auth: Plugin-level connection must be enabled and authenticated (plugin accepts stored API key or access token)
-- Response: IGNY8 unified response format (`success`, `data`, `message`, `request_id`)
-
-Example response:
-
-```json
-{
- "success": true,
- "data": {
- "post_types": {
- "post": { "label": "Posts", "count": 123 },
- "page": { "label": "Pages", "count": 12 }
- },
- "taxonomies": {
- "category": { "label": "Categories", "count": 25 },
- "post_tag": { "label": "Tags", "count": 102 }
- },
- "generated_at": 1700553600
- },
- "message": "Site metadata retrieved",
- "request_id": "550e8400-e29b-41d4-a716-446655440000"
-}
-```
-
----
-
-## File Structure
-
-```
-igny8-ai-os/
-├── igny8-bridge.php # Main plugin file
-├── README.md # This file
-├── docs/ # Documentation hub
-│ ├── README.md # Index of available docs
-│ ├── WORDPRESS-PLUGIN-INTEGRATION.md
-│ ├── wp-bridge-implementation-plan.md
-│ ├── missing-saas-api-endpoints.md
-│ ├── STATUS_SYNC_DOCUMENTATION.md
-│ └── STYLE_GUIDE.md
-├── includes/
-│ ├── class-igny8-api.php # API client class
-│ ├── class-igny8-sync.php # Sync handler class
-│ ├── class-igny8-site.php # Site integration class
-│ └── functions.php # Helper functions
-├── admin/
-│ ├── class-admin.php # Admin interface
-│ ├── settings.php # Settings page
-│ └── assets/
-│ ├── css/
-│ └── js/
-├── sync/
-│ ├── hooks.php # WordPress hooks
-│ ├── post-sync.php # Post synchronization
-│ └── taxonomy-sync.php # Taxonomy synchronization
-├── data/
-│ ├── site-collection.php # Site data collection
-│ └── semantic-mapping.php # Semantic mapping
-└── uninstall.php # Uninstall handler
-```
-
----
-
-## Development
-
-### Code Standards
-
-- Follow WordPress Coding Standards
-- Use WordPress native functions
-- No custom database tables
-- All data in WordPress native structures
-
-### Testing
-
-```bash
-# Run WordPress unit tests
-phpunit
-
-# Test API connection
-wp eval 'var_dump((new Igny8API())->login("test@example.com", "password"));'
-```
-
----
-
-## Troubleshooting
-
-### Authentication Issues
-
-**Problem**: Cannot connect to IGNY8 API
-
-**Solutions**:
-1. Verify email and password are correct
-2. Check API endpoint is accessible
-3. Check WordPress REST API is enabled
-4. Review error logs in WordPress debug log
-
-### Sync Issues
-
-**Problem**: Posts not syncing to IGNY8
-
-**Solutions**:
-1. Verify `_igny8_task_id` exists in post meta
-2. Check API token is valid (not expired)
-3. Review WordPress hooks are firing
-4. Check error logs
-
-### Token Expiration
-
-**Problem**: Token expires frequently
-
-**Solution**: Plugin automatically refreshes tokens. If issues persist, check token refresh logic.
-
----
-
-## Support
-
-- **Documentation**: See `WORDPRESS-PLUGIN-INTEGRATION.md`
-- **API Documentation**: https://api.igny8.com/docs
-- **Issues**: [GitHub Issues](repository-url/issues)
-
----
-
-## License
-
-[Your License Here]
-
----
-
-## Changelog
-
-### 1.0.0 - 2025-10-17
-- Initial release
-- API authentication
-- Two-way sync
-- Site data collection
-- Semantic mapping
-
----
-
-**Last Updated**: 2025-10-17
-
diff --git a/igny8-wp-integration-plugin/admin/assets/css/admin.css b/igny8-wp-integration-plugin/admin/assets/css/admin.css
deleted file mode 100644
index fa2ef1ac..00000000
--- a/igny8-wp-integration-plugin/admin/assets/css/admin.css
+++ /dev/null
@@ -1,347 +0,0 @@
-/**
- * Admin Styles
- *
- * All styles for IGNY8 Bridge admin interface
- * Update this file to change global design
- *
- * @package Igny8Bridge
- */
-
-/* ============================================
- Container & Layout
- ============================================ */
-
-.igny8-settings-container {
- max-width: 1200px;
-}
-
-.igny8-settings-card {
- background: #fff;
- border: 1px solid #ccd0d4;
- box-shadow: 0 1px 1px rgba(0,0,0,.04);
- padding: 20px;
- margin: 20px 0;
-}
-
-.igny8-settings-card h2 {
- margin-top: 0;
- padding-bottom: 10px;
- border-bottom: 1px solid #eee;
-}
-
-/* ============================================
- Status Indicators
- ============================================ */
-
-.igny8-status-connected {
- color: #46b450;
- font-weight: bold;
-}
-
-.igny8-status-disconnected {
- color: #dc3232;
- font-weight: bold;
-}
-
-.igny8-test-result {
- margin-left: 10px;
-}
-
-.igny8-test-result .igny8-success {
- color: #46b450;
-}
-
-.igny8-test-result .igny8-error {
- color: #dc3232;
-}
-
-.igny8-test-result .igny8-loading {
- color: #2271b1;
-}
-
-/* ============================================
- Sync Operations
- ============================================ */
-
-.igny8-sync-actions {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- margin-bottom: 20px;
-}
-
-.igny8-sync-actions .button {
- min-width: 150px;
-}
-
-.igny8-sync-status {
- margin-top: 15px;
- padding: 10px;
- border-radius: 4px;
- display: none;
-}
-
-.igny8-sync-status.igny8-sync-status-success {
- background-color: #d4edda;
- border: 1px solid #c3e6cb;
- color: #155724;
- display: block;
-}
-
-.igny8-sync-status.igny8-sync-status-error {
- background-color: #f8d7da;
- border: 1px solid #f5c6cb;
- color: #721c24;
- display: block;
-}
-
-.igny8-sync-status.igny8-sync-status-loading {
- background-color: #d1ecf1;
- border: 1px solid #bee5eb;
- color: #0c5460;
- display: block;
-}
-
-/* ============================================
- Statistics
- ============================================ */
-
-.igny8-stats-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 20px;
- margin-top: 15px;
-}
-
-.igny8-stat-item {
- padding: 15px;
- background: #f9f9f9;
- border: 1px solid #ddd;
- border-radius: 4px;
-}
-
-.igny8-stat-label {
- font-size: 12px;
- color: #666;
- text-transform: uppercase;
- margin-bottom: 8px;
-}
-
-.igny8-stat-value {
- font-size: 24px;
- font-weight: bold;
- color: #2271b1;
-}
-
-/* ============================================
- Diagnostics
- ============================================ */
-
-.igny8-diagnostics-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
- gap: 16px;
- margin-top: 15px;
-}
-
-.igny8-diagnostic-item {
- padding: 15px;
- background-color: #f6f7f7;
- border: 1px solid #dcdcde;
- border-radius: 4px;
-}
-
-.igny8-diagnostic-label {
- font-size: 11px;
- text-transform: uppercase;
- letter-spacing: 0.05em;
- color: #555d66;
- margin-bottom: 6px;
-}
-
-.igny8-diagnostic-value {
- font-size: 18px;
- font-weight: 600;
- color: #1d2327;
-}
-
-.igny8-diagnostic-item .description {
- margin: 6px 0 0;
- color: #646970;
-}
-
-/* ============================================
- Buttons
- ============================================ */
-
-.igny8-button-group {
- display: flex;
- gap: 10px;
- margin: 15px 0;
-}
-
-.igny8-button-group .button {
- flex: 1;
-}
-
-/* ============================================
- Loading States
- ============================================ */
-
-.igny8-loading {
- opacity: 0.6;
- pointer-events: none;
-}
-
-.igny8-spinner {
- display: inline-block;
- width: 16px;
- height: 16px;
- border: 2px solid #f3f3f3;
- border-top: 2px solid #2271b1;
- border-radius: 50%;
- animation: igny8-spin 1s linear infinite;
- margin-right: 8px;
- vertical-align: middle;
-}
-
-@keyframes igny8-spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
-}
-
-/* ============================================
- Messages & Notifications
- ============================================ */
-
-.igny8-message {
- padding: 12px;
- margin: 15px 0;
- border-left: 4px solid;
- background: #fff;
-}
-
-.igny8-message.igny8-message-success {
- border-color: #46b450;
- background-color: #f0f8f0;
-}
-
-.igny8-message.igny8-message-error {
- border-color: #dc3232;
- background-color: #fff5f5;
-}
-
-.igny8-message.igny8-message-info {
- border-color: #2271b1;
- background-color: #f0f6fc;
-}
-
-.igny8-message.igny8-message-warning {
- border-color: #f0b849;
- background-color: #fffbf0;
-}
-
-/* ============================================
- Tables
- ============================================ */
-
-.igny8-table {
- width: 100%;
- border-collapse: collapse;
- margin: 15px 0;
-}
-
-.igny8-table th,
-.igny8-table td {
- padding: 10px;
- text-align: left;
- border-bottom: 1px solid #ddd;
-}
-
-.igny8-table th {
- background-color: #f9f9f9;
- font-weight: 600;
-}
-
-.igny8-table tr:hover {
- background-color: #f9f9f9;
-}
-
-/* ============================================
- Admin Columns
- ============================================ */
-
-.igny8-badge {
- display: inline-block;
- padding: 3px 8px;
- border-radius: 3px;
- font-size: 11px;
- font-weight: 600;
- text-transform: uppercase;
- line-height: 1.4;
-}
-
-.igny8-badge-igny8 {
- background-color: #2271b1;
- color: #fff;
-}
-
-.igny8-badge-wordpress {
- background-color: #646970;
- color: #fff;
-}
-
-.igny8-terms-list {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
-}
-
-.igny8-term-badge {
- display: inline-block;
- padding: 2px 6px;
- background-color: #f0f0f1;
- border: 1px solid #c3c4c7;
- border-radius: 2px;
- font-size: 11px;
- color: #50575e;
-}
-
-.igny8-empty {
- color: #a7aaad;
- font-style: italic;
-}
-
-.igny8-action-link {
- color: #2271b1;
- text-decoration: none;
- cursor: pointer;
-}
-
-.igny8-action-link:hover {
- color: #135e96;
- text-decoration: underline;
-}
-
-/* ============================================
- Responsive
- ============================================ */
-
-@media (max-width: 782px) {
- .igny8-sync-actions {
- flex-direction: column;
- }
-
- .igny8-sync-actions .button {
- width: 100%;
- }
-
- .igny8-stats-grid {
- grid-template-columns: 1fr;
- }
-
- .igny8-diagnostics-grid {
- grid-template-columns: 1fr;
- }
-}
-
diff --git a/igny8-wp-integration-plugin/admin/assets/js/admin.js b/igny8-wp-integration-plugin/admin/assets/js/admin.js
deleted file mode 100644
index b6e0f306..00000000
--- a/igny8-wp-integration-plugin/admin/assets/js/admin.js
+++ /dev/null
@@ -1,178 +0,0 @@
-/**
- * Admin JavaScript
- *
- * @package Igny8Bridge
- */
-
-(function($) {
- 'use strict';
-
- $(document).ready(function() {
- // Test connection button
- $('#igny8-test-connection').on('click', function() {
- var $button = $(this);
- var $result = $('#igny8-test-result');
-
- $button.prop('disabled', true).addClass('igny8-loading');
- $result.html('Testing...');
-
- $.ajax({
- url: igny8Admin.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_test_connection',
- nonce: igny8Admin.nonce
- },
- success: function(response) {
- if (response.success) {
- $result.html('✓ Connection successful');
- } else {
- $result.html('✗ ' + (response.data.message || 'Connection failed') + '');
- }
- },
- error: function() {
- $result.html('✗ Request failed');
- },
- complete: function() {
- $button.prop('disabled', false).removeClass('igny8-loading');
- }
- });
- });
-
- // Sync posts to IGNY8
- $('#igny8-sync-posts').on('click', function() {
- igny8TriggerSync('igny8_sync_posts', 'Syncing posts to IGNY8...');
- });
-
- // Sync taxonomies
- $('#igny8-sync-taxonomies').on('click', function() {
- igny8TriggerSync('igny8_sync_taxonomies', 'Syncing taxonomies...');
- });
-
- // Sync from IGNY8
- $('#igny8-sync-from-igny8').on('click', function() {
- igny8TriggerSync('igny8_sync_from_igny8', 'Syncing from IGNY8...');
- });
-
- // Collect and send site data
- $('#igny8-collect-site-data').on('click', function() {
- igny8TriggerSync('igny8_collect_site_data', 'Collecting and sending site data...');
- });
-
- // Load sync statistics
- igny8LoadStats();
-
- // Handle row action links
- $(document).on('click', '.igny8-action-link', function(e) {
- e.preventDefault();
-
- var $link = $(this);
- var postId = $link.data('post-id');
- var action = $link.data('action');
-
- if (!postId) {
- return;
- }
-
- if (!confirm('Are you sure you want to ' + (action === 'send' ? 'send' : 'update') + ' this post to IGNY8?')) {
- return;
- }
-
- $link.text('Processing...').prop('disabled', true);
-
- $.ajax({
- url: igny8Admin.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_send_to_igny8',
- post_id: postId,
- action_type: action,
- nonce: igny8Admin.nonce
- },
- success: function(response) {
- if (response.success) {
- alert(response.data.message || 'Success!');
- location.reload();
- } else {
- alert(response.data.message || 'Failed to send to IGNY8');
- $link.text(action === 'send' ? 'Send to IGNY8' : 'Update in IGNY8').prop('disabled', false);
- }
- },
- error: function() {
- alert('Request failed');
- $link.text(action === 'send' ? 'Send to IGNY8' : 'Update in IGNY8').prop('disabled', false);
- }
- });
- });
- });
-
- /**
- * Trigger sync operation
- */
- function igny8TriggerSync(action, message) {
- var $status = $('#igny8-sync-status');
- var $button = $('#' + action.replace('igny8_', 'igny8-'));
-
- $status.removeClass('igny8-sync-status-success igny8-sync-status-error')
- .addClass('igny8-sync-status-loading')
- .html('' + message);
-
- $button.prop('disabled', true).addClass('igny8-loading');
-
- $.ajax({
- url: igny8Admin.ajaxUrl,
- type: 'POST',
- data: {
- action: action,
- nonce: igny8Admin.nonce
- },
- success: function(response) {
- if (response.success) {
- $status.removeClass('igny8-sync-status-loading')
- .addClass('igny8-sync-status-success')
- .html('✓ ' + (response.data.message || 'Operation completed successfully'));
-
- // Reload stats
- igny8LoadStats();
- } else {
- $status.removeClass('igny8-sync-status-loading')
- .addClass('igny8-sync-status-error')
- .html('✗ ' + (response.data.message || 'Operation failed'));
- }
- },
- error: function() {
- $status.removeClass('igny8-sync-status-loading')
- .addClass('igny8-sync-status-error')
- .html('✗ Request failed');
- },
- complete: function() {
- $button.prop('disabled', false).removeClass('igny8-loading');
- }
- });
- }
-
- /**
- * Load sync statistics
- */
- function igny8LoadStats() {
- $.ajax({
- url: igny8Admin.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_get_stats',
- nonce: igny8Admin.nonce
- },
- success: function(response) {
- if (response.success && response.data) {
- if (response.data.synced_posts !== undefined) {
- $('#igny8-stat-posts').text(response.data.synced_posts);
- }
- if (response.data.last_sync) {
- $('#igny8-stat-last-sync').text(response.data.last_sync);
- }
- }
- }
- });
- }
-})(jQuery);
-
diff --git a/igny8-wp-integration-plugin/admin/assets/js/post-editor.js b/igny8-wp-integration-plugin/admin/assets/js/post-editor.js
deleted file mode 100644
index 2be154c1..00000000
--- a/igny8-wp-integration-plugin/admin/assets/js/post-editor.js
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- * Post Editor JavaScript
- *
- * Handles AJAX interactions for Planner and Optimizer meta boxes
- *
- * @package Igny8Bridge
- */
-
-(function($) {
- 'use strict';
-
- $(document).ready(function() {
- // Fetch Planner Brief
- $('#igny8-fetch-brief').on('click', function() {
- var $button = $(this);
- var $message = $('#igny8-planner-brief-message');
- var postId = $button.data('post-id');
- var taskId = $button.data('task-id');
-
- $button.prop('disabled', true).text('Fetching...');
- $message.hide().removeClass('notice-success notice-error');
-
- $.ajax({
- url: igny8PostEditor.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_fetch_planner_brief',
- nonce: igny8PostEditor.nonce,
- post_id: postId,
- task_id: taskId
- },
- success: function(response) {
- if (response.success) {
- $message.addClass('notice notice-success inline')
- .html('
' + response.data.message + '
')
- .show();
-
- // Reload page to show updated brief
- setTimeout(function() {
- location.reload();
- }, 1000);
- } else {
- $message.addClass('notice notice-error inline')
- .html('' + (response.data.message || 'Failed to fetch brief') + '
')
- .show();
- $button.prop('disabled', false).text('Fetch Brief');
- }
- },
- error: function() {
- $message.addClass('notice notice-error inline')
- .html('Request failed
')
- .show();
- $button.prop('disabled', false).text('Fetch Brief');
- }
- });
- });
-
- // Refresh Planner Task
- $('#igny8-refresh-task').on('click', function() {
- var $button = $(this);
- var $message = $('#igny8-planner-brief-message');
- var postId = $button.data('post-id');
- var taskId = $button.data('task-id');
-
- if (!confirm('Are you sure you want to request a refresh of this task from IGNY8 Planner?')) {
- return;
- }
-
- $button.prop('disabled', true).text('Requesting...');
- $message.hide().removeClass('notice-success notice-error');
-
- $.ajax({
- url: igny8PostEditor.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_refresh_planner_task',
- nonce: igny8PostEditor.nonce,
- post_id: postId,
- task_id: taskId
- },
- success: function(response) {
- if (response.success) {
- $message.addClass('notice notice-success inline')
- .html('' + response.data.message + '
')
- .show();
- } else {
- $message.addClass('notice notice-error inline')
- .html('' + (response.data.message || 'Failed to request refresh') + '
')
- .show();
- }
- $button.prop('disabled', false).text('Request Refresh');
- },
- error: function() {
- $message.addClass('notice notice-error inline')
- .html('Request failed
')
- .show();
- $button.prop('disabled', false).text('Request Refresh');
- }
- });
- });
-
- // Create Optimizer Job
- $('#igny8-create-optimizer-job').on('click', function() {
- var $button = $(this);
- var $message = $('#igny8-optimizer-message');
- var postId = $button.data('post-id');
- var taskId = $button.data('task-id');
-
- if (!confirm('Create a new optimizer job for this post?')) {
- return;
- }
-
- $button.prop('disabled', true).text('Creating...');
- $message.hide().removeClass('notice-success notice-error');
-
- $.ajax({
- url: igny8PostEditor.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_create_optimizer_job',
- nonce: igny8PostEditor.nonce,
- post_id: postId,
- task_id: taskId,
- job_type: 'audit',
- priority: 'normal'
- },
- success: function(response) {
- if (response.success) {
- $message.addClass('notice notice-success inline')
- .html('' + response.data.message + '
')
- .show();
-
- // Reload page to show updated status
- setTimeout(function() {
- location.reload();
- }, 1000);
- } else {
- $message.addClass('notice notice-error inline')
- .html('' + (response.data.message || 'Failed to create job') + '
')
- .show();
- $button.prop('disabled', false).text('Request Optimization');
- }
- },
- error: function() {
- $message.addClass('notice notice-error inline')
- .html('Request failed
')
- .show();
- $button.prop('disabled', false).text('Request Optimization');
- }
- });
- });
-
- // Check Optimizer Status
- $('#igny8-check-optimizer-status').on('click', function() {
- var $button = $(this);
- var $message = $('#igny8-optimizer-message');
- var postId = $button.data('post-id');
- var jobId = $button.data('job-id');
-
- $button.prop('disabled', true).text('Checking...');
- $message.hide().removeClass('notice-success notice-error');
-
- $.ajax({
- url: igny8PostEditor.ajaxUrl,
- type: 'POST',
- data: {
- action: 'igny8_get_optimizer_status',
- nonce: igny8PostEditor.nonce,
- post_id: postId,
- job_id: jobId
- },
- success: function(response) {
- if (response.success) {
- $message.addClass('notice notice-success inline')
- .html('Status: ' + response.data.status + '
')
- .show();
-
- // Reload page to show updated status
- setTimeout(function() {
- location.reload();
- }, 1000);
- } else {
- $message.addClass('notice notice-error inline')
- .html('' + (response.data.message || 'Failed to get status') + '
')
- .show();
- }
- $button.prop('disabled', false).text('Check Status');
- },
- error: function() {
- $message.addClass('notice notice-error inline')
- .html('Request failed
')
- .show();
- $button.prop('disabled', false).text('Check Status');
- }
- });
- });
- });
-
-})(jQuery);
-
diff --git a/igny8-wp-integration-plugin/admin/class-admin-columns.php b/igny8-wp-integration-plugin/admin/class-admin-columns.php
deleted file mode 100644
index 59f9de5b..00000000
--- a/igny8-wp-integration-plugin/admin/class-admin-columns.php
+++ /dev/null
@@ -1,335 +0,0 @@
- $value) {
- $new_columns[$key] = $value;
-
- if ($key === 'title') {
- $new_columns['igny8_source'] = __('Source', 'igny8-bridge');
- $new_columns['igny8_sectors'] = __('Sectors', 'igny8-bridge');
- $new_columns['igny8_clusters'] = __('Clusters', 'igny8-bridge');
- }
- }
-
- return $new_columns;
- }
-
- /**
- * Render column content
- *
- * @param string $column_name Column name
- * @param int $post_id Post ID
- */
- public function render_column_content($column_name, $post_id) {
- switch ($column_name) {
- case 'igny8_source':
- $this->render_source_column($post_id);
- break;
-
- case 'igny8_sectors':
- $this->render_sectors_column($post_id);
- break;
-
- case 'igny8_clusters':
- $this->render_clusters_column($post_id);
- break;
- }
- }
-
- /**
- * Render source column
- *
- * @param int $post_id Post ID
- */
- private function render_source_column($post_id) {
- $task_id = get_post_meta($post_id, '_igny8_task_id', true);
-
- if ($task_id) {
- echo '';
- echo esc_html__('IGNY8', 'igny8-bridge');
- echo '';
- } else {
- echo '';
- echo esc_html__('WP', 'igny8-bridge');
- echo '';
- }
- }
-
- /**
- * Render sectors column
- *
- * @param int $post_id Post ID
- */
- private function render_sectors_column($post_id) {
- $sectors = wp_get_post_terms($post_id, 'igny8_sectors', array('fields' => 'names'));
-
- if (!empty($sectors) && !is_wp_error($sectors)) {
- echo '';
- foreach ($sectors as $sector) {
- echo '' . esc_html($sector) . '';
- }
- echo '
';
- } else {
- echo '—';
- }
- }
-
- /**
- * Render clusters column
- *
- * @param int $post_id Post ID
- */
- private function render_clusters_column($post_id) {
- $clusters = wp_get_post_terms($post_id, 'igny8_clusters', array('fields' => 'names'));
-
- if (!empty($clusters) && !is_wp_error($clusters)) {
- echo '';
- foreach ($clusters as $cluster) {
- echo '' . esc_html($cluster) . '';
- }
- echo '
';
- } else {
- echo '—';
- }
- }
-
- /**
- * Make columns sortable
- *
- * @param array $columns Sortable columns
- * @return array Modified columns
- */
- public function make_columns_sortable($columns) {
- $columns['igny8_source'] = 'igny8_source';
- return $columns;
- }
-
- /**
- * Add row actions
- *
- * @param array $actions Existing actions
- * @param WP_Post $post Post object
- * @return array Modified actions
- */
- public function add_row_actions($actions, $post) {
- // Only add for published posts
- if ($post->post_status !== 'publish') {
- return $actions;
- }
-
- // Check if already synced to IGNY8
- $task_id = get_post_meta($post->ID, '_igny8_task_id', true);
-
- if ($task_id) {
- // Already synced - show update action
- $actions['igny8_update'] = sprintf(
- '%s',
- '#',
- $post->ID,
- __('Update in IGNY8', 'igny8-bridge')
- );
- } else {
- // Not synced - show send action
- $actions['igny8_send'] = sprintf(
- '%s',
- '#',
- $post->ID,
- __('Send to IGNY8', 'igny8-bridge')
- );
- }
-
- return $actions;
- }
-
- /**
- * Send post to IGNY8 (AJAX handler)
- */
- public static function send_to_igny8() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('edit_posts')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
- $action = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : 'send';
-
- if (!$post_id) {
- wp_send_json_error(array('message' => 'Invalid post ID'));
- }
-
- $post = get_post($post_id);
- if (!$post) {
- wp_send_json_error(array('message' => 'Post not found'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated with IGNY8'));
- }
-
- $site_id = get_option('igny8_site_id');
- if (!$site_id) {
- wp_send_json_error(array('message' => 'Site ID not set'));
- }
-
- // Prepare post data for IGNY8
- $post_data = array(
- 'title' => $post->post_title,
- 'content' => $post->post_content,
- 'excerpt' => $post->post_excerpt,
- 'status' => $post->post_status === 'publish' ? 'completed' : 'draft',
- 'post_type' => $post->post_type,
- 'url' => get_permalink($post_id),
- 'wordpress_post_id' => $post_id
- );
-
- // Get categories
- $categories = wp_get_post_categories($post_id, array('fields' => 'names'));
- if (!empty($categories)) {
- $post_data['categories'] = $categories;
- }
-
- // Get tags
- $tags = wp_get_post_tags($post_id, array('fields' => 'names'));
- if (!empty($tags)) {
- $post_data['tags'] = $tags;
- }
-
- // Get featured image
- $featured_image_id = get_post_thumbnail_id($post_id);
- if ($featured_image_id) {
- $post_data['featured_image'] = wp_get_attachment_image_url($featured_image_id, 'full');
- }
-
- // Get sectors and clusters
- $sectors = wp_get_post_terms($post_id, 'igny8_sectors', array('fields' => 'ids'));
- $clusters = wp_get_post_terms($post_id, 'igny8_clusters', array('fields' => 'ids'));
-
- if (!empty($sectors)) {
- // Get IGNY8 sector IDs from term meta
- $igny8_sector_ids = array();
- foreach ($sectors as $term_id) {
- $igny8_sector_id = get_term_meta($term_id, '_igny8_sector_id', true);
- if ($igny8_sector_id) {
- $igny8_sector_ids[] = $igny8_sector_id;
- }
- }
- if (!empty($igny8_sector_ids)) {
- $post_data['sector_id'] = $igny8_sector_ids[0]; // Use first sector
- }
- }
-
- if (!empty($clusters)) {
- // Get IGNY8 cluster IDs from term meta
- $igny8_cluster_ids = array();
- foreach ($clusters as $term_id) {
- $igny8_cluster_id = get_term_meta($term_id, '_igny8_cluster_id', true);
- if ($igny8_cluster_id) {
- $igny8_cluster_ids[] = $igny8_cluster_id;
- }
- }
- if (!empty($igny8_cluster_ids)) {
- $post_data['cluster_id'] = $igny8_cluster_ids[0]; // Use first cluster
- }
- }
-
- // Check if post already has task ID
- $existing_task_id = get_post_meta($post_id, '_igny8_task_id', true);
-
- if ($existing_task_id && $action === 'update') {
- // Update existing task
- $response = $api->put("/writer/tasks/{$existing_task_id}/", $post_data);
- } else {
- // Create new task
- $response = $api->post("/writer/tasks/", $post_data);
- }
-
- if ($response['success']) {
- $task_id = $response['data']['id'] ?? $existing_task_id;
-
- // Store task ID
- update_post_meta($post_id, '_igny8_task_id', $task_id);
- update_post_meta($post_id, '_igny8_last_synced', current_time('mysql'));
-
- wp_send_json_success(array(
- 'message' => $action === 'update' ? 'Post updated in IGNY8' : 'Post sent to IGNY8',
- 'task_id' => $task_id
- ));
- } else {
- wp_send_json_error(array(
- 'message' => 'Failed to send to IGNY8: ' . ($response['error'] ?? 'Unknown error')
- ));
- }
- }
-}
-
-// Initialize
-new Igny8AdminColumns();
-
-// Register AJAX handler
-add_action('wp_ajax_igny8_send_to_igny8', array('Igny8AdminColumns', 'send_to_igny8'));
-
diff --git a/igny8-wp-integration-plugin/admin/class-admin.php b/igny8-wp-integration-plugin/admin/class-admin.php
deleted file mode 100644
index ed1f4b00..00000000
--- a/igny8-wp-integration-plugin/admin/class-admin.php
+++ /dev/null
@@ -1,566 +0,0 @@
- 'boolean',
- 'sanitize_callback' => array($this, 'sanitize_boolean'),
- 'default' => 1
- ));
-
- register_setting('igny8_bridge_connection', 'igny8_connection_enabled', array(
- 'type' => 'boolean',
- 'sanitize_callback' => array($this, 'sanitize_boolean'),
- 'default' => 1
- ));
-
- register_setting('igny8_bridge_controls', 'igny8_enabled_post_types', array(
- 'type' => 'array',
- 'sanitize_callback' => array($this, 'sanitize_post_types'),
- 'default' => array_keys(igny8_get_supported_post_types())
- ));
-
- register_setting('igny8_bridge_controls', 'igny8_enable_woocommerce', array(
- 'type' => 'boolean',
- 'sanitize_callback' => array($this, 'sanitize_boolean'),
- 'default' => class_exists('WooCommerce') ? 1 : 0
- ));
-
- register_setting('igny8_bridge_controls', 'igny8_control_mode', array(
- 'type' => 'string',
- 'sanitize_callback' => array($this, 'sanitize_control_mode'),
- 'default' => 'mirror'
- ));
-
- register_setting('igny8_bridge_controls', 'igny8_enabled_modules', array(
- 'type' => 'array',
- 'sanitize_callback' => array($this, 'sanitize_modules'),
- 'default' => array_keys(igny8_get_available_modules())
- ));
- }
-
- /**
- * Enqueue admin scripts and styles
- *
- * @param string $hook Current admin page hook
- */
- public function enqueue_scripts($hook) {
- // Enqueue on settings page
- if ($hook === 'settings_page_igny8-settings') {
- wp_enqueue_style(
- 'igny8-admin-style',
- IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
- array(),
- IGNY8_BRIDGE_VERSION
- );
-
- wp_enqueue_script(
- 'igny8-admin-script',
- IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/admin.js',
- array('jquery'),
- IGNY8_BRIDGE_VERSION,
- true
- );
-
- wp_localize_script('igny8-admin-script', 'igny8Admin', array(
- 'ajaxUrl' => admin_url('admin-ajax.php'),
- 'nonce' => wp_create_nonce('igny8_admin_nonce'),
- ));
- }
-
- // Enqueue on post/page/product list pages
- if (strpos($hook, 'edit.php') !== false) {
- $screen = get_current_screen();
- if ($screen && in_array($screen->post_type, array('post', 'page', 'product', ''))) {
- wp_enqueue_style(
- 'igny8-admin-style',
- IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
- array(),
- IGNY8_BRIDGE_VERSION
- );
-
- wp_enqueue_script(
- 'igny8-admin-script',
- IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/js/admin.js',
- array('jquery'),
- IGNY8_BRIDGE_VERSION,
- true
- );
-
- wp_localize_script('igny8-admin-script', 'igny8Admin', array(
- 'ajaxUrl' => admin_url('admin-ajax.php'),
- 'nonce' => wp_create_nonce('igny8_admin_nonce'),
- ));
- }
- }
- }
-
- /**
- * Render settings page
- */
- public function render_settings_page() {
- // Handle form submission (use wp_verify_nonce to avoid wp_die on failure)
- if (isset($_POST['igny8_connect'])) {
- if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_settings_nonce')) {
- add_settings_error(
- 'igny8_settings',
- 'igny8_nonce',
- __('Security check failed. Please refresh the page and try again.', 'igny8-bridge'),
- 'error'
- );
- } else {
- $this->handle_connection();
- }
- }
-
- // Handle revoke API key (use wp_verify_nonce)
- if (isset($_POST['igny8_revoke_api_key'])) {
- if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_revoke_api_key')) {
- add_settings_error(
- 'igny8_settings',
- 'igny8_nonce_revoke',
- __('Security check failed. Could not revoke API key.', 'igny8-bridge'),
- 'error'
- );
- } else {
- self::revoke_api_key();
- add_settings_error(
- 'igny8_settings',
- 'igny8_api_key_revoked',
- __('API key revoked and removed from this site.', 'igny8-bridge'),
- 'updated'
- );
- }
- }
-
- // Handle webhook secret regeneration (use wp_verify_nonce)
- if (isset($_POST['igny8_regenerate_secret'])) {
- if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_regenerate_secret')) {
- add_settings_error(
- 'igny8_settings',
- 'igny8_nonce_regen',
- __('Security check failed. Could not regenerate secret.', 'igny8-bridge'),
- 'error'
- );
- } else {
- $new_secret = igny8_regenerate_webhook_secret();
- add_settings_error(
- 'igny8_settings',
- 'igny8_secret_regenerated',
- __('Webhook secret regenerated. Update it in your IGNY8 SaaS app settings.', 'igny8-bridge'),
- 'updated'
- );
- }
- }
-
- // Include settings template
- include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
- }
-
- /**
- * Handle API connection
- */
- private function handle_connection() {
- $email = sanitize_email($_POST['igny8_email'] ?? '');
- $password = $_POST['igny8_password'] ?? '';
- $api_key = sanitize_text_field($_POST['igny8_api_key'] ?? '');
-
- // Require email, password AND API key per updated policy
- if (empty($email) || empty($password) || empty($api_key)) {
- add_settings_error(
- 'igny8_settings',
- 'igny8_error',
- __('Email, password and API key are all required to establish the connection.', 'igny8-bridge'),
- 'error'
- );
- return;
- }
-
- // First, attempt login with email/password
- $api = new Igny8API();
-
- if (!$api->login($email, $password)) {
- add_settings_error(
- 'igny8_settings',
- 'igny8_error',
- __('Failed to connect to IGNY8 API with provided credentials.', 'igny8-bridge'),
- 'error'
- );
- return;
- }
-
- // Store email
- update_option('igny8_email', $email);
-
- // Store API key securely and also set access token to the API key for subsequent calls if desired
- if (function_exists('igny8_store_secure_option')) {
- igny8_store_secure_option('igny8_api_key', $api_key);
- igny8_store_secure_option('igny8_access_token', $api_key);
- } else {
- update_option('igny8_api_key', $api_key);
- update_option('igny8_access_token', $api_key);
- }
-
- // Try to get site ID (if available) using the authenticated client
- $site_response = $api->get('/system/sites/');
- if ($site_response['success'] && !empty($site_response['results'])) {
- $site = $site_response['results'][0];
- update_option('igny8_site_id', $site['id']);
- }
-
- add_settings_error(
- 'igny8_settings',
- 'igny8_connected',
- __('Successfully connected to IGNY8 API and stored API key.', 'igny8-bridge'),
- 'updated'
- );
- }
-
- /**
- * Revoke stored API key (secure delete)
- *
- * Public so tests can call it directly.
- */
- public static function revoke_api_key() {
- if (function_exists('igny8_delete_secure_option')) {
- igny8_delete_secure_option('igny8_api_key');
- igny8_delete_secure_option('igny8_access_token');
- igny8_delete_secure_option('igny8_refresh_token');
- } else {
- delete_option('igny8_api_key');
- delete_option('igny8_access_token');
- delete_option('igny8_refresh_token');
- }
-
- // Also clear token-issued timestamps
- delete_option('igny8_token_refreshed_at');
- delete_option('igny8_access_token_issued');
- }
-
- /**
- * Test API connection (AJAX handler)
- */
- public static function test_connection() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('manage_options')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations to test.'));
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated'));
- }
-
- // Test with a simple API call
- $response = $api->get('/planner/keywords/?page_size=1');
-
- if ($response['success']) {
- $checked_at = current_time('timestamp');
- update_option('igny8_last_api_health_check', $checked_at);
- wp_send_json_success(array(
- 'message' => 'Connection successful',
- 'data' => $response,
- 'checked_at' => $checked_at
- ));
- } else {
- wp_send_json_error(array(
- 'message' => 'Connection failed',
- 'error' => $response['error']
- ));
- }
- }
-
- /**
- * Sync posts to IGNY8 (AJAX handler)
- */
- public static function sync_posts() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('manage_options')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $result = igny8_batch_sync_post_statuses();
-
- wp_send_json_success(array(
- 'message' => sprintf('Synced %d posts, %d failed', $result['synced'], $result['failed']),
- 'data' => $result
- ));
- }
-
- /**
- * Sync taxonomies (AJAX handler)
- */
- public static function sync_taxonomies() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('manage_options')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $api = new Igny8API();
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated'));
- }
-
- // Sync sectors and clusters from IGNY8
- $sectors_result = igny8_sync_igny8_sectors_to_wp();
- $clusters_result = igny8_sync_igny8_clusters_to_wp();
-
- wp_send_json_success(array(
- 'message' => sprintf('Synced %d sectors, %d clusters',
- $sectors_result['synced'] ?? 0,
- $clusters_result['synced'] ?? 0
- ),
- 'data' => array(
- 'sectors' => $sectors_result,
- 'clusters' => $clusters_result
- )
- ));
- }
-
- /**
- * Sync from IGNY8 (AJAX handler)
- */
- public static function sync_from_igny8() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('manage_options')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $result = igny8_sync_igny8_tasks_to_wp();
-
- if ($result['success']) {
- wp_send_json_success(array(
- 'message' => sprintf('Created %d posts, updated %d posts',
- $result['created'],
- $result['updated']
- ),
- 'data' => $result
- ));
- } else {
- wp_send_json_error(array(
- 'message' => $result['error'] ?? 'Sync failed'
- ));
- }
- }
-
- /**
- * Collect and send site data (AJAX handler)
- */
- public static function collect_site_data() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('manage_options')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $site_id = get_option('igny8_site_id');
- if (!$site_id) {
- wp_send_json_error(array('message' => 'Site ID not set'));
- }
-
- $result = igny8_send_site_data_to_igny8($site_id);
-
- if ($result) {
- wp_send_json_success(array(
- 'message' => 'Site data collected and sent successfully',
- 'data' => $result
- ));
- } else {
- wp_send_json_error(array('message' => 'Failed to send site data'));
- }
- }
-
- /**
- * Get sync statistics (AJAX handler)
- */
- public static function get_stats() {
- check_ajax_referer('igny8_admin_nonce', 'nonce');
-
- if (!current_user_can('manage_options')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- global $wpdb;
-
- // Count synced posts
- $synced_posts = $wpdb->get_var("
- SELECT COUNT(DISTINCT post_id)
- FROM {$wpdb->postmeta}
- WHERE meta_key = '_igny8_task_id'
- ");
-
- // Get last sync time
- $last_sync = get_option('igny8_last_site_sync', 0);
- $last_sync_formatted = $last_sync ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $last_sync) : 'Never';
-
- wp_send_json_success(array(
- 'synced_posts' => intval($synced_posts),
- 'last_sync' => $last_sync_formatted
- ));
- }
-
- /**
- * Sanitize post types option
- *
- * @param mixed $value Raw value
- * @return array
- */
- public function sanitize_post_types($value) {
- $supported = array_keys(igny8_get_supported_post_types());
-
- if (!is_array($value)) {
- return $supported;
- }
-
- $clean = array();
- foreach ($value as $post_type) {
- $post_type = sanitize_key($post_type);
- if (in_array($post_type, $supported, true)) {
- $clean[] = $post_type;
- }
- }
-
- return !empty($clean) ? $clean : $supported;
- }
-
- /**
- * Sanitize boolean option
- *
- * @param mixed $value Raw value
- * @return int
- */
- public function sanitize_boolean($value) {
- return $value ? 1 : 0;
- }
-
- /**
- * Sanitize control mode
- *
- * @param mixed $value Raw value
- * @return string
- */
- public function sanitize_control_mode($value) {
- $value = is_string($value) ? strtolower($value) : 'mirror';
- return in_array($value, array('mirror', 'hybrid'), true) ? $value : 'mirror';
- }
-
- /**
- * Sanitize module toggles
- *
- * @param mixed $value Raw value
- * @return array
- */
- public function sanitize_modules($value) {
- $supported = array_keys(igny8_get_available_modules());
-
- if (!is_array($value)) {
- return $supported;
- }
-
- $clean = array();
- foreach ($value as $module) {
- $module = sanitize_key($module);
- if (in_array($module, $supported, true)) {
- $clean[] = $module;
- }
- }
-
- return !empty($clean) ? $clean : $supported;
- }
-}
-
-// Register AJAX handlers
-add_action('wp_ajax_igny8_test_connection', array('Igny8Admin', 'test_connection'));
-add_action('wp_ajax_igny8_sync_posts', array('Igny8Admin', 'sync_posts'));
-add_action('wp_ajax_igny8_sync_taxonomies', array('Igny8Admin', 'sync_taxonomies'));
-add_action('wp_ajax_igny8_sync_from_igny8', array('Igny8Admin', 'sync_from_igny8'));
-add_action('wp_ajax_igny8_collect_site_data', array('Igny8Admin', 'collect_site_data'));
-add_action('wp_ajax_igny8_get_stats', array('Igny8Admin', 'get_stats'));
-
diff --git a/igny8-wp-integration-plugin/admin/class-post-meta-boxes.php b/igny8-wp-integration-plugin/admin/class-post-meta-boxes.php
deleted file mode 100644
index d0d6ebcb..00000000
--- a/igny8-wp-integration-plugin/admin/class-post-meta-boxes.php
+++ /dev/null
@@ -1,469 +0,0 @@
- admin_url('admin-ajax.php'),
- 'nonce' => wp_create_nonce('igny8_post_editor_nonce'),
- ));
- }
-
- /**
- * Render Planner Brief meta box
- */
- public function render_planner_brief_box($post) {
- $task_id = get_post_meta($post->ID, '_igny8_task_id', true);
- $brief = get_post_meta($post->ID, '_igny8_task_brief', true);
- $brief_cached_at = get_post_meta($post->ID, '_igny8_brief_cached_at', true);
- $cluster_id = get_post_meta($post->ID, '_igny8_cluster_id', true);
-
- if (!$task_id && !$cluster_id) {
- echo '';
- _e('This post is not linked to an IGNY8 task or cluster.', 'igny8-bridge');
- echo '
';
- return;
- }
-
- wp_nonce_field('igny8_post_editor_nonce', 'igny8_post_editor_nonce');
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ';
- foreach ($keywords as $keyword) {
- echo '' . esc_html(trim($keyword)) . '';
- }
- echo '';
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ID, '_igny8_task_id', true);
- $optimizer_job_id = get_post_meta($post->ID, '_igny8_optimizer_job_id', true);
- $optimizer_status = get_post_meta($post->ID, '_igny8_optimizer_status', true);
-
- if (!$task_id) {
- echo '';
- _e('This post is not linked to an IGNY8 task.', 'igny8-bridge');
- echo '
';
- return;
- }
-
- wp_nonce_field('igny8_post_editor_nonce', 'igny8_post_editor_nonce');
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
- $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
-
- if (!$post_id || !$task_id) {
- wp_send_json_error(array('message' => 'Invalid post ID or task ID'));
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated'));
- }
-
- // Try to fetch from Planner first
- $response = $api->get("/planner/tasks/{$task_id}/brief/");
-
- if (!$response['success']) {
- // Fallback to Writer brief
- $response = $api->get("/writer/tasks/{$task_id}/brief/");
- }
-
- if ($response['success'] && !empty($response['data'])) {
- update_post_meta($post_id, '_igny8_task_brief', $response['data']);
- update_post_meta($post_id, '_igny8_brief_cached_at', current_time('mysql'));
-
- wp_send_json_success(array(
- 'message' => 'Brief fetched successfully',
- 'brief' => $response['data']
- ));
- } else {
- wp_send_json_error(array(
- 'message' => 'Failed to fetch brief: ' . ($response['error'] ?? 'Unknown error')
- ));
- }
- }
-
- /**
- * Refresh Planner task (AJAX handler)
- */
- public static function refresh_planner_task() {
- check_ajax_referer('igny8_post_editor_nonce', 'nonce');
-
- if (!current_user_can('edit_posts')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
- $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
-
- if (!$post_id || !$task_id) {
- wp_send_json_error(array('message' => 'Invalid post ID or task ID'));
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated'));
- }
-
- $response = $api->post("/planner/tasks/{$task_id}/refresh/", array(
- 'wordpress_post_id' => $post_id,
- 'reason' => 'reoptimize',
- 'notes' => 'Requested refresh from WordPress editor'
- ));
-
- if ($response['success']) {
- wp_send_json_success(array(
- 'message' => 'Refresh requested successfully',
- 'data' => $response['data']
- ));
- } else {
- wp_send_json_error(array(
- 'message' => 'Failed to request refresh: ' . ($response['error'] ?? 'Unknown error')
- ));
- }
- }
-
- /**
- * Create Optimizer job (AJAX handler)
- */
- public static function create_optimizer_job() {
- check_ajax_referer('igny8_post_editor_nonce', 'nonce');
-
- if (!current_user_can('edit_posts')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
- $task_id = isset($_POST['task_id']) ? intval($_POST['task_id']) : 0;
- $job_type = isset($_POST['job_type']) ? sanitize_text_field($_POST['job_type']) : 'audit';
- $priority = isset($_POST['priority']) ? sanitize_text_field($_POST['priority']) : 'normal';
-
- if (!$post_id || !$task_id) {
- wp_send_json_error(array('message' => 'Invalid post ID or task ID'));
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated'));
- }
-
- $response = $api->post("/optimizer/jobs/", array(
- 'post_id' => $post_id,
- 'task_id' => $task_id,
- 'job_type' => $job_type,
- 'priority' => $priority
- ));
-
- if ($response['success'] && !empty($response['data'])) {
- $job_id = $response['data']['id'] ?? $response['data']['job_id'] ?? null;
-
- if ($job_id) {
- update_post_meta($post_id, '_igny8_optimizer_job_id', $job_id);
- update_post_meta($post_id, '_igny8_optimizer_status', $response['data']['status'] ?? 'pending');
- update_post_meta($post_id, '_igny8_optimizer_job_created_at', current_time('mysql'));
- }
-
- wp_send_json_success(array(
- 'message' => 'Optimizer job created successfully',
- 'job_id' => $job_id,
- 'data' => $response['data']
- ));
- } else {
- wp_send_json_error(array(
- 'message' => 'Failed to create optimizer job: ' . ($response['error'] ?? 'Unknown error')
- ));
- }
- }
-
- /**
- * Get Optimizer job status (AJAX handler)
- */
- public static function get_optimizer_status() {
- check_ajax_referer('igny8_post_editor_nonce', 'nonce');
-
- if (!current_user_can('edit_posts')) {
- wp_send_json_error(array('message' => 'Unauthorized'));
- }
-
- if (!igny8_is_connection_enabled()) {
- wp_send_json_error(array('message' => 'Connection is disabled. Enable sync operations first.'));
- }
-
- $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
- $job_id = isset($_POST['job_id']) ? intval($_POST['job_id']) : 0;
-
- if (!$post_id || !$job_id) {
- wp_send_json_error(array('message' => 'Invalid post ID or job ID'));
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- wp_send_json_error(array('message' => 'Not authenticated'));
- }
-
- $response = $api->get("/optimizer/jobs/{$job_id}/");
-
- if ($response['success'] && !empty($response['data'])) {
- $status = $response['data']['status'] ?? 'unknown';
- update_post_meta($post_id, '_igny8_optimizer_status', $status);
-
- if (!empty($response['data']['score_changes'])) {
- update_post_meta($post_id, '_igny8_optimizer_score_changes', $response['data']['score_changes']);
- }
-
- if (!empty($response['data']['recommendations'])) {
- update_post_meta($post_id, '_igny8_optimizer_recommendations', $response['data']['recommendations']);
- }
-
- wp_send_json_success(array(
- 'message' => 'Status retrieved successfully',
- 'status' => $status,
- 'data' => $response['data']
- ));
- } else {
- wp_send_json_error(array(
- 'message' => 'Failed to get status: ' . ($response['error'] ?? 'Unknown error')
- ));
- }
- }
-}
-
-// Initialize
-new Igny8PostMetaBoxes();
-
diff --git a/igny8-wp-integration-plugin/admin/settings.php b/igny8-wp-integration-plugin/admin/settings.php
deleted file mode 100644
index 59c6b358..00000000
--- a/igny8-wp-integration-plugin/admin/settings.php
+++ /dev/null
@@ -1,584 +0,0 @@
- 10));
- $two_way_sync = (int) get_option('igny8_enable_two_way_sync', 1);
-
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- |
- |
- |
- |
-
-
-
-
-
- |
- |
- |
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- |
- |
- |
-
-
-
-
-
- |
-
-
-
-
- |
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/igny8-wp-integration-plugin/data/link-graph.php b/igny8-wp-integration-plugin/data/link-graph.php
deleted file mode 100644
index 55011eff..00000000
--- a/igny8-wp-integration-plugin/data/link-graph.php
+++ /dev/null
@@ -1,192 +0,0 @@
-post_content;
- $source_url = get_permalink($post_id);
- $site_url = get_site_url();
- $links = array();
-
- // Match all anchor tags with href attributes
- preg_match_all('/]+href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/is', $content, $matches, PREG_SET_ORDER);
-
- foreach ($matches as $match) {
- $href = $match[1];
- $anchor = strip_tags($match[2]);
-
- // Skip empty anchors
- if (empty(trim($anchor))) {
- continue;
- }
-
- // Only process internal links
- if (strpos($href, $site_url) === 0 || strpos($href, '/') === 0) {
- // Convert relative URLs to absolute
- if (strpos($href, '/') === 0 && strpos($href, '//') !== 0) {
- $href = $site_url . $href;
- }
-
- // Normalize URL (remove trailing slash, fragments, query params for matching)
- $target_url = rtrim($href, '/');
-
- // Skip if source and target are the same
- if ($source_url === $target_url) {
- continue;
- }
-
- $links[] = array(
- 'source_url' => $source_url,
- 'target_url' => $target_url,
- 'anchor' => trim($anchor),
- 'post_id' => $post_id
- );
- }
- }
-
- return $links;
-}
-
-/**
- * Extract link graph from all posts
- *
- * @param array $post_ids Optional array of post IDs to process. If empty, processes all enabled posts.
- * @return array Link graph array
- */
-function igny8_extract_link_graph($post_ids = array()) {
- // Skip if connection is disabled
- if (!igny8_is_connection_enabled()) {
- return array();
- }
-
- if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
- return array();
- }
-
- $enabled_post_types = igny8_get_enabled_post_types();
-
- if (empty($post_ids)) {
- // Get all published posts of enabled types
- $query_args = array(
- 'post_type' => $enabled_post_types,
- 'post_status' => 'publish',
- 'posts_per_page' => -1,
- 'fields' => 'ids',
- 'suppress_filters' => true
- );
-
- $post_ids = get_posts($query_args);
- }
-
- $link_graph = array();
- $processed = 0;
-
- foreach ($post_ids as $post_id) {
- $links = igny8_extract_post_links($post_id);
-
- if (!empty($links)) {
- $link_graph = array_merge($link_graph, $links);
- }
-
- $processed++;
-
- // Limit processing to prevent timeout (can be increased or made configurable)
- if ($processed >= 1000) {
- break;
- }
- }
-
- return $link_graph;
-}
-
-/**
- * Send link graph to IGNY8 Linker module
- *
- * @param int $site_id IGNY8 site ID
- * @param array $link_graph Link graph array (optional, will extract if not provided)
- * @return array|false Response data or false on failure
- */
-function igny8_send_link_graph_to_igny8($site_id, $link_graph = null) {
- // Skip if connection is disabled
- if (!igny8_is_connection_enabled()) {
- return false;
- }
-
- if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('linker')) {
- return false;
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- return false;
- }
-
- // Extract link graph if not provided
- if ($link_graph === null) {
- $link_graph = igny8_extract_link_graph();
- }
-
- if (empty($link_graph)) {
- return array('success' => true, 'message' => 'No links found', 'links_count' => 0);
- }
-
- // Send in batches (max 500 links per batch)
- $batch_size = 500;
- $batches = array_chunk($link_graph, $batch_size);
- $total_sent = 0;
- $errors = array();
-
- foreach ($batches as $batch) {
- $response = $api->post("/linker/link-map/", array(
- 'site_id' => $site_id,
- 'links' => $batch,
- 'total_links' => count($link_graph),
- 'batch_number' => count($batches) > 1 ? (count($batches) - count($batches) + array_search($batch, $batches) + 1) : 1,
- 'total_batches' => count($batches)
- ));
-
- if ($response['success']) {
- $total_sent += count($batch);
- } else {
- $errors[] = $response['error'] ?? 'Unknown error';
- }
- }
-
- if ($total_sent > 0) {
- update_option('igny8_last_link_graph_sync', current_time('timestamp'));
- update_option('igny8_last_link_graph_count', $total_sent);
-
- return array(
- 'success' => true,
- 'links_sent' => $total_sent,
- 'total_links' => count($link_graph),
- 'batches' => count($batches),
- 'errors' => $errors
- );
- }
-
- return false;
-}
-
diff --git a/igny8-wp-integration-plugin/data/semantic-mapping.php b/igny8-wp-integration-plugin/data/semantic-mapping.php
deleted file mode 100644
index 1aeb58aa..00000000
--- a/igny8-wp-integration-plugin/data/semantic-mapping.php
+++ /dev/null
@@ -1,225 +0,0 @@
- false, 'error' => 'Connection disabled');
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- return array('success' => false, 'error' => 'Not authenticated');
- }
-
- // Extract semantic structure from site data
- $semantic_map = array(
- 'sectors' => array(),
- 'clusters' => array(),
- 'keywords' => array()
- );
-
- // Map taxonomies to sectors
- foreach ($site_data['taxonomies'] as $tax_name => $tax_data) {
- if ($tax_data['taxonomy']['hierarchical']) {
- // Hierarchical taxonomies (categories) become sectors
- $sector = array(
- 'name' => $tax_data['taxonomy']['label'],
- 'slug' => $tax_data['taxonomy']['name'],
- 'description' => $tax_data['taxonomy']['description'],
- 'source' => 'wordpress_taxonomy',
- 'source_id' => $tax_name
- );
-
- // Map terms to clusters
- $clusters = array();
- foreach ($tax_data['terms'] as $term) {
- $clusters[] = array(
- 'name' => $term['name'],
- 'slug' => $term['slug'],
- 'description' => $term['description'],
- 'source' => 'wordpress_term',
- 'source_id' => $term['id']
- );
-
- // Extract keywords from posts in this term
- $keywords = igny8_extract_keywords_from_term_posts($term['id'], $tax_name);
- $semantic_map['keywords'] = array_merge($semantic_map['keywords'], $keywords);
- }
-
- $sector['clusters'] = $clusters;
- $semantic_map['sectors'][] = $sector;
- }
- }
-
- // Map WooCommerce product categories to sectors
- if (!empty($site_data['product_categories'])) {
- $product_sector = array(
- 'name' => 'Products',
- 'slug' => 'products',
- 'description' => 'WooCommerce product categories',
- 'source' => 'woocommerce',
- 'clusters' => array()
- );
-
- foreach ($site_data['product_categories'] as $category) {
- $product_sector['clusters'][] = array(
- 'name' => $category['name'],
- 'slug' => $category['slug'],
- 'description' => $category['description'],
- 'source' => 'woocommerce_category',
- 'source_id' => $category['id']
- );
- }
-
- $semantic_map['sectors'][] = $product_sector;
- }
-
- // Send semantic map to IGNY8
- $response = $api->post("/planner/sites/{$site_id}/semantic-map/", array(
- 'semantic_map' => $semantic_map,
- 'site_data' => $site_data
- ));
-
- return $response;
-}
-
-/**
- * Extract keywords from posts associated with a taxonomy term
- *
- * @param int $term_id Term ID
- * @param string $taxonomy Taxonomy name
- * @return array Formatted keywords array
- */
-function igny8_extract_keywords_from_term_posts($term_id, $taxonomy) {
- $args = array(
- 'post_type' => 'any',
- 'posts_per_page' => -1,
- 'tax_query' => array(
- array(
- 'taxonomy' => $taxonomy,
- 'field' => 'term_id',
- 'terms' => $term_id
- )
- )
- );
-
- $query = new WP_Query($args);
- $keywords = array();
-
- if ($query->have_posts()) {
- while ($query->have_posts()) {
- $query->the_post();
-
- // Extract keywords from post title and content
- $title_words = str_word_count(get_the_title(), 1);
- $content_words = str_word_count(strip_tags(get_the_content()), 1);
-
- // Combine and get unique keywords
- $all_words = array_merge($title_words, $content_words);
- $unique_words = array_unique(array_map('strtolower', $all_words));
-
- // Filter out common words (stop words)
- $stop_words = array('the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by');
- $keywords = array_merge($keywords, array_diff($unique_words, $stop_words));
- }
- wp_reset_postdata();
- }
-
- // Format keywords
- $formatted_keywords = array();
- foreach (array_unique($keywords) as $keyword) {
- if (strlen($keyword) > 3) { // Only keywords longer than 3 characters
- $formatted_keywords[] = array(
- 'keyword' => $keyword,
- 'source' => 'wordpress_post',
- 'source_term_id' => $term_id
- );
- }
- }
-
- return $formatted_keywords;
-}
-
-/**
- * Complete workflow: Fetch site data → Map to semantic strategy → Restructure content
- *
- * @param int $site_id IGNY8 site ID
- * @return array|false Analysis result or false on failure
- */
-function igny8_analyze_and_restructure_site($site_id) {
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- return false;
- }
-
- // Step 1: Collect all site data
- $site_data = igny8_collect_site_data();
-
- // Step 2: Send to IGNY8 for analysis
- $analysis_response = $api->post("/system/sites/{$site_id}/analyze/", array(
- 'site_data' => $site_data,
- 'analysis_type' => 'full_site_restructure'
- ));
-
- if (!$analysis_response['success']) {
- return false;
- }
-
- $analysis_id = $analysis_response['data']['analysis_id'] ?? null;
-
- // Step 3: Map to semantic strategy
- $mapping_response = igny8_map_site_to_semantic_strategy($site_id, $site_data);
-
- if (!$mapping_response['success']) {
- return false;
- }
-
- // Step 4: Get restructuring recommendations
- $recommendations_response = $api->get("/system/sites/{$site_id}/recommendations/");
-
- if (!$recommendations_response['success']) {
- return false;
- }
-
- // Get keywords count from mapping response
- $keywords_count = 0;
- if (isset($mapping_response['data']['keywords'])) {
- $keywords_count = count($mapping_response['data']['keywords']);
- }
-
- return array(
- 'analysis_id' => $analysis_id,
- 'semantic_map' => $mapping_response['data'] ?? null,
- 'recommendations' => $recommendations_response['data'] ?? null,
- 'site_data_summary' => array(
- 'total_posts' => count($site_data['posts']),
- 'total_taxonomies' => count($site_data['taxonomies']),
- 'total_products' => count($site_data['products'] ?? array()),
- 'total_keywords' => $keywords_count
- )
- );
-}
-
diff --git a/igny8-wp-integration-plugin/data/site-collection.php b/igny8-wp-integration-plugin/data/site-collection.php
deleted file mode 100644
index e60f90f2..00000000
--- a/igny8-wp-integration-plugin/data/site-collection.php
+++ /dev/null
@@ -1,579 +0,0 @@
- 'publish',
- 'after' => null,
- 'max_pages' => 5,
- );
- $args = wp_parse_args($args, $defaults);
-
- $post_type_object = get_post_type_object($post_type);
- $rest_base = ($post_type_object && !empty($post_type_object->rest_base)) ? $post_type_object->rest_base : $post_type;
-
- $base_url = sprintf('%s/wp-json/wp/v2/%s', get_site_url(), $rest_base);
-
- $query_args = array(
- 'per_page' => min($per_page, 100),
- 'status' => $args['status'],
- 'orderby' => 'modified',
- 'order' => 'desc',
- );
-
- if (!empty($args['after'])) {
- $query_args['after'] = gmdate('c', $args['after']);
- }
-
- $formatted_posts = array();
- $page = 1;
-
- do {
- $query_args['page'] = $page;
- $response = wp_remote_get(add_query_arg($query_args, $base_url));
-
- if (is_wp_error($response)) {
- break;
- }
-
- $posts = json_decode(wp_remote_retrieve_body($response), true);
-
- if (!is_array($posts) || empty($posts)) {
- break;
- }
-
- foreach ($posts as $post) {
- $content = $post['content']['rendered'] ?? '';
- $word_count = str_word_count(strip_tags($content));
-
- $formatted_posts[] = array(
- 'id' => $post['id'],
- 'title' => html_entity_decode($post['title']['rendered'] ?? ''),
- 'content' => $content,
- 'excerpt' => $post['excerpt']['rendered'] ?? '',
- 'status' => $post['status'] ?? 'draft',
- 'url' => $post['link'] ?? '',
- 'published' => $post['date'] ?? '',
- 'modified' => $post['modified'] ?? '',
- 'author' => $post['author'] ?? 0,
- 'post_type' => $post['type'] ?? $post_type,
- 'taxonomies' => array(
- 'categories' => $post['categories'] ?? array(),
- 'tags' => $post['tags'] ?? array(),
- ),
- 'meta' => array(
- 'word_count' => $word_count,
- 'reading_time' => $word_count ? ceil($word_count / 200) : 0,
- 'featured_media' => $post['featured_media'] ?? 0,
- )
- );
- }
-
- if (count($posts) < $query_args['per_page']) {
- break;
- }
-
- $page++;
- } while ($page <= $args['max_pages']);
-
- return $formatted_posts;
-}
-
-/**
- * Fetch all available post types from WordPress
- *
- * @return array|false Post types array or false on failure
- */
-function igny8_fetch_all_post_types() {
- $wp_response = wp_remote_get(get_site_url() . '/wp-json/wp/v2/types');
-
- if (is_wp_error($wp_response)) {
- return false;
- }
-
- $types = json_decode(wp_remote_retrieve_body($wp_response), true);
-
- if (!is_array($types)) {
- return false;
- }
-
- $post_types = array();
- foreach ($types as $type_name => $type_data) {
- if ($type_data['public']) {
- $post_types[] = array(
- 'name' => $type_name,
- 'label' => $type_data['name'],
- 'description' => $type_data['description'] ?? '',
- 'rest_base' => $type_data['rest_base'] ?? $type_name
- );
- }
- }
-
- return $post_types;
-}
-
-/**
- * Fetch all posts from all post types
- *
- * @param int $per_page Posts per page
- * @return array All posts
- */
-function igny8_fetch_all_wordpress_posts($per_page = 100) {
- $post_types = igny8_fetch_all_post_types();
-
- if (!$post_types) {
- return array();
- }
-
- $all_posts = array();
- foreach ($post_types as $type) {
- $posts = igny8_fetch_wordpress_posts($type['name'], $per_page);
- if ($posts) {
- $all_posts = array_merge($all_posts, $posts);
- }
- }
-
- return $all_posts;
-}
-
-/**
- * Fetch all taxonomies from WordPress
- *
- * @return array|false Taxonomies array or false on failure
- */
-function igny8_fetch_wordpress_taxonomies() {
- $wp_response = wp_remote_get(get_site_url() . '/wp-json/wp/v2/taxonomies');
-
- if (is_wp_error($wp_response)) {
- return false;
- }
-
- $taxonomies = json_decode(wp_remote_retrieve_body($wp_response), true);
-
- if (!is_array($taxonomies)) {
- return false;
- }
-
- $formatted_taxonomies = array();
- foreach ($taxonomies as $tax_name => $tax_data) {
- if ($tax_data['public']) {
- $formatted_taxonomies[] = array(
- 'name' => $tax_name,
- 'label' => $tax_data['name'],
- 'description' => $tax_data['description'] ?? '',
- 'hierarchical' => $tax_data['hierarchical'],
- 'rest_base' => $tax_data['rest_base'] ?? $tax_name,
- 'object_types' => $tax_data['types'] ?? array()
- );
- }
- }
-
- return $formatted_taxonomies;
-}
-
-/**
- * Fetch all terms for a specific taxonomy
- *
- * @param string $taxonomy Taxonomy name
- * @param int $per_page Terms per page
- * @return array|false Formatted terms array or false on failure
- */
-function igny8_fetch_taxonomy_terms($taxonomy, $per_page = 100) {
- $taxonomy_obj = get_taxonomy($taxonomy);
- $rest_base = ($taxonomy_obj && !empty($taxonomy_obj->rest_base)) ? $taxonomy_obj->rest_base : $taxonomy;
-
- $base_url = sprintf('%s/wp-json/wp/v2/%s', get_site_url(), $rest_base);
-
- $formatted_terms = array();
- $page = 1;
-
- do {
- $response = wp_remote_get(add_query_arg(array(
- 'per_page' => min($per_page, 100),
- 'page' => $page
- ), $base_url));
-
- if (is_wp_error($response)) {
- break;
- }
-
- $terms = json_decode(wp_remote_retrieve_body($response), true);
-
- if (!is_array($terms) || empty($terms)) {
- break;
- }
-
- foreach ($terms as $term) {
- $formatted_terms[] = array(
- 'id' => $term['id'],
- 'name' => $term['name'],
- 'slug' => $term['slug'],
- 'description' => $term['description'] ?? '',
- 'count' => $term['count'],
- 'parent' => $term['parent'] ?? 0,
- 'taxonomy' => $taxonomy,
- 'url' => $term['link'] ?? ''
- );
- }
-
- if (count($terms) < min($per_page, 100)) {
- break;
- }
-
- $page++;
- } while (true);
-
- return $formatted_terms;
-}
-
-/**
- * Fetch all terms from all taxonomies
- *
- * @param int $per_page Terms per page
- * @return array All terms organized by taxonomy
- */
-function igny8_fetch_all_taxonomy_terms($per_page = 100) {
- $taxonomies = igny8_fetch_wordpress_taxonomies();
-
- if (!$taxonomies) {
- return array();
- }
-
- $all_terms = array();
- foreach ($taxonomies as $taxonomy) {
- $terms = igny8_fetch_taxonomy_terms($taxonomy['rest_base'], $per_page);
- if ($terms) {
- $all_terms[$taxonomy['name']] = $terms;
- }
- }
-
- return $all_terms;
-}
-
-/**
- * Collect all WordPress site data for IGNY8 semantic mapping
- *
- * @return array Complete site data
- */
-function igny8_collect_site_data($args = array()) {
- // Skip if connection is disabled
- if (!igny8_is_connection_enabled()) {
- return array('disabled' => true, 'reason' => 'connection_disabled');
- }
-
- if (function_exists('igny8_is_module_enabled') && !igny8_is_module_enabled('sites')) {
- return array('disabled' => true);
- }
-
- $settings = igny8_get_site_scan_settings($args);
-
- $site_data = array(
- 'site_url' => get_site_url(),
- 'site_name' => get_bloginfo('name'),
- 'site_description' => get_bloginfo('description'),
- 'collected_at' => current_time('mysql'),
- 'settings' => $settings,
- 'posts' => array(),
- 'taxonomies' => array(),
- 'products' => array(),
- 'product_categories' => array(),
- 'product_attributes' => array()
- );
-
- foreach ((array) $settings['post_types'] as $post_type) {
- if (!post_type_exists($post_type) || !igny8_is_post_type_enabled($post_type)) {
- continue;
- }
-
- $posts = igny8_fetch_wordpress_posts($post_type, $settings['per_page'], array(
- 'after' => $settings['since'],
- 'status' => 'publish'
- ));
-
- if ($posts) {
- $site_data['posts'] = array_merge($site_data['posts'], $posts);
- }
- }
-
- $tracked_taxonomies = array('category', 'post_tag', 'igny8_sectors', 'igny8_clusters');
- foreach ($tracked_taxonomies as $taxonomy) {
- if (!taxonomy_exists($taxonomy)) {
- continue;
- }
-
- $terms = igny8_fetch_taxonomy_terms($taxonomy, 100);
- if ($terms) {
- $tax_obj = get_taxonomy($taxonomy);
- $site_data['taxonomies'][$taxonomy] = array(
- 'taxonomy' => array(
- 'name' => $taxonomy,
- 'label' => $tax_obj ? $tax_obj->label : $taxonomy,
- 'description' => $tax_obj->description ?? '',
- 'hierarchical' => $tax_obj ? $tax_obj->hierarchical : false,
- ),
- 'terms' => $terms
- );
- }
- }
-
- if (!empty($settings['include_products']) && function_exists('igny8_is_woocommerce_active') && igny8_is_woocommerce_active()) {
- require_once IGNY8_BRIDGE_PLUGIN_DIR . 'data/woocommerce.php';
-
- $products = igny8_fetch_woocommerce_products(100);
- if ($products) {
- $site_data['products'] = $products;
- }
-
- $product_categories = igny8_fetch_product_categories(100);
- if ($product_categories) {
- $site_data['product_categories'] = $product_categories;
- }
-
- $product_attributes = igny8_fetch_product_attributes();
- if ($product_attributes) {
- $site_data['product_attributes'] = $product_attributes;
- }
- }
-
- // Extract link graph if Linker module is enabled
- if (function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
- $post_ids = wp_list_pluck($site_data['posts'], 'id');
- $link_graph = igny8_extract_link_graph($post_ids);
-
- if (!empty($link_graph)) {
- $site_data['link_graph'] = $link_graph;
- }
- }
-
- $site_data['summary'] = array(
- 'posts' => count($site_data['posts']),
- 'taxonomies' => count($site_data['taxonomies']),
- 'products' => count($site_data['products']),
- 'links' => isset($site_data['link_graph']) ? count($site_data['link_graph']) : 0
- );
-
- update_option('igny8_last_site_snapshot', array(
- 'timestamp' => current_time('timestamp'),
- 'summary' => $site_data['summary']
- ));
-
- return $site_data;
-}
-
-/**
- * Send WordPress site data to IGNY8 for semantic strategy mapping
- *
- * @param int $site_id IGNY8 site ID
- * @return array|false Response data or false on failure
- */
-function igny8_send_site_data_to_igny8($site_id, $site_data = null, $args = array()) {
- // Skip if connection is disabled
- if (!igny8_is_connection_enabled()) {
- return false;
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- return false;
- }
-
- // Collect all site data if not provided
- if (empty($site_data)) {
- $site_data = igny8_collect_site_data($args);
- }
-
- if (empty($site_data) || isset($site_data['disabled'])) {
- return false;
- }
-
- // Send to IGNY8 API
- $response = $api->post("/system/sites/{$site_id}/import/", array(
- 'site_data' => $site_data,
- 'import_type' => $args['mode'] ?? 'full_site_scan'
- ));
-
- if ($response['success']) {
- // Store import ID for tracking
- update_option('igny8_last_site_import_id', $response['data']['import_id'] ?? null);
- update_option('igny8_last_site_sync', current_time('timestamp'));
-
- // Send link graph separately to Linker module if available
- if (!empty($site_data['link_graph']) && function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
- $link_result = igny8_send_link_graph_to_igny8($site_id, $site_data['link_graph']);
- if ($link_result) {
- error_log(sprintf('IGNY8: Sent %d links to Linker module', $link_result['links_sent'] ?? 0));
- }
- }
-
- return $response['data'];
- } else {
- error_log("IGNY8: Failed to send site data: " . ($response['error'] ?? 'Unknown error'));
- return false;
- }
-}
-
-/**
- * Sync only changed posts/taxonomies since last sync
- *
- * @param int $site_id IGNY8 site ID
- * @return array|false Sync result or false on failure
- */
-function igny8_sync_incremental_site_data($site_id, $settings = array()) {
- // Skip if connection is disabled
- if (!igny8_is_connection_enabled()) {
- return array('synced' => 0, 'message' => 'Connection disabled');
- }
-
- $api = new Igny8API();
-
- if (!$api->is_authenticated()) {
- return false;
- }
-
- $settings = igny8_get_site_scan_settings(wp_parse_args($settings, array('mode' => 'incremental')));
- $since = $settings['since'] ?? intval(get_option('igny8_last_site_sync', 0));
-
- $formatted_posts = array();
-
- foreach ((array) $settings['post_types'] as $post_type) {
- if (!post_type_exists($post_type) || !igny8_is_post_type_enabled($post_type)) {
- continue;
- }
-
- $query_args = array(
- 'post_type' => $post_type,
- 'post_status' => array('publish', 'pending', 'draft', 'future'),
- 'posts_per_page' => -1,
- 'orderby' => 'modified',
- 'order' => 'DESC',
- 'suppress_filters' => true,
- );
-
- if ($since) {
- $query_args['date_query'] = array(
- array(
- 'column' => 'post_modified_gmt',
- 'after' => gmdate('Y-m-d H:i:s', $since)
- )
- );
- }
-
- $posts = get_posts($query_args);
-
- foreach ($posts as $post) {
- $word_count = str_word_count(strip_tags($post->post_content));
-
- $formatted_posts[] = array(
- 'id' => $post->ID,
- 'title' => get_the_title($post),
- 'content' => $post->post_content,
- 'status' => $post->post_status,
- 'modified' => $post->post_modified_gmt,
- 'post_type' => $post->post_type,
- 'url' => get_permalink($post),
- 'taxonomies' => array(
- 'categories' => wp_get_post_terms($post->ID, 'category', array('fields' => 'ids')),
- 'tags' => wp_get_post_terms($post->ID, 'post_tag', array('fields' => 'ids')),
- ),
- 'meta' => array(
- 'task_id' => get_post_meta($post->ID, '_igny8_task_id', true),
- 'cluster_id' => get_post_meta($post->ID, '_igny8_cluster_id', true),
- 'sector_id' => get_post_meta($post->ID, '_igny8_sector_id', true),
- 'word_count' => $word_count,
- )
- );
- }
- }
-
- if (empty($formatted_posts)) {
- return array('synced' => 0, 'message' => 'No changes since last sync');
- }
-
- $response = $api->post("/system/sites/{$site_id}/sync/", array(
- 'posts' => $formatted_posts,
- 'sync_type' => 'incremental',
- 'last_sync' => $since,
- 'post_types' => $settings['post_types']
- ));
-
- if ($response['success']) {
- update_option('igny8_last_site_sync', current_time('timestamp'));
- update_option('igny8_last_incremental_site_sync', array(
- 'timestamp' => current_time('timestamp'),
- 'count' => count($formatted_posts)
- ));
-
- return array(
- 'synced' => count($formatted_posts),
- 'message' => 'Incremental sync completed'
- );
- }
-
- return false;
-}
-
-/**
- * Run a full site scan and semantic mapping
- *
- * @param int $site_id IGNY8 site ID
- * @param array $settings Scan settings
- * @return array|false
- */
-function igny8_perform_full_site_scan($site_id, $settings = array()) {
- $site_data = igny8_collect_site_data($settings);
-
- if (empty($site_data) || isset($site_data['disabled'])) {
- return false;
- }
-
- $import = igny8_send_site_data_to_igny8($site_id, $site_data, array('mode' => 'full_site_scan'));
-
- if (!$import) {
- return false;
- }
-
- update_option('igny8_last_full_site_scan', current_time('timestamp'));
-
- // Map to semantic strategy (requires Planner module)
- if (!function_exists('igny8_is_module_enabled') || igny8_is_module_enabled('planner')) {
- $map_response = igny8_map_site_to_semantic_strategy($site_id, $site_data);
- if (!empty($map_response['success'])) {
- update_option('igny8_last_semantic_map', current_time('timestamp'));
- update_option('igny8_last_semantic_map_summary', array(
- 'sectors' => count($map_response['data']['sectors'] ?? array()),
- 'keywords' => count($map_response['data']['keywords'] ?? array())
- ));
- }
- }
-
- // Send link graph to Linker module if available
- if (!empty($site_data['link_graph']) && function_exists('igny8_is_module_enabled') && igny8_is_module_enabled('linker')) {
- $link_result = igny8_send_link_graph_to_igny8($site_id, $site_data['link_graph']);
- if ($link_result) {
- error_log(sprintf('IGNY8: Sent %d links to Linker module during full scan', $link_result['links_sent'] ?? 0));
- }
- }
-
- return $import;
-}
-
diff --git a/igny8-wp-integration-plugin/data/woocommerce.php b/igny8-wp-integration-plugin/data/woocommerce.php
deleted file mode 100644
index 7f01218e..00000000
--- a/igny8-wp-integration-plugin/data/woocommerce.php
+++ /dev/null
@@ -1,226 +0,0 @@
- $headers
- ));
-
- if (is_wp_error($wp_response)) {
- return false;
- }
-
- $products = json_decode(wp_remote_retrieve_body($wp_response), true);
-
- if (!is_array($products)) {
- return false;
- }
-
- $formatted_products = array();
- foreach ($products as $product) {
- $formatted_products[] = array(
- 'id' => $product['id'],
- 'name' => $product['name'],
- 'slug' => $product['slug'],
- 'sku' => $product['sku'],
- 'type' => $product['type'],
- 'status' => $product['status'],
- 'description' => $product['description'],
- 'short_description' => $product['short_description'],
- 'price' => $product['price'],
- 'regular_price' => $product['regular_price'],
- 'sale_price' => $product['sale_price'],
- 'on_sale' => $product['on_sale'],
- 'stock_status' => $product['stock_status'],
- 'stock_quantity' => $product['stock_quantity'],
- 'categories' => $product['categories'] ?? array(),
- 'tags' => $product['tags'] ?? array(),
- 'images' => $product['images'] ?? array(),
- 'attributes' => $product['attributes'] ?? array(),
- 'variations' => $product['variations'] ?? array(),
- 'url' => $product['permalink']
- );
- }
-
- return $formatted_products;
-}
-
-/**
- * Fetch WooCommerce product categories
- *
- * @param int $per_page Categories per page
- * @return array|false Formatted categories array or false on failure
- */
-function igny8_fetch_product_categories($per_page = 100) {
- if (!igny8_is_woocommerce_active()) {
- return false;
- }
-
- $consumer_key = get_option('woocommerce_api_consumer_key', '');
- $consumer_secret = get_option('woocommerce_api_consumer_secret', '');
-
- $headers = array();
- if ($consumer_key && $consumer_secret) {
- $headers['Authorization'] = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
- }
-
- $wp_response = wp_remote_get(sprintf(
- '%s/wp-json/wc/v3/products/categories?per_page=%d',
- get_site_url(),
- $per_page
- ), array(
- 'headers' => $headers
- ));
-
- if (is_wp_error($wp_response)) {
- return false;
- }
-
- $categories = json_decode(wp_remote_retrieve_body($wp_response), true);
-
- if (!is_array($categories)) {
- return false;
- }
-
- $formatted_categories = array();
- foreach ($categories as $category) {
- $formatted_categories[] = array(
- 'id' => $category['id'],
- 'name' => $category['name'],
- 'slug' => $category['slug'],
- 'description' => $category['description'] ?? '',
- 'count' => $category['count'],
- 'parent' => $category['parent'] ?? 0,
- 'image' => $category['image']['src'] ?? null
- );
- }
-
- return $formatted_categories;
-}
-
-/**
- * Fetch WooCommerce product attributes
- *
- * @return array|false Formatted attributes array or false on failure
- */
-function igny8_fetch_product_attributes() {
- if (!igny8_is_woocommerce_active()) {
- return false;
- }
-
- $consumer_key = get_option('woocommerce_api_consumer_key', '');
- $consumer_secret = get_option('woocommerce_api_consumer_secret', '');
-
- $headers = array();
- if ($consumer_key && $consumer_secret) {
- $headers['Authorization'] = 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret);
- }
-
- $wp_response = wp_remote_get(
- get_site_url() . '/wp-json/wc/v3/products/attributes',
- array(
- 'headers' => $headers
- )
- );
-
- if (is_wp_error($wp_response)) {
- return false;
- }
-
- $attributes = json_decode(wp_remote_retrieve_body($wp_response), true);
-
- if (!is_array($attributes)) {
- return false;
- }
-
- $formatted_attributes = array();
- foreach ($attributes as $attribute) {
- // Get attribute terms
- $terms_response = wp_remote_get(sprintf(
- '%s/wp-json/wc/v3/products/attributes/%d/terms',
- get_site_url(),
- $attribute['id']
- ), array(
- 'headers' => $headers
- ));
-
- $terms = array();
- if (!is_wp_error($terms_response)) {
- $terms_data = json_decode(wp_remote_retrieve_body($terms_response), true);
- if (is_array($terms_data)) {
- foreach ($terms_data as $term) {
- $terms[] = array(
- 'id' => $term['id'],
- 'name' => $term['name'],
- 'slug' => $term['slug']
- );
- }
- }
- }
-
- $formatted_attributes[] = array(
- 'id' => $attribute['id'],
- 'name' => $attribute['name'],
- 'slug' => $attribute['slug'],
- 'type' => $attribute['type'],
- 'order_by' => $attribute['order_by'],
- 'has_archives' => $attribute['has_archives'],
- 'terms' => $terms
- );
- }
-
- return $formatted_attributes;
-}
-
diff --git a/igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md b/igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md
deleted file mode 100644
index eec33324..00000000
--- a/igny8-wp-integration-plugin/docs/PHASE_5_IMPLEMENTATION_SUMMARY.md
+++ /dev/null
@@ -1,185 +0,0 @@
-# Phase 5 Implementation Summary
-
-## Overview
-Phase 5 implementation adds Planner, Linker, and Optimizer module hooks to the WordPress bridge plugin.
-
-## Implemented Features
-
-### 5.1 Planner Briefs Display & Refresh Actions ✅
-
-**Files Created:**
-- `admin/class-post-meta-boxes.php` - Meta boxes for post editor
-- `admin/assets/js/post-editor.js` - JavaScript for AJAX interactions
-
-**Features:**
-- **Planner Brief Meta Box** in post editor sidebar
- - Displays cached brief with title, content, outline, keywords, and tone
- - Shows cache timestamp
- - "Fetch Brief" button to load from IGNY8 API
- - "Request Refresh" button to trigger Planner refresh
-
-**API Endpoints Used:**
-- `GET /planner/tasks/{id}/brief/` - Fetch Planner brief (with fallback to Writer brief)
-- `POST /planner/tasks/{id}/refresh/` - Request Planner task refresh
-
-**Meta Fields Added:**
-- `_igny8_task_brief` - Cached brief data
-- `_igny8_brief_cached_at` - Brief cache timestamp
-
-**AJAX Handlers:**
-- `igny8_fetch_planner_brief` - Fetches and caches brief
-- `igny8_refresh_planner_task` - Requests Planner refresh
-
----
-
-### 5.2 Link Graph Export ✅
-
-**Files Created:**
-- `data/link-graph.php` - Link graph extraction and export
-
-**Features:**
-- **Link Extraction** from post content
- - Extracts all internal links (anchor tags)
- - Captures source URL, target URL, and anchor text
- - Filters to only internal links (same domain)
- - Normalizes URLs for consistency
-
-- **Link Graph Collection**
- - Processes all enabled post types
- - Extracts links from published posts
- - Configurable batch processing (max 1000 posts per run)
-
-- **Automatic Export During Site Scans**
- - Integrated into `igny8_collect_site_data()`
- - Included in site data payload
- - Also sent separately to Linker module endpoint
-
-**API Endpoints Used:**
-- `POST /linker/link-map/` - Send link graph to Linker module
-
-**Functions:**
-- `igny8_extract_post_links($post_id)` - Extract links from single post
-- `igny8_extract_link_graph($post_ids)` - Extract links from multiple posts
-- `igny8_send_link_graph_to_igny8($site_id, $link_graph)` - Send to IGNY8 API
-
-**Integration Points:**
-- `igny8_collect_site_data()` - Includes link graph in site data
-- `igny8_send_site_data_to_igny8()` - Sends link graph after site import
-- `igny8_perform_full_site_scan()` - Sends link graph during full scans
-
-**Options Stored:**
-- `igny8_last_link_graph_sync` - Last sync timestamp
-- `igny8_last_link_graph_count` - Number of links sent
-
----
-
-### 5.4 Optimizer Triggers ✅
-
-**Features:**
-- **Optimizer Meta Box** in post editor sidebar
- - Displays current optimizer job ID and status
- - "Request Optimization" button to create new job
- - "Check Status" button to fetch latest job status
- - Shows score changes and recommendations when available
-
-**API Endpoints Used:**
-- `POST /optimizer/jobs/` - Create optimizer job
-- `GET /optimizer/jobs/{id}/` - Get optimizer job status
-
-**Meta Fields Added:**
-- `_igny8_optimizer_job_id` - Optimizer job ID
-- `_igny8_optimizer_status` - Job status (pending, processing, completed, failed)
-- `_igny8_optimizer_score_changes` - Score changes from optimization
-- `_igny8_optimizer_recommendations` - Optimization recommendations
-- `_igny8_optimizer_job_created_at` - Job creation timestamp
-
-**AJAX Handlers:**
-- `igny8_create_optimizer_job` - Creates new optimizer job
-- `igny8_get_optimizer_status` - Fetches job status and updates meta
-
-**Job Parameters:**
-- `post_id` - WordPress post ID
-- `task_id` - IGNY8 task ID
-- `job_type` - Type of job (default: 'audit')
-- `priority` - Job priority (default: 'normal')
-
----
-
-## Module Toggle Support
-
-All Phase 5 features respect the module toggle settings:
-- **Planner** module must be enabled for brief fetching/refresh
-- **Linker** module must be enabled for link graph export
-- **Optimizer** module must be enabled for optimizer jobs
-
-Features also check `igny8_is_connection_enabled()` to ensure sync operations are active.
-
----
-
-## UI/UX Features
-
-### Post Editor Meta Boxes
-- Clean, organized display in sidebar
-- Real-time AJAX updates
-- Success/error message display
-- Auto-refresh after operations
-- Disabled state when connection is off
-
-### JavaScript Enhancements
-- Loading states on buttons
-- Confirmation dialogs for destructive actions
-- Error handling and user feedback
-- Non-blocking AJAX requests
-
----
-
-## Integration with Existing Code
-
-### Modified Files:
-- `igny8-bridge.php` - Added meta boxes class loading
-- `includes/functions.php` - Added optimizer meta field registrations
-- `data/site-collection.php` - Integrated link graph extraction
-
-### New Dependencies:
-- None (uses existing Igny8API class)
-
----
-
-## Testing Checklist
-
-- [ ] Planner brief displays correctly in post editor
-- [ ] Fetch brief button works and caches data
-- [ ] Request refresh button triggers Planner refresh
-- [ ] Link graph extraction works on posts with links
-- [ ] Link graph is included in site scans
-- [ ] Link graph is sent to Linker endpoint
-- [ ] Optimizer job creation works
-- [ ] Optimizer status check works
-- [ ] All features respect module toggles
-- [ ] All features respect connection enabled toggle
-- [ ] Meta boxes only show for posts with task IDs
-- [ ] Error handling works correctly
-
----
-
-## Notes
-
-1. **Link Graph Processing**: Currently limited to 1000 posts per run to prevent timeouts. Can be increased or made configurable.
-
-2. **Brief Caching**: Briefs are cached in post meta to reduce API calls. Cache can be refreshed manually.
-
-3. **Optimizer Jobs**: Jobs are created asynchronously. Status must be checked manually or via webhook (Phase 6).
-
-4. **Module Dependencies**: All features check module enablement before executing.
-
-5. **Connection Toggle**: All features respect the master connection toggle added earlier.
-
----
-
-## Next Steps (Phase 6)
-
-Phase 5.3 (Accept link recommendations via webhook) is deferred to Phase 6, which will implement:
-- REST endpoint `/wp-json/igny8/v1/event` with shared secret
-- Webhook handler for link recommendations
-- Link insertion queue system
-
diff --git a/igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md b/igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md
deleted file mode 100644
index 827c88b9..00000000
--- a/igny8-wp-integration-plugin/docs/PHASE_6_IMPLEMENTATION_SUMMARY.md
+++ /dev/null
@@ -1,298 +0,0 @@
-# Phase 6 Implementation Summary
-
-## Overview
-Phase 6 implementation adds webhook support and remote control capabilities, allowing IGNY8 SaaS to send events to WordPress.
-
-## Implemented Features
-
-### 6.1 REST Route Registration with Shared Secret ✅
-
-**Files Created:**
-- `includes/class-igny8-webhooks.php` - Main webhook handler class
-
-**Features:**
-- **REST Endpoint**: `/wp-json/igny8/v1/event` (POST)
-- **Shared Secret Authentication**: HMAC-SHA256 signature verification
-- **Connection Check**: All webhook handlers verify `igny8_is_connection_enabled()` before processing
-
-**Security:**
-- Webhook secret stored in WordPress options (auto-generated on first use)
-- Signature verification via `X-IGNY8-Signature` header
-- Secret can be regenerated from settings page
-- Failed authentication attempts are logged
-
-**Functions:**
-- `igny8_get_webhook_secret()` - Get or generate webhook secret
-- `igny8_regenerate_webhook_secret()` - Regenerate secret
-
----
-
-### 6.2 SaaS Event Handlers ✅
-
-**Event Types Supported:**
-
-#### 1. Task Published (`task_published`, `task_completed`)
-- Creates or updates WordPress post from IGNY8 task
-- Fetches full task data from Writer API
-- Respects enabled post types
-- Updates post status if task is completed
-
-#### 2. Link Recommendation (`link_recommendation`, `insert_link`)
-- Queues link insertion for processing
-- Validates required parameters (post_id, target_url, anchor)
-- Respects Linker module toggle
-- Processes links asynchronously via cron
-
-#### 3. Optimizer Request (`optimizer_request`, `optimizer_job_completed`)
-- Updates optimizer job status
-- Stores score changes and recommendations
-- Updates post meta with optimizer data
-- Respects Optimizer module toggle
-
-**Event Handler Flow:**
-1. Verify connection is enabled
-2. Verify module is enabled (if applicable)
-3. Validate event data
-4. Process event
-5. Log activity
-6. Return unified JSON response
-
----
-
-### 6.3 Webhook Activity Logging ✅
-
-**Files Created:**
-- `includes/class-igny8-webhook-logs.php` - Logging functions
-
-**Features:**
-- **Log Storage**: WordPress options (last 500 logs)
-- **Log Fields**:
- - Event type
- - Event data
- - IP address
- - User agent
- - Status (received, processed, failed)
- - Response data
- - Timestamps (received_at, processed_at)
- - Error messages
-
-**Functions:**
-- `igny8_log_webhook_activity()` - Log webhook receipt
-- `igny8_update_webhook_log()` - Update log with processing result
-- `igny8_get_webhook_logs()` - Retrieve logs with filtering
-- `igny8_clear_old_webhook_logs()` - Cleanup old logs
-
-**UI Display:**
-- Recent webhook activity table in settings page
-- Shows last 10 webhook events
-- Displays event type, status, and timestamp
-
----
-
-### Link Queue System ✅
-
-**Files Created:**
-- `includes/class-igny8-link-queue.php` - Link insertion queue
-
-**Features:**
-- **Queue Storage**: WordPress options
-- **Queue Processing**: Cron-based (processes 10 items per run)
-- **Retry Logic**: Up to 3 attempts per link
-- **Status Tracking**: pending, completed, failed
-
-**Link Insertion Logic:**
-1. Finds anchor text in post content
-2. Wraps anchor with link tag
-3. Avoids duplicate links
-4. Falls back to appending link if anchor not found
-
-**Queue Management:**
-- Automatic processing via cron
-- Manual trigger available
-- Queue size limit (1000 items)
-- Status tracking per item
-
----
-
-## Connection Checks
-
-**All handlers check connection status:**
-
-✅ **Webhook Handler** (`verify_webhook_secret`)
-- Checks `igny8_is_connection_enabled()` before allowing webhook
-
-✅ **Event Handlers** (`handle_webhook`, `handle_task_published`, etc.)
-- Double-checks connection enabled before processing
-- Returns error if connection disabled
-
-✅ **Link Queue** (`igny8_queue_link_insertion`, `igny8_process_link_queue`)
-- Checks connection enabled before queuing/processing
-
-✅ **REST API Endpoints** (existing endpoints)
-- Updated to check connection enabled
-- Returns 403 if connection disabled
-
----
-
-## Settings UI Enhancements
-
-**New Sections Added:**
-
-1. **Webhook Configuration**
- - Webhook URL display with copy button
- - Webhook secret display with copy button
- - Regenerate secret button
-
-2. **Link Queue**
- - Shows pending links count
- - Displays queue table (last 10 items)
- - Shows post ID, anchor, target URL, status
-
-3. **Recent Webhook Activity**
- - Shows last 10 webhook events
- - Displays event type, status, timestamp
- - Color-coded status badges
-
----
-
-## Security Features
-
-1. **HMAC Signature Verification**
- - Uses SHA-256 HMAC
- - Compares request body signature
- - Prevents replay attacks
-
-2. **Connection Toggle Protection**
- - All endpoints check connection status
- - Webhooks rejected if connection disabled
- - Clear error messages
-
-3. **Module Toggle Respect**
- - Events only processed if module enabled
- - Graceful error responses
-
-4. **Input Validation**
- - All parameters sanitized
- - Required fields validated
- - Type checking
-
----
-
-## API Response Format
-
-All webhook responses follow unified JSON format:
-
-**Success:**
-```json
-{
- "success": true,
- "message": "Event processed",
- "data": { ... }
-}
-```
-
-**Error:**
-```json
-{
- "success": false,
- "error": "Error message",
- "code": "error_code"
-}
-```
-
----
-
-## Integration Points
-
-### Modified Files:
-- `igny8-bridge.php` - Added webhook classes loading
-- `includes/functions.php` - Added webhook secret functions
-- `includes/class-igny8-rest-api.php` - Added connection checks
-- `admin/settings.php` - Added webhook UI sections
-- `admin/class-admin.php` - Added secret regeneration handler
-
-### New Dependencies:
-- None (uses existing WordPress functions)
-
----
-
-## Testing Checklist
-
-- [ ] Webhook endpoint accessible at `/wp-json/igny8/v1/event`
-- [ ] Signature verification works correctly
-- [ ] Invalid signatures are rejected
-- [ ] Connection disabled blocks webhooks
-- [ ] Task published event creates/updates posts
-- [ ] Link recommendation queues links
-- [ ] Link queue processes links correctly
-- [ ] Optimizer events update post meta
-- [ ] Webhook logs are created and updated
-- [ ] Settings UI displays webhook info
-- [ ] Secret regeneration works
-- [ ] All events respect module toggles
-- [ ] All events respect connection toggle
-
----
-
-## Notes
-
-1. **Webhook Secret**: Auto-generated on first use. Must be configured in IGNY8 SaaS app.
-
-2. **Link Queue**: Processes 10 items per cron run to prevent timeouts. Can be adjusted.
-
-3. **Log Retention**: Keeps last 500 logs. Older logs can be cleared manually.
-
-4. **Signature Header**: IGNY8 SaaS must send `X-IGNY8-Signature` header with HMAC-SHA256 signature of request body.
-
-5. **Connection Toggle**: All webhook handlers check connection status before processing. This ensures no data is processed when connection is disabled.
-
-6. **Module Toggles**: Each event type checks if its module is enabled before processing.
-
----
-
-## Webhook Payload Examples
-
-### Task Published
-```json
-{
- "event": "task_published",
- "data": {
- "task_id": 123,
- "status": "completed"
- }
-}
-```
-
-### Link Recommendation
-```json
-{
- "event": "link_recommendation",
- "data": {
- "post_id": 456,
- "target_url": "https://example.com/page",
- "anchor": "example link",
- "priority": "normal"
- }
-}
-```
-
-### Optimizer Request
-```json
-{
- "event": "optimizer_job_completed",
- "data": {
- "post_id": 456,
- "job_id": 789,
- "status": "completed",
- "score_changes": { ... },
- "recommendations": [ ... ]
- }
-}
-```
-
----
-
-## Next Steps
-
-Phase 6 is complete. All webhook functionality is implemented with proper security, logging, and connection checks.
-
diff --git a/igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md b/igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md
deleted file mode 100644
index 14591a62..00000000
--- a/igny8-wp-integration-plugin/docs/STATUS_SYNC_DOCUMENTATION.md
+++ /dev/null
@@ -1,249 +0,0 @@
-# Status Sync & Content ID Documentation
-
-**Last Updated**: 2025-10-17
-
----
-
-## Overview
-
-This document explains how the plugin handles status synchronization and content_id tracking between IGNY8 and WordPress.
-
----
-
-## Status Mapping
-
-### IGNY8 → WordPress
-
-When content arrives from IGNY8, status is mapped as follows:
-
-| IGNY8 Status | WordPress Status | Description |
-|--------------|------------------|-------------|
-| `completed` | `publish` | Content is published |
-| `draft` | `draft` | Content is draft |
-| `pending` | `pending` | Content pending review |
-| `scheduled` | `future` | Content scheduled |
-| `archived` | `trash` | Content archived |
-
-### WordPress → IGNY8
-
-When WordPress post status changes, it's mapped back to IGNY8:
-
-| WordPress Status | IGNY8 Status | Description |
-|------------------|--------------|-------------|
-| `publish` | `completed` | Post is published |
-| `draft` | `draft` | Post is draft |
-| `pending` | `pending` | Post pending review |
-| `private` | `completed` | Post is private (published) |
-| `trash` | `archived` | Post is deleted |
-| `future` | `scheduled` | Post is scheduled |
-
----
-
-## Content ID Tracking
-
-### Storage
-
-The plugin stores IGNY8 `content_id` in post meta:
-- **Meta Key**: `_igny8_content_id`
-- **Type**: Integer
-- **REST API**: Available via `/wp-json/wp/v2/posts?meta_key=_igny8_content_id&meta_value=123`
-
-### Task ID Tracking
-
-The plugin also stores IGNY8 `task_id`:
-- **Meta Key**: `_igny8_task_id`
-- **Type**: Integer
-- **REST API**: Available via `/wp-json/wp/v2/posts?meta_key=_igny8_task_id&meta_value=456`
-
----
-
-## Response to IGNY8
-
-When content is created/updated in WordPress, the plugin responds to IGNY8 with:
-
-```json
-{
- "assigned_post_id": 123,
- "post_url": "https://example.com/post/",
- "wordpress_status": "publish",
- "status": "completed",
- "synced_at": "2025-10-17 12:00:00",
- "post_type": "post",
- "content_type": "post",
- "content_id": 789
-}
-```
-
-### Response Fields
-
-- **`assigned_post_id`**: WordPress post ID
-- **`post_url`**: Full URL to the post
-- **`wordpress_status`**: Actual WordPress status (publish/pending/draft)
-- **`status`**: IGNY8 mapped status (completed/pending/draft)
-- **`synced_at`**: Timestamp of sync
-- **`post_type`**: WordPress post type
-- **`content_type`**: IGNY8 content type
-- **`content_id`**: IGNY8 content ID (if provided)
-
----
-
-## REST API Endpoints
-
-The plugin provides REST API endpoints for IGNY8 to query WordPress:
-
-### 1. Get Post by Content ID
-
-**Endpoint**: `GET /wp-json/igny8/v1/post-by-content-id/{content_id}`
-
-**Response**:
-```json
-{
- "success": true,
- "data": {
- "post_id": 123,
- "title": "Post Title",
- "status": "publish",
- "wordpress_status": "publish",
- "igny8_status": "completed",
- "url": "https://example.com/post/",
- "post_type": "post",
- "content_id": 789,
- "task_id": 456,
- "last_synced": "2025-10-17 12:00:00"
- }
-}
-```
-
-### 2. Get Post by Task ID
-
-**Endpoint**: `GET /wp-json/igny8/v1/post-by-task-id/{task_id}`
-
-**Response**: Same format as above
-
-### 3. Get Post Status by Content ID
-
-**Endpoint**: `GET /wp-json/igny8/v1/post-status/{content_id}`
-
-**Response**:
-```json
-{
- "success": true,
- "data": {
- "post_id": 123,
- "wordpress_status": "publish",
- "igny8_status": "completed",
- "status_mapping": {
- "publish": "completed",
- "draft": "draft",
- "pending": "pending",
- "private": "completed",
- "trash": "archived",
- "future": "scheduled"
- },
- "content_id": 789,
- "url": "https://example.com/post/",
- "last_synced": "2025-10-17 12:00:00"
- }
-}
-```
-
----
-
-## Status Flow
-
-### When Content Arrives from IGNY8
-
-1. **Receive** content with `content_type`, `status`, `content_id`, `task_id`
-2. **Map** IGNY8 status to WordPress status
-3. **Create** WordPress post with mapped status
-4. **Store** `content_id` and `task_id` in post meta
-5. **Respond** to IGNY8 with:
- - WordPress post ID
- - WordPress actual status
- - IGNY8 mapped status
- - Post URL
- - Content ID
-
-### When WordPress Status Changes
-
-1. **Detect** status change via `save_post` or `transition_post_status` hook
-2. **Get** `task_id` and `content_id` from post meta
-3. **Map** WordPress status to IGNY8 status
-4. **Update** IGNY8 task with:
- - WordPress actual status
- - IGNY8 mapped status
- - Post URL
- - Content ID (if available)
- - Sync timestamp
-
----
-
-## Available Meta Fields
-
-All fields are available for IGNY8 to read via REST API:
-
-- `_igny8_task_id` - IGNY8 task ID
-- `_igny8_content_id` - IGNY8 content ID
-- `_igny8_cluster_id` - IGNY8 cluster ID
-- `_igny8_sector_id` - IGNY8 sector ID
-- `_igny8_keyword_ids` - Array of keyword IDs
-- `_igny8_wordpress_status` - WordPress post status
-- `_igny8_last_synced` - Last sync timestamp
-
----
-
-## Query Examples
-
-### Via WordPress REST API
-
-```bash
-# Get post by content_id
-GET /wp-json/wp/v2/posts?meta_key=_igny8_content_id&meta_value=123
-
-# Get post by task_id
-GET /wp-json/wp/v2/posts?meta_key=_igny8_task_id&meta_value=456
-
-# Get all IGNY8 posts
-GET /wp-json/wp/v2/posts?meta_key=_igny8_task_id
-```
-
-### Via IGNY8 REST API Endpoints
-
-```bash
-# Get post by content_id (with status info)
-GET /wp-json/igny8/v1/post-by-content-id/123
-
-# Get post status only
-GET /wp-json/igny8/v1/post-status/123
-
-# Get post by task_id
-GET /wp-json/igny8/v1/post-by-task-id/456
-```
-
----
-
-## Authentication
-
-REST API endpoints require:
-- IGNY8 API authentication (access token)
-- Authorization header: `Bearer {access_token}`
-
-Or internal use when IGNY8 is connected.
-
----
-
-## Status Flags Available
-
-✅ **Task ID** - Stored and queryable
-✅ **Content ID** - Stored and queryable
-✅ **WordPress Status** - Stored and sent to IGNY8
-✅ **IGNY8 Status** - Mapped and sent to IGNY8
-✅ **Post Type** - Stored and sent to IGNY8
-✅ **Content Type** - Stored and sent to IGNY8
-✅ **Sync Timestamp** - Stored and sent to IGNY8
-✅ **Post URL** - Sent to IGNY8
-
----
-
-**All status information is available for IGNY8 to read and query!**
-
diff --git a/igny8-wp-integration-plugin/docs/STYLE_GUIDE.md b/igny8-wp-integration-plugin/docs/STYLE_GUIDE.md
deleted file mode 100644
index 059cd8ee..00000000
--- a/igny8-wp-integration-plugin/docs/STYLE_GUIDE.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# Style Guide - IGNY8 WordPress Bridge
-
-**Last Updated**: 2025-10-17
-
----
-
-## CSS Architecture
-
-### No Inline CSS Policy
-
-✅ **All styles are in `admin/assets/css/admin.css`**
-❌ **No inline `style=""` attributes**
-❌ **No `