$npx -y skills add Lonsdale201/wp-agent-skills --skill fluentcrm-overviewOrient skill for FluentCRM extension development. Covers the
| 1 | # FluentCRM: developer overview |
| 2 | |
| 3 | Pick this skill first when starting a new FluentCRM extension or when you don't yet know which contract to extend. It's the orient layer; it doesn't teach any single extension point in depth — it lays out the file map and points you at the focused skills (`fluentcrm-funnel-trigger`, `fluentcrm-funnel-action`, `fluentcrm-funnel-benchmark`, `fluentcrm-rest-options`). |
| 4 | |
| 5 | ## Plugin identity |
| 6 | |
| 7 | | Field | Value | Source | |
| 8 | |--|--|--| |
| 9 | | Slug | `fluent-crm` | header | |
| 10 | | Version | `3.1.8` | [fluent-crm.php](../../../../wp-content/plugins/fluent-crm/fluent-crm.php) | |
| 11 | | Pro slug | `fluentcampaign-pro` | header | |
| 12 | | Pro version | `3.1.8` | [fluentcampaign-pro.php](../../../../wp-content/plugins/fluentcampaign-pro/fluentcampaign-pro.php) | |
| 13 | | Boot constant | `FLUENTCRM` ('fluentcrm') | [fluent-crm.php:20](fluent-crm.php) | |
| 14 | | Pro constant | `FLUENTCAMPAIGN_PLUGIN_VERSION` | [fluentcampaign-pro.php](fluentcampaign-pro.php) | |
| 15 | | Path constant (Free) | `FLUENTCRM_PLUGIN_PATH` | [fluent-crm.php:22](fluent-crm.php) | |
| 16 | | Path constant (Pro) | `FLUENTCAMPAIGN_PLUGIN_PATH` | [fluentcampaign-pro.php:20](fluentcampaign-pro.php) | |
| 17 | | Min PHP (verified) | 7.4 | composer.json | |
| 18 | |
| 19 | **Active-detection** — never test for `class_exists('FluentCRM')` (no such class). Use: |
| 20 | |
| 21 | ```php |
| 22 | public static function isFluentCRMActive(): bool { |
| 23 | return defined('FLUENTCRM'); |
| 24 | } |
| 25 | |
| 26 | public static function isFluentCRMProActive(): bool { |
| 27 | return defined('FLUENTCAMPAIGN_PLUGIN_VERSION') |
| 28 | && version_compare(FLUENTCAMPAIGN_PLUGIN_VERSION, '3.1.8', '>='); |
| 29 | } |
| 30 | ``` |
| 31 | |
| 32 | Use a lower Pro floor only when you have verified the exact Pro API you call. Core 3.1.8 declares `FLUENTCRM_MIN_PRO_VERSION` as `3.1.8`, so extension code that depends on current Pro internals should not silently accept older Pro builds. |
| 33 | |
| 34 | ### Active-detection canon for OTHER plugins (load-order trap) |
| 35 | |
| 36 | Triggers / actions / benchmarks register on `fluentcrm_loaded:5`, which fires from FluentCRM's own `plugins_loaded:10` callback. Any dep check in your registrar runs at that point — and **only** symbols declared at FILE LOAD are guaranteed available. WordPress runs `plugins_loaded:10` callbacks in plugin registration order (non-deterministic across installs), so any symbol that's only set up INSIDE another plugin's `plugins_loaded` callback is racy. |
| 37 | |
| 38 | **Safe** (declared at file load — top-level scope, no conditional require): |
| 39 | |
| 40 | | Plugin | Use | Why | |
| 41 | |--|--|--| |
| 42 | | WooCommerce | `class_exists('WooCommerce')` | `woocommerce.php` includes `class-woocommerce.php` at file scope | |
| 43 | | FluentCampaign Pro | `defined('FLUENTCAMPAIGN_PLUGIN_VERSION')` | `fluentcampaign-pro.php` defines the constant before bootstrapping | |
| 44 | | WC Memberships | `class_exists('WC_Memberships_Loader')` | top-level loader class, `WC_Memberships` itself is loaded inside `init_plugin()` and is racy | |
| 45 | | LW LMS | `class_exists('LightweightPlugins\\LMS\\Plugin')` | autoloader registered at file scope | |
| 46 | | Jet Engine | `class_exists('Jet_Engine')` | top-level class declaration | |
| 47 | |
| 48 | **Unsafe** (declared inside the dep's own callback — DO NOT use at registration time): |
| 49 | |
| 50 | ```php |
| 51 | // WRONG — wc_memberships() function is declared inside Memberships's |
| 52 | // plugins_loaded:10 callback. If your registrar runs before Memberships's |
| 53 | // callback, function_exists returns false and your action is silently |
| 54 | // excluded from the editor. |
| 55 | return function_exists('wc_memberships'); |
| 56 | |
| 57 | // RIGHT — WC_Memberships_Loader is at file scope. |
| 58 | return class_exists('WC_Memberships_Loader'); |
| 59 | ``` |
| 60 | |
| 61 | If the dep's main object only exists post-init, find a top-level loader/registrar to test against (every well-formed plugin has o |