From 443d8a229646e8a2379b2a14743097a77af00640 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Thu, 9 Jul 2026 19:41:20 +0530
Subject: [PATCH 1/7] feat: integrate Plugin Check Namer as standard AI Name
Check static check
---
assets/js/plugin-check-namer.js | 432 ----------------
includes/Admin/Namer_Page.php | 460 ------------------
includes/Checker/Abstract_Check_Runner.php | 13 +-
.../Checks/Plugin_Repo/AI_Name_Check.php | 179 +++++++
includes/Checker/Default_Check_Repository.php | 1 +
includes/Plugin_Main.php | 5 -
.../Checker/Checks/AI_Name_Check_Tests.php | 33 ++
7 files changed, 225 insertions(+), 898 deletions(-)
delete mode 100644 assets/js/plugin-check-namer.js
delete mode 100644 includes/Admin/Namer_Page.php
create mode 100644 includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
create mode 100644 tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
diff --git a/assets/js/plugin-check-namer.js b/assets/js/plugin-check-namer.js
deleted file mode 100644
index f8e3d90cb..000000000
--- a/assets/js/plugin-check-namer.js
+++ /dev/null
@@ -1,432 +0,0 @@
-/**
- * Plugin Check Namer tool.
- */
-
-/* global pluginCheckNamer */
-
-( function () {
- 'use strict';
-
- function setText( el, text ) {
- if ( ! el ) {
- return;
- }
- el.textContent = text;
- }
-
- function setHtml( el, html ) {
- if ( ! el ) {
- return;
- }
- el.innerHTML = html;
- }
-
- function escapeHtml( text ) {
- const div = document.createElement( 'div' );
- div.textContent = text;
- return div.innerHTML;
- }
-
- document.addEventListener( 'DOMContentLoaded', function () {
- const form = document.getElementById( 'plugin-check-namer-form' );
- const input = document.getElementById( 'plugin_check_namer_input' );
-
- if ( ! form || ! input || ! window.pluginCheckNamer ) {
- return;
- }
-
- const authorInput = document.getElementById(
- 'plugin_check_namer_author'
- );
- const modelSelect = document.getElementById(
- 'plugin_check_namer_model'
- );
-
- const submitBtn = document.getElementById(
- 'plugin-check-namer-submit'
- );
- const spinner = document.getElementById( 'plugin-check-namer-spinner' );
-
- const resultWrap = document.getElementById(
- 'plugin-check-namer-result'
- );
- const verdictEl = document.getElementById(
- 'plugin-check-namer-verdict'
- );
- const explainEl = document.getElementById(
- 'plugin-check-namer-explanation'
- );
- const verdictContainer = document.getElementById(
- 'plugin-check-namer-verdict-container'
- );
- const confusionPluginsDiv = document.getElementById(
- 'plugin-check-namer-confusion-plugins'
- );
- const confusionPluginsList = document.getElementById(
- 'plugin-check-namer-confusion-plugins-list'
- );
- const confusionOthersDiv = document.getElementById(
- 'plugin-check-namer-confusion-others'
- );
- const confusionOthersList = document.getElementById(
- 'plugin-check-namer-confusion-others-list'
- );
- const timingDiv = document.getElementById(
- 'plugin-check-namer-timing'
- );
- const timingValue = document.getElementById(
- 'plugin-check-namer-timing-value'
- );
- const tokensDiv = document.getElementById(
- 'plugin-check-namer-tokens'
- );
- const tokensValue = document.getElementById(
- 'plugin-check-namer-tokens-value'
- );
- const errorDiv = document.getElementById( 'plugin-check-namer-error' );
- const errorEl = errorDiv ? errorDiv.querySelector( 'p' ) : null;
-
- function setLoading( isLoading ) {
- if ( spinner ) {
- spinner.classList.toggle( 'is-active', isLoading );
- }
- submitBtn.disabled = isLoading;
- }
-
- form.addEventListener( 'submit', function ( event ) {
- event.preventDefault();
-
- const name = ( input.value || '' ).trim();
- if ( ! name ) {
- setText( errorEl, pluginCheckNamer.messages.missingName );
- if ( errorEl ) {
- errorEl.classList.remove( 'plugin-check-namer-hidden' );
- }
- return;
- }
-
- // Clear previous results.
- if ( resultWrap ) {
- resultWrap.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( verdictEl ) {
- setText( verdictEl, '' );
- }
- if ( explainEl ) {
- setHtml( explainEl, '' );
- }
- if ( errorDiv ) {
- errorDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( errorEl ) {
- setText( errorEl, '' );
- }
- if ( confusionPluginsDiv ) {
- confusionPluginsDiv.classList.add(
- 'plugin-check-namer-hidden'
- );
- }
- if ( confusionPluginsList ) {
- confusionPluginsList.innerHTML = '';
- }
- if ( confusionOthersDiv ) {
- confusionOthersDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( confusionOthersList ) {
- confusionOthersList.innerHTML = '';
- }
- if ( timingDiv ) {
- timingDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( timingValue ) {
- setText( timingValue, '' );
- }
- if ( tokensDiv ) {
- tokensDiv.classList.add( 'plugin-check-namer-hidden' );
- }
- if ( tokensValue ) {
- setText( tokensValue, '' );
- }
- if ( verdictContainer ) {
- verdictContainer.classList.add( 'plugin-check-namer-hidden' );
- }
-
- // Record start time.
- const startTime = Date.now();
-
- setLoading( true );
-
- const formData = new FormData();
- formData.append( 'action', 'plugin_check_namer_analyze' );
- formData.append( 'nonce', pluginCheckNamer.nonce );
- formData.append( 'plugin_name', name );
- if ( authorInput ) {
- formData.append(
- 'author_name',
- ( authorInput.value || '' ).trim()
- );
- }
- if ( modelSelect ) {
- formData.append(
- 'model_preference',
- ( modelSelect.value || '' ).trim()
- );
- }
-
- fetch( pluginCheckNamer.ajaxUrl, {
- method: 'POST',
- credentials: 'same-origin',
- body: formData,
- } )
- .then( function ( response ) {
- return response.json();
- } )
- .then( function ( payload ) {
- if ( ! payload || ! payload.success ) {
- throw new Error(
- payload && payload.data && payload.data.message
- ? payload.data.message
- : pluginCheckNamer.messages.genericError
- );
- }
-
- setText( verdictEl, payload.data.verdict || '' );
- setHtml( explainEl, payload.data.explanation || '' );
-
- // Set border color based on verdict.
- if ( verdictContainer ) {
- const verdict = (
- payload.data.verdict || ''
- ).toLowerCase();
- let borderColor = '#2271b1'; // Default blue.
-
- if ( verdict.indexOf( 'disallowed' ) !== -1 ) {
- borderColor = '#d63638'; // Red for disallowed.
- } else if (
- verdict.indexOf( 'good' ) !== -1 ||
- verdict.indexOf( 'low' ) !== -1 ||
- verdict.indexOf( 'no issues' ) !== -1
- ) {
- borderColor = '#00a32a'; // Green for good.
- } else if (
- verdict.indexOf( 'generally allowable' ) !== -1 ||
- verdict.indexOf( 'allowable' ) !== -1
- ) {
- borderColor = '#2271b1'; // Blue for generally allowable.
- } else if (
- verdict.indexOf( 'review' ) !== -1 ||
- verdict.indexOf( 'medium' ) !== -1 ||
- verdict.indexOf( 'issues found' ) !== -1
- ) {
- borderColor = '#dba617'; // Yellow/orange for needs review.
- } else if (
- verdict.indexOf( 'problematic' ) !== -1 ||
- verdict.indexOf( 'high' ) !== -1
- ) {
- borderColor = '#d63638'; // Red for problematic.
- }
-
- verdictContainer.style.borderLeftColor = borderColor;
- verdictContainer.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Calculate and display elapsed time.
- if ( timingDiv && timingValue ) {
- const endTime = Date.now();
- const elapsedSeconds = Math.round(
- ( endTime - startTime ) / 1000
- );
- timingValue.textContent =
- elapsedSeconds + ' ' + 'seconds';
- timingDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Display token usage if available.
- if (
- tokensDiv &&
- tokensValue &&
- payload.data.token_usage
- ) {
- const tokenUsage = payload.data.token_usage;
- let tokensText = '';
-
- // Add AI provider and model info if available.
- if ( payload.data.ai_info ) {
- const parts = [];
- if ( payload.data.ai_info.provider ) {
- parts.push( payload.data.ai_info.provider );
- }
- if ( payload.data.ai_info.model ) {
- parts.push( payload.data.ai_info.model );
- }
- if ( parts.length ) {
- tokensText = parts.join( ' / ' ) + ' - ';
- }
- }
-
- const totalTokens =
- tokenUsage.total_tokens ||
- ( tokenUsage.prompt_tokens &&
- tokenUsage.completion_tokens
- ? tokenUsage.prompt_tokens +
- tokenUsage.completion_tokens
- : null );
-
- if ( totalTokens ) {
- tokensText += totalTokens + ' total';
- }
-
- // Add breakdown with prompt and completion tokens.
- if (
- tokenUsage.prompt_tokens &&
- tokenUsage.completion_tokens
- ) {
- tokensText +=
- ' (' +
- tokenUsage.prompt_tokens +
- ' prompt + ' +
- tokenUsage.completion_tokens +
- ' completion)';
- }
-
- // Add similar name query tokens if available.
- if (
- tokenUsage.similar_name &&
- tokenUsage.similar_name.total_tokens
- ) {
- tokensText +=
- ' [Similar name query: ' +
- tokenUsage.similar_name.total_tokens +
- ' tokens]';
- }
-
- tokensValue.textContent = tokensText;
- tokensDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Display confusion_existing_plugins if available.
- if (
- confusionPluginsDiv &&
- confusionPluginsList &&
- payload.data.confusion_existing_plugins &&
- payload.data.confusion_existing_plugins.length > 0
- ) {
- confusionPluginsList.innerHTML = '';
- payload.data.confusion_existing_plugins.forEach(
- function ( plugin ) {
- const div = document.createElement( 'div' );
- div.className =
- 'plugin-check-namer-confusion-item';
- div.innerHTML =
- '' +
- escapeHtml( plugin.name || '' ) +
- '' +
- ( plugin.active_installations
- ? ' (' +
- escapeHtml(
- plugin.active_installations
- ) +
- ' ' +
- 'active installs' +
- ')'
- : '' ) +
- ( plugin.owner_username
- ? ' - ' +
- escapeHtml( plugin.owner_username ) +
- ''
- : '' ) +
- '
' +
- '' +
- escapeHtml( plugin.explanation || '' ) +
- '' +
- ( plugin.link
- ? '
' +
- escapeHtml( plugin.link ) +
- ''
- : '' );
- confusionPluginsList.appendChild( div );
- }
- );
- confusionPluginsDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- } else if ( confusionPluginsDiv ) {
- confusionPluginsDiv.classList.add(
- 'plugin-check-namer-hidden'
- );
- }
-
- // Display confusion_existing_others if available.
- if (
- confusionOthersDiv &&
- confusionOthersList &&
- payload.data.confusion_existing_others &&
- payload.data.confusion_existing_others.length > 0
- ) {
- confusionOthersList.innerHTML = '';
- payload.data.confusion_existing_others.forEach(
- function ( item ) {
- const div = document.createElement( 'div' );
- div.className =
- 'plugin-check-namer-confusion-item plugin-check-namer-confusion-item-others';
- div.innerHTML =
- '' +
- escapeHtml( item.name || '' ) +
- '' +
- '
' +
- '' +
- escapeHtml( item.explanation || '' ) +
- '' +
- ( item.link
- ? '
' +
- escapeHtml( item.link ) +
- ''
- : '' );
- confusionOthersList.appendChild( div );
- }
- );
- confusionOthersDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- } else if ( confusionOthersDiv ) {
- confusionOthersDiv.classList.add(
- 'plugin-check-namer-hidden'
- );
- }
-
- if ( resultWrap ) {
- resultWrap.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
- } )
- .catch( function ( err ) {
- setText(
- errorEl,
- err && err.message
- ? err.message
- : pluginCheckNamer.messages.genericError
- );
-
- if ( errorDiv ) {
- errorDiv.classList.remove(
- 'plugin-check-namer-hidden'
- );
- }
- } )
- .finally( function () {
- setLoading( false );
- } );
- } );
- } );
-} )();
diff --git a/includes/Admin/Namer_Page.php b/includes/Admin/Namer_Page.php
deleted file mode 100644
index cb4b0d415..000000000
--- a/includes/Admin/Namer_Page.php
+++ /dev/null
@@ -1,460 +0,0 @@
-hook_suffix = add_management_page(
- __( 'Plugin Check Namer', 'plugin-check' ),
- __( 'Plugin Check Namer', 'plugin-check' ),
- 'manage_options',
- self::MENU_SLUG,
- array( $this, 'render_page' )
- );
- }
-
- /**
- * Enqueues scripts for the tools page.
- *
- * @since 1.8.0
- *
- * @param string $hook_suffix Current admin page hook suffix.
- */
- public function enqueue_scripts( $hook_suffix ) {
- if ( $hook_suffix !== $this->hook_suffix ) {
- return;
- }
-
- wp_enqueue_style(
- 'plugin-check-admin',
- plugins_url( 'assets/css/plugin-check-admin.css', WP_PLUGIN_CHECK_MAIN_FILE ),
- array(),
- WP_PLUGIN_CHECK_VERSION
- );
-
- wp_enqueue_script(
- 'plugin-check-namer',
- plugins_url( 'assets/js/plugin-check-namer.js', WP_PLUGIN_CHECK_MAIN_FILE ),
- array(),
- WP_PLUGIN_CHECK_VERSION,
- true
- );
-
- wp_localize_script(
- 'plugin-check-namer',
- 'pluginCheckNamer',
- array(
- 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
- 'nonce' => wp_create_nonce( 'plugin_check_namer_ajax' ),
- 'messages' => array(
- 'missingName' => __( 'Please enter a plugin name.', 'plugin-check' ),
- 'genericError' => __( 'An unexpected error occurred.', 'plugin-check' ),
- ),
- )
- );
- }
-
- /**
- * AJAX handler to analyze a plugin name.
- *
- * @since 1.8.0
- */
- public function ajax_analyze() {
- check_ajax_referer( 'plugin_check_namer_ajax', 'nonce' );
-
- if ( ! current_user_can( 'manage_options' ) ) {
- wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'plugin-check' ) ) );
- }
-
- $name = $this->get_plugin_name_from_request();
- if ( empty( $name ) ) {
- wp_send_json_error( array( 'message' => __( 'Please enter a plugin name.', 'plugin-check' ) ) );
- }
-
- $author = $this->get_author_name_from_request();
-
- $model_preference = $this->get_model_preference_from_request();
- $ai_config = $this->get_ai_config( $model_preference );
- if ( is_wp_error( $ai_config ) ) {
- wp_send_json_error( array( 'message' => $ai_config->get_error_message() ) );
- }
-
- $analysis = $this->run_name_analysis( $ai_config['model_preference'], $name, $author );
- if ( is_wp_error( $analysis ) ) {
- wp_send_json_error( array( 'message' => $analysis->get_error_message() ) );
- }
-
- $parsed = $this->parse_analysis( $analysis );
- $response = $this->build_ajax_response( $parsed, $analysis, $ai_config );
-
- wp_send_json_success( $response );
- }
-
- /**
- * Builds AJAX response from parsed analysis.
- *
- * @since 1.8.0
- *
- * @param array $parsed Parsed analysis.
- * @param string|array $analysis Raw analysis.
- * @param array $ai_config AI configuration with model preference info.
- * @return array Response array.
- */
- protected function build_ajax_response( $parsed, $analysis, $ai_config = array() ) {
- $raw_output = $this->get_raw_output( $parsed, $analysis );
- $raw_output = $this->format_json_output( $raw_output );
-
- $response = array(
- 'verdict' => $parsed['verdict'],
- 'explanation' => $parsed['explanation'],
- 'raw' => $raw_output,
- );
-
- if ( ! empty( $parsed['confusion_existing_plugins'] ) ) {
- $response['confusion_existing_plugins'] = $parsed['confusion_existing_plugins'];
- }
- if ( ! empty( $parsed['confusion_existing_others'] ) ) {
- $response['confusion_existing_others'] = $parsed['confusion_existing_others'];
- }
- if ( ! empty( $parsed['token_usage'] ) ) {
- $response['token_usage'] = $parsed['token_usage'];
- }
-
- // Add AI model preference information.
- if ( ! empty( $ai_config['model_preference'] ) ) {
- $response['ai_info'] = array(
- 'model' => $ai_config['model_preference'],
- );
- }
-
- return $response;
- }
-
- /**
- * Gets plugin name from request.
- *
- * @since 1.8.0
- *
- * @return string Plugin name or empty string.
- */
- protected function get_plugin_name_from_request() {
- $name = isset( $_POST['plugin_name'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_name'] ) ) : '';
- return trim( $name );
- }
-
- /**
- * Gets author name from request.
- *
- * @since 1.8.0
- *
- * @return string Author name or empty string.
- */
- protected function get_author_name_from_request() {
- $author = isset( $_POST['author_name'] ) ? sanitize_text_field( wp_unslash( $_POST['author_name'] ) ) : '';
- return trim( $author );
- }
-
- /**
- * Renders the page.
- *
- * @since 1.8.0
- *
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- public function render_page() {
- if ( ! current_user_can( 'manage_options' ) ) {
- return;
- }
-
- $ai_config = $this->get_ai_config();
- ?>
-
-
-
-
-
-
- get_error_message() );
- ?>
-
-
-
- check_ai_prerequisites() && is_wp_error( $this->check_ai_connectors() ) ) {
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- get_model_preference_from_request();
- $user_id = get_current_user_id();
-
- if ( empty( $input ) ) {
- $this->handle_analyze_error( $user_id, '', new WP_Error( 'missing_input', __( 'Please enter a plugin name.', 'plugin-check' ) ) );
- return;
- }
-
- $ai_config = $this->get_ai_config( $model_preference );
- if ( is_wp_error( $ai_config ) ) {
- $this->handle_analyze_error( $user_id, $input, $ai_config );
- return;
- }
-
- $analysis = $this->run_name_analysis( $ai_config['model_preference'], $input, $author );
-
- if ( is_wp_error( $analysis ) ) {
- $this->handle_analyze_error( $user_id, $input, $analysis );
- return;
- }
-
- $this->store_result(
- $user_id,
- array(
- 'input' => $input,
- 'analysis' => $analysis,
- )
- );
- wp_safe_redirect( $this->get_page_url() );
- exit;
- }
-
- /**
- * Handles analyze error and redirects.
- *
- * @since 1.8.0
- *
- * @param int $user_id User ID.
- * @param string $input Input value.
- * @param WP_Error $error Error object.
- */
- protected function handle_analyze_error( $user_id, $input, $error ) {
- $this->store_result(
- $user_id,
- array(
- 'input' => $input,
- 'error' => $error,
- )
- );
- wp_safe_redirect( $this->get_page_url() );
- exit;
- }
-
- /**
- * Gets the page URL.
- *
- * @since 1.8.0
- *
- * @return string
- */
- protected function get_page_url() {
- return add_query_arg( array( 'page' => self::MENU_SLUG ), admin_url( 'tools.php' ) );
- }
-}
diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php
index 8f66010ac..5040bec8b 100644
--- a/includes/Checker/Abstract_Check_Runner.php
+++ b/includes/Checker/Abstract_Check_Runner.php
@@ -342,11 +342,22 @@ final public function set_ai_model_preference( $model_preference ) {
*
* @return bool True if AI analysis should be used, false otherwise.
*/
- protected function should_use_ai() {
+ public function should_use_ai() {
// Check if explicitly set via setter (e.g., CLI flag or checkbox).
return $this->use_ai;
}
+ /**
+ * Gets the AI model preference for analysis.
+ *
+ * @since 2.1.0
+ *
+ * @return string Model preference.
+ */
+ final public function get_ai_model_preference() {
+ return $this->ai_model_preference;
+ }
+
/**
* Sets categories for filtering the checks.
*
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
new file mode 100644
index 000000000..5e5a4d109
--- /dev/null
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -0,0 +1,179 @@
+should_use_ai() ) {
+ return;
+ }
+
+ if ( is_wp_error( $this->check_ai_prerequisites() ) || is_wp_error( $this->check_ai_connectors() ) ) {
+ return;
+ }
+
+ // Retrieve model preference.
+ $model_preference = '';
+ if ( method_exists( $runner, 'get_ai_model_preference' ) ) {
+ $model_preference = $runner->get_ai_model_preference();
+ }
+ if ( empty( $model_preference ) && class_exists( Settings_Page::class ) ) {
+ $model_preference = Settings_Page::get_model_preference();
+ }
+
+ if ( ! function_exists( 'get_plugin_data' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+
+ $plugin_main_file = $result->plugin()->main_file();
+ $plugin_header = get_plugin_data( $plugin_main_file );
+ $plugin_name = isset( $plugin_header['Name'] ) ? $plugin_header['Name'] : '';
+ $author_name = isset( $plugin_header['AuthorName'] ) ? $plugin_header['AuthorName'] : '';
+
+ if ( empty( $plugin_name ) ) {
+ return;
+ }
+
+ $analysis = $this->run_name_analysis( $model_preference, $plugin_name, $author_name );
+ if ( is_wp_error( $analysis ) ) {
+ $this->add_result_warning_for_file(
+ $result,
+ sprintf(
+ /* translators: %s: Error message. */
+ __( 'AI plugin name check failed: %s', 'plugin-check' ),
+ $analysis->get_error_message()
+ ),
+ 'ai_name_check_failed',
+ $plugin_main_file
+ );
+ return;
+ }
+
+ $parsed = $this->parse_analysis( $analysis );
+
+ // If there is a verdict indicating issues:
+ if ( isset( $parsed['processed_data'] ) ) {
+ $data = $parsed['processed_data'];
+
+ // 1. If disallowed:
+ if ( ! empty( $data['disallowed'] ) ) {
+ $msg = isset( $data['disallowed_explanation'] ) ? $data['disallowed_explanation'] : __( 'The plugin name is disallowed.', 'plugin-check' );
+ $this->add_result_error_for_file(
+ $result,
+ $msg,
+ 'plugin_name_disallowed',
+ $plugin_main_file
+ );
+ }
+
+ // 2. If possible naming issues:
+ if ( ! empty( $data['possible_naming_issues'] ) ) {
+ $msg = isset( $data['naming_explanation'] ) ? $data['naming_explanation'] : __( 'The plugin name has possible naming issues.', 'plugin-check' );
+ $this->add_result_warning_for_file(
+ $result,
+ $msg,
+ 'plugin_name_issue',
+ $plugin_main_file
+ );
+ }
+
+ // 3. If possible owner/trademark issues:
+ if ( ! empty( $data['possible_owner_issues'] ) ) {
+ $msg = isset( $data['owner_explanation'] ) ? $data['owner_explanation'] : __( 'The plugin name has possible trademark or ownership issues.', 'plugin-check' );
+ $this->add_result_warning_for_file(
+ $result,
+ $msg,
+ 'plugin_name_trademark_issue',
+ $plugin_main_file
+ );
+ }
+
+ // 4. Group list of similar plugins/trademarks into recommendations:
+ if ( ! empty( $parsed['confusion_existing_plugins'] ) && is_array( $parsed['confusion_existing_plugins'] ) ) {
+ $plugins_list = array();
+ foreach ( $parsed['confusion_existing_plugins'] as $plugin ) {
+ $plugins_list[] = sprintf( '%s (%s, %s active installs)', $plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] );
+ }
+ $this->add_result_warning_for_file(
+ $result,
+ sprintf(
+ /* translators: %s: List of similar plugins. */
+ __( 'Plugin name is similar to existing plugins: %s', 'plugin-check' ),
+ implode( '; ', $plugins_list )
+ ),
+ 'plugin_name_similarity',
+ $plugin_main_file
+ );
+ }
+ }
+ }
+
+ /**
+ * Gets the description for the check.
+ *
+ * @since 2.1.0
+ *
+ * @return string Description.
+ */
+ public function get_description(): string {
+ return __( 'Checks the plugin name for guideline compliance, trademark conflicts, and similarity with existing plugins using AI.', 'plugin-check' );
+ }
+
+ /**
+ * Gets the documentation URL for the check.
+ *
+ * @since 2.1.0
+ *
+ * @return string The documentation URL.
+ */
+ public function get_documentation_url(): string {
+ return __( 'https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/', 'plugin-check' );
+ }
+}
diff --git a/includes/Checker/Default_Check_Repository.php b/includes/Checker/Default_Check_Repository.php
index c1b7d420c..eb1a81f96 100644
--- a/includes/Checker/Default_Check_Repository.php
+++ b/includes/Checker/Default_Check_Repository.php
@@ -103,6 +103,7 @@ private function register_default_checks() {
'direct_file_access' => new Checks\Plugin_Repo\Direct_File_Access_Check(),
'external_admin_menu_links' => new Checks\Plugin_Repo\External_Admin_Menu_Links_Check(),
'wp_functions_compatibility' => new Checks\Plugin_Repo\WP_Functions_Compatibility_Check(),
+ 'ai_name' => new Checks\Plugin_Repo\AI_Name_Check(),
)
);
diff --git a/includes/Plugin_Main.php b/includes/Plugin_Main.php
index 3aa0a4343..d211a1afd 100644
--- a/includes/Plugin_Main.php
+++ b/includes/Plugin_Main.php
@@ -77,10 +77,5 @@ public function add_hooks() {
// Create the Settings page.
$settings_page = new Settings_Page();
$settings_page->add_hooks();
-
- // Create the Plugin Check Namer tool page.
- $namer_page_class = '\\WordPress\\Plugin_Check\\Admin\\Namer_Page';
- $namer_page = new $namer_page_class();
- $namer_page->add_hooks();
}
}
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
new file mode 100644
index 000000000..3bc47a50c
--- /dev/null
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -0,0 +1,33 @@
+createMock( AJAX_Runner::class );
+ $runner->method( 'should_use_ai' )->willReturn( false );
+
+ $reflection = new \ReflectionClass( Plugin_Request_Utility::class );
+ $property = $reflection->getProperty( 'runner' );
+ $property->setAccessible( true );
+ $property->setValue( null, $runner );
+
+ $check = new AI_Name_Check();
+ $context = $this->createMock( Check_Context::class );
+ $result = new Check_Result( $context );
+
+ $check->run( $result );
+
+ $this->assertEmpty( $result->get_errors() );
+ $this->assertEmpty( $result->get_warnings() );
+
+ $property->setValue( null, null );
+ }
+}
From 173023daa3eae98d0bc404f9a2a5d5455bf54661 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Mon, 13 Jul 2026 12:24:36 +0530
Subject: [PATCH 2/7] fix: resolve PHPCS formatting
---
includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php | 8 ++++----
.../phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index 5e5a4d109..aba4a0d27 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -7,15 +7,15 @@
namespace WordPress\Plugin_Check\Checker\Checks\Plugin_Repo;
+use WordPress\Plugin_Check\Admin\Settings_Page;
use WordPress\Plugin_Check\Checker\Check_Categories;
use WordPress\Plugin_Check\Checker\Check_Result;
use WordPress\Plugin_Check\Checker\Static_Check;
-use WordPress\Plugin_Check\Traits\Amend_Check_Result;
-use WordPress\Plugin_Check\Traits\Stable_Check;
use WordPress\Plugin_Check\Traits\AI_Check_Names;
use WordPress\Plugin_Check\Traits\AI_Utils;
+use WordPress\Plugin_Check\Traits\Amend_Check_Result;
+use WordPress\Plugin_Check\Traits\Stable_Check;
use WordPress\Plugin_Check\Utilities\Plugin_Request_Utility;
-use WordPress\Plugin_Check\Admin\Settings_Page;
/**
* Check for plugin name issues using AI.
@@ -98,7 +98,7 @@ public function run( Check_Result $result ) {
$parsed = $this->parse_analysis( $analysis );
- // If there is a verdict indicating issues:
+ // If there is a verdict indicating issues.
if ( isset( $parsed['processed_data'] ) ) {
$data = $parsed['processed_data'];
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
index 3bc47a50c..54864aa55 100644
--- a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -1,12 +1,12 @@
-
Date: Mon, 13 Jul 2026 12:30:39 +0530
Subject: [PATCH 3/7] fix: resolve sorting
---
.../phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
index 54864aa55..759cd6d1f 100644
--- a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -1,11 +1,10 @@
- setValue( null, $runner );
$check = new AI_Name_Check();
- $context = $this->createMock( Check_Context::class );
+ $context = new Check_Context( WP_PLUGIN_CHECK_MAIN_FILE );
$result = new Check_Result( $context );
$check->run( $result );
From 53e33b98f8f68a9054d6ff0d3720db94e873bf64 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Mon, 13 Jul 2026 12:37:41 +0530
Subject: [PATCH 4/7] refactor: reduce method length
---
.../Checks/Plugin_Repo/AI_Name_Check.php | 143 ++++++++++++------
1 file changed, 93 insertions(+), 50 deletions(-)
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index aba4a0d27..30c5bde6c 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -97,61 +97,104 @@ public function run( Check_Result $result ) {
}
$parsed = $this->parse_analysis( $analysis );
+ $this->process_analysis_results( $result, $parsed, $plugin_main_file );
+ }
- // If there is a verdict indicating issues.
- if ( isset( $parsed['processed_data'] ) ) {
- $data = $parsed['processed_data'];
-
- // 1. If disallowed:
- if ( ! empty( $data['disallowed'] ) ) {
- $msg = isset( $data['disallowed_explanation'] ) ? $data['disallowed_explanation'] : __( 'The plugin name is disallowed.', 'plugin-check' );
- $this->add_result_error_for_file(
- $result,
- $msg,
- 'plugin_name_disallowed',
- $plugin_main_file
- );
- }
+ /**
+ * Process analysis results and add appropriate warnings/errors.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $parsed The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function process_analysis_results( Check_Result $result, array $parsed, string $plugin_main_file ) {
+ if ( ! isset( $parsed['processed_data'] ) ) {
+ return;
+ }
- // 2. If possible naming issues:
- if ( ! empty( $data['possible_naming_issues'] ) ) {
- $msg = isset( $data['naming_explanation'] ) ? $data['naming_explanation'] : __( 'The plugin name has possible naming issues.', 'plugin-check' );
- $this->add_result_warning_for_file(
- $result,
- $msg,
- 'plugin_name_issue',
- $plugin_main_file
- );
- }
+ $data = $parsed['processed_data'];
- // 3. If possible owner/trademark issues:
- if ( ! empty( $data['possible_owner_issues'] ) ) {
- $msg = isset( $data['owner_explanation'] ) ? $data['owner_explanation'] : __( 'The plugin name has possible trademark or ownership issues.', 'plugin-check' );
- $this->add_result_warning_for_file(
- $result,
- $msg,
- 'plugin_name_trademark_issue',
- $plugin_main_file
- );
- }
+ $this->check_disallowed_name( $result, $data, $plugin_main_file );
+ $this->check_naming_issues( $result, $data, $plugin_main_file );
+ $this->check_owner_issues( $result, $data, $plugin_main_file );
+ $this->check_similar_plugins( $result, $parsed, $plugin_main_file );
+ }
- // 4. Group list of similar plugins/trademarks into recommendations:
- if ( ! empty( $parsed['confusion_existing_plugins'] ) && is_array( $parsed['confusion_existing_plugins'] ) ) {
- $plugins_list = array();
- foreach ( $parsed['confusion_existing_plugins'] as $plugin ) {
- $plugins_list[] = sprintf( '%s (%s, %s active installs)', $plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] );
- }
- $this->add_result_warning_for_file(
- $result,
- sprintf(
- /* translators: %s: List of similar plugins. */
- __( 'Plugin name is similar to existing plugins: %s', 'plugin-check' ),
- implode( '; ', $plugins_list )
- ),
- 'plugin_name_similarity',
- $plugin_main_file
- );
+ /**
+ * Check if the plugin name is disallowed.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $data The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_disallowed_name( Check_Result $result, array $data, string $plugin_main_file ) {
+ if ( ! empty( $data['disallowed'] ) ) {
+ $msg = isset( $data['disallowed_explanation'] ) ? $data['disallowed_explanation'] : __( 'The plugin name is disallowed.', 'plugin-check' );
+ $this->add_result_error_for_file( $result, $msg, 'plugin_name_disallowed', $plugin_main_file );
+ }
+ }
+
+ /**
+ * Check for possible naming issues.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $data The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_naming_issues( Check_Result $result, array $data, string $plugin_main_file ) {
+ if ( ! empty( $data['possible_naming_issues'] ) ) {
+ $msg = isset( $data['naming_explanation'] ) ? $data['naming_explanation'] : __( 'The plugin name has possible naming issues.', 'plugin-check' );
+ $this->add_result_warning_for_file( $result, $msg, 'plugin_name_issue', $plugin_main_file );
+ }
+ }
+
+ /**
+ * Check for possible owner issues.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $data The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_owner_issues( Check_Result $result, array $data, string $plugin_main_file ) {
+ if ( ! empty( $data['possible_owner_issues'] ) ) {
+ $msg = isset( $data['owner_explanation'] ) ? $data['owner_explanation'] : __( 'The plugin name has possible trademark or ownership issues.', 'plugin-check' );
+ $this->add_result_warning_for_file( $result, $msg, 'plugin_name_trademark_issue', $plugin_main_file );
+ }
+ }
+
+ /**
+ * Check for similar plugins.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $parsed The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_similar_plugins( Check_Result $result, array $parsed, string $plugin_main_file ) {
+ if ( ! empty( $parsed['confusion_existing_plugins'] ) && is_array( $parsed['confusion_existing_plugins'] ) ) {
+ $plugins_list = array();
+ foreach ( $parsed['confusion_existing_plugins'] as $plugin ) {
+ $plugins_list[] = sprintf( '%s (%s, %s active installs)', $plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] );
}
+ $this->add_result_warning_for_file(
+ $result,
+ sprintf(
+ /* translators: %s: List of similar plugins. */
+ __( 'Plugin name is similar to existing plugins: %s', 'plugin-check' ),
+ implode( '; ', $plugins_list )
+ ),
+ 'plugin_name_similarity',
+ $plugin_main_file
+ );
}
}
From b4095ea5abb6a84b2e7671556b9dd6f15fc6e9d8 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Mon, 13 Jul 2026 12:41:50 +0530
Subject: [PATCH 5/7] refactor: reduce run method NPath complexity to satisfy
PHPMD
---
.../Checks/Plugin_Repo/AI_Name_Check.php | 100 +++++++++++++++---
1 file changed, 83 insertions(+), 17 deletions(-)
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index 30c5bde6c..384fe68cf 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -49,39 +49,105 @@ public function get_categories() {
*/
public function run( Check_Result $result ) {
$runner = Plugin_Request_Utility::get_runner();
-
- // Only run when AI analysis is enabled.
- if ( ! $runner || ! method_exists( $runner, 'should_use_ai' ) || ! $runner->should_use_ai() ) {
+ if ( ! $this->should_run_ai_check( $runner ) ) {
return;
}
- if ( is_wp_error( $this->check_ai_prerequisites() ) || is_wp_error( $this->check_ai_connectors() ) ) {
+ $model_preference = $this->get_model_preference( $runner );
+ $plugin_data = $this->get_plugin_name_and_author( $result );
+ if ( ! $plugin_data ) {
return;
}
- // Retrieve model preference.
- $model_preference = '';
- if ( method_exists( $runner, 'get_ai_model_preference' ) ) {
- $model_preference = $runner->get_ai_model_preference();
+ $analysis = $this->run_name_analysis( $model_preference, $plugin_data['name'], $plugin_data['author'] );
+ $this->handle_analysis_response( $result, $analysis, $plugin_data['file'] );
+ }
+
+ /**
+ * Determines if the AI check should run.
+ *
+ * @since 2.1.0
+ *
+ * @param mixed $runner The active runner.
+ * @return bool True if the check should run, false otherwise.
+ */
+ private function should_run_ai_check( $runner ) {
+ if ( ! $runner ) {
+ return false;
+ }
+ if ( ! method_exists( $runner, 'should_use_ai' ) ) {
+ return false;
+ }
+ if ( ! $runner->should_use_ai() ) {
+ return false;
+ }
+ if ( is_wp_error( $this->check_ai_prerequisites() ) ) {
+ return false;
+ }
+ if ( is_wp_error( $this->check_ai_connectors() ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Gets the selected AI model preference.
+ *
+ * @since 2.1.0
+ *
+ * @param mixed $runner The active runner.
+ * @return string The model preference.
+ */
+ private function get_model_preference( $runner ) {
+ $model = '';
+ if ( $runner && method_exists( $runner, 'get_ai_model_preference' ) ) {
+ $model = $runner->get_ai_model_preference();
}
- if ( empty( $model_preference ) && class_exists( Settings_Page::class ) ) {
- $model_preference = Settings_Page::get_model_preference();
+ if ( empty( $model ) && class_exists( Settings_Page::class ) ) {
+ $model = Settings_Page::get_model_preference();
}
+ return $model;
+ }
+ /**
+ * Gets the plugin name and author from headers.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @return array|null Name and author if found, null otherwise.
+ */
+ private function get_plugin_name_and_author( Check_Result $result ) {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
- $plugin_main_file = $result->plugin()->main_file();
- $plugin_header = get_plugin_data( $plugin_main_file );
- $plugin_name = isset( $plugin_header['Name'] ) ? $plugin_header['Name'] : '';
- $author_name = isset( $plugin_header['AuthorName'] ) ? $plugin_header['AuthorName'] : '';
+ $file = $result->plugin()->main_file();
+ $header = get_plugin_data( $file );
+ $name = isset( $header['Name'] ) ? $header['Name'] : '';
+ $author = isset( $header['AuthorName'] ) ? $header['AuthorName'] : '';
- if ( empty( $plugin_name ) ) {
- return;
+ if ( empty( $name ) ) {
+ return null;
}
- $analysis = $this->run_name_analysis( $model_preference, $plugin_name, $author_name );
+ return array(
+ 'name' => $name,
+ 'author' => $author,
+ 'file' => $file,
+ );
+ }
+
+ /**
+ * Handles the AI analysis response and parses results.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param mixed $analysis The analysis response or WP_Error.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function handle_analysis_response( Check_Result $result, $analysis, string $plugin_main_file ) {
if ( is_wp_error( $analysis ) ) {
$this->add_result_warning_for_file(
$result,
From 33e0cf4109de6fb4e6878ed031845e63201a9e5e Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Wed, 15 Jul 2026 20:57:51 +0530
Subject: [PATCH 6/7] refactor: separate AI name check option
---
assets/js/plugin-check-admin.js | 19 +++++++++++
includes/Admin/Admin_AJAX.php | 15 +++++----
includes/CLI/Plugin_Check_Command.php | 5 +++
includes/Checker/Abstract_Check_Runner.php | 32 ++++++++++++++++++-
.../Checks/Plugin_Repo/AI_Name_Check.php | 32 +++++++++----------
templates/admin-page.php | 3 ++
.../Checker/Checks/AI_Name_Check_Tests.php | 2 +-
7 files changed, 84 insertions(+), 24 deletions(-)
diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js
index 22227cc4e..0bc320444 100644
--- a/assets/js/plugin-check-admin.js
+++ b/assets/js/plugin-check-admin.js
@@ -38,6 +38,7 @@
'plugin-check__include-experimental'
);
const useAi = document.getElementById( 'plugin-check__use-ai' );
+ const useAiName = document.getElementById( 'plugin-check__use-ai-name' );
// Handle disabling the Check it button when a plugin is not selected.
function canRunChecks() {
@@ -92,6 +93,9 @@
if ( useAi ) {
useAi.disabled = true;
}
+ if ( useAiName ) {
+ useAiName.disabled = true;
+ }
if ( includeExperimental ) {
includeExperimental.disabled = true;
}
@@ -144,6 +148,9 @@
if ( useAi ) {
useAi.disabled = false;
}
+ if ( useAiName ) {
+ useAiName.disabled = false;
+ }
if ( includeExperimental ) {
includeExperimental.disabled = false;
}
@@ -596,6 +603,10 @@
includeExperimental && includeExperimental.checked ? 1 : 0
);
pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < data.checks.length; i++ ) {
pluginCheckData.append( 'checks[]', data.checks[ i ] );
@@ -669,6 +680,10 @@
includeExperimental && includeExperimental.checked ? 1 : 0
);
pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < categoriesList.length; i++ ) {
if ( categoriesList[ i ].checked ) {
@@ -957,6 +972,10 @@
includeExperimental && includeExperimental.checked ? 1 : 0
);
pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < typesList.length; i++ ) {
if ( typesList[ i ].checked ) {
diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php
index eef695078..06d36bdfe 100644
--- a/includes/Admin/Admin_AJAX.php
+++ b/includes/Admin/Admin_AJAX.php
@@ -226,12 +226,13 @@ public function clean_up_environment() {
public function get_checks_to_run() {
$this->check_request_validity();
- $categories = filter_input( INPUT_POST, 'categories', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
- $categories = is_null( $categories ) ? array() : $categories;
- $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
- $checks = is_null( $checks ) ? array() : $checks;
- $use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
- $runner = $this->get_ajax_runner();
+ $categories = filter_input( INPUT_POST, 'categories', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
+ $categories = is_null( $categories ) ? array() : $categories;
+ $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
+ $checks = is_null( $checks ) ? array() : $checks;
+ $use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
+ $use_ai_name = 1 === filter_input( INPUT_POST, 'use-ai-name', FILTER_VALIDATE_INT );
+ $runner = $this->get_ajax_runner();
if ( is_wp_error( $runner ) ) {
wp_send_json_error( $runner, 500 );
@@ -295,6 +296,7 @@ public function run_checks() {
$include_experimental = 1 === filter_input( INPUT_POST, 'include-experimental', FILTER_VALIDATE_INT );
$use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
+ $use_ai_name = 1 === filter_input( INPUT_POST, 'use-ai-name', FILTER_VALIDATE_INT );
$types = filter_input( INPUT_POST, 'types', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$types = is_null( $types ) ? array( 'error', 'warning' ) : $types;
@@ -303,6 +305,7 @@ public function run_checks() {
$runner->set_check_slugs( $checks );
$runner->set_plugin( $plugin );
$runner->set_use_ai( $use_ai );
+ $runner->set_use_ai_name( $use_ai_name );
$results = $runner->run();
} catch ( Exception $error ) {
wp_send_json_error(
diff --git a/includes/CLI/Plugin_Check_Command.php b/includes/CLI/Plugin_Check_Command.php
index 834478e78..119e5b8d6 100644
--- a/includes/CLI/Plugin_Check_Command.php
+++ b/includes/CLI/Plugin_Check_Command.php
@@ -149,6 +149,9 @@ public function __construct( Plugin_Context $plugin_context ) {
* [--ai-model=]
* : AI model preference for analysis (e.g., 'openai::gpt-4o'). Requires --ai.
*
+ * [--ai-name]
+ * : Enable AI-based plugin name checking.
+ *
* ## EXAMPLES
*
* wp plugin check akismet
@@ -191,6 +194,7 @@ public function check( $args, $assoc_args ) {
'mode' => 'new',
'ai' => false,
'ai-model' => '',
+ 'ai-name' => false,
)
);
@@ -252,6 +256,7 @@ static function ( $dirs ) use ( $excluded_files ) {
$runner->set_slug( $options['slug'] );
$runner->set_mode( $options['mode'] );
$runner->set_use_ai( $options['ai'] );
+ $runner->set_use_ai_name( $options['ai-name'] );
if ( ! empty( $options['ai-model'] ) ) {
$runner->set_ai_model_preference( $options['ai-model'] );
}
diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php
index 5040bec8b..510e628b0 100644
--- a/includes/Checker/Abstract_Check_Runner.php
+++ b/includes/Checker/Abstract_Check_Runner.php
@@ -42,6 +42,14 @@ abstract class Abstract_Check_Runner implements Check_Runner {
*/
protected $use_ai = false;
+ /**
+ * Whether AI plugin name checking is enabled.
+ *
+ * @since x.x.x
+ * @var bool
+ */
+ protected $use_ai_name = false;
+
/**
* AI model preference for analysis.
*
@@ -324,6 +332,17 @@ final public function set_use_ai( $use_ai ) {
$this->use_ai = (bool) $use_ai;
}
+ /**
+ * Sets whether to use AI plugin name check.
+ *
+ * @since x.x.x
+ *
+ * @param bool $use_ai_name True to enable AI name check, false to disable.
+ */
+ final public function set_use_ai_name( $use_ai_name ) {
+ $this->use_ai_name = (bool) $use_ai_name;
+ }
+
/**
* Sets the AI model preference for analysis.
*
@@ -347,10 +366,21 @@ public function should_use_ai() {
return $this->use_ai;
}
+ /**
+ * Determines if AI plugin name check should be used.
+ *
+ * @since x.x.x
+ *
+ * @return bool True if AI name check should be used, false otherwise.
+ */
+ public function should_use_ai_name() {
+ return $this->use_ai_name;
+ }
+
/**
* Gets the AI model preference for analysis.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return string Model preference.
*/
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index 384fe68cf..10aa17eab 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -20,7 +20,7 @@
/**
* Check for plugin name issues using AI.
*
- * @since 2.1.0
+ * @since x.x.x
*/
class AI_Name_Check implements Static_Check {
@@ -32,7 +32,7 @@ class AI_Name_Check implements Static_Check {
/**
* Gets the categories for the check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return array The categories for the check.
*/
@@ -43,7 +43,7 @@ public function get_categories() {
/**
* Runs the AI Name Check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result to amend.
*/
@@ -66,7 +66,7 @@ public function run( Check_Result $result ) {
/**
* Determines if the AI check should run.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param mixed $runner The active runner.
* @return bool True if the check should run, false otherwise.
@@ -75,10 +75,10 @@ private function should_run_ai_check( $runner ) {
if ( ! $runner ) {
return false;
}
- if ( ! method_exists( $runner, 'should_use_ai' ) ) {
+ if ( ! method_exists( $runner, 'should_use_ai_name' ) ) {
return false;
}
- if ( ! $runner->should_use_ai() ) {
+ if ( ! $runner->should_use_ai_name() ) {
return false;
}
if ( is_wp_error( $this->check_ai_prerequisites() ) ) {
@@ -93,7 +93,7 @@ private function should_run_ai_check( $runner ) {
/**
* Gets the selected AI model preference.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param mixed $runner The active runner.
* @return string The model preference.
@@ -112,7 +112,7 @@ private function get_model_preference( $runner ) {
/**
* Gets the plugin name and author from headers.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @return array|null Name and author if found, null otherwise.
@@ -141,7 +141,7 @@ private function get_plugin_name_and_author( Check_Result $result ) {
/**
* Handles the AI analysis response and parses results.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param mixed $analysis The analysis response or WP_Error.
@@ -169,7 +169,7 @@ private function handle_analysis_response( Check_Result $result, $analysis, stri
/**
* Process analysis results and add appropriate warnings/errors.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $parsed The parsed analysis data.
@@ -191,7 +191,7 @@ private function process_analysis_results( Check_Result $result, array $parsed,
/**
* Check if the plugin name is disallowed.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $data The parsed analysis data.
@@ -207,7 +207,7 @@ private function check_disallowed_name( Check_Result $result, array $data, strin
/**
* Check for possible naming issues.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $data The parsed analysis data.
@@ -223,7 +223,7 @@ private function check_naming_issues( Check_Result $result, array $data, string
/**
* Check for possible owner issues.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $data The parsed analysis data.
@@ -239,7 +239,7 @@ private function check_owner_issues( Check_Result $result, array $data, string $
/**
* Check for similar plugins.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $parsed The parsed analysis data.
@@ -267,7 +267,7 @@ private function check_similar_plugins( Check_Result $result, array $parsed, str
/**
* Gets the description for the check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return string Description.
*/
@@ -278,7 +278,7 @@ public function get_description(): string {
/**
* Gets the documentation URL for the check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return string The documentation URL.
*/
diff --git a/templates/admin-page.php b/templates/admin-page.php
index d8bfe935e..26183e659 100644
--- a/templates/admin-page.php
+++ b/templates/admin-page.php
@@ -85,6 +85,9 @@
+
+
+
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
index 759cd6d1f..29be2abc0 100644
--- a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -11,7 +11,7 @@ class AI_Name_Check_Tests extends \WP_UnitTestCase {
public function test_run_bails_early_when_ai_disabled() {
$runner = $this->createMock( AJAX_Runner::class );
- $runner->method( 'should_use_ai' )->willReturn( false );
+ $runner->method( 'should_use_ai_name' )->willReturn( false );
$reflection = new \ReflectionClass( Plugin_Request_Utility::class );
$property = $reflection->getProperty( 'runner' );
From d535e57287b7d7eecc082f9d4a97817a6651ce65 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Wed, 15 Jul 2026 21:04:53 +0530
Subject: [PATCH 7/7] fix: resolve phpmd warning
---
includes/Admin/Admin_AJAX.php | 1 +
includes/Checker/Abstract_Check_Runner.php | 1 +
2 files changed, 2 insertions(+)
diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php
index 06d36bdfe..cb5afad13 100644
--- a/includes/Admin/Admin_AJAX.php
+++ b/includes/Admin/Admin_AJAX.php
@@ -242,6 +242,7 @@ public function get_checks_to_run() {
$this->configure_runner( $runner );
$runner->set_categories( $categories );
$runner->set_use_ai( $use_ai );
+ $runner->set_use_ai_name( $use_ai_name );
$plugin_basename = $runner->get_plugin_basename();
$checks_to_run = $runner->get_checks_to_run();
diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php
index 510e628b0..69c1ecf19 100644
--- a/includes/Checker/Abstract_Check_Runner.php
+++ b/includes/Checker/Abstract_Check_Runner.php
@@ -21,6 +21,7 @@
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
+ * @SuppressWarnings(PHPMD.TooManyFields)
*/
abstract class Abstract_Check_Runner implements Check_Runner {