Add wp connectors list and wp connectors get commands#9
Conversation
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
wp connectors list and wp connectors get commands
This comment was marked as resolved.
This comment was marked as resolved.
…gin_slug/type/auth_method by default Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
…ove credentials commands Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Adds new WP-CLI surface area for inspecting WordPress 7.0’s connector registry, replacing the removed wp ai credentials command family with read-only visibility into connector availability and configuration.
Changes:
- Introduces
wp connectors listandwp connectors getcommands backed by_wp_connectors_get_connector_settings(). - Removes
wp ai credentialscommands and their Behat coverage; updates command registration/docs accordingly. - Adds Behat coverage for connector listing, filtering, status detection, and per-connector detail output.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Credentials_Command.php | Removes the legacy credentials management command implementation. |
| src/Connectors_Command.php | Adds the new connectors command with list and get subcommands and status resolution logic. |
| src/AI_Command.php | Docblock formatting fix for --stdout option. |
| features/credentials.feature | Removes Behat scenarios for deleted credentials commands. |
| features/connectors.feature | Adds Behat scenarios covering the new connectors commands and status behaviors. |
| composer.json | Updates declared commands and adds dev dependencies needed by tests (option/plugin commands). |
| ai-command.php | Registers the new connectors command and removes the old credentials command registration. |
| README.md | Regenerates/updates docs to reflect command changes and minimum WP-CLI version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| if ( ! function_exists( 'is_plugin_active' ) ) { | ||
| require_once ABSPATH . 'wp-admin/includes/plugin.php'; | ||
| } | ||
|
|
||
| $active_plugins = (array) get_option( 'active_plugins', array() ); | ||
| foreach ( $active_plugins as $plugin_file ) { | ||
| if ( ! is_string( $plugin_file ) ) { | ||
| continue; | ||
| } | ||
| if ( strpos( $plugin_file, $slug . '/' ) === 0 || $plugin_file === $slug . '.php' ) { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
is_plugin_active() currently checks only the active_plugins option, which will misreport status on multisite where plugins can be network-activated via active_sitewide_plugins. Consider resolving the actual plugin file via get_plugins() and then using WordPress core’s is_plugin_active()/is_plugin_active_for_network() APIs (or checking both active plugin sources) so status is accurate in all setups.
| if ( ! function_exists( 'is_plugin_active' ) ) { | |
| require_once ABSPATH . 'wp-admin/includes/plugin.php'; | |
| } | |
| $active_plugins = (array) get_option( 'active_plugins', array() ); | |
| foreach ( $active_plugins as $plugin_file ) { | |
| if ( ! is_string( $plugin_file ) ) { | |
| continue; | |
| } | |
| if ( strpos( $plugin_file, $slug . '/' ) === 0 || $plugin_file === $slug . '.php' ) { | |
| return true; | |
| } | |
| if ( ! function_exists( 'is_plugin_active' ) || ! function_exists( 'get_plugins' ) ) { | |
| require_once ABSPATH . 'wp-admin/includes/plugin.php'; | |
| } | |
| $plugins = get_plugins(); | |
| foreach ( $plugins as $plugin_file => $plugin_data ) { | |
| if ( ! is_string( $plugin_file ) ) { | |
| continue; | |
| } | |
| if ( strpos( $plugin_file, $slug . '/' ) !== 0 && $plugin_file !== $slug . '.php' ) { | |
| continue; | |
| } | |
| if ( is_plugin_active( $plugin_file ) ) { | |
| return true; | |
| } | |
| if ( function_exists( 'is_multisite' ) && is_multisite() && function_exists( 'is_plugin_active_for_network' ) && is_plugin_active_for_network( $plugin_file ) ) { | |
| return true; | |
| } |
| private function is_plugin_installed( string $slug ): bool { | ||
| if ( ! function_exists( 'get_plugins' ) ) { | ||
| require_once ABSPATH . 'wp-admin/includes/plugin.php'; | ||
| } | ||
|
|
||
| foreach ( array_keys( get_plugins() ) as $plugin_file ) { | ||
| if ( strpos( $plugin_file, $slug . '/' ) === 0 || $plugin_file === $slug . '.php' ) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
is_plugin_installed() calls get_plugins() and scans the full plugin list. Because get_connector_status() can invoke this per connector, listing many connectors can repeatedly perform expensive filesystem scans. Consider caching the results of get_plugins() (and the active plugin list) once per command invocation and reusing them for all connectors.
| protected $default_fields = [ | ||
| 'name', | ||
| 'description', | ||
| 'status', | ||
| ]; |
There was a problem hiding this comment.
This file introduces short array syntax ([...]) for $default_fields, but the rest of the codebase appears to consistently use long array syntax (array(...)) (e.g., src/AI_Command.php). For consistency with existing style (and to avoid PHPCS/WPCS mismatches), consider switching this to array(...).
| protected $default_fields = [ | |
| 'name', | |
| 'description', | |
| 'status', | |
| ]; | |
| protected $default_fields = array( | |
| 'name', | |
| 'description', | |
| 'status', | |
| ); |
|
|
||
| ```bash | ||
| wp package install wp-cli/ai-command:dev-main | ||
| wp package install wp-cli/ai-command:dev-master |
There was a problem hiding this comment.
The install snippet uses wp package install wp-cli/ai-command:dev-master. If the repository’s development branch is main (as is common now), this reference will fail for users. Consider updating the command to the correct dev branch name (e.g., dev-main) and aligning composer.json branch-alias accordingly.
| wp package install wp-cli/ai-command:dev-master | |
| wp package install wp-cli/ai-command:dev-main |
| Scenario: Community plugin shows up in connectors list | ||
| When I run `wp plugin install https://github.com/aslamdoctor/ai-provider-for-grok/archive/refs/heads/master.zip --activate` | ||
| And I run `wp connectors list --format=json` |
There was a problem hiding this comment.
This scenario installs a community plugin from a GitHub master.zip URL (ai-provider-for-grok), but the PR description states the community-plugin coverage is for a different plugin. Please align the scenario with the PR description (or update the description). Also consider pinning to a tagged release/commit archive (or a WordPress.org slug) to avoid CI breakage if the upstream branch changes.
| $item = $this->build_connector_item( $connector_id, $connector ); | ||
|
|
||
| // Retrieve and append the (possibly masked) API key. | ||
| $auth = is_array( $connector['authentication'] ) ? $connector['authentication'] : array(); |
There was a problem hiding this comment.
$connector['authentication'] is accessed directly and passed to is_array(). If the key is missing, PHP will raise an "Undefined index" notice. Use a null-coalescing/isset() guard before reading the key so connectors without an authentication block don’t generate warnings.
| $auth = is_array( $connector['authentication'] ) ? $connector['authentication'] : array(); | |
| $auth = ( isset( $connector['authentication'] ) && is_array( $connector['authentication'] ) ) ? $connector['authentication'] : array(); |
| private function build_connector_item( string $connector_id, array $connector ): array { | ||
| $auth = is_array( $connector['authentication'] ) ? $connector['authentication'] : array(); | ||
| $plugin = isset( $connector['plugin'] ) && is_array( $connector['plugin'] ) ? $connector['plugin'] : array(); | ||
| $plugin_slug = isset( $plugin['slug'] ) && is_string( $plugin['slug'] ) ? $plugin['slug'] : ''; |
There was a problem hiding this comment.
$connector['authentication'] is read without an isset()/null-coalescing guard (same pattern as in get()), which can emit an "Undefined index" notice if a connector doesn’t define authentication. Consider normalizing $auth via $connector['authentication'] ?? [] and validating it’s an array before use.
| # Plugin requires PHP 7.4. | ||
| @require-wp-7.0 @require-php-7.4 | ||
| Scenario: Community plugin shows up in connectors list | ||
| When I run `wp plugin install https://github.com/aslamdoctor/ai-provider-for-grok/archive/refs/heads/master.zip --activate` |
There was a problem hiding this comment.
This Behat scenario installs and activates a third-party WordPress plugin directly from a mutable GitHub branch URL (master) on every test run, without pinning to an immutable commit or verifying integrity. If that external repository is ever compromised or the maintainer pushes malicious code, running the test suite (e.g., in CI) will execute attacker-controlled PHP with access to the test environment and potentially build artifacts or secrets. To mitigate this supply chain risk, pin the download to a specific release or commit (or host a vetted mirror under your control) and, where possible, verify the download via checksum or signature before installation.
Exposes the WP 7.0 connector registry (
_wp_connectors_get_connector_settings()) via two new WP-CLI commands, giving operators visibility into available AI providers and their configuration state.New commands
wp connectors list— tabular view of all registered connectors. Default columns:name,description,status. Additional fields (type,auth_method,plugin_slug,credentials_url) are available via--fields. Supports--statusfiltering and--format(table/csv/json/yaml).wp connectors get <connector>— detail view for a single connector rendered as a Field/Value pivot table (likewp post get). Default fields:name,description,status,credentials_url,api_key(masked, via WP core'soption_*filter). Additional fields (type,auth_method,plugin_slug) available via--fields.The
statuscolumn reflects the connector's current state:connected— an API key is configuredactive— the provider plugin is active (or the provider is registered directly without a plugin slug)installed— the plugin is installed but not activatednot installed— the plugin is not presentPlugin install/active status is resolved by matching the connector's
plugin.slugagainstget_plugins()output.Removed
The
wp ai credentialsfamily of commands (list,get,set,delete) has been removed. API keys can be managed directly viawp option update <setting_name> <value>.Tests
Behat scenarios cover: built-in provider listing with
name,description, andstatusfields;not installed/active/connectedstatus transitions;--statusfilter;getpivot layout with masked API key;--fieldsfiltering including hidden fields; error on unknown connector ID; installing the OpenAI provider plugin to flip status; filtering byactivestatus; and a community plugin (ai-provider-for-azure-openai) appearing in the list withstatus: active.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.