# SearchJet: WordPress Abilities API Plan + Sentient AI Cross-Sell

> **Status:** Draft | **Target:** WP 6.9+ | **Author:** Maidul

---

## Table of Contents

1. [Overview](#overview)
2. [Part 1: WordPress Abilities API Integration](#part-1-wordpress-abilities-api-integration)
   - [What is the Abilities API?](#what-is-the-abilities-api)
   - [File Structure](#file-structure)
   - [Category Registration](#category-registration)
   - [Ability Definitions](#ability-definitions)
   - [Implementation Pattern](#implementation-pattern)
   - [Backward Compatibility](#backward-compatibility)
   - [Testing](#testing)
3. [Part 2: Sentient AI Cross-Sell](#part-2-sentient-ai-cross-sell)
   - [Why Sentient AI for SearchJet Users?](#why-sentient-ai-for-searchjet-users)
   - [Feature Gap Analysis](#feature-gap-analysis)
   - [Cross-Sell Touch Points in SearchJet UI](#cross-sell-touch-points-in-searchjet-ui)
   - [Existing Integration (SearchJet_Integration.php)](#existing-integration-searchjet_integrationphp)
4. [Execution Order](#execution-order)

---

## Overview

This document covers two initiatives:

1. **Abilities API for SearchJet** – Register SearchJet's capabilities (search, indexing, analytics, AI agents) as standard WordPress Abilities, making them discoverable by AI agents, automation tools, and external systems via the `/wp-abilities/v1/` REST namespace.

2. **Sentient AI Cross-Sell** – Identify complementary features that Sentient AI (our AI chatbot plugin) offers to SearchJet users, and plan promotion points within the SearchJet admin UI.

---

## Part 1: WordPress Abilities API Integration

### What is the Abilities API?

The WordPress Abilities API (shipping in WP 6.9) provides a central registry for exposing discrete units of functionality ("Abilities") with:

- **JSON Schema** input/output validation
- **Permission callbacks** for access control
- **Automatic REST API exposure** under `/wp-abilities/v1/`
- **Annotations** (`readonly`, `destructive`, `idempotent`) for AI agents

Key functions:
- `wp_register_ability_category()` – on `wp_abilities_api_categories_init` hook
- `wp_register_ability()` – on `wp_abilities_api_init` hook
- `wp_get_ability()`, `wp_get_abilities()`, `wp_has_ability()`
- `$ability->execute()`, `$ability->check_permissions()`

### File Structure

```
includes/
└── Abilities/                          # NEW
    ├── AbilitiesProvider.php            # Registers category + bootstraps all abilities
    ├── IndexAbilities.php               # Index management abilities
    ├── AnalyticsAbilities.php           # Analytics & AI usage abilities
    └── SearchAbilities.php              # Search & health check abilities
```

### Category Registration

**File:** `AbilitiesProvider.php`

```php
add_action( 'wp_abilities_api_categories_init', [self::class, 'register_category'] );

public static function register_category(): void {
    if ( ! function_exists( 'wp_register_ability_category' ) ) {
        return;
    }

    wp_register_ability_category( 'searchjet', [
        'label'       => __( 'SearchJet Search', 'searchjet-instant-search' ),
        'description' => __( 'AI-powered instant search and indexing capabilities.', 'searchjet-instant-search' ),
    ] );
}
```

### Ability Definitions

#### 1. Search & Health (`SearchAbilities.php`)

| Ability | Readonly | Input Schema | Output Schema | Permission |
|---|---|---|---|---|
| `searchjet/instant-search` | ✅ | `{ query: string, filters?: object, page?: int, limit?: int }` | `{ hits: array, total: int, facets?: object }` | `__return_true` |
| `searchjet/search-health` | ✅ | `{}` | `{ status: string, doc_count: int, last_sync: string, plan: string }` | `manage_options` |

**`instant-search` execute callback** delegates to:
```php
\SearchJet\API\SearchJetRemoteClient::search( $input['query'], $input );
```

**`search-health` execute callback** wraps:
```php
// Combines: RestController ping + SearchJetRemoteClient fetchClientInfo
{
    'status'    => 'ok' | 'error',
    'version'   => SEARCHJET_VERSION,
    'doc_count' => $docCount,
    'plan'      => PlanManager::get_current_plan(),
    'connected' => ! empty( get_option( 'searchjet_api_key' ) ),
}
```

#### 2. Index Management (`IndexAbilities.php`)

| Ability | Readonly | Input Schema | Output Schema | Permission |
|---|---|---|---|---|
| `searchjet/get-index-stats` | ✅ | `{}` | `{ docs_total: int, post_types: array, last_indexed: string, task_count: int }` | `manage_options` |
| `searchjet/reindex-content` | ❌ | `{ post_types?: string[], purge_first?: bool }` | `{ task_id: string, status: string, queued: int }` | `manage_options` |
| `searchjet/index-health` | ✅ | `{}` | `{ connected: bool, index_size: int, last_error?: string }` | `manage_options` |

**Callbacks reuse:**
- `get-index-stats` → `SearchJetRemoteClient::getIndexStats()`
- `reindex-content` → `\SearchJet\Services\Reindexer::reindexAll( $post_types )`
- `index-health` → `MeilisearchClient::getIndexStats()` + index connection check

#### 3. Analytics & AI (`AnalyticsAbilities.php`)

| Ability | Readonly | Input Schema | Output Schema | Permission |
|---|---|---|---|---|
| `searchjet/search-analytics` | ✅ | `{ period?: '7d'\|'30d'\|'90d', group_by?: string }` | `{ total_queries: int, top_queries: array, zero_results: int }` | `manage_options` |
| `searchjet/list-ai-actions` | ✅ | `{}` | `{ actions: array }` | `manage_options` |
| `searchjet/dispatch-ai-action` | ❌ | `{ action: string, payload?: object }` | `{ ok: bool, result?: mixed, error?: string }` | `manage_options` |
| `searchjet/ai-usage` | ✅ | `{}` | `{ total: int, remaining: int, limit: int }` | `manage_options` |

**Callbacks reuse:**
- `search-analytics` → `SearchJetRemoteClient::fetchSearchAnalytics()`
- `list-ai-actions` → `\SearchJet\AI\Router::actions()`
- `dispatch-ai-action` → `\SearchJet\AI\Router::dispatch()`
- `ai-usage` → `AIAnswerEndpoint` / `AIAnswerGenerator::checkUsageLimit()`

### Implementation Pattern

Each ability class follows this pattern:

```php
namespace SearchJet\Abilities;

class SearchAbilities {

    public static function init(): void {
        if ( ! function_exists( 'wp_register_ability' ) ) {
            return; // WP < 6.9 — no-op
        }

        add_action( 'wp_abilities_api_init', [self::class, 'register_abilities'] );
    }

    public static function register_abilities(): void {
        $common = [
            'category'            => 'searchjet',
            'permission_callback' => function () {
                return current_user_can( 'manage_options' );
            },
            'meta'                => [
                'show_in_rest' => true,
                'annotations'  => [ 'readonly' => true ],
            ],
        ];

        wp_register_ability( 'searchjet/instant-search', [
            'label'            => __( 'Instant Search', 'searchjet-instant-search' ),
            'description'      => __( 'Perform a search against the SearchJet index.', 'searchjet-instant-search' ),
            'category'         => 'searchjet',
            'input_schema'     => [
                'type'       => 'object',
                'properties' => [
                    'query' => [ 'type' => 'string', 'description' => 'Search query' ],
                    'limit' => [ 'type' => 'integer', 'default' => 10 ],
                ],
                'required'   => [ 'query' ],
            ],
            'output_schema'    => [
                'type'       => 'object',
                'properties' => [
                    'hits'  => [ 'type' => 'array', 'description' => 'Search results' ],
                    'total' => [ 'type' => 'integer', 'description' => 'Total results count' ],
                ],
            ],
            'execute_callback'    => [ self::class, 'execute_instant_search' ],
            'permission_callback' => '__return_true',
            'meta'                => [
                'show_in_rest' => true,
                'annotations'  => [ 'readonly' => true ],
            ],
        ] );

        // ... register remaining abilities
    }

    public static function execute_instant_search( $input ) {
        $results = \SearchJet\API\SearchJetRemoteClient::search( $input['query'], $input );
        return rest_ensure_response( $results );
    }
}
```

### Wiring in Plugin Bootstrap

**In `AbilitiesProvider.php`:**

```php
namespace SearchJet\Abilities;

class AbilitiesProvider {
    public static function init(): void {
        if ( ! function_exists( 'wp_register_ability_category' ) ) {
            return; // WP < 6.9
        }

        add_action( 'wp_abilities_api_categories_init', [self::class, 'register_category'] );
        add_action( 'wp_abilities_api_init', [self::class, 'register_abilities'] );
    }

    public static function register_category(): void { /* ... */ }
    public static function register_abilities(): void {
        SearchAbilities::register_abilities();
        IndexAbilities::register_abilities();
        AnalyticsAbilities::register_abilities();
    }
}
```

**In `includes/Plugin.php`:**

```php
// Add to existing init():
\SearchJet\Abilities\AbilitiesProvider::init();
```

### Backward Compatibility

All Abilities API calls are wrapped in `function_exists()` checks. On WP < 6.9, the entire `Abilities/` directory is a no-op — no errors, no notices. SearchJet continues working exactly as before.

### Testing

- **Unit:** Test each `execute_callback` with valid/invalid input against its schema
- **Integration:** Verify abilities appear at `GET /wp-abilities/v1/abilities?category=searchjet`
- **Edge cases:** Missing API key, expired plan, network failure (callbacks should return `WP_Error`)
- **WP version matrix:** WP 6.8 (no Abilities API) + WP 6.9 (with Abilities API)

---

## Part 2: Sentient AI Cross-Sell

### Why Sentient AI for SearchJet Users?

SearchJet delivers **AI-powered instant search** for WooCommerce & WordPress. But on-site search is only one piece of the AI puzzle. Sentient AI complements SearchJet by adding **AI-powered conversation, site management, and content optimization** — features SearchJet does not provide.

### Feature Gap Analysis

| Feature Area | SearchJet | Sentient AI | Cross-Sell Message |
|---|---|---|---|
| **Search** | ✅ Instant, typo-tolerant, faceted | ✅ Semantic vector search (local + cloud) | "Add semantic FAQ search that works without any API key" |
| **Chatbot** | ❌ | ✅ Multi-provider AI chatbot | "Let visitors ask follow-up questions after searching" |
| **FAQ Management** | ❌ | ✅ Full FAQ DB with matching | "Store & serve answers for common queries your search misses" |
| **Zero-Cost Answers** | ❌ | ✅ Browser-based FAQ matching | "Answer 30%+ of queries instantly — zero API cost" |
| **Autopilot** | ❌ | ✅ Chat-based site management | "Reindex, check health, pull analytics — all through chat" |
| **GEO Scanner** | ❌ | ✅ AI search engine optimization | "Optimize content for Google SGE, Perplexity, ChatGPT Search" |
| **MCP Server** | ❌ | ✅ External AI agent protocol | "Connect Claude Desktop / Cursor directly to your site" |
| **Local Vector DB** | ❌ (SaaS) | ✅ MySQL vector engine | "Semantic search on shared hosting — no SaaS needed" |
| **Emotion Detection** | ❌ | ✅ Empathy Engine | "Understand when users are frustrated with search results" |
| **Privacy Mode** | ❌ | ✅ Zero external API calls | "AI features on regulated sites — no data leaves your server" |
| **Multi-Provider AI** | SaaS-locked | ✅ OpenAI/Claude/Gemini/OpenRouter | "Choice of AI provider, not locked into one" |
| **Sentiment Analytics** | ❌ | ✅ Emotion trends over time | "Track how users feel about their search experience" |

### Existing Integration (SearchJet_Integration.php)

Sentient AI already ships `src/Includes/SearchJet_Integration.php` which:

1. **Indexes content** into SearchJet on `save_post` / `delete_post`
2. **Searches** SearchJet from the chatbot context
3. **Pulls analytics** from SearchJet for dashboard display
4. **Has admin settings** for API Key + Site ID

**Problem:** SearchJet users have no idea this exists. There is no mention of Sentient AI anywhere in the SearchJet admin.

### Cross-Sell Touch Points in SearchJet UI

#### 1. Dashboard Card (high priority)

Add a card to the SearchJet dashboard when the user is connected:

```
┌─────────────────────────────────────────────┐
│  🤖 Also try: Sentient AI Chatbot          │
│                                              │
│  Add an AI chatbot that:                     │
│  • Answers follow-up questions after search  │
│  • Auto-indexes FAQs into SearchJet          │
│  • Runs on shared hosting — no server setup  │
│  • Works in Privacy Mode for regulated sites │
│                                              │
│  [Learn More →]  [Install Now →]             │
└─────────────────────────────────────────────┘
```

**Conditions:** Only show when `searchjet_api_key` is set and `sentient-ai/sentient-ai.php` is not active.

#### 2. Documentation Page (medium priority)

Add a "Recommended Plugins" section to `DocumentationPage.php`:

- **Sentient AI** – AI chatbot + Autopilot + GEO Scanner
- Brief description of integration benefits
- Link to install `/wp-admin/plugin-install.php?s=sentient+ai+maidul&tab=search&type=term`

#### 3. Onboarding Wizard (low priority)

In `SettingsPage.php` wizard, add a final step:
> "Want AI-powered conversations too? Install Sentient AI — it integrates seamlessly with SearchJet."

#### 4. Banner Notice (optional)

On admin pages when the plugin is connected but Sentient AI is not installed, show a dismissible notice:
> "Supercharge your site with Sentient AI chatbot — works great with SearchJet. [Dismiss]"

---

## Execution Order

| Step | Component | Files | Dependencies |
|---|---|---|---|
| **Phase 1: Abilities API** | | | |
| 1 | Create `AbilitiesProvider.php` | `includes/Abilities/AbilitiesProvider.php` | WP 6.9+ |
| 2 | Implement `SearchAbilities` | `includes/Abilities/SearchAbilities.php` | `SearchJetRemoteClient`, `PlanManager` |
| 3 | Implement `IndexAbilities` | `includes/Abilities/IndexAbilities.php` | `Reindexer`, `MeilisearchClient` |
| 4 | Implement `AnalyticsAbilities` | `includes/Abilities/AnalyticsAbilities.php` | `Router`, `AIAnswerGenerator` |
| 5 | Wire into `Plugin::init()` | `includes/Plugin.php` | Steps 1-4 |
| 6 | Unit tests | `tests/Unit/AbilitiesProviderTest.php` | Steps 1-5 |
| **Phase 2: Cross-Sell** | | | |
| 7 | Add dashboard card | `includes/Admin/DashboardPage.php` | Design decision on card content |
| 8 | Add docs section | `includes/Admin/DocumentationPage.php` | Copywriting |
| 9 | Optional: onboarding step | `includes/Admin/SettingsPage.php` | Step 7 first |
| **Phase 3: Documentation** | | | |
| 10 | Update `API_DOCUMENTATION.md` | `docs/API_DOCUMENTATION.md` | Steps 1-5 |
| 11 | Update `readme.txt` | `readme.txt` | Steps 1-9 |

---

## Open Questions

1. **`searchjet/instant-search` permission** — public (`__return_true`) or require API key header? Public matches current JS SDK behavior but means anyone can query the index via the REST API.

2. **Sentient AI cross-sell card design** — simple text card or rich component with screenshot preview?

3. **Should reindex also accept `post_ids` array** for selective reindex, or only `post_types`? Current `Reindexer` supports both.

4. **Do we want to show the cross-sell only to users on the Free plan** (who might benefit most from Sentient AI's free FAQ matching) or to all users?

---

*Document version 1.0 — Last updated July 2026*
