diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 59a105b..25a76e1 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -38,7 +38,7 @@ npm run lint:js # JS lint Every ability needs BOTH PHP (backend) and JS (chat interface): 1. **PHP**: `includes/abilities/{kebab-case}.php` — use `register_agentic_ability()`. Must include `permission_callback` and `input_schema` with top-level `default` for optional-input abilities (GET requests fail without it). Return `{ success: bool, message: string, ... }`. -2. **JS**: `src/extensions/abilities/{kebab-case}.js` — use `registerAbility( 'wp-agentic-admin/{id}', { label, description, keywords, execute, summarize, interpretResult, ... } )`. Keep `label` and `description` as **string literals** (test loader extracts via regex). +2. **JS**: `src/extensions/abilities/{kebab-case}.js` — use `registerAbility( 'agentic-admin/{id}', { label, description, keywords, execute, summarize, interpretResult, ... } )`. Keep `label` and `description` as **string literals** (test loader extracts via regex). 3. **Register**: import and call in `src/extensions/abilities/index.js` 4. **Tests**: ability auto-appears. Add test cases to `tests/abilities/core-abilities.test.js` @@ -51,7 +51,7 @@ See [Abilities Guide](docs/ABILITIES-GUIDE.md) for `parseIntent()`, confirmation ## Version Bumping Update ALL of these: -1. `wp-agentic-admin.php` — header `Version:`, `WP_AGENTIC_ADMIN_VERSION` constant, `activate()` hook +1. `agentic-admin.php` — header `Version:`, `AGENTIC_ADMIN_VERSION` constant, `activate()` hook 2. `package.json` — `version` field 3. `readme.txt` — `Stable tag:` + changelog entry 4. `npm install --package-lock-only` to sync `package-lock.json` diff --git a/.claude/skills/build-ability/SKILL.md b/.claude/skills/build-ability/SKILL.md index f4fc820..96f164f 100644 --- a/.claude/skills/build-ability/SKILL.md +++ b/.claude/skills/build-ability/SKILL.md @@ -58,7 +58,7 @@ Create files in this order: File: `includes/abilities/{name}.php` Rules: -- Function names: `wp_agentic_admin_register_{snake}` and `wp_agentic_admin_execute_{snake}` +- Function names: `agentic_admin_register_{snake}` and `agentic_admin_execute_{snake}` - Always include `permission_callback` - Always include `input_schema` with top-level `default` for optional/no-input abilities - Always include `output_schema` diff --git a/.claude/skills/new-ability/SKILL.md b/.claude/skills/new-ability/SKILL.md index b0dfbb2..135b7c6 100644 --- a/.claude/skills/new-ability/SKILL.md +++ b/.claude/skills/new-ability/SKILL.md @@ -16,7 +16,7 @@ Read these for the full patterns: ## Steps 1. **PHP backend** — create `includes/abilities/$ARGUMENTS.php`: - - Use `register_agentic_ability( 'wp-agentic-admin/$ARGUMENTS', $php_config, $js_config )` + - Use `register_agentic_ability( 'agentic-admin/$ARGUMENTS', $php_config, $js_config )` - Include `permission_callback` with appropriate `current_user_can()` check - Include `input_schema` with top-level `default` field - Include `output_schema` @@ -24,7 +24,7 @@ Read these for the full patterns: - Implement `execute_callback` returning `{ success: bool, message: string, ... }` 2. **JS chat interface** — create `src/extensions/abilities/$ARGUMENTS.js`: - - Use `registerAbility( 'wp-agentic-admin/$ARGUMENTS', { ... } )` + - Use `registerAbility( 'agentic-admin/$ARGUMENTS', { ... } )` - Include: `label` (string literal), `description` (string literal), `keywords` (array), `initialMessage`, `summarize()`, `interpretResult()`, `execute()` - Set `requiresConfirmation` based on whether the ability is destructive diff --git a/.claude/skills/playground/SKILL.md b/.claude/skills/playground/SKILL.md index a68560a..ad52dc5 100644 --- a/.claude/skills/playground/SKILL.md +++ b/.claude/skills/playground/SKILL.md @@ -6,7 +6,7 @@ allowed-tools: "mcp__wp-playground__start_playground, mcp__wp-playground__wp_cli # Start WordPress Playground -Spin up an ephemeral WordPress Playground with wp-agentic-admin mounted from the local working directory. +Spin up an ephemeral WordPress Playground with agentic-admin mounted from the local working directory. ## Important: Mount + Activation Gotchas @@ -18,15 +18,15 @@ Spin up an ephemeral WordPress Playground with wp-agentic-admin mounted from the 1. **Start Playground** with an empty blueprint and mount the plugin: - Blueprint: `{"steps": []}` - - Options: `{"mount": [":/wordpress/wp-content/plugins/wp-agentic-admin"]}` + - Options: `{"mount": [":/wordpress/wp-content/plugins/agentic-admin"]}` - Where `` is the current working directory 2. **Activate the plugin** via WP-CLI (only after Playground is running): - - `plugin activate wp-agentic-admin` + - `plugin activate agentic-admin` 3. **Report** the URL and credentials to the user: - Site URL (typically http://127.0.0.1:9400) - - Plugin page: `/wp-admin/admin.php?page=wp-agentic-admin` + - Plugin page: `/wp-admin/admin.php?page=agentic-admin` - Credentials: admin / password 4. If any step fails, check logs with `get_playground_logs` and report the error. diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index e4881e9..5ca2d52 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -10,7 +10,7 @@ allowed-tools: "Bash(npm *), Bash(git *), Bash(gh *), Read, Edit" ## Steps 1. **Version bump** — update ALL of these to `$ARGUMENTS`: - - `wp-agentic-admin.php`: plugin header `Version:`, `WP_AGENTIC_ADMIN_VERSION` constant, `activate()` hook + - `agentic-admin.php`: plugin header `Version:`, `AGENTIC_ADMIN_VERSION` constant, `activate()` hook - `package.json`: `version` field - `readme.txt`: `Stable tag:` field - Run `npm install --package-lock-only` to sync `package-lock.json` diff --git a/.distpackage b/.distpackage index c172ab6..fa54ee7 100644 --- a/.distpackage +++ b/.distpackage @@ -1,6 +1,5 @@ includes/ -wp-agentic-admin.php -sw-loader.php +agentic-admin.php uninstall.php build-extensions/ languages/ diff --git a/.eslintrc.js b/.eslintrc.js index 51af2dd..ec85c2b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -16,7 +16,7 @@ module.exports = { jest: true, // For test files (describe, it, expect, etc.) }, globals: { - wpAgenticAdmin: 'readonly', + agenticAdmin: 'readonly', }, rules: { // Allow console statements (useful for debugging AI agent behavior) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b4c67f1..c79a621 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,7 +9,7 @@ Welcome, hackathon contributor! This is a quick-start guide to get you up and ru 1. **Fork & clone** the repo 2. `npm install` 3. `npm run start` (watch mode) or `npx wp-scripts build` -4. Copy/symlink the plugin into `wp-content/plugins/wp-agentic-admin/` +4. Copy/symlink the plugin into `wp-content/plugins/agentic-admin/` 5. Activate in WordPress admin ### Requirements diff --git a/.github/templates-full/CONTRIBUTING.md b/.github/templates-full/CONTRIBUTING.md index 283879c..344b1a9 100644 --- a/.github/templates-full/CONTRIBUTING.md +++ b/.github/templates-full/CONTRIBUTING.md @@ -53,7 +53,7 @@ This project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDU ``` 6. **Set up WordPress**: - - Copy plugin to your local WordPress installation: `wp-content/plugins/wp-agentic-admin/` + - Copy plugin to your local WordPress installation: `wp-content/plugins/agentic-admin/` - Activate the plugin - Navigate to "Agentic Admin" in WordPress admin @@ -157,7 +157,7 @@ Follow [WordPress PHP Coding Standards](https://developer.wordpress.org/coding-s ```php // Good -function wp_agentic_admin_example_function( string $param ): bool { +function agentic_admin_example_function( string $param ): bool { if ( ! current_user_can( 'manage_options' ) ) { return false; } diff --git a/.github/templates-full/pull_request_template.md b/.github/templates-full/pull_request_template.md index f80a1a8..f34d672 100644 --- a/.github/templates-full/pull_request_template.md +++ b/.github/templates-full/pull_request_template.md @@ -65,7 +65,7 @@ Related to # - [ ] Example queries provided **Ability Details:** -- Ability ID: `wp-agentic-admin/` +- Ability ID: `agentic-admin/` - Category: - Operation Type: [ ] read-only [ ] write [ ] write-destructive - Required Capability: diff --git a/.playground/blueprint-config.json b/.playground/blueprint-config.json index bf88689..9a2d5ab 100644 --- a/.playground/blueprint-config.json +++ b/.playground/blueprint-config.json @@ -2,7 +2,7 @@ "wpVersion": "6.9", "phpVersion": "8.3", "plugins": [], - "landingPage": "/wp-admin/admin.php?page=wp-agentic-admin", + "landingPage": "/wp-admin/admin.php?page=agentic-admin", "multisite": false, "autoLogin": true, "phpConstants": { diff --git a/.playground/blueprint.json b/.playground/blueprint.json index bfdd1c2..513a942 100644 --- a/.playground/blueprint.json +++ b/.playground/blueprint.json @@ -1,6 +1,6 @@ { "$schema": "https://playground.wordpress.net/blueprint-schema.json", - "landingPage": "/wp-admin/admin.php?page=wp-agentic-admin", + "landingPage": "/wp-admin/admin.php?page=agentic-admin", "preferredVersions": { "wp": "6.9", "php": "8.3" diff --git a/.release-notes/0.11.0.md b/.release-notes/0.11.0.md index dcd90f4..4e9787b 100644 --- a/.release-notes/0.11.0.md +++ b/.release-notes/0.11.0.md @@ -16,11 +16,11 @@ compliance, and critical bug fixes. ``` = 0.11.0 = * Renamed: Plugin is now "Agentic Admin for WordPress". Text domain "agentic-admin", function prefix agentic_admin_*. WordPress.org submission-ready. -* Removed: feedback system, WebMCP bridge, voice input, and three low-value abilities (backup-check, opcode-cache-status, disk-usage). Code preserved in git history; voice + write-file + content-generate + plugin-ecosystem abilities parked for v1.x as opt-in via WP_AGENTIC_ADMIN_ENABLE_LABS constant. +* Removed: feedback system, WebMCP bridge, voice input, and three low-value abilities (backup-check, opcode-cache-status, disk-usage). Code preserved in git history; voice + write-file + content-generate + plugin-ecosystem abilities parked for v1.x as opt-in via AGENTIC_ADMIN_ENABLE_LABS constant. * Security: blocked sensitive-column reads (user_email, user_pass) in query-database to prevent reconnaissance attacks (#166). Hardened query length cap and read-only verb gate. * Security: escaped output in functions-abilities.php (#121). Added sw-loader.php access-control rationale (#123). Documented direct-DB-call rationale in db-optimize and database-check. * Compliance: removed deprecated load_plugin_textdomain() call, prefixed uninstall.php globals, fixed nonexistent Domain Path header, excluded CLOUDFEST_HACKATHON.md from distribution build. -* New: ability manifest as single source of truth for which abilities register, with a wp_agentic_admin_enabled_abilities filter for selective override. PHP authoritative for PHP-backed abilities; JS adds back the JS-only ones. +* New: ability manifest as single source of truth for which abilities register, with a agentic_admin_enabled_abilities filter for selective override. PHP authoritative for PHP-backed abilities; JS adds back the JS-only ones. * Fixed: thinking-disable setting now actually suppresses the streaming UI even when Qwen ignores /nothink (#181). * Fixed: Settings panel is now a real tab (not a stateful cog toggle). No more lingering Chat-tab underline or stale-state regressions (#197). * Fixed: Activate/Deactivate buttons on plugin-list rows now flip to the inverse action after a successful click (#179). @@ -60,9 +60,9 @@ compliance, and critical bug fixes. ## Files to update for the version bump -1. `wp-agentic-admin.php` +1. `agentic-admin.php` - Header: `Version: 0.10.0` → `Version: 0.11.0` - - Constant: `define( 'WP_AGENTIC_ADMIN_VERSION', '0.10.0' )` → `'0.11.0'` + - Constant: `define( 'AGENTIC_ADMIN_VERSION', '0.10.0' )` → `'0.11.0'` - Activate hook: `update_option( 'agentic_admin_version', '0.10.0' )` → `'0.11.0'` 2. `package.json`: `"version": "0.10.0"` → `"0.11.0"` 3. `readme.txt`: `Stable tag: 0.10.0` → `0.11.0` @@ -75,7 +75,7 @@ compliance, and critical bug fixes. **#206 (rename) MUST merge last.** It conflicts with 3 of the small PRs. Local `git rerere` has recorded the resolutions, so when the actual rebase happens, git replays them automatically. To re-prime rerere on another machine, redo the dry-run merge: `git checkout -b dryrun dev; git config rerere.enabled true; for b in ; do git merge --no-ff --no-edit origin/$b; done; git merge origin/fix/v0-11-wp-org-rename`, resolve, commit, discard branch. -### Conflict 1: `wp-agentic-admin.php` (#206 ↔ #198) +### Conflict 1: `agentic-admin.php` (#206 ↔ #198) Two blocks in the file: 1. Plugin header `Text Domain` line + `Domain Path` line — keep #206's `Text Domain: agentic-admin`, drop `Domain Path` per #198 (no `languages/` dir). @@ -89,7 +89,7 @@ Two blocks in the file: 1. Function signature: keep `agentic_admin_check_query_safety` (combines #206's prefix-rename with #199's new function name + `string|true` return type). 2. Caller: `$safety = agentic_admin_check_query_safety( $query );` (uses the new function + checks `true !== $safety`). -Also: #199's new functions (`agentic_admin_sensitive_columns`, `agentic_admin_redact_sensitive_row`) need the rename applied manually because #206 didn't see them yet. All `'wp-agentic-admin'` text-domain strings introduced by #199 → `'agentic-admin'`. +Also: #199's new functions (`agentic_admin_sensitive_columns`, `agentic_admin_redact_sensitive_row`) need the rename applied manually because #206 didn't see them yet. All `'agentic-admin'` text-domain strings introduced by #199 → `'agentic-admin'`. ### Conflict 3: `includes/functions-abilities.php` (#206 ↔ #201) @@ -121,4 +121,4 @@ git push --force-with-lease origin fix/v0-11-wp-org-rename - [ ] Disable thinking → send a message → no thinking UI flashes - [ ] Open a fresh wp-admin page (not the plugin page) — model does NOT preload; click superhero icon to open sidebar — only then does the WebGPU check + auto-load run - [ ] Try `SELECT * FROM wp_users WHERE user_email = 'admin@…'` via query-database — gets blocked with the sensitive-column message -- [ ] Verify `wp-agentic-admin.zip` build via `npm run dist` does NOT contain `CLOUDFEST_HACKATHON.md` or `docs/FEEDBACK-DEV.md` +- [ ] Verify `agentic-admin.zip` build via `npm run dist` does NOT contain `CLOUDFEST_HACKATHON.md` or `docs/FEEDBACK-DEV.md` diff --git a/ONBOARDING.md b/ONBOARDING.md index b1ed611..7145264 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -56,7 +56,7 @@ We have 4 workflows today. We'd love more. ### The codebase at a glance ``` -wp-agentic-admin/ +agentic-admin/ ├── includes/ ← PHP: plugin core + abilities │ ├── abilities/ ← One PHP file per ability │ ├── class-abilities.php ← Ability registration system @@ -186,7 +186,7 @@ Find your role below. Each section tells you what you'll build, where the code l **What you'll do:** 1. Pick an ability from the [must-have list](CLOUDFEST_HACKATHON.md#goal-2-expanded-abilities-must-have) or grab an issue labeled `ability` + `php` -2. Create a PHP file in `includes/abilities/` using `wp_agentic_admin_register_ability()` +2. Create a PHP file in `includes/abilities/` using `agentic_admin_register_ability()` 3. Coordinate with a JS developer who builds the chat-side counterpart **Your workspace:** diff --git a/PROGRESS.md b/PROGRESS.md index f2f1aab..1ddccca 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -69,7 +69,7 @@ Users can now constrain the AI to curated tool sets via a `+` icon in the input - **WCAG 2.2 AA accessibility** (PR #155 by @Stefan0x) — ARIA roles/labels, keyboard navigation, focus indicators, reduced motion support across entire chat UI. - **Fuzzy plugin matching** (PR #154 by @tomepajk) — activate/deactivate now accepts display names with tiered matching and candidate buttons for ambiguous matches. Fixes #53. - **Ability result status UI** (PR #153 by @0xLoopTheory) — three-state result display (success/info/error) instead of misleading green checkmark on failures. Fixes #74. -- **Prefix global functions** (PR #152 by @AlexanderMelde) — all public PHP functions now use `wp_agentic_admin_` prefix. Fixes #120. +- **Prefix global functions** (PR #152 by @AlexanderMelde) — all public PHP functions now use `agentic_admin_` prefix. Fixes #120. - **Dynamic plugin bundles** (PR #151 by @BoweFrankema) — plugin abilities appear as selectable bundles in the chat dropdown. - **Rewrite list categorization** (PR #159 by @Lucisu) — categorized rules with balanced sampling instead of raw dump. Fixes #56. - **HTTP WebGPU error** (PR #50 by @robert81) — clear "please use HTTPS" message instead of confusing WebGPU error. Day 1 PR finally merged! diff --git a/README.md b/README.md index 7d79e4a..f6feedf 100644 --- a/README.md +++ b/README.md @@ -244,11 +244,11 @@ AI: [Executing: site-health] ## Project Structure ``` -wp-agentic-admin/ -├── wp-agentic-admin.php # Main plugin file +agentic-admin/ +├── agentic-admin.php # Main plugin file ├── uninstall.php # Clean uninstall (multisite-aware) ├── includes/ -│ ├── functions-abilities.php # Public API: wp_agentic_admin_register_ability() +│ ├── functions-abilities.php # Public API: agentic_admin_register_ability() │ ├── class-abilities.php # Ability registration orchestrator │ ├── class-admin-page.php # Admin page & assets │ ├── class-settings.php # Plugin settings (model selection, etc.) @@ -274,7 +274,7 @@ wp-agentic-admin/ ### Building ```bash -cd wp-agentic-admin +cd agentic-admin npm install npm run build ``` diff --git a/wp-agentic-admin.php b/agentic-admin.php similarity index 52% rename from wp-agentic-admin.php rename to agentic-admin.php index da0dfdc..b27edfc 100644 --- a/wp-agentic-admin.php +++ b/agentic-admin.php @@ -3,18 +3,18 @@ * Agentic Admin - Main Plugin File * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @since 0.1.0 */ // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- This is the main plugin file, not a class file. /** * Plugin Name: Agentic Admin - * Plugin URI: https://pluginslab.com/agentic-admin + * Plugin URI: https://github.com/pluginslab/wp-agentic-admin * Description: A privacy-first AI Site Reliability Engineer running entirely in the browser. Uses WebAssembly and WebGPU to execute Small Language Models locally, transforming wp-admin into a natural language command center via the WordPress Abilities API. * Version: 0.11.0 - * Author: Pluginslab - * Author URI: https://pluginslab.com + * Author: Marcel Schmitz + * Author URI: https://profiles.wordpress.org/schmitzoide/ * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Text Domain: agentic-admin @@ -22,21 +22,21 @@ * Requires PHP: 8.2 * Tested up to: 7.0 * - * @package WP_Agentic_Admin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { exit; } -if ( ! class_exists( 'WPAgenticAdmin' ) ) { +if ( ! class_exists( 'AgenticAdmin' ) ) { /** * Main plugin class for Agentic Admin. * * @since 0.1.0 */ - final class WPAgenticAdmin { + final class AgenticAdmin { /** * Constructor - sets up the plugin initialization. @@ -64,45 +64,50 @@ public function init() { $this->define_constants(); + // Migrate legacy option keys on upgrade. The activation hook does not + // fire on WordPress.org auto-updates, so this also runs on every load + // (cheap and idempotent: the old option is deleted once migrated). + self::maybe_migrate_settings(); + // Translations: WordPress 4.6+ loads them automatically for plugins // hosted on WordPress.org, so no explicit load_plugin_textdomain() // call is needed here. // Load functions first (provides agentic_admin_register_ability API). - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/functions-abilities.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/functions-abilities.php'; - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-utils.php'; - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-settings.php'; - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-admin-page.php'; - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-abilities.php'; - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-llm-proxy.php'; - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-connectors.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-utils.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-settings.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-admin-page.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-abilities.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-llm-proxy.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/class-connectors.php'; // Initialize Utility Hooks (Cache Invalidation). - if ( class_exists( '\\WPAgenticAdmin\\Utils' ) ) { - \WPAgenticAdmin\Utils::init_hooks(); + if ( class_exists( '\\AgenticAdmin\\Utils' ) ) { + \AgenticAdmin\Utils::init_hooks(); } - if ( class_exists( '\\WPAgenticAdmin\\Settings' ) ) { - \WPAgenticAdmin\Settings::get_instance(); + if ( class_exists( '\\AgenticAdmin\\Settings' ) ) { + \AgenticAdmin\Settings::get_instance(); } - if ( class_exists( '\\WPAgenticAdmin\\Admin_Page' ) ) { - \WPAgenticAdmin\Admin_Page::get_instance(); + if ( class_exists( '\\AgenticAdmin\\Admin_Page' ) ) { + \AgenticAdmin\Admin_Page::get_instance(); } - if ( class_exists( '\\WPAgenticAdmin\\Abilities' ) ) { - \WPAgenticAdmin\Abilities::get_instance(); + if ( class_exists( '\\AgenticAdmin\\Abilities' ) ) { + \AgenticAdmin\Abilities::get_instance(); } // Initialize LLM Proxy for external provider support. - if ( class_exists( '\\WPAgenticAdmin\\LLM_Proxy' ) ) { - \WPAgenticAdmin\LLM_Proxy::init(); + if ( class_exists( '\\AgenticAdmin\\LLM_Proxy' ) ) { + \AgenticAdmin\LLM_Proxy::init(); } // Initialize Connectors REST endpoint (WP 7.0+). - if ( class_exists( '\\WPAgenticAdmin\\Connectors' ) ) { - \WPAgenticAdmin\Connectors::init(); + if ( class_exists( '\\AgenticAdmin\\Connectors' ) ) { + \AgenticAdmin\Connectors::init(); } } @@ -153,10 +158,32 @@ public function abilities_api_missing_notice(): void { * @return void */ private function define_constants(): void { - define( 'WP_AGENTIC_ADMIN_VERSION', '0.11.0' ); - define( 'WP_AGENTIC_ADMIN_FILE', __FILE__ ); - define( 'WP_AGENTIC_ADMIN_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); - define( 'WP_AGENTIC_ADMIN_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); + define( 'AGENTIC_ADMIN_VERSION', '0.11.0' ); + define( 'AGENTIC_ADMIN_FILE', __FILE__ ); + define( 'AGENTIC_ADMIN_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); + define( 'AGENTIC_ADMIN_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); + } + + /** + * Migrate settings from the legacy option key. + * + * The settings option key was renamed `wp_agentic_admin_settings` → + * `agentic_admin_settings` during the de-branding rename. Without this, + * upgrading users would silently lose their saved settings (model + * selection, endpoint URL, API keys, etc.). + * + * Idempotent: only copies when the old option exists and the new one + * does not, then removes the old key so subsequent calls short-circuit. + * + * @since 0.11.0 + * @return void + */ + private static function maybe_migrate_settings(): void { + $old = get_option( 'wp_agentic_admin_settings' ); + if ( false !== $old && false === get_option( 'agentic_admin_settings' ) ) { + update_option( 'agentic_admin_settings', $old ); + delete_option( 'wp_agentic_admin_settings' ); + } } /** @@ -164,6 +191,7 @@ private function define_constants(): void { */ public static function activate(): void { update_option( 'agentic_admin_version', '0.11.0' ); + self::maybe_migrate_settings(); flush_rewrite_rules(); } @@ -176,8 +204,8 @@ public static function deactivate(): void { } } - register_activation_hook( __FILE__, array( 'WPAgenticAdmin', 'activate' ) ); - register_deactivation_hook( __FILE__, array( 'WPAgenticAdmin', 'deactivate' ) ); + register_activation_hook( __FILE__, array( 'AgenticAdmin', 'activate' ) ); + register_deactivation_hook( __FILE__, array( 'AgenticAdmin', 'deactivate' ) ); - new WPAgenticAdmin(); + new AgenticAdmin(); } diff --git a/docs/ABILITIES-GUIDE.md b/docs/ABILITIES-GUIDE.md index 3321c63..7dc2dac 100644 --- a/docs/ABILITIES-GUIDE.md +++ b/docs/ABILITIES-GUIDE.md @@ -75,7 +75,7 @@ See the [Shared Helper Functions](#shared-helper-functions-best-practice) sectio ## File Structure ``` -wp-agentic-admin/ +agentic-admin/ ├── includes/ │ ├── functions-abilities.php # Public API: register/unregister/exists │ ├── class-abilities.php # Loads ability files, fires registration hook @@ -113,7 +113,7 @@ Create a new file in `includes/abilities/` (e.g., `my-new-ability.php`): * * Description of what this ability does. * - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -125,13 +125,13 @@ if ( ! defined( 'ABSPATH' ) ) { * * @return void */ -function wp_agentic_admin_register_my_new_ability(): void { - wp_agentic_admin_register_ability( - 'wp-agentic-admin/my-new-ability', +function agentic_admin_register_my_new_ability(): void { + agentic_admin_register_ability( + 'agentic-admin/my-new-ability', // PHP configuration for WordPress Abilities API array( - 'label' => __( 'My New Ability', 'wp-agentic-admin' ), - 'description' => __( 'Does something useful.', 'wp-agentic-admin' ), + 'label' => __( 'My New Ability', 'agentic-admin' ), + 'description' => __( 'Does something useful.', 'agentic-admin' ), 'category' => 'sre-tools', 'input_schema' => array( 'type' => 'object', @@ -144,11 +144,11 @@ function wp_agentic_admin_register_my_new_ability(): void { 'properties' => array( 'success' => array( 'type' => 'boolean', - 'description' => __( 'Whether the operation succeeded.', 'wp-agentic-admin' ), + 'description' => __( 'Whether the operation succeeded.', 'agentic-admin' ), ), ), ), - 'execute_callback' => 'wp_agentic_admin_execute_my_new_ability', + 'execute_callback' => 'agentic_admin_execute_my_new_ability', 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, @@ -164,7 +164,7 @@ function wp_agentic_admin_register_my_new_ability(): void { // JS configuration for chat interface array( 'keywords' => array( 'my', 'ability', 'keywords' ), - 'initialMessage' => __( 'Working on it...', 'wp-agentic-admin' ), + 'initialMessage' => __( 'Working on it...', 'agentic-admin' ), ) ); } @@ -175,7 +175,7 @@ function wp_agentic_admin_register_my_new_ability(): void { * @param array $input Input parameters. * @return array */ -function wp_agentic_admin_execute_my_new_ability( array $input = array() ): array { +function agentic_admin_execute_my_new_ability( array $input = array() ): array { // Your implementation here return array( 'success' => true, @@ -188,17 +188,17 @@ function wp_agentic_admin_execute_my_new_ability( array $input = array() ): arra **For core WP-Agentic-Admin abilities:** Add a call in the `register_core_abilities()` method in `class-abilities.php`: ```php -if ( function_exists( 'wp_agentic_admin_register_my_new_ability' ) ) { - wp_agentic_admin_register_my_new_ability(); +if ( function_exists( 'agentic_admin_register_my_new_ability' ) ) { + agentic_admin_register_my_new_ability(); } ``` -**For third-party plugins:** Use the `wp_agentic_admin_register_abilities` action hook instead of modifying `class-abilities.php` directly. This hook fires after all core abilities are registered: +**For third-party plugins:** Use the `agentic_admin_register_abilities` action hook instead of modifying `class-abilities.php` directly. This hook fires after all core abilities are registered: ```php -add_action( 'wp_agentic_admin_register_abilities', function() { - if ( function_exists( 'wp_agentic_admin_register_ability' ) ) { - wp_agentic_admin_register_my_new_ability(); +add_action( 'agentic_admin_register_abilities', function() { + if ( function_exists( 'agentic_admin_register_ability' ) ) { + agentic_admin_register_my_new_ability(); } } ); ``` @@ -211,7 +211,7 @@ Create a new file in `src/extensions/abilities/` (e.g., `my-new-ability.js`): /** * My New Ability * - * @package WPAgenticAdmin + * @package AgenticAdmin */ import { registerAbility, executeAbility } from '../services/agentic-abilities-api'; @@ -220,7 +220,7 @@ import { registerAbility, executeAbility } from '../services/agentic-abilities-a * Register the my-new-ability with the chat system. */ export function registerMyNewAbility() { - registerAbility('wp-agentic-admin/my-new-ability', { + registerAbility('agentic-admin/my-new-ability', { // Label is used in the AI's system prompt to describe available capabilities label: 'My new ability description', keywords: ['my', 'ability', 'keywords'], @@ -234,7 +234,7 @@ export function registerMyNewAbility() { }, execute: async (params) => { - return executeAbility('wp-agentic-admin/my-new-ability', {}); + return executeAbility('agentic-admin/my-new-ability', {}); }, requiresConfirmation: false, @@ -274,7 +274,7 @@ The WP Abilities API determines HTTP method based on annotations: GET requests don't have a JSON body. When no `input` query parameter is provided, the API receives `null`. If your schema says `type: 'object'` but receives `null`, validation fails: ``` -Error: Ability "wp-agentic-admin/my-ability" has invalid input. Reason: input is not of type object. +Error: Ability "agentic-admin/my-ability" has invalid input. Reason: input is not of type object. ``` ### Solution @@ -348,7 +348,7 @@ Use the `required` array in input schema: 'properties' => array( 'plugin' => array( 'type' => 'string', - 'description' => __( 'Plugin file path to deactivate.', 'wp-agentic-admin' ), + 'description' => __( 'Plugin file path to deactivate.', 'agentic-admin' ), ), ), 'required' => array( 'plugin' ), @@ -371,7 +371,7 @@ execute: async (params) => { } const pluginSlug = pluginMatch[1]; - return executeAbility('wp-agentic-admin/plugin-deactivate', { + return executeAbility('agentic-admin/plugin-deactivate', { plugin: `${pluginSlug}/${pluginSlug}.php` }); }, @@ -429,7 +429,7 @@ const merged = { }; ``` -The original PHP label is still available as `phpLabel` (set by `wp_agentic_admin_get_abilities_js_config()` in `functions-abilities.php`). This means: +The original PHP label is still available as `phpLabel` (set by `agentic_admin_get_abilities_js_config()` in `functions-abilities.php`). This means: - `tool.label` -- the JS label (or PHP label if JS didn't provide one) - `tool.phpLabel` -- always the PHP-defined label, if one was registered @@ -437,7 +437,7 @@ The original PHP label is still available as `phpLabel` (set by `wp_agentic_admi If you don't provide a `label` in either PHP or JS, one is derived from the ability ID: - `my-plugin/user-list` -> "user list" -- `wp-agentic-admin/cache-flush` -> "cache flush" +- `agentic-admin/cache-flush` -> "cache flush" This works but isn't as descriptive. Always provide an explicit `label` for best results. @@ -548,7 +548,7 @@ By default, after a single ability runs, the LLM generates a natural-language su Set `preferSummarize: true` on the JS ability to bypass the LLM entirely. The orchestrator will call `tool.summarize()` directly and display the result instantly, without streaming. ```javascript -registerAbility('wp-agentic-admin/my-ability', { +registerAbility('agentic-admin/my-ability', { // ... summarize: (result) => { @@ -644,7 +644,7 @@ Use `parseIntent` when your ability needs to extract structured parameters from From `revision-cleanup.js`: ```javascript -registerAbility('wp-agentic-admin/revision-cleanup', { +registerAbility('agentic-admin/revision-cleanup', { parseIntent: (message) => { const lowerMessage = message.toLowerCase(); @@ -670,7 +670,7 @@ registerAbility('wp-agentic-admin/revision-cleanup', { requiresConfirmation: (params) => !params.dry_run, execute: async (params) => { - return executeAbility('wp-agentic-admin/revision-cleanup', { + return executeAbility('agentic-admin/revision-cleanup', { keep_last: params.keep_last, dry_run: params.dry_run, }); @@ -694,12 +694,12 @@ When creating multiple related abilities (e.g., `plugin-list`, `plugin-activate` Following [WordPress Abilities API best practices](https://developer.wordpress.org/news/2025/11/introducing-the-wordpress-abilities-api/), each ability should represent **one discrete operation** with clear operational characteristics (readonly, destructive, HTTP method). This means: ✅ **Correct:** Three separate abilities -- `wp-agentic-admin/plugin-list` (readonly: true) -- `wp-agentic-admin/plugin-activate` (readonly: false, destructive: false) -- `wp-agentic-admin/plugin-deactivate` (readonly: false, destructive: true) +- `agentic-admin/plugin-list` (readonly: true) +- `agentic-admin/plugin-activate` (readonly: false, destructive: false) +- `agentic-admin/plugin-deactivate` (readonly: false, destructive: true) ❌ **Incorrect:** One unified ability with action parameter -- `wp-agentic-admin/plugin-manager` with mixed characteristics +- `agentic-admin/plugin-manager` with mixed characteristics However, separate abilities can lead to code duplication. **Solution: Shared helper functions.** @@ -712,7 +712,7 @@ Create `includes/abilities/shared/plugin-helpers.php`: /** * Shared Plugin Helper Functions * - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -725,7 +725,7 @@ if ( ! defined( 'ABSPATH' ) ) { * @param string $status_filter Filter by status: 'all', 'active', or 'inactive'. * @return array Array with plugins list and counts. */ -function wp_agentic_admin_get_all_plugins( string $status_filter = 'all' ): array { +function agentic_admin_get_all_plugins( string $status_filter = 'all' ): array { if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } @@ -771,7 +771,7 @@ function wp_agentic_admin_get_all_plugins( string $status_filter = 'all' ): arra * @param string $plugin_file The plugin file path (slug) to activate. * @return array Result with success status and message. */ -function wp_agentic_admin_activate_plugin_by_slug( string $plugin_file ): array { +function agentic_admin_activate_plugin_by_slug( string $plugin_file ): array { // Implementation with validation, error handling, etc. } ``` @@ -780,20 +780,20 @@ Then your ability files become thin wrappers: ```php // includes/abilities/plugin-list.php -function wp_agentic_admin_execute_plugin_list( array $input = array() ): array { +function agentic_admin_execute_plugin_list( array $input = array() ): array { $status = isset( $input['status'] ) ? $input['status'] : 'all'; - return wp_agentic_admin_get_all_plugins( $status ); + return agentic_admin_get_all_plugins( $status ); } // includes/abilities/plugin-activate.php -function wp_agentic_admin_execute_plugin_activate( array $input = array() ): array { +function agentic_admin_execute_plugin_activate( array $input = array() ): array { if ( empty( $input['plugin'] ) ) { return array( 'success' => false, - 'message' => __( 'No plugin specified.', 'wp-agentic-admin' ), + 'message' => __( 'No plugin specified.', 'agentic-admin' ), ); } - return wp_agentic_admin_activate_plugin_by_slug( $input['plugin'] ); + return agentic_admin_activate_plugin_by_slug( $input['plugin'] ); } ``` @@ -801,7 +801,7 @@ function wp_agentic_admin_execute_plugin_activate( array $input = array() ): arr ```php private function load_ability_files(): void { - $abilities_dir = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities/'; + $abilities_dir = AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities/'; // Load shared helper functions first $shared_helpers = $abilities_dir . 'shared/plugin-helpers.php'; @@ -838,7 +838,7 @@ export function extractPluginParams(userMessage, actionKeywords = []) { }; // 2. Check dynamic plugin list from plugin-list ability - if (window.wpAgenticAdmin?.pluginsList) { + if (window.agenticAdmin?.pluginsList) { // Fuzzy match against actual plugin names } @@ -877,7 +877,7 @@ function extractParams(userMessage) { } export function registerPluginActivate() { - registerAbility('wp-agentic-admin/plugin-activate', { + registerAbility('agentic-admin/plugin-activate', { // ... summarize: (result) => formatPluginActionResult( result, @@ -888,7 +888,7 @@ export function registerPluginActivate() { if (!params) { return { error: 'Could not determine which plugin to activate.' }; } - return executeAbility('wp-agentic-admin/plugin-activate', params); + return executeAbility('agentic-admin/plugin-activate', params); }, }); } @@ -951,47 +951,47 @@ export function registerCoreSiteInfo() { Key differences from regular abilities: - **No PHP file** in `includes/abilities/` -- WordPress core handles backend registration - **No entry in `class-abilities.php`** -- the ability already exists in the WP Abilities API -- The ability ID uses the `core/` namespace (e.g., `core/get-site-info`), not `wp-agentic-admin/` +- The ability ID uses the `core/` namespace (e.g., `core/get-site-info`), not `agentic-admin/` - See `core-site-info.js` and `core-environment-info.js` for working examples ## PHP API Reference All public PHP functions are defined in `functions-abilities.php`. -### `wp_agentic_admin_register_ability( string $id, array $php_args, array $js_args = array() ): bool` +### `agentic_admin_register_ability( string $id, array $php_args, array $js_args = array() ): bool` Registers an ability with both the WordPress Abilities API (backend) and stores JS configuration for the frontend. See the [Creating a New Ability](#creating-a-new-ability) section for full usage. -### `wp_agentic_admin_unregister_ability( string $id ): bool` +### `agentic_admin_unregister_ability( string $id ): bool` Removes a previously registered ability. Returns `true` if the ability was unregistered, `false` if it did not exist. Also calls `wp_unregister_ability()` if the WordPress Abilities API is available. ```php // Example: conditionally remove an ability -if ( wp_agentic_admin_ability_exists( 'wp-agentic-admin/cache-flush' ) ) { - wp_agentic_admin_unregister_ability( 'wp-agentic-admin/cache-flush' ); +if ( agentic_admin_ability_exists( 'agentic-admin/cache-flush' ) ) { + agentic_admin_unregister_ability( 'agentic-admin/cache-flush' ); } ``` -### `wp_agentic_admin_ability_exists( string $id ): bool` +### `agentic_admin_ability_exists( string $id ): bool` Checks whether an ability with the given ID is currently registered. Useful for guard checks before registering or unregistering. ```php -if ( ! wp_agentic_admin_ability_exists( 'my-plugin/my-ability' ) ) { - wp_agentic_admin_register_ability( 'my-plugin/my-ability', $php_args, $js_args ); +if ( ! agentic_admin_ability_exists( 'my-plugin/my-ability' ) ) { + agentic_admin_register_ability( 'my-plugin/my-ability', $php_args, $js_args ); } ``` -### `wp_agentic_admin_get_abilities(): array` +### `agentic_admin_get_abilities(): array` Returns all registered abilities as an associative array keyed by ability ID. -### `wp_agentic_admin_get_ability( string $id ): ?array` +### `agentic_admin_get_ability( string $id ): ?array` Returns the configuration for a single ability, or `null` if not found. -### `wp_agentic_admin_get_abilities_js_config(): array` +### `agentic_admin_get_abilities_js_config(): array` Returns JS-facing configurations for all abilities. Used internally by `wp_localize_script()` to pass ability metadata to the frontend. Includes `phpLabel`, `description`, and `annotations` from the PHP config. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 0fb3560..e40fe3b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -82,7 +82,7 @@ Our design decisions are optimized for these user personas: - **Require:** Clear APIs, WordPress coding standards compliance, extensibility **What they need:** -- Simple `wp_agentic_admin_register_ability()` API +- Simple `agentic_admin_register_ability()` API - PHP/JavaScript dual registration pattern - WordPress Abilities API integration - Shared helper functions to avoid duplication @@ -285,7 +285,7 @@ skills: ### WordPress Abilities (Chosen) ```php -wp_agentic_admin_register_ability('wp-agentic-admin/db-optimize', [ +agentic_admin_register_ability('agentic-admin/db-optimize', [ 'label' => 'Optimize Database', 'description' => 'Optimize database tables', 'category' => 'sre-tools', @@ -475,7 +475,7 @@ The PHP settings system is managed by `class-settings.php` (`includes/class-sett - **Confirm destructive actions** - Toggle requiring user confirmation before executing destructive abilities (enabled by default) - **Max log lines** - Configure the maximum number of log lines to read at once (default: 100) -Settings are stored as a single WordPress option (`wp_agentic_admin_settings`) and accessed via the `Settings` singleton. +Settings are stored as a single WordPress option (`agentic_admin_settings`) and accessed via the `Settings` singleton. --- diff --git a/docs/RAG-CODEBASE-SEARCH.md b/docs/RAG-CODEBASE-SEARCH.md index 6d37439..d6cc9f3 100644 --- a/docs/RAG-CODEBASE-SEARCH.md +++ b/docs/RAG-CODEBASE-SEARCH.md @@ -84,7 +84,7 @@ To rebuild: say **"reindex the codebase"**. ### What gets scanned - Active theme (`get_stylesheet_directory()`) -- All active plugins (except wp-agentic-admin itself) +- All active plugins (except agentic-admin itself) ### What gets skipped diff --git a/docs/THIRD-PARTY-INTEGRATION.md b/docs/THIRD-PARTY-INTEGRATION.md index feb8c6a..08aa591 100644 --- a/docs/THIRD-PARTY-INTEGRATION.md +++ b/docs/THIRD-PARTY-INTEGRATION.md @@ -10,11 +10,11 @@ WP-Agentic-Admin provides a public API that allows any WordPress plugin to regis ### PHP: Register the Backend -Hook into `wp_agentic_admin_register_abilities` to register your ability: +Hook into `agentic_admin_register_abilities` to register your ability: ```php -add_action( 'wp_agentic_admin_register_abilities', function() { - wp_agentic_admin_register_ability( +add_action( 'agentic_admin_register_abilities', function() { + agentic_admin_register_ability( 'my-plugin/my-ability', array( 'label' => __( 'My Ability', 'my-plugin' ), @@ -62,18 +62,18 @@ function my_plugin_execute_ability( array $input = array() ): array { ### JavaScript: Register the Frontend -Enqueue your script with `wp-agentic-admin` as a dependency: +Enqueue your script with `agentic-admin` as a dependency: ```php add_action( 'admin_enqueue_scripts', function( $hook ) { - if ( 'toplevel_page_wp-agentic-admin' !== $hook ) { + if ( 'toplevel_page_agentic-admin' !== $hook ) { return; } wp_enqueue_script( 'my-plugin-agentic-abilities', plugins_url( 'assets/js/agentic-abilities.js', __FILE__ ), - array( 'wp-agentic-admin' ), + array( 'agentic-admin' ), '1.0.0', true ); @@ -119,7 +119,7 @@ Then register your ability in JavaScript: ### PHP API -#### `wp_agentic_admin_register_ability( $id, $php_config, $js_config )` +#### `agentic_admin_register_ability( $id, $php_config, $js_config )` Registers an ability with both the WordPress Abilities API and the Agentic Admin chat system. @@ -164,44 +164,44 @@ array( ) ``` -> **Auto-forwarded PHP values:** The `wp_agentic_admin_get_abilities_js_config()` function automatically forwards the PHP config's `label` and `description` values to the JavaScript frontend as `phpLabel` and `description` keys respectively. This means you do not need to duplicate label/description information in your JS config -- simply set them in the PHP config and they will be available on the JS side automatically. +> **Auto-forwarded PHP values:** The `agentic_admin_get_abilities_js_config()` function automatically forwards the PHP config's `label` and `description` values to the JavaScript frontend as `phpLabel` and `description` keys respectively. This means you do not need to duplicate label/description information in your JS config -- simply set them in the PHP config and they will be available on the JS side automatically. -#### `wp_agentic_admin_unregister_ability( string $id ): bool` +#### `agentic_admin_unregister_ability( string $id ): bool` Removes a previously registered ability. Returns `true` if the ability was found and removed, `false` if it did not exist. ```php -$removed = wp_agentic_admin_unregister_ability( 'my-plugin/my-ability' ); +$removed = agentic_admin_unregister_ability( 'my-plugin/my-ability' ); ``` -#### `wp_agentic_admin_get_abilities(): array` +#### `agentic_admin_get_abilities(): array` Returns all registered abilities as an associative array keyed by ability ID. Each entry contains `id`, `php` (PHP config), and `js` (JS config) keys. ```php -$all_abilities = wp_agentic_admin_get_abilities(); +$all_abilities = agentic_admin_get_abilities(); foreach ( $all_abilities as $id => $ability ) { echo $ability['php']['label']; } ``` -#### `wp_agentic_admin_get_ability( string $id ): ?array` +#### `agentic_admin_get_ability( string $id ): ?array` Returns the configuration for a specific ability, or `null` if not found. ```php -$ability = wp_agentic_admin_get_ability( 'my-plugin/my-ability' ); +$ability = agentic_admin_get_ability( 'my-plugin/my-ability' ); if ( $ability ) { echo $ability['php']['label']; } ``` -#### `wp_agentic_admin_ability_exists( string $id ): bool` +#### `agentic_admin_ability_exists( string $id ): bool` Checks if an ability with the given ID is registered. ```php -if ( wp_agentic_admin_ability_exists( 'my-plugin/my-ability' ) ) { +if ( agentic_admin_ability_exists( 'my-plugin/my-ability' ) ) { // Ability is registered } ``` @@ -499,12 +499,12 @@ Available context variables are auto-extracted from previous step results: ## Filters -### `wp_agentic_admin_supported_post_types` +### `agentic_admin_supported_post_types` This filter (defined in `class-utils.php`) allows third-party plugins to modify the list of post types that support the block editor. The value is an array of arrays, each with `value` (post type slug) and `label` (human-readable name) keys. ```php -add_filter( 'wp_agentic_admin_supported_post_types', function( $post_types ) { +add_filter( 'agentic_admin_supported_post_types', function( $post_types ) { // Add a custom post type $post_types[] = array( 'value' => 'my_custom_type', @@ -700,12 +700,12 @@ Here's a complete example of a third-party plugin adding an ability: */ // Register the ability -add_action( 'wp_agentic_admin_register_abilities', function() { - if ( ! function_exists( 'wp_agentic_admin_register_ability' ) ) { +add_action( 'agentic_admin_register_abilities', function() { + if ( ! function_exists( 'agentic_admin_register_ability' ) ) { return; } - wp_agentic_admin_register_ability( + agentic_admin_register_ability( 'my-plugin/count-posts', array( 'label' => __( 'Count Posts', 'my-plugin' ), @@ -760,14 +760,14 @@ function my_plugin_count_posts( array $input = array() ): array { // Enqueue JS add_action( 'admin_enqueue_scripts', function( $hook ) { - if ( 'toplevel_page_wp-agentic-admin' !== $hook ) { + if ( 'toplevel_page_agentic-admin' !== $hook ) { return; } wp_enqueue_script( 'my-plugin-agentic', plugins_url( 'agentic.js', __FILE__ ), - array( 'wp-agentic-admin' ), + array( 'agentic-admin' ), '1.0.0', true ); @@ -830,4 +830,4 @@ Use the Abilities tab in Agentic Admin to test your ability's REST endpoint dire 1. **"input is not of type object"** - Add `'default' => array()` to your input_schema 2. **Ability not triggering** - Check keywords match user input 3. **Permission denied** - Verify permission_callback returns true for current user -4. **Script not loading** - Ensure dependency on `wp-agentic-admin` +4. **Script not loading** - Ensure dependency on `agentic-admin` diff --git a/docs/UI-AUDIT-WP-7.0.md b/docs/UI-AUDIT-WP-7.0.md index 883aeb8..baea8ae 100644 --- a/docs/UI-AUDIT-WP-7.0.md +++ b/docs/UI-AUDIT-WP-7.0.md @@ -96,7 +96,7 @@ Screenshots in `docs/audit-assets/wp-7.0-before/`. | # | Finding | Priority | Recommended fix | |---|---|---|---| -| 1.1 | Hand-rolled tab strip ("Chat / Abilities / Plugin Abilities" + "Settings" gear button) using `wp-agentic-admin-tab` class | P1 | Use `@wordpress/components` `TabPanel` or `Tabs` (32.0.0). Keeps keyboard nav + a11y for free. | +| 1.1 | Hand-rolled tab strip ("Chat / Abilities / Plugin Abilities" + "Settings" gear button) using `agentic-admin-tab` class | P1 | Use `@wordpress/components` `TabPanel` or `Tabs` (32.0.0). Keeps keyboard nav + a11y for free. | | 1.2 | "Copy All" + "Clear Chat" buttons at top of chat panel are hand-rolled | P2 | Use `Button` `variant="tertiary"` for "Copy All", `variant="secondary"` for "Clear Chat" (`isDestructive` if confirming). | | 1.3 | Greeting bubble has an inline timestamp + copy button — custom card | P2 | Acceptable as a custom chat-bubble design (this is the product's identity). No fix. | diff --git a/docs/WORKFLOWS-GUIDE.md b/docs/WORKFLOWS-GUIDE.md index c5e1b52..02bb219 100644 --- a/docs/WORKFLOWS-GUIDE.md +++ b/docs/WORKFLOWS-GUIDE.md @@ -96,11 +96,11 @@ wp.agenticAdmin.registerWorkflow('my-plugin/cleanup', { keywords: ['cleanup', 'clean up', 'tidy up'], steps: [ { - abilityId: 'wp-agentic-admin/cache-flush', + abilityId: 'agentic-admin/cache-flush', label: 'Clear caches', }, { - abilityId: 'wp-agentic-admin/db-optimize', + abilityId: 'agentic-admin/db-optimize', label: 'Optimize database', }, ], @@ -149,11 +149,11 @@ wp.agenticAdmin.registerWorkflow('my-plugin/full-check', { keywords: ['full check', 'diagnose everything'], steps: [ { - abilityId: 'wp-agentic-admin/site-health', + abilityId: 'agentic-admin/site-health', label: 'Check site health', }, { - abilityId: 'wp-agentic-admin/error-log-read', + abilityId: 'agentic-admin/error-log-read', label: 'Review error logs', optional: true, // Continue if error log unavailable }, @@ -410,10 +410,10 @@ WP-Agentic-Admin includes these workflows out of the box: | ID | Label | Steps | |----|-------|-------| -| `wp-agentic-admin/site-cleanup` | Full Site Cleanup | Cache flush → DB optimize (conditional, `includeIf` skips if recently optimized) → Site health | -| `wp-agentic-admin/performance-check` | Quick Performance Check | Site health → Error log (optional, `includeIf` skips unless debug mode is on or user mentioned errors) | -| `wp-agentic-admin/plugin-audit` | Plugin Audit | Plugin list → Site health | -| `wp-agentic-admin/database-maintenance` | Database Maintenance | DB optimize → Cache flush | +| `agentic-admin/site-cleanup` | Full Site Cleanup | Cache flush → DB optimize (conditional, `includeIf` skips if recently optimized) → Site health | +| `agentic-admin/performance-check` | Quick Performance Check | Site health → Error log (optional, `includeIf` skips unless debug mode is on or user mentioned errors) | +| `agentic-admin/plugin-audit` | Plugin Audit | Plugin list → Site health | +| `agentic-admin/database-maintenance` | Database Maintenance | DB optimize → Cache flush | ## Workflow Types: Rigid vs Flexible @@ -477,11 +477,11 @@ wp.agenticAdmin.registerWorkflow('my-plugin/performance-optimization', { keywords: ['optimize', 'performance', 'speed up'], steps: [ { - abilityId: 'wp-agentic-admin/site-health', + abilityId: 'agentic-admin/site-health', label: 'Check site health' }, { - abilityId: 'wp-agentic-admin/db-optimize', + abilityId: 'agentic-admin/db-optimize', label: 'Optimize database', includeIf: (previousResults, params) => { // Access previous step results @@ -505,7 +505,7 @@ wp.agenticAdmin.registerWorkflow('my-plugin/performance-optimization', { } }, { - abilityId: 'wp-agentic-admin/cache-flush', + abilityId: 'agentic-admin/cache-flush', label: 'Clear caches' } ], @@ -531,11 +531,11 @@ wp.agenticAdmin.registerWorkflow('my-plugin/performance-optimization', { keywords: ['optimize', 'performance', 'speed up'], steps: [ { - abilityId: 'wp-agentic-admin/site-health', + abilityId: 'agentic-admin/site-health', label: 'Check site health' }, { - abilityId: 'wp-agentic-admin/db-optimize', + abilityId: 'agentic-admin/db-optimize', label: 'Optimize database', includeIf: { prompt: `Should we optimize the database? @@ -560,7 +560,7 @@ wp.agenticAdmin.registerWorkflow('my-plugin/performance-optimization', { } }, { - abilityId: 'wp-agentic-admin/cache-flush', + abilityId: 'agentic-admin/cache-flush', label: 'Clear caches' } ], @@ -615,7 +615,7 @@ includeIf: { **StepResult Structure:** ```typescript { - abilityId: string, // e.g., "wp-agentic-admin/site-health" + abilityId: string, // e.g., "agentic-admin/site-health" label: string, // e.g., "Check site health" stepIndex: number, // Position in workflow (0-based) success: boolean, // Did the step succeed? @@ -683,7 +683,7 @@ You: test performance optimization workflow ```javascript // Simulate previous results const mockResults = [{ - abilityId: 'wp-agentic-admin/site-health', + abilityId: 'agentic-admin/site-health', success: true, result: { database_size: 600 * 1024 * 1024, last_optimized: '2024-01-01' } }]; @@ -897,7 +897,7 @@ summarize: (results) => { // results is an array like: // [ // { - // abilityId: 'wp-agentic-admin/plugin-list', + // abilityId: 'agentic-admin/plugin-list', // label: 'List all installed plugins', // stepIndex: 0, // success: true, @@ -905,7 +905,7 @@ summarize: (results) => { // duration: 234 // }, // { - // abilityId: 'wp-agentic-admin/site-health', + // abilityId: 'agentic-admin/site-health', // label: 'Check site health', // stepIndex: 1, // success: true, @@ -925,8 +925,8 @@ summarize: (results) => { ```javascript summarize: (results) => { // Find specific step results by abilityId - const pluginResult = results.find(r => r.abilityId === 'wp-agentic-admin/plugin-list'); - const healthResult = results.find(r => r.abilityId === 'wp-agentic-admin/site-health'); + const pluginResult = results.find(r => r.abilityId === 'agentic-admin/plugin-list'); + const healthResult = results.find(r => r.abilityId === 'agentic-admin/site-health'); ``` #### 2. Check success before accessing data @@ -995,14 +995,14 @@ wp.agenticAdmin.registerWorkflow('my-plugin/plugin-audit', { description: 'Lists all plugins and checks for issues.', keywords: ['plugin audit', 'audit plugins', 'check plugins'], steps: [ - { abilityId: 'wp-agentic-admin/plugin-list', label: 'List plugins' }, - { abilityId: 'wp-agentic-admin/site-health', label: 'Check health' }, + { abilityId: 'agentic-admin/plugin-list', label: 'List plugins' }, + { abilityId: 'agentic-admin/site-health', label: 'Check health' }, ], requiresConfirmation: false, summarize: (results) => { - const pluginResult = results.find(r => r.abilityId === 'wp-agentic-admin/plugin-list'); - const healthResult = results.find(r => r.abilityId === 'wp-agentic-admin/site-health'); + const pluginResult = results.find(r => r.abilityId === 'agentic-admin/plugin-list'); + const healthResult = results.find(r => r.abilityId === 'agentic-admin/site-health'); let summary = 'Plugin audit complete.\n\n'; diff --git a/docs/ai-fundamentals/05-quantization.md b/docs/ai-fundamentals/05-quantization.md index 804a9ad..1c99ca3 100644 --- a/docs/ai-fundamentals/05-quantization.md +++ b/docs/ai-fundamentals/05-quantization.md @@ -224,4 +224,4 @@ WebLLM's q4f16 variants use mixed precision strategies, though the exact distrib Quantization reduces model size by lowering numerical precision. Agentic Admin for WordPress uses q4f16 quantization to fit 7-billion parameter models into ~4.5GB, making browser-based AI practical. The trade-off is slight accuracy loss (~5%), which is acceptable for structured WordPress tasks. Models are pre-quantized and distributed via CDN — your browser downloads the compressed version directly. -**Next:** [Models in Agentic Admin for WordPress](06-models-in-wp-agentic-admin.md) +**Next:** [Models in Agentic Admin for WordPress](06-models-in-agentic-admin.md) diff --git a/docs/ai-fundamentals/06-models-in-wp-agentic-admin.md b/docs/ai-fundamentals/06-models-in-wp-agentic-admin.md index 18ca99f..ddc8316 100644 --- a/docs/ai-fundamentals/06-models-in-wp-agentic-admin.md +++ b/docs/ai-fundamentals/06-models-in-wp-agentic-admin.md @@ -66,7 +66,7 @@ Qwen models natively support function/tool calling through their chat template. ```json { - "name": "wp-agentic-admin/plugin-list", + "name": "agentic-admin/plugin-list", "arguments": {} } ``` @@ -142,7 +142,7 @@ The 7B model better understands conditional logic and multi-step reasoning. ### Current Method (Manual) -In `wp-agentic-admin` settings: +In `agentic-admin` settings: 1. Navigate to **Agentic Admin** in WordPress admin 2. Click the **Settings** tab diff --git a/docs/ai-fundamentals/07-service-worker-persistence.md b/docs/ai-fundamentals/07-service-worker-persistence.md index cefab88..a9c6536 100644 --- a/docs/ai-fundamentals/07-service-worker-persistence.md +++ b/docs/ai-fundamentals/07-service-worker-persistence.md @@ -391,7 +391,7 @@ Add to plugin settings (future enhancement): ```javascript // Force page mode (no Service Worker) -wpAgenticAdmin.config.serviceWorkerEnabled = false; +agenticAdmin.config.serviceWorkerEnabled = false; ``` This loads the model in page context instead of Service Worker, unloading on navigation. diff --git a/docs/ai-fundamentals/08-react-pattern.md b/docs/ai-fundamentals/08-react-pattern.md index ff1c93c..3d6d758 100644 --- a/docs/ai-fundamentals/08-react-pattern.md +++ b/docs/ai-fundamentals/08-react-pattern.md @@ -128,7 +128,7 @@ The AI selects a tool and provides parameters: ```json { - "tool": "wp-agentic-admin/error-log-read", + "tool": "agentic-admin/error-log-read", "arguments": { "lines": 50 } } ``` @@ -284,7 +284,7 @@ The AI generates tool calls as JSON: ```json { - "tool": "wp-agentic-admin/plugin-list", + "tool": "agentic-admin/plugin-list", "arguments": {} } ``` @@ -293,7 +293,7 @@ The AI generates tool calls as JSON: ```json { - "tool": "wp-agentic-admin/error-log-read", + "tool": "agentic-admin/error-log-read", "arguments": { "lines": 100, "severity": "error" @@ -524,7 +524,7 @@ The JSON parser extracts the tool call. Since `site-health` is read-only, no con The system calls the WordPress REST API: ``` -GET /wp-json/wp-agentic-admin/v1/abilities/site-health/execute +GET /wp-json/agentic-admin/v1/abilities/site-health/execute ``` WordPress runs the PHP callback, which gathers page load time, active plugin count, PHP version, memory usage, and database health. The response is truncated to 2,000 characters and fed back as an **Observation**. diff --git a/docs/ai-fundamentals/09-tools-and-abilities.md b/docs/ai-fundamentals/09-tools-and-abilities.md index 348a90c..67f57ff 100644 --- a/docs/ai-fundamentals/09-tools-and-abilities.md +++ b/docs/ai-fundamentals/09-tools-and-abilities.md @@ -19,7 +19,7 @@ AI: Formats response for user ``` Each ability has: -- **ID** — Unique identifier (e.g., `wp-agentic-admin/plugin-list`) +- **ID** — Unique identifier (e.g., `agentic-admin/plugin-list`) - **Description** — What it does (for the AI to understand) - **Input Schema** — What parameters it accepts - **Output Schema** — What data it returns @@ -34,7 +34,7 @@ These terms are used interchangeably, but technically: - WordPress ecosystem term - Used in WordPress Abilities API (core feature in WP 6.9+) - PHP-side concept -- Example: `wp-agentic-admin/cache-flush` +- Example: `agentic-admin/cache-flush` **Tool:** - AI/LLM ecosystem term @@ -55,8 +55,8 @@ Both refer to the same underlying operations. Abilities are registered with WordPress using the Abilities API: ```php -wp_agentic_admin_register_ability( - 'wp-agentic-admin/plugin-list', // Unique ID +agentic_admin_register_ability( + 'agentic-admin/plugin-list', // Unique ID array( 'label' => 'List installed plugins', 'description' => 'Get all installed WordPress plugins', @@ -73,7 +73,7 @@ wp_agentic_admin_register_ability( 'total' => array( 'type' => 'integer' ), ), ), - 'execute_callback' => 'wp_agentic_admin_execute_plugin_list', + 'execute_callback' => 'agentic_admin_execute_plugin_list', 'permission_callback' => function() { return current_user_can( 'manage_options' ); }, @@ -99,7 +99,7 @@ When the AI calls an ability, WordPress: 5. Returns result via REST API ```php -function wp_agentic_admin_execute_plugin_list( array $input = array() ): array { +function agentic_admin_execute_plugin_list( array $input = array() ): array { if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } @@ -128,12 +128,12 @@ function wp_agentic_admin_execute_plugin_list( array $input = array() ): array { The frontend registers a JS wrapper that tells the AI about the ability: ```javascript -registerAbility('wp-agentic-admin/plugin-list', { +registerAbility('agentic-admin/plugin-list', { label: 'List installed plugins with status', keywords: ['plugin', 'list', 'installed'], execute: async (params) => { - return executeAbility('wp-agentic-admin/plugin-list', {}); + return executeAbility('agentic-admin/plugin-list', {}); }, summarize: (result) => { @@ -406,7 +406,7 @@ Destructive abilities require user confirmation before execution: ### JavaScript Configuration ```javascript -registerAbility('wp-agentic-admin/plugin-deactivate', { +registerAbility('agentic-admin/plugin-deactivate', { requiresConfirmation: true, confirmationMessage: 'Deactivating a plugin may break site functionality. Continue?', @@ -494,8 +494,8 @@ The AI cannot bypass WordPress security — it operates within the logged-in use Other plugins can register custom abilities: ```php -add_action( 'wp_agentic_admin_register_abilities', function() { - wp_agentic_admin_register_ability( +add_action( 'agentic_admin_register_abilities', function() { + agentic_admin_register_ability( 'my-plugin/custom-ability', array( 'label' => 'My Custom Action', @@ -522,8 +522,8 @@ Happens on every page load: add_action( 'init', array( $this, 'register_core_abilities' ) ); public function register_core_abilities() { - wp_agentic_admin_register_plugin_list(); - wp_agentic_admin_register_site_health(); + agentic_admin_register_plugin_list(); + agentic_admin_register_site_health(); // ... all core abilities } ``` @@ -551,7 +551,7 @@ const tools = getAvailableTools(); When the AI calls a tool: ```javascript -const result = await executeAbility('wp-agentic-admin/plugin-list', {}); +const result = await executeAbility('agentic-admin/plugin-list', {}); // Makes REST API request to WordPress // Returns result to AI for observation ``` diff --git a/docs/ai-fundamentals/10-performance-optimization.md b/docs/ai-fundamentals/10-performance-optimization.md index 1e5277f..3bf3d7c 100644 --- a/docs/ai-fundamentals/10-performance-optimization.md +++ b/docs/ai-fundamentals/10-performance-optimization.md @@ -172,7 +172,7 @@ Users see responses immediately, not after full generation. // User: "list plugins" // Skip ReAct, go straight to plugin-list tool if (matchesKeywords(message, ['list', 'plugin'])) { - return executeAbility('wp-agentic-admin/plugin-list'); + return executeAbility('agentic-admin/plugin-list'); } ``` @@ -363,7 +363,7 @@ Only return fields the AI needs. Cache expensive operations: ```php -function wp_agentic_admin_execute_site_health( $input ) { +function agentic_admin_execute_site_health( $input ) { $cache_key = 'agentic_site_health'; $cached = get_transient( $cache_key ); diff --git a/docs/ai-fundamentals/11-tool-selection-at-scale.md b/docs/ai-fundamentals/11-tool-selection-at-scale.md index b5e2818..8f15411 100644 --- a/docs/ai-fundamentals/11-tool-selection-at-scale.md +++ b/docs/ai-fundamentals/11-tool-selection-at-scale.md @@ -242,7 +242,7 @@ Action: search-tools("cron management") A special ability that searches the full tool index: ```javascript -registerAbility('wp-agentic-admin/search-tools', { +registerAbility('agentic-admin/search-tools', { label: 'Search for additional tools by description', execute: async ({ query }) => { @@ -268,7 +268,7 @@ registerAbility('wp-agentic-admin/search-tools', { { "matches": [ { - "id": "wp-agentic-admin/cron-list", + "id": "agentic-admin/cron-list", "label": "List scheduled cron events", "description": "Get all WordPress cron events with schedules" } @@ -302,7 +302,7 @@ The LLM discovers tools **on-demand** based on observations, not upfront. This i Each ability already has a `keywords` array: ```javascript -registerAbility('wp-agentic-admin/plugin-list', { +registerAbility('agentic-admin/plugin-list', { keywords: ['plugin', 'list', 'installed', 'active'], // ... }); @@ -339,7 +339,7 @@ Third-party abilities automatically work: ```php // Third-party plugin -wp_agentic_admin_register_ability( +agentic_admin_register_ability( 'my-plugin/backup-restore', array( 'description' => 'Restore site from backup file', @@ -459,7 +459,7 @@ test('should select site-health for "my site is slow"', () => { const message = "my site is slow"; const topTools = selectTopTools(message, abilities, 5); - expect(topTools[0].id).toBe('wp-agentic-admin/site-health'); + expect(topTools[0].id).toBe('agentic-admin/site-health'); }); test('should fall back to all tools when no match', () => { @@ -589,7 +589,7 @@ test( 'selects plugin-list for "show me my plugins"', () => { // Test ambiguous messages (user doesn't use exact keywords) test( 'selects site-health for "everything feels sluggish"', () => { const result = router.selectTools( 'everything feels sluggish' ); - expect( result.map( t => t.id ) ).toContain( 'wp-agentic-admin/site-health' ); + expect( result.map( t => t.id ) ).toContain( 'agentic-admin/site-health' ); } ); // Test zero-match fallback diff --git a/docs/ai-fundamentals/12-debugging-development.md b/docs/ai-fundamentals/12-debugging-development.md index 7bd2a86..6f6774b 100644 --- a/docs/ai-fundamentals/12-debugging-development.md +++ b/docs/ai-fundamentals/12-debugging-development.md @@ -40,10 +40,10 @@ npm run dev ```bash # Link plugin to WordPress installation -ln -s $(pwd) /path/to/wordpress/wp-content/plugins/wp-agentic-admin +ln -s $(pwd) /path/to/wordpress/wp-content/plugins/agentic-admin # Or use wp-cli -wp plugin install /path/to/wp-agentic-admin --activate +wp plugin install /path/to/agentic-admin --activate ``` ### Local Development Workflow @@ -88,7 +88,7 @@ This enables detailed logging: ``` [WebLLM] Model loading: 67% [ReAct] Iteration 1: Calling site-health -[Abilities] Executing wp-agentic-admin/site-health +[Abilities] Executing agentic-admin/site-health [ReAct] Observation: {success: true, ...} ``` @@ -199,7 +199,7 @@ console.log(messages); const result = window.__wpAgenticTestHook.getLastReactResult(); console.log(result); // { -// toolsUsed: ['wp-agentic-admin/plugin-list'], +// toolsUsed: ['agentic-admin/plugin-list'], // observations: [{tool: 'plugin-list', result: {...}}], // iterations: 1, // finalResponse: 'You have 24 plugins...' @@ -208,7 +208,7 @@ console.log(result); // Get tools called in last execution const tools = window.__wpAgenticTestHook.getToolsUsed(); console.log(tools); -// ['wp-agentic-admin/plugin-list'] +// ['agentic-admin/plugin-list'] // Get tool observations const observations = window.__wpAgenticTestHook.getObservations(); @@ -237,7 +237,7 @@ window.__wpAgenticTestHook.sendMessage('list plugins'); setTimeout(() => { const tools = window.__wpAgenticTestHook.getToolsUsed(); console.assert( - tools.includes('wp-agentic-admin/plugin-list'), + tools.includes('agentic-admin/plugin-list'), 'Expected plugin-list to be called' ); }, 10000); @@ -382,13 +382,13 @@ fetch('/wp-json/abilities/v1/list') }); // Check specific ability -fetch('/wp-json/abilities/v1/describe/wp-agentic-admin/plugin-list') +fetch('/wp-json/abilities/v1/describe/agentic-admin/plugin-list') .then(r => r.json()) .then(schema => console.log('Ability schema:', schema)) .catch(e => console.error('Ability not found:', e)); // Test execution manually -fetch('/wp-json/abilities/v1/execute/wp-agentic-admin/plugin-list', { +fetch('/wp-json/abilities/v1/execute/agentic-admin/plugin-list', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -447,7 +447,7 @@ console.log(sanitized); // Should fix it ```javascript // Check ability configuration const tools = window.__wpAgenticTools; -const tool = tools['wp-agentic-admin/plugin-deactivate']; +const tool = tools['agentic-admin/plugin-deactivate']; console.log('Requires confirmation:', tool.requiresConfirmation); console.log('Confirmation message:', tool.confirmationMessage); @@ -527,7 +527,7 @@ See [TESTING.md](../../tests/TESTING.md) for complete E2E testing guide. name: 'List plugins', input: 'list all installed plugins', assertions: { - toolsCalled: ['wp-agentic-admin/plugin-list'], + toolsCalled: ['agentic-admin/plugin-list'], responseNotEmpty: true, }, } @@ -605,7 +605,7 @@ console.log(`${tokensPerSec.toFixed(1)} tokens/sec`); ```javascript console.time('Tool: plugin-list'); -const result = await executeAbility('wp-agentic-admin/plugin-list', {}); +const result = await executeAbility('agentic-admin/plugin-list', {}); console.timeEnd('Tool: plugin-list'); // Tool: plugin-list: 142ms diff --git a/docs/ai-fundamentals/INDEX.md b/docs/ai-fundamentals/INDEX.md index 005175f..215b8cf 100644 --- a/docs/ai-fundamentals/INDEX.md +++ b/docs/ai-fundamentals/INDEX.md @@ -16,7 +16,7 @@ This guide covers everything from the basics of language models to advanced opti ## Implementation in Agentic Admin for WordPress -6. [Models in Agentic Admin for WordPress](06-models-in-wp-agentic-admin.md) +6. [Models in Agentic Admin for WordPress](06-models-in-agentic-admin.md) 7. [Service Worker Persistence](07-service-worker-persistence.md) 8. [The ReAct Pattern](08-react-pattern.md) 9. [Tools & Abilities](09-tools-and-abilities.md) diff --git a/docs/ai-fundamentals/glossary.md b/docs/ai-fundamentals/glossary.md index 81567dd..db8dd07 100644 --- a/docs/ai-fundamentals/glossary.md +++ b/docs/ai-fundamentals/glossary.md @@ -10,7 +10,7 @@ Quick reference for the terms used across the AI Fundamentals chapters. Entries A discrete operation the AI can execute on WordPress — for example, reading error logs or listing plugins. Each ability has an ID, description, input/output schema, execute callback, and permission check. See [Ch. 9](09-tools-and-abilities.md). **Abilities API** -The WordPress registration system for tools. PHP abilities are registered with `wp_agentic_admin_register_ability()`, JS abilities with `registerAbility()`. See [Ch. 9](09-tools-and-abilities.md). +The WordPress registration system for tools. PHP abilities are registered with `agentic_admin_register_ability()`, JS abilities with `registerAbility()`. See [Ch. 9](09-tools-and-abilities.md). **Activation (Service Worker)** The lifecycle phase where a newly installed Service Worker takes control and can start intercepting requests. See [Ch. 7](07-service-worker-persistence.md). @@ -29,7 +29,7 @@ The number of bits used to store each value in a quantized model. Lower bit-widt The OpenAI-compatible API format for conversational inference. WebLLM adopts this format, making it easy to swap between local and cloud models. See [Ch. 4](04-webllm.md). **Chat Template** -A structured format that models use to encode conversations, system prompts, and function calls into a token sequence the model understands. See [Ch. 6](06-models-in-wp-agentic-admin.md). +A structured format that models use to encode conversations, system prompts, and function calls into a token sequence the model understands. See [Ch. 6](06-models-in-agentic-admin.md). **Cold Start** The initial model load when no cached copy exists — includes downloading weights from the CDN (~1-5 minutes) and compiling shaders (~10-30 seconds). Subsequent loads use the cache. See [Ch. 10](10-performance-optimization.md). @@ -65,7 +65,7 @@ The WebLLM inference engine object that manages model lifecycle: loading, compil Floating-point number formats using 16 or 32 bits of precision. Float32 is standard training precision; float16 is a common inference optimization. In quantization names like `q4f16`, the `f16` refers to activation precision. See [Ch. 5](05-quantization.md). **Function Calling** -An LLM capability where the model generates structured JSON tool invocations instead of plain text. Qwen models have native function calling support, making them well-suited for agentic tasks. See [Ch. 1](01-what-is-a-language-model.md), [Ch. 6](06-models-in-wp-agentic-admin.md). +An LLM capability where the model generates structured JSON tool invocations instead of plain text. Qwen models have native function calling support, making them well-suited for agentic tasks. See [Ch. 1](01-what-is-a-language-model.md), [Ch. 6](06-models-in-agentic-admin.md). ## G diff --git a/includes/abilities-manifest.php b/includes/abilities-manifest.php index 364963d..975ab7b 100644 --- a/includes/abilities-manifest.php +++ b/includes/abilities-manifest.php @@ -7,22 +7,12 @@ * - CORE Always on. Defines what the plugin IS. * - LOCAL_ONLY Registered, but the JS layer hides them from the LLM when * an external AI provider is active (sensitive data must stay local). - * - LABS Opt-in. Preserved for v1.x add-on releases. Off by default - * in v0.11+; enable via constant or filter. * - * Re-enable parked abilities globally: - * - * define( 'WP_AGENTIC_ADMIN_ENABLE_LABS', true ); - * - * Or selectively via filter: - * - * add_filter( 'agentic_admin_enabled_abilities', function( $abilities ) { - * $abilities['write-file'] = 'agentic_admin_register_write_file'; - * return $abilities; - * }); + * Site owners can disable individual abilities via the + * agentic_admin_enabled_abilities filter (see below). * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -37,51 +27,56 @@ function agentic_admin_core_abilities(): array { return array( // Diagnostics. - 'site-health' => 'agentic_admin_register_site_health', + 'site-health' => 'agentic_admin_register_site_health', // Security suite. - 'security-scan' => 'agentic_admin_register_security_scan', - 'verify-core-checksums' => 'agentic_admin_register_verify_core_checksums', - 'verify-plugin-checksums' => 'agentic_admin_register_verify_plugin_checksums', - 'file-scan' => 'agentic_admin_register_file_scan', - 'uploads-scan' => 'agentic_admin_register_uploads_scan', - 'database-check' => 'agentic_admin_register_database_check', - 'role-capabilities-check' => 'agentic_admin_register_role_capabilities_check', + 'security-scan' => 'agentic_admin_register_security_scan', + 'verify-core-checksums' => 'agentic_admin_register_verify_core_checksums', + 'verify-plugin-checksums' => 'agentic_admin_register_verify_plugin_checksums', + 'file-scan' => 'agentic_admin_register_file_scan', + 'uploads-scan' => 'agentic_admin_register_uploads_scan', + 'database-check' => 'agentic_admin_register_database_check', + 'role-capabilities-check' => 'agentic_admin_register_role_capabilities_check', // Troubleshooting. - 'error-log-read' => 'agentic_admin_register_error_log_read', - 'error-log-search' => 'agentic_admin_register_error_log_search', - 'cron-list' => 'agentic_admin_register_cron_list', - 'rewrite-list' => 'agentic_admin_register_rewrite_list', + 'error-log-read' => 'agentic_admin_register_error_log_read', + 'error-log-search' => 'agentic_admin_register_error_log_search', + 'cron-list' => 'agentic_admin_register_cron_list', + 'rewrite-list' => 'agentic_admin_register_rewrite_list', // Inventory. - 'plugin-list' => 'agentic_admin_register_plugin_list', - 'theme-list' => 'agentic_admin_register_theme_list', - 'user-list' => 'agentic_admin_register_user_list', - 'post-list' => 'agentic_admin_register_post_list', - 'comment-stats' => 'agentic_admin_register_comment_stats', - 'update-check' => 'agentic_admin_register_update_check', + 'plugin-list' => 'agentic_admin_register_plugin_list', + 'theme-list' => 'agentic_admin_register_theme_list', + 'user-list' => 'agentic_admin_register_user_list', + 'post-list' => 'agentic_admin_register_post_list', + 'comment-stats' => 'agentic_admin_register_comment_stats', + 'update-check' => 'agentic_admin_register_update_check', // Maintenance. - 'cache-flush' => 'agentic_admin_register_cache_flush', - 'transient-flush' => 'agentic_admin_register_transient_flush', - 'rewrite-flush' => 'agentic_admin_register_rewrite_flush', - 'db-optimize' => 'agentic_admin_register_db_optimize', - 'revision-cleanup' => 'agentic_admin_register_revision_cleanup', + 'cache-flush' => 'agentic_admin_register_cache_flush', + 'transient-flush' => 'agentic_admin_register_transient_flush', + 'rewrite-flush' => 'agentic_admin_register_rewrite_flush', + 'db-optimize' => 'agentic_admin_register_db_optimize', + 'revision-cleanup' => 'agentic_admin_register_revision_cleanup', // Plugin management. - 'plugin-activate' => 'agentic_admin_register_plugin_activate', - 'plugin-deactivate' => 'agentic_admin_register_plugin_deactivate', - 'plugin-install' => 'agentic_admin_register_plugin_install', + 'plugin-activate' => 'agentic_admin_register_plugin_activate', + 'plugin-deactivate' => 'agentic_admin_register_plugin_deactivate', + 'plugin-install' => 'agentic_admin_register_plugin_install', // Knowledge. - 'web-search' => 'agentic_admin_register_web_search', + 'web-search' => 'agentic_admin_register_web_search', // RAG infrastructure (used by the knowledge base — always on). - 'schema-extract' => 'agentic_admin_register_schema_extract', - 'wp-api-extract' => 'agentic_admin_register_wp_api_extract', - 'docs-extract' => 'agentic_admin_register_docs_extract', - 'codebase-extract' => 'agentic_admin_register_codebase_extract', + 'schema-extract' => 'agentic_admin_register_schema_extract', + 'wp-api-extract' => 'agentic_admin_register_wp_api_extract', + 'docs-extract' => 'agentic_admin_register_docs_extract', + 'codebase-extract' => 'agentic_admin_register_codebase_extract', + + // Plugin abilities platform — lets the assistant discover and run + // abilities that other plugins register via the WordPress Abilities API. + 'discover-plugin-abilities' => 'agentic_admin_register_discover_plugin_abilities', + 'run-plugin-ability' => 'agentic_admin_register_run_plugin_ability', ); } @@ -96,34 +91,15 @@ function agentic_admin_core_abilities(): array { */ function agentic_admin_local_only_abilities(): array { return array( - 'query-database' => 'agentic_admin_register_query_database', - 'read-file' => 'agentic_admin_register_read_file', - ); -} - -/** - * Labs (parked) abilities — opt-in via WP_AGENTIC_ADMIN_ENABLE_LABS - * or the agentic_admin_enabled_abilities filter. Preserved for - * v1.x add-on releases. Off by default. - * - * @return array - */ -function agentic_admin_labs_abilities(): array { - return array( - 'write-file' => 'agentic_admin_register_write_file', - // content-generate is JS-only (lives in the JS labs manifest). - 'discover-plugin-abilities' => 'agentic_admin_register_discover_plugin_abilities', - 'run-plugin-ability' => 'agentic_admin_register_run_plugin_ability', + 'read-file' => 'agentic_admin_register_read_file', ); } /** * Resolve the final list of enabled abilities. * - * Core and local-only abilities always register. Labs only register when - * the WP_AGENTIC_ADMIN_ENABLE_LABS constant is defined and truthy, or - * when an entry is added back via the agentic_admin_enabled_abilities - * filter. + * All abilities are fully functional and registered. Site owners may opt + * out of individual abilities via the agentic_admin_enabled_abilities filter. * * @return array */ @@ -133,18 +109,14 @@ function agentic_admin_resolve_enabled_abilities(): array { agentic_admin_local_only_abilities() ); - if ( defined( 'WP_AGENTIC_ADMIN_ENABLE_LABS' ) && WP_AGENTIC_ADMIN_ENABLE_LABS ) { - $enabled = array_merge( $enabled, agentic_admin_labs_abilities() ); - } - /** * Filter the final list of abilities to register. * - * Use this to selectively re-enable a labs ability without flipping - * the global WP_AGENTIC_ADMIN_ENABLE_LABS constant: + * Site owners can use this to disable an ability they do not want the + * assistant to use, for example: * * add_filter( 'agentic_admin_enabled_abilities', function ( $abilities ) { - * $abilities['write-file'] = 'agentic_admin_register_write_file'; + * unset( $abilities['web-search'] ); * return $abilities; * }); * @@ -154,12 +126,3 @@ function agentic_admin_resolve_enabled_abilities(): array { */ return (array) apply_filters( 'agentic_admin_enabled_abilities', $enabled ); } - -/** - * Whether labs (parked) abilities are enabled this request. - * - * @return bool True when WP_AGENTIC_ADMIN_ENABLE_LABS is defined and truthy. - */ -function agentic_admin_labs_enabled(): bool { - return defined( 'WP_AGENTIC_ADMIN_ENABLE_LABS' ) && WP_AGENTIC_ADMIN_ENABLE_LABS; -} diff --git a/includes/abilities/cache-flush.php b/includes/abilities/cache-flush.php index 98cbdf5..219cd73 100644 --- a/includes/abilities/cache-flush.php +++ b/includes/abilities/cache-flush.php @@ -5,7 +5,7 @@ * Flushes the WordPress object cache. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_cache_flush(): void { agentic_admin_register_ability( - 'wp-agentic-admin/cache-flush', + 'agentic-admin/cache-flush', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Flush Cache', 'agentic-admin' ), diff --git a/includes/abilities/codebase-extract.php b/includes/abilities/codebase-extract.php index 762c991..0b9f8b4 100644 --- a/includes/abilities/codebase-extract.php +++ b/includes/abilities/codebase-extract.php @@ -5,7 +5,7 @@ * Extracts code chunks from the active theme and plugins for local RAG indexing. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_codebase_extract(): void { agentic_admin_register_ability( - 'wp-agentic-admin/codebase-extract', + 'agentic-admin/codebase-extract', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Extract Codebase', 'agentic-admin' ), @@ -112,7 +112,7 @@ function agentic_admin_collect_code_files(): array { ); } - // 2. Active plugins (exclude wp-agentic-admin itself). + // 2. Active plugins (exclude agentic-admin itself). $active_plugins = get_option( 'active_plugins', array() ); $plugins_dir = WP_PLUGIN_DIR; diff --git a/includes/abilities/comment-stats.php b/includes/abilities/comment-stats.php index b8bd5c8..e331732 100644 --- a/includes/abilities/comment-stats.php +++ b/includes/abilities/comment-stats.php @@ -5,7 +5,7 @@ * Shows comment counts by status. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_comment_stats(): void { agentic_admin_register_ability( - 'wp-agentic-admin/comment-stats', + 'agentic-admin/comment-stats', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Comment Statistics', 'agentic-admin' ), diff --git a/includes/abilities/cron-list.php b/includes/abilities/cron-list.php index 3b8b8ee..d9368e0 100644 --- a/includes/abilities/cron-list.php +++ b/includes/abilities/cron-list.php @@ -6,7 +6,7 @@ * Similar to WP-CLI: wp cron event list * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @since 0.1.0 */ @@ -21,7 +21,7 @@ */ function agentic_admin_register_cron_list(): void { agentic_admin_register_ability( - 'wp-agentic-admin/cron-list', + 'agentic-admin/cron-list', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'List Cron Events', 'agentic-admin' ), diff --git a/includes/abilities/current-user-role.php b/includes/abilities/current-user-role.php index 3c7ad35..cc61a50 100644 --- a/includes/abilities/current-user-role.php +++ b/includes/abilities/current-user-role.php @@ -5,7 +5,7 @@ * Returns the current logged-in user's role and account info. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_current_user_role(): void { agentic_admin_register_ability( - 'wp-agentic-admin/current-user-role', + 'agentic-admin/current-user-role', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Get Current User Role', 'agentic-admin' ), diff --git a/includes/abilities/db-optimize.php b/includes/abilities/db-optimize.php index fdf0bab..1d89571 100644 --- a/includes/abilities/db-optimize.php +++ b/includes/abilities/db-optimize.php @@ -5,7 +5,7 @@ * Optimizes WordPress database tables. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_db_optimize(): void { agentic_admin_register_ability( - 'wp-agentic-admin/db-optimize', + 'agentic-admin/db-optimize', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Optimize Database', 'agentic-admin' ), diff --git a/includes/abilities/discover-plugin-abilities.php b/includes/abilities/discover-plugin-abilities.php index ebe3028..3c3b002 100644 --- a/includes/abilities/discover-plugin-abilities.php +++ b/includes/abilities/discover-plugin-abilities.php @@ -6,7 +6,7 @@ * Returns a compact summary to avoid polluting the LLM context window. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -20,7 +20,7 @@ */ function agentic_admin_register_discover_plugin_abilities(): void { agentic_admin_register_ability( - 'wp-agentic-admin/discover-plugin-abilities', + 'agentic-admin/discover-plugin-abilities', array( 'label' => __( 'Discover Plugin Abilities', 'agentic-admin' ), 'description' => __( 'Discover abilities registered by other plugins on this WordPress site.', 'agentic-admin' ), @@ -79,7 +79,7 @@ function agentic_admin_register_discover_plugin_abilities(): void { * Execute the discover-plugin-abilities ability. * * Queries the WP Abilities API registry to find abilities registered - * by other plugins (excluding our own wp-agentic-admin/* abilities). + * by other plugins (excluding our own agentic-admin/* abilities). * * @param array $input Input parameters. * @return array Discovered abilities in compact format. @@ -166,7 +166,7 @@ function agentic_admin_execute_discover_plugin_abilities( $input = array() ): ar $search = isset( $input['search'] ) ? strtolower( $input['search'] ) : ''; // Our own namespaces to exclude — we already have these as native tools. - $own_namespaces = array( 'wp-agentic-admin/', 'core/' ); + $own_namespaces = array( 'agentic-admin/', 'core/' ); $external = array(); diff --git a/includes/abilities/docs-extract.php b/includes/abilities/docs-extract.php index 3426f44..93c8854 100644 --- a/includes/abilities/docs-extract.php +++ b/includes/abilities/docs-extract.php @@ -6,7 +6,7 @@ * by heading boundaries for local RAG knowledge base indexing. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -20,7 +20,7 @@ */ function agentic_admin_register_docs_extract(): void { agentic_admin_register_ability( - 'wp-agentic-admin/docs-extract', + 'agentic-admin/docs-extract', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Extract Reference Docs', 'agentic-admin' ), @@ -135,7 +135,7 @@ function agentic_admin_execute_docs_extract( array $input = array() ): array { WP_Filesystem(); } - $docs_dir = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'docs/knowledge/'; + $docs_dir = AGENTIC_ADMIN_PLUGIN_DIR . 'docs/knowledge/'; if ( ! is_dir( $docs_dir ) ) { return array( diff --git a/includes/abilities/error-log-read.php b/includes/abilities/error-log-read.php index 5527fb8..517c853 100644 --- a/includes/abilities/error-log-read.php +++ b/includes/abilities/error-log-read.php @@ -5,7 +5,7 @@ * Reads recent entries from the WordPress debug.log file. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_error_log_read(): void { agentic_admin_register_ability( - 'wp-agentic-admin/error-log-read', + 'agentic-admin/error-log-read', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Read Error Log', 'agentic-admin' ), @@ -92,7 +92,7 @@ function agentic_admin_execute_error_log_read( array $input = array() ): array { $lines = isset( $input['lines'] ) ? absint( $input['lines'] ) : 50; // Check if debug.log exists. - $log_file = \WPAgenticAdmin\Utils::get_debug_log_path(); + $log_file = \AgenticAdmin\Utils::get_debug_log_path(); if ( ! file_exists( $log_file ) ) { return array( diff --git a/includes/abilities/error-log-search.php b/includes/abilities/error-log-search.php index 6e47418..6fd1e99 100644 --- a/includes/abilities/error-log-search.php +++ b/includes/abilities/error-log-search.php @@ -5,7 +5,7 @@ * Filters debug.log by keyword and severity. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_error_log_search(): void { agentic_admin_register_ability( - 'wp-agentic-admin/error-log-search', + 'agentic-admin/error-log-search', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Search Error Log', 'agentic-admin' ), @@ -101,7 +101,7 @@ function agentic_admin_execute_error_log_search( array $input = array() ): array $level = isset( $input['level'] ) ? sanitize_text_field( $input['level'] ) : ''; $max_lines = isset( $input['lines'] ) ? min( absint( $input['lines'] ), 500 ) : 100; - $log_file = \WPAgenticAdmin\Utils::get_debug_log_path(); + $log_file = \AgenticAdmin\Utils::get_debug_log_path(); if ( ! file_exists( $log_file ) || ! is_readable( $log_file ) ) { return array( diff --git a/includes/abilities/plugin-activate.php b/includes/abilities/plugin-activate.php index 70d6dff..5fb88e5 100644 --- a/includes/abilities/plugin-activate.php +++ b/includes/abilities/plugin-activate.php @@ -5,7 +5,7 @@ * Activates a specific plugin by its slug. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_plugin_activate(): void { agentic_admin_register_ability( - 'wp-agentic-admin/plugin-activate', + 'agentic-admin/plugin-activate', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Activate Plugin', 'agentic-admin' ), diff --git a/includes/abilities/plugin-deactivate.php b/includes/abilities/plugin-deactivate.php index b9ed368..59d4cc9 100644 --- a/includes/abilities/plugin-deactivate.php +++ b/includes/abilities/plugin-deactivate.php @@ -5,7 +5,7 @@ * Deactivates a specific plugin by its slug. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_plugin_deactivate(): void { agentic_admin_register_ability( - 'wp-agentic-admin/plugin-deactivate', + 'agentic-admin/plugin-deactivate', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Deactivate Plugin', 'agentic-admin' ), diff --git a/includes/abilities/plugin-install.php b/includes/abilities/plugin-install.php index 7e60f65..e6aad24 100644 --- a/includes/abilities/plugin-install.php +++ b/includes/abilities/plugin-install.php @@ -5,7 +5,7 @@ * Installs a plugin from the WordPress.org plugin directory. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_plugin_install(): void { agentic_admin_register_ability( - 'wp-agentic-admin/plugin-install', + 'agentic-admin/plugin-install', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Install Plugin', 'agentic-admin' ), @@ -113,17 +113,17 @@ function agentic_admin_execute_plugin_install( array $input = array() ): array { $status = $plugin_data && $plugin_data['active'] ? __( 'active', 'agentic-admin' ) : __( 'inactive', 'agentic-admin' ); return array( - 'success' => true, - 'message' => sprintf( + 'success' => true, + 'message' => sprintf( /* translators: 1: plugin name, 2: plugin status */ __( 'Plugin "%1$s" is already installed (%2$s).', 'agentic-admin' ), $resolved['plugin_name'], $status ), 'already_installed' => true, - 'plugin_name' => $resolved['plugin_name'], - 'plugin_slug' => $resolved['plugin_file'], - 'active' => $plugin_data && $plugin_data['active'], + 'plugin_name' => $resolved['plugin_name'], + 'plugin_slug' => $resolved['plugin_file'], + 'active' => $plugin_data && $plugin_data['active'], ); } diff --git a/includes/abilities/plugin-list.php b/includes/abilities/plugin-list.php index ba2de99..daacc20 100644 --- a/includes/abilities/plugin-list.php +++ b/includes/abilities/plugin-list.php @@ -5,7 +5,7 @@ * Lists all installed plugins with their status. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_plugin_list(): void { agentic_admin_register_ability( - 'wp-agentic-admin/plugin-list', + 'agentic-admin/plugin-list', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'List Plugins', 'agentic-admin' ), diff --git a/includes/abilities/post-list.php b/includes/abilities/post-list.php index 09d48e8..d294215 100644 --- a/includes/abilities/post-list.php +++ b/includes/abilities/post-list.php @@ -5,7 +5,7 @@ * Lists recent WordPress posts. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_post_list(): void { agentic_admin_register_ability( - 'wp-agentic-admin/post-list', + 'agentic-admin/post-list', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'List Posts', 'agentic-admin' ), diff --git a/includes/abilities/query-database.php b/includes/abilities/query-database.php deleted file mode 100644 index dae861f..0000000 --- a/includes/abilities/query-database.php +++ /dev/null @@ -1,238 +0,0 @@ - __( 'Query Database', 'agentic-admin' ), - 'description' => __( 'Execute read-only SQL queries for site inspection.', 'agentic-admin' ), - 'category' => 'sre-tools', - 'input_schema' => array( - 'type' => 'object', - 'default' => array( 'query' => '' ), - 'properties' => array( - 'query' => array( - 'type' => 'string', - 'description' => __( 'SQL SELECT query to execute.', 'agentic-admin' ), - ), - ), - 'required' => array( 'query' ), - 'additionalProperties' => false, - ), - 'output_schema' => array( - 'type' => 'object', - 'properties' => array( - 'success' => array( - 'type' => 'boolean', - 'description' => __( 'Whether the query succeeded.', 'agentic-admin' ), - ), - 'results' => array( - 'type' => 'array', - 'description' => __( 'Query results.', 'agentic-admin' ), - ), - 'rows' => array( - 'type' => 'integer', - 'description' => __( 'Number of rows returned.', 'agentic-admin' ), - ), - ), - ), - 'execute_callback' => 'agentic_admin_execute_query_database', - 'permission_callback' => function () { - return current_user_can( 'manage_options' ); - }, - 'meta' => array( - 'show_in_rest' => true, - 'annotations' => array( - 'readonly' => true, - 'destructive' => false, - 'idempotent' => true, - ), - ), - ), - // JS configuration for chat interface. - array( - 'keywords' => array( 'query', 'database', 'sql', 'select', 'table', 'rows' ), - 'initialMessage' => __( "I'll run that query...", 'agentic-admin' ), - ) - ); -} - -/** - * Maximum length (in characters) for a query string. Prevents pathological - * queries that try to bypass the keyword blocklist via obfuscation. - * - * @var int - */ -const AGENTIC_ADMIN_QUERY_MAX_LENGTH = 2000; - -/** - * Column names that must never appear in a query — neither in SELECT lists - * nor in WHERE clauses. - * - * Blocking them in WHERE clauses prevents email/password reconnaissance - * attacks (e.g. SELECT * FROM wp_users WHERE user_email='target@x.com'), - * where an empty vs. non-empty result set itself leaks whether the value - * exists in the database. See issue #166. - * - * @return string[] - */ -function agentic_admin_sensitive_columns(): array { - return array( - 'user_pass', - 'user_email', - 'user_activation_key', - 'session_tokens', - ); -} - -/** - * Check if a SQL query is safe (SELECT-family only, no sensitive columns). - * - * @param string $query SQL query string. - * @return string|true True if safe, otherwise an error message. - */ -function agentic_admin_check_query_safety( string $query ) { - if ( strlen( $query ) > AGENTIC_ADMIN_QUERY_MAX_LENGTH ) { - return sprintf( - /* translators: %d: maximum query length in characters */ - __( 'Query exceeds the maximum length of %d characters.', 'agentic-admin' ), - AGENTIC_ADMIN_QUERY_MAX_LENGTH - ); - } - - $upper = strtoupper( trim( $query ) ); - - // Must start with SELECT, SHOW, DESCRIBE, or EXPLAIN. - if ( ! preg_match( '/^\s*(SELECT|SHOW|DESCRIBE|EXPLAIN)\b/i', $upper ) ) { - return __( 'Only SELECT, SHOW, DESCRIBE, and EXPLAIN queries are allowed.', 'agentic-admin' ); - } - - $forbidden = array( 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'ALTER', 'TRUNCATE', 'CREATE', 'REPLACE', 'GRANT', 'REVOKE' ); - foreach ( $forbidden as $keyword ) { - if ( preg_match( '/\b' . $keyword . '\b/', $upper ) ) { - return __( 'Only SELECT, SHOW, DESCRIBE, and EXPLAIN queries are allowed.', 'agentic-admin' ); - } - } - - // Block any reference to sensitive columns (in SELECT lists, WHERE - // clauses, JOIN conditions — anywhere). Even read access via WHERE - // leaks information by row-count side-channel. See issue #166. - foreach ( agentic_admin_sensitive_columns() as $col ) { - if ( preg_match( '/\b' . preg_quote( $col, '/' ) . '\b/i', $query ) ) { - return sprintf( - /* translators: %s: sensitive column name */ - __( 'Queries that reference the sensitive column "%s" are blocked.', 'agentic-admin' ), - $col - ); - } - } - - return true; -} - -/** - * Redact sensitive column values in a row of results. - * - * Belt-and-suspenders defense: even when the query itself doesn't name - * a sensitive column (e.g. SELECT *), the result rows must not leak - * password hashes or activation keys. - * - * @param array $row Associative array of column => value. - * @return array Row with sensitive columns replaced by [REDACTED]. - */ -function agentic_admin_redact_sensitive_row( array $row ): array { - foreach ( agentic_admin_sensitive_columns() as $col ) { - if ( array_key_exists( $col, $row ) ) { - $row[ $col ] = '[REDACTED]'; - } - } - return $row; -} - -/** - * Execute the query-database ability. - * - * @param array $input Input parameters. - * @return array - */ -function agentic_admin_execute_query_database( array $input = array() ): array { - global $wpdb; - - $query = isset( $input['query'] ) ? trim( $input['query'] ) : ''; - - if ( empty( $query ) ) { - return array( - 'success' => false, - 'message' => __( 'No query provided.', 'agentic-admin' ), - 'results' => array(), - 'rows' => 0, - ); - } - - $safety = agentic_admin_check_query_safety( $query ); - if ( true !== $safety ) { - return array( - 'success' => false, - 'message' => $safety, - 'results' => array(), - 'rows' => 0, - ); - } - - // Replace {prefix} placeholder with actual prefix. - $query = str_replace( '{prefix}', $wpdb->prefix, $query ); - - // The query is fully built by the LLM (the whole point of the ability), - // so $wpdb->prepare() can't be used here — there are no separable - // placeholders. Safety is enforced upstream by: - // - read-only verb gate (SELECT/SHOW/DESCRIBE/EXPLAIN only) - // - forbidden-keyword blocklist (INSERT/UPDATE/DELETE/DROP/…) - // - sensitive-column blocklist (user_pass/user_email/…) - // - 2000-char length cap - // - manage_options capability check in permission_callback - // - per-row redaction of sensitive columns as belt-and-suspenders. - // Caching is intentionally skipped: each LLM-generated query is unique, - // so a cache would never hit while doubling our database call surface. - // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching - $results = $wpdb->get_results( $query, ARRAY_A ); - - if ( null === $results ) { - return array( - 'success' => false, - 'message' => ! empty( $wpdb->last_error ) ? $wpdb->last_error : __( 'Query failed.', 'agentic-admin' ), - 'results' => array(), - 'rows' => 0, - ); - } - - // Limit to 50 rows, then redact any sensitive columns that snuck - // through (e.g. via SELECT *). - $results = array_map( - 'agentic_admin_redact_sensitive_row', - array_slice( $results, 0, 50 ) - ); - - return array( - 'success' => true, - 'results' => $results, - 'rows' => count( $results ), - ); -} diff --git a/includes/abilities/read-file.php b/includes/abilities/read-file.php index 3038081..7c90976 100644 --- a/includes/abilities/read-file.php +++ b/includes/abilities/read-file.php @@ -5,7 +5,7 @@ * Reads WordPress files with automatic sanitization of sensitive data. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_read_file(): void { agentic_admin_register_ability( - 'wp-agentic-admin/read-file', + 'agentic-admin/read-file', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Read File', 'agentic-admin' ), diff --git a/includes/abilities/revision-cleanup.php b/includes/abilities/revision-cleanup.php index 308841b..e33009c 100644 --- a/includes/abilities/revision-cleanup.php +++ b/includes/abilities/revision-cleanup.php @@ -6,7 +6,7 @@ * Similar to WP-CLI: wp post delete $(wp post list --post_type=revision --format=ids) * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @since 0.1.0 */ @@ -21,7 +21,7 @@ */ function agentic_admin_register_revision_cleanup(): void { agentic_admin_register_ability( - 'wp-agentic-admin/revision-cleanup', + 'agentic-admin/revision-cleanup', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Clean Up Revisions', 'agentic-admin' ), diff --git a/includes/abilities/rewrite-flush.php b/includes/abilities/rewrite-flush.php index 03967cd..1a48ab9 100644 --- a/includes/abilities/rewrite-flush.php +++ b/includes/abilities/rewrite-flush.php @@ -6,7 +6,7 @@ * Similar to WP-CLI: wp rewrite flush * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @since 0.1.0 */ @@ -21,7 +21,7 @@ */ function agentic_admin_register_rewrite_flush(): void { agentic_admin_register_ability( - 'wp-agentic-admin/rewrite-flush', + 'agentic-admin/rewrite-flush', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Flush Rewrite Rules', 'agentic-admin' ), diff --git a/includes/abilities/rewrite-list.php b/includes/abilities/rewrite-list.php index 3d3dc74..3163bb5 100644 --- a/includes/abilities/rewrite-list.php +++ b/includes/abilities/rewrite-list.php @@ -6,7 +6,7 @@ * Similar to WP-CLI: wp rewrite list * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @since 0.1.0 */ @@ -21,7 +21,7 @@ */ function agentic_admin_register_rewrite_list(): void { agentic_admin_register_ability( - 'wp-agentic-admin/rewrite-list', + 'agentic-admin/rewrite-list', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'List Rewrite Rules', 'agentic-admin' ), diff --git a/includes/abilities/run-plugin-ability.php b/includes/abilities/run-plugin-ability.php index 33bbf43..c0f2e97 100644 --- a/includes/abilities/run-plugin-ability.php +++ b/includes/abilities/run-plugin-ability.php @@ -6,7 +6,7 @@ * This allows the LLM to call abilities from other plugins dynamically. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -20,7 +20,7 @@ */ function agentic_admin_register_run_plugin_ability(): void { agentic_admin_register_ability( - 'wp-agentic-admin/run-plugin-ability', + 'agentic-admin/run-plugin-ability', array( 'label' => __( 'Run Plugin Ability', 'agentic-admin' ), 'description' => __( 'Run an ability from another plugin by its ID.', 'agentic-admin' ), diff --git a/includes/abilities/schema-extract.php b/includes/abilities/schema-extract.php index 7d1fec2..e7f3654 100644 --- a/includes/abilities/schema-extract.php +++ b/includes/abilities/schema-extract.php @@ -6,7 +6,7 @@ * Returns human-readable schema chunks with semantic context for core WP tables. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -20,7 +20,7 @@ */ function agentic_admin_register_schema_extract(): void { agentic_admin_register_ability( - 'wp-agentic-admin/schema-extract', + 'agentic-admin/schema-extract', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Extract Database Schema', 'agentic-admin' ), diff --git a/includes/abilities/security-scan.php b/includes/abilities/security-scan.php index 1c31d46..09c844f 100644 --- a/includes/abilities/security-scan.php +++ b/includes/abilities/security-scan.php @@ -5,7 +5,7 @@ * Runs basic WordPress security checks. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_security_scan(): void { agentic_admin_register_ability( - 'wp-agentic-admin/security-scan', + 'agentic-admin/security-scan', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Security Scan', 'agentic-admin' ), diff --git a/includes/abilities/security/database-check.php b/includes/abilities/security/database-check.php index 1a1ef4c..686e057 100644 --- a/includes/abilities/security/database-check.php +++ b/includes/abilities/security/database-check.php @@ -7,7 +7,7 @@ * suspicious admin users, SEO spam, and more. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -21,7 +21,7 @@ */ function agentic_admin_register_database_check(): void { agentic_admin_register_ability( - 'wp-agentic-admin/database-check', + 'agentic-admin/database-check', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Database Security Check', 'agentic-admin' ), diff --git a/includes/abilities/security/file-scan.php b/includes/abilities/security/file-scan.php index 872dd30..79a41eb 100644 --- a/includes/abilities/security/file-scan.php +++ b/includes/abilities/security/file-scan.php @@ -9,7 +9,7 @@ * that have no checksums on WordPress.org (premium/custom) and all themes. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -23,7 +23,7 @@ */ function agentic_admin_register_file_scan(): void { agentic_admin_register_ability( - 'wp-agentic-admin/file-scan', + 'agentic-admin/file-scan', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Scan Files for Malware Patterns', 'agentic-admin' ), @@ -207,7 +207,7 @@ function agentic_admin_execute_file_scan( array $input = array() ): array { $dirs_to_scan[] = WP_PLUGIN_DIR; // Also scan must-use plugins if the directory exists. - $mu_dir = defined( 'WPMU_PLUGIN_DIR' ) ? WPMU_PLUGIN_DIR : WP_CONTENT_DIR . '/mu-plugins'; + $mu_dir = WPMU_PLUGIN_DIR; if ( is_dir( $mu_dir ) ) { $dirs_to_scan[] = $mu_dir; } @@ -236,7 +236,7 @@ function agentic_admin_execute_file_scan( array $input = array() ): array { $skip_dirs = apply_filters( 'agentic_admin_file_scan_skip_dirs', $skip_dirs ); // Exclude this plugin's own directory — it contains pattern strings that would self-trigger. - $self_dir = defined( 'WP_AGENTIC_ADMIN_PLUGIN_DIR' ) ? wp_normalize_path( WP_AGENTIC_ADMIN_PLUGIN_DIR ) : ''; + $self_dir = defined( 'AGENTIC_ADMIN_PLUGIN_DIR' ) ? wp_normalize_path( AGENTIC_ADMIN_PLUGIN_DIR ) : ''; /** * Filters absolute paths to exclude from the file scan. @@ -287,7 +287,7 @@ function ( $current, $key, $iterator ) use ( $skip_dirs, $excluded_paths ) { $normalized_dir = wp_normalize_path( $dir ); $is_plugin_dir = wp_normalize_path( WP_PLUGIN_DIR ) === $normalized_dir; - $mu_plugin_dir = defined( 'WPMU_PLUGIN_DIR' ) ? WPMU_PLUGIN_DIR : WP_CONTENT_DIR . '/mu-plugins'; + $mu_plugin_dir = WPMU_PLUGIN_DIR; $is_mu_dir = wp_normalize_path( $mu_plugin_dir ) === $normalized_dir; foreach ( $iterator as $file_info ) { diff --git a/includes/abilities/security/role-capabilities-check.php b/includes/abilities/security/role-capabilities-check.php index be503fe..0b8a854 100644 --- a/includes/abilities/security/role-capabilities-check.php +++ b/includes/abilities/security/role-capabilities-check.php @@ -7,7 +7,7 @@ * extra roles, and modified capabilities. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -21,7 +21,7 @@ */ function agentic_admin_register_role_capabilities_check(): void { agentic_admin_register_ability( - 'wp-agentic-admin/role-capabilities-check', + 'agentic-admin/role-capabilities-check', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Check Role Capabilities', 'agentic-admin' ), diff --git a/includes/abilities/security/uploads-scan.php b/includes/abilities/security/uploads-scan.php index b94593d..87ac99e 100644 --- a/includes/abilities/security/uploads-scan.php +++ b/includes/abilities/security/uploads-scan.php @@ -10,7 +10,7 @@ * documents). Any executable file is a strong indicator of malware. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -24,7 +24,7 @@ */ function agentic_admin_register_uploads_scan(): void { agentic_admin_register_ability( - 'wp-agentic-admin/uploads-scan', + 'agentic-admin/uploads-scan', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Scan Uploads for Suspicious Files', 'agentic-admin' ), @@ -172,7 +172,12 @@ function agentic_admin_execute_uploads_scan( array $input = array() ): array { // 3. Scan .well-known directory (recursive). // Attackers hide backdoors in .well-known/ because admins rarely check it // and some security scanners skip dotfiles/dotdirs. - $well_known_dir = ABSPATH . '.well-known'; + // .well-known lives at the site root (document root), which is get_home_path() + // and can differ from ABSPATH on subdirectory installs. + if ( ! function_exists( 'get_home_path' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + $well_known_dir = trailingslashit( get_home_path() ) . '.well-known'; if ( is_dir( $well_known_dir ) ) { $areas_scanned[] = '.well-known'; agentic_admin_scan_directory_for_dangerous_files( diff --git a/includes/abilities/security/verify-core-checksums.php b/includes/abilities/security/verify-core-checksums.php index d74c3aa..f3ee414 100644 --- a/includes/abilities/security/verify-core-checksums.php +++ b/includes/abilities/security/verify-core-checksums.php @@ -6,7 +6,7 @@ * and reports any mismatches or missing files with diffs. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -20,7 +20,7 @@ */ function agentic_admin_register_verify_core_checksums(): void { agentic_admin_register_ability( - 'wp-agentic-admin/verify-core-checksums', + 'agentic-admin/verify-core-checksums', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Verify Core Checksums', 'agentic-admin' ), diff --git a/includes/abilities/security/verify-plugin-checksums.php b/includes/abilities/security/verify-plugin-checksums.php index d523e75..4cae129 100644 --- a/includes/abilities/security/verify-plugin-checksums.php +++ b/includes/abilities/security/verify-plugin-checksums.php @@ -6,7 +6,7 @@ * Plugins not hosted on WordPress.org (no checksums available) are counted and skipped. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @see https://github.com/wp-cli/checksum-command — WP-CLI plugin verify-checksums */ @@ -21,7 +21,7 @@ */ function agentic_admin_register_verify_plugin_checksums(): void { agentic_admin_register_ability( - 'wp-agentic-admin/verify-plugin-checksums', + 'agentic-admin/verify-plugin-checksums', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Verify Plugin Checksums', 'agentic-admin' ), diff --git a/includes/abilities/shared/diff-helpers.php b/includes/abilities/shared/diff-helpers.php index 9b6abb5..a61dd14 100644 --- a/includes/abilities/shared/diff-helpers.php +++ b/includes/abilities/shared/diff-helpers.php @@ -5,7 +5,7 @@ * Common diff functionality used across security abilities. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -59,14 +59,14 @@ function agentic_admin_get_remote_file_diff( string $original_url, string $file_ function agentic_admin_generate_unified_diff( array $old_lines, array $new_lines, string $old_label, string $new_label ): string { // Use WordPress built-in Text_Diff if available. if ( ! class_exists( 'Text_Diff', false ) ) { - $diff_file = ABSPATH . 'wp-includes/Text/Diff.php'; + $diff_file = ABSPATH . WPINC . '/Text/Diff.php'; if ( file_exists( $diff_file ) ) { require_once $diff_file; } } if ( ! class_exists( 'Text_Diff_Renderer_unified', false ) ) { - $renderer_file = ABSPATH . 'wp-includes/Text/Diff/Renderer/unified.php'; + $renderer_file = ABSPATH . WPINC . '/Text/Diff/Renderer/unified.php'; if ( file_exists( $renderer_file ) ) { require_once $renderer_file; } diff --git a/includes/abilities/shared/plugin-helpers.php b/includes/abilities/shared/plugin-helpers.php index 37dd3bb..fdd59a0 100644 --- a/includes/abilities/shared/plugin-helpers.php +++ b/includes/abilities/shared/plugin-helpers.php @@ -5,7 +5,7 @@ * Common functionality used across plugin-related abilities to avoid code duplication. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -58,14 +58,14 @@ function agentic_admin_get_all_plugins( string $status_filter = 'all' ): array { $actions[] = array( 'label' => $plugin['name'], 'button_label' => __( 'Deactivate', 'agentic-admin' ), - 'action' => 'wp-agentic-admin/plugin-deactivate', + 'action' => 'agentic-admin/plugin-deactivate', 'args' => array( 'plugin' => $plugin['slug'] ), ); } else { $actions[] = array( 'label' => $plugin['name'], 'button_label' => __( 'Activate', 'agentic-admin' ), - 'action' => 'wp-agentic-admin/plugin-activate', + 'action' => 'agentic-admin/plugin-activate', 'args' => array( 'plugin' => $plugin['slug'] ), ); } @@ -303,7 +303,7 @@ function agentic_admin_activate_plugin_by_slug( string $plugin_identifier ): arr return agentic_admin_build_candidate_response( $resolved, $plugin_identifier, - 'wp-agentic-admin/plugin-activate', + 'agentic-admin/plugin-activate', __( 'Activate', 'agentic-admin' ) ); } @@ -403,7 +403,7 @@ function agentic_admin_deactivate_plugin_by_slug( string $plugin_identifier ): a return agentic_admin_build_candidate_response( $resolved, $plugin_identifier, - 'wp-agentic-admin/plugin-deactivate', + 'agentic-admin/plugin-deactivate', __( 'Deactivate', 'agentic-admin' ) ); } @@ -463,7 +463,7 @@ function agentic_admin_deactivate_plugin_by_slug( string $plugin_identifier ): a * * @param array $resolved Result from agentic_admin_resolve_plugin(). * @param string $original_input The user's original input string. - * @param string $action_id The ability action ID (e.g. "wp-agentic-admin/plugin-activate"). + * @param string $action_id The ability action ID (e.g. "agentic-admin/plugin-activate"). * @param string $button_label Label for the action button (e.g. "Activate"). * @return array Response with actions for UI buttons. */ diff --git a/includes/abilities/site-health.php b/includes/abilities/site-health.php index 81955e6..2dacb2b 100644 --- a/includes/abilities/site-health.php +++ b/includes/abilities/site-health.php @@ -5,7 +5,7 @@ * Gets comprehensive site health information. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_site_health(): void { agentic_admin_register_ability( - 'wp-agentic-admin/site-health', + 'agentic-admin/site-health', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Site Health', 'agentic-admin' ), diff --git a/includes/abilities/theme-list.php b/includes/abilities/theme-list.php index e1e5dae..baae72a 100644 --- a/includes/abilities/theme-list.php +++ b/includes/abilities/theme-list.php @@ -5,7 +5,7 @@ * Lists all installed themes with their status. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_theme_list(): void { agentic_admin_register_ability( - 'wp-agentic-admin/theme-list', + 'agentic-admin/theme-list', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'List Themes', 'agentic-admin' ), diff --git a/includes/abilities/transient-flush.php b/includes/abilities/transient-flush.php index 4e301e9..997f93b 100644 --- a/includes/abilities/transient-flush.php +++ b/includes/abilities/transient-flush.php @@ -6,7 +6,7 @@ * Similar to WP-CLI: wp transient delete --all / --expired * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin * @since 0.1.0 */ @@ -21,7 +21,7 @@ */ function agentic_admin_register_transient_flush(): void { agentic_admin_register_ability( - 'wp-agentic-admin/transient-flush', + 'agentic-admin/transient-flush', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Flush Transients', 'agentic-admin' ), diff --git a/includes/abilities/update-check.php b/includes/abilities/update-check.php index f17c52d..356c251 100644 --- a/includes/abilities/update-check.php +++ b/includes/abilities/update-check.php @@ -5,7 +5,7 @@ * Checks for available WordPress core, plugin, and theme updates. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_update_check(): void { agentic_admin_register_ability( - 'wp-agentic-admin/update-check', + 'agentic-admin/update-check', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Check Updates', 'agentic-admin' ), diff --git a/includes/abilities/user-list.php b/includes/abilities/user-list.php index 01dd748..0f9ed34 100644 --- a/includes/abilities/user-list.php +++ b/includes/abilities/user-list.php @@ -5,7 +5,7 @@ * Lists all WordPress users with their roles. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_user_list(): void { agentic_admin_register_ability( - 'wp-agentic-admin/user-list', + 'agentic-admin/user-list', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'List Users', 'agentic-admin' ), diff --git a/includes/abilities/web-search.php b/includes/abilities/web-search.php index e534cf5..a73d514 100644 --- a/includes/abilities/web-search.php +++ b/includes/abilities/web-search.php @@ -5,7 +5,7 @@ * Searches the web for documentation and troubleshooting. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -19,7 +19,7 @@ */ function agentic_admin_register_web_search(): void { agentic_admin_register_ability( - 'wp-agentic-admin/web-search', + 'agentic-admin/web-search', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Web Search', 'agentic-admin' ), diff --git a/includes/abilities/wp-api-extract.php b/includes/abilities/wp-api-extract.php index 9e8b2f4..3b51fbd 100644 --- a/includes/abilities/wp-api-extract.php +++ b/includes/abilities/wp-api-extract.php @@ -7,7 +7,7 @@ * not function bodies, to keep chunk count manageable. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ if ( ! defined( 'ABSPATH' ) ) { @@ -21,7 +21,7 @@ */ function agentic_admin_register_wp_api_extract(): void { agentic_admin_register_ability( - 'wp-agentic-admin/wp-api-extract', + 'agentic-admin/wp-api-extract', // PHP configuration for WordPress Abilities API. array( 'label' => __( 'Extract WP API Signatures', 'agentic-admin' ), @@ -73,7 +73,7 @@ function agentic_admin_register_wp_api_extract(): void { * @return string[] Array of absolute file paths. */ function agentic_admin_collect_wp_includes_files(): array { - $wp_includes = ABSPATH . 'wp-includes/'; + $wp_includes = ABSPATH . WPINC . '/'; if ( ! is_dir( $wp_includes ) ) { return array(); @@ -268,7 +268,7 @@ function agentic_admin_execute_wp_api_extract( array $input = array() ): array { $files = agentic_admin_collect_wp_includes_files(); $total_files = count( $files ); $chunks = array(); - $wp_includes = ABSPATH . 'wp-includes/'; + $wp_includes = ABSPATH . WPINC . '/'; foreach ( $files as $file_path ) { $relative_path = str_replace( $wp_includes, '', $file_path ); diff --git a/includes/abilities/write-file.php b/includes/abilities/write-file.php deleted file mode 100644 index 9036d03..0000000 --- a/includes/abilities/write-file.php +++ /dev/null @@ -1,173 +0,0 @@ - __( 'Write File', 'agentic-admin' ), - 'description' => __( 'Edit WordPress files with automatic backup.', 'agentic-admin' ), - 'category' => 'sre-tools', - 'input_schema' => array( - 'type' => 'object', - 'default' => array( - 'file_path' => '', - 'content' => '', - 'mode' => 'replace', - ), - 'properties' => array( - 'file_path' => array( - 'type' => 'string', - 'description' => __( 'File path relative to WordPress root.', 'agentic-admin' ), - ), - 'content' => array( - 'type' => 'string', - 'description' => __( 'Content to write.', 'agentic-admin' ), - ), - 'mode' => array( - 'type' => 'string', - 'enum' => array( 'replace', 'append', 'prepend' ), - 'default' => 'replace', - 'description' => __( 'Write mode: replace, append, or prepend.', 'agentic-admin' ), - ), - ), - 'required' => array( 'file_path', 'content' ), - 'additionalProperties' => false, - ), - 'output_schema' => array( - 'type' => 'object', - 'properties' => array( - 'success' => array( - 'type' => 'boolean', - 'description' => __( 'Whether the write succeeded.', 'agentic-admin' ), - ), - 'message' => array( - 'type' => 'string', - 'description' => __( 'Status message.', 'agentic-admin' ), - ), - 'backup' => array( - 'type' => 'string', - 'description' => __( 'Backup file path.', 'agentic-admin' ), - ), - ), - ), - 'execute_callback' => 'agentic_admin_execute_write_file', - 'permission_callback' => function () { - return current_user_can( 'manage_options' ); - }, - 'meta' => array( - 'show_in_rest' => true, - 'annotations' => array( - 'readonly' => false, - 'destructive' => false, - 'idempotent' => false, - ), - ), - ), - // JS configuration for chat interface. - array( - 'keywords' => array( 'write', 'edit', 'modify', 'change', 'fix', 'add line', 'add code', 'enable debug', 'update file', 'functions.php', 'wp-config', '.htaccess' ), - 'initialMessage' => __( "I'll edit that file...", 'agentic-admin' ), - ) - ); -} - -/** - * Execute the write-file ability. - * - * @param array $input Input parameters. - * @return array - */ -function agentic_admin_execute_write_file( array $input = array() ): array { - $file_path = isset( $input['file_path'] ) ? sanitize_text_field( $input['file_path'] ) : ''; - $content = isset( $input['content'] ) ? $input['content'] : ''; - $mode = isset( $input['mode'] ) ? sanitize_text_field( $input['mode'] ) : 'replace'; - - if ( empty( $file_path ) || '' === $content ) { - return array( - 'success' => false, - 'message' => 'File path and content are required.', - ); - } - - // Resolve to absolute path. - $abs_path = realpath( ABSPATH . ltrim( $file_path, '/' ) ); - - // For new files, check parent directory. - if ( false === $abs_path ) { - $parent = realpath( dirname( ABSPATH . ltrim( $file_path, '/' ) ) ); - if ( false === $parent || 0 !== strpos( $parent, realpath( ABSPATH ) ) ) { - return array( - 'success' => false, - 'message' => 'File path is outside the WordPress directory.', - ); - } - $abs_path = $parent . '/' . basename( $file_path ); - } elseif ( 0 !== strpos( $abs_path, realpath( ABSPATH ) ) ) { - return array( - 'success' => false, - 'message' => 'File path is outside the WordPress directory.', - ); - } - - // Create backup if file exists. - $backup_path = ''; - if ( file_exists( $abs_path ) ) { - $backup_path = $abs_path . '.bak.' . time(); - if ( ! copy( $abs_path, $backup_path ) ) { - return array( - 'success' => false, - 'message' => 'Failed to create backup.', - ); - } - } - - // Apply content based on mode. - $existing = file_exists( $abs_path ) ? file_get_contents( $abs_path ) : ''; - - switch ( $mode ) { - case 'append': - $final = $existing . "\n" . $content; - break; - case 'prepend': - $final = $content . "\n" . $existing; - break; - default: - $final = $content; - } - - // Write the file. - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents - $result = file_put_contents( $abs_path, $final ); - - if ( false === $result ) { - return array( - 'success' => false, - 'message' => 'Failed to write file.', - 'backup' => $backup_path, - ); - } - - return array( - 'success' => true, - 'message' => sprintf( 'File %s successfully (%s mode).', $file_path, $mode ), - 'backup' => $backup_path ? str_replace( realpath( ABSPATH ), '', $backup_path ) : '', - ); -} diff --git a/includes/class-abilities.php b/includes/class-abilities.php index 30e6dfd..a9a30a8 100644 --- a/includes/class-abilities.php +++ b/includes/class-abilities.php @@ -7,10 +7,10 @@ * the 'agentic_admin_register_abilities' action. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ -namespace WPAgenticAdmin; +namespace AgenticAdmin; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -79,7 +79,7 @@ public function register_category(): void { * Load all ability files from the abilities directory. */ private function load_ability_files(): void { - $abilities_dir = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities/'; + $abilities_dir = AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities/'; // Check if directory exists. if ( ! is_dir( $abilities_dir ) ) { @@ -128,7 +128,7 @@ private function load_ability_files(): void { * to register their own abilities. */ public function register_core_abilities(): void { - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities-manifest.php'; + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities-manifest.php'; foreach ( agentic_admin_resolve_enabled_abilities() as $slug => $register_fn ) { if ( function_exists( $register_fn ) ) { @@ -138,7 +138,7 @@ public function register_core_abilities(): void { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error \trigger_error( sprintf( - 'wp-agentic-admin: enabled ability "%s" has no registrar function %s().', + 'agentic-admin: enabled ability "%s" has no registrar function %s().', \esc_html( $slug ), \esc_html( $register_fn ) ), diff --git a/includes/class-admin-page.php b/includes/class-admin-page.php index fb51617..b315983 100644 --- a/includes/class-admin-page.php +++ b/includes/class-admin-page.php @@ -5,10 +5,10 @@ * Handles the main Agentic Admin page in WP-Admin. * * @license GPL-2.0-or-later - * @package WPAgenticAdmin + * @package AgenticAdmin */ -namespace WPAgenticAdmin; +namespace AgenticAdmin; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -48,12 +48,46 @@ public static function get_instance(): Admin_Page { public function __construct() { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); + add_action( 'admin_post_agentic_admin_sw', array( $this, 'serve_service_worker' ) ); + add_action( 'admin_post_nopriv_agentic_admin_sw', array( $this, 'serve_service_worker' ) ); + } + + /** + * Serve the Service Worker through WordPress. + * + * The browser fetches the SW via admin-post.php so WordPress is fully + * loaded — there is no direct plugin-file access. We send the + * Service-Worker-Allowed header so the SW registered from + * '/wp-admin/admin-post.php' may control the '/wp-admin/' scope, then + * stream the compiled sw.js. + * + * @return void + */ + public function serve_service_worker(): void { + $sw_file = AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/sw.js'; + + if ( ! file_exists( $sw_file ) ) { + status_header( 404 ); + exit; + } + + header( 'Content-Type: application/javascript; charset=utf-8' ); + header( 'Service-Worker-Allowed: /wp-admin/' ); + header( 'Cache-Control: no-cache, no-store, must-revalidate' ); + + // Stream the compiled, static asset. WP_Filesystem is unnecessary for a + // hard-coded, plugin-local path with no user input or traversal vector. + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile + readfile( $sw_file ); + exit; } /** * Add the top-level admin menu. */ public function add_admin_menu(): void { + // Appended to the bottom of the menu (position null) so it integrates + // alongside other plugins rather than competing with core items. add_menu_page( __( 'Agentic Admin', 'agentic-admin' ), __( 'Agentic Admin', 'agentic-admin' ), @@ -61,7 +95,7 @@ public function add_admin_menu(): void { 'agentic-admin', array( $this, 'render_page' ), 'dashicons-superhero-alt', - 3 + null ); } @@ -76,7 +110,7 @@ public function enqueue_scripts( string $hook ): void { return; } - $asset_file = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.asset.php'; + $asset_file = AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.asset.php'; if ( ! file_exists( $asset_file ) ) { return; @@ -84,17 +118,17 @@ public function enqueue_scripts( string $hook ): void { $asset = require $asset_file; $deps = isset( $asset['dependencies'] ) ? (array) $asset['dependencies'] : array( 'wp-element' ); - $ver = isset( $asset['version'] ) ? $asset['version'] : WP_AGENTIC_ADMIN_VERSION; + $ver = isset( $asset['version'] ) ? $asset['version'] : AGENTIC_ADMIN_VERSION; // Enqueue WordPress components styles. wp_enqueue_style( 'wp-components' ); // Enqueue our styles. - $css_file = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.css'; + $css_file = AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.css'; if ( file_exists( $css_file ) ) { wp_enqueue_style( - 'wp-agentic-admin-style', - WP_AGENTIC_ADMIN_PLUGIN_URL . 'build-extensions/index.css', + 'agentic-admin-style', + AGENTIC_ADMIN_PLUGIN_URL . 'build-extensions/index.css', array( 'wp-components' ), filemtime( $css_file ) ); @@ -103,7 +137,7 @@ public function enqueue_scripts( string $hook ): void { // Register and enqueue our script. wp_register_script( 'agentic-admin', - WP_AGENTIC_ADMIN_PLUGIN_URL . 'build-extensions/index.js', + AGENTIC_ADMIN_PLUGIN_URL . 'build-extensions/index.js', $deps, $ver, true @@ -112,7 +146,7 @@ public function enqueue_scripts( string $hook ): void { // Localize script with data. wp_localize_script( 'agentic-admin', - 'wpAgenticAdmin', + 'agenticAdmin', self::get_localized_data() ); @@ -144,29 +178,29 @@ public static function get_localized_data(): array { $abilities_js_config = agentic_admin_get_abilities_js_config(); } - // Resolve the PHP-side enabled abilities (CORE + LOCAL_ONLY + LABS-if-enabled), - // then expose to JS so the manifest can mirror the PHP gate. - require_once WP_AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities-manifest.php'; + // Resolve the enabled abilities (CORE + LOCAL_ONLY) and expose to JS + // so the manifest mirrors the PHP-authoritative list. + require_once AGENTIC_ADMIN_PLUGIN_DIR . 'includes/abilities-manifest.php'; $enabled_abilities = array_keys( agentic_admin_resolve_enabled_abilities() ); // Asset versions exposed to JS so bug reports / copied transcripts can show // which bundle the user was running (helps spot stale-cache reports). - $js_asset_file = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.asset.php'; + $js_asset_file = AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.asset.php'; $js_version = file_exists( $js_asset_file ) ? ( ( require $js_asset_file )['version'] ?? '' ) : ''; - $css_file = WP_AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.css'; + $css_file = AGENTIC_ADMIN_PLUGIN_DIR . 'build-extensions/index.css'; $css_version = file_exists( $css_file ) ? (string) filemtime( $css_file ) : ''; return array( 'restUrl' => esc_url_raw( rest_url( 'wp-abilities/v1' ) ), 'restRoot' => esc_url_raw( rest_url() ), - 'connectorsRestUrl' => esc_url_raw( rest_url( 'wp-agentic-admin/v1/connectors' ) ), + 'connectorsRestUrl' => esc_url_raw( rest_url( 'agentic-admin/v1/connectors' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'userId' => get_current_user_id(), - 'pluginUrl' => esc_url( WP_AGENTIC_ADMIN_PLUGIN_URL ), - 'swUrl' => esc_url( WP_AGENTIC_ADMIN_PLUGIN_URL . 'sw-loader.php' ), - 'version' => WP_AGENTIC_ADMIN_VERSION, + 'pluginUrl' => esc_url( AGENTIC_ADMIN_PLUGIN_URL ), + 'swUrl' => esc_url( admin_url( 'admin-post.php?action=agentic_admin_sw' ) ), + 'version' => AGENTIC_ADMIN_VERSION, 'jsVersion' => $js_version, 'cssVersion' => $css_version, 'hasPrettyPermalinks' => self::has_pretty_permalinks(), @@ -179,7 +213,6 @@ public static function get_localized_data(): array { 'browserRequirements' => Utils::get_browser_requirements(), 'abilities' => $abilities_js_config, 'enabledAbilities' => $enabled_abilities, - 'enableLabs' => agentic_admin_labs_enabled(), 'i18n' => array( 'loading' => __( 'Loading AI model...', 'agentic-admin' ), 'ready' => __( 'AI assistant ready', 'agentic-admin' ), @@ -207,7 +240,7 @@ public function render_page(): void { ?>

-
+