{"id":311005,"date":"2026-06-26T06:49:51","date_gmt":"2026-06-26T06:49:51","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/acrossai-abilities-manager\/"},"modified":"2026-08-26T17:33:00","modified_gmt":"2026-08-26T17:33:00","slug":"acrossai-abilities-manager","status":"publish","type":"plugin","link":"https:\/\/it.wordpress.org\/plugins\/acrossai-abilities-manager\/","author":15295430,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.0.31","stable_tag":"0.0.31","tested":"7.0.4","requires":"6.9","requires_php":"8.1","requires_plugins":null,"header_name":"AcrossAI Abilities Manager","header_author":"raftaar1191","header_description":"Manage and customize the abilities of AcrossAI on your WordPress site. Tailor the AI's capabilities to suit your needs, enhancing user experience and engagement.","assets_banners_color":"fdfdfe","last_updated":"2026-08-26 17:33:00","external_support_url":"","external_repository_url":"","donate_link":"https:\/\/github.com\/acrosswp\/acrossai-abilities-manager","header_plugin_uri":"https:\/\/acrossai.co\/","header_author_uri":"https:\/\/profiles.wordpress.org\/raftaar1191\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":1066,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.0.1":{"tag":"0.0.1","author":"raftaar1191","date":"2026-06-26 06:49:29"},"0.0.10":{"tag":"0.0.10","author":"raftaar1191","date":"2026-07-18 00:50:18"},"0.0.11":{"tag":"0.0.11","author":"raftaar1191","date":"2026-07-18 15:51:33"},"0.0.12":{"tag":"0.0.12","author":"raftaar1191","date":"2026-07-18 16:15:59"},"0.0.13":{"tag":"0.0.13","author":"raftaar1191","date":"2026-07-20 03:01:52"},"0.0.14":{"tag":"0.0.14","author":"raftaar1191","date":"2026-07-20 03:08:58"},"0.0.15":{"tag":"0.0.15","author":"raftaar1191","date":"2026-07-20 19:33:50"},"0.0.17":{"tag":"0.0.17","author":"raftaar1191","date":"2026-07-25 15:38:31"},"0.0.18":{"tag":"0.0.18","author":"raftaar1191","date":"2026-07-27 09:05:53"},"0.0.19":{"tag":"0.0.19","author":"raftaar1191","date":"2026-07-31 07:41:16"},"0.0.2":{"tag":"0.0.2","author":"raftaar1191","date":"2026-07-02 12:04:52"},"0.0.20":{"tag":"0.0.20","author":"raftaar1191","date":"2026-08-02 14:45:00"},"0.0.21":{"tag":"0.0.21","author":"raftaar1191","date":"2026-08-08 09:21:59"},"0.0.22":{"tag":"0.0.22","author":"raftaar1191","date":"2026-08-10 18:14:44"},"0.0.23":{"tag":"0.0.23","author":"raftaar1191","date":"2026-08-11 09:38:46"},"0.0.24":{"tag":"0.0.24","author":"raftaar1191","date":"2026-08-12 17:51:24"},"0.0.25":{"tag":"0.0.25","author":"raftaar1191","date":"2026-08-13 13:28:41"},"0.0.26":{"tag":"0.0.26","author":"raftaar1191","date":"2026-08-13 19:38:26"},"0.0.27":{"tag":"0.0.27","author":"raftaar1191","date":"2026-08-13 20:23:39"},"0.0.29":{"tag":"0.0.29","author":"raftaar1191","date":"2026-08-18 09:29:25"},"0.0.3":{"tag":"0.0.3","author":"raftaar1191","date":"2026-07-02 12:30:19"},"0.0.30":{"tag":"0.0.30","author":"raftaar1191","date":"2026-08-19 13:19:05"},"0.0.31":{"tag":"0.0.31","author":"raftaar1191","date":"2026-08-26 17:33:00"},"0.0.4":{"tag":"0.0.4","author":"raftaar1191","date":"2026-07-03 22:38:31"},"0.0.5":{"tag":"0.0.5","author":"raftaar1191","date":"2026-07-04 01:29:09"},"0.0.6":{"tag":"0.0.6","author":"raftaar1191","date":"2026-07-13 14:09:04"},"0.0.7":{"tag":"0.0.7","author":"raftaar1191","date":"2026-07-13 18:14:55"},"0.0.8":{"tag":"0.0.8","author":"raftaar1191","date":"2026-07-17 00:32:32"},"0.0.9":{"tag":"0.0.9","author":"raftaar1191","date":"2026-07-17 16:29:43"}},"upgrade_notice":{"0.0.21":"<p>Bumps the <code>wpboilerplate\/wpb-access-control<\/code> composer dependency from <code>^2.0.0<\/code> to <code>^3.1.0<\/code> \u2014 two library releases in one hop. v3.0.0 removed two plugin-dependent providers (<code>BuddyBossProfileTypeProvider<\/code>, <code>MemberPressMembershipProvider<\/code>) that were extracted into a separate add-on (<code>acrossai\/user-access-pro<\/code>); this plugin uses only the core <code>AccessControlManager<\/code> + <code>RuleTable<\/code> classes, so no consumer code change is required. v3.1.0 adds a new &quot;Any logged-in user&quot; option to the Access Control dropdown (backed by a new <code>authenticated<\/code> sentinel rule type), and renames &quot;Everyone (no restriction)&quot; \u2192 &quot;Public (no login required)&quot; for clarity. Existing rules unaffected. Safe upgrade from 0.0.20.<\/p>","0.0.20":"<p>Routes the access-control library-missing warning through the new shared AcrossAI notice hub (<code>acrossai_notices<\/code> filter shipped by <code>acrossai-co\/main-menu<\/code> 0.0.30). Instead of a raw wp-admin banner on every screen, the notice now appears on the new AcrossAI \u2192 Notices submenu (with a count bubble on the menu label) and as a single top-of-page summary banner (&quot;AcrossAI has N notifications for your attention \u2014 View notices \u2192&quot;) whose dismissal persists per user until the notice set changes. The fail-open semantics and message copy are unchanged. No breaking changes; existing abilities unaffected. Safe upgrade from 0.0.19.<\/p>","0.0.19":"<p>Adds a blue promotional callout on the ability edit form (MCP Exposure section) that advertises the sibling AcrossAI MCP Manager plugin when it is not installed \/ active. The callout links to the AcrossAI Add-ons page for install and to acrossai.co\/mcp-manager\/ for more info. Fully suppressed when the AcrossAI MCP Manager plugin is active. Also bumps the <code>acrossai-co\/main-menu<\/code> composer dependency from 0.0.27 to 0.0.29 \u2014 0.0.28 refreshes the Add-ons page baseline catalogue (AcrossAI Abilities Manager + AcrossAI MCP Manager + AI Connectors) with shared brand icon, <code>contain<\/code>-fitted icon boxes, fixed 3-column grid layout, and a new optional <code>learn_more_url<\/code> field; 0.0.29 reworks the card action states so active add-ons render a non-clickable &quot;\u25cf Running&quot; pill (deactivation stays in Plugins \u2192 Installed Plugins) and installed non-wp.org add-ons now show an in-page Activate button instead of always linking out. No breaking changes; existing abilities unaffected. Safe upgrade from 0.0.18.<\/p>","0.0.18":"<p>New third-party integration framework (Feature 060) with Advanced Custom Fields as the first concrete integration \u2014 flip one toggle on the new &quot;Acf&quot; tab of the Ability Library page to enable ACF&#039;s AI abilities without editing code. Also new: extensibility surface so other AcrossAI plugins can add their own cards to an integration&#039;s tab, filterable capability check for the toggle (via <code>acrossai_integration_toggle_capability<\/code>), and audit action (<code>acrossai_integration_toggle_denied<\/code>). Fixes a sparse-storage bug that could silently strip the integration ON state. Bumps the <code>acrossai-co\/main-menu<\/code> composer dependency from 0.0.23 to 0.0.27 to land two WordPress.org plugin directory guideline #8 fixes: the Consultations submenu now uses an external-link CTA instead of an embedded Calendly iframe, and the Add-ons page install action is now WordPress.org-only (non-wp.org cards render as external &quot;Get add-on \u2197&quot; links opening the vendor&#039;s site in a new tab). No breaking changes; existing abilities unaffected. Safe upgrade from 0.0.17.<\/p>","0.0.17":"<p>BREAKING \u2014 every ability slug has been renamed. Namespace shortens from <code>acrossai-abilities-manager\/<\/code> to <code>acrossai\/<\/code>; suffixes flip to verb-first form (e.g. <code>site-title-get<\/code> \u2192 <code>get-site-title<\/code>, <code>theme-activate<\/code> \u2192 <code>activate-theme<\/code>). External callers (custom code, saved MCP client configs, ACL entries created outside the plugin&#039;s UI, scripts calling <code>\/wp-json\/wp-abilities\/v1\/abilities\/acrossai-abilities-manager\/\/run<\/code>) must update their slug references to <code>\/wp-json\/wp-abilities\/v1\/abilities\/acrossai\/\/run<\/code>. No backwards-compatibility aliases; no automatic data migration \u2014 clear pre-existing overrides + ACL rules keyed on old slugs from the admin UI and re-add them under the new names. Also new: 7 Recovery Mode abilities (detect recovery, list paused plugins\/themes, unpause, exit URL, fatal-error log filter) and <code>core\/reinstall-wp-core<\/code>. 162 PHP class files renamed to match slugs (internal-only; PSR-4 autoload picks up automatically). PHP 8.1+ \/ WP 6.9+ floor unchanged.<\/p>","0.0.15":"<p>UI-only release. Replaces the Custom Abilities Bulk Actions dropdown (Publish \/ Unpublish \/ Delete) with Site Access, MCP Exposure, User Access, and Overrides operations that match the per-row edit drawer. Row-level checkbox now works on every ability regardless of Source. Reuses existing REST endpoints; no new database tables, no new endpoints, no PHP changes, no dependency changes, no permission changes. Also fixes a bug that stored composer User Access rule keys with the ability slug&#039;s <code>\/<\/code> character stripped when applied via the (new) bulk path. Safe upgrade.<\/p>","0.0.14":"<p>wp.org assets only. Refreshes the banner artwork and renames both banner files from <code>banner{width}x{height}.png<\/code> to the WP.org-canonical <code>banner-{width}x{height}.png<\/code> (the 0.0.13 filenames were not being auto-detected by the plugin directory). No plugin code touched; no REST, DB, or capability changes. Safe upgrade.<\/p>","0.0.13":"<p>Docs + wp.org assets only. Adds <code>specs\/054-ability-gap-audit\/<\/code> (a reference audit of abilities that external AI-tool inventories expect but the plugin does not yet expose) and commits the previously-untracked <code>.wordpress-org<\/code> banner (1544\u00d7500 + 772\u00d7250) and a sixth screenshot covering the Settings page. No functional changes; no REST, DB, or capability changes; no code touched under <code>includes\/<\/code> or <code>src\/<\/code>. Safe upgrade.\nAdds 31 new abilities across 10 domains (187 \u2192 218). Two new categories join the Ability Library: Admin Menu (5 abilities) and Content Search (11 abilities). Introduces two option-backed data stores: a lifecycle event log for plugin\/theme activate\/deactivate\/update timestamps, and an internal-link suggestion queue capped at 500 entries. Zero new REST endpoints, zero new capability requirements beyond the operation-specific caps already enforced by WP core (moderate_comments, upload_files, edit_others_posts). Zero external HTTP; zero new database tables. No breaking changes to existing abilities. Safe upgrade.<\/p>","0.0.12":"<p>Adds a third ability to the Core tab \u2014 <code>wp-core-rollback<\/code> \u2014 that rolls back WordPress core to an earlier version via WP core&#039;s <code>Core_Upgrader::upgrade()<\/code>, the same class the dashboard uses for forward updates. Requires both <code>manage_options<\/code> and <code>update_core<\/code>; honours <code>DISALLOW_FILE_MODS<\/code>; refuses when the target version isn&#039;t strictly older than the currently-installed version. Introduces the plugin&#039;s first outbound HTTP request (to <code>api.wordpress.org\/core\/version-check\/1.7\/<\/code>), rate-bounded to at most one request per day per locale per site via a site-transient cache. No breaking changes; no database, REST, or capability changes to existing abilities. Safe upgrade.<\/p>","0.0.11":"<p>Adds two WordPress-core-scoped abilities under a new &quot;Core&quot; tab in the Ability Library \u2014 <code>wp-core-update-check<\/code> (report availability) and <code>wp-core-update<\/code> (apply via <code>Core_Upgrader<\/code>). The update ability requires both <code>manage_options<\/code> and <code>update_core<\/code>; honours <code>DISALLOW_FILE_MODS<\/code>; multisite-guarded. Also changes backup filenames from <code>backup-{type}-{slug}-{random}.zip<\/code> to <code>{slug}-{unix-timestamp}-{ms}.zip<\/code> \u2014 human-readable and time-sortable, but predictable (directory listing remains disabled on the backups dir). Existing backups continue to work; the filename change only affects new backups. No breaking changes; no database, REST, or capability changes to existing abilities. Safe upgrade.<\/p>","0.0.10":"<p>Bugfix release. <code>Create_Zip_Backup<\/code> with <code>include_hidden=false<\/code> was silently descending into hidden directories and archiving their contents in 0.0.9 (only the top-level <code>.git\/<\/code> etc. entry was skipped, not the files beneath it). Fixed to check every segment of each entry&#039;s relative path. Regenerate any <code>include_hidden=false<\/code> archives created on 0.0.9 if their source tree contained hidden directories. No breaking changes; no database, REST, or capability changes. Safe upgrade.<\/p>","0.0.9":"<p>Adds eight new abilities: six under FileManager for zip-based backup \/ restore workflows (<code>zip-create<\/code>, <code>zip-upload<\/code>, <code>zip-extract<\/code>, <code>zip-download<\/code>, <code>zip-list<\/code>, <code>zip-delete<\/code>) plus <code>plugin-update<\/code> and <code>theme-update<\/code> that finally let AI clients apply pending WordPress core updates through the Abilities API. All new abilities enforce <code>manage_options<\/code>; mutating abilities additionally honour <code>DISALLOW_FILE_MODS<\/code>. Zip extraction rejects zip-slip archives (any entry containing <code>..<\/code>, an absolute path, a backslash, or a null byte). Zip uploads are validated for the <code>PK<\/code> magic signature before finalization. A new <code>wp-content\/uploads\/acrossai-backups\/<\/code> directory is created on first use, hardened with an <code>.htaccess<\/code> that blocks PHP execution but permits <code>.zip<\/code> downloads (required so the URLs returned by <code>zip-create<\/code> remain reachable). No breaking changes to existing abilities, REST endpoints, capability requirements, or database schema. Safe upgrade.<\/p>","0.0.8":"<p>IMPORTANT: this release <strong>removes the Freemius integration entirely<\/strong> \u2014 the plugin no longer sends any data to Freemius and no longer offers a Connect \/ Login \/ Buy affordance on the Add-ons page. If you previously connected a Freemius account tied to this plugin, that connection is now inert; stale <code>fs_*<\/code> or <code>freemius_*<\/code> rows in <code>wp_options<\/code> are safe to delete manually. Also: the Add-ons page now shows only free WordPress.org companion plugins (and no longer lists this plugin itself); the Library page compacts its title + bulk-action buttons onto one horizontal row; and <code>acrossai-co\/main-menu<\/code> bumps <code>0.0.14 \u2192 0.0.23<\/code>. No breaking changes to REST endpoints, capability requirements, or database schema. Safe upgrade.<\/p>","0.0.7":"<p>Adds Library page bulk Enable All \/ Disable All buttons scoped to the active tab, URL-synced tabs (<code>?tab=<\/code>) for deep-linkable views, and a readonly ability preview on disabled cards. No breaking changes; no database schema changes; no new REST endpoints; no new capability requirements. <code>mode<\/code> and per-slug selections are preserved through disable \/ enable cycles. Safe upgrade.<\/p>","0.0.6":"<p>IMPORTANT: this release absorbs the companion <code>acrossai-core-abilities<\/code> plugin \u2014 deactivate and uninstall that plugin after upgrading to avoid duplicate ability registrations. BREAKING for downstream integrators: 17 category slugs rebranded <code>acrossai-core-abilities-<\/code> \u2192 <code>acrossai-abilities-manager-<\/code> and 176 ability slugs <code>acrossai-core-abilities\/<\/code> \u2192 <code>acrossai\/<\/code>; update any MCP\/REST\/WP-CLI callers that referenced the legacy slugs. Ability payload shapes and permission callbacks unchanged. Also promotes Themes \/ Blocks \/ Plugins \/ Users \/ Database \/ Cron \/ Cache \/ File Manager to their own Library page tabs, bumps <code>acrossai-co\/main-menu<\/code> to <code>0.0.14<\/code>, and rotates Freemius credentials.<\/p>","0.0.5":"<p>Dependency-only release: refreshes the bundled <code>acrossai-co\/main-menu<\/code> package to <code>0.0.11<\/code>. No functional changes to this plugin. Safe upgrade.<\/p>","0.0.4":"<p>IMPORTANT for add-on developers: Library display fields <code>sub_group<\/code>, <code>sub_group_label<\/code>, and <code>tab_group<\/code> must now be nested under <code>$args[&amp;#039;meta&amp;#039;][&amp;#039;acrossai&amp;#039;]<\/code> when calling <code>wp_register_ability()<\/code>. The old top-level shape is silently dropped \u2014 cards will render without their sub-group heading or custom tab placement until you migrate. End users and site administrators are not affected; no data migration, no DB or REST changes. Also swaps the WordPress.org plugin icon to an SVG and drops the directory banners.<\/p>","0.0.3":"<p>Fixes the 0.0.2 activation error on WordPress.org installs \u2014 the release ZIP now includes the Composer autoloader. No functional or user-facing changes vs 0.0.2. If you hit the &quot;Composer autoloader is missing&quot; error on 0.0.2, delete the plugin folder and reinstall 0.0.3.<\/p>","0.0.2":"<p>IMPORTANT: (1) This release does NOT migrate Access Control rules from previous versions. If you had configured any Access Control rules on abilities, audit and reconfigure them after upgrading. Pre-existing rules remain in the database (in the orphaned <code>{prefix}wpb_access_control<\/code> table) but are no longer applied. (2) Ability execution logging has been removed \u2014 the Logs admin page is gone; ability-execution denials are no longer recorded by this plugin. Install a compatible logging plugin if you need this signal.<\/p>","0.0.1":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon.svg":{"filename":"icon.svg","revision":3595583,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3614045,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3614045,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.0.1","0.0.10","0.0.11","0.0.12","0.0.13","0.0.14","0.0.15","0.0.17","0.0.18","0.0.19","0.0.2","0.0.20","0.0.21","0.0.22","0.0.23","0.0.24","0.0.25","0.0.26","0.0.27","0.0.29","0.0.3","0.0.30","0.0.31","0.0.4","0.0.5","0.0.6","0.0.7","0.0.8","0.0.9"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3614043,"resolution":"1","location":"assets","locale":"","width":3268,"height":1874},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3614043,"resolution":"2","location":"assets","locale":"","width":3268,"height":1874},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3614043,"resolution":"3","location":"assets","locale":"","width":3268,"height":1874},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3614043,"resolution":"4","location":"assets","locale":"","width":3268,"height":1874},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3614043,"resolution":"5","location":"assets","locale":"","width":3268,"height":1874},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3614043,"resolution":"6","location":"assets","locale":"","width":3268,"height":1874}},"screenshots":{"1":"The Abilities Manager admin page \u2014 searchable, sortable ability table.","2":"The edit drawer \u2014 tri-state override controls for each ability field.","3":"Bulk actions toolbar for allow\/disallow\/reset across multiple abilities.","4":"The Ability Library page \u2014 enable\/disable add-on ability groups.","5":"The Add-ons page \u2014 browse free companion plugins.","6":"Settings \u2014 Display (abilities-per-page) and Upload Media Abilities (allowed-MIME list + Add file types)."}},"plugin_section":[],"plugin_tags":[251511,268952,1912,2353,174442],"plugin_category":[],"plugin_contributors":[140910],"plugin_business_model":[],"class_list":["post-311005","plugin","type-plugin","status-publish","hentry","plugin_tags-abilities","plugin_tags-ability-management","plugin_tags-access-control","plugin_tags-ai","plugin_tags-site-management","plugin_contributors-raftaar1191","plugin_committers-raftaar1191"],"banners":{"banner":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/banner-772x250.png?rev=3614045","banner_2x":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/banner-1544x500.png?rev=3614045","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/icon.svg?rev=3595583","icon":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/icon.svg?rev=3595583","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/screenshot-1.png?rev=3614043","caption":"The Abilities Manager admin page \u2014 searchable, sortable ability table."},{"src":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/screenshot-2.png?rev=3614043","caption":"The edit drawer \u2014 tri-state override controls for each ability field."},{"src":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/screenshot-3.png?rev=3614043","caption":"Bulk actions toolbar for allow\/disallow\/reset across multiple abilities."},{"src":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/screenshot-4.png?rev=3614043","caption":"The Ability Library page \u2014 enable\/disable add-on ability groups."},{"src":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/screenshot-5.png?rev=3614043","caption":"The Add-ons page \u2014 browse free companion plugins."},{"src":"https:\/\/ps.w.org\/acrossai-abilities-manager\/assets\/screenshot-6.png?rev=3614043","caption":"Settings \u2014 Display (abilities-per-page) and Upload Media Abilities (allowed-MIME list + Add file types)."}],"raw_content":"<!--section=description-->\n<p>AcrossAI Abilities Manager gives site administrators full visibility and control over every ability registered via the WordPress Abilities API (<code>wp_get_ability()<\/code>).<\/p>\n\n<p><strong>Features:<\/strong><\/p>\n\n<ul>\n<li><strong>Browse all abilities<\/strong> \u2014 a searchable, sortable, paginated table listing every registered ability with slug, provider, source, and current status.<\/li>\n<li><strong>Toggle allow\/disallow<\/strong> \u2014 enable or disable any ability site-wide with a single click. Changes are saved instantly without a page reload.<\/li>\n<li><strong>Edit ability metadata<\/strong> \u2014 override <code>readonly<\/code>, <code>destructive<\/code>, <code>idempotent<\/code>, <code>show_in_rest<\/code>, <code>show_in_mcp<\/code>, <code>mcp_type<\/code>, and <code>mcp_servers<\/code> fields per ability using a tri-state system (Yes \/ No \/ Inherit from registry).<\/li>\n<li><strong>Reset overrides<\/strong> \u2014 restore any ability back to its registry defaults with one click.<\/li>\n<li><strong>Bulk actions<\/strong> \u2014 allow, disallow, or reset up to 50 abilities at once.<\/li>\n<li><strong>Ability Library<\/strong> \u2014 enable or disable add-on ability groups from a dedicated Library page, with All\/Specific mode controls per group.<\/li>\n<li><strong>Add-ons page<\/strong> \u2014 browse companion plugins from the WordPress admin. WordPress.org-hosted add-ons install \/ activate \/ deactivate in place; add-ons distributed elsewhere link out to the vendor's site so you can install them via Plugins \u2192 Add New \u2192 Upload Plugin.<\/li>\n<li><strong>MCP server list<\/strong> \u2014 view all registered MCP servers when the MCP Adapter plugin is active.<\/li>\n<li><strong>Debugging \u2192 Conflict Testing<\/strong> \u2014 toggle any installed plugin's <em>effective<\/em> active state without ever writing to <code>wp_options.active_plugins<\/code>. Seven WP Abilities API abilities (<code>acrossai\/conflict-test-list-plugins<\/code>, <code>-get-overrides<\/code>, <code>-set-override<\/code>, <code>-bulk-set-overrides<\/code>, <code>-clear-overrides<\/code>, <code>-deploy-mu-plugin<\/code>, <code>-remove-mu-plugin<\/code>) let a REST client, MCP AI client, or another plugin reproduce a plugin conflict for a browser session or a support call, then restore the site to its exact prior state by clearing one JSON file. Overrides cascade through WP 6.5+ <code>Requires Plugins:<\/code> headers by default. Every <code>active=true<\/code> write is guarded by a WordPress-core-style <code>plugin_sandbox_scrape<\/code> probe, so a broken plugin can never leave the site in a state where every subsequent page load fatals \u2014 the override is refused instead. Feature 061.<\/li>\n<\/ul>\n\n<p>All overrides are stored in a dedicated database table. The WordPress ability registry is never modified \u2014 only the fields that differ from registry defaults are persisted.<\/p>\n\n<p><strong>Security:<\/strong><\/p>\n\n<ul>\n<li>All endpoints require <code>manage_options<\/code> capability.<\/li>\n<li>All state-changing requests are protected by WordPress nonce verification.<\/li>\n<li>All input is sanitized; all output is escaped.<\/li>\n<\/ul>\n\n<p><strong>Third-party integrations (optional):<\/strong><\/p>\n\n<ul>\n<li><strong>MCP Adapter plugin<\/strong> \u2014 if active, the plugin displays a list of registered MCP servers inside the ability edit panel. No data is sent to any external service. The MCP Adapter plugin communicates only with your own WordPress installation.<\/li>\n<\/ul>\n\n<p>This plugin's own code makes no external HTTP requests. One admin-only surface can contact an external service on your behalf: the AcrossAI \u2192 Add-ons page installs WordPress.org-hosted companion plugins directly through WordPress core's own plugin installer (<code>api.wordpress.org<\/code> + <code>downloads.wordpress.org<\/code>). Add-ons registered with any other source (e.g. GitHub, Freemius) are shown as external \"Get add-on \u2197\" links that open the vendor's site in a new browser tab \u2014 the plugin does not download or install them itself. The AcrossAI \u2192 Consultations submenu renders a static call-to-action button that opens <code>calendly.com<\/code> in a new browser tab only after the administrator clicks it \u2014 no third-party asset is loaded inside wp-admin. Full disclosure \u2014 including what data is transmitted to each service and links to their terms + privacy policies \u2014 is in the <strong>External Services<\/strong> section below.<\/p>\n\n<h3>External Services<\/h3>\n\n<p>This plugin connects to the following external services on your behalf. Each connection is triggered by a specific admin-only action and is disclosed here per the WordPress.org plugin directory guidelines.<\/p>\n\n<p><strong>1. Calendly external link (<code>calendly.com<\/code>)<\/strong><\/p>\n\n<p><em>What it is:<\/em> Calendly is a third-party scheduling service. The AcrossAI \u2192 Consultations submenu displays a static call-to-action button that links out to a Calendly booking page for AcrossAI consultations (\"Using AI in WordPress\").<\/p>\n\n<p><em>When it is contacted:<\/em> Never on page render. The Consultations submenu at <code>\/wp-admin\/admin.php?page=acrossai-consultations<\/code> is a self-contained wp-admin page \u2014 it does not load any Calendly script, iframe, cookie, or asset. Calendly is only contacted if the administrator explicitly clicks the \"Book a Consultation\" button, at which point their browser navigates directly to <code>https:\/\/calendly.com\/acrossai\/using-ai-in-wordpress<\/code> in a new tab (<code>target=\"_blank\" rel=\"noopener noreferrer\"<\/code>). This is identical to clicking any external hyperlink from an admin page.<\/p>\n\n<p><em>What is loaded on the Consultations page:<\/em> Nothing from Calendly. The page renders self-contained HTML + CSS. The only external asset referenced by the page is Google Fonts (Space Grotesk + IBM Plex Sans via <code>fonts.googleapis.com<\/code>) \u2014 permitted under the \"third-party CDNs beyond fonts\" carve-out in the WordPress plugin guidelines.<\/p>\n\n<p><em>What data is transmitted to Calendly:<\/em> Nothing by this plugin. If the administrator clicks the CTA button, their browser navigates directly to Calendly and sends standard browser metadata (IP address, User-Agent, referrer) to Calendly as with any external link. If the administrator then chooses to book a consultation on Calendly's own site, any information they enter into Calendly's booking form (name, email address, meeting preferences, etc.) is transmitted to and processed by Calendly. This plugin does not intercept, store, or forward that data.<\/p>\n\n<p><em>Terms of service:<\/em> https:\/\/calendly.com\/pages\/terms\n<em>Privacy policy:<\/em> https:\/\/calendly.com\/pages\/privacy<\/p>\n\n<p><strong>2. WordPress.org plugin directory (<code>api.wordpress.org<\/code> and <code>downloads.wordpress.org<\/code>)<\/strong><\/p>\n\n<p><em>What it is:<\/em> The Add-ons page (<code>\/wp-admin\/admin.php?page=acrossai-addons<\/code>) uses the WordPress.org plugin directory to install free companion plugins directly from wp-admin.<\/p>\n\n<p><em>When it is contacted:<\/em> Only when an authenticated administrator (<code>install_plugins<\/code> capability) clicks the \"Install\" button on a card whose <code>source<\/code> is <code>wordpress.org<\/code>. Contact happens through WordPress core's own <code>plugins_api()<\/code> and <code>Plugin_Upgrader<\/code> \u2014 this plugin does not issue direct HTTP requests. Add-ons registered with any other source (e.g. <code>github<\/code>, <code>freemius<\/code>) are rendered as external \"Get add-on \u2197\" links that open the vendor's site in a new browser tab; the plugin does NOT download or install those add-ons itself, so no request is made to the vendor's servers from wp-admin.<\/p>\n\n<p><em>What data is transmitted:<\/em> The WordPress core plugin API request payload (site URL, WP version, PHP version, locale) as per WordPress core's standard update check protocol.<\/p>\n\n<p><em>Terms of service:<\/em> https:\/\/wordpress.org\/about\/terms\/\n<em>Privacy policy:<\/em> https:\/\/wordpress.org\/about\/privacy\/<\/p>\n\n<p><strong>3. WordPress.org core version-check API (<code>api.wordpress.org\/core\/version-check\/1.7\/<\/code>)<\/strong><\/p>\n\n<p>Called only when an administrator invokes the <code>core\/rollback-wp-core<\/code> ability (registered under the Core category) and the local core-version cache has expired. Rate-bounded to at most one request per day per locale per site via a site-transient cache. This is a WordPress-core-hosted API \u2014 no data beyond the standard WordPress core version-check request payload is transmitted. Same wp.org terms + privacy policy as service #2 above.<\/p>\n\n<h3>Privacy Policy<\/h3>\n\n<p>This plugin does not itself collect, store, or transmit any user data to any third party.<\/p>\n\n<p>Several admin-only actions can cause external services to receive data \u2014 all are described in the External Services section above and are triggered only by an authenticated administrator:<\/p>\n\n<ul>\n<li>The AcrossAI \u2192 Consultations admin page displays a static call-to-action button. Merely loading the Consultations page sends no data to Calendly \u2014 no Calendly script, iframe, or asset is loaded inside wp-admin. If the administrator clicks the CTA button, their browser opens <code>calendly.com\/acrossai\/using-ai-in-wordpress<\/code> in a new tab, at which point standard browser metadata (IP, User-Agent, referrer) is sent to Calendly and Calendly's own privacy policy applies. If they then book a consultation on Calendly's site, information they enter into Calendly's form (name, email, meeting details) is transmitted to Calendly.<\/li>\n<li>Installing a WordPress.org-hosted add-on from the AcrossAI \u2192 Add-ons page contacts the WordPress.org plugin directory via WordPress core's own <code>plugins_api()<\/code> and <code>Plugin_Upgrader<\/code> (<code>api.wordpress.org<\/code> + <code>downloads.wordpress.org<\/code>). Add-ons distributed elsewhere (e.g. GitHub, Freemius) are rendered as external \"Get add-on \u2197\" links that open the vendor's site in a new browser tab \u2014 the plugin itself does not download or install those add-ons, so no request is sent to the vendor's servers from wp-admin. If the administrator clicks the external link, their browser navigates directly to the vendor and standard browser metadata (IP, User-Agent, referrer) is sent to the vendor as with any external hyperlink.<\/li>\n<li>Invoking the <code>core\/rollback-wp-core<\/code> ability contacts the WordPress.org core version-check API (a WordPress-core-hosted service) via the standard WordPress update API.<\/li>\n<\/ul>\n\n<p>No data is sent to any external server without an explicit administrator action.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>acrossai-abilities-manager<\/code> folder to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> menu in WordPress.<\/li>\n<li>Navigate to <strong>AcrossAI Abilities Manager<\/strong> in the WordPress admin menu.<\/li>\n<\/ol>\n\n<p><strong>Add-ons:<\/strong><\/p>\n\n<ol>\n<li>Go to <strong>AcrossAI \u2192 Add-ons<\/strong> to browse available companion plugins.<\/li>\n<li>All add-ons are free and hosted on WordPress.org; each card offers a one-click Install \/ Activate \/ Deactivate action via the standard WordPress plugin installer.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20support%20multisite%3F\"><h3>Does this plugin support Multisite?<\/h3><\/dt>\n<dd><p>No. This plugin has not been tested on WordPress Multisite installations.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20modify%20the%20wordpress%20ability%20registry%3F\"><h3>Does this plugin modify the WordPress ability registry?<\/h3><\/dt>\n<dd><p>No. The plugin stores only overrides \u2014 fields that differ from the registry defaults. The ability registry itself (<code>wp_get_ability()<\/code>) is never modified.<\/p><\/dd>\n<dt id=\"what%20happens%20when%20i%20reset%20an%20override%3F\"><h3>What happens when I reset an override?<\/h3><\/dt>\n<dd><p>The override row is deleted from the database. The ability will inherit its values from the registry again.<\/p><\/dd>\n<dt id=\"what%20is%20the%20ability%20library%3F\"><h3>What is the Ability Library?<\/h3><\/dt>\n<dd><p>The Library page lets you enable or disable ability groups registered by add-on plugins. Each group shows an ON\/OFF master toggle and an All\/Specific mode selector. In Specific mode, individual ability slots can be toggled independently.<\/p><\/dd>\n<dt id=\"what%20is%20the%20mcp%20adapter%20integration%3F\"><h3>What is the MCP Adapter integration?<\/h3><\/dt>\n<dd><p>If the MCP Adapter plugin is active on your site, AcrossAI Abilities Manager will display the list of registered MCP servers in the ability edit panel. This is entirely optional \u2014 the plugin works without the MCP Adapter.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20make%20external%20http%20requests%3F\"><h3>Does this plugin make external HTTP requests?<\/h3><\/dt>\n<dd><p>The plugin's own code makes no external HTTP requests. Two admin-only surfaces trigger external connections on behalf of an authenticated administrator:<\/p>\n\n<ul>\n<li><strong>AcrossAI \u2192 Consultations<\/strong> submenu \u2014 renders a static call-to-action button that links to <code>https:\/\/calendly.com\/acrossai\/using-ai-in-wordpress<\/code> and opens in a new browser tab. The plugin does not load any Calendly script, iframe, or asset inside wp-admin. Calendly is only contacted if the administrator explicitly clicks the button \u2014 at which point their browser navigates directly to <code>calendly.com<\/code>, exactly as with any external hyperlink.<\/li>\n<li><strong>AcrossAI \u2192 Add-ons<\/strong> submenu \u2014 installs WordPress.org-hosted companion plugins in place through WordPress core's <code>plugins_api()<\/code> + <code>Plugin_Upgrader<\/code> (contacts <code>api.wordpress.org<\/code> + <code>downloads.wordpress.org<\/code>). Add-ons registered with any other source (e.g. GitHub, Freemius) render as external \"Get add-on \u2197\" links that open the vendor's site in a new browser tab \u2014 the plugin does not download or install those add-ons itself. Users install off-directory add-ons via WP admin's standard <strong>Plugins \u2192 Add New \u2192 Upload Plugin<\/strong> flow (or via the vendor's own installer once the paid plugin is activated).<\/li>\n<\/ul>\n\n<p>Full disclosure \u2014 including what data is transmitted, and links to each service's terms + privacy policy \u2014 is in the <strong>External Services<\/strong> section of this readme.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>Unreleased<\/h4>\n\n<h4>0.0.31 - 2026-08-26<\/h4>\n\n<p><strong>Release theme: File Manager consolidation + hardening + audit log.<\/strong> Bundles the six-feature series (089 \u2192 094) that turned <code>file-manager\/*<\/code> from a loose collection of read\/write primitives into a self-contained subsystem with an admin tab, allowlist-and-content-filter enforcement, and an append-only audit log with pre-image backups. Also cuts the inline <code>.bak.&lt;timestamp&gt;<\/code> scheme in <code>Delete_File<\/code> (BREAKING for callers of <code>response.backup<\/code> \u2014 new canonical field is <code>response.backup_path<\/code>).<\/p>\n\n<p><strong>Feature 094 \u2014 File Manager Audit Log + Backup Harness (partial).<\/strong> Consumes the four Backup &amp; Audit option keys shipped as scaffold in PR #144 and enforced-toggle-only in PR #146. New <code>Audit_Trail<\/code> utility owns pre-image backups (into <code>wp-content\/acrossai-file-manager-backups\/&lt;YYYY-MM-DD&gt;\/<\/code>) + append-only log (into <code>wp-content\/acrossai-file-manager-logs\/acrossai-file-manager.log<\/code>) + amortised 1-in-10 cleanup + stats. Both storage locations get a <code>Deny from all<\/code> <code>.htaccess<\/code> on first creation. All I\/O goes through <code>WP_Filesystem<\/code>.<\/p>\n\n<p><strong>New ability:<\/strong> <code>file-manager\/get-changelog<\/code> \u2014 tails the last N entries (default 100, max 500) via MCP. Honours the read allowlist. Empty log returns a friendly message, not an error. <code>manage_options<\/code> gated.<\/p>\n\n<p><strong>Log entry format<\/strong> (blank-line separated, one entry per mutation):<\/p>\n\n<p>[YYYY-MM-DD HH:MM:SS UTC] \n    Ability: file-manager\/\n    File: \n    User:  (ID:) IP:\n    Size:  -&gt;  bytes\n    Destination:        (COPY \/ MOVE only)\n    Backup: \n    Context: <\/p>\n\n<p><strong>New action hook:<\/strong> <code>do_action('acrossai_file_manager_log_entry', $entry)<\/code> fires after every log write. Subscribers (Slack, Datadog, SIEM\u2026) receive the parsed entry as an assoc array. Zero cost when no subscribers.<\/p>\n\n<p><strong>New optional <code>context<\/code> input field<\/strong> on wired abilities (<code>delete-file<\/code>, <code>edit-file<\/code>, <code>create-directory<\/code> in this PR \u2014 more in the follow-up). Schema max 2000 chars; log writer truncates to 500 chars via <code>sanitize_text_field<\/code> before persisting.<\/p>\n\n<p><strong>BREAKING \u2014 <code>file-manager\/delete-file<\/code> <code>backup<\/code> response field.<\/strong> The inline <code>&lt;path&gt;.bak.&lt;time&gt;<\/code> scheme is REPLACED by the centralised backup dir. When <code>backup_enabled=true<\/code> the response's new canonical field is <code>backup_path<\/code>; the legacy <code>backup<\/code> field is populated with the same value for one transition release and will be removed. When <code>backup_enabled=false<\/code> NO backup is written at all \u2014 callers who relied on the inline <code>.bak<\/code> return value now get <code>null<\/code> for both fields when the toggle is off. Direct callers to read <code>backup_path<\/code> instead of <code>backup<\/code>.<\/p>\n\n<p><strong>Retention.<\/strong> <code>backup_retention_days<\/code> deletes backup dirs older than N days; <code>audit_log_retention_days<\/code> trims log entries older than N days. Both fire probabilistically (1-in-10 per log write) \u2014 no WP-Cron dependency.<\/p>\n\n<p><strong>Scope note (PARTIAL).<\/strong> This PR wires three abilities end-to-end (<code>delete-file<\/code>, <code>edit-file<\/code>, <code>create-directory<\/code>) to prove the design across the backup + log-only paths. The remaining seven mutation abilities (<code>create-file<\/code>, <code>append-file<\/code>, <code>copy-file<\/code>, <code>move-file<\/code>, <code>delete-directory<\/code>, <code>edit-wp-config<\/code>, <code>clear-debug-log<\/code>) and the <code>BackupAuditPanel<\/code> scaffold-banner flip + <code>\/backup-audit-stats<\/code> REST endpoint + uninstall extension are DEFERRED to a follow-up PR. Test coverage is scoped accordingly (35 new tests, mostly structural \u2014 behavioural I\/O tests need a full-WP bootstrap and run in the CI matrix).<\/p>\n\n<p><strong>Feature 093 \u2014 File Manager Hardening (enforcement pass for PR #144 scaffold).<\/strong> The eight Content Filters knobs and the one sensitive-read denylist that PR #144 shipped as UI scaffold are now enforced at runtime by every file-manager write ability and by <code>file-manager\/read-file<\/code>. Persistence layer (<code>Hardening_Settings<\/code>) unchanged; the new <code>Hardening_Enforcer<\/code> utility runs each check after the existing <code>File_Mods_Guard<\/code> + <code>Path_Allowlist_Guard<\/code> gates and returns the standard <code>{success:false, blocked_reason, path, message, \u2026context}<\/code> envelope on refusal. Enforcement order (best-cheap-first): dangerous_extensions \u2192 block_double_extensions \u2192 sanitize_filename_check \u2192 strict_filename_filter \u2192 mime_type_check \u2192 htaccess_directive_scan \u2192 write_max_bytes. Empty list \/ false toggle is a no-op so callers upgrading with defaults get zero behaviour change beyond what defaults dictate.<\/p>\n\n<p><strong>Six abilities wired<\/strong> \u2014 <code>file-manager\/{create-file, edit-file, append-file, copy-file, move-file}<\/code> for the seven content-filter checks; <code>file-manager\/read-file<\/code> for the sensitive-read denylist. Copy\/move check the DESTINATION basename; append-file scans only the appended bytes for <code>.htaccess<\/code> directives and caps size on <code>new_size = existing + appended<\/code>; copy\/move use source file size for the size cap and read source content lazily for the <code>.htaccess<\/code> scan (only when destination basename is <code>.htaccess<\/code>). <code>mime_type_check<\/code> skips append-file (extension didn't change) and always allows <code>{php, txt, log, json, xml, css, js, md, html, htm, htaccess}<\/code> even with the check on \u2014 prevents breaking the mu-plugins deploy use case the write allowlist explicitly permits.<\/p>\n\n<p><strong>Eight new <code>blocked_reason<\/code> values<\/strong> \u2014 <code>extension_blocked<\/code> (+<code>extension<\/code>), <code>double_extension_blocked<\/code> (+<code>basename<\/code>), <code>htaccess_directive_blocked<\/code> (+<code>directive<\/code>), <code>filename_sanitize_failed<\/code> (+<code>input<\/code>, <code>sanitized<\/code>), <code>write_size_exceeded<\/code> (+<code>size<\/code>, <code>max_bytes<\/code>), <code>filename_strict_blocked<\/code> (+<code>marker<\/code>), <code>mime_type_blocked<\/code> (+<code>extension<\/code>), <code>sensitive_read_blocked<\/code> (+<code>basename<\/code>, <code>matched_pattern<\/code>). Every affected ability's <code>output_schema<\/code> declares the union of these context fields. Existing <code>path_not_allowed_for_write<\/code> \/ <code>path_not_allowed_for_read<\/code> \/ <code>protected_write<\/code> \/ <code>file_mods_disabled<\/code> envelopes are unchanged and take precedence \u2014 hardening refusals fire only when all earlier gates pass.<\/p>\n\n<p><strong>Ordering guarantee<\/strong> \u2014 sensitive-read denylist runs AFTER the read allowlist (spec FR-011). If the allowlist would refuse a path, that refusal (<code>path_not_allowed_for_read<\/code>) is returned; the denylist only fires when the allowlist would permit. This lets an admin safely widen the read allowlist and still keep <code>.env \/ *.key \/ id_rsa<\/code> blocked.<\/p>\n\n<p><strong>Panel banner update<\/strong> \u2014 the Content Filters panel drops its yellow \"Scaffold only\" <code>notice-warning<\/code> (added in PR #144) and replaces it with a small <code>notice-info<\/code> reading \"This list now gates create-file \/ edit-file \/ append-file \/ copy-file \/ move-file.\" The Backup &amp; Audit panel keeps its scaffold banner but the text now references <code>094-file-manager-audit-log<\/code> explicitly. REST GET <code>\/acrossai\/v1\/file-manager-settings\/content-filters<\/code> flips <code>scaffold_only:true \u2192 false<\/code> and <code>follow_up_spec<\/code> becomes <code>null<\/code>; <code>\/backup-audit<\/code> unchanged.<\/p>\n\n<p><strong>Dotfile carve-out for <code>sanitize_filename_check<\/code>.<\/strong> Legitimate WordPress-adjacent dotfiles (<code>.htaccess<\/code>, <code>.htpasswd<\/code>, <code>.user.ini<\/code>) are exempted from the sanitize-filename roundtrip check because real WP's <code>sanitize_file_name()<\/code> strips leading dots \u2014 applied literally, the check would refuse every valid dotfile and starve the htaccess-directive scanner of any target. Other dotfiles (<code>.gitignore<\/code>, <code>.env<\/code>, etc.) are still refused when the check is on; admins who need them disable the check.<\/p>\n\n<p><strong>Not touched.<\/strong> <code>Delete_File<\/code>, <code>Delete_Directory<\/code>, <code>Create_Directory<\/code>, <code>File_Info<\/code>, <code>Read_Debug_Log<\/code> (fixed target), <code>Read_Wp_Config<\/code>, <code>Get_Wp_Config_Constant<\/code>, <code>Edit_Wp_Config<\/code>, all six zip abilities \u2014 spec explicitly excludes these.<\/p>\n\n<p><strong>Feature 092 \u2014 File Manager admin tab: per-folder read\/write allowlists + configurable secret redactor.<\/strong> New \"File Manager\" tab at <code>admin.php?page=acrossai-settings<\/code> gives site admins three per-folder controls over what MCP clients can do via <code>file-manager\/*<\/code> abilities. Also introduces a hardened secret-scrubber that runs on every read response.<\/p>\n\n<p><strong>Write allowlist.<\/strong> The 8 write-capable file-manager abilities (<code>create-file<\/code>, <code>edit-file<\/code>, <code>delete-file<\/code>, <code>copy-file<\/code>, <code>move-file<\/code>, <code>append-file<\/code>, <code>create-directory<\/code>, <code>delete-directory<\/code>) refuse any operation whose target path resolves outside the admin's allowlist. Default on activation: <code>['wp-content']<\/code> (writes only inside wp-content). <code>copy-file<\/code> and <code>move-file<\/code> check both source and destination. Refusal returns <code>{success:false, blocked_reason:\"path_not_allowed_for_write\", allowed_roots:[\u2026]}<\/code>.<\/p>\n\n<p><strong>Read allowlist.<\/strong> The 2 content-reading abilities (<code>read-file<\/code>, <code>read-debug-log<\/code>) can also be gated. Default on activation: <code>[]<\/code> \u2014 unrestricted (every path readable). Admins can flip a \"Restrict reads to specific folders\" toggle and pick specific folders. Refusal returns <code>{success:false, blocked_reason:\"path_not_allowed_for_read\"}<\/code>. <code>list-directory<\/code> and <code>file-info<\/code> remain ungated (metadata only, no content leak).<\/p>\n\n<p><strong>Secret redactor.<\/strong> Every text response from <code>read-file<\/code> and <code>read-debug-log<\/code> is scrubbed before return. Ships one built-in pattern: <strong>WordPress credentials<\/strong> (DB_PASSWORD, DB_USER, all 8 auth keys\/salts, SECRET_KEY) \u2014 value replaced but constant name preserved. Everything else the admin adds via the custom-literals textarea (case-sensitive string match). Third-party API-key regexes are intentionally NOT hardcoded \u2014 assumptions about someone else's key format belong in the site's own config, not in this plugin.<\/p>\n\n<p><strong>Auto-scrub for AI-connector API keys.<\/strong> If the WordPress <strong>AI plugin<\/strong> (github.com\/WordPress\/ai) is installed and any of its provider connectors has an API key configured, that value is added to the redactor's literal list transparently \u2014 the admin does not have to copy those keys into the custom-literals textarea. Currently covers OpenAI (<code>connectors_ai_openai_api_key<\/code>), Anthropic (<code>connectors_ai_anthropic_api_key<\/code>), and Google (<code>connectors_ai_google_api_key<\/code>). No configuration required; the redactor reads these options on every scrub call. Responses grow two fields: <code>redacted:bool<\/code> and <code>redaction_count:int<\/code>.<\/p>\n\n<p><strong>REST endpoints.<\/strong> Six new routes under <code>acrossai\/v1<\/code>: GET\/POST <code>\/file-manager-settings\/write-allowlist<\/code>, <code>\/read-allowlist<\/code>, <code>\/redaction<\/code>. <code>manage_options<\/code> + <code>X-WP-Nonce<\/code> required. GET responses include enumeration data (immediate ABSPATH children, <code>get_plugins()<\/code>, <code>wp_get_themes()<\/code>) so the React UI renders without a second round-trip.<\/p>\n\n<p><strong>BREAKING \u2014 <code>file-manager\/read-file<\/code>.<\/strong> The previous outright refusal of <code>wp-config.php<\/code> and <code>.htaccess<\/code> (<code>blocked_reason:\"protected_read\"<\/code>) is REMOVED. Those files are now readable; sensitive content is redacted per the secret redactor above. Callers that programmatically handled <code>blocked_reason:\"protected_read\"<\/code> should switch to reading the returned content with <code>redacted:true<\/code>. Write-side refusals on <code>wp-config.php<\/code> \/ <code>.htaccess<\/code> for <code>create-file<\/code>, <code>edit-file<\/code>, <code>delete-file<\/code>, <code>copy-file<\/code>, <code>move-file<\/code>, <code>append-file<\/code> are UNCHANGED.<\/p>\n\n<p><strong>Not touched.<\/strong> <code>Ability_Definition<\/code>, <code>File_Mods_Guard<\/code>, <code>Wp_Filesystem_Init<\/code>, <code>read-wp-config<\/code>, <code>edit-wp-config<\/code>, <code>get-wp-config-constant<\/code>, <code>list-directory<\/code>, <code>file-info<\/code>, all 6 zip-backup abilities, and every ability outside <code>file-manager\/*<\/code>.<\/p>\n\n<h4>Feature 091 milestone<\/h4>\n\n<p><strong>Feature 091 \u2014 WP_Filesystem migration for <code>file-manager\/*<\/code> abilities.<\/strong> Every filesystem read, write, list, delete, copy, move, and stat performed by 19 file-manager abilities now routes through WordPress's <code>WP_Filesystem<\/code> transport instead of raw PHP filesystem functions. On the majority of hosts (<code>FS_METHOD='direct'<\/code>) the behaviour is identical. On hosts where WordPress requires FTP \/ SSH credentials (<code>FS_METHOD='ftpext'<\/code> \/ <code>'ftpsockets'<\/code> \/ <code>'ssh2'<\/code>) the abilities now succeed via the same channel WordPress core's file editor uses instead of silently failing.<\/p>\n\n<p><strong>Biggest wins \u2014 <code>wp-config.php<\/code> and <code>debug.log<\/code>:<\/strong> <code>file-manager\/read-wp-config<\/code>, <code>file-manager\/edit-wp-config<\/code>, <code>file-manager\/read-debug-log<\/code>, <code>file-manager\/clear-debug-log<\/code> are the abilities most likely to touch files owned by the SSH user rather than the web-server user. Those calls previously failed on non-<code>direct<\/code> transports; they now work.<\/p>\n\n<p><strong>New response value:<\/strong> every migrated ability's <code>blocked_reason<\/code> enum widens by one value \u2014 <code>filesystem_unavailable<\/code> \u2014 returned when <code>WP_Filesystem()<\/code> initialisation fails (typically missing <code>FTP_HOST<\/code> \/ <code>FTP_USER<\/code> \/ <code>FTP_PASS<\/code> on a non-<code>direct<\/code> host).<\/p>\n\n<p><strong>BREAKING \u2014 <code>file-manager\/file-info<\/code> schema shrink:<\/strong> the response no longer includes <code>ctime<\/code> or <code>atime<\/code> fields. <code>WP_Filesystem_Base<\/code> does not expose these consistently across transports (native <code>stat<\/code> returns them on <code>direct<\/code>, FTP\/SSH transports don't). Callers programmatically reading <code>.ctime<\/code> or <code>.atime<\/code> must switch to <code>.mtime<\/code> or accept the loss. Every other field on the response is unchanged.<\/p>\n\n<p><strong>Deferred to feature 092:<\/strong> <code>file-manager\/create-zip-backup<\/code>, <code>file-manager\/extract-zip-backup<\/code>, and <code>file-manager\/upload-zip-backup<\/code> remain on native PHP for now. <code>ZipArchive<\/code> requires direct filesystem access and has no <code>WP_Filesystem<\/code> equivalent, and chunked upload uses <code>fopen<\/code>\/<code>fwrite<\/code>\/<code>fclose<\/code> file-handle APIs that <code>WP_Filesystem<\/code> does not expose. These three abilities continue to work exactly as before on <code>direct<\/code> transports and continue to fail as before on FTP\/SSH ones. Every file carries a <code>\/\/ TODO(feature-092)<\/code> marker.<\/p>\n\n<p><strong>Housekeeping:<\/strong> approximately 20 <code>phpcs:ignore WordPress.WP.AlternativeFunctions<\/code> suppressions removed from the 19 migrated files. <code>Ability_Definition<\/code> and <code>File_Mods_Guard<\/code> are unchanged (verified \u2014 sibling plugin <code>acrossai-buddyboss<\/code> continues to extend the former without issue).<\/p>\n\n<p><strong>Feature 090 \u2014 file-manager additions.<\/strong> Four new abilities extend the <code>file-manager\/*<\/code> namespace to cover directory management and metadata: <code>file-manager\/append-file<\/code> (append or prepend to an existing file; refuses missing files and refuses <code>wp-config.php<\/code> \/ <code>.htaccess<\/code>), <code>file-manager\/create-directory<\/code> (recursive-by-default <code>mkdir<\/code> under ABSPATH; idempotent), <code>file-manager\/delete-directory<\/code> (empty-only by default; opt-in <code>recursive:true<\/code>; requires <code>confirm:true<\/code>; refuses nine critical WordPress directories), and <code>file-manager\/file-info<\/code> (read-only stat wrapper with optional POSIX owner\/group names). Ability count 385 \u2192 389.<\/p>\n\n<p><strong>Feature 089 \u2014 file abilities consolidation.<\/strong> Every file read \/ write \/ list \/ copy \/ move for the WordPress installation now flows through the <code>file-manager\/*<\/code> namespace. Three new abilities added; six duplicate theme- and plugin-scoped abilities removed; a pre-existing security gap closed.<\/p>\n\n<p><strong>Added \u2014 three new <code>file-manager\/*<\/code> abilities:<\/strong><\/p>\n\n<ul>\n<li><strong><code>file-manager\/list-directory<\/code><\/strong> \u2014 recursive directory walk under ABSPATH. Bounded by <code>max_depth<\/code> (default 5, max 20) and <code>max_entries<\/code> (default 1000, max 5000); response sets <code>truncated:true<\/code> when a bound is reached. Symlinks are not followed. Replaces <code>themes\/read-theme-structure<\/code> and <code>plugins\/read-plugin-structure<\/code>.<\/li>\n<li><strong><code>file-manager\/copy-file<\/code><\/strong> \u2014 copy a file between two paths under ABSPATH. Default refuses when the destination exists; pass <code>overwrite:true<\/code> to replace. Refuses copies <strong>onto<\/strong> <code>wp-config.php<\/code> or <code>.htaccess<\/code> even with <code>overwrite:true<\/code>. Replaces the copy mode of <code>plugins\/manage-plugin-files<\/code>.<\/li>\n<li><strong><code>file-manager\/move-file<\/code><\/strong> \u2014 rename\/move a file between two paths under ABSPATH. Same overwrite semantics as <code>copy-file<\/code>, plus refuses moves <strong>from<\/strong> <code>wp-config.php<\/code> or <code>.htaccess<\/code>. Replaces the move mode of <code>plugins\/manage-plugin-files<\/code>.<\/li>\n<\/ul>\n\n<p><strong>Removed \u2014 six duplicate abilities (BREAKING):<\/strong> any MCP client hardcoding these slugs will get an \"unknown ability\" error. Migrate to the <code>file-manager\/*<\/code> replacement.<\/p>\n\n\n\n\n  Removed slug\n  Replacement\n\n\n\n\n  <code>themes\/read-theme-code<\/code>\n  <code>file-manager\/read-file<\/code>\n\n\n  <code>themes\/edit-theme-file<\/code>\n  <code>file-manager\/edit-file<\/code>\n\n\n  <code>themes\/read-theme-structure<\/code>\n  <code>file-manager\/list-directory<\/code>\n\n\n  <code>plugins\/read-plugin-code<\/code>\n  <code>file-manager\/read-file<\/code>\n\n\n  <code>plugins\/read-plugin-structure<\/code>\n  <code>file-manager\/list-directory<\/code>\n\n\n  <code>plugins\/manage-plugin-files<\/code>\n  <code>file-manager\/copy-file<\/code> or <code>file-manager\/move-file<\/code>\n\n\n\n\n<p><strong>Hardened \u2014 <code>file-manager\/create-file<\/code> and <code>file-manager\/edit-file<\/code> now refuse <code>wp-config.php<\/code> and <code>.htaccess<\/code>.<\/strong> Before this release, these two abilities silently allowed overwriting those files even though <code>read-file<\/code> and <code>delete-file<\/code> refused them. This closes the last generic write path to those protected files; the specialized <code>file-manager\/edit-wp-config<\/code> (single-constant edit with secret-key allowlist) remains the only supported way to modify <code>wp-config.php<\/code>.<\/p>\n\n<p><strong>Kept as-is (not duplicates):<\/strong> <code>file-manager\/read-wp-config<\/code>, <code>file-manager\/edit-wp-config<\/code>, <code>file-manager\/get-wp-config-constant<\/code>, <code>file-manager\/read-debug-log<\/code>, <code>file-manager\/clear-debug-log<\/code>, <code>recovery\/list-recent-fatal-errors<\/code>, and all theme \/ plugin lifecycle abilities (install \/ activate \/ update \/ delete-theme \/ lifecycle-context \/ checksums \/ etc.).<\/p>\n\n<p><strong>Ability count:<\/strong> 388 \u2192 385 (-6 removed, +3 added). Full per-ability inventory refreshed at <code>docs\/abilities-inventory.md<\/code>.<\/p>\n\n<h4>0.0.30 - 2026-08-19<\/h4>\n\n<p><strong>Ability namespace migration \u2014 every ability slug moves from <code>acrossai\/*<\/code> to a topic-based prefix.<\/strong> 388 abilities across 24 topic namespaces. No behavioural changes; this is a slug rename only. Delivered as four disjoint PRs merged in order: #134 (blocks), #135 (elementor), #136 (rank-math), #137 (remaining 21 domains).<\/p>\n\n<p><strong>Breaking for every MCP client:<\/strong> any reference to <code>acrossai\/&lt;slug&gt;<\/code> must switch to <code>&lt;topic&gt;\/&lt;slug&gt;<\/code>. There is no back-compat alias \u2014 discovery now returns the new names only.<\/p>\n\n<ul>\n<li><strong><code>blocks\/*<\/code> (40)<\/strong> \u2014 every block-editor primitive: templates, template parts, patterns, style variations, global styles, theme.json, site-editor context, blocks\/reusable blocks. Also includes the 7 block-tree ops that previously lived under <code>acrossai\/*<\/code> in the Content folder (add-block, duplicate-block, get-post-blocks, insert-pattern, move-block, remove-block, update-post-block). Prefix-only rename \u2014 the second segment is preserved (e.g. <code>acrossai\/create-block-template<\/code> \u2192 <code>blocks\/create-block-template<\/code>).<\/li>\n<li><strong><code>elementor\/*<\/code> (62)<\/strong> \u2014 every Elementor ability. Redundant <code>elementor-<\/code> fragment collapsed into the namespace, so <code>acrossai\/elementor-add-widget<\/code> \u2192 <code>elementor\/add-widget<\/code>, <code>acrossai\/elementor-create-template<\/code> \u2192 <code>elementor\/create-template<\/code>, etc. <code>Base_Audit_Ability<\/code> now builds slugs as <code>'elementor\/' . audit_slug()<\/code> \u2014 all dynamic audit subclasses inherit the new prefix.<\/li>\n<li><strong><code>rank-math\/*<\/code> (61)<\/strong> \u2014 every Rank Math ability. Redundant <code>rank-math-<\/code> fragment collapsed. <code>Base_Rank_Math_Ability<\/code> slug construction changed in one line (<code>'rank-math\/' . slug()<\/code>) \u2014 the entire suite picks up the rename automatically. User-visible error messages that name specific slugs (e.g. <code>Utilities\/RankMath\/Maintenance_Tools<\/code>) refreshed to match.<\/li>\n<li><strong>Remaining 21 topic namespaces (225)<\/strong> \u2014 prefix-only rename per domain:\n\n<ul>\n<li><code>admin-menu\/<\/code> (5), <code>cache\/<\/code> (7), <code>comments\/<\/code> (12), <code>content\/<\/code> (29), <code>content-search\/<\/code> (11), <code>core\/<\/code> (6), <code>cron\/<\/code> (16), <code>database\/<\/code> (11), <code>file-manager\/<\/code> (15), <code>fonts\/<\/code> (8), <code>media\/<\/code> (11), <code>menus\/<\/code> (12), <code>options\/<\/code> (7), <code>plugins\/<\/code> (13), <code>recovery\/<\/code> (7), <code>settings\/<\/code> (11), <code>site-health\/<\/code> (6), <code>taxonomies\/<\/code> (10), <code>themes\/<\/code> (10), <code>users\/<\/code> (16), <code>widgets\/<\/code> (2).<\/li>\n<li>Examples: <code>acrossai\/get-option<\/code> \u2192 <code>options\/get-option<\/code>; <code>acrossai\/list-db-tables<\/code> \u2192 <code>database\/list-db-tables<\/code>; <code>acrossai\/create-user<\/code> \u2192 <code>users\/create-user<\/code>.<\/li>\n<li>The second segment is unchanged from what shipped before \u2014 only the vendor prefix moves. No collapsing (unlike Elementor\/Rank Math, where the redundant fragment was literally the namespace name).<\/li>\n<\/ul><\/li>\n<\/ul>\n\n<p><strong>Why:<\/strong> topic namespaces make the ability surface discoverable \u2014 a client fetching <code>mcp-adapter-discover-abilities<\/code> and filtering on the prefix gets exactly the domain it asked for. The old <code>acrossai\/<\/code> prefix tagged ownership but carried no discovery information. Full per-ability inventory is now published at <code>docs\/abilities-inventory.md<\/code>.<\/p>\n\n<p><strong>Category taxonomy slugs (<code>acrossai-abilities-manager-*<\/code>) are unchanged.<\/strong> Internal PHP class namespaces are unchanged. Tests, spec artifacts, and docstring cross-references were updated in the same commits as the slug renames \u2014 nothing left pointing at <code>acrossai\/*<\/code>.<\/p>\n\n<h4>0.0.29 - 2026-08-18<\/h4>\n\n<p><strong>Feature 065 \u2014 safety envelope + payload enrichment across 9 existing abilities.<\/strong> No new abilities. Plugin version bumped 0.0.28 \u2192 0.0.29. Two changes are breaking for programmatic callers: <code>media\/delete-media<\/code> and <code>file-manager\/delete-file<\/code> now require an explicit <code>confirm: true<\/code>; <code>content\/update-post<\/code> silently strips protected meta keys (reported back in <code>dropped_meta_keys<\/code>). Every guardrail-triggered refusal now returns <code>success: false<\/code> + a machine-readable <code>blocked_reason<\/code> + a human <code>message<\/code>, with no state mutation on the refusal path.<\/p>\n\n<ul>\n<li><strong><code>plugins\/deactivate-plugin<\/code> \u2014 protected-plugin guard.<\/strong> Refuses to deactivate <code>acrossai-mcp-manager<\/code>, <code>acrossai-abilities-manager<\/code>, or <code>acrossai-pro<\/code> \u2014 the three plugins that host either the ability surface itself or the MCP transport the AI is using to reach the site. Match runs against the <em>resolved<\/em> plugin file path, so slug \/ partial-name \/ file-path variants that fuzzy-resolve to a protected plugin are all refused (<code>blocked_reason: \"protected_plugin\"<\/code>).<\/li>\n<li><strong><code>media\/delete-media<\/code> \u2014 explicit confirmation + trash-aware.<\/strong> Requires <code>confirm: true<\/code> (refuses with <code>blocked_reason: \"confirmation_required\"<\/code> otherwise). Honours the <code>MEDIA_TRASH<\/code> constant \u2014 trashes when defined truthy and <code>force<\/code> is absent; permanent-deletes otherwise. Response now carries <code>deleted: \"deleted\" | \"trashed\"<\/code>.<\/li>\n<li><strong><code>file-manager\/delete-file<\/code> \u2014 confirmation + protected-write + backup + opcache invalidation.<\/strong> Requires <code>confirm: true<\/code>. Refuses on <code>wp-config.php<\/code> \/ <code>.htaccess<\/code> at ABSPATH (<code>blocked_reason: \"protected_write\"<\/code>). Writes a <code>.bak.&lt;timestamp&gt;<\/code> copy next to the target before the delete and returns the backup path in <code>backup<\/code>. Calls <code>opcache_invalidate()<\/code> on the deleted path when OPcache is loaded.<\/li>\n<li><strong><code>file-manager\/read-file<\/code> \u2014 protected-read + size cap + binary detection.<\/strong> Refuses on <code>wp-config.php<\/code> \/ <code>.htaccess<\/code> at ABSPATH (<code>blocked_reason: \"protected_read\"<\/code>) \u2014 this closes the highest-value accidental disclosure path (database password + eight auth constants). Refuses files over 5 MB without loading them into memory (<code>blocked_reason: \"file_too_large\"<\/code>; response reports observed size + cap). Non-UTF-8 payloads return <code>{ binary: true, size, path, message }<\/code> instead of raw bytes.<\/li>\n<li><strong><code>media\/list-media<\/code> \u2014 alt-text search.<\/strong> <code>search<\/code> now matches against <code>_wp_attachment_image_alt<\/code> postmeta in addition to WP_Query's default <code>s<\/code> fields (title \/ caption \/ description). Results are de-duplicated by attachment ID, so an image matched by both title and alt-text appears once.<\/li>\n<li><strong><code>media\/update-media<\/code> \u2014 updated-fields report.<\/strong> Response now carries an <code>updated<\/code> array naming each field that was actually written (subset of <code>title<\/code> \/ <code>caption<\/code> \/ <code>description<\/code> \/ <code>alt_text<\/code>), in the order fields were processed. Empty array when no update fields were passed.<\/li>\n<li><strong><code>content\/update-post<\/code> \u2014 writability + protected-meta + publish \/ author gates.<\/strong> Refuses on post types that are neither <code>public: true<\/code> nor <code>show_in_rest: true<\/code> (matches WP-REST writability). Filters caller-supplied <code>meta<\/code> to drop <code>_<\/code>-prefixed keys and any key that <code>is_protected_meta()<\/code> reports; the <code>acrossai_allowed_protected_meta<\/code> filter opts specific keys back in. Dropped keys are reported in the response as <code>dropped_meta_keys<\/code>. Refuses <code>status: \"publish\"<\/code> (or any status entering a public state) unless the caller holds <code>publish_posts<\/code> for the post type. Refuses <code>author: &lt;different_user_id&gt;<\/code> unless the caller holds <code>edit_others_posts<\/code>.<\/li>\n<li><strong><code>content\/get-post<\/code> \u2014 hydrated payload.<\/strong> Response now includes <code>terms<\/code> (object keyed by taxonomy, each entry <code>{ term_id, name, slug }<\/code>), <code>meta<\/code> (non-protected keys only \u2014 same allow-list filter as <code>update-post<\/code>), <code>featured_image<\/code> (<code>{ id, url, alt }<\/code> or <code>null<\/code>), <code>permalink<\/code>, <code>edit_link<\/code>, and <code>author: { id, name }<\/code>. Callers no longer need 4\u20135 follow-up hydration calls per post.<\/li>\n<li><strong><code>content\/delete-post<\/code> \u2014 suggested-redirect hint.<\/strong> When the target was <code>publish<\/code> and <code>force: true<\/code> is passed, the response includes <code>suggested_redirect: { from: &lt;permalink&gt;, to: &lt;parent-or-archive-or-root-url&gt; }<\/code>. Omitted for drafts and for trash operations (URL may return on restore).<\/li>\n<\/ul>\n\n<p><strong>Test coverage.<\/strong> <code>Test_Feature_065_Safety_And_Payload<\/code> \u2014 23 source-inspection tests covering all 23 FRs. Full suite green; PHPCS (WPCS strict) and PHPStan level 8 clean.<\/p>\n\n<h4>0.0.28 - 2026-08-17<\/h4>\n\n<p><strong>Feature 069 \u2014 Rank Math ability suite: 61 new abilities under a new \"Rank Math\" tab.<\/strong> Gated on Rank Math SEO being active; absent entirely without it. Plugin version bumped 0.0.27 \u2192 0.0.28.<\/p>\n\n<p>Coverage baseline was deliberately narrow: Rank Math core ships <strong>13<\/strong> abilities of its own under <code>rank-math\/<\/code>, and only those 13 counted as existing coverage. The third-party <code>mcp-abilities-rankmath<\/code> companion plugin was <strong>not<\/strong> treated as coverage \u2014 it is not ours to maintain, its writes go through raw <code>update_option()<\/code> blobs that bypass Rank Math's sanitizer, and it gates every ability on blanket <code>manage_options<\/code> regardless of the Role Manager. Slugs do not collide (<code>rank-math\/<\/code> vs <code>rankmath\/<\/code> vs <code>rank-math\/<\/code>).<\/p>\n\n<p><strong>Batch 1 \u2014 plumbing.<\/strong> <code>RankMath\\Category_Registrar<\/code> registers <code>acrossai-abilities-manager-rank-math<\/code>, guarded on <code>class_exists('\\RankMath\\Helper')<\/code>. <code>Base_Rank_Math_Ability<\/code> is the sole assembler of <code>ability()<\/code> and sole enforcer of the <code>execute()<\/code> guard order, which is what guarantees <code>tab_group =&gt; 'rank-math'<\/code> on all 61 \u2014 the Feature 078 regression class. <code>Rank_Math_Guard<\/code> holds every guard plus the response envelope.<\/p>\n\n<p><strong>Batch 2 \u2014 typed settings (6 abilities).<\/strong> <code>rank-math\/get-settings<\/code> reads any of 20 panels with each field's type, allowed values, bounds and current value, which makes the writers' accepted keys discoverable at runtime. <code>-update-general-settings<\/code>, <code>-update-title-settings<\/code> and <code>-update-sitemap-settings<\/code> take a section\/scope enum, replacing ~20 near-identical per-panel classes. <code>-update-instant-indexing-settings<\/code> and <code>-update-robots-txt<\/code> are separate because the first writes a different option and the second is conditional on state the caller cannot see. Titles &amp; Meta templates \u2014 the global per-post-type and per-taxonomy layer \u2014 had no read or write anywhere before this.<\/p>\n\n<p><strong>Batch 3 \u2014 Instant Indexing, modules, sitemap, routes (10 abilities).<\/strong> <code>-submit-urls<\/code>, <code>-get-indexing-log<\/code>, <code>-clear-indexing-log<\/code>, <code>-reset-indexing-key<\/code>; <code>-list-modules<\/code> and <code>-set-module-state<\/code>; <code>-get-sitemap-status<\/code>, <code>-list-sitemap-urls<\/code>, <code>-invalidate-sitemap-cache<\/code>; <code>-get-llms-status<\/code> and <code>-refresh-llms-route<\/code>. <code>-set-module-state<\/code> replicates Rank Math's own <code>save_module()<\/code> in full including the rewrite-rule refresh and <code>rank_math\/module_changed<\/code> action \u2014 omitting either leaves stale rewrite rules, so the sitemap and llms.txt routes 404 while the module reports itself active.<\/p>\n\n<p><strong>Batch 4 \u2014 redirections, 404 logs, roles (13 abilities).<\/strong> <code>-list-redirections<\/code> (with the <code>status=trashed<\/code> filter), <code>-find-redirection<\/code>, <code>-get-redirection-stats<\/code>, <code>-export-redirections<\/code>, <code>-create-redirection<\/code>, <code>-update-redirection<\/code>, <code>-change-redirection-status<\/code>, <code>-delete-redirections<\/code>, <code>-delete-trashed-redirections<\/code>; <code>-list-404-logs<\/code> and <code>-delete-404-logs<\/code>; <code>-get-role-capabilities<\/code> and <code>-reset-role-capabilities<\/code>. <strong><code>-update-redirection<\/code> fills a real gap: nothing could previously EDIT a redirection<\/strong>, and emulating it by delete-then-recreate loses the rule's id, hit counter and creation date. Apache\/Nginx export is a port of Rank Math's private formatters, since its own exporter reads <code>$_GET<\/code>, calls <code>check_admin_referer()<\/code>, echoes and exits.<\/p>\n\n<p><strong>Batch 5 \u2014 status, maintenance, backups (8 abilities).<\/strong> <code>-get-status<\/code> (5 panels behind an enum), <code>-run-maintenance-tool<\/code> (12 tools behind an enum), <code>-export-settings<\/code>, <code>-import-settings<\/code>, <code>-list-backups<\/code>, <code>-create-backup<\/code>, <code>-manage-backup<\/code>, <code>-detect-seo-plugins<\/code>, plus <code>-get-seo-analysis-results<\/code> for the cached audit.<\/p>\n\n<p><strong>Batch 6 \u2014 analytics and post-level content (16 abilities).<\/strong> <code>-get-analytics-summary<\/code> (6 reports), <code>-get-analytics-rows<\/code> (3 datasets), <code>-get-index-status<\/code>, <code>-inspect-url<\/code>; <code>-update-seo-meta<\/code>, <code>-bulk-update-meta<\/code>, <code>-update-seo-scores<\/code>, <code>-get-primary-term<\/code>, <code>-update-primary-term<\/code>, <code>-update-post-schemas<\/code>, <code>-delete-post-schemas<\/code>, <code>-get-schema-status<\/code>, <code>-get-rendered-head<\/code>, <code>-audit-content-seo<\/code>, <code>-get-inbound-links<\/code>, <code>-audit-faq-links<\/code>. <code>-get-inbound-links<\/code> answers which pages link <strong>to<\/strong> a page, including navigation-menu links \u2014 the opposite direction from every existing outbound-link ability.<\/p>\n\n<p><strong>Batch 7 \u2014 entitlement-gated (6 abilities).<\/strong> <code>-get-content-ai-status<\/code>, <code>-manage-content-ai-prompts<\/code>, <code>-manage-content-ai-output<\/code>, <code>-research-keyword<\/code>; <code>-get-ai-visibility-brand<\/code>, <code>-update-ai-visibility-object<\/code>. Registered <strong>unconditionally<\/strong> and gated at runtime, deliberately unlike <code>register_elementor_pro_abilities()<\/code>: Content AI and AI Visibility ship in Rank Math <em>free<\/em> and gate on cloud-account registration plus a credit balance, not on a separate plugin, so availability can change without an activation and cannot be decided at registration time.<\/p>\n\n<p><strong>Security.<\/strong> Every ability requires <code>manage_options<\/code> <strong>and<\/strong> Rank Math's own granular <code>rank_math_*<\/code> capability, matching the convention across the rest of the plugin's ability suites. The floor is uniform across all 61 and declared <code>final<\/code> so it cannot be lowered per ability. Revoking a capability in Rank Math's Role Manager therefore genuinely blocks the corresponding ability, which the companion plugin's blanket <code>manage_options<\/code> ignores. One documented filter, <code>acrossai_abilities_manager_rank_math_permission<\/code>, lets site owners relax the policy. Twelve abilities are irreversible and require <code>confirm: true<\/code>. The post-scoped writers additionally perform per-object <code>edit_post<\/code> \/ <code>edit_user<\/code> \/ <code>edit_terms<\/code> checks inside their handler as defence in depth, and the schema writers verify that a <code>schema-&lt;meta_id&gt;<\/code> row actually belongs to the named object before writing \u2014 Rank Math addresses schema rows by meta id and would otherwise update a different object's row.<\/p>\n\n<p><strong>Data-loss prevention.<\/strong> Rank Math's settings sanitizer defaults any field it was not told the type of to single-line text, which strips newlines, and its own field definitions use <em>legacy<\/em> CMB2 type names while the sanitizer's cases are the <em>React<\/em> names \u2014 11 of 19 legacy types match no case. <code>Settings_Registry<\/code> therefore ships a declarative field-spec table for all 20 panels, mirroring the Rank Math source with a file citation per panel, and maps legacy names onto the sanitizer's vocabulary. Verified live with a control: writing <code>nofollow_domains<\/code> with the mapped <code>textarea<\/code> type preserves newlines, while the identical write using Rank Math's own declared <code>textarea_small<\/code> stores them collapsed onto one line. Nine multi-line settings were at risk.<\/p>\n\n<p><strong>Notable.<\/strong> No raw Rank Math option read\/write ability ships \u2014 the plugin already provides generic option abilities, and adding Rank Math-branded raw writers would reintroduce exactly the data-loss path above. No bulk role-capability writer ships either, because <code>Helper::set_capabilities()<\/code> strips capabilities from roles omitted from the payload; the existing per-capability abilities cannot trigger that. The <code>.htaccess<\/code> editor, version rollback and beta opt-in are out of scope.<\/p>\n\n<h4>0.0.27 - 2026-08-14<\/h4>\n\n<p><strong>Patch release \u2014 UI polish + admin-surface rename following the 0.0.26 Feature 067 rollup.<\/strong> No new abilities; both entries below are UX-affecting changes to the admin surface. Plugin version bumped 0.0.26 \u2192 0.0.27.<\/p>\n\n<ul>\n<li><strong>Rename \u2014 \"Ability Library\" admin page is now \"Ability Integrations\".<\/strong> The submenu label (\"Library\" \u2192 \"Integrations\"), page title (\"Ability Library\" \u2192 \"Ability Integrations\"), main heading, and URL slug (<code>page=acrossai-abilities-library<\/code> \u2192 <code>page=acrossai-abilities-integrations<\/code>) all updated. Bookmarks \/ external links to the old slug will 404 in wp-admin \u2014 update saved links to the new URL. Internal class names, hook names, REST endpoint namespace (<code>\/wp-json\/acrossai-abilities-library\/v1\/<\/code>), and the DOM mount id are unchanged (deliberately scoped rename \u2014 extending to the REST namespace would break external MCP callers).<\/li>\n<li><strong>UI fix \u2014 Elementor abilities now render under their own \"Elementor\" tab in the Ability Integrations screen, not \"Core\".<\/strong> Every Elementor ability (all 88 under <code>elementor\/*<\/code>) had its meta <code>tab_group<\/code> set to <code>'core'<\/code>, causing the group to appear in the Core tab with only a sub-heading identifying it as Elementor. Flipped every declaration to <code>tab_group =&gt; 'elementor'<\/code> (63 files including <code>Base_Audit_Ability<\/code>, which drives the 25 audit subclasses via inheritance). The Ability Integrations UI auto-derives tab names from distinct <code>tab_group<\/code> values, so a new \"Elementor\" tab appears without any frontend\/asset rebuild.<\/li>\n<\/ul>\n\n<h4>0.0.26 - 2026-08-14<\/h4>\n\n<p><strong>Release rollup \u2014 89 abilities total: 87 unreleased Elementor abilities (Feature 067 completion) + 2 native site maintenance-mode abilities.<\/strong> Plugin version bumped 0.0.25 \u2192 0.0.26. Elementor abilities gate on <code>class_exists('\\Elementor\\Plugin')<\/code> (with 8 additionally gated on Elementor Pro); site maintenance-mode toggle has no plugin dependency.<\/p>\n\n<ul>\n<li><p><strong>Native site maintenance-mode toggle (2 abilities):<\/strong><\/p>\n\n<ul>\n<li><code>site-health\/set-site-maintenance-mode<\/code> \u2014 activate WordPress core maintenance mode by writing the <code>ABSPATH\/.maintenance<\/code> marker file (the same file WP core writes during plugin\/theme\/core updates). A wp-cron event refreshes the marker every 5 minutes so the site stays down for the requested <code>duration_minutes<\/code> (default 60, hard-cap 1440). Requires <code>confirm=true<\/code> \u2014 blocks wp-admin as well as the frontend.<\/li>\n<li><code>site-health\/unset-site-maintenance-mode<\/code> \u2014 deactivate: delete the marker, clear the refresh cron, drop the expiry option. Idempotent \u2014 safe to call when maintenance mode is already inactive. Reports <code>was_active<\/code> in the response.<\/li>\n<li>Both live under the existing <code>acrossai-abilities-manager-site-health<\/code> category alongside <code>site-health\/get-maintenance-mode-status<\/code> (Feature 063 read). No Elementor \/ plugin dependency \u2014 works on every WP install.<\/li>\n<\/ul><\/li>\n<li><p><strong>Feature 067 COMPLETE \u2014 87 additional Elementor abilities ship in this release.<\/strong> Combined with the 2 foundation abilities from 0.0.25, the full 88 planned abilities are now available under the <code>elementor\/*<\/code> namespace. Design-audit ability logic is skeletal (<code>Base_Audit_Ability<\/code> skeleton returning empty findings) \u2014 real audit heuristics to be filled in follow-up work.<\/p><\/li>\n<\/ul>\n\n<p><strong>Batch 10 \u2014 full-document replacement (closes the parity gap):<\/strong>\n  * <code>elementor\/update-data<\/code> \u2014 overwrite the entire <code>_elementor_data<\/code> tree for a post with a caller-supplied element array; optional <code>page_settings<\/code> merge; <code>force_replace=true<\/code> required when the new payload is materially smaller than the existing document. Returns <code>element_count<\/code> + cache scope report.<\/p>\n\n<p><strong>Batch 9 \u2014 29 design-audit abilities (this commit):<\/strong><\/p>\n\n<p>Aggregators + scorers (4):\n  * <code>elementor\/evaluate-design<\/code> \u2014 aggregate report from every registered design audit (score + findings + recommendations).\n  * <code>elementor\/suggest-design-fixes<\/code> \u2014 turn aggregated findings into concrete fix recommendations.\n  * <code>elementor\/score-distinctiveness<\/code> \u2014 neutral distinctiveness score for structural repetition.\n  * <code>elementor\/extract-design-tokens<\/code> \u2014 extract recurring colors \/ typography \/ spacing \/ dimensional tokens.<\/p>\n\n<p>Individual audits (14):\n  * Column: <code>audit-column-alignment-rhythm<\/code>, <code>audit-column-balance<\/code>, <code>audit-column-dominance<\/code>, <code>audit-column-necessity<\/code>, <code>audit-column-patterns<\/code>\n  * Composition &amp; emphasis: <code>audit-composition-rhythm<\/code>, <code>audit-emphasis-drift<\/code>, <code>audit-section-rivalry<\/code>, <code>audit-separator-discipline<\/code>, <code>audit-surface-overuse<\/code>\n  * Layout &amp; repetition: <code>audit-generic-component-repetition<\/code>, <code>audit-generic-layout-patterns<\/code>, <code>audit-layout-mechanism-fit<\/code>, <code>audit-native-widget-opportunities<\/code><\/p>\n\n<p>Subtree operations \u2014 destructive (7):\n  * <code>apply-text-hierarchy<\/code>, <code>enforce-boundary-coherence<\/code>, <code>fix-visible-gap-rhythm<\/code>, <code>normalize-responsive-values<\/code>, <code>normalize-section-spacing-rhythm<\/code>, <code>reset-negative-margins-subtree<\/code>, <code>zero-container-padding-subtree<\/code><\/p>\n\n<p>Copy \/ sync \/ convert helpers \u2014 destructive (4):\n  * <code>copy-lane-settings<\/code>, <code>copy-row-balance<\/code>, <code>image-widget-to-background-container<\/code>, <code>sync-component-variant<\/code><\/p>\n\n<p>New utility class <code>includes\/Abilities\/Elementor\/Base_Audit_Ability.php<\/code> provides the shared skeleton for 27 of the 29 audit abilities \u2014 subclasses supply <code>audit_slug<\/code>, <code>audit_label<\/code>, <code>audit_description<\/code>, and <code>analyze()<\/code>. <code>Evaluate_Design<\/code> and <code>Suggest_Design_Fixes<\/code> are self-contained aggregators.<\/p>\n\n<p><strong>Batch 8 \u2014 8 Elementor Pro-gated abilities:<\/strong>\n  * <code>elementor\/list-custom-code<\/code> \u2014 list Custom Code snippets from <code>elementor_snippet<\/code> CPT; optional location filter.\n  * <code>elementor\/get-custom-code<\/code> \u2014 read one snippet including its code body.\n  * <code>elementor\/create-custom-code<\/code> \u2014 create snippet with title, code, location (head \/ body_start \/ body_end \/ footer), priority, status.\n  * <code>elementor\/update-custom-code<\/code> \u2014 update snippet fields.\n  * <code>elementor\/delete-custom-code<\/code> \u2014 trash (default) or permanently delete with <code>force=true<\/code>.\n  * <code>elementor\/list-form-submissions<\/code> \u2014 list Form widget submissions from the <code>e_submissions<\/code> table; optional <code>form_id<\/code> filter + <code>include_values<\/code> flag. Graceful degradation when the Pro submissions table is missing.\n  * <code>elementor\/get-form-submission<\/code> \u2014 read one submission by ID; optional field values.\n  * <code>elementor\/delete-form-submission<\/code> \u2014 permanently delete submission + its <code>e_submissions_values<\/code> rows; requires <code>confirm=true<\/code>.<\/p>\n\n<p>All 8 Pro abilities gated on <strong>both<\/strong> <code>class_exists( '\\Elementor\\Plugin' )<\/code> <strong>and<\/strong> <code>class_exists( '\\ElementorPro\\Plugin' ) || defined( 'ELEMENTOR_PRO_VERSION' )<\/code> \u2014 silently absent on sites without Elementor Pro. Runtime deactivation returns <code>error_code: elementor_pro_missing<\/code>.<\/p>\n\n<p><strong>Batch 7 \u2014 7 kits &amp; site-settings abilities:<\/strong>\n  * <code>elementor\/list-kits<\/code> \u2014 list all Elementor kits; marks active kit.\n  * <code>elementor\/get-kit-settings<\/code> \u2014 read kit settings (defaults to active kit).\n  * <code>elementor\/update-kit-settings<\/code> \u2014 merge new settings; <code>force_replace<\/code> for full overwrite; site-wide cache invalidation.\n  * <code>elementor\/set-active-kit<\/code> \u2014 switch site-wide active kit; invalidates cache.\n  * <code>elementor\/list-global-widgets<\/code> \u2014 list global (reusable) widgets from elementor_library CPT.\n  * <code>elementor\/list-experiments<\/code> \u2014 list feature flags with current + default state.\n  * <code>elementor\/update-experiment<\/code> \u2014 toggle experiment state (active | inactive | default).<\/p>\n\n<p><strong>Batch 6 \u2014 11 template abilities:<\/strong>\n  * <code>elementor\/list-templates<\/code> \u2014 list saved templates with filters on <code>template_type<\/code> + <code>status<\/code> + pagination.\n  * <code>elementor\/get-template<\/code> \u2014 return one template's metadata + conditions + optional <code>_elementor_data<\/code>.\n  * <code>elementor\/create-template<\/code> \u2014 create a new template of type page \/ section \/ popup \/ header \/ footer \/ single \/ archive; sets taxonomy term + Elementor meta.\n  * <code>elementor\/update-template<\/code> \u2014 update title \/ page_settings \/ full data with <code>force_replace<\/code> guard.\n  * <code>elementor\/delete-template<\/code> \u2014 trash (default) or permanently delete with <code>force=true<\/code>.\n  * <code>elementor\/restore-template<\/code> \u2014 restore a trashed template.\n  * <code>elementor\/duplicate-template<\/code> \u2014 clone template preserving type + conditions + sub_type; regenerates element IDs.\n  * <code>elementor\/empty-trash<\/code> \u2014 permanently delete every trashed template; requires <code>confirm=true<\/code>.\n  * <code>elementor\/export-template<\/code> \u2014 export template as JSON-encodable object (title, template_type, sub_type, page_settings, content, conditions).\n  * <code>elementor\/import-template<\/code> \u2014 import from JSON export; regenerates element IDs; optional <code>overwrite_id<\/code> to replace an existing template.\n  * <code>elementor\/find-template-for-pattern<\/code> \u2014 rank saved templates by keyword match (title + tax term + widget-types in content); returns top N with scores.<\/p>\n\n<p><strong>Batch 5 \u2014 11 site-management abilities:<\/strong>\n  * <code>elementor\/clear-cache<\/code> \u2014 clear Elementor cache at post \/ site \/ all scope; optional <code>regenerate_css=true<\/code> for a specific post.\n  * <code>elementor\/replace-urls<\/code> \u2014 bulk find\/replace URLs across every Elementor document on the site with <code>dry_run=true<\/code> default preview.\n  * <code>elementor\/get-maintenance-mode<\/code> \u2014 read current maintenance mode settings (mode, template, exclude rules).\n  * <code>elementor\/update-maintenance-mode<\/code> \u2014 enable\/disable maintenance mode with mode selection (maintenance | coming_soon).\n  * <code>elementor\/get-theme-builder-conditions<\/code> \u2014 read display conditions attached to an Elementor template.\n  * <code>elementor\/update-theme-builder-conditions<\/code> \u2014 replace display conditions; pass empty array to clear. Invalidates Elementor's condition cache.\n  * <code>elementor\/get-official-widget-catalog<\/code> \u2014 canonical widget catalog (Basic \/ Pro \/ Theme \/ WooCommerce) with 12-hour transient.\n  * <code>elementor\/get-official-pattern-guidance<\/code> \u2014 pattern &amp; layout guidance (widgets \/ patterns \/ layouts topics) grounded in Elementor documentation.\n  * <code>elementor\/get-theme-context<\/code> \u2014 active theme + Elementor version + active kit + viewport settings snapshot.\n  * <code>elementor\/get-style-guide<\/code> \u2014 style-guide summary from active kit (colors, typography, buttons, forms, layout, custom CSS).\n  * <code>elementor\/evaluate-render-context<\/code> \u2014 inspect frontend template + canvas type + edit-mode flag for a post.<\/p>\n\n<p><strong>Batch 4 \u2014 9 page-composition abilities:<\/strong>\n  * <code>elementor\/create-page<\/code> \u2014 insert a new post\/page pre-configured for Elementor  &hellip;<\/p>","raw_excerpt":"Manage every WordPress ability registered on your site \u2014 view, search, override, and bulk-control ability metadata from a single admin page.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/311005","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=311005"}],"author":[{"embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/raftaar1191"}],"wp:attachment":[{"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=311005"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=311005"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=311005"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=311005"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=311005"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/it.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=311005"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}