* @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; } /** * Read_Plugin_File_Tool class. */ class Read_Plugin_File_Tool extends Abstract_File_Reader_Tool { /** * Get the tool ID. * * @return string The tool ID. * @since 1.0.0 */ public function get_id() { return 'read_plugin_file'; } /** * Get the tool name. * * @return string The tool name. * @since 1.0.0 */ public function get_name() { return __( 'Read Plugin File', 'agenistrator' ); } /** * Get the tool description. * * @return string The tool description. * @since 1.0.0 */ public function get_description() { return 'Read a plugin file.'; } /** * Get usage instructions for the LLM. * * @return string Instructions for using this tool. * @since 1.0.0 */ public function get_instructions() { return parent::get_instructions() . ' File path is relative to the 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() { $params = parent::get_parameters(); $params['properties'] = array_merge( array( 'plugin' => array( 'type' => 'string', 'description' => 'Plugin slug (directory name) or main plugin file path', ), ), $params['properties'] ); $params['required'][] = 'plugin'; return $params; } /** * 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' ); } /** * 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' ); } if ( ! isset( $args['file'] ) || empty( trim( $args['file'] ) ) ) { return $this->error_response( 'File path 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'] ); $file = $args['file']; $plugin_dir = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $slug; $file_path = $plugin_dir . DIRECTORY_SEPARATOR . $file; // Validate file path is within plugin directory (prevent directory traversal) $real_file_path = realpath( $file_path ); $real_plugin_dir = realpath( $plugin_dir ); if ( false === $real_file_path || false === $real_plugin_dir || ! PHP_Utils::is_path_within( $real_file_path, $real_plugin_dir ) ) { return $this->error_response( 'Invalid file path or file does not exist' ); } if ( ! is_file( $real_file_path ) ) { return $this->error_response( 'File not found' ); } try { $data = $this->execute_mode( $real_file_path, $args, array( 'plugin' => $slug, 'file' => $file, 'extension' => pathinfo( $real_file_path, PATHINFO_EXTENSION ), 'modified' => gmdate( 'Y-m-d H:i:s e', filemtime( $real_file_path ) ), ) ); } catch ( \Throwable $e ) { return $this->error_response( $e->getMessage() ); } return $this->success_response( 'File read', $data ); } }