From 83cfcd9fb1347d55bc119d8b83adf7dc53c9a589 Mon Sep 17 00:00:00 2001 From: Mikan <72257910+Mikan-DS@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:16:03 +0300 Subject: [PATCH] initial --- .gitignore | 4 + .idea/.gitignore | 8 ++ .idea/G4F.iml | 13 ++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 + ReactDev/__init__.py | 111 ++++++++++++++++++ ReactDev/config.py | 4 + ReactDev/core/ai_interface.py | 62 ++++++++++ ReactDev/core/commands.py | 26 ++++ ReactDev/core/file_analyzer.py | 37 ++++++ ReactDev/core/project_map.py | 81 +++++++++++++ ReactDev/examples/instructions.md | 9 ++ ReactDev/utils/file_utils.py | 21 ++++ ReactDev/utils/logger.py | 13 ++ ReactDev/utils/prompt_utils.py | 0 main.py | 10 ++ requirements.txt | 4 + 19 files changed, 430 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/G4F.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 ReactDev/__init__.py create mode 100644 ReactDev/config.py create mode 100644 ReactDev/core/ai_interface.py create mode 100644 ReactDev/core/commands.py create mode 100644 ReactDev/core/file_analyzer.py create mode 100644 ReactDev/core/project_map.py create mode 100644 ReactDev/examples/instructions.md create mode 100644 ReactDev/utils/file_utils.py create mode 100644 ReactDev/utils/logger.py create mode 100644 ReactDev/utils/prompt_utils.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b45655 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/react_app_map.txt +/instructions.md +/prompt.md +/workingon.md diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/G4F.iml b/.idea/G4F.iml new file mode 100644 index 0000000..961e841 --- /dev/null +++ b/.idea/G4F.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..aecbf0c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c9c1627 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ReactDev/__init__.py b/ReactDev/__init__.py new file mode 100644 index 0000000..59a387a --- /dev/null +++ b/ReactDev/__init__.py @@ -0,0 +1,111 @@ +# Инициализация пакета +import os + +from .core.ai_interface import AIInterface +from .core.project_map import ProjectMap +from .core.file_analyzer import FileAnalyzer +from .core.commands import CommandGenerator +from .utils.logger import log +from .utils.file_utils import read_file, write_file +from .config import REACT_PROJECT_PATH + +__all__ = ["AIInterface", "ProjectMap", "FileAnalyzer", "CommandGenerator", "run_workflow"] + + +def ensure_default_files(): + """ + Проверяет наличие необходимых файлов и создает их с базовым содержимым, если они отсутствуют. + """ + default_files = { + "workingon.md": "/path/to/react/project/src/Auth.tsx", + "prompt.md": "Добавить функцию авторизации в файл Auth.tsx", + "instructions.md": "# Инструкции для ИИ\n\n1. Функции должны быть написаны в TypeScript.\n2. Используйте современные практики React (например, хуки).\n3. При создании новых компонентов добавьте комментарии.", + } + + for file_name, content in default_files.items(): + if not os.path.exists(file_name): + log(f"File {file_name} not found. Creating it with default content.") + write_file(file_name, content) + + +def parse_workingon_content(content: str) -> dict: + """ + Парсит содержимое файла workingon.md. + Возвращает словарь с путем к файлу и дополнительным контекстом. + """ + lines = content.strip().split("\n") + result = {"file_path": None, "context": ""} + + if lines: + # Первая строка — путь к файлу + result["file_path"] = lines[0].strip() + # Остальные строки — дополнительный контекст + result["context"] = "\n".join(lines[1:]).strip() + + return result + + + +def run_workflow(): + """ + Основной алгоритм работы библиотеки ReactDev. + 1. Создает карту структуры проекта. + 2. Анализирует файлы на наличие функций. + 3. Отправляет запрос к ИИ с контекстом. + 4. Генерирует план модификации. + """ + log("Starting ReactDev workflow...") + + # Шаг 0: Убедиться, что все необходимые файлы существуют + ensure_default_files() + + # Шаг 1: Создание карты структуры проекта + project_map = ProjectMap(REACT_PROJECT_PATH) + project_structure = project_map.generate_map() + project_map.save_map() + + log(f"Generated project structure:\n{project_structure}") + + # Шаг 2: Чтение и парсинг workingon.md + workingon_content = read_file("workingon.md") + workingon_data = parse_workingon_content(workingon_content) + + target_file = workingon_data["file_path"] + additional_context = workingon_data["context"] + + if target_file and not os.path.exists(target_file): + log(f"Target file {target_file} does not exist.", level="error") + return + + # Шаг 3: Анализ целевого файла (если указан) + functions_info = {} + if target_file: + file_analyzer = FileAnalyzer() + functions_info = file_analyzer.analyze_file(target_file) + log(f"Analyzed functions in {target_file}: {functions_info}") + + # Шаг 4: Отправка запроса к ИИ + ai_interface = AIInterface() + user_prompt = read_file("prompt.md").strip() + + # Добавляем дополнительный контекст из workingon.md + full_context = { + "project_structure": project_structure, + "target_file": target_file, + "functions_info": functions_info, + "additional_context": additional_context, + } + + ai_response = ai_interface.analyze_project(user_prompt, full_context) + log(f"AI response: {ai_response}") + + # Шаг 5: Генерация плана модификации + command_generator = CommandGenerator() + modifications = ai_response.get("modifications", []) + modification_plan = command_generator.generate_commands(modifications) + + with open("modification_plan.md", "w") as f: + f.write(modification_plan) + + log(f"Generated modification plan:\n{modification_plan}") + log("ReactDev workflow completed.") diff --git a/ReactDev/config.py b/ReactDev/config.py new file mode 100644 index 0000000..e9da3ac --- /dev/null +++ b/ReactDev/config.py @@ -0,0 +1,4 @@ +REACT_PROJECT_PATH = r'C:\Users\Mikan\PycharmProjects\tisbiProFi\frontend\react-source' +OPENAI_API_KEY = "secret" # Замените на ваш API-ключ +OPENAI_BASE_URL = "http://localhost:51337/v1" # Базовый URL для OpenAI +DEFAULT_MODEL = "gpt-4o-mini" # Модель по умолчанию diff --git a/ReactDev/core/ai_interface.py b/ReactDev/core/ai_interface.py new file mode 100644 index 0000000..610a79b --- /dev/null +++ b/ReactDev/core/ai_interface.py @@ -0,0 +1,62 @@ +from openai import OpenAI +from ..config import OPENAI_API_KEY, OPENAI_BASE_URL, DEFAULT_MODEL +from ..utils.logger import log + +class AIInterface: + def __init__(self): + self.client = OpenAI( + api_key=OPENAI_API_KEY, + base_url=OPENAI_BASE_URL, + ) + + def send_request(self, prompt: str, context: dict = None) -> dict: + """ + Отправляет запрос на эндпоинт ИИ через OpenAI и возвращает ответ. + """ + try: + log(f"Sending request to AI endpoint with prompt: {prompt}") + + # Подготовка сообщений для чат-комплитшена + messages = [{"role": "user", "content": prompt}] + if context: + # Преобразуем контекст в строку + context_str = self._format_context(context) + messages.append({"role": "system", "content": context_str}) + + # Создание запроса к OpenAI + response = self.client.chat.completions.create( + model=DEFAULT_MODEL, + messages=[{"role": "user", "content": "Write a poem about a tree"}], + stream=False, # Пока отключаем стриминг для простоты + ) + + # Обработка ответа + return {"response": response.choices[0].message.content} + + except Exception as e: + log(f"Error communicating with AI endpoint: {e}", level="error") + raise + + def _format_context(self, context: dict) -> str: + """ + Преобразует контекст в строку для передачи в OpenAI API. + """ + formatted_context = [] + for key, value in context.items(): + formatted_context.append(f"{key}: {value}") + return "\n".join(formatted_context) + + + def analyze_project(self, project_structure: str, prompt: str) -> dict: + """ + Анализирует структуру проекта и запрос пользователя через ИИ. + """ + context = {"project_structure": project_structure} + return self.send_request(prompt, context) + + def request_additional_info(self, message: str) -> str: + """ + Запрашивает дополнительную информацию у пользователя. + """ + log("Requesting additional information from user.") + return input(f"{message}\n> ") \ No newline at end of file diff --git a/ReactDev/core/commands.py b/ReactDev/core/commands.py new file mode 100644 index 0000000..616d746 --- /dev/null +++ b/ReactDev/core/commands.py @@ -0,0 +1,26 @@ +from ..utils.logger import log + +class CommandGenerator: + def __init__(self): + pass + + def generate_commands(self, modifications: list) -> str: + """ + Генерирует план модификации на основе анализа ИИ. + """ + log("Generating modification plan...") + commands = [] + for mod in modifications: + action = mod.get("action") + file = mod.get("file") + function = mod.get("function") + code = mod.get("code", "") + + if action == "CREATE": + commands.append(f"CREATE {function} {file}") + elif action == "DELETE": + commands.append(f"DELETE {function} {file}") + elif action == "MODIFY": + commands.append(f"MODIFY {function} {file}") + commands.append(f"```ts\n{code}\n```") + return "\n".join(commands) \ No newline at end of file diff --git a/ReactDev/core/file_analyzer.py b/ReactDev/core/file_analyzer.py new file mode 100644 index 0000000..44046f6 --- /dev/null +++ b/ReactDev/core/file_analyzer.py @@ -0,0 +1,37 @@ +import esprima +from ..utils.logger import log + +class FileAnalyzer: + def __init__(self): + pass + + def analyze_file(self, file_path: str) -> dict: + """ + Анализирует файл и извлекает информацию о функциях. + """ + log(f"Analyzing file: {file_path}") + with open(file_path, "r") as f: + code = f.read() + + try: + parsed = esprima.parseScript(code, loc=True) + functions = self._extract_functions(parsed) + return functions + except Exception as e: + log(f"Error parsing file {file_path}: {e}", level="error") + return {} + + def _extract_functions(self, ast: dict) -> dict: + """ + Извлекает информацию о функциях из AST. + """ + functions = {} + for node in ast.body: + if node.type == "FunctionDeclaration": + name = node.id.name + params = [param.name for param in node.params] + functions[name] = { + "params": params, + "return_type": "unknown", # Тип возвращаемого значения не всегда доступен + } + return functions \ No newline at end of file diff --git a/ReactDev/core/project_map.py b/ReactDev/core/project_map.py new file mode 100644 index 0000000..fa7538a --- /dev/null +++ b/ReactDev/core/project_map.py @@ -0,0 +1,81 @@ +import os +from pathlib import Path + +from pathspec import PathSpec +from pathspec.patterns import GitWildMatchPattern + +from ..utils.logger import log + + +class ProjectMap: + def __init__(self, project_path: str): + self.project_path = project_path + self.gitignore_rules = self._load_gitignore() + + def generate_map(self) -> str: + """ + Создает карту структуры проекта, игнорируя файлы из .gitignore и директорию .git/. + """ + log("Generating project structure map...") + structure = self._traverse_directory(self.project_path) + return structure + + def _traverse_directory(self, path: str, level: int = 0) -> str: + """ + Рекурсивно обходит директории и формирует строку структуры, + игнорируя файлы и директории из .gitignore и .git/. + """ + indent = " " * level + result = "" + for entry in sorted(os.listdir(path)): + full_path = os.path.join(path, entry) + + # Игнорируем директорию .git/ + if entry == ".git": + continue + + # Проверяем, игнорируется ли файл или директория по правилам .gitignore + relative_path = os.path.relpath(full_path, self.project_path) + if self._is_ignored(relative_path): + continue + + if os.path.isdir(full_path): + result += f"{indent}- {entry}/\n" + result += self._traverse_directory(full_path, level + 1) + else: + result += f"{indent}- {entry}\n" + return result + + def save_map(self, output_file: str = "react_app_map.txt"): + """ + Сохраняет карту структуры в файл. + """ + map_content = self.generate_map() + with open(output_file, "w") as f: + f.write(map_content) + log(f"Project map saved to {output_file}") + + def _load_gitignore(self) -> list: + """ + Загружает правила из .gitignore, если файл существует. + """ + gitignore_path = os.path.join(self.project_path, ".gitignore") + rules = [] + if os.path.exists(gitignore_path): + with open(gitignore_path, "r") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#"): # Игнорируем комментарии и пустые строки + rules.append(line) + return rules + + def _is_ignored(self, path: str) -> bool: + """ + Проверяет, игнорируется ли файл или директория по правилам .gitignore, + включая поддержку сложных правил (например, **/logs/*.log). + """ + # Преобразуем правила .gitignore в PathSpec + spec = PathSpec.from_lines(GitWildMatchPattern, self.gitignore_rules) + + # Проверяем, соответствует ли путь правилам + return spec.match_file(path) diff --git a/ReactDev/examples/instructions.md b/ReactDev/examples/instructions.md new file mode 100644 index 0000000..b1acf9a --- /dev/null +++ b/ReactDev/examples/instructions.md @@ -0,0 +1,9 @@ +1. Компоненты должны быть функциональными и писаться в формате +```tsx +function Component({a}:{a:type}){ + // ... +} +``` +2. Функции должны быть написаны в TypeScript. +3. Используйте современные практики React (например, хуки). +4. При создании новых компонентов добавьте комментарии. \ No newline at end of file diff --git a/ReactDev/utils/file_utils.py b/ReactDev/utils/file_utils.py new file mode 100644 index 0000000..0e10a0c --- /dev/null +++ b/ReactDev/utils/file_utils.py @@ -0,0 +1,21 @@ +import os + +def read_file(file_path: str) -> str: + """ + Читает содержимое файла. + """ + with open(file_path, "r") as f: + return f.read() + +def write_file(file_path: str, content: str): + """ + Записывает содержимое в файл. + """ + with open(file_path, "w", encoding="utf-8") as f: + f.write(content) + +def create_directory(path: str): + """ + Создает директорию, если она не существует. + """ + os.makedirs(path, exist_ok=True) \ No newline at end of file diff --git a/ReactDev/utils/logger.py b/ReactDev/utils/logger.py new file mode 100644 index 0000000..36ff2cd --- /dev/null +++ b/ReactDev/utils/logger.py @@ -0,0 +1,13 @@ +import sys + +def log(message: str, level: str = "info"): + """ + Логирует сообщения в консоль. + """ + levels = { + "info": "[INFO]", + "warning": "[WARNING]", + "error": "[ERROR]", + } + prefix = levels.get(level, "[UNKNOWN]") + print(f"{prefix} {message}", file=sys.stderr if level == "error" else sys.stdout) \ No newline at end of file diff --git a/ReactDev/utils/prompt_utils.py b/ReactDev/utils/prompt_utils.py new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..72d0c33 --- /dev/null +++ b/main.py @@ -0,0 +1,10 @@ +# This is a sample Python script. +import ReactDev + +# Press Shift+F10 to execute it or replace it with your code. +# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. + + +if __name__ == '__main__': + ReactDev.run_workflow() + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..58856f1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +requests~=2.32.3 +esprima~=4.0.1 +pathspec~=0.12.1 +openai~=1.82.1 \ No newline at end of file