AcrossAI Abilities Manager

ຄຳອະທິບາຍ

AcrossAI Abilities Manager gives site administrators full visibility and control over every ability registered via the WordPress Abilities API (wp_get_ability()).

Features:

  • Browse all abilities — a searchable, sortable, paginated table listing every registered ability with slug, provider, source, and current status.
  • Toggle allow/disallow — enable or disable any ability site-wide with a single click. Changes are saved instantly without a page reload.
  • Edit ability metadata — override readonly, destructive, idempotent, show_in_rest, show_in_mcp, mcp_type, and mcp_servers fields per ability using a tri-state system (Yes / No / Inherit from registry).
  • Reset overrides — restore any ability back to its registry defaults with one click.
  • Bulk actions — allow, disallow, or reset up to 50 abilities at once.
  • Ability Library — enable or disable add-on ability groups from a dedicated Library page, with All/Specific mode controls per group.
  • Add-ons page — 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 Add New Upload Plugin.
  • MCP server list — view all registered MCP servers when the MCP Adapter plugin is active.
  • Debugging Conflict Testing — toggle any installed plugin’s effective active state without ever writing to wp_options.active_plugins. Seven WP Abilities API abilities (acrossai/conflict-test-list-plugins, -get-overrides, -set-override, -bulk-set-overrides, -clear-overrides, -deploy-mu-plugin, -remove-mu-plugin) 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+ Requires Plugins: headers by default. Every active=true write is guarded by a WordPress-core-style plugin_sandbox_scrape probe, so a broken plugin can never leave the site in a state where every subsequent page load fatals — the override is refused instead. Feature 061.

All overrides are stored in a dedicated database table. The WordPress ability registry is never modified — only the fields that differ from registry defaults are persisted.

Security:

  • All endpoints require manage_options capability.
  • All state-changing requests are protected by WordPress nonce verification.
  • All input is sanitized; all output is escaped.

Third-party integrations (optional):

  • MCP Adapter plugin — 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.

This plugin’s own code makes no external HTTP requests. One admin-only surface can contact an external service on your behalf: the AcrossAI Add-ons page installs WordPress.org-hosted companion plugins directly through WordPress core’s own plugin installer (api.wordpress.org + downloads.wordpress.org). Add-ons registered with any other source (e.g. GitHub, Freemius) are shown as external “Get add-on ” links that open the vendor’s site in a new browser tab — the plugin does not download or install them itself. The AcrossAI Consultations submenu renders a static call-to-action button that opens calendly.com in a new browser tab only after the administrator clicks it — no third-party asset is loaded inside wp-admin. Full disclosure — including what data is transmitted to each service and links to their terms + privacy policies — is in the External Services section below.

External Services

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.

1. Calendly external link (calendly.com)

What it is: Calendly is a third-party scheduling service. The AcrossAI Consultations submenu displays a static call-to-action button that links out to a Calendly booking page for AcrossAI consultations (“Using AI in WordPress”).

When it is contacted: Never on page render. The Consultations submenu at /wp-admin/admin.php?page=acrossai-consultations is a self-contained wp-admin page — 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 https://calendly.com/acrossai/using-ai-in-wordpress in a new tab (target="_blank" rel="noopener noreferrer"). This is identical to clicking any external hyperlink from an admin page.

What is loaded on the Consultations page: 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 fonts.googleapis.com) — permitted under the “third-party CDNs beyond fonts” carve-out in the WordPress plugin guidelines.

What data is transmitted to Calendly: 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.

Terms of service: https://calendly.com/pages/terms
Privacy policy: https://calendly.com/pages/privacy

2. WordPress.org plugin directory (api.wordpress.org and downloads.wordpress.org)

What it is: The Add-ons page (/wp-admin/admin.php?page=acrossai-addons) uses the WordPress.org plugin directory to install free companion plugins directly from wp-admin.

When it is contacted: Only when an authenticated administrator (install_plugins capability) clicks the “Install” button on a card whose source is wordpress.org. Contact happens through WordPress core’s own plugins_api() and Plugin_Upgrader — this plugin does not issue direct HTTP requests. Add-ons registered with any other source (e.g. github, freemius) are rendered as external “Get add-on ” 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.

What data is transmitted: The WordPress core plugin API request payload (site URL, WP version, PHP version, locale) as per WordPress core’s standard update check protocol.

Terms of service: https://wordpress.org/about/terms/
Privacy policy: https://wordpress.org/about/privacy/

3. WordPress.org core version-check API (api.wordpress.org/core/version-check/1.7/)

Called only when an administrator invokes the core/rollback-wp-core 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 — no data beyond the standard WordPress core version-check request payload is transmitted. Same wp.org terms + privacy policy as service #2 above.

Privacy Policy

This plugin does not itself collect, store, or transmit any user data to any third party.

Several admin-only actions can cause external services to receive data — all are described in the External Services section above and are triggered only by an authenticated administrator:

  • The AcrossAI Consultations admin page displays a static call-to-action button. Merely loading the Consultations page sends no data to Calendly — no Calendly script, iframe, or asset is loaded inside wp-admin. If the administrator clicks the CTA button, their browser opens calendly.com/acrossai/using-ai-in-wordpress 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.
  • Installing a WordPress.org-hosted add-on from the AcrossAI Add-ons page contacts the WordPress.org plugin directory via WordPress core’s own plugins_api() and Plugin_Upgrader (api.wordpress.org + downloads.wordpress.org). Add-ons distributed elsewhere (e.g. GitHub, Freemius) are rendered as external “Get add-on ” links that open the vendor’s site in a new browser tab — 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.
  • Invoking the core/rollback-wp-core ability contacts the WordPress.org core version-check API (a WordPress-core-hosted service) via the standard WordPress update API.

No data is sent to any external server without an explicit administrator action.

ພາບໜ້າຈໍ

ການຕິດຕັ້ງ

  1. Upload the acrossai-abilities-manager folder to /wp-content/plugins/.
  2. Activate the plugin through the Plugins menu in WordPress.
  3. Navigate to AcrossAI Abilities Manager in the WordPress admin menu.

Add-ons:

  1. Go to AcrossAI Add-ons to browse available companion plugins.
  2. 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.

ຄຳຖາມທີ່ພົບເລື້ອຍ

Does this plugin support Multisite?

No. This plugin has not been tested on WordPress Multisite installations.

Does this plugin modify the WordPress ability registry?

No. The plugin stores only overrides — fields that differ from the registry defaults. The ability registry itself (wp_get_ability()) is never modified.

What happens when I reset an override?

The override row is deleted from the database. The ability will inherit its values from the registry again.

What is the Ability Library?

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.

What is the MCP Adapter integration?

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 — the plugin works without the MCP Adapter.

Does this plugin make external HTTP requests?

The plugin’s own code makes no external HTTP requests. Two admin-only surfaces trigger external connections on behalf of an authenticated administrator:

  • AcrossAI Consultations submenu — renders a static call-to-action button that links to https://calendly.com/acrossai/using-ai-in-wordpress 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 — at which point their browser navigates directly to calendly.com, exactly as with any external hyperlink.
  • AcrossAI Add-ons submenu — installs WordPress.org-hosted companion plugins in place through WordPress core’s plugins_api() + Plugin_Upgrader (contacts api.wordpress.org + downloads.wordpress.org). Add-ons registered with any other source (e.g. GitHub, Freemius) render as external “Get add-on ” links that open the vendor’s site in a new browser tab — the plugin does not download or install those add-ons itself. Users install off-directory add-ons via WP admin’s standard Plugins Add New Upload Plugin flow (or via the vendor’s own installer once the paid plugin is activated).

Full disclosure — including what data is transmitted, and links to each service’s terms + privacy policy — is in the External Services section of this readme.

ການຣີວິວ

ບໍ່ມີການຣີວິວສຳລັບປລັກອິນນີ້.

ຜູ້ຮ່ວມພັດທະນາ ແລະ ຜູ້ພັດທະນາ

“AcrossAI Abilities Manager” ແມ່ນຊອຟແວໂອເພັນຊອດ (Open Source). ບຸກຄົນຕໍ່ໄປນີ້ໄດ້ມີສ່ວນຮ່ວມໃນການພັດທະນາປລັກອິນນີ້.

ຜູ້ຮ່ວມພັດທະນາ

ແປ “AcrossAI Abilities Manager” ເປັນພາສາຂອງເຈົ້າ.

ສົນໃຈຮ່ວມພັດທະນາບໍ່?

ເບິ່ງລະຫັດ, ກວດເບິ່ງ ຄັງເກັບ SVN, ຫຼື ຕິດຕາມ ບັນທຶກການພັດທະນາ ຜ່ານ RSS.

ບັນທຶກການປ່ຽນແປງ

Unreleased

0.0.31 – 2026-08-26

Release theme: File Manager consolidation + hardening + audit log. Bundles the six-feature series (089 094) that turned file-manager/* 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 .bak.<timestamp> scheme in Delete_File (BREAKING for callers of response.backup — new canonical field is response.backup_path).

Feature 094 — File Manager Audit Log + Backup Harness (partial). Consumes the four Backup & Audit option keys shipped as scaffold in PR #144 and enforced-toggle-only in PR #146. New Audit_Trail utility owns pre-image backups (into wp-content/acrossai-file-manager-backups/<YYYY-MM-DD>/) + append-only log (into wp-content/acrossai-file-manager-logs/acrossai-file-manager.log) + amortised 1-in-10 cleanup + stats. Both storage locations get a Deny from all .htaccess on first creation. All I/O goes through WP_Filesystem.

New ability: file-manager/get-changelog — tails the last N entries (default 100, max 500) via MCP. Honours the read allowlist. Empty log returns a friendly message, not an error. manage_options gated.

Log entry format (blank-line separated, one entry per mutation):

[YYYY-MM-DD HH:MM:SS UTC]
Ability: file-manager/
File:
User: (ID:) IP:
Size: -> bytes
Destination: (COPY / MOVE only)
Backup:
Context:

New action hook: do_action('acrossai_file_manager_log_entry', $entry) fires after every log write. Subscribers (Slack, Datadog, SIEM…) receive the parsed entry as an assoc array. Zero cost when no subscribers.

New optional context input field on wired abilities (delete-file, edit-file, create-directory in this PR — more in the follow-up). Schema max 2000 chars; log writer truncates to 500 chars via sanitize_text_field before persisting.

BREAKING — file-manager/delete-file backup response field. The inline <path>.bak.<time> scheme is REPLACED by the centralised backup dir. When backup_enabled=true the response’s new canonical field is backup_path; the legacy backup field is populated with the same value for one transition release and will be removed. When backup_enabled=false NO backup is written at all — callers who relied on the inline .bak return value now get null for both fields when the toggle is off. Direct callers to read backup_path instead of backup.

Retention. backup_retention_days deletes backup dirs older than N days; audit_log_retention_days trims log entries older than N days. Both fire probabilistically (1-in-10 per log write) — no WP-Cron dependency.

Scope note (PARTIAL). This PR wires three abilities end-to-end (delete-file, edit-file, create-directory) to prove the design across the backup + log-only paths. The remaining seven mutation abilities (create-file, append-file, copy-file, move-file, delete-directory, edit-wp-config, clear-debug-log) and the BackupAuditPanel scaffold-banner flip + /backup-audit-stats REST endpoint + uninstall extension are DEFERRED to a follow-up PR. Test coverage is scoped accordingly (35 new tests, mostly structural — behavioural I/O tests need a full-WP bootstrap and run in the CI matrix).

Feature 093 — File Manager Hardening (enforcement pass for PR #144 scaffold). 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 file-manager/read-file. Persistence layer (Hardening_Settings) unchanged; the new Hardening_Enforcer utility runs each check after the existing File_Mods_Guard + Path_Allowlist_Guard gates and returns the standard {success:false, blocked_reason, path, message, …context} envelope on refusal. Enforcement order (best-cheap-first): dangerous_extensions block_double_extensions sanitize_filename_check strict_filename_filter mime_type_check htaccess_directive_scan write_max_bytes. Empty list / false toggle is a no-op so callers upgrading with defaults get zero behaviour change beyond what defaults dictate.

Six abilities wiredfile-manager/{create-file, edit-file, append-file, copy-file, move-file} for the seven content-filter checks; file-manager/read-file for the sensitive-read denylist. Copy/move check the DESTINATION basename; append-file scans only the appended bytes for .htaccess directives and caps size on new_size = existing + appended; copy/move use source file size for the size cap and read source content lazily for the .htaccess scan (only when destination basename is .htaccess). mime_type_check skips append-file (extension didn’t change) and always allows {php, txt, log, json, xml, css, js, md, html, htm, htaccess} even with the check on — prevents breaking the mu-plugins deploy use case the write allowlist explicitly permits.

Eight new blocked_reason valuesextension_blocked (+extension), double_extension_blocked (+basename), htaccess_directive_blocked (+directive), filename_sanitize_failed (+input, sanitized), write_size_exceeded (+size, max_bytes), filename_strict_blocked (+marker), mime_type_blocked (+extension), sensitive_read_blocked (+basename, matched_pattern). Every affected ability’s output_schema declares the union of these context fields. Existing path_not_allowed_for_write / path_not_allowed_for_read / protected_write / file_mods_disabled envelopes are unchanged and take precedence — hardening refusals fire only when all earlier gates pass.

Ordering guarantee — sensitive-read denylist runs AFTER the read allowlist (spec FR-011). If the allowlist would refuse a path, that refusal (path_not_allowed_for_read) is returned; the denylist only fires when the allowlist would permit. This lets an admin safely widen the read allowlist and still keep .env / *.key / id_rsa blocked.

Panel banner update — the Content Filters panel drops its yellow “Scaffold only” notice-warning (added in PR #144) and replaces it with a small notice-info reading “This list now gates create-file / edit-file / append-file / copy-file / move-file.” The Backup & Audit panel keeps its scaffold banner but the text now references 094-file-manager-audit-log explicitly. REST GET /acrossai/v1/file-manager-settings/content-filters flips scaffold_only:true → false and follow_up_spec becomes null; /backup-audit unchanged.

Dotfile carve-out for sanitize_filename_check. Legitimate WordPress-adjacent dotfiles (.htaccess, .htpasswd, .user.ini) are exempted from the sanitize-filename roundtrip check because real WP’s sanitize_file_name() strips leading dots — applied literally, the check would refuse every valid dotfile and starve the htaccess-directive scanner of any target. Other dotfiles (.gitignore, .env, etc.) are still refused when the check is on; admins who need them disable the check.

Not touched. Delete_File, Delete_Directory, Create_Directory, File_Info, Read_Debug_Log (fixed target), Read_Wp_Config, Get_Wp_Config_Constant, Edit_Wp_Config, all six zip abilities — spec explicitly excludes these.

Feature 092 — File Manager admin tab: per-folder read/write allowlists + configurable secret redactor. New “File Manager” tab at admin.php?page=acrossai-settings gives site admins three per-folder controls over what MCP clients can do via file-manager/* abilities. Also introduces a hardened secret-scrubber that runs on every read response.

Write allowlist. The 8 write-capable file-manager abilities (create-file, edit-file, delete-file, copy-file, move-file, append-file, create-directory, delete-directory) refuse any operation whose target path resolves outside the admin’s allowlist. Default on activation: ['wp-content'] (writes only inside wp-content). copy-file and move-file check both source and destination. Refusal returns {success:false, blocked_reason:"path_not_allowed_for_write", allowed_roots:[…]}.

Read allowlist. The 2 content-reading abilities (read-file, read-debug-log) can also be gated. Default on activation: [] — unrestricted (every path readable). Admins can flip a “Restrict reads to specific folders” toggle and pick specific folders. Refusal returns {success:false, blocked_reason:"path_not_allowed_for_read"}. list-directory and file-info remain ungated (metadata only, no content leak).

Secret redactor. Every text response from read-file and read-debug-log is scrubbed before return. Ships one built-in pattern: WordPress credentials (DB_PASSWORD, DB_USER, all 8 auth keys/salts, SECRET_KEY) — 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 — assumptions about someone else’s key format belong in the site’s own config, not in this plugin.

Auto-scrub for AI-connector API keys. If the WordPress AI plugin (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 — the admin does not have to copy those keys into the custom-literals textarea. Currently covers OpenAI (connectors_ai_openai_api_key), Anthropic (connectors_ai_anthropic_api_key), and Google (connectors_ai_google_api_key). No configuration required; the redactor reads these options on every scrub call. Responses grow two fields: redacted:bool and redaction_count:int.

REST endpoints. Six new routes under acrossai/v1: GET/POST /file-manager-settings/write-allowlist, /read-allowlist, /redaction. manage_options + X-WP-Nonce required. GET responses include enumeration data (immediate ABSPATH children, get_plugins(), wp_get_themes()) so the React UI renders without a second round-trip.

BREAKING — file-manager/read-file. The previous outright refusal of wp-config.php and .htaccess (blocked_reason:"protected_read") is REMOVED. Those files are now readable; sensitive content is redacted per the secret redactor above. Callers that programmatically handled blocked_reason:"protected_read" should switch to reading the returned content with redacted:true. Write-side refusals on wp-config.php / .htaccess for create-file, edit-file, delete-file, copy-file, move-file, append-file are UNCHANGED.

Not touched. Ability_Definition, File_Mods_Guard, Wp_Filesystem_Init, read-wp-config, edit-wp-config, get-wp-config-constant, list-directory, file-info, all 6 zip-backup abilities, and every ability outside file-manager/*.

Feature 091 milestone

Feature 091 — WP_Filesystem migration for file-manager/* abilities. Every filesystem read, write, list, delete, copy, move, and stat performed by 19 file-manager abilities now routes through WordPress’s WP_Filesystem transport instead of raw PHP filesystem functions. On the majority of hosts (FS_METHOD='direct') the behaviour is identical. On hosts where WordPress requires FTP / SSH credentials (FS_METHOD='ftpext' / 'ftpsockets' / 'ssh2') the abilities now succeed via the same channel WordPress core’s file editor uses instead of silently failing.

Biggest wins — wp-config.php and debug.log: file-manager/read-wp-config, file-manager/edit-wp-config, file-manager/read-debug-log, file-manager/clear-debug-log 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-direct transports; they now work.

New response value: every migrated ability’s blocked_reason enum widens by one value — filesystem_unavailable — returned when WP_Filesystem() initialisation fails (typically missing FTP_HOST / FTP_USER / FTP_PASS on a non-direct host).

BREAKING — file-manager/file-info schema shrink: the response no longer includes ctime or atime fields. WP_Filesystem_Base does not expose these consistently across transports (native stat returns them on direct, FTP/SSH transports don’t). Callers programmatically reading .ctime or .atime must switch to .mtime or accept the loss. Every other field on the response is unchanged.

Deferred to feature 092: file-manager/create-zip-backup, file-manager/extract-zip-backup, and file-manager/upload-zip-backup remain on native PHP for now. ZipArchive requires direct filesystem access and has no WP_Filesystem equivalent, and chunked upload uses fopen/fwrite/fclose file-handle APIs that WP_Filesystem does not expose. These three abilities continue to work exactly as before on direct transports and continue to fail as before on FTP/SSH ones. Every file carries a // TODO(feature-092) marker.

Housekeeping: approximately 20 phpcs:ignore WordPress.WP.AlternativeFunctions suppressions removed from the 19 migrated files. Ability_Definition and File_Mods_Guard are unchanged (verified — sibling plugin acrossai-buddyboss continues to extend the former without issue).

Feature 090 — file-manager additions. Four new abilities extend the file-manager/* namespace to cover directory management and metadata: file-manager/append-file (append or prepend to an existing file; refuses missing files and refuses wp-config.php / .htaccess), file-manager/create-directory (recursive-by-default mkdir under ABSPATH; idempotent), file-manager/delete-directory (empty-only by default; opt-in recursive:true; requires confirm:true; refuses nine critical WordPress directories), and file-manager/file-info (read-only stat wrapper with optional POSIX owner/group names). Ability count 385 389.

Feature 089 — file abilities consolidation. Every file read / write / list / copy / move for the WordPress installation now flows through the file-manager/* namespace. Three new abilities added; six duplicate theme- and plugin-scoped abilities removed; a pre-existing security gap closed.

Added — three new file-manager/* abilities:

  • file-manager/list-directory — recursive directory walk under ABSPATH. Bounded by max_depth (default 5, max 20) and max_entries (default 1000, max 5000); response sets truncated:true when a bound is reached. Symlinks are not followed. Replaces themes/read-theme-structure and plugins/read-plugin-structure.
  • file-manager/copy-file — copy a file between two paths under ABSPATH. Default refuses when the destination exists; pass overwrite:true to replace. Refuses copies onto wp-config.php or .htaccess even with overwrite:true. Replaces the copy mode of plugins/manage-plugin-files.
  • file-manager/move-file — rename/move a file between two paths under ABSPATH. Same overwrite semantics as copy-file, plus refuses moves from wp-config.php or .htaccess. Replaces the move mode of plugins/manage-plugin-files.

Removed — six duplicate abilities (BREAKING): any MCP client hardcoding these slugs will get an “unknown ability” error. Migrate to the file-manager/* replacement.

Removed slug
Replacement

themes/read-theme-code
file-manager/read-file

themes/edit-theme-file
file-manager/edit-file

themes/read-theme-structure
file-manager/list-directory

plugins/read-plugin-code
file-manager/read-file

plugins/read-plugin-structure
file-manager/list-directory

plugins/manage-plugin-files
file-manager/copy-file or file-manager/move-file

Hardened — file-manager/create-file and file-manager/edit-file now refuse wp-config.php and .htaccess. Before this release, these two abilities silently allowed overwriting those files even though read-file and delete-file refused them. This closes the last generic write path to those protected files; the specialized file-manager/edit-wp-config (single-constant edit with secret-key allowlist) remains the only supported way to modify wp-config.php.

Kept as-is (not duplicates): file-manager/read-wp-config, file-manager/edit-wp-config, file-manager/get-wp-config-constant, file-manager/read-debug-log, file-manager/clear-debug-log, recovery/list-recent-fatal-errors, and all theme / plugin lifecycle abilities (install / activate / update / delete-theme / lifecycle-context / checksums / etc.).

Ability count: 388 385 (-6 removed, +3 added). Full per-ability inventory refreshed at docs/abilities-inventory.md.

0.0.30 – 2026-08-19

Ability namespace migration — every ability slug moves from acrossai/* to a topic-based prefix. 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).

Breaking for every MCP client: any reference to acrossai/<slug> must switch to <topic>/<slug>. There is no back-compat alias — discovery now returns the new names only.

  • blocks/* (40) — 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 acrossai/* in the Content folder (add-block, duplicate-block, get-post-blocks, insert-pattern, move-block, remove-block, update-post-block). Prefix-only rename — the second segment is preserved (e.g. acrossai/create-block-template blocks/create-block-template).
  • elementor/* (62) — every Elementor ability. Redundant elementor- fragment collapsed into the namespace, so acrossai/elementor-add-widget elementor/add-widget, acrossai/elementor-create-template elementor/create-template, etc. Base_Audit_Ability now builds slugs as 'elementor/' . audit_slug() — all dynamic audit subclasses inherit the new prefix.
  • rank-math/* (61) — every Rank Math ability. Redundant rank-math- fragment collapsed. Base_Rank_Math_Ability slug construction changed in one line ('rank-math/' . slug()) — the entire suite picks up the rename automatically. User-visible error messages that name specific slugs (e.g. Utilities/RankMath/Maintenance_Tools) refreshed to match.
  • Remaining 21 topic namespaces (225) — prefix-only rename per domain:
    • admin-menu/ (5), cache/ (7), comments/ (12), content/ (29), content-search/ (11), core/ (6), cron/ (16), database/ (11), file-manager/ (15), fonts/ (8), media/ (11), menus/ (12), options/ (7), plugins/ (13), recovery/ (7), settings/ (11), site-health/ (6), taxonomies/ (10), themes/ (10), users/ (16), widgets/ (2).
    • Examples: acrossai/get-option options/get-option; acrossai/list-db-tables database/list-db-tables; acrossai/create-user users/create-user.
    • The second segment is unchanged from what shipped before — only the vendor prefix moves. No collapsing (unlike Elementor/Rank Math, where the redundant fragment was literally the namespace name).

Why: topic namespaces make the ability surface discoverable — a client fetching mcp-adapter-discover-abilities and filtering on the prefix gets exactly the domain it asked for. The old acrossai/ prefix tagged ownership but carried no discovery information. Full per-ability inventory is now published at docs/abilities-inventory.md.

Category taxonomy slugs (acrossai-abilities-manager-*) are unchanged. Internal PHP class namespaces are unchanged. Tests, spec artifacts, and docstring cross-references were updated in the same commits as the slug renames — nothing left pointing at acrossai/*.

0.0.29 – 2026-08-18

Feature 065 — safety envelope + payload enrichment across 9 existing abilities. No new abilities. Plugin version bumped 0.0.28 0.0.29. Two changes are breaking for programmatic callers: media/delete-media and file-manager/delete-file now require an explicit confirm: true; content/update-post silently strips protected meta keys (reported back in dropped_meta_keys). Every guardrail-triggered refusal now returns success: false + a machine-readable blocked_reason + a human message, with no state mutation on the refusal path.

  • plugins/deactivate-plugin — protected-plugin guard. Refuses to deactivate acrossai-mcp-manager, acrossai-abilities-manager, or acrossai-pro — 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 resolved plugin file path, so slug / partial-name / file-path variants that fuzzy-resolve to a protected plugin are all refused (blocked_reason: "protected_plugin").
  • media/delete-media — explicit confirmation + trash-aware. Requires confirm: true (refuses with blocked_reason: "confirmation_required" otherwise). Honours the MEDIA_TRASH constant — trashes when defined truthy and force is absent; permanent-deletes otherwise. Response now carries deleted: "deleted" | "trashed".
  • file-manager/delete-file — confirmation + protected-write + backup + opcache invalidation. Requires confirm: true. Refuses on wp-config.php / .htaccess at ABSPATH (blocked_reason: "protected_write"). Writes a .bak.<timestamp> copy next to the target before the delete and returns the backup path in backup. Calls opcache_invalidate() on the deleted path when OPcache is loaded.
  • file-manager/read-file — protected-read + size cap + binary detection. Refuses on wp-config.php / .htaccess at ABSPATH (blocked_reason: "protected_read") — this closes the highest-value accidental disclosure path (database password + eight auth constants). Refuses files over 5 MB without loading them into memory (blocked_reason: "file_too_large"; response reports observed size + cap). Non-UTF-8 payloads return { binary: true, size, path, message } instead of raw bytes.
  • media/list-media — alt-text search. search now matches against _wp_attachment_image_alt postmeta in addition to WP_Query’s default s fields (title / caption / description). Results are de-duplicated by attachment ID, so an image matched by both title and alt-text appears once.
  • media/update-media — updated-fields report. Response now carries an updated array naming each field that was actually written (subset of title / caption / description / alt_text), in the order fields were processed. Empty array when no update fields were passed.
  • content/update-post — writability + protected-meta + publish / author gates. Refuses on post types that are neither public: true nor show_in_rest: true (matches WP-REST writability). Filters caller-supplied meta to drop _-prefixed keys and any key that is_protected_meta() reports; the acrossai_allowed_protected_meta filter opts specific keys back in. Dropped keys are reported in the response as dropped_meta_keys. Refuses status: "publish" (or any status entering a public state) unless the caller holds publish_posts for the post type. Refuses author: <different_user_id> unless the caller holds edit_others_posts.
  • content/get-post — hydrated payload. Response now includes terms (object keyed by taxonomy, each entry { term_id, name, slug }), meta (non-protected keys only — same allow-list filter as update-post), featured_image ({ id, url, alt } or null), permalink, edit_link, and author: { id, name }. Callers no longer need 4–5 follow-up hydration calls per post.
  • content/delete-post — suggested-redirect hint. When the target was publish and force: true is passed, the response includes suggested_redirect: { from: <permalink>, to: <parent-or-archive-or-root-url> }. Omitted for drafts and for trash operations (URL may return on restore).

Test coverage. Test_Feature_065_Safety_And_Payload — 23 source-inspection tests covering all 23 FRs. Full suite green; PHPCS (WPCS strict) and PHPStan level 8 clean.

0.0.28 – 2026-08-17

Feature 069 — Rank Math ability suite: 61 new abilities under a new “Rank Math” tab. Gated on Rank Math SEO being active; absent entirely without it. Plugin version bumped 0.0.27 0.0.28.

Coverage baseline was deliberately narrow: Rank Math core ships 13 abilities of its own under rank-math/, and only those 13 counted as existing coverage. The third-party mcp-abilities-rankmath companion plugin was not treated as coverage — it is not ours to maintain, its writes go through raw update_option() blobs that bypass Rank Math’s sanitizer, and it gates every ability on blanket manage_options regardless of the Role Manager. Slugs do not collide (rank-math/ vs rankmath/ vs rank-math/).

Batch 1 — plumbing. RankMath\Category_Registrar registers acrossai-abilities-manager-rank-math, guarded on class_exists('\RankMath\Helper'). Base_Rank_Math_Ability is the sole assembler of ability() and sole enforcer of the execute() guard order, which is what guarantees tab_group => 'rank-math' on all 61 — the Feature 078 regression class. Rank_Math_Guard holds every guard plus the response envelope.

Batch 2 — typed settings (6 abilities). rank-math/get-settings 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. -update-general-settings, -update-title-settings and -update-sitemap-settings take a section/scope enum, replacing ~20 near-identical per-panel classes. -update-instant-indexing-settings and -update-robots-txt are separate because the first writes a different option and the second is conditional on state the caller cannot see. Titles & Meta templates — the global per-post-type and per-taxonomy layer — had no read or write anywhere before this.

Batch 3 — Instant Indexing, modules, sitemap, routes (10 abilities). -submit-urls, -get-indexing-log, -clear-indexing-log, -reset-indexing-key; -list-modules and -set-module-state; -get-sitemap-status, -list-sitemap-urls, -invalidate-sitemap-cache; -get-llms-status and -refresh-llms-route. -set-module-state replicates Rank Math’s own save_module() in full including the rewrite-rule refresh and rank_math/module_changed action — omitting either leaves stale rewrite rules, so the sitemap and llms.txt routes 404 while the module reports itself active.

Batch 4 — redirections, 404 logs, roles (13 abilities). -list-redirections (with the status=trashed filter), -find-redirection, -get-redirection-stats, -export-redirections, -create-redirection, -update-redirection, -change-redirection-status, -delete-redirections, -delete-trashed-redirections; -list-404-logs and -delete-404-logs; -get-role-capabilities and -reset-role-capabilities. -update-redirection fills a real gap: nothing could previously EDIT a redirection, 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 $_GET, calls check_admin_referer(), echoes and exits.

Batch 5 — status, maintenance, backups (8 abilities). -get-status (5 panels behind an enum), -run-maintenance-tool (12 tools behind an enum), -export-settings, -import-settings, -list-backups, -create-backup, -manage-backup, -detect-seo-plugins, plus -get-seo-analysis-results for the cached audit.

Batch 6 — analytics and post-level content (16 abilities). -get-analytics-summary (6 reports), -get-analytics-rows (3 datasets), -get-index-status, -inspect-url; -update-seo-meta, -bulk-update-meta, -update-seo-scores, -get-primary-term, -update-primary-term, -update-post-schemas, -delete-post-schemas, -get-schema-status, -get-rendered-head, -audit-content-seo, -get-inbound-links, -audit-faq-links. -get-inbound-links answers which pages link to a page, including navigation-menu links — the opposite direction from every existing outbound-link ability.

Batch 7 — entitlement-gated (6 abilities). -get-content-ai-status, -manage-content-ai-prompts, -manage-content-ai-output, -research-keyword; -get-ai-visibility-brand, -update-ai-visibility-object. Registered unconditionally and gated at runtime, deliberately unlike register_elementor_pro_abilities(): Content AI and AI Visibility ship in Rank Math free 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.

Security. Every ability requires manage_options and Rank Math’s own granular rank_math_* capability, matching the convention across the rest of the plugin’s ability suites. The floor is uniform across all 61 and declared final 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 manage_options ignores. One documented filter, acrossai_abilities_manager_rank_math_permission, lets site owners relax the policy. Twelve abilities are irreversible and require confirm: true. The post-scoped writers additionally perform per-object edit_post / edit_user / edit_terms checks inside their handler as defence in depth, and the schema writers verify that a schema-<meta_id> row actually belongs to the named object before writing — Rank Math addresses schema rows by meta id and would otherwise update a different object’s row.

Data-loss prevention. 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 legacy CMB2 type names while the sanitizer’s cases are the React names — 11 of 19 legacy types match no case. Settings_Registry 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 nofollow_domains with the mapped textarea type preserves newlines, while the identical write using Rank Math’s own declared textarea_small stores them collapsed onto one line. Nine multi-line settings were at risk.

Notable. No raw Rank Math option read/write ability ships — 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 Helper::set_capabilities() strips capabilities from roles omitted from the payload; the existing per-capability abilities cannot trigger that. The .htaccess editor, version rollback and beta opt-in are out of scope.

0.0.27 – 2026-08-14

Patch release — UI polish + admin-surface rename following the 0.0.26 Feature 067 rollup. No new abilities; both entries below are UX-affecting changes to the admin surface. Plugin version bumped 0.0.26 0.0.27.

  • Rename — “Ability Library” admin page is now “Ability Integrations”. The submenu label (“Library” “Integrations”), page title (“Ability Library” “Ability Integrations”), main heading, and URL slug (page=acrossai-abilities-library page=acrossai-abilities-integrations) all updated. Bookmarks / external links to the old slug will 404 in wp-admin — update saved links to the new URL. Internal class names, hook names, REST endpoint namespace (/wp-json/acrossai-abilities-library/v1/), and the DOM mount id are unchanged (deliberately scoped rename — extending to the REST namespace would break external MCP callers).
  • UI fix — Elementor abilities now render under their own “Elementor” tab in the Ability Integrations screen, not “Core”. Every Elementor ability (all 88 under elementor/*) had its meta tab_group set to 'core', causing the group to appear in the Core tab with only a sub-heading identifying it as Elementor. Flipped every declaration to tab_group => 'elementor' (63 files including Base_Audit_Ability, which drives the 25 audit subclasses via inheritance). The Ability Integrations UI auto-derives tab names from distinct tab_group values, so a new “Elementor” tab appears without any frontend/asset rebuild.

0.0.26 – 2026-08-14

Release rollup — 89 abilities total: 87 unreleased Elementor abilities (Feature 067 completion) + 2 native site maintenance-mode abilities. Plugin version bumped 0.0.25 0.0.26. Elementor abilities gate on class_exists('\Elementor\Plugin') (with 8 additionally gated on Elementor Pro); site maintenance-mode toggle has no plugin dependency.

  • Native site maintenance-mode toggle (2 abilities):

    • site-health/set-site-maintenance-mode — activate WordPress core maintenance mode by writing the ABSPATH/.maintenance 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 duration_minutes (default 60, hard-cap 1440). Requires confirm=true — blocks wp-admin as well as the frontend.
    • site-health/unset-site-maintenance-mode — deactivate: delete the marker, clear the refresh cron, drop the expiry option. Idempotent — safe to call when maintenance mode is already inactive. Reports was_active in the response.
    • Both live under the existing acrossai-abilities-manager-site-health category alongside site-health/get-maintenance-mode-status (Feature 063 read). No Elementor / plugin dependency — works on every WP install.
  • Feature 067 COMPLETE — 87 additional Elementor abilities ship in this release. Combined with the 2 foundation abilities from 0.0.25, the full 88 planned abilities are now available under the elementor/* namespace. Design-audit ability logic is skeletal (Base_Audit_Ability skeleton returning empty findings) — real audit heuristics to be filled in follow-up work.

Batch 10 — full-document replacement (closes the parity gap):
* elementor/update-data — overwrite the entire _elementor_data tree for a post with a caller-supplied element array; optional page_settings merge; force_replace=true required when the new payload is materially smaller than the existing document. Returns element_count + cache scope report.

Batch 9 — 29 design-audit abilities (this commit):

Aggregators + scorers (4):
* elementor/evaluate-design — aggregate report from every registered design audit (score + findings + recommendations).
* elementor/suggest-design-fixes — turn aggregated findings into concrete fix recommendations.
* elementor/score-distinctiveness — neutral distinctiveness score for structural repetition.
* elementor/extract-design-tokens — extract recurring colors / typography / spacing / dimensional tokens.

Individual audits (14):
* Column: audit-column-alignment-rhythm, audit-column-balance, audit-column-dominance, audit-column-necessity, audit-column-patterns
* Composition & emphasis: audit-composition-rhythm, audit-emphasis-drift, audit-section-rivalry, audit-separator-discipline, audit-surface-overuse
* Layout & repetition: audit-generic-component-repetition, audit-generic-layout-patterns, audit-layout-mechanism-fit, audit-native-widget-opportunities

Subtree operations — destructive (7):
* apply-text-hierarchy, enforce-boundary-coherence, fix-visible-gap-rhythm, normalize-responsive-values, normalize-section-spacing-rhythm, reset-negative-margins-subtree, zero-container-padding-subtree

Copy / sync / convert helpers — destructive (4):
* copy-lane-settings, copy-row-balance, image-widget-to-background-container, sync-component-variant

New utility class includes/Abilities/Elementor/Base_Audit_Ability.php provides the shared skeleton for 27 of the 29 audit abilities — subclasses supply audit_slug, audit_label, audit_description, and analyze(). Evaluate_Design and Suggest_Design_Fixes are self-contained aggregators.

Batch 8 — 8 Elementor Pro-gated abilities:
* elementor/list-custom-code — list Custom Code snippets from elementor_snippet CPT; optional location filter.
* elementor/get-custom-code — read one snippet including its code body.
* elementor/create-custom-code — create snippet with title, code, location (head / body_start / body_end / footer), priority, status.
* elementor/update-custom-code — update snippet fields.
* elementor/delete-custom-code — trash (default) or permanently delete with force=true.
* elementor/list-form-submissions — list Form widget submissions from the e_submissions table; optional form_id filter + include_values flag. Graceful degradation when the Pro submissions table is missing.
* elementor/get-form-submission — read one submission by ID; optional field values.
* elementor/delete-form-submission — permanently delete submission + its e_submissions_values rows; requires confirm=true.

All 8 Pro abilities gated on both class_exists( '\Elementor\Plugin' ) and class_exists( '\ElementorPro\Plugin' ) || defined( 'ELEMENTOR_PRO_VERSION' ) — silently absent on sites without Elementor Pro. Runtime deactivation returns error_code: elementor_pro_missing.

Batch 7 — 7 kits & site-settings abilities:
* elementor/list-kits — list all Elementor kits; marks active kit.
* elementor/get-kit-settings — read kit settings (defaults to active kit).
* elementor/update-kit-settings — merge new settings; force_replace for full overwrite; site-wide cache invalidation.
* elementor/set-active-kit — switch site-wide active kit; invalidates cache.
* elementor/list-global-widgets — list global (reusable) widgets from elementor_library CPT.
* elementor/list-experiments — list feature flags with current + default state.
* elementor/update-experiment — toggle experiment state (active | inactive | default).

Batch 6 — 11 template abilities:
* elementor/list-templates — list saved templates with filters on template_type + status + pagination.
* elementor/get-template — return one template’s metadata + conditions + optional _elementor_data.
* elementor/create-template — create a new template of type page / section / popup / header / footer / single / archive; sets taxonomy term + Elementor meta.
* elementor/update-template — update title / page_settings / full data with force_replace guard.
* elementor/delete-template — trash (default) or permanently delete with force=true.
* elementor/restore-template — restore a trashed template.
* elementor/duplicate-template — clone template preserving type + conditions + sub_type; regenerates element IDs.
* elementor/empty-trash — permanently delete every trashed template; requires confirm=true.
* elementor/export-template — export template as JSON-encodable object (title, template_type, sub_type, page_settings, content, conditions).
* elementor/import-template — import from JSON export; regenerates element IDs; optional overwrite_id to replace an existing template.
* elementor/find-template-for-pattern — rank saved templates by keyword match (title + tax term + widget-types in content); returns top N with scores.

Batch 5 — 11 site-management abilities:
* elementor/clear-cache — clear Elementor cache at post / site / all scope; optional regenerate_css=true for a specific post.
* elementor/replace-urls — bulk find/replace URLs across every Elementor document on the site with dry_run=true default preview.
* elementor/get-maintenance-mode — read current maintenance mode settings (mode, template, exclude rules).
* elementor/update-maintenance-mode — enable/disable maintenance mode with mode selection (maintenance | coming_soon).
* elementor/get-theme-builder-conditions — read display conditions attached to an Elementor template.
* elementor/update-theme-builder-conditions — replace display conditions; pass empty array to clear. Invalidates Elementor’s condition cache.
* elementor/get-official-widget-catalog — canonical widget catalog (Basic / Pro / Theme / WooCommerce) with 12-hour transient.
* elementor/get-official-pattern-guidance — pattern & layout guidance (widgets / patterns / layouts topics) grounded in Elementor documentation.
* elementor/get-theme-context — active theme + Elementor version + active kit + viewport settings snapshot.
* elementor/get-style-guide — style-guide summary from active kit (colors, typography, buttons, forms, layout, custom CSS).
* elementor/evaluate-render-context — inspect frontend template + canvas type + edit-mode flag for a post.

Batch 4 — 9 page-composition abilities:
* elementor/create-page — insert a new post/page pre-configured for Elementor …