Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion assets/js/plugin-check-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
const usePcpignore = document.getElementById(
'plugin-check__use-pcpignore'
);
Expand Down Expand Up @@ -69,6 +70,27 @@
return values;
}

/**
* Reorders a list of check slugs so that priority checks run (and thus
* render) first, in the given order, followed by the remaining checks
* in their original order.
*
* @since 2.2.0
*
* @param {Array} checks Check slugs to run.
* @param {Array} priorityChecks Check slugs that should run first, in order.
* @return {Array} Reordered check slugs.
*/
function prioritizeChecks( checks, priorityChecks ) {
const remaining = checks.filter(
( check ) => ! priorityChecks.includes( check )
);
const prioritized = priorityChecks.filter( ( check ) =>
checks.includes( check )
);
return [ ...prioritized, ...remaining ];
}

/**
* Posts FormData to the plugin's AJAX endpoint.
*
Expand Down Expand Up @@ -144,6 +166,9 @@
if ( useAi ) {
useAi.disabled = true;
}
if ( useAiName ) {
useAiName.disabled = true;
}
if ( usePcpignore ) {
usePcpignore.disabled = true;
}
Expand All @@ -155,6 +180,7 @@
const categories = getSelectedValues( categoriesList );
const types = getSelectedValues( typesList );
const useAiChecked = useAi && useAi.checked ? 1 : 0;
const useAiNameChecked = useAiName && useAiName.checked ? 1 : 0;
const usePcpignoreChecked =
usePcpignore && usePcpignore.checked ? 1 : 0;
const includeExperimentalChecked =
Expand All @@ -166,15 +192,19 @@
categories,
includeExperimentalChecked,
useAiChecked,
useAiNameChecked,
usePcpignoreChecked
)
.then( ( data ) => {
currentChecks = data.checks;
currentChecks = prioritizeChecks( data.checks, [

Check failure on line 199 in assets/js/plugin-check-admin.js

View workflow job for this annotation

GitHub Actions / Lint

Replace `⏎↹↹↹↹↹'ai_name',⏎↹↹↹↹` with `·'ai_name'·`
'ai_name',
] );
return setUpEnvironment(
plugin,
currentChecks,
includeExperimentalChecked,
useAiChecked,
useAiNameChecked,
usePcpignoreChecked
);
} )
Expand All @@ -185,6 +215,7 @@
types,
includeExperimentalChecked,
useAiChecked,
useAiNameChecked,
usePcpignoreChecked
)
)
Expand Down Expand Up @@ -235,6 +266,9 @@
if ( useAi ) {
useAi.disabled = false;
}
if ( useAiName ) {
useAiName.disabled = false;
}
if ( usePcpignore ) {
usePcpignore.disabled = false;
}
Expand Down Expand Up @@ -653,6 +687,7 @@
* @param {Array} checks Check slugs that will run.
* @param {number} includeExperimentalInput Whether to include experimental checks.
* @param {number} useAiInput Whether to enable AI analysis.
* @param {number} useAiNameInput Whether to enable AI name check.
* @param {number} usePcpignoreInput Whether to apply .pcpignore exclusions.
* @return {Promise<Object>} Resolves with the response message.
*/
Expand All @@ -661,6 +696,7 @@
checks,
includeExperimentalInput,
useAiInput,
useAiNameInput,
usePcpignoreInput
) {
const pluginCheckData = new FormData();
Expand All @@ -674,6 +710,7 @@
includeExperimentalInput
);
pluginCheckData.append( 'use-ai', useAiInput );
pluginCheckData.append( 'use-ai-name', useAiNameInput );
pluginCheckData.append( 'use-pcpignore', usePcpignoreInput );

for ( let i = 0; i < checks.length; i++ ) {
Expand Down Expand Up @@ -723,6 +760,7 @@
* @param {Array} categories Selected category slugs.
* @param {number} includeExperimentalInput Whether to include experimental checks.
* @param {number} useAiInput Whether to enable AI analysis.
* @param {number} useAiNameInput Whether to enable AI name check.
* @param {number} usePcpignoreInput Whether to apply .pcpignore exclusions.
* @return {Promise<Object>} Resolves with the response containing plugin and checks.
*/
Expand All @@ -731,6 +769,7 @@
categories,
includeExperimentalInput,
useAiInput,
useAiNameInput,
usePcpignoreInput
) {
const pluginCheckData = new FormData();
Expand All @@ -741,6 +780,7 @@
includeExperimentalInput
);
pluginCheckData.append( 'use-ai', useAiInput );
pluginCheckData.append( 'use-ai-name', useAiNameInput );
pluginCheckData.append( 'use-pcpignore', usePcpignoreInput );

for ( let i = 0; i < categories.length; i++ ) {
Expand All @@ -766,6 +806,7 @@
* @param {Array} types Result types to include (error, warning).
* @param {number} includeExperimentalInput Whether to include experimental checks.
* @param {number} useAiInput Whether to enable AI analysis.
* @param {number} useAiNameInput Whether to enable AI name check.
* @param {number} usePcpignoreInput Whether to apply .pcpignore exclusions.
*/
async function runChecks(
Expand All @@ -774,6 +815,7 @@
types,
includeExperimentalInput,
useAiInput,
useAiNameInput,
usePcpignoreInput
) {
let isSuccessMessage = true;
Expand All @@ -786,6 +828,7 @@
types,
includeExperimentalInput,
useAiInput,
useAiNameInput,
usePcpignoreInput
);
const splitResults = splitResultsByFalsePositive( results );
Expand Down Expand Up @@ -1032,6 +1075,7 @@
* @param {Array} types Result types to include (error, warning).
* @param {number} includeExperimentalInput Whether to include experimental checks.
* @param {number} useAiInput Whether to enable AI analysis.
* @param {number} useAiNameInput Whether to enable AI name check.
* @param {number} usePcpignoreInput Whether to apply .pcpignore exclusions.
* @return {Promise<Object>} The check results.
*/
Expand All @@ -1041,6 +1085,7 @@
types,
includeExperimentalInput,
useAiInput,
useAiNameInput,
usePcpignoreInput
) {
const pluginCheckData = new FormData();
Expand All @@ -1052,6 +1097,7 @@
includeExperimentalInput
);
pluginCheckData.append( 'use-ai', useAiInput );
pluginCheckData.append( 'use-ai-name', useAiNameInput );
pluginCheckData.append( 'use-pcpignore', usePcpignoreInput );

for ( let i = 0; i < types.length; i++ ) {
Expand Down
34 changes: 14 additions & 20 deletions includes/Admin/Admin_AJAX.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ private function get_ajax_runner() {
return new WP_Error( 'invalid-runner', __( 'AJAX Runner was not initialized correctly.', 'plugin-check' ) );
}

// Register the runner so that checks relying on Plugin_Request_Utility::get_runner()
// (e.g. AI_Name_Check) can access it, even when no runtime checks are involved.
Plugin_Request_Utility::set_runner( $runner );

return $runner;
}

Expand Down Expand Up @@ -219,12 +223,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 );
Expand All @@ -234,6 +239,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 );

$checks_to_run = $runner->get_checks_to_run();
} catch ( Exception $error ) {
Expand Down Expand Up @@ -266,26 +272,13 @@ public function run_checks() {
wp_send_json_error( $runner, 500 );
}

$runner = Plugin_Request_Utility::get_runner();

if ( is_null( $runner ) ) {
$runner = new AJAX_Runner();
}

// Make sure we are using the correct runner instance.
if ( ! ( $runner instanceof AJAX_Runner ) ) {
wp_send_json_error(
new WP_Error( 'invalid-runner', __( 'AJAX Runner was not initialized correctly.', 'plugin-check' ) ),
500
);
}

$checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$checks = is_null( $checks ) ? array() : $checks;
$plugin = filter_input( INPUT_POST, 'plugin', FILTER_SANITIZE_FULL_SPECIAL_CHARS );

$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 );
$use_pcpignore = 1 === filter_input( INPUT_POST, 'use-pcpignore', FILTER_VALIDATE_INT );
$types = filter_input( INPUT_POST, 'types', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$types = is_null( $types ) ? array( 'error', 'warning' ) : $types;
Expand All @@ -305,6 +298,7 @@ public function run_checks() {
$pcpignore_warning = PCP_Ignore_Utility::get_warning();
}
$runner->set_use_ai( $use_ai );
$runner->set_use_ai_name( $use_ai_name );
$results = $runner->run();
} catch ( Exception $error ) {
wp_send_json_error(
Expand Down
5 changes: 5 additions & 0 deletions includes/CLI/Plugin_Check_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ public function __construct( Plugin_Context $plugin_context ) {
* [--ai-model=<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
Expand Down Expand Up @@ -208,6 +211,7 @@ public function check( $args, $assoc_args ) {
'mode' => 'new',
'ai' => false,
'ai-model' => '',
'ai-name' => false,
'use-pcpignore' => false,
)
);
Expand Down Expand Up @@ -280,6 +284,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'] );
}
Expand Down
44 changes: 43 additions & 1 deletion includes/Checker/Abstract_Check_Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.TooManyFields)
*/
abstract class Abstract_Check_Runner implements Check_Runner {

Expand All @@ -42,6 +43,14 @@ abstract class Abstract_Check_Runner implements Check_Runner {
*/
protected $use_ai = false;

/**
* Whether AI plugin name checking is enabled.
*
* @since 2.2.0
* @var bool
*/
protected $use_ai_name = false;

/**
* AI model preference for analysis.
*
Expand Down Expand Up @@ -324,6 +333,17 @@ final public function set_use_ai( $use_ai ) {
$this->use_ai = (bool) $use_ai;
}

/**
* Sets whether to use AI plugin name check.
*
* @since 2.2.0
*
* @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.
*
Expand All @@ -342,11 +362,33 @@ 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;
}

/**
* Determines if AI plugin name check should be used.
*
* @since 2.2.0
*
* @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 x.x.x
*
* @return string Model preference.
*/
final public function get_ai_model_preference() {
return $this->ai_model_preference;
}

/**
* Sets categories for filtering the checks.
*
Expand Down
Loading
Loading