* @author Maximum Software Inc * @license GPL-3.0-only * @link https://agenistrator.com/ * * SPDX-FileCopyrightText: 2025-2026 Alex Ruban * SPDX-FileCopyrightText: 2025-2026 Maximum Software Inc * SPDX-License-Identifier: GPL-3.0-only */ namespace Agenistrator; // Direct access prevention if ( ! defined( 'ABSPATH' ) ) { exit; } /** * List_Plugin_Files_Tool class. */ class List_Plugin_Files_Tool extends Tool { /** * Get the tool ID. * * @return string The tool ID. * @since 1.0.0 */ public function get_id() { return 'list_plugin_files'; } /** * Get the tool name. * * @return string The tool name. * @since 1.0.0 */ public function get_name() { return __( 'List Plugin Files', 'agenistrator' ); } /** * Get the tool description. * * @return string The tool description. * @since 1.0.0 */ public function get_description() { return 'List all files in a plugin directory.'; } /** * Get the parameters schema for this tool. * * @return array Parameters schema in JSON Schema format. * @since 1.0.0 */ public function get_parameters() { return array( 'type' => 'object', 'properties' => array( 'plugin' => array( 'type' => 'string', 'description' => 'Plugin slug (directory name) or main plugin file path (e.g., "my-plugin" or "my-plugin/my-plugin.php")', ), 'limit' => array( 'type' => 'integer', 'description' => 'Maximum number of files to return. Default: 100.', ), 'offset' => array( 'type' => 'integer', 'description' => 'Number of files to skip before returning results. Default: 0.', ), ), 'required' => array( 'plugin' ), ); } /** * Check if the current user can execute this tool. * * @return bool True if the current user has permission. * @since 1.0.0 */ public function current_user_can_execute() { return current_user_can( 'edit_plugins' ); } /** * Check if this tool is read-only. * * @return bool True if the tool only reads data. * @since 1.0.0 */ public function is_read_only() { return true; } /** * Execute the tool with the given arguments. * * @param array $args The tool arguments. * * @return array The tool execution result. * @since 1.0.0 */ protected function execute( $args ) { if ( ! isset( $args['plugin'] ) || empty( trim( $args['plugin'] ) ) ) { return $this->error_response( 'Plugin identifier is required' ); } // Find the plugin using WordPress's plugin registry $plugin = WordPress_Utils::find_plugin( $args['plugin'] ); if ( null === $plugin ) { return $this->error_response( 'Plugin not found: ' . $args['plugin'] ); } $slug = WordPress_Utils::get_plugin_slug( $plugin['file'] ); $plugin_dir = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $slug; // Ensure path is within plugins directory (prevent directory traversal) $real_plugin_dir = realpath( $plugin_dir ); $real_plugins_dir = realpath( WP_PLUGIN_DIR ); if ( false === $real_plugin_dir || false === $real_plugins_dir || ! PHP_Utils::is_path_within( $real_plugin_dir, $real_plugins_dir ) ) { return $this->error_response( 'Invalid plugin path' ); } $files = $this->scan_directory( $real_plugin_dir, $real_plugin_dir ); // Apply pagination $limit = max( 1, $args['limit'] ?? 100 ); $offset = max( 0, $args['offset'] ?? 0 ); $total = count( $files ); $paginated = array_slice( $files, $offset, $limit ); $has_more = $total > ( $offset + $limit ); return $this->success_response( 'Plugin files listed successfully', array( 'plugin' => $slug, 'directory' => $plugin_dir, 'count' => count( $paginated ), 'total' => $total, 'files' => $paginated, 'limit' => $limit, 'offset' => $offset, 'has_more' => $has_more, ) ); } /** * Recursively scan directory and return file list */ private function scan_directory( $dir, $base_dir ) { $files = array(); $items = scandir( $dir ); foreach ( $items as $item ) { if ( '.' === $item || '..' === $item ) { continue; } $path = $dir . DIRECTORY_SEPARATOR . $item; $relative_path = substr( $path, strlen( $base_dir ) + 1 ); if ( is_dir( $path ) ) { $files[] = array( 'path' => $relative_path, 'type' => 'directory', ); $files = array_merge( $files, $this->scan_directory( $path, $base_dir ) ); } else { $files[] = array( 'path' => $relative_path, 'type' => 'file', 'size' => filesize( $path ), 'modified' => gmdate( 'Y-m-d H:i:s e', filemtime( $path ) ), ); } } return $files; } }