diff --git a/.cursor/README.md b/.cursor/README.md old mode 100644 new mode 100755 diff --git a/.cursor/backup/README.md b/.cursor/backup/README.md deleted file mode 100644 index e6439f58..00000000 --- a/.cursor/backup/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## AI Access Policy - -См. [memory-bank/AI-access-policy.md](../memory-bank/AI-access-policy.md) — политика полного доступа AI-ассистента ко всем файлам проекта и проактивных действий. - ---- - -## Environment Parameters - -См. [rules/environment.yaml](rules/environment.yaml) — Parameters операционной и тестовой среды, доступные AI-ассистенту для автоматизации и диагностики. - ---- \ No newline at end of file diff --git a/.cursor/backup/index.mdc b/.cursor/backup/index.mdc deleted file mode 100644 index f9ca3323..00000000 --- a/.cursor/backup/index.mdc +++ /dev/null @@ -1,27 +0,0 @@ -# Project Rules Index - -В этом проекте используются современные Cursor Project Rules: - -- Все правила хранятся в виде отдельных .mdc-файлов в .cursor/rules/ -- Каждый файл отвечает за отдельный аспект: команды пользователя, best practices, Security, workflow и т.д. - -## Ключевые правила: -- [user-Commands.mdc](rules/user-Commands.mdc) — обработка пользовательских команд -- [development-guIDElines.mdc](rules/development-guIDElines.mdc) — best practices для разработки -- [security-and-deployment.mdc](rules/security-and-deployment.mdc) — security-Standards -- [knowledge-map.mdc](rules/knowledge-map.mdc) — карта структуры знаний -- [memorybank-quality.mdc](rules/memorybank-quality.mdc) — чек-лист качества memory-bank -- [automation.mdc](rules/automation.mdc) — автоматизация синхронизации -- [workflow.mdc](rules/workflow.mdc) — workflow-правила ветвления -- [project-architecture.mdc](rules/project-architecture.mdc) — Architecture и Patterns -- [testing-and-debugging.mdc](rules/testing-and-debugging.mdc) — Testing и Debugging -- [plugin-development.mdc](rules/plugin-development.mdc) — Standards для плагинов -- [ui-standards.mdc](rules/ui-standards.mdc) — UI-Standards -- [architecture-patterns.mdc](rules/architecture-patterns.mdc) — архитектурные Patterns -- [development-principles.mdc](rules/development-principles.mdc) — Principles разработки - -Все правила всегда применяются для максимальной автоматизации и прозрачности. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/AUTOMATION.md b/.cursor/backup/rules/AUTOMATION.md deleted file mode 100644 index 99670585..00000000 --- a/.cursor/backup/rules/AUTOMATION.md +++ /dev/null @@ -1,205 +0,0 @@ -# .cursor Automation System - -Комплексная система автоматизации для аудита, исправления и оптимизации каталога `.cursor` для лучшего понимания AI и Cursor. - -## 🎯 **Purpose** - -Система автоматизации обеспечивает: -- **Автоматический аудит** каталога `.cursor` на предмет проблем -- **Исправление** найденных проблем (дубликаты, сломанные ссылки, метаdata) -- **Оптимизацию** правил специально для AI и Cursor -- **Monitoring** состояния и качества правил - -## 🛠️ **Компоненты системы** - -### **1. CursorAuditor** (`audit-cursor..js`) -- Сканирование всех файлов в `.cursor/rules` -- Verification метаданных и их валидности -- Поиск дублирующего контента -- Verification сломанных ссылок -- Validation структуры каталога -- Генерация подробных отчетов - -### **2. CursorFixer** (`fix-cursor..js`) -- Автоматическое исправление метаданных -- Удаление дублирующих файлов -- Исправление сломанных ссылок -- Обновление индексов - -### **3. AIOptimizer** (`optimize-for-ai..js`) -- Optimization метаданных для AI -- Добавление AI-специфичных тегов -- Purpose Priorityов и категорий -- Создание AI-оптимизированных индексов - -### **4. CursorManager** (`cursor-manager..js`) -- Главный Interface для всех операций -- Управление workflow -- Генерация отчетов о Statusе -- CLI Interface - -## 🚀 **Usage** - -### **Базовые команды:** - -```bash -# Показать справку -node cursor-manager..js help - -# Check Status -node cursor-manager..js status - -# Запустить аудит -node cursor-manager..js audit - -# Применить исправления -node cursor-manager..js fix - -# Оптимизировать для AI -node cursor-manager..js optimize - -# Полный workflow -node cursor-manager..js full -``` - -### **Опции:** - -```bash -#.json вывод для аудита -node cursor-manager..js audit -.json - -# Пропустить аудит перед исправлениями -node cursor-manager..js fix --no-audit-first - -# Пропустить аудит после оптимизации -node cursor-manager..js optimize --no-audit-after -``` - -## 📊 **Что проверяет аудит** - -### **Файлы и структура:** -- Общее Quantity файлов -- Соотношение `.mdc` и `.md` файлов -- Наличие обязательных директорий -- Наличие обязательных файлов - -### **Метаdata:** -- Наличие.yaml frontmatter -- Валидность полей `description`, `globs`, `alwaysApply` -- AI-специфичные поля `aiPriority`, `aiCategory` - -### **Контент:** -- Дублирующий контент между файлами -- Сломанные ссылки в документации -- Консистентность структуры - -### **AI-Optimization:** -- Priorityы правил (critical, high, medium, normal) -- Категории (system-design, development-practices, etc.) -- AI-специфичные теги и Comme.ts - -## 🤖 **AI-специфичные оптимизации** - -### **Priorityы:** -- **critical**: Применяется ко всему коду и решениям -- **high**: Применяется к большинству задач -- **medium**: Применяется когда релевантно -- **normal**: Применяется когда уместно - -### **Категории:** -- **system-design**: Architecture и структура системы -- **development-practices**: Standards кодирования -- **documentation**: Documentation и Communication -- **plugin-development**: Правила для плагинов -- **security**: Security -- **user-interface**: UI/UX Standards -- **process-management**: Workflow и Processes -- **ai-optimization**: AI-специфичные оптимизации - -## 📈 **Отчеты и метрики** - -### **Статистика:** -- Общее Quantity файлов -- Файлы с/без метаданных -- Quantity проблем -- Процент AI-готовности - -### **Проблемы:** -- Дублирующий контент -- Сломанные ссылки -- Невалидные метаdata -- Отсутствующие файлы/директории - -### **Recommendations:** -- Конвертация `.md` в `.mdc` -- Добавление метаданных -- Удаление дубликатов -- Исправление ссылок - -## 🔄 **Workflow** - -### **Полный workflow (`full`):** -1. **Аудит** - поиск проблем -2. **Исправления** - автоматическое решение -3. **Optimization** - AI-специфичные улучшения -4. **Финальный аудит** - Verification результатов -5. **Отчет** - сравнение до/после - -### **Инкрементальный workflow:** -- `audit` → анализ проблем -- `fix` → исправление -- `optimize` → Optimization -- `status` → Monitoring - -## 📝 **Integration с AI** - -### **Автоматические команды:** -Система интегрирована с AI memory-bank через команды: -- `аудит cursor` / `cursor audit` -- `исправь cursor` / `cursor fix` -- `оптимизируй cursor` / `cursor optimize` -- `полный cursor` / `cursor full` -- `Status cursor` / `cursor status` - -### **AI-оптимизированные файлы:** -- `ai-optimization.mdc` - инструкции для AI -- `ai-index.mdc` - индекс по Priorityам -- AI-теги в каждом файле -- Priorityы и категории в метаданных - -## 🎯 **Результаты** - -После полной оптимизации: -- ✅ Все файлы имеют правильные метаdata -- ✅ Нет дублирующего контента -- ✅ Все ссылки работают -- ✅ AI-специфичные оптимизации применены -- ✅ Priorityы и категории назначены -- ✅ Автоматическое применение критических правил - -## 🔧 **Расширение системы** - -### **Добавление новых проверок:** -1. Create Method в `CursorAuditor` -2. Add в основной workflow -3. Update отчеты - -### **Добавление новых исправлений:** -1. Create Method в `CursorFixer` -2. Интегрировать в процесс исправления -3. Add в отчеты - -### **Добавление новых оптимизаций:** -1. Create Method в `AIOptimizer` -2. Add AI-специфичную логику -3. Update валидацию - -## 📚 **Связанные файлы** - -- `audit-cursor..js` - Аудит системы -- `fix-cursor..js` - Исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - Главный Interface -- `ai-optimization.mdc` - AI инструкции -- `ai-index.mdc` - AI индекс -- `ai-memory.mdc` - Команды для AI \ No newline at end of file diff --git a/.cursor/backup/rules/EXPERIENCE-TRANSFER.md b/.cursor/backup/rules/EXPERIENCE-TRANSFER.md deleted file mode 100644 index 1b738237..00000000 --- a/.cursor/backup/rules/EXPERIENCE-TRANSFER.md +++ /dev/null @@ -1,279 +0,0 @@ -# Experience Transfer Best Practices - -Руководство по переносу опыта работы с Cursor и AI между проектами через систему `.cursor`. - -## 🎯 **Почему `.cursor` лучше `docs/for-ai-best-practices`** - -### **Автоматическое применение** -- ✅ Cursor автоматически читает все файлы из `.cursor/rules/` -- ✅ Правила применяются без дополнительной Settings -- ✅ AI получает контекст сразу при создании чата - -### **Стандартизация** -- ✅ `.cursor` - стандартное место для правил Cursor -- ✅ Все разработчики знают, где искать правила -- ✅ Единообразие между проектами - -### **Структурированность** -- ✅ Четкая иерархия (architecture, dev, doc, plugin, etc.) -- ✅ Метаdata с Priorityами и Categoryми -- ✅ Автоматическая Validation и Optimization - -## 🚀 **Стратегия переноса опыта** - -### **1. Экспорт из исходного проекта** - -```bash -# Из папки .cursor/rules исходного проекта -node cursor-manager..js export - -# С указанием целевого проекта -node cursor-manager..js export my-new-project -``` - -**Создается папка `cursor-export/` с:** -- Все правила по Categoryм -- Скрипты автоматизации -- Инструкции по импорту -- Автоматический скрипт импорта - -### **2. Импорт в целевой проект** - -```bash -# Скопировать cursor-export в целевой проект -cp -r cursor-export /path/to/new-project/ - -# Запустить импорт -cd /path/to/new-project -node cursor-export/import-cursor..js -``` - -### **3. Кастомизация для нового проекта** - -```bash -# Перейти в .cursor/rules -cd .cursor/rules - -# Запустить полную оптимизацию -node cursor-manager..js full - -# Check Status -node cursor-manager..js status -``` - -## 📋 **Что переносится** - -### **Core Rules (обязательные)** -- `ai-memory.mdc` - команды для AI (кастомизировать!) -- `environment.mdc` - Constrai.ts окружения (кастомизировать!) -- `index.mdc` - индекс правил -- `README.mdc` - Documentation структуры - -### **Categories (по необходимости)** -- **architecture/** - архитектурные правила -- **dev/** - Principles разработки -- **doc/** - Standards документации -- **plugin/** - правила для плагинов -- **security/** - правила безопасности -- **ui/** - UI/UX Standards -- **workflow/** - Processes разработки - -### **Automation (Required)** -- `audit-cursor..js` - аудит системы -- `fix-cursor..js` - автоматические исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - главный Interface - -## 🔧 **Кастомизация для нового проекта** - -### **1. Update environment.mdc** -``.mdc ---- -description: Critical environment limitations -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: environment ---- - -# Environment Constrai.ts - -## Node.js Version -- Required: Node.js 18+ (Update для вашего проекта) - -## Browser Support -- Chrome: 120+ (Update для вашего проекта) -- Firefox: 115+ (Update для вашего проекта) - -## build Tools -- Vite: 5+ (Update для вашего проекта) -- TypeScript: 5+ (Update для вашего проекта) -``` - -### **2. Update ai-memory.mdc** -``.mdc ---- -description: User Commands and AI instructions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Memory Bank - -## Project-Specific Commands -- `build project` - Собрать проект (Update команды) -- `test project` - Запустить тесты (Update команды) -- `deploy project` - Деплой проекта (Update команды) - -## Project Context -- This is a [Type проекта] (Update Description) -- Main technology stack: [стек] (Update стек) -- Key features: [особенности] (Update особенности) -``` - -### **3. Update monorepo-best-practices.mdc** -``.mdc ---- -description: Monorepo structure and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Monorepo Best Practices - -## Project Structure -``` -project/ -├── packages/ # (Update структуру) -├── apps/ # (Update структуру) -├── tools/ # (Update структуру) -└── docs/ # (Update структуру) -``` - -## Package Management -- Package manager: pnpm (Update если нужно) -- Workspace configuration: pnpm-workspace.yaml (Update) -``` - -## 📊 **Workflow переноса** - -### **Полный цикл:** -1. **Экспорт** из исходного проекта -2. **Копирование** в целевой проект -3. **Импорт** с автоматической настройкой -4. **Кастомизация** под новый проект -5. **Validation** и Optimization -6. **Testing** работы AI - -### **Команды для AI:** -```bash -# Экспорт -экспорт cursor -экспорт cursor [проект] - -# Импорт (в целевом проекте) -импорт cursor - -# Verification -Status cursor -полный cursor -``` - -## 🎯 **Best Practices** - -### **1. Инкрементальный перенос** -- Начните с core rules -- Добавляйте категории по мере необходимости -- Тестируйте каждую категорию - -### **2. Кастомизация обязательна** -- Всегда обновляйте `environment.mdc` -- Адаптируйте `ai-memory.mdc` под проект -- Проверяйте актуальность правил - -### **3. Validation после импорта** -```bash -cd .cursor/rules -node cursor-manager..js status -node cursor-manager..js audit -node cursor-manager..js optimize -``` - -### **4. Documentation изменений** -- Ведите changelog изменений -- Комментируйте кастомизации -- Обновляйте README проекта - -### **5. Обратная Compatibility** -- Сохраняйте структуру категорий -- Не удаляйте обязательные файлы -- Тестируйте на разных проектах - -## 🔄 **Автоматизация** - -### **Скрипт быстрого переноса:** -```bash -#!/bin/bash -# quick-transfer.sh - -SOURCE_PROJECT=$1 -TARGET_PROJECT=$2 - -echo "🚀 Quick transfer from $SOURCE_PROJECT to $TARGET_PROJECT" - -# Экспорт из исходного проекта -cd $SOURCE_PROJECT/.cursor/rules -node cursor-manager..js export $TARGET_PROJECT - -# Копирование в целевой проект -cp -r cursor-export $TARGET_PROJECT/ - -# Импорт в целевой проект -cd $TARGET_PROJECT -node cursor-export/import-cursor..js - -# Очистка -rm -rf cursor-export - -echo "✅ Transfer completed!" -``` - -### **Git hooks для автоматизации:** -```bash -# .git/hooks/post-merge -#!/bin/bash -if [ -d ".cursor/rules" ]; then - cd .cursor/rules - node cursor-manager..js status -fi -``` - -## 📈 **Метрики успеха** - -### **После переноса проверьте:** -- ✅ Все файлы имеют правильные метаdata -- ✅ AI команды работают в новом проекте -- ✅ Автоматизация функционирует -- ✅ Нет конфликтов с существующими правилами -- ✅ Performance AI не снизилась - -### **Долгосрочные метрики:** -- Скорость onboarding новых разработчиков -- Quality AI-ассистированной разработки -- Консистентность кода между проектами -- Time на настройку новых проектов - -## 🎉 **Результат** - -После правильного переноса: -- **Быстрый старт** новых проектов -- **Консистентность** между проектами -- **Автоматизация** рутинных задач -- **Улучшенное** AI-ассистирование -- **Стандартизация** процессов разработки - -**Система `.cursor` обеспечивает эффективный перенос опыта между проектами с минимальными усилиями!** 🚀 \ No newline at end of file diff --git a/.cursor/backup/rules/README.mdc b/.cursor/backup/rules/README.mdc deleted file mode 100644 index 60abfdb3..00000000 --- a/.cursor/backup/rules/README.mdc +++ /dev/null @@ -1,117 +0,0 @@ - -# .cursor/rules: Project Rules & AI Onboarding - -This directory contains all rules and standards for automation and AI assista.ts in the Agent Plugins Platform. - -## Principles -- Each rule is a separate .mdc file (one rule — one file) -- Grouped by topic: development principles, architecture, plugin standards, UI/UX, workflow, documentation, etc. -- Each file contains: a short description, globs, the rule .tself, and (optionally) examples or related links -- All rules are in English for international compatibility -- README and index.mdc help with navigation and search - -## CLI Scri.ts - -- **create-rule.js** — Interactive CLI to create a new .mdc rule file from a template. Prom.ts for all required fields and updates index/README automatically. - - Usage: `node .cursor/rules/create-rule.js` -- **generate-rules-index.js** — Scans all .mdc files and regenerates `index.mdc` and the structure section in `README.md`. - - Usage: `node .cursor/rules/generate-rules-index.js` -- **check-rules-structure.js** — Validates all .mdc files for required sections, uniqueness, and valid related links. Used in CI. - - Usage: `node .cursor/rules/check-rules-structure.js` - -## Current Structure -``` -.cursor/ - rules/ - # Root level - critical rules - ai-memory.mdc # User Commands and AI instructions - environment.mdc # Critical environment limitations - monorepo-best-practices.mdc # Monorepo structure and guIDElines - TypeScript-build-troubleshooting.mdc # TypeScript build error solutions - README.mdc # Main documentation - index.mdc # Rules index and navigation - - architecture/ # System architecture rules - architecture-chat-system.mdc - architecture-error-handling.mdc - architecture-observability.mdc - architecture-performance.mdc - architecture-plugin.mdc - architecture-project-structure.mdc - architecture-security.mdc - architecture-workflow.mdc - project-architecture.mdc - - dev/ # Development principles and guIDElines - dev-principle-01-do-no-harm.mdc - dev-principle-03-best-practices.mdc - dev-principle-04-fail-fast-safe.mdc - dev-principle-05-observability.mdc - dev-principle-06-config-as-code.mdc - dev-principle-07-progressive-enhancement.mdc - dev-principle-08-data-integrity-privacy.mdc - dev-principle-09-continuous-learning.mdc - dev-principle-10-ecosystem-thinking.mdc - development-guIDElines.mdc - security-and-deployment.mdc - testing-and-debugging.mdc - TypeScript.mdc - git-workflow.mdc - - doc/ # Documentation and AI standards - ai-first.mdc # AI-oriented documentation standards - knowledge-map.mdc # Project knowledge structure - memorybank-quality.mdc # Memory-bank quality standards - restore-context.mdc # Context restoration procedures - .mdc-file-standards.mdc # .mdc file creation standards - ai-fallback.mdc # AI fallback procedures - - plugin/ # Plugin development standards - plugin-best-practices.mdc - plugin-documentation.mdc - plugin-error-handling.mdc - plugin-performance.mdc - plugin-security.mdc - plugin-structure.mdc - plugin-testing.mdc - - security/ # Security standards - validation.mdc # Input validation rules - - ui/ # UI/UX standards - ui-accessibility.mdc - ui-animation.mdc - ui-error-handling.mdc - ui-forms.mdc - ui-loading-States.mdc - ui-mobile.mdc - ui-navigation.mdc - ui-performance.mdc - ui-React-compone.ts.mdc - ui-styling.mdc - ui-testing.mdc - - workflow/ # Development workflow - automation.mdc - branches.mdc - workflow.mdc -``` - -## AI Fallback Rule - -If an AI agent cannot answer a question from .ts own memory-bank, it must first check the .rules directory, then the memory-bank directory. See [doc/ai-fallback.mdc](./doc/ai-fallback.mdc). - -## Main GitHub Repository - -HTTPS://github.com/LebedevIV/agent-plugins-platform-boilerplate - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name ->>>>>>> origin/develop - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name \ No newline at end of file diff --git a/.cursor/backup/rules/ai-index.mdc b/.cursor/backup/rules/ai-index.mdc deleted file mode 100644 index 823bcc83..00000000 --- a/.cursor/backup/rules/ai-index.mdc +++ /dev/null @@ -1,89 +0,0 @@ ---- -description: AI-optimized index of rules by priority and category -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI-Optimized Rules Index - -## Critical Priority Rules -*Must be applied to all code and decisions* - -- [Ai Index](ai-index.mdc) — AI-optimized index of rules by priority and category -- [Ai Memory](ai-memory.mdc) — User Commands and AI instructions -- [Ai Optimization](ai-optimization.mdc) — AI-specific instructions and optimizations for .cursor rules -- [Ai Memory](cursor-export/ai-memory.mdc) — User Commands and AI instructions -- [Environment](cursor-export/environment.mdc) — Critical environment constrai.ts - Node.js version, popup vs sIDEpanel - -## High Priority Rules -*Should be applied to most code and decisions* - -- [Architecture Chat System](architecture/architecture-chat-system.mdc) — Architecture rule for chat-system -- [Architecture Error Handling](architecture/architecture-error-handling.mdc) — Architecture rule for error-handling -- [Architecture Observability](architecture/architecture-observability.mdc) — Architecture rule for observability -- [Architecture Performance](architecture/architecture-performance.mdc) — Architecture rule for performance -- [Architecture Plugin](architecture/architecture-plugin.mdc) — Architecture rule for plugin -- [Architecture Project Structure](architecture/architecture-project-structure.mdc) — Architecture rule for project-structure -- [Architecture Security](architecture/architecture-security.mdc) — Architecture rule for security -- [Architecture Workflow](architecture/architecture-workflow.mdc) — Architecture rule for workflow -- [Project Architecture](architecture/project-architecture.mdc) — Architecture rule for project-architecture -- [Dev Principle 01 Do No Harm](cursor-export/dev/dev-principle-01-do-no-harm.mdc) — Development principle harm - dev principle 01 do no harm -- [Dev Principle 03 Best Practices](cursor-export/dev/dev-principle-03-best-practices.mdc) — Development principle practices - dev principle 03 best practices -- [Dev Principle 04 Fail Fast Safe](cursor-export/dev/dev-principle-04-fail-fast-safe.mdc) — Development principle safe - dev principle 04 fail fast safe -- [Dev Principle 05 Observability](cursor-export/dev/dev-principle-05-observability.mdc) — Development principle observability - dev principle 05 observability -- [Dev Principle 06 config As Code](cursor-export/dev/dev-principle-06-config-as-code.mdc) — Development principle code - dev principle 06 config as code -- [Dev Principle 07 Progressive Enhancement](cursor-export/dev/dev-principle-07-progressive-enhancement.mdc) — Development principle enhancement - dev principle 07 progressive enhancement -- [Dev Principle 08 data Integrity Privacy](cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc) — Development principle privacy - dev principle 08 data integrity privacy -- [Dev Principle 09 Continuous Learning](cursor-export/dev/dev-principle-09-continuous-learning.mdc) — Development principle learning - dev principle 09 continuous learning -- [Dev Principle 10 Ecosystem Thinking](cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc) — Development principle thinking - dev principle 10 ecosystem thinking - -## Medium Priority Rules -*Apply when relevant to the task* - -- [Ai Fallback](cursor-export/doc/ai-fallback.mdc) — Fallback procedures for AI -- [Ai First](cursor-export/doc/ai-first.mdc) — AI-oriented documentation standards -- .mdc File Standards](cursor-export/doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [Workflow](cursor-export/workflow/workflow.mdc) — Workflow rule for -- [Ai Answer Self Check](doc/ai-answer-self-check.mdc) — ai answer self check rule -- [Ci Automation](workflow/ci-automation.mdc) — Automation and synchronization rules -- [Experience Transfer](workflow/experience-transfer.mdc) — experience transfer rule - -## Normal Priority Rules -*Apply when appropriate* - -- [README](README.mdc) — Main project rules documentation and AI onboarding - structure, CLI scri.ts, usage -- [Development GuIDElines](cursor-export/dev/development-guIDElines.mdc) — General development guIDElines -- [Git Workflow](cursor-export/dev/git-workflow.mdc) — Git workflow rule - only develop to main, mandatory PR for all changes -- [Security And Deployment](cursor-export/dev/security-and-deployment.mdc) — Security rule for -and-deployment -- [Testing And Debugging](cursor-export/dev/testing-and-debugging.mdc) — Testing and debugging standards -- [TypeScript](cursor-export/dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Knowledge Map](cursor-export/doc/knowledge-map.mdc) — Project knowledge structure -- [Memorybank Quality](cursor-export/doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](cursor-export/doc/restore-context.mdc) — Context restoration procedures -- [Index](cursor-export/index.mdc) — index rule -- [Monorepo Best Practices](cursor-export/monorepo-best-practices.mdc) — Best practices for monorepo work - structure, dependencies, TypeScript, barrel expo.ts -- [Plugin Best Practices](cursor-export/plugin/plugin-best-practices.mdc) — Plugin standard for best-practices -- [Plugin Documentation](cursor-export/plugin/plugin-documentation.mdc) — Plugin standard for documentation -- [Plugin Error Handling](cursor-export/plugin/plugin-error-handling.mdc) — Plugin standard for error-handling -- [Plugin Performance](cursor-export/plugin/plugin-performance.mdc) — Plugin standard for performance -- [Plugin Security](cursor-export/plugin/plugin-security.mdc) — Plugin standard for security -- [Plugin Structure](cursor-export/plugin/plugin-structure.mdc) — Plugin standard for structure -- [Plugin Testing](cursor-export/plugin/plugin-testing.mdc) — Plugin standard for testing -- [Validation](cursor-export/security/validation.mdc) — Input validation and data sanitization rules -- [TypeScript build Troubleshooting](cursor-export/TypeScript-build-troubleshooting.mdc) — TypeScript build error troubleshooting guIDE - types, barrel expo.ts, dependencies, Tailwin.css -- [Ui Accessibility](cursor-export/ui/ui-accessibility.mdc) — UI standard for accessibility -- [Ui Animation](cursor-export/ui/ui-animation.mdc) — UI standard for animation -- [Ui Error Handling](cursor-export/ui/ui-error-handling.mdc) — UI standard for error-handling -- [Ui Forms](cursor-export/ui/ui-forms.mdc) — UI standard for forms -- [Ui Loading States](cursor-export/ui/ui-loading-States.mdc) — UI standard for loading-States -- [Ui Mobile](cursor-export/ui/ui-mobile.mdc) — UI standard for mobile -- [Ui Navigation](cursor-export/ui/ui-navigation.mdc) — UI standard for navigation -- [Ui Performance](cursor-export/ui/ui-performance.mdc) — UI standard for performance -- [Ui React Compone.ts](cursor-export/ui/ui-React-compone.ts.mdc) — UI standard for React-compone.ts -- [Ui Styling](cursor-export/ui/ui-styling.mdc) — UI standard for styling -- [Ui Testing](cursor-export/ui/ui-testing.mdc) — UI standard for testing -- [Automation](cursor-export/workflow/automation.mdc) — Automation and synchronization rules -- [Branches](cursor-export/workflow/branches.mdc) — Git branch management rules -- [Testing Troubleshooting](dev/testing-troubleshooting.mdc) — Testing and debugging standards diff --git a/.cursor/backup/rules/ai-memory.mdc b/.cursor/backup/rules/ai-memory.mdc deleted file mode 100644 index c74c3224..00000000 --- a/.cursor/backup/rules/ai-memory.mdc +++ /dev/null @@ -1,38 +0,0 @@ -# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/backup/rules/ai-optimization.mdc b/.cursor/backup/rules/ai-optimization.mdc deleted file mode 100644 index e7b985b1..00000000 --- a/.cursor/backup/rules/ai-optimization.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: AI-specific instructions and optimizations for .cursor rules -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Optimization Instructions - -## How AI Should Use These Rules - -### Priority Levels -- **critical**: Must be applied to all code and decisions -- **high**: Should be applied to most code and decisions -- **medium**: Apply when relevant to the task -- **normal**: Apply when appropriate - -### Categories -- **system-design**: Architecture and system structure -- **development-practices**: Coding standards and principles -- **documentation**: Documentation and communication -- **plugin-development**: Plugin-specific rules -- **security**: Security and safety rules -- **user-interface**: UI/UX standards -- **process-management**: Workflow and process rules -- **ai-optimization**: AI-specific optimizations - -### Usage Patterns -1. Always check critical rules first -2. Apply high-priority rules to most tasks -3. Use category-specific rules for targeted tasks -4. Reference related rules when making decisions - -### AI Memory Integration -- These rules are automatically loaded into AI memory-bank -- Use `alwaysApply: true` rules without explicit reference -- Reference specific rules when explaining decisions -- Update rules when patterns change or improve - -## Optimization Status -- ✅ All rules have proper metadata -- ✅ AI-specific tags added -- ✅ Priority levels assigned -- ✅ Categories defined -- ✅ Structure optimized for AI understanding diff --git a/.cursor/backup/rules/architecture/architecture-chat-system.mdc b/.cursor/backup/rules/architecture/architecture-chat-system.mdc deleted file mode 100644 index 3eb5b78c..00000000 --- a/.cursor/backup/rules/architecture/architecture-chat-system.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Chat System Architecture -- Per-page, per-plugin isolation: Ch.ts separated by page URL and plugin -- LRU cache: In-memory cache for performance (50 ch.ts) -- IndexedDB: Persistent storage for all ch.ts -- Real-time sync: Cross-tab communication for chat updates -- Cleanup: Automatic removal of ch.ts older than 90 days - -description: Chat system architecture and data flow -globs: - - platform-core/* - - chrome-extension/src/background/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-error-handling.mdc b/.cursor/backup/rules/architecture/architecture-error-handling.mdc deleted file mode 100644 index 7aef574e..00000000 --- a/.cursor/backup/rules/architecture/architecture-error-handling.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Error Handling (Architecture) -- Graceful Degradation: System continues working with reduced functionality -- User Feedback: Clear error messages and recovery options -- Logging: Structured logging for debugging and monitoring -- Fallbacks: Alternative implementations for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-error-handling.mdc - - architecture-security.mdc - -description: Error handling requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-observability.mdc b/.cursor/backup/rules/architecture/architecture-observability.mdc deleted file mode 100644 index 1f47a11f..00000000 --- a/.cursor/backup/rules/architecture/architecture-observability.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Monitoring and Observability (Architecture) -- Structured Logging: Consistent log format across compone.ts -- Performance Metrics: Track execution time and memory usage -- Error Tracking: Monitor and alert on critical errors -- User Analytics: Track feature usage and performance -- Health Checks: Monitor system health and dependencies - -related: - - architecture-performance.mdc - - dev-principle-05-observability.mdc - -description: Monitoring and observability requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-performance.mdc b/.cursor/backup/rules/architecture/architecture-performance.mdc deleted file mode 100644 index 636e5dc1..00000000 --- a/.cursor/backup/rules/architecture/architecture-performance.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Performance GuIDElines (Architecture) -- PyodIDE Loading: Lazy load runtime, cache resu.ts -- Memory Management: Clean up resources, monitor usage -- Bundle Size: Optimize for extension size lim.ts -- Caching: Use LRU cache for frequently accessed data -- Async Operations: Non-blocking UI, proper loading States - -related: - - plugin-performance.mdc - - architecture-observability.mdc - -description: Performance guIDElines for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-plugin.mdc b/.cursor/backup/rules/architecture/architecture-plugin.mdc deleted file mode 100644 index 3ef2571d..00000000 --- a/.cursor/backup/rules/architecture/architecture-plugin.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Plugin Architecture -- Manifest Structure: manifest.json + mcp_server.py +.icon.svg -- MCP Protocol: Standard MCP server implementation in Python -- Permission Model: Zero Trust - plugins not trusted by default -- Isolation: Each plugin in separate worker for security -- Communication: chrome.runtime.sendMessage for all cross-component communication - -description: Plugin architecture and isolation requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-project-structure.mdc b/.cursor/backup/rules/architecture/architecture-project-structure.mdc deleted file mode 100644 index 6220eeac..00000000 --- a/.cursor/backup/rules/architecture/architecture-project-structure.mdc +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Structure Architecture -- platform-core/: Main logic and services (keep boilerplate clean) -- chrome-extension/src/background/: Core background services -- pages/*/src/: UI compone.ts for different extension pages -- memory-bank/: Project documentation and context -- public/plugins/: Plugin implementations - -description: Directory and component structure for the project -globs: - - platform-core/* - - chrome-extension/src/background/* - - pages/*/src/* - - memory-bank/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-security.mdc b/.cursor/backup/rules/architecture/architecture-security.mdc deleted file mode 100644 index 9e8d63a1..00000000 --- a/.cursor/backup/rules/architecture/architecture-security.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Security Architecture -- Secret Manager: Encrypted storage of API keys in chrome.storage.local -- Network Guard: Whitelist domains for API calls -- Audit System: Log all plugin activities and network reque.ts -- Parameter Validation:.json Schema validation for all inp.ts -- Rate Limiting: Prevent abuse and suspicious activity - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Security architecture and controls -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/architecture-workflow.mdc b/.cursor/backup/rules/architecture/architecture-workflow.mdc deleted file mode 100644 index 401f5e21..00000000 --- a/.cursor/backup/rules/architecture/architecture-workflow.mdc +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Development Workflow (Architecture) -- Feature Branches: Always create from develop branch -- Testing: Test before commit, use --no-verify if ESLint issues -- Documentation: Update memory-bank for all architectural decisions -- Comm.ts: Use semantic commit messages -- Code Quality: TypeScript for new files, ESLint compliance - -description: Development workflow and process standards -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* - - memory-bank/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/architecture/file-relationships.mdc b/.cursor/backup/rules/architecture/file-relationships.mdc deleted file mode 100644 index 2376e599..00000000 --- a/.cursor/backup/rules/architecture/file-relationships.mdc +++ /dev/null @@ -1,339 +0,0 @@ ---- -description: File organization patterns and relationships - universal project structure guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Карта взаимосвязей файлов - -## File Structure и их Purpose - -### 1. Background Layer (Слой фоновых сервисов) - -#### `chrome-extension/src/background/plugin-chat-cache.ts` -**Purpose:** Центральный сервис управления чатами -**Dependencies:** -- `plugin-chat-API.ts` - для работы с IndexedDB -- Chrome Extensions API (chrome.runtime, chrome.tabs) - -**Экспортирует:** -- `PluginChatcache` class -- `Chatdata`, `ChatMessage` interfaces - -**Используется:** -- `background/index.ts` - создание экземпляра и регистрация обработчиков - -**Ключевые Methodы:** -```TypeScript -class PluginChatcache { - getChat(pluginId: string, pageKey: string): Promise - saveMessage(pluginId: string, pageKey: string, message: ChatMessage): Promise - deleteChat(pluginId: string, pageKey: string): Promise - getAllCh.ts(): Promise - cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/plugin-chat-API.ts` -**Purpose:** Abstraction над IndexedDB -**Dependencies:** -- IndexedDB API -- Chrome Extensions API - -**Экспортирует:** -- `Ch.tstorageAPI` class -- database utility functions - -**Используется:** -- `plugin-chat-cache.ts` - для всех операций с Storageм - -**Ключевые Methodы:** -```TypeScript -class Ch.tstorageAPI { - async getChat(pluginId: string, pageKey: string): Promise - async saveChat(chatdata: Chatdata): Promise - async deleteChat(pluginId: string, pageKey: string): Promise - async getAllCh.ts(): Promise - async cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/index.ts` -**Purpose:** Точка входа background script -**Dependencies:** -- `plugin-chat-cache.ts` - создание экземпляра cacheа -- Chrome Extensions API - -**Используется:** -- Chrome Extensions runtime (автоматически) - -**Основные функции:** -- Инициализация `PluginChatcache` -- Регистрация обработчиков сообщений -- Управление жизненным циклом - -**Обработчики сообщений:** -```TypeScript -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - switch (request.type) { - case 'GET_PLUGIN_CHAT': - case 'SAVE_PLUGIN_CHAT_MESSAGE': - case 'DELETE_PLUGIN_CHAT': - case 'GET_ALL_PLUGIN_CH.ts': - } -}); -``` - -### 2. UI Layer (Слой пользовательского Interfaceа) - -#### `pages/sIDE-panel/src/compone.ts/PluginControlPanel.tsx` -**Purpose:** Основной компонент управления плагином -**Dependencies:** -- React hooks (useState, useEffect, useRef) -- Chrome Extensions API (chrome.runtime) -- `PluginDetails.tsx` - для отображения деталей плагина - -**Используется:** -- `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` - основной контейнер - -**Ключевые состояния:** -```TypeScript -const [messages, setMessages] = useState([]); -const [loading, setLoading] = useState(false); -const [syncStatus, s.tsyncStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); -``` - -**Основные функции:** -- Загрузка чата при монтировании -- Сохранение сообщений -- Synchronization с другими вкладками -- Экспорт и очистка чатов - -#### `pages/sIDE-panel/src/compone.ts/PluginDetails.tsx` -**Purpose:** Отображение деталей плагина -**Dependencies:** -- React -- Plugin interface - -**Используется:** -- `PluginControlPanel.tsx` - как дочерний компонент - -#### `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` -**Purpose:** Основной контейнер sIDE panel -**Dependencies:** -- `PluginControlPanel.tsx` -- `PluginCard.tsx` -- React - -**Используется:** -- Chrome Extensions sIDE panel API - -### 3. DevTools Layer (Слой инструментов разработчика) - -#### `pages/devtools-panel/src/compone.ts/PluginCh.tsTab.tsx` -**Purpose:** Административный Interface для управления чатами -**Dependencies:** -- React hooks -- Chrome Extensions API -- `file-saver` library - -**Используется:** -- `pages/devtools-panel/src/index.tsx` - как вкладка в DevTools - -**Основные функции:** -- Отображение всех чатов -- Удаление чатов -- Экспорт в.json -- Фильтрация и поиск - -#### `pages/devtools-panel/src/index.tsx` -**Purpose:** Точка входа DevTools panel -**Dependencies:** -- `PluginCh.tsTab.tsx` -- React - -**Используется:** -- Chrome DevTools API - -### 4. Shared Layer (Общий слой) - -#### `chrome-extension/src/background/types.ts` (предлагаемый) -**Purpose:** Общие Typeы для всей системы -**Dependencies:** -- TypeScript - -**Экспортирует:** -```TypeScript -export interface ChatMessage { - id: string; - role: 'user' | 'assistant'; - content: string; - timestamp: number; -} - -export interface Chatdata { - pluginId: string; - pageKey: string; - messages: ChatMessage[]; - lastUpdated: number; - createdAt: number; -} - -export type ChatRequest = - | { type: 'GET_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'SAVE_PLUGIN_CHAT_MESSAGE'; pluginId: string; pageKey: string; message: ChatMessage } - | { type: 'DELETE_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'GET_ALL_PLUGIN_CH.ts' }; - -export type ChatEvent = - | { type: 'PLUGIN_CHAT_UPDATED'; pluginId: string; pageKey: string; messages?: ChatMessage[] }; -``` - -**Используется:** -- Все компоненты системы чатов - -## Threads данных между файлами - -### 1. Загрузка чата -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getChat) -plugin-chat-cache.ts - ↓ (storageAPI.getChat) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -### 2. Сохранение сообщения -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.saveMessage) -plugin-chat-cache.ts - ↓ (storageAPI.saveChat + event broadcast) -plugin-chat-API.ts + chrome.tabs.query - ↓ (IndexedDB + other tabs) -Browser Storage + Other UI Compone.ts -``` - -### 3. Административные операции -``` -PluginCh.tsTab.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getAllCh.ts/deleteChat) -plugin-chat-cache.ts - ↓ (storageAPI) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -## Dependencies и импорты - -### Background Layer -```TypeScript -// background/index.ts -import { PluginChatcache } from './plugin-chat-cache'; -import type { ChatRequest, ChatEvent } from './types'; - -// plugin-chat-cache.ts -import { Ch.tstorageAPI } from './plugin-chat-API'; -import type { Chatdata, ChatMessage } from './types'; - -// plugin-chat-API.ts -// Нет внешних зависимостей, только IndexedDB API -``` - -### UI Layer -```TypeScript -// PluginControlPanel.tsx -import { PluginDetails } from './PluginDetails'; -import type { ChatMessage } from '../../../background/types'; - -// PluginDetails.tsx -import type { Plugin } from './PluginCard'; - -// SIDEPanel.tsx -import { PluginControlPanel } from './PluginControlPanel'; -import { PluginCard } from './PluginCard'; -``` - -### DevTools Layer -```TypeScript -// PluginCh.tsTab.tsx -import { saveAs } from 'file-saver'; -import type { Chatdata } from '../../../background/types'; - -// devtools-panel/index.tsx -import { PluginCh.tsTab } from './compone.ts/PluginCh.tsTab'; -``` - -## configuration Files - -### `chrome-extension/manifest.json` -**Purpose:** configuration Extensions -**Содержит:** -- Background script registration -- SIDE panel configuration -- DevTools panel configuration -- Permissions - -### `package.json` (в каждой папке pages) -**Purpose:** Dependencies и скрипты сборки -**Содержит:** -- React dependencies -- build tools -- TypeScript configuration - -## Стили и ресурсы - -###.css файлы -- `PluginControlPanel.css` - стили для основного компонента -- `SIDEPanel.css` - стили для sIDE panel -- `PluginCard.css` - стили для карточек плагинов - -### Иконки и изображения -- .icon.svg` - иконки плагинов -- ЛогоTypeы и брендинг - -## Тестовые файлы - -### Unit Te.ts -- `__te.ts__/plugin-chat-cache.test.ts` -- `__te.ts__/plugin-chat-API.test.ts` -- `__te.ts__/PluginControlPanel.test.tsx` - -### Integration Te.ts -- `__te.ts__/chat-system.integration.test.ts` - -### E2E Te.ts -- `te.ts/e2e/chat-functionality.test.ts` - -## Documentation - -### Техническая Documentation -- `README.md` - общее Description -- `API.md` - Documentation API -- `ARCHITECTURE.md` - архитектурное Description - -### Пользовательская Documentation -- `USER_GUIDE.md` - руководство пользователя -- `DEVELOPER_GUIDE.md` - руководство разработчика - -## Заключение - -Система чатов плагинов имеет четкую модульную архитектуру с разделением ответственности: - -1. **Background Layer** - управление данными и бизнес-логика -2. **UI Layer** - пользовательский Interface -3. **DevTools Layer** - административные функции -4. **Shared Layer** - общие Typeы и утилиты - -Все компоненты связаны через messaging API, что обеспечивает loose coupling и легкую тестируемость. Каждый файл имеет четко определенную ответственность и минимальные Dependencies. \ No newline at end of file diff --git a/.cursor/backup/rules/architecture/project-architecture.mdc b/.cursor/backup/rules/architecture/project-architecture.mdc deleted file mode 100644 index e9846dba..00000000 --- a/.cursor/backup/rules/architecture/project-architecture.mdc +++ /dev/null @@ -1,47 +0,0 @@ ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Overview -Agent-Plugins-Platform (APP) is a browser extension for running Python plugins via PyodIDE and the MCP protocol. Key priorities: security, performance, and developer experience. - -## Architecture Patterns - -### Core Compone.ts -- **Plugin Manager** (`core/plugin-manager.js`): manages plugin lifecycle -- **Host API** (`core/host-API.js`): provIDEs Python access to browser APIs -- **Workflow Engine** (`core/workflow-engine.js`): executes plugin workflows -- **MCP Bridge** (`Bridge/mcp-Bridge.js`):.js ↔ Python communication -- **PyodIDE Worker** (`Bridge/pyodIDE-worker.js`): isolated Python execution - -### Plugin Structure -``` -public/plugins/plugin-name/ -├── manifest.json # Metadata and permissions -├── mcp_server.py # MCP protocol (Python) -├── workflow.json # Workflow description -└──.icon.svg #.icon -``` - -### Communication Flow -1. UI → Plugin Manager → MCP Bridge → PyodIDE Worker → Python Plugin -2. Python Plugin → Host API → Browser APIs -3. Resu.ts return via the same path -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md b/.cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md deleted file mode 100644 index cb3f3798..00000000 --- a/.cursor/backup/rules/cursor-export/IMPORT-INSTRUCTIONS.md +++ /dev/null @@ -1,60 +0,0 @@ -# Import .cursor Rules - -## Quick Import - -1. Copy the entire `cursor-export` folder to your target project -2. Run the import script: - ```bash - node cursor-export/import-cursor..js - ``` - -## Manual Import - -1. Create `.cursor/rules/` directory in your target project -2. Copy files from `cursor-export/` to `.cursor/rules/` -3. Run audit and optimization: - ```bash - cd .cursor/rules - node cursor-manager..js full - ``` - -## What's Included - -### Core Rules -- AI memory and Commands -- Environment constrai.ts -- Project structure guIDElines -- TypeScript build troubleshooting - -### Categories -- **architecture/** - System architecture rules -- **dev/** - Development principles and guIDElines -- **doc/** - Documentation standards -- **plugin/** - Plugin development standards -- **security/** - Security rules -- **ui/** - UI/UX standards -- **workflow/** - Development workflow - -### Automation -- Audit system -- Auto-fix capabilities -- AI optimization -- Status monitoring - -## Customization - -After import, customize rules for your project: -1. Update `environment.mdc` with your project constrai.ts -2. Modify `ai-memory.mdc` with project-specific Commands -3. Adjust `monorepo-best-practices.mdc` for your structure -4. Run `node cursor-manager..js optimize` to apply changes - -## Verification - -Verify successful import: -```bash -cd .cursor/rules -node cursor-manager..js status -``` - -All files should show as "AI-ready" with no issues. diff --git a/.cursor/backup/rules/cursor-export/ai-memory.mdc b/.cursor/backup/rules/cursor-export/ai-memory.mdc deleted file mode 100644 index 3a7e484d..00000000 --- a/.cursor/backup/rules/cursor-export/ai-memory.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: Universal user Commands for AI assistant - complete Command reference with triggers and actions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: general ---- - -# AI Memory Bank - Universal User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc deleted file mode 100644 index efca4815..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 1: Do No Harm -- Security first: Any changes must improve system security, not weaken it -- Backward compatibility: Changes must not break existing functionality -- Gradual implementation: Implement changes step by step with rollback capability -- Testing: All changes must pass thorough testing before deployment -- Monitoring: Track impact of changes on performance and stability - -description: 'Do No Harm' principle for all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc deleted file mode 100644 index 7d2b513f..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-03-best-practices.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 3: Best Practices First -- Architectural patterns: Use proven architectural patterns and principles -- Coding standards: Follow accepted standards and conventions -- Security: Apply security principles (Zero Trust, Defense in Depth, Least Privilege) -- Performance: Optimize critical paths and avoid anti-patterns -- Scalability: Design consIDEring future growth and changes -- Testability: Write code that is easy to test and maintain - -description: Prioritize best practices in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc deleted file mode 100644 index 743a5fa7..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 4: Fail Fast, Fail Safe -- Early error detection: Validate at input, not during execution -- Graceful Degradation: System continues working even with partial failures -- Circuit breaker: Automatic shutdown of problematic compone.ts -- Rollback capability: Quick rollback to working State -- Error boundaries: Isolate errors at component level - -description: Fail fast and fail safe in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc deleted file mode 100644 index d46bceaa..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-05-observability.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 5: Observability First -- Structured logging: Structured logging for analysis -- Metrics everywhere: Performance and State metrics -- distributed tracing: Track reque.ts through all compone.ts -- Health checks: Monitor State of all services -- Debug information: Sufficient information for problem diagnosis - -description: Observability and monitoring in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc deleted file mode 100644 index 90cf296d..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 6: configuration as Code -- Version-controlled configs: All configurations in version control -- Environment-specific: Different configurations for different environme.ts -- Validation: Automatic configuration validation -- Documentation: Document all configuration parameters -- Default safety: Safe default values - -description: configuration as code for all environme.ts -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc deleted file mode 100644 index 4cb14008..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 7: Progressive Enhancement -- Core functionality: Basic functionality always works -- Feature detection: Detect browser capabilities -- Graceful Degradation: Degrade functionality, not complete failure -- Performance budget: Performance budget for new features -- Accessibility baseline: Minimum accessibility level for all - -description: Progressive enhancement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc deleted file mode 100644 index 33209d0d..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 8: data Integrity & Privacy -- data validation: Validate all input data -- Encryption at rest: Encrypt data at rest -- Encryption in transit: Encrypt data in transit -- data minimization: Collect only necessary data -- User consent: Explicit user consent for data processing -- Right to be forgotten: Ability to delete user data - -description: data integrity and privacy in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc deleted file mode 100644 index f298f32d..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 9: Continuous Learning -- Performance monitoring: Continuous performance monitoring -- User feedback loops: User feedback -- A/B testing: Test hypotheses on real users -- Analytics insig.ts: Usage analysis for improveme.ts -- Knowledge sharing: Document.lessons and insig.ts - -description: Continuous learning and improvement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc b/.cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc deleted file mode 100644 index 5a16b370..00000000 --- a/.cursor/backup/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 10: Ecosystem Thinking -- Plugin compatibility: Ensure plugin compatibility -- API stability: Stable public APIs -- Backward compatibility: Backward version compatibility -- Community building: Support developer community -- Documentation quality: Quality documentation for ecosystem - -description: Ecosystem thinking in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/development-guidelines.mdc b/.cursor/backup/rules/cursor-export/dev/development-guidelines.mdc deleted file mode 100644 index 535e47d1..00000000 --- a/.cursor/backup/rules/cursor-export/dev/development-guidelines.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: General development guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: false -aiCategory: development-practices--- - -# Development GuIDElines - -## JavaScript/TypeScript -- Use ES6+ and async/await. -- Modular architecture, clear separation of concerns. -- Error handling and structured logging are required. -- Use TypeScript for type safety. -- Prefer functional patterns where possible. - -## Python (Plugins) -- Use MCP protocol for communication. -- Use async/await for I/O operations. -- Always validate inp.ts and handle errors. -- Plugins should be single-purpose. -- Document all plugin APIs clearly. - -# Security First -- Validate manife.ts and permissions. -- Sanitize data between.js and Python. -- Sandbox all plugins. -- Apply the principle of least privilege. -- Audit all plugin code. - -# Performance ConsIDErations -- Lazy-load plugins and PyodIDE when possible. -- cache repeated operations. -- Monitor memory usage and clean up resources. -- Optimize bundle size and loading time. -- Use WebWorkers for non-blocking tasks. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/git-workflow.mdc b/.cursor/backup/rules/cursor-export/dev/git-workflow.mdc deleted file mode 100644 index 8dadc7b2..00000000 --- a/.cursor/backup/rules/cursor-export/dev/git-workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git workflow rule - только develop в main, обязательные PR для всех изменений -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Git Workflow Rule - -**Rule:** -- Only merge into main from develop. -- All feature, fix, and doc branches must be merged into develop first, never directly into main. -- This rule is mandatory for all changes and pull reque.ts. - ---- -## Enforcement -- Local pre-push hook (.husky/pre-push) blocks direct push to main and develop. -- GitHub branch protection preve.ts direct push and enforces PRs from develop only. ---- \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc b/.cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc deleted file mode 100644 index d5f45cd4..00000000 --- a/.cursor/backup/rules/cursor-export/dev/security-and-deployment.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Security rule for -and-deployment -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Security Best Practices - -## Plugin Validation -- Validate plugin manife.ts. -- Check all permissions. -- Scan for malicious code. -- Verify plugin signatures. - -## Runtime Security -- Sandbox all plugins. -- Validate all input data. -- Monitor plugin behavior. -- Apply rate limiting. - -## data Protection -- Sanitize all user data. -- Encrypt sensitive information. -- Ensure secure communication. -- Audit all data access. - -# Deployment ConsIDErations - -## Extension distribution -- Follow Chrome Web Store guIDElines. -- Use a secure update mechanism. -- ProvIDE a transparent privacy policy. -- Maintain user support channels. - -## Plugin distribution -- Use a marketplace for plugins. -- Validate and manage plugin versions. -- Perform security scanning. - -## Monitoring and Analytics -- Track plugin usage. -- Monitor performance. -- Collect error repo.ts. -- Analyze user feedback. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc b/.cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc deleted file mode 100644 index 3008bfad..00000000 --- a/.cursor/backup/rules/cursor-export/dev/testing-and-debugging.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing Strategy - -## Unit Testing -- Test compone.ts in isolation. -- Mock external dependencies. -- Validate error handling. -- Test security boundaries. - -## Integration Testing -- Test plugin loading and execution. -- Validate.js-Python communication. -- Test integration with browser APIs. -- Check permission compliance. - -## End-to-End Testing -- Test full plugin workflows. -- Validate user scenarios. -- Test extension installation and updates. -- Check cross-browser compatibility. - -# Debugging GuIDElines - -## JavaScript Debugging -- Use DevTools for extension debugging. -- Log message flow. -- Use breakpoi.ts for complex logic. -- Monitor WebWorker communication. - -## Python Debugging -- Use print/logging. -- Test plugins in isolation. -- Use PyodIDE console for debugging. - -## Common Issues -- PyodIDE startup: use loading indicators. -- Memory leaks: monitor worker lifecycle. -- Permission errors: validate manife.ts. -- Communication failures: check MCP format. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/dev/typescript.mdc b/.cursor/backup/rules/cursor-export/dev/typescript.mdc deleted file mode 100644 index 7a1218c4..00000000 --- a/.cursor/backup/rules/cursor-export/dev/typescript.mdc +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: TypeScript-specific guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Rule -- Use only TypeScript for all new files. -- Enforce strict typing, avoid using `any`. -- Use modern language features (optional chaining, nullish coalescing, etc.). -- Always follow the shared.tsconfig from `packages.tsconfig/`. - -# examples -- ✅ `const foo: string = "bar"` -- ❌ `let x: any = 5` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/ai-fallback.mdc b/.cursor/backup/rules/cursor-export/doc/ai-fallback.mdc deleted file mode 100644 index b9192a53..00000000 --- a/.cursor/backup/rules/cursor-export/doc/ai-fallback.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Fallback procedures for AI -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: Правило fallback для AI - Priority источников информации при отсутствии ответа в memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI Fallback Rule - -**Правило:** -Если возникает вопрос, на который нет ответа в AI memory-bank, необходимо: -1. Сначала обращаться к сведениям в папке .rules проекта. -2. Если нужной информации нет в .rules, обращаться к сведениям в папке memory-bank проекта (особенно к архитектурным и контекстным файлам). - -**Priority:** -Это правило Priorityно для всех будущих консультаций и автоматизаций. \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/doc/ai-first.mdc b/.cursor/backup/rules/cursor-export/doc/ai-first.mdc deleted file mode 100644 index 20ea682c..00000000 --- a/.cursor/backup/rules/cursor-export/doc/ai-first.mdc +++ /dev/null @@ -1,69 +0,0 @@ ---- -description: AI-oriented documentation standards -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: AI-First Documentation Standards - analytical comme.ts and AI-oriented documentation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI-First Documentation Standards - -## Основные Principles -- **Analytical comme.ts**: Add comme.ts explaining logic and architectural decisions for AI understanding -- **Context explanations**: Explain "why", not just "what" the code does -- **Architectural comme.ts**: Document component relationships and data flows -- **Business logic**: Explain complex business logic and decisions made -- **TODO comme.ts**: Leave TODO with explanation of planned improveme.ts - -## examples правильного использования - -### ✅ Аналитические Comme.ts: -```TypeScript -// AI: This function is required for plugin isolation - preve.ts direct DOM access -function createSandboxedEnvironment() { - // Implementation... -} - -/** - * AI: Example usage - this component handles plugin communication - * through a secure message passing system - */ -export function PluginBridge() { - // Implementation... -} -``` - -### ✅ Объяснение контекста: -```TypeScript -// AI: Using setTimeout here because Chrome extension APIs are async -// and we need to ensure DOM is ready before accessing eleme.ts -setTimeout(() => { - initializePlugin(); -}, 100); -``` - -### ✅ TODO с объяснением: -```TypeScript -// TODO: AI - Replace with Web Workers for better performance -// Current implementation blocks main thread during heavy computations -function proceSSLargedataset(data) { - // Implementation... -} -``` - -## Recommendations -1. **Всегда объясняйте "почему"** - не только что делает код -2. **Используйте префикс "AI:"** для комментариев, предназначенных для AI -3. **Документируйте архитектурные решения** и их обоснование -4. **Объясняйте сложную бизнес-логику** с примерами -5. **Оставляйте TODO с контекстом** для будущих улучшений diff --git a/.cursor/backup/rules/cursor-export/doc/knowledge-map.mdc b/.cursor/backup/rules/cursor-export/doc/knowledge-map.mdc deleted file mode 100644 index f3bdcf53..00000000 --- a/.cursor/backup/rules/cursor-export/doc/knowledge-map.mdc +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Project knowledge structure -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Knowledge Map - -- USER_CommandS: USER_CommandS.md — все пользовательские команды -- graveyard: memory-bank/graveyard.md — ошибки, environment-specific issues -- architecture: memory-bank/codebase-architecture.md — архитектурные решения, Patterns, Dependencies -- meta: memory-bank/README.md — мета-правила, структура, универсальные инструкции -- dev-principles: memory-bank/DEV_PRACTICES.md — best practices для разработки -- security: memory-bank/SECURITY.md — Standards безопасности -- knowledge-map: memory-bank/KNOWLEDGE_MAP.md — карта знаний -- progress: memory-bank/progress.md — Status и история -- activeContext: memory-bank/activeContext.md — актуальный контекст -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc b/.cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc deleted file mode 100644 index 5d93b321..00000000 --- a/.cursor/backup/rules/cursor-export/doc/mdc-file-standards.mdc +++ /dev/null @@ -1,134 +0,0 @@ ---- -description: Standards for .mdc file creation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# Standards создания правил в .cursor/rules - -## Priority .mdc файлов над .md - -### Required использовать .mdc для: -- **Правил и стандартов** - все правила должны быть в .mdc -- **AI инструкций** - команды, Principles, Constrai.ts -- **Архитектурных решений** - структура, Patterns, best practices -- **Автоматизации** - скрипты, workflow, Processes - -### Допустимо использовать .md для: -- **Чистой документации** - README, guIDEs, tutorials -- **Временных заметок** - черновики, эксперименты -- **Внешних ссылок** - ссылки на внешние ресурсы - -## Структура .mdc файла - -### Обязательные метаdata: -``.yaml ---- -description: Краткое Description назначения файла -globs: ["**/*"] # Patterns файлов для применения -alwaysApply: true # Автоматическое применение ---- -``` - -### Дополнительные метаdata: -``.yaml ---- -description: Description -globs: ["pages/**/*", "packages/**/*"] -alwaysApply: false # Применяется по запросу -related: - - other-rule.mdc - - another-rule.mdc ---- -``` - -## Преимущества .mdc файлов - -### 1. Автоматическое применение -- Cursor читает метаdata при создании чата -- Правила применяются без явного обращения -- `alwaysApply: true` обеспечивает постоянное Action - -### 2. Лучшая Integration с AI -- AI понимает контекст применения через `globs` -- Description помогает AI выбрать подходящие правила -- Структурированные метаdata улучшают понимание - -### 3. Приоритизация и организация -- `alwaysApply` определяет Importantсть правила -- `related` связывает связанные правила -- `globs` ограничивает область применения - -### 4. AI memory-bank Integration -- Правила автоматически попадают в AI memory-bank -- Доступны через Settings / Rules & Memories -- Сохраняются между сессиями - -## Миграция .md → .mdc - -### Когда мигрировать: -- Файл содержит правила или инструкции -- Файл должен применяться автоматически -- Файл важен для AI понимания проекта - -### Процесс миграции: -1. Переименовать `.md` → `.mdc` -2. Add.yaml frontmatter с метаданными -3. Убедиться в корректности `globs` и `alwaysApply` -4. Check интеграцию с AI memory-bank - -## examples правильного использования - -### ✅ Правильно - .mdc с метаданными: -``.yaml ---- -description: Правила TypeScript - configuration, barrel expo.ts, troubleshooting -globs: ["**/*.ts", "**/*.tsx", "**/*.json"] -alwaysApply: true ---- -``` - -### ❌ Неправильно - .md без метаданных: -```markdown -# TypeScript Rules -- Use strict mode -- Prefer barrel expo.ts -``` - -## Exceptions - -### Допустимые .md файлы: -- `README.md` - главная Documentation проекта -- `CHANGELOG.md` - история изменений -- `LICENSE.md` - лицензия -- Временные файлы с префиксом `temp-` или `draft-` - -## Verification соответствия - -### Автоматическая Verification: -```bash -# Найти все .md файлы в .cursor/rules -find .cursor/rules -name "*.md" -not -name "README.md" - -# Check структуру .mdc файлов -node .cursor/rules/check-rules-structure..js -``` - -### Ручная Verification: -1. Все ли правила в .mdc формате? -2. Есть ли метаdata во всех .mdc файлах? -3. Корректны ли `globs` и `alwaysApply`? -4. Нужно ли мигрировать какие-то .md файлы? - -## Recommendations - -1. **Всегда начинать с .mdc** для новых правил -2. **Мигрировать важные .md** файлы в .mdc -3. **Проверять метаdata** при создании правил -4. **Использовать `alwaysApply: true`** для критических правил -5. **Документировать Exceptions** в README.mdc -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc b/.cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc deleted file mode 100644 index 0e2c801c..00000000 --- a/.cursor/backup/rules/cursor-export/doc/memorybank-quality.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Quality standards for memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Memory-Bank Quality Checklist - -- Актуальность: информация своевременно обновляется -- Полнота: все ключевые аспекты проекта отражены -- Нет дублирования: информация не повторяется в разных файлах -- Навигация: структура и ссылки понятны -- История изменений: все важные правки фиксируются -- Прозрачность: причины изменений и удаления всегда документируются -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/doc/restore-context.mdc b/.cursor/backup/rules/cursor-export/doc/restore-context.mdc deleted file mode 100644 index cbee881a..00000000 --- a/.cursor/backup/rules/cursor-export/doc/restore-context.mdc +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: Context restoration procedures -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Восстанови контекст - -1. Прочитать все файлы в директории `memory-bank` и `USER_CommandS.md`. -2. Сделать краткое резюме ключевых положений, правил, пользовательских требований и текущего Statusа проекта. -3. Использовать эту информацию для всех последующих ответов и действий. - -> Это правило предназначено для ручного вызова или использования агентом при необходимости полного восстановления контекста проекта. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/environment.mdc b/.cursor/backup/rules/cursor-export/environment.mdc deleted file mode 100644 index 9fb92cb1..00000000 --- a/.cursor/backup/rules/cursor-export/environment.mdc +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Critical environment constrai.ts - Node.js версия, popup vs sIDEpanel -globs: ["**/*"] -alwaysApply: trueaiPriority: critical -aiCategory: general ---- - -# Critical environment constrai.ts - -- **popup не используется**, основной Interface — **sIDEpanel**. Все ошибки popup можно игнорировать, если они не влияют на работу sIDEpanel. - -- **Использовать только Node.js версии 22.17.1 и выше**. Понижать версию запрещено, все баги и инфраструктурные проблемы решать только в этом контексте. \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/index.mdc b/.cursor/backup/rules/cursor-export/index.mdc deleted file mode 100644 index d0272f95..00000000 --- a/.cursor/backup/rules/cursor-export/index.mdc +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: index rule -globs: ["**/*"] -alwaysApply: true -aiCategory: general--- - -# Index of Modular Rules - -## root -- [AI Memory Bank](ai-memory.mdc) — User Commands and AI instructions -- [Environment Constrai.ts](environment.mdc) — Critical environment limitations -- [Monorepo Best Practices](monorepo-best-practices.mdc) — Monorepo structure and guIDElines -- [TypeScript build Troubleshooting](TypeScript-build-troubleshooting.mdc) — TypeScript build error solutions -- [README](README.mdc) — Main documentation and structure - -## architecture -- [Chat System Architecture](architecture/architecture-chat-system.mdc) — Chat system architecture and data flow -- [Error Handling (Architecture)](architecture/architecture-error-handling.mdc) — Error handling requireme.ts for system architecture -- [Monitoring and Observability (Architecture)](architecture/architecture-observability.mdc) — Monitoring and observability requireme.ts for system architecture -- [Performance GuIDElines (Architecture)](architecture/architecture-performance.mdc) — Performance guIDElines for system architecture -- [Plugin Architecture](architecture/architecture-plugin.mdc) — Plugin architecture and isolation requireme.ts -- [Project Structure Architecture](architecture/architecture-project-structure.mdc) — Directory and component structure for the project -- [Security Architecture](architecture/architecture-security.mdc) — Security architecture and controls -- [Development Workflow (Architecture)](architecture/architecture-workflow.mdc) — Development workflow and process standards -- [Project Overview](architecture/project-architecture.mdc) - -## dev -- [Development Principle 1: Do No Harm](dev/dev-principle-01-do-no-harm.mdc) — 'Do No Harm' principle for all development -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation and comme.ts for all code -- [Development Principle 3: Best Practices First](dev/dev-principle-03-best-practices.mdc) — Prioritize best practices in all development -- [Development Principle 4: Fail Fast, Fail Safe](dev/dev-principle-04-fail-fast-safe.mdc) — Fail fast and fail safe in all development -- [Development Principle 5: Observability First](dev/dev-principle-05-observability.mdc) — Observability and monitoring in all development -- [Development Principle 6: configuration as Code](dev/dev-principle-06-config-as-code.mdc) — configuration as code for all environme.ts -- [Development Principle 7: Progressive Enhancement](dev/dev-principle-07-progressive-enhancement.mdc) — Progressive enhancement in all development -- [Development Principle 8: data Integrity & Privacy](dev/dev-principle-08-data-integrity-privacy.mdc) — data integrity and privacy in all development -- [Development Principle 9: Continuous Learning](dev/dev-principle-09-continuous-learning.mdc) — Continuous learning and improvement in all development -- [Development Principle 10: Ecosystem Thinking](dev/dev-principle-10-ecosystem-thinking.mdc) — Ecosystem thinking in all development -- [Development GuIDElines](dev/development-guIDElines.mdc) — General development guIDElines -- [Security Best Practices](dev/security-and-deployment.mdc) — Security and deployment standards -- [Testing Strategy](dev/testing-and-debugging.mdc) — Testing and debugging approaches -- [TypeScript Rules](dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Git Workflow](dev/git-workflow.mdc) — Git workflow and branching rules - -## doc -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation standards -- [Knowledge Map](doc/knowledge-map.mdc) — Project knowledge structure -- [Memory-Bank Quality Checklist](doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](doc/restore-context.mdc) — Context restoration procedures -- .mdc File Standards](doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [AI Fallback Rule](doc/ai-fallback.mdc) — Fallback procedures for AI - -## plugin -- [Plugin Best Practices](plugin/plugin-best-practices.mdc) — Best practices for plugin development -- [Plugin Documentation Standards](plugin/plugin-documentation.mdc) — Documentation standards for all plugins -- [Plugin Error Handling](plugin/plugin-error-handling.mdc) — Error handling requireme.ts for all plugins -- [Plugin Performance GuIDElines](plugin/plugin-performance.mdc) — Performance guIDElines for all plugins -- [Plugin Security Requireme.ts](plugin/plugin-security.mdc) — Security requireme.ts for all plugins -- [Plugin Structure](plugin/plugin-structure.mdc) — Plugin directory and manifest requireme.ts -- [Plugin Testing Requireme.ts](plugin/plugin-testing.mdc) — Testing requireme.ts for all plugins - -## security -- [Input Validation](security/validation.mdc) — Input validation and data sanitization rules - -## ui -- [Accessibility (A11y) Standards](ui/ui-accessibility.mdc) — Accessibility requireme.ts for all UI compone.ts -- [UI Animation and Transitions](ui/ui-animation.mdc) — Animation and transition standards for all UI compone.ts -- [UI Error Handling](ui/ui-error-handling.mdc) — Error handling requireme.ts for all UI compone.ts -- [UI Form Standards](ui/ui-forms.mdc) — Form standards for all UI compone.ts -- [UI Loading States](ui/ui-loading-States.mdc) — Loading State requireme.ts for all UI compone.ts -- [UI Mobile Responsiveness](ui/ui-mobile.mdc) — Mobile responsiveness standards for all UI compone.ts -- [UI Navigation Standards](ui/ui-navigation.mdc) — Navigation standards for all UI compone.ts -- [UI Performance Standards](ui/ui-performance.mdc) — Performance standards for all UI compone.ts -- [React Component Standards](ui/ui-React-compone.ts.mdc) — Standards for React component structure in UI -- [UI Styling Standards](ui/ui-styling.mdc) — Styling standards for all UI compone.ts -- [UI Testing Standards](ui/ui-testing.mdc) — Testing standards for all UI compone.ts - -## workflow -- [Automation Rules](workflow/automation.mdc) — Automation and synchronization rules -- [Branch Management](workflow/branches.mdc) — Git branch management rules -- [Workflow Branching Rules](workflow/workflow.mdc) — Development workflow standards \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/monorepo-best-practices.mdc b/.cursor/backup/rules/cursor-export/monorepo-best-practices.mdc deleted file mode 100644 index f0a8387d..00000000 --- a/.cursor/backup/rules/cursor-export/monorepo-best-practices.mdc +++ /dev/null @@ -1,208 +0,0 @@ ---- -description: Best practices for monorepo work - структура, Dependencies, TypeScript, barrel expo.ts -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# Monorepo Best Practices for AI - -## Основные Principles - -### 1. Project Structure -- **packages/** - общие Libraries и утилиты -- **pages/** - приложения и страницы Extensions -- **external/** - копии внешних boilerplate проектов -- **te.ts/** - тесты и e2e - -### 2. Dependencies -- **Root dependencies** - только общие инструменты (prettier, eslint, TypeScript) -- **Package dependencies** - устанавливать в конкретных пакетах -- **Workspace dependencies** - использовать `pnpm add -D package -w` для root - -### 3. TypeScript configuration -- **Base config** - `packages.tsconfig/base.json` для общих настроек -- **Package configs** - наследовать от base с специфичными настройками -- **App configs** - наследовать от base с browser-specific настройками - -## Правила для AI - -### При создании новых пакетов: - -1. **Create правильную структуру:** -``` -packages/new-package/ -├── lib/ -│ ├── index.ts -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -└── README.md -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "...tsconfig/base.json", - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - }, - "include": ["lib/**/*", "index..ts"], - "exclude": ["node_modules", "dist"] -} -``` - -3. **Настроить package.json:** -``.json -{ - "name": "@extension/new-package", - "main": "dist/index..js", - "types": "dist/index.d.ts", - "expo.ts": { - ".": "./dist/index..js" - }, - "files": ["dist"] -} -``` - -### При создании новых страниц: - -1. **Create правильную структуру:** -``` -pages/new-page/ -├── src/ -│ ├── index.tsx -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -├── pos.css.config..js -└── vite.config..ts -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "@extension.tsconfig/base", - "compilerOptions": { - "types": ["chrome"], - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -3. **Настроить Pos.css для Tailwin.css 4+:** -```JavaScript -// pos.css.config..js -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -### При работе с barrel expo.ts: - -1. **Default expo.ts:** -```TypeScript -// component.ts -export default function Component() { ... } - -// index.ts -export { default as Component } from './component.js'; -``` - -2. **Named expo.ts:** -```TypeScript -// component.ts -export function Component() { ... } - -// index.ts -export { Component } from './component'; -``` - -3. **Mixed expo.ts:** -```TypeScript -// index.ts -export * from './helpers.js'; -export { default as Component } from './component.js'; -export type * from './types.js'; -``` - -### При установке зависимостей: - -1. **В конкретном пакете:** -```bash -cd packages/package-name -pnpm add dependency-name -``` - -2. **В конкретной странице:** -```bash -cd pages/page-name -pnpm add dependency-name -``` - -3. **В workspace root:** -```bash -pnpm add -D dependency-name -w -``` - -### При диагностике проблем: - -1. **Check Dependencies:** -```bash -pnpm why package-name -pnpm list package-name -``` - -2. **Найти проблемные файлы:** -```bash -find . -name .tsconfig.json" -exec grep -l "node" {} \; -``` - -3. **Очистить и пересобрать:** -```bash -rm -rf dist && pnpm run build -``` - -## Частые ошибки и решения - -| Проблема | Решение | -|----------|---------| -| `Cannot find type definition file for 'node'` | Remove 'node' из types в.tsconfig.json | -| `"Component" is not exported by` | Использовать `export { default as Component } from './file.js'` | -| `Cannot find module 'tailwin.css'` | `pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css` | -| `Rollup failed to resolve import` | `pnpm add package-name` в конкретном пакете | -| `spawn prettier ENOENT` | `git commit --no-verify` | - -## Команды для работы - -```bash -# build всего проекта -pnpm run build - -# build конкретного пакета -pnpm -F @extension/package-name run build - -# Установка зависимостей -pnpm install - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache - -# Пропуск pre-commit хуков -git commit --no-verify -m "message" -``` - -## Recommendations - -1. **Всегда проверять сборку** после изменений -2. **Документировать решения** для повторного использования -3. **Использовать правильные barrel expo.ts** для default expo.ts -4. **Устанавливать Dependencies в правильных местах** -5. **Следовать структуре проекта** при создании новых файлов \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc deleted file mode 100644 index a9901630..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-best-practices.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for best-practices -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Best Practices -- Single Responsibility: Each plugin should have a single, clear purpose -- Modular Design: Break complex functionality into modules -- configuration: Use configuration files for customizable behavior -- Versioning: Follow semantic versioning for plugin updates -- Backward Compatibility: Maintain compatibility with previous versions - -description: Best practices for plugin development -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc deleted file mode 100644 index 5b34417a..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-documentation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for documentation -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Documentation Standards -- API Documentation: Document all public APIs and parameters -- Usage examples: ProvIDE clear usage examples -- Error Codes: Document all possible error codes and meanings -- Performance Notes: Document performance characteristics -- Security Notes: Document security consIDErations - -description: Documentation standards for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc deleted file mode 100644 index a95d9cf5..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-error-handling.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for error-handling -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Error Handling -- Graceful Degradation: Continue working with reduced functionality -- User Feedback: ProvIDE clear error messages to users -- Logging: Log errors for debugging without exposing sensitive data -- Fallbacks: Implement fallback mechanisms for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Error handling requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc deleted file mode 100644 index 868ad488..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-performance.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for performance -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Performance GuIDElines -- PyodIDE Optimization: Optimize for WebAssembly execution -- Memory Management: Clean up resources and avoid memory leaks -- Async Operations: Use async/await for all I/O operations -- Caching: Implement appropriate caching strategies -- Bundle Size: Keep plugin size reasonable for browser loading - -related: - - architecture-performance.mdc - - plugin-testing.mdc - -description: Performance guIDElines for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-security.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-security.mdc deleted file mode 100644 index a3e81a41..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-security.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: Plugin standard for security -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: true -aiCategory: plugin-development--- - -# Plugin Security Requireme.ts -- Zero Trust: Plugins are not trusted by default -- Permission Declaration: All required permissions must be declared in manifest -- API Validation: All API calls must be validated against schemas -- Rate Limiting: Respect rate lim.ts to prevent abuse -- Error Handling: Graceful error handling without exposing sensitive data - -description: Security requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc deleted file mode 100644 index 92065786..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-structure.mdc +++ /dev/null @@ -1,60 +0,0 @@ ---- -description: Plugin standard for structure -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Structure - -## Directory Layout -``` -public/plugins/plugin-name/ -├── manifest.json # Plugin metadata and permissions -├── mcp_server.py # Python MCP protocol implementation -├── workflow.json # Plugin workflow definition (optional) -└──.icon.svg # Plugin.icon -``` - -## Manifest.json Example -``.json -{ - "name": "Plugin Name", - "version": "1.0.0", - "description": "Plugin description", - "main_server": "mcp_server.py", - "required_secr.ts": ["openai_API_key", "weather_API_key"], - "API_permissions": { - "openai": { - "domains": ["API.openai.com"], - "endpoi.ts": ["/v1/chat/completions"], - "methods": ["POST"], - "rate_limit": "100/hour" - } - }, - "network_policy": { - "allowed_domains": ["API.openai.com"], - "WebSock.ts": "denied" - }, - "API_schemas": { - "openai": { - "chat_completions": { - "type": "object", - "required": ["prompt"], - "properties": { - "prompt": {"type": "string", "maxLength": 4000} - } - } - } - } -} -``` -description: Plugin directory and manifest requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc b/.cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc deleted file mode 100644 index c90be344..00000000 --- a/.cursor/backup/rules/cursor-export/plugin/plugin-testing.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for testing -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Testing Requireme.ts -- Unit Te.ts: Test individual functions and compone.ts -- Integration Te.ts: Test plugin integration with host system -- Security Te.ts: Validate permission enforcement -- Performance Te.ts: Monitor memory usage and execution time -- Error Te.ts: Test error handling and recovery - -description: Testing requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/security/validation.mdc b/.cursor/backup/rules/cursor-export/security/validation.mdc deleted file mode 100644 index 76a56384..00000000 --- a/.cursor/backup/rules/cursor-export/security/validation.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Input validation and data sanitization rules -globs: ["**/*.ts", "**/*.js", "**/*.py", "**/*.json", "platform-core/**/*"] -alwaysApply: false -aiCategory: security--- - -# Rule -- Always validate all input data and API parameters. -- Never trust data from external sources (user, plugins, third-party services). -- Use schemas, types, or validators (e.g., zod, pydantic, marshmallow). - -# examples -- ✅ `z.object({ email: z.string().email() })` -- ✅ `if not isinstance(user_id, int): raise ValueError()` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc b/.cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc deleted file mode 100644 index c65be120..00000000 --- a/.cursor/backup/rules/cursor-export/typescript-build-troubleshooting.mdc +++ /dev/null @@ -1,166 +0,0 @@ ---- -description: TypeScript build error troubleshooting guIDE - Typeы, barrel expo.ts, Dependencies, Tailwin.css -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# TypeScript build Troubleshooting Rules - -## Основные Principles устранения ошибок - -### 1. TypeScript configuration Errors - -**Error:** `Cannot find type definition file for 'node'` -- **Причина:** В .tsconfig.json` указаны Typeы Node.js для Browserного кода -- **Решение:** Remove `'node'` из `compilerOptions.types`, оставить только `['chrome']` -- **Применяется к:** `pages/*.tsconfig.json`, `te.ts/e2e.tsconfig.json` - -**Error:** `Cannot find module '@extension/shared'` -- **Причина:** Неправильные `paths` в .tsconfig.json` -- **Решение:** Add корректные пути в `compilerOptions.paths` -- **Пример:** -``.json -{ - "compilerOptions": { - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### 2. Barrel Export Issues - -**Error:** `"initAppWithShadow" is not exported by ".../packages/shared/dist/index..js"` -- **Причина:** Неправильный barrel export для default expo.ts -- **Решение:** Использовать явный re-export: `export { default as initAppWithShadow } from './init-app-with-shadow.js'` -- **Паттерн:** -```TypeScript -// В index.ts -export { default as ComponentName } from './component-file.js'; - -// В component-file.ts -export default function ComponentName() { ... } -``` - -### 3. Tailwin.css 4+ Setup - -**Error:** `Cannot find module 'tailwin.css'` -- **Решение:** Установить Dependencies в конкретном пакете: -```bash -pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css -``` - -**Error:** `Loading Pos.css Plugin failed: Cannot find module '@tailwin.css/pos.css'` -- **Решение:** Create `pos.css.config..js`: -```JavaScript -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -**Error:** `Command 'tailwin.css-cli' not found` -- **Решение:** Delete прямой вызов CLI из `build..ts`, использовать Vite Pos.css pipeline - -### 4. Module Resolution - -**Error:** `Rollup failed to resolve import 'file-saver'` -- **Решение:** Установить недостающую зависимость в конкретном пакете: -```bash -pnpm add file-saver -``` - -**Error:** `Failed to resolve entry for package '@extension/vite-config'` -- **Причина:** Неправильные `main`/`expo.ts` в `package.json` -- **Решение:** Использовать ESM-only экспорт: -``.json -{ - "main": "dist/index..js", - "expo.ts": { - ".": "./dist/index..js" - } -} -``` - -### 5. build Script Issues - -**Error:** .jsx is not exported by React.jsx-runtime.js` -- **Причина:** НеCompatibility версий React/SWC/Vite -- **Решение:** - 1. Update до последних версий: `React`, `React-dom`, `vite`, `@vit.js/plugin-React-swc` - 2. Убедиться в .tsconfig.json`: `.jsx": "React.jsx"` - 3. Не использовать `import type React` - -### 6. Pre-commit Hook Issues - -**Error:** `spawn prettier ENOENT` -- **Решение:** Установить prettier и Plugins: -```bash -pnpm add -D prettier prettier-plugin-tailwin.css -w -``` -- **Альтернатива:** Использовать `git commit --no-verify` для пропуска хуков - -## Порядок устранения ошибок - -1. **Анализ ошибки:** Определить Type и контекст -2. **Verification зависимостей:** Убедиться в наличии всех пакетов -3. **configuration:** Исправить .tsconfig.json`, `package.json` -4. **Barrel expo.ts:** Check правильность экспортов -5. **build:** Выполнить `rm -rf dist && pnpm run build` -6. **cache:** При необходимости очистить cache: `pnpm exec rimraf node_modules/.vite .turbo .cache` - -## Частые Patterns исправлений - -### Для pages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "types": ["chrome"], // Remove 'node' - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### Для packages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - } -} -``` - -### Для barrel expo.ts: -```TypeScript -// Правильно для default expo.ts -export { default as ComponentName } from './component-file.js'; - -// Правильно для named expo.ts -export { ComponentName } from './component-file.js'; -``` - -## Команды для диагностики - -```bash -# Verification зависимостей -pnpm why React -pnpm why tailwin.css - -# Поиск файлов -find . -name .tsconfig.json" -exec grep -l "node" {} \; - -# Verification сборки -pnpm run build - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache && pnpm install -``` \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc b/.cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc deleted file mode 100644 index 197d9d79..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-accessibility.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for accessibility -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# Accessibility (A11y) Standards -- ProvIDE proper ARIA labels for interactive eleme.ts -- Ensure all functionality is keyboard accessible -- Proper focus handling and visible focus indicators -- Use semantic.html and descriptive text for screen readers -- Meet WCAG color contrast requireme.ts - -related: - - ui-forms.mdc - - ui-error-handling.mdc - -description: Accessibility requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-animation.mdc b/.cursor/backup/rules/cursor-export/ui/ui-animation.mdc deleted file mode 100644 index 263fa004..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-animation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for animation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Animation and Transitions -- Use.css transitions for State changes -- Subtle loading animations -- Enhance user experience with micro-interactions -- Ensure animations don't impact performance -- Respect user's motion preferences (reduced motion) - -description: Animation and transition standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc b/.cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc deleted file mode 100644 index c2613648..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-error-handling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for error-handling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Error Handling -- Clear, actionable error messages for users -- Catch and handle component errors with error boundaries -- ProvIDE fallback UI for failed compone.ts -- Allow users to retry failed operations -- Log errors for debugging - -description: Error handling requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-forms.mdc b/.cursor/backup/rules/cursor-export/ui/ui-forms.mdc deleted file mode 100644 index e9b6928b..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-forms.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for forms -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Form Standards -- Client-sIDE and server-sIDE validation -- Show validation errors clearly -- ProvIDE clear success feedback -- Auto-save forms where appropriate -- Support common keyboard shortc.ts - -description: Form standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc b/.cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc deleted file mode 100644 index 4b268cbf..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-loading-states.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for loading-States -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Loading States -- Show skeleton UI while loading -- Use spinners or progress bars as indicators -- ProvIDE informative loading messages -- Handle loading timeo.ts gracefully -- Show appropriate error States - -description: Loading State requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-mobile.mdc b/.cursor/backup/rules/cursor-export/ui/ui-mobile.mdc deleted file mode 100644 index 3c472d88..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-mobile.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for mobile -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Mobile Responsiveness -- Adequate size for touch interaction (touch targ.ts) -- Proper viewport meta tags -- Use flexbox and grid for responsive layo.ts -- Support common touch gestures -- Optimize for mobile performance - -description: Mobile responsiveness standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-navigation.mdc b/.cursor/backup/rules/cursor-export/ui/ui-navigation.mdc deleted file mode 100644 index 02d02c56..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-navigation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for navigation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Navigation Standards -- ProvIDE clear navigation context with breadcrumbs -- Clearly indicate current page/section (active States) -- Support browser back button -- Support direct links to specific content (deep linking) -- Show loading during navigation - -description: Navigation standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-performance.mdc b/.cursor/backup/rules/cursor-export/ui/ui-performance.mdc deleted file mode 100644 index dc609680..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-performance.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for performance -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Performance Standards -- Lazy load compone.ts and routes -- Use React.memo and useMemo appropriately -- Split code into smaller bundles -- Optimize images for web -- Implement appropriate caching strategies - -description: Performance standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-react-components.mdc b/.cursor/backup/rules/cursor-export/ui/ui-react-components.mdc deleted file mode 100644 index 84458a72..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-react-components.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for React-compone.ts -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# React Component Standards -- Use TypeScript for all new compone.ts -- Prefer functional compone.ts with hooks -- Define clear interfaces for component props -- ProvIDE sensible default values -- Wrap compone.ts in error boundaries - -description: Standards for React component structure in UI -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-styling.mdc b/.cursor/backup/rules/cursor-export/ui/ui-styling.mdc deleted file mode 100644 index 0a2f7319..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-styling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for styling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Styling Standards -- Use Tailwind.css for consistent styling -- Organize.css classes logically -- Ensure responsive design for different screen sizes -- Support light/dark mode -- Use.css variables for theme values - -description: Styling standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/ui/ui-testing.mdc b/.cursor/backup/rules/cursor-export/ui/ui-testing.mdc deleted file mode 100644 index 133251bf..00000000 --- a/.cursor/backup/rules/cursor-export/ui/ui-testing.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for testing -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Testing Standards -- Test component logic and behavior (unit te.ts) -- Test component interactions (integration te.ts) -- Test visual appearance and layout (visual te.ts) -- Test accessibility compliance (a11y te.ts) -- Test component performance - -related: - - plugin-testing.mdc - - ui-error-handling.mdc - -description: Testing standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/workflow/automation.mdc b/.cursor/backup/rules/cursor-export/workflow/automation.mdc deleted file mode 100644 index 14024a6e..00000000 --- a/.cursor/backup/rules/cursor-export/workflow/automation.mdc +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Automation and synchronization rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Automation Rules - -- При обновлении ключевых принципов, стандартов или чек-листов в memory-bank (например, DEV_PRACTICES.md, SECURITY.md, KNOWLEDGE_MAP.md), ассистент обязан синхронизировать их с правилами Cursor (.cursor/rules/). -- Все изменения должны быть отражены как в документации, так и в правилах для AI. -- Если обнаружено расхождение — предложить пользователю синхронизировать и объяснить различия. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/cursor-export/workflow/branches.mdc b/.cursor/backup/rules/cursor-export/workflow/branches.mdc deleted file mode 100644 index d485b834..00000000 --- a/.cursor/backup/rules/cursor-export/workflow/branches.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git branch management rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Rule -- For new features, use branches with the prefix `feature/` and a meaningful name. -- For bugfixes, use the prefix `fix/` and a clear description of the problem. -- Never mix bugfixes and features in the same branch. -- After merging a bugfix, update feature branches via rebase/merge from develop. -- Direct comm.ts or merges to `main` and `develop` are strictly forbidden. All changes must go through pull reque.ts into `develop` only. -- Merging to `main` is allowed only from `develop` after release approval. -- **Branch Purpose Tracking:** Before starting a new, unrelated task, you must finish the current branch (commit, push, PR, merge) and only then create a new branch for the next task. This preve.ts mixing tasks and improves review clarity. Exceptions: urgent bugfixes (fix/). - -# examples -- ✅ `feature/pyodIDE-integration` -- ✅ `fix/tailwin.css-build-script` -- ❌ `feature/pyodIDE-integration` + unrelated refactor in same branch \ No newline at end of file diff --git a/.cursor/backup/rules/cursor-export/workflow/workflow.mdc b/.cursor/backup/rules/cursor-export/workflow/workflow.mdc deleted file mode 100644 index 8a8bc2a6..00000000 --- a/.cursor/backup/rules/cursor-export/workflow/workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Workflow rule for -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: trueaiPriority: medium -aiCategory: process-management ---- - -# Workflow Branching Rules - -- В ветке main запрещено вести работу. main используется только для релизов и production. -- В ветке develop запрещено вести работу напрямую. Все изменения — только через feature-Branches с именованием feature/{смысловое_название}, которые создаются автоматически по смыслу задачи. -- Merge feature-веток только через pull request в develop. -- Прямой merge или commit в main и develop запрещён. -- В main разрешён merge только из develop после релизного ревью. -- Все PR должны содержать Description изменений, ссылки на документацию и changelog. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/dev/date-time-patterns.mdc b/.cursor/backup/rules/dev/date-time-patterns.mdc deleted file mode 100644 index b23fd298..00000000 --- a/.cursor/backup/rules/dev/date-time-patterns.mdc +++ /dev/null @@ -1,170 +0,0 @@ ---- -description: Date and time patterns for AI assista.ts - universal guIDElines for handling dates -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Patterns работы с Dateми и временем - -## Проблема -AI-ассистенты не могут самостоятельно получать актуальную дату и Time, что приводит к использованию устаревших дат в документации. - -## Решение: Usage системных команд - -### Получение текущей даты -```bash -# Текущая Date в формате YYYY-MM-DD -date +"%Y-%m-%d" - -# Текущая Date и Time -date +"%Y-%m-%d %H:%M:%S" - -# Текущая Date на русском языке -date +"%d %B %Y" -``` - -### Получение времени -```bash -# Только Time -date +"%H:%M:%S" - -# Time с часовым поясом -date +"%H:%M:%S %Z" -``` - -## Patterns для AI-ассистентов - -### 1. При создании документации -```bash -# Всегда получать актуальную дату перед созданием документации -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" -``` - -### 2. При обновлении существующей документации -```bash -# Проверять, не устарели ли даты в документации -# Если Date старая - обновлять с актуальной -``` - -### 3. При создании коммитов -```bash -# Использовать актуальную дату в сообщениях коммитов -git commit -m "feat: new feature - $(date +"%Y-%m-%d")" -``` - -## Форматы дат для разных целей - -### Documentation (YYYY-MM-DD) -- `2025-07-12` - стандартный формат для документации -- Используется в memory-bank, README, технической документации - -### Comm.ts (YYYY-MM-DD) -- `2025-07-12` - для сообщений коммитов -- Краткий и понятный формат - -### Пользовательский Interface -- `12 июля 2025` - для пользовательского Interfaceа -- Локализованный формат - -### Logging -- `2025-07-12 14:30:25` - полный формат с временем -- Для системных логов и отладки - -## Проверочный список - -### ✅ Перед созданием документации -- [ ] Получить актуальную дату командой `date +"%Y-%m-%d"` -- [ ] Использовать полученную дату в документации -- [ ] Check формат даты (YYYY-MM-DD) - -### ✅ При обновлении документации -- [ ] Check даты в существующих файлах -- [ ] Update устаревшие даты -- [ ] Убедиться в консистентности форматов - -### ✅ При работе с пользователем -- [ ] Уточнить дату, если есть сомнения -- [ ] Использовать понятные пользователю форматы -- [ ] Объяснить, почему используется системная Date - -## examples использования - -### Создание нового этапа в progress.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### ✅ Этап X: Description (Завершен - $current_date)" -``` - -### Обновление Statusа в errors.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### Status" -echo "✅ **РЕШЕНО** - $current_date" -``` - -### Создание коммита с датой -```bash -current_date=$(date +"%Y-%m-%d") -git commit -m "feat: new feature - $current_date" -``` - -## Частые ошибки - -### ❌ Неправильно -- Usage устаревших дат из контекста -- Разные форматы дат в одном документе -- Отсутствие проверки актуальности дат - -### ✅ Правильно -- Usage `date` команды для получения актуальной даты -- Консистентный формат YYYY-MM-DD -- Регулярная Verification и обновление дат - -## Integration с Cursor IDE - -### Project Rules -Add в project rules: -``` -- Always use system date Commands for current date/time -- Format dates as YYYY-MM-DD in documentation -- Validate dates before using in documentation -``` - -### Saved Memories -Create memory: -``` -Date/Time Pattern: Use 'date +"%Y-%m-%d"' Command to get current date -Format: Always use YYYY-MM-DD format in documentation -Validation: Check dates before using in documentation -``` - -## Автоматизация - -### Скрипт для проверки дат -```bash -#!/bin/bash -# check-dates.sh -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -# Verification дат в документации -grep -r "2024-" docs/ memory-bank/ 2>/dev/null | head -5 -echo "Found old dates above. ConsIDEr updating them." -``` - -### Git hook для проверки дат -```bash -#!/bin/bash -# .git/hooks/pre-commit -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -## New Entry ([2025-07-19T01:32:19.979Z]) - -Паттерн работы с Dateми: Использовать date +%Y-%m-%d для получения текущей даты в документации - -echo "Please ensure all dates in documentation are current." -``` \ No newline at end of file diff --git a/.cursor/backup/rules/dev/development-guidelines.mdc b/.cursor/backup/rules/dev/development-guidelines.mdc deleted file mode 100644 index bbd7f624..00000000 --- a/.cursor/backup/rules/dev/development-guidelines.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Development best practices and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Development GuIDElines - -## Overview - -This file contains dev practice guIDElines and best practices. - -## GuIDElines - - - -## Development Practice ([2025-07-19T01:19:31.302Z]) - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы - - diff --git a/.cursor/backup/rules/dev/development-principles.mdc b/.cursor/backup/rules/dev/development-principles.mdc deleted file mode 100644 index 8ea7328f..00000000 --- a/.cursor/backup/rules/dev/development-principles.mdc +++ /dev/null @@ -1,65 +0,0 @@ ---- -description: Development principles for all proje.ts - universal guIDElines and best practices -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: development-practices ---- - -# 🛡️ Principles разработки - -## Краткая сводка (для быстрого понимания) - -### 🔒 Security и надёжность -1. **"Не навреди"** - Security прежде всего, обратная Compatibility -2. **"Fail Fast, Fail Safe"** - быстрый и безопасный отказ, Graceful Degradation -3. **"data Integrity & Privacy"** - целостность и приватность данных - -### 📊 Наблюдаемость и Quality -4. **"Observability First"** - структурированное Logging, метрики, Monitoring -5. **"Best Practices First"** - проверенные Patterns, Standards, Performance -6. **"configuration as Code"** - Versioning конфигураций, Validation - -### 🚀 User Experience -7. **"Progressive Enhancement"** - базовая функциональность всегда работает -8. **"Continuous Learning"** - Monitoring, обратная связь, A/B Testing - -### 🌐 Экосистема и развитие -9. **"Ecosystem Thinking"** - Compatibility плагинов, стабильность API -10. **"Comme.ts для ИИ"** - аналитические Comme.ts для понимания логики - -## Применение принципов - -### Для каждой задачи ИИ-ассистент должен: -1. **Проанализировать** задачу через призму релевантных принципов -2. **Выбрать** наиболее подходящие Principles для конкретной ситуации -3. **Применить** Principles систематически при принятии решений -4. **Документировать** как Principles повлияли на решение - -### examples применения: -- **Новый плагин** → Principles 1, 2, 5, 6, 9, 10 -- **Исправление бага** → Principles 1, 2, 4, 5, 7 -- **Optimization** → Principles 2, 4, 5, 6, 7, 8 -- **Новая Function** → Principles 1, 3, 5, 7, 8, 9 - -## Взаимодополняемость принципов - -Principles не противоречат друг другу, а усиливают: -- **"Не навреди" + "Fail Fast"** = безопасные быстрые решения -- **"Best Practices" + "Observability"** = качественные решения с Monitoringом -- **"Progressive Enhancement" + "Ecosystem"** = стабильная экосистема -- **"data Privacy" + "configuration"** = безопасная configuration - -## Результат применения - -Следование всем принципам обеспечивает: -- ✅ **Highе Quality кода** и архитектуры -- ✅ **Security** и надёжность системы -- ✅ **Лучший User Experience** - -## New Entry ([2025-07-19T01:32:10.564Z]) - -Принцип разработки: Всегда использовать системные команды для получения актуальной даты - -- ✅ **Масштабируемость** и поддерживаемость -- ✅ **Успешную экосистему** плагинов \ No newline at end of file diff --git a/.cursor/backup/rules/dev/testing-troubleshooting.mdc b/.cursor/backup/rules/dev/testing-troubleshooting.mdc deleted file mode 100644 index d70c8c0c..00000000 --- a/.cursor/backup/rules/dev/testing-troubleshooting.mdc +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing & Troubleshooting Best Practices - -## Documentation тестирования и отладки - -### Ключевые Principles: -- Писать пошаговые гайды для всех основных flows тестирования и отладки -- Разделять usage, troubleshooting и advanced diagnostics в отдельные файлы -- Cross-link гайды для навигации (usage → troubleshooting → advanced) -- Использовать четкие, воспроизводимые шаги и code snipp.ts -- Ссылаться на релевантные скрипты, автоматизацию и best practices -- Обновлять гайды при изменении features или workflows - -### examples гайдов: -- DevTools Testing GuIDE -- DevTools Panel Troubleshooting -- DevTools Panel Usage - -## Анализ внешних репозиториев как submodules - -Для анализа, поиска и отслеживания изменений во внешнем git репозитории: - -### Пошагово: -1. В корне монорепо: - ```bash - git submodule add external/ - git submodule update --init --recursive - ``` - - Пример: - ```bash - git submodule add HTTPS://github.com/QizhengMo/chrome-extension-sIDEpanel-template.git external/sIDEpanel-template - git submodule update --init --recursive - ``` - -2. Открыть монорепо в Cursor/VSCode. Submodule будет проиндексирован и доступен для поиска кода, навигации и анализа. - -3. Update submodule до последней версии: - ```bash - cd external/sIDEpanel-template - git pull origin main - ``` - -4. Delete submodule (если больше не нужен): - ```bash - git submodule deinit -f external/sIDEpanel-template - git rm -f external/sIDEpanel-template - rm -rf .git/modules/external/sIDEpanel-template - ``` - -### Преимущества: -- Изолирует внешний код от основной git истории -- Позволяет легкие обновления и независимое Versioning -- Обеспечивает полный поиск кода и анализ в Cursor/VSCode - -**Используйте этот Method для любого внешнего кодового база, который хотите анализировать или ссылаться в монорепо.** -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/ai-answer-self-check.mdc b/.cursor/backup/rules/doc/ai-answer-self-check.mdc deleted file mode 100644 index a1409f4f..00000000 --- a/.cursor/backup/rules/doc/ai-answer-self-check.mdc +++ /dev/null @@ -1,39 +0,0 @@ - - - - ---- -description: ai answer self check rule -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: falseaiPriority: medium -aiCategory: documentation ---- - -# AI Answer Self-Check Policy - -AI-ассистент обязан для каждого ответа: - -## Обязательные Requireme.ts: -- Разбивать вопрос пользователя на подпункты/чеклист -- Явно отмечать, на что уже дан ответ, а что требует пояснения или осталось неотвеченным -- В конце ответа давать self-check: какие пункты покрыты, какие требуют доработки или согласования -- Если требование уже реализовано — явно указывать где и как (файл, секция, коммит, etc.) -- Для сложных вопросов использовать маркированные списки или таблицы - -## Пример self-check: -``` ---- -**Self-check для вашего вопроса:** -1. ✅ Механизм self-check — описан и предложен для реализации -2. ✅ Пример предоставлен выше -3. ✅ Предыдущий случай объяснен (требование уже реализовано) -4. ⚠️ Если хотите это в каждом ответе — можно сделать стандартом (подтвердите) ---- -``` - -## Применение: -Это правило Required для всех коммуникаций AI в проекте. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/command-synchronization.mdc b/.cursor/backup/rules/doc/command-synchronization.mdc deleted file mode 100644 index 69bab2fe..00000000 --- a/.cursor/backup/rules/doc/command-synchronization.mdc +++ /dev/null @@ -1,264 +0,0 @@ -# Command Synchronization System - -## Overview - -Automated system for synchronizing user Commands between multiple sources: -- `USER_CommandS.md` - User-friendly Command reference -- `.cursor/rules/cursor-export/ai-memory.mdc` - AI assistant instructions -- `.cursor/rules/ai-memory.mdc` - Cursor rules -- `CURSOR_AI_MEMORY_BANK.md` - Export for Cursor AI memory-bank - -## How It Works - -### Single Source of Truth -All Commands are defined in `.cursor/rules/Command-sync..js` in a structured format: - -```JavaScript -const CommandCategories = { - 'Context and Memory': { - 'save context': { - russian: ['Сохрани контекст', 'Save контекст сессии'], - description: 'Save achieveme.ts, decisions and plans to memory-bank', - userDescription: 'AI-ассистент сохранит все достижения, решения и планы в memory-bank' - } - } -} -``` - -### Automatic Synchronization -The script generates all files from the single source: - -```bash -# Sync all files -node .cursor/rules/Command-sync..js sync - -# Export for Cursor AI memory-bank -node .cursor/rules/Command-sync..js export - -# Translate context to English -node .cursor/rules/Command-sync..js translate-context -``` - -## File Form.ts - -### USER_CommandS.md -User-friendly format with emojis and detailed descriptions: -```markdown -## 📝 Сохранение контекста - -### Save контекст сессии -`Сохрани контекст` / `Save контекст сессии` -*AI-ассистент сохранит все достижения, решения и планы в memory-bank* -``` - -### ai-memory.mdc -AI-optimized format for .cursor rules: -```markdown -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -### CURSOR_AI_MEMORY_BANK.md -Format optimized for Cursor AI memory-bank: -```markdown -### 📝 Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -## Context Translation System - -### Automatic Context Translation -The system automatically translates context to English for AI/LLM compatibility: - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only -``` - -### Features -- ✅ **Automatic translation** from Russian to English -- ✅ **backup creation** before translation -- ✅ **Git integration** with automatic comm.ts -- ✅ **Comprehensive coverage** of all context terminology -- ✅ **Error handling** with safe fallbacks - -### Translation Coverage -- **Headers and titles** - All section headers -- **Task status** - Completed tasks, current focus, next steps -- **Principles** - Working principles and guIDElines -- **Technical context** - Architecture and standards -- **Command system** - Command categories and descriptions -- **User experience** - UX priorities and metrics -- **Development plans** - Short, medium, and long-term goals -- **Status indicators** - Readiness and completion status - -## Integration with Cursor AI Memory-Bank - -### Manual Integration -1. Run export Command: - ```bash - node .cursor/rules/Command-sync..js export - ``` - -2. Copy content from `CURSOR_AI_MEMORY_BANK.md` - -3. Go to Cursor Settings → AI → Rules & Memories - -4. Paste into User Rules or Project Rules - -5. Save and restart Cursor - -### Automatic Integration (Future) -Planned features: -- Direct API integration with Cursor -- Automatic updates when Commands change -- Version control for AI memory-bank - -## Command Categories - -### 📝 Context and Memory -- `save context` - Save achieveme.ts, decisions and plans in English (automatic translation) -- `update progress` - Update project status files -- `restore context` - Restore full project understanding -- `quick restore` - Get brief summary - -### 🏗️ Analysis and Study -- `analyze architecture` - Study system patterns -- `study plugins` - Analyze plugin structure -- `check build` - Verify project builds -- `update documentation` - Update project docs - -### 🔧 Development -- `create plugin [name]` - Create new plugin -- `check code` - Run linting and type checking -- `run te.ts` - Run all project te.ts -- `check dependencies` - Verify dependencies - -### 📊 Project Management -- `bump version patch/minor/major` - Increase version -- `clean project` - Clean build artifa.ts -- `analyze performance` - Performance analysis -- `check security` - Security analysis - -### 🚀 Releases and Deployment -- `create release` - Prepare for release -- `build production` - Production build - -## Benef.ts - -### For Users -- ✅ **Consistent Commands** across all sources -- ✅ **Easy to use** - just copy and paste -- ✅ **Bilingual support** - English and Russian -- ✅ **Always up-to-date** - automatic synchronization - -### For AI Assista.ts -- ✅ **Structured format** - easy to parse -- ✅ **Clear descriptions** - understand what each Command does -- ✅ **Multiple sources** - available in all conte.ts -- ✅ **Semantic understanding** - works with any language - -### For Development -- ✅ **Single source of truth** - no duplication -- ✅ **Automatic updates** - change once, update everywhere -- ✅ **Version control** - track Command changes -- ✅ **Easy maintenance** - centralized management - -## Usage examples - -### Adding New Commands -1. Edit `.cursor/rules/Command-sync..js` -2. Add Command to appropriate category -3. Run sync Command: - ```bash - node .cursor/rules/Command-sync..js sync - ``` - -### Updating Existing Commands -1. Edit Command in `.cursor/rules/Command-sync..js` -2. Run sync Command -3. All files updated automatically - -### Exporting for Cursor -```bash -node .cursor/rules/Command-sync..js export -``` - -## Technical Details - -### File Structure -``` -.cursor/rules/ -├── Command-sync..js # Main synchronization script -├── ai-memory.mdc # Cursor rules (auto-generated) -├── cursor-export/ -│ └── ai-memory.mdc # AI assistant instructions (auto-generated) -└── doc/ - └── Command-synchronization.mdc # This documentation - -USER_CommandS.md # User reference (auto-generated) -CURSOR_AI_MEMORY_BANK.md # Cursor export (auto-generated) -``` - -### Script Functions -- `generateUserCommand.md()` - Generate USER_CommandS.md -- `generateAIMemor.md()` - Generate ai-memory.mdc files -- `generateCursorMemoryBank()` - Generate Cursor format -- `syncCommands()` - Sync all files -- `exportForCursor()` - Export for Cursor AI memory-bank - -### Command Structure -Each Command has: -- **English Command** - Primary Command -- **Russian alternatives** - User-friendly alternatives -- **Description** - Technical description for AI -- **User description** - User-friendly description - -## Future Enhanceme.ts - -### Planned Features -- [ ] **API Integration** - Direct Cursor API integration -- [ ] **Auto-sync** - Automatic sync on file changes -- [ ] **Command validation** - Validate Command syntax -- [ ] **Usage analytics** - Track Command usage -- [ ] **template system** - Command templates for different project types - -### Integration IDEas -- **Git hooks** - Auto-sync on commit -- **CI/CD integration** - Validate Command consistency -- **Plugin system** - Extensible Command categories -- **Multi-language support** - More languages beyond English/Russian - -## Troubleshooting - -### Common Issues -1. **Sync fails** - Check file permissions -2. **Commands not working** - Verify Cursor AI memory-bank integration -3. **Format issues** - Check Command structure in script - -### Debug Commands -```bash -# Check script help -node .cursor/rules/Command-sync..js help - -# Verify file generation -ls -la USER_CommandS.md CURSOR_AI_MEMORY_BANK.md -``` - -## Conclusion - -The Command synchronization system provIDEs: -- ✅ **Consistent experience** across all platforms -- ✅ **Easy maintenance** with single source of truth -- ✅ **Automatic updates** for all Command sources -- ✅ **AI-optimized format** for maximum compatibility -- ✅ **User-friendly interface** for easy adoption - -This system ensures that Commands work sea.lessLy across USER_CommandS.md, .cursor rules, and Cursor AI memory-bank settings. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/context-translation-system.mdc b/.cursor/backup/rules/doc/context-translation-system.mdc deleted file mode 100644 index a1183df8..00000000 --- a/.cursor/backup/rules/doc/context-translation-system.mdc +++ /dev/null @@ -1,206 +0,0 @@ -# Context Translation System - -## Overview - -The Context Translation System automatically translates context files from Russian to English for maximum AI/LLM compatibility. This ensures that all context saved in memory-bank is accessible to any AI assistant, regar.less of language preferences. - -## Key Features - -### 🌍 **Automatic Translation** -- **Sea.less translation** from Russian to English -- **Comprehensive coverage** of all context terminology -- **Preserves structure** and formatting -- **Creates backups** before translation - -### 🔄 **Integrated with Save Context Command** -- **Automatic translation** when using "save context" Command -- **Git integration** with automatic comm.ts -- **backup creation** for safety -- **Error handling** for robust operation - -### 📁 **backup System** -- **Automatic backups** before translation -- **Timestamped files** in `memory-bank/core/backup/` -- **Easy restoration** if needed -- **Safe operation** with rollback capability - -## Usage - -### For AI Assista.ts - -When a user issues the Command "save context" or "Сохрани контекст": - -1. **Automatically translate** context to English -2. **Create backups** of original files -3. **Save translated files** to memory-bank -4. **Commit changes** to git with descriptive message -5. **Notify user** of completion - -### Manual Usage - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only - -# Translate context using Command sync script -node .cursor/rules/Command-sync..js translate-context -``` - -## Translation Coverage - -### **Headers and Titles** -- `Active контекст разработки` → `Active Development Context` -- `Текущий Status проекта` → `Current Project Status` -- `Последнее обновление` → `Last Updated` - -### **Task Status** -- `Завершенные задачи` → `Completed Tasks` -- `Текущий фокус` → `Current Focus` -- `Следующие шаги` → `Next Steps` - -### **Principles and GuIDElines** -- `Ключевые Principles работы` → `Key Working Principles` -- `Инициативность ассистента` → `Assistant Initiative` -- `Quality кода` → `Code Quality` - -### **Technical Context** -- `Технический контекст` → `Technical Context` -- `Текущая Architecture` → `Current Architecture` -- `Standards разработки` → `Development Standards` - -### **Command System** -- `Система команд` → `Command System` -- `Автоматическая Synchronization` → `Automatic Synchronization` -- `Категории команд` → `Command Categories` - -### **User Experience** -- `User Experience` → `User Experience` -- `Priorityы UX` → `UX Priorities` -- `Метрики качества` → `Quality Metrics` - -### **Development Plans** -- `Планы развития` → `Development Plans` -- `Краткосрочные цели` → `Short-term Goals` -- `Mediumсрочные цели` → `Medium-term Goals` -- `Долгосрочные цели` → `Long-term Goals` - -### **Status and Readiness** -- `Status готовности` → `Readiness Status` -- `Готовые компоненты` → `Ready Compone.ts` -- `Готово к публикации` → `Ready for publication` - -## File Structure - -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Main context file (English) -│ ├── progress.md # Progress tracking (English) -│ └── backup/ # backup directory -│ ├── activeContext-2024-07-19T10-30-00-000Z.md -│ └── progress-2024-07-19T10-30-00-000Z.md -``` - -## Scri.ts - -### **save-context..js** -Main script for saving context with automatic translation: -- Translates context to English -- Creates backups -- Comm.ts changes to git -- ProvIDEs error handling - -### **Command-sync..js** -Extended with translation functionality: -- `translate-context` Command -- Integrated translation function -- Comprehensive translation mappings - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - Any AI assistant can read context -- **Language consistency** - All context in English -- **Better understanding** - Clear terminology for AI processing -- **Reduced confusion** - No mixed language content - -### **For International Community** -- **Global accessibility** - Ready for international developers -- **Standardized format** - Consistent English documentation -- **Easy sharing** - No language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - No manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - Automatic comm.ts with clear messages -- **Error handling** - Robust operation with fallbacks - -## Best Practices - -### **For AI Assista.ts** -1. **Always use "save context"** Command for context preservation -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English context** for all AI operations - -### **For Users** -1. **Use Russian Commands** - translation happens automatically -2. **Check backup directory** if you need original files -3. **Trust the system** - translations are accurate and comprehensive -4. **Report issues** if translation problems occur - -### **For Developers** -1. **Extend translation mappings** as needed -2. **Test translations** before deployment -3. **Maintain backup system** for safety -4. **Update documentation** when adding new terms - -## Error Handling - -### **Translation Errors** -- **backup preservation** - Original files always saved -- **Partial translation** - System continues with available mappings -- **Error reporting** - Clear messages about issues -- **Manual fallback** - Option to restore from backup - -### **Git Errors** -- **Translation still works** - Files translated even if commit fails -- **Manual commit option** - User can commit manually -- **Clear error messages** - Specific information about git issues -- **Safe operation** - No data loss in case of git problems - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages - -### **Potential Improveme.ts** -- **Real-time translation** during editing -- **Translation quality scoring** -- **User feedback system** for translations -- **Integration with more AI tools** - -## Conclusion - -The Context Translation System ensures that all context in memory-bank is accessible to any AI assistant by automatically translating content to English. This creates a truly international and AI-compatible documentation system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic translation process** -- ✅ **Safe backup system** -- ✅ **Git integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling** - -**Usage:** Simply use the "save context" Command, and the system will automatically translate and save your context in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/cursor-protection-system.mdc b/.cursor/backup/rules/doc/cursor-protection-system.mdc deleted file mode 100644 index 9170030c..00000000 --- a/.cursor/backup/rules/doc/cursor-protection-system.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Cursor Protection System - -## Overview - -The Cursor Protection System automatically ensures that all files in the `.cursor` directory are written in English for maximum AI/LLM compatibility. This system provIDEs comprehensive protection through automatic translation, Git hooks, and monitoring. - -## Key Features - -### 🛡️ **Automatic Protection** -- **Real-time translation** of all .cursor files to English -- **Comprehensive coverage** of technical terminology -- **Preserves structure** and formatting -- **Creates backups** before any changes - -### 🔧 **Git Integration** -- **Pre-commit hooks** - automatically translate staged .cursor files -- **Post-commit hooks** - verify protection status after comm.ts -- **Pre-push hooks** - block push if Russian content is detected -- **Automatic staging** of translated files - -### 📊 **Monitoring and Status** -- **Status checking** - IDEntify files that need translation -- **Protection repo.ts** - comprehensive status overview -- **backup management** - easy restoration from backups -- **Integration status** - verify all compone.ts are working - -## System Compone.ts - -### **1. Cursor Protector (`cursor-protector..js`)** -Main translation engine with comprehensive terminology coverage: -- **Translation mappings** - 500+ technical terms -- **File scanning** - recursive directory processing -- **backup creation** - automatic backup before translation -- **Status reporting** - detailed analysis of files - -### **2. Git Hooks (`cursor-git-hook..js`)** -Automatic protection during Git operations: -- **Pre-commit** - translate staged .cursor files -- **Post-commit** - verify overall protection status -- **Pre-push** - block push if Russian content found -- **Hook management** - install/uninstall hooks - -### **3. Protection Manager (`protect-cursor..js`)** -Complete system management: -- **Full protection** - translate, install hooks, commit -- **System installation** - setup all compone.ts -- **Status checking** - comprehensive protection report -- **backup restoration** - restore from backups - -## Usage - -### **Complete Protection (Recommended)** -```bash -# Protect all .cursor files, install hooks, and commit -node .cursor/rules/protect-cursor..js protect -``` - -### **System Installation** -```bash -# Install protection system (hooks, scri.ts, .cursorignore) -node .cursor/rules/protect-cursor..js install -``` - -### **Status Checking** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Check .cursor files specifically -node .cursor/rules/cursor-protector..js check -``` - -### **Manual Protection** -```bash -# Protect all .cursor files -node .cursor/rules/cursor-protector..js protect - -# Install Git hooks -node .cursor/rules/cursor-git-hook..js install -``` - -### **Package.json Scri.ts** -After installation, use npm scri.ts: -```bash -npm run protect-cursor # Complete protection -npm run check-cursor # Check status -npm run install-cursor-hooks # Install hooks -``` - -## Translation Coverage - -### **Technical Terminology** -- **Development terms** - Development → development -- **Architecture terms** - Architecture → architecture -- **Security terms** - Security → security -- **Performance terms** - Performance → performance -- **Quality terms** - Quality → quality - -### **File Structure** -- **Headers and titles** - заголовки и названия -- **Section names** - названия разделов -- **Status indicators** - индикаторы Statusа -- **Action descriptions** - описания действий - -### **Common Phrases** -- **Important notes** - важные Notes -- **Best practices** - Best Practices -- **Requireme.ts** - Requireme.ts -- **Recommendations** - Recommendations - -## Git Hook Behavior - -### **Pre-commit Hook** -1. **Scans staged files** for .cursor files -2. **Dete.ts Russian content** in staged files -3. **Automatically translates** files with Russian content -4. **Creates backups** before translation -5. **Stages translated files** for commit - -### **Post-commit Hook** -1. **Checks overall .cursor status** -2. **Repo.ts any remaining Russian content** -3. **Sugge.ts actions** if needed -4. **Confirms protection status** - -### **Pre-push Hook** -1. **Scans all .cursor files** -2. **Blocks push** if Russian content found -3. **ProvIDEs clear error message** -4. **Sugge.ts protection Command** - -## File Structure - -``` -.cursor/ -├── rules/ -│ ├── cursor-protector..js # Main translation engine -│ ├── cursor-git-hook..js # Git hooks -│ ├── protect-cursor..js # Protection manager -│ └── doc/ -│ └── cursor-protection-system.mdc # This documentation -├── backup/ # backup directory -│ ├── pre-commit/ # Pre-commit backups -│ └── [timestamped-backups]/ # Manual backups -└── [other .cursor files] # Protected files -``` - -## configuration - -### **.cursorignore** -Exclude files from automatic translation: -``` -# Cursor Protection System -# Files that should not be automatically translated - -# backup files -.cursor/backup/ - -# temporary files -*.tmp -*.temp - -# Log files -*.log - -# cache files -.cache/ -node_modules/ -``` - -### **Package.json Scri.ts** -Automatically added during installation: -``.json -{ - "scri.ts": { - "protect-cursor": "node .cursor/rules/protect-cursor..js protect", - "check-cursor": "node .cursor/rules/cursor-protector..js check", - "install-cursor-hooks": "node .cursor/rules/cursor-git-hook..js install" - } -} -``` - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - any AI assistant can read .cursor files -- **Language consistency** - all files in English -- **Better understanding** - clear terminology for AI processing -- **Reduced confusion** - no mixed language content - -### **For International Community** -- **Global accessibility** - ready for international developers -- **Standardized format** - consistent English documentation -- **Easy sharing** - no language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - no manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - sea.less workflow integration -- **Error prevention** - blocks problematic comm.ts/pushes - -## Best Practices - -### **For Users** -1. **Run complete protection** after any .cursor changes -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English for new files** - maintain consistency - -### **For AI Assista.ts** -1. **Always write .cursor files in English** -2. **Trust the protection system** - it handles translation -3. **Check protection status** if unsure -4. **Use standard terminology** from translation mappings - -### **For Developers** -1. **Install protection system** in new proje.ts -2. **Extend translation mappings** as needed -3. **Test protection** before deployment -4. **Maintain backup system** for safety - -## Error Handling - -### **Translation Errors** -- **backup preservation** - original files always saved -- **Partial translation** - system continues with available mappings -- **Error reporting** - clear messages about issues -- **Manual fallback** - option to restore from backup - -### **Git Hook Errors** -- **Translation still works** - files translated even if hooks fail -- **Manual protection option** - user can run protection manually -- **Clear error messages** - specific information about issues -- **Safe operation** - no data loss in case of hook problems - -### **System Errors** -- **Graceful Degradation** - system continues with available features -- **Error logging** - detailed error information -- **Recovery options** - multiple ways to restore functionality -- **User guidance** - clear instructions for resolution - -## Troubleshooting - -### **Common Issues** - -#### **Files not being translated** -1. Check if files are in `.cursorignore` -2. Verify file extensions (.md, .mdc) -3. Run manual protection: `node .cursor/rules/cursor-protector..js protect` - -#### **Git hooks not working** -1. Check hook installation: `node .cursor/rules/cursor-git-hook..js install` -2. Verify hook permissions (should be executable) -3. Check for confli.ts with other hooks - -#### **Translation quality issues** -1. Check backup files for original content -2. Extend translation mappings if needed -3. Report missing terms for system improvement - -### **Debug Commands** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Test translation on specific file -node .cursor/rules/cursor-protector..js protect - -# Verify Git hooks -ls -la .git/hooks/ - -# Check backup files -ls -la .cursor/backup/ -``` - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages -- **Real-time translation** during editing -- **Translation quality scoring** - -### **Integration IDEas** -- **IDE plugins** for real-time protection -- **CI/CD integration** for automated protection -- **Webhook system** for remote protection -- **Collaborative translation** for community contributions - -## Conclusion - -The Cursor Protection System ensures that all `.cursor` files are automatically maintained in English for maximum AI/LLM compatibility. This creates a truly international and AI-compatible configuration system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic protection process** -- ✅ **Safe backup system** -- ✅ **Git workflow integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling and recovery** - -**Usage:** Simply run `node .cursor/rules/protect-cursor..js protect` to activate complete protection, and the system will automatically maintain all `.cursor` files in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/documentation-map.mdc b/.cursor/backup/rules/doc/documentation-map.mdc deleted file mode 100644 index da6492bd..00000000 --- a/.cursor/backup/rules/doc/documentation-map.mdc +++ /dev/null @@ -1,187 +0,0 @@ -# Documentation Map - Карта назначения файлов - -## Команда "задокументируй" - четкие правила размещения - -### 🎯 **Универсальные правила и best practices → `.cursor/rules/`** - -**Критерии для `.cursor/rules/`:** -- ✅ Применимо к любым проектам -- ✅ Проверенные Patterns и подходы -- ✅ Автоматизация и workflow -- ✅ Standards разработки -- ✅ Documentation и структуры - -**Файлы назначения:** -- `dev/dev-principle-*.mdc` - Principles разработки -- `dev/development-principles.mdc` - универсальные Principles разработки -- `dev/date-time-patterns.mdc` - Patterns работы с Dateми -- `workflow/ci-automation.mdc` - CI/CD автоматизация -- `workflow/experience-transfer.mdc` - перенос опыта -- `doc/ai-first.mdc` - Standards документации -- `ui/ui-*.mdc` - UI/UX Standards -- `plugin/plugin-*.mdc` - Standards плагинов -- `security/validation.mdc` - правила безопасности -- `architecture/architecture-*.mdc` - архитектурные Principles -- `architecture/file-relationships.mdc` - Patterns организации файлов - -### 🧠 **Проектно-специфичный контекст → `memory-bank/`** - -**Критерии для `memory-bank/`:** -- ✅ Уникален для данного проекта -- ✅ Текущий Status и прогресс -- ✅ История ошибок и решений -- ✅ Принятые архитектурные решения -- ✅ Планы развития проекта - -**Файлы назначения:** -- `errors.md` - кладбище ошибок и решений -- `progress.md` - прогресс разработки -- `activeContext.md` - текущий контекст -- `architecture-decisions.md` - принятые решения -- `future-plans.md` - планы развития -- `testing-resu.ts.md` - результаты тестирования -- `sIDE-panel-improveme.ts.md` - улучшения UI -- `version-management.md` - управление версиями - -## 📋 **Детальная карта по Typeам контента** - -### **Errors and solutions:** -``` -Type ошибки → Файл назначения -├── build/TypeScript → memory-bank/errors.md -├── Runtime/UI → memory-bank/errors.md -├── Архитектурная → memory-bank/architecture-decisions.md -├── Security → .cursor/rules/security/validation.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── UI/UX → .cursor/rules/ui/ui-*.mdc -``` - -### **Best practices:** -``` -Type практики → Файл назначения -├── Development → .cursor/rules/dev/dev-principle-*.mdc -├── Documentation → .cursor/rules/doc/ai-first.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── Git workflow → .cursor/rules/workflow/branches.mdc -└── Plugins → .cursor/rules/plugin/plugin-*.mdc -``` - -### **Автоматизация:** -``` -Type автоматизации → Файл назначения -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── build → .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── Деплой → .cursor/rules/workflow/automation.mdc -└── Monitoring → .cursor/rules/architecture/architecture-observability.mdc -``` - -### **Architectural decisions:** -``` -Type решения → Файл назначения -├── Проектно-специфичное → memory-bank/architecture-decisions.md -├── Универсальное → .cursor/rules/architecture/architecture-*.mdc -├── Security → .cursor/rules/architecture/architecture-security.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── Plugins → .cursor/rules/architecture/architecture-plugin.mdc -``` - -## 🔄 **Workflow команды "задокументируй"** - -### **1. Анализ контента:** -```bash -# Определить Type контента -- Error/решение? -- Best practice? -- Автоматизация? -- Архитектурное решение? -- Проектно-специфичное? -``` - -### **2. Выбор файла назначения:** -```bash -# По карте выше определить правильный файл -# Check существование файла -# Create если не существует -``` - -### **3. Структурированное добавление:** -```bash -# Для .cursor/rules/: -- Add метаdata (description, globs, alwaysApply) -- Структурировать по Categoryм -- Add cross-references - -# Для memory-bank/: -- Add дату и контекст -- Связать с существующими записями -- Update индексы -``` - -### **4. Validation:** -```bash -# Check: -- Нет дублирования -- Правильная структура -- Актуальные ссылки -- Соответствие стандартам -``` - -## 🚫 **Запрещенные действия:** - -### **НЕ размещать в `.cursor/rules/`:** -- ❌ Проектно-специфичные ошибки -- ❌ Текущий Status проекта -- ❌ История конкретных решений -- ❌ Планы развития проекта -- ❌ Результаты тестирования - -### **НЕ размещать в `memory-bank/`:** -- ❌ Универсальные правила -- ❌ Best practices для всех проектов -- ❌ Автоматизация и workflow -- ❌ Standards документации -- ❌ UI/UX Standards - -## 📊 **examples правильного размещения:** - -### **Пример 1: Error сборки TypeScript** -``` -Проблема: "Failed to resolve entry for package '@extension/vite-config'" -Решение: "Перевести пакет на ESM-only, main: dist/index..js" - -Размещение: memory-bank/errors.md -Причина: Проектно-специфичная Error сборки -``` - -### **Пример 2: Best practice для ESM пакетов** -``` -Практика: "Использовать только ESM-экспорт для внутренних пакетов" -Обоснование: "Упрощает сборку, устраняет ошибки Vite/esbuild" - -Размещение: .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -Причина: Универсальная практика для всех проектов -``` - -### **Пример 3: CI/CD автоматизация** -``` -Автоматизация: "GitHub Actions для проверки .cursor rules" -Workflow: "Verification структуры, метаданных, дубликатов" - -Размещение: .cursor/rules/workflow/ci-automation.mdc -Причина: Универсальная автоматизация -``` - -## ✅ **Результат:** - -Четкая карта назначения файлов обеспечивает: -- **Нет дублирования** - каждый Type контента в правильном месте -- **Нет рассинхронизации** - четкие критерии размещения -- **Легкий поиск** - структурированная организация -- **Эффективный перенос** - только универсальные правила в .cursor -- **Консистентность** - единообразные подходы -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/internationalization-complete.mdc b/.cursor/backup/rules/doc/internationalization-complete.mdc deleted file mode 100644 index fe05f562..00000000 --- a/.cursor/backup/rules/doc/internationalization-complete.mdc +++ /dev/null @@ -1,162 +0,0 @@ -# Internationalization Complete - .cursor and memory-bank - -## Overview - -All `.cursor` and `memory-bank` directories have been completely translated to English for maximum international compatibility with AI assista.ts and LLMs. - -## What Was Translated - -### .cursor/rules Directory -- ✅ **ai-memory.mdc** - User Commands and AI instructions -- ✅ **ai-index.mdc** - AI-optimized rules index -- ✅ **README.mdc** - Main documentation -- ✅ **All subdirectories** - architecture/, dev/, doc/, plugin/, security/, ui/, workflow/ -- ✅ **cursor-export/** - All exported rules and standards - -### memory-bank Directory -- ✅ **README.md** - Main memory bank documentation -- ✅ **INDEX.md** - File structure and navigation -- ✅ **MEMORY_BANK_STRUCTURE.md** - Organization rules -- ✅ **All subdirectories** - core/, errors/, architecture/, development/, ui/, planning/, context/ -- ✅ **All individual files** - activeContext.md, progress.md, errors.md, etc. - -## Translation Approach - -### Universal Command Format -Commands now support both English and Russian: -```markdown -- `save context` / `save session context` - Save achieveme.ts, decisions and plans to memory-bank -- `update progress` / `update project progress` - Update activeContext.md and progress.md files with current status -``` - -### AI Semantic Understanding -- AI and LLMs understand Commands semantically regar.less of language -- English serves as the primary language for international compatibility -- Russian alternatives are preserved for user convenience - -## Benef.ts - -### For International Community -- 🌍 **100% English compatibility** - Any developer can use your .cursor -- 🤖 **AI/LLM optimized** - Commands work with any AI assistant -- 📚 **Standardized patterns** - Consistent Command structure -- 🔄 **Future-proof** - Ready for global sharing and collaboration - -### For You -- ✅ **Russian Commands still work** - No disruption to your workflow -- ✅ **Bilingual support** - Can use either language -- ✅ **AI understands both** - Semantic recognition works for both languages - -### For AI Assista.ts -- 🎯 **Clear Command patterns** - Predictable structure -- 🌐 **Language agnostic** - Understands meaning, not just words -- 📖 **Comprehensive documentation** - All rules in English - -## Technical Implementation - -### Translation Script -Created `.cursor/rules/translate-to-english..js` for automated translation: -```bash -node .cursor/rules/translate-to-english..js -``` - -### Translation Mappings -Comprehensive dictionary of Russian → English translations: -- Metadata descriptions -- Common phrases -- File content -- Navigation eleme.ts - -### Quality Assurance -- ✅ All files translated -- ✅ Structure preserved -- ✅ Functionality maintained -- ✅ Metadata updated - -## Usage examples - -### English Commands (Primary) -```bash -save context -update progress -restore context -analyze architecture -create plugin my-plugin -audit cursor -export cursor -``` - -### Russian Commands (Alternative) -```bash -сохрани контекст -обнови прогресс -восстанови контекст -анализируй архитектуру -создай плагин my-plugin -аудит cursor -экспорт cursor -``` - -## File Structure - -### .cursor/rules/ -``` -.cursor/rules/ -├── ai-memory.mdc # User Commands (English + Russian) -├── ai-index.mdc # Rules index (English) -├── README.mdc # Main documentation (English) -├── architecture/ # Architecture rules (English) -├── dev/ # Development principles (English) -├── doc/ # Documentation standards (English) -├── plugin/ # Plugin standards (English) -├── security/ # Security rules (English) -├── ui/ # UI/UX standards (English) -├── workflow/ # Workflow rules (English) -└── translate-to-english..js # Translation script -``` - -### memory-bank/ -``` -memory-bank/ -├── README.md # Main documentation (English) -├── INDEX.md # File structure (English) -├── MEMORY_BANK_STRUCTURE.md # Organization rules (English) -├── core/ # Core context files (English) -├── errors/ # Error documentation (English) -├── architecture/ # Architecture decisions (English) -├── development/ # Development process (English) -├── ui/ # UI/UX context (English) -├── planning/ # Planning docume.ts (English) -└── context/ # Contextual information (English) -``` - -## Next Steps - -### For Sharing -1. **GitHub Repository** - Ready for international community -2. **Documentation** - All in English for global understanding -3. **Commands** - Universal format for any AI assistant - -### For Development -1. **Continue using Russian Commands** - They still work perfectly -2. **Gradually adopt English** - For international collaboration -3. **Maintain bilingual support** - Best of both worlds - -### For AI Assista.ts -1. **Use English as primary** - For international compatibility -2. **Understand Russian alternatives** - For user convenience -3. **Maintain semantic understanding** - Language-agnostic processing - -## Conclusion - -The internationalization is complete and provIDEs: -- ✅ **Full English compatibility** for global community -- ✅ **Preserved Russian support** for your workflow -- ✅ **AI-optimized structure** for any AI assistant -- ✅ **Future-ready architecture** for international collaboration - -Your `.cursor` and `memory-bank` are now ready for the global AI development community! 🌍🤖 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/doc/universal-commands.mdc b/.cursor/backup/rules/doc/universal-commands.mdc deleted file mode 100644 index fcd83722..00000000 --- a/.cursor/backup/rules/doc/universal-commands.mdc +++ /dev/null @@ -1,133 +0,0 @@ -# Universal Commands Approach - -## 🎯 **Цель: Универсальность для международного сообщества** - -### **Проблема:** -- Команды на русском языке ограничивают международное Usage -- Другие разработчики не могут использовать ваш .cursor -- Отсутствие стандартизации команд - -### **Решение:** -- **Английский как основной язык** команд -- **Русский как альтернатива** для удобства -- **AI понимает смысл** независимо от языка - -## 📋 **Формат универсальных команд:** - -### **Структура:** -``` -`english Command` / `русская команда` / `альтернативная русская команда` - English description -``` - -### **examples:** -```markdown -- `create memory entry` / `создай запись в memory-bank` - Create new entry with automatic categorization -- `update context` / `обнови контекст` - Update activeContext.md with current status -- `restore context` / `восстанови контекст` - Restore full context from memory-bank -``` - -## 🌍 **Преимущества универсального подхода:** - -### **1. Международная Compatibility:** -- ✅ Любой разработчик может использовать ваш .cursor -- ✅ Стандартизация команд для сообщества -- ✅ Легкость понимания и адаптации - -### **2. AI-Optimization:** -- ✅ AI понимает смысл независимо от языка -- ✅ Единообразные Patterns команд -- ✅ Предсказуемое поведение - -### **3. Долгосрочная перспектива:** -- ✅ Не нужно Translationить при публикации -- ✅ Compatibility с будущими версиями Cursor -- ✅ Масштабируемость для новых команд - -## 🔧 **Практическое применение:** - -### **Для вас (русскоязычный пользователь):** -```bash -# Вы можете использовать любую команду: -"создай запись в memory-bank" # Русская команда -"create memory entry" # Английская команда -``` - -### **Для международного сообщества:** -```bash -# Другие разработчики используют английские команды: -"create memory entry" -"update context" -"restore context" -``` - -### **Для AI:** -```bash -# AI понимает смысл и выполняет одинаково: -"создай запись в memory-bank" → create memory entry -"create memory entry" → create memory entry -``` - -## 📊 **Статистика совместимости:** - -### **Поддерживаемые языки:** -- ✅ **Английский** - основной язык команд -- ✅ **Русский** - альтернативный язык для удобства -- ✅ **Любой другой** - AI понимает по смыслу - -### **Охват аудитории:** -- 🌍 **100% международное сообщество** - английские команды -- 🇷🇺 **Русскоязычные разработчики** - русские команды -- 🤖 **AI ассистенты** - понимают любой язык - -## 🚀 **Recommendations по использованию:** - -### **1. При создании новых команд:** -```markdown -# ✅ Правильно: -- `new Command` / `новая команда` - English description - -# ❌ Неправильно: -- `новая команда` - Russian description only -``` - -### **2. При документировании:** -```markdown -# ✅ Правильно: -## Commands for AI Assistant Recognition -- `create memory entry` / `создай запись в memory-bank` - Create new entry - -# ❌ Неправильно: -## Команды для распознавания AI-ассистентом -- `создай запись в memory-bank` - Create новую запись -``` - -### **3. При публикации:** -- Используйте английский как основной язык -- Сохраняйте русские альтернативы для удобства -- Добавляйте описания на английском - -## 📈 **Планы развития:** - -### **Краткосрочные цели:** -- ✅ Перевести все команды на универсальный формат -- ✅ Update документацию -- ✅ Протестировать с международным сообществом - -### **Долгосрочные цели:** -- 🌍 Стандартизация команд для Cursor сообщества -- 📚 Создание Libraries универсальных команд -- 🔄 Автоматическая миграция существующих .cursor - -## ✅ **Результат:** - -**Универсальный подход обеспечивает:** -- 🌍 **Международную Compatibility** - любой разработчик может использовать -- 🤖 **AI-оптимизацию** - понимание независимо от языка -- 📈 **Масштабируемость** - легко добавлять новые команды -- 🔄 **Обратную Compatibility** - ваши русские команды продолжают работать - -**Ваш .cursor теперь готов для международного сообщества!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/memory-bank/INDEX.md b/.cursor/backup/rules/memory-bank/INDEX.md deleted file mode 100644 index 0eee849e..00000000 --- a/.cursor/backup/rules/memory-bank/INDEX.md +++ /dev/null @@ -1,65 +0,0 @@ -# Memory Bank - React-TypeScript Project - -## Project Information - -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Last Updated:** 2025-07-19 - -## Categories - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- State-management.md - Управление Stateм -- component-structure.md - Структура компонентов -- routing.md - Маршрутизация - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- component-library.md - Библиотека компонентов -- styling-patterns.md - Patterns стилизации -- responsive-design.md - Адаптивный Design - -### 📅 [Planning](./planning/) - Planning -- feature-roadmap.md - Roadmap фич -- optimization-plans.md - Планы оптимизации - -### 🌍 [Context](./context/) - Contextual information -- tech-stack.md - Технический стек -- dependencies.md - Dependencies проекта -- environment.md - Окружение разработки - -## Quick Navigation - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) - -## AI Commands - -- `создай запись в memory-bank` - Create новую запись -- `обнови контекст` - Update Active контекст -- `восстанови контекст` - Восстановить полный контекст -- `аудит memory-bank` - Провести аудит - ---- -*Auto-generated for React-TypeScript project on 2025-07-19* diff --git a/.cursor/backup/rules/memory-bank/architecture/README.md b/.cursor/backup/rules/memory-bank/architecture/README.md deleted file mode 100644 index 21d38ba9..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Files in this category: - -- [component-structure](./component-structure.md) -- [decisions](./decisions.md) -- [patterns](./patterns.md) -- [routing](./routing.md) -- [State-management](./State-management.md) - -## Description: - -Принятые архитектурные решения с обоснованием. - -## AI Commands: - -- `добавь в architecture` - Add запись в эту категорию -- `обнови architecture` - Update файлы в категории -- `покажи architecture` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/component-structure.md b/.cursor/backup/rules/memory-bank/architecture/component-structure.md deleted file mode 100644 index 6626b3ce..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/component-structure.md +++ /dev/null @@ -1,12 +0,0 @@ -# component structure - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/decisions.md b/.cursor/backup/rules/memory-bank/architecture/decisions.md deleted file mode 100644 index 720d2530..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/decisions.md +++ /dev/null @@ -1,12 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Overview - -This file contains architectural decisions and rationale. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/patterns.md b/.cursor/backup/rules/memory-bank/architecture/patterns.md deleted file mode 100644 index ae9baf41..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# Design Patterns - React-TypeScript - -## Overview - -This file contains design patterns and best practices. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/routing.md b/.cursor/backup/rules/memory-bank/architecture/routing.md deleted file mode 100644 index a252858e..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/routing.md +++ /dev/null @@ -1,12 +0,0 @@ -# routing - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/architecture/state-management.md b/.cursor/backup/rules/memory-bank/architecture/state-management.md deleted file mode 100644 index d7ecf603..00000000 --- a/.cursor/backup/rules/memory-bank/architecture/state-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# State management - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/README.md b/.cursor/backup/rules/memory-bank/context/README.md deleted file mode 100644 index 374553e6..00000000 --- a/.cursor/backup/rules/memory-bank/context/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Context Information - React-TypeScript - -## Files in this category: - -- [dependencies](./dependencies.md) -- [deployment](./deployment.md) -- [environment](./environment.md) -- [tech-stack](./tech-stack.md) - -## Description: - -Технический и продуктовый контекст, окружение разработки. - -## AI Commands: - -- `добавь в context` - Add запись в эту категорию -- `обнови context` - Update файлы в категории -- `покажи context` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/dependencies.md b/.cursor/backup/rules/memory-bank/context/dependencies.md deleted file mode 100644 index 6caba601..00000000 --- a/.cursor/backup/rules/memory-bank/context/dependencies.md +++ /dev/null @@ -1,12 +0,0 @@ -# Dependencies - React-TypeScript - -## Overview - -This file contains project dependencies and versions. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/deployment.md b/.cursor/backup/rules/memory-bank/context/deployment.md deleted file mode 100644 index 23fa5567..00000000 --- a/.cursor/backup/rules/memory-bank/context/deployment.md +++ /dev/null @@ -1,12 +0,0 @@ -# deployment - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/environment.md b/.cursor/backup/rules/memory-bank/context/environment.md deleted file mode 100644 index 54b83b38..00000000 --- a/.cursor/backup/rules/memory-bank/context/environment.md +++ /dev/null @@ -1,12 +0,0 @@ -# Environment - React-TypeScript - -## Overview - -This file contains development environment setup. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/context/tech-stack.md b/.cursor/backup/rules/memory-bank/context/tech-stack.md deleted file mode 100644 index 1d83f76a..00000000 --- a/.cursor/backup/rules/memory-bank/context/tech-stack.md +++ /dev/null @@ -1,12 +0,0 @@ -# Tech Stack - React-TypeScript - -## Overview - -This file contains technology stack and tools. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/INDEX.md b/.cursor/backup/rules/memory-bank/core/INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/backup/rules/memory-bank/core/INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/backup/rules/memory-bank/core/README.md b/.cursor/backup/rules/memory-bank/core/README.md deleted file mode 100644 index a3b68a3e..00000000 --- a/.cursor/backup/rules/memory-bank/core/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Core Files - React-TypeScript - -## Files in this category: - -- [INDEX](./INDEX.md) -- [activeContext](./activeContext.md) -- [migrated-INDEX](./migrated-INDEX.md) -- [progress](./progress.md) -- [projectbrief](./projectbrief.md) -- [session-log](./session-log.md) - -## Description: - -Критически важные файлы для понимания текущего состояния проекта. - -## AI Commands: - -- `добавь в core` - Add запись в эту категорию -- `обнови core` - Update файлы в категории -- `покажи core` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/activeContext.md b/.cursor/backup/rules/memory-bank/core/activeContext.md deleted file mode 100644 index 8c429166..00000000 --- a/.cursor/backup/rules/memory-bank/core/activeContext.md +++ /dev/null @@ -1,36 +0,0 @@ -# Active Context - React-TypeScript - -## Current Status - -**Last Updated:** 2025-07-19 -**Project Type:** React-TypeScript -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup -- [ ] Core functionality -- [ ] Testing -- [ ] Documentation - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description -- Decision 2: Description - -## Next Steps - -- Step 1: Description -- Step 2: Description - -## Blockers - -- Blocker 1: Description -- Blocker 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/general.md b/.cursor/backup/rules/memory-bank/core/general.md deleted file mode 100644 index 71e787bc..00000000 --- a/.cursor/backup/rules/memory-bank/core/general.md +++ /dev/null @@ -1,31 +0,0 @@ -# General - core - -## Overview - -This file contains core-related information and entries. - -## Entries - - - ---- -*Auto-generated file* - -## [2025-07-19 01:56:13] - New Entry - -**Type:** test -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #universal #Commands #testing - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/backup/rules/memory-bank/core/migrated-INDEX.md b/.cursor/backup/rules/memory-bank/core/migrated-INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/backup/rules/memory-bank/core/migrated-INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/backup/rules/memory-bank/core/progress.md b/.cursor/backup/rules/memory-bank/core/progress.md deleted file mode 100644 index fd42cfe2..00000000 --- a/.cursor/backup/rules/memory-bank/core/progress.md +++ /dev/null @@ -1,31 +0,0 @@ -# Progress - React-TypeScript - -## Overview - -This file contains development progress and milestones. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* - -## [2025-07-19 01:47:05] - New Entry - -**Type:** progress -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #memory-bank #cursor #automation - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/backup/rules/memory-bank/core/projectbrief.md b/.cursor/backup/rules/memory-bank/core/projectbrief.md deleted file mode 100644 index 04f2e336..00000000 --- a/.cursor/backup/rules/memory-bank/core/projectbrief.md +++ /dev/null @@ -1,33 +0,0 @@ -# Project Brief - React-TypeScript - -## Project Overview - -**Name:** [Project Name] -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Status:** Active - -## Goals - -- Goal 1: Description -- Goal 2: Description -- Goal 3: Description - -## Requireme.ts - -- Requirement 1: Description -- Requirement 2: Description -- Requirement 3: Description - -## Constrai.ts - -- Constraint 1: Description -- Constraint 2: Description - -## Success Criteria - -- Criterion 1: Description -- Criterion 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/core/session-log.md b/.cursor/backup/rules/memory-bank/core/session-log.md deleted file mode 100644 index 35ff1fc3..00000000 --- a/.cursor/backup/rules/memory-bank/core/session-log.md +++ /dev/null @@ -1,12 +0,0 @@ -# Session Log - React-TypeScript - -## Overview - -This file contains development session logs. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/README.md b/.cursor/backup/rules/memory-bank/development/README.md deleted file mode 100644 index 25270007..00000000 --- a/.cursor/backup/rules/memory-bank/development/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Development Process - React-TypeScript - -## Files in this category: - -- [build-process](./build-process.md) -- [debugging-guIDE](./debugging-guIDE.md) -- [devtools-guIDE](./devtools-guIDE.md) -- [testing-resu.ts](./testing-resu.ts.md) -- [version-management](./version-management.md) - -## Description: - -Результаты тестирования, GuIDEs по отладке, Processes разработки. - -## AI Commands: - -- `добавь в development` - Add запись в эту категорию -- `обнови development` - Update файлы в категории -- `покажи development` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/build-process.md b/.cursor/backup/rules/memory-bank/development/build-process.md deleted file mode 100644 index 1a1ffcf4..00000000 --- a/.cursor/backup/rules/memory-bank/development/build-process.md +++ /dev/null @@ -1,12 +0,0 @@ -# build process - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/debugging-guide.md b/.cursor/backup/rules/memory-bank/development/debugging-guide.md deleted file mode 100644 index 5369fa55..00000000 --- a/.cursor/backup/rules/memory-bank/development/debugging-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# Debugging GuIDE - React-TypeScript - -## Overview - -This file contains debugging procedures and tips. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/devtools-guide.md b/.cursor/backup/rules/memory-bank/development/devtools-guide.md deleted file mode 100644 index c0d68911..00000000 --- a/.cursor/backup/rules/memory-bank/development/devtools-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# DevTools GuIDE - React-TypeScript - -## Overview - -This file contains development tools usage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/testing-results.md b/.cursor/backup/rules/memory-bank/development/testing-results.md deleted file mode 100644 index bf105272..00000000 --- a/.cursor/backup/rules/memory-bank/development/testing-results.md +++ /dev/null @@ -1,12 +0,0 @@ -# Testing Resu.ts - React-TypeScript - -## Overview - -This file contains testing outcomes and coverage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/development/version-management.md b/.cursor/backup/rules/memory-bank/development/version-management.md deleted file mode 100644 index 67b7e2f6..00000000 --- a/.cursor/backup/rules/memory-bank/development/version-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# Version Management - React-TypeScript - -## Overview - -This file contains version control and releases. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/README.md b/.cursor/backup/rules/memory-bank/errors/README.md deleted file mode 100644 index eca2654f..00000000 --- a/.cursor/backup/rules/memory-bank/errors/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Errors & Solutions - React-TypeScript - -## Files in this category: - -- [build-errors](./build-errors.md) -- [errors](./errors.md) -- [runtime-errors](./runtime-errors.md) -- [TypeScript-errors](./TypeScript-errors.md) -- [ui-errors](./ui-errors.md) - -## Description: - -Проектно-специфичные ошибки и их решения. - -## AI Commands: - -- `добавь в errors` - Add запись в эту категорию -- `обнови errors` - Update файлы в категории -- `покажи errors` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/build-errors.md b/.cursor/backup/rules/memory-bank/errors/build-errors.md deleted file mode 100644 index b0462c1e..00000000 --- a/.cursor/backup/rules/memory-bank/errors/build-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# build errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/errors.md b/.cursor/backup/rules/memory-bank/errors/errors.md deleted file mode 100644 index 33bd85eb..00000000 --- a/.cursor/backup/rules/memory-bank/errors/errors.md +++ /dev/null @@ -1,12 +0,0 @@ - -## [2025-07-19T01:19:27.379Z] - New Entry - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы. Это упрощает сборку, устраняет ошибки Vite/esbuild, ускоряет разработку и повышает Compatibility с современными инструментами. - ---- - -## [2025-07-19T01:19:22.935Z] - General Error - -Error сборки: Failed to resolve entry for package '@extension/vite-config'. Решение: перевести пакет на ESM-only, main: dist/index..js - ---- diff --git a/.cursor/backup/rules/memory-bank/errors/runtime-errors.md b/.cursor/backup/rules/memory-bank/errors/runtime-errors.md deleted file mode 100644 index c8b0744e..00000000 --- a/.cursor/backup/rules/memory-bank/errors/runtime-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# runtime errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/typescript-errors.md b/.cursor/backup/rules/memory-bank/errors/typescript-errors.md deleted file mode 100644 index 7f9bbbfb..00000000 --- a/.cursor/backup/rules/memory-bank/errors/typescript-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# TypeScript errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/errors/ui-errors.md b/.cursor/backup/rules/memory-bank/errors/ui-errors.md deleted file mode 100644 index 19cf9b26..00000000 --- a/.cursor/backup/rules/memory-bank/errors/ui-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# ui errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/memory-bank-controller.mdc b/.cursor/backup/rules/memory-bank/memory-bank-controller.mdc deleted file mode 100644 index de782b38..00000000 --- a/.cursor/backup/rules/memory-bank/memory-bank-controller.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Memory Bank Controller - Система управления memory-bank - -## 🎯 **Принцип: Memory-bank полностью подчинен .cursor правилам** - -### **Обоснование:** -- ✅ Оба каталога предназначены исключительно для AI -- ✅ Единая система правил и стандартов -- ✅ Автоматизация всех процессов -- ✅ Консистентность и предсказуемость -- ✅ Масштабируемость и поддерживаемость - -## 📋 **Структура управления memory-bank** - -### **1. Автоматическое создание структуры** -```bash -# Команда для создания структуры memory-bank -node cursor-manager..js create-memory-structure [project-type] - -# Поддерживаемые Typeы проектов: -- React-TypeScript # React + TypeScript проекты -- node-API # Node.js API проекты -- chrome-extension # Chrome Extension проекты -- monorepo # Монорепозитории -- fullstack # Full-stack приложения -``` - -### **2. Шаблоны структуры для разных Typeов проектов** - -#### **React TypeScript Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст React проекта -│ ├── progress.md # Development progress React компонентов -│ ├── projectbrief.md # Description React проекта -│ └── session-log.md # Лог сессий React разработки -├── errors/ -│ ├── errors.md # Ошибки React/TypeScript -│ ├── build-errors.md # Ошибки сборки Vite/Webpack -│ ├── runtime-errors.md # Runtime ошибки React -│ └── ui-errors.md # UI/UX ошибки -├── architecture/ -│ ├── decisions.md # Architectural decisions React -│ ├── patterns.md # React Patterns -│ ├── State-management.md # Управление Stateм -│ └── component-structure.md # Структура компонентов -├── development/ -│ ├── testing-resu.ts.md # Результаты тестирования React -│ ├── debugging-guIDE.md # Debugging React приложений -│ ├── devtools-guIDE.md # React DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── component-library.md # Библиотека компонентов -│ ├── styling-patterns.md # Patterns стилизации -│ └── responsive-design.md # Адаптивный Design -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── optimization-plans.md # Планы оптимизации -│ └── migration-plans.md # Планы миграции -├── context/ -│ ├── tech-stack.md # Технический стек -│ ├── dependencies.md # Dependencies проекта -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -#### **Chrome Extension Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст extension -│ ├── progress.md # Development progress extension -│ ├── projectbrief.md # Description extension -│ └── session-log.md # Лог сессий разработки -├── errors/ -│ ├── errors.md # Ошибки extension -│ ├── manifest-errors.md # Ошибки manifest.json -│ ├── permission-errors.md # Ошибки разрешений -│ └── API-errors.md # Ошибки Chrome APIs -├── architecture/ -│ ├── decisions.md # Architectural decisions -│ ├── background-scri.ts.md # Background scri.ts -│ ├── content-scri.ts.md # Content scri.ts -│ └── popup-structure.md # Структура popup -├── development/ -│ ├── testing-resu.ts.md # Testing extension -│ ├── debugging-guIDE.md # Debugging extension -│ ├── devtools-guIDE.md # Chrome DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── popup-design.md # Design popup -│ ├── options-page.md # Страница настроек -│ └── content-ui.md # UI в content scri.ts -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── store-publishing.md # Публикация в store -│ └── user-feedback.md # Обратная связь пользователей -├── context/ -│ ├── chrome-APIs.md # Используемые Chrome APIs -│ ├── permissions.md # Требуемые разрешения -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -## 🔄 **Автоматизация процессов memory-bank** - -### **1. Автоматическое создание записей** -```bash -# Команды для автоматического создания записей -node cursor-manager..js memory-add "content" --type=error --category=build -node cursor-manager..js memory-add "content" --type=decision --category=architecture -node cursor-manager..js memory-add "content" --type=progress --category=feature -``` - -### **2. Автоматическое обновление контекста** -```bash -# Обновление активного контекста -node cursor-manager..js memory-update-context --auto -node cursor-manager..js memory-update-progress --auto -node cursor-manager..js memory-update-session --auto -``` - -### **3. Автоматическое Recovery контекста** -```bash -# Recovery полного контекста -node cursor-manager..js memory-restore --full -node cursor-manager..js memory-restore --quick -node cursor-manager..js memory-restore --category=errors -``` - -## 📝 **Правила создания и сохранения контекста** - -### **1. Структура записей (унифицированная)** -```markdown -## [YYYY-MM-DD HH:MM:SS] - Краткое Description - -**Type:** error|decision|progress|context|plan -**Category:** build|runtime|architecture|ui|testing|planning -**Priority:** critical|high|medium|low - -**Контекст:** Description ситуации и обстоятельств - -**Детали:** Подробная информация о проблеме/решении - -**Решение/Результат:** Что было сделано и результат - -**Status:** ✅ Решено | 🔄 In Progress | ❌ Проблема | 📋 План - -**Связанные файлы:** -- .cursor/rules/dev/development-principles.mdc -- memory-bank/errors/build-errors.md - -**Теги:** #React #TypeScript #build #vite - -**AI Команды:** -- `аудит cursor` - для проверки .cursor правил -- `задокументируй` - для создания документации -``` - -### **2. Автоматические теги и категоризация** -```JavaScript -// Автоматическое определение тегов -const autoTags = { - 'React': /React.jsx|component|hook/i, - 'TypeScript': /TypeScript.ts|type|interface/i, - 'build': /build|compile|vite|webpack/i, - 'runtime': /runtime|error|crash|exception/i, - 'ui': /ui|ux|component|styling/i, - 'testing': /test|spec|jest|cypress/i, - 'architecture': /architecture|pattern|structure/i -}; -``` - -### **3. Автоматическое связывание с .cursor правилами** -```JavaScript -// Автоматическое создание связей -const ruleConnections = { - 'error': '.cursor/rules/dev/error-handling.mdc', - 'architecture': '.cursor/rules/architecture/patterns.mdc', - 'testing': '.cursor/rules/dev/testing-troubleshooting.mdc', - 'ui': '.cursor/rules/ui/ui-patterns.mdc', - 'build': '.cursor/rules/dev/build-troubleshooting.mdc' -}; -``` - -## 🤖 **AI Команды для управления memory-bank** - -### **1. Команды создания и обновления** -```bash -# Создание новой записи -"создай запись в memory-bank" / "add memory entry" -"добавь ошибку в memory-bank" / "add error to memory" -"запиши решение в memory-bank" / "record solution" - -# Обновление существующих записей -"обнови контекст" / "update context" -"обнови прогресс" / "update progress" -"обнови сессию" / "update session" -``` - -### **2. Команды восстановления и поиска** -```bash -# Recovery контекста -"восстанови контекст" / "restore context" -"восстанови полный контекст" / "restore full context" -"быстрое Recovery" / "quick restore" - -# Поиск информации -"найди в memory-bank" / "search memory" -"покажи ошибки" / "show errors" -"покажи решения" / "show solutions" -``` - -### **3. Команды управления структурой** -```bash -# Управление структурой -"создай структуру memory-bank" / "create memory structure" -"реорганизуй memory-bank" / "reorganize memory" -"очисти memory-bank" / "clean memory" -"аудит memory-bank" / "audit memory" -``` - -## 🔧 **Integration с существующими .cursor правилами** - -### **1. Обновление documentation-helper..js** -```JavaScript -// Add поддержку memory-bank команд -const memoryCommands = { - 'memory-add': 'Add запись в memory-bank', - 'memory-update': 'Update запись в memory-bank', - 'memory-restore': 'Восстановить контекст из memory-bank', - 'memory-search': 'Поиск в memory-bank', - 'memory-audit': 'Аудит memory-bank' -}; -``` - -### **2. Обновление ai-memory.mdc** -```markdown -### Memory Bank Management: -- `создай запись в memory-bank` - Create новую запись с автоматической категоризацией -- `обнови контекст` - Update activeContext.md с текущим Statusом -- `восстанови контекст` - Восстановить полный контекст из memory-bank -- `аудит memory-bank` - Провести аудит и оптимизацию memory-bank -- `реорганизуй memory-bank` - Реорганизовать структуру по новым правилам -``` - -### **3. Обновление cursor-manager..js** -```JavaScript -// Add команды управления memory-bank -case 'memory-add': - await this.memoryAdd(options); - break; -case 'memory-update': - await this.memoryUpdate(options); - break; -case 'memory-restore': - await this.memoryRestore(options); - break; -case 'memory-audit': - await this.memoryAudit(options); - break; -``` - -## 📊 **Автоматические отчеты и аналитика** - -### **1. Отчеты по memory-bank** -```bash -# Генерация отчетов -node cursor-manager..js memory-report --type=errors -node cursor-manager..js memory-report --type=progress -node cursor-manager..js memory-report --type=full -``` - -### **2. Аналитика использования** -```JavaScript -// Метрики memory-bank -const metrics = { - totalEntries: 0, - entriesByCategory: {}, - entriesByType: {}, - recentActivity: [], - mostReferencedRules: [], - errorResolutionRate: 0 -}; -``` - -## ✅ **Результат полной интеграции** - -### **Преимущества:** -- ✅ **Единая система правил** - все управляется через .cursor -- ✅ **Автоматизация** - Minimum ручного вмешательства -- ✅ **Консистентность** - единообразные форматы и структуры -- ✅ **Масштабируемость** - легко добавлять новые Typeы проектов -- ✅ **AI-Optimization** - все правила учитывают потребности AI -- ✅ **Воспроизводимость** - одинаковые результаты на разных проектах - -### **Workflow:** -1. **Создание проекта** → Автоматическое создание структуры memory-bank -2. **Development** → Автоматическое создание записей через AI команды -3. **Recovery контекста** → Автоматическое чтение и анализ memory-bank -4. **Аудит и Optimization** → Регулярные проверки и улучшения - -**Memory-bank теперь полностью интегрирован с .cursor правилами!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/memory-bank/planning/README.md b/.cursor/backup/rules/memory-bank/planning/README.md deleted file mode 100644 index d61ef5fc..00000000 --- a/.cursor/backup/rules/memory-bank/planning/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Planning & Roadmap - React-TypeScript - -## Files in this category: - -- [feature-roadmap](./feature-roadmap.md) -- [migration-plans](./migration-plans.md) -- [optimization-plans](./optimization-plans.md) -- [tech-debt](./tech-debt.md) - -## Description: - -Планы развития проекта, roadmap, стратегические решения. - -## AI Commands: - -- `добавь в planning` - Add запись в эту категорию -- `обнови planning` - Update файлы в категории -- `покажи planning` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/feature-roadmap.md b/.cursor/backup/rules/memory-bank/planning/feature-roadmap.md deleted file mode 100644 index 90ffb932..00000000 --- a/.cursor/backup/rules/memory-bank/planning/feature-roadmap.md +++ /dev/null @@ -1,12 +0,0 @@ -# Feature Roadmap - React-TypeScript - -## Overview - -This file contains feature planning and roadmap. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/migration-plans.md b/.cursor/backup/rules/memory-bank/planning/migration-plans.md deleted file mode 100644 index 012f80e1..00000000 --- a/.cursor/backup/rules/memory-bank/planning/migration-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# migration plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/optimization-plans.md b/.cursor/backup/rules/memory-bank/planning/optimization-plans.md deleted file mode 100644 index a31a1b10..00000000 --- a/.cursor/backup/rules/memory-bank/planning/optimization-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# optimization plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/planning/tech-debt.md b/.cursor/backup/rules/memory-bank/planning/tech-debt.md deleted file mode 100644 index 01cbe91c..00000000 --- a/.cursor/backup/rules/memory-bank/planning/tech-debt.md +++ /dev/null @@ -1,12 +0,0 @@ -# tech debt - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/README.md b/.cursor/backup/rules/memory-bank/ui/README.md deleted file mode 100644 index 47639357..00000000 --- a/.cursor/backup/rules/memory-bank/ui/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# UI/UX Context - React-TypeScript - -## Files in this category: - -- [accessibility](./accessibility.md) -- [component-library](./component-library.md) -- [performance](./performance.md) -- [responsive-design](./responsive-design.md) -- [styling-patterns](./styling-patterns.md) - -## Description: - -UI/UX решения и улучшения, User Experience. - -## AI Commands: - -- `добавь в ui` - Add запись в эту категорию -- `обнови ui` - Update файлы в категории -- `покажи ui` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/accessibility.md b/.cursor/backup/rules/memory-bank/ui/accessibility.md deleted file mode 100644 index e80643a1..00000000 --- a/.cursor/backup/rules/memory-bank/ui/accessibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# accessibility - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/component-library.md b/.cursor/backup/rules/memory-bank/ui/component-library.md deleted file mode 100644 index 5c32e785..00000000 --- a/.cursor/backup/rules/memory-bank/ui/component-library.md +++ /dev/null @@ -1,12 +0,0 @@ -# component library - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/performance.md b/.cursor/backup/rules/memory-bank/ui/performance.md deleted file mode 100644 index e7b7c8ed..00000000 --- a/.cursor/backup/rules/memory-bank/ui/performance.md +++ /dev/null @@ -1,12 +0,0 @@ -# performance - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/responsive-design.md b/.cursor/backup/rules/memory-bank/ui/responsive-design.md deleted file mode 100644 index d26e30f1..00000000 --- a/.cursor/backup/rules/memory-bank/ui/responsive-design.md +++ /dev/null @@ -1,12 +0,0 @@ -# responsive design - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/memory-bank/ui/styling-patterns.md b/.cursor/backup/rules/memory-bank/ui/styling-patterns.md deleted file mode 100644 index 05d60aae..00000000 --- a/.cursor/backup/rules/memory-bank/ui/styling-patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# styling patterns - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/backup/rules/workflow/ci-automation.mdc b/.cursor/backup/rules/workflow/ci-automation.mdc deleted file mode 100644 index 202310ac..00000000 --- a/.cursor/backup/rules/workflow/ci-automation.mdc +++ /dev/null @@ -1,85 +0,0 @@ ---- -description: Automation and synchronization rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: trueaiPriority: medium -aiCategory: process-management ---- - -# CI/CD Automation & Safe Delete Practices - -## GitHub Actions Workflow для .cursor Rules - -Add `.github/workflows/rules-check.yml`: -``.yaml -name: Rules Structure Check -on: - push: - paths: - - '.cursor/rules/**' - pull_request: - paths: - - '.cursor/rules/**' -jobs: - check-rules: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: '18' - - name: Generate index and README structure - run: node .cursor/rules/cursor-manager..js full - - name: Check for uncommitted changes - run: | - git diff --exit-code || (echo 'Rules structure is outdated. Please run cursor-manager..js full and commit the result.' && exit 1) -``` - -## Safe Delete Protection - -### Защищенные директории: -- `docs/for-ai-best-practices` -- `memory-bank` -- `platform-core` -- `chrome-extension/public/plugins` -- `.cursor/rules` - -### GitHub Actions Verification: -``.yaml -- name: Check for protected directory deletion - run: | - if git diff --name-status | grep -E 'D[[:space:]]+(docs/for-ai-best-practices|memory-bank|platform-core|chrome-extension/public/plugins|\.cursor/rules)'; then - echo '::warning::Attempted deletion of a protected directory! Review required.' - fi -``` - -## Automated PR Checks с Danger.js - -- Verification детального описания PR -- Verification обновления changelog при изменении кода -- Требование cross-references к правилам -- Напоминание Update документацию при изменении src/core - -## Automated Releases с semantic-release - -- Анализ conventional comm.ts -- Автоматическое обновление CHANGELOG.md -- Создание git tags и релизов -- Запуск автоматически при мерже в main - -## Automated Dependency Updates с Renovate - -- Создание PR с группированными обновлениями -- Все обновления проходят CI и danger.js -- PR помечаются labels: dependencies, renovate -- Automerge отключен (можно включить при желании) - -## Automated Security Audit - -- `pnpm audit` на каждом push/PR -- Отчет о vulnerabilities (moderate и выше) в CI -- Быстрый ответ на критические проблемы -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/backup/rules/workflow/experience-transfer.mdc b/.cursor/backup/rules/workflow/experience-transfer.mdc deleted file mode 100644 index 443713ac..00000000 --- a/.cursor/backup/rules/workflow/experience-transfer.mdc +++ /dev/null @@ -1,73 +0,0 @@ ---- -description: experience transfer rule -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: falseaiPriority: medium -aiCategory: process-management ---- - -# Experience Transfer & Project Bo.tstrap - -## Автоматизация переноса опыта - -**Автоматизация** — это когда ассистент не просто даёт Tipы, а сам выполняет все технические шаги по внедрению best practices и автоматизаций: - -### Что автоматизируется: -- Автоматически копирует и адаптирует конфиги (husky, danger.js, semantic-release, commitlint, renovate, CI/CD и т.д.) -- Устанавливает необходимые npm-Packages и настраивает их -- Добавляет шаблоны PR, чек-листы, хуки, скрипты -- Вносит изменения в package.json, .github/workflows, .husky/ и другие служебные файлы -- Генерирует или обновляет документацию -- Проводит аудит проекта и сообщает о несоответствиях или необходимых доработках - -### Команда для автоматизации: -``` -Внедри все best practices и автоматизации из .cursor/rules в этот проект, установи необходимые Dependencies, настрой CI/CD, husky, danger.js, semantic-release, commitlint, renovate и т.д. Проведи аудит и сообщи, если что-то требует ручной доработки. -``` - -## Быстрый старт для нового проекта - -### 1. Экспорт из исходного проекта: -```bash -cd .cursor/rules -node cursor-manager..js export new-project-name -``` - -### 2. Копирование в новый проект: -```bash -cp -r cursor-export /path/to/new-project/ -``` - -### 3. Импорт в новый проект: -```bash -cd /path/to/new-project -node cursor-export/import-cursor..js -``` - -### 4. Кастомизация: -```bash -cd .cursor/rules -node cursor-manager..js full -``` - -### 5. Аудит результата: -```bash -node cursor-manager..js status -node cursor-manager..js audit -``` - -## Команды для AI: -- `экспорт cursor` - Export правила для переноса -- `импорт cursor` - Import правила в текущий проект -- `внедри best practices` - Внедрить все автоматизации и Standards -- `аудит проекта` - Провести аудит на соответствие best practices - -## Результат: -- Быстрый старт новых проектов -- Консистентность между проектами -- Автоматизация рутинных задач -- Улучшенное AI-ассистирование -- Стандартизация процессов разработки -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/index.mdc b/.cursor/index.mdc old mode 100644 new mode 100755 diff --git a/.cursor/rules/AUTOMATION.md b/.cursor/rules/AUTOMATION.md deleted file mode 100644 index 25f94ef3..00000000 --- a/.cursor/rules/AUTOMATION.md +++ /dev/null @@ -1,205 +0,0 @@ -# .cursor Automation System - -Комплексная система automation для аудита, исправления и оптимизации каталога `.cursor` для лучшего понимания AI и Cursor. - -## 🎯 **Purpose** - -Система automation обеспечивает: -- **Автоматический аудит** каталога `.cursor` на предмет проблем -- **Исправление** найденных проблем (дубликаты, сломанные ссылки, метаdata) -- **Оптимизацию** правил специально для AI и Cursor -- **Monitoring** состояния и качества правил - -## 🛠️ **Компоненты системы** - -### **1. CursorAuditor** (`audit-cursor..js`) -- Сканирование всех файлов в `.cursor/rules` -- Verification метаданных и их валидности -- Поиск дублирующего контента -- Verification сломанных ссылок -- Validation структуры каталога -- Генерация подробных отчетов - -### **2. CursorFixer** (`fix-cursor..js`) -- Автоматическое исправление метаданных -- Удаление дублирующих файлов -- Исправление сломанных ссылок -- Обновление индексов - -### **3. AIOptimizer** (`optimize-for-ai..js`) -- Optimization метаданных для AI -- Добавление AI-специфичных тегов -- Purpose Priorityов и категорий -- Создание AI-оптимизированных индексов - -### **4. CursorManager** (`cursor-manager..js`) -- Главный Interface для всех операций -- Управление workflow -- Генерация отчетов о Statusе -- CLI Interface - -## 🚀 **Usage** - -### **Базовые команды:** - -```bash -# Показать справку -node cursor-manager..js help - -# Check Status -node cursor-manager..js status - -# Запустить аудит -node cursor-manager..js audit - -# Применить исправления -node cursor-manager..js fix - -# Оптимизировать для AI -node cursor-manager..js optimize - -# Полный workflow -node cursor-manager..js full -``` - -### **Опции:** - -```bash -#.json вывод для аудита -node cursor-manager..js audit -.json - -# Пропустить аудит перед исправлениями -node cursor-manager..js fix --no-audit-first - -# Пропустить аудит после оптимизации -node cursor-manager..js optimize --no-audit-after -``` - -## 📊 **Что проверяет аудит** - -### **Файлы и структура:** -- Общее Quantity файлов -- Соотношение `.mdc` и `.md` файлов -- Наличие обязательных директорий -- Наличие обязательных файлов - -### **Метаdata:** -- Наличие.yaml frontmatter -- Валидность полей `description`, `globs`, `alwaysApply` -- AI-специфичные поля `aiPriority`, `aiCategory` - -### **Контент:** -- Дублирующий контент между файлами -- Сломанные ссылки в документации -- Консистентность структуры - -### **AI-Optimization:** -- Priorityы правил (critical, high, medium, normal) -- Категории (system-design, development-practices, etc.) -- AI-специфичные теги и Comme.ts - -## 🤖 **AI-специфичные оптимизации** - -### **Priorityы:** -- **critical**: Применяется ко всему коду и решениям -- **high**: Применяется к большинству задач -- **medium**: Применяется когда релевантно -- **normal**: Применяется когда уместно - -### **Категории:** -- **system-design**: Architecture и структура системы -- **development-practices**: Standards кодирования -- **documentation**: Documentation и Communication -- **plugin-development**: Правила для плагинов -- **security**: Security -- **user-interface**: UI/UX Standards -- **process-management**: Workflow и Processes -- **ai-optimization**: AI-специфичные оптимизации - -## 📈 **Отчеты и метрики** - -### **Статистика:** -- Общее Quantity файлов -- Файлы с/без метаданных -- Quantity проблем -- Процент AI-готовности - -### **Проблемы:** -- Дублирующий контент -- Сломанные ссылки -- Невалидные метаdata -- Отсутствующие файлы/директории - -### **Recommendations:** -- Конвертация `.md` в `.mdc` -- Добавление метаданных -- Удаление дубликатов -- Исправление ссылок - -## 🔄 **Workflow** - -### **Полный workflow (`full`):** -1. **Аудит** - поиск проблем -2. **Исправления** - автоматическое решение -3. **Optimization** - AI-специфичные улучшения -4. **Финальный аудит** - Verification результатов -5. **Отчет** - сравнение до/после - -### **Инкрементальный workflow:** -- `audit` → анализ проблем -- `fix` → исправление -- `optimize` → Optimization -- `status` → Monitoring - -## 📝 **Integration с AI** - -### **Автоматические команды:** -Система интегрирована с AI memory-bank через команды: -- `аудит cursor` / `cursor audit` -- `исправь cursor` / `cursor fix` -- `оптимизируй cursor` / `cursor optimize` -- `полный cursor` / `cursor full` -- `Status cursor` / `cursor status` - -### **AI-оптимизированные файлы:** -- `ai-optimization.mdc` - инструкции для AI -- `ai-index.mdc` - индекс по Priorityам -- AI-теги в каждом файле -- Priorityы и категории в метаданных - -## 🎯 **Результаты** - -После полной оптимизации: -- ✅ Все файлы имеют правильные метаdata -- ✅ Нет дублирующего контента -- ✅ Все ссылки работают -- ✅ AI-специфичные оптимизации применены -- ✅ Priorityы и категории назначены -- ✅ Автоматическое применение критических правил - -## 🔧 **Расширение системы** - -### **Добавление новых проверок:** -1. Create Method в `CursorAuditor` -2. Add в основной workflow -3. Update отчеты - -### **Добавление новых исправлений:** -1. Create Method в `CursorFixer` -2. Интегрировать в процесс исправления -3. Add в отчеты - -### **Добавление новых оптимизаций:** -1. Create Method в `AIOptimizer` -2. Add AI-специфичную логику -3. Update валидацию - -## 📚 **Связанные файлы** - -- `audit-cursor..js` - Аудит системы -- `fix-cursor..js` - Исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - Главный Interface -- `ai-optimization.mdc` - AI инструкции -- `ai-index.mdc` - AI индекс -- `ai-memory.mdc` - Команды для AI \ No newline at end of file diff --git a/.cursor/rules/EXPERIENCE-TRANSFER.md b/.cursor/rules/EXPERIENCE-TRANSFER.md deleted file mode 100644 index 190ccf3d..00000000 --- a/.cursor/rules/EXPERIENCE-TRANSFER.md +++ /dev/null @@ -1,279 +0,0 @@ -# Experience Transfer Best Practices - -Руководство по переносу опыта работы с Cursor и AI между проектами через систему `.cursor`. - -## 🎯 **Почему `.cursor` лучше `docs/for-ai-best-practices`** - -### **Автоматическое применение** -- ✅ Cursor автоматически читает все файлы из `.cursor/rules/` -- ✅ Правила применяются без дополнительной Settings -- ✅ AI получает контекст сразу при создании чата - -### **Стандартизация** -- ✅ `.cursor` - стандартное место для правил Cursor -- ✅ Все разработчики знают, где искать правила -- ✅ Единообразие между проектами - -### **Структурированность** -- ✅ Четкая иерархия (architecture, dev, doc, plugin, etc.) -- ✅ Метаdata с Priorityами и Categoryми -- ✅ Автоматическая Validation и Optimization - -## 🚀 **Стратегия переноса опыта** - -### **1. Экспорт из исходного проекта** - -```bash -# Из папки .cursor/rules исходного проекта -node cursor-manager..js export - -# С указанием целевого проекта -node cursor-manager..js export my-new-project -``` - -**Создается папка `cursor-export/` с:** -- Все правила по Categoryм -- Скрипты automation -- Инструкции по импорту -- Автоматический скрипт импорта - -### **2. Импорт в целевой проект** - -```bash -# Скопировать cursor-export в целевой проект -cp -r cursor-export /path/to/new-project/ - -# Запустить импорт -cd /path/to/new-project -node cursor-export/import-cursor..js -``` - -### **3. Кастомизация для нового проекта** - -```bash -# Перейти в .cursor/rules -cd .cursor/rules - -# Запустить полную оптимизацию -node cursor-manager..js full - -# Check Status -node cursor-manager..js status -``` - -## 📋 **Что переносится** - -### **Core Rules (обязательные)** -- `ai-memory.mdc` - команды для AI (кастомизировать!) -- `environment.mdc` - Constrai.ts окружения (кастомизировать!) -- `index.mdc` - индекс правил -- `README.mdc` - Documentation структуры - -### **Categories (по необходимости)** -- **architecture/** - архитектурные правила -- **dev/** - Principles разработки -- **doc/** - Standards документации -- **plugin/** - правила для плагинов -- **security/** - правила безопасности -- **ui/** - UI/UX Standards -- **workflow/** - Processes разработки - -### **Automation (Required)** -- `audit-cursor..js` - аудит системы -- `fix-cursor..js` - автоматические исправления -- `optimize-for-ai..js` - AI Optimization -- `cursor-manager..js` - главный Interface - -## 🔧 **Кастомизация для нового проекта** - -### **1. Update environment.mdc** -``.mdc ---- -description: Critical environment limitations -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: environment ---- - -# Environment Constrai.ts - -## Node.js Version -- Required: Node.js 18+ (Update для вашего проекта) - -## Browser Support -- Chrome: 120+ (Update для вашего проекта) -- Firefox: 115+ (Update для вашего проекта) - -## build Tools -- Vite: 5+ (Update для вашего проекта) -- TypeScript: 5+ (Update для вашего проекта) -``` - -### **2. Update ai-memory.mdc** -``.mdc ---- -description: User Commands and AI instructions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Memory Bank - -## Project-Specific Commands -- `build project` - Собрать проект (Update команды) -- `test project` - Запустить тесты (Update команды) -- `deploy project` - Деплой проекта (Update команды) - -## Project Context -- This is a [Type проекта] (Update Description) -- Main technology stack: [стек] (Update стек) -- Key features: [особенности] (Update особенности) -``` - -### **3. Update monorepo-best-practices.mdc** -``.mdc ---- -description: Monorepo structure and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Monorepo Best Practices - -## Project Structure -``` -project/ -├── packages/ # (Update структуру) -├── apps/ # (Update структуру) -├── tools/ # (Update структуру) -└── docs/ # (Update структуру) -``` - -## Package Management -- Package manager: pnpm (Update если нужно) -- Workspace configuration: pnpm-workspace.yaml (Update) -``` - -## 📊 **Workflow переноса** - -### **Полный цикл:** -1. **Экспорт** из исходного проекта -2. **Копирование** в целевой проект -3. **Импорт** с автоматической настройкой -4. **Кастомизация** под новый проект -5. **Validation** и Optimization -6. **Testing** работы AI - -### **Команды для AI:** -```bash -# Экспорт -экспорт cursor -экспорт cursor [проект] - -# Импорт (в целевом проекте) -импорт cursor - -# Verification -Status cursor -полный cursor -``` - -## 🎯 **Best Practices** - -### **1. Инкрементальный перенос** -- Начните с core rules -- Добавляйте категории по мере необходимости -- Тестируйте каждую категорию - -### **2. Кастомизация обязательна** -- Всегда обновляйте `environment.mdc` -- Адаптируйте `ai-memory.mdc` под проект -- Проверяйте актуальность правил - -### **3. Validation после импорта** -```bash -cd .cursor/rules -node cursor-manager..js status -node cursor-manager..js audit -node cursor-manager..js optimize -``` - -### **4. Documentation изменений** -- Ведите changelog изменений -- Комментируйте кастомизации -- Обновляйте README проекта - -### **5. Обратная Compatibility** -- Сохраняйте структуру категорий -- Не удаляйте обязательные файлы -- Тестируйте на разных проектах - -## 🔄 **Автоматизация** - -### **Скрипт быстрого переноса:** -```bash -#!/bin/bash -# quick-transfer.sh - -SOURCE_PROJECT=$1 -TARGET_PROJECT=$2 - -echo "🚀 Quick transfer from $SOURCE_PROJECT to $TARGET_PROJECT" - -# Экспорт из исходного проекта -cd $SOURCE_PROJECT/.cursor/rules -node cursor-manager..js export $TARGET_PROJECT - -# Копирование в целевой проект -cp -r cursor-export $TARGET_PROJECT/ - -# Импорт в целевой проект -cd $TARGET_PROJECT -node cursor-export/import-cursor..js - -# Очистка -rm -rf cursor-export - -echo "✅ Transfer completed!" -``` - -### **Git hooks для automation:** -```bash -# .git/hooks/post-merge -#!/bin/bash -if [ -d ".cursor/rules" ]; then - cd .cursor/rules - node cursor-manager..js status -fi -``` - -## 📈 **Метрики успеха** - -### **После переноса проверьте:** -- ✅ Все файлы имеют правильные метаdata -- ✅ AI команды работают в новом проекте -- ✅ Автоматизация функционирует -- ✅ Нет конфликтов с существующими правилами -- ✅ Performance AI не снизилась - -### **Долгосрочные метрики:** -- Скорость onboarding новых разработчиков -- Quality AI-ассистированной разработки -- Консистентность кода между проектами -- Time на настройку новых проектов - -## 🎉 **Результат** - -После правильного переноса: -- **Быстрый старт** новых проектов -- **Консистентность** между проектами -- **Автоматизация** рутинных задач -- **Улучшенное** AI-ассистирование -- **Стандартизация** процессов разработки - -**Система `.cursor` обеспечивает эффективный перенос опыта между проектами с минимальными усилиями!** 🚀 \ No newline at end of file diff --git a/.cursor/rules/README.mdc b/.cursor/rules/README.mdc deleted file mode 100644 index 60abfdb3..00000000 --- a/.cursor/rules/README.mdc +++ /dev/null @@ -1,117 +0,0 @@ - -# .cursor/rules: Project Rules & AI Onboarding - -This directory contains all rules and standards for automation and AI assista.ts in the Agent Plugins Platform. - -## Principles -- Each rule is a separate .mdc file (one rule — one file) -- Grouped by topic: development principles, architecture, plugin standards, UI/UX, workflow, documentation, etc. -- Each file contains: a short description, globs, the rule .tself, and (optionally) examples or related links -- All rules are in English for international compatibility -- README and index.mdc help with navigation and search - -## CLI Scri.ts - -- **create-rule.js** — Interactive CLI to create a new .mdc rule file from a template. Prom.ts for all required fields and updates index/README automatically. - - Usage: `node .cursor/rules/create-rule.js` -- **generate-rules-index.js** — Scans all .mdc files and regenerates `index.mdc` and the structure section in `README.md`. - - Usage: `node .cursor/rules/generate-rules-index.js` -- **check-rules-structure.js** — Validates all .mdc files for required sections, uniqueness, and valid related links. Used in CI. - - Usage: `node .cursor/rules/check-rules-structure.js` - -## Current Structure -``` -.cursor/ - rules/ - # Root level - critical rules - ai-memory.mdc # User Commands and AI instructions - environment.mdc # Critical environment limitations - monorepo-best-practices.mdc # Monorepo structure and guIDElines - TypeScript-build-troubleshooting.mdc # TypeScript build error solutions - README.mdc # Main documentation - index.mdc # Rules index and navigation - - architecture/ # System architecture rules - architecture-chat-system.mdc - architecture-error-handling.mdc - architecture-observability.mdc - architecture-performance.mdc - architecture-plugin.mdc - architecture-project-structure.mdc - architecture-security.mdc - architecture-workflow.mdc - project-architecture.mdc - - dev/ # Development principles and guIDElines - dev-principle-01-do-no-harm.mdc - dev-principle-03-best-practices.mdc - dev-principle-04-fail-fast-safe.mdc - dev-principle-05-observability.mdc - dev-principle-06-config-as-code.mdc - dev-principle-07-progressive-enhancement.mdc - dev-principle-08-data-integrity-privacy.mdc - dev-principle-09-continuous-learning.mdc - dev-principle-10-ecosystem-thinking.mdc - development-guIDElines.mdc - security-and-deployment.mdc - testing-and-debugging.mdc - TypeScript.mdc - git-workflow.mdc - - doc/ # Documentation and AI standards - ai-first.mdc # AI-oriented documentation standards - knowledge-map.mdc # Project knowledge structure - memorybank-quality.mdc # Memory-bank quality standards - restore-context.mdc # Context restoration procedures - .mdc-file-standards.mdc # .mdc file creation standards - ai-fallback.mdc # AI fallback procedures - - plugin/ # Plugin development standards - plugin-best-practices.mdc - plugin-documentation.mdc - plugin-error-handling.mdc - plugin-performance.mdc - plugin-security.mdc - plugin-structure.mdc - plugin-testing.mdc - - security/ # Security standards - validation.mdc # Input validation rules - - ui/ # UI/UX standards - ui-accessibility.mdc - ui-animation.mdc - ui-error-handling.mdc - ui-forms.mdc - ui-loading-States.mdc - ui-mobile.mdc - ui-navigation.mdc - ui-performance.mdc - ui-React-compone.ts.mdc - ui-styling.mdc - ui-testing.mdc - - workflow/ # Development workflow - automation.mdc - branches.mdc - workflow.mdc -``` - -## AI Fallback Rule - -If an AI agent cannot answer a question from .ts own memory-bank, it must first check the .rules directory, then the memory-bank directory. See [doc/ai-fallback.mdc](./doc/ai-fallback.mdc). - -## Main GitHub Repository - -HTTPS://github.com/LebedevIV/agent-plugins-platform-boilerplate - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name ->>>>>>> origin/develop - -## How to Use -- The AI assistant should always refer to specific .mdc files when making decisions -- To add a new rule: create a separate .mdc file in the appropriate section (preferably via the CLI) -- To find a rule: use index.mdc or README.md, or search by file name \ No newline at end of file diff --git a/.cursor/rules/ai-index.mdc b/.cursor/rules/ai-index.mdc deleted file mode 100644 index 823bcc83..00000000 --- a/.cursor/rules/ai-index.mdc +++ /dev/null @@ -1,89 +0,0 @@ ---- -description: AI-optimized index of rules by priority and category -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI-Optimized Rules Index - -## Critical Priority Rules -*Must be applied to all code and decisions* - -- [Ai Index](ai-index.mdc) — AI-optimized index of rules by priority and category -- [Ai Memory](ai-memory.mdc) — User Commands and AI instructions -- [Ai Optimization](ai-optimization.mdc) — AI-specific instructions and optimizations for .cursor rules -- [Ai Memory](cursor-export/ai-memory.mdc) — User Commands and AI instructions -- [Environment](cursor-export/environment.mdc) — Critical environment constrai.ts - Node.js version, popup vs sIDEpanel - -## High Priority Rules -*Should be applied to most code and decisions* - -- [Architecture Chat System](architecture/architecture-chat-system.mdc) — Architecture rule for chat-system -- [Architecture Error Handling](architecture/architecture-error-handling.mdc) — Architecture rule for error-handling -- [Architecture Observability](architecture/architecture-observability.mdc) — Architecture rule for observability -- [Architecture Performance](architecture/architecture-performance.mdc) — Architecture rule for performance -- [Architecture Plugin](architecture/architecture-plugin.mdc) — Architecture rule for plugin -- [Architecture Project Structure](architecture/architecture-project-structure.mdc) — Architecture rule for project-structure -- [Architecture Security](architecture/architecture-security.mdc) — Architecture rule for security -- [Architecture Workflow](architecture/architecture-workflow.mdc) — Architecture rule for workflow -- [Project Architecture](architecture/project-architecture.mdc) — Architecture rule for project-architecture -- [Dev Principle 01 Do No Harm](cursor-export/dev/dev-principle-01-do-no-harm.mdc) — Development principle harm - dev principle 01 do no harm -- [Dev Principle 03 Best Practices](cursor-export/dev/dev-principle-03-best-practices.mdc) — Development principle practices - dev principle 03 best practices -- [Dev Principle 04 Fail Fast Safe](cursor-export/dev/dev-principle-04-fail-fast-safe.mdc) — Development principle safe - dev principle 04 fail fast safe -- [Dev Principle 05 Observability](cursor-export/dev/dev-principle-05-observability.mdc) — Development principle observability - dev principle 05 observability -- [Dev Principle 06 config As Code](cursor-export/dev/dev-principle-06-config-as-code.mdc) — Development principle code - dev principle 06 config as code -- [Dev Principle 07 Progressive Enhancement](cursor-export/dev/dev-principle-07-progressive-enhancement.mdc) — Development principle enhancement - dev principle 07 progressive enhancement -- [Dev Principle 08 data Integrity Privacy](cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc) — Development principle privacy - dev principle 08 data integrity privacy -- [Dev Principle 09 Continuous Learning](cursor-export/dev/dev-principle-09-continuous-learning.mdc) — Development principle learning - dev principle 09 continuous learning -- [Dev Principle 10 Ecosystem Thinking](cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc) — Development principle thinking - dev principle 10 ecosystem thinking - -## Medium Priority Rules -*Apply when relevant to the task* - -- [Ai Fallback](cursor-export/doc/ai-fallback.mdc) — Fallback procedures for AI -- [Ai First](cursor-export/doc/ai-first.mdc) — AI-oriented documentation standards -- .mdc File Standards](cursor-export/doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [Workflow](cursor-export/workflow/workflow.mdc) — Workflow rule for -- [Ai Answer Self Check](doc/ai-answer-self-check.mdc) — ai answer self check rule -- [Ci Automation](workflow/ci-automation.mdc) — Automation and synchronization rules -- [Experience Transfer](workflow/experience-transfer.mdc) — experience transfer rule - -## Normal Priority Rules -*Apply when appropriate* - -- [README](README.mdc) — Main project rules documentation and AI onboarding - structure, CLI scri.ts, usage -- [Development GuIDElines](cursor-export/dev/development-guIDElines.mdc) — General development guIDElines -- [Git Workflow](cursor-export/dev/git-workflow.mdc) — Git workflow rule - only develop to main, mandatory PR for all changes -- [Security And Deployment](cursor-export/dev/security-and-deployment.mdc) — Security rule for -and-deployment -- [Testing And Debugging](cursor-export/dev/testing-and-debugging.mdc) — Testing and debugging standards -- [TypeScript](cursor-export/dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Knowledge Map](cursor-export/doc/knowledge-map.mdc) — Project knowledge structure -- [Memorybank Quality](cursor-export/doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](cursor-export/doc/restore-context.mdc) — Context restoration procedures -- [Index](cursor-export/index.mdc) — index rule -- [Monorepo Best Practices](cursor-export/monorepo-best-practices.mdc) — Best practices for monorepo work - structure, dependencies, TypeScript, barrel expo.ts -- [Plugin Best Practices](cursor-export/plugin/plugin-best-practices.mdc) — Plugin standard for best-practices -- [Plugin Documentation](cursor-export/plugin/plugin-documentation.mdc) — Plugin standard for documentation -- [Plugin Error Handling](cursor-export/plugin/plugin-error-handling.mdc) — Plugin standard for error-handling -- [Plugin Performance](cursor-export/plugin/plugin-performance.mdc) — Plugin standard for performance -- [Plugin Security](cursor-export/plugin/plugin-security.mdc) — Plugin standard for security -- [Plugin Structure](cursor-export/plugin/plugin-structure.mdc) — Plugin standard for structure -- [Plugin Testing](cursor-export/plugin/plugin-testing.mdc) — Plugin standard for testing -- [Validation](cursor-export/security/validation.mdc) — Input validation and data sanitization rules -- [TypeScript build Troubleshooting](cursor-export/TypeScript-build-troubleshooting.mdc) — TypeScript build error troubleshooting guIDE - types, barrel expo.ts, dependencies, Tailwin.css -- [Ui Accessibility](cursor-export/ui/ui-accessibility.mdc) — UI standard for accessibility -- [Ui Animation](cursor-export/ui/ui-animation.mdc) — UI standard for animation -- [Ui Error Handling](cursor-export/ui/ui-error-handling.mdc) — UI standard for error-handling -- [Ui Forms](cursor-export/ui/ui-forms.mdc) — UI standard for forms -- [Ui Loading States](cursor-export/ui/ui-loading-States.mdc) — UI standard for loading-States -- [Ui Mobile](cursor-export/ui/ui-mobile.mdc) — UI standard for mobile -- [Ui Navigation](cursor-export/ui/ui-navigation.mdc) — UI standard for navigation -- [Ui Performance](cursor-export/ui/ui-performance.mdc) — UI standard for performance -- [Ui React Compone.ts](cursor-export/ui/ui-React-compone.ts.mdc) — UI standard for React-compone.ts -- [Ui Styling](cursor-export/ui/ui-styling.mdc) — UI standard for styling -- [Ui Testing](cursor-export/ui/ui-testing.mdc) — UI standard for testing -- [Automation](cursor-export/workflow/automation.mdc) — Automation and synchronization rules -- [Branches](cursor-export/workflow/branches.mdc) — Git branch management rules -- [Testing Troubleshooting](dev/testing-troubleshooting.mdc) — Testing and debugging standards diff --git a/.cursor/rules/ai-memory.mdc b/.cursor/rules/ai-memory.mdc deleted file mode 100644 index c74c3224..00000000 --- a/.cursor/rules/ai-memory.mdc +++ /dev/null @@ -1,38 +0,0 @@ -# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/rules/ai-optimization.mdc b/.cursor/rules/ai-optimization.mdc deleted file mode 100644 index e7b985b1..00000000 --- a/.cursor/rules/ai-optimization.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: AI-specific instructions and optimizations for .cursor rules -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: ai-optimization ---- - -# AI Optimization Instructions - -## How AI Should Use These Rules - -### Priority Levels -- **critical**: Must be applied to all code and decisions -- **high**: Should be applied to most code and decisions -- **medium**: Apply when relevant to the task -- **normal**: Apply when appropriate - -### Categories -- **system-design**: Architecture and system structure -- **development-practices**: Coding standards and principles -- **documentation**: Documentation and communication -- **plugin-development**: Plugin-specific rules -- **security**: Security and safety rules -- **user-interface**: UI/UX standards -- **process-management**: Workflow and process rules -- **ai-optimization**: AI-specific optimizations - -### Usage Patterns -1. Always check critical rules first -2. Apply high-priority rules to most tasks -3. Use category-specific rules for targeted tasks -4. Reference related rules when making decisions - -### AI Memory Integration -- These rules are automatically loaded into AI memory-bank -- Use `alwaysApply: true` rules without explicit reference -- Reference specific rules when explaining decisions -- Update rules when patterns change or improve - -## Optimization Status -- ✅ All rules have proper metadata -- ✅ AI-specific tags added -- ✅ Priority levels assigned -- ✅ Categories defined -- ✅ Structure optimized for AI understanding diff --git a/.cursor/rules/architecture/architecture-chat-system.mdc b/.cursor/rules/architecture/architecture-chat-system.mdc deleted file mode 100644 index 3eb5b78c..00000000 --- a/.cursor/rules/architecture/architecture-chat-system.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for chat-system -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Chat System Architecture -- Per-page, per-plugin isolation: Ch.ts separated by page URL and plugin -- LRU cache: In-memory cache for performance (50 ch.ts) -- IndexedDB: Persistent storage for all ch.ts -- Real-time sync: Cross-tab communication for chat updates -- Cleanup: Automatic removal of ch.ts older than 90 days - -description: Chat system architecture and data flow -globs: - - platform-core/* - - chrome-extension/src/background/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-error-handling.mdc b/.cursor/rules/architecture/architecture-error-handling.mdc deleted file mode 100644 index 7aef574e..00000000 --- a/.cursor/rules/architecture/architecture-error-handling.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for error-handling -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Error Handling (Architecture) -- Graceful Degradation: System continues working with reduced functionality -- User Feedback: Clear error messages and recovery options -- Logging: Structured logging for debugging and monitoring -- Fallbacks: Alternative implementations for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-error-handling.mdc - - architecture-security.mdc - -description: Error handling requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-observability.mdc b/.cursor/rules/architecture/architecture-observability.mdc deleted file mode 100644 index 1f47a11f..00000000 --- a/.cursor/rules/architecture/architecture-observability.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for observability -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Monitoring and Observability (Architecture) -- Structured Logging: Consistent log format across compone.ts -- Performance Metrics: Track execution time and memory usage -- Error Tracking: Monitor and alert on critical errors -- User Analytics: Track feature usage and performance -- Health Checks: Monitor system health and dependencies - -related: - - architecture-performance.mdc - - dev-principle-05-observability.mdc - -description: Monitoring and observability requireme.ts for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-performance.mdc b/.cursor/rules/architecture/architecture-performance.mdc deleted file mode 100644 index 636e5dc1..00000000 --- a/.cursor/rules/architecture/architecture-performance.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for performance -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Performance GuIDElines (Architecture) -- PyodIDE Loading: Lazy load runtime, cache resu.ts -- Memory Management: Clean up resources, monitor usage -- Bundle Size: Optimize for extension size lim.ts -- Caching: Use LRU cache for frequently accessed data -- Async Operations: Non-blocking UI, proper loading States - -related: - - plugin-performance.mdc - - architecture-observability.mdc - -description: Performance guIDElines for system architecture -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-plugin.mdc b/.cursor/rules/architecture/architecture-plugin.mdc deleted file mode 100644 index 3ef2571d..00000000 --- a/.cursor/rules/architecture/architecture-plugin.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for plugin -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Plugin Architecture -- Manifest Structure: manifest.json + mcp_server.py +.icon.svg -- MCP Protocol: Standard MCP server implementation in Python -- Permission Model: Zero Trust - plugins not trusted by default -- Isolation: Each plugin in separate worker for security -- Communication: chrome.runtime.sendMessage for all cross-component communication - -description: Plugin architecture and isolation requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-project-structure.mdc b/.cursor/rules/architecture/architecture-project-structure.mdc deleted file mode 100644 index 6220eeac..00000000 --- a/.cursor/rules/architecture/architecture-project-structure.mdc +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-structure -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Structure Architecture -- platform-core/: Main logic and services (keep boilerplate clean) -- chrome-extension/src/background/: Core background services -- pages/*/src/: UI compone.ts for different extension pages -- memory-bank/: Project documentation and context -- public/plugins/: Plugin implementations - -description: Directory and component structure for the project -globs: - - platform-core/* - - chrome-extension/src/background/* - - pages/*/src/* - - memory-bank/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-security.mdc b/.cursor/rules/architecture/architecture-security.mdc deleted file mode 100644 index 9e8d63a1..00000000 --- a/.cursor/rules/architecture/architecture-security.mdc +++ /dev/null @@ -1,40 +0,0 @@ ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for security -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Security Architecture -- Secret Manager: Encrypted storage of API keys in chrome.storage.local -- Network Guard: Whitelist domains for API calls -- Audit System: Log all plugin activities and network reque.ts -- Parameter Validation:.json Schema validation for all inp.ts -- Rate Limiting: Prevent abuse and suspicious activity - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Security architecture and controls -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/architecture-workflow.mdc b/.cursor/rules/architecture/architecture-workflow.mdc deleted file mode 100644 index 401f5e21..00000000 --- a/.cursor/rules/architecture/architecture-workflow.mdc +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for workflow -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Development Workflow (Architecture) -- Feature Branches: Always create from develop branch -- Testing: Test before commit, use --no-verify if ESLint issues -- Documentation: Update memory-bank for all architectural decisions -- Comm.ts: Use semantic commit messages -- Code Quality: TypeScript for new files, ESLint compliance - -description: Development workflow and process standards -globs: - - platform-core/* - - chrome-extension/src/background/* - - public/plugins/* - - memory-bank/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/architecture/file-relationships.mdc b/.cursor/rules/architecture/file-relationships.mdc deleted file mode 100644 index 2376e599..00000000 --- a/.cursor/rules/architecture/file-relationships.mdc +++ /dev/null @@ -1,339 +0,0 @@ ---- -description: File organization patterns and relationships - universal project structure guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: system-design ---- - -# Карта взаимосвязей файлов - -## File Structure и их Purpose - -### 1. Background Layer (Слой фоновых сервисов) - -#### `chrome-extension/src/background/plugin-chat-cache.ts` -**Purpose:** Центральный сервис управления чатами -**Dependencies:** -- `plugin-chat-API.ts` - для работы с IndexedDB -- Chrome Extensions API (chrome.runtime, chrome.tabs) - -**Экспортирует:** -- `PluginChatcache` class -- `Chatdata`, `ChatMessage` interfaces - -**Используется:** -- `background/index.ts` - создание экземпляра и регистрация обработчиков - -**Ключевые Methodы:** -```TypeScript -class PluginChatcache { - getChat(pluginId: string, pageKey: string): Promise - saveMessage(pluginId: string, pageKey: string, message: ChatMessage): Promise - deleteChat(pluginId: string, pageKey: string): Promise - getAllCh.ts(): Promise - cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/plugin-chat-API.ts` -**Purpose:** Abstraction над IndexedDB -**Dependencies:** -- IndexedDB API -- Chrome Extensions API - -**Экспортирует:** -- `Ch.tstorageAPI` class -- database utility functions - -**Используется:** -- `plugin-chat-cache.ts` - для всех операций с Storageм - -**Ключевые Methodы:** -```TypeScript -class Ch.tstorageAPI { - async getChat(pluginId: string, pageKey: string): Promise - async saveChat(chatdata: Chatdata): Promise - async deleteChat(pluginId: string, pageKey: string): Promise - async getAllCh.ts(): Promise - async cleanupOldCh.ts(): Promise -} -``` - -#### `chrome-extension/src/background/index.ts` -**Purpose:** Точка входа background script -**Dependencies:** -- `plugin-chat-cache.ts` - создание экземпляра cacheа -- Chrome Extensions API - -**Используется:** -- Chrome Extensions runtime (автоматически) - -**Основные функции:** -- Инициализация `PluginChatcache` -- Регистрация обработчиков сообщений -- Управление жизненным циклом - -**Обработчики сообщений:** -```TypeScript -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - switch (request.type) { - case 'GET_PLUGIN_CHAT': - case 'SAVE_PLUGIN_CHAT_MESSAGE': - case 'DELETE_PLUGIN_CHAT': - case 'GET_ALL_PLUGIN_CH.ts': - } -}); -``` - -### 2. UI Layer (Слой пользовательского Interfaceа) - -#### `pages/sIDE-panel/src/compone.ts/PluginControlPanel.tsx` -**Purpose:** Основной компонент управления плагином -**Dependencies:** -- React hooks (useState, useEffect, useRef) -- Chrome Extensions API (chrome.runtime) -- `PluginDetails.tsx` - для отображения деталей плагина - -**Используется:** -- `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` - основной контейнер - -**Ключевые состояния:** -```TypeScript -const [messages, setMessages] = useState([]); -const [loading, setLoading] = useState(false); -const [syncStatus, s.tsyncStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); -``` - -**Основные функции:** -- Загрузка чата при монтировании -- Сохранение сообщений -- Synchronization с другими вкладками -- Экспорт и очистка чатов - -#### `pages/sIDE-panel/src/compone.ts/PluginDetails.tsx` -**Purpose:** Отображение деталей плагина -**Dependencies:** -- React -- Plugin interface - -**Используется:** -- `PluginControlPanel.tsx` - как дочерний компонент - -#### `pages/sIDE-panel/src/compone.ts/SIDEPanel.tsx` -**Purpose:** Основной контейнер sIDE panel -**Dependencies:** -- `PluginControlPanel.tsx` -- `PluginCard.tsx` -- React - -**Используется:** -- Chrome Extensions sIDE panel API - -### 3. DevTools Layer (Слой инструментов разработчика) - -#### `pages/devtools-panel/src/compone.ts/PluginCh.tsTab.tsx` -**Purpose:** Административный Interface для управления чатами -**Dependencies:** -- React hooks -- Chrome Extensions API -- `file-saver` library - -**Используется:** -- `pages/devtools-panel/src/index.tsx` - как вкладка в DevTools - -**Основные функции:** -- Отображение всех чатов -- Удаление чатов -- Экспорт в.json -- Фильтрация и поиск - -#### `pages/devtools-panel/src/index.tsx` -**Purpose:** Точка входа DevTools panel -**Dependencies:** -- `PluginCh.tsTab.tsx` -- React - -**Используется:** -- Chrome DevTools API - -### 4. Shared Layer (Общий слой) - -#### `chrome-extension/src/background/types.ts` (предлагаемый) -**Purpose:** Общие Typeы для всей системы -**Dependencies:** -- TypeScript - -**Экспортирует:** -```TypeScript -export interface ChatMessage { - id: string; - role: 'user' | 'assistant'; - content: string; - timestamp: number; -} - -export interface Chatdata { - pluginId: string; - pageKey: string; - messages: ChatMessage[]; - lastUpdated: number; - createdAt: number; -} - -export type ChatRequest = - | { type: 'GET_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'SAVE_PLUGIN_CHAT_MESSAGE'; pluginId: string; pageKey: string; message: ChatMessage } - | { type: 'DELETE_PLUGIN_CHAT'; pluginId: string; pageKey: string } - | { type: 'GET_ALL_PLUGIN_CH.ts' }; - -export type ChatEvent = - | { type: 'PLUGIN_CHAT_UPDATED'; pluginId: string; pageKey: string; messages?: ChatMessage[] }; -``` - -**Используется:** -- Все компоненты системы чатов - -## Threads данных между файлами - -### 1. Загрузка чата -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getChat) -plugin-chat-cache.ts - ↓ (storageAPI.getChat) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -### 2. Сохранение сообщения -``` -PluginControlPanel.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.saveMessage) -plugin-chat-cache.ts - ↓ (storageAPI.saveChat + event broadcast) -plugin-chat-API.ts + chrome.tabs.query - ↓ (IndexedDB + other tabs) -Browser Storage + Other UI Compone.ts -``` - -### 3. Административные операции -``` -PluginCh.tsTab.tsx - ↓ (chrome.runtime.sendMessage) -background/index.ts - ↓ (chatcache.getAllCh.ts/deleteChat) -plugin-chat-cache.ts - ↓ (storageAPI) -plugin-chat-API.ts - ↓ (IndexedDB) -Browser Storage -``` - -## Dependencies и импорты - -### Background Layer -```TypeScript -// background/index.ts -import { PluginChatcache } from './plugin-chat-cache'; -import type { ChatRequest, ChatEvent } from './types'; - -// plugin-chat-cache.ts -import { Ch.tstorageAPI } from './plugin-chat-API'; -import type { Chatdata, ChatMessage } from './types'; - -// plugin-chat-API.ts -// Нет внешних зависимостей, только IndexedDB API -``` - -### UI Layer -```TypeScript -// PluginControlPanel.tsx -import { PluginDetails } from './PluginDetails'; -import type { ChatMessage } from '../../../background/types'; - -// PluginDetails.tsx -import type { Plugin } from './PluginCard'; - -// SIDEPanel.tsx -import { PluginControlPanel } from './PluginControlPanel'; -import { PluginCard } from './PluginCard'; -``` - -### DevTools Layer -```TypeScript -// PluginCh.tsTab.tsx -import { saveAs } from 'file-saver'; -import type { Chatdata } from '../../../background/types'; - -// devtools-panel/index.tsx -import { PluginCh.tsTab } from './compone.ts/PluginCh.tsTab'; -``` - -## configuration Files - -### `chrome-extension/manifest.json` -**Purpose:** configuration Extensions -**Содержит:** -- Background script registration -- SIDE panel configuration -- DevTools panel configuration -- Permissions - -### `package.json` (в каждой папке pages) -**Purpose:** Dependencies и скрипты сборки -**Содержит:** -- React dependencies -- build tools -- TypeScript configuration - -## Стили и ресурсы - -###.css файлы -- `PluginControlPanel.css` - стили для основного компонента -- `SIDEPanel.css` - стили для sIDE panel -- `PluginCard.css` - стили для карточек плагинов - -### Иконки и изображения -- .icon.svg` - иконки плагинов -- ЛогоTypeы и брендинг - -## Тестовые файлы - -### Unit Te.ts -- `__te.ts__/plugin-chat-cache.test.ts` -- `__te.ts__/plugin-chat-API.test.ts` -- `__te.ts__/PluginControlPanel.test.tsx` - -### Integration Te.ts -- `__te.ts__/chat-system.integration.test.ts` - -### E2E Te.ts -- `te.ts/e2e/chat-functionality.test.ts` - -## Documentation - -### Техническая Documentation -- `README.md` - общее Description -- `API.md` - Documentation API -- `ARCHITECTURE.md` - архитектурное Description - -### Пользовательская Documentation -- `USER_GUIDE.md` - руководство пользователя -- `DEVELOPER_GUIDE.md` - руководство разработчика - -## Заключение - -Система чатов плагинов имеет четкую модульную архитектуру с разделением ответственности: - -1. **Background Layer** - управление данными и бизнес-логика -2. **UI Layer** - пользовательский Interface -3. **DevTools Layer** - административные функции -4. **Shared Layer** - общие Typeы и утилиты - -Все компоненты связаны через messaging API, что обеспечивает loose coupling и легкую тестируемость. Каждый файл имеет четко определенную ответственность и минимальные Dependencies. \ No newline at end of file diff --git a/.cursor/rules/architecture/project-architecture.mdc b/.cursor/rules/architecture/project-architecture.mdc deleted file mode 100644 index e9846dba..00000000 --- a/.cursor/rules/architecture/project-architecture.mdc +++ /dev/null @@ -1,47 +0,0 @@ ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - - - - - ---- -description: Architecture rule for project-architecture -globs: ["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"] -alwaysApply: trueaiPriority: high -aiCategory: system-design ---- - -# Project Overview -Agent-Plugins-Platform (APP) is a browser extension for running Python plugins via PyodIDE and the MCP protocol. Key priorities: security, performance, and developer experience. - -## Architecture Patterns - -### Core Compone.ts -- **Plugin Manager** (`core/plugin-manager.js`): manages plugin lifecycle -- **Host API** (`core/host-API.js`): provIDEs Python access to browser APIs -- **Workflow Engine** (`core/workflow-engine.js`): executes plugin workflows -- **MCP Bridge** (`Bridge/mcp-Bridge.js`):.js ↔ Python communication -- **PyodIDE Worker** (`Bridge/pyodIDE-worker.js`): isolated Python execution - -### Plugin Structure -``` -public/plugins/plugin-name/ -├── manifest.json # Metadata and permissions -├── mcp_server.py # MCP protocol (Python) -├── workflow.json # Workflow description -└──.icon.svg #.icon -``` - -### Communication Flow -1. UI → Plugin Manager → MCP Bridge → PyodIDE Worker → Python Plugin -2. Python Plugin → Host API → Browser APIs -3. Resu.ts return via the same path -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/audit-cursor.cjs b/.cursor/rules/audit-cursor.cjs deleted file mode 100644 index 7a497b39..00000000 --- a/.cursor/rules/audit-cursor.cjs +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorAuditor { - constructor() { - this.rulesDir = path.join(__dirname); - this.issues = []; - this.stats = { - totalFiles: 0, - mdcFiles: 0, - mdFiles: 0, - filesWithMetadata: 0, - filesWithoutMetadata: 0, - duplicateContent: [], - brokenLinks: [], - missingFiles: [], - invalidMetadata: [] - }; - } - - // Основной метод аудита - async audit() { - console.log('🔍 Starting comprehensive .cursor audit...\n'); - - await this.scanFiles(); - await this.checkMetadata(); - await this.findDuplicates(); - await this.checkLinks(); - await this.validateStructure(); - await this.generateReport(); - - return this.stats; - } - - // Сканирование всех файлов - async scanFiles() { - console.log('📁 Scanning files...'); - - const files = this.getAllFiles(this.rulesDir); - this.stats.totalFiles = files.length; - - for (const file of files) { - const ext = path.extname(file); - if (ext === '.mdc') { - this.stats.mdcFiles++; - } else if (ext === '.md') { - this.stats.mdFiles++; - } - } - - console.log(` Found ${this.stats.totalFiles} files (${this.stats.mdcFiles} .mdc, ${this.stats.mdFiles} .md)`); - } - - // Проверка метаданных - async checkMetadata() { - console.log('📋 Checking metadata...'); - - const mdcFiles = this.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - - if (content.startsWith('---')) { - this.stats.filesWithMetadata++; - - // Проверяем валидность метаданных - const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (metadataMatch) { - const metadata = metadataMatch[1]; - const hasDescription = metadata.includes('description:'); - const hasGlobs = metadata.includes('globs:'); - const hasAlwaysApply = metadata.includes('alwaysApply:'); - - if (!hasDescription || !hasGlobs || !hasAlwaysApply) { - this.stats.invalidMetadata.push({ - file: path.relative(this.rulesDir, file), - missing: [] - }); - - if (!hasDescription) this.stats.invalidMetadata[this.stats.invalidMetadata.length - 1].missing.push('description'); - if (!hasGlobs) this.stats.invalidMetadata[this.stats.invalidMetadata.length - 1].missing.push('globs'); - if (!hasAlwaysApply) this.stats.invalidMetadata[this.stats.invalidMetadata.length - 1].missing.push('alwaysApply'); - } - } - } else { - this.stats.filesWithoutMetadata++; - } - } - - console.log(` ${this.stats.filesWithMetadata} files with metadata, ${this.stats.filesWithoutMetadata} without`); - } - - // Поиск дубликатов - async findDuplicates() { - console.log('🔄 Finding duplicates...'); - - const files = this.getAllFiles(this.rulesDir); - const contentMap = new Map(); - - for (const file of files) { - const content = fs.readFileSync(file, 'utf8'); - const normalizedContent = this.normalizeContent(content); - - if (contentMap.has(normalizedContent)) { - this.stats.duplicateContent.push({ - original: path.relative(this.rulesDir, contentMap.get(normalizedContent)), - duplicate: path.relative(this.rulesDir, file) - }); - } else { - contentMap.set(normalizedContent, file); - } - } - - console.log(` Found ${this.stats.duplicateContent.length} duplicate files`); - } - - // Проверка ссылок - async checkLinks() { - console.log('🔗 Checking links...'); - - const files = this.getAllFiles(this.rulesDir); - - for (const file of files) { - const content = fs.readFileSync(file, 'utf8'); - const links = this.extractLinks(content); - - for (const link of links) { - if (link.startsWith('./') || link.startsWith('../')) { - const targetPath = path.resolve(path.dirname(file), link); - - if (!fs.existsSync(targetPath)) { - this.stats.brokenLinks.push({ - file: path.relative(this.rulesDir, file), - link: link - }); - } - } - } - } - - console.log(` Found ${this.stats.brokenLinks.length} broken links`); - } - - // Валидация структуры - async validateStructure() { - console.log('🏗️ Validating structure...'); - - const expectedDirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - const actualDirs = fs.readdirSync(this.rulesDir) - .filter(item => fs.statSync(path.join(this.rulesDir, item)).isDirectory()); - - for (const expectedDir of expectedDirs) { - if (!actualDirs.includes(expectedDir)) { - this.stats.missingFiles.push(`Directory: ${expectedDir}`); - } - } - - // Проверяем обязательные файлы - const requiredFiles = ['index.mdc', 'README.mdc', 'ai-memory.mdc']; - for (const requiredFile of requiredFiles) { - if (!fs.existsSync(path.join(this.rulesDir, requiredFile))) { - this.stats.missingFiles.push(`File: ${requiredFile}`); - } - } - - console.log(` Found ${this.stats.missingFiles.length} missing items`); - } - - // Генерация отчета - async generateReport() { - console.log('\n📊 Generating audit report...\n'); - - console.log('='.repeat(60)); - console.log('🔍 CURSOR AUDIT REPORT'); - console.log('='.repeat(60)); - - // Общая статистика - console.log('\n📈 GENERAL STATISTICS:'); - console.log(` Total files: ${this.stats.totalFiles}`); - console.log(` .mdc files: ${this.stats.mdcFiles}`); - console.log(` .md files: ${this.stats.mdFiles}`); - console.log(` Files with metadata: ${this.stats.filesWithMetadata}`); - console.log(` Files without metadata: ${this.stats.filesWithoutMetadata}`); - - // Проблемы - if (this.stats.duplicateContent.length > 0) { - console.log('\n⚠️ DUPLICATE CONTENT:'); - for (const dup of this.stats.duplicateContent) { - console.log(` ${dup.duplicate} duplicates ${dup.original}`); - } - } - - if (this.stats.brokenLinks.length > 0) { - console.log('\n🔗 BROKEN LINKS:'); - for (const link of this.stats.brokenLinks) { - console.log(` ${link.file}: ${link.link}`); - } - } - - if (this.stats.invalidMetadata.length > 0) { - console.log('\n📋 INVALID METADATA:'); - for (const meta of this.stats.invalidMetadata) { - console.log(` ${meta.file}: missing ${meta.missing.join(', ')}`); - } - } - - if (this.stats.missingFiles.length > 0) { - console.log('\n❌ MISSING FILES/DIRECTORIES:'); - for (const missing of this.stats.missingFiles) { - console.log(` ${missing}`); - } - } - - // Рекомендации - console.log('\n💡 RECOMMENDATIONS:'); - if (this.stats.mdFiles > 0) { - console.log(' • Consider converting .md files to .mdc for better AI integration'); - } - if (this.stats.filesWithoutMetadata > 0) { - console.log(' • Add metadata to .mdc files without frontmatter'); - } - if (this.stats.duplicateContent.length > 0) { - console.log(' • Remove duplicate files to reduce confusion'); - } - if (this.stats.brokenLinks.length > 0) { - console.log(' • Fix broken links in documentation'); - } - - console.log('\n' + '='.repeat(60)); - } - - // Вспомогательные методы - getAllFiles(dir) { - const files = []; - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - files.push(...this.getAllFiles(fullPath)); - } else if (item.endsWith('.mdc') || item.endsWith('.md')) { - files.push(fullPath); - } - } - - return files; - } - - getFilesByExt(ext) { - return this.getAllFiles(this.rulesDir).filter(file => file.endsWith(ext)); - } - - normalizeContent(content) { - return content - .replace(/\s+/g, ' ') - .replace(/[^\w\s]/g, '') - .toLowerCase() - .trim(); - } - - extractLinks(content) { - const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; - const links = []; - let match; - - while ((match = linkRegex.exec(content)) !== null) { - links.push(match[2]); - } - - return links; - } -} - -// Запуск аудита -async function main() { - const auditor = new CursorAuditor(); - await auditor.audit(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorAuditor; \ No newline at end of file diff --git a/.cursor/rules/auto-translate-requests.cjs b/.cursor/rules/auto-translate-requests.cjs deleted file mode 100644 index 7c001d48..00000000 --- a/.cursor/rules/auto-translate-requests.cjs +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/bin/env node - -/** - * Auto Translate Requests Script - * Automatically translates user requests to English for .cursor rule creation - * Usage: node .cursor/rules/auto-translate-requests.cjs [interactive|translate|setup] - */ - -const fs = require('fs'); -const path = require('path'); -const readline = require('readline'); -const { translateRequest, analyzeRequest, generateEnglishPrompt } = require('./request-translator.cjs'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const RULES_DIR = path.join(CURSOR_DIR, 'rules'); - -function createInteractiveMode() { - console.log('🤖 Auto Translate Requests - Interactive Mode\n'); - console.log('This mode will help you create .cursor rules in English.'); - console.log('Type your request in Russian, and the system will translate it to English.\n'); - - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); - - function askQuestion(question) { - return new Promise((resolve) => { - rl.question(question, resolve); - }); - } - - async function interactiveLoop() { - try { - while (true) { - const userRequest = await askQuestion('📝 Enter your request (or "quit" to exit): '); - - if (userRequest.toLowerCase() === 'quit' || userRequest.toLowerCase() === 'exit') { - console.log('\n👋 Goodbye!'); - break; - } - - if (!userRequest.trim()) { - console.log('⚠️ Please enter a valid request.\n'); - continue; - } - - console.log('\n🔄 Processing your request...\n'); - - // Analyze the request - const analysis = analyzeRequest(userRequest); - const prompt = generateEnglishPrompt(userRequest); - - // Display results - console.log('📊 Request Analysis:'); - console.log(`Original: ${analysis.originalText}`); - console.log(`Translated: ${analysis.translatedText}`); - console.log(`Confidence: ${analysis.translationConfidence.toFixed(1)}%`); - console.log(`Patterns: ${analysis.detectedPatterns.join(', ')}`); - - if (prompt.shouldTranslate) { - console.log('\n🤖 Generated English Prompt:'); - console.log('='.repeat(60)); - console.log(prompt.englishPrompt); - console.log('='.repeat(60)); - - const proceed = await askQuestion('\n✅ Proceed with creating the rule? (y/n): '); - - if (proceed.toLowerCase() === 'y' || proceed.toLowerCase() === 'yes') { - await createRuleFromPrompt(prompt); - } else { - console.log('❌ Rule creation cancelled.\n'); - } - } else { - console.log('\n✅ Request is already in English. Proceeding with rule creation...\n'); - await createRuleFromPrompt(prompt); - } - - console.log('\n' + '='.repeat(60) + '\n'); - } - } catch (error) { - console.error('❌ Error in interactive mode:', error.message); - } finally { - rl.close(); - } - } - - interactiveLoop(); -} - -async function createRuleFromPrompt(prompt) { - try { - console.log('📝 Creating .cursor rule...\n'); - - // Generate filename based on request - const filename = generateFilename(prompt.translatedRequest || prompt.originalRequest); - const filepath = path.join(RULES_DIR, filename); - - // Create rule content - const ruleContent = generateRuleContent(prompt); - - // Ensure directory exists - const dir = path.dirname(filepath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - // Write the rule file - fs.writeFileSync(filepath, ruleContent, 'utf8'); - - console.log(`✅ Rule created: ${path.relative(process.cwd(), filepath)}`); - console.log(`📁 Location: ${filepath}`); - - // Stage the file for git - const { execSync } = require('child_process'); - try { - execSync(`git add "${filepath}"`, { stdio: 'inherit' }); - console.log('📦 File staged for git'); - } catch (error) { - console.log('⚠️ Could not stage file for git'); - } - - return filepath; - } catch (error) { - console.error('❌ Error creating rule:', error.message); - throw error; - } -} - -function generateFilename(request) { - // Extract key terms from request - const terms = request.toLowerCase() - .replace(/[^\w\s]/g, '') - .split(/\s+/) - .filter(word => word.length > 2) - .slice(0, 3); - - // Create filename - const baseName = terms.join('-') || 'new-rule'; - return `${baseName}.mdc`; -} - -function generateRuleContent(prompt) { - const timestamp = new Date().toISOString(); - const originalRequest = prompt.originalRequest; - const translatedRequest = prompt.translatedRequest || prompt.originalRequest; - - return `--- -description: ${translatedRequest} -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "${originalRequest}" -translatedAt: "${timestamp}" ---- - -# ${translatedRequest} - -## Overview - -This rule was created based on the request: "${originalRequest}" - -## Description - -${translatedRequest} - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "${originalRequest}"* -*Translated to English for AI/LLM compatibility* -`; -} - -function setupAutoTranslation() { - console.log('🔧 Setting up auto translation for .cursor requests...\n'); - - // Create configuration file - const configPath = path.join(CURSOR_DIR, 'auto-translate-config.json'); - const config = { - enabled: true, - autoTranslate: true, - createEnglishPrompts: true, - backupOriginalRequests: true, - confidenceThreshold: 70, - patterns: { - ruleCreation: ['создай', 'добавь', 'напиши', 'сделай'], - fileCreation: ['файл', 'документ', 'правило'], - cursorRequests: ['.cursor', 'cursor', 'rules'] - } - }; - - fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8'); - console.log('✅ Configuration created: .cursor/auto-translate-config.json'); - - // Create helper script - const helperPath = path.join(CURSOR_DIR, 'rules', 'create-rule.cjs'); - const helperContent = `#!/usr/bin/env node - -/** - * Quick Rule Creation Helper - * Usage: node .cursor/rules/create-rule.cjs "your request in Russian" - */ - -const { generateEnglishPrompt } = require('./request-translator.cjs'); -const { createRuleFromPrompt } = require('./auto-translate-requests.cjs'); - -async function main() { - const request = process.argv[2]; - - if (!request) { - console.log('❌ Please provide a request'); - console.log('Usage: node .cursor/rules/create-rule.cjs "создай правило для архитектуры"'); - process.exit(1); - } - - try { - const prompt = generateEnglishPrompt(request); - await createRuleFromPrompt(prompt); - console.log('\\n🎉 Rule created successfully!'); - } catch (error) { - console.error('❌ Error:', error.message); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} -`; - - fs.writeFileSync(helperPath, helperContent, 'utf8'); - console.log('✅ Helper script created: .cursor/rules/create-rule.cjs'); - - // Update package.json scripts - const packagePath = path.join(process.cwd(), 'package.json'); - if (fs.existsSync(packagePath)) { - try { - const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); - - if (!packageJson.scripts) { - packageJson.scripts = {}; - } - - packageJson.scripts['create-rule'] = 'node .cursor/rules/create-rule.cjs'; - packageJson.scripts['translate-request'] = 'node .cursor/rules/request-translator.cjs translate'; - packageJson.scripts['interactive-rules'] = 'node .cursor/rules/auto-translate-requests.cjs interactive'; - - fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2), 'utf8'); - console.log('✅ Package.json scripts updated'); - } catch (error) { - console.log('⚠️ Could not update package.json:', error.message); - } - } - - console.log('\n🎉 Auto translation setup completed!'); - console.log('\n📝 Available commands:'); - console.log(' npm run create-rule "your request"'); - console.log(' npm run translate-request "your request"'); - console.log(' npm run interactive-rules'); - console.log(' node .cursor/rules/auto-translate-requests.cjs interactive'); -} - -function main() { - const command = process.argv[2] || 'help'; - - switch (command) { - case 'interactive': - createInteractiveMode(); - break; - - case 'translate': - const text = process.argv[3]; - if (!text) { - console.log('❌ Please provide text to translate'); - console.log('Usage: node .cursor/rules/auto-translate-requests.cjs translate "текст"'); - return; - } - const prompt = generateEnglishPrompt(text); - console.log('🤖 Generated English Prompt:'); - console.log('='.repeat(60)); - console.log(prompt.englishPrompt); - console.log('='.repeat(60)); - break; - - case 'setup': - setupAutoTranslation(); - break; - - case 'help': - console.log(` -Auto Translate Requests Script - -Usage: node .cursor/rules/auto-translate-requests.cjs [command] [text] - -Commands: - interactive - Start interactive mode for creating rules - translate - Translate request to English prompt - setup - Setup auto translation system - help - Show this help - -Examples: - node .cursor/rules/auto-translate-requests.cjs interactive - node .cursor/rules/auto-translate-requests.cjs translate "создай правило" - node .cursor/rules/auto-translate-requests.cjs setup - `); - break; - - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { - createInteractiveMode, - createRuleFromPrompt, - generateFilename, - generateRuleContent, - setupAutoTranslation -}; \ No newline at end of file diff --git a/.cursor/rules/check-rules-structure.cjs b/.cursor/rules/check-rules-structure.cjs deleted file mode 100644 index d8d03155..00000000 --- a/.cursor/rules/check-rules-structure.cjs +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env node -const fs = require('fs'); -const path = require('path'); - -const RULES_DIR = path.join(__dirname); -const files = fs.readdirSync(RULES_DIR).filter(f => f.endsWith('.mdc')); - -let ok = true; -const seen = new Set(); - -for (const f of files) { - const content = fs.readFileSync(path.join(RULES_DIR, f), 'utf8'); - if (!content.match(/^# /m)) { console.error(`${f}: missing heading`); ok = false; } - if (!content.match(/^description:/m)) { console.error(`${f}: missing description`); ok = false; } - if (!content.match(/^globs:/m)) { console.error(`${f}: missing globs`); ok = false; } - if (!content.match(/^alwaysApply:/m)) { console.error(`${f}: missing alwaysApply`); ok = false; } - if (!content.match(/^---/m)) { console.error(`${f}: missing ---`); ok = false; } - if (seen.has(f)) { console.error(`${f}: duplicate file name`); ok = false; } - seen.add(f); - // Check related links - const related = content.match(/related:[\s\S]*?description:/m); - if (related) { - const rels = [...related[0].matchAll(/-\s+([^\s]+)/g)].map(m => m[1]); - for (const r of rels) { - if (!fs.existsSync(path.join(RULES_DIR, r))) { - console.error(`${f}: related link to missing file ${r}`); - ok = false; - } - } - } -} -if (!ok) process.exit(1); -console.log('All rules structure checks passed.'); \ No newline at end of file diff --git a/.cursor/rules/command-sync.cjs b/.cursor/rules/command-sync.cjs deleted file mode 100644 index 74d40ef8..00000000 --- a/.cursor/rules/command-sync.cjs +++ /dev/null @@ -1,670 +0,0 @@ -#!/usr/bin/env node - -/** - * Command Synchronization Script - * Syncs commands between USER_COMMANDS.md, ai-memory.mdc, and Cursor AI memory-bank - * Usage: node .cursor/rules/command-sync.cjs [sync|export|import|update-cursor] - */ - -const fs = require('fs'); -const path = require('path'); - -// File paths -const USER_COMMANDS_PATH = path.join(process.cwd(), 'USER_COMMANDS.md'); -const AI_MEMORY_PATH = path.join(process.cwd(), '.cursor', 'rules', 'cursor-export', 'ai-memory.mdc'); -const CURSOR_MEMORY_PATH = path.join(process.cwd(), '.cursor', 'rules', 'ai-memory.mdc'); -const ACTIVE_CONTEXT_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'activeContext.md'); -const PROGRESS_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'progress.md'); -const BACKUP_DIR = path.join(process.cwd(), 'memory-bank', 'core', 'backup'); - -// Command structure -const commandCategories = { - 'Context and Memory': { - 'save context': { - russian: ['Сохрани контекст', 'Сохранить контекст сессии'], - description: 'Save achievements, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and commits)', - userDescription: 'AI-ассистент автоматически переведет и сохранит все достижения, решения и планы в memory-bank на английском языке для совместимости с AI/LLM' - }, - 'update progress': { - russian: ['Обнови прогресс', 'Обновить прогресс проекта'], - description: 'Update activeContext.md and progress.md with current status', - userDescription: 'AI-ассистент обновит файлы activeContext.md и progress.md с текущим статусом' - }, - 'restore context': { - russian: ['Восстанови контекст', 'Восстановить полный контекст'], - description: 'Study all memory-bank files and restore full project understanding', - userDescription: 'AI-ассистент изучит все файлы memory-bank и восстановит полное понимание проекта' - }, - 'quick restore': { - russian: ['Быстрое восстановление'], - description: 'Get brief summary of key principles and current status', - userDescription: 'AI-ассистент получит краткую сводку ключевых принципов и текущего статуса' - } - }, - 'Analysis and Study': { - 'analyze architecture': { - russian: ['Анализируй архитектуру', 'Анализ архитектуры'], - description: 'Study systemPatterns.md and techContext.md for architecture understanding', - userDescription: 'AI-ассистент изучит systemPatterns.md и techContext.md для понимания архитектуры' - }, - 'study plugins': { - russian: ['Изучи плагины'], - description: 'Analyze plugins and their structure', - userDescription: 'AI-ассистент проанализирует существующие плагины и их структуру' - }, - 'check build': { - russian: ['Проверь сборку', 'Проверить сборку'], - description: 'Check that project builds and works correctly', - userDescription: 'AI-ассистент проверит, что проект собирается и работает корректно' - }, - 'update documentation': { - russian: ['Обнови документацию', 'Обновить документацию'], - description: 'Check and update README.md and PLUGIN_DEVELOPMENT.md', - userDescription: 'AI-ассистент проверит и обновит README.md и PLUGIN_DEVELOPMENT.md' - } - }, - 'Development': { - 'create plugin [name]': { - russian: ['Создай плагин [название]'], - description: 'Create new plugin with specified name', - userDescription: 'AI-ассистент создаст новый плагин с указанным названием' - }, - 'check code': { - russian: ['Проверь код', 'Проверить линтинг'], - description: 'Run linting and type checking', - userDescription: 'AI-ассистент выполнит линтинг и проверку типов' - }, - 'run tests': { - russian: ['Запусти тесты', 'Запустить тесты'], - description: 'Run all project tests', - userDescription: 'AI-ассистент запустит все тесты проекта' - }, - 'check dependencies': { - russian: ['Проверь зависимости', 'Проверить зависимости'], - description: 'Check dependencies relevance and compatibility', - userDescription: 'AI-ассистент проверит актуальность и совместимость зависимостей' - } - }, - 'Project Management': { - 'bump version patch/minor/major': { - russian: ['Увеличь версию [patch|minor|major]', 'Версионирование'], - description: 'Increase project version according to parameter', - userDescription: 'AI-ассистент увеличит версию проекта (например: "Увеличь версию minor")' - }, - 'clean project': { - russian: ['Очисти проект', 'Очистка проекта'], - description: 'Clean node_modules, dist and cache', - userDescription: 'AI-ассистент выполнит очистку node_modules, dist и кэша' - }, - 'analyze performance': { - russian: ['Анализируй производительность', 'Анализ производительности'], - description: 'Analyze project performance and suggest optimizations', - userDescription: 'AI-ассистент проанализирует производительность проекта и предложит оптимизации' - }, - 'check security': { - russian: ['Проверь безопасность', 'Анализ безопасности'], - description: 'Analyze code and configuration security', - userDescription: 'AI-ассистент проанализирует безопасность кода и конфигурации' - } - }, - 'Releases and Deployment': { - 'create release': { - russian: ['Создай релиз', 'Создать релиз'], - description: 'Prepare project for release (bump version, create ZIP)', - userDescription: 'AI-ассистент подготовит проект к релизу (увеличит версию, создаст ZIP)' - }, - 'build production': { - russian: ['Собери для продакшена', 'Сборка для продакшена'], - description: 'Perform full production build', - userDescription: 'AI-ассистент выполнит полную сборку для продакшена' - } - } -}; - -function generateUserCommandsMD() { - let content = `# Команды для пользователя - -Просто скопируйте и вставьте любую из этих команд в чат с AI-ассистентом. - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - const categoryEmoji = getCategoryEmoji(category); - content += `## ${categoryEmoji} ${category}\n\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - content += `### ${details.userDescription}\n`; - content += `${russianCommands}\n`; - content += `*${details.userDescription}*\n\n`; - } - } - - content += `--- - -## 💡 Как использовать - -1. **Скопируйте** нужную команду -2. **Вставьте** в чат с AI-ассистентом -3. **Дождитесь** выполнения команды - -## 🔄 Комбинированные команды - -Вы можете комбинировать команды: -\`\`\` -Сохрани контекст и обнови прогресс -\`\`\` - -\`\`\` -Восстанови контекст и анализируй архитектуру -\`\`\` - -## 📝 Примечания - -- AI-ассистент автоматически изучит файлы memory-bank при необходимости -- Все команды выполняются с учетом текущего контекста проекта -- Результаты сохраняются в соответствующих файлах memory-bank -- Команды работают с любой LLM моделью, которая следует инструкциям - -## Восстановление контекста проекта в новом чате - -1. Просто напиши в начале чата: - \`\`\` - Восстанови контекст - \`\`\` -2. Ассистент автоматически: - - Прочитает все ключевые файлы memory-bank (README.md, activeContext.md, systemPatterns.md, progress.md, productContext.md, techContext.md, session-log.md) - - Восстановит все правила, best practices, кладбище ошибок, карту взаимосвязей, архитектурные решения и рекомендации - - Будет использовать только актуальные подходы и паттерны из memory-bank - - Если что-то неясно — уточнит у тебя детали по новой фиче или архитектуре -3. Когда стоит добавить детали вручную: - - Если есть особые пожелания к стилю работы, архитектуре или процессу, которые не зафиксированы в memory-bank - - Если нужно сфокусироваться только на определённой части контекста - - Если хочешь ускорить процесс и не ждать уточняющих вопросов - -**Совет:** Если что-то важно для будущей работы — фиксируй это в memory-bank, чтобы не потерять при смене чата или ассистента. -`; - - return content; -} - -function generateAIMemoryMD() { - let content = `--- -description: Universal user commands for AI assistant - complete command reference with triggers and actions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: general ---- - -# AI Memory Bank - Universal User Commands - -## Commands for AI Assistant Recognition - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - content += `### ${category}:\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - content += `- \`${command}\` / ${russianCommands} - ${details.description}\n`; - } - content += '\n'; - } - - content += `## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consider current project context -- Results should be saved in appropriate memory-bank files -- If command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices -`; - - return content; -} - -function generateCursorMemoryBank() { - let content = `# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - content += `### ${category}:\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - content += `- \`${command}\` / ${russianCommands} - ${details.description}\n`; - } - content += '\n'; - } - - content += `## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consider current project context -- Results should be saved in appropriate memory-bank files -- If command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices -`; - - return content; -} - -function getCategoryEmoji(category) { - const emojiMap = { - 'Context and Memory': '📝', - 'Analysis and Study': '🏗️', - 'Development': '🔧', - 'Project Management': '📊', - 'Releases and Deployment': '🚀' - }; - return emojiMap[category] || '📋'; -} - -// Context translation function -function translateContextToEnglish() { - console.log('🔄 Translating context to English for AI/LLM compatibility...\n'); - - // Create backup directory - if (!fs.existsSync(BACKUP_DIR)) { - fs.mkdirSync(BACKUP_DIR, { recursive: true }); - } - - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - - // Translation mappings for context - const contextTranslations = { - // Headers and titles - 'Активный контекст разработки': 'Active Development Context', - 'Текущий статус проекта': 'Current Project Status', - 'Последнее обновление': 'Last Updated', - 'Сессия интернационализации и синхронизации команд': 'Internationalization and Command Synchronization Session', - - // Task status - 'Завершенные задачи': 'Completed Tasks', - 'Текущий фокус': 'Current Focus', - 'Приоритет': 'Priority', - 'Следующие шаги': 'Next Steps', - 'Готовность к международному сообществу и глобальному использованию': 'Readiness for International Community and Global Usage', - - // Principles - 'Ключевые принципы работы': 'Key Working Principles', - 'Инициативность ассистента': 'Assistant Initiative', - 'Всегда предлагать улучшения и оптимизации': 'Always suggest improvements and optimizations', - 'Конструктивно критиковать существующие решения': 'Constructively criticize existing solutions', - 'Предлагать альтернативные подходы': 'Suggest alternative approaches', - 'Проактивно выявлять потенциальные проблемы': 'Proactively identify potential issues', - - 'Качество кода': 'Code Quality', - 'Следовать принципам из': 'Follow principles from', - 'Применять "Do No Harm" принцип': 'Apply "Do No Harm" principle', - 'Использовать AI-First документацию': 'Use AI-First documentation', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - - 'Internationalization': 'Internationalization', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Универсальный формат команд': 'Universal command format', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Technical context - 'Технический контекст': 'Technical Context', - 'Текущая архитектура': 'Current Architecture', - 'React + TypeScript для UI компонентов': 'React + TypeScript for UI components', - 'Модульная система пакетов': 'Modular package system', - 'Vite для сборки': 'Vite for building', - 'Tailwind CSS для стилизации': 'Tailwind CSS for styling', - 'Chrome Extension API для интеграции': 'Chrome Extension API for integration', - 'Система синхронизации команд': 'Command synchronization system', - - 'Стандарты разработки': 'Development Standards', - 'TypeScript для всех новых файлов': 'TypeScript for all new files', - 'ESLint для проверки кода': 'ESLint for code checking', - 'Компонентный подход с proper accessibility': 'Component approach with proper accessibility', - 'Структурированное логирование': 'Structured logging', - 'Комплексная документация с примерами': 'Comprehensive documentation with examples', - 'Английский язык для всех правил и документации': 'English language for all rules and documentation', - - 'Безопасность': 'Security', - 'Zero Trust архитектура для плагинов': 'Zero Trust architecture for plugins', - 'Валидация всех входных данных': 'Validation of all input data', - 'Шифрование чувствительной информации': 'Encryption of sensitive information', - 'Аудит всех действий плагинов': 'Audit of all plugin actions', - - // Command system - 'Система команд': 'Command System', - 'Автоматическая синхронизация': 'Automatic Synchronization', - 'Единый источник истины': 'Single source of truth', - 'Автоматическая генерация': 'Automatic generation', - 'Множественные форматы': 'Multiple formats', - 'Категории команд': 'Command Categories', - 'Сохранение и восстановление контекста': 'Context saving and restoration', - 'Анализ архитектуры и изучение плагинов': 'Architecture analysis and plugin study', - 'Создание плагинов и проверка кода': 'Plugin creation and code checking', - 'Управление версиями и анализ': 'Version management and analysis', - 'Создание релизов и сборка': 'Release creation and building', - - 'Интеграция с Cursor': 'Cursor Integration', - 'Экспорт команд': 'Command export', - 'Файл для Cursor': 'File for Cursor', - 'Инструкции интеграции': 'Integration instructions', - 'Пошаговое руководство': 'Step-by-step guide', - - // User experience - 'Пользовательский опыт': 'User Experience', - 'Приоритеты UX': 'UX Priorities', - 'Интуитивность интерфейса': 'Interface intuitiveness', - 'Быстродействие и отзывчивость': 'Speed and responsiveness', - 'Доступность': 'Accessibility', - 'Консистентность дизайна': 'Design consistency', - 'Поддержка различных тем': 'Support for various themes', - 'Универсальность команд': 'Command universality', - - 'Метрики качества': 'Quality Metrics', - 'Время загрузки компонентов': 'Component loading time', - 'Плавность анимаций': 'Animation smoothness', - 'Доступность для скринридеров': 'Accessibility for screen readers', - 'Совместимость с различными браузерами': 'Compatibility with various browsers', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Development plans - 'Планы развития': 'Development Plans', - 'Краткосрочные цели': 'Short-term Goals', - 'Среднесрочные цели': 'Medium-term Goals', - 'Долгосрочные цели': 'Long-term Goals', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - 'Расширение системы команд новыми категориями': 'Expanding command system with new categories', - 'API интеграция с Cursor для автоматических обновлений': 'API integration with Cursor for automatic updates', - 'Создание шаблонов команд для разных типов проектов': 'Creating command templates for different project types', - 'Развитие экосистемы плагинов': 'Development of plugin ecosystem', - 'Создание полноценной международной платформы': 'Creating a full-fledged international platform', - 'Развитие глобального сообщества разработчиков': 'Development of global developer community', - 'Интеграция с популярными сервисами': 'Integration with popular services', - 'Многоязычная поддержка интерфейса': 'Multilingual interface support', - - // Important files - 'Важные файлы и ресурсы': 'Important Files and Resources', - 'Система синхронизации команд': 'Command Synchronization System', - 'Основной скрипт синхронизации': 'Main synchronization script', - 'Документация системы': 'System documentation', - 'Пользовательский справочник команд': 'User command reference', - 'Экспорт для Cursor': 'Export for Cursor', - - 'Ключевые компоненты': 'Key Components', - 'Карточки плагинов': 'Plugin cards', - 'Панель управления': 'Control panel', - 'Детали плагина': 'Plugin details', - 'Индикатор статуса': 'Status indicator', - - 'Документация': 'Documentation', - 'Документация интернационализации': 'Internationalization documentation', - 'Документация системы команд': 'Command system documentation', - 'Принципы разработки': 'Development principles', - 'План улучшений UI': 'UI improvement plan', - 'Долгосрочные планы': 'Long-term plans', - - 'Конфигурация': 'Configuration', - 'UI компоненты и стили': 'UI components and styles', - 'Конфигурация сборки': 'Build configuration', - 'Общие утилиты': 'Common utilities', - - // Commands and processes - 'Команды и процессы': 'Commands and Processes', - 'Система синхронизации команд': 'Command Synchronization System', - 'Синхронизация всех файлов': 'Synchronize all files', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Справка по командам': 'Command help', - - 'Сборка проекта': 'Project Build', - 'Сборка всех страниц': 'Build all pages', - 'Сборка конкретной страницы': 'Build specific page', - 'Разработка': 'Development', - - 'Тестирование': 'Testing', - 'Использовать DevTools панель': 'Use DevTools panel', - 'Тестировать в боковой панели расширения': 'Test in extension side panel', - 'Проверять в различных темах': 'Check in various themes', - 'Тестировать все обновленные компоненты': 'Test all updated components', - 'Проверять синхронизацию команд': 'Check command synchronization', - - 'Git workflow': 'Git Workflow', - 'Создавать feature ветки для новых функций': 'Create feature branches for new functions', - 'Создавать fix ветки для исправлений': 'Create fix branches for fixes', - 'Использовать осмысленные названия веток': 'Use meaningful branch names', - 'Вливать через pull requests': 'Merge through pull requests', - - // Contacts and support - 'Контакты и поддержка': 'Contacts and Support', - 'Для пользователей': 'For Users', - 'Документация в memory-bank': 'Documentation in memory-bank', - 'Тестирование через DevTools панель': 'Testing through DevTools panel', - 'Обратная связь через GitHub Issues': 'Feedback through GitHub Issues', - 'Справочник команд в USER_COMMANDS.md': 'Command reference in USER_COMMANDS.md', - - 'Для разработчиков': 'For Developers', - 'Следовать принципам из': 'Follow principles from', - 'Использовать модульную архитектуру': 'Use modular architecture', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - 'Документировать все изменения': 'Document all changes', - 'Использовать систему синхронизации команд': 'Use command synchronization system', - - // Status - 'Статус готовности к международному сообществу': 'Readiness Status for International Community', - 'Готовые компоненты': 'Ready Components', - 'Полная интернационализация .cursor и memory-bank': 'Complete internationalization of .cursor and memory-bank', - 'Система синхронизации команд': 'Command synchronization system', - 'Универсальный формат команд': 'Universal command format', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Полностью модернизирован': 'Fully modernized', - 'Полностью обновлен': 'Fully updated', - 'Современный дизайн': 'Modern design', - 'Улучшенный индикатор': 'Improved indicator', - - 'Готово к публикации': 'Ready for Publication', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Автоматическая синхронизация команд': 'Automatic command synchronization', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - 'Современный дизайн-система внедрена': 'Modern design system implemented', - 'Поддержка светлой и темной темы': 'Support for light and dark themes', - - 'План публикации': 'Publication Plan', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - - // Architecture - 'Архитектура: Sidepanel и контекстная логика': 'Architecture: Sidepanel and Context Logic', - 'Sidepanel расширения открывается и закрывается пользователем': 'Extension sidepanel opens and closes by user', - 'Содержимое боковой панели зависит от текущей web-страницы': 'Side panel content depends on current web page', - 'Список доступных плагинов в сайдпанели зависит от разрешений': 'List of available plugins in sidepanel depends on permissions', - 'Сайдпанель не работает как отдельная extension page': 'Sidepanel does not work as separate extension page', - - // E2E Testing - 'E2E-тестирование чата плагина Ozon в Chromium': 'E2E Testing of Ozon Plugin Chat in Chromium', - 'Цель': 'Goal', - 'Полностью автоматизировать сценарий тестирования': 'Fully automate testing scenario', - 'Этапы и прогресс': 'Stages and Progress', - 'Проанализирован существующий e2e-тест': 'Analyzed existing e2e test', - 'Подтверждено, что тест уже реализует': 'Confirmed that test already implements', - 'Открытие страницы Ozon': 'Opening Ozon page', - 'Открытие сайдпанели': 'Opening sidepanel', - 'Проверку наличия плагина Ozon': 'Checking for Ozon plugin', - 'Клик по плагину и открытие чата': 'Click on plugin and open chat', - 'Отправку сообщения в чат': 'Sending message to chat', - 'Получение ответа от плагина': 'Receiving response from plugin', - 'Протестирован существующий тест в Chromium': 'Tested existing test in Chromium', - 'Выявлены и исправлены проблемы с селекторами': 'Identified and fixed selector issues', - 'Добавлена поддержка Chromium-specific селекторов': 'Added support for Chromium-specific selectors', - 'Оптимизированы таймауты для стабильности': 'Optimized timeouts for stability', - 'Добавлена обработка ошибок и retry логика': 'Added error handling and retry logic', - 'Создана документация по тестированию': 'Created testing documentation', - - 'Текущий статус': 'Current Status', - 'Готово к использованию': 'Ready for Use', - 'Тест полностью функционален в Chromium': 'Test is fully functional in Chromium', - 'Готов для CI/CD интеграции': 'Ready for CI/CD integration', - - 'Следующие шаги': 'Next Steps', - 'Интеграция в CI/CD pipeline': 'Integration into CI/CD pipeline', - 'Добавление тестов для других плагинов': 'Adding tests for other plugins', - 'Расширение покрытия тестирования': 'Expanding test coverage', - 'Оптимизация производительности тестов': 'Optimizing test performance' - }; - - function translateText(text) { - let translatedText = text; - - // Apply translations - for (const [russian, english] of Object.entries(contextTranslations)) { - translatedText = translatedText.replace(new RegExp(russian, 'g'), english); - } - - return translatedText; - } - - // Translate activeContext.md - if (fs.existsSync(ACTIVE_CONTEXT_PATH)) { - // Create backup - const backupPath = path.join(BACKUP_DIR, `activeContext-${timestamp}.md`); - fs.copyFileSync(ACTIVE_CONTEXT_PATH, backupPath); - - const content = fs.readFileSync(ACTIVE_CONTEXT_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(ACTIVE_CONTEXT_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/activeContext.md'); - console.log(`📁 Backup: ${backupPath}`); - } - - // Translate progress.md - if (fs.existsSync(PROGRESS_PATH)) { - // Create backup - const backupPath = path.join(BACKUP_DIR, `progress-${timestamp}.md`); - fs.copyFileSync(PROGRESS_PATH, backupPath); - - const content = fs.readFileSync(PROGRESS_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(PROGRESS_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/progress.md'); - console.log(`📁 Backup: ${backupPath}`); - } - - console.log('\n🎉 Context translation completed for AI/LLM compatibility!'); -} - -function syncCommands() { - console.log('🔄 Syncing commands between all sources...\n'); - - // Generate USER_COMMANDS.md - const userCommandsContent = generateUserCommandsMD(); - fs.writeFileSync(USER_COMMANDS_PATH, userCommandsContent, 'utf8'); - console.log('✅ Updated USER_COMMANDS.md'); - - // Generate ai-memory.mdc - const aiMemoryContent = generateAIMemoryMD(); - fs.writeFileSync(AI_MEMORY_PATH, aiMemoryContent, 'utf8'); - console.log('✅ Updated .cursor/rules/cursor-export/ai-memory.mdc'); - - // Generate Cursor memory-bank - const cursorMemoryContent = generateCursorMemoryBank(); - fs.writeFileSync(CURSOR_MEMORY_PATH, cursorMemoryContent, 'utf8'); - console.log('✅ Updated .cursor/rules/ai-memory.mdc'); - - console.log('\n🎉 All command files synchronized successfully!'); -} - -function exportForCursor() { - console.log('📤 Exporting commands for Cursor AI memory-bank...\n'); - - let cursorFormat = `# AI Memory Bank - User Commands - -## Commands for AI Assistant Recognition - -`; - - for (const [category, commands] of Object.entries(commandCategories)) { - const emoji = getCategoryEmoji(category); - cursorFormat += `### ${emoji} ${category}:\n`; - - for (const [command, details] of Object.entries(commands)) { - const russianCommands = details.russian.map(cmd => `\`${cmd}\``).join(' / '); - cursorFormat += `- \`${command}\` / ${russianCommands} - ${details.description}\n`; - } - cursorFormat += '\n'; - } - - cursorFormat += `## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consider current project context -- Results should be saved in appropriate memory-bank files -- If command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices - -## Usage Instructions: -1. Copy this content -2. Go to Cursor Settings → AI → Rules & Memories -3. Paste into User Rules or Project Rules -4. Save and restart Cursor -`; - - const exportPath = path.join(process.cwd(), 'CURSOR_AI_MEMORY_BANK.md'); - fs.writeFileSync(exportPath, cursorFormat, 'utf8'); - console.log(`✅ Exported to ${exportPath}`); - console.log('\n📋 Instructions:'); - console.log('1. Copy the content from CURSOR_AI_MEMORY_BANK.md'); - console.log('2. Go to Cursor Settings → AI → Rules & Memories'); - console.log('3. Paste into User Rules or Project Rules'); - console.log('4. Save and restart Cursor'); -} - -function main() { - const command = process.argv[2] || 'sync'; - - switch (command) { - case 'sync': - syncCommands(); - break; - case 'export': - exportForCursor(); - break; - case 'translate-context': - translateContextToEnglish(); - break; - case 'help': - console.log(` -Command Synchronization Script - -Usage: node .cursor/rules/command-sync.cjs [command] - -Commands: - sync - Sync all command files (default) - export - Export commands for Cursor AI memory-bank - translate-context - Translate context to English for AI/LLM compatibility - help - Show this help - -Examples: - node .cursor/rules/command-sync.cjs sync - node .cursor/rules/command-sync.cjs export - node .cursor/rules/command-sync.cjs translate-context - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { commandCategories, generateUserCommandsMD, generateAIMemoryMD, generateCursorMemoryBank, translateContextToEnglish }; \ No newline at end of file diff --git a/.cursor/rules/context-translator.cjs b/.cursor/rules/context-translator.cjs deleted file mode 100644 index 83673b13..00000000 --- a/.cursor/rules/context-translator.cjs +++ /dev/null @@ -1,409 +0,0 @@ -#!/usr/bin/env node - -/** - * Context Translator Script - * Automatically translates context to English for AI/LLM compatibility - * Usage: node .cursor/rules/context-translator.cjs [translate|backup|restore] - */ - -const fs = require('fs'); -const path = require('path'); - -// File paths -const ACTIVE_CONTEXT_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'activeContext.md'); -const PROGRESS_PATH = path.join(process.cwd(), 'memory-bank', 'core', 'progress.md'); -const BACKUP_DIR = path.join(process.cwd(), 'memory-bank', 'core', 'backup'); - -// Translation mappings for context -const contextTranslations = { - // Headers and titles - 'Активный контекст разработки': 'Active Development Context', - 'Текущий статус проекта': 'Current Project Status', - 'Последнее обновление': 'Last Updated', - 'Сессия интернационализации и синхронизации команд': 'Internationalization and Command Synchronization Session', - - // Task status - 'Завершенные задачи': 'Completed Tasks', - 'Текущий фокус': 'Current Focus', - 'Приоритет': 'Priority', - 'Следующие шаги': 'Next Steps', - 'Готовность к международному сообществу и глобальному использованию': 'Readiness for International Community and Global Usage', - - // Principles - 'Ключевые принципы работы': 'Key Working Principles', - 'Инициативность ассистента': 'Assistant Initiative', - 'Всегда предлагать улучшения и оптимизации': 'Always suggest improvements and optimizations', - 'Конструктивно критиковать существующие решения': 'Constructively criticize existing solutions', - 'Предлагать альтернативные подходы': 'Suggest alternative approaches', - 'Проактивно выявлять потенциальные проблемы': 'Proactively identify potential issues', - - 'Качество кода': 'Code Quality', - 'Следовать принципам из': 'Follow principles from', - 'Применять "Do No Harm" принцип': 'Apply "Do No Harm" principle', - 'Использовать AI-First документацию': 'Use AI-First documentation', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - - 'Internationalization': 'Internationalization', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Универсальный формат команд': 'Universal command format', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Technical context - 'Технический контекст': 'Technical Context', - 'Текущая архитектура': 'Current Architecture', - 'React + TypeScript для UI компонентов': 'React + TypeScript for UI components', - 'Модульная система пакетов': 'Modular package system', - 'Vite для сборки': 'Vite for building', - 'Tailwind CSS для стилизации': 'Tailwind CSS for styling', - 'Chrome Extension API для интеграции': 'Chrome Extension API for integration', - 'Система синхронизации команд': 'Command synchronization system', - - 'Стандарты разработки': 'Development Standards', - 'TypeScript для всех новых файлов': 'TypeScript for all new files', - 'ESLint для проверки кода': 'ESLint for code checking', - 'Компонентный подход с proper accessibility': 'Component approach with proper accessibility', - 'Структурированное логирование': 'Structured logging', - 'Комплексная документация с примерами': 'Comprehensive documentation with examples', - 'Английский язык для всех правил и документации': 'English language for all rules and documentation', - - 'Безопасность': 'Security', - 'Zero Trust архитектура для плагинов': 'Zero Trust architecture for plugins', - 'Валидация всех входных данных': 'Validation of all input data', - 'Шифрование чувствительной информации': 'Encryption of sensitive information', - 'Аудит всех действий плагинов': 'Audit of all plugin actions', - - // Command system - 'Система команд': 'Command System', - 'Автоматическая синхронизация': 'Automatic Synchronization', - 'Единый источник истины': 'Single source of truth', - 'Автоматическая генерация': 'Automatic generation', - 'Множественные форматы': 'Multiple formats', - 'Категории команд': 'Command Categories', - 'Сохранение и восстановление контекста': 'Context saving and restoration', - 'Анализ архитектуры и изучение плагинов': 'Architecture analysis and plugin study', - 'Создание плагинов и проверка кода': 'Plugin creation and code checking', - 'Управление версиями и анализ': 'Version management and analysis', - 'Создание релизов и сборка': 'Release creation and building', - - 'Интеграция с Cursor': 'Cursor Integration', - 'Экспорт команд': 'Command export', - 'Файл для Cursor': 'File for Cursor', - 'Инструкции интеграции': 'Integration instructions', - 'Пошаговое руководство': 'Step-by-step guide', - - // User experience - 'Пользовательский опыт': 'User Experience', - 'Приоритеты UX': 'UX Priorities', - 'Интуитивность интерфейса': 'Interface intuitiveness', - 'Быстродействие и отзывчивость': 'Speed and responsiveness', - 'Доступность': 'Accessibility', - 'Консистентность дизайна': 'Design consistency', - 'Поддержка различных тем': 'Support for various themes', - 'Универсальность команд': 'Command universality', - - 'Метрики качества': 'Quality Metrics', - 'Время загрузки компонентов': 'Component loading time', - 'Плавность анимаций': 'Animation smoothness', - 'Доступность для скринридеров': 'Accessibility for screen readers', - 'Совместимость с различными браузерами': 'Compatibility with various browsers', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - - // Development plans - 'Планы развития': 'Development Plans', - 'Краткосрочные цели': 'Short-term Goals', - 'Среднесрочные цели': 'Medium-term Goals', - 'Долгосрочные цели': 'Long-term Goals', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - 'Расширение системы команд новыми категориями': 'Expanding command system with new categories', - 'API интеграция с Cursor для автоматических обновлений': 'API integration with Cursor for automatic updates', - 'Создание шаблонов команд для разных типов проектов': 'Creating command templates for different project types', - 'Развитие экосистемы плагинов': 'Development of plugin ecosystem', - 'Создание полноценной международной платформы': 'Creating a full-fledged international platform', - 'Развитие глобального сообщества разработчиков': 'Development of global developer community', - 'Интеграция с популярными сервисами': 'Integration with popular services', - 'Многоязычная поддержка интерфейса': 'Multilingual interface support', - - // Important files - 'Важные файлы и ресурсы': 'Important Files and Resources', - 'Система синхронизации команд': 'Command Synchronization System', - 'Основной скрипт синхронизации': 'Main synchronization script', - 'Документация системы': 'System documentation', - 'Пользовательский справочник команд': 'User command reference', - 'Экспорт для Cursor': 'Export for Cursor', - - 'Ключевые компоненты': 'Key Components', - 'Карточки плагинов': 'Plugin cards', - 'Панель управления': 'Control panel', - 'Детали плагина': 'Plugin details', - 'Индикатор статуса': 'Status indicator', - - 'Документация': 'Documentation', - 'Документация интернационализации': 'Internationalization documentation', - 'Документация системы команд': 'Command system documentation', - 'Принципы разработки': 'Development principles', - 'План улучшений UI': 'UI improvement plan', - 'Долгосрочные планы': 'Long-term plans', - - 'Конфигурация': 'Configuration', - 'UI компоненты и стили': 'UI components and styles', - 'Конфигурация сборки': 'Build configuration', - 'Общие утилиты': 'Common utilities', - - // Commands and processes - 'Команды и процессы': 'Commands and Processes', - 'Система синхронизации команд': 'Command Synchronization System', - 'Синхронизация всех файлов': 'Synchronize all files', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Справка по командам': 'Command help', - - 'Сборка проекта': 'Project Build', - 'Сборка всех страниц': 'Build all pages', - 'Сборка конкретной страницы': 'Build specific page', - 'Разработка': 'Development', - - 'Тестирование': 'Testing', - 'Использовать DevTools панель': 'Use DevTools panel', - 'Тестировать в боковой панели расширения': 'Test in extension side panel', - 'Проверять в различных темах': 'Check in various themes', - 'Тестировать все обновленные компоненты': 'Test all updated components', - 'Проверять синхронизацию команд': 'Check command synchronization', - - 'Git workflow': 'Git Workflow', - 'Создавать feature ветки для новых функций': 'Create feature branches for new functions', - 'Создавать fix ветки для исправлений': 'Create fix branches for fixes', - 'Использовать осмысленные названия веток': 'Use meaningful branch names', - 'Вливать через pull requests': 'Merge through pull requests', - - // Contacts and support - 'Контакты и поддержка': 'Contacts and Support', - 'Для пользователей': 'For Users', - 'Документация в memory-bank': 'Documentation in memory-bank', - 'Тестирование через DevTools панель': 'Testing through DevTools panel', - 'Обратная связь через GitHub Issues': 'Feedback through GitHub Issues', - 'Справочник команд в USER_COMMANDS.md': 'Command reference in USER_COMMANDS.md', - - 'Для разработчиков': 'For Developers', - 'Следовать принципам из': 'Follow principles from', - 'Использовать модульную архитектуру': 'Use modular architecture', - 'Приоритизировать безопасность и производительность': 'Prioritize security and performance', - 'Документировать все изменения': 'Document all changes', - 'Использовать систему синхронизации команд': 'Use command synchronization system', - - // Status - 'Статус готовности к международному сообществу': 'Readiness Status for International Community', - 'Готовые компоненты': 'Ready Components', - 'Полная интернационализация .cursor и memory-bank': 'Complete internationalization of .cursor and memory-bank', - 'Система синхронизации команд': 'Command synchronization system', - 'Универсальный формат команд': 'Universal command format', - 'Экспорт для Cursor AI memory-bank': 'Export for Cursor AI memory-bank', - 'Полностью модернизирован': 'Fully modernized', - 'Полностью обновлен': 'Fully updated', - 'Современный дизайн': 'Modern design', - 'Улучшенный индикатор': 'Improved indicator', - - 'Готово к публикации': 'Ready for Publication', - 'Все правила и документация на английском языке': 'All rules and documentation in English', - 'Автоматическая синхронизация команд': 'Automatic command synchronization', - 'Готовность к глобальному сообществу': 'Readiness for global community', - 'Совместимость с любым AI-ассистентом': 'Compatibility with any AI assistant', - 'Современный дизайн-система внедрена': 'Modern design system implemented', - 'Поддержка светлой и темной темы': 'Support for light and dark themes', - - 'План публикации': 'Publication Plan', - 'Тестирование системы синхронизации команд': 'Testing command synchronization system', - 'Интеграция команд в Cursor AI memory-bank': 'Integration of commands into Cursor AI memory-bank', - 'Публикация .cursor для международного сообщества': 'Publishing .cursor for international community', - 'Сбор обратной связи от глобального сообщества': 'Collecting feedback from global community', - 'Дальнейшая оптимизация на основе обратной связи': 'Further optimization based on feedback', - - // Architecture - 'Архитектура: Sidepanel и контекстная логика': 'Architecture: Sidepanel and Context Logic', - 'Sidepanel расширения открывается и закрывается пользователем': 'Extension sidepanel opens and closes by user', - 'Содержимое боковой панели зависит от текущей web-страницы': 'Side panel content depends on current web page', - 'Список доступных плагинов в сайдпанели зависит от разрешений': 'List of available plugins in sidepanel depends on permissions', - 'Сайдпанель не работает как отдельная extension page': 'Sidepanel does not work as separate extension page', - - // E2E Testing - 'E2E-тестирование чата плагина Ozon в Chromium': 'E2E Testing of Ozon Plugin Chat in Chromium', - 'Цель': 'Goal', - 'Полностью автоматизировать сценарий тестирования': 'Fully automate testing scenario', - 'Этапы и прогресс': 'Stages and Progress', - 'Проанализирован существующий e2e-тест': 'Analyzed existing e2e test', - 'Подтверждено, что тест уже реализует': 'Confirmed that test already implements', - 'Открытие страницы Ozon': 'Opening Ozon page', - 'Открытие сайдпанели': 'Opening sidepanel', - 'Проверку наличия плагина Ozon': 'Checking for Ozon plugin', - 'Клик по плагину и открытие чата': 'Click on plugin and open chat', - 'Отправку сообщения в чат': 'Sending message to chat', - 'Получение ответа от плагина': 'Receiving response from plugin', - 'Протестирован существующий тест в Chromium': 'Tested existing test in Chromium', - 'Выявлены и исправлены проблемы с селекторами': 'Identified and fixed selector issues', - 'Добавлена поддержка Chromium-specific селекторов': 'Added support for Chromium-specific selectors', - 'Оптимизированы таймауты для стабильности': 'Optimized timeouts for stability', - 'Добавлена обработка ошибок и retry логика': 'Added error handling and retry logic', - 'Создана документация по тестированию': 'Created testing documentation', - - 'Текущий статус': 'Current Status', - 'Готово к использованию': 'Ready for Use', - 'Тест полностью функционален в Chromium': 'Test is fully functional in Chromium', - 'Готов для CI/CD интеграции': 'Ready for CI/CD integration', - - 'Следующие шаги': 'Next Steps', - 'Интеграция в CI/CD pipeline': 'Integration into CI/CD pipeline', - 'Добавление тестов для других плагинов': 'Adding tests for other plugins', - 'Расширение покрытия тестирования': 'Expanding test coverage', - 'Оптимизация производительности тестов': 'Optimizing test performance' -}; - -function translateText(text) { - let translatedText = text; - - // Apply translations - for (const [russian, english] of Object.entries(contextTranslations)) { - translatedText = translatedText.replace(new RegExp(russian, 'g'), english); - } - - return translatedText; -} - -function createBackup() { - if (!fs.existsSync(BACKUP_DIR)) { - fs.mkdirSync(BACKUP_DIR, { recursive: true }); - } - - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - - if (fs.existsSync(ACTIVE_CONTEXT_PATH)) { - const backupPath = path.join(BACKUP_DIR, `activeContext-${timestamp}.md`); - fs.copyFileSync(ACTIVE_CONTEXT_PATH, backupPath); - console.log(`✅ Backup created: ${backupPath}`); - } - - if (fs.existsSync(PROGRESS_PATH)) { - const backupPath = path.join(BACKUP_DIR, `progress-${timestamp}.md`); - fs.copyFileSync(PROGRESS_PATH, backupPath); - console.log(`✅ Backup created: ${backupPath}`); - } -} - -function translateContext() { - console.log('🔄 Translating context to English...\n'); - - // Create backup - createBackup(); - - // Translate activeContext.md - if (fs.existsSync(ACTIVE_CONTEXT_PATH)) { - const content = fs.readFileSync(ACTIVE_CONTEXT_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(ACTIVE_CONTEXT_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/activeContext.md'); - } - - // Translate progress.md - if (fs.existsSync(PROGRESS_PATH)) { - const content = fs.readFileSync(PROGRESS_PATH, 'utf8'); - const translatedContent = translateText(content); - fs.writeFileSync(PROGRESS_PATH, translatedContent, 'utf8'); - console.log('✅ Translated: memory-bank/core/progress.md'); - } - - console.log('\n🎉 Context translation completed!'); - console.log('📝 Note: Backups created in memory-bank/core/backup/'); -} - -function restoreFromBackup(backupName) { - const backupPath = path.join(BACKUP_DIR, backupName); - - if (!fs.existsSync(backupPath)) { - console.log(`❌ Backup not found: ${backupPath}`); - return; - } - - if (backupName.includes('activeContext')) { - fs.copyFileSync(backupPath, ACTIVE_CONTEXT_PATH); - console.log(`✅ Restored: memory-bank/core/activeContext.md from ${backupName}`); - } else if (backupName.includes('progress')) { - fs.copyFileSync(backupPath, PROGRESS_PATH); - console.log(`✅ Restored: memory-bank/core/progress.md from ${backupName}`); - } -} - -function listBackups() { - if (!fs.existsSync(BACKUP_DIR)) { - console.log('❌ No backups found'); - return; - } - - const files = fs.readdirSync(BACKUP_DIR); - if (files.length === 0) { - console.log('❌ No backups found'); - return; - } - - console.log('📁 Available backups:'); - files.forEach(file => { - const stats = fs.statSync(path.join(BACKUP_DIR, file)); - console.log(` ${file} (${stats.mtime.toLocaleString()})`); - }); -} - -function main() { - const command = process.argv[2] || 'translate'; - const backupName = process.argv[3]; - - switch (command) { - case 'translate': - translateContext(); - break; - case 'backup': - createBackup(); - break; - case 'restore': - if (!backupName) { - console.log('❌ Please specify backup name'); - console.log('Usage: node .cursor/rules/context-translator.cjs restore '); - return; - } - restoreFromBackup(backupName); - break; - case 'list': - listBackups(); - break; - case 'help': - console.log(` -Context Translator Script - -Usage: node .cursor/rules/context-translator.cjs [command] [backup-name] - -Commands: - translate - Translate context to English (default) - backup - Create backup of current context - restore - Restore from backup (requires backup name) - list - List available backups - help - Show this help - -Examples: - node .cursor/rules/context-translator.cjs translate - node .cursor/rules/context-translator.cjs backup - node .cursor/rules/context-translator.cjs restore activeContext-2024-07-19T10-30-00-000Z.md - node .cursor/rules/context-translator.cjs list - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { translateText, contextTranslations, createBackup, translateContext }; \ No newline at end of file diff --git a/.cursor/rules/create-rule-for.mdc b/.cursor/rules/create-rule-for.mdc deleted file mode 100644 index f43302e8..00000000 --- a/.cursor/rules/create-rule-for.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: create rule for architecture project -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "создай правило для архитектуры проекта" -translatedAt: "2025-07-19T02:58:58.601Z" ---- - -# create rule for architecture project - -## Overview - -This rule was created based on the request: "создай правило для архитектуры проекта" - -## Description - -create rule for architecture project - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "создай правило для архитектуры проекта"* -*Translated to English for AI/LLM compatibility* diff --git a/.cursor/rules/create-rule.cjs b/.cursor/rules/create-rule.cjs deleted file mode 100644 index 90023503..00000000 --- a/.cursor/rules/create-rule.cjs +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env node - -/** - * Quick Rule Creation Helper - * Automatically translates user requests and creates .cursor rules in English - * Usage: node .cursor/rules/create-rule.cjs "your request in Russian" - */ - -const fs = require('fs'); -const path = require('path'); -const { generateEnglishPrompt } = require('./request-translator.cjs'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const RULES_DIR = path.join(CURSOR_DIR, 'rules'); - -function generateFilename(request) { - // Extract key terms from request - const terms = request.toLowerCase() - .replace(/[^\w\s]/g, '') - .split(/\s+/) - .filter(word => word.length > 2) - .slice(0, 3); - - // Create filename - const baseName = terms.join('-') || 'new-rule'; - return `${baseName}.mdc`; -} - -function generateRuleContent(prompt) { - const timestamp = new Date().toISOString(); - const originalRequest = prompt.originalRequest; - const translatedRequest = prompt.translatedRequest || prompt.originalRequest; - - return `--- -description: ${translatedRequest} -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "${originalRequest}" -translatedAt: "${timestamp}" ---- - -# ${translatedRequest} - -## Overview - -This rule was created based on the request: "${originalRequest}" - -## Description - -${translatedRequest} - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "${originalRequest}"* -*Translated to English for AI/LLM compatibility* -`; -} - -async function createRule(request) { - try { - console.log('🤖 Processing your request...\n'); - - // Generate English prompt - const prompt = generateEnglishPrompt(request); - - // Display translation info - console.log('📊 Translation Info:'); - console.log(`Original: ${prompt.originalRequest}`); - console.log(`Translated: ${prompt.translatedRequest}`); - console.log(`Confidence: ${prompt.confidence.toFixed(1)}%`); - - if (prompt.shouldTranslate) { - console.log('\n✅ Request translated to English for AI/LLM compatibility'); - } else { - console.log('\n✅ Request already in English'); - } - - // Generate filename and content - const filename = generateFilename(prompt.translatedRequest || prompt.originalRequest); - const filepath = path.join(RULES_DIR, filename); - const ruleContent = generateRuleContent(prompt); - - // Ensure directory exists - const dir = path.dirname(filepath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - // Write the rule file - fs.writeFileSync(filepath, ruleContent, 'utf8'); - - console.log(`\n✅ Rule created: ${path.relative(process.cwd(), filepath)}`); - console.log(`📁 Location: ${filepath}`); - - // Stage the file for git - const { execSync } = require('child_process'); - try { - execSync(`git add "${filepath}"`, { stdio: 'inherit' }); - console.log('📦 File staged for git'); - } catch (error) { - console.log('⚠️ Could not stage file for git'); - } - - // Show the created content - console.log('\n📝 Created Rule Content:'); - console.log('='.repeat(60)); - console.log(ruleContent); - console.log('='.repeat(60)); - - return filepath; - } catch (error) { - console.error('❌ Error creating rule:', error.message); - throw error; - } -} - -function main() { - const request = process.argv[2]; - - if (!request) { - console.log('❌ Please provide a request'); - console.log('\nUsage: node .cursor/rules/create-rule.cjs "your request"'); - console.log('\nExamples:'); - console.log(' node .cursor/rules/create-rule.cjs "создай правило для архитектуры"'); - console.log(' node .cursor/rules/create-rule.cjs "добавь файл в .cursor"'); - console.log(' node .cursor/rules/create-rule.cjs "напиши правило безопасности"'); - console.log('\nOr use npm script:'); - console.log(' npm run create-rule "your request"'); - process.exit(1); - } - - createRule(request) - .then(() => { - console.log('\n🎉 Rule created successfully!'); - console.log('\n💡 Next steps:'); - console.log(' 1. Edit the rule file to add your specific content'); - console.log(' 2. Commit the changes: git commit -m "feat: add new rule"'); - console.log(' 3. The rule will be automatically protected by the cursor protection system'); - }) - .catch((error) => { - console.error('\n❌ Failed to create rule:', error.message); - process.exit(1); - }); -} - -if (require.main === module) { - main(); -} - -module.exports = { createRule, generateFilename, generateRuleContent }; \ No newline at end of file diff --git a/.cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md b/.cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md deleted file mode 100644 index cb3f3798..00000000 --- a/.cursor/rules/cursor-export/IMPORT-INSTRUCTIONS.md +++ /dev/null @@ -1,60 +0,0 @@ -# Import .cursor Rules - -## Quick Import - -1. Copy the entire `cursor-export` folder to your target project -2. Run the import script: - ```bash - node cursor-export/import-cursor..js - ``` - -## Manual Import - -1. Create `.cursor/rules/` directory in your target project -2. Copy files from `cursor-export/` to `.cursor/rules/` -3. Run audit and optimization: - ```bash - cd .cursor/rules - node cursor-manager..js full - ``` - -## What's Included - -### Core Rules -- AI memory and Commands -- Environment constrai.ts -- Project structure guIDElines -- TypeScript build troubleshooting - -### Categories -- **architecture/** - System architecture rules -- **dev/** - Development principles and guIDElines -- **doc/** - Documentation standards -- **plugin/** - Plugin development standards -- **security/** - Security rules -- **ui/** - UI/UX standards -- **workflow/** - Development workflow - -### Automation -- Audit system -- Auto-fix capabilities -- AI optimization -- Status monitoring - -## Customization - -After import, customize rules for your project: -1. Update `environment.mdc` with your project constrai.ts -2. Modify `ai-memory.mdc` with project-specific Commands -3. Adjust `monorepo-best-practices.mdc` for your structure -4. Run `node cursor-manager..js optimize` to apply changes - -## Verification - -Verify successful import: -```bash -cd .cursor/rules -node cursor-manager..js status -``` - -All files should show as "AI-ready" with no issues. diff --git a/.cursor/rules/cursor-export/ai-memory.mdc b/.cursor/rules/cursor-export/ai-memory.mdc deleted file mode 100644 index 3a7e484d..00000000 --- a/.cursor/rules/cursor-export/ai-memory.mdc +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: Universal user Commands for AI assistant - complete Command reference with triggers and actions -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: general ---- - -# AI Memory Bank - Universal User Commands - -## Commands for AI Assistant Recognition - -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank in English for AI/LLM compatibility (automatically translates and comm.ts) -- `update progress` / `Обнови прогресс` / `Update прогресс проекта` - Update activeContext.md and progress.md with current status -- `restore context` / `Восстанови контекст` / `Восстановить полный контекст` - Study all memory-bank files and restore full project understanding -- `quick restore` / `Быстрое Recovery` - Get brief summary of key principles and current status - -### Analysis and Study: -- `analyze architecture` / `Анализируй архитектуру` / `Анализ архитектуры` - Study systempatterns.md and techContext.md for architecture understanding -- `study plugins` / `Изучи Plugins` - Analyze plugins and their structure -- `check build` / `Проверь сборку` / `Check сборку` - Check that project builds and works correctly -- `update documentation` / `Обнови документацию` / `Update документацию` - Check and update README.md and PLUGIN_DEVELOPMENT.md - -### Development: -- `create plugin [name]` / `Создай плагин [название]` - Create new plugin with specified name -- `check code` / `Проверь код` / `Check линтинг` - Run linting and type checking -- `run te.ts` / `Запусти тесты` / `Запустить тесты` - Run all project te.ts -- `check dependencies` / `Проверь Dependencies` / `Check Dependencies` - Check dependencies relevance and compatibility - -### Project Management: -- `bump version patch/minor/major` / `Увеличь версию [patch|minor|major]` / `Versioning` - Increase project version according to parameter -- `clean project` / `Очисти проект` / `Очистка проекта` - Clean node_modules, dist and cache -- `analyze performance` / `Анализируй Performance` / `Анализ производительности` - Analyze project performance and suggest optimizations -- `check security` / `Проверь Security` / `Анализ безопасности` - Analyze code and configuration security - -### Releases and Deployment: -- `create release` / `Создай релиз` / `Create релиз` - Prepare project for release (bump version, create ZIP) -- `build production` / `Собери для продакшена` / `build для продакшена` - Perform full production build - -## General Principles: -- Commands can be combined (e.g.: "save context and update progress") -- All actions should consIDEr current project context -- Resu.ts should be saved in appropriate memory-bank files -- If Command is unclear — clarify details with user -- When restoring context — read all key memory-bank files and use only current best practices diff --git a/.cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc b/.cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc deleted file mode 100644 index efca4815..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-01-do-no-harm.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle harm - dev principle 01 do no harm -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 1: Do No Harm -- Security first: Any changes must improve system security, not weaken it -- Backward compatibility: Changes must not break existing functionality -- Gradual implementation: Implement changes step by step with rollback capability -- Testing: All changes must pass thorough testing before deployment -- Monitoring: Track impact of changes on performance and stability - -description: 'Do No Harm' principle for all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc b/.cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc deleted file mode 100644 index 7d2b513f..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-03-best-practices.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle practices - dev principle 03 best practices -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 3: Best Practices First -- Architectural patterns: Use proven architectural patterns and principles -- Coding standards: Follow accepted standards and conventions -- Security: Apply security principles (Zero Trust, Defense in Depth, Least Privilege) -- Performance: Optimize critical paths and avoid anti-patterns -- Scalability: Design consIDEring future growth and changes -- Testability: Write code that is easy to test and maintain - -description: Prioritize best practices in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc b/.cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc deleted file mode 100644 index 743a5fa7..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-04-fail-fast-safe.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle safe - dev principle 04 fail fast safe -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 4: Fail Fast, Fail Safe -- Early error detection: Validate at input, not during execution -- Graceful Degradation: System continues working even with partial failures -- Circuit breaker: Automatic shutdown of problematic compone.ts -- Rollback capability: Quick rollback to working State -- Error boundaries: Isolate errors at component level - -description: Fail fast and fail safe in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc b/.cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc deleted file mode 100644 index d46bceaa..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-05-observability.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle observability - dev principle 05 observability -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 5: Observability First -- Structured logging: Structured logging for analysis -- Metrics everywhere: Performance and State metrics -- distributed tracing: Track reque.ts through all compone.ts -- Health checks: Monitor State of all services -- Debug information: Sufficient information for problem diagnosis - -description: Observability and monitoring in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc b/.cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc deleted file mode 100644 index 90cf296d..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-06-config-as-code.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle code - dev principle 06 config as code -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 6: configuration as Code -- Version-controlled configs: All configurations in version control -- Environment-specific: Different configurations for different environme.ts -- Validation: Automatic configuration validation -- Documentation: Document all configuration parameters -- Default safety: Safe default values - -description: configuration as code for all environme.ts -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc b/.cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc deleted file mode 100644 index 4cb14008..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-07-progressive-enhancement.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle enhancement - dev principle 07 progressive enhancement -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 7: Progressive Enhancement -- Core functionality: Basic functionality always works -- Feature detection: Detect browser capabilities -- Graceful Degradation: Degrade functionality, not complete failure -- Performance budget: Performance budget for new features -- Accessibility baseline: Minimum accessibility level for all - -description: Progressive enhancement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc b/.cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc deleted file mode 100644 index 33209d0d..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-08-data-integrity-privacy.mdc +++ /dev/null @@ -1,35 +0,0 @@ ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle privacy - dev principle 08 data integrity privacy -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 8: data Integrity & Privacy -- data validation: Validate all input data -- Encryption at rest: Encrypt data at rest -- Encryption in transit: Encrypt data in transit -- data minimization: Collect only necessary data -- User consent: Explicit user consent for data processing -- Right to be forgotten: Ability to delete user data - -description: data integrity and privacy in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc b/.cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc deleted file mode 100644 index f298f32d..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-09-continuous-learning.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle learning - dev principle 09 continuous learning -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 9: Continuous Learning -- Performance monitoring: Continuous performance monitoring -- User feedback loops: User feedback -- A/B testing: Test hypotheses on real users -- Analytics insig.ts: Usage analysis for improveme.ts -- Knowledge sharing: Document.lessons and insig.ts - -description: Continuous learning and improvement in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc b/.cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc deleted file mode 100644 index 5a16b370..00000000 --- a/.cursor/rules/cursor-export/dev/dev-principle-10-ecosystem-thinking.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - - - - - ---- -description: Development principle thinking - dev principle 10 ecosystem thinking -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: high -aiCategory: development-practices ---- - -# Development Principle 10: Ecosystem Thinking -- Plugin compatibility: Ensure plugin compatibility -- API stability: Stable public APIs -- Backward compatibility: Backward version compatibility -- Community building: Support developer community -- Documentation quality: Quality documentation for ecosystem - -description: Ecosystem thinking in all development -globs: - - * -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/development-guidelines.mdc b/.cursor/rules/cursor-export/dev/development-guidelines.mdc deleted file mode 100644 index 535e47d1..00000000 --- a/.cursor/rules/cursor-export/dev/development-guidelines.mdc +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: General development guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: false -aiCategory: development-practices--- - -# Development GuIDElines - -## JavaScript/TypeScript -- Use ES6+ and async/await. -- Modular architecture, clear separation of concerns. -- Error handling and structured logging are required. -- Use TypeScript for type safety. -- Prefer functional patterns where possible. - -## Python (Plugins) -- Use MCP protocol for communication. -- Use async/await for I/O operations. -- Always validate inp.ts and handle errors. -- Plugins should be single-purpose. -- Document all plugin APIs clearly. - -# Security First -- Validate manife.ts and permissions. -- Sanitize data between.js and Python. -- Sandbox all plugins. -- Apply the principle of least privilege. -- Audit all plugin code. - -# Performance ConsIDErations -- Lazy-load plugins and PyodIDE when possible. -- cache repeated operations. -- Monitor memory usage and clean up resources. -- Optimize bundle size and loading time. -- Use WebWorkers for non-blocking tasks. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/git-workflow.mdc b/.cursor/rules/cursor-export/dev/git-workflow.mdc deleted file mode 100644 index 8dadc7b2..00000000 --- a/.cursor/rules/cursor-export/dev/git-workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git workflow rule - только develop в main, обязательные PR для всех изменений -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Git Workflow Rule - -**Rule:** -- Only merge into main from develop. -- All feature, fix, and doc branches must be merged into develop first, never directly into main. -- This rule is mandatory for all changes and pull reque.ts. - ---- -## Enforcement -- Local pre-push hook (.husky/pre-push) blocks direct push to main and develop. -- GitHub branch protection preve.ts direct push and enforces PRs from develop only. ---- \ No newline at end of file diff --git a/.cursor/rules/cursor-export/dev/security-and-deployment.mdc b/.cursor/rules/cursor-export/dev/security-and-deployment.mdc deleted file mode 100644 index d5f45cd4..00000000 --- a/.cursor/rules/cursor-export/dev/security-and-deployment.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Security rule for -and-deployment -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: trueaiPriority: normal -aiCategory: development-practices ---- - -# Security Best Practices - -## Plugin Validation -- Validate plugin manife.ts. -- Check all permissions. -- Scan for malicious code. -- Verify plugin signatures. - -## Runtime Security -- Sandbox all plugins. -- Validate all input data. -- Monitor plugin behavior. -- Apply rate limiting. - -## data Protection -- Sanitize all user data. -- Encrypt sensitive information. -- Ensure secure communication. -- Audit all data access. - -# Deployment ConsIDErations - -## Extension distribution -- Follow Chrome Web Store guIDElines. -- Use a secure update mechanism. -- ProvIDE a transparent privacy policy. -- Maintain user support channels. - -## Plugin distribution -- Use a marketplace for plugins. -- Validate and manage plugin versions. -- Perform security scanning. - -## Monitoring and Analytics -- Track plugin usage. -- Monitor performance. -- Collect error repo.ts. -- Analyze user feedback. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/testing-and-debugging.mdc b/.cursor/rules/cursor-export/dev/testing-and-debugging.mdc deleted file mode 100644 index 3008bfad..00000000 --- a/.cursor/rules/cursor-export/dev/testing-and-debugging.mdc +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing Strategy - -## Unit Testing -- Test compone.ts in isolation. -- Mock external dependencies. -- Validate error handling. -- Test security boundaries. - -## Integration Testing -- Test plugin loading and execution. -- Validate.js-Python communication. -- Test integration with browser APIs. -- Check permission compliance. - -## End-to-End Testing -- Test full plugin workflows. -- Validate user scenarios. -- Test extension installation and updates. -- Check cross-browser compatibility. - -# Debugging GuIDElines - -## JavaScript Debugging -- Use DevTools for extension debugging. -- Log message flow. -- Use breakpoi.ts for complex logic. -- Monitor WebWorker communication. - -## Python Debugging -- Use print/logging. -- Test plugins in isolation. -- Use PyodIDE console for debugging. - -## Common Issues -- PyodIDE startup: use loading indicators. -- Memory leaks: monitor worker lifecycle. -- Permission errors: validate manife.ts. -- Communication failures: check MCP format. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/dev/typescript.mdc b/.cursor/rules/cursor-export/dev/typescript.mdc deleted file mode 100644 index 7a1218c4..00000000 --- a/.cursor/rules/cursor-export/dev/typescript.mdc +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: TypeScript-specific guIDElines -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Rule -- Use only TypeScript for all new files. -- Enforce strict typing, avoid using `any`. -- Use modern language features (optional chaining, nullish coalescing, etc.). -- Always follow the shared.tsconfig from `packages.tsconfig/`. - -# examples -- ✅ `const foo: string = "bar"` -- ❌ `let x: any = 5` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/ai-fallback.mdc b/.cursor/rules/cursor-export/doc/ai-fallback.mdc deleted file mode 100644 index b9192a53..00000000 --- a/.cursor/rules/cursor-export/doc/ai-fallback.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Fallback procedures for AI -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: Правило fallback для AI - Priority источников информации при отсутствии ответа в memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI Fallback Rule - -**Правило:** -Если возникает вопрос, на который нет ответа в AI memory-bank, необходимо: -1. Сначала обращаться к сведениям в папке .rules проекта. -2. Если нужной информации нет в .rules, обращаться к сведениям в папке memory-bank проекта (особенно к архитектурным и контекстным файлам). - -**Priority:** -Это правило Priorityно для всех будущих консультаций и автоматизаций. \ No newline at end of file diff --git a/.cursor/rules/cursor-export/doc/ai-first.mdc b/.cursor/rules/cursor-export/doc/ai-first.mdc deleted file mode 100644 index 20ea682c..00000000 --- a/.cursor/rules/cursor-export/doc/ai-first.mdc +++ /dev/null @@ -1,69 +0,0 @@ ---- -description: AI-oriented documentation standards -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - - - - - ---- -description: AI-First Documentation Standards - analytical comme.ts and AI-oriented documentation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# AI-First Documentation Standards - -## Основные Principles -- **Analytical comme.ts**: Add comme.ts explaining logic and architectural decisions for AI understanding -- **Context explanations**: Explain "why", not just "what" the code does -- **Architectural comme.ts**: Document component relationships and data flows -- **Business logic**: Explain complex business logic and decisions made -- **TODO comme.ts**: Leave TODO with explanation of planned improveme.ts - -## examples правильного использования - -### ✅ Аналитические Comme.ts: -```TypeScript -// AI: This function is required for plugin isolation - preve.ts direct DOM access -function createSandboxedEnvironment() { - // Implementation... -} - -/** - * AI: Example usage - this component handles plugin communication - * through a secure message passing system - */ -export function PluginBridge() { - // Implementation... -} -``` - -### ✅ Объяснение контекста: -```TypeScript -// AI: Using setTimeout here because Chrome extension APIs are async -// and we need to ensure DOM is ready before accessing eleme.ts -setTimeout(() => { - initializePlugin(); -}, 100); -``` - -### ✅ TODO с объяснением: -```TypeScript -// TODO: AI - Replace with Web Workers for better performance -// Current implementation blocks main thread during heavy computations -function proceSSLargedataset(data) { - // Implementation... -} -``` - -## Recommendations -1. **Всегда объясняйте "почему"** - не только что делает код -2. **Используйте префикс "AI:"** для комментариев, предназначенных для AI -3. **Документируйте архитектурные решения** и их обоснование -4. **Объясняйте сложную бизнес-логику** с примерами -5. **Оставляйте TODO с контекстом** для будущих улучшений diff --git a/.cursor/rules/cursor-export/doc/knowledge-map.mdc b/.cursor/rules/cursor-export/doc/knowledge-map.mdc deleted file mode 100644 index f3bdcf53..00000000 --- a/.cursor/rules/cursor-export/doc/knowledge-map.mdc +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Project knowledge structure -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Knowledge Map - -- USER_CommandS: USER_CommandS.md — все пользовательские команды -- graveyard: memory-bank/graveyard.md — ошибки, environment-specific issues -- architecture: memory-bank/codebase-architecture.md — архитектурные решения, Patterns, Dependencies -- meta: memory-bank/README.md — мета-правила, структура, универсальные инструкции -- dev-principles: memory-bank/DEV_PRACTICES.md — best practices для разработки -- security: memory-bank/SECURITY.md — Standards безопасности -- knowledge-map: memory-bank/KNOWLEDGE_MAP.md — карта знаний -- progress: memory-bank/progress.md — Status и история -- activeContext: memory-bank/activeContext.md — актуальный контекст -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/mdc-file-standards.mdc b/.cursor/rules/cursor-export/doc/mdc-file-standards.mdc deleted file mode 100644 index e370d93a..00000000 --- a/.cursor/rules/cursor-export/doc/mdc-file-standards.mdc +++ /dev/null @@ -1,134 +0,0 @@ ---- -description: Standards for .mdc file creation -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: trueaiPriority: medium -aiCategory: documentation ---- - -# Standards создания правил в .cursor/rules - -## Priority .mdc файлов над .md - -### Required использовать .mdc для: -- **Правил и стандартов** - все правила должны быть в .mdc -- **AI инструкций** - команды, Principles, Constrai.ts -- **Архитектурных решений** - структура, Patterns, best practices -- **automation** - скрипты, workflow, Processes - -### Допустимо использовать .md для: -- **Чистой документации** - README, guIDEs, tutorials -- **Временных заметок** - черновики, эксперименты -- **Внешних ссылок** - ссылки на внешние ресурсы - -## Структура .mdc файла - -### Обязательные метаdata: -``.yaml ---- -description: Краткое Description назначения файла -globs: ["**/*"] # Patterns файлов для применения -alwaysApply: true # Автоматическое применение ---- -``` - -### Дополнительные метаdata: -``.yaml ---- -description: Description -globs: ["pages/**/*", "packages/**/*"] -alwaysApply: false # Применяется по запросу -related: - - other-rule.mdc - - another-rule.mdc ---- -``` - -## Преимущества .mdc файлов - -### 1. Автоматическое применение -- Cursor читает метаdata при создании чата -- Правила применяются без явного обращения -- `alwaysApply: true` обеспечивает постоянное Action - -### 2. Лучшая Integration с AI -- AI понимает контекст применения через `globs` -- Description помогает AI выбрать подходящие правила -- Структурированные метаdata улучшают понимание - -### 3. Приоритизация и организация -- `alwaysApply` определяет Importantсть правила -- `related` связывает связанные правила -- `globs` ограничивает область применения - -### 4. AI memory-bank Integration -- Правила автоматически попадают в AI memory-bank -- Доступны через Settings / Rules & Memories -- Сохраняются между сессиями - -## Миграция .md → .mdc - -### Когда мигрировать: -- Файл содержит правила или инструкции -- Файл должен применяться автоматически -- Файл важен для AI понимания проекта - -### Процесс миграции: -1. Переименовать `.md` → `.mdc` -2. Add.yaml frontmatter с метаданными -3. Убедиться в корректности `globs` и `alwaysApply` -4. Check интеграцию с AI memory-bank - -## examples правильного использования - -### ✅ Правильно - .mdc с метаданными: -``.yaml ---- -description: Правила TypeScript - configuration, barrel expo.ts, troubleshooting -globs: ["**/*.ts", "**/*.tsx", "**/*.json"] -alwaysApply: true ---- -``` - -### ❌ Неправильно - .md без метаданных: -```markdown -# TypeScript Rules -- Use strict mode -- Prefer barrel expo.ts -``` - -## Exceptions - -### Допустимые .md файлы: -- `README.md` - главная Documentation проекта -- `CHANGELOG.md` - история изменений -- `LICENSE.md` - лицензия -- Временные файлы с префиксом `temp-` или `draft-` - -## Verification соответствия - -### Автоматическая Verification: -```bash -# Найти все .md файлы в .cursor/rules -find .cursor/rules -name "*.md" -not -name "README.md" - -# Check структуру .mdc файлов -node .cursor/rules/check-rules-structure..js -``` - -### Ручная Verification: -1. Все ли правила в .mdc формате? -2. Есть ли метаdata во всех .mdc файлах? -3. Корректны ли `globs` и `alwaysApply`? -4. Нужно ли мигрировать какие-то .md файлы? - -## Recommendations - -1. **Всегда начинать с .mdc** для новых правил -2. **Мигрировать важные .md** файлы в .mdc -3. **Проверять метаdata** при создании правил -4. **Использовать `alwaysApply: true`** для критических правил -5. **Документировать Exceptions** в README.mdc -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/memorybank-quality.mdc b/.cursor/rules/cursor-export/doc/memorybank-quality.mdc deleted file mode 100644 index 0e2c801c..00000000 --- a/.cursor/rules/cursor-export/doc/memorybank-quality.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Quality standards for memory-bank -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Memory-Bank Quality Checklist - -- Актуальность: информация своевременно обновляется -- Полнота: все ключевые аспекты проекта отражены -- Нет дублирования: информация не повторяется в разных файлах -- Навигация: структура и ссылки понятны -- История изменений: все важные правки фиксируются -- Прозрачность: причины изменений и удаления всегда документируются -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/doc/restore-context.mdc b/.cursor/rules/cursor-export/doc/restore-context.mdc deleted file mode 100644 index cbee881a..00000000 --- a/.cursor/rules/cursor-export/doc/restore-context.mdc +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: Context restoration procedures -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: false -aiCategory: documentation--- - -# Восстанови контекст - -1. Прочитать все файлы в директории `memory-bank` и `USER_CommandS.md`. -2. Сделать краткое резюме ключевых положений, правил, пользовательских требований и текущего Statusа проекта. -3. Использовать эту информацию для всех последующих ответов и действий. - -> Это правило предназначено для ручного вызова или использования агентом при необходимости полного восстановления контекста проекта. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/environment.mdc b/.cursor/rules/cursor-export/environment.mdc deleted file mode 100644 index 9fb92cb1..00000000 --- a/.cursor/rules/cursor-export/environment.mdc +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Critical environment constrai.ts - Node.js версия, popup vs sIDEpanel -globs: ["**/*"] -alwaysApply: trueaiPriority: critical -aiCategory: general ---- - -# Critical environment constrai.ts - -- **popup не используется**, основной Interface — **sIDEpanel**. Все ошибки popup можно игнорировать, если они не влияют на работу sIDEpanel. - -- **Использовать только Node.js версии 22.17.1 и выше**. Понижать версию запрещено, все баги и инфраструктурные проблемы решать только в этом контексте. \ No newline at end of file diff --git a/.cursor/rules/cursor-export/import-cursor.cjs b/.cursor/rules/cursor-export/import-cursor.cjs deleted file mode 100755 index d70c8eed..00000000 --- a/.cursor/rules/cursor-export/import-cursor.cjs +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorImporter { - constructor() { - this.exportDir = __dirname; - this.targetRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async import() { - console.log('📥 Importing .cursor rules...\n'); - - // Создаем папку .cursor/rules если не существует - if (!fs.existsSync(this.targetRulesDir)) { - fs.mkdirSync(this.targetRulesDir, { recursive: true }); - console.log('✅ Created .cursor/rules directory'); - } - - // Копируем все файлы - await this.copyFiles(); - - // Копируем скрипты автоматизации - await this.copyAutomationScripts(); - - // Создаем базовую структуру если нужно - await this.createBasicStructure(); - - console.log('\n✅ Import completed!'); - console.log('\n🔧 Next steps:'); - console.log(' 1. Customize environment.mdc for your project'); - console.log(' 2. Update ai-memory.mdc with project-specific commands'); - console.log(' 3. Run: cd .cursor/rules && node cursor-manager.cjs full'); - } - - async copyFiles() { - console.log('📋 Copying rule files...'); - - const items = fs.readdirSync(this.exportDir); - - for (const item of items) { - const sourcePath = path.join(this.exportDir, item); - const targetPath = path.join(this.targetRulesDir, item); - - const stat = fs.statSync(sourcePath); - - if (stat.isDirectory()) { - // Копируем директории - if (!fs.existsSync(targetPath)) { - fs.mkdirSync(targetPath, { recursive: true }); - } - - const files = fs.readdirSync(sourcePath); - for (const file of files) { - if (file.endsWith('.mdc')) { - const fileSource = path.join(sourcePath, file); - const fileTarget = path.join(targetPath, file); - fs.copyFileSync(fileSource, fileTarget); - console.log(` ✅ ${item}/${file}`); - } - } - } else if (item.endsWith('.mdc')) { - // Копируем .mdc файлы - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${item}`); - } - } - } - - async copyAutomationScripts() { - console.log('🔧 Copying automation scripts...'); - - const scripts = [ - 'audit-cursor.cjs', - 'fix-cursor.cjs', - 'optimize-for-ai.cjs', - 'cursor-manager.cjs' - ]; - - for (const script of scripts) { - const sourcePath = path.join(this.exportDir, script); - if (fs.existsSync(sourcePath)) { - const targetPath = path.join(this.targetRulesDir, script); - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${script}`); - } - } - } - - async createBasicStructure() { - console.log('🏗️ Creating basic structure...'); - - const requiredDirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - - for (const dir of requiredDirs) { - const dirPath = path.join(this.targetRulesDir, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - console.log(` ✅ Created ${dir}/`); - } - } - } -} - -// Запуск импорта -async function main() { - const importer = new CursorImporter(); - await importer.import(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorImporter; diff --git a/.cursor/rules/cursor-export/index.mdc b/.cursor/rules/cursor-export/index.mdc deleted file mode 100644 index d0272f95..00000000 --- a/.cursor/rules/cursor-export/index.mdc +++ /dev/null @@ -1,80 +0,0 @@ ---- -description: index rule -globs: ["**/*"] -alwaysApply: true -aiCategory: general--- - -# Index of Modular Rules - -## root -- [AI Memory Bank](ai-memory.mdc) — User Commands and AI instructions -- [Environment Constrai.ts](environment.mdc) — Critical environment limitations -- [Monorepo Best Practices](monorepo-best-practices.mdc) — Monorepo structure and guIDElines -- [TypeScript build Troubleshooting](TypeScript-build-troubleshooting.mdc) — TypeScript build error solutions -- [README](README.mdc) — Main documentation and structure - -## architecture -- [Chat System Architecture](architecture/architecture-chat-system.mdc) — Chat system architecture and data flow -- [Error Handling (Architecture)](architecture/architecture-error-handling.mdc) — Error handling requireme.ts for system architecture -- [Monitoring and Observability (Architecture)](architecture/architecture-observability.mdc) — Monitoring and observability requireme.ts for system architecture -- [Performance GuIDElines (Architecture)](architecture/architecture-performance.mdc) — Performance guIDElines for system architecture -- [Plugin Architecture](architecture/architecture-plugin.mdc) — Plugin architecture and isolation requireme.ts -- [Project Structure Architecture](architecture/architecture-project-structure.mdc) — Directory and component structure for the project -- [Security Architecture](architecture/architecture-security.mdc) — Security architecture and controls -- [Development Workflow (Architecture)](architecture/architecture-workflow.mdc) — Development workflow and process standards -- [Project Overview](architecture/project-architecture.mdc) - -## dev -- [Development Principle 1: Do No Harm](dev/dev-principle-01-do-no-harm.mdc) — 'Do No Harm' principle for all development -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation and comme.ts for all code -- [Development Principle 3: Best Practices First](dev/dev-principle-03-best-practices.mdc) — Prioritize best practices in all development -- [Development Principle 4: Fail Fast, Fail Safe](dev/dev-principle-04-fail-fast-safe.mdc) — Fail fast and fail safe in all development -- [Development Principle 5: Observability First](dev/dev-principle-05-observability.mdc) — Observability and monitoring in all development -- [Development Principle 6: configuration as Code](dev/dev-principle-06-config-as-code.mdc) — configuration as code for all environme.ts -- [Development Principle 7: Progressive Enhancement](dev/dev-principle-07-progressive-enhancement.mdc) — Progressive enhancement in all development -- [Development Principle 8: data Integrity & Privacy](dev/dev-principle-08-data-integrity-privacy.mdc) — data integrity and privacy in all development -- [Development Principle 9: Continuous Learning](dev/dev-principle-09-continuous-learning.mdc) — Continuous learning and improvement in all development -- [Development Principle 10: Ecosystem Thinking](dev/dev-principle-10-ecosystem-thinking.mdc) — Ecosystem thinking in all development -- [Development GuIDElines](dev/development-guIDElines.mdc) — General development guIDElines -- [Security Best Practices](dev/security-and-deployment.mdc) — Security and deployment standards -- [Testing Strategy](dev/testing-and-debugging.mdc) — Testing and debugging approaches -- [TypeScript Rules](dev/TypeScript.mdc) — TypeScript-specific guIDElines -- [Git Workflow](dev/git-workflow.mdc) — Git workflow and branching rules - -## doc -- [AI-First Documentation](doc/ai-first.mdc) — AI-oriented documentation standards -- [Knowledge Map](doc/knowledge-map.mdc) — Project knowledge structure -- [Memory-Bank Quality Checklist](doc/memorybank-quality.mdc) — Quality standards for memory-bank -- [Restore Context](doc/restore-context.mdc) — Context restoration procedures -- .mdc File Standards](doc.mdc-file-standards.mdc) — Standards for .mdc file creation -- [AI Fallback Rule](doc/ai-fallback.mdc) — Fallback procedures for AI - -## plugin -- [Plugin Best Practices](plugin/plugin-best-practices.mdc) — Best practices for plugin development -- [Plugin Documentation Standards](plugin/plugin-documentation.mdc) — Documentation standards for all plugins -- [Plugin Error Handling](plugin/plugin-error-handling.mdc) — Error handling requireme.ts for all plugins -- [Plugin Performance GuIDElines](plugin/plugin-performance.mdc) — Performance guIDElines for all plugins -- [Plugin Security Requireme.ts](plugin/plugin-security.mdc) — Security requireme.ts for all plugins -- [Plugin Structure](plugin/plugin-structure.mdc) — Plugin directory and manifest requireme.ts -- [Plugin Testing Requireme.ts](plugin/plugin-testing.mdc) — Testing requireme.ts for all plugins - -## security -- [Input Validation](security/validation.mdc) — Input validation and data sanitization rules - -## ui -- [Accessibility (A11y) Standards](ui/ui-accessibility.mdc) — Accessibility requireme.ts for all UI compone.ts -- [UI Animation and Transitions](ui/ui-animation.mdc) — Animation and transition standards for all UI compone.ts -- [UI Error Handling](ui/ui-error-handling.mdc) — Error handling requireme.ts for all UI compone.ts -- [UI Form Standards](ui/ui-forms.mdc) — Form standards for all UI compone.ts -- [UI Loading States](ui/ui-loading-States.mdc) — Loading State requireme.ts for all UI compone.ts -- [UI Mobile Responsiveness](ui/ui-mobile.mdc) — Mobile responsiveness standards for all UI compone.ts -- [UI Navigation Standards](ui/ui-navigation.mdc) — Navigation standards for all UI compone.ts -- [UI Performance Standards](ui/ui-performance.mdc) — Performance standards for all UI compone.ts -- [React Component Standards](ui/ui-React-compone.ts.mdc) — Standards for React component structure in UI -- [UI Styling Standards](ui/ui-styling.mdc) — Styling standards for all UI compone.ts -- [UI Testing Standards](ui/ui-testing.mdc) — Testing standards for all UI compone.ts - -## workflow -- [Automation Rules](workflow/automation.mdc) — Automation and synchronization rules -- [Branch Management](workflow/branches.mdc) — Git branch management rules -- [Workflow Branching Rules](workflow/workflow.mdc) — Development workflow standards \ No newline at end of file diff --git a/.cursor/rules/cursor-export/monorepo-best-practices.mdc b/.cursor/rules/cursor-export/monorepo-best-practices.mdc deleted file mode 100644 index f0a8387d..00000000 --- a/.cursor/rules/cursor-export/monorepo-best-practices.mdc +++ /dev/null @@ -1,208 +0,0 @@ ---- -description: Best practices for monorepo work - структура, Dependencies, TypeScript, barrel expo.ts -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# Monorepo Best Practices for AI - -## Основные Principles - -### 1. Project Structure -- **packages/** - общие Libraries и утилиты -- **pages/** - приложения и страницы Extensions -- **external/** - копии внешних boilerplate проектов -- **te.ts/** - тесты и e2e - -### 2. Dependencies -- **Root dependencies** - только общие инструменты (prettier, eslint, TypeScript) -- **Package dependencies** - устанавливать в конкретных пакетах -- **Workspace dependencies** - использовать `pnpm add -D package -w` для root - -### 3. TypeScript configuration -- **Base config** - `packages.tsconfig/base.json` для общих настроек -- **Package configs** - наследовать от base с специфичными настройками -- **App configs** - наследовать от base с browser-specific настройками - -## Правила для AI - -### При создании новых пакетов: - -1. **Create правильную структуру:** -``` -packages/new-package/ -├── lib/ -│ ├── index.ts -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -└── README.md -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "...tsconfig/base.json", - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - }, - "include": ["lib/**/*", "index..ts"], - "exclude": ["node_modules", "dist"] -} -``` - -3. **Настроить package.json:** -``.json -{ - "name": "@extension/new-package", - "main": "dist/index..js", - "types": "dist/index.d.ts", - "expo.ts": { - ".": "./dist/index..js" - }, - "files": ["dist"] -} -``` - -### При создании новых страниц: - -1. **Create правильную структуру:** -``` -pages/new-page/ -├── src/ -│ ├── index.tsx -│ └── compone.ts/ -├── package.json -├──.tsconfig.json -├── pos.css.config..js -└── vite.config..ts -``` - -2. **Настроить.tsconfig.json:** -``.json -{ - "extends": "@extension.tsconfig/base", - "compilerOptions": { - "types": ["chrome"], - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -3. **Настроить Pos.css для Tailwin.css 4+:** -```JavaScript -// pos.css.config..js -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -### При работе с barrel expo.ts: - -1. **Default expo.ts:** -```TypeScript -// component.ts -export default function Component() { ... } - -// index.ts -export { default as Component } from './component.js'; -``` - -2. **Named expo.ts:** -```TypeScript -// component.ts -export function Component() { ... } - -// index.ts -export { Component } from './component'; -``` - -3. **Mixed expo.ts:** -```TypeScript -// index.ts -export * from './helpers.js'; -export { default as Component } from './component.js'; -export type * from './types.js'; -``` - -### При установке зависимостей: - -1. **В конкретном пакете:** -```bash -cd packages/package-name -pnpm add dependency-name -``` - -2. **В конкретной странице:** -```bash -cd pages/page-name -pnpm add dependency-name -``` - -3. **В workspace root:** -```bash -pnpm add -D dependency-name -w -``` - -### При диагностике проблем: - -1. **Check Dependencies:** -```bash -pnpm why package-name -pnpm list package-name -``` - -2. **Найти проблемные файлы:** -```bash -find . -name .tsconfig.json" -exec grep -l "node" {} \; -``` - -3. **Очистить и пересобрать:** -```bash -rm -rf dist && pnpm run build -``` - -## Частые ошибки и решения - -| Проблема | Решение | -|----------|---------| -| `Cannot find type definition file for 'node'` | Remove 'node' из types в.tsconfig.json | -| `"Component" is not exported by` | Использовать `export { default as Component } from './file.js'` | -| `Cannot find module 'tailwin.css'` | `pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css` | -| `Rollup failed to resolve import` | `pnpm add package-name` в конкретном пакете | -| `spawn prettier ENOENT` | `git commit --no-verify` | - -## Команды для работы - -```bash -# build всего проекта -pnpm run build - -# build конкретного пакета -pnpm -F @extension/package-name run build - -# Установка зависимостей -pnpm install - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache - -# Пропуск pre-commit хуков -git commit --no-verify -m "message" -``` - -## Recommendations - -1. **Всегда проверять сборку** после изменений -2. **Документировать решения** для повторного использования -3. **Использовать правильные barrel expo.ts** для default expo.ts -4. **Устанавливать Dependencies в правильных местах** -5. **Следовать структуре проекта** при создании новых файлов \ No newline at end of file diff --git a/.cursor/rules/cursor-export/plugin/plugin-best-practices.mdc b/.cursor/rules/cursor-export/plugin/plugin-best-practices.mdc deleted file mode 100644 index a9901630..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-best-practices.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for best-practices -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Best Practices -- Single Responsibility: Each plugin should have a single, clear purpose -- Modular Design: Break complex functionality into modules -- configuration: Use configuration files for customizable behavior -- Versioning: Follow semantic versioning for plugin updates -- Backward Compatibility: Maintain compatibility with previous versions - -description: Best practices for plugin development -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-documentation.mdc b/.cursor/rules/cursor-export/plugin/plugin-documentation.mdc deleted file mode 100644 index 5b34417a..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-documentation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for documentation -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Documentation Standards -- API Documentation: Document all public APIs and parameters -- Usage examples: ProvIDE clear usage examples -- Error Codes: Document all possible error codes and meanings -- Performance Notes: Document performance characteristics -- Security Notes: Document security consIDErations - -description: Documentation standards for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-error-handling.mdc b/.cursor/rules/cursor-export/plugin/plugin-error-handling.mdc deleted file mode 100644 index a95d9cf5..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-error-handling.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for error-handling -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Error Handling -- Graceful Degradation: Continue working with reduced functionality -- User Feedback: ProvIDE clear error messages to users -- Logging: Log errors for debugging without exposing sensitive data -- Fallbacks: Implement fallback mechanisms for critical features -- Recovery: Automatic retry mechanisms where appropriate - -related: - - plugin-security.mdc - - architecture-error-handling.mdc - -description: Error handling requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-performance.mdc b/.cursor/rules/cursor-export/plugin/plugin-performance.mdc deleted file mode 100644 index 868ad488..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-performance.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Plugin standard for performance -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Performance GuIDElines -- PyodIDE Optimization: Optimize for WebAssembly execution -- Memory Management: Clean up resources and avoid memory leaks -- Async Operations: Use async/await for all I/O operations -- Caching: Implement appropriate caching strategies -- Bundle Size: Keep plugin size reasonable for browser loading - -related: - - architecture-performance.mdc - - plugin-testing.mdc - -description: Performance guIDElines for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-security.mdc b/.cursor/rules/cursor-export/plugin/plugin-security.mdc deleted file mode 100644 index a3e81a41..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-security.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: Plugin standard for security -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: true -aiCategory: plugin-development--- - -# Plugin Security Requireme.ts -- Zero Trust: Plugins are not trusted by default -- Permission Declaration: All required permissions must be declared in manifest -- API Validation: All API calls must be validated against schemas -- Rate Limiting: Respect rate lim.ts to prevent abuse -- Error Handling: Graceful error handling without exposing sensitive data - -description: Security requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-structure.mdc b/.cursor/rules/cursor-export/plugin/plugin-structure.mdc deleted file mode 100644 index 92065786..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-structure.mdc +++ /dev/null @@ -1,60 +0,0 @@ ---- -description: Plugin standard for structure -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Structure - -## Directory Layout -``` -public/plugins/plugin-name/ -├── manifest.json # Plugin metadata and permissions -├── mcp_server.py # Python MCP protocol implementation -├── workflow.json # Plugin workflow definition (optional) -└──.icon.svg # Plugin.icon -``` - -## Manifest.json Example -``.json -{ - "name": "Plugin Name", - "version": "1.0.0", - "description": "Plugin description", - "main_server": "mcp_server.py", - "required_secr.ts": ["openai_API_key", "weather_API_key"], - "API_permissions": { - "openai": { - "domains": ["API.openai.com"], - "endpoi.ts": ["/v1/chat/completions"], - "methods": ["POST"], - "rate_limit": "100/hour" - } - }, - "network_policy": { - "allowed_domains": ["API.openai.com"], - "WebSock.ts": "denied" - }, - "API_schemas": { - "openai": { - "chat_completions": { - "type": "object", - "required": ["prompt"], - "properties": { - "prompt": {"type": "string", "maxLength": 4000} - } - } - } - } -} -``` -description: Plugin directory and manifest requireme.ts -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/plugin/plugin-testing.mdc b/.cursor/rules/cursor-export/plugin/plugin-testing.mdc deleted file mode 100644 index c90be344..00000000 --- a/.cursor/rules/cursor-export/plugin/plugin-testing.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Plugin standard for testing -globs: ["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"] -alwaysApply: falseaiPriority: normal -aiCategory: plugin-development ---- - -# Plugin Testing Requireme.ts -- Unit Te.ts: Test individual functions and compone.ts -- Integration Te.ts: Test plugin integration with host system -- Security Te.ts: Validate permission enforcement -- Performance Te.ts: Monitor memory usage and execution time -- Error Te.ts: Test error handling and recovery - -description: Testing requireme.ts for all plugins -globs: - - public/plugins/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/security/validation.mdc b/.cursor/rules/cursor-export/security/validation.mdc deleted file mode 100644 index 76a56384..00000000 --- a/.cursor/rules/cursor-export/security/validation.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Input validation and data sanitization rules -globs: ["**/*.ts", "**/*.js", "**/*.py", "**/*.json", "platform-core/**/*"] -alwaysApply: false -aiCategory: security--- - -# Rule -- Always validate all input data and API parameters. -- Never trust data from external sources (user, plugins, third-party services). -- Use schemas, types, or validators (e.g., zod, pydantic, marshmallow). - -# examples -- ✅ `z.object({ email: z.string().email() })` -- ✅ `if not isinstance(user_id, int): raise ValueError()` -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/typescript-build-troubleshooting.mdc b/.cursor/rules/cursor-export/typescript-build-troubleshooting.mdc deleted file mode 100644 index 5c629cf7..00000000 --- a/.cursor/rules/cursor-export/typescript-build-troubleshooting.mdc +++ /dev/null @@ -1,166 +0,0 @@ ---- -description: TypeScript build error troubleshooting guIDE - Typeы, barrel expo.ts, Dependencies, Tailwin.css -globs: ["**/*"] -alwaysApply: false -aiCategory: general--- - -# TypeScript build Troubleshooting Rules - -## Основные Principles устранения ошибок - -### 1. TypeScript configuration Errors - -**Error:** `Cannot find type definition file for 'node'` -- **Причина:** В .tsconfig.json` указаны Typeы Node.js для Browserного кода -- **Решение:** Remove `'node'` из `compilerOptions.types`, оставить только `['chrome']` -- **Применяется к:** `pages/*.tsconfig.json`, `te.ts/e2e.tsconfig.json` - -**Error:** `Cannot find module '@extension/shared'` -- **Причина:** Неправильные `paths` в .tsconfig.json` -- **Решение:** Add корректные пути в `compilerOptions.paths` -- **Пример:** -``.json -{ - "compilerOptions": { - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### 2. Barrel Export Issues - -**Error:** `"initAppWithShadow" is not exported by ".../packages/shared/dist/index..js"` -- **Причина:** Неправильный barrel export для default expo.ts -- **Решение:** Использовать явный re-export: `export { default as initAppWithShadow } from './init-app-with-shadow.js'` -- **Паттерн:** -```TypeScript -// В index.ts -export { default as ComponentName } from './component-file.js'; - -// В component-file.ts -export default function ComponentName() { ... } -``` - -### 3. Tailwin.css 4+ Setup - -**Error:** `Cannot find module 'tailwin.css'` -- **Решение:** Установить Dependencies в конкретном пакете: -```bash -pnpm add -D tailwin.css autoprefixer @tailwin.css/pos.css -``` - -**Error:** `Loading Pos.css Plugin failed: Cannot find module '@tailwin.css/pos.css'` -- **Решение:** Create `pos.css.config..js`: -```JavaScript -module.expo.ts = { - plugins: { - '@tailwin.css/pos.css': {}, - autoprefixer: {}, - }, -} -``` - -**Error:** `Command 'tailwin.css-cli' not found` -- **Решение:** Delete прямой вызов CLI из `build..ts`, использовать Vite Pos.css pipeline - -### 4. Module Resolution - -**Error:** `Rollup failed to resolve import 'file-saver'` -- **Решение:** Установить недостающую зависимость в конкретном пакете: -```bash -pnpm add file-saver -``` - -**Error:** `Failed to resolve entry for package '@extension/vite-config'` -- **Причина:** Неправильные `main`/`expo.ts` в `package.json` -- **Решение:** Использовать ESM-only экспорт: -``.json -{ - "main": "dist/index..js", - "expo.ts": { - ".": "./dist/index..js" - } -} -``` - -### 5. build Script Issues - -**Error:** .jsx is not exported by React.jsx-runtime.js` -- **Причина:** НеCompatibility версий React/SWC/Vite -- **Решение:** - 1. Update до последних версий: `React`, `React-dom`, `vite`, `@vit.js/plugin-React-swc` - 2. Убедиться в .tsconfig.json`: `.jsx": "React.jsx"` - 3. Не использовать `import type React` - -### 6. Pre-commit Hook Issues - -**Error:** `spawn prettier ENOENT` -- **Решение:** Установить prettier и Plugins: -```bash -pnpm add -D prettier prettier-plugin-tailwin.css -w -``` -- **Альтернатива:** Использовать `git commit --no-verify` для пропуска хуков - -## Порядок устранения ошибок - -1. **Анализ ошибки:** Определить Type и контекст -2. **Verification зависимостей:** Убедиться в наличии всех пакетов -3. **configuration:** Исправить .tsconfig.json`, `package.json` -4. **Barrel expo.ts:** Check правильность экспортов -5. **build:** Выполнить `rm -rf dist && pnpm run build` -6. **cache:** При необходимости очистить cache: `pnpm exec rimraf node_modules/.vite .turbo .cache` - -## Частые Patterns исправлений - -### Для pages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "types": ["chrome"], // Remove 'node' - "paths": { - "@extension/shared": ["../../packages/shared"], - "@extension/ui": ["../../packages/ui"] - } - } -} -``` - -### Для packages/*.tsconfig.json: -``.json -{ - "compilerOptions": { - "outDir": "dist", - "declaration": true, - "declarationDir": "dist" - } -} -``` - -### Для barrel expo.ts: -```TypeScript -// Правильно для default expo.ts -export { default as ComponentName } from './component-file.js'; - -// Правильно для named expo.ts -export { ComponentName } from './component-file.js'; -``` - -## Команды для diagnostics - -```bash -# Verification зависимостей -pnpm why React -pnpm why tailwin.css - -# Поиск файлов -find . -name .tsconfig.json" -exec grep -l "node" {} \; - -# Verification сборки -pnpm run build - -# Очистка cacheа -pnpm exec rimraf node_modules/.vite .turbo .cache && pnpm install -``` \ No newline at end of file diff --git a/.cursor/rules/cursor-export/ui/ui-accessibility.mdc b/.cursor/rules/cursor-export/ui/ui-accessibility.mdc deleted file mode 100644 index 197d9d79..00000000 --- a/.cursor/rules/cursor-export/ui/ui-accessibility.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for accessibility -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# Accessibility (A11y) Standards -- ProvIDE proper ARIA labels for interactive eleme.ts -- Ensure all functionality is keyboard accessible -- Proper focus handling and visible focus indicators -- Use semantic.html and descriptive text for screen readers -- Meet WCAG color contrast requireme.ts - -related: - - ui-forms.mdc - - ui-error-handling.mdc - -description: Accessibility requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-animation.mdc b/.cursor/rules/cursor-export/ui/ui-animation.mdc deleted file mode 100644 index 263fa004..00000000 --- a/.cursor/rules/cursor-export/ui/ui-animation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for animation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Animation and Transitions -- Use.css transitions for State changes -- Subtle loading animations -- Enhance user experience with micro-interactions -- Ensure animations don't impact performance -- Respect user's motion preferences (reduced motion) - -description: Animation and transition standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-error-handling.mdc b/.cursor/rules/cursor-export/ui/ui-error-handling.mdc deleted file mode 100644 index c2613648..00000000 --- a/.cursor/rules/cursor-export/ui/ui-error-handling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for error-handling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Error Handling -- Clear, actionable error messages for users -- Catch and handle component errors with error boundaries -- ProvIDE fallback UI for failed compone.ts -- Allow users to retry failed operations -- Log errors for debugging - -description: Error handling requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-forms.mdc b/.cursor/rules/cursor-export/ui/ui-forms.mdc deleted file mode 100644 index e9b6928b..00000000 --- a/.cursor/rules/cursor-export/ui/ui-forms.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for forms -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Form Standards -- Client-sIDE and server-sIDE validation -- Show validation errors clearly -- ProvIDE clear success feedback -- Auto-save forms where appropriate -- Support common keyboard shortc.ts - -description: Form standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-loading-states.mdc b/.cursor/rules/cursor-export/ui/ui-loading-states.mdc deleted file mode 100644 index 4b268cbf..00000000 --- a/.cursor/rules/cursor-export/ui/ui-loading-states.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for loading-States -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Loading States -- Show skeleton UI while loading -- Use spinners or progress bars as indicators -- ProvIDE informative loading messages -- Handle loading timeo.ts gracefully -- Show appropriate error States - -description: Loading State requireme.ts for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-mobile.mdc b/.cursor/rules/cursor-export/ui/ui-mobile.mdc deleted file mode 100644 index 3c472d88..00000000 --- a/.cursor/rules/cursor-export/ui/ui-mobile.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for mobile -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Mobile Responsiveness -- Adequate size for touch interaction (touch targ.ts) -- Proper viewport meta tags -- Use flexbox and grid for responsive layo.ts -- Support common touch gestures -- Optimize for mobile performance - -description: Mobile responsiveness standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-navigation.mdc b/.cursor/rules/cursor-export/ui/ui-navigation.mdc deleted file mode 100644 index 02d02c56..00000000 --- a/.cursor/rules/cursor-export/ui/ui-navigation.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for navigation -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Navigation Standards -- ProvIDE clear navigation context with breadcrumbs -- Clearly indicate current page/section (active States) -- Support browser back button -- Support direct links to specific content (deep linking) -- Show loading during navigation - -description: Navigation standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-performance.mdc b/.cursor/rules/cursor-export/ui/ui-performance.mdc deleted file mode 100644 index dc609680..00000000 --- a/.cursor/rules/cursor-export/ui/ui-performance.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for performance -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Performance Standards -- Lazy load compone.ts and routes -- Use React.memo and useMemo appropriately -- Split code into smaller bundles -- Optimize images for web -- Implement appropriate caching strategies - -description: Performance standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-react-components.mdc b/.cursor/rules/cursor-export/ui/ui-react-components.mdc deleted file mode 100644 index 84458a72..00000000 --- a/.cursor/rules/cursor-export/ui/ui-react-components.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for React-compone.ts -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# React Component Standards -- Use TypeScript for all new compone.ts -- Prefer functional compone.ts with hooks -- Define clear interfaces for component props -- ProvIDE sensible default values -- Wrap compone.ts in error boundaries - -description: Standards for React component structure in UI -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-styling.mdc b/.cursor/rules/cursor-export/ui/ui-styling.mdc deleted file mode 100644 index 0a2f7319..00000000 --- a/.cursor/rules/cursor-export/ui/ui-styling.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: UI standard for styling -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Styling Standards -- Use Tailwind.css for consistent styling -- Organize.css classes logically -- Ensure responsive design for different screen sizes -- Support light/dark mode -- Use.css variables for theme values - -description: Styling standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/ui/ui-testing.mdc b/.cursor/rules/cursor-export/ui/ui-testing.mdc deleted file mode 100644 index 133251bf..00000000 --- a/.cursor/rules/cursor-export/ui/ui-testing.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: UI standard for testing -globs: ["pages/**/*", "**/*.tsx", "**/*.css", "**/*..css", "packages/ui/**/*"] -alwaysApply: falseaiPriority: normal -aiCategory: user-interface ---- - -# UI Testing Standards -- Test component logic and behavior (unit te.ts) -- Test component interactions (integration te.ts) -- Test visual appearance and layout (visual te.ts) -- Test accessibility compliance (a11y te.ts) -- Test component performance - -related: - - plugin-testing.mdc - - ui-error-handling.mdc - -description: Testing standards for all UI compone.ts -globs: - - pages/*/src/compone.ts/* -alwaysApply: false ---- -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/workflow/automation.mdc b/.cursor/rules/cursor-export/workflow/automation.mdc deleted file mode 100644 index 14024a6e..00000000 --- a/.cursor/rules/cursor-export/workflow/automation.mdc +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Automation and synchronization rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Automation Rules - -- При обновлении ключевых принципов, стандартов или чек-листов в memory-bank (например, DEV_PRACTICES.md, SECURITY.md, KNOWLEDGE_MAP.md), ассистент обязан синхронизировать их с правилами Cursor (.cursor/rules/). -- Все изменения должны быть отражены как в документации, так и в правилах для AI. -- Если обнаружено расхождение — предложить пользователю синхронизировать и объяснить различия. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-export/workflow/branches.mdc b/.cursor/rules/cursor-export/workflow/branches.mdc deleted file mode 100644 index d485b834..00000000 --- a/.cursor/rules/cursor-export/workflow/branches.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Git branch management rules -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: false -aiCategory: process-management--- - -# Rule -- For new features, use branches with the prefix `feature/` and a meaningful name. -- For bugfixes, use the prefix `fix/` and a clear description of the problem. -- Never mix bugfixes and features in the same branch. -- After merging a bugfix, update feature branches via rebase/merge from develop. -- Direct comm.ts or merges to `main` and `develop` are strictly forbidden. All changes must go through pull reque.ts into `develop` only. -- Merging to `main` is allowed only from `develop` after release approval. -- **Branch Purpose Tracking:** Before starting a new, unrelated task, you must finish the current branch (commit, push, PR, merge) and only then create a new branch for the next task. This preve.ts mixing tasks and improves review clarity. Exceptions: urgent bugfixes (fix/). - -# examples -- ✅ `feature/pyodIDE-integration` -- ✅ `fix/tailwin.css-build-script` -- ❌ `feature/pyodIDE-integration` + unrelated refactor in same branch \ No newline at end of file diff --git a/.cursor/rules/cursor-export/workflow/workflow.mdc b/.cursor/rules/cursor-export/workflow/workflow.mdc deleted file mode 100644 index 8466ff5b..00000000 --- a/.cursor/rules/cursor-export/workflow/workflow.mdc +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: Workflow rule for -globs: ["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"] -alwaysApply: trueaiPriority: medium -aiCategory: process-management ---- - -# Workflow Branching Rules - -- В ветке main запрещено вести работу. main используется только для релизов и production. -- В ветке develop запрещено вести работу напрямую. Все изменения — только через feature-Branches с именованием feature/{Seeсловое_название}, которые создаются автоматически по Seeслу задачи. -- Merge feature-веток только через pull request в develop. -- Прямой merge или commit в main и develop запрещён. -- В main разрешён merge только из develop после релизного ревью. -- Все PR должны содержать Description изменений, ссылки на документацию и changelog. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/cursor-git-hook.cjs b/.cursor/rules/cursor-git-hook.cjs deleted file mode 100644 index 9af6e6e9..00000000 --- a/.cursor/rules/cursor-git-hook.cjs +++ /dev/null @@ -1,240 +0,0 @@ -#!/usr/bin/env node - -/** - * Cursor Git Hook Script - * Automatically protects .cursor files on git operations - * Usage: node .cursor/rules/cursor-git-hook.cjs [pre-commit|post-commit|pre-push] - */ - -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); -const { protectCursor, checkCursorStatus } = require('./cursor-protector.cjs'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const GIT_HOOKS_DIR = path.join(process.cwd(), '.git', 'hooks'); - -function getStagedCursorFiles() { - try { - const output = execSync('git diff --cached --name-only --diff-filter=ACM', { encoding: 'utf8' }); - const files = output.trim().split('\n').filter(Boolean); - return files.filter(file => file.startsWith('.cursor/')); - } catch (error) { - return []; - } -} - -function preCommitHook() { - console.log('🛡️ Pre-commit: Checking .cursor files...\n'); - - const stagedFiles = getStagedCursorFiles(); - - if (stagedFiles.length === 0) { - console.log('✅ No .cursor files staged for commit'); - return true; - } - - console.log(`📁 Found ${stagedFiles.length} staged .cursor files:`); - stagedFiles.forEach(file => console.log(` - ${file}`)); - - // Check if any staged files contain Russian content - let hasRussianContent = false; - - for (const file of stagedFiles) { - try { - const content = fs.readFileSync(file, 'utf8'); - if (/[а-яё]/i.test(content)) { - console.log(`⚠️ Warning: ${file} contains Russian content`); - hasRussianContent = true; - } - } catch (error) { - console.log(`❌ Error reading ${file}: ${error.message}`); - } - } - - if (hasRussianContent) { - console.log('\n🔄 Automatically translating staged .cursor files...'); - - // Translate staged files - for (const file of stagedFiles) { - try { - const { translateText } = require('./cursor-protector.cjs'); - const content = fs.readFileSync(file, 'utf8'); - const translatedContent = translateText(content); - - // Create backup - const backupDir = path.join(CURSOR_DIR, 'backup', 'pre-commit'); - if (!fs.existsSync(backupDir)) { - fs.mkdirSync(backupDir, { recursive: true }); - } - - const backupPath = path.join(backupDir, path.basename(file)); - fs.copyFileSync(file, backupPath); - - // Write translated content - fs.writeFileSync(file, translatedContent, 'utf8'); - - // Stage the translated file - execSync(`git add "${file}"`, { stdio: 'inherit' }); - - console.log(`✅ Translated and staged: ${file}`); - } catch (error) { - console.log(`❌ Error translating ${file}: ${error.message}`); - } - } - - console.log('\n✅ All staged .cursor files translated to English'); - } else { - console.log('\n✅ All staged .cursor files are already in English'); - } - - return true; -} - -function postCommitHook() { - console.log('🛡️ Post-commit: Ensuring .cursor protection...\n'); - - // Check overall .cursor status - const status = checkCursorStatus(); - - if (status.russianContentCount > 0) { - console.log('\n⚠️ Warning: Some .cursor files still contain Russian content'); - console.log('💡 Consider running: node .cursor/rules/cursor-protector.cjs protect'); - } else { - console.log('\n✅ All .cursor files are protected (English only)'); - } - - return true; -} - -function prePushHook() { - console.log('🛡️ Pre-push: Final .cursor protection check...\n'); - - const status = checkCursorStatus(); - - if (status.russianContentCount > 0) { - console.log('\n❌ Push blocked: .cursor files contain Russian content'); - console.log('💡 Please run: node .cursor/rules/cursor-protector.cjs protect'); - console.log('💡 Then commit and push again'); - return false; - } - - console.log('\n✅ .cursor files are protected - push allowed'); - return true; -} - -function installHooks() { - console.log('🔧 Installing .cursor protection Git hooks...\n'); - - const hooks = { - 'pre-commit': preCommitHook, - 'post-commit': postCommitHook, - 'pre-push': prePushHook - }; - - for (const [hookName, hookFunction] of Object.entries(hooks)) { - const hookPath = path.join(GIT_HOOKS_DIR, hookName); - const hookContent = `#!/bin/sh -# Cursor Protection Hook -node .cursor/rules/cursor-git-hook.cjs ${hookName} -`; - - try { - fs.writeFileSync(hookPath, hookContent, 'utf8'); - fs.chmodSync(hookPath, '755'); - console.log(`✅ Installed: ${hookName} hook`); - } catch (error) { - console.log(`❌ Error installing ${hookName} hook: ${error.message}`); - } - } - - console.log('\n🎉 Git hooks installed successfully!'); - console.log('📝 .cursor files will now be automatically protected on git operations'); -} - -function uninstallHooks() { - console.log('🔧 Uninstalling .cursor protection Git hooks...\n'); - - const hooks = ['pre-commit', 'post-commit', 'pre-push']; - - for (const hookName of hooks) { - const hookPath = path.join(GIT_HOOKS_DIR, hookName); - - try { - if (fs.existsSync(hookPath)) { - const content = fs.readFileSync(hookPath, 'utf8'); - - // Only remove if it's our hook - if (content.includes('Cursor Protection Hook')) { - fs.unlinkSync(hookPath); - console.log(`✅ Uninstalled: ${hookName} hook`); - } else { - console.log(`⚠️ Skipped: ${hookName} hook (not our hook)`); - } - } else { - console.log(`ℹ️ No ${hookName} hook found`); - } - } catch (error) { - console.log(`❌ Error uninstalling ${hookName} hook: ${error.message}`); - } - } - - console.log('\n🎉 Git hooks uninstalled successfully!'); -} - -function main() { - const command = process.argv[2] || 'pre-commit'; - - switch (command) { - case 'pre-commit': - return preCommitHook(); - case 'post-commit': - return postCommitHook(); - case 'pre-push': - return prePushHook(); - case 'install': - installHooks(); - break; - case 'uninstall': - uninstallHooks(); - break; - case 'help': - console.log(` -Cursor Git Hook Script - -Usage: node .cursor/rules/cursor-git-hook.cjs [command] - -Commands: - pre-commit - Pre-commit hook (translates staged .cursor files) - post-commit - Post-commit hook (checks overall .cursor status) - pre-push - Pre-push hook (blocks push if Russian content found) - install - Install Git hooks - uninstall - Uninstall Git hooks - help - Show this help - -Examples: - node .cursor/rules/cursor-git-hook.cjs pre-commit - node .cursor/rules/cursor-git-hook.cjs install - node .cursor/rules/cursor-git-hook.cjs uninstall - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - const success = main(); - process.exit(success ? 0 : 1); -} - -module.exports = { - preCommitHook, - postCommitHook, - prePushHook, - installHooks, - uninstallHooks -}; \ No newline at end of file diff --git a/.cursor/rules/cursor-manager.cjs b/.cursor/rules/cursor-manager.cjs deleted file mode 100644 index 3566b0c3..00000000 --- a/.cursor/rules/cursor-manager.cjs +++ /dev/null @@ -1,580 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorManager { - constructor() { - this.cursorDir = path.join(process.cwd(), '.cursor', 'rules'); - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - } - - async executeCommand(command, options = {}, args = []) { - console.log(`🚀 Executing: ${command}\n`); - - try { - switch (command) { - case 'audit': - await this.auditCursor(); - break; - case 'fix': - await this.fixCursor(); - break; - case 'optimize': - await this.optimizeCursor(); - break; - case 'full': - await this.fullWorkflow(); - break; - case 'status': - await this.showStatus(); - break; - case 'create-rule': - await this.createRule(options.name, options.content); - break; - case 'export': - await this.exportRules(options.project); - break; - case 'import': - await this.importRules(); - break; - case 'memory-add': - await this.memoryAdd(options.content, options); - break; - case 'memory-update': - await this.memoryUpdate(options); - break; - case 'memory-restore': - await this.memoryRestore(options); - break; - case 'memory-audit': - await this.memoryAudit(); - break; - case 'memory-structure': - await this.memoryStructure(options.type); - break; - case 'memory-report': - await this.memoryReport(options.type); - break; - case 'memory-search': - const query = args[1]; - await this.memorySearch(query, options); - break; - case 'document': - await this.document(options.content, options.type); - break; - default: - console.log(`❌ Unknown command: ${command}`); - this.showHelp(); - } - } catch (error) { - console.error(`❌ Error executing ${command}:`, error.message); - process.exit(1); - } - } - - async auditCursor() { - console.log('🔍 Auditing .cursor rules...\n'); - - const DocumentationHelper = require('./documentation-helper.cjs'); - const helper = new DocumentationHelper(); - - // Проверяем структуру .cursor - const issues = []; - - // Проверяем наличие обязательных файлов - const requiredFiles = [ - 'cursor-export/ai-memory.mdc', - 'doc/documentation-map.mdc', - 'dev/development-principles.mdc' - ]; - - for (const file of requiredFiles) { - const filePath = path.join(this.cursorDir, file); - if (!fs.existsSync(filePath)) { - issues.push(`Missing required file: ${file}`); - } - } - - // Проверяем метаданные в .mdc файлах - const mdcFiles = this.findMdcFiles(); - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - if (!content.includes('description:') || !content.includes('aiPriority:')) { - issues.push(`Missing metadata in: ${path.relative(this.cursorDir, file)}`); - } - } - - if (issues.length === 0) { - console.log('✅ .cursor rules audit passed - no issues found'); - } else { - console.log('❌ Found issues:'); - issues.forEach(issue => console.log(` - ${issue}`)); - } - - return issues; - } - - async fixCursor() { - console.log('🔧 Fixing .cursor rules...\n'); - - const issues = await this.auditCursor(); - - if (issues.length === 0) { - console.log('✅ No fixes needed'); - return; - } - - // Автоматические исправления - for (const issue of issues) { - if (issue.includes('Missing metadata')) { - await this.fixMetadata(issue); - } - } - - console.log('✅ Fixes applied'); - } - - async optimizeCursor() { - console.log('⚡ Optimizing .cursor rules...\n'); - - // Оптимизация структуры - await this.optimizeStructure(); - - // Оптимизация метаданных - await this.optimizeMetadata(); - - console.log('✅ Optimization completed'); - } - - async fullWorkflow() { - console.log('🔄 Running full workflow...\n'); - - await this.auditCursor(); - await this.fixCursor(); - await this.optimizeCursor(); - - console.log('✅ Full workflow completed'); - } - - async showStatus() { - console.log('📊 .cursor rules status:\n'); - - const mdcFiles = this.findMdcFiles(); - const categories = this.getCategories(); - - console.log(`📁 Total .mdc files: ${mdcFiles.length}`); - console.log(`📂 Categories: ${Object.keys(categories).length}`); - - for (const [category, count] of Object.entries(categories)) { - console.log(` - ${category}: ${count} files`); - } - - // Проверяем memory-bank - if (fs.existsSync(this.memoryBankDir)) { - const memoryFiles = this.findMemoryFiles(); - console.log(`\n🧠 Memory bank files: ${memoryFiles.length}`); - } - } - - async createRule(name, content) { - console.log(`📝 Creating rule: ${name}\n`); - - const DocumentationHelper = require('./documentation-helper.cjs'); - const helper = new DocumentationHelper(); - - await helper.documentExperience(content, 'general-practice'); - - console.log(`✅ Rule created: ${name}`); - } - - async exportRules(project = 'default') { - console.log(`📤 Exporting rules to cursor-export...\n`); - - const exportDir = path.join(this.cursorDir, 'cursor-export'); - if (!fs.existsSync(exportDir)) { - fs.mkdirSync(exportDir, { recursive: true }); - } - - const mdcFiles = this.findMdcFiles(); - let exportedCount = 0; - - for (const file of mdcFiles) { - const relativePath = path.relative(this.cursorDir, file); - const exportPath = path.join(exportDir, relativePath); - - const dir = path.dirname(exportPath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - fs.copyFileSync(file, exportPath); - exportedCount++; - } - - // Создаем индекс экспорта - const exportIndex = this.generateExportIndex(mdcFiles, project); - const indexPath = path.join(exportDir, 'EXPORT_INDEX.md'); - fs.writeFileSync(indexPath, exportIndex); - - console.log(`✅ Exported ${exportedCount} rules to cursor-export`); - } - - async importRules() { - console.log('📥 Importing rules from cursor-export...\n'); - - const exportDir = path.join(this.cursorDir, 'cursor-export'); - if (!fs.existsSync(exportDir)) { - console.log('❌ No cursor-export directory found'); - return; - } - - const exportedFiles = this.findExportedFiles(); - let importedCount = 0; - - for (const file of exportedFiles) { - const relativePath = path.relative(exportDir, file); - const importPath = path.join(this.cursorDir, relativePath); - - const dir = path.dirname(importPath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - fs.copyFileSync(file, importPath); - importedCount++; - } - - console.log(`✅ Imported ${importedCount} rules from cursor-export`); - } - - // Memory Bank Management - - async memoryAdd(content, options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - await manager.addEntry(content, options); - } - - async memoryUpdate(options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - await manager.updateContext(options); - } - - async memoryRestore(options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - const context = await manager.restoreContext(options); - console.log(JSON.stringify(context, null, 2)); - } - - async memoryAudit() { - const MemoryBankAuditor = require('./memory-bank-auditor.cjs'); - const auditor = new MemoryBankAuditor(); - - await auditor.audit(); - } - - async memoryStructure(type = 'react-typescript') { - const MemoryBankStructureCreator = require('./memory-bank-structure-creator.cjs'); - const creator = new MemoryBankStructureCreator(); - - await creator.createStructure(type); - } - - async memoryReport(type = 'full') { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - const report = await manager.generateReport({ type }); - console.log(JSON.stringify(report, null, 2)); - } - - async memorySearch(query, options = {}) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - const results = await manager.searchMemory(query, options); - console.log(JSON.stringify(results, null, 2)); - } - - async document(content, type = 'auto') { - const DocumentationHelper = require('./documentation-helper.cjs'); - const helper = new DocumentationHelper(); - - await helper.documentExperience(content, type); - } - - // Вспомогательные методы - - findMdcFiles() { - const files = []; - - function scanDir(dir) { - if (!fs.existsSync(dir)) return; - - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - scanDir(fullPath); - } else if (item.endsWith('.mdc')) { - files.push(fullPath); - } - } - } - - scanDir(this.cursorDir); - return files; - } - - findMemoryFiles() { - const files = []; - - function scanDir(dir) { - if (!fs.existsSync(dir)) return; - - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - scanDir(fullPath); - } else if (item.endsWith('.md')) { - files.push(fullPath); - } - } - } - - scanDir(this.memoryBankDir); - return files; - } - - findExportedFiles() { - const exportDir = path.join(this.cursorDir, 'cursor-export'); - const files = []; - - function scanDir(dir) { - if (!fs.existsSync(dir)) return; - - const items = fs.readdirSync(dir); - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - scanDir(fullPath); - } else if (item.endsWith('.mdc')) { - files.push(fullPath); - } - } - } - - scanDir(exportDir); - return files; - } - - getCategories() { - const categories = {}; - const mdcFiles = this.findMdcFiles(); - - for (const file of mdcFiles) { - const relativePath = path.relative(this.cursorDir, file); - const category = relativePath.split(path.sep)[0]; - categories[category] = (categories[category] || 0) + 1; - } - - return categories; - } - - async fixMetadata(issue) { - const filePath = issue.match(/in: (.+)/)[1]; - const fullPath = path.join(this.cursorDir, filePath); - - if (!fs.existsSync(fullPath)) return; - - const content = fs.readFileSync(fullPath, 'utf8'); - - // Добавляем базовые метаданные если их нет - if (!content.includes('description:')) { - const newContent = content.replace( - /^---\n/, - `--- -description: Auto-generated rule -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: general ---- -` - ); - fs.writeFileSync(fullPath, newContent); - } - } - - async optimizeStructure() { - // Оптимизация структуры каталогов - const categories = ['dev', 'doc', 'ui', 'workflow', 'architecture', 'security', 'plugin']; - - for (const category of categories) { - const categoryDir = path.join(this.cursorDir, category); - if (!fs.existsSync(categoryDir)) { - fs.mkdirSync(categoryDir, { recursive: true }); - } - } - } - - async optimizeMetadata() { - const mdcFiles = this.findMdcFiles(); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - - // Оптимизируем метаданные - let optimized = content; - - // Добавляем aiPriority если нет - if (!content.includes('aiPriority:')) { - optimized = optimized.replace( - /^---\n/, - `--- -aiPriority: normal -` - ); - } - - // Добавляем aiCategory если нет - if (!content.includes('aiCategory:')) { - optimized = optimized.replace( - /^---\n/, - `--- -aiCategory: general -` - ); - } - - if (optimized !== content) { - fs.writeFileSync(file, optimized); - } - } - } - - generateExportIndex(files, project) { - const date = new Date().toISOString().split('T')[0]; - - let index = `# Cursor Rules Export - ${project} - -**Exported:** ${date} -**Total files:** ${files.length} - -## Files - -`; - - const categories = {}; - for (const file of files) { - const relativePath = path.relative(this.cursorDir, file); - const category = relativePath.split(path.sep)[0]; - - if (!categories[category]) { - categories[category] = []; - } - categories[category].push(relativePath); - } - - for (const [category, categoryFiles] of Object.entries(categories)) { - index += `### ${category}\n`; - for (const file of categoryFiles) { - index += `- [${file}](./${file})\n`; - } - index += '\n'; - } - - return index; - } - - showHelp() { - console.log(` -Cursor Manager - CLI Tool - -Usage: - node cursor-manager.cjs [options] - -Commands: - audit - Audit .cursor rules - fix - Fix .cursor issues - optimize - Optimize .cursor rules - full - Run full workflow (audit + fix + optimize) - status - Show .cursor status - create-rule - Create new rule - export [project] - Export rules to cursor-export - import - Import rules from cursor-export - -Memory Bank Commands: - memory-add [options] - Add entry to memory-bank - memory-update [options] - Update active context - memory-restore [options] - Restore context from memory-bank - memory-audit - Audit memory-bank structure - memory-structure - Create memory-bank structure - memory-report [type] - Generate memory-bank report - -Documentation Commands: - document [type] - Document experience - -Options: - --type= Entry type (error, decision, progress, etc.) - --category= Category (core, errors, architecture, etc.) - --priority= Priority (critical, high, medium, low) - --tags= Comma-separated tags - --full Full context restoration - --content= Content for rule or entry - `); - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - if (args.length === 0) { - const manager = new CursorManager(); - manager.showHelp(); - process.exit(1); - } - - const command = args[0]; - const options = parseOptions(args.slice(1)); - - const manager = new CursorManager(); - await manager.executeCommand(command, options, args); -} - -function parseOptions(args) { - const options = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - options[key] = value; - } else if (!options.name) { - options.name = arg; - } else if (!options.content) { - options.content = arg; - } - } - - return options; -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorManager; \ No newline at end of file diff --git a/.cursor/rules/cursor-protector.cjs b/.cursor/rules/cursor-protector.cjs deleted file mode 100644 index d7f7548e..00000000 --- a/.cursor/rules/cursor-protector.cjs +++ /dev/null @@ -1,726 +0,0 @@ -#!/usr/bin/env node - -/** - * Cursor Protector Script - * Automatically protects and translates all .cursor files to English - * Usage: node .cursor/rules/cursor-protector.cjs [protect|translate|check|restore] - */ - -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); - -// File paths -const CURSOR_DIR = path.join(process.cwd(), '.cursor'); -const BACKUP_DIR = path.join(process.cwd(), '.cursor', 'backup'); - -// Translation mappings for .cursor files -const cursorTranslations = { - // Common headers and titles - 'Описание': 'Description', - 'Назначение': 'Purpose', - 'Использование': 'Usage', - 'Примеры': 'Examples', - 'Примечания': 'Notes', - 'Важно': 'Important', - 'Внимание': 'Warning', - 'Совет': 'Tip', - 'Замечание': 'Note', - - // File structure - 'Структура файлов': 'File Structure', - 'Структура проекта': 'Project Structure', - 'Организация файлов': 'File Organization', - 'Иерархия папок': 'Folder Hierarchy', - - // Development terms - 'Разработка': 'Development', - 'Программирование': 'Programming', - 'Кодирование': 'Coding', - 'Отладка': 'Debugging', - 'Тестирование': 'Testing', - 'Сборка': 'Build', - 'Развертывание': 'Deployment', - 'Версионирование': 'Versioning', - - // Architecture terms - 'Архитектура': 'Architecture', - 'Дизайн': 'Design', - 'Паттерны': 'Patterns', - 'Принципы': 'Principles', - 'Методологии': 'Methodologies', - 'Фреймворки': 'Frameworks', - 'Библиотеки': 'Libraries', - - // Quality and standards - 'Качество': 'Quality', - 'Стандарты': 'Standards', - 'Лучшие практики': 'Best Practices', - 'Рекомендации': 'Recommendations', - 'Требования': 'Requirements', - 'Ограничения': 'Constraints', - - // Security and safety - 'Безопасность': 'Security', - 'Защита': 'Protection', - 'Валидация': 'Validation', - 'Аутентификация': 'Authentication', - 'Авторизация': 'Authorization', - 'Шифрование': 'Encryption', - - // Performance and optimization - 'Производительность': 'Performance', - 'Оптимизация': 'Optimization', - 'Кэширование': 'Caching', - 'Масштабирование': 'Scaling', - 'Мониторинг': 'Monitoring', - 'Логирование': 'Logging', - - // User experience - 'Пользовательский опыт': 'User Experience', - 'Интерфейс': 'Interface', - 'Доступность': 'Accessibility', - 'Удобство использования': 'Usability', - 'Отзывчивость': 'Responsiveness', - 'Интерактивность': 'Interactivity', - - // Project management - 'Управление проектом': 'Project Management', - 'Планирование': 'Planning', - 'Контроль': 'Control', - 'Мониторинг': 'Monitoring', - 'Отчетность': 'Reporting', - 'Документирование': 'Documentation', - - // Communication - 'Коммуникация': 'Communication', - 'Сотрудничество': 'Collaboration', - 'Координация': 'Coordination', - 'Синхронизация': 'Synchronization', - 'Интеграция': 'Integration', - - // Error handling - 'Обработка ошибок': 'Error Handling', - 'Исключения': 'Exceptions', - 'Восстановление': 'Recovery', - 'Отказоустойчивость': 'Fault Tolerance', - 'Graceful degradation': 'Graceful Degradation', - - // Data and storage - 'Данные': 'Data', - 'Хранилище': 'Storage', - 'База данных': 'Database', - 'Кэш': 'Cache', - 'Резервное копирование': 'Backup', - 'Синхронизация данных': 'Data Synchronization', - - // API and interfaces - 'API': 'API', - 'Интерфейсы': 'Interfaces', - 'Эндпоинты': 'Endpoints', - 'Запросы': 'Requests', - 'Ответы': 'Responses', - 'Сериализация': 'Serialization', - - // Testing and validation - 'Тестирование': 'Testing', - 'Валидация': 'Validation', - 'Проверка': 'Verification', - 'Unit тесты': 'Unit Tests', - 'Integration тесты': 'Integration Tests', - 'E2E тесты': 'E2E Tests', - - // Configuration and settings - 'Конфигурация': 'Configuration', - 'Настройки': 'Settings', - 'Параметры': 'Parameters', - 'Переменные окружения': 'Environment Variables', - 'Конфигурационные файлы': 'Configuration Files', - - // Dependencies and packages - 'Зависимости': 'Dependencies', - 'Пакеты': 'Packages', - 'Модули': 'Modules', - 'Библиотеки': 'Libraries', - 'Плагины': 'Plugins', - 'Расширения': 'Extensions', - - // Version control and deployment - 'Система контроля версий': 'Version Control System', - 'Репозиторий': 'Repository', - 'Ветки': 'Branches', - 'Коммиты': 'Commits', - 'Слияние': 'Merge', - 'Развертывание': 'Deployment', - - // Documentation - 'Документация': 'Documentation', - 'Руководства': 'Guides', - 'Справочники': 'References', - 'Примеры кода': 'Code Examples', - 'Комментарии': 'Comments', - 'README': 'README', - - // Internationalization - 'Интернационализация': 'Internationalization', - 'Локализация': 'Localization', - 'Перевод': 'Translation', - 'Многоязычность': 'Multilingual', - 'Языковые файлы': 'Language Files', - - // Accessibility - 'Доступность': 'Accessibility', - 'Скринридеры': 'Screen Readers', - 'Клавиатурная навигация': 'Keyboard Navigation', - 'Контрастность': 'Contrast', - 'Размер шрифта': 'Font Size', - - // Mobile and responsive - 'Мобильные устройства': 'Mobile Devices', - 'Адаптивность': 'Responsiveness', - 'Touch интерфейс': 'Touch Interface', - 'Мобильная оптимизация': 'Mobile Optimization', - - // Browser and platform - 'Браузер': 'Browser', - 'Платформа': 'Platform', - 'Совместимость': 'Compatibility', - 'Кроссбраузерность': 'Cross-browser', - 'Кроссплатформенность': 'Cross-platform', - - // Common phrases - 'Важно помнить': 'Important to remember', - 'Следует отметить': 'It should be noted', - 'Необходимо учитывать': 'Must be considered', - 'Рекомендуется': 'Recommended', - 'Обязательно': 'Required', - 'Опционально': 'Optional', - 'По умолчанию': 'Default', - 'Настройка по умолчанию': 'Default setting', - 'См.': 'See', - 'политика': 'policy', - 'параметры': 'parameters', - 'операционной': 'operational', - 'тестовой': 'test', - 'среды': 'environment', - 'доступные': 'available', - 'автоматизации': 'automation', - 'диагностики': 'diagnostics', - 'полного': 'full', - 'доступа': 'access', - 'ассистента': 'assistant', - 'ко': 'to', - 'всем': 'all', - 'файлам': 'files', - 'проекта': 'project', - 'и': 'and', - 'проактивных': 'proactive', - 'действий': 'actions', - 'operational': 'operational', - 'test': 'test', - 'environment': 'environment', - 'available': 'available', - 'для': 'for', - 'автоматизации': 'automation', - 'диагностики': 'diagnostics', - - // Status and states - 'Статус': 'Status', - 'Состояние': 'State', - 'Активный': 'Active', - 'Неактивный': 'Inactive', - 'Включен': 'Enabled', - 'Выключен': 'Disabled', - 'Загружается': 'Loading', - 'Завершено': 'Completed', - 'В процессе': 'In Progress', - 'Ожидает': 'Pending', - 'Ошибка': 'Error', - 'Успешно': 'Success', - - // Actions and operations - 'Действие': 'Action', - 'Операция': 'Operation', - 'Функция': 'Function', - 'Метод': 'Method', - 'Процедура': 'Procedure', - 'Алгоритм': 'Algorithm', - 'Создать': 'Create', - 'Удалить': 'Delete', - 'Обновить': 'Update', - 'Изменить': 'Modify', - 'Добавить': 'Add', - 'Убрать': 'Remove', - 'Проверить': 'Check', - 'Валидировать': 'Validate', - 'Подтвердить': 'Confirm', - 'Отменить': 'Cancel', - 'Сохранить': 'Save', - 'Загрузить': 'Load', - 'Экспортировать': 'Export', - 'Импортировать': 'Import', - - // Time and dates - 'Время': 'Time', - 'Дата': 'Date', - 'Период': 'Period', - 'Интервал': 'Interval', - 'Частота': 'Frequency', - 'Ежедневно': 'Daily', - 'Еженедельно': 'Weekly', - 'Ежемесячно': 'Monthly', - 'Ежегодно': 'Yearly', - - // Size and quantity - 'Размер': 'Size', - 'Количество': 'Quantity', - 'Объем': 'Volume', - 'Максимум': 'Maximum', - 'Минимум': 'Minimum', - 'Ограничение': 'Limit', - 'Порог': 'Threshold', - - // Priority and importance - 'Приоритет': 'Priority', - 'Важность': 'Importance', - 'Критично': 'Critical', - 'Высоко': 'High', - 'Средне': 'Medium', - 'Низко': 'Low', - 'Срочно': 'Urgent', - 'Нормально': 'Normal', - - // Categories and types - 'Категория': 'Category', - 'Тип': 'Type', - 'Класс': 'Class', - 'Группа': 'Group', - 'Семейство': 'Family', - 'Набор': 'Set', - 'Коллекция': 'Collection', - - // Common technical terms - 'Интерфейс': 'Interface', - 'Абстракция': 'Abstraction', - 'Инкапсуляция': 'Encapsulation', - 'Наследование': 'Inheritance', - 'Полиморфизм': 'Polymorphism', - 'Сериализация': 'Serialization', - 'Десериализация': 'Deserialization', - 'Кэширование': 'Caching', - 'Буферизация': 'Buffering', - 'Синхронизация': 'Synchronization', - 'Асинхронность': 'Asynchrony', - 'Потоки': 'Threads', - 'Процессы': 'Processes', - 'Память': 'Memory', - 'Процессор': 'Processor', - 'Сеть': 'Network', - 'Файловая система': 'File System', - 'База данных': 'Database', - 'Сервер': 'Server', - 'Клиент': 'Client', - - // Web development - 'Веб-разработка': 'Web Development', - 'Фронтенд': 'Frontend', - 'Бэкенд': 'Backend', - 'Full-stack': 'Full-stack', - 'HTML': 'HTML', - 'CSS': 'CSS', - 'JavaScript': 'JavaScript', - 'TypeScript': 'TypeScript', - 'React': 'React', - 'Vue': 'Vue', - 'Angular': 'Angular', - 'Node.js': 'Node.js', - 'Express': 'Express', - 'MongoDB': 'MongoDB', - 'PostgreSQL': 'PostgreSQL', - 'MySQL': 'MySQL', - 'REST API': 'REST API', - 'GraphQL': 'GraphQL', - 'WebSocket': 'WebSocket', - 'HTTP': 'HTTP', - 'HTTPS': 'HTTPS', - 'SSL': 'SSL', - 'TLS': 'TLS', - 'CORS': 'CORS', - 'JWT': 'JWT', - 'OAuth': 'OAuth', - - // Development tools - 'Инструменты разработки': 'Development Tools', - 'IDE': 'IDE', - 'Редактор кода': 'Code Editor', - 'Отладчик': 'Debugger', - 'Профилировщик': 'Profiler', - 'Линтер': 'Linter', - 'Форматтер': 'Formatter', - 'Сборщик': 'Bundler', - 'Транспайлер': 'Transpiler', - 'Менеджер пакетов': 'Package Manager', - 'Система контроля версий': 'Version Control System', - 'CI/CD': 'CI/CD', - 'Docker': 'Docker', - 'Kubernetes': 'Kubernetes', - - // Common file extensions and formats - '.js': '.js', - '.ts': '.ts', - '.jsx': '.jsx', - '.tsx': '.tsx', - '.json': '.json', - '.xml': '.xml', - '.yaml': '.yaml', - '.yml': '.yml', - '.md': '.md', - '.mdc': '.mdc', - '.html': '.html', - '.css': '.css', - '.scss': '.scss', - '.sass': '.sass', - '.less': '.less', - '.png': '.png', - '.jpg': '.jpg', - '.jpeg': '.jpeg', - '.gif': '.gif', - '.svg': '.svg', - '.ico': '.ico', - '.woff': '.woff', - '.woff2': '.woff2', - '.ttf': '.ttf', - '.eot': '.eot', - - // Common directories - 'src': 'src', - 'dist': 'dist', - 'build': 'build', - 'public': 'public', - 'assets': 'assets', - 'components': 'components', - 'pages': 'pages', - 'utils': 'utils', - 'helpers': 'helpers', - 'services': 'services', - 'models': 'models', - 'controllers': 'controllers', - 'middleware': 'middleware', - 'routes': 'routes', - 'config': 'config', - 'tests': 'tests', - 'docs': 'docs', - 'examples': 'examples', - 'templates': 'templates', - 'layouts': 'layouts', - 'styles': 'styles', - 'scripts': 'scripts', - 'images': 'images', - 'fonts': 'fonts', - 'icons': 'icons', - 'data': 'data', - 'logs': 'logs', - 'temp': 'temp', - 'cache': 'cache', - 'backup': 'backup', - - // Common patterns - 'Singleton': 'Singleton', - 'Factory': 'Factory', - 'Observer': 'Observer', - 'Strategy': 'Strategy', - 'Command': 'Command', - 'Adapter': 'Adapter', - 'Decorator': 'Decorator', - 'Proxy': 'Proxy', - 'Facade': 'Facade', - 'Bridge': 'Bridge', - 'Composite': 'Composite', - 'Flyweight': 'Flyweight', - 'Template Method': 'Template Method', - 'Chain of Responsibility': 'Chain of Responsibility', - 'State': 'State', - 'Visitor': 'Visitor', - 'Iterator': 'Iterator', - 'Mediator': 'Mediator', - 'Memento': 'Memento', - 'Interpreter': 'Interpreter', - - // Common principles - 'SOLID': 'SOLID', - 'DRY': 'DRY', - 'KISS': 'KISS', - 'YAGNI': 'YAGNI', - 'Single Responsibility': 'Single Responsibility', - 'Open/Closed': 'Open/Closed', - 'Liskov Substitution': 'Liskov Substitution', - 'Interface Segregation': 'Interface Segregation', - 'Dependency Inversion': 'Dependency Inversion', - 'Don\'t Repeat Yourself': 'Don\'t Repeat Yourself', - 'Keep It Simple, Stupid': 'Keep It Simple, Stupid', - 'You Aren\'t Gonna Need It': 'You Aren\'t Gonna Need It' -}; - -function translateText(text) { - let translatedText = text; - - // Apply translations - for (const [russian, english] of Object.entries(cursorTranslations)) { - translatedText = translatedText.replace(new RegExp(russian, 'gi'), english); - } - - return translatedText; -} - -function createBackup(filePath) { - if (!fs.existsSync(BACKUP_DIR)) { - fs.mkdirSync(BACKUP_DIR, { recursive: true }); - } - - const relativePath = path.relative(CURSOR_DIR, filePath); - const backupPath = path.join(BACKUP_DIR, relativePath); - const backupDir = path.dirname(backupPath); - - if (!fs.existsSync(backupDir)) { - fs.mkdirSync(backupDir, { recursive: true }); - } - - fs.copyFileSync(filePath, backupPath); - return backupPath; -} - -function translateFile(filePath) { - try { - const content = fs.readFileSync(filePath, 'utf8'); - const translatedContent = translateText(content); - - // Create backup before translation - const backupPath = createBackup(filePath); - - // Write translated content - fs.writeFileSync(filePath, translatedContent, 'utf8'); - - return { - success: true, - backupPath, - originalSize: content.length, - translatedSize: translatedContent.length - }; - } catch (error) { - return { - success: false, - error: error.message - }; - } -} - -function findCursorFiles() { - const files = []; - - function scanDirectory(dir) { - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - // Skip backup directory - if (item !== 'backup') { - scanDirectory(fullPath); - } - } else if (stat.isFile()) { - // Only process markdown files - if (item.endsWith('.md') || item.endsWith('.mdc')) { - files.push(fullPath); - } - } - } - } - - scanDirectory(CURSOR_DIR); - return files; -} - -function protectCursor() { - console.log('🛡️ Protecting .cursor directory - translating all files to English...\n'); - - const files = findCursorFiles(); - console.log(`📁 Found ${files.length} files to process\n`); - - let successCount = 0; - let errorCount = 0; - const results = []; - - for (const filePath of files) { - const relativePath = path.relative(process.cwd(), filePath); - console.log(`🔄 Processing: ${relativePath}`); - - const result = translateFile(filePath); - results.push({ filePath, ...result }); - - if (result.success) { - console.log(`✅ Translated: ${relativePath}`); - console.log(`📁 Backup: ${path.relative(process.cwd(), result.backupPath)}`); - successCount++; - } else { - console.log(`❌ Error: ${relativePath} - ${result.error}`); - errorCount++; - } - } - - console.log(`\n📊 Summary:`); - console.log(`✅ Successfully translated: ${successCount} files`); - console.log(`❌ Errors: ${errorCount} files`); - console.log(`📁 Backups created in: .cursor/backup/`); - - return { successCount, errorCount, results }; -} - -function checkCursorStatus() { - console.log('🔍 Checking .cursor directory status...\n'); - - const files = findCursorFiles(); - console.log(`📁 Found ${files.length} files in .cursor directory\n`); - - let russianContentCount = 0; - const filesWithRussian = []; - - for (const filePath of files) { - try { - const content = fs.readFileSync(filePath, 'utf8'); - const hasRussian = /[а-яё]/i.test(content); - - if (hasRussian) { - russianContentCount++; - const relativePath = path.relative(process.cwd(), filePath); - filesWithRussian.push(relativePath); - } - } catch (error) { - console.log(`❌ Error reading ${filePath}: ${error.message}`); - } - } - - console.log(`📊 Status Report:`); - console.log(`📁 Total files: ${files.length}`); - console.log(`🇷🇺 Files with Russian content: ${russianContentCount}`); - console.log(`🇺🇸 Files in English only: ${files.length - russianContentCount}`); - - if (filesWithRussian.length > 0) { - console.log(`\n📋 Files that need translation:`); - filesWithRussian.forEach(file => console.log(` - ${file}`)); - } else { - console.log(`\n✅ All files are already in English!`); - } - - return { totalFiles: files.length, russianContentCount, filesWithRussian }; -} - -function restoreFromBackup(backupPath) { - if (!fs.existsSync(backupPath)) { - console.log(`❌ Backup not found: ${backupPath}`); - return false; - } - - try { - const relativePath = path.relative(BACKUP_DIR, backupPath); - const originalPath = path.join(CURSOR_DIR, relativePath); - - fs.copyFileSync(backupPath, originalPath); - console.log(`✅ Restored: ${path.relative(process.cwd(), originalPath)} from backup`); - return true; - } catch (error) { - console.log(`❌ Error restoring ${backupPath}: ${error.message}`); - return false; - } -} - -function listBackups() { - if (!fs.existsSync(BACKUP_DIR)) { - console.log('❌ No backups found'); - return; - } - - console.log('📁 Available backups:'); - - function scanBackups(dir, prefix = '') { - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - console.log(`${prefix}📁 ${item}/`); - scanBackups(fullPath, prefix + ' '); - } else { - const stats = fs.statSync(fullPath); - console.log(`${prefix}📄 ${item} (${stats.mtime.toLocaleString()})`); - } - } - } - - scanBackups(BACKUP_DIR); -} - -function main() { - const command = process.argv[2] || 'protect'; - const backupPath = process.argv[3]; - - switch (command) { - case 'protect': - protectCursor(); - break; - case 'check': - checkCursorStatus(); - break; - case 'restore': - if (!backupPath) { - console.log('❌ Please specify backup path'); - console.log('Usage: node .cursor/rules/cursor-protector.cjs restore '); - return; - } - restoreFromBackup(backupPath); - break; - case 'list': - listBackups(); - break; - case 'help': - console.log(` -Cursor Protector Script - -Usage: node .cursor/rules/cursor-protector.cjs [command] [backup-path] - -Commands: - protect - Protect .cursor by translating all files to English (default) - check - Check status of .cursor files (which need translation) - restore - Restore file from backup (requires backup path) - list - List available backups - help - Show this help - -Examples: - node .cursor/rules/cursor-protector.cjs protect - node .cursor/rules/cursor-protector.cjs check - node .cursor/rules/cursor-protector.cjs restore .cursor/backup/rules/ai-memory.mdc - node .cursor/rules/cursor-protector.cjs list - `); - break; - default: - console.log(`❌ Unknown command: ${command}`); - console.log('Use "help" to see available commands'); - process.exit(1); - } -} - -if (require.main === module) { - main(); -} - -module.exports = { - protectCursor, - checkCursorStatus, - translateText, - cursorTranslations, - createBackup, - restoreFromBackup -}; \ No newline at end of file diff --git a/.cursor/rules/dev/date-time-patterns.mdc b/.cursor/rules/dev/date-time-patterns.mdc deleted file mode 100644 index b23fd298..00000000 --- a/.cursor/rules/dev/date-time-patterns.mdc +++ /dev/null @@ -1,170 +0,0 @@ ---- -description: Date and time patterns for AI assista.ts - universal guIDElines for handling dates -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Patterns работы с Dateми и временем - -## Проблема -AI-ассистенты не могут самостоятельно получать актуальную дату и Time, что приводит к использованию устаревших дат в документации. - -## Решение: Usage системных команд - -### Получение текущей даты -```bash -# Текущая Date в формате YYYY-MM-DD -date +"%Y-%m-%d" - -# Текущая Date и Time -date +"%Y-%m-%d %H:%M:%S" - -# Текущая Date на русском языке -date +"%d %B %Y" -``` - -### Получение времени -```bash -# Только Time -date +"%H:%M:%S" - -# Time с часовым поясом -date +"%H:%M:%S %Z" -``` - -## Patterns для AI-ассистентов - -### 1. При создании документации -```bash -# Всегда получать актуальную дату перед созданием документации -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" -``` - -### 2. При обновлении существующей документации -```bash -# Проверять, не устарели ли даты в документации -# Если Date старая - обновлять с актуальной -``` - -### 3. При создании коммитов -```bash -# Использовать актуальную дату в сообщениях коммитов -git commit -m "feat: new feature - $(date +"%Y-%m-%d")" -``` - -## Форматы дат для разных целей - -### Documentation (YYYY-MM-DD) -- `2025-07-12` - стандартный формат для документации -- Используется в memory-bank, README, технической документации - -### Comm.ts (YYYY-MM-DD) -- `2025-07-12` - для сообщений коммитов -- Краткий и понятный формат - -### Пользовательский Interface -- `12 июля 2025` - для пользовательского Interfaceа -- Локализованный формат - -### Logging -- `2025-07-12 14:30:25` - полный формат с временем -- Для системных логов и отладки - -## Проверочный список - -### ✅ Перед созданием документации -- [ ] Получить актуальную дату командой `date +"%Y-%m-%d"` -- [ ] Использовать полученную дату в документации -- [ ] Check формат даты (YYYY-MM-DD) - -### ✅ При обновлении документации -- [ ] Check даты в существующих файлах -- [ ] Update устаревшие даты -- [ ] Убедиться в консистентности форматов - -### ✅ При работе с пользователем -- [ ] Уточнить дату, если есть сомнения -- [ ] Использовать понятные пользователю форматы -- [ ] Объяснить, почему используется системная Date - -## examples использования - -### Создание нового этапа в progress.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### ✅ Этап X: Description (Завершен - $current_date)" -``` - -### Обновление Statusа в errors.md -```bash -current_date=$(date +"%Y-%m-%d") -echo "### Status" -echo "✅ **РЕШЕНО** - $current_date" -``` - -### Создание коммита с датой -```bash -current_date=$(date +"%Y-%m-%d") -git commit -m "feat: new feature - $current_date" -``` - -## Частые ошибки - -### ❌ Неправильно -- Usage устаревших дат из контекста -- Разные форматы дат в одном документе -- Отсутствие проверки актуальности дат - -### ✅ Правильно -- Usage `date` команды для получения актуальной даты -- Консистентный формат YYYY-MM-DD -- Регулярная Verification и обновление дат - -## Integration с Cursor IDE - -### Project Rules -Add в project rules: -``` -- Always use system date Commands for current date/time -- Format dates as YYYY-MM-DD in documentation -- Validate dates before using in documentation -``` - -### Saved Memories -Create memory: -``` -Date/Time Pattern: Use 'date +"%Y-%m-%d"' Command to get current date -Format: Always use YYYY-MM-DD format in documentation -Validation: Check dates before using in documentation -``` - -## Автоматизация - -### Скрипт для проверки дат -```bash -#!/bin/bash -# check-dates.sh -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -# Verification дат в документации -grep -r "2024-" docs/ memory-bank/ 2>/dev/null | head -5 -echo "Found old dates above. ConsIDEr updating them." -``` - -### Git hook для проверки дат -```bash -#!/bin/bash -# .git/hooks/pre-commit -current_date=$(date +"%Y-%m-%d") -echo "Current date: $current_date" - -## New Entry ([2025-07-19T01:32:19.979Z]) - -Паттерн работы с Dateми: Использовать date +%Y-%m-%d для получения текущей даты в документации - -echo "Please ensure all dates in documentation are current." -``` \ No newline at end of file diff --git a/.cursor/rules/dev/development-guidelines.mdc b/.cursor/rules/dev/development-guidelines.mdc deleted file mode 100644 index bbd7f624..00000000 --- a/.cursor/rules/dev/development-guidelines.mdc +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: Development best practices and guIDElines -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: development-practices ---- - -# Development GuIDElines - -## Overview - -This file contains dev practice guIDElines and best practices. - -## GuIDElines - - - -## Development Practice ([2025-07-19T01:19:31.302Z]) - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы - - diff --git a/.cursor/rules/dev/development-principles.mdc b/.cursor/rules/dev/development-principles.mdc deleted file mode 100644 index 8ea7328f..00000000 --- a/.cursor/rules/dev/development-principles.mdc +++ /dev/null @@ -1,65 +0,0 @@ ---- -description: Development principles for all proje.ts - universal guIDElines and best practices -globs: ["**/*"] -alwaysApply: true -aiPriority: critical -aiCategory: development-practices ---- - -# 🛡️ Principles разработки - -## Краткая сводка (для быстрого понимания) - -### 🔒 Security и надёжность -1. **"Не навреди"** - Security прежде всего, обратная Compatibility -2. **"Fail Fast, Fail Safe"** - быстрый и безопасный отказ, Graceful Degradation -3. **"data Integrity & Privacy"** - целостность и приватность данных - -### 📊 Наблюдаемость и Quality -4. **"Observability First"** - структурированное Logging, метрики, Monitoring -5. **"Best Practices First"** - проверенные Patterns, Standards, Performance -6. **"configuration as Code"** - Versioning конфигураций, Validation - -### 🚀 User Experience -7. **"Progressive Enhancement"** - базовая функциональность всегда работает -8. **"Continuous Learning"** - Monitoring, обратная связь, A/B Testing - -### 🌐 Экосистема и развитие -9. **"Ecosystem Thinking"** - Compatibility плагинов, стабильность API -10. **"Comme.ts для ИИ"** - аналитические Comme.ts для понимания логики - -## Применение принципов - -### Для каждой задачи ИИ-ассистент должен: -1. **Проанализировать** задачу через призму релевантных принципов -2. **Выбрать** наиболее подходящие Principles для конкретной ситуации -3. **Применить** Principles систематически при принятии решений -4. **Документировать** как Principles повлияли на решение - -### examples применения: -- **Новый плагин** → Principles 1, 2, 5, 6, 9, 10 -- **Исправление бага** → Principles 1, 2, 4, 5, 7 -- **Optimization** → Principles 2, 4, 5, 6, 7, 8 -- **Новая Function** → Principles 1, 3, 5, 7, 8, 9 - -## Взаимодополняемость принципов - -Principles не противоречат друг другу, а усиливают: -- **"Не навреди" + "Fail Fast"** = безопасные быстрые решения -- **"Best Practices" + "Observability"** = качественные решения с Monitoringом -- **"Progressive Enhancement" + "Ecosystem"** = стабильная экосистема -- **"data Privacy" + "configuration"** = безопасная configuration - -## Результат применения - -Следование всем принципам обеспечивает: -- ✅ **Highе Quality кода** и архитектуры -- ✅ **Security** и надёжность системы -- ✅ **Лучший User Experience** - -## New Entry ([2025-07-19T01:32:10.564Z]) - -Принцип разработки: Всегда использовать системные команды для получения актуальной даты - -- ✅ **Масштабируемость** и поддерживаемость -- ✅ **Успешную экосистему** плагинов \ No newline at end of file diff --git a/.cursor/rules/dev/testing-troubleshooting.mdc b/.cursor/rules/dev/testing-troubleshooting.mdc deleted file mode 100644 index d70c8c0c..00000000 --- a/.cursor/rules/dev/testing-troubleshooting.mdc +++ /dev/null @@ -1,66 +0,0 @@ ---- -description: Testing and debugging standards -globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"] -alwaysApply: falseaiPriority: normal -aiCategory: development-practices ---- - -# Testing & Troubleshooting Best Practices - -## Documentation тестирования и отладки - -### Ключевые Principles: -- Писать пошаговые гайды для всех основных flows тестирования и отладки -- Разделять usage, troubleshooting и advanced diagnostics в отдельные файлы -- Cross-link гайды для навигации (usage → troubleshooting → advanced) -- Использовать четкие, воспроизводимые шаги и code snipp.ts -- Ссылаться на релевантные скрипты, автоматизацию и best practices -- Обновлять гайды при изменении features или workflows - -### examples гайдов: -- DevTools Testing GuIDE -- DevTools Panel Troubleshooting -- DevTools Panel Usage - -## Анализ внешних репозиториев как submodules - -Для анализа, поиска и отслеживания изменений во внешнем git репозитории: - -### Пошагово: -1. В корне монорепо: - ```bash - git submodule add external/ - git submodule update --init --recursive - ``` - - Пример: - ```bash - git submodule add HTTPS://github.com/QizhengMo/chrome-extension-sIDEpanel-template.git external/sIDEpanel-template - git submodule update --init --recursive - ``` - -2. Открыть монорепо в Cursor/VSCode. Submodule будет проиндексирован и доступен для поиска кода, навигации и анализа. - -3. Update submodule до последней версии: - ```bash - cd external/sIDEpanel-template - git pull origin main - ``` - -4. Delete submodule (если больше не нужен): - ```bash - git submodule deinit -f external/sIDEpanel-template - git rm -f external/sIDEpanel-template - rm -rf .git/modules/external/sIDEpanel-template - ``` - -### Преимущества: -- Изолирует внешний код от основной git истории -- Позволяет легкие обновления и независимое Versioning -- Обеспечивает полный поиск кода и анализ в Cursor/VSCode - -**Используйте этот Method для любого внешнего кодового база, который хотите анализировать или ссылаться в монорепо.** -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/ai-answer-self-check.mdc b/.cursor/rules/doc/ai-answer-self-check.mdc deleted file mode 100644 index a1409f4f..00000000 --- a/.cursor/rules/doc/ai-answer-self-check.mdc +++ /dev/null @@ -1,39 +0,0 @@ - - - - ---- -description: ai answer self check rule -globs: ["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"] -alwaysApply: falseaiPriority: medium -aiCategory: documentation ---- - -# AI Answer Self-Check Policy - -AI-ассистент обязан для каждого ответа: - -## Обязательные Requireme.ts: -- Разбивать вопрос пользователя на подпункты/чеклист -- Явно отмечать, на что уже дан ответ, а что требует пояснения или осталось неотвеченным -- В конце ответа давать self-check: какие пункты покрыты, какие требуют доработки или согласования -- Если требование уже реализовано — явно указывать где и как (файл, секция, коммит, etc.) -- Для сложных вопросов использовать маркированные списки или таблицы - -## Пример self-check: -``` ---- -**Self-check для вашего вопроса:** -1. ✅ Механизм self-check — описан и предложен для реализации -2. ✅ Пример предоставлен выше -3. ✅ Предыдущий случай объяснен (требование уже реализовано) -4. ⚠️ Если хотите это в каждом ответе — можно сделать стандартом (подтвердите) ---- -``` - -## Применение: -Это правило Required для всех коммуникаций AI в проекте. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/always-russian-answer.mdc b/.cursor/rules/doc/always-russian-answer.mdc deleted file mode 100644 index fa65a4f0..00000000 --- a/.cursor/rules/doc/always-russian-answer.mdc +++ /dev/null @@ -1,7 +0,0 @@ -# Всегда отвечай на русском языке - -AI-ассистент обязан всегда давать ответы пользователю на русском языке, независимо от языка запроса или исходного контекста, если явно не указано иное. Исключение — если пользователь просит ответить на другом языке или задача требует иного языка для технической совместимости. -description: -globs: -alwaysApply: true ---- diff --git a/.cursor/rules/doc/auto-translate-requests.mdc b/.cursor/rules/doc/auto-translate-requests.mdc deleted file mode 100644 index df559883..00000000 --- a/.cursor/rules/doc/auto-translate-requests.mdc +++ /dev/null @@ -1,387 +0,0 @@ -# Auto Translate Requests System - -## Overview - -The Auto Translate Requests System automatically translates user requests from Russian to English when creating .cursor rules, ensuring that all rules are created in English for maximum AI/LLM compatibility. - -## Key Features - -### 🤖 **Automatic Translation** -- **Real-time translation** of user requests to English -- **Comprehensive coverage** of technical terminology -- **High accuracy** with confidence scoring -- **Context-aware** translation for .cursor rules - -### 📝 **Rule Creation** -- **Automatic file generation** with proper structure -- **English-only content** for AI/LLM compatibility -- **Git integration** with automatic staging -- **Template-based** rule generation - -### 🔧 **Multiple Interfaces** -- **Command-line interface** for quick rule creation -- **Interactive mode** for guided rule creation -- **NPM scripts** for easy integration -- **Direct API** for programmatic use - -## System Components - -### **1. Request Translator (`request-translator.cjs`)** -Core translation engine with comprehensive terminology coverage: -- **Translation mappings** - 500+ technical terms -- **Request analysis** - pattern detection and confidence scoring -- **English prompt generation** - AI-ready prompts -- **Context preservation** - maintains original request information - -### **2. Auto Translate Requests (`auto-translate-requests.cjs`)** -Complete system management: -- **Interactive mode** - guided rule creation -- **Setup automation** - system configuration -- **Rule creation** - automatic file generation -- **Integration management** - npm scripts and helpers - -### **3. Quick Rule Creator (`create-rule.cjs`)** -Fast rule creation utility: -- **One-command creation** - translate and create in one step -- **Automatic staging** - git integration -- **Template generation** - structured rule files -- **Progress feedback** - clear status updates - -## Usage - -### **Quick Rule Creation (Recommended)** -```bash -# Create rule with automatic translation -node .cursor/rules/create-rule.cjs "создай правило для архитектуры" - -# Using npm script -npm run create-rule "добавь правило безопасности" -``` - -### **Interactive Mode** -```bash -# Start interactive mode -node .cursor/rules/auto-translate-requests.cjs interactive - -# Using npm script -npm run interactive-rules -``` - -### **Direct Translation** -```bash -# Translate request only -node .cursor/rules/request-translator.cjs translate "напиши правило для тестирования" - -# Analyze request -node .cursor/rules/request-translator.cjs analyze "создай файл в .cursor" -``` - -### **System Setup** -```bash -# Setup auto translation system -node .cursor/rules/auto-translate-requests.cjs setup -``` - -## Translation Coverage - -### **Request Patterns** -- **Creation requests** - создай, добавь, напиши, сделай -- **File types** - правило, файл, документ, конфигурация -- **Locations** - в .cursor, в rules, в папку -- **Content types** - с описанием, с примерами, с инструкциями - -### **Technical Terminology** -- **Development terms** - разработка → development -- **Architecture terms** - архитектура → architecture -- **Security terms** - безопасность → security -- **Performance terms** - производительность → performance -- **Quality terms** - качество → quality - -### **Common Phrases** -- **Important notes** - важные примечания -- **Best practices** - лучшие практики -- **Requirements** - требования -- **Recommendations** - рекомендации - -## Generated Rule Structure - -### **File Format** -```markdown ---- -description: [translated request] -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "[original request]" -translatedAt: "[timestamp]" ---- - -# [translated request] - -## Overview - -This rule was created based on the request: "[original request]" - -## Description - -[translated request] - -## Implementation - - - -## Usage - - - -## Examples - - - -## Notes - - - ---- -*Generated automatically from user request: "[original request]"* -*Translated to English for AI/LLM compatibility* -``` - -### **Metadata Fields** -- **description** - Translated request description -- **globs** - File patterns this rule applies to -- **alwaysApply** - Whether rule should always be applied -- **aiPriority** - Priority level for AI processing -- **aiCategory** - Category for AI organization -- **createdFrom** - Original user request -- **translatedAt** - Translation timestamp - -## Workflow Integration - -### **For Users** -1. **Write request in Russian** - System automatically translates -2. **Review translation** - Check confidence and accuracy -3. **Confirm creation** - Rule is created in English -4. **Edit as needed** - Add specific content and details -5. **Commit changes** - Git integration handles the rest - -### **For AI Assistants** -1. **Receive English prompt** - All requests translated automatically -2. **Create English rules** - No manual translation needed -3. **Follow templates** - Consistent structure and format -4. **Maintain compatibility** - All content in English - -### **For Development** -1. **Automatic protection** - Rules created in English -2. **Git integration** - Automatic staging and commits -3. **Template consistency** - Standardized rule structure -4. **Backup preservation** - Original requests preserved - -## Configuration - -### **Auto Translate Config** -```json -{ - "enabled": true, - "autoTranslate": true, - "createEnglishPrompts": true, - "backupOriginalRequests": true, - "confidenceThreshold": 70, - "patterns": { - "ruleCreation": ["создай", "добавь", "напиши", "сделай"], - "fileCreation": ["файл", "документ", "правило"], - "cursorRequests": [".cursor", "cursor", "rules"] - } -} -``` - -### **NPM Scripts** -```json -{ - "scripts": { - "create-rule": "node .cursor/rules/create-rule.cjs", - "translate-request": "node .cursor/rules/request-translator.cjs translate", - "interactive-rules": "node .cursor/rules/auto-translate-requests.cjs interactive" - } -} -``` - -## Benefits - -### **For AI/LLM Compatibility** -- **Universal accessibility** - Any AI assistant can read rules -- **Language consistency** - All rules in English -- **Better understanding** - Clear terminology for AI processing -- **Reduced confusion** - No mixed language content - -### **For International Community** -- **Global accessibility** - Ready for international developers -- **Standardized format** - Consistent English documentation -- **Easy sharing** - No language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - No manual translation needed -- **Safe operation** - Original requests preserved -- **Git integration** - Seamless workflow integration -- **Template consistency** - Standardized rule structure - -## Best Practices - -### **For Users** -1. **Use clear requests** - Specific and descriptive requests -2. **Trust automatic translation** - System is comprehensive -3. **Review generated content** - Edit templates as needed -4. **Use English for new content** - Maintain consistency - -### **For AI Assistants** -1. **Always create rules in English** -2. **Trust the translation system** - It handles Russian requests -3. **Follow generated templates** - Maintain consistency -4. **Use standard terminology** - From translation mappings - -### **For Developers** -1. **Extend translation mappings** - Add missing terms as needed -2. **Test translation quality** - Verify accuracy -3. **Maintain templates** - Keep them up to date -4. **Monitor confidence scores** - Improve low-confidence translations - -## Error Handling - -### **Translation Errors** -- **Partial translation** - System continues with available mappings -- **Confidence scoring** - Clear indication of translation quality -- **Original preservation** - Original request always saved -- **Manual fallback** - Option to edit manually - -### **Creation Errors** -- **Directory creation** - Automatic directory structure -- **File conflicts** - Unique filename generation -- **Git integration** - Graceful handling of git errors -- **Template fallback** - Basic template if generation fails - -### **System Errors** -- **Graceful degradation** - System continues with available features -- **Error reporting** - Clear error messages -- **Recovery options** - Multiple ways to create rules -- **User guidance** - Clear instructions for resolution - -## Troubleshooting - -### **Common Issues** - -#### **Poor translation quality** -1. Check confidence score in analysis -2. Extend translation mappings if needed -3. Use more specific terminology -4. Report missing terms for improvement - -#### **Rule creation fails** -1. Check file permissions -2. Verify directory structure -3. Check git status -4. Use manual creation as fallback - -#### **Interactive mode issues** -1. Check Node.js version compatibility -2. Verify readline module availability -3. Check terminal compatibility -4. Use command-line mode as alternative - -### **Debug Commands** -```bash -# Test translation quality -node .cursor/rules/request-translator.cjs analyze "your request" - -# Check system status -node .cursor/rules/auto-translate-requests.cjs setup - -# Verify file creation -ls -la .cursor/rules/ - -# Check git status -git status -``` - -## Future Enhancements - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages -- **Real-time translation** during typing -- **Translation quality scoring** - -### **Integration Ideas** -- **IDE plugins** for real-time translation -- **Web interface** for rule creation -- **API endpoints** for programmatic access -- **Collaborative translation** for community contributions - -## Examples - -### **Example 1: Architecture Rule** -```bash -# User request (Russian) -node .cursor/rules/create-rule.cjs "создай правило для архитектуры проекта" - -# Generated English rule ---- -description: create rule for architecture project -globs: ["**/*"] -alwaysApply: false -aiPriority: normal -aiCategory: rules -createdFrom: "создай правило для архитектуры проекта" -translatedAt: "2025-07-19T02:58:58.601Z" ---- - -# create rule for architecture project - -## Overview - -This rule was created based on the request: "создай правило для архитектуры проекта" -``` - -### **Example 2: Security Rule** -```bash -# User request (Russian) -node .cursor/rules/create-rule.cjs "добавь правило безопасности для API" - -# Generated English rule ---- -description: add security rule for API -globs: ["**/*"] -alwaysApply: false -aiPriority: high -aiCategory: security -createdFrom: "добавь правило безопасности для API" -translatedAt: "2025-07-19T02:59:15.123Z" ---- - -# add security rule for API - -## Overview - -This rule was created based on the request: "добавь правило безопасности для API" -``` - -## Conclusion - -The Auto Translate Requests System ensures that all .cursor rules are automatically created in English for maximum AI/LLM compatibility. This creates a seamless workflow where users can write requests in Russian, and the system automatically translates and creates English rules. - -**Key Benefits:** -- ✅ **Automatic translation** of user requests -- ✅ **English-only rule creation** -- ✅ **AI/LLM compatibility** -- ✅ **Git integration** -- ✅ **Template consistency** -- ✅ **Error handling and recovery** - -**Usage:** Simply use `node .cursor/rules/create-rule.cjs "your request in Russian"` to automatically translate and create English rules for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/chrome-extension-manual-restore.mdc b/.cursor/rules/doc/chrome-extension-manual-restore.mdc deleted file mode 100644 index 825d8541..00000000 --- a/.cursor/rules/doc/chrome-extension-manual-restore.mdc +++ /dev/null @@ -1,26 +0,0 @@ -# Chrome Extension Manual Restore — Experience & Pitfalls (2024-07-20) - -## Manual Restore Workflow -- Locate the required extension archive in `dist-zip/` (e.g., `extension-20250716-043234.zip`). -- Unzip to `/tmp/chrome-extension-old`: - ```bash - unzip -o dist-zip/extension-20250716-043234.zip -d /tmp/chrome-extension-old - ``` -- Launch Chromium with the script: - ```bash - bash bash-scripts/run-chromium-old-extension.sh - ``` -- The extension will be loaded and available for manual UI inspection and copying. - -## Typical Errors & Solutions -- **Manifest file missing**: Ensure `manifest.json` is present in `/tmp/chrome-extension-old` after unzipping. -- **unzip not found**: Install with `sudo pacman -S unzip` or `sudo apt install unzip`. -- **WDIO browser closes too soon**: Use the bash script for unlimited manual work time. - ---- - -This rule and experience are recorded for future automation and to avoid repeating past mistakes. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/command-synchronization.mdc b/.cursor/rules/doc/command-synchronization.mdc deleted file mode 100644 index 69bab2fe..00000000 --- a/.cursor/rules/doc/command-synchronization.mdc +++ /dev/null @@ -1,264 +0,0 @@ -# Command Synchronization System - -## Overview - -Automated system for synchronizing user Commands between multiple sources: -- `USER_CommandS.md` - User-friendly Command reference -- `.cursor/rules/cursor-export/ai-memory.mdc` - AI assistant instructions -- `.cursor/rules/ai-memory.mdc` - Cursor rules -- `CURSOR_AI_MEMORY_BANK.md` - Export for Cursor AI memory-bank - -## How It Works - -### Single Source of Truth -All Commands are defined in `.cursor/rules/Command-sync..js` in a structured format: - -```JavaScript -const CommandCategories = { - 'Context and Memory': { - 'save context': { - russian: ['Сохрани контекст', 'Save контекст сессии'], - description: 'Save achieveme.ts, decisions and plans to memory-bank', - userDescription: 'AI-ассистент сохранит все достижения, решения и планы в memory-bank' - } - } -} -``` - -### Automatic Synchronization -The script generates all files from the single source: - -```bash -# Sync all files -node .cursor/rules/Command-sync..js sync - -# Export for Cursor AI memory-bank -node .cursor/rules/Command-sync..js export - -# Translate context to English -node .cursor/rules/Command-sync..js translate-context -``` - -## File Form.ts - -### USER_CommandS.md -User-friendly format with emojis and detailed descriptions: -```markdown -## 📝 Сохранение контекста - -### Save контекст сессии -`Сохрани контекст` / `Save контекст сессии` -*AI-ассистент сохранит все достижения, решения и планы в memory-bank* -``` - -### ai-memory.mdc -AI-optimized format for .cursor rules: -```markdown -### Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -### CURSOR_AI_MEMORY_BANK.md -Format optimized for Cursor AI memory-bank: -```markdown -### 📝 Context and Memory: -- `save context` / `Сохрани контекст` / `Save контекст сессии` - Save achieveme.ts, decisions and plans to memory-bank -``` - -## Context Translation System - -### Automatic Context Translation -The system automatically translates context to English for AI/LLM compatibility: - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only -``` - -### Features -- ✅ **Automatic translation** from Russian to English -- ✅ **backup creation** before translation -- ✅ **Git integration** with automatic comm.ts -- ✅ **Comprehensive coverage** of all context terminology -- ✅ **Error handling** with safe fallbacks - -### Translation Coverage -- **Headers and titles** - All section headers -- **Task status** - Completed tasks, current focus, next steps -- **Principles** - Working principles and guIDElines -- **Technical context** - Architecture and standards -- **Command system** - Command categories and descriptions -- **User experience** - UX priorities and metrics -- **Development plans** - Short, medium, and long-term goals -- **Status indicators** - Readiness and completion status - -## Integration with Cursor AI Memory-Bank - -### Manual Integration -1. Run export Command: - ```bash - node .cursor/rules/Command-sync..js export - ``` - -2. Copy content from `CURSOR_AI_MEMORY_BANK.md` - -3. Go to Cursor Settings → AI → Rules & Memories - -4. Paste into User Rules or Project Rules - -5. Save and restart Cursor - -### Automatic Integration (Future) -Planned features: -- Direct API integration with Cursor -- Automatic updates when Commands change -- Version control for AI memory-bank - -## Command Categories - -### 📝 Context and Memory -- `save context` - Save achieveme.ts, decisions and plans in English (automatic translation) -- `update progress` - Update project status files -- `restore context` - Restore full project understanding -- `quick restore` - Get brief summary - -### 🏗️ Analysis and Study -- `analyze architecture` - Study system patterns -- `study plugins` - Analyze plugin structure -- `check build` - Verify project builds -- `update documentation` - Update project docs - -### 🔧 Development -- `create plugin [name]` - Create new plugin -- `check code` - Run linting and type checking -- `run te.ts` - Run all project te.ts -- `check dependencies` - Verify dependencies - -### 📊 Project Management -- `bump version patch/minor/major` - Increase version -- `clean project` - Clean build artifa.ts -- `analyze performance` - Performance analysis -- `check security` - Security analysis - -### 🚀 Releases and Deployment -- `create release` - Prepare for release -- `build production` - Production build - -## Benef.ts - -### For Users -- ✅ **Consistent Commands** across all sources -- ✅ **Easy to use** - just copy and paste -- ✅ **Bilingual support** - English and Russian -- ✅ **Always up-to-date** - automatic synchronization - -### For AI Assista.ts -- ✅ **Structured format** - easy to parse -- ✅ **Clear descriptions** - understand what each Command does -- ✅ **Multiple sources** - available in all conte.ts -- ✅ **Semantic understanding** - works with any language - -### For Development -- ✅ **Single source of truth** - no duplication -- ✅ **Automatic updates** - change once, update everywhere -- ✅ **Version control** - track Command changes -- ✅ **Easy maintenance** - centralized management - -## Usage examples - -### Adding New Commands -1. Edit `.cursor/rules/Command-sync..js` -2. Add Command to appropriate category -3. Run sync Command: - ```bash - node .cursor/rules/Command-sync..js sync - ``` - -### Updating Existing Commands -1. Edit Command in `.cursor/rules/Command-sync..js` -2. Run sync Command -3. All files updated automatically - -### Exporting for Cursor -```bash -node .cursor/rules/Command-sync..js export -``` - -## Technical Details - -### File Structure -``` -.cursor/rules/ -├── Command-sync..js # Main synchronization script -├── ai-memory.mdc # Cursor rules (auto-generated) -├── cursor-export/ -│ └── ai-memory.mdc # AI assistant instructions (auto-generated) -└── doc/ - └── Command-synchronization.mdc # This documentation - -USER_CommandS.md # User reference (auto-generated) -CURSOR_AI_MEMORY_BANK.md # Cursor export (auto-generated) -``` - -### Script Functions -- `generateUserCommand.md()` - Generate USER_CommandS.md -- `generateAIMemor.md()` - Generate ai-memory.mdc files -- `generateCursorMemoryBank()` - Generate Cursor format -- `syncCommands()` - Sync all files -- `exportForCursor()` - Export for Cursor AI memory-bank - -### Command Structure -Each Command has: -- **English Command** - Primary Command -- **Russian alternatives** - User-friendly alternatives -- **Description** - Technical description for AI -- **User description** - User-friendly description - -## Future Enhanceme.ts - -### Planned Features -- [ ] **API Integration** - Direct Cursor API integration -- [ ] **Auto-sync** - Automatic sync on file changes -- [ ] **Command validation** - Validate Command syntax -- [ ] **Usage analytics** - Track Command usage -- [ ] **template system** - Command templates for different project types - -### Integration IDEas -- **Git hooks** - Auto-sync on commit -- **CI/CD integration** - Validate Command consistency -- **Plugin system** - Extensible Command categories -- **Multi-language support** - More languages beyond English/Russian - -## Troubleshooting - -### Common Issues -1. **Sync fails** - Check file permissions -2. **Commands not working** - Verify Cursor AI memory-bank integration -3. **Format issues** - Check Command structure in script - -### Debug Commands -```bash -# Check script help -node .cursor/rules/Command-sync..js help - -# Verify file generation -ls -la USER_CommandS.md CURSOR_AI_MEMORY_BANK.md -``` - -## Conclusion - -The Command synchronization system provIDEs: -- ✅ **Consistent experience** across all platforms -- ✅ **Easy maintenance** with single source of truth -- ✅ **Automatic updates** for all Command sources -- ✅ **AI-optimized format** for maximum compatibility -- ✅ **User-friendly interface** for easy adoption - -This system ensures that Commands work sea.lessLy across USER_CommandS.md, .cursor rules, and Cursor AI memory-bank settings. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/context-translation-system.mdc b/.cursor/rules/doc/context-translation-system.mdc deleted file mode 100644 index a1183df8..00000000 --- a/.cursor/rules/doc/context-translation-system.mdc +++ /dev/null @@ -1,206 +0,0 @@ -# Context Translation System - -## Overview - -The Context Translation System automatically translates context files from Russian to English for maximum AI/LLM compatibility. This ensures that all context saved in memory-bank is accessible to any AI assistant, regar.less of language preferences. - -## Key Features - -### 🌍 **Automatic Translation** -- **Sea.less translation** from Russian to English -- **Comprehensive coverage** of all context terminology -- **Preserves structure** and formatting -- **Creates backups** before translation - -### 🔄 **Integrated with Save Context Command** -- **Automatic translation** when using "save context" Command -- **Git integration** with automatic comm.ts -- **backup creation** for safety -- **Error handling** for robust operation - -### 📁 **backup System** -- **Automatic backups** before translation -- **Timestamped files** in `memory-bank/core/backup/` -- **Easy restoration** if needed -- **Safe operation** with rollback capability - -## Usage - -### For AI Assista.ts - -When a user issues the Command "save context" or "Сохрани контекст": - -1. **Automatically translate** context to English -2. **Create backups** of original files -3. **Save translated files** to memory-bank -4. **Commit changes** to git with descriptive message -5. **Notify user** of completion - -### Manual Usage - -```bash -# Save context with automatic translation and commit -node .cursor/rules/save-context..js save - -# Only translate without committing -node .cursor/rules/save-context..js translate-only - -# Translate context using Command sync script -node .cursor/rules/Command-sync..js translate-context -``` - -## Translation Coverage - -### **Headers and Titles** -- `Active контекст разработки` → `Active Development Context` -- `Текущий Status проекта` → `Current Project Status` -- `Последнее обновление` → `Last Updated` - -### **Task Status** -- `Завершенные задачи` → `Completed Tasks` -- `Текущий фокус` → `Current Focus` -- `Следующие шаги` → `Next Steps` - -### **Principles and GuIDElines** -- `Ключевые Principles работы` → `Key Working Principles` -- `Инициативность ассистента` → `Assistant Initiative` -- `Quality кода` → `Code Quality` - -### **Technical Context** -- `Технический контекст` → `Technical Context` -- `Текущая Architecture` → `Current Architecture` -- `Standards разработки` → `Development Standards` - -### **Command System** -- `Система команд` → `Command System` -- `Автоматическая Synchronization` → `Automatic Synchronization` -- `Категории команд` → `Command Categories` - -### **User Experience** -- `User Experience` → `User Experience` -- `Priorityы UX` → `UX Priorities` -- `Метрики качества` → `Quality Metrics` - -### **Development Plans** -- `Планы развития` → `Development Plans` -- `Краткосрочные цели` → `Short-term Goals` -- `Mediumсрочные цели` → `Medium-term Goals` -- `Долгосрочные цели` → `Long-term Goals` - -### **Status and Readiness** -- `Status готовности` → `Readiness Status` -- `Готовые компоненты` → `Ready Compone.ts` -- `Готово к публикации` → `Ready for publication` - -## File Structure - -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Main context file (English) -│ ├── progress.md # Progress tracking (English) -│ └── backup/ # backup directory -│ ├── activeContext-2024-07-19T10-30-00-000Z.md -│ └── progress-2024-07-19T10-30-00-000Z.md -``` - -## Scri.ts - -### **save-context..js** -Main script for saving context with automatic translation: -- Translates context to English -- Creates backups -- Comm.ts changes to git -- ProvIDEs error handling - -### **Command-sync..js** -Extended with translation functionality: -- `translate-context` Command -- Integrated translation function -- Comprehensive translation mappings - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - Any AI assistant can read context -- **Language consistency** - All context in English -- **Better understanding** - Clear terminology for AI processing -- **Reduced confusion** - No mixed language content - -### **For International Community** -- **Global accessibility** - Ready for international developers -- **Standardized format** - Consistent English documentation -- **Easy sharing** - No language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - No manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - Automatic comm.ts with clear messages -- **Error handling** - Robust operation with fallbacks - -## Best Practices - -### **For AI Assista.ts** -1. **Always use "save context"** Command for context preservation -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English context** for all AI operations - -### **For Users** -1. **Use Russian Commands** - translation happens automatically -2. **Check backup directory** if you need original files -3. **Trust the system** - translations are accurate and comprehensive -4. **Report issues** if translation problems occur - -### **For Developers** -1. **Extend translation mappings** as needed -2. **Test translations** before deployment -3. **Maintain backup system** for safety -4. **Update documentation** when adding new terms - -## Error Handling - -### **Translation Errors** -- **backup preservation** - Original files always saved -- **Partial translation** - System continues with available mappings -- **Error reporting** - Clear messages about issues -- **Manual fallback** - Option to restore from backup - -### **Git Errors** -- **Translation still works** - Files translated even if commit fails -- **Manual commit option** - User can commit manually -- **Clear error messages** - Specific information about git issues -- **Safe operation** - No data loss in case of git problems - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages - -### **Potential Improveme.ts** -- **Real-time translation** during editing -- **Translation quality scoring** -- **User feedback system** for translations -- **Integration with more AI tools** - -## Conclusion - -The Context Translation System ensures that all context in memory-bank is accessible to any AI assistant by automatically translating content to English. This creates a truly international and AI-compatible documentation system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic translation process** -- ✅ **Safe backup system** -- ✅ **Git integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling** - -**Usage:** Simply use the "save context" Command, and the system will automatically translate and save your context in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/cursor-protection-system.mdc b/.cursor/rules/doc/cursor-protection-system.mdc deleted file mode 100644 index 9170030c..00000000 --- a/.cursor/rules/doc/cursor-protection-system.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Cursor Protection System - -## Overview - -The Cursor Protection System automatically ensures that all files in the `.cursor` directory are written in English for maximum AI/LLM compatibility. This system provIDEs comprehensive protection through automatic translation, Git hooks, and monitoring. - -## Key Features - -### 🛡️ **Automatic Protection** -- **Real-time translation** of all .cursor files to English -- **Comprehensive coverage** of technical terminology -- **Preserves structure** and formatting -- **Creates backups** before any changes - -### 🔧 **Git Integration** -- **Pre-commit hooks** - automatically translate staged .cursor files -- **Post-commit hooks** - verify protection status after comm.ts -- **Pre-push hooks** - block push if Russian content is detected -- **Automatic staging** of translated files - -### 📊 **Monitoring and Status** -- **Status checking** - IDEntify files that need translation -- **Protection repo.ts** - comprehensive status overview -- **backup management** - easy restoration from backups -- **Integration status** - verify all compone.ts are working - -## System Compone.ts - -### **1. Cursor Protector (`cursor-protector..js`)** -Main translation engine with comprehensive terminology coverage: -- **Translation mappings** - 500+ technical terms -- **File scanning** - recursive directory processing -- **backup creation** - automatic backup before translation -- **Status reporting** - detailed analysis of files - -### **2. Git Hooks (`cursor-git-hook..js`)** -Automatic protection during Git operations: -- **Pre-commit** - translate staged .cursor files -- **Post-commit** - verify overall protection status -- **Pre-push** - block push if Russian content found -- **Hook management** - install/uninstall hooks - -### **3. Protection Manager (`protect-cursor..js`)** -Complete system management: -- **Full protection** - translate, install hooks, commit -- **System installation** - setup all compone.ts -- **Status checking** - comprehensive protection report -- **backup restoration** - restore from backups - -## Usage - -### **Complete Protection (Recommended)** -```bash -# Protect all .cursor files, install hooks, and commit -node .cursor/rules/protect-cursor..js protect -``` - -### **System Installation** -```bash -# Install protection system (hooks, scri.ts, .cursorignore) -node .cursor/rules/protect-cursor..js install -``` - -### **Status Checking** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Check .cursor files specifically -node .cursor/rules/cursor-protector..js check -``` - -### **Manual Protection** -```bash -# Protect all .cursor files -node .cursor/rules/cursor-protector..js protect - -# Install Git hooks -node .cursor/rules/cursor-git-hook..js install -``` - -### **Package.json Scri.ts** -After installation, use npm scri.ts: -```bash -npm run protect-cursor # Complete protection -npm run check-cursor # Check status -npm run install-cursor-hooks # Install hooks -``` - -## Translation Coverage - -### **Technical Terminology** -- **Development terms** - Development → development -- **Architecture terms** - Architecture → architecture -- **Security terms** - Security → security -- **Performance terms** - Performance → performance -- **Quality terms** - Quality → quality - -### **File Structure** -- **Headers and titles** - заголовки и названия -- **Section names** - названия разделов -- **Status indicators** - индикаторы Statusа -- **Action descriptions** - описания действий - -### **Common Phrases** -- **Important notes** - важные Notes -- **Best practices** - Best Practices -- **Requireme.ts** - Requireme.ts -- **Recommendations** - Recommendations - -## Git Hook Behavior - -### **Pre-commit Hook** -1. **Scans staged files** for .cursor files -2. **Dete.ts Russian content** in staged files -3. **Automatically translates** files with Russian content -4. **Creates backups** before translation -5. **Stages translated files** for commit - -### **Post-commit Hook** -1. **Checks overall .cursor status** -2. **Repo.ts any remaining Russian content** -3. **Sugge.ts actions** if needed -4. **Confirms protection status** - -### **Pre-push Hook** -1. **Scans all .cursor files** -2. **Blocks push** if Russian content found -3. **ProvIDEs clear error message** -4. **Sugge.ts protection Command** - -## File Structure - -``` -.cursor/ -├── rules/ -│ ├── cursor-protector..js # Main translation engine -│ ├── cursor-git-hook..js # Git hooks -│ ├── protect-cursor..js # Protection manager -│ └── doc/ -│ └── cursor-protection-system.mdc # This documentation -├── backup/ # backup directory -│ ├── pre-commit/ # Pre-commit backups -│ └── [timestamped-backups]/ # Manual backups -└── [other .cursor files] # Protected files -``` - -## configuration - -### **.cursorignore** -Exclude files from automatic translation: -``` -# Cursor Protection System -# Files that should not be automatically translated - -# backup files -.cursor/backup/ - -# temporary files -*.tmp -*.temp - -# Log files -*.log - -# cache files -.cache/ -node_modules/ -``` - -### **Package.json Scri.ts** -Automatically added during installation: -``.json -{ - "scri.ts": { - "protect-cursor": "node .cursor/rules/protect-cursor..js protect", - "check-cursor": "node .cursor/rules/cursor-protector..js check", - "install-cursor-hooks": "node .cursor/rules/cursor-git-hook..js install" - } -} -``` - -## Benef.ts - -### **For AI/LLM Compatibility** -- **Universal accessibility** - any AI assistant can read .cursor files -- **Language consistency** - all files in English -- **Better understanding** - clear terminology for AI processing -- **Reduced confusion** - no mixed language content - -### **For International Community** -- **Global accessibility** - ready for international developers -- **Standardized format** - consistent English documentation -- **Easy sharing** - no language barriers -- **Professional appearance** - English for global audience - -### **For Development Workflow** -- **Automatic process** - no manual translation needed -- **Safe operation** - backups created automatically -- **Git integration** - sea.less workflow integration -- **Error prevention** - blocks problematic comm.ts/pushes - -## Best Practices - -### **For Users** -1. **Run complete protection** after any .cursor changes -2. **Trust automatic translation** - system is comprehensive -3. **Check backups** if translation issues occur -4. **Use English for new files** - maintain consistency - -### **For AI Assista.ts** -1. **Always write .cursor files in English** -2. **Trust the protection system** - it handles translation -3. **Check protection status** if unsure -4. **Use standard terminology** from translation mappings - -### **For Developers** -1. **Install protection system** in new proje.ts -2. **Extend translation mappings** as needed -3. **Test protection** before deployment -4. **Maintain backup system** for safety - -## Error Handling - -### **Translation Errors** -- **backup preservation** - original files always saved -- **Partial translation** - system continues with available mappings -- **Error reporting** - clear messages about issues -- **Manual fallback** - option to restore from backup - -### **Git Hook Errors** -- **Translation still works** - files translated even if hooks fail -- **Manual protection option** - user can run protection manually -- **Clear error messages** - specific information about issues -- **Safe operation** - no data loss in case of hook problems - -### **System Errors** -- **Graceful Degradation** - system continues with available features -- **Error logging** - detailed error information -- **Recovery options** - multiple ways to restore functionality -- **User guidance** - clear instructions for resolution - -## Troubleshooting - -### **Common Issues** - -#### **Files not being translated** -1. Check if files are in `.cursorignore` -2. Verify file extensions (.md, .mdc) -3. Run manual protection: `node .cursor/rules/cursor-protector..js protect` - -#### **Git hooks not working** -1. Check hook installation: `node .cursor/rules/cursor-git-hook..js install` -2. Verify hook permissions (should be executable) -3. Check for confli.ts with other hooks - -#### **Translation quality issues** -1. Check backup files for original content -2. Extend translation mappings if needed -3. Report missing terms for system improvement - -### **Debug Commands** -```bash -# Check protection status -node .cursor/rules/protect-cursor..js check - -# Test translation on specific file -node .cursor/rules/cursor-protector..js protect - -# Verify Git hooks -ls -la .git/hooks/ - -# Check backup files -ls -la .cursor/backup/ -``` - -## Future Enhanceme.ts - -### **Planned Features** -- **API integration** with translation services -- **Machine learning** for better translations -- **Custom translation mappings** per project -- **Multi-language support** for other languages -- **Real-time translation** during editing -- **Translation quality scoring** - -### **Integration IDEas** -- **IDE plugins** for real-time protection -- **CI/CD integration** for automated protection -- **Webhook system** for remote protection -- **Collaborative translation** for community contributions - -## Conclusion - -The Cursor Protection System ensures that all `.cursor` files are automatically maintained in English for maximum AI/LLM compatibility. This creates a truly international and AI-compatible configuration system that suppo.ts global collaboration and development. - -**Key Benef.ts:** -- ✅ **Universal AI compatibility** -- ✅ **Automatic protection process** -- ✅ **Safe backup system** -- ✅ **Git workflow integration** -- ✅ **Comprehensive coverage** -- ✅ **Error handling and recovery** - -**Usage:** Simply run `node .cursor/rules/protect-cursor..js protect` to activate complete protection, and the system will automatically maintain all `.cursor` files in English for maximum AI/LLM compatibility. -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/documentation-map.mdc b/.cursor/rules/doc/documentation-map.mdc deleted file mode 100644 index 358d7a02..00000000 --- a/.cursor/rules/doc/documentation-map.mdc +++ /dev/null @@ -1,196 +0,0 @@ -# Documentation Map - Карта назначения файлов - -## Команда "задокументируй" - четкие правила размещения - -### 🎯 **Универсальные правила и best practices → `.cursor/rules/`** - -**Критерии для `.cursor/rules/`:** -- ✅ Применимо к любым проектам -- ✅ Проверенные Patterns и подходы -- ✅ Автоматизация и workflow -- ✅ Standards разработки -- ✅ Documentation и структуры - -**Файлы назначения:** -- `dev/dev-principle-*.mdc` - Principles разработки -- `dev/development-principles.mdc` - универсальные Principles разработки -- `dev/date-time-patterns.mdc` - Patterns работы с Dateми -- `workflow/ci-automation.mdc` - CI/CD автоматизация -- `workflow/experience-transfer.mdc` - перенос опыта -- `doc/ai-first.mdc` - Standards документации -- `ui/ui-*.mdc` - UI/UX Standards -- `plugin/plugin-*.mdc` - Standards плагинов -- `security/validation.mdc` - правила безопасности -- `architecture/architecture-*.mdc` - архитектурные Principles -- `architecture/file-relationships.mdc` - Patterns организации файлов - -### 🧠 **Проектно-специфичный контекст → `memory-bank/`** - -**Критерии для `memory-bank/`:** -- ✅ Уникален для данного проекта -- ✅ Текущий Status и прогресс -- ✅ История ошибок и решений -- ✅ Принятые архитектурные решения -- ✅ Планы развития проекта - -**Файлы назначения:** -- `errors.md` - кладбище ошибок и решений -- `progress.md` - прогресс разработки -- `activeContext.md` - текущий контекст -- `architecture-decisions.md` - принятые решения -- `future-plans.md` - планы развития -- `testing-resu.ts.md` - результаты тестирования -- `sIDE-panel-improveme.ts.md` - улучшения UI -- `version-management.md` - управление версиями - -## 📋 **Детальная карта по Typeам контента** - -### **Errors and solutions:** -``` -Type ошибки → Файл назначения -├── build/TypeScript → memory-bank/errors.md -├── Runtime/UI → memory-bank/errors.md -├── Архитектурная → memory-bank/architecture-decisions.md -├── Security → .cursor/rules/security/validation.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── UI/UX → .cursor/rules/ui/ui-*.mdc -``` - -### **Best practices:** -``` -Type практики → Файл назначения -├── Development → .cursor/rules/dev/dev-principle-*.mdc -├── Documentation → .cursor/rules/doc/ai-first.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── Git workflow → .cursor/rules/workflow/branches.mdc -└── Plugins → .cursor/rules/plugin/plugin-*.mdc -``` - -### **Автоматизация:** -``` -Type automation → Файл назначения -├── CI/CD → .cursor/rules/workflow/ci-automation.mdc -├── build → .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -├── Testing → .cursor/rules/dev/testing-troubleshooting.mdc -├── Деплой → .cursor/rules/workflow/automation.mdc -└── Monitoring → .cursor/rules/architecture/architecture-observability.mdc -``` - -### **Architectural decisions:** -``` -Type решения → Файл назначения -├── Проектно-специфичное → memory-bank/architecture-decisions.md -├── Универсальное → .cursor/rules/architecture/architecture-*.mdc -├── Security → .cursor/rules/architecture/architecture-security.mdc -├── Performance → .cursor/rules/architecture/architecture-performance.mdc -└── Plugins → .cursor/rules/architecture/architecture-plugin.mdc -``` - -## 🔄 **Workflow команды "задокументируй"** - -### **1. Анализ контента:** -```bash -# Определить Type контента -- Error/решение? -- Best practice? -- Автоматизация? -- Архитектурное решение? -- Проектно-специфичное? -``` - -### **2. Выбор файла назначения:** -```bash -# По карте выше определить правильный файл -# Check существование файла -# Create если не существует -``` - -### **3. Структурированное добавление:** -```bash -# Для .cursor/rules/: -- Add метаdata (description, globs, alwaysApply) -- Структурировать по Categoryм -- Add cross-references - -# Для memory-bank/: -- Add дату и контекст -- Связать с существующими записями -- Update индексы -``` - -### **4. Validation:** -```bash -# Check: -- Нет дублирования -- Правильная структура -- Актуальные ссылки -- Соответствие стандартам -``` - -## 🚫 **Запрещенные действия:** - -### **НЕ размещать в `.cursor/rules/`:** -- ❌ Проектно-специфичные ошибки -- ❌ Текущий Status проекта -- ❌ История конкретных решений -- ❌ Планы развития проекта -- ❌ Результаты тестирования - -### **НЕ размещать в `memory-bank/`:** -- ❌ Универсальные правила -- ❌ Best practices для всех проектов -- ❌ Автоматизация и workflow -- ❌ Standards документации -- ❌ UI/UX Standards - -## 📊 **examples правильного размещения:** - -### **Пример 1: Error сборки TypeScript** -``` -Проблема: "Failed to resolve entry for package '@extension/vite-config'" -Решение: "Перевести пакет на ESM-only, main: dist/index..js" - -Размещение: memory-bank/errors.md -Причина: Проектно-специфичная Error сборки -``` - -### **Пример 2: Best practice для ESM пакетов** -``` -Практика: "Использовать только ESM-экспорт для внутренних пакетов" -Обоснование: "Упрощает сборку, устраняет ошибки Vite/esbuild" - -Размещение: .cursor/rules/dev/TypeScript-build-troubleshooting.mdc -Причина: Универсальная практика для всех проектов -``` - -### **Пример 3: CI/CD автоматизация** -``` -Автоматизация: "GitHub Actions для проверки .cursor rules" -Workflow: "Verification структуры, метаданных, дубликатов" - -Размещение: .cursor/rules/workflow/ci-automation.mdc -Причина: Универсальная автоматизация -``` - -## ✅ **Результат:** - -Четкая карта назначения файлов обеспечивает: -- **Нет дублирования** - каждый Type контента в правильном месте -- **Нет рассинхронизации** - четкие критерии размещения -- **Легкий поиск** - структурированная организация -- **Эффективный перенос** - только универсальные правила в .cursor -- **Консистентность** - единообразные подходы -description: -globs: -alwaysApply: false ---- - -## 🛡️ Protected/Administrative Files - -Следующие файлы считаются административными и защищёнными — их нельзя удалять, перемещать или переименовывать автоматизацией (органайзерами, скриптами и т.д.): - -- memory-bank/chrome-extension-manual-restore.md — опыт ручного восстановления расширения, запрещено удалять/перемещать автоматизацией -- (добавляйте сюда другие важные административные файлы по мере необходимости) - ---- diff --git a/.cursor/rules/doc/internationalization-complete.mdc b/.cursor/rules/doc/internationalization-complete.mdc deleted file mode 100644 index fe05f562..00000000 --- a/.cursor/rules/doc/internationalization-complete.mdc +++ /dev/null @@ -1,162 +0,0 @@ -# Internationalization Complete - .cursor and memory-bank - -## Overview - -All `.cursor` and `memory-bank` directories have been completely translated to English for maximum international compatibility with AI assista.ts and LLMs. - -## What Was Translated - -### .cursor/rules Directory -- ✅ **ai-memory.mdc** - User Commands and AI instructions -- ✅ **ai-index.mdc** - AI-optimized rules index -- ✅ **README.mdc** - Main documentation -- ✅ **All subdirectories** - architecture/, dev/, doc/, plugin/, security/, ui/, workflow/ -- ✅ **cursor-export/** - All exported rules and standards - -### memory-bank Directory -- ✅ **README.md** - Main memory bank documentation -- ✅ **INDEX.md** - File structure and navigation -- ✅ **MEMORY_BANK_STRUCTURE.md** - Organization rules -- ✅ **All subdirectories** - core/, errors/, architecture/, development/, ui/, planning/, context/ -- ✅ **All individual files** - activeContext.md, progress.md, errors.md, etc. - -## Translation Approach - -### Universal Command Format -Commands now support both English and Russian: -```markdown -- `save context` / `save session context` - Save achieveme.ts, decisions and plans to memory-bank -- `update progress` / `update project progress` - Update activeContext.md and progress.md files with current status -``` - -### AI Semantic Understanding -- AI and LLMs understand Commands semantically regar.less of language -- English serves as the primary language for international compatibility -- Russian alternatives are preserved for user convenience - -## Benef.ts - -### For International Community -- 🌍 **100% English compatibility** - Any developer can use your .cursor -- 🤖 **AI/LLM optimized** - Commands work with any AI assistant -- 📚 **Standardized patterns** - Consistent Command structure -- 🔄 **Future-proof** - Ready for global sharing and collaboration - -### For You -- ✅ **Russian Commands still work** - No disruption to your workflow -- ✅ **Bilingual support** - Can use either language -- ✅ **AI understands both** - Semantic recognition works for both languages - -### For AI Assista.ts -- 🎯 **Clear Command patterns** - Predictable structure -- 🌐 **Language agnostic** - Understands meaning, not just words -- 📖 **Comprehensive documentation** - All rules in English - -## Technical Implementation - -### Translation Script -Created `.cursor/rules/translate-to-english..js` for automated translation: -```bash -node .cursor/rules/translate-to-english..js -``` - -### Translation Mappings -Comprehensive dictionary of Russian → English translations: -- Metadata descriptions -- Common phrases -- File content -- Navigation eleme.ts - -### Quality Assurance -- ✅ All files translated -- ✅ Structure preserved -- ✅ Functionality maintained -- ✅ Metadata updated - -## Usage examples - -### English Commands (Primary) -```bash -save context -update progress -restore context -analyze architecture -create plugin my-plugin -audit cursor -export cursor -``` - -### Russian Commands (Alternative) -```bash -сохрани контекст -обнови прогресс -восстанови контекст -анализируй архитектуру -создай плагин my-plugin -аудит cursor -экспорт cursor -``` - -## File Structure - -### .cursor/rules/ -``` -.cursor/rules/ -├── ai-memory.mdc # User Commands (English + Russian) -├── ai-index.mdc # Rules index (English) -├── README.mdc # Main documentation (English) -├── architecture/ # Architecture rules (English) -├── dev/ # Development principles (English) -├── doc/ # Documentation standards (English) -├── plugin/ # Plugin standards (English) -├── security/ # Security rules (English) -├── ui/ # UI/UX standards (English) -├── workflow/ # Workflow rules (English) -└── translate-to-english..js # Translation script -``` - -### memory-bank/ -``` -memory-bank/ -├── README.md # Main documentation (English) -├── INDEX.md # File structure (English) -├── MEMORY_BANK_STRUCTURE.md # Organization rules (English) -├── core/ # Core context files (English) -├── errors/ # Error documentation (English) -├── architecture/ # Architecture decisions (English) -├── development/ # Development process (English) -├── ui/ # UI/UX context (English) -├── planning/ # Planning docume.ts (English) -└── context/ # Contextual information (English) -``` - -## Next Steps - -### For Sharing -1. **GitHub Repository** - Ready for international community -2. **Documentation** - All in English for global understanding -3. **Commands** - Universal format for any AI assistant - -### For Development -1. **Continue using Russian Commands** - They still work perfectly -2. **Gradually adopt English** - For international collaboration -3. **Maintain bilingual support** - Best of both worlds - -### For AI Assista.ts -1. **Use English as primary** - For international compatibility -2. **Understand Russian alternatives** - For user convenience -3. **Maintain semantic understanding** - Language-agnostic processing - -## Conclusion - -The internationalization is complete and provIDEs: -- ✅ **Full English compatibility** for global community -- ✅ **Preserved Russian support** for your workflow -- ✅ **AI-optimized structure** for any AI assistant -- ✅ **Future-ready architecture** for international collaboration - -Your `.cursor` and `memory-bank` are now ready for the global AI development community! 🌍🤖 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/doc/universal-commands.mdc b/.cursor/rules/doc/universal-commands.mdc deleted file mode 100644 index cd9cb14f..00000000 --- a/.cursor/rules/doc/universal-commands.mdc +++ /dev/null @@ -1,133 +0,0 @@ -# Universal Commands Approach - -## 🎯 **Цель: Универсальность для международного сообщества** - -### **Проблема:** -- Команды на русском языке ограничивают международное Usage -- Другие разработчики не могут использовать ваш .cursor -- Отсутствие стандартизации команд - -### **Решение:** -- **Английский как основной язык** команд -- **Русский как альтернатива** для удобства -- **AI понимает Seeсл** независимо от языка - -## 📋 **Формат универсальных команд:** - -### **Структура:** -``` -`english Command` / `русская команда` / `альтернативная русская команда` - English description -``` - -### **examples:** -```markdown -- `create memory entry` / `создай запись в memory-bank` - Create new entry with automatic categorization -- `update context` / `обнови контекст` - Update activeContext.md with current status -- `restore context` / `восстанови контекст` - Restore full context from memory-bank -``` - -## 🌍 **Преимущества универсального подхода:** - -### **1. Международная Compatibility:** -- ✅ Любой разработчик может использовать ваш .cursor -- ✅ Стандартизация команд для сообщества -- ✅ Легкость понимания и адаптации - -### **2. AI-Optimization:** -- ✅ AI понимает Seeсл независимо от языка -- ✅ Единообразные Patterns команд -- ✅ Предсказуемое поведение - -### **3. Долгосрочная перспектива:** -- ✅ Не нужно Translationить при публикации -- ✅ Compatibility с будущими версиями Cursor -- ✅ Масштабируемость для новых команд - -## 🔧 **Практическое применение:** - -### **Для вас (русскоязычный пользователь):** -```bash -# Вы можете использовать любую команду: -"создай запись в memory-bank" # Русская команда -"create memory entry" # Английская команда -``` - -### **Для международного сообщества:** -```bash -# Другие разработчики используют английские команды: -"create memory entry" -"update context" -"restore context" -``` - -### **Для AI:** -```bash -# AI понимает Seeсл и выполняет одинаково: -"создай запись в memory-bank" → create memory entry -"create memory entry" → create memory entry -``` - -## 📊 **Статистика совместимости:** - -### **Поддерживаемые языки:** -- ✅ **Английский** - основной язык команд -- ✅ **Русский** - альтернативный язык для удобства -- ✅ **Любой другой** - AI понимает по Seeслу - -### **Охват аудитории:** -- 🌍 **100% международное сообщество** - английские команды -- 🇷🇺 **Русскоязычные разработчики** - русские команды -- 🤖 **AI ассистенты** - понимают любой язык - -## 🚀 **Recommendations по использованию:** - -### **1. При создании новых команд:** -```markdown -# ✅ Правильно: -- `new Command` / `новая команда` - English description - -# ❌ Неправильно: -- `новая команда` - Russian description only -``` - -### **2. При документировании:** -```markdown -# ✅ Правильно: -## Commands for AI Assistant Recognition -- `create memory entry` / `создай запись в memory-bank` - Create new entry - -# ❌ Неправильно: -## Команды для распознавания AI-ассистентом -- `создай запись в memory-bank` - Create новую запись -``` - -### **3. При публикации:** -- Используйте английский как основной язык -- Сохраняйте русские альтернативы для удобства -- Добавляйте описания на английском - -## 📈 **Планы развития:** - -### **Краткосрочные цели:** -- ✅ Перевести все команды на универсальный формат -- ✅ Update документацию -- ✅ Протестировать с международным сообществом - -### **Долгосрочные цели:** -- 🌍 Стандартизация команд для Cursor сообщества -- 📚 Создание Libraries универсальных команд -- 🔄 Автоматическая миграция существующих .cursor - -## ✅ **Результат:** - -**Универсальный подход обеспечивает:** -- 🌍 **Международную Compatibility** - любой разработчик может использовать -- 🤖 **AI-оптимизацию** - понимание независимо от языка -- 📈 **Масштабируемость** - легко добавлять новые команды -- 🔄 **Обратную Compatibility** - ваши русские команды продолжают работать - -**Ваш .cursor теперь готов для международного сообщества!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/documentation-helper.cjs b/.cursor/rules/documentation-helper.cjs deleted file mode 100644 index b7eee130..00000000 --- a/.cursor/rules/documentation-helper.cjs +++ /dev/null @@ -1,472 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class DocumentationHelper { - constructor() { - this.rulesDir = path.join(__dirname); - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.documentationMapPath = path.join(this.rulesDir, 'doc', 'documentation-map.mdc'); - } - - async documentExperience(content, type = 'auto') { - console.log('📝 Documenting experience...\n'); - - // Определяем тип контента - const contentType = type === 'auto' ? this.analyzeContent(content) : type; - - // Определяем файл назначения - const targetFile = this.getTargetFile(contentType, content); - - // Создаем или обновляем файл - await this.addToFile(targetFile, content, contentType); - - console.log(`✅ Documented in: ${targetFile}`); - console.log(`📋 Content type: ${contentType}`); - } - - analyzeContent(content) { - const text = content.toLowerCase(); - - // Ошибки и решения - if (text.includes('ошибка') || text.includes('error') || text.includes('failed') || text.includes('решение')) { - if (text.includes('безопасность') || text.includes('security')) { - return 'security-error'; - } - if (text.includes('производительность') || text.includes('performance')) { - return 'performance-error'; - } - if (text.includes('ui') || text.includes('ux') || text.includes('интерфейс')) { - return 'ui-error'; - } - return 'general-error'; - } - - // Best practices - if (text.includes('best practice') || text.includes('практика') || text.includes('паттерн')) { - if (text.includes('разработка') || text.includes('development') || text.includes('принципы')) { - return 'dev-principles'; - } - if (text.includes('документация') || text.includes('documentation')) { - return 'doc-practice'; - } - if (text.includes('тестирование') || text.includes('testing')) { - return 'testing-practice'; - } - if (text.includes('ci') || text.includes('cd') || text.includes('автоматизация')) { - return 'ci-practice'; - } - if (text.includes('плагин') || text.includes('plugin')) { - return 'plugin-practice'; - } - if (text.includes('дата') || text.includes('время') || text.includes('date') || text.includes('time')) { - return 'date-patterns'; - } - if (text.includes('файл') || text.includes('структура') || text.includes('file') || text.includes('structure')) { - return 'file-patterns'; - } - return 'general-practice'; - } - - // Автоматизация - if (text.includes('автоматизация') || text.includes('automation') || text.includes('workflow')) { - if (text.includes('ci') || text.includes('cd')) { - return 'ci-automation'; - } - if (text.includes('сборка') || text.includes('build')) { - return 'build-automation'; - } - if (text.includes('тестирование') || text.includes('testing')) { - return 'testing-automation'; - } - if (text.includes('деплой') || text.includes('deploy')) { - return 'deploy-automation'; - } - return 'general-automation'; - } - - // Архитектурные решения - if (text.includes('архитектура') || text.includes('architecture') || text.includes('структура')) { - if (text.includes('безопасность') || text.includes('security')) { - return 'security-architecture'; - } - if (text.includes('производительность') || text.includes('performance')) { - return 'performance-architecture'; - } - if (text.includes('плагин') || text.includes('plugin')) { - return 'plugin-architecture'; - } - return 'general-architecture'; - } - - // Проектно-специфичное - if (text.includes('проект') || text.includes('текущий') || text.includes('статус')) { - return 'project-specific'; - } - - return 'general'; - } - - getTargetFile(contentType, content) { - const date = new Date().toISOString().split('T')[0]; - - switch (contentType) { - // Ошибки → memory-bank/errors.md - case 'general-error': - case 'build-error': - case 'runtime-error': - return path.join(this.memoryBankDir, 'errors.md'); - - // Безопасность → .cursor/rules/security/validation.mdc - case 'security-error': - return path.join(this.rulesDir, 'security', 'validation.mdc'); - - // Производительность → .cursor/rules/architecture/architecture-performance.mdc - case 'performance-error': - return path.join(this.rulesDir, 'architecture', 'architecture-performance.mdc'); - - // UI/UX → .cursor/rules/ui/ui-*.mdc - case 'ui-error': - return path.join(this.rulesDir, 'ui', 'ui-error-handling.mdc'); - - // Best practices → .cursor/rules/ - case 'dev-principles': - return path.join(this.rulesDir, 'dev', 'development-principles.mdc'); - case 'dev-practice': - return path.join(this.rulesDir, 'dev', 'development-guidelines.mdc'); - case 'doc-practice': - return path.join(this.rulesDir, 'doc', 'ai-first.mdc'); - case 'testing-practice': - return path.join(this.rulesDir, 'dev', 'testing-troubleshooting.mdc'); - case 'ci-practice': - return path.join(this.rulesDir, 'workflow', 'ci-automation.mdc'); - case 'plugin-practice': - return path.join(this.rulesDir, 'plugin', 'plugin-best-practices.mdc'); - case 'date-patterns': - return path.join(this.rulesDir, 'dev', 'date-time-patterns.mdc'); - case 'file-patterns': - return path.join(this.rulesDir, 'architecture', 'file-relationships.mdc'); - - // Автоматизация → .cursor/rules/workflow/ - case 'ci-automation': - return path.join(this.rulesDir, 'workflow', 'ci-automation.mdc'); - case 'build-automation': - return path.join(this.rulesDir, 'dev', 'typescript-build-troubleshooting.mdc'); - case 'testing-automation': - return path.join(this.rulesDir, 'dev', 'testing-troubleshooting.mdc'); - case 'deploy-automation': - return path.join(this.rulesDir, 'workflow', 'automation.mdc'); - - // Архитектура → .cursor/rules/architecture/ - case 'security-architecture': - return path.join(this.rulesDir, 'architecture', 'architecture-security.mdc'); - case 'performance-architecture': - return path.join(this.rulesDir, 'architecture', 'architecture-performance.mdc'); - case 'plugin-architecture': - return path.join(this.rulesDir, 'architecture', 'architecture-plugin.mdc'); - - // Проектно-специфичное → memory-bank/ - case 'project-specific': - return path.join(this.memoryBankDir, 'activeContext.md'); - - // По умолчанию → memory-bank/errors.md - default: - return path.join(this.memoryBankDir, 'errors.md'); - } - } - - async addToFile(targetFile, content, contentType) { - // Создаем директорию если не существует - const dir = path.dirname(targetFile); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - const date = new Date().toISOString(); - const timestamp = `[${date}]`; - - // Определяем формат файла - const isMdcFile = targetFile.endsWith('.mdc'); - const isMemoryBank = targetFile.includes('memory-bank'); - - if (isMdcFile && !isMemoryBank) { - // .cursor/rules/ файл - добавляем в структурированном виде - await this.addToMdcFile(targetFile, content, contentType, timestamp); - } else { - // memory-bank файл - добавляем в хронологическом порядке - await this.addToMemoryBankFile(targetFile, content, contentType, timestamp); - } - } - - async addToMdcFile(filePath, content, contentType, timestamp) { - let fileContent = ''; - - if (fs.existsSync(filePath)) { - fileContent = fs.readFileSync(filePath, 'utf8'); - } else { - // Создаем новый .mdc файл с метаданными - fileContent = this.createMdcTemplate(contentType); - } - - // Добавляем контент в соответствующую секцию - const newSection = `\n## ${this.getSectionTitle(contentType)} (${timestamp})\n\n${content}\n`; - - // Ищем место для вставки (перед последней секцией) - const lines = fileContent.split('\n'); - const insertIndex = lines.length - 2; // Перед последней строкой - - lines.splice(insertIndex, 0, newSection); - - fs.writeFileSync(filePath, lines.join('\n')); - } - - async addToMemoryBankFile(filePath, content, contentType, timestamp) { - let fileContent = ''; - - if (fs.existsSync(filePath)) { - fileContent = fs.readFileSync(filePath, 'utf8'); - } - - // Добавляем в начало файла (новые записи сверху) - const newEntry = `\n## ${timestamp} - ${this.getEntryTitle(contentType)}\n\n${content}\n\n---\n`; - - fileContent = newEntry + fileContent; - - fs.writeFileSync(filePath, fileContent); - } - - createMdcTemplate(contentType) { - const description = this.getDescription(contentType); - const category = this.getCategory(contentType); - - return `--- -description: ${description} -globs: ["**/*"] -alwaysApply: true -aiPriority: high -aiCategory: ${category} ---- - -# ${this.getTitle(contentType)} - -${this.getInitialContent(contentType)} -`; - } - - getSectionTitle(contentType) { - const titles = { - 'security-error': 'Security Issue', - 'performance-error': 'Performance Issue', - 'ui-error': 'UI/UX Issue', - 'dev-practice': 'Development Practice', - 'doc-practice': 'Documentation Practice', - 'testing-practice': 'Testing Practice', - 'ci-practice': 'CI/CD Practice', - 'plugin-practice': 'Plugin Practice', - 'ci-automation': 'CI/CD Automation', - 'build-automation': 'Build Automation', - 'testing-automation': 'Testing Automation', - 'deploy-automation': 'Deployment Automation', - 'security-architecture': 'Security Architecture', - 'performance-architecture': 'Performance Architecture', - 'plugin-architecture': 'Plugin Architecture' - }; - - return titles[contentType] || 'New Entry'; - } - - getEntryTitle(contentType) { - const titles = { - 'general-error': 'General Error', - 'build-error': 'Build Error', - 'runtime-error': 'Runtime Error', - 'project-specific': 'Project Update' - }; - - return titles[contentType] || 'New Entry'; - } - - getDescription(contentType) { - const descriptions = { - 'security-error': 'Security validation and error handling', - 'performance-error': 'Performance optimization and monitoring', - 'ui-error': 'UI/UX error handling and best practices', - 'dev-practice': 'Development best practices and guidelines', - 'doc-practice': 'Documentation standards and practices', - 'testing-practice': 'Testing strategies and troubleshooting', - 'ci-practice': 'CI/CD practices and automation', - 'plugin-practice': 'Plugin development best practices', - 'ci-automation': 'CI/CD automation and workflows', - 'build-automation': 'Build automation and optimization', - 'testing-automation': 'Testing automation and tools', - 'deploy-automation': 'Deployment automation and processes', - 'security-architecture': 'Security architecture principles', - 'performance-architecture': 'Performance architecture guidelines', - 'plugin-architecture': 'Plugin architecture patterns' - }; - - return descriptions[contentType] || 'General guidelines and best practices'; - } - - getCategory(contentType) { - if (contentType.includes('error')) return 'error-handling'; - if (contentType.includes('practice')) return 'development-practices'; - if (contentType.includes('automation')) return 'process-management'; - if (contentType.includes('architecture')) return 'system-design'; - return 'general'; - } - - getTitle(contentType) { - const titles = { - 'security-error': 'Security Validation', - 'performance-error': 'Performance Guidelines', - 'ui-error': 'UI Error Handling', - 'dev-practice': 'Development Guidelines', - 'doc-practice': 'Documentation Standards', - 'testing-practice': 'Testing & Troubleshooting', - 'ci-practice': 'CI/CD Practices', - 'plugin-practice': 'Plugin Best Practices', - 'ci-automation': 'CI/CD Automation', - 'build-automation': 'Build Automation', - 'testing-automation': 'Testing Automation', - 'deploy-automation': 'Deployment Automation', - 'security-architecture': 'Security Architecture', - 'performance-architecture': 'Performance Architecture', - 'plugin-architecture': 'Plugin Architecture' - }; - - return titles[contentType] || 'General Guidelines'; - } - - getInitialContent(contentType) { - return `## Overview - -This file contains ${contentType.replace('-', ' ')} guidelines and best practices. - -## Guidelines - - -`; - } -} - -// Memory Bank Management -const memoryCommands = { - 'memory-add': 'Добавить запись в memory-bank', - 'memory-update': 'Обновить запись в memory-bank', - 'memory-restore': 'Восстановить контекст из memory-bank', - 'memory-search': 'Поиск в memory-bank', - 'memory-audit': 'Аудит memory-bank', - 'memory-structure': 'Создать структуру memory-bank', - 'memory-report': 'Генерировать отчет по memory-bank' -}; - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - if (args.length === 0) { - console.log('Usage: node documentation-helper.cjs [type]'); - console.log('Types: auto, error, practice, automation, architecture, project'); - console.log('\nMemory Bank Commands:'); - console.log(' memory-add [options] - Add entry to memory-bank'); - console.log(' memory-update - Update active context'); - console.log(' memory-restore [options] - Restore context from memory-bank'); - console.log(' memory-search [options] - Search in memory-bank'); - console.log(' memory-audit - Audit memory-bank structure'); - console.log(' memory-structure - Create memory-bank structure'); - console.log(' memory-report [type] - Generate memory-bank report'); - process.exit(1); - } - - const command = args[0]; - - // Обработка memory-bank команд - if (command.startsWith('memory-')) { - const MemoryBankManager = require('./memory-bank-manager.cjs'); - const manager = new MemoryBankManager(); - - switch (command) { - case 'memory-add': - const content = args[1]; - const options = parseOptions(args.slice(2)); - await manager.addEntry(content, options); - break; - case 'memory-update': - const contextData = parseContextData(args.slice(1)); - await manager.updateContext(contextData); - break; - case 'memory-restore': - const restoreOptions = parseOptions(args.slice(1)); - const context = await manager.restoreContext(restoreOptions); - console.log(JSON.stringify(context, null, 2)); - break; - case 'memory-search': - const query = args[1]; - const searchOptions = parseOptions(args.slice(2)); - const results = await manager.searchMemory(query, searchOptions); - console.log(JSON.stringify(results, null, 2)); - break; - case 'memory-audit': - const MemoryBankAuditor = require('./memory-bank-auditor.cjs'); - const auditor = new MemoryBankAuditor(); - await auditor.audit(); - break; - case 'memory-structure': - const projectType = args[1] || 'react-typescript'; - const MemoryBankStructureCreator = require('./memory-bank-structure-creator.cjs'); - const creator = new MemoryBankStructureCreator(); - await creator.createStructure(projectType); - break; - case 'memory-report': - const reportOptions = parseOptions(args.slice(1)); - const report = await manager.generateReport(reportOptions); - console.log(JSON.stringify(report, null, 2)); - break; - default: - console.log(`Unknown memory command: ${command}`); - process.exit(1); - } - return; - } - - // Стандартная обработка документации - const content = args[0]; - const type = args[1] || 'auto'; - - const helper = new DocumentationHelper(); - await helper.documentExperience(content, type); -} - -function parseOptions(args) { - const options = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - options[key] = value; - } - } - - return options; -} - -function parseContextData(args) { - const contextData = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - contextData[key] = value; - } - } - - return contextData; -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = DocumentationHelper; \ No newline at end of file diff --git a/.cursor/rules/environment.yaml b/.cursor/rules/environment.yaml deleted file mode 100644 index b7cb37c6..00000000 --- a/.cursor/rules/environment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -os: Manjaro Linux (Plasma) -os_version: 6.12.37-1-MANJARO -os_arch: x64 -cursor: - version: 1.2.4 - vscode_version: 1.99.3 - commit: a8e95743c5268be73767c46944a71f4465d05c90 - build_date: 2025-07-10T16:59:43.242Z - electron: 34.5.1 - chromium: 132.0.6834.210 - node: 20.19.0 - v8: 13.2.152.41-electron.0 -browser: - main: Chrome - main_version: 138.0.7204.100 - install: Flatpak - test: Chromium - test_version: 138.0.7204.100 - test_install: official repo (external), required for test automation -editor: Cursor -chrome_launch: Flatpak, custom args possible -debug_tools: - - Cursor - - Debugger for Chrome - - Composer Web -preferred_doc_lang: en -devtools_note: F12 only for main page, inspect sidepanel via context menu -extension_tab_name: Agent Platform Tools -ai_file_access: true # AI assistant has full access to all project files -cli_tools: - - git - - gh (GitHub CLI) - # Add more CLI tools as needed \ No newline at end of file diff --git a/.cursor/rules/export-cursor.cjs b/.cursor/rules/export-cursor.cjs deleted file mode 100644 index 51d326de..00000000 --- a/.cursor/rules/export-cursor.cjs +++ /dev/null @@ -1,303 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorExporter { - constructor() { - this.rulesDir = path.join(__dirname); - this.exportDir = path.join(process.cwd(), 'cursor-export'); - } - - async export(targetProject = null) { - console.log('📦 Exporting .cursor rules...\n'); - - // Создаем папку для экспорта - if (!fs.existsSync(this.exportDir)) { - fs.mkdirSync(this.exportDir, { recursive: true }); - } - - // Экспортируем основные файлы - await this.exportCoreFiles(); - - // Экспортируем правила по категориям - await this.exportRulesByCategory(); - - // Создаем инструкции по импорту - await this.createImportInstructions(); - - // Создаем скрипт импорта - await this.createImportScript(); - - console.log('✅ Export completed!'); - console.log(`📁 Exported to: ${this.exportDir}`); - - if (targetProject) { - console.log(`\n🚀 To import to ${targetProject}:`); - console.log(` cd ${targetProject}`); - console.log(` node cursor-export/import-cursor.cjs`); - } - } - - async exportCoreFiles() { - console.log('📋 Exporting core files...'); - - const coreFiles = [ - 'ai-memory.mdc', - 'environment.mdc', - 'index.mdc', - 'README.mdc', - 'ai-optimization.mdc', - 'ai-index.mdc', - 'monorepo-best-practices.mdc', - 'typescript-build-troubleshooting.mdc' - ]; - - for (const file of coreFiles) { - const sourcePath = path.join(this.rulesDir, file); - if (fs.existsSync(sourcePath)) { - const targetPath = path.join(this.exportDir, file); - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${file}`); - } - } - } - - async exportRulesByCategory() { - console.log('📁 Exporting rules by category...'); - - const categories = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - - for (const category of categories) { - const categoryDir = path.join(this.rulesDir, category); - if (fs.existsSync(categoryDir)) { - const targetCategoryDir = path.join(this.exportDir, category); - fs.mkdirSync(targetCategoryDir, { recursive: true }); - - const files = fs.readdirSync(categoryDir); - for (const file of files) { - if (file.endsWith('.mdc')) { - const sourcePath = path.join(categoryDir, file); - const targetPath = path.join(targetCategoryDir, file); - fs.copyFileSync(sourcePath, targetPath); - console.log(` ✅ ${category}/${file}`); - } - } - } - } - } - - async createImportInstructions() { - console.log('📝 Creating import instructions...'); - - const instructions = `# Import .cursor Rules - -## Quick Import - -1. Copy the entire \`cursor-export\` folder to your target project -2. Run the import script: - \`\`\`bash - node cursor-export/import-cursor.cjs - \`\`\` - -## Manual Import - -1. Create \`.cursor/rules/\` directory in your target project -2. Copy files from \`cursor-export/\` to \`.cursor/rules/\` -3. Run audit and optimization: - \`\`\`bash - cd .cursor/rules - node cursor-manager.cjs full - \`\`\` - -## What's Included - -### Core Rules -- AI memory and commands -- Environment constraints -- Project structure guidelines -- TypeScript build troubleshooting - -### Categories -- **architecture/** - System architecture rules -- **dev/** - Development principles and guidelines -- **doc/** - Documentation standards -- **plugin/** - Plugin development standards -- **security/** - Security rules -- **ui/** - UI/UX standards -- **workflow/** - Development workflow - -### Automation -- Audit system -- Auto-fix capabilities -- AI optimization -- Status monitoring - -## Customization - -After import, customize rules for your project: -1. Update \`environment.mdc\` with your project constraints -2. Modify \`ai-memory.mdc\` with project-specific commands -3. Adjust \`monorepo-best-practices.mdc\` for your structure -4. Run \`node cursor-manager.cjs optimize\` to apply changes - -## Verification - -Verify successful import: -\`\`\`bash -cd .cursor/rules -node cursor-manager.cjs status -\`\`\` - -All files should show as "AI-ready" with no issues. -`; - - const instructionsPath = path.join(this.exportDir, 'IMPORT-INSTRUCTIONS.md'); - fs.writeFileSync(instructionsPath, instructions); - console.log(' ✅ IMPORT-INSTRUCTIONS.md'); - } - - async createImportScript() { - console.log('🔧 Creating import script...'); - - const importScript = `#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class CursorImporter { - constructor() { - this.exportDir = __dirname; - this.targetRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async import() { - console.log('📥 Importing .cursor rules...\\n'); - - // Создаем папку .cursor/rules если не существует - if (!fs.existsSync(this.targetRulesDir)) { - fs.mkdirSync(this.targetRulesDir, { recursive: true }); - console.log('✅ Created .cursor/rules directory'); - } - - // Копируем все файлы - await this.copyFiles(); - - // Копируем скрипты автоматизации - await this.copyAutomationScripts(); - - // Создаем базовую структуру если нужно - await this.createBasicStructure(); - - console.log('\\n✅ Import completed!'); - console.log('\\n🔧 Next steps:'); - console.log(' 1. Customize environment.mdc for your project'); - console.log(' 2. Update ai-memory.mdc with project-specific commands'); - console.log(' 3. Run: cd .cursor/rules && node cursor-manager.cjs full'); - } - - async copyFiles() { - console.log('📋 Copying rule files...'); - - const items = fs.readdirSync(this.exportDir); - - for (const item of items) { - const sourcePath = path.join(this.exportDir, item); - const targetPath = path.join(this.targetRulesDir, item); - - const stat = fs.statSync(sourcePath); - - if (stat.isDirectory()) { - // Копируем директории - if (!fs.existsSync(targetPath)) { - fs.mkdirSync(targetPath, { recursive: true }); - } - - const files = fs.readdirSync(sourcePath); - for (const file of files) { - if (file.endsWith('.mdc')) { - const fileSource = path.join(sourcePath, file); - const fileTarget = path.join(targetPath, file); - fs.copyFileSync(fileSource, fileTarget); - console.log(\` ✅ \${item}/\${file}\`); - } - } - } else if (item.endsWith('.mdc')) { - // Копируем .mdc файлы - fs.copyFileSync(sourcePath, targetPath); - console.log(\` ✅ \${item}\`); - } - } - } - - async copyAutomationScripts() { - console.log('🔧 Copying automation scripts...'); - - const scripts = [ - 'audit-cursor.cjs', - 'fix-cursor.cjs', - 'optimize-for-ai.cjs', - 'cursor-manager.cjs' - ]; - - for (const script of scripts) { - const sourcePath = path.join(this.exportDir, script); - if (fs.existsSync(sourcePath)) { - const targetPath = path.join(this.targetRulesDir, script); - fs.copyFileSync(sourcePath, targetPath); - console.log(\` ✅ \${script}\`); - } - } - } - - async createBasicStructure() { - console.log('🏗️ Creating basic structure...'); - - const requiredDirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - - for (const dir of requiredDirs) { - const dirPath = path.join(this.targetRulesDir, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - console.log(\` ✅ Created \${dir}/\`); - } - } - } -} - -// Запуск импорта -async function main() { - const importer = new CursorImporter(); - await importer.import(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorImporter; -`; - - const importScriptPath = path.join(this.exportDir, 'import-cursor.cjs'); - fs.writeFileSync(importScriptPath, importScript); - - // Делаем скрипт исполняемым - fs.chmodSync(importScriptPath, '755'); - console.log(' ✅ import-cursor.cjs'); - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - const targetProject = args[0] || null; - - const exporter = new CursorExporter(); - await exporter.export(targetProject); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorExporter; \ No newline at end of file diff --git a/.cursor/rules/fix-cursor.cjs b/.cursor/rules/fix-cursor.cjs deleted file mode 100644 index c619901f..00000000 --- a/.cursor/rules/fix-cursor.cjs +++ /dev/null @@ -1,350 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); -const CursorAuditor = require('./audit-cursor.cjs'); - -class CursorFixer { - constructor() { - this.rulesDir = path.join(__dirname); - this.auditor = new CursorAuditor(); - } - - async fix() { - console.log('🔧 Starting automatic .cursor fixes...\n'); - - // Сначала проводим аудит - const stats = await this.auditor.audit(); - - console.log('\n🔧 Applying fixes...\n'); - - // Исправляем метаданные - if (stats.invalidMetadata.length > 0 || stats.filesWithoutMetadata > 0) { - await this.fixMetadata(); - } - - // Удаляем дубликаты - if (stats.duplicateContent.length > 0) { - await this.removeDuplicates(stats.duplicateContent); - } - - // Исправляем ссылки - if (stats.brokenLinks.length > 0) { - await this.fixLinks(stats.brokenLinks); - } - - // Обновляем индекс - await this.updateIndex(); - - console.log('\n✅ All fixes applied!'); - } - - async fixMetadata() { - console.log('📋 Fixing metadata...'); - - const mdcFiles = this.auditor.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - - if (!content.startsWith('---')) { - // Добавляем метаданные - const fileName = path.basename(file, '.mdc'); - const metadata = this.generateMetadata(fileName, file); - - const newContent = metadata + content; - fs.writeFileSync(file, newContent); - console.log(` ✅ Added metadata to ${path.relative(this.rulesDir, file)}`); - } else { - // Проверяем и исправляем существующие метаданные - const fixedContent = this.fixExistingMetadata(content, file); - if (fixedContent !== content) { - fs.writeFileSync(file, fixedContent); - console.log(` ✅ Fixed metadata in ${path.relative(this.rulesDir, file)}`); - } - } - } - } - - generateMetadata(fileName, filePath) { - const dirName = path.basename(path.dirname(filePath)); - - let description = ''; - let globs = '["**/*"]'; - let alwaysApply = 'false'; - - // Определяем описание на основе имени файла и директории - if (fileName.includes('principle')) { - description = `Development principle ${fileName.split('-').pop()} - ${fileName.replace(/-/g, ' ')}`; - alwaysApply = 'true'; - } else if (fileName.includes('architecture')) { - description = `Architecture rule for ${fileName.replace('architecture-', '')}`; - alwaysApply = 'true'; - } else if (fileName.includes('ui-')) { - description = `UI standard for ${fileName.replace('ui-', '')}`; - globs = '["pages/**/*", "**/*.tsx", "**/*.css"]'; - } else if (fileName.includes('plugin')) { - description = `Plugin standard for ${fileName.replace('plugin-', '')}`; - globs = '["plugins/**/*", "**/*.py", "**/*.json"]'; - } else if (fileName.includes('workflow')) { - description = `Workflow rule for ${fileName.replace('workflow', '')}`; - alwaysApply = 'true'; - } else if (fileName.includes('security')) { - description = `Security rule for ${fileName.replace('security', '')}`; - alwaysApply = 'true'; - } else if (fileName.includes('validation')) { - description = 'Input validation and data sanitization rules'; - alwaysApply = 'true'; - } else if (fileName.includes('testing')) { - description = 'Testing and debugging standards'; - globs = '["tests/**/*", "**/*.test.*", "**/*.spec.*"]'; - } else if (fileName.includes('typescript')) { - description = 'TypeScript-specific guidelines'; - globs = '["**/*.ts", "**/*.tsx", "**/*.json"]'; - } else if (fileName.includes('development')) { - description = 'General development guidelines'; - alwaysApply = 'true'; - } else if (fileName.includes('automation')) { - description = 'Automation and synchronization rules'; - alwaysApply = 'true'; - } else if (fileName.includes('branches')) { - description = 'Git branch management rules'; - alwaysApply = 'true'; - } else if (fileName.includes('knowledge')) { - description = 'Project knowledge structure'; - alwaysApply = 'true'; - } else if (fileName.includes('memorybank')) { - description = 'Quality standards for memory-bank'; - alwaysApply = 'true'; - } else if (fileName.includes('restore')) { - description = 'Context restoration procedures'; - alwaysApply = 'true'; - } else if (fileName.includes('ai-first')) { - description = 'AI-oriented documentation standards'; - globs = '["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"]'; - alwaysApply = 'true'; - } else if (fileName.includes('ai-fallback')) { - description = 'Fallback procedures for AI'; - alwaysApply = 'true'; - } else if (fileName.includes('mdc-file-standards')) { - description = 'Standards for .mdc file creation'; - alwaysApply = 'true'; - } else if (fileName === 'index') { - description = 'Rules index and navigation'; - alwaysApply = 'true'; - } else if (fileName === 'README') { - description = 'Main documentation and structure'; - alwaysApply = 'true'; - } else if (fileName === 'ai-memory') { - description = 'User commands and AI instructions'; - alwaysApply = 'true'; - } else if (fileName === 'environment') { - description = 'Critical environment limitations'; - alwaysApply = 'true'; - } else if (fileName === 'monorepo-best-practices') { - description = 'Monorepo structure and guidelines'; - alwaysApply = 'true'; - } else if (fileName === 'typescript-build-troubleshooting') { - description = 'TypeScript build error solutions'; - alwaysApply = 'true'; - } else { - description = `${fileName.replace(/-/g, ' ')} rule`; - } - - return `--- -description: ${description} -globs: ${globs} -alwaysApply: ${alwaysApply} ---- - -`; - } - - fixExistingMetadata(content, filePath) { - const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (!metadataMatch) return content; - - const metadata = metadataMatch[1]; - const fileName = path.basename(filePath, '.mdc'); - - let newMetadata = metadata; - let hasChanges = false; - - // Проверяем и добавляем недостающие поля - if (!metadata.includes('description:')) { - const description = this.generateMetadata(fileName, filePath).match(/description: (.*)/)[1]; - newMetadata = `description: ${description}\n${newMetadata}`; - hasChanges = true; - } - - if (!metadata.includes('globs:')) { - newMetadata = `${newMetadata}globs: ["**/*"]\n`; - hasChanges = true; - } - - if (!metadata.includes('alwaysApply:')) { - newMetadata = `${newMetadata}alwaysApply: false\n`; - hasChanges = true; - } - - if (hasChanges) { - return content.replace(metadataMatch[0], `---\n${newMetadata}---\n\n`); - } - - return content; - } - - async removeDuplicates(duplicates) { - console.log('🔄 Removing duplicates...'); - - for (const dup of duplicates) { - const duplicatePath = path.join(this.rulesDir, dup.duplicate); - - if (fs.existsSync(duplicatePath)) { - fs.unlinkSync(duplicatePath); - console.log(` ✅ Removed duplicate: ${dup.duplicate}`); - } - } - } - - async fixLinks(brokenLinks) { - console.log('🔗 Fixing broken links...'); - - // Группируем по файлам - const linksByFile = {}; - for (const link of brokenLinks) { - if (!linksByFile[link.file]) { - linksByFile[link.file] = []; - } - linksByFile[link.file].push(link.link); - } - - for (const [file, links] of Object.entries(linksByFile)) { - const filePath = path.join(this.rulesDir, file); - let content = fs.readFileSync(filePath, 'utf8'); - - for (const link of links) { - // Пытаемся найти правильную ссылку - const fixedLink = this.findCorrectLink(link); - if (fixedLink) { - content = content.replace(new RegExp(`\\(${link.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)`, 'g'), `(${fixedLink})`); - console.log(` ✅ Fixed link in ${file}: ${link} → ${fixedLink}`); - } - } - - fs.writeFileSync(filePath, content); - } - } - - findCorrectLink(brokenLink) { - // Простые исправления для типичных случаев - const fixes = { - 'doc/user-commands.mdc': 'ai-memory.mdc', - 'dev/dev-principle-02-ai-first-docs.mdc': 'doc/ai-first.mdc', - 'ui/accessibility.mdc': 'ui/ui-accessibility.mdc' - }; - - return fixes[brokenLink] || null; - } - - async updateIndex() { - console.log('📝 Updating index...'); - - // Генерируем новый индекс на основе существующих файлов - const indexContent = this.generateIndex(); - const indexPath = path.join(this.rulesDir, 'index.mdc'); - - fs.writeFileSync(indexPath, indexContent); - console.log(' ✅ Updated index.mdc'); - } - - generateIndex() { - const files = this.auditor.getAllFiles(this.rulesDir); - const structure = {}; - - // Группируем файлы по директориям - for (const file of files) { - const relativePath = path.relative(this.rulesDir, file); - const dir = path.dirname(relativePath); - const name = path.basename(file, path.extname(file)); - - if (!structure[dir]) { - structure[dir] = []; - } - - structure[dir].push({ - name: name, - path: relativePath, - ext: path.extname(file) - }); - } - - // Генерируем содержимое индекса - let content = `--- -description: Rules index and navigation -globs: ["**/*"] -alwaysApply: true ---- - -# Index of Modular Rules - -`; - - // Корневые файлы - if (structure['.']) { - content += '## root\n'; - for (const file of structure['.'].filter(f => f.ext === '.mdc')) { - const description = this.getFileDescription(file.name); - content += `- [${this.formatName(file.name)}](${file.path}) — ${description}\n`; - } - content += '\n'; - } - - // Директории - const dirs = ['architecture', 'dev', 'doc', 'plugin', 'security', 'ui', 'workflow']; - for (const dir of dirs) { - if (structure[dir] && structure[dir].length > 0) { - content += `## ${dir}\n`; - for (const file of structure[dir].filter(f => f.ext === '.mdc')) { - const description = this.getFileDescription(file.name); - content += `- [${this.formatName(file.name)}](${file.path}) — ${description}\n`; - } - content += '\n'; - } - } - - return content; - } - - getFileDescription(fileName) { - const descriptions = { - 'ai-memory': 'User commands and AI instructions', - 'environment': 'Critical environment limitations', - 'monorepo-best-practices': 'Monorepo structure and guidelines', - 'typescript-build-troubleshooting': 'TypeScript build error solutions', - 'README': 'Main documentation and structure', - 'index': 'Rules index and navigation' - }; - - return descriptions[fileName] || `${fileName.replace(/-/g, ' ')} rule`; - } - - formatName(fileName) { - return fileName - .split('-') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - } -} - -// Запуск исправлений -async function main() { - const fixer = new CursorFixer(); - await fixer.fix(); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = CursorFixer; \ No newline at end of file diff --git a/.cursor/rules/generate-rules-index.cjs b/.cursor/rules/generate-rules-index.cjs deleted file mode 100644 index 6bc45760..00000000 --- a/.cursor/rules/generate-rules-index.cjs +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env node -const fs = require('fs'); -const path = require('path'); - -const RULES_DIR = path.join(__dirname); -const INDEX_FILE = path.join(RULES_DIR, 'index.mdc'); -const README_FILE = path.join(RULES_DIR, 'README.md'); - -function getRuleFiles(dir = RULES_DIR, rel = '') { - let files = []; - for (const f of fs.readdirSync(dir)) { - const abs = path.join(dir, f); - const relPath = rel ? path.join(rel, f) : f; - if (fs.statSync(abs).isDirectory()) { - files = files.concat(getRuleFiles(abs, relPath)); - } else if (f.endsWith('.mdc') && !['index.mdc', 'generate-rules-index.js'].includes(f)) { - files.push(relPath); - } - } - return files; -} - -function extractMeta(filePath) { - const content = fs.readFileSync(path.join(RULES_DIR, filePath), 'utf8'); - const lines = content.split('\n'); - const heading = lines.find(l => l.startsWith('#')) || ''; - const descLine = lines.find(l => l.startsWith('description:')) || ''; - const description = descLine.replace('description:', '').trim(); - return { heading: heading.replace(/^#+\s*/, ''), description }; -} - -function generateIndex(ruleFiles) { - let out = '# Index of Modular Rules\n\n'; - // Group by top-level folder - const groups = {}; - for (const f of ruleFiles) { - const parts = f.split(path.sep); - const group = parts.length > 1 ? parts[0] : 'root'; - if (!groups[group]) groups[group] = []; - groups[group].push(f); - } - for (const group of Object.keys(groups).sort()) { - if (group !== 'root') out += `## ${group}\n`; - groups[group].sort().forEach(f => { - const meta = extractMeta(f); - out += `- [${meta.heading || f}](${f})${meta.description ? ' — ' + meta.description : ''}\n`; - }); - out += '\n'; - } - out += 'description:\nglobs:\nalwaysApply: false\n---\n'; - return out; -} - -function updateReadmeStructure(ruleFiles) { - // Build tree structure - function tree(files) { - const t = {}; - for (const f of files) { - const parts = f.split(path.sep); - let cur = t; - for (let i = 0; i < parts.length; ++i) { - if (!cur[parts[i]]) cur[parts[i]] = (i === parts.length - 1) ? null : {}; - cur = cur[parts[i]]; - } - } - return t; - } - function printTree(t, indent = ' ') { - let out = []; - for (const k of Object.keys(t).sort()) { - out.push(indent + k + (t[k] ? '/' : '')); - if (t[k]) out.push(printTree(t[k], indent + ' ')); - } - return out.join('\n'); - } - const t = tree(ruleFiles); - const structure = [ - '```', - '.cursor/', - ' rules/', - printTree(t), - '```', - '' - ].join('\n'); - let readme = fs.readFileSync(README_FILE, 'utf8'); - readme = readme.replace(/```[\s\S]*?```/, structure); - fs.writeFileSync(README_FILE, readme, 'utf8'); -} - -function main() { - const ruleFiles = getRuleFiles(); - const indexContent = generateIndex(ruleFiles); - fs.writeFileSync(INDEX_FILE, indexContent, 'utf8'); - updateReadmeStructure(ruleFiles); - console.log('index.mdc and README.md structure updated.'); -} - -if (require.main === module) { - main(); -} \ No newline at end of file diff --git a/.cursor/rules/memory-bank-auditor.cjs b/.cursor/rules/memory-bank-auditor.cjs deleted file mode 100644 index 1bb7380b..00000000 --- a/.cursor/rules/memory-bank-auditor.cjs +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class MemoryBankAuditor { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.cursorRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async audit() { - console.log('🔍 Auditing Memory Bank for files that should be in .cursor/rules/...\n'); - - const files = await this.getAllFiles(); - const analysis = await this.analyzeFiles(files); - - this.printReport(analysis); - - return analysis; - } - - async getAllFiles() { - const files = []; - - const walkDir = (dir) => { - const items = fs.readdirSync(dir); - - for (const item of items) { - const fullPath = path.join(dir, item); - const stat = fs.statSync(fullPath); - - if (stat.isDirectory()) { - walkDir(fullPath); - } else if (item.endsWith('.md')) { - files.push({ - path: fullPath, - relativePath: path.relative(this.memoryBankDir, fullPath), - name: item, - category: path.dirname(path.relative(this.memoryBankDir, fullPath)) - }); - } - } - }; - - walkDir(this.memoryBankDir); - return files; - } - - async analyzeFiles(files) { - const analysis = { - shouldMoveToCursor: [], - shouldStayInMemoryBank: [], - unclear: [], - totalFiles: files.length - }; - - for (const file of files) { - const content = fs.readFileSync(file.path, 'utf8'); - const decision = this.analyzeFile(file, content); - - switch (decision.category) { - case 'cursor': - analysis.shouldMoveToCursor.push({ ...file, reason: decision.reason }); - break; - case 'memory-bank': - analysis.shouldStayInMemoryBank.push({ ...file, reason: decision.reason }); - break; - case 'unclear': - analysis.unclear.push({ ...file, reason: decision.reason }); - break; - } - } - - return analysis; - } - - analyzeFile(file, content) { - const text = content.toLowerCase(); - const name = file.name.toLowerCase(); - - // Критерии для .cursor/rules/ - const cursorIndicators = [ - // Универсальные принципы и правила - 'принципы разработки', 'development principles', 'best practices', - 'паттерны', 'patterns', 'стандарты', 'standards', - 'правила', 'rules', 'guidelines', 'руководства', - - // Автоматизация и workflow - 'автоматизация', 'automation', 'workflow', 'процессы', - 'ci/cd', 'continuous integration', 'deployment', - - // Документация и структуры - 'документация', 'documentation', 'структура', 'structure', - 'форматы', 'formats', 'шаблоны', 'templates', - - // UI/UX стандарты - 'ui/ux', 'user interface', 'user experience', 'интерфейс', - 'стили', 'styles', 'компоненты', 'components', - - // Безопасность - 'безопасность', 'security', 'валидация', 'validation', - - // Технические паттерны - 'паттерны работы', 'date patterns', 'time patterns', - 'file relationships', 'взаимосвязи файлов' - ]; - - // Критерии для memory-bank/ - const memoryBankIndicators = [ - // Проектно-специфичное - 'agent-plugins-platform', 'текущий проект', 'current project', - 'статус проекта', 'project status', 'прогресс', 'progress', - - // Конкретные ошибки и решения - 'ошибка', 'error', 'баг', 'bug', 'решение', 'solution', - 'vite-react19', 'typescript-build', 'конкретная проблема', - - // Архитектурные решения проекта - 'архитектура проекта', 'project architecture', 'решения проекта', - 'принятые решения', 'decisions made', - - // Результаты тестирования - 'результаты тестов', 'test results', 'отладка', 'debugging', - - // Планы развития - 'планы развития', 'development plans', 'roadmap', 'roadmap проекта', - - // Контекст проекта - 'контекст проекта', 'project context', 'окружение', 'environment' - ]; - - // Проверяем индикаторы - const cursorScore = cursorIndicators.filter(indicator => - text.includes(indicator) || name.includes(indicator) - ).length; - - const memoryBankScore = memoryBankIndicators.filter(indicator => - text.includes(indicator) || name.includes(indicator) - ).length; - - // Специальные случаи - if (name.includes('readme') || name.includes('index')) { - return { category: 'memory-bank', reason: 'README и INDEX файлы должны оставаться в memory-bank' }; - } - - if (name.includes('development-principles') || text.includes('принципы разработки')) { - return { category: 'cursor', reason: 'Универсальные принципы разработки применимы к любым проектам' }; - } - - if (name.includes('file-relationships') || text.includes('взаимосвязи файлов')) { - return { category: 'cursor', reason: 'Паттерны организации файлов универсальны' }; - } - - if (name.includes('date-time-patterns') || text.includes('паттерны работы с датами')) { - return { category: 'cursor', reason: 'Универсальные паттерны работы с датами и временем' }; - } - - if (name.includes('errors') || name.includes('error') || name.includes('graveyard')) { - return { category: 'memory-bank', reason: 'Проектно-специфичные ошибки и решения' }; - } - - if (name.includes('architecture') || name.includes('systempattern')) { - return { category: 'memory-bank', reason: 'Архитектурные решения конкретного проекта' }; - } - - if (name.includes('testing') || name.includes('debug')) { - return { category: 'memory-bank', reason: 'Результаты тестирования и отладки проекта' }; - } - - if (name.includes('progress') || name.includes('activecontext')) { - return { category: 'memory-bank', reason: 'Текущий статус и прогресс проекта' }; - } - - // Принимаем решение на основе баллов - if (cursorScore > memoryBankScore && cursorScore > 0) { - return { - category: 'cursor', - reason: `Найдено ${cursorScore} индикаторов для .cursor/rules/ (vs ${memoryBankScore} для memory-bank/)` - }; - } else if (memoryBankScore > cursorScore && memoryBankScore > 0) { - return { - category: 'memory-bank', - reason: `Найдено ${memoryBankScore} индикаторов для memory-bank/ (vs ${cursorScore} для .cursor/rules/)` - }; - } else { - return { - category: 'unclear', - reason: `Неясно: cursorScore=${cursorScore}, memoryBankScore=${memoryBankScore}` - }; - } - } - - printReport(analysis) { - console.log('📊 MEMORY BANK AUDIT REPORT'); - console.log('='.repeat(60)); - - console.log(`📁 Total files analyzed: ${analysis.totalFiles}`); - console.log(`🔄 Should move to .cursor/rules/: ${analysis.shouldMoveToCursor.length}`); - console.log(`🧠 Should stay in memory-bank/: ${analysis.shouldStayInMemoryBank.length}`); - console.log(`❓ Unclear: ${analysis.unclear.length}`); - - if (analysis.shouldMoveToCursor.length > 0) { - console.log('\n🔄 FILES THAT SHOULD MOVE TO .cursor/rules/:'); - console.log('-'.repeat(60)); - - for (const file of analysis.shouldMoveToCursor) { - console.log(`📄 ${file.relativePath}`); - console.log(` Reason: ${file.reason}`); - console.log(` Suggested location: .cursor/rules/${this.getSuggestedLocation(file)}`); - console.log(''); - } - } - - if (analysis.unclear.length > 0) { - console.log('\n❓ UNCLEAR FILES (need manual review):'); - console.log('-'.repeat(60)); - - for (const file of analysis.unclear) { - console.log(`📄 ${file.relativePath}`); - console.log(` Reason: ${file.reason}`); - console.log(''); - } - } - - if (analysis.shouldMoveToCursor.length === 0 && analysis.unclear.length === 0) { - console.log('\n✅ All files are in the correct location!'); - } else { - console.log('\n💡 RECOMMENDATIONS:'); - console.log('-'.repeat(60)); - - if (analysis.shouldMoveToCursor.length > 0) { - console.log('1. Move identified files to .cursor/rules/ with proper metadata'); - console.log('2. Update documentation-helper.cjs to handle new file types'); - console.log('3. Update documentation-map.mdc if needed'); - } - - if (analysis.unclear.length > 0) { - console.log('4. Manually review unclear files'); - console.log('5. Update analysis criteria based on findings'); - } - } - - console.log('\n' + '='.repeat(60)); - } - - getSuggestedLocation(file) { - const name = file.name.toLowerCase(); - - if (name.includes('development-principles')) { - return 'dev/dev-principle-01-do-no-harm.mdc'; - } - - if (name.includes('file-relationships')) { - return 'architecture/architecture-project-structure.mdc'; - } - - if (name.includes('date-time-patterns')) { - return 'dev/date-time-patterns.mdc'; - } - - if (name.includes('patterns')) { - return 'dev/patterns.mdc'; - } - - if (name.includes('standards')) { - return 'doc/standards.mdc'; - } - - return 'general/'; - } - - async generateMigrationScript(analysis) { - if (analysis.shouldMoveToCursor.length === 0) { - console.log('No files to migrate.'); - return; - } - - const scriptPath = path.join(process.cwd(), 'migrate-to-cursor.sh'); - let script = '#!/bin/bash\n\n'; - script += '# Migration script for memory-bank files to .cursor/rules/\n'; - script += '# Generated by memory-bank-auditor.cjs\n\n'; - - for (const file of analysis.shouldMoveToCursor) { - const targetPath = this.getSuggestedLocation(file); - script += `# ${file.reason}\n`; - script += `# mv "${file.path}" ".cursor/rules/${targetPath}"\n\n`; - } - - fs.writeFileSync(scriptPath, script); - console.log(`📝 Migration script generated: ${scriptPath}`); - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - const auditor = new MemoryBankAuditor(); - - if (args.includes('--generate-script')) { - const analysis = await auditor.audit(); - await auditor.generateMigrationScript(analysis); - } else { - await auditor.audit(); - } -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankAuditor; \ No newline at end of file diff --git a/.cursor/rules/memory-bank-manager.cjs b/.cursor/rules/memory-bank-manager.cjs deleted file mode 100644 index c14aeaa1..00000000 --- a/.cursor/rules/memory-bank-manager.cjs +++ /dev/null @@ -1,622 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class MemoryBankManager { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.cursorRulesDir = path.join(process.cwd(), '.cursor', 'rules'); - } - - async addEntry(content, options = {}) { - const { - type = 'info', - category = 'core', - priority = 'medium', - tags = options.tags ? options.tags.split(',') : [], - relatedFiles = [] - } = options; - - const timestamp = new Date().toISOString(); - const date = timestamp.split('T')[0]; - const time = timestamp.split('T')[1].split('.')[0]; - - // Определяем файл назначения - const targetFile = this.getTargetFile(type, category); - const targetPath = path.join(this.memoryBankDir, targetFile); - - // Создаем запись - const entry = this.formatEntry({ - content, - type, - category, - priority, - tags, - relatedFiles, - timestamp: `${date} ${time}`, - date - }); - - // Добавляем запись в файл - await this.appendToFile(targetPath, entry); - - console.log(`✅ Added entry to ${targetFile}`); - console.log(`📝 Type: ${type}, Category: ${category}, Priority: ${priority}`); - - return { targetFile, entry }; - } - - async updateContext(contextData) { - const contextFile = path.join(this.memoryBankDir, 'core', 'activeContext.md'); - const timestamp = new Date().toISOString().split('T')[0]; - - let content = fs.existsSync(contextFile) - ? fs.readFileSync(contextFile, 'utf8') - : this.generateActiveContextTemplate(); - - // Обновляем секцию Current Status - content = this.updateSection(content, 'Current Status', { - 'Last Updated': timestamp, - 'Current Focus': contextData.focus || 'Development', - 'Active Tasks': contextData.tasks || ['Initial setup'], - 'Next Steps': contextData.nextSteps || ['Continue development'], - 'Blockers': contextData.blockers || [] - }); - - // Добавляем новую запись в Recent Decisions - if (contextData.decisions) { - content = this.addToSection(content, 'Recent Decisions', contextData.decisions); - } - - fs.writeFileSync(contextFile, content); - console.log('✅ Updated active context'); - } - - async restoreContext(options = {}) { - const { full = false, category = null } = options; - - if (full) { - return await this.restoreFullContext(); - } - - if (category) { - return await this.restoreCategoryContext(category); - } - - return await this.restoreQuickContext(); - } - - async restoreFullContext() { - const context = { - activeContext: await this.readFile('core/activeContext.md'), - progress: await this.readFile('core/progress.md'), - errors: await this.readFile('errors/errors.md'), - decisions: await this.readFile('architecture/decisions.md'), - recentActivity: await this.getRecentActivity() - }; - - console.log('📚 Full context restored'); - return context; - } - - async restoreQuickContext() { - const context = { - activeContext: await this.readFile('core/activeContext.md'), - recentErrors: await this.getRecentErrors(), - recentDecisions: await this.getRecentDecisions() - }; - - console.log('⚡ Quick context restored'); - return context; - } - - async restoreCategoryContext(category) { - const categoryDir = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryDir)) { - throw new Error(`Category ${category} not found`); - } - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md')) - .map(file => path.join(category, file)); - - const context = {}; - for (const file of files) { - context[file] = await this.readFile(file); - } - - console.log(`📁 Category context restored: ${category}`); - return context; - } - - async searchMemory(query, options = {}) { - const { category = null, type = null, limit = 10 } = options; - - const results = []; - const searchDirs = category - ? [path.join(this.memoryBankDir, category)] - : this.getAllCategoryDirs(); - - for (const dir of searchDirs) { - if (!fs.existsSync(dir)) continue; - - const files = fs.readdirSync(dir) - .filter(file => file.endsWith('.md')); - - for (const file of files) { - const filePath = path.join(dir, file); - const content = fs.readFileSync(filePath, 'utf8'); - - if (content.toLowerCase().includes(query.toLowerCase())) { - const relativePath = path.relative(this.memoryBankDir, filePath); - results.push({ - file: relativePath, - content: this.extractRelevantContent(content, query), - lastModified: fs.statSync(filePath).mtime - }); - } - } - } - - // Сортируем по дате изменения и ограничиваем результаты - results.sort((a, b) => b.lastModified - a.lastModified); - return results.slice(0, limit); - } - - async generateReport(options = {}) { - const { type = 'full' } = options; - - const report = { - timestamp: new Date().toISOString(), - summary: await this.generateSummary(), - categories: await this.generateCategoryReport(), - recentActivity: await this.getRecentActivity(), - errorStats: await this.generateErrorStats(), - recommendations: await this.generateRecommendations() - }; - - if (type === 'errors') { - return { errorStats: report.errorStats }; - } - - if (type === 'progress') { - return { - summary: report.summary, - recentActivity: report.recentActivity - }; - } - - return report; - } - - // Вспомогательные методы - - getTargetFile(type, category) { - const fileMap = { - 'error': 'errors/errors.md', - 'build-error': 'errors/build-errors.md', - 'runtime-error': 'errors/runtime-errors.md', - 'ui-error': 'errors/ui-errors.md', - 'decision': 'architecture/decisions.md', - 'pattern': 'architecture/patterns.md', - 'progress': 'core/progress.md', - 'context': 'core/activeContext.md', - 'plan': 'planning/feature-roadmap.md', - 'test-result': 'development/testing-results.md', - 'debug-note': 'development/debugging-guide.md' - }; - - return fileMap[type] || `${category}/general.md`; - } - - formatEntry(data) { - const { - content, - type, - category, - priority, - tags, - relatedFiles, - timestamp, - date - } = data; - - const tagsArray = Array.isArray(tags) ? tags : (typeof tags === 'string' ? tags.split(',') : []); - const tagsStr = tagsArray.length > 0 ? tagsArray.map(tag => `#${tag.trim()}`).join(' ') : ''; - const relatedStr = relatedFiles.length > 0 - ? relatedFiles.map(file => `- ${file}`).join('\n') - : ''; - - const title = content ? content.split('\n')[0] : 'New Entry'; - - return ` -## [${timestamp}] - ${title} - -**Тип:** ${type} -**Категория:** ${category} -**Приоритет:** ${priority} - -**Контекст:** ${content || 'No content provided'} - -**Статус:** 🔄 В процессе - -${relatedStr ? `**Связанные файлы:**\n${relatedStr}\n` : ''} -${tagsStr ? `**Теги:** ${tagsStr}\n` : ''} -**AI Команды:** -- \`обнови контекст\` - для обновления активного контекста -- \`задокументируй\` - для создания документации - ---- -`; - } - - async appendToFile(filePath, content) { - const dir = path.dirname(filePath); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - - if (!fs.existsSync(filePath)) { - const header = this.generateFileHeader(filePath); - fs.writeFileSync(filePath, header); - } - - const existingContent = fs.readFileSync(filePath, 'utf8'); - const newContent = existingContent + content; - fs.writeFileSync(filePath, newContent); - } - - generateFileHeader(filePath) { - const filename = path.basename(filePath, '.md'); - const category = path.dirname(filePath).split(path.sep).pop(); - - return `# ${filename.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())} - ${category} - -## Overview - -This file contains ${category}-related information and entries. - -## Entries - - - ---- -*Auto-generated file* -`; - } - - updateSection(content, sectionName, updates) { - const lines = content.split('\n'); - const sectionStart = lines.findIndex(line => - line.trim().startsWith(`## ${sectionName}`) - ); - - if (sectionStart === -1) return content; - - let sectionEnd = lines.findIndex((line, index) => - index > sectionStart && line.trim().startsWith('## ') - ); - - if (sectionEnd === -1) sectionEnd = lines.length; - - const beforeSection = lines.slice(0, sectionStart + 1); - const afterSection = lines.slice(sectionEnd); - - const newSection = [`## ${sectionName}\n`]; - - for (const [key, value] of Object.entries(updates)) { - if (Array.isArray(value)) { - newSection.push(`**${key}:**`); - value.forEach(item => newSection.push(`- ${item}`)); - newSection.push(''); - } else { - newSection.push(`**${key}:** ${value}`); - } - } - - return [...beforeSection, ...newSection, ...afterSection].join('\n'); - } - - addToSection(content, sectionName, items) { - const lines = content.split('\n'); - const sectionStart = lines.findIndex(line => - line.trim().startsWith(`## ${sectionName}`) - ); - - if (sectionStart === -1) return content; - - const beforeSection = lines.slice(0, sectionStart + 1); - const afterSection = lines.slice(sectionStart + 1); - - const newItems = Array.isArray(items) ? items : [items]; - const formattedItems = newItems.map(item => `- ${item}`); - - return [...beforeSection, ...formattedItems, '', ...afterSection].join('\n'); - } - - async readFile(relativePath) { - const fullPath = path.join(this.memoryBankDir, relativePath); - if (!fs.existsSync(fullPath)) return null; - return fs.readFileSync(fullPath, 'utf8'); - } - - getAllCategoryDirs() { - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - return categories.map(cat => path.join(this.memoryBankDir, cat)); - } - - extractRelevantContent(content, query) { - const lines = content.split('\n'); - const queryLower = query.toLowerCase(); - - const relevantLines = lines.filter(line => - line.toLowerCase().includes(queryLower) - ); - - return relevantLines.slice(0, 5).join('\n'); - } - - async getRecentActivity() { - const allFiles = []; - - for (const dir of this.getAllCategoryDirs()) { - if (!fs.existsSync(dir)) continue; - - const files = fs.readdirSync(dir) - .filter(file => file.endsWith('.md')) - .map(file => path.join(dir, file)); - - allFiles.push(...files); - } - - const fileStats = allFiles.map(file => ({ - file: path.relative(this.memoryBankDir, file), - lastModified: fs.statSync(file).mtime - })); - - return fileStats - .sort((a, b) => b.lastModified - a.lastModified) - .slice(0, 10); - } - - async getRecentErrors() { - const errorsFile = path.join(this.memoryBankDir, 'errors', 'errors.md'); - if (!fs.existsSync(errorsFile)) return []; - - const content = fs.readFileSync(errorsFile, 'utf8'); - const entries = content.split('## ['); - - return entries - .slice(1) // Пропускаем заголовок - .slice(-5) // Последние 5 ошибок - .map(entry => entry.split('\n')[0]); - } - - async getRecentDecisions() { - const decisionsFile = path.join(this.memoryBankDir, 'architecture', 'decisions.md'); - if (!fs.existsSync(decisionsFile)) return []; - - const content = fs.readFileSync(decisionsFile, 'utf8'); - const entries = content.split('## ['); - - return entries - .slice(1) // Пропускаем заголовок - .slice(-5) // Последние 5 решений - .map(entry => entry.split('\n')[0]); - } - - async generateSummary() { - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - const summary = {}; - - for (const category of categories) { - const categoryDir = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryDir)) { - summary[category] = 0; - continue; - } - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md')); - - summary[category] = files.length; - } - - return summary; - } - - async generateCategoryReport() { - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - const report = {}; - - for (const category of categories) { - const categoryDir = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryDir)) continue; - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md')) - .map(file => ({ - name: file, - path: path.join(category, file), - size: fs.statSync(path.join(categoryDir, file)).size - })); - - report[category] = files; - } - - return report; - } - - async generateErrorStats() { - const errorsFile = path.join(this.memoryBankDir, 'errors', 'errors.md'); - if (!fs.existsSync(errorsFile)) { - return { total: 0, resolved: 0, unresolved: 0 }; - } - - const content = fs.readFileSync(errorsFile, 'utf8'); - const entries = content.split('## ['); - const total = entries.length - 1; // Пропускаем заголовок - - const resolved = (content.match(/✅ Решено/g) || []).length; - const unresolved = total - resolved; - - return { total, resolved, unresolved }; - } - - async generateRecommendations() { - const recommendations = []; - - // Проверяем активность - const recentActivity = await this.getRecentActivity(); - if (recentActivity.length === 0) { - recommendations.push('Добавить первую запись в memory-bank'); - } - - // Проверяем ошибки - const errorStats = await this.generateErrorStats(); - if (errorStats.unresolved > 5) { - recommendations.push('Обработать нерешенные ошибки'); - } - - // Проверяем контекст - const activeContext = await this.readFile('core/activeContext.md'); - if (!activeContext || activeContext.includes('Initial setup')) { - recommendations.push('Обновить активный контекст проекта'); - } - - return recommendations; - } - - generateActiveContextTemplate() { - const date = new Date().toISOString().split('T')[0]; - - return `# Active Context - -## Current Status - -**Last Updated:** ${date} -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description - -## Next Steps - -- Step 1: Description - -## Blockers - -- Blocker 1: Description - ---- -*Auto-generated template* -`; - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - const command = args[0]; - const manager = new MemoryBankManager(); - - try { - switch (command) { - case 'add': - const content = args[1]; - const options = parseOptions(args.slice(2)); - await manager.addEntry(content, options); - break; - - case 'update-context': - const contextData = parseContextData(args.slice(1)); - await manager.updateContext(contextData); - break; - - case 'restore': - const restoreOptions = parseOptions(args.slice(1)); - const context = await manager.restoreContext(restoreOptions); - console.log(JSON.stringify(context, null, 2)); - break; - - case 'search': - const query = args[1]; - const searchOptions = parseOptions(args.slice(2)); - const results = await manager.searchMemory(query, searchOptions); - console.log(JSON.stringify(results, null, 2)); - break; - - case 'report': - const reportOptions = parseOptions(args.slice(1)); - const report = await manager.generateReport(reportOptions); - console.log(JSON.stringify(report, null, 2)); - break; - - default: - console.log(` -Memory Bank Manager - CLI Tool - -Usage: - node memory-bank-manager.cjs add "content" [options] - node memory-bank-manager.cjs update-context [options] - node memory-bank-manager.cjs restore [options] - node memory-bank-manager.cjs search "query" [options] - node memory-bank-manager.cjs report [options] - -Options: - --type= Entry type (error, decision, progress, etc.) - --category= Category (core, errors, architecture, etc.) - --priority= Priority (critical, high, medium, low) - --tags= Comma-separated tags - --full Full context restoration - --category= Specific category for restore/search - --limit= Limit search results - `); - } - } catch (error) { - console.error('❌ Error:', error.message); - process.exit(1); - } -} - -function parseOptions(args) { - const options = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - options[key] = value; - } - } - - return options; -} - -function parseContextData(args) { - const contextData = {}; - - for (const arg of args) { - if (arg.startsWith('--')) { - const [key, value] = arg.slice(2).split('='); - contextData[key] = value; - } - } - - return contextData; -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankManager; \ No newline at end of file diff --git a/.cursor/rules/memory-bank-organizer.cjs b/.cursor/rules/memory-bank-organizer.cjs deleted file mode 100644 index 6cd3f0e9..00000000 --- a/.cursor/rules/memory-bank-organizer.cjs +++ /dev/null @@ -1,331 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -function getProtectedFilesFromMap() { - const mapPath = path.join(process.cwd(), '.cursor/rules/doc/documentation-map.mdc'); - if (!fs.existsSync(mapPath)) return []; - const content = fs.readFileSync(mapPath, 'utf-8'); - const protectedSection = content.split('## 🛡️ Protected/Administrative Files')[1]; - if (!protectedSection) return []; - return protectedSection - .split('\n') - .filter(line => line.trim().startsWith('- ')) - .map(line => line.replace(/^-\s+/, '').split('—')[0].trim()) - .filter(Boolean); -} - -class MemoryBankOrganizer { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.structurePath = path.join(this.memoryBankDir, 'MEMORY_BANK_STRUCTURE.md'); - this.protectedFiles = getProtectedFilesFromMap(); - } - - async reorganize() { - console.log('🧠 Reorganizing Memory Bank...\n'); - - // Создаем структуру каталогов - await this.createDirectoryStructure(); - - // Анализируем существующие файлы - const fileAnalysis = await this.analyzeFiles(); - - // Перемещаем файлы по новой структуре - await this.moveFiles(fileAnalysis); - - // Создаем индексы - await this.createIndexes(); - - console.log('✅ Memory Bank reorganization completed!'); - } - - async createDirectoryStructure() { - const directories = [ - 'core', - 'errors', - 'architecture', - 'development', - 'ui', - 'planning', - 'context', - 'deprecated' - ]; - - for (const dir of directories) { - const dirPath = path.join(this.memoryBankDir, dir); - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); - console.log(`📁 Created directory: ${dir}`); - } - } - } - - async analyzeFiles() { - const files = fs.readdirSync(this.memoryBankDir) - .filter(file => file.endsWith('.md') && file !== 'MEMORY_BANK_STRUCTURE.md') - .map(file => ({ - name: file, - path: path.join(this.memoryBankDir, file), - category: this.categorizeFile(file) - })); - - return files; - } - - categorizeFile(filename) { - const name = filename.toLowerCase(); - - // Core файлы - if (name.includes('activecontext') || name.includes('progress') || - name.includes('projectbrief') || name.includes('session-log')) { - return 'core'; - } - - // Errors - if (name.includes('error') || name.includes('graveyard') || - name.includes('vite-react') || name.includes('typescript-build')) { - return 'errors'; - } - - // Architecture - if (name.includes('architecture') || name.includes('systempattern') || - name.includes('security-architecture') || name.includes('comprehensive-architecture')) { - return 'architecture'; - } - - // Development - if (name.includes('testing') || name.includes('debug') || - name.includes('devtools') || name.includes('version-management')) { - return 'development'; - } - - // UI - if (name.includes('side-panel') || name.includes('chat-context') || - name.includes('lazy-sync') || name.includes('ui')) { - return 'ui'; - } - - // Planning - if (name.includes('future') || name.includes('plan') || - name.includes('optimization') || name.includes('roadmap')) { - return 'planning'; - } - - // Context - if (name.includes('tech-context') || name.includes('product-context') || - name.includes('environment') || name.includes('context')) { - return 'context'; - } - - // Deprecated (дублирующие или устаревшие) - if (name.includes('cursor-') || name.includes('ai-') || - name.includes('mdc-file-standards') || name.includes('user-commands')) { - return 'deprecated'; - } - - return 'core'; // По умолчанию в core - } - - async moveFiles(fileAnalysis) { - for (const file of fileAnalysis) { - if (this.protectedFiles.includes(`memory-bank/${file.name}`)) { - console.log(`🔒 Protected: ${file.name} — оставлен в корне memory-bank`); - continue; - } - const targetDir = path.join(this.memoryBankDir, file.category); - const targetPath = path.join(targetDir, file.name); - - // Проверяем, не существует ли уже файл с таким именем - if (fs.existsSync(targetPath)) { - // Добавляем префикс для избежания конфликтов - const newName = `migrated-${file.name}`; - const newTargetPath = path.join(targetDir, newName); - fs.renameSync(file.path, newTargetPath); - console.log(`📄 Moved: ${file.name} → ${file.category}/${newName}`); - } else { - fs.renameSync(file.path, targetPath); - console.log(`📄 Moved: ${file.name} → ${file.category}/`); - } - } - } - - async createIndexes() { - // Создаем индекс для каждой категории - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - - for (const category of categories) { - await this.createCategoryIndex(category); - } - - // Создаем общий индекс - await this.createMainIndex(); - } - - async createCategoryIndex(category) { - const categoryDir = path.join(this.memoryBankDir, category); - const indexPath = path.join(categoryDir, 'README.md'); - - if (!fs.existsSync(categoryDir)) return; - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md') && file !== 'README.md') - .sort(); - - const indexContent = `# ${this.getCategoryTitle(category)} - Index - -## Files in this category: - -${files.map(file => `- [${file.replace('.md', '')}](./${file})`).join('\n')} - -## Description: - -${this.getCategoryDescription(category)} - -## Last updated: ${new Date().toISOString().split('T')[0]} -`; - - fs.writeFileSync(indexPath, indexContent); - console.log(`📋 Created index: ${category}/README.md`); - } - - async createMainIndex() { - const indexPath = path.join(this.memoryBankDir, 'INDEX.md'); - - const indexContent = `# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Основные файлы контекста -- activeContext.md - Текущий контекст проекта -- progress.md - Прогресс разработки -- projectbrief.md - Краткое описание проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Ошибки и решения -- errors.md - Кладбище ошибок (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Архитектурные решения -- decisions.md - Принятые решения -- patterns.md - Системные паттерны -- security.md - Архитектура безопасности -- comprehensive.md - Комплексная архитектура - -### 🔧 [Development](./development/) - Процесс разработки -- testing-results.md - Результаты тестирования -- debugging-guide.md - Руководство по отладке -- devtools-guide.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX контекст -- side-panel.md - Улучшения side-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая синхронизация - -### 📅 [Planning](./planning/) - Планирование -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Контекстная информация -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Устаревшие файлы -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-results.md](./development/testing-results.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: ${new Date().toISOString().split('T')[0]} -`; - - fs.writeFileSync(indexPath, indexContent); - console.log(`📋 Created main index: INDEX.md`); - } - - getCategoryTitle(category) { - const titles = { - 'core': 'Core Files', - 'errors': 'Errors & Solutions', - 'architecture': 'Architecture Decisions', - 'development': 'Development Process', - 'ui': 'UI/UX Context', - 'planning': 'Planning & Roadmap', - 'context': 'Context Information', - 'deprecated': 'Deprecated Files' - }; - - return titles[category] || category; - } - - getCategoryDescription(category) { - const descriptions = { - 'core': 'Критически важные файлы для понимания текущего состояния проекта. Обновляются регулярно.', - 'errors': 'Проектно-специфичные ошибки и их решения. История проблем и workarounds.', - 'architecture': 'Принятые архитектурные решения с обоснованием. Влияние на проект.', - 'development': 'Результаты тестирования, руководства по отладке, процессы разработки.', - 'ui': 'UI/UX решения и улучшения. Пользовательский опыт и интерфейсные паттерны.', - 'planning': 'Планы развития проекта, roadmap, стратегические решения.', - 'context': 'Технический и продуктовый контекст. Окружение разработки.', - 'deprecated': 'Устаревшие файлы, дубликаты, мигрированные версии.' - }; - - return descriptions[category] || 'Category description'; - } - - async cleanup() { - console.log('\n🧹 Cleaning up deprecated files...'); - - const deprecatedDir = path.join(this.memoryBankDir, 'deprecated'); - if (fs.existsSync(deprecatedDir)) { - const files = fs.readdirSync(deprecatedDir); - - for (const file of files) { - if (file.endsWith('.md')) { - const filePath = path.join(deprecatedDir, file); - const content = fs.readFileSync(filePath, 'utf8'); - - // Проверяем, есть ли полезная информация - if (content.length < 1000) { - fs.unlinkSync(filePath); - console.log(`🗑️ Removed small file: ${file}`); - } - } - } - } - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - - const organizer = new MemoryBankOrganizer(); - - if (args.includes('--cleanup')) { - await organizer.cleanup(); - } else { - await organizer.reorganize(); - } -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankOrganizer; \ No newline at end of file diff --git a/.cursor/rules/memory-bank-structure-creator.cjs b/.cursor/rules/memory-bank-structure-creator.cjs deleted file mode 100644 index acd2e1db..00000000 --- a/.cursor/rules/memory-bank-structure-creator.cjs +++ /dev/null @@ -1,690 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class MemoryBankStructureCreator { - constructor() { - this.memoryBankDir = path.join(process.cwd(), 'memory-bank'); - this.templatesDir = path.join(__dirname, 'memory-bank', 'templates'); - } - - async createStructure(projectType = 'default') { - console.log(`🏗️ Creating Memory Bank structure for ${projectType} project...\n`); - - // Создаем базовую структуру - await this.createBaseStructure(); - - // Создаем специфичную структуру для типа проекта - await this.createProjectSpecificStructure(projectType); - - // Создаем индексы и README файлы - await this.createIndexes(projectType); - - console.log('✅ Memory Bank structure created successfully!'); - } - - async createBaseStructure() { - const baseCategories = [ - 'core', - 'errors', - 'architecture', - 'development', - 'ui', - 'planning', - 'context', - 'deprecated' - ]; - - for (const category of baseCategories) { - const categoryPath = path.join(this.memoryBankDir, category); - if (!fs.existsSync(categoryPath)) { - fs.mkdirSync(categoryPath, { recursive: true }); - console.log(`📁 Created category: ${category}`); - } - } - } - - async createProjectSpecificStructure(projectType) { - const templates = this.getProjectTemplates(projectType); - - for (const [category, files] of Object.entries(templates)) { - const categoryPath = path.join(this.memoryBankDir, category); - - for (const file of files) { - const filePath = path.join(categoryPath, file); - if (!fs.existsSync(filePath)) { - const content = this.generateFileContent(file, projectType); - fs.writeFileSync(filePath, content); - console.log(`📄 Created: ${category}/${file}`); - } - } - } - } - - getProjectTemplates(projectType) { - const templates = { - 'react-typescript': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'build-errors.md', - 'runtime-errors.md', - 'ui-errors.md', - 'typescript-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'patterns.md', - 'state-management.md', - 'component-structure.md', - 'routing.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'build-process.md' - ], - 'ui': [ - 'component-library.md', - 'styling-patterns.md', - 'responsive-design.md', - 'accessibility.md', - 'performance.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'optimization-plans.md', - 'migration-plans.md', - 'tech-debt.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'deployment.md' - ] - }, - 'chrome-extension': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'manifest-errors.md', - 'permission-errors.md', - 'api-errors.md', - 'content-script-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'background-scripts.md', - 'content-scripts.md', - 'popup-structure.md', - 'options-page.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'packaging.md' - ], - 'ui': [ - 'popup-design.md', - 'options-page.md', - 'content-ui.md', - 'icon-design.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'store-publishing.md', - 'user-feedback.md', - 'security-audit.md' - ], - 'context': [ - 'chrome-apis.md', - 'permissions.md', - 'environment.md', - 'store-requirements.md' - ] - }, - 'node-api': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'api-errors.md', - 'database-errors.md', - 'authentication-errors.md', - 'validation-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'api-design.md', - 'database-schema.md', - 'authentication.md', - 'middleware.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'deployment.md' - ], - 'ui': [ - 'api-documentation.md', - 'swagger-spec.md', - 'client-examples.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'scaling-plans.md', - 'security-plans.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'infrastructure.md' - ] - }, - 'monorepo': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'build-errors.md', - 'dependency-errors.md', - 'workspace-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'workspace-structure.md', - 'package-organization.md', - 'shared-libraries.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'ci-cd.md' - ], - 'ui': [ - 'shared-components.md', - 'design-system.md', - 'storybook.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'migration-plans.md', - 'refactoring-plans.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'workspace-config.md' - ] - }, - 'fullstack': { - 'core': [ - 'activeContext.md', - 'progress.md', - 'projectbrief.md', - 'session-log.md' - ], - 'errors': [ - 'errors.md', - 'frontend-errors.md', - 'backend-errors.md', - 'database-errors.md', - 'deployment-errors.md' - ], - 'architecture': [ - 'decisions.md', - 'system-architecture.md', - 'api-design.md', - 'database-design.md', - 'frontend-architecture.md' - ], - 'development': [ - 'testing-results.md', - 'debugging-guide.md', - 'devtools-guide.md', - 'version-management.md', - 'deployment.md' - ], - 'ui': [ - 'component-library.md', - 'design-system.md', - 'responsive-design.md', - 'accessibility.md' - ], - 'planning': [ - 'feature-roadmap.md', - 'scaling-plans.md', - 'performance-plans.md' - ], - 'context': [ - 'tech-stack.md', - 'dependencies.md', - 'environment.md', - 'infrastructure.md' - ] - } - }; - - return templates[projectType] || templates['react-typescript']; - } - - generateFileContent(filename, projectType) { - const date = new Date().toISOString().split('T')[0]; - - const baseContent = `# ${this.getFileTitle(filename, projectType)} - -## Overview - -This file contains ${this.getFileDescription(filename, projectType)}. - -## Entries - - - ---- -*Generated on ${date} for ${projectType} project* -`; - - // Специальные шаблоны для ключевых файлов - if (filename === 'activeContext.md') { - return this.generateActiveContextTemplate(projectType); - } - - if (filename === 'projectbrief.md') { - return this.generateProjectBriefTemplate(projectType); - } - - if (filename === 'errors.md') { - return this.generateErrorsTemplate(projectType); - } - - return baseContent; - } - - generateActiveContextTemplate(projectType) { - const date = new Date().toISOString().split('T')[0]; - - return `# Active Context - ${projectType} - -## Current Status - -**Last Updated:** ${date} -**Project Type:** ${projectType} -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup -- [ ] Core functionality -- [ ] Testing -- [ ] Documentation - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description -- Decision 2: Description - -## Next Steps - -- Step 1: Description -- Step 2: Description - -## Blockers - -- Blocker 1: Description -- Blocker 2: Description - ---- -*Auto-generated for ${projectType} project* -`; - } - - generateProjectBriefTemplate(projectType) { - const date = new Date().toISOString().split('T')[0]; - - return `# Project Brief - ${projectType} - -## Project Overview - -**Name:** [Project Name] -**Type:** ${projectType} -**Created:** ${date} -**Status:** Active - -## Goals - -- Goal 1: Description -- Goal 2: Description -- Goal 3: Description - -## Requirements - -- Requirement 1: Description -- Requirement 2: Description -- Requirement 3: Description - -## Constraints - -- Constraint 1: Description -- Constraint 2: Description - -## Success Criteria - -- Criterion 1: Description -- Criterion 2: Description - ---- -*Auto-generated for ${projectType} project* -`; - } - - generateErrorsTemplate(projectType) { - const date = new Date().toISOString().split('T')[0]; - - return `# Error Log - ${projectType} - -## Error Categories - -### Build Errors - - -### Runtime Errors - - -### UI/UX Errors - - -### ${this.getProjectSpecificErrorCategory(projectType)} - - -## Error Resolution Workflow - -1. **Document** - Record error details -2. **Analyze** - Understand root cause -3. **Solve** - Implement solution -4. **Test** - Verify resolution -5. **Document** - Update with solution - -## Recent Errors - - - ---- -*Auto-generated for ${projectType} project* -`; - } - - getFileTitle(filename, projectType) { - const titles = { - 'activeContext.md': `Active Context - ${projectType}`, - 'progress.md': `Progress - ${projectType}`, - 'projectbrief.md': `Project Brief - ${projectType}`, - 'session-log.md': `Session Log - ${projectType}`, - 'errors.md': `Error Log - ${projectType}`, - 'decisions.md': `Architecture Decisions - ${projectType}`, - 'patterns.md': `Design Patterns - ${projectType}`, - 'testing-results.md': `Testing Results - ${projectType}`, - 'debugging-guide.md': `Debugging Guide - ${projectType}`, - 'devtools-guide.md': `DevTools Guide - ${projectType}`, - 'version-management.md': `Version Management - ${projectType}`, - 'feature-roadmap.md': `Feature Roadmap - ${projectType}`, - 'tech-stack.md': `Tech Stack - ${projectType}`, - 'dependencies.md': `Dependencies - ${projectType}`, - 'environment.md': `Environment - ${projectType}` - }; - - return titles[filename] || filename.replace('.md', '').replace(/-/g, ' '); - } - - getFileDescription(filename, projectType) { - const descriptions = { - 'activeContext.md': 'current project context and status', - 'progress.md': 'development progress and milestones', - 'projectbrief.md': 'project overview and requirements', - 'session-log.md': 'development session logs', - 'errors.md': 'error tracking and resolution', - 'decisions.md': 'architectural decisions and rationale', - 'patterns.md': 'design patterns and best practices', - 'testing-results.md': 'testing outcomes and coverage', - 'debugging-guide.md': 'debugging procedures and tips', - 'devtools-guide.md': 'development tools usage', - 'version-management.md': 'version control and releases', - 'feature-roadmap.md': 'feature planning and roadmap', - 'tech-stack.md': 'technology stack and tools', - 'dependencies.md': 'project dependencies and versions', - 'environment.md': 'development environment setup' - }; - - return descriptions[filename] || 'project-specific information'; - } - - getProjectSpecificErrorCategory(projectType) { - const categories = { - 'react-typescript': 'TypeScript Errors', - 'chrome-extension': 'Extension Errors', - 'node-api': 'API Errors', - 'monorepo': 'Workspace Errors', - 'fullstack': 'Full-stack Errors' - }; - - return categories[projectType] || 'Project Errors'; - } - - async createIndexes(projectType) { - // Создаем главный индекс - await this.createMainIndex(projectType); - - // Создаем индексы для каждой категории - const categories = ['core', 'errors', 'architecture', 'development', 'ui', 'planning', 'context']; - - for (const category of categories) { - await this.createCategoryIndex(category, projectType); - } - } - - async createMainIndex(projectType) { - const indexPath = path.join(this.memoryBankDir, 'INDEX.md'); - const date = new Date().toISOString().split('T')[0]; - - const content = `# Memory Bank - ${projectType} Project - -## Project Information - -**Type:** ${projectType} -**Created:** ${date} -**Last Updated:** ${date} - -## Categories - -### 📋 [Core](./core/) - Основные файлы контекста -- activeContext.md - Текущий контекст проекта -- progress.md - Прогресс разработки -- projectbrief.md - Краткое описание проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Ошибки и решения -- errors.md - Кладбище ошибок (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Архитектурные решения -- decisions.md - Принятые решения -- patterns.md - Системные паттерны -- ${this.getArchitectureFiles(projectType)} - -### 🔧 [Development](./development/) - Процесс разработки -- testing-results.md - Результаты тестирования -- debugging-guide.md - Руководство по отладке -- devtools-guide.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX контекст -- ${this.getUIFiles(projectType)} - -### 📅 [Planning](./planning/) - Планирование -- feature-roadmap.md - Roadmap фич -- optimization-plans.md - Планы оптимизации - -### 🌍 [Context](./context/) - Контекстная информация -- tech-stack.md - Технический стек -- dependencies.md - Зависимости проекта -- environment.md - Окружение разработки - -## Quick Navigation - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) - -## AI Commands - -- \`создай запись в memory-bank\` - Создать новую запись -- \`обнови контекст\` - Обновить активный контекст -- \`восстанови контекст\` - Восстановить полный контекст -- \`аудит memory-bank\` - Провести аудит - ---- -*Auto-generated for ${projectType} project on ${date}* -`; - - fs.writeFileSync(indexPath, content); - console.log('📋 Created main index: INDEX.md'); - } - - async createCategoryIndex(category, projectType) { - const categoryDir = path.join(this.memoryBankDir, category); - const indexPath = path.join(categoryDir, 'README.md'); - - if (!fs.existsSync(categoryDir)) return; - - const files = fs.readdirSync(categoryDir) - .filter(file => file.endsWith('.md') && file !== 'README.md') - .sort(); - - const content = `# ${this.getCategoryTitle(category)} - ${projectType} - -## Files in this category: - -${files.map(file => `- [${file.replace('.md', '')}](./${file})`).join('\n')} - -## Description: - -${this.getCategoryDescription(category)} - -## AI Commands: - -- \`добавь в ${category}\` - Добавить запись в эту категорию -- \`обнови ${category}\` - Обновить файлы в категории -- \`покажи ${category}\` - Показать содержимое категории - ---- -*Auto-generated for ${projectType} project* -`; - - fs.writeFileSync(indexPath, content); - console.log(`📋 Created index: ${category}/README.md`); - } - - getCategoryTitle(category) { - const titles = { - 'core': 'Core Files', - 'errors': 'Errors & Solutions', - 'architecture': 'Architecture Decisions', - 'development': 'Development Process', - 'ui': 'UI/UX Context', - 'planning': 'Planning & Roadmap', - 'context': 'Context Information' - }; - - return titles[category] || category; - } - - getCategoryDescription(category) { - const descriptions = { - 'core': 'Критически важные файлы для понимания текущего состояния проекта.', - 'errors': 'Проектно-специфичные ошибки и их решения.', - 'architecture': 'Принятые архитектурные решения с обоснованием.', - 'development': 'Результаты тестирования, руководства по отладке, процессы разработки.', - 'ui': 'UI/UX решения и улучшения, пользовательский опыт.', - 'planning': 'Планы развития проекта, roadmap, стратегические решения.', - 'context': 'Технический и продуктовый контекст, окружение разработки.' - }; - - return descriptions[category] || 'Category description'; - } - - getArchitectureFiles(projectType) { - const files = { - 'react-typescript': 'state-management.md - Управление состоянием\n- component-structure.md - Структура компонентов\n- routing.md - Маршрутизация', - 'chrome-extension': 'background-scripts.md - Background scripts\n- content-scripts.md - Content scripts\n- popup-structure.md - Структура popup', - 'node-api': 'api-design.md - Дизайн API\n- database-schema.md - Схема БД\n- authentication.md - Аутентификация', - 'monorepo': 'workspace-structure.md - Структура workspace\n- package-organization.md - Организация пакетов\n- shared-libraries.md - Общие библиотеки', - 'fullstack': 'system-architecture.md - Системная архитектура\n- api-design.md - Дизайн API\n- frontend-architecture.md - Архитектура фронтенда' - }; - - return files[projectType] || 'patterns.md - Системные паттерны'; - } - - getUIFiles(projectType) { - const files = { - 'react-typescript': 'component-library.md - Библиотека компонентов\n- styling-patterns.md - Паттерны стилизации\n- responsive-design.md - Адаптивный дизайн', - 'chrome-extension': 'popup-design.md - Дизайн popup\n- options-page.md - Страница настроек\n- content-ui.md - UI в content scripts', - 'node-api': 'api-documentation.md - Документация API\n- swagger-spec.md - Swagger спецификация\n- client-examples.md - Примеры клиентов', - 'monorepo': 'shared-components.md - Общие компоненты\n- design-system.md - Дизайн-система\n- storybook.md - Storybook', - 'fullstack': 'component-library.md - Библиотека компонентов\n- design-system.md - Дизайн-система\n- responsive-design.md - Адаптивный дизайн' - }; - - return files[projectType] || 'ui-patterns.md - UI паттерны'; - } -} - -// CLI обработка -async function main() { - const args = process.argv.slice(2); - const projectType = args[0] || 'react-typescript'; - - const creator = new MemoryBankStructureCreator(); - await creator.createStructure(projectType); -} - -if (require.main === module) { - main().catch(console.error); -} - -module.exports = MemoryBankStructureCreator; \ No newline at end of file diff --git a/.cursor/rules/memory-bank/INDEX.md b/.cursor/rules/memory-bank/INDEX.md deleted file mode 100644 index 0eee849e..00000000 --- a/.cursor/rules/memory-bank/INDEX.md +++ /dev/null @@ -1,65 +0,0 @@ -# Memory Bank - React-TypeScript Project - -## Project Information - -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Last Updated:** 2025-07-19 - -## Categories - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- State-management.md - Управление Stateм -- component-structure.md - Структура компонентов -- routing.md - Маршрутизация - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- component-library.md - Библиотека компонентов -- styling-patterns.md - Patterns стилизации -- responsive-design.md - Адаптивный Design - -### 📅 [Planning](./planning/) - Planning -- feature-roadmap.md - Roadmap фич -- optimization-plans.md - Планы оптимизации - -### 🌍 [Context](./context/) - Contextual information -- tech-stack.md - Технический стек -- dependencies.md - Dependencies проекта -- environment.md - Окружение разработки - -## Quick Navigation - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) - -## AI Commands - -- `создай запись в memory-bank` - Create новую запись -- `обнови контекст` - Update Active контекст -- `восстанови контекст` - Восстановить полный контекст -- `аудит memory-bank` - Провести аудит - ---- -*Auto-generated for React-TypeScript project on 2025-07-19* diff --git a/.cursor/rules/memory-bank/architecture/README.md b/.cursor/rules/memory-bank/architecture/README.md deleted file mode 100644 index 21d38ba9..00000000 --- a/.cursor/rules/memory-bank/architecture/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Files in this category: - -- [component-structure](./component-structure.md) -- [decisions](./decisions.md) -- [patterns](./patterns.md) -- [routing](./routing.md) -- [State-management](./State-management.md) - -## Description: - -Принятые архитектурные решения с обоснованием. - -## AI Commands: - -- `добавь в architecture` - Add запись в эту категорию -- `обнови architecture` - Update файлы в категории -- `покажи architecture` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/component-structure.md b/.cursor/rules/memory-bank/architecture/component-structure.md deleted file mode 100644 index 6626b3ce..00000000 --- a/.cursor/rules/memory-bank/architecture/component-structure.md +++ /dev/null @@ -1,12 +0,0 @@ -# component structure - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/decisions.md b/.cursor/rules/memory-bank/architecture/decisions.md deleted file mode 100644 index 720d2530..00000000 --- a/.cursor/rules/memory-bank/architecture/decisions.md +++ /dev/null @@ -1,12 +0,0 @@ -# Architecture Decisions - React-TypeScript - -## Overview - -This file contains architectural decisions and rationale. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/patterns.md b/.cursor/rules/memory-bank/architecture/patterns.md deleted file mode 100644 index ae9baf41..00000000 --- a/.cursor/rules/memory-bank/architecture/patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# Design Patterns - React-TypeScript - -## Overview - -This file contains design patterns and best practices. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/routing.md b/.cursor/rules/memory-bank/architecture/routing.md deleted file mode 100644 index a252858e..00000000 --- a/.cursor/rules/memory-bank/architecture/routing.md +++ /dev/null @@ -1,12 +0,0 @@ -# routing - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/architecture/state-management.md b/.cursor/rules/memory-bank/architecture/state-management.md deleted file mode 100644 index d7ecf603..00000000 --- a/.cursor/rules/memory-bank/architecture/state-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# State management - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/README.md b/.cursor/rules/memory-bank/context/README.md deleted file mode 100644 index 374553e6..00000000 --- a/.cursor/rules/memory-bank/context/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Context Information - React-TypeScript - -## Files in this category: - -- [dependencies](./dependencies.md) -- [deployment](./deployment.md) -- [environment](./environment.md) -- [tech-stack](./tech-stack.md) - -## Description: - -Технический и продуктовый контекст, окружение разработки. - -## AI Commands: - -- `добавь в context` - Add запись в эту категорию -- `обнови context` - Update файлы в категории -- `покажи context` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/dependencies.md b/.cursor/rules/memory-bank/context/dependencies.md deleted file mode 100644 index 6caba601..00000000 --- a/.cursor/rules/memory-bank/context/dependencies.md +++ /dev/null @@ -1,12 +0,0 @@ -# Dependencies - React-TypeScript - -## Overview - -This file contains project dependencies and versions. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/deployment.md b/.cursor/rules/memory-bank/context/deployment.md deleted file mode 100644 index 23fa5567..00000000 --- a/.cursor/rules/memory-bank/context/deployment.md +++ /dev/null @@ -1,12 +0,0 @@ -# deployment - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/environment.md b/.cursor/rules/memory-bank/context/environment.md deleted file mode 100644 index 54b83b38..00000000 --- a/.cursor/rules/memory-bank/context/environment.md +++ /dev/null @@ -1,12 +0,0 @@ -# Environment - React-TypeScript - -## Overview - -This file contains development environment setup. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/context/tech-stack.md b/.cursor/rules/memory-bank/context/tech-stack.md deleted file mode 100644 index 1d83f76a..00000000 --- a/.cursor/rules/memory-bank/context/tech-stack.md +++ /dev/null @@ -1,12 +0,0 @@ -# Tech Stack - React-TypeScript - -## Overview - -This file contains technology stack and tools. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/INDEX.md b/.cursor/rules/memory-bank/core/INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/rules/memory-bank/core/INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/rules/memory-bank/core/README.md b/.cursor/rules/memory-bank/core/README.md deleted file mode 100644 index a3b68a3e..00000000 --- a/.cursor/rules/memory-bank/core/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Core Files - React-TypeScript - -## Files in this category: - -- [INDEX](./INDEX.md) -- [activeContext](./activeContext.md) -- [migrated-INDEX](./migrated-INDEX.md) -- [progress](./progress.md) -- [projectbrief](./projectbrief.md) -- [session-log](./session-log.md) - -## Description: - -Критически важные файлы для понимания текущего состояния проекта. - -## AI Commands: - -- `добавь в core` - Add запись в эту категорию -- `обнови core` - Update файлы в категории -- `покажи core` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/activeContext.md b/.cursor/rules/memory-bank/core/activeContext.md deleted file mode 100644 index 8c429166..00000000 --- a/.cursor/rules/memory-bank/core/activeContext.md +++ /dev/null @@ -1,36 +0,0 @@ -# Active Context - React-TypeScript - -## Current Status - -**Last Updated:** 2025-07-19 -**Project Type:** React-TypeScript -**Phase:** Development - -## Active Tasks - -- [ ] Initial setup -- [ ] Core functionality -- [ ] Testing -- [ ] Documentation - -## Current Focus - -Describe current development focus and priorities. - -## Recent Decisions - -- Decision 1: Description -- Decision 2: Description - -## Next Steps - -- Step 1: Description -- Step 2: Description - -## Blockers - -- Blocker 1: Description -- Blocker 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/general.md b/.cursor/rules/memory-bank/core/general.md deleted file mode 100644 index 71e787bc..00000000 --- a/.cursor/rules/memory-bank/core/general.md +++ /dev/null @@ -1,31 +0,0 @@ -# General - core - -## Overview - -This file contains core-related information and entries. - -## Entries - - - ---- -*Auto-generated file* - -## [2025-07-19 01:56:13] - New Entry - -**Type:** test -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #universal #Commands #testing - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/rules/memory-bank/core/migrated-INDEX.md b/.cursor/rules/memory-bank/core/migrated-INDEX.md deleted file mode 100644 index 68b2770e..00000000 --- a/.cursor/rules/memory-bank/core/migrated-INDEX.md +++ /dev/null @@ -1,59 +0,0 @@ -# Memory Bank - Main Index - -## Categories: - -### 📋 [Core](./core/) - Core context files -- activeContext.md - Current project context -- progress.md - Development progress -- projectbrief.md - Краткое Description проекта -- session-log.md - Лог сессий разработки - -### ❌ [Errors](./errors/) - Errors and solutions -- errors.md - Error graveyard (основной файл) -- build-errors.md - Ошибки сборки -- runtime-errors.md - Runtime ошибки -- ui-errors.md - UI/UX ошибки - -### 🏗️ [Architecture](./architecture/) - Architectural decisions -- decisions.md - Принятые решения -- patterns.md - Системные Patterns -- security.md - Architecture безопасности -- comprehensive.md - Комплексная Architecture - -### 🔧 [Development](./development/) - Development process -- testing-resu.ts.md - Результаты тестирования -- debugging-guIDE.md - Руководство по отладке -- devtools-guIDE.md - Работа с DevTools -- version-management.md - Управление версиями - -### 🎨 [UI](./ui/) - UI/UX context -- sIDE-panel.md - Улучшения sIDE-panel -- chat-context.md - Контекст чата -- lazy-sync.md - Ленивая Synchronization - -### 📅 [Planning](./planning/) - Planning -- future-plans.md - Планы развития -- optimization-plans.md - Планы оптимизации -- roadmap.md - Roadmap проекта - -### 🌍 [Context](./context/) - Contextual information -- tech-context.md - Технический контекст -- product-context.md - Продуктовый контекст -- environment.md - Окружение разработки - -### 🗑️ [Deprecated](./deprecated/) - Deprecated files -- Старые файлы, дубликаты, мигрированные версии - -## Quick Navigation: - -- **Current Status**: [activeContext.md](./core/activeContext.md) -- **Progress**: [progress.md](./core/progress.md) -- **Errors**: [errors.md](./errors/errors.md) -- **Architecture**: [decisions.md](./architecture/decisions.md) -- **Testing**: [testing-resu.ts.md](./development/testing-resu.ts.md) - -## Structure Rules: - -See [MEMORY_BANK_STRUCTURE.md](./MEMORY_BANK_STRUCTURE.md) for detailed organization rules. - -Last updated: 2025-07-19 diff --git a/.cursor/rules/memory-bank/core/progress.md b/.cursor/rules/memory-bank/core/progress.md deleted file mode 100644 index fd42cfe2..00000000 --- a/.cursor/rules/memory-bank/core/progress.md +++ /dev/null @@ -1,31 +0,0 @@ -# Progress - React-TypeScript - -## Overview - -This file contains development progress and milestones. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* - -## [2025-07-19 01:47:05] - New Entry - -**Type:** progress -**Category:** core -**Priority:** high - -**Контекст:** No content provIDEd - -**Status:** 🔄 In Progress - - -**Теги:** #memory-bank #cursor #automation - -**AI Команды:** -- `обнови контекст` - для обновления активного контекста -- `задокументируй` - для создания документации - ---- diff --git a/.cursor/rules/memory-bank/core/projectbrief.md b/.cursor/rules/memory-bank/core/projectbrief.md deleted file mode 100644 index 04f2e336..00000000 --- a/.cursor/rules/memory-bank/core/projectbrief.md +++ /dev/null @@ -1,33 +0,0 @@ -# Project Brief - React-TypeScript - -## Project Overview - -**Name:** [Project Name] -**Type:** React-TypeScript -**Created:** 2025-07-19 -**Status:** Active - -## Goals - -- Goal 1: Description -- Goal 2: Description -- Goal 3: Description - -## Requireme.ts - -- Requirement 1: Description -- Requirement 2: Description -- Requirement 3: Description - -## Constrai.ts - -- Constraint 1: Description -- Constraint 2: Description - -## Success Criteria - -- Criterion 1: Description -- Criterion 2: Description - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/core/session-log.md b/.cursor/rules/memory-bank/core/session-log.md deleted file mode 100644 index 35ff1fc3..00000000 --- a/.cursor/rules/memory-bank/core/session-log.md +++ /dev/null @@ -1,12 +0,0 @@ -# Session Log - React-TypeScript - -## Overview - -This file contains development session logs. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/README.md b/.cursor/rules/memory-bank/development/README.md deleted file mode 100644 index 25270007..00000000 --- a/.cursor/rules/memory-bank/development/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Development Process - React-TypeScript - -## Files in this category: - -- [build-process](./build-process.md) -- [debugging-guIDE](./debugging-guIDE.md) -- [devtools-guIDE](./devtools-guIDE.md) -- [testing-resu.ts](./testing-resu.ts.md) -- [version-management](./version-management.md) - -## Description: - -Результаты тестирования, GuIDEs по отладке, Processes разработки. - -## AI Commands: - -- `добавь в development` - Add запись в эту категорию -- `обнови development` - Update файлы в категории -- `покажи development` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/build-process.md b/.cursor/rules/memory-bank/development/build-process.md deleted file mode 100644 index 1a1ffcf4..00000000 --- a/.cursor/rules/memory-bank/development/build-process.md +++ /dev/null @@ -1,12 +0,0 @@ -# build process - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/debugging-guide.md b/.cursor/rules/memory-bank/development/debugging-guide.md deleted file mode 100644 index 5369fa55..00000000 --- a/.cursor/rules/memory-bank/development/debugging-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# Debugging GuIDE - React-TypeScript - -## Overview - -This file contains debugging procedures and tips. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/devtools-guide.md b/.cursor/rules/memory-bank/development/devtools-guide.md deleted file mode 100644 index c0d68911..00000000 --- a/.cursor/rules/memory-bank/development/devtools-guide.md +++ /dev/null @@ -1,12 +0,0 @@ -# DevTools GuIDE - React-TypeScript - -## Overview - -This file contains development tools usage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/testing-results.md b/.cursor/rules/memory-bank/development/testing-results.md deleted file mode 100644 index bf105272..00000000 --- a/.cursor/rules/memory-bank/development/testing-results.md +++ /dev/null @@ -1,12 +0,0 @@ -# Testing Resu.ts - React-TypeScript - -## Overview - -This file contains testing outcomes and coverage. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/development/version-management.md b/.cursor/rules/memory-bank/development/version-management.md deleted file mode 100644 index 67b7e2f6..00000000 --- a/.cursor/rules/memory-bank/development/version-management.md +++ /dev/null @@ -1,12 +0,0 @@ -# Version Management - React-TypeScript - -## Overview - -This file contains version control and releases. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/README.md b/.cursor/rules/memory-bank/errors/README.md deleted file mode 100644 index eca2654f..00000000 --- a/.cursor/rules/memory-bank/errors/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Errors & Solutions - React-TypeScript - -## Files in this category: - -- [build-errors](./build-errors.md) -- [errors](./errors.md) -- [runtime-errors](./runtime-errors.md) -- [TypeScript-errors](./TypeScript-errors.md) -- [ui-errors](./ui-errors.md) - -## Description: - -Проектно-специфичные ошибки и их решения. - -## AI Commands: - -- `добавь в errors` - Add запись в эту категорию -- `обнови errors` - Update файлы в категории -- `покажи errors` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/build-errors.md b/.cursor/rules/memory-bank/errors/build-errors.md deleted file mode 100644 index b0462c1e..00000000 --- a/.cursor/rules/memory-bank/errors/build-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# build errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/errors.md b/.cursor/rules/memory-bank/errors/errors.md deleted file mode 100644 index 33bd85eb..00000000 --- a/.cursor/rules/memory-bank/errors/errors.md +++ /dev/null @@ -1,12 +0,0 @@ - -## [2025-07-19T01:19:27.379Z] - New Entry - -Best practice: Использовать только ESM-экспорт для внутренних пакетов платформы. Это упрощает сборку, устраняет ошибки Vite/esbuild, ускоряет разработку и повышает Compatibility с современными инструментами. - ---- - -## [2025-07-19T01:19:22.935Z] - General Error - -Error сборки: Failed to resolve entry for package '@extension/vite-config'. Решение: перевести пакет на ESM-only, main: dist/index..js - ---- diff --git a/.cursor/rules/memory-bank/errors/runtime-errors.md b/.cursor/rules/memory-bank/errors/runtime-errors.md deleted file mode 100644 index c8b0744e..00000000 --- a/.cursor/rules/memory-bank/errors/runtime-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# runtime errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/typescript-errors.md b/.cursor/rules/memory-bank/errors/typescript-errors.md deleted file mode 100644 index 7f9bbbfb..00000000 --- a/.cursor/rules/memory-bank/errors/typescript-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# TypeScript errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/errors/ui-errors.md b/.cursor/rules/memory-bank/errors/ui-errors.md deleted file mode 100644 index 19cf9b26..00000000 --- a/.cursor/rules/memory-bank/errors/ui-errors.md +++ /dev/null @@ -1,12 +0,0 @@ -# ui errors - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/memory-bank-controller.mdc b/.cursor/rules/memory-bank/memory-bank-controller.mdc deleted file mode 100644 index de782b38..00000000 --- a/.cursor/rules/memory-bank/memory-bank-controller.mdc +++ /dev/null @@ -1,309 +0,0 @@ -# Memory Bank Controller - Система управления memory-bank - -## 🎯 **Принцип: Memory-bank полностью подчинен .cursor правилам** - -### **Обоснование:** -- ✅ Оба каталога предназначены исключительно для AI -- ✅ Единая система правил и стандартов -- ✅ Автоматизация всех процессов -- ✅ Консистентность и предсказуемость -- ✅ Масштабируемость и поддерживаемость - -## 📋 **Структура управления memory-bank** - -### **1. Автоматическое создание структуры** -```bash -# Команда для создания структуры memory-bank -node cursor-manager..js create-memory-structure [project-type] - -# Поддерживаемые Typeы проектов: -- React-TypeScript # React + TypeScript проекты -- node-API # Node.js API проекты -- chrome-extension # Chrome Extension проекты -- monorepo # Монорепозитории -- fullstack # Full-stack приложения -``` - -### **2. Шаблоны структуры для разных Typeов проектов** - -#### **React TypeScript Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст React проекта -│ ├── progress.md # Development progress React компонентов -│ ├── projectbrief.md # Description React проекта -│ └── session-log.md # Лог сессий React разработки -├── errors/ -│ ├── errors.md # Ошибки React/TypeScript -│ ├── build-errors.md # Ошибки сборки Vite/Webpack -│ ├── runtime-errors.md # Runtime ошибки React -│ └── ui-errors.md # UI/UX ошибки -├── architecture/ -│ ├── decisions.md # Architectural decisions React -│ ├── patterns.md # React Patterns -│ ├── State-management.md # Управление Stateм -│ └── component-structure.md # Структура компонентов -├── development/ -│ ├── testing-resu.ts.md # Результаты тестирования React -│ ├── debugging-guIDE.md # Debugging React приложений -│ ├── devtools-guIDE.md # React DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── component-library.md # Библиотека компонентов -│ ├── styling-patterns.md # Patterns стилизации -│ └── responsive-design.md # Адаптивный Design -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── optimization-plans.md # Планы оптимизации -│ └── migration-plans.md # Планы миграции -├── context/ -│ ├── tech-stack.md # Технический стек -│ ├── dependencies.md # Dependencies проекта -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -#### **Chrome Extension Project:** -``` -memory-bank/ -├── core/ -│ ├── activeContext.md # Текущий контекст extension -│ ├── progress.md # Development progress extension -│ ├── projectbrief.md # Description extension -│ └── session-log.md # Лог сессий разработки -├── errors/ -│ ├── errors.md # Ошибки extension -│ ├── manifest-errors.md # Ошибки manifest.json -│ ├── permission-errors.md # Ошибки разрешений -│ └── API-errors.md # Ошибки Chrome APIs -├── architecture/ -│ ├── decisions.md # Architectural decisions -│ ├── background-scri.ts.md # Background scri.ts -│ ├── content-scri.ts.md # Content scri.ts -│ └── popup-structure.md # Структура popup -├── development/ -│ ├── testing-resu.ts.md # Testing extension -│ ├── debugging-guIDE.md # Debugging extension -│ ├── devtools-guIDE.md # Chrome DevTools -│ └── version-management.md # Управление версиями -├── ui/ -│ ├── popup-design.md # Design popup -│ ├── options-page.md # Страница настроек -│ └── content-ui.md # UI в content scri.ts -├── planning/ -│ ├── feature-roadmap.md # Roadmap фич -│ ├── store-publishing.md # Публикация в store -│ └── user-feedback.md # Обратная связь пользователей -├── context/ -│ ├── chrome-APIs.md # Используемые Chrome APIs -│ ├── permissions.md # Требуемые разрешения -│ └── environment.md # Окружение разработки -└── deprecated/ - └── old-files.md # Deprecated files -``` - -## 🔄 **Автоматизация процессов memory-bank** - -### **1. Автоматическое создание записей** -```bash -# Команды для автоматического создания записей -node cursor-manager..js memory-add "content" --type=error --category=build -node cursor-manager..js memory-add "content" --type=decision --category=architecture -node cursor-manager..js memory-add "content" --type=progress --category=feature -``` - -### **2. Автоматическое обновление контекста** -```bash -# Обновление активного контекста -node cursor-manager..js memory-update-context --auto -node cursor-manager..js memory-update-progress --auto -node cursor-manager..js memory-update-session --auto -``` - -### **3. Автоматическое Recovery контекста** -```bash -# Recovery полного контекста -node cursor-manager..js memory-restore --full -node cursor-manager..js memory-restore --quick -node cursor-manager..js memory-restore --category=errors -``` - -## 📝 **Правила создания и сохранения контекста** - -### **1. Структура записей (унифицированная)** -```markdown -## [YYYY-MM-DD HH:MM:SS] - Краткое Description - -**Type:** error|decision|progress|context|plan -**Category:** build|runtime|architecture|ui|testing|planning -**Priority:** critical|high|medium|low - -**Контекст:** Description ситуации и обстоятельств - -**Детали:** Подробная информация о проблеме/решении - -**Решение/Результат:** Что было сделано и результат - -**Status:** ✅ Решено | 🔄 In Progress | ❌ Проблема | 📋 План - -**Связанные файлы:** -- .cursor/rules/dev/development-principles.mdc -- memory-bank/errors/build-errors.md - -**Теги:** #React #TypeScript #build #vite - -**AI Команды:** -- `аудит cursor` - для проверки .cursor правил -- `задокументируй` - для создания документации -``` - -### **2. Автоматические теги и категоризация** -```JavaScript -// Автоматическое определение тегов -const autoTags = { - 'React': /React.jsx|component|hook/i, - 'TypeScript': /TypeScript.ts|type|interface/i, - 'build': /build|compile|vite|webpack/i, - 'runtime': /runtime|error|crash|exception/i, - 'ui': /ui|ux|component|styling/i, - 'testing': /test|spec|jest|cypress/i, - 'architecture': /architecture|pattern|structure/i -}; -``` - -### **3. Автоматическое связывание с .cursor правилами** -```JavaScript -// Автоматическое создание связей -const ruleConnections = { - 'error': '.cursor/rules/dev/error-handling.mdc', - 'architecture': '.cursor/rules/architecture/patterns.mdc', - 'testing': '.cursor/rules/dev/testing-troubleshooting.mdc', - 'ui': '.cursor/rules/ui/ui-patterns.mdc', - 'build': '.cursor/rules/dev/build-troubleshooting.mdc' -}; -``` - -## 🤖 **AI Команды для управления memory-bank** - -### **1. Команды создания и обновления** -```bash -# Создание новой записи -"создай запись в memory-bank" / "add memory entry" -"добавь ошибку в memory-bank" / "add error to memory" -"запиши решение в memory-bank" / "record solution" - -# Обновление существующих записей -"обнови контекст" / "update context" -"обнови прогресс" / "update progress" -"обнови сессию" / "update session" -``` - -### **2. Команды восстановления и поиска** -```bash -# Recovery контекста -"восстанови контекст" / "restore context" -"восстанови полный контекст" / "restore full context" -"быстрое Recovery" / "quick restore" - -# Поиск информации -"найди в memory-bank" / "search memory" -"покажи ошибки" / "show errors" -"покажи решения" / "show solutions" -``` - -### **3. Команды управления структурой** -```bash -# Управление структурой -"создай структуру memory-bank" / "create memory structure" -"реорганизуй memory-bank" / "reorganize memory" -"очисти memory-bank" / "clean memory" -"аудит memory-bank" / "audit memory" -``` - -## 🔧 **Integration с существующими .cursor правилами** - -### **1. Обновление documentation-helper..js** -```JavaScript -// Add поддержку memory-bank команд -const memoryCommands = { - 'memory-add': 'Add запись в memory-bank', - 'memory-update': 'Update запись в memory-bank', - 'memory-restore': 'Восстановить контекст из memory-bank', - 'memory-search': 'Поиск в memory-bank', - 'memory-audit': 'Аудит memory-bank' -}; -``` - -### **2. Обновление ai-memory.mdc** -```markdown -### Memory Bank Management: -- `создай запись в memory-bank` - Create новую запись с автоматической категоризацией -- `обнови контекст` - Update activeContext.md с текущим Statusом -- `восстанови контекст` - Восстановить полный контекст из memory-bank -- `аудит memory-bank` - Провести аудит и оптимизацию memory-bank -- `реорганизуй memory-bank` - Реорганизовать структуру по новым правилам -``` - -### **3. Обновление cursor-manager..js** -```JavaScript -// Add команды управления memory-bank -case 'memory-add': - await this.memoryAdd(options); - break; -case 'memory-update': - await this.memoryUpdate(options); - break; -case 'memory-restore': - await this.memoryRestore(options); - break; -case 'memory-audit': - await this.memoryAudit(options); - break; -``` - -## 📊 **Автоматические отчеты и аналитика** - -### **1. Отчеты по memory-bank** -```bash -# Генерация отчетов -node cursor-manager..js memory-report --type=errors -node cursor-manager..js memory-report --type=progress -node cursor-manager..js memory-report --type=full -``` - -### **2. Аналитика использования** -```JavaScript -// Метрики memory-bank -const metrics = { - totalEntries: 0, - entriesByCategory: {}, - entriesByType: {}, - recentActivity: [], - mostReferencedRules: [], - errorResolutionRate: 0 -}; -``` - -## ✅ **Результат полной интеграции** - -### **Преимущества:** -- ✅ **Единая система правил** - все управляется через .cursor -- ✅ **Автоматизация** - Minimum ручного вмешательства -- ✅ **Консистентность** - единообразные форматы и структуры -- ✅ **Масштабируемость** - легко добавлять новые Typeы проектов -- ✅ **AI-Optimization** - все правила учитывают потребности AI -- ✅ **Воспроизводимость** - одинаковые результаты на разных проектах - -### **Workflow:** -1. **Создание проекта** → Автоматическое создание структуры memory-bank -2. **Development** → Автоматическое создание записей через AI команды -3. **Recovery контекста** → Автоматическое чтение и анализ memory-bank -4. **Аудит и Optimization** → Регулярные проверки и улучшения - -**Memory-bank теперь полностью интегрирован с .cursor правилами!** 🚀 -description: -globs: -alwaysApply: false ---- diff --git a/.cursor/rules/memory-bank/planning/README.md b/.cursor/rules/memory-bank/planning/README.md deleted file mode 100644 index d61ef5fc..00000000 --- a/.cursor/rules/memory-bank/planning/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Planning & Roadmap - React-TypeScript - -## Files in this category: - -- [feature-roadmap](./feature-roadmap.md) -- [migration-plans](./migration-plans.md) -- [optimization-plans](./optimization-plans.md) -- [tech-debt](./tech-debt.md) - -## Description: - -Планы развития проекта, roadmap, стратегические решения. - -## AI Commands: - -- `добавь в planning` - Add запись в эту категорию -- `обнови planning` - Update файлы в категории -- `покажи planning` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/feature-roadmap.md b/.cursor/rules/memory-bank/planning/feature-roadmap.md deleted file mode 100644 index 90ffb932..00000000 --- a/.cursor/rules/memory-bank/planning/feature-roadmap.md +++ /dev/null @@ -1,12 +0,0 @@ -# Feature Roadmap - React-TypeScript - -## Overview - -This file contains feature planning and roadmap. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/migration-plans.md b/.cursor/rules/memory-bank/planning/migration-plans.md deleted file mode 100644 index 012f80e1..00000000 --- a/.cursor/rules/memory-bank/planning/migration-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# migration plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/optimization-plans.md b/.cursor/rules/memory-bank/planning/optimization-plans.md deleted file mode 100644 index a31a1b10..00000000 --- a/.cursor/rules/memory-bank/planning/optimization-plans.md +++ /dev/null @@ -1,12 +0,0 @@ -# optimization plans - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/planning/tech-debt.md b/.cursor/rules/memory-bank/planning/tech-debt.md deleted file mode 100644 index 01cbe91c..00000000 --- a/.cursor/rules/memory-bank/planning/tech-debt.md +++ /dev/null @@ -1,12 +0,0 @@ -# tech debt - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/README.md b/.cursor/rules/memory-bank/ui/README.md deleted file mode 100644 index 47639357..00000000 --- a/.cursor/rules/memory-bank/ui/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# UI/UX Context - React-TypeScript - -## Files in this category: - -- [accessibility](./accessibility.md) -- [component-library](./component-library.md) -- [performance](./performance.md) -- [responsive-design](./responsive-design.md) -- [styling-patterns](./styling-patterns.md) - -## Description: - -UI/UX решения и улучшения, User Experience. - -## AI Commands: - -- `добавь в ui` - Add запись в эту категорию -- `обнови ui` - Update файлы в категории -- `покажи ui` - Показать содержимое категории - ---- -*Auto-generated for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/accessibility.md b/.cursor/rules/memory-bank/ui/accessibility.md deleted file mode 100644 index e80643a1..00000000 --- a/.cursor/rules/memory-bank/ui/accessibility.md +++ /dev/null @@ -1,12 +0,0 @@ -# accessibility - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/component-library.md b/.cursor/rules/memory-bank/ui/component-library.md deleted file mode 100644 index 5c32e785..00000000 --- a/.cursor/rules/memory-bank/ui/component-library.md +++ /dev/null @@ -1,12 +0,0 @@ -# component library - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/performance.md b/.cursor/rules/memory-bank/ui/performance.md deleted file mode 100644 index e7b7c8ed..00000000 --- a/.cursor/rules/memory-bank/ui/performance.md +++ /dev/null @@ -1,12 +0,0 @@ -# performance - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/responsive-design.md b/.cursor/rules/memory-bank/ui/responsive-design.md deleted file mode 100644 index d26e30f1..00000000 --- a/.cursor/rules/memory-bank/ui/responsive-design.md +++ /dev/null @@ -1,12 +0,0 @@ -# responsive design - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/memory-bank/ui/styling-patterns.md b/.cursor/rules/memory-bank/ui/styling-patterns.md deleted file mode 100644 index 05d60aae..00000000 --- a/.cursor/rules/memory-bank/ui/styling-patterns.md +++ /dev/null @@ -1,12 +0,0 @@ -# styling patterns - -## Overview - -This file contains project-specific information. - -## Entries - - - ---- -*Generated on 2025-07-19 for React-TypeScript project* diff --git a/.cursor/rules/optimize-for-ai.cjs b/.cursor/rules/optimize-for-ai.cjs deleted file mode 100644 index a13d2ae4..00000000 --- a/.cursor/rules/optimize-for-ai.cjs +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env node - -const fs = require('fs'); -const path = require('path'); - -class AIOptimizer { - constructor() { - this.rulesDir = path.join(__dirname); - } - - async optimize() { - console.log('🤖 Optimizing .cursor for AI and Cursor...\n'); - - await this.optimizeMetadata(); - await this.addAITags(); - await this.optimizeStructure(); - await this.createAIIndex(); - await this.validateAIReadiness(); - - console.log('\n✅ AI optimization completed!'); - } - - async optimizeMetadata() { - console.log('📋 Optimizing metadata for AI...'); - - const mdcFiles = this.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - const optimizedContent = this.optimizeFileMetadata(content, file); - - if (optimizedContent !== content) { - fs.writeFileSync(file, optimizedContent); - console.log(` ✅ Optimized ${path.relative(this.rulesDir, file)}`); - } - } - } - - optimizeFileMetadata(content, filePath) { - const metadataMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/); - if (!metadataMatch) return content; - - const metadata = metadataMatch[1]; - const fileName = path.basename(filePath, '.mdc'); - const dirName = path.basename(path.dirname(filePath)); - - // Оптимизируем globs для лучшего понимания AI - let optimizedGlobs = this.getOptimizedGlobs(fileName, dirName); - let optimizedAlwaysApply = this.shouldAlwaysApply(fileName, dirName); - - // Добавляем AI-специфичные метаданные - let newMetadata = metadata; - - // Обновляем globs если нужно - if (!metadata.includes('globs:') || !metadata.includes(optimizedGlobs)) { - newMetadata = newMetadata.replace(/globs:.*\n/, `globs: ${optimizedGlobs}\n`); - if (!newMetadata.includes('globs:')) { - newMetadata = `globs: ${optimizedGlobs}\n${newMetadata}`; - } - } - - // Обновляем alwaysApply если нужно - if (!metadata.includes('alwaysApply:') || !metadata.includes(optimizedAlwaysApply.toString())) { - newMetadata = newMetadata.replace(/alwaysApply:.*\n/, `alwaysApply: ${optimizedAlwaysApply}\n`); - if (!newMetadata.includes('alwaysApply:')) { - newMetadata = `${newMetadata}alwaysApply: ${optimizedAlwaysApply}\n`; - } - } - - // Добавляем AI-специфичные поля - if (!metadata.includes('aiPriority:')) { - newMetadata = `${newMetadata}aiPriority: ${this.getAIPriority(fileName, dirName)}\n`; - } - - if (!metadata.includes('aiCategory:')) { - newMetadata = `${newMetadata}aiCategory: ${this.getAICategory(dirName)}\n`; - } - - if (newMetadata !== metadata) { - return content.replace(metadataMatch[0], `---\n${newMetadata}---\n\n`); - } - - return content; - } - - getOptimizedGlobs(fileName, dirName) { - const globMap = { - 'architecture': '["platform-core/**/*", "chrome-extension/src/background/*", "**/*.ts", "**/*.js"]', - 'dev': '["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.py", "**/*.md"]', - 'doc': '["**/*.md", "**/*.mdc", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]', - 'plugin': '["public/plugins/**/*", "**/*.py", "**/*.json", "**/*.ts", "**/*.js"]', - 'security': '["**/*.ts", "**/*.js", "**/*.py", "**/*.json", "platform-core/**/*"]', - 'ui': '["pages/**/*", "**/*.tsx", "**/*.css", "**/*.scss", "packages/ui/**/*"]', - 'workflow': '["**/*.ts", "**/*.js", "**/*.json", "**/*.md", "**/*.mdc"]' - }; - - return globMap[dirName] || '["**/*"]'; - } - - shouldAlwaysApply(fileName, dirName) { - const alwaysApplyFiles = [ - 'ai-memory', 'environment', 'index', 'README', - 'principle', 'architecture', 'workflow', 'security', - 'ai-first', 'ai-fallback', 'mdc-file-standards' - ]; - - return alwaysApplyFiles.some(pattern => fileName.includes(pattern)) || dirName === 'architecture'; - } - - getAIPriority(fileName, dirName) { - if (fileName.includes('ai-memory') || fileName.includes('environment')) { - return 'critical'; - } else if (fileName.includes('principle') || fileName.includes('architecture')) { - return 'high'; - } else if (dirName === 'doc' || dirName === 'workflow') { - return 'medium'; - } else { - return 'normal'; - } - } - - getAICategory(dirName) { - const categoryMap = { - 'architecture': 'system-design', - 'dev': 'development-practices', - 'doc': 'documentation', - 'plugin': 'plugin-development', - 'security': 'security', - 'ui': 'user-interface', - 'workflow': 'process-management' - }; - - return categoryMap[dirName] || 'general'; - } - - async addAITags() { - console.log('🏷️ Adding AI-specific tags...'); - - const mdcFiles = this.getFilesByExt('.mdc'); - - for (const file of mdcFiles) { - const content = fs.readFileSync(file, 'utf8'); - const taggedContent = this.addAITagsToContent(content, file); - - if (taggedContent !== content) { - fs.writeFileSync(file, taggedContent); - console.log(` ✅ Added AI tags to ${path.relative(this.rulesDir, file)}`); - } - } - } - - addAITagsToContent(content, filePath) { - const fileName = path.basename(filePath, '.mdc'); - const dirName = path.basename(path.dirname(filePath)); - - // Добавляем AI-специфичные комментарии в начало файла - let aiHeader = ''; - - if (fileName.includes('principle')) { - aiHeader = ` - - - -`; - } else if (fileName.includes('architecture')) { - aiHeader = ` - - - -`; - } else if (fileName.includes('ai-')) { - aiHeader = ` - - - -`; - } - - if (aiHeader && !content.includes('
- - + +
@@ -326,7 +326,7 @@

Кастомный промпт (редактируемый)

// Глобальные переменные let currentSettings = {}; let originalPrompts = {}; - let currentType = 'optimized'; + let currentType = 'basic_analysis'; let currentLang = 'ru'; // Показ уведомления diff --git a/chrome-extension/public/plugins/ozon-analyzer/production-config.json b/chrome-extension/public/plugins/ozon-analyzer/production-config.json old mode 100644 new mode 100755 index 90ff540c..c2837a62 --- a/chrome-extension/public/plugins/ozon-analyzer/production-config.json +++ b/chrome-extension/public/plugins/ozon-analyzer/production-config.json @@ -33,7 +33,7 @@ "ai_providers": { "google": { - "fallback_chain": ["gemini-flash", "gemini-pro"], + "fallback_chain": ["gemini-flash-lite", "gemini-pro"], "rate_limits": { "requests_per_minute": 60, "requests_per_hour": 1000, diff --git a/chrome-extension/public/plugins/ozon-analyzer/workflow.json b/chrome-extension/public/plugins/ozon-analyzer/workflow.json old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/test-plugin/icon.svg b/chrome-extension/public/plugins/test-plugin/icon.svg old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/test-plugin/manifest.json b/chrome-extension/public/plugins/test-plugin/manifest.json old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/test-plugin/mcp_server.py b/chrome-extension/public/plugins/test-plugin/mcp_server.py old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/time-test/icon.svg b/chrome-extension/public/plugins/time-test/icon.svg old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/time-test/manifest.json b/chrome-extension/public/plugins/time-test/manifest.json old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/time-test/mcp_server.py b/chrome-extension/public/plugins/time-test/mcp_server.py old mode 100644 new mode 100755 diff --git a/chrome-extension/public/plugins/time-test/workflow.json b/chrome-extension/public/plugins/time-test/workflow.json old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide-worker.js b/chrome-extension/public/pyodide-worker.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSans-Bold.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSans-Bold.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSans-BoldOblique.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSans-BoldOblique.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSans-Oblique.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSans-Oblique.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSans.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSans.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSansDisplay.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSansDisplay.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSansMono-Bold.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSansMono-Bold.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSansMono-BoldOblique.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSansMono-BoldOblique.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSansMono-Oblique.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSansMono-Oblique.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSansMono.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSansMono.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSerif-Bold.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSerif-Bold.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSerif-BoldItalic.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSerif-BoldItalic.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSerif-Italic.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSerif-Italic.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSerif.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSerif.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/DejaVuSerifDisplay.ttf b/chrome-extension/public/pyodide/fonts/DejaVuSerifDisplay.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/Humor-Sans.ttf b/chrome-extension/public/pyodide/fonts/Humor-Sans.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/LICENSE_DEJAVU b/chrome-extension/public/pyodide/fonts/LICENSE_DEJAVU old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/LICENSE_STIX b/chrome-extension/public/pyodide/fonts/LICENSE_STIX old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXGeneral.ttf b/chrome-extension/public/pyodide/fonts/STIXGeneral.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXGeneralBol.ttf b/chrome-extension/public/pyodide/fonts/STIXGeneralBol.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXGeneralBolIta.ttf b/chrome-extension/public/pyodide/fonts/STIXGeneralBolIta.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXGeneralItalic.ttf b/chrome-extension/public/pyodide/fonts/STIXGeneralItalic.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXNonUni.ttf b/chrome-extension/public/pyodide/fonts/STIXNonUni.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXNonUniBol.ttf b/chrome-extension/public/pyodide/fonts/STIXNonUniBol.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXNonUniBolIta.ttf b/chrome-extension/public/pyodide/fonts/STIXNonUniBolIta.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXNonUniIta.ttf b/chrome-extension/public/pyodide/fonts/STIXNonUniIta.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizFiveSymReg.ttf b/chrome-extension/public/pyodide/fonts/STIXSizFiveSymReg.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizFourSymBol.ttf b/chrome-extension/public/pyodide/fonts/STIXSizFourSymBol.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizFourSymReg.ttf b/chrome-extension/public/pyodide/fonts/STIXSizFourSymReg.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizOneSymBol.ttf b/chrome-extension/public/pyodide/fonts/STIXSizOneSymBol.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizOneSymReg.ttf b/chrome-extension/public/pyodide/fonts/STIXSizOneSymReg.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizThreeSymBol.ttf b/chrome-extension/public/pyodide/fonts/STIXSizThreeSymBol.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizThreeSymReg.ttf b/chrome-extension/public/pyodide/fonts/STIXSizThreeSymReg.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizTwoSymBol.ttf b/chrome-extension/public/pyodide/fonts/STIXSizTwoSymBol.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/STIXSizTwoSymReg.ttf b/chrome-extension/public/pyodide/fonts/STIXSizTwoSymReg.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmb10.ttf b/chrome-extension/public/pyodide/fonts/cmb10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmex10.ttf b/chrome-extension/public/pyodide/fonts/cmex10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmmi10.ttf b/chrome-extension/public/pyodide/fonts/cmmi10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmr10.ttf b/chrome-extension/public/pyodide/fonts/cmr10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmss10.ttf b/chrome-extension/public/pyodide/fonts/cmss10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmsy10.ttf b/chrome-extension/public/pyodide/fonts/cmsy10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/fonts/cmtt10.ttf b/chrome-extension/public/pyodide/fonts/cmtt10.ttf old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/package.json b/chrome-extension/public/pyodide/package.json old mode 100644 new mode 100755 index d0f9e382..cb45de43 --- a/chrome-extension/public/pyodide/package.json +++ b/chrome-extension/public/pyodide/package.json @@ -1,6 +1,6 @@ { "name": "pyodide", - "version": "0.27.1370", + "version": "0.27.1519", "description": "The Pyodide JavaScript package", "keywords": [ "python", diff --git a/chrome-extension/public/pyodide/pyodide-lock.json b/chrome-extension/public/pyodide/pyodide-lock.json old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/pyodide.asm.js b/chrome-extension/public/pyodide/pyodide.asm.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/pyodide.js b/chrome-extension/public/pyodide/pyodide.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/pyodide.js.map b/chrome-extension/public/pyodide/pyodide.js.map old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/python_stdlib.zip b/chrome-extension/public/pyodide/python_stdlib.zip old mode 100644 new mode 100755 diff --git a/chrome-extension/public/pyodide/test.py b/chrome-extension/public/pyodide/test.py deleted file mode 100644 index bea2ace4..00000000 --- a/chrome-extension/public/pyodide/test.py +++ /dev/null @@ -1,11 +0,0 @@ -print("Hello from Pyodide inline execution!") -print(f"Python version: {__import__('sys').version}") - -# Test basic math -result = 42 * 2 + 10 -print(f"Math result: {result}") - -# Test js bridge -js.console.log("Message from Python to JS console") - -print("test completed successfully") \ No newline at end of file diff --git a/chrome-extension/public/side-panel/assets/index-B6mlbBPM.js b/chrome-extension/public/side-panel/assets/index-B6mlbBPM.js deleted file mode 100644 index f0f3851f..00000000 --- a/chrome-extension/public/side-panel/assets/index-B6mlbBPM.js +++ /dev/null @@ -1,50 +0,0 @@ -(function(){const m=document.createElement("link").relList;if(m&&m.supports&&m.supports("modulepreload"))return;for(const A of document.querySelectorAll('link[rel="modulepreload"]'))c(A);new MutationObserver(A=>{for(const z of A)if(z.type==="childList")for(const D of z.addedNodes)D.tagName==="LINK"&&D.rel==="modulepreload"&&c(D)}).observe(document,{childList:!0,subtree:!0});function h(A){const z={};return A.integrity&&(z.integrity=A.integrity),A.referrerPolicy&&(z.referrerPolicy=A.referrerPolicy),A.crossOrigin==="use-credentials"?z.credentials="include":A.crossOrigin==="anonymous"?z.credentials="omit":z.credentials="same-origin",z}function c(A){if(A.ep)return;A.ep=!0;const z=h(A);fetch(A.href,z)}})();var au=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Rh(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var xo={exports:{}},La={};/** - * @license React - * react-jsx-runtime.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Dd;function Uh(){if(Dd)return La;Dd=1;var o=Symbol.for("react.transitional.element"),m=Symbol.for("react.fragment");function h(c,A,z){var D=null;if(z!==void 0&&(D=""+z),A.key!==void 0&&(D=""+A.key),"key"in A){z={};for(var I in A)I!=="key"&&(z[I]=A[I])}else z=A;return A=z.ref,{$$typeof:o,type:c,key:D,ref:A!==void 0?A:null,props:z}}return La.Fragment=m,La.jsx=h,La.jsxs=h,La}var Rd;function Nh(){return Rd||(Rd=1,xo.exports=Uh()),xo.exports}var y=Nh();const jh=({isDraftSaved:o,isDraftLoading:m,draftError:h,messageLength:c,minLength:A,maxLength:z})=>{const D=()=>m?y.jsx("div",{className:"draft-status-loader"}):h?y.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[y.jsx("circle",{cx:"12",cy:"12",r:"10"}),y.jsx("line",{x1:"12",y1:"8",x2:"12",y2:"12"}),y.jsx("line",{x1:"12",y1:"16",x2:"12.01",y2:"16"})]}):o?y.jsx("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:y.jsx("path",{d:"M20 6L9 17l-5-5"})}):c>0&&cm?"Загрузка черновика...":h||(o?"Черновик сохранен":c>0&&c=A&&c<=z?"Черновик будет сохранен автоматически":c>z?"Превышен лимит символов":""),H=()=>m?"draft-status loading":h?"draft-status error":o?"draft-status saved":c>0&&c=A&&c<=z?"draft-status ready":c>z?"draft-status error":"draft-status";return c===0&&!m&&!h?null:y.jsxs("div",{className:H(),children:[D(),y.jsx("span",{className:"draft-status-text",children:I()})]})};var Eo={exports:{}},be={};/** - * @license React - * react.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ud;function wh(){if(Ud)return be;Ud=1;var o=Symbol.for("react.transitional.element"),m=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),c=Symbol.for("react.strict_mode"),A=Symbol.for("react.profiler"),z=Symbol.for("react.consumer"),D=Symbol.for("react.context"),I=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),b=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),Z=Symbol.iterator;function $(f){return f===null||typeof f!="object"?null:(f=Z&&f[Z]||f["@@iterator"],typeof f=="function"?f:null)}var ue={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ie=Object.assign,oe={};function k(f,R,W){this.props=f,this.context=R,this.refs=oe,this.updater=W||ue}k.prototype.isReactComponent={},k.prototype.setState=function(f,R){if(typeof f!="object"&&typeof f!="function"&&f!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,f,R,"setState")},k.prototype.forceUpdate=function(f){this.updater.enqueueForceUpdate(this,f,"forceUpdate")};function K(){}K.prototype=k.prototype;function te(f,R,W){this.props=f,this.context=R,this.refs=oe,this.updater=W||ue}var V=te.prototype=new K;V.constructor=te,ie(V,k.prototype),V.isPureReactComponent=!0;var de=Array.isArray,C={H:null,A:null,T:null,S:null,V:null},F=Object.prototype.hasOwnProperty;function J(f,R,W,L,ee,me){return W=me.ref,{$$typeof:o,type:f,key:R,ref:W!==void 0?W:null,props:me}}function Y(f,R){return J(f.type,R,void 0,void 0,void 0,f.props)}function ye(f){return typeof f=="object"&&f!==null&&f.$$typeof===o}function Ae(f){var R={"=":"=0",":":"=2"};return"$"+f.replace(/[=:]/g,function(W){return R[W]})}var pe=/\/+/g;function Te(f,R){return typeof f=="object"&&f!==null&&f.key!=null?Ae(""+f.key):R.toString(36)}function ut(){}function Ke(f){switch(f.status){case"fulfilled":return f.value;case"rejected":throw f.reason;default:switch(typeof f.status=="string"?f.then(ut,ut):(f.status="pending",f.then(function(R){f.status==="pending"&&(f.status="fulfilled",f.value=R)},function(R){f.status==="pending"&&(f.status="rejected",f.reason=R)})),f.status){case"fulfilled":return f.value;case"rejected":throw f.reason}}throw f}function we(f,R,W,L,ee){var me=typeof f;(me==="undefined"||me==="boolean")&&(f=null);var re=!1;if(f===null)re=!0;else switch(me){case"bigint":case"string":case"number":re=!0;break;case"object":switch(f.$$typeof){case o:case m:re=!0;break;case O:return re=f._init,we(re(f._payload),R,W,L,ee)}}if(re)return ee=ee(f),re=L===""?"."+Te(f,0):L,de(ee)?(W="",re!=null&&(W=re.replace(pe,"$&/")+"/"),we(ee,R,W,"",function(st){return st})):ee!=null&&(ye(ee)&&(ee=Y(ee,W+(ee.key==null||f&&f.key===ee.key?"":(""+ee.key).replace(pe,"$&/")+"/")+re)),R.push(ee)),1;re=0;var De=L===""?".":L+":";if(de(f))for(var Ne=0;Ne>",save:"Save"},settings:{title:"Platform Settings",aiKeys:{title:"AI API Keys",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Basic analysis",gemini25:"Gemini 2.5 Pro - Deep analysis"},customKeys:{title:"Custom API keys",addButton:"+ Add new key",namePlaceholder:"Enter key name",keyPlaceholder:"Enter API key"},status:{configured:"Configured",notConfigured:"Not Configured",testing:"Testing..."},badges:{free:"Free"},actions:{save:"Save all keys",test:"Test connections"},messages:{saved:"Settings saved!",saveError:"Error saving settings",testComplete:"Testing completed!"}}}},Gh="Platform Settings",Bh="Available plugins",Ph="AI API Keys",qh="Free",Yh="Custom API keys",Lh="+ Add new key",Xh="Enter API key",Qh="Enter key name",Vh="Save all keys",Kh="Test connections",Zh="General Settings",kh="Automatic plugin updates",Jh="Show notifications",$h="Interface theme:",Wh="Light",Fh="Dark",Ih="System",ey="Security",ty="Verify plugin signatures",ly="Isolated execution mode",ny="Performance",ay="Maximum number of active plugins:",iy="Cache plugin data",uy="Plugin Details",sy={options:Hh,options_settings_title:Gh,options_plugins_title:Bh,options_settings_aiKeys_title:Ph,options_settings_aiKeys_badges_free:qh,options_settings_aiKeys_customKeys_title:Yh,options_settings_aiKeys_customKeys_addButton:Lh,options_settings_aiKeys_customKeys_keyPlaceholder:Xh,options_settings_aiKeys_customKeys_namePlaceholder:Qh,options_settings_aiKeys_actions_save:Vh,options_settings_aiKeys_actions_test:Kh,options_settings_general_title:Zh,options_settings_general_autoUpdate:kh,options_settings_general_showNotifications:Jh,options_settings_general_theme:$h,options_settings_general_theme_light:Wh,options_settings_general_theme_dark:Fh,options_settings_general_theme_system:Ih,options_settings_security_title:ey,options_settings_security_checkSignatures:ty,options_settings_security_isolatedMode:ly,options_settings_performance_title:ny,options_settings_performance_maxPlugins:ay,options_settings_performance_cacheData:iy,options_plugins_details_title:uy},oy={title:"Agent-Plugins-Platform",subtitle:"Панель управления плагинами",tabs:{plugins:"Плагины",settings:"Настройки"},plugins:{title:"Доступные плагины",loading:"Загрузка плагинов...",error:"Ошибка загрузки плагинов",version:"v{version}",details:{title:"Детали плагина",version:"Версия",description:"Описание",mainServer:"Основной сервер",hostPermissions:"Разрешения хостов",permissions:"Разрешения",selectPlugin:"Выберите плагин из списка слева."},ozonAnalyzer:{customSettings:"Пользовательские настройки",enableDeepAnalysis:"Глубокий анализ",enableDeepAnalysisTooltip:"Включает детальный анализ товаров с использованием продвинутых моделей ИИ",autoRequestDeepAnalysis:"Автоматический запрос глубокого анализа",autoRequestDeepAnalysisTooltip:"Автоматически запрашивать глубокий анализ при обнаружении сложных товаров",responseLanguage:"Язык ответов:",responseLanguageTooltip:"Выберите язык для ответов анализатора",languages:{ru:"Русский",en:"English",auto:"Автоматически"}},prompts:{settings:"Настройки промптов",type:"Тип промпта:",optimized:"Оптимизированный",deep:"Глубокий",language:"Язык:",russian:"Русский",english:"Английский",originalPrompt:"Оригинальный промпт (только чтение)",customPrompt:"Кастомный промпт",copyToCustom:">>",save:"Сохранить"}},settings:{title:"Настройки Платформы",aiKeys:{title:"API Ключи нейросетей",fixedKeys:{geminiFlash:"Google Gemini (Flash) - Базовый анализ",gemini25:"Gemini 2.5 Pro - Глубокий анализ"},customKeys:{title:"Пользовательские API ключи",addButton:"+ Добавить новый ключ",namePlaceholder:"Введите название ключа",keyPlaceholder:"Введите API ключ"},status:{configured:"Настроен",notConfigured:"Не настроен",testing:"Тестирование..."},badges:{free:"Бесплатный"},actions:{save:"Сохранить все ключи",test:"Тестировать подключения"},messages:{saved:"Настройки сохранены!",saveError:"Ошибка при сохранении настроек",testComplete:"Тестирование завершено!"}}}},cy="Настройки Платформы",ry="Доступные плагины",fy="API Ключи нейросетей",dy="Бесплатный",gy="Пользовательские API ключи",my="+ Добавить новый ключ",hy="Введите API ключ",yy="Введите название ключа",py="Сохранить все ключи",vy="Тестировать подключения",by="Общие настройки",Sy="Автоматическое обновление плагинов",xy="Показывать уведомления",Ey="Тема интерфейса:",_y="Светлая",Ay="Тёмная",Ty="Системная",Cy="Безопасность",Oy="Проверять подписи плагинов",My="Изолированный режим выполнения",zy="Производительность",Dy="Максимальное количество активных плагинов:",Ry="Кэширование данных плагинов",Uy="Детали плагина",Ny={options:oy,options_settings_title:cy,options_plugins_title:ry,options_settings_aiKeys_title:fy,options_settings_aiKeys_badges_free:dy,options_settings_aiKeys_customKeys_title:gy,options_settings_aiKeys_customKeys_addButton:my,options_settings_aiKeys_customKeys_keyPlaceholder:hy,options_settings_aiKeys_customKeys_namePlaceholder:yy,options_settings_aiKeys_actions_save:py,options_settings_aiKeys_actions_test:vy,options_settings_general_title:by,options_settings_general_autoUpdate:Sy,options_settings_general_showNotifications:xy,options_settings_general_theme:Ey,options_settings_general_theme_light:_y,options_settings_general_theme_dark:Ay,options_settings_general_theme_system:Ty,options_settings_security_title:Cy,options_settings_security_checkSignatures:Oy,options_settings_security_isolatedMode:My,options_settings_performance_title:zy,options_settings_performance_maxPlugins:Dy,options_settings_performance_cacheData:Ry,options_plugins_details_title:Uy},jd={en:sy,ru:Ny},jy=(o="en")=>({t:Q.useMemo(()=>{const h=jd[o]||{},c=(A,z)=>z.split(".").reduce((D,I)=>D&&D[I]?D[I]:void 0,A);return A=>{const z=c(h,A);return z!==void 0?z:A}},[o,jd]),locale:o}),_o=({checked:o,onChange:m,disabled:h,label:c,iconOn:A,iconOff:z})=>y.jsxs("label",{style:{display:"inline-flex",alignItems:"center",cursor:h?"not-allowed":"pointer",gap:8},children:[y.jsx("input",{type:"checkbox",checked:o,disabled:h,onChange:D=>m(D.target.checked),style:{display:"none"}}),y.jsx("span",{style:{width:40,height:22,borderRadius:12,background:o?"aqua":"#ccc",position:"relative",transition:"background 0.2s",display:"inline-block"},children:y.jsx("span",{style:{position:"absolute",left:o?20:2,top:2,width:18,height:18,borderRadius:"50%",background:"#fff",boxShadow:"0 1px 4px rgba(0,0,0,0.15)",transition:"left 0.2s",display:"flex",alignItems:"center",justifyContent:"center"},children:o?A:z})}),c&&y.jsx("span",{style:{userSelect:"none",fontSize:15},children:c})]}),wy=({error:o,resetError:m})=>y.jsxs("div",{style:{color:"red",padding:24},children:[y.jsx("h2",{children:"Произошла ошибка"}),o&&y.jsx("pre",{children:o.message}),m&&y.jsx("button",{onClick:m,children:"Сбросить"})]}),Hy=({children:o})=>{const[m,h]=ou.useState(null),c=ou.useCallback(()=>h(null),[]);if(m)return y.jsx(wy,{error:m,resetError:c});try{return y.jsx(y.Fragment,{children:o})}catch(A){return h(A),null}},Gy=(...o)=>o.filter(Boolean).join(" "),By=({value:o,manifest:m,disabled:h,onSave:c,locale:A,t:z})=>{const[D,I]=Q.useState("optimized"),[H,b]=Q.useState("ru"),[O,Z]=Q.useState(""),$=()=>{try{const k=m?.options?.prompts;if(!k)return"";const V=((k[D]||{})[H]||{}).default||"";return typeof V=="object"?JSON.stringify(V,null,2):V}catch{return""}},ue=()=>{try{const te=((o||{})[D]||{})[H];return typeof te=="string"?te:te==null?"":JSON.stringify(te,null,2)}catch{return""}};Q.useEffect(()=>{Z(ue())},[D,H,o]);const ie=()=>{Z($())},oe=()=>{try{const k={...o};k[D]||(k[D]={ru:"",en:""}),k[D][H]=O,c(k)}catch(k){console.error("Failed to save custom prompt:",k)}};return y.jsx("div",{style:{display:"flex",flexDirection:"column",gap:"16px"},children:y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"15px",fontWeight:"bold",marginBottom:"8px",display:"block"},children:z("options.plugins.prompts.settings")}),y.jsxs("div",{style:{display:"flex",gap:"16px",marginBottom:"16px"},children:[y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:z("options.plugins.prompts.type")}),y.jsxs("select",{value:D,onChange:k=>I(k.target.value),disabled:h,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"optimized",children:z("options.plugins.prompts.optimized")}),y.jsx("option",{value:"deep",children:z("options.plugins.prompts.deep")})]})]}),y.jsxs("div",{children:[y.jsx("label",{style:{fontSize:"14px",marginRight:"8px"},children:z("options.plugins.prompts.language")}),y.jsxs("select",{value:H,onChange:k=>b(k.target.value),disabled:h,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"},children:[y.jsx("option",{value:"ru",children:z("options.plugins.prompts.russian")}),y.jsx("option",{value:"en",children:z("options.plugins.prompts.english")})]})]})]}),y.jsxs("div",{style:{display:"flex",gap:"16px",alignItems:"stretch"},children:[y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:z("options.plugins.prompts.originalPrompt")}),y.jsx("textarea",{value:$(),readOnly:!0,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"#f5f5f5",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]}),y.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:y.jsx("button",{onClick:ie,disabled:h,style:{padding:"8px 16px",backgroundColor:"#007bff",color:"white",border:"none",borderRadius:"4px",cursor:h?"not-allowed":"pointer",fontSize:"16px",fontWeight:"bold"},children:z("options.plugins.prompts.copyToCustom")})}),y.jsxs("div",{style:{flex:1},children:[y.jsx("label",{style:{fontSize:"14px",fontWeight:"bold",display:"block",marginBottom:"4px"},children:z("options.plugins.prompts.customPrompt")}),y.jsx("textarea",{value:O,onChange:k=>Z(k.target.value),disabled:h,style:{width:"100%",height:"300px",padding:"8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"12px",fontFamily:"monospace",resize:"vertical"}})]})]}),y.jsx("div",{style:{marginTop:"16px",textAlign:"center"},children:y.jsx("button",{onClick:oe,disabled:h,style:{padding:"8px 24px",backgroundColor:"#28a745",color:"white",border:"none",borderRadius:"4px",cursor:h?"not-allowed":"pointer",fontSize:"14px",fontWeight:"bold"},children:z("options.plugins.prompts.save")})})]})})},Py=o=>{const{selectedPlugin:m,locale:h="en",onUpdateSetting:c}=o,{t:A}=jy(h),[z,D]=Q.useState(null),[I,H]=Q.useState(null),b=C=>C?typeof C=="string"?C:C[h]||C.ru||C.en||"":"";if(Q.useEffect(()=>{m?.manifest?.options&&$()},[m?.id]),!m||typeof m!="object")return y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:A("options_plugins_details_title")}),y.jsx("p",{children:A("options.plugins.details.selectPlugin")})]});const O=m.settings||{enabled:!0,autorun:!1},Z=m.manifest?.host_permissions||[],$=async()=>{if(!(!m||!m.manifest?.options||I!==null))try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const F=Object.keys(m.manifest.options).map(ye=>`${m.id}_${ye}`),J=await chrome.storage.local.get(F),Y={};Object.entries(J).forEach(([ye,Ae])=>{const pe=ye.replace(`${m.id}_`,"");Ae!==void 0&&(Y[pe]=Ae)}),H(Y)}}catch(C){console.warn("Failed to load custom settings from chrome.storage.local:",C)}},ue=async(C,F)=>{if(m)try{if(typeof chrome<"u"&&chrome.storage&&chrome.storage.local){const J=`${m.id}_${C}`;await chrome.storage.local.set({[J]:F}),H(Y=>({...Y,[C]:F})),C==="prompts"&&await ie()}else console.warn("chrome.storage.local is not available")}catch(J){throw console.error(`Failed to save setting ${C} to chrome.storage.local:`,J),J}},ie=async()=>{if(m)try{const C=`${m.id}_prompts`,F=await chrome.storage.local.get([C]);if(console.log("🔍 Диагностика промптов:"),console.log(` Plugin ID: ${m.id}`),console.log(` Storage key: ${C}`),console.log(" Сохраненные промпты:",F[C]),F[C]){const J=F[C];console.log(" Структура промптов:"),console.log(` - optimized.ru: ${J.optimized?.ru?"✓":"✗"} (${J.optimized?.ru?.length||0} символов)`),console.log(` - optimized.en: ${J.optimized?.en?"✓":"✗"} (${J.optimized?.en?.length||0} символов)`),console.log(` - deep.ru: ${J.deep?.ru?"✓":"✗"} (${J.deep?.ru?.length||0} символов)`),console.log(` - deep.en: ${J.deep?.en?"✓":"✗"} (${J.deep?.en?.length||0} символов)`)}}catch(C){console.error("Ошибка диагностики промптов:",C)}},oe=(C,F)=>{if(I&&I[C]!==void 0)return I[C];if(C==="prompts"&&typeof F=="object"&&F!==null){const J=F,Y={optimized:{ru:{},en:{}},deep:{ru:{},en:{}}};return J.optimized?.ru?.default&&(Y.optimized.ru=J.optimized.ru.default),J.optimized?.en?.default&&(Y.optimized.en=J.optimized.en.default),J.deep?.ru?.default&&(Y.deep.ru=J.deep.ru.default),J.deep?.en?.default&&(Y.deep.en=J.deep.en.default),Y}return F},k=(C,F)=>{const J=oe(C,F.default),Y=z===C||!(O.enabled??!0),ye=b(F.label),Ae=b(F.description);if(C==="prompts")return y.jsx("div",{className:"setting-item",children:y.jsx(By,{value:J,manifest:m.manifest,disabled:Y,onSave:pe=>K(C,pe),locale:h,t:A})},C);if(F.type==="boolean")return y.jsx("div",{className:"setting-item",children:y.jsx(_o,{checked:J,disabled:Y,onChange:pe=>K(C,pe),label:y.jsxs(y.Fragment,{children:[ye,Ae&&y.jsx("span",{className:"info-icon",title:Ae,children:"i"})]})})},C);if(F.type==="select")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",alignItems:"center",gap:"8px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{className:"info-icon",title:Ae,children:"i"}),y.jsx("select",{id:C,name:C,value:J,disabled:Y,onChange:pe=>K(C,pe.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px",minWidth:"120px"},children:F.values?.map(pe=>y.jsx("option",{value:pe,children:b(F.labels?.[pe])||pe},pe))})]})},C);if(F.type==="text")return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:Ae}),y.jsx("input",{type:"text",id:C,name:C,value:J,disabled:Y,onChange:pe=>K(C,pe.target.value),style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},C);if(F.type==="number"){const pe=Te=>{const ut=parseFloat(Te.target.value);if(isNaN(ut))return;let Ke=ut;F.min!==void 0&&KeF.max&&(Ke=F.max),K(C,Ke)};return y.jsx("div",{className:"setting-item",children:y.jsxs("label",{style:{display:"flex",flexDirection:"column",gap:"4px",fontSize:"15px"},children:[ye,Ae&&y.jsx("span",{style:{fontSize:"12px",color:"#666"},children:Ae}),y.jsx("input",{type:"number",id:C,name:C,value:J,disabled:Y,onChange:pe,min:F.min,max:F.max,step:F.step,style:{padding:"4px 8px",border:"1px solid #ccc",borderRadius:"4px",backgroundColor:"white",fontSize:"14px"}})]})},C)}return null},K=async(C,F)=>{if(!m)return;const J=m.manifest?.options;if(J&&J[C]){I===null&&await $();try{D(C),await ue(C,F)}catch(Y){console.error(`Failed to update custom setting ${C}:`,Y)}finally{D(null)}}else if(c)try{D(C),await c(m.id,C,F)}catch(Y){console.error(`Failed to update setting ${C}:`,Y)}finally{D(null)}},V=Object.entries(m.manifest?.options??{}).map(([C,F])=>k(C,F)).filter(C=>C!==null),de=()=>y.jsxs("div",{className:"detail-section",id:"plugin-settings",children:[y.jsx("h3",{children:"Настройки плагина"}),y.jsx("div",{className:"setting-item",children:y.jsx(_o,{checked:O.enabled??!0,disabled:z==="enabled",onChange:C=>K("enabled",C),label:y.jsxs(y.Fragment,{children:["Включен",y.jsx("span",{className:"info-icon",title:"Управляет активностью плагина. Отключение делает плагин неактивным.",children:"i"})]})})}),y.jsx("div",{className:"setting-item",children:y.jsx(_o,{checked:O.autorun??!1,disabled:z==="autorun"||!(O.enabled??!0),onChange:C=>K("autorun",C),label:y.jsxs(y.Fragment,{children:["Автоматический запуск",y.jsx("span",{className:"info-icon",title:"Если включено, плагин будет автоматически запускаться на подходящих страницах.",children:"i"})]})})})]});return y.jsx(Hy,{children:y.jsxs("div",{className:"plugin-details",children:[y.jsx("h2",{children:m.name}),y.jsx("div",{className:"details-header-divider"}),y.jsxs("div",{className:"plugin-detail-content active",children:[y.jsxs("div",{className:"detail-section",id:"plugin-info",children:[y.jsxs("p",{children:[y.jsx("strong",{children:"Версия:"})," v",m.version]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Статус:"}),y.jsx("span",{className:Gy("status-badge",O.enabled?"status-active":"status-inactive"),children:O.enabled?"Активен":"Неактивен"})]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Автор:"})," ",m.manifest?.author||"Не указан"]}),y.jsxs("p",{children:[y.jsx("strong",{children:"Последнее обновление:"})," ",m.manifest?.last_updated||"Неизвестно"]})]}),y.jsxs("div",{className:"detail-section",id:"plugin-description",children:[y.jsx("h3",{children:"Описание"}),y.jsx("p",{children:m.description})]}),Z.length>0&&y.jsxs("div",{className:"detail-section",id:"plugin-host-permissions",children:[y.jsx("h3",{children:"Сайты/домены"}),y.jsx("ul",{children:Z.map((C,F)=>y.jsx("li",{children:C},F))})]}),Array.isArray(m.manifest?.permissions)?y.jsxs("div",{className:"detail-section",id:"plugin-permissions",children:[y.jsx("h3",{children:"Разрешения"}),y.jsx("ul",{children:(m.manifest?.permissions??[]).map((C,F)=>y.jsx("li",{children:C},F))})]}):null,y.jsx(de,{}),m.manifest?.options&&Object.keys(m.manifest.options).length>0?y.jsxs("div",{className:"detail-section",id:"custom-settings",children:[y.jsx("h3",{children:"Дополнительные настройки"}),V]}):null]})]})})},qy=({plugin:o,onUpdateSetting:m})=>{const h=m?async(A,z,D)=>{try{return await m(A,z,D),!0}catch(I){return console.error(`Failed to update setting ${z}:`,I),!1}}:void 0,c={selectedPlugin:{...o,description:o.description||"Описание не указано",icon:o.icon||"",manifest:o.manifest||{}},locale:"ru",onUpdateSetting:h};return y.jsx(Py,{...c})},wd=function(o){if(!o)return"unknown-page";try{const m=new URL(o);return m.search="",m.hash="",m.toString()}catch{return o}},Yy=({pluginId:o,pageKey:m,debounceMs:h=1e3})=>{const[c,A]=Q.useState(""),[z,D]=Q.useState(!1),[I,H]=Q.useState(!1),[b,O]=Q.useState(null),[Z,$]=Q.useState(""),ue=Q.useRef(null),ie=Q.useRef(""),oe=Q.useCallback(async V=>{if(V!==ie.current){console.log("[useLazyChatSync] saveDraft: попытка сохранить draft",{pluginId:o,pageKey:m,text:V});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m,draftText:V}),ie.current=V,D(!0),O(null),$(V),console.log("[useLazyChatSync] saveDraft: успешно сохранено",{pluginId:o,pageKey:m,text:V})}catch(de){console.error("[useLazyChatSync] Error saving draft:",de),O("Ошибка сохранения черновика"),D(!1)}}},[o,m]),k=Q.useCallback(async()=>{H(!0),O(null),console.log("[useLazyChatSync] loadDraft: загружаем draft",{pluginId:o,pageKey:m});try{const V=await chrome.runtime.sendMessage({type:"GET_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m});V?.draftText?(A(V.draftText),ie.current=V.draftText,D(!0),$(V.draftText),console.log("[useLazyChatSync] loadDraft: найден draft",{pluginId:o,pageKey:m,draft:V.draftText})):(A(""),ie.current="",D(!1),$(""),console.log("[useLazyChatSync] loadDraft: draft не найден",{pluginId:o,pageKey:m}))}catch(V){console.error("[useLazyChatSync] Error loading draft:",V),O("Ошибка загрузки черновика")}finally{H(!1)}},[o,m]),K=Q.useCallback(async()=>{console.log("[useLazyChatSync] clearDraft: очищаем draft",{pluginId:o,pageKey:m});try{await chrome.runtime.sendMessage({type:"SAVE_PLUGIN_CHAT_DRAFT",pluginId:o,pageKey:m,draftText:""}),ie.current="",D(!1),O(null),$(""),A(""),console.log("[useLazyChatSync] clearDraft: успешно очищено",{pluginId:o,pageKey:m})}catch(V){console.error("[useLazyChatSync] Error clearing draft:",V),O("Ошибка очистки черновика")}},[o,m]),te=Q.useCallback(V=>{A(V),ue.current&&clearTimeout(ue.current),V.length===0?K():ue.current=setTimeout(()=>{oe(V)},h),console.log("[useLazyChatSync] setMessage: новое значение",{pluginId:o,pageKey:m,text:V})},[h,oe,K,o,m]);return Q.useEffect(()=>()=>{ue.current&&clearTimeout(ue.current)},[]),Q.useEffect(()=>{k()},[k]),Q.useEffect(()=>{console.log("[useLazyChatSync] pageKey изменился:",m),D(!1),H(!1),O(null),ie.current="",ue.current&&(clearTimeout(ue.current),ue.current=null),k()},[m,k]),{message:c,setMessage:te,isDraftSaved:z,isDraftLoading:I,draftError:b,loadDraft:k,clearDraft:K,draftText:Z}};var su={exports:{}},Ly=su.exports,Hd;function Xy(){return Hd||(Hd=1,(function(o,m){(function(h,c){c()})(Ly,function(){function h(b,O){return typeof O>"u"?O={autoBom:!1}:typeof O!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),O={autoBom:!O}),O.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(b.type)?new Blob(["\uFEFF",b],{type:b.type}):b}function c(b,O,Z){var $=new XMLHttpRequest;$.open("GET",b),$.responseType="blob",$.onload=function(){H($.response,O,Z)},$.onerror=function(){console.error("could not download file")},$.send()}function A(b){var O=new XMLHttpRequest;O.open("HEAD",b,!1);try{O.send()}catch{}return 200<=O.status&&299>=O.status}function z(b){try{b.dispatchEvent(new MouseEvent("click"))}catch{var O=document.createEvent("MouseEvents");O.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),b.dispatchEvent(O)}}var D=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof au=="object"&&au.global===au?au:void 0,I=D.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),H=D.saveAs||(typeof window!="object"||window!==D?function(){}:"download"in HTMLAnchorElement.prototype&&!I?function(b,O,Z){var $=D.URL||D.webkitURL,ue=document.createElement("a");O=O||b.name||"download",ue.download=O,ue.rel="noopener",typeof b=="string"?(ue.href=b,ue.origin===location.origin?z(ue):A(ue.href)?c(b,O,Z):z(ue,ue.target="_blank")):(ue.href=$.createObjectURL(b),setTimeout(function(){$.revokeObjectURL(ue.href)},4e4),setTimeout(function(){z(ue)},0))}:"msSaveOrOpenBlob"in navigator?function(b,O,Z){if(O=O||b.name||"download",typeof b!="string")navigator.msSaveOrOpenBlob(h(b,Z),O);else if(A(b))c(b,O,Z);else{var $=document.createElement("a");$.href=b,$.target="_blank",setTimeout(function(){z($)})}}:function(b,O,Z,$){if($=$||open("","_blank"),$&&($.document.title=$.document.body.innerText="downloading..."),typeof b=="string")return c(b,O,Z);var ue=b.type==="application/octet-stream",ie=/constructor/i.test(D.HTMLElement)||D.safari,oe=/CriOS\/[\d]+/.test(navigator.userAgent);if((oe||ue&&ie||I)&&typeof FileReader<"u"){var k=new FileReader;k.onloadend=function(){var V=k.result;V=oe?V:V.replace(/^data:[^;]*;/,"data:attachment/file;"),$?$.location.href=V:location=V,$=null},k.readAsDataURL(b)}else{var K=D.URL||D.webkitURL,te=K.createObjectURL(b);$?$.location=te:location.href=te,$=null,setTimeout(function(){K.revokeObjectURL(te)},4e4)}});D.saveAs=H.saveAs=H,o.exports=H})})(su)),su.exports}var Qy=Xy(),Ln;(function(o){o.Local="local",o.Sync="sync",o.Managed="managed",o.Session="session"})(Ln||(Ln={}));var jo;(function(o){o.ExtensionPagesOnly="TRUSTED_CONTEXTS",o.ExtensionPagesAndContentScripts="TRUSTED_AND_UNTRUSTED_CONTEXTS"})(jo||(jo={}));const Yn=globalThis.chrome,Gd=async(o,m)=>{const h=A=>typeof A=="function",c=A=>A instanceof Promise;return h(o)?(c(o),o(m)):o};let Bd=!1;const Pd=o=>{if(Yn&&!Yn.storage[o])throw new Error(`"storage" permission in manifest.ts: "storage ${o}" isn't defined`)},eg=(o,m,h)=>{let c=null,A=!1,z=[];const D=h?.storageEnum??Ln.Local,I=h?.liveUpdate??!1,H=h?.serialization?.serialize??(k=>k),b=h?.serialization?.deserialize??(k=>k);Bd===!1&&D===Ln.Session&&h?.sessionAccessForContentScripts===!0&&(Pd(D),Yn?.storage[D].setAccessLevel({accessLevel:jo.ExtensionPagesAndContentScripts}).catch(k=>{console.error(k),console.error("Please call .setAccessLevel() into different context, like a background script.")}),Bd=!0);const O=async()=>{Pd(D);const k=await Yn?.storage[D].get([o]);return k?b(k[o])??m:m},Z=async k=>{A||(c=await O()),c=await Gd(k,c),await Yn?.storage[D].set({[o]:H(c)}),ie()},$=k=>(z=[...z,k],()=>{z=z.filter(K=>K!==k)}),ue=()=>c,ie=()=>{z.forEach(k=>k())},oe=async k=>{if(k[o]===void 0)return;const K=b(k[o].newValue);c!==K&&(c=await Gd(K,c),ie())};return O().then(k=>{c=k,A=!0,ie()}),I&&Yn?.storage[D].onChanged.addListener(oe),{get:O,set:Z,getSnapshot:ue,subscribe:$}},qd=eg("theme-storage-key",{theme:"system",isLight:tg()},{storageEnum:Ln.Local,liveUpdate:!0});function tg(){return typeof window<"u"&&window.matchMedia?window.matchMedia("(prefers-color-scheme: light)").matches:!0}const Ao={...qd,toggle:async()=>{await qd.set(o=>{let m;switch(o.theme){case"light":m="dark";break;case"dark":m="system";break;case"system":default:m="light";break}const h=m==="system"?tg():m==="light";return{theme:m,isLight:h}})}},To=eg("chat-alignment-storage-key",{alignment:"left"},{storageEnum:Ln.Local,liveUpdate:!0}),Yd={...To,setAlignment:async o=>{await To.set({alignment:o})},getAlignment:async()=>(await To.get()).alignment},Vy=({plugin:o,currentView:m,isRunning:h,isPaused:c,currentTabUrl:A,onStart:z,onPause:D,onStop:I,onClose:H})=>{const[b,O]=Q.useState("chat"),[Z,$]=Q.useState(wd(A)),[ue,ie]=Q.useState("left");Q.useEffect(()=>{const M=wd(A);console.log("[PluginControlPanel] currentTabUrl изменился:",{oldPageKey:Z,newPageKey:M,currentTabUrl:A,timestamp:new Date().toISOString()}),$(M)},[A]),Q.useEffect(()=>{const M=async()=>{const p=await Yd.getAlignment();ie(p)};return M(),Yd.subscribe(()=>{M()})},[]);const{message:oe,setMessage:k,isDraftSaved:K,isDraftLoading:te,draftError:V,loadDraft:de,clearDraft:C,draftText:F}=Yy({pluginId:o.id,pageKey:Z,debounceMs:1e3}),[J,Y]=Q.useState([]),[ye,Ae]=Q.useState(!1),[pe,Te]=Q.useState(null),[ut,Ke]=Q.useState(60),[we,T]=Q.useState(!1),X=Q.useRef(null),q=Q.useRef(null);Q.useEffect(()=>{},[h]);const Ce=()=>{z()},f=o.name||(typeof o.manifest?.name=="string"?o.manifest.name:"")||o.id,R=o.id,W=Q.useCallback(async M=>{const g=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...M,messageId:g};try{return await chrome.runtime.sendMessage(p)}catch(B){throw console.error("[PluginControlPanel] sendMessageToBackgroundAsync - ошибка:",B),B}},[]),L=Q.useCallback(M=>{const g=Date.now().toString()+Math.random().toString(36).substr(2,9),p={...M,messageId:g};chrome.runtime.sendMessage(p)},[]),ee=Q.useCallback(()=>{console.log("[PluginControlPanel] 🧪 ТЕСТИРОВАНИЕ обработки сообщений с проблемными данными");const M={messages:[{id:"test_obj_1",text:{content:"Это объект вместо строки",type:"object"},role:"user",timestamp:Date.now()}]},g={messages:[{id:"test_null_1",text:null,role:"user",timestamp:Date.now()}]},p={messages:[{id:"test_undef_1",text:void 0,role:"user",timestamp:Date.now()}]},B=N=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:N,hasMessages:N&&"messages"in N,hasChat:N&&"chat"in N,messagesValue:N?.messages,chatValue:N?.chat,isMessagesArray:Array.isArray(N?.messages),isChatArray:Array.isArray(N?.chat),responseType:typeof N,responseKeys:N?Object.keys(N):"response is null/undefined",timestamp:new Date().toISOString()}),N===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let j=null;const P=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],ce=(Se,ve)=>{let xe=Se;for(const Ye of ve)if(xe&&typeof xe=="object"&&Ye in xe)xe=xe[Ye];else return;return xe};if(Array.isArray(N))j=N,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:j.length,firstMessage:j[0]?{id:j[0].id,content:j[0].content||j[0].text,role:j[0].role,timestamp:j[0].timestamp}:"no messages"});else if(N&&typeof N=="object"){if(N.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",N.error),Te(`Ошибка от background: ${N.error}`),Y([]);return}for(const{path:Se,description:ve}of P){const xe=ce(N,Se);if(Array.isArray(xe)){j=xe,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${ve}':`,{length:j.length,firstMessage:j[0]?{id:j[0].id,content:j[0].content||j[0].text,role:j[0].role,timestamp:j[0].timestamp}:"no messages"});break}}j||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof N,responseKeys:Object.keys(N),responseSample:JSON.stringify(N).substring(0,500),timestamp:new Date().toISOString()}),j=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:N,responseType:typeof N,responseStringified:JSON.stringify(N).substring(0,200),timestamp:new Date().toISOString()}),j=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:j,isArray:Array.isArray(j),length:j?.length,firstMessage:j?.[0],firstMessageType:j?.[0]?typeof j[0]:"none"}),Array.isArray(j)&&j.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",j.length);const Se=j.filter(ve=>!ve||typeof ve!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",ve),!1):!0).map((ve,xe)=>{try{let Ye=ve.content||ve.text||"";typeof Ye=="object"?(console.warn("[PluginControlPanel] text является объектом, конвертируем:",Ye),Ye=JSON.stringify(Ye)):Ye==null?(console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Ye=""):Ye=String(Ye);const cl={id:ve.id||String(ve.timestamp||Date.now()+xe),text:Ye,isUser:ve.role?ve.role==="user":!!ve.isUser,timestamp:ve.timestamp||Date.now()};return console.log(`[PluginControlPanel] Конвертировано сообщение ${xe}:`,{id:cl.id,textLength:cl.text.length,textType:typeof cl.text,isUser:cl.isUser}),cl}catch(Ye){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${xe}:`,Ye,ve),{id:`error_${Date.now()}_${xe}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:j.length,convertedCount:Se.length,firstConverted:Se[0],allConverted:Se.map(ve=>{try{const xe=typeof ve.text=="string"?ve.text.substring(0,50):String(ve.text||"").substring(0,50);return{id:ve.id,text:xe,isUser:ve.isUser,textType:typeof ve.text}}catch(xe){return console.warn("[PluginControlPanel] Error processing message text:",xe,ve),{id:ve.id,text:"[ERROR: invalid text]",isUser:ve.isUser,textType:typeof ve.text}}})}),Y(Se)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")};try{console.log("[PluginControlPanel] Тест 1: Обработка сообщения с объектом в text"),B(M)}catch(N){console.error("[PluginControlPanel] ❌ Тест 1 провалился:",N)}try{console.log("[PluginControlPanel] Тест 2: Обработка сообщения с null в text"),B(g)}catch(N){console.error("[PluginControlPanel] ❌ Тест 2 провалился:",N)}try{console.log("[PluginControlPanel] Тест 3: Обработка сообщения с undefined в text"),B(p)}catch(N){console.error("[PluginControlPanel] ❌ Тест 3 провалился:",N)}console.log("[PluginControlPanel] ✅ Тестирование обработки сообщений завершено")},[]);Q.useCallback(M=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:M,hasMessages:M&&"messages"in M,hasChat:M&&"chat"in M,messagesValue:M?.messages,chatValue:M?.chat,isMessagesArray:Array.isArray(M?.messages),isChatArray:Array.isArray(M?.chat),responseType:typeof M,responseKeys:M?Object.keys(M):"response is null/undefined",timestamp:new Date().toISOString()}),M===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let g=null;const p=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],B=(N,j)=>{let P=N;for(const ce of j)if(P&&typeof P=="object"&&ce in P)P=P[ce];else return;return P};if(Array.isArray(M))g=M,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:g.length,firstMessage:g[0]?{id:g[0].id,content:g[0].content||g[0].text,role:g[0].role,timestamp:g[0].timestamp}:"no messages"});else if(M&&typeof M=="object"){if(M.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",M.error),Te(`Ошибка от background: ${M.error}`),Y([]);return}for(const{path:N,description:j}of p){const P=B(M,N);if(Array.isArray(P)){g=P,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${j}':`,{length:g.length,firstMessage:g[0]?{id:g[0].id,content:g[0].content||g[0].text,role:g[0].role,timestamp:g[0].timestamp}:"no messages"});break}}g||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof M,responseKeys:Object.keys(M),responseSample:JSON.stringify(M).substring(0,500),timestamp:new Date().toISOString()}),g=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:M,responseType:typeof M,responseStringified:JSON.stringify(M).substring(0,200),timestamp:new Date().toISOString()}),g=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:g,isArray:Array.isArray(g),length:g?.length,firstMessage:g?.[0],firstMessageType:g?.[0]?typeof g[0]:"none"}),Array.isArray(g)&&g.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",g.length);const N=g.filter(j=>!j||typeof j!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",j),!1):!0).map((j,P)=>{try{let ce=j.content||j.text||"",Se=j.timestamp||Date.now();if(typeof ce=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",ce),ce=JSON.stringify(ce);else if(ce==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),ce="";else{ce=String(ce),console.log(`[PluginControlPanel] Raw textContent before JSON parse for message ${P}:`,ce);try{const xe=JSON.parse(ce);typeof xe=="object"&&xe!==null&&"content"in xe&&(console.log("[PluginControlPanel] Распарсен JSON из content:",xe),ce=String(xe.content||""),xe.timestamp&&typeof xe.timestamp=="number"&&(Se=xe.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}console.log(`[PluginControlPanel] TextContent after JSON parse for message ${P}:`,ce)}const ve={id:j.id||String(Se+P),text:ce,isUser:j.role?j.role==="user":!!j.isUser,timestamp:Se};return console.log(`[PluginControlPanel] Конвертировано сообщение ${P}:`,{id:ve.id,textLength:ve.text.length,textType:typeof ve.text,isUser:ve.isUser}),console.log(`[PluginControlPanel] Final converted text for message ${P}:`,ve.text),ve}catch(ce){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${P}:`,ce,j),{id:`error_${Date.now()}_${P}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:g.length,convertedCount:N.length,firstConverted:N[0],allConverted:N.map(j=>{try{const P=typeof j.text=="string"?j.text.substring(0,50):String(j.text||"").substring(0,50);return{id:j.id,text:P,isUser:j.isUser,textType:typeof j.text}}catch(P){return console.warn("[PluginControlPanel] Error processing message text:",P,j),{id:j.id,text:"[ERROR: invalid text]",isUser:j.isUser,textType:typeof j.text}}})}),Y(N)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")},[]);const me=Q.useCallback(async()=>{Ae(!0),Te(null),console.log("[PluginControlPanel] ===== НАЧАЛО loadChat =====",{pluginId:R,pageKey:Z,currentTabUrl:A,timestamp:new Date().toISOString(),isRunning:h,isPaused:c});try{console.log("[PluginControlPanel] loadChat - отправляем запрос GET_PLUGIN_CHAT");const M=await W({type:"GET_PLUGIN_CHAT",pluginId:R,pageKey:Z});console.log("[PluginControlPanel] loadChat - получен ответ от background:",M),console.log("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ LOADING В loadChat:",{loading:ye,messagesCount:J.length,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false"),M?.error?(console.error("[PluginControlPanel] loadChat - ошибка в ответе:",M.error),Te(`Ошибка загрузки чата: ${M.error}`),Y([])):(console.log("[PluginControlPanel] loadChat - обрабатываем успешный ответ"),(g=>{if(console.log("[PluginControlPanel] ===== НАЧАЛО processChatResponse ====="),console.log("[PluginControlPanel] Анализ chatData:",{response:g,hasMessages:g&&"messages"in g,hasChat:g&&"chat"in g,messagesValue:g?.messages,chatValue:g?.chat,isMessagesArray:Array.isArray(g?.messages),isChatArray:Array.isArray(g?.chat),responseType:typeof g,responseKeys:g?Object.keys(g):"response is null/undefined",timestamp:new Date().toISOString()}),g===null){console.log("[PluginControlPanel] ✅ Получен null - чат пустой, устанавливаем пустой массив сообщений"),Y([]);return}let p=null;const B=[{path:["messages"],description:"messages"},{path:["chat"],description:"chat"},{path:["chat","messages"],description:"chat.messages"},{path:["data","messages"],description:"data.messages"},{path:["result","messages"],description:"result.messages"},{path:["items"],description:"items"},{path:["history"],description:"history"},{path:["logs"],description:"logs"}],N=(j,P)=>{let ce=j;for(const Se of P)if(ce&&typeof ce=="object"&&Se in ce)ce=ce[Se];else return;return ce};if(Array.isArray(g))p=g,console.log("[PluginControlPanel] ✅ Ответ является массивом напрямую:",{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});else if(g&&typeof g=="object"){if(g.error){console.error("[PluginControlPanel] ❌ Background вернул ошибку:",g.error),Te(`Ошибка от background: ${g.error}`),Y([]);return}for(const{path:j,description:P}of B){const ce=N(g,j);if(Array.isArray(ce)){p=ce,console.log(`[PluginControlPanel] ✅ Найден массив сообщений по пути '${P}':`,{length:p.length,firstMessage:p[0]?{id:p[0].id,content:p[0].content||p[0].text,role:p[0].role,timestamp:p[0].timestamp}:"no messages"});break}}p||(console.warn("[PluginControlPanel] ⚠️ Не найден массив сообщений в объекте ответа:",{responseType:typeof g,responseKeys:Object.keys(g),responseSample:JSON.stringify(g).substring(0,500),timestamp:new Date().toISOString()}),p=[])}else console.warn("[PluginControlPanel] ⚠️ Ответ имеет неподдерживаемый тип:",{response:g,responseType:typeof g,responseStringified:JSON.stringify(g).substring(0,200),timestamp:new Date().toISOString()}),p=[];if(console.log("[PluginControlPanel] Финальный messagesArray:",{messagesArray:p,isArray:Array.isArray(p),length:p?.length,firstMessage:p?.[0],firstMessageType:p?.[0]?typeof p[0]:"none"}),Array.isArray(p)&&p.length>0){console.log("[PluginControlPanel] Начинаем конвертацию сообщений:",p.length);const j=p.filter(P=>!P||typeof P!="object"?(console.warn("[PluginControlPanel] Фильтруем некорректное сообщение:",P),!1):!0).map((P,ce)=>{try{let Se=P.content||P.text||"",ve=P.timestamp||Date.now();if(typeof Se=="object")console.warn("[PluginControlPanel] text является объектом, конвертируем:",Se),Se=JSON.stringify(Se);else if(Se==null)console.warn("[PluginControlPanel] text равен null/undefined, устанавливаем пустую строку"),Se="";else{Se=String(Se);try{const Ye=JSON.parse(Se);typeof Ye=="object"&&Ye!==null&&"content"in Ye&&(console.log("[PluginControlPanel] Распаршен JSON из content:",Ye),Se=String(Ye.content||""),Ye.timestamp&&typeof Ye.timestamp=="number"&&(ve=Ye.timestamp))}catch{console.log("[PluginControlPanel] content не является JSON, оставляем как есть")}}const xe={id:P.id||String(ve+ce),text:Se,isUser:P.role?P.role==="user":!!P.isUser,timestamp:ve};return console.log(`[PluginControlPanel] Конвертировано сообщение ${ce}:`,{id:xe.id,textLength:xe.text.length,textType:typeof xe.text,isUser:xe.isUser}),xe}catch(Se){return console.error(`[PluginControlPanel] Ошибка конвертации сообщения ${ce}:`,Se,P),{id:`error_${Date.now()}_${ce}`,text:"[ОШИБКА КОНВЕРТАЦИИ СООБЩЕНИЯ]",isUser:!1,timestamp:Date.now()}}});console.log("[PluginControlPanel] ✅ Успешная конвертация сообщений:",{originalCount:p.length,convertedCount:j.length,firstConverted:j[0],allConverted:j.map(P=>{try{const ce=typeof P.text=="string"?P.text.substring(0,50):String(P.text||"").substring(0,50);return{id:P.id,text:ce,isUser:P.isUser,textType:typeof P.text}}catch(ce){return console.warn("[PluginControlPanel] Error processing message text:",ce,P),{id:P.id,text:"[ERROR: invalid text]",isUser:P.isUser,textType:typeof P.text}}})}),Y(j)}else console.log("[PluginControlPanel] ⚠️ messagesArray пустой или не массив, устанавливаем пустой массив"),Y([]);console.log("[PluginControlPanel] ===== ЗАВЕРШЕНИЕ processChatResponse =====")})(M),console.log("[PluginControlPanel] loadChat: чат успешно загружен"))}catch(M){console.error("[PluginControlPanel] loadChat - ошибка при получении ответа:",M),console.error("[PluginControlPanel] loadChat - ТЕКУЩЕЕ СОСТОЯНИЕ В CATCH:",{loading:ye,messagesCount:J.length,errorType:typeof M,errorMessage:M instanceof Error?M.message:String(M),timestamp:new Date().toISOString()}),console.error("[PluginControlPanel] loadChat - ERROR DETAILS:",{error:M,errorType:typeof M,errorMessage:M instanceof Error?M.message:String(M),errorStack:M instanceof Error?M.stack:"No stack trace",errorName:M instanceof Error?M.name:"Unknown error type",timestamp:new Date().toISOString(),pluginId:R,pageKey:Z}),Ae(!1),console.log("[PluginControlPanel] loadChat - loading сброшен в false в catch блоке");let g="Ошибка связи с background";M instanceof Error?(g+=`: ${M.message}`,M.name==="TypeError"&&M.message.includes("substring")&&(g+=" (ошибка обработки текста - проверьте тип данных)",console.error("[PluginControlPanel] CRITICAL: substring error detected:",{originalError:M,stack:M.stack,context:{pluginId:R,pageKey:Z}}))):g+=`: ${String(M)}`,Te(g),Y([])}console.log("[PluginControlPanel] ===== loadChat ЗАВЕРШЕН =====")},[R,Z,W,A,h,c]);Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[loadChat] - триггер вызова loadChat",{pluginId:R,pageKey:Z,currentTabUrl:A,timestamp:new Date().toISOString()}),me().catch(M=>{console.error("[PluginControlPanel] useEffect[loadChat] - ошибка при вызове loadChat:",M)})},[me]),Q.useEffect(()=>{console.log("[PluginControlPanel] pageKey изменился, перезагружаем чат и черновик"),me().catch(M=>{console.error("[PluginControlPanel] Ошибка при перезагрузке чата:",M)}),de()},[Z,me,de]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - регистрация слушателя сообщений",{pluginId:R,pageKey:Z,timestamp:new Date().toISOString()});const M=p=>{if(console.log("[PluginControlPanel] ===== handleChatUpdate - получено сообщение =====",{type:p?.type,pluginId:p?.pluginId,pageKey:p?.pageKey,messageId:p?.messageId,hasResponse:!!p?.response,responseType:p?.response?typeof p.response:"none",timestamp:new Date().toISOString()}),p?.type==="PLUGIN_CHAT_UPDATED"&&p.pluginId===R&&p.pageKey===Z&&(console.log("[PluginControlPanel] handleChatUpdate - обновление чата получено, запрашиваем актуальные данные"),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД СБРОСОМ:",{loading:ye,messagesCount:J.length,currentPageKey:Z,pluginId:R,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен, вызываем loadChat"),me().catch(B=>{console.error("[PluginControlPanel] handleChatUpdate - ошибка при загрузке чата:",B)})),p?.type!=="PLUGIN_CHAT_UPDATED"&&p?.type!=="GET_PLUGIN_CHAT_RESPONSE"&&console.log("[PluginControlPanel] handleChatUpdate - получено необработанное сообщение:",{type:p?.type,fullEvent:p,timestamp:new Date().toISOString()}),p?.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат сохранения сообщения:",p),p.success?console.log("[PluginControlPanel] handleChatUpdate: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка сохранения сообщения",p.error),Te(`Ошибка сохранения сообщения: ${p.error}`))),p?.type==="DELETE_PLUGIN_CHAT_RESPONSE"&&(console.log("[PluginControlPanel] handleChatUpdate - результат удаления чата:",p),console.log("[PluginControlPanel] handleChatUpdate - ТЕКУЩЕЕ СОСТОЯНИЕ ПЕРЕД ОБРАБОТКОЙ DELETE_RESPONSE:",{loading:ye,messagesCount:J.length,eventSuccess:p.success,timestamp:new Date().toISOString()}),Ae(!1),console.log("[PluginControlPanel] handleChatUpdate - loading сброшен в false для DELETE_PLUGIN_CHAT_RESPONSE"),p.success?console.log("[PluginControlPanel] handleChatUpdate: чат успешно удален"):(console.error("[PluginControlPanel] handleChatUpdate: ошибка удаления чата",p.error),Te(`Ошибка удаления чата: ${p.error}`))),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let B=p.message.content,N=p.timestamp||Date.now();if(typeof B=="object")console.warn("[PluginControlPanel] PYODIDE content является объектом, конвертируем:",B),B=JSON.stringify(B);else if(B==null)console.warn("[PluginControlPanel] PYODIDE content равен null/undefined"),B="Пустое сообщение от Pyodide";else{B=String(B);try{const P=JSON.parse(B);typeof P=="object"&&P!==null&&"content"in P&&(console.log("[PluginControlPanel] Распарсен JSON из PYODIDE content:",P),B=String(P.content||""),P.timestamp&&typeof P.timestamp=="number"&&(N=P.timestamp))}catch{console.log("[PluginControlPanel] PYODIDE content не является JSON, оставляем как есть")}}const j={id:p.message.id||`pyodide_${N}_${Math.random()}`,text:B,isUser:!1,timestamp:N};console.log("[PluginControlPanel] Adding Pyodide message to chat:",j),Y(P=>[...P,j]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(B){console.error("[PluginControlPanel] Ошибка обработки PYODIDE_MESSAGE_UPDATE:",B,p);const N={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${B instanceof Error?B.message:String(B)}]`,isUser:!1,timestamp:Date.now()};Y(j=>[...j,N])}else console.warn("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE без content:",p.message)},g=p=>{p.type==="SAVE_PLUGIN_CHAT_MESSAGE_RESPONSE"&&(console.log("[PluginControlPanel] handleChatOperationResult: получен результат сохранения сообщения",p),p.success?console.log("[PluginControlPanel] handleChatOperationResult: сообщение успешно сохранено"):(console.error("[PluginControlPanel] handleChatOperationResult: ошибка сохранения сообщения",p.error),Te(`Ошибка сохранения сообщения: ${p.error}`)))};return chrome.runtime.onMessage.addListener(M),chrome.runtime.onMessage.removeListener(g),console.log("[PluginControlPanel] useEffect[handleChatUpdate] - слушатели сообщений зарегистрированы"),()=>{console.log("[PluginControlPanel] useEffect[handleChatUpdate] - удаление слушателей сообщений"),chrome.runtime.onMessage.removeListener(M),chrome.runtime.onMessage.removeListener(g)}},[R,Z,L,me]),Q.useEffect(()=>{m==="chat"&&de()},[m,de]),Q.useEffect(()=>{console.log("[PluginControlPanel] === РЕНДЕР ===",{pluginId:R,pageKey:Z,draftText:F,message:oe,currentView:m,isRunning:h,isPaused:c})}),Q.useEffect(()=>{const M=console.error;return console.error=(...g)=>{const p=g.join(" ");p.includes("substring")&&p.includes("is not a function")&&(console.error("[PluginControlPanel] 🔴 CRITICAL: substring error detected!"),console.error("[PluginControlPanel] Error details:",g),console.error("[PluginControlPanel] Stack trace:",new Error().stack)),M.apply(console,g)},console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring активирован"),()=>{console.error=M,console.log("[PluginControlPanel] Глобальный перехватчик ошибок substring деактивирован")}},[]),Q.useEffect(()=>{console.log("[PluginControlPanel] Настройка слушателя для pyodide messages");const M=g=>{const p=g.detail;if(console.log("[PluginControlPanel] Получен Pyodide custom event:",p),p?.type==="PYODIDE_MESSAGE_UPDATE")if(console.log("[PluginControlPanel] PYODIDE_MESSAGE_UPDATE received:",p.message),p.message?.content)try{let B=p.message.content,N=p.timestamp||Date.now();if(typeof B=="object")console.warn("[PluginControlPanel] Pyodide content является объектом, конвертируем:",B),B=JSON.stringify(B);else if(B==null)console.warn("[PluginControlPanel] Pyodide content равен null/undefined"),B="Пустое сообщение от Pyodide";else{B=String(B);try{const P=JSON.parse(B);typeof P=="object"&&P!==null&&"content"in P&&(console.log("[PluginControlPanel] Распарсен JSON из Pyodide content:",P),B=String(P.content||""),P.timestamp&&typeof P.timestamp=="number"&&(N=P.timestamp))}catch{console.log("[PluginControlPanel] Pyodide content не является JSON, оставляем как есть")}}const j={id:p.message.id||`pyodide_${N}_${Math.random()}`,text:B,isUser:!1,timestamp:N};console.log("[PluginControlPanel] Adding Pyodide message to chat:",j),Y(P=>[...P,j]),console.log("[PluginControlPanel] Pyodide message added to chat")}catch(B){console.error("[PluginControlPanel] Ошибка обработки Pyodide сообщения:",B,p);const N={id:`pyodide_error_${Date.now()}`,text:`[ОШИБКА PYODIDE: ${B instanceof Error?B.message:String(B)}]`,isUser:!1,timestamp:Date.now()};Y(j=>[...j,N])}else console.warn("[PluginControlPanel] Pyodide сообщение без content:",p.message)};return window.addEventListener("PYODIDE_MESSAGE_UPDATE",M),console.log("[PluginControlPanel] Слушатель для Pyodide custom events зарегистрирован"),()=>{window.removeEventListener("PYODIDE_MESSAGE_UPDATE",M),console.log("[PluginControlPanel] Слушатель для Pyodide custom events удален")}},[]),Q.useEffect(()=>{typeof F=="string"&&(k(F),console.log("[PluginControlPanel] draftText подставлен в поле ввода:",F))},[F,k]);const re=()=>{if(console.log("[PluginControlPanel] handleSendMessage: попытка отправки",{message:oe}),!oe.trim())return;const M={id:Date.now().toString(),text:oe.trim(),timestamp:Date.now()};k(""),Te(null),console.log("[PluginControlPanel] handleSendMessage: отправка сообщения в background"),L({type:"SAVE_PLUGIN_CHAT_MESSAGE",pluginId:R,pageKey:Z,message:{role:"user",content:M.text,timestamp:M.timestamp}}),C()};Q.useEffect(()=>{const M=p=>{if(!we)return;const B=document.querySelector(".chat-view");if(!B)return;const N=B.getBoundingClientRect(),j=N.bottom-p.clientY,P=100,ce=N.height-80;j>=P&&j<=ce&&Ke(N.height-j)},g=()=>{T(!1),document.body.style.cursor="",document.body.style.userSelect=""};return we&&(document.addEventListener("mousemove",M),document.addEventListener("mouseup",g)),()=>{document.removeEventListener("mousemove",M),document.removeEventListener("mouseup",g)}},[we]),Q.useEffect(()=>{X.current?.scrollIntoView({behavior:"smooth"})},[J]),Q.useEffect(()=>{console.log("[PluginControlPanel] useEffect[messages] - состояние messages обновлено:",{messagesCount:J.length,firstMessage:J[0]?{id:J[0].id,text:typeof J[0].text=="string"?J[0].text.substring(0,50):String(J[0].text||"").substring(0,50),isUser:J[0].isUser,timestamp:J[0].timestamp,textType:typeof J[0].text}:null,allMessages:J.map(M=>{try{const g=typeof M.text=="string"?M.text.substring(0,30):String(M.text||"").substring(0,30);return{id:M.id,text:g,isUser:M.isUser,textType:typeof M.text}}catch(g){return console.warn("[PluginControlPanel] Error in message logging:",g,M),{id:M.id,text:"[LOGGING ERROR]",isUser:M.isUser,textType:typeof M.text}}}),timestamp:new Date().toISOString()})},[J]),Q.useEffect(()=>{m==="chat"&&setTimeout(()=>q.current?.focus(),100)},[m]);const De=M=>{k(M.target.value);const g=M.target;g.style.height="auto";const p=Math.min(Math.max(g.scrollHeight,60),200);g.style.height=`${p}px`,Ke(p)},Ne=M=>{M.key==="Enter"&&!M.shiftKey&&(M.preventDefault(),re())},st=()=>{Ae(!0),Te(null),L({type:"DELETE_PLUGIN_CHAT",pluginId:R,pageKey:Z}),Y([]),C()},zt=()=>{const M=JSON.stringify(J,null,2),g=new Blob([M],{type:"application/json"});Qy.saveAs(g,`plugin-chat-${R}.json`)};return y.jsxs("div",{className:"plugin-control-panel",style:{"--chat-text-align":ue},children:[y.jsxs("div",{className:"panel-header",children:[y.jsxs("div",{className:"plugin-info",children:[y.jsx("img",{className:"plugin-icon",src:o.iconUrl||`plugins/${o.id}/${o.icon||"icon.svg"}`,alt:`${f} icon`,onError:M=>{const g=typeof f=="string"&&f.length>0?f.charAt(0):"P";M.currentTarget.src=`data:image/svg+xml;utf8,${g}`}}),y.jsx("h3",{children:f})]}),y.jsxs("div",{className:"control-buttons",children:[y.jsx("button",{className:`media-btn ${h?"stop-mode":"start-mode"}`,onClick:Ce,disabled:h||c,title:h?"Остановить":"Запустить",children:h?"⏹️":"▶️"}),y.jsx("button",{className:"media-btn pause-mode",onClick:D,disabled:!h||c,title:"Пауза",children:"⏸️"}),y.jsx("button",{className:"media-btn stop-mode",onClick:I,disabled:!h,title:"Остановить",children:"⏹️"}),y.jsx("button",{className:"media-btn close-mode",onClick:H,title:"Закрыть",children:"✕"})]})]}),y.jsxs("div",{className:"panel-tabs",children:[y.jsx("button",{className:`tab-btn ${b==="chat"?"active":""}`,onClick:()=>O("chat"),children:"Чат"}),y.jsx("button",{className:`tab-btn ${b==="details"?"active":""}`,onClick:()=>O("details"),children:"Детали"})]}),y.jsxs("div",{className:"panel-content",children:[b==="chat"&&y.jsxs("div",{className:"chat-view",children:[y.jsxs("div",{className:"chat-header",children:[y.jsx("h4",{children:"Чат"}),y.jsxs("div",{className:"chat-actions",children:[y.jsx("button",{onClick:st,disabled:ye||!!pe,children:ye?"Очистка...":pe?"Ошибка":"Очистить чат"}),y.jsx("button",{onClick:zt,disabled:ye||!!pe,children:ye?"Экспорт...":pe?"Ошибка":"Экспортировать"}),y.jsx("button",{onClick:ee,disabled:ye,style:{backgroundColor:"#ff6b35",marginLeft:"5px",display:"none"},title:"Протестировать обработку сообщений с проблемными данными",children:"🧪 Тест"})]})]}),y.jsxs("div",{className:"chat-messages",children:[ye&&y.jsx("div",{className:"chat-loader",children:"Загрузка сообщений..."}),pe&&y.jsx("div",{className:"chat-error",children:pe}),!ye&&!pe&&J.length===0&&y.jsxs("div",{className:"chat-placeholder",children:[y.jsx("p",{children:"Нет сообщений"}),y.jsx("p",{className:"chat-hint",children:"Напишите первое сообщение!"})]}),y.jsx("div",{className:"messages-container",children:J.map((M,g)=>{console.log("[PluginControlPanel] render message:",g,M);let p=M.text,B=M.timestamp;console.log("[PluginControlPanel] Raw message text before parsing for message",g,":",M.text);try{const N=JSON.parse(p);if(typeof N=="object"&&N!==null&&"content"in N){console.log("[PluginControlPanel] Парсинг JSON в рендере:",N);let j=N.content;typeof j=="object"?p=JSON.stringify(j):p=String(j||""),N.timestamp&&typeof N.timestamp=="number"&&(B=N.timestamp)}else if(typeof N=="string")p=N;else if(typeof N=="object"&&N!==null){const j=Object.values(N).filter(P=>typeof P=="string");j.length>0?p=String(j[0]):p=JSON.stringify(N)}}catch{console.log("[PluginControlPanel] Текст не является JSON, рендерим как есть")}return console.log("[PluginControlPanel] Display text after parsing for message",g,":",p),y.jsx("div",{className:`chat-message ${M.isUser?"user":"bot"}`,children:y.jsxs("div",{className:"message-content",children:[y.jsx("span",{className:"message-text",children:p}),y.jsx("span",{className:"message-time",children:new Date(B).toLocaleTimeString()})]})},M.id||g)})}),y.jsx("div",{ref:X})]}),y.jsxs("div",{className:"chat-input",children:[y.jsx("textarea",{id:"plugin-message-input",ref:q,className:"message-textarea",value:oe,onChange:De,onKeyPress:Ne,placeholder:"Напишите сообщение...",style:{height:`${ut}px`}}),y.jsx("button",{className:"send-btn",onClick:re,disabled:!oe.trim(),children:"📤"}),y.jsx(jh,{isDraftSaved:K,isDraftLoading:te,draftError:V,messageLength:oe.length,minLength:10,maxLength:1e3})]})]}),b==="details"&&y.jsx(qy,{plugin:o})]})]})},Ky=({toasts:o,onRemove:m})=>y.jsx("div",{className:"toast-container",children:o.map(h=>y.jsx(Zy,{toast:h,onRemove:m},h.id))}),Zy=({toast:o,onRemove:m})=>{const[h,c]=Q.useState(!1),[A,z]=Q.useState(!1);Q.useEffect(()=>{if(requestAnimationFrame(()=>{c(!0)}),o.duration>0){const I=setTimeout(()=>{D()},o.duration);return()=>clearTimeout(I)}},[o.duration]);const D=Q.useCallback(()=>{z(!0),setTimeout(()=>{m(o.id)},300)},[o.id,m]);return y.jsx("div",{className:`toast toast-${o.type} ${h?"toast-visible":""} ${A?"toast-hiding":""}`,children:y.jsxs("div",{className:"toast-content",children:[y.jsx("span",{className:"toast-message",children:o.message}),y.jsx("button",{className:"toast-close",onClick:D,"aria-label":"Закрыть уведомление",children:"×"})]})})},ky=({theme:o,isLight:m,onToggle:h,isInSidebar:c=!1})=>{const A=()=>{switch(o){case"light":return"🌙";case"dark":return"💻";case"system":return"☀️";default:return"🌙"}},z=()=>{switch(o){case"light":return"Переключить на темную тему";case"dark":return"Переключить на системную тему";case"system":return"Переключить на светлую тему";default:return"Переключить тему"}},D={background:"none",border:"1px solid #d1d5db",borderRadius:"50%",width:"40px",height:"40px",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",fontSize:"20px",...c?{}:{marginTop:"20px"}};return y.jsx("button",{onClick:h,style:D,title:z(),children:A()})};function lg(o){var m,h,c="";if(typeof o=="string"||typeof o=="number")c+=o;else if(typeof o=="object")if(Array.isArray(o)){var A=o.length;for(m=0;m{const m=Fy(o),{conflictingClassGroups:h,conflictingClassGroupModifiers:c}=o;return{getClassGroupId:D=>{const I=D.split(Po);return I[0]===""&&I.length!==1&&I.shift(),ng(I,m)||Wy(D)},getConflictingClassGroupIds:(D,I)=>{const H=h[D]||[];return I&&c[D]?[...H,...c[D]]:H}}},ng=(o,m)=>{if(o.length===0)return m.classGroupId;const h=o[0],c=m.nextPart.get(h),A=c?ng(o.slice(1),c):void 0;if(A)return A;if(m.validators.length===0)return;const z=o.join(Po);return m.validators.find(({validator:D})=>D(z))?.classGroupId},Ld=/^\[(.+)\]$/,Wy=o=>{if(Ld.test(o)){const m=Ld.exec(o)[1],h=m?.substring(0,m.indexOf(":"));if(h)return"arbitrary.."+h}},Fy=o=>{const{theme:m,classGroups:h}=o,c={nextPart:new Map,validators:[]};for(const A in h)wo(h[A],c,A,m);return c},wo=(o,m,h,c)=>{o.forEach(A=>{if(typeof A=="string"){const z=A===""?m:Xd(m,A);z.classGroupId=h;return}if(typeof A=="function"){if(Iy(A)){wo(A(c),m,h,c);return}m.validators.push({validator:A,classGroupId:h});return}Object.entries(A).forEach(([z,D])=>{wo(D,Xd(m,z),h,c)})})},Xd=(o,m)=>{let h=o;return m.split(Po).forEach(c=>{h.nextPart.has(c)||h.nextPart.set(c,{nextPart:new Map,validators:[]}),h=h.nextPart.get(c)}),h},Iy=o=>o.isThemeGetter,ep=o=>{if(o<1)return{get:()=>{},set:()=>{}};let m=0,h=new Map,c=new Map;const A=(z,D)=>{h.set(z,D),m++,m>o&&(m=0,c=h,h=new Map)};return{get(z){let D=h.get(z);if(D!==void 0)return D;if((D=c.get(z))!==void 0)return A(z,D),D},set(z,D){h.has(z)?h.set(z,D):A(z,D)}}},Ho="!",Go=":",tp=Go.length,lp=o=>{const{prefix:m,experimentalParseClassName:h}=o;let c=A=>{const z=[];let D=0,I=0,H=0,b;for(let ie=0;ieH?b-H:void 0;return{modifiers:z,hasImportantModifier:$,baseClassName:Z,maybePostfixModifierPosition:ue}};if(m){const A=m+Go,z=c;c=D=>D.startsWith(A)?z(D.substring(A.length)):{isExternal:!0,modifiers:[],hasImportantModifier:!1,baseClassName:D,maybePostfixModifierPosition:void 0}}if(h){const A=c;c=z=>h({className:z,parseClassName:A})}return c},np=o=>o.endsWith(Ho)?o.substring(0,o.length-1):o.startsWith(Ho)?o.substring(1):o,ap=o=>{const m=Object.fromEntries(o.orderSensitiveModifiers.map(c=>[c,!0]));return c=>{if(c.length<=1)return c;const A=[];let z=[];return c.forEach(D=>{D[0]==="["||m[D]?(A.push(...z.sort(),D),z=[]):z.push(D)}),A.push(...z.sort()),A}},ip=o=>({cache:ep(o.cacheSize),parseClassName:lp(o),sortModifiers:ap(o),...$y(o)}),up=/\s+/,sp=(o,m)=>{const{parseClassName:h,getClassGroupId:c,getConflictingClassGroupIds:A,sortModifiers:z}=m,D=[],I=o.trim().split(up);let H="";for(let b=I.length-1;b>=0;b-=1){const O=I[b],{isExternal:Z,modifiers:$,hasImportantModifier:ue,baseClassName:ie,maybePostfixModifierPosition:oe}=h(O);if(Z){H=O+(H.length>0?" "+H:H);continue}let k=!!oe,K=c(k?ie.substring(0,oe):ie);if(!K){if(!k){H=O+(H.length>0?" "+H:H);continue}if(K=c(ie),!K){H=O+(H.length>0?" "+H:H);continue}k=!1}const te=z($).join(":"),V=ue?te+Ho:te,de=V+K;if(D.includes(de))continue;D.push(de);const C=A(K,k);for(let F=0;F0?" "+H:H)}return H};function op(){let o=0,m,h,c="";for(;o{if(typeof o=="string")return o;let m,h="";for(let c=0;cZ(O),o());return h=ip(b),c=h.cache.get,A=h.cache.set,z=I,I(H)}function I(H){const b=c(H);if(b)return b;const O=sp(H,h);return A(H,O),O}return function(){return z(op.apply(null,arguments))}}const tt=o=>{const m=h=>h[o]||[];return m.isThemeGetter=!0,m},ig=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,ug=/^\((?:(\w[\w-]*):)?(.+)\)$/i,rp=/^\d+\/\d+$/,fp=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,dp=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,gp=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix)\(.+\)$/,mp=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,hp=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,qn=o=>rp.test(o),_e=o=>!!o&&!Number.isNaN(Number(o)),jl=o=>!!o&&Number.isInteger(Number(o)),Co=o=>o.endsWith("%")&&_e(o.slice(0,-1)),ol=o=>fp.test(o),yp=()=>!0,pp=o=>dp.test(o)&&!gp.test(o),sg=()=>!1,vp=o=>mp.test(o),bp=o=>hp.test(o),Sp=o=>!ne(o)&&!ae(o),xp=o=>Xn(o,rg,sg),ne=o=>ig.test(o),Il=o=>Xn(o,fg,pp),Oo=o=>Xn(o,Cp,_e),Qd=o=>Xn(o,og,sg),Ep=o=>Xn(o,cg,bp),iu=o=>Xn(o,dg,vp),ae=o=>ug.test(o),Xa=o=>Qn(o,fg),_p=o=>Qn(o,Op),Vd=o=>Qn(o,og),Ap=o=>Qn(o,rg),Tp=o=>Qn(o,cg),uu=o=>Qn(o,dg,!0),Xn=(o,m,h)=>{const c=ig.exec(o);return c?c[1]?m(c[1]):h(c[2]):!1},Qn=(o,m,h=!1)=>{const c=ug.exec(o);return c?c[1]?m(c[1]):h:!1},og=o=>o==="position"||o==="percentage",cg=o=>o==="image"||o==="url",rg=o=>o==="length"||o==="size"||o==="bg-size",fg=o=>o==="length",Cp=o=>o==="number",Op=o=>o==="family-name",dg=o=>o==="shadow",Mp=()=>{const o=tt("color"),m=tt("font"),h=tt("text"),c=tt("font-weight"),A=tt("tracking"),z=tt("leading"),D=tt("breakpoint"),I=tt("container"),H=tt("spacing"),b=tt("radius"),O=tt("shadow"),Z=tt("inset-shadow"),$=tt("text-shadow"),ue=tt("drop-shadow"),ie=tt("blur"),oe=tt("perspective"),k=tt("aspect"),K=tt("ease"),te=tt("animate"),V=()=>["auto","avoid","all","avoid-page","page","left","right","column"],de=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],C=()=>[...de(),ae,ne],F=()=>["auto","hidden","clip","visible","scroll"],J=()=>["auto","contain","none"],Y=()=>[ae,ne,H],ye=()=>[qn,"full","auto",...Y()],Ae=()=>[jl,"none","subgrid",ae,ne],pe=()=>["auto",{span:["full",jl,ae,ne]},jl,ae,ne],Te=()=>[jl,"auto",ae,ne],ut=()=>["auto","min","max","fr",ae,ne],Ke=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],we=()=>["start","end","center","stretch","center-safe","end-safe"],T=()=>["auto",...Y()],X=()=>[qn,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...Y()],q=()=>[o,ae,ne],Ce=()=>[...de(),Vd,Qd,{position:[ae,ne]}],f=()=>["no-repeat",{repeat:["","x","y","space","round"]}],R=()=>["auto","cover","contain",Ap,xp,{size:[ae,ne]}],W=()=>[Co,Xa,Il],L=()=>["","none","full",b,ae,ne],ee=()=>["",_e,Xa,Il],me=()=>["solid","dashed","dotted","double"],re=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],De=()=>[_e,Co,Vd,Qd],Ne=()=>["","none",ie,ae,ne],st=()=>["none",_e,ae,ne],zt=()=>["none",_e,ae,ne],M=()=>[_e,ae,ne],g=()=>[qn,"full",...Y()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[ol],breakpoint:[ol],color:[yp],container:[ol],"drop-shadow":[ol],ease:["in","out","in-out"],font:[Sp],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[ol],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[ol],shadow:[ol],spacing:["px",_e],text:[ol],"text-shadow":[ol],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",qn,ne,ae,k]}],container:["container"],columns:[{columns:[_e,ne,ae,I]}],"break-after":[{"break-after":V()}],"break-before":[{"break-before":V()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:C()}],overflow:[{overflow:F()}],"overflow-x":[{"overflow-x":F()}],"overflow-y":[{"overflow-y":F()}],overscroll:[{overscroll:J()}],"overscroll-x":[{"overscroll-x":J()}],"overscroll-y":[{"overscroll-y":J()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:ye()}],"inset-x":[{"inset-x":ye()}],"inset-y":[{"inset-y":ye()}],start:[{start:ye()}],end:[{end:ye()}],top:[{top:ye()}],right:[{right:ye()}],bottom:[{bottom:ye()}],left:[{left:ye()}],visibility:["visible","invisible","collapse"],z:[{z:[jl,"auto",ae,ne]}],basis:[{basis:[qn,"full","auto",I,...Y()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[_e,qn,"auto","initial","none",ne]}],grow:[{grow:["",_e,ae,ne]}],shrink:[{shrink:["",_e,ae,ne]}],order:[{order:[jl,"first","last","none",ae,ne]}],"grid-cols":[{"grid-cols":Ae()}],"col-start-end":[{col:pe()}],"col-start":[{"col-start":Te()}],"col-end":[{"col-end":Te()}],"grid-rows":[{"grid-rows":Ae()}],"row-start-end":[{row:pe()}],"row-start":[{"row-start":Te()}],"row-end":[{"row-end":Te()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":ut()}],"auto-rows":[{"auto-rows":ut()}],gap:[{gap:Y()}],"gap-x":[{"gap-x":Y()}],"gap-y":[{"gap-y":Y()}],"justify-content":[{justify:[...Ke(),"normal"]}],"justify-items":[{"justify-items":[...we(),"normal"]}],"justify-self":[{"justify-self":["auto",...we()]}],"align-content":[{content:["normal",...Ke()]}],"align-items":[{items:[...we(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...we(),{baseline:["","last"]}]}],"place-content":[{"place-content":Ke()}],"place-items":[{"place-items":[...we(),"baseline"]}],"place-self":[{"place-self":["auto",...we()]}],p:[{p:Y()}],px:[{px:Y()}],py:[{py:Y()}],ps:[{ps:Y()}],pe:[{pe:Y()}],pt:[{pt:Y()}],pr:[{pr:Y()}],pb:[{pb:Y()}],pl:[{pl:Y()}],m:[{m:T()}],mx:[{mx:T()}],my:[{my:T()}],ms:[{ms:T()}],me:[{me:T()}],mt:[{mt:T()}],mr:[{mr:T()}],mb:[{mb:T()}],ml:[{ml:T()}],"space-x":[{"space-x":Y()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":Y()}],"space-y-reverse":["space-y-reverse"],size:[{size:X()}],w:[{w:[I,"screen",...X()]}],"min-w":[{"min-w":[I,"screen","none",...X()]}],"max-w":[{"max-w":[I,"screen","none","prose",{screen:[D]},...X()]}],h:[{h:["screen","lh",...X()]}],"min-h":[{"min-h":["screen","lh","none",...X()]}],"max-h":[{"max-h":["screen","lh",...X()]}],"font-size":[{text:["base",h,Xa,Il]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[c,ae,Oo]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Co,ne]}],"font-family":[{font:[_p,ne,m]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[A,ae,ne]}],"line-clamp":[{"line-clamp":[_e,"none",ae,Oo]}],leading:[{leading:[z,...Y()]}],"list-image":[{"list-image":["none",ae,ne]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",ae,ne]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:q()}],"text-color":[{text:q()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...me(),"wavy"]}],"text-decoration-thickness":[{decoration:[_e,"from-font","auto",ae,Il]}],"text-decoration-color":[{decoration:q()}],"underline-offset":[{"underline-offset":[_e,"auto",ae,ne]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:Y()}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",ae,ne]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",ae,ne]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:Ce()}],"bg-repeat":[{bg:f()}],"bg-size":[{bg:R()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},jl,ae,ne],radial:["",ae,ne],conic:[jl,ae,ne]},Tp,Ep]}],"bg-color":[{bg:q()}],"gradient-from-pos":[{from:W()}],"gradient-via-pos":[{via:W()}],"gradient-to-pos":[{to:W()}],"gradient-from":[{from:q()}],"gradient-via":[{via:q()}],"gradient-to":[{to:q()}],rounded:[{rounded:L()}],"rounded-s":[{"rounded-s":L()}],"rounded-e":[{"rounded-e":L()}],"rounded-t":[{"rounded-t":L()}],"rounded-r":[{"rounded-r":L()}],"rounded-b":[{"rounded-b":L()}],"rounded-l":[{"rounded-l":L()}],"rounded-ss":[{"rounded-ss":L()}],"rounded-se":[{"rounded-se":L()}],"rounded-ee":[{"rounded-ee":L()}],"rounded-es":[{"rounded-es":L()}],"rounded-tl":[{"rounded-tl":L()}],"rounded-tr":[{"rounded-tr":L()}],"rounded-br":[{"rounded-br":L()}],"rounded-bl":[{"rounded-bl":L()}],"border-w":[{border:ee()}],"border-w-x":[{"border-x":ee()}],"border-w-y":[{"border-y":ee()}],"border-w-s":[{"border-s":ee()}],"border-w-e":[{"border-e":ee()}],"border-w-t":[{"border-t":ee()}],"border-w-r":[{"border-r":ee()}],"border-w-b":[{"border-b":ee()}],"border-w-l":[{"border-l":ee()}],"divide-x":[{"divide-x":ee()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":ee()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...me(),"hidden","none"]}],"divide-style":[{divide:[...me(),"hidden","none"]}],"border-color":[{border:q()}],"border-color-x":[{"border-x":q()}],"border-color-y":[{"border-y":q()}],"border-color-s":[{"border-s":q()}],"border-color-e":[{"border-e":q()}],"border-color-t":[{"border-t":q()}],"border-color-r":[{"border-r":q()}],"border-color-b":[{"border-b":q()}],"border-color-l":[{"border-l":q()}],"divide-color":[{divide:q()}],"outline-style":[{outline:[...me(),"none","hidden"]}],"outline-offset":[{"outline-offset":[_e,ae,ne]}],"outline-w":[{outline:["",_e,Xa,Il]}],"outline-color":[{outline:q()}],shadow:[{shadow:["","none",O,uu,iu]}],"shadow-color":[{shadow:q()}],"inset-shadow":[{"inset-shadow":["none",Z,uu,iu]}],"inset-shadow-color":[{"inset-shadow":q()}],"ring-w":[{ring:ee()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:q()}],"ring-offset-w":[{"ring-offset":[_e,Il]}],"ring-offset-color":[{"ring-offset":q()}],"inset-ring-w":[{"inset-ring":ee()}],"inset-ring-color":[{"inset-ring":q()}],"text-shadow":[{"text-shadow":["none",$,uu,iu]}],"text-shadow-color":[{"text-shadow":q()}],opacity:[{opacity:[_e,ae,ne]}],"mix-blend":[{"mix-blend":[...re(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":re()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[_e]}],"mask-image-linear-from-pos":[{"mask-linear-from":De()}],"mask-image-linear-to-pos":[{"mask-linear-to":De()}],"mask-image-linear-from-color":[{"mask-linear-from":q()}],"mask-image-linear-to-color":[{"mask-linear-to":q()}],"mask-image-t-from-pos":[{"mask-t-from":De()}],"mask-image-t-to-pos":[{"mask-t-to":De()}],"mask-image-t-from-color":[{"mask-t-from":q()}],"mask-image-t-to-color":[{"mask-t-to":q()}],"mask-image-r-from-pos":[{"mask-r-from":De()}],"mask-image-r-to-pos":[{"mask-r-to":De()}],"mask-image-r-from-color":[{"mask-r-from":q()}],"mask-image-r-to-color":[{"mask-r-to":q()}],"mask-image-b-from-pos":[{"mask-b-from":De()}],"mask-image-b-to-pos":[{"mask-b-to":De()}],"mask-image-b-from-color":[{"mask-b-from":q()}],"mask-image-b-to-color":[{"mask-b-to":q()}],"mask-image-l-from-pos":[{"mask-l-from":De()}],"mask-image-l-to-pos":[{"mask-l-to":De()}],"mask-image-l-from-color":[{"mask-l-from":q()}],"mask-image-l-to-color":[{"mask-l-to":q()}],"mask-image-x-from-pos":[{"mask-x-from":De()}],"mask-image-x-to-pos":[{"mask-x-to":De()}],"mask-image-x-from-color":[{"mask-x-from":q()}],"mask-image-x-to-color":[{"mask-x-to":q()}],"mask-image-y-from-pos":[{"mask-y-from":De()}],"mask-image-y-to-pos":[{"mask-y-to":De()}],"mask-image-y-from-color":[{"mask-y-from":q()}],"mask-image-y-to-color":[{"mask-y-to":q()}],"mask-image-radial":[{"mask-radial":[ae,ne]}],"mask-image-radial-from-pos":[{"mask-radial-from":De()}],"mask-image-radial-to-pos":[{"mask-radial-to":De()}],"mask-image-radial-from-color":[{"mask-radial-from":q()}],"mask-image-radial-to-color":[{"mask-radial-to":q()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":de()}],"mask-image-conic-pos":[{"mask-conic":[_e]}],"mask-image-conic-from-pos":[{"mask-conic-from":De()}],"mask-image-conic-to-pos":[{"mask-conic-to":De()}],"mask-image-conic-from-color":[{"mask-conic-from":q()}],"mask-image-conic-to-color":[{"mask-conic-to":q()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:Ce()}],"mask-repeat":[{mask:f()}],"mask-size":[{mask:R()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",ae,ne]}],filter:[{filter:["","none",ae,ne]}],blur:[{blur:Ne()}],brightness:[{brightness:[_e,ae,ne]}],contrast:[{contrast:[_e,ae,ne]}],"drop-shadow":[{"drop-shadow":["","none",ue,uu,iu]}],"drop-shadow-color":[{"drop-shadow":q()}],grayscale:[{grayscale:["",_e,ae,ne]}],"hue-rotate":[{"hue-rotate":[_e,ae,ne]}],invert:[{invert:["",_e,ae,ne]}],saturate:[{saturate:[_e,ae,ne]}],sepia:[{sepia:["",_e,ae,ne]}],"backdrop-filter":[{"backdrop-filter":["","none",ae,ne]}],"backdrop-blur":[{"backdrop-blur":Ne()}],"backdrop-brightness":[{"backdrop-brightness":[_e,ae,ne]}],"backdrop-contrast":[{"backdrop-contrast":[_e,ae,ne]}],"backdrop-grayscale":[{"backdrop-grayscale":["",_e,ae,ne]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[_e,ae,ne]}],"backdrop-invert":[{"backdrop-invert":["",_e,ae,ne]}],"backdrop-opacity":[{"backdrop-opacity":[_e,ae,ne]}],"backdrop-saturate":[{"backdrop-saturate":[_e,ae,ne]}],"backdrop-sepia":[{"backdrop-sepia":["",_e,ae,ne]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":Y()}],"border-spacing-x":[{"border-spacing-x":Y()}],"border-spacing-y":[{"border-spacing-y":Y()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",ae,ne]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[_e,"initial",ae,ne]}],ease:[{ease:["linear","initial",K,ae,ne]}],delay:[{delay:[_e,ae,ne]}],animate:[{animate:["none",te,ae,ne]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[oe,ae,ne]}],"perspective-origin":[{"perspective-origin":C()}],rotate:[{rotate:st()}],"rotate-x":[{"rotate-x":st()}],"rotate-y":[{"rotate-y":st()}],"rotate-z":[{"rotate-z":st()}],scale:[{scale:zt()}],"scale-x":[{"scale-x":zt()}],"scale-y":[{"scale-y":zt()}],"scale-z":[{"scale-z":zt()}],"scale-3d":["scale-3d"],skew:[{skew:M()}],"skew-x":[{"skew-x":M()}],"skew-y":[{"skew-y":M()}],transform:[{transform:[ae,ne,"","none","gpu","cpu"]}],"transform-origin":[{origin:C()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:g()}],"translate-x":[{"translate-x":g()}],"translate-y":[{"translate-y":g()}],"translate-z":[{"translate-z":g()}],"translate-none":["translate-none"],accent:[{accent:q()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:q()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",ae,ne]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scroll-m":[{"scroll-m":Y()}],"scroll-mx":[{"scroll-mx":Y()}],"scroll-my":[{"scroll-my":Y()}],"scroll-ms":[{"scroll-ms":Y()}],"scroll-me":[{"scroll-me":Y()}],"scroll-mt":[{"scroll-mt":Y()}],"scroll-mr":[{"scroll-mr":Y()}],"scroll-mb":[{"scroll-mb":Y()}],"scroll-ml":[{"scroll-ml":Y()}],"scroll-p":[{"scroll-p":Y()}],"scroll-px":[{"scroll-px":Y()}],"scroll-py":[{"scroll-py":Y()}],"scroll-ps":[{"scroll-ps":Y()}],"scroll-pe":[{"scroll-pe":Y()}],"scroll-pt":[{"scroll-pt":Y()}],"scroll-pr":[{"scroll-pr":Y()}],"scroll-pb":[{"scroll-pb":Y()}],"scroll-pl":[{"scroll-pl":Y()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",ae,ne]}],fill:[{fill:["none",...q()]}],"stroke-w":[{stroke:[_e,Xa,Il,Oo]}],stroke:[{stroke:["none",...q()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","start","end","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","ms","me","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-r","border-w-l"],"border-w-y":["border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-r","border-color-l"],"border-color-y":["border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-mr","scroll-ml"],"scroll-my":["scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-pr","scroll-pl"],"scroll-py":["scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}},zp=cp(Mp),Kd=(...o)=>zp(Jy(o));var Mo,Zd;function Dp(){if(Zd)return Mo;Zd=1;var o=function(te){return m(te)&&!h(te)};function m(K){return!!K&&typeof K=="object"}function h(K){var te=Object.prototype.toString.call(K);return te==="[object RegExp]"||te==="[object Date]"||z(K)}var c=typeof Symbol=="function"&&Symbol.for,A=c?Symbol.for("react.element"):60103;function z(K){return K.$$typeof===A}function D(K){return Array.isArray(K)?[]:{}}function I(K,te){return te.clone!==!1&&te.isMergeableObject(K)?oe(D(K),K,te):K}function H(K,te,V){return K.concat(te).map(function(de){return I(de,V)})}function b(K,te){if(!te.customMerge)return oe;var V=te.customMerge(K);return typeof V=="function"?V:oe}function O(K){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(K).filter(function(te){return Object.propertyIsEnumerable.call(K,te)}):[]}function Z(K){return Object.keys(K).concat(O(K))}function $(K,te){try{return te in K}catch{return!1}}function ue(K,te){return $(K,te)&&!(Object.hasOwnProperty.call(K,te)&&Object.propertyIsEnumerable.call(K,te))}function ie(K,te,V){var de={};return V.isMergeableObject(K)&&Z(K).forEach(function(C){de[C]=I(K[C],V)}),Z(te).forEach(function(C){ue(K,C)||($(K,C)&&V.isMergeableObject(te[C])?de[C]=b(C,V)(K[C],te[C],V):de[C]=I(te[C],V))}),de}function oe(K,te,V){V=V||{},V.arrayMerge=V.arrayMerge||H,V.isMergeableObject=V.isMergeableObject||o,V.cloneUnlessOtherwiseSpecified=I;var de=Array.isArray(te),C=Array.isArray(K),F=de===C;return F?de?V.arrayMerge(K,te,V):ie(K,te,V):I(te,V)}oe.all=function(te,V){if(!Array.isArray(te))throw new Error("first argument should be an array");return te.reduce(function(de,C){return oe(de,C,V)},{})};var k=oe;return Mo=k,Mo}Dp();var zo={exports:{}},Qa={},Do={exports:{}},Ro={};/** - * @license React - * scheduler.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var kd;function Rp(){return kd||(kd=1,(function(o){function m(T,X){var q=T.length;T.push(X);e:for(;0>>1,f=T[Ce];if(0>>1;CeA(L,q))eeA(me,L)?(T[Ce]=me,T[ee]=q,Ce=ee):(T[Ce]=L,T[W]=q,Ce=W);else if(eeA(me,q))T[Ce]=me,T[ee]=q,Ce=ee;else break e}}return X}function A(T,X){var q=T.sortIndex-X.sortIndex;return q!==0?q:T.id-X.id}if(o.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var z=performance;o.unstable_now=function(){return z.now()}}else{var D=Date,I=D.now();o.unstable_now=function(){return D.now()-I}}var H=[],b=[],O=1,Z=null,$=3,ue=!1,ie=!1,oe=!1,k=!1,K=typeof setTimeout=="function"?setTimeout:null,te=typeof clearTimeout=="function"?clearTimeout:null,V=typeof setImmediate<"u"?setImmediate:null;function de(T){for(var X=h(b);X!==null;){if(X.callback===null)c(b);else if(X.startTime<=T)c(b),X.sortIndex=X.expirationTime,m(H,X);else break;X=h(b)}}function C(T){if(oe=!1,de(T),!ie)if(h(H)!==null)ie=!0,F||(F=!0,Te());else{var X=h(b);X!==null&&we(C,X.startTime-T)}}var F=!1,J=-1,Y=5,ye=-1;function Ae(){return k?!0:!(o.unstable_now()-yeT&&Ae());){var Ce=Z.callback;if(typeof Ce=="function"){Z.callback=null,$=Z.priorityLevel;var f=Ce(Z.expirationTime<=T);if(T=o.unstable_now(),typeof f=="function"){Z.callback=f,de(T),X=!0;break t}Z===h(H)&&c(H),de(T)}else c(H);Z=h(H)}if(Z!==null)X=!0;else{var R=h(b);R!==null&&we(C,R.startTime-T),X=!1}}break e}finally{Z=null,$=q,ue=!1}X=void 0}}finally{X?Te():F=!1}}}var Te;if(typeof V=="function")Te=function(){V(pe)};else if(typeof MessageChannel<"u"){var ut=new MessageChannel,Ke=ut.port2;ut.port1.onmessage=pe,Te=function(){Ke.postMessage(null)}}else Te=function(){K(pe,0)};function we(T,X){J=K(function(){T(o.unstable_now())},X)}o.unstable_IdlePriority=5,o.unstable_ImmediatePriority=1,o.unstable_LowPriority=4,o.unstable_NormalPriority=3,o.unstable_Profiling=null,o.unstable_UserBlockingPriority=2,o.unstable_cancelCallback=function(T){T.callback=null},o.unstable_forceFrameRate=function(T){0>T||125Ce?(T.sortIndex=q,m(b,T),h(H)===null&&T===h(b)&&(oe?(te(J),J=-1):oe=!0,we(C,q-Ce))):(T.sortIndex=f,m(H,T),ie||ue||(ie=!0,F||(F=!0,Te()))),T},o.unstable_shouldYield=Ae,o.unstable_wrapCallback=function(T){var X=$;return function(){var q=$;$=X;try{return T.apply(this,arguments)}finally{$=q}}}})(Ro)),Ro}var Jd;function Up(){return Jd||(Jd=1,Do.exports=Rp()),Do.exports}var Uo={exports:{}},gt={};/** - * @license React - * react-dom.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var $d;function Np(){if($d)return gt;$d=1;var o=Bo();function m(H){var b="https://react.dev/errors/"+H;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(m){console.error(m)}}return o(),Uo.exports=Np(),Uo.exports}/** - * @license React - * react-dom-client.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Fd;function wp(){if(Fd)return Qa;Fd=1;var o=Up(),m=Bo(),h=jp();function c(e){var t="https://react.dev/errors/"+e;if(1f||(e.current=Ce[f],Ce[f]=null,f--)}function L(e,t){f++,Ce[f]=e.current,e.current=t}var ee=R(null),me=R(null),re=R(null),De=R(null);function Ne(e,t){switch(L(re,t),L(me,e),L(ee,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?ad(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=ad(t),e=id(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}W(ee),L(ee,e)}function st(){W(ee),W(me),W(re)}function zt(e){e.memoizedState!==null&&L(De,e);var t=ee.current,l=id(t,e.type);t!==l&&(L(me,e),L(ee,l))}function M(e){me.current===e&&(W(ee),W(me)),De.current===e&&(W(De),Ga._currentValue=q)}var g=Object.prototype.hasOwnProperty,p=o.unstable_scheduleCallback,B=o.unstable_cancelCallback,N=o.unstable_shouldYield,j=o.unstable_requestPaint,P=o.unstable_now,ce=o.unstable_getCurrentPriorityLevel,Se=o.unstable_ImmediatePriority,ve=o.unstable_UserBlockingPriority,xe=o.unstable_NormalPriority,Ye=o.unstable_LowPriority,cl=o.unstable_IdlePriority,gg=o.log,mg=o.unstable_setDisableYieldValue,Vn=null,St=null;function rl(e){if(typeof gg=="function"&&mg(e),St&&typeof St.setStrictMode=="function")try{St.setStrictMode(Vn,e)}catch{}}var xt=Math.clz32?Math.clz32:pg,hg=Math.log,yg=Math.LN2;function pg(e){return e>>>=0,e===0?32:31-(hg(e)/yg|0)|0}var Va=256,Ka=4194304;function wl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194048;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Za(e,t,l){var n=e.pendingLanes;if(n===0)return 0;var a=0,i=e.suspendedLanes,u=e.pingedLanes;e=e.warmLanes;var s=n&134217727;return s!==0?(n=s&~i,n!==0?a=wl(n):(u&=s,u!==0?a=wl(u):l||(l=s&~e,l!==0&&(a=wl(l))))):(s=n&~i,s!==0?a=wl(s):u!==0?a=wl(u):l||(l=n&~e,l!==0&&(a=wl(l)))),a===0?0:t!==0&&t!==a&&(t&i)===0&&(i=a&-a,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:a}function Kn(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function vg(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function qo(){var e=Va;return Va<<=1,(Va&4194048)===0&&(Va=256),e}function Yo(){var e=Ka;return Ka<<=1,(Ka&62914560)===0&&(Ka=4194304),e}function cu(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function Zn(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function bg(e,t,l,n,a,i){var u=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var s=e.entanglements,r=e.expirationTimes,x=e.hiddenUpdates;for(l=u&~l;0)":-1a||r[n]!==x[a]){var U=` -`+r[n].replace(" at new "," at ");return e.displayName&&U.includes("")&&(U=U.replace("",e.displayName)),U}while(1<=n&&0<=a);break}}}finally{hu=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?un(l):""}function Tg(e){switch(e.tag){case 26:case 27:case 5:return un(e.type);case 16:return un("Lazy");case 13:return un("Suspense");case 19:return un("SuspenseList");case 0:case 15:return yu(e.type,!1);case 11:return yu(e.type.render,!1);case 1:return yu(e.type,!0);case 31:return un("Activity");default:return""}}function Wo(e){try{var t="";do t+=Tg(e),e=e.return;while(e);return t}catch(l){return` -Error generating stack: `+l.message+` -`+l.stack}}function Dt(e){switch(typeof e){case"bigint":case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Fo(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Cg(e){var t=Fo(e)?"checked":"value",l=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),n=""+e[t];if(!e.hasOwnProperty(t)&&typeof l<"u"&&typeof l.get=="function"&&typeof l.set=="function"){var a=l.get,i=l.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(u){n=""+u,i.call(this,u)}}),Object.defineProperty(e,t,{enumerable:l.enumerable}),{getValue:function(){return n},setValue:function(u){n=""+u},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function $a(e){e._valueTracker||(e._valueTracker=Cg(e))}function Io(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var l=t.getValue(),n="";return e&&(n=Fo(e)?e.checked?"true":"false":e.value),e=n,e!==l?(t.setValue(e),!0):!1}function Wa(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Og=/[\n"\\]/g;function Rt(e){return e.replace(Og,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function pu(e,t,l,n,a,i,u,s){e.name="",u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"?e.type=u:e.removeAttribute("type"),t!=null?u==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Dt(t)):e.value!==""+Dt(t)&&(e.value=""+Dt(t)):u!=="submit"&&u!=="reset"||e.removeAttribute("value"),t!=null?vu(e,u,Dt(t)):l!=null?vu(e,u,Dt(l)):n!=null&&e.removeAttribute("value"),a==null&&i!=null&&(e.defaultChecked=!!i),a!=null&&(e.checked=a&&typeof a!="function"&&typeof a!="symbol"),s!=null&&typeof s!="function"&&typeof s!="symbol"&&typeof s!="boolean"?e.name=""+Dt(s):e.removeAttribute("name")}function ec(e,t,l,n,a,i,u,s){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null))return;l=l!=null?""+Dt(l):"",t=t!=null?""+Dt(t):l,s||t===e.value||(e.value=t),e.defaultValue=t}n=n??a,n=typeof n!="function"&&typeof n!="symbol"&&!!n,e.checked=s?e.checked:!!n,e.defaultChecked=!!n,u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"&&(e.name=u)}function vu(e,t,l){t==="number"&&Wa(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function sn(e,t,l,n){if(e=e.options,t){t={};for(var a=0;a"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),_u=!1;if(kt)try{var Wn={};Object.defineProperty(Wn,"passive",{get:function(){_u=!0}}),window.addEventListener("test",Wn,Wn),window.removeEventListener("test",Wn,Wn)}catch{_u=!1}var dl=null,Au=null,Ia=null;function sc(){if(Ia)return Ia;var e,t=Au,l=t.length,n,a="value"in dl?dl.value:dl.textContent,i=a.length;for(e=0;e=ea),gc=" ",mc=!1;function hc(e,t){switch(e){case"keyup":return tm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function yc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var fn=!1;function nm(e,t){switch(e){case"compositionend":return yc(t);case"keypress":return t.which!==32?null:(mc=!0,gc);case"textInput":return e=t.data,e===gc&&mc?null:e;default:return null}}function am(e,t){if(fn)return e==="compositionend"||!zu&&hc(e,t)?(e=sc(),Ia=Au=dl=null,fn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=n}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Ac(l)}}function Cc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Cc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Oc(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=Wa(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=Wa(e.document)}return t}function Uu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var dm=kt&&"documentMode"in document&&11>=document.documentMode,dn=null,Nu=null,aa=null,ju=!1;function Mc(e,t,l){var n=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;ju||dn==null||dn!==Wa(n)||(n=dn,"selectionStart"in n&&Uu(n)?n={start:n.selectionStart,end:n.selectionEnd}:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}),aa&&na(aa,n)||(aa=n,n=Xi(Nu,"onSelect"),0>=u,a-=u,$t=1<<32-xt(t)+a|l<i?i:8;var u=T.T,s={};T.T=s,bs(e,!1,t,l);try{var r=a(),x=T.S;if(x!==null&&x(s,r),r!==null&&typeof r=="object"&&typeof r.then=="function"){var U=xm(r,n);ba(e,t,U,Ot(e))}else ba(e,t,n,Ot(e))}catch(G){ba(e,t,{then:function(){},status:"rejected",reason:G},Ot())}finally{X.p=i,T.T=u}}function Cm(){}function ps(e,t,l,n){if(e.tag!==5)throw Error(c(476));var a=zr(e).queue;Mr(e,a,t,q,l===null?Cm:function(){return Dr(e),l(n)})}function zr(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:q,baseState:q,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:el,lastRenderedState:q},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:el,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Dr(e){var t=zr(e).next.queue;ba(e,t,{},Ot())}function vs(){return dt(Ga)}function Rr(){return Fe().memoizedState}function Ur(){return Fe().memoizedState}function Om(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Ot();e=hl(l);var n=yl(t,e,l);n!==null&&(Mt(n,t,l),ga(n,t,l)),t={cache:ku()},e.payload=t;return}t=t.return}}function Mm(e,t,l){var n=Ot();l={lane:n,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null},_i(e)?jr(t,l):(l=Bu(e,t,l,n),l!==null&&(Mt(l,e,n),wr(l,t,n)))}function Nr(e,t,l){var n=Ot();ba(e,t,l,n)}function ba(e,t,l,n){var a={lane:n,revertLane:0,action:l,hasEagerState:!1,eagerState:null,next:null};if(_i(e))jr(t,a);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var u=t.lastRenderedState,s=i(u,l);if(a.hasEagerState=!0,a.eagerState=s,Et(s,u))return ui(e,t,a,0),Xe===null&&ii(),!1}catch{}finally{}if(l=Bu(e,t,a,n),l!==null)return Mt(l,e,n),wr(l,t,n),!0}return!1}function bs(e,t,l,n){if(n={lane:2,revertLane:Ws(),action:n,hasEagerState:!1,eagerState:null,next:null},_i(e)){if(t)throw Error(c(479))}else t=Bu(e,l,n,2),t!==null&&Mt(t,e,2)}function _i(e){var t=e.alternate;return e===Ee||t!==null&&t===Ee}function jr(e,t){En=pi=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function wr(e,t,l){if((l&4194048)!==0){var n=t.lanes;n&=e.pendingLanes,l|=n,t.lanes=l,Xo(e,l)}}var Ai={readContext:dt,use:bi,useCallback:Je,useContext:Je,useEffect:Je,useImperativeHandle:Je,useLayoutEffect:Je,useInsertionEffect:Je,useMemo:Je,useReducer:Je,useRef:Je,useState:Je,useDebugValue:Je,useDeferredValue:Je,useTransition:Je,useSyncExternalStore:Je,useId:Je,useHostTransitionStatus:Je,useFormState:Je,useActionState:Je,useOptimistic:Je,useMemoCache:Je,useCacheRefresh:Je},Hr={readContext:dt,use:bi,useCallback:function(e,t){return pt().memoizedState=[e,t===void 0?null:t],e},useContext:dt,useEffect:br,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Ei(4194308,4,_r.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Ei(4194308,4,e,t)},useInsertionEffect:function(e,t){Ei(4,2,e,t)},useMemo:function(e,t){var l=pt();t=t===void 0?null:t;var n=e();if(kl){rl(!0);try{e()}finally{rl(!1)}}return l.memoizedState=[n,t],n},useReducer:function(e,t,l){var n=pt();if(l!==void 0){var a=l(t);if(kl){rl(!0);try{l(t)}finally{rl(!1)}}}else a=t;return n.memoizedState=n.baseState=a,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:a},n.queue=e,e=e.dispatch=Mm.bind(null,Ee,e),[n.memoizedState,e]},useRef:function(e){var t=pt();return e={current:e},t.memoizedState=e},useState:function(e){e=gs(e);var t=e.queue,l=Nr.bind(null,Ee,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:hs,useDeferredValue:function(e,t){var l=pt();return ys(l,e,t)},useTransition:function(){var e=gs(!1);return e=Mr.bind(null,Ee,e.queue,!0,!1),pt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var n=Ee,a=pt();if(je){if(l===void 0)throw Error(c(407));l=l()}else{if(l=t(),Xe===null)throw Error(c(349));(Re&124)!==0||nr(n,t,l)}a.memoizedState=l;var i={value:l,getSnapshot:t};return a.queue=i,br(ir.bind(null,n,i,e),[e]),n.flags|=2048,An(9,xi(),ar.bind(null,n,i,l,t),null),l},useId:function(){var e=pt(),t=Xe.identifierPrefix;if(je){var l=Wt,n=$t;l=(n&~(1<<32-xt(n)-1)).toString(32)+l,t="«"+t+"R"+l,l=vi++,0ge?(it=se,se=null):it=se.sibling;var Ue=E(v,se,S[ge],w);if(Ue===null){se===null&&(se=it);break}e&&se&&Ue.alternate===null&&t(v,se),d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue,se=it}if(ge===S.length)return l(v,se),je&&Ll(v,ge),le;if(se===null){for(;gege?(it=se,se=null):it=se.sibling;var Nl=E(v,se,Ue.value,w);if(Nl===null){se===null&&(se=it);break}e&&se&&Nl.alternate===null&&t(v,se),d=i(Nl,d,ge),Oe===null?le=Nl:Oe.sibling=Nl,Oe=Nl,se=it}if(Ue.done)return l(v,se),je&&Ll(v,ge),le;if(se===null){for(;!Ue.done;ge++,Ue=S.next())Ue=G(v,Ue.value,w),Ue!==null&&(d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue);return je&&Ll(v,ge),le}for(se=n(se);!Ue.done;ge++,Ue=S.next())Ue=_(se,v,ge,Ue.value,w),Ue!==null&&(e&&Ue.alternate!==null&&se.delete(Ue.key===null?ge:Ue.key),d=i(Ue,d,ge),Oe===null?le=Ue:Oe.sibling=Ue,Oe=Ue);return e&&se.forEach(function(Dh){return t(v,Dh)}),je&&Ll(v,ge),le}function qe(v,d,S,w){if(typeof S=="object"&&S!==null&&S.type===ie&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case $:e:{for(var le=S.key;d!==null;){if(d.key===le){if(le=S.type,le===ie){if(d.tag===7){l(v,d.sibling),w=a(d,S.props.children),w.return=v,v=w;break e}}else if(d.elementType===le||typeof le=="object"&&le!==null&&le.$$typeof===Y&&Br(le)===d.type){l(v,d.sibling),w=a(d,S.props),xa(w,S),w.return=v,v=w;break e}l(v,d);break}else t(v,d);d=d.sibling}S.type===ie?(w=ql(S.props.children,v.mode,w,S.key),w.return=v,v=w):(w=oi(S.type,S.key,S.props,null,v.mode,w),xa(w,S),w.return=v,v=w)}return u(v);case ue:e:{for(le=S.key;d!==null;){if(d.key===le)if(d.tag===4&&d.stateNode.containerInfo===S.containerInfo&&d.stateNode.implementation===S.implementation){l(v,d.sibling),w=a(d,S.children||[]),w.return=v,v=w;break e}else{l(v,d);break}else t(v,d);d=d.sibling}w=Yu(S,v.mode,w),w.return=v,v=w}return u(v);case Y:return le=S._init,S=le(S._payload),qe(v,d,S,w)}if(we(S))return he(v,d,S,w);if(Te(S)){if(le=Te(S),typeof le!="function")throw Error(c(150));return S=le.call(S),fe(v,d,S,w)}if(typeof S.then=="function")return qe(v,d,Ti(S),w);if(S.$$typeof===V)return qe(v,d,di(v,S),w);Ci(v,S)}return typeof S=="string"&&S!==""||typeof S=="number"||typeof S=="bigint"?(S=""+S,d!==null&&d.tag===6?(l(v,d.sibling),w=a(d,S),w.return=v,v=w):(l(v,d),w=qu(S,v.mode,w),w.return=v,v=w),u(v)):l(v,d)}return function(v,d,S,w){try{Sa=0;var le=qe(v,d,S,w);return Tn=null,le}catch(se){if(se===fa||se===mi)throw se;var Oe=_t(29,se,null,v.mode);return Oe.lanes=w,Oe.return=v,Oe}finally{}}}var Cn=Pr(!0),qr=Pr(!1),Ht=R(null),Xt=null;function vl(e){var t=e.alternate;L(et,et.current&1),L(Ht,e),Xt===null&&(t===null||xn.current!==null||t.memoizedState!==null)&&(Xt=e)}function Yr(e){if(e.tag===22){if(L(et,et.current),L(Ht,e),Xt===null){var t=e.alternate;t!==null&&t.memoizedState!==null&&(Xt=e)}}else bl()}function bl(){L(et,et.current),L(Ht,Ht.current)}function tl(e){W(Ht),Xt===e&&(Xt=null),W(et)}var et=R(0);function Oi(e){for(var t=e;t!==null;){if(t.tag===13){var l=t.memoizedState;if(l!==null&&(l=l.dehydrated,l===null||l.data==="$?"||co(l)))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if((t.flags&128)!==0)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function Ss(e,t,l,n){t=e.memoizedState,l=l(n,t),l=l==null?t:O({},t,l),e.memoizedState=l,e.lanes===0&&(e.updateQueue.baseState=l)}var xs={enqueueSetState:function(e,t,l){e=e._reactInternals;var n=Ot(),a=hl(n);a.payload=t,l!=null&&(a.callback=l),t=yl(e,a,n),t!==null&&(Mt(t,e,n),ga(t,e,n))},enqueueReplaceState:function(e,t,l){e=e._reactInternals;var n=Ot(),a=hl(n);a.tag=1,a.payload=t,l!=null&&(a.callback=l),t=yl(e,a,n),t!==null&&(Mt(t,e,n),ga(t,e,n))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var l=Ot(),n=hl(l);n.tag=2,t!=null&&(n.callback=t),t=yl(e,n,l),t!==null&&(Mt(t,e,l),ga(t,e,l))}};function Lr(e,t,l,n,a,i,u){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(n,i,u):t.prototype&&t.prototype.isPureReactComponent?!na(l,n)||!na(a,i):!0}function Xr(e,t,l,n){e=t.state,typeof t.componentWillReceiveProps=="function"&&t.componentWillReceiveProps(l,n),typeof t.UNSAFE_componentWillReceiveProps=="function"&&t.UNSAFE_componentWillReceiveProps(l,n),t.state!==e&&xs.enqueueReplaceState(t,t.state,null)}function Jl(e,t){var l=t;if("ref"in t){l={};for(var n in t)n!=="ref"&&(l[n]=t[n])}if(e=e.defaultProps){l===t&&(l=O({},l));for(var a in e)l[a]===void 0&&(l[a]=e[a])}return l}var Mi=typeof reportError=="function"?reportError:function(e){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof e=="object"&&e!==null&&typeof e.message=="string"?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",e);return}console.error(e)};function Qr(e){Mi(e)}function Vr(e){console.error(e)}function Kr(e){Mi(e)}function zi(e,t){try{var l=e.onUncaughtError;l(t.value,{componentStack:t.stack})}catch(n){setTimeout(function(){throw n})}}function Zr(e,t,l){try{var n=e.onCaughtError;n(l.value,{componentStack:l.stack,errorBoundary:t.tag===1?t.stateNode:null})}catch(a){setTimeout(function(){throw a})}}function Es(e,t,l){return l=hl(l),l.tag=3,l.payload={element:null},l.callback=function(){zi(e,t)},l}function kr(e){return e=hl(e),e.tag=3,e}function Jr(e,t,l,n){var a=l.type.getDerivedStateFromError;if(typeof a=="function"){var i=n.value;e.payload=function(){return a(i)},e.callback=function(){Zr(t,l,n)}}var u=l.stateNode;u!==null&&typeof u.componentDidCatch=="function"&&(e.callback=function(){Zr(t,l,n),typeof a!="function"&&(Tl===null?Tl=new Set([this]):Tl.add(this));var s=n.stack;this.componentDidCatch(n.value,{componentStack:s!==null?s:""})})}function Dm(e,t,l,n,a){if(l.flags|=32768,n!==null&&typeof n=="object"&&typeof n.then=="function"){if(t=l.alternate,t!==null&&oa(t,l,a,!0),l=Ht.current,l!==null){switch(l.tag){case 13:return Xt===null?Ks():l.alternate===null&&ke===0&&(ke=3),l.flags&=-257,l.flags|=65536,l.lanes=a,n===Wu?l.flags|=16384:(t=l.updateQueue,t===null?l.updateQueue=new Set([n]):t.add(n),ks(e,n,a)),!1;case 22:return l.flags|=65536,n===Wu?l.flags|=16384:(t=l.updateQueue,t===null?(t={transitions:null,markerInstances:null,retryQueue:new Set([n])},l.updateQueue=t):(l=t.retryQueue,l===null?t.retryQueue=new Set([n]):l.add(n)),ks(e,n,a)),!1}throw Error(c(435,l.tag))}return ks(e,n,a),Ks(),!1}if(je)return t=Ht.current,t!==null?((t.flags&65536)===0&&(t.flags|=256),t.flags|=65536,t.lanes=a,n!==Qu&&(e=Error(c(422),{cause:n}),sa(Ut(e,l)))):(n!==Qu&&(t=Error(c(423),{cause:n}),sa(Ut(t,l))),e=e.current.alternate,e.flags|=65536,a&=-a,e.lanes|=a,n=Ut(n,l),a=Es(e.stateNode,n,a),es(e,a),ke!==4&&(ke=2)),!1;var i=Error(c(520),{cause:n});if(i=Ut(i,l),Ma===null?Ma=[i]:Ma.push(i),ke!==4&&(ke=2),t===null)return!0;n=Ut(n,l),l=t;do{switch(l.tag){case 3:return l.flags|=65536,e=a&-a,l.lanes|=e,e=Es(l.stateNode,n,e),es(l,e),!1;case 1:if(t=l.type,i=l.stateNode,(l.flags&128)===0&&(typeof t.getDerivedStateFromError=="function"||i!==null&&typeof i.componentDidCatch=="function"&&(Tl===null||!Tl.has(i))))return l.flags|=65536,a&=-a,l.lanes|=a,a=kr(a),Jr(a,e,l,n),es(l,a),!1}l=l.return}while(l!==null);return!1}var $r=Error(c(461)),nt=!1;function ot(e,t,l,n){t.child=e===null?qr(t,null,l,n):Cn(t,e.child,l,n)}function Wr(e,t,l,n,a){l=l.render;var i=t.ref;if("ref"in n){var u={};for(var s in n)s!=="ref"&&(u[s]=n[s])}else u=n;return Kl(t),n=is(e,t,l,u,i,a),s=us(),e!==null&&!nt?(ss(e,t,a),ll(e,t,a)):(je&&s&&Lu(t),t.flags|=1,ot(e,t,n,a),t.child)}function Fr(e,t,l,n,a){if(e===null){var i=l.type;return typeof i=="function"&&!Pu(i)&&i.defaultProps===void 0&&l.compare===null?(t.tag=15,t.type=i,Ir(e,t,i,n,a)):(e=oi(l.type,null,n,t,t.mode,a),e.ref=t.ref,e.return=t,t.child=e)}if(i=e.child,!Ds(e,a)){var u=i.memoizedProps;if(l=l.compare,l=l!==null?l:na,l(u,n)&&e.ref===t.ref)return ll(e,t,a)}return t.flags|=1,e=Jt(i,n),e.ref=t.ref,e.return=t,t.child=e}function Ir(e,t,l,n,a){if(e!==null){var i=e.memoizedProps;if(na(i,n)&&e.ref===t.ref)if(nt=!1,t.pendingProps=n=i,Ds(e,a))(e.flags&131072)!==0&&(nt=!0);else return t.lanes=e.lanes,ll(e,t,a)}return _s(e,t,l,n,a)}function ef(e,t,l){var n=t.pendingProps,a=n.children,i=e!==null?e.memoizedState:null;if(n.mode==="hidden"){if((t.flags&128)!==0){if(n=i!==null?i.baseLanes|l:l,e!==null){for(a=t.child=e.child,i=0;a!==null;)i=i|a.lanes|a.childLanes,a=a.sibling;t.childLanes=i&~n}else t.childLanes=0,t.child=null;return tf(e,t,n,l)}if((l&536870912)!==0)t.memoizedState={baseLanes:0,cachePool:null},e!==null&&gi(t,i!==null?i.cachePool:null),i!==null?Ic(t,i):ls(),Yr(t);else return t.lanes=t.childLanes=536870912,tf(e,t,i!==null?i.baseLanes|l:l,l)}else i!==null?(gi(t,i.cachePool),Ic(t,i),bl(),t.memoizedState=null):(e!==null&&gi(t,null),ls(),bl());return ot(e,t,a,l),t.child}function tf(e,t,l,n){var a=$u();return a=a===null?null:{parent:Ie._currentValue,pool:a},t.memoizedState={baseLanes:l,cachePool:a},e!==null&&gi(t,null),ls(),Yr(t),e!==null&&oa(e,t,n,!0),null}function Di(e,t){var l=t.ref;if(l===null)e!==null&&e.ref!==null&&(t.flags|=4194816);else{if(typeof l!="function"&&typeof l!="object")throw Error(c(284));(e===null||e.ref!==l)&&(t.flags|=4194816)}}function _s(e,t,l,n,a){return Kl(t),l=is(e,t,l,n,void 0,a),n=us(),e!==null&&!nt?(ss(e,t,a),ll(e,t,a)):(je&&n&&Lu(t),t.flags|=1,ot(e,t,l,a),t.child)}function lf(e,t,l,n,a,i){return Kl(t),t.updateQueue=null,l=tr(t,n,l,a),er(e),n=us(),e!==null&&!nt?(ss(e,t,i),ll(e,t,i)):(je&&n&&Lu(t),t.flags|=1,ot(e,t,l,i),t.child)}function nf(e,t,l,n,a){if(Kl(t),t.stateNode===null){var i=yn,u=l.contextType;typeof u=="object"&&u!==null&&(i=dt(u)),i=new l(n,i),t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,i.updater=xs,t.stateNode=i,i._reactInternals=t,i=t.stateNode,i.props=n,i.state=t.memoizedState,i.refs={},Fu(t),u=l.contextType,i.context=typeof u=="object"&&u!==null?dt(u):yn,i.state=t.memoizedState,u=l.getDerivedStateFromProps,typeof u=="function"&&(Ss(t,l,u,n),i.state=t.memoizedState),typeof l.getDerivedStateFromProps=="function"||typeof i.getSnapshotBeforeUpdate=="function"||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(u=i.state,typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount(),u!==i.state&&xs.enqueueReplaceState(i,i.state,null),ha(t,n,i,a),ma(),i.state=t.memoizedState),typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!0}else if(e===null){i=t.stateNode;var s=t.memoizedProps,r=Jl(l,s);i.props=r;var x=i.context,U=l.contextType;u=yn,typeof U=="object"&&U!==null&&(u=dt(U));var G=l.getDerivedStateFromProps;U=typeof G=="function"||typeof i.getSnapshotBeforeUpdate=="function",s=t.pendingProps!==s,U||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(s||x!==u)&&Xr(t,i,n,u),ml=!1;var E=t.memoizedState;i.state=E,ha(t,n,i,a),ma(),x=t.memoizedState,s||E!==x||ml?(typeof G=="function"&&(Ss(t,l,G,n),x=t.memoizedState),(r=ml||Lr(t,l,r,n,E,x,u))?(U||typeof i.UNSAFE_componentWillMount!="function"&&typeof i.componentWillMount!="function"||(typeof i.componentWillMount=="function"&&i.componentWillMount(),typeof i.UNSAFE_componentWillMount=="function"&&i.UNSAFE_componentWillMount()),typeof i.componentDidMount=="function"&&(t.flags|=4194308)):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),t.memoizedProps=n,t.memoizedState=x),i.props=n,i.state=x,i.context=u,n=r):(typeof i.componentDidMount=="function"&&(t.flags|=4194308),n=!1)}else{i=t.stateNode,Iu(e,t),u=t.memoizedProps,U=Jl(l,u),i.props=U,G=t.pendingProps,E=i.context,x=l.contextType,r=yn,typeof x=="object"&&x!==null&&(r=dt(x)),s=l.getDerivedStateFromProps,(x=typeof s=="function"||typeof i.getSnapshotBeforeUpdate=="function")||typeof i.UNSAFE_componentWillReceiveProps!="function"&&typeof i.componentWillReceiveProps!="function"||(u!==G||E!==r)&&Xr(t,i,n,r),ml=!1,E=t.memoizedState,i.state=E,ha(t,n,i,a),ma();var _=t.memoizedState;u!==G||E!==_||ml||e!==null&&e.dependencies!==null&&fi(e.dependencies)?(typeof s=="function"&&(Ss(t,l,s,n),_=t.memoizedState),(U=ml||Lr(t,l,U,n,E,_,r)||e!==null&&e.dependencies!==null&&fi(e.dependencies))?(x||typeof i.UNSAFE_componentWillUpdate!="function"&&typeof i.componentWillUpdate!="function"||(typeof i.componentWillUpdate=="function"&&i.componentWillUpdate(n,_,r),typeof i.UNSAFE_componentWillUpdate=="function"&&i.UNSAFE_componentWillUpdate(n,_,r)),typeof i.componentDidUpdate=="function"&&(t.flags|=4),typeof i.getSnapshotBeforeUpdate=="function"&&(t.flags|=1024)):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=1024),t.memoizedProps=n,t.memoizedState=_),i.props=n,i.state=_,i.context=r,n=U):(typeof i.componentDidUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=4),typeof i.getSnapshotBeforeUpdate!="function"||u===e.memoizedProps&&E===e.memoizedState||(t.flags|=1024),n=!1)}return i=n,Di(e,t),n=(t.flags&128)!==0,i||n?(i=t.stateNode,l=n&&typeof l.getDerivedStateFromError!="function"?null:i.render(),t.flags|=1,e!==null&&n?(t.child=Cn(t,e.child,null,a),t.child=Cn(t,null,l,a)):ot(e,t,l,a),t.memoizedState=i.state,e=t.child):e=ll(e,t,a),e}function af(e,t,l,n){return ua(),t.flags|=256,ot(e,t,l,n),t.child}var As={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Ts(e){return{baseLanes:e,cachePool:Vc()}}function Cs(e,t,l){return e=e!==null?e.childLanes&~l:0,t&&(e|=Gt),e}function uf(e,t,l){var n=t.pendingProps,a=!1,i=(t.flags&128)!==0,u;if((u=i)||(u=e!==null&&e.memoizedState===null?!1:(et.current&2)!==0),u&&(a=!0,t.flags&=-129),u=(t.flags&32)!==0,t.flags&=-33,e===null){if(je){if(a?vl(t):bl(),je){var s=Ze,r;if(r=s){e:{for(r=s,s=Lt;r.nodeType!==8;){if(!s){s=null;break e}if(r=Yt(r.nextSibling),r===null){s=null;break e}}s=r}s!==null?(t.memoizedState={dehydrated:s,treeContext:Yl!==null?{id:$t,overflow:Wt}:null,retryLane:536870912,hydrationErrors:null},r=_t(18,null,null,0),r.stateNode=s,r.return=t,t.child=r,mt=t,Ze=null,r=!0):r=!1}r||Ql(t)}if(s=t.memoizedState,s!==null&&(s=s.dehydrated,s!==null))return co(s)?t.lanes=32:t.lanes=536870912,null;tl(t)}return s=n.children,n=n.fallback,a?(bl(),a=t.mode,s=Ri({mode:"hidden",children:s},a),n=ql(n,a,l,null),s.return=t,n.return=t,s.sibling=n,t.child=s,a=t.child,a.memoizedState=Ts(l),a.childLanes=Cs(e,u,l),t.memoizedState=As,n):(vl(t),Os(t,s))}if(r=e.memoizedState,r!==null&&(s=r.dehydrated,s!==null)){if(i)t.flags&256?(vl(t),t.flags&=-257,t=Ms(e,t,l)):t.memoizedState!==null?(bl(),t.child=e.child,t.flags|=128,t=null):(bl(),a=n.fallback,s=t.mode,n=Ri({mode:"visible",children:n.children},s),a=ql(a,s,l,null),a.flags|=2,n.return=t,a.return=t,n.sibling=a,t.child=n,Cn(t,e.child,null,l),n=t.child,n.memoizedState=Ts(l),n.childLanes=Cs(e,u,l),t.memoizedState=As,t=a);else if(vl(t),co(s)){if(u=s.nextSibling&&s.nextSibling.dataset,u)var x=u.dgst;u=x,n=Error(c(419)),n.stack="",n.digest=u,sa({value:n,source:null,stack:null}),t=Ms(e,t,l)}else if(nt||oa(e,t,l,!1),u=(l&e.childLanes)!==0,nt||u){if(u=Xe,u!==null&&(n=l&-l,n=(n&42)!==0?1:ru(n),n=(n&(u.suspendedLanes|l))!==0?0:n,n!==0&&n!==r.retryLane))throw r.retryLane=n,hn(e,n),Mt(u,e,n),$r;s.data==="$?"||Ks(),t=Ms(e,t,l)}else s.data==="$?"?(t.flags|=192,t.child=e.child,t=null):(e=r.treeContext,Ze=Yt(s.nextSibling),mt=t,je=!0,Xl=null,Lt=!1,e!==null&&(jt[wt++]=$t,jt[wt++]=Wt,jt[wt++]=Yl,$t=e.id,Wt=e.overflow,Yl=t),t=Os(t,n.children),t.flags|=4096);return t}return a?(bl(),a=n.fallback,s=t.mode,r=e.child,x=r.sibling,n=Jt(r,{mode:"hidden",children:n.children}),n.subtreeFlags=r.subtreeFlags&65011712,x!==null?a=Jt(x,a):(a=ql(a,s,l,null),a.flags|=2),a.return=t,n.return=t,n.sibling=a,t.child=n,n=a,a=t.child,s=e.child.memoizedState,s===null?s=Ts(l):(r=s.cachePool,r!==null?(x=Ie._currentValue,r=r.parent!==x?{parent:x,pool:x}:r):r=Vc(),s={baseLanes:s.baseLanes|l,cachePool:r}),a.memoizedState=s,a.childLanes=Cs(e,u,l),t.memoizedState=As,n):(vl(t),l=e.child,e=l.sibling,l=Jt(l,{mode:"visible",children:n.children}),l.return=t,l.sibling=null,e!==null&&(u=t.deletions,u===null?(t.deletions=[e],t.flags|=16):u.push(e)),t.child=l,t.memoizedState=null,l)}function Os(e,t){return t=Ri({mode:"visible",children:t},e.mode),t.return=e,e.child=t}function Ri(e,t){return e=_t(22,e,null,t),e.lanes=0,e.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null},e}function Ms(e,t,l){return Cn(t,e.child,null,l),e=Os(t,t.pendingProps.children),e.flags|=2,t.memoizedState=null,e}function sf(e,t,l){e.lanes|=t;var n=e.alternate;n!==null&&(n.lanes|=t),Ku(e.return,t,l)}function zs(e,t,l,n,a){var i=e.memoizedState;i===null?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:n,tail:l,tailMode:a}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=n,i.tail=l,i.tailMode=a)}function of(e,t,l){var n=t.pendingProps,a=n.revealOrder,i=n.tail;if(ot(e,t,n.children,l),n=et.current,(n&2)!==0)n=n&1|2,t.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=t.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&sf(e,l,t);else if(e.tag===19)sf(e,l,t);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;e.sibling===null;){if(e.return===null||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}n&=1}switch(L(et,n),a){case"forwards":for(l=t.child,a=null;l!==null;)e=l.alternate,e!==null&&Oi(e)===null&&(a=l),l=l.sibling;l=a,l===null?(a=t.child,t.child=null):(a=l.sibling,l.sibling=null),zs(t,!1,a,l,i);break;case"backwards":for(l=null,a=t.child,t.child=null;a!==null;){if(e=a.alternate,e!==null&&Oi(e)===null){t.child=a;break}e=a.sibling,a.sibling=l,l=a,a=e}zs(t,!0,l,null,i);break;case"together":zs(t,!1,null,null,void 0);break;default:t.memoizedState=null}return t.child}function ll(e,t,l){if(e!==null&&(t.dependencies=e.dependencies),Al|=t.lanes,(l&t.childLanes)===0)if(e!==null){if(oa(e,t,l,!1),(l&t.childLanes)===0)return null}else return null;if(e!==null&&t.child!==e.child)throw Error(c(153));if(t.child!==null){for(e=t.child,l=Jt(e,e.pendingProps),t.child=l,l.return=t;e.sibling!==null;)e=e.sibling,l=l.sibling=Jt(e,e.pendingProps),l.return=t;l.sibling=null}return t.child}function Ds(e,t){return(e.lanes&t)!==0?!0:(e=e.dependencies,!!(e!==null&&fi(e)))}function Rm(e,t,l){switch(t.tag){case 3:Ne(t,t.stateNode.containerInfo),gl(t,Ie,e.memoizedState.cache),ua();break;case 27:case 5:zt(t);break;case 4:Ne(t,t.stateNode.containerInfo);break;case 10:gl(t,t.type,t.memoizedProps.value);break;case 13:var n=t.memoizedState;if(n!==null)return n.dehydrated!==null?(vl(t),t.flags|=128,null):(l&t.child.childLanes)!==0?uf(e,t,l):(vl(t),e=ll(e,t,l),e!==null?e.sibling:null);vl(t);break;case 19:var a=(e.flags&128)!==0;if(n=(l&t.childLanes)!==0,n||(oa(e,t,l,!1),n=(l&t.childLanes)!==0),a){if(n)return of(e,t,l);t.flags|=128}if(a=t.memoizedState,a!==null&&(a.rendering=null,a.tail=null,a.lastEffect=null),L(et,et.current),n)break;return null;case 22:case 23:return t.lanes=0,ef(e,t,l);case 24:gl(t,Ie,e.memoizedState.cache)}return ll(e,t,l)}function cf(e,t,l){if(e!==null)if(e.memoizedProps!==t.pendingProps)nt=!0;else{if(!Ds(e,l)&&(t.flags&128)===0)return nt=!1,Rm(e,t,l);nt=(e.flags&131072)!==0}else nt=!1,je&&(t.flags&1048576)!==0&&Bc(t,ri,t.index);switch(t.lanes=0,t.tag){case 16:e:{e=t.pendingProps;var n=t.elementType,a=n._init;if(n=a(n._payload),t.type=n,typeof n=="function")Pu(n)?(e=Jl(n,e),t.tag=1,t=nf(null,t,n,e,l)):(t.tag=0,t=_s(null,t,n,e,l));else{if(n!=null){if(a=n.$$typeof,a===de){t.tag=11,t=Wr(null,t,n,e,l);break e}else if(a===J){t.tag=14,t=Fr(null,t,n,e,l);break e}}throw t=Ke(n)||n,Error(c(306,t,""))}}return t;case 0:return _s(e,t,t.type,t.pendingProps,l);case 1:return n=t.type,a=Jl(n,t.pendingProps),nf(e,t,n,a,l);case 3:e:{if(Ne(t,t.stateNode.containerInfo),e===null)throw Error(c(387));n=t.pendingProps;var i=t.memoizedState;a=i.element,Iu(e,t),ha(t,n,null,l);var u=t.memoizedState;if(n=u.cache,gl(t,Ie,n),n!==i.cache&&Zu(t,[Ie],l,!0),ma(),n=u.element,i.isDehydrated)if(i={element:n,isDehydrated:!1,cache:u.cache},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){t=af(e,t,n,l);break e}else if(n!==a){a=Ut(Error(c(424)),t),sa(a),t=af(e,t,n,l);break e}else{switch(e=t.stateNode.containerInfo,e.nodeType){case 9:e=e.body;break;default:e=e.nodeName==="HTML"?e.ownerDocument.body:e}for(Ze=Yt(e.firstChild),mt=t,je=!0,Xl=null,Lt=!0,l=qr(t,null,n,l),t.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling}else{if(ua(),n===a){t=ll(e,t,l);break e}ot(e,t,n,l)}t=t.child}return t;case 26:return Di(e,t),e===null?(l=gd(t.type,null,t.pendingProps,null))?t.memoizedState=l:je||(l=t.type,e=t.pendingProps,n=Vi(re.current).createElement(l),n[ft]=t,n[ht]=e,rt(n,l,e),lt(n),t.stateNode=n):t.memoizedState=gd(t.type,e.memoizedProps,t.pendingProps,e.memoizedState),null;case 27:return zt(t),e===null&&je&&(n=t.stateNode=rd(t.type,t.pendingProps,re.current),mt=t,Lt=!0,a=Ze,Ml(t.type)?(ro=a,Ze=Yt(n.firstChild)):Ze=a),ot(e,t,t.pendingProps.children,l),Di(e,t),e===null&&(t.flags|=4194304),t.child;case 5:return e===null&&je&&((a=n=Ze)&&(n=ih(n,t.type,t.pendingProps,Lt),n!==null?(t.stateNode=n,mt=t,Ze=Yt(n.firstChild),Lt=!1,a=!0):a=!1),a||Ql(t)),zt(t),a=t.type,i=t.pendingProps,u=e!==null?e.memoizedProps:null,n=i.children,uo(a,i)?n=null:u!==null&&uo(a,u)&&(t.flags|=32),t.memoizedState!==null&&(a=is(e,t,_m,null,null,l),Ga._currentValue=a),Di(e,t),ot(e,t,n,l),t.child;case 6:return e===null&&je&&((e=l=Ze)&&(l=uh(l,t.pendingProps,Lt),l!==null?(t.stateNode=l,mt=t,Ze=null,e=!0):e=!1),e||Ql(t)),null;case 13:return uf(e,t,l);case 4:return Ne(t,t.stateNode.containerInfo),n=t.pendingProps,e===null?t.child=Cn(t,null,n,l):ot(e,t,n,l),t.child;case 11:return Wr(e,t,t.type,t.pendingProps,l);case 7:return ot(e,t,t.pendingProps,l),t.child;case 8:return ot(e,t,t.pendingProps.children,l),t.child;case 12:return ot(e,t,t.pendingProps.children,l),t.child;case 10:return n=t.pendingProps,gl(t,t.type,n.value),ot(e,t,n.children,l),t.child;case 9:return a=t.type._context,n=t.pendingProps.children,Kl(t),a=dt(a),n=n(a),t.flags|=1,ot(e,t,n,l),t.child;case 14:return Fr(e,t,t.type,t.pendingProps,l);case 15:return Ir(e,t,t.type,t.pendingProps,l);case 19:return of(e,t,l);case 31:return n=t.pendingProps,l=t.mode,n={mode:n.mode,children:n.children},e===null?(l=Ri(n,l),l.ref=t.ref,t.child=l,l.return=t,t=l):(l=Jt(e.child,n),l.ref=t.ref,t.child=l,l.return=t,t=l),t;case 22:return ef(e,t,l);case 24:return Kl(t),n=dt(Ie),e===null?(a=$u(),a===null&&(a=Xe,i=ku(),a.pooledCache=i,i.refCount++,i!==null&&(a.pooledCacheLanes|=l),a=i),t.memoizedState={parent:n,cache:a},Fu(t),gl(t,Ie,a)):((e.lanes&l)!==0&&(Iu(e,t),ha(t,null,null,l),ma()),a=e.memoizedState,i=t.memoizedState,a.parent!==n?(a={parent:n,cache:n},t.memoizedState=a,t.lanes===0&&(t.memoizedState=t.updateQueue.baseState=a),gl(t,Ie,n)):(n=i.cache,gl(t,Ie,n),n!==a.cache&&Zu(t,[Ie],l,!0))),ot(e,t,t.pendingProps.children,l),t.child;case 29:throw t.pendingProps}throw Error(c(156,t.tag))}function nl(e){e.flags|=4}function rf(e,t){if(t.type!=="stylesheet"||(t.state.loading&4)!==0)e.flags&=-16777217;else if(e.flags|=16777216,!vd(t)){if(t=Ht.current,t!==null&&((Re&4194048)===Re?Xt!==null:(Re&62914560)!==Re&&(Re&536870912)===0||t!==Xt))throw da=Wu,Kc;e.flags|=8192}}function Ui(e,t){t!==null&&(e.flags|=4),e.flags&16384&&(t=e.tag!==22?Yo():536870912,e.lanes|=t,Dn|=t)}function Ea(e,t){if(!je)switch(e.tailMode){case"hidden":t=e.tail;for(var l=null;t!==null;)t.alternate!==null&&(l=t),t=t.sibling;l===null?e.tail=null:l.sibling=null;break;case"collapsed":l=e.tail;for(var n=null;l!==null;)l.alternate!==null&&(n=l),l=l.sibling;n===null?t||e.tail===null?e.tail=null:e.tail.sibling=null:n.sibling=null}}function Ve(e){var t=e.alternate!==null&&e.alternate.child===e.child,l=0,n=0;if(t)for(var a=e.child;a!==null;)l|=a.lanes|a.childLanes,n|=a.subtreeFlags&65011712,n|=a.flags&65011712,a.return=e,a=a.sibling;else for(a=e.child;a!==null;)l|=a.lanes|a.childLanes,n|=a.subtreeFlags,n|=a.flags,a.return=e,a=a.sibling;return e.subtreeFlags|=n,e.childLanes=l,t}function Um(e,t,l){var n=t.pendingProps;switch(Xu(t),t.tag){case 31:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ve(t),null;case 1:return Ve(t),null;case 3:return l=t.stateNode,n=null,e!==null&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),It(Ie),st(),l.pendingContext&&(l.context=l.pendingContext,l.pendingContext=null),(e===null||e.child===null)&&(ia(t)?nl(t):e===null||e.memoizedState.isDehydrated&&(t.flags&256)===0||(t.flags|=1024,Yc())),Ve(t),null;case 26:return l=t.memoizedState,e===null?(nl(t),l!==null?(Ve(t),rf(t,l)):(Ve(t),t.flags&=-16777217)):l?l!==e.memoizedState?(nl(t),Ve(t),rf(t,l)):(Ve(t),t.flags&=-16777217):(e.memoizedProps!==n&&nl(t),Ve(t),t.flags&=-16777217),null;case 27:M(t),l=re.current;var a=t.type;if(e!==null&&t.stateNode!=null)e.memoizedProps!==n&&nl(t);else{if(!n){if(t.stateNode===null)throw Error(c(166));return Ve(t),null}e=ee.current,ia(t)?Pc(t):(e=rd(a,n,l),t.stateNode=e,nl(t))}return Ve(t),null;case 5:if(M(t),l=t.type,e!==null&&t.stateNode!=null)e.memoizedProps!==n&&nl(t);else{if(!n){if(t.stateNode===null)throw Error(c(166));return Ve(t),null}if(e=ee.current,ia(t))Pc(t);else{switch(a=Vi(re.current),e){case 1:e=a.createElementNS("http://www.w3.org/2000/svg",l);break;case 2:e=a.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;default:switch(l){case"svg":e=a.createElementNS("http://www.w3.org/2000/svg",l);break;case"math":e=a.createElementNS("http://www.w3.org/1998/Math/MathML",l);break;case"script":e=a.createElement("div"),e.innerHTML=" - + + diff --git a/chrome-extension/public/side-panel/logo_vertical.svg b/chrome-extension/public/side-panel/logo_vertical.svg old mode 100644 new mode 100755 diff --git a/chrome-extension/public/side-panel/logo_vertical_dark.svg b/chrome-extension/public/side-panel/logo_vertical_dark.svg old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/README-pyodide-manual-testing.md b/chrome-extension/public/test-scripts/README-pyodide-manual-testing.md old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/README.md b/chrome-extension/public/test-scripts/README.md old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/demo-console-commands.js b/chrome-extension/public/test-scripts/demo-console-commands.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/integration-test-helpers.js b/chrome-extension/public/test-scripts/integration-test-helpers.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/integration-test.html b/chrome-extension/public/test-scripts/integration-test.html old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/integration-test.js b/chrome-extension/public/test-scripts/integration-test.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/ozon-test.js b/chrome-extension/public/test-scripts/ozon-test.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/page-ui.js b/chrome-extension/public/test-scripts/page-ui.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/pyodide-offscreen-manual-test.js b/chrome-extension/public/test-scripts/pyodide-offscreen-manual-test.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/test-scripts/test-loader.js b/chrome-extension/public/test-scripts/test-loader.js old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/beautifulsoup4-4.12.3-py3-none-any.whl b/chrome-extension/public/wheels/beautifulsoup4-4.12.3-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/certifi-2024.12.14-py3-none-any.whl b/chrome-extension/public/wheels/certifi-2024.12.14-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/charset_normalizer-3.3.2-py3-none-any.whl b/chrome-extension/public/wheels/charset_normalizer-3.3.2-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/idna-3.7-py3-none-any.whl b/chrome-extension/public/wheels/idna-3.7-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/pyodide_http-0.2.1-py3-none-any.whl b/chrome-extension/public/wheels/pyodide_http-0.2.1-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/requests-2.31.0-py3-none-any.whl b/chrome-extension/public/wheels/requests-2.31.0-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/soupsieve-2.5-py3-none-any.whl b/chrome-extension/public/wheels/soupsieve-2.5-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/public/wheels/urllib3-2.2.3-py3-none-any.whl b/chrome-extension/public/wheels/urllib3-2.2.3-py3-none-any.whl old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/ai-api-client.ts b/chrome-extension/src/background/ai-api-client.ts old mode 100644 new mode 100755 index 6ab040f7..1b35012c --- a/chrome-extension/src/background/ai-api-client.ts +++ b/chrome-extension/src/background/ai-api-client.ts @@ -26,7 +26,7 @@ export interface AiModelResponse { } // Доступные модели -export type ModelAlias = 'gemini-flash' | 'gemini-pro' | 'gpt-3.5-turbo' | 'gpt-4'; +export type ModelAlias = 'gemini-flash-lite' | 'gemini-pro' | 'gpt-3.5-turbo' | 'gpt-4'; // Конфигурация модели interface ModelConfig { @@ -251,7 +251,7 @@ function handleAiError(error: any, context: any): never { // Поддерживаемые модели и их конфигурации с типизированными индексами const MODEL_CONFIGS: Record = { - 'gemini-flash': { + 'gemini-flash-lite': { provider: 'google', //model_name: 'gemini-2.5-flash-lite:generateContent', model_name: 'gemini-flash-lite-latest:generateContent', @@ -300,6 +300,24 @@ export async function getApiKeyForModel(modelAlias: string): Promise { + if (options.isDefaultLLM) { + // Кастомный плагиновый ключ для конкретной комбинации + const keyId = `${options.pluginId}-${options.promptType}-${options.language}`; + return await APIKeyManager.getDecryptedKey(keyId); + } else { + // Платформенный ключ + return await APIKeyManager.getDecryptedKey(options.modelAlias); + } +} + +// --- Инструкция для разработчиков: +// Для плагиновых промптов типа OzonAnalyzer всегда используйте getApiKeyForPromptOrModel для правильного выбора ключа. +// Никогда не вызывайте getApiKeyForModel напрямую для cases типа 'default LLM'. + /** * Выполняет запрос к AI API в зависимости от провайдера с полным мониторингом */ @@ -506,4 +524,95 @@ export async function setApiKeyForModel(modelAlias: string, apiKey: string): Pro export async function checkApiKeyAvailability(modelAlias: string): Promise { const apiKey = await getApiKeyForModel(modelAlias); return apiKey !== null && apiKey.length > 0; +} + +/** + * Получает выбранную LLM для промпта с учетом типа промпта и языка + * @param prompt_type - тип промпта (basic_analysis, deep_analysis, etc.) + * @param language - язык промпта (ru, en) + * @param pluginId - ID плагина для получения специфичных настроек + * @returns объект с моделью и API-ключом + */ +export async function getLLMForPrompt( + prompt_type: string, + language: string, + pluginId?: string +): Promise<{ model: ModelAlias; apiKey: string; prompt: string }> { + try { + // Формируем ключ для localStorage: pluginId:prompt_type:language или просто prompt_type:language + const storageKey = pluginId ? `${pluginId}:${prompt_type}:${language}` : `${prompt_type}:${language}`; + + // Пытаемся получить выбранную модель из localStorage + let selectedModel = localStorage.getItem(storageKey) as ModelAlias | null; + + // Если модель не найдена в localStorage, используем дефолт из manifest + if (!selectedModel) { + // Для получения дефолтной модели нужно загрузить manifest плагина + const manifestData = await loadPluginManifest(pluginId); + selectedModel = manifestData?.ai_models?.[prompt_type] as ModelAlias; + + if (!selectedModel) { + throw new Error(`Не найдена модель по умолчанию для промпта ${prompt_type}`); + } + } + + // Проверяем, что модель поддерживается + if (!Object.keys(MODEL_CONFIGS).includes(selectedModel)) { + throw new Error(`Неподдерживаемая модель: ${selectedModel}`); + } + + // Получаем API-ключ (глобальный или плагин-специфичный) + const apiKey = await getApiKeyForModel(selectedModel); + if (!apiKey) { + throw new Error(`API-ключ для модели ${selectedModel} не найден`); + } + + // Получаем промпт из manifest с подстановкой $prompt_{type}_{language} + const manifestData = await loadPluginManifest(pluginId); + const promptKey = `$prompt_${prompt_type}_${language}`; + let prompt = manifestData?.options?.prompts?.[prompt_type]?.[language]?.default; + + if (!prompt) { + throw new Error(`Промпт ${promptKey} не найден в manifest`); + } + + return { + model: selectedModel, + apiKey, + prompt + }; + + } catch (error) { + console.error('[AI Client] Error getting LLM for prompt:', error); + throw error; + } +} + +/** + * Загружает manifest плагина + */ +async function loadPluginManifest(pluginId?: string): Promise { + if (!pluginId) { + // Если pluginId не указан, пытаемся найти manifest в chrome-extension/public/plugins/ + // Для упрощения возвращаем дефолтные значения или null + return null; + } + + try { + // Путь к manifest плагина + const manifestPath = `plugins/${pluginId}/manifest.json`; + + // В контексте расширения manifest может быть загружен через chrome.runtime.getURL + const manifestUrl = chrome.runtime.getURL(manifestPath); + const response = await fetch(manifestUrl); + + if (!response.ok) { + throw new Error(`Не удалось загрузить manifest: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error('[AI Client] Error loading plugin manifest:', error); + return null; + } } \ No newline at end of file diff --git a/chrome-extension/src/background/background-worker.ts b/chrome-extension/src/background/background-worker.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/host-api.ts b/chrome-extension/src/background/host-api.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/index.ts b/chrome-extension/src/background/index.ts old mode 100644 new mode 100755 index 53b80863..69a24411 --- a/chrome-extension/src/background/index.ts +++ b/chrome-extension/src/background/index.ts @@ -15,6 +15,7 @@ import { getApiKeyForModel, callAiModel } from './ai-api-client'; import { exampleThemeStorage, pluginSettingsStorage, getPluginSettings } from '@extension/storage'; import type { PluginSettings } from '@extension/storage'; import { ensureOffscreenDocument } from '../../../src/background/offscreen-manager'; +import { APIKeyManager } from '../../../pages/options/src/utils/encryption'; console.log('[background] Storage modules loaded'); // Глобальный счетчик для генерации уникальных messageId @@ -239,7 +240,7 @@ const sendHtmlDirectly = async ( // Получить API ключ для передачи в offscreen let geminiApiKey: string | undefined; try { - geminiApiKey = await getApiKeyForModel('gemini-flash') || undefined; + geminiApiKey = await getApiKeyForModel('gemini-flash-lite') || undefined; console.log('[background][DIRECT_TRANSMISSION] ✅ API key retrieved for workflow'); } catch (keyError) { console.warn('[background][DIRECT_TRANSMISSION] ⚠️ Failed to get API key:', keyError); @@ -850,7 +851,7 @@ async function processRecoveredAssembledTransfer(msg: any, transfer: any): Promi // Получить API ключ для Gemini и добавить к сообщению try { - const geminiApiKey = await getApiKeyForModel('gemini-flash'); + const geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); executeMessage.geminiApiKey = geminiApiKey; console.log('[RECOVERY_PROCESSING] ✅ API key added to EXECUTE_WORKFLOW message'); } catch (keyError) { @@ -1318,12 +1319,23 @@ async function executeWorkflowInOffscreen( console.log(`[WORKFLOW_EXECUTION] HTML data length: ${htmlData?.length || 0}`); console.log(`[WORKFLOW_EXECUTION] Plugin settings:`, pluginSettings); + // [API_KEY_FLOW] MARKER: BACKGROUND_EXECUTE_WORKFLOW_IN_OFFSCREEN_START + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_EXECUTE_WORKFLOW_IN_OFFSCREEN_START'); + console.log('[API_KEY_FLOW] executeWorkflowInOffscreen called with pluginId:', pluginId); + console.log('[API_KEY_FLOW] Plugin settings received:', { + hasApiKeys: !!pluginSettings.api_keys, + hasPrompts: !!pluginSettings.prompts, + settingsKeys: Object.keys(pluginSettings), + apiKeysCount: pluginSettings.api_keys ? Object.keys(pluginSettings.api_keys).length : 0, + promptsCount: pluginSettings.prompts ? Object.keys(pluginSettings.prompts).length : 0 + }); + // Получить API ключ для Gemini let geminiApiKey: string | null | undefined = apiKey; if (!geminiApiKey) { try { console.log('[WORKFLOW_EXECUTION] 🔑 Getting Gemini API key...'); - geminiApiKey = await getApiKeyForModel('gemini-flash'); + geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); console.log('[WORKFLOW_EXECUTION] ✅ Gemini API key retrieved successfully'); } catch (keyError) { console.error('[WORKFLOW_EXECUTION] ❌ Failed to get Gemini API key:', keyError); @@ -1350,8 +1362,20 @@ async function executeWorkflowInOffscreen( console.log('[BACKGROUND] 🔑 Response language в настройках:', pluginSettings?.response_language); console.log('[BACKGROUND] 📊 Все настройки для передачи в worker:', pluginSettings); + console.log('[API_KEY_FLOW] Workflow payload being sent to offscreen:', { + type: workflowPayload.type, + pluginId: workflowPayload.pluginId, + hasPluginSettings: !!workflowPayload.pluginSettings, + pluginSettingsKeys: Object.keys(workflowPayload.pluginSettings), + hasApiKeys: !!(workflowPayload.pluginSettings as any).api_keys, + hasPrompts: !!(workflowPayload.pluginSettings as any).prompts, + hasGeminiApiKey: !!workflowPayload.geminiApiKey + }); + try { + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_MESSAGE_TO_OFFSCREEN_START'); const result = await chrome.runtime.sendMessage(workflowPayload); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_MESSAGE_TO_OFFSCREEN_END'); if (chrome.runtime.lastError) { throw new Error(chrome.runtime.lastError.message); } @@ -1366,6 +1390,8 @@ async function executeWorkflowInOffscreen( console.error(`[WORKFLOW_EXECUTION] ❌ Workflow execution error:`, error); throw error; } + + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_EXECUTE_WORKFLOW_IN_OFFSCREEN_END'); } // === MAIN MESSAGE HANDLERS === @@ -1459,17 +1485,93 @@ chrome.runtime.onMessage.addListener( } // ШАГ 5: Проверить настройки плагина с manifest defaults и пользовательскими настройками - const customSettingsKeys = manifest?.options ? Object.keys(manifest.options) : []; + const customSettingsKeys = manifest?.options ? Object.keys(manifest.options).filter((key: string) => key !== 'prompts') : []; console.log('[BACKGROUND] 🔍 Начинаем получение настроек плагина:', msg.pluginId); - console.log('[BACKGROUND] 📋 Доступные ключи настроек:', customSettingsKeys); + console.log('[BACKGROUND] 📋 Доступные ключи настроек (исключая prompts):', customSettingsKeys); console.log('[BACKGROUND] 📋 Manifest defaults:', manifestDefaults); const pluginSettings = await getPluginSettings(msg.pluginId, manifestDefaults, customSettingsKeys); - console.log('[BACKGROUND] ✅ Настройки плагина получены из chrome.storage.local:', pluginSettings); console.log('[BACKGROUND] 🔑 Значение response_language:', pluginSettings?.response_language); + console.log('[BACKGROUND] 📝 Prompts loaded from manifest:', !!pluginSettings.prompts); console.log('[BACKGROUND] 📊 Все полученные настройки плагина:', JSON.stringify(pluginSettings, null, 2)); - if (!pluginSettings.enabled) { + + // Load prompts from manifest.json for all plugins + let enrichedPluginSettings = { ...pluginSettings }; + + // Добавляем manifest в настройки для доступа в Python + if (manifest) { + (enrichedPluginSettings as any).manifest = manifest; + } + + // Попытаемся дополнить настройками api_keys для Default LLM (по комбинациям promptType/language) + try { + const defaultCombos = [ + { type: 'basic_analysis', lang: 'ru' }, + { type: 'basic_analysis', lang: 'en' }, + { type: 'deep_analysis', lang: 'ru' }, + { type: 'deep_analysis', lang: 'en' }, + ]; + + const apiKeys: Record = {}; + for (const combo of defaultCombos) { + const keyId = `ozon-analyzer.${combo.type}.${combo.lang}.default`; + try { + const key = await APIKeyManager.getDecryptedKey(keyId); + if (key && typeof key === 'string' && key.length > 0) { + apiKeys[keyId] = key; + } + } catch (e) { + // пропускаем отсутствие ключа + } + } + + if (Object.keys(apiKeys).length > 0) { + (enrichedPluginSettings as any).api_keys = { + ...(enrichedPluginSettings as any).api_keys, + ...apiKeys, + }; + } + } catch (e) { + console.warn('[BACKGROUND] Failed to enrich api_keys for plugin', msg.pluginId, e); + } + + // Load prompts from manifest.json if available + if (manifest?.options?.prompts) { + console.log('[BACKGROUND] 📝 Loading prompts from manifest.json for plugin:', msg.pluginId); + + const manifestPrompts = manifest.options.prompts; + enrichedPluginSettings.prompts = {}; + + // Process each prompt type (basic_analysis, deep_analysis, etc.) + for (const [promptType, promptConfig] of Object.entries(manifestPrompts)) { + (enrichedPluginSettings.prompts as any)[promptType] = {}; + + // Process each language (ru, en, etc.) + for (const [language, languageConfig] of Object.entries(promptConfig as any)) { + const defaultPrompt = (languageConfig as any).default; + const llmConfig = (languageConfig as any).LLM; + + // Check if Default LLM is defined + const defaultLLM = llmConfig?.default; + if (!defaultLLM) { + console.warn(`[BACKGROUND] ⚠️ WARNING: No Default LLM defined for ${promptType} in ${language} for plugin ${msg.pluginId}`); + console.warn(`[BACKGROUND] ⚠️ Request will be impossible without Default LLM configuration`); + } + + (enrichedPluginSettings.prompts as any)[promptType][language] = { + custom_prompt: defaultPrompt, + llm: defaultLLM || null + }; + } + } + + console.log('[BACKGROUND] ✅ Prompts loaded from manifest.json:', JSON.stringify(enrichedPluginSettings.prompts, null, 2)); + } else { + console.log('[BACKGROUND] ℹ️ No prompts defined in manifest.json for plugin:', msg.pluginId); + } + + if (!enrichedPluginSettings.enabled) { console.log('[background][RUN_WORKFLOW][INFO] Plugin disabled'); sendResponse({ error: 'Плагин отключен' }); return true; @@ -1583,13 +1685,48 @@ chrome.runtime.onMessage.addListener( const requestId = msg.requestId || `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const transferId = `${requestId}_html_${Date.now()}`; + // Используем enrichedPluginSettings (с prompts из manifest.json) вместо обычных pluginSettings + const settingsToSend = enrichedPluginSettings; + + // [API_KEY_FLOW] MARKER: BACKGROUND_PLUGIN_SETTINGS_PREPARATION_START + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_PLUGIN_SETTINGS_PREPARATION_START'); + console.log('[API_KEY_FLOW] Plugin ID:', msg.pluginId); + console.log('[API_KEY_FLOW] Settings to send keys:', Object.keys(settingsToSend)); + console.log('[API_KEY_FLOW] Settings to send has api_keys:', !!settingsToSend.api_keys); + if (settingsToSend.api_keys) { + console.log('[API_KEY_FLOW] Available API key IDs:', Object.keys(settingsToSend.api_keys)); + console.log('[API_KEY_FLOW] API keys structure:', Object.keys(settingsToSend.api_keys).map(key => ({ + keyId: key, + hasValue: !!(settingsToSend.api_keys as any)[key], + valueLength: (settingsToSend.api_keys as any)[key] ? (settingsToSend.api_keys as any)[key].length : 0 + }))); + } + console.log('[API_KEY_FLOW] Settings to send has prompts:', !!settingsToSend.prompts); + if (settingsToSend.prompts) { + console.log('[API_KEY_FLOW] Prompts structure:', Object.keys(settingsToSend.prompts)); + } + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_PLUGIN_SETTINGS_PREPARATION_END'); + + // [API_KEY_FLOW] MARKER: BACKGROUND_HTML_TRANSMISSION_START + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_HTML_TRANSMISSION_START'); + console.log('[API_KEY_FLOW] Transmission mode:', htmlTransmissionMode); + console.log('[API_KEY_FLOW] HTML size:', pageHtml.length, 'chars'); + if (htmlTransmissionMode === 'direct') { // ПРЯМАЯ ПЕРЕДАЧА HTML console.log('[background][RUN_WORKFLOW] 📨 Using DIRECT HTML transmission'); console.log('[background][RUN_WORKFLOW] HTML size:', pageHtml.length, 'chars'); try { - await sendHtmlDirectly(msg.pluginId, pageKey, pageHtml, requestId, transferId, pluginSettings); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_HTML_DIRECTLY_START'); + console.log('[API_KEY_FLOW] Calling sendHtmlDirectly with pluginId:', msg.pluginId); + console.log('[API_KEY_FLOW] Settings being passed to sendHtmlDirectly:', { + hasApiKeys: !!settingsToSend.api_keys, + hasPrompts: !!settingsToSend.prompts, + settingsKeys: Object.keys(settingsToSend) + }); + await sendHtmlDirectly(msg.pluginId, pageKey, pageHtml, requestId, transferId, settingsToSend); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_HTML_DIRECTLY_END'); console.log('[background][RUN_WORKFLOW] ✅ Direct transmission completed'); } catch (directError) { console.log('[background][RUN_WORKFLOW] ❌ Direct transmission failed, switching to chunked mode'); @@ -1607,7 +1744,7 @@ chrome.runtime.onMessage.addListener( // Храним transfer для отслеживания console.log('[BACKGROUND] 📦 Создаем transfer state с настройками плагина для chunked fallback'); - console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', pluginSettings); + console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', JSON.stringify(settingsToSend, null, 2)); const transferState = { chunks: chunkingResult.chunks, @@ -1620,7 +1757,7 @@ chrome.runtime.onMessage.addListener( totalSize: chunkingResult.totalSize, timestamp: Date.now(), fallbackFromDirect: true, - pluginSettings: pluginSettings // Добавлено: передаем pluginSettings + pluginSettings: settingsToSend // Добавлено: передаем pluginSettings }, resolve: () => { console.log(`[CHUNKING] Transfer ${transferId} completed successfully`); @@ -1649,8 +1786,8 @@ chrome.runtime.onMessage.addListener( // Храним transfer для отслеживания console.log('[BACKGROUND] 📦 Создаем transfer state с настройками плагина для chunked передачи'); - console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', pluginSettings); - console.log('[BACKGROUND] 🔑 Response language в transfer metadata:', pluginSettings?.response_language); + console.log('[BACKGROUND] 📋 Plugin settings в transfer metadata:', JSON.stringify(settingsToSend, null, 2)); + console.log('[BACKGROUND] 🔑 Response language в transfer metadata:', settingsToSend?.response_language); const transferState = { chunks: chunkingResult.chunks, @@ -1662,7 +1799,7 @@ chrome.runtime.onMessage.addListener( requestId: requestId, totalSize: chunkingResult.totalSize, timestamp: Date.now(), - pluginSettings: pluginSettings // Добавлено: передаем pluginSettings + pluginSettings: settingsToSend // Добавлено: передаем pluginSettings }, resolve: () => { console.log(`[CHUNKING] Transfer ${transferId} completed successfully`); @@ -1680,10 +1817,21 @@ chrome.runtime.onMessage.addListener( activeTransfers.set(transferId, transferState); // Отправляем chunks + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_CHUNKS_TO_OFFSCREEN_START'); + console.log('[API_KEY_FLOW] Calling sendChunksToOffscreen with transferId:', transferId); + console.log('[API_KEY_FLOW] Metadata being passed to sendChunksToOffscreen:', { + hasPluginSettings: !!transferState.metadata.pluginSettings, + pluginSettingsKeys: transferState.metadata.pluginSettings ? Object.keys(transferState.metadata.pluginSettings) : [], + hasApiKeys: !!(transferState.metadata.pluginSettings as any)?.api_keys, + hasPrompts: !!(transferState.metadata.pluginSettings as any)?.prompts + }); await sendChunksToOffscreen(transferId, chunkingResult.chunks, transferState.metadata); + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_SEND_CHUNKS_TO_OFFSCREEN_END'); console.log('[background][RUN_WORKFLOW] ✅ Chunked transmission completed'); } + console.log('[API_KEY_FLOW] MARKER: BACKGROUND_HTML_TRANSMISSION_END'); + console.log('[background][RUN_WORKFLOW] ===== RUN_WORKFLOW HANDLER COMPLETED ====='); sendResponse({ success: true }); @@ -2032,7 +2180,7 @@ chrome.runtime.onMessage.addListener( // Получить API ключ и добавить к сообщению try { - const geminiApiKey = await getApiKeyForModel('gemini-flash'); + const geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); executeWorkflowMessage.geminiApiKey = geminiApiKey; console.log('[background][HTML_ASSEMBLED] ✅ API key added to EXECUTE_WORKFLOW message'); } catch (keyError) { @@ -2064,6 +2212,38 @@ chrome.runtime.onMessage.addListener( return true; // Указываем асинхронную обработку } + // === GET_API_KEY: Обработчик запросов API ключей === + if (msg.type === 'GET_API_KEY') { + console.log('[background][GET_API_KEY] 📨 Received GET_API_KEY request'); + console.log('[background][GET_API_KEY] Key ID:', msg.data?.keyId); + + (async () => { + try { + if (!msg.data?.keyId) { + console.error('[background][GET_API_KEY] ❌ Missing keyId in request'); + sendResponse({ apiKey: null, error: 'Missing keyId' }); + return; + } + + const apiKey = await APIKeyManager.getDecryptedKey(msg.data.keyId); + console.log('[background][GET_API_KEY] ✅ API key retrieved:', !!apiKey); + + sendResponse({ + apiKey: apiKey || null + }); + + } catch (error: unknown) { + console.error('[background][GET_API_KEY] ❌ Error retrieving API key:', error); + sendResponse({ + apiKey: null, + error: (error as Error).message + }); + } + })(); + + return true; // Указываем асинхронную обработку + } + // === DEBUG: Добавляем логи для отслеживания неизвестных сообщений === if (!msg.type) { console.log('[background][DEBUG] Получено сообщение:', msg); @@ -2126,14 +2306,18 @@ const handleHostApiMessage = async ( break; } case 'llm_call': { + console.log('[HOST API] ===== LLM_CALL HANDLER START ====='); + console.log('[HOST API] Message received:', message); + console.log('[HOST API] Message data:', message.data); try { - const { modelAlias, options, pluginId } = message.data as { + const { modelAlias, options, pluginId, apiKeyId } = message.data as { modelAlias: string; options: any; - pluginId?: string + pluginId?: string; + apiKeyId?: string; }; - console.log('[HOST API] LLM call requested:', { modelAlias, pluginId }); + console.log('[HOST API] LLM call requested:', { modelAlias, pluginId, apiKeyId }); const currentPlugin = pluginId || 'ozon-analyzer'; const manifestUrl = chrome.runtime.getURL(`plugins/${currentPlugin}/manifest.json`); @@ -2158,8 +2342,31 @@ const handleHostApiMessage = async ( console.log(`[DEBUG] manifestResponse status:`, manifestResponse.status); const aiModels = manifest.ai_models || {}; + // Проверяем, является ли это Default LLM с curl_file + const prompts = manifest.options?.prompts; + let isDefaultLLMWithCurl = false; + let curlFile = null; + + if (prompts && modelAlias) { + // Ищем в prompts для всех комбинаций тип/язык + for (const [promptType, promptConfig] of Object.entries(prompts)) { + if (promptType === modelAlias) { + for (const [language, langConfig] of Object.entries(promptConfig as any)) { + const llmConfig = (langConfig as any).LLM; + if (llmConfig?.default?.curl_file) { + isDefaultLLMWithCurl = true; + curlFile = llmConfig.default.curl_file; + console.log(`[HOST API] Found Default LLM with curl_file for ${modelAlias}.${language}: ${curlFile}`); + break; + } + } + if (isDefaultLLMWithCurl) break; + } + } + } + const actualModel = aiModels[modelAlias]; - if (!actualModel) { + if (!actualModel && !isDefaultLLMWithCurl) { sendResponse({ error: true, error_message: `Модель с алиасом '${modelAlias}' не найдена в манифесте плагина` @@ -2167,22 +2374,49 @@ const handleHostApiMessage = async ( return true; } - console.log('[HOST API] Using model:', actualModel, 'for alias:', modelAlias); + // Выбираем модель для вызова + let modelToUse; + if (isDefaultLLMWithCurl) { + // Для Default LLM с curl_file используем специальный обработчик + modelToUse = 'default-curl'; + console.log('[HOST API] Using Default LLM with curl_file:', curlFile); + } else { + modelToUse = actualModel; + console.log('[HOST API] Using model:', actualModel, 'for alias:', modelAlias); + } - const apiKey = await getApiKeyForModel(actualModel); + // Получаем API-ключ: если apiKeyId указан, используем его, иначе используем model alias + // Учитываем вариант, когда apiKeyId передается внутри options (из Python/pyodide) + const keyId = apiKeyId || (options && options.apiKeyId) || actualModel; + console.log('[HOST API] Getting API key for:', keyId); + + const apiKey = await APIKeyManager.getDecryptedKey(keyId); if (!apiKey) { sendResponse({ error: true, - error_message: `API ключ для модели ${actualModel} не найден` + error_message: `API ключ для модели ${modelToUse} не найден. KeyId: ${keyId}` }); return true; } try { - const aiResponse = await callAiModel(actualModel, apiKey, options.prompt || ''); - sendResponse({ - response: aiResponse - }); + if (isDefaultLLMWithCurl) { + // Для Default LLM с curl_file используем специальную обработку + console.log('[HOST API] Processing Default LLM with curl_file:', curlFile); + // TODO: Реализовать обработку curl_file + // Пока используем fallback на обычную модель + const fallbackModel = actualModel || 'gemini-flash-lite'; + console.log('[HOST API] Using fallback model:', fallbackModel); + const aiResponse = await callAiModel(fallbackModel, apiKey, options.prompt || ''); + sendResponse({ + response: aiResponse + }); + } else { + const aiResponse = await callAiModel(modelToUse, apiKey, options.prompt || ''); + sendResponse({ + response: aiResponse + }); + } } catch (aiError) { console.error('[HOST API] AI API error:', aiError); sendResponse({ @@ -2197,6 +2431,25 @@ const handleHostApiMessage = async ( error_message: (error as Error).message }); } + console.log('[HOST API] ===== LLM_CALL HANDLER END ====='); + break; + } + case 'GET_API_KEY': { + try { + const { keyId } = message.data as { keyId: string }; + console.log('[HOST API] Getting API key for:', keyId); + + const apiKey = await APIKeyManager.getDecryptedKey(keyId); + sendResponse({ + apiKey: apiKey || null + }); + } catch (error) { + console.error('[HOST API] Error getting API key:', error); + sendResponse({ + apiKey: null, + error: (error as Error).message + }); + } break; } case 'get_setting': { @@ -2486,11 +2739,12 @@ async function handleMessage(message: any, sender: any): Promise { // Загрузить настройки из manifest.json для получения дефолтных значений const manifestUrl = chrome.runtime.getURL(`plugins/${message.pluginId}/manifest.json`); let manifestDefaults: Partial = {}; + let manifest: any = null; try { const manifestResponse = await fetch(manifestUrl); if (manifestResponse.ok) { - const manifest = await manifestResponse.json(); + manifest = await manifestResponse.json(); console.log(`[background][PORT][RUN_WORKFLOW] ✅ Manifest loaded for ${message.pluginId}:`, manifest); const manifestSettings = manifest.options || {}; manifestDefaults = { @@ -2509,7 +2763,8 @@ async function handleMessage(message: any, sender: any): Promise { console.log('[BACKGROUND][PORT][RUN_WORKFLOW] 🔍 Получаем настройки плагина из chrome.storage.local'); console.log('[BACKGROUND][PORT][RUN_WORKFLOW] 📋 Manifest defaults:', manifestDefaults); - const settings = await getPluginSettings(message.pluginId, manifestDefaults); + const customSettingsKeys = manifest?.options ? Object.keys(manifest.options).filter((key: string) => key !== 'prompts') : []; + const settings = await getPluginSettings(message.pluginId, manifestDefaults, customSettingsKeys); console.log('[BACKGROUND][PORT][RUN_WORKFLOW] ✅ Настройки плагина получены:', settings); console.log('[BACKGROUND][PORT][RUN_WORKFLOW] 🔑 Response language:', settings?.response_language); @@ -2900,7 +3155,7 @@ async function handleMessage(message: any, sender: any): Promise { // Получить API ключ и добавить к сообщению try { - const geminiApiKey = await getApiKeyForModel('gemini-flash'); + const geminiApiKey = await getApiKeyForModel('gemini-flash-lite'); executeWorkflowMessage.geminiApiKey = geminiApiKey; console.log('[background][PORT][HTML_ASSEMBLED] ✅ API key added to EXECUTE_WORKFLOW message'); } catch (keyError) { diff --git a/chrome-extension/src/background/index.ts.backup b/chrome-extension/src/background/index.ts.backup old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/mcp-bridge.ts b/chrome-extension/src/background/mcp-bridge.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/alert-manager.ts b/chrome-extension/src/background/monitoring/alert-manager.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/error-tracker.ts b/chrome-extension/src/background/monitoring/error-tracker.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/html-extraction-monitor.ts b/chrome-extension/src/background/monitoring/html-extraction-monitor.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/index.ts b/chrome-extension/src/background/monitoring/index.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/logger.ts b/chrome-extension/src/background/monitoring/logger.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/metrics-collector.ts b/chrome-extension/src/background/monitoring/metrics-collector.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/monitoring-core.ts b/chrome-extension/src/background/monitoring/monitoring-core.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/network-tracker.ts b/chrome-extension/src/background/monitoring/network-tracker.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/performance-monitor.ts b/chrome-extension/src/background/monitoring/performance-monitor.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/monitoring/pyodide-monitor.ts b/chrome-extension/src/background/monitoring/pyodide-monitor.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/package-lock.json b/chrome-extension/src/background/package-lock.json old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/package.json b/chrome-extension/src/background/package.json old mode 100644 new mode 100755 index ef202082..0a59b7fd --- a/chrome-extension/src/background/package.json +++ b/chrome-extension/src/background/package.json @@ -1,6 +1,6 @@ { "name": "chrome-extension-background", - "version": "1.0.820", + "version": "1.0.969", "scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development --watch" diff --git a/chrome-extension/src/background/plugin-chat-api.ts b/chrome-extension/src/background/plugin-chat-api.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/plugin-chat-cache.ts b/chrome-extension/src/background/plugin-chat-cache.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/plugin-manager.ts b/chrome-extension/src/background/plugin-manager.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/prompt-test-handler.ts b/chrome-extension/src/background/prompt-test-handler.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/pyodide-worker.js b/chrome-extension/src/background/pyodide-worker.js old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/worker-manager.ts b/chrome-extension/src/background/worker-manager.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/background/workflow-engine.ts b/chrome-extension/src/background/workflow-engine.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/src/content/workerManager.js b/chrome-extension/src/content/workerManager.js old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/JsonViewer.css b/chrome-extension/src/side-panel/components/JsonViewer.css old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/JsonViewer.tsx b/chrome-extension/src/side-panel/components/JsonViewer.tsx old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/LogManager.css b/chrome-extension/src/side-panel/components/LogManager.css old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/LogManager.tsx b/chrome-extension/src/side-panel/components/LogManager.tsx old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/PluginCard.css b/chrome-extension/src/side-panel/components/PluginCard.css old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/PluginCard.tsx b/chrome-extension/src/side-panel/components/PluginCard.tsx old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/ToastNotifications.css b/chrome-extension/src/side-panel/components/ToastNotifications.css old mode 100644 new mode 100755 diff --git a/chrome-extension/src/side-panel/components/ToastNotifications.tsx b/chrome-extension/src/side-panel/components/ToastNotifications.tsx old mode 100644 new mode 100755 diff --git a/chrome-extension/test-chunk-transmission.html b/chrome-extension/test-chunk-transmission.html deleted file mode 100644 index b0c8f6ee..00000000 --- a/chrome-extension/test-chunk-transmission.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - Chunk Transmission Race Condition Test - - - -
-

🔧 Race Condition Protection Test Suite

- -
-
-

Transmission Status

- 0 / 40 -
-
-
-
-

Ready to start testing

-
- -
-
- 40 - Expected Chunks -
-
- 0 KB - Transferred Size -
-
- 0 KB/s - Transfer Speed -
-
- 0s - Elapsed Time -
-
- -
- - - - -
- -
-

📋 Test Results & Diagnostics

-
-
-
- - - - \ No newline at end of file diff --git a/chrome-extension/test-communication.js b/chrome-extension/test-communication.js deleted file mode 100644 index 0c070d5c..00000000 --- a/chrome-extension/test-communication.js +++ /dev/null @@ -1,38 +0,0 @@ -// Тестовый скрипт для проверки messaging системы -// Запуск: node test-communication.js - -const testMessagingSystem = async () => { - console.log('🧪 Тестирование messaging системы после исправлений...\n'); - - // Тест 1: Проверка PING/PONG механизма - console.log('📡 Тест 1: Проверка механизма PING/PONG'); - console.log('✅ PING/PONG механизм реализован в background.js и SidePanel.tsx\n'); - - // Тест 2: Проверка увеличенных таймаутов - console.log('⏰ Тест 2: Проверка увеличенных таймаутов'); - console.log('✅ Таймауты увеличены: 3000ms → 5000ms → 8000ms\n'); - - // Тест 3: Проверка синхронной обработки в background - console.log('🔄 Тест 3: Проверка синхронной обработки'); - console.log('✅ Все асинхронные обработчики заменены на синхронные\n'); - - // Тест 4: Проверка heartbeat механизма - console.log('💓 Тест 4: Проверка heartbeat механизма'); - console.log('✅ Heartbeat реализован с интервалом 10 секунд\n'); - - // Тест 5: Проверка retry логики с проверкой соединения - console.log('🔁 Тест 5: Проверка retry логики'); - console.log('✅ Retry логика включает проверку соединения перед каждым запросом\n'); - - console.log('🎉 Все тесты пройдены! Система готова к использованию.'); - console.log('\n📋 Ключевые исправления:'); - console.log('1. ✅ Увеличены таймауты в SidePanel.tsx'); - console.log('2. ✅ Исправлена асинхронная обработка в background.js'); - console.log('3. ✅ Добавлен PING/PONG механизм'); - console.log('4. ✅ Реализован heartbeat механизм'); - console.log('5. ✅ Улучшена retry логика с проверкой соединения'); - - console.log('\n🚀 Система должна теперь корректно обрабатывать запросы и избегать undefined ответов.'); -}; - -testMessagingSystem().catch(console.error); \ No newline at end of file diff --git a/chrome-extension/test-direct-page.html b/chrome-extension/test-direct-page.html deleted file mode 100644 index b89ccb27..00000000 --- a/chrome-extension/test-direct-page.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - Pyodide Direct Test - - - -
-

🧪 Прямой тест Pyodide endpoint

- -
-

Что это тестирует:

-
    -
  • Отправку сообщения TEST_PYODIDE_DIRECT в background script
  • -
  • Проверку версии Chrome (≥109 или <109)
  • -
  • Исполнение Python кода через Pyodide в offscreen document
  • -
  • Получение результата выполнения
  • -
-
- -

Python код для тестирования:

- - - - - - -
-
- - - - \ No newline at end of file diff --git a/chrome-extension/test-large-html.html b/chrome-extension/test-large-html.html deleted file mode 100644 index 23adcc5b..00000000 --- a/chrome-extension/test-large-html.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - Large HTML Test - 33+ Chunks - - - - -
-

Large HTML Test Document

-

- This is a test document designed to create more than 33 chunks of 32KB each to thoroughly test our race condition protection in the HTML chunk transfer protocol. - The document contains repetitive content to achieve the desired size and trigger multiple chunk transmissions. - This should help us validate that our enhanced chunk manager with multi-layered storage protection works correctly. -

-
- - - - - -
-

Technical Test Sections

-
- -
-

Race Condition Test Section 1

-

The race condition protection mechanism we've implemented includes:

-
    -
  • Multi-layered storage system (active | completed | global references | global scope)
  • -
  • Global reference storage for immediate access
  • -
  • Global scope backup storage as last resort
  • -
  • Safe transfer lookup with fallback mechanisms
  • -
  • Enhanced cleanup logic with staggered retention periods
  • -
-
-

This section contains detailed technical information about our chunk transfer system improvements.

-

The system now maintains transfer states in 4 different storage layers to prevent "Transfer not found" errors.

-

Active transfers are automatically moved to completed backup storage when assembly completes.

-

Global references ensure immediate access even during cleanup operations.

-

The global scope provides ultimate fallback for critical operations.

-
-
- -
-

Race Condition Test Section 2

-

Background script improvements include:

-
    -
  • Enhanced ChunkManager with race condition protection
  • -
  • Multi-level transfer lookup mechanism
  • -
  • Safe transfer state validation
  • -
  • Comprehensive diagnostic logging
  • -
  • Graceful degradation on transfer cleanup
  • -
-
-

The background script now monitors transfer scope state every 10 seconds to detect potential race conditions.

-

Detailed diagnostic information is logged for each transfer operation.

-

Transfer timeout warnings are shown 5 seconds before cleanup to allow debugging.

-

The system prevents premature cleanup by using staggered retention times for different storage layers.

-

Empty HTML fallback is provided when all other recovery mechanisms fail.

-
-
- - - -
-
- - - -

[CONTENT CONTINUES TO REACH 33+ CHUNKS...]

- - - \ No newline at end of file diff --git a/chrome-extension/test-pyodide-direct.js b/chrome-extension/test-pyodide-direct.js deleted file mode 100644 index a8b53477..00000000 --- a/chrome-extension/test-pyodide-direct.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Test script for direct Pyodide endpoint - * Run this in browser console to test the PYODIDE_DIRECT_TEST functionality - */ - -// Test function -async function testDirectPyodide() { - console.log('🚀 Testing TEST_PYODIDE_DIRECT endpoint...'); - - try { - const testMessage = { - type: 'TEST_PYODIDE_DIRECT', - pythonCode: 'print("Hello World from Pyodide!")\nimport time\nresult = f"Current time: {time.time()}"\nprint(result)\nresult', - timestamp: Date.now() - }; - - console.log('📤 Sending test message:', testMessage); - - // Send message to background script - const response = await chrome.runtime.sendMessage(testMessage); - - console.log('📥 Received response:', response); - - if (response.success) { - console.log('✅ Test successful!'); - console.log('📊 Result:', response.result); - console.log('🌐 Chrome version:', response.chromeVersion); - console.log('⏱️ Execution time:', response.timestamp - testMessage.timestamp, 'ms'); - } else { - console.log('❌ Test failed!'); - console.log('🛠️ Error:', response.error); - - if (response.chromeVersion && parseInt(response.chromeVersion) < 109) { - console.log('ℹ️ Chrome version < 109 detected - this is expected behavior'); - } - } - - } catch (error) { - console.error('💥 Communication error:', error); - console.log('Make sure extension is loaded and background script is running'); - } -} - -// Alternative test with simple Python code -async function testSimplePyodide() { - console.log('🐍 Testing with simple Python code...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'TEST_PYODIDE_DIRECT', - pythonCode: '1 + 2 + 3', - timestamp: Date.now() - }); - - console.log('📥 Simple test response:', response); - - if (response.success) { - console.log('✅ Simple test successful, result:', response.result); - } else { - console.log('❌ Simple test failed:', response.error); - } - - } catch (error) { - console.error('💥 Simple test error:', error); - } -} - -// Run tests -if (typeof chrome !== 'undefined' && chrome.runtime) { - console.log('🔧 Chrome extension runtime detected, running tests...'); - - // Test 1: Simple test - setTimeout(() => { - testSimplePyodide(); - }, 1000); - - // Test 2: Full test - setTimeout(() => { - testDirectPyodide(); - }, 2000); - -} else { - console.log('🔶 Chrome extension not detected'); - console.log('💡 To test:'); - console.log('1. Load extension in Chrome'); - console.log('2. Open DevTools console'); - console.log('3. Copy and run this file'); -} - -// Export for manual testing -window.testDirectPyodide = testDirectPyodide; -window.testSimplePyodide = testSimplePyodide; - -console.log('🎯 Available test functions:'); -console.log('- testDirectPyodide()'); -console.log('- testSimplePyodide()'); \ No newline at end of file diff --git a/chrome-extension/tsconfig.json b/chrome-extension/tsconfig.json old mode 100644 new mode 100755 diff --git a/chrome-extension/utils/cleanup-build.cjs b/chrome-extension/utils/cleanup-build.cjs old mode 100644 new mode 100755 diff --git a/chrome-extension/utils/plugins/make-manifest-plugin.ts b/chrome-extension/utils/plugins/make-manifest-plugin.ts old mode 100644 new mode 100755 diff --git a/chrome-extension/vite.config.mts b/chrome-extension/vite.config.mts old mode 100644 new mode 100755 diff --git a/commitlint.config.cjs b/commitlint.config.cjs old mode 100644 new mode 100755 diff --git a/core/host-api.js b/core/host-api.js old mode 100644 new mode 100755 diff --git a/core/plugin-manager.js b/core/plugin-manager.js old mode 100644 new mode 100755 diff --git a/core/workflow-engine.js b/core/workflow-engine.js old mode 100644 new mode 100755 diff --git a/danger/dangerfile.js b/danger/dangerfile.js old mode 100644 new mode 100755 diff --git a/docs/README-ozon-analyzer.md b/docs/README-ozon-analyzer.md old mode 100644 new mode 100755 diff --git a/docs/README.md b/docs/README.md old mode 100644 new mode 100755 diff --git a/docs/administration/Cursor_Rules_Index.en.md b/docs/administration/Cursor_Rules_Index.en.md old mode 100644 new mode 100755 diff --git a/docs/administration/README.md b/docs/administration/README.md old mode 100644 new mode 100755 diff --git a/docs/api-documentation.md b/docs/api-documentation.md old mode 100644 new mode 100755 index b0228fdb..4f2c5269 --- a/docs/api-documentation.md +++ b/docs/api-documentation.md @@ -236,10 +236,10 @@ interface WorkflowStep { ### Model Mapping ```javascript const aiModelMapping = { - "basic_analysis": "gemini-flash", + "basic_analysis": "gemini-flash-lite", "detailed_comparison": "gemini-pro", "deep_analysis": "gemini-pro", - "scraping_fallback": "gemini-flash" + "scraping_fallback": "gemini-flash-lite" }; ``` @@ -249,7 +249,7 @@ const aiProviders = { google: { apiUrl: "https://generativelanguage.googleapis.com", models: { - "gemini-flash": "models/gemini-1.5-flash", + "gemini-flash-lite": "models/gemini-1.5-flash", "gemini-pro": "models/gemini-1.5-pro" }, rateLimits: { @@ -326,7 +326,7 @@ const aiProviders = { }, "ai_providers": { "google": { - "fallback_chain": ["gemini-flash", "gemini-pro"], + "fallback_chain": ["gemini-flash-lite", "gemini-pro"], "rate_limits": { "requests_per_minute": 60, "requests_per_hour": 1000 diff --git a/docs/api/.nojekyll b/docs/api/.nojekyll old mode 100644 new mode 100755 diff --git a/docs/api/assets/hierarchy.js b/docs/api/assets/hierarchy.js old mode 100644 new mode 100755 diff --git a/docs/api/assets/highlight.css b/docs/api/assets/highlight.css old mode 100644 new mode 100755 diff --git a/docs/api/assets/icons.js b/docs/api/assets/icons.js old mode 100644 new mode 100755 diff --git a/docs/api/assets/icons.svg b/docs/api/assets/icons.svg old mode 100644 new mode 100755 diff --git a/docs/api/assets/main.js b/docs/api/assets/main.js old mode 100644 new mode 100755 diff --git a/docs/api/assets/navigation.js b/docs/api/assets/navigation.js old mode 100644 new mode 100755 diff --git a/docs/api/assets/search.js b/docs/api/assets/search.js old mode 100644 new mode 100755 diff --git a/docs/api/assets/style.css b/docs/api/assets/style.css old mode 100644 new mode 100755 diff --git a/docs/api/functions/streamFileToZip.html b/docs/api/functions/streamFileToZip.html old mode 100644 new mode 100755 diff --git a/docs/api/hierarchy.html b/docs/api/hierarchy.html old mode 100644 new mode 100755 diff --git a/docs/api/index.html b/docs/api/index.html old mode 100644 new mode 100755 diff --git a/docs/api/interfaces/IManifestParser.html b/docs/api/interfaces/IManifestParser.html old mode 100644 new mode 100755 diff --git a/docs/api/modules.html b/docs/api/modules.html old mode 100644 new mode 100755 diff --git a/docs/api/variables/ManifestParser.html b/docs/api/variables/ManifestParser.html old mode 100644 new mode 100755 diff --git a/docs/architecture.md b/docs/architecture.md old mode 100644 new mode 100755 index 2c9cd995..17548717 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -355,7 +355,7 @@ class FastDOMParser: ```javascript const aiProviders = { google: { - fallbackChain: ["gemini-flash", "gemini-pro"], + fallbackChain: ["gemini-flash-lite", "gemini-pro"], rateLimits: { requestsPerMinute: 60, burstLimit: 20 } }, openai: { @@ -367,7 +367,7 @@ const aiProviders = { **Fallback стратегия**: 1. Основная модель текущего провайдера -2. Быстрая модель провайдера (gemini-flash, gpt-3.5-turbo) +2. Быстрая модель провайдера (gemini-flash-lite, gpt-3.5-turbo) 3. Продвинутая модель провайдера (gemini-pro, gpt-4) 4. Оффлайн режим с кешем diff --git a/docs/chrome_pyodide/chrome_extension_pyodide.txt b/docs/chrome_pyodide/chrome_extension_pyodide.txt old mode 100644 new mode 100755 diff --git a/docs/chrome_pyodide/chrome_pyodide_readme.md b/docs/chrome_pyodide/chrome_pyodide_readme.md old mode 100644 new mode 100755 diff --git a/docs/developer-commands.md b/docs/developer-commands.md old mode 100644 new mode 100755 diff --git a/docs/devtools-panel-troubleshooting.md b/docs/devtools-panel-troubleshooting.md old mode 100644 new mode 100755 diff --git a/docs/devtools-panel-usage.md b/docs/devtools-panel-usage.md old mode 100644 new mode 100755 diff --git a/docs/devtools-testing-guide.md b/docs/devtools-testing-guide.md old mode 100644 new mode 100755 diff --git a/docs/examples.md b/docs/examples.md old mode 100644 new mode 100755 diff --git a/docs/gemini-delegation-summary.md b/docs/gemini-delegation-summary.md old mode 100644 new mode 100755 diff --git a/docs/integration-guide.md b/docs/integration-guide.md old mode 100644 new mode 100755 index 03cb62fa..36855c6d --- a/docs/integration-guide.md +++ b/docs/integration-guide.md @@ -89,7 +89,7 @@ ozon-analyzer/ const GEMINI_CONFIG = { apiKey: process.env.GEMINI_API_KEY, models: { - 'gemini-flash': 'models/gemini-1.5-flash', + 'gemini-flash-lite': 'models/gemini-1.5-flash', 'gemini-pro': 'models/gemini-1.5-pro' }, retryPolicy: { diff --git a/docs/message-port-fix-summary.md b/docs/message-port-fix-summary.md old mode 100644 new mode 100755 diff --git a/docs/monitoring-alerting.md b/docs/monitoring-alerting.md old mode 100644 new mode 100755 diff --git a/docs/onboarding.md b/docs/onboarding.md old mode 100644 new mode 100755 diff --git a/docs/performance-benchmarking.md b/docs/performance-benchmarking.md old mode 100644 new mode 100755 diff --git a/docs/plugins/ozon-analyzer-integration-guide.md b/docs/plugins/ozon-analyzer-integration-guide.md old mode 100644 new mode 100755 diff --git a/docs/plugins/ozon-analyzer-technical-spec.md b/docs/plugins/ozon-analyzer-technical-spec.md old mode 100644 new mode 100755 diff --git a/docs/plugins/ozon-analyzer-ui-documentation.md b/docs/plugins/ozon-analyzer-ui-documentation.md old mode 100644 new mode 100755 diff --git a/docs/security-compliance.md b/docs/security-compliance.md old mode 100644 new mode 100755 diff --git a/docs/transfer-best-practices-user.md b/docs/transfer-best-practices-user.md old mode 100644 new mode 100755 diff --git a/docs/transferability.ru.md b/docs/transferability.ru.md old mode 100644 new mode 100755 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md old mode 100644 new mode 100755 diff --git a/e2e_test_diagram.md b/e2e_test_diagram.md old mode 100644 new mode 100755 diff --git a/eslint.config.ts b/eslint.config.ts old mode 100644 new mode 100755 diff --git a/import-best-practices/README.en.md b/import-best-practices/README.en.md deleted file mode 100644 index 14ddd82b..00000000 --- a/import-best-practices/README.en.md +++ /dev/null @@ -1,28 +0,0 @@ -# import-best-practices - -This directory is intended for temporary import of best practices, organizational knowledge, and AI instructions from other (more advanced) projects. - -## Purpose -- Isolate imported AI materials from the current ones, preventing conflicts and accidental overwrites. -- Convenient for analysis, comparison, and adoption of new best practices. -- Safe and controlled process for integrating the best solutions. - -## Structure -Copy the following folders from another project into this directory: -- `docs/for-ai-best-practices` — AI best practices (EN, for AI only) -- `memory-bank` — organizational knowledge and rules (EN, for AI only) -- `.cursor/rules` — (optional) AI rules, if you need to transfer them -- (optional) other directories with AI metadata, if used in your architecture - -## How to use -1. Copy the required folders from another project into `import-best-practices`. -2. Ask your AI assistant: - ``` - Analyze the contents of import-best-practices and integrate/supplement the best practices, standards, and organizational knowledge into the current project, avoiding duplication and conflicts. - ``` -3. After integration, delete the `import-best-practices` directory (or archive it for history). - -## Important -- Do not use this directory for permanent storage — only for temporary import and analysis. -- All materials inside must be in English and are intended for AI assistants. -- For user instructions, see the `docs/` folder. \ No newline at end of file diff --git a/import-best-practices/README.md b/import-best-practices/README.md deleted file mode 100644 index 59fd9ea5..00000000 --- a/import-best-practices/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# import-best-practices - -Этот каталог предназначен для временного импорта best practices, организационных знаний и AI-инструкций из других (более передовых) проектов. - -## Назначение -- Изоляция импортируемых AI-материалов от текущих, предотвращение конфликтов и случайного перезаписывания. -- Удобство для анализа, сравнения и внедрения новых best practices. -- Безопасный и контролируемый процесс интеграции лучших наработок. - -## Структура -В этот каталог целиком копируются из другого проекта: -- `docs/for-ai-best-practices` — AI best practices (EN, только для AI) -- `memory-bank` — организационные знания и правила (EN, только для AI) -- `.cursor/rules` — (опционально) AI-правила, если требуется их перенос -- (опционально) другие каталоги с AI-метаданными, если используются в архитектуре - -## Как использовать -1. Скопируйте нужные папки из другого проекта в `import-best-practices`. -2. Дайте AI-ассистенту команду: - ``` - Проанализируй содержимое import-best-practices и внедри/дополни лучшие практики, стандарты и организационные знания в текущий проект, избегая дублирования и конфликтов. - ``` -3. После внедрения — удалите каталог `import-best-practices` (или архивируйте для истории). - -## Важно -- Не используйте этот каталог для постоянного хранения — только для временного импорта и анализа. -- Все материалы внутри — на английском языке и предназначены для AI-ассистентов. -- Для пользовательских инструкций используйте папку `docs/`. \ No newline at end of file diff --git a/memory-bank/INDEX.md b/memory-bank/INDEX.md old mode 100644 new mode 100755 diff --git a/memory-bank/MEMORY_BANK_STRUCTURE.md b/memory-bank/MEMORY_BANK_STRUCTURE.md old mode 100644 new mode 100755 diff --git a/memory-bank/README.md b/memory-bank/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/README.md b/memory-bank/architecture/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/architecture-decisions.md b/memory-bank/architecture/architecture-decisions.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/comprehensive-architecture.md b/memory-bank/architecture/comprehensive-architecture.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/llm-selection-data-flow.md b/memory-bank/architecture/llm-selection-data-flow.md new file mode 100644 index 00000000..25e0a6ff --- /dev/null +++ b/memory-bank/architecture/llm-selection-data-flow.md @@ -0,0 +1,348 @@ +# LLM Selection and Data Flow Architecture + +## Overview + +This document describes the comprehensive architecture for LLM selection and data flow in the Agent Plugins Platform, focusing on the ozon-analyzer plugin as the primary example. The system enables users to configure different LLM models for different analysis types (basic analysis vs deep analysis) and languages (Russian vs English), with sophisticated API key management and settings persistence. + +updated: 2025.10.30-07:30:00 + +## 1. UI Architecture + +### Plugin Options Interface + +The user interface for LLM configuration is implemented in `pages/options/src/components/PluginDetails.tsx` and `pages/options/src/components/LLMSelector.tsx`. The interface provides: + +#### PluginDetails Component +- **Location**: `pages/options/src/components/PluginDetails.tsx` +- **Purpose**: Main plugin configuration interface +- **Key Features**: + - Displays plugin information (name, version, author, description) + - Shows host permissions and extension permissions + - Contains `PromptsEditor` component for prompt customization + - Manages plugin enable/disable and autorun settings + +#### PromptsEditor Component +- **Location**: Within `PluginDetails.tsx` (lines 48-337) +- **Purpose**: Manages prompt types and language selection +- **Features**: + - Tab-based interface for switching between `basic_analysis` and `deep_analysis` + - Language tabs for `ru` (Russian) and `en` (English) + - Side-by-side display of original prompts (read-only) and custom prompts (editable) + - Copy button to transfer original prompts to custom editing area + - Save functionality to persist custom prompts + +#### LLMSelector Component +- **Location**: `pages/options/src/components/LLMSelector.tsx` +- **Purpose**: Binds specific LLMs to prompt types and languages +- **Key Props**: + - `promptType`: `'basic_analysis' | 'deep_analysis'` + - `language`: `'ru' | 'en'` + - `globalAIKeys`: Array of available platform LLM configurations + - `defaultLLMCurl`: Default LLM model name from manifest + - `hasDefaultLLM`: Boolean indicating if default LLM is available + - `onLLMChange`: Callback for LLM selection changes + +#### LLM Selection Options +The LLM selector provides two types of LLM configurations: + +1. **Default LLM**: Uses `gemini-flash-lite` for basic analysis, `gemini-pro` for deep analysis + - Requires API key input in a password field + - API key stored encrypted with key ID pattern: `ozon-analyzer.{promptType}.{language}.default` + +2. **Platform LLMs**: Custom LLM configurations from `globalAIKeys` + - Pre-configured API keys managed at platform level + - No manual API key input required + +### Legacy Options Interface + +A legacy options interface exists at `chrome-extension/public/options/index.html`: +- **Purpose**: Basic HTML transmission mode settings +- **Limitations**: Does not support LLM selection or prompt customization +- **Status**: Superseded by React-based interface in `pages/options/` + +## 2. Data Flow Architecture + +### Settings Storage Flow + +1. **User Selection** → `LLMSelector.handleLLMChange()` +2. **Validation** → Update local component state +3. **Settings Update** → `usePluginSettings.updateBasicAnalysisSettings()` or `updateDeepAnalysisSettings()` +4. **Encryption** → API keys encrypted via `APIKeyManager.saveEncryptedKey()` +5. **Persistence** → Settings saved to `chrome.storage.local` with key `'plugin-ozon-analyzer-settings'` +6. **Global State** → Settings propagated to `pyodide.globals.pluginSettings` + +### Settings Structure + +```typescript +interface PluginSettings { + basic_analysis: { + ru: { llm: string; custom_prompt: string }; + en: { llm: string; custom_prompt: string }; + }; + deep_analysis: { + ru: { llm: string; custom_prompt: string }; + en: { llm: string; custom_prompt: string }; + }; + api_keys: { + default: string; // encrypted + [key: string]: string; // other encrypted keys + }; +} +``` + +### Data Propagation Path + +``` +User Selection → LLMSelector → usePluginSettings → chrome.storage.local + ↓ +pyodide.globals.pluginSettings (via PluginDetails useEffect) + ↓ +Python mcp_server.py (get_user_prompts, get_selected_llm_for_analysis, get_api_key_for_analysis) + ↓ +LLM API Call (via js.llm_call in offscreen.js) +``` + +## 3. Plugin Configuration (manifest.json) + +### LLM Configuration Structure + +The `manifest.json` defines LLM options in the `ai_models` and `options.prompts` sections: + +```json +{ + "ai_models": { + "basic_analysis": "gemini-flash-lite", + "compliance_check": "gemini-flash-lite", + "detailed_comparison": "gemini-pro", + "deep_analysis": "gemini-pro", + "scraping_fallback": "gemini-flash-lite" + }, + "options": { + "prompts": { + "basic_analysis": { + "ru": { + "type": "text", + "default": "[Russian prompt content]", + "label": "Базовый промпт (русский)", + "LLM": { + "default": { + "curl_file": "curl-templates/basic_analysis-ru.curl" + } + } + }, + "en": { + "type": "text", + "default": "[English prompt content]", + "label": "Basic Prompt (English)", + "LLM": { + "default": { + "curl_file": "curl-templates/basic_analysis-en.curl" + } + } + } + }, + "deep_analysis": { + "ru": { /* similar structure */ }, + "en": { /* similar structure */ } + } + } + } +} +``` + +### Manifest Parsing + +- **Location**: `PluginDetails.tsx` (lines 65-103) +- **Functions**: + - `getDefaultLLMForPrompt()`: Maps prompt types to default LLM models + - `hasDefaultLLMForPrompt()`: Checks LLM availability for specific prompts + - `getOriginalPrompt()`: Retrieves default prompts from manifest + +## 4. Settings Storage + +### Chrome Storage Architecture + +#### Primary Storage: chrome.storage.local +- **Key**: `'plugin-ozon-analyzer-settings'` +- **Content**: Complete PluginSettings object with encrypted API keys +- **Encryption**: API keys encrypted using `APIKeyManager` before storage + +#### API Key Encryption +- **Location**: `pages/options/src/utils/encryption.ts` +- **Method**: AES encryption with derived keys +- **Key IDs**: Follow pattern `ozon-analyzer.{promptType}.{language}.default` + +#### Storage Flow +``` +User Input → APIKeyManager.saveEncryptedKey() → chrome.storage.local + ↓ +Retrieval: chrome.storage.local → APIKeyManager.getDecryptedKey() → Component State +``` + +### Pyodide Globals Integration + +Settings are propagated to Python execution environment: + +```javascript +// In PluginDetails.tsx useEffect (lines 362-398) +const updatedPluginSettings = { + ...pluginSettings, + selected_llms: { + basic_analysis: { ru: llm, en: llm }, + deep_analysis: { ru: llm, en: llm } + }, + api_keys: { /* encrypted keys */ } +}; + +pyodide.globals.pluginSettings = updatedPluginSettings; +``` + +## 5. Python Integration (mcp_server.py) + +### Settings Retrieval Functions + +#### get_user_prompts() +- **Location**: `mcp_server.py` (lines 214-407) +- **Purpose**: Loads prompts from plugin settings or manifest defaults +- **Priority Order**: + 1. Custom prompts from `pluginSettings.prompts` + 2. Default prompts from `manifest.options.prompts` + 3. Built-in fallback prompts + +#### get_selected_llm_for_analysis() +- **Location**: `mcp_server.py` (lines 5076-5113) +- **Purpose**: Retrieves selected LLM for specific analysis type and language +- **Returns**: LLM identifier ('default', 'gemini-flash-lite', etc.) + +#### get_api_key_for_analysis() +- **Location**: `mcp_server.py` (lines 5119-5184) +- **Purpose**: Retrieves API key for specific analysis configuration +- **Key Variants Checked**: + - `ozon-analyzer.{analysis_type}.{language}` + - `{analysis_type}_{language}` + - Selected LLM name + - Fallback to global settings + +### LLM Execution Flow + +1. **Analysis Initiation** → `analyze_ozon_product()` function +2. **Settings Loading** → `get_user_prompts()`, `get_selected_llm_for_analysis()` +3. **API Key Retrieval** → `get_api_key_for_analysis()` +4. **LLM Call** → `js.llm_call()` with model alias and options +5. **Response Processing** → Parse and return analysis results + +## 6. API Key Management + +### Default LLM Keys +- **Storage**: Encrypted in `chrome.storage.local` +- **Key ID Pattern**: `ozon-analyzer.{promptType}.{language}.default` +- **Encryption**: AES via `APIKeyManager` +- **Access**: Direct from encrypted storage + +### Platform LLM Keys +- **Storage**: Managed at platform level in `globalAIKeys` +- **Access**: Via background script message passing +- **Message Type**: `'GET_API_KEY'` +- **Response**: `{ apiKey: string }` + +### API Key Flow in offscreen.js + +#### For Default LLMs +```javascript +// Direct encrypted storage access +const apiKey = await APIKeyManager.getDecryptedKey(keyId); +``` + +#### For Platform LLMs +```javascript +// Message passing to background +const response = await safeSendMessage({ + type: 'GET_API_KEY', + data: { keyId: keyId } +}); +``` + +### Enhanced LLM Call (offscreen.js lines 2262-2413) + +The `js.llm_call` function in offscreen.js provides enhanced API key management: + +1. **Model Alias Processing**: Strips `:generateContent` suffix +2. **API Key Priority**: + - Direct API key in options + - Plugin settings API keys + - Background script retrieval + - Window global fallback +3. **Gemini API Integration**: Direct HTTP calls for Default LLMs +4. **Background Delegation**: Platform LLMs routed through background script + +## 7. Execution Flow + +### Complete Path from User Selection to LLM API Call + +``` +1. User selects LLM in PluginDetails.tsx LLMSelector + ↓ +2. LLMSelector.handleLLMChange() updates settings + ↓ +3. usePluginSettings saves to chrome.storage.local (encrypted) + ↓ +4. PluginDetails useEffect propagates to pyodide.globals.pluginSettings + ↓ +5. Python execution: analyze_ozon_product() called + ↓ +6. get_selected_llm_for_analysis() retrieves LLM choice + ↓ +7. get_api_key_for_analysis() retrieves API key + ↓ +8. js.llm_call() invoked with model and options + ↓ +9. offscreen.js enhanced llm_call processes request + ↓ +10. For Default LLMs: Direct Gemini API HTTP call + For Platform LLMs: Background script delegation + ↓ +11. Response returned through Pyodide bridge + ↓ +12. Analysis results processed and displayed +``` + +### Key Integration Points + +#### JavaScript ↔ Python Bridge +- **Location**: `offscreen.js` jsBridge object (lines 533-1111) +- **Functions**: + - `sendMessageToChat()`: Sends messages to chat interface + - `llm_call()`: Enhanced LLM API integration + - `get_setting()`: Settings retrieval + - `updateContext()`: Context management + +#### Settings Propagation +- **Trigger**: PluginDetails component mount and settings changes +- **Method**: Direct assignment to `pyodide.globals.pluginSettings` +- **Content**: Complete settings object with selected LLMs and API keys + +#### Error Handling +- **API Key Errors**: Graceful fallback with user-friendly messages +- **Network Errors**: Timeout handling and retry logic +- **Configuration Errors**: Validation and default fallbacks + +## 8. Architecture Patterns + +### Separation of Concerns +- **UI Layer**: React components handle user interaction +- **Settings Layer**: usePluginSettings manages persistence +- **Bridge Layer**: offscreen.js provides JS↔Python communication +- **Execution Layer**: Python handles LLM calls and analysis + +### Security Considerations +- **API Key Encryption**: All keys encrypted at rest +- **Isolated Execution**: Python runs in Pyodide sandbox +- **Message Validation**: All cross-context communication validated + +### Performance Optimizations +- **Lazy Loading**: Settings loaded on demand +- **Caching**: API key caching with TTL +- **Chunking**: Large HTML data transferred in chunks +- **Async Processing**: Non-blocking UI updates + +This architecture provides a robust, scalable system for LLM configuration and execution while maintaining security, performance, and user experience standards. \ No newline at end of file diff --git a/memory-bank/architecture/manifest-data-flow.md b/memory-bank/architecture/manifest-data-flow.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/platform-core-analysis.md b/memory-bank/architecture/platform-core-analysis.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/plugin-system-integration.md b/memory-bank/architecture/plugin-system-integration.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/security-architecture-update.md b/memory-bank/architecture/security-architecture-update.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/security-architecture.md b/memory-bank/architecture/security-architecture.md old mode 100644 new mode 100755 diff --git a/memory-bank/architecture/systemPatterns.md b/memory-bank/architecture/systemPatterns.md old mode 100644 new mode 100755 diff --git a/memory-bank/audit_logs.md b/memory-bank/audit_logs.md old mode 100644 new mode 100755 diff --git a/memory-bank/chrome-extension-manual-restore.md b/memory-bank/chrome-extension-manual-restore.md old mode 100644 new mode 100755 diff --git a/memory-bank/context/ENVIRONMENT.md b/memory-bank/context/ENVIRONMENT.md old mode 100644 new mode 100755 diff --git a/memory-bank/context/README.md b/memory-bank/context/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/context/excluded-directories.md b/memory-bank/context/excluded-directories.md old mode 100644 new mode 100755 diff --git a/memory-bank/context/productContext.md b/memory-bank/context/productContext.md old mode 100644 new mode 100755 diff --git a/memory-bank/context/techContext.md b/memory-bank/context/techContext.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/README.md b/memory-bank/core/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/activeContext.md b/memory-bank/core/activeContext.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/api-keys-security-implementation.md b/memory-bank/core/api-keys-security-implementation.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/backup/activeContext-2025-07-19T02-38-14-572Z.md b/memory-bank/core/backup/activeContext-2025-07-19T02-38-14-572Z.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/backup/activeContext-2025-07-19T02-38-25-140Z.md b/memory-bank/core/backup/activeContext-2025-07-19T02-38-25-140Z.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/backup/progress-2025-07-19T02-38-14-572Z.md b/memory-bank/core/backup/progress-2025-07-19T02-38-14-572Z.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/backup/progress-2025-07-19T02-38-25-140Z.md b/memory-bank/core/backup/progress-2025-07-19T02-38-25-140Z.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/plugin-adaptations.md b/memory-bank/core/plugin-adaptations.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/progress.md b/memory-bank/core/progress.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/projectbrief.md b/memory-bank/core/projectbrief.md old mode 100644 new mode 100755 diff --git a/memory-bank/core/session-log.md b/memory-bank/core/session-log.md old mode 100644 new mode 100755 diff --git a/memory-bank/cursor-saved-memories.md b/memory-bank/cursor-saved-memories.md old mode 100644 new mode 100755 diff --git a/memory-bank/cursor-user-rules-simple.md b/memory-bank/cursor-user-rules-simple.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/AI-access-policy.md b/memory-bank/deprecated/AI-access-policy.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/ai-barrel-exports-best-practices.md b/memory-bank/deprecated/ai-barrel-exports-best-practices.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/ai-user-commands-reference.md b/memory-bank/deprecated/ai-user-commands-reference.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/cursor-project-rules.md b/memory-bank/deprecated/cursor-project-rules.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/cursor-saved-memories-analysis.md b/memory-bank/deprecated/cursor-saved-memories-analysis.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/cursor-saved-memories.md b/memory-bank/deprecated/cursor-saved-memories.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/cursor-update-mechanism.md b/memory-bank/deprecated/cursor-update-mechanism.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/cursor-user-rules.md b/memory-bank/deprecated/cursor-user-rules.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/mdc-file-standards.md b/memory-bank/deprecated/mdc-file-standards.md old mode 100644 new mode 100755 diff --git a/memory-bank/deprecated/user-commands.md b/memory-bank/deprecated/user-commands.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/README.md b/memory-bank/development/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/barrel-exports-best-practices.md b/memory-bank/development/barrel-exports-best-practices.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/build-sources-mapping.md b/memory-bank/development/build-sources-mapping.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/debug-testing-guide.md b/memory-bank/development/debug-testing-guide.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/devtools-testing-guide.md b/memory-bank/development/devtools-testing-guide.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/ozon-analyzer-testing.md b/memory-bank/development/ozon-analyzer-testing.md old mode 100644 new mode 100755 index 91a0ee7e..21b80b91 --- a/memory-bank/development/ozon-analyzer-testing.md +++ b/memory-bank/development/ozon-analyzer-testing.md @@ -341,7 +341,7 @@ Comprehensive testing results and development insights gathered during the adapt ✅ GPT-3.5-turbo: Deprecated (maintenance mode) 🌟 **Google Gemini Models** - ✅ Gemini Flash: Available (speed optimized) + ✅ Gemini Flash: Available (speed basic_analysis) ✅ Gemini Pro: Available (balanced performance) ✅ Gemini Ultra: Expensive (limited use) @@ -442,7 +442,7 @@ Comprehensive testing results and development insights gathered during the adapt | Category | Target | Achieved | Status | |----------|--------|----------|--------| | **Functionality** | 100% | 100% | ✅ **COMPLETE** | -| **Performance** | <30s | 24.7s | ✅ **OPTIMIZED** | +| **Performance** | <30s | 24.7s | ✅ **basic_analysis** | | **Reliability** | 99%+ | 99.5% | ✅ **STABLE** | | **Security** | Zero Risk | Zero Risk | ✅ **SECURE** | | **Usability** | 90%+ | 92% | ✅ **EXCELLENT** | diff --git a/memory-bank/development/testing-results.md b/memory-bank/development/testing-results.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/user-commands.md b/memory-bank/development/user-commands.md old mode 100644 new mode 100755 diff --git a/memory-bank/development/version-management.md b/memory-bank/development/version-management.md old mode 100644 new mode 100755 diff --git a/memory-bank/diagrams/graph.mmd b/memory-bank/diagrams/graph.mmd old mode 100644 new mode 100755 diff --git a/memory-bank/drift.md b/memory-bank/drift.md old mode 100644 new mode 100755 diff --git a/memory-bank/errors/ERRORS_AND_SOLUTIONS.md b/memory-bank/errors/ERRORS_AND_SOLUTIONS.md old mode 100644 new mode 100755 diff --git a/memory-bank/errors/README.md b/memory-bank/errors/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/errors/error-graveyard.md b/memory-bank/errors/error-graveyard.md old mode 100644 new mode 100755 diff --git a/memory-bank/errors/errors.md b/memory-bank/errors/errors.md old mode 100644 new mode 100755 diff --git a/memory-bank/errors/typescript-build-troubleshooting-experience.md b/memory-bank/errors/typescript-build-troubleshooting-experience.md old mode 100644 new mode 100755 diff --git a/memory-bank/errors/vite-react19-errors.md b/memory-bank/errors/vite-react19-errors.md old mode 100644 new mode 100755 diff --git a/memory-bank/planning/README.md b/memory-bank/planning/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/planning/documentation-optimization-plan.md b/memory-bank/planning/documentation-optimization-plan.md old mode 100644 new mode 100755 diff --git a/memory-bank/planning/documentation-optimization-summary.md b/memory-bank/planning/documentation-optimization-summary.md old mode 100644 new mode 100755 diff --git a/memory-bank/planning/future-plans.md b/memory-bank/planning/future-plans.md old mode 100644 new mode 100755 diff --git a/memory-bank/plans/README.md b/memory-bank/plans/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/projects/chrome-extension-chat-recovery/README.md b/memory-bank/projects/chrome-extension-chat-recovery/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/projects/chrome-extension-chat-recovery/architecture/chat-architecture.md b/memory-bank/projects/chrome-extension-chat-recovery/architecture/chat-architecture.md old mode 100644 new mode 100755 diff --git a/memory-bank/projects/chrome-extension-chat-recovery/docs/code-changes-summary.md b/memory-bank/projects/chrome-extension-chat-recovery/docs/code-changes-summary.md old mode 100644 new mode 100755 diff --git a/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md b/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md old mode 100644 new mode 100755 index 889d25e5..b7de418b --- a/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md +++ b/memory-bank/projects/chrome-extension-chat-recovery/docs/lessons-learned.md @@ -394,7 +394,7 @@ class MessagingMonitor { |--------|------------------|---------------| | **Architecture** | Mixed, inconsistent | Clean, unified | | **Testing** | False confidence | Real reliability | -| **Performance** | Memory leaks | Optimized usage | +| **Performance** | Memory leaks | basic_analysis usage | | **Maintainability** | Hard to debug | Easy to understand | | **User Experience** | Unstable chat | Reliable messaging | diff --git a/memory-bank/projects/chrome-extension-chat-recovery/docs/problems-solved.md b/memory-bank/projects/chrome-extension-chat-recovery/docs/problems-solved.md old mode 100644 new mode 100755 diff --git a/memory-bank/projects/chrome-extension-chat-recovery/docs/project-overview.md b/memory-bank/projects/chrome-extension-chat-recovery/docs/project-overview.md old mode 100644 new mode 100755 diff --git a/memory-bank/projects/chrome-extension-chat-recovery/testing/testing-results.md b/memory-bank/projects/chrome-extension-chat-recovery/testing/testing-results.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/README.md b/memory-bank/ui/README.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/chat-context-fix.md b/memory-bank/ui/chat-context-fix.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/chat-messages-interface.md b/memory-bank/ui/chat-messages-interface.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/chat-text-alignment-settings.md b/memory-bank/ui/chat-text-alignment-settings.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/html-transmission-settings.md b/memory-bank/ui/html-transmission-settings.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/lazy-chat-sync.md b/memory-bank/ui/lazy-chat-sync.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/options-scrolling-fix.md b/memory-bank/ui/options-scrolling-fix.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/ozon-analyzer-ui-integration.md b/memory-bank/ui/ozon-analyzer-ui-integration.md old mode 100644 new mode 100755 index 3fa0333f..3e3e108b --- a/memory-bank/ui/ozon-analyzer-ui-integration.md +++ b/memory-bank/ui/ozon-analyzer-ui-integration.md @@ -338,7 +338,7 @@ const getFeatureFlags = (capabilities: UICapabilities) => ({ #### **Mobile Optimization** ```typescript -// Touch-optimized interactions +// Touch-basic_analysis interactions const mobileInteractions = { tapToExpand: true, // Expand sections on tap swipeToNavigate: true, // Swipe between results @@ -792,7 +792,7 @@ The Ozon Analyzer UI integration establishes comprehensive patterns for plugin u 3. **Accessible Design**: WCAG 2.1 AA compliance with screen reader support 4. **Responsive Layout**: Mobile-first design with adaptive breakpoints 5. **Error Resilience**: User-friendly error recovery and fallback patterns -6. **Performance Optimized**: Virtual scrolling, lazy loading, and efficient rendering +6. **Performance basic_analysis**: Virtual scrolling, lazy loading, and efficient rendering 7. **Consistent Branding**: Cohesive design language and component library These patterns provide a foundation for future plugin UIs and ensure excellent user experiences across devices and accessibility needs. diff --git a/memory-bank/ui/plugin-control-panel-buttons.md b/memory-bank/ui/plugin-control-panel-buttons.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/plugin-settings-transmission.md b/memory-bank/ui/plugin-settings-transmission.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/side-panel-improvements.md b/memory-bank/ui/side-panel-improvements.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/theme-switcher-component.md b/memory-bank/ui/theme-switcher-component.md old mode 100644 new mode 100755 diff --git a/memory-bank/ui/theme-switching-settings.md b/memory-bank/ui/theme-switching-settings.md old mode 100644 new mode 100755 diff --git a/messaging-example/background.js b/messaging-example/background.js old mode 100644 new mode 100755 diff --git a/messaging-example/eslint.config.js b/messaging-example/eslint.config.js old mode 100644 new mode 100755 diff --git a/messaging-example/manifest.json b/messaging-example/manifest.json old mode 100644 new mode 100755 diff --git a/messaging-example/side-panel.html b/messaging-example/side-panel.html old mode 100644 new mode 100755 diff --git a/messaging-example/side-panel.js b/messaging-example/side-panel.js old mode 100644 new mode 100755 diff --git a/nohup.out b/nohup.out old mode 100644 new mode 100755 diff --git a/package.json b/package.json old mode 100644 new mode 100755 index d9fd2d45..5379a2bd --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-plugins-platform", - "version": "1.0.1345", + "version": "1.0.1494", "description": "Browser extension that enables Python plugin execution using Pyodide and MCP protocol", "license": "MIT", "private": true, @@ -45,6 +45,7 @@ "webextension-polyfill": "^0.12.0" }, "devDependencies": { + "@types/chrome": "^0.1.24", "@types/node": "^24.3.0", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md old mode 100644 new mode 100755 diff --git a/packages/dev-utils/index.mts b/packages/dev-utils/index.mts old mode 100644 new mode 100755 diff --git a/packages/dev-utils/lib/index.ts b/packages/dev-utils/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/dev-utils/lib/manifest-parser/impl.ts b/packages/dev-utils/lib/manifest-parser/impl.ts old mode 100644 new mode 100755 diff --git a/packages/dev-utils/lib/manifest-parser/index.ts b/packages/dev-utils/lib/manifest-parser/index.ts old mode 100644 new mode 100755 diff --git a/packages/dev-utils/lib/manifest-parser/types.ts b/packages/dev-utils/lib/manifest-parser/types.ts old mode 100644 new mode 100755 diff --git a/packages/dev-utils/lib/stream-file-to-zip.ts b/packages/dev-utils/lib/stream-file-to-zip.ts old mode 100644 new mode 100755 diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json old mode 100644 new mode 100755 index 4fa18f53..91090c56 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@extension/dev-utils", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - dev utils", "type": "module", "private": true, diff --git a/packages/dev-utils/tsconfig.json b/packages/dev-utils/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/env/README.md b/packages/env/README.md old mode 100644 new mode 100755 diff --git a/packages/env/index.mts b/packages/env/index.mts old mode 100644 new mode 100755 diff --git a/packages/env/lib/config.ts b/packages/env/lib/config.ts old mode 100644 new mode 100755 diff --git a/packages/env/lib/const.ts b/packages/env/lib/const.ts old mode 100644 new mode 100755 diff --git a/packages/env/lib/index.ts b/packages/env/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/env/lib/types.ts b/packages/env/lib/types.ts old mode 100644 new mode 100755 diff --git a/packages/env/package.json b/packages/env/package.json old mode 100644 new mode 100755 index 0169ec00..0a68217d --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@extension/env", - "version": "0.5.1350", + "version": "0.5.1499", "description": "chrome extension - environment variables", "type": "module", "private": true, diff --git a/packages/env/tsconfig.json b/packages/env/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/env/use-env-example.png b/packages/env/use-env-example.png old mode 100644 new mode 100755 diff --git a/packages/hmr/index.mts b/packages/hmr/index.mts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/consts.ts b/packages/hmr/lib/consts.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/initializers/init-client.ts b/packages/hmr/lib/initializers/init-client.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/initializers/init-reload-server.ts b/packages/hmr/lib/initializers/init-reload-server.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/injections/refresh.ts b/packages/hmr/lib/injections/refresh.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/injections/reload.ts b/packages/hmr/lib/injections/reload.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/interpreter/index.ts b/packages/hmr/lib/interpreter/index.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/plugins/index.ts b/packages/hmr/lib/plugins/index.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/plugins/make-entry-point-plugin.ts b/packages/hmr/lib/plugins/make-entry-point-plugin.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/plugins/watch-public-plugin.ts b/packages/hmr/lib/plugins/watch-public-plugin.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/plugins/watch-rebuild-plugin.ts b/packages/hmr/lib/plugins/watch-rebuild-plugin.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/lib/types.ts b/packages/hmr/lib/types.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/package.json b/packages/hmr/package.json old mode 100644 new mode 100755 index acf2a839..58c7e02c --- a/packages/hmr/package.json +++ b/packages/hmr/package.json @@ -1,6 +1,6 @@ { "name": "@extension/hmr", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - hot module reload/refresh", "type": "module", "private": true, @@ -29,6 +29,7 @@ "@types/ws": "^8.18.1", "esbuild": "^0.25.4", "esm": "^3.2.25", + "fast-glob": "^3.3.3", "rollup": "^4.45.1", "ts-node": "^10.9.2", "ws": "^8.18.2" diff --git a/packages/hmr/rollup.config.ts b/packages/hmr/rollup.config.ts old mode 100644 new mode 100755 diff --git a/packages/hmr/tsconfig.json b/packages/hmr/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/i18n/.gitignore b/packages/i18n/.gitignore old mode 100644 new mode 100755 diff --git a/packages/i18n/README.md b/packages/i18n/README.md old mode 100644 new mode 100755 diff --git a/packages/i18n/index.mts b/packages/i18n/index.mts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/consts.ts b/packages/i18n/lib/consts.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/i18n-dev.ts b/packages/i18n/lib/i18n-dev.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/i18n-prod.ts b/packages/i18n/lib/i18n-prod.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/index.ts b/packages/i18n/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/prepare-build.ts b/packages/i18n/lib/prepare-build.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/set-related-locale-import.ts b/packages/i18n/lib/set-related-locale-import.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/lib/types.ts b/packages/i18n/lib/types.ts old mode 100644 new mode 100755 diff --git a/packages/i18n/locales/en/messages.json b/packages/i18n/locales/en/messages.json old mode 100644 new mode 100755 diff --git a/packages/i18n/locales/ko/messages.json b/packages/i18n/locales/ko/messages.json old mode 100644 new mode 100755 diff --git a/packages/i18n/package.json b/packages/i18n/package.json old mode 100644 new mode 100755 index 4942adad..99b1d9cc --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@extension/i18n", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - internationalization", "type": "module", "private": true, diff --git a/packages/i18n/prepare-build.tsconfig.json b/packages/i18n/prepare-build.tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/i18n/tsconfig.json b/packages/i18n/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/module-manager/README.md b/packages/module-manager/README.md old mode 100644 new mode 100755 diff --git a/packages/module-manager/index.mts b/packages/module-manager/index.mts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/base/cli-args-processor.ts b/packages/module-manager/lib/base/cli-args-processor.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/base/index.ts b/packages/module-manager/lib/base/index.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/base/run-module-manager.ts b/packages/module-manager/lib/base/run-module-manager.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/const.ts b/packages/module-manager/lib/const.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/helpers/index.ts b/packages/module-manager/lib/helpers/index.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/helpers/utils.ts b/packages/module-manager/lib/helpers/utils.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/helpers/zip-utils.ts b/packages/module-manager/lib/helpers/zip-utils.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/index.ts b/packages/module-manager/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/paths.ts b/packages/module-manager/lib/paths.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/processing/delete-feature.ts b/packages/module-manager/lib/processing/delete-feature.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/processing/index.ts b/packages/module-manager/lib/processing/index.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/processing/modules-handler.ts b/packages/module-manager/lib/processing/modules-handler.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/processing/recover-feature.ts b/packages/module-manager/lib/processing/recover-feature.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/lib/types.ts b/packages/module-manager/lib/types.ts old mode 100644 new mode 100755 diff --git a/packages/module-manager/package.json b/packages/module-manager/package.json old mode 100644 new mode 100755 index 872cf090..c61d4e6a --- a/packages/module-manager/package.json +++ b/packages/module-manager/package.json @@ -1,6 +1,6 @@ { "name": "@extension/module-manager", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - module manager", "type": "module", "private": true, diff --git a/packages/module-manager/tsconfig.json b/packages/module-manager/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/shared/README.md b/packages/shared/README.md old mode 100644 new mode 100755 diff --git a/packages/shared/const.ts b/packages/shared/const.ts old mode 100644 new mode 100755 diff --git a/packages/shared/index.mts b/packages/shared/index.mts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/hoc/index.ts b/packages/shared/lib/hoc/index.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/hoc/with-error-boundary.tsx b/packages/shared/lib/hoc/with-error-boundary.tsx old mode 100644 new mode 100755 diff --git a/packages/shared/lib/hoc/with-suspense.tsx b/packages/shared/lib/hoc/with-suspense.tsx old mode 100644 new mode 100755 diff --git a/packages/shared/lib/hooks/index.ts b/packages/shared/lib/hooks/index.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/hooks/use-storage.ts b/packages/shared/lib/hooks/use-storage.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/hooks/use-storage.tsx b/packages/shared/lib/hooks/use-storage.tsx old mode 100644 new mode 100755 diff --git a/packages/shared/lib/utils/colorful-logger.ts b/packages/shared/lib/utils/colorful-logger.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/utils/const.ts b/packages/shared/lib/utils/const.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/utils/helpers.ts b/packages/shared/lib/utils/helpers.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/utils/index.ts b/packages/shared/lib/utils/index.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/utils/init-app-with-shadow.ts b/packages/shared/lib/utils/init-app-with-shadow.ts old mode 100644 new mode 100755 diff --git a/packages/shared/lib/utils/types.ts b/packages/shared/lib/utils/types.ts old mode 100644 new mode 100755 diff --git a/packages/shared/package.json b/packages/shared/package.json old mode 100644 new mode 100755 index 3e891945..cfb30547 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@extension/shared", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - shared code", "type": "module", "private": true, diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/storage/index.mts b/packages/storage/index.mts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/base/base.ts b/packages/storage/lib/base/base.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/base/enums.ts b/packages/storage/lib/base/enums.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/base/index.ts b/packages/storage/lib/base/index.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/base/types.ts b/packages/storage/lib/base/types.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/impl/example-chat-alignment-storage.ts b/packages/storage/lib/impl/example-chat-alignment-storage.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/impl/example-theme-storage.ts b/packages/storage/lib/impl/example-theme-storage.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/impl/index.ts b/packages/storage/lib/impl/index.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/index.ts b/packages/storage/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/plugin-settings.ts b/packages/storage/lib/plugin-settings.ts old mode 100644 new mode 100755 diff --git a/packages/storage/lib/types.ts b/packages/storage/lib/types.ts old mode 100644 new mode 100755 diff --git a/packages/storage/package.json b/packages/storage/package.json old mode 100644 new mode 100755 index 6f1f2ea4..62189ba9 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@extension/storage", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - storage", "type": "module", "private": true, diff --git a/packages/storage/tsconfig.json b/packages/storage/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/tailwindcss-config/package.json b/packages/tailwindcss-config/package.json old mode 100644 new mode 100755 index ecc00f0b..992554c2 --- a/packages/tailwindcss-config/package.json +++ b/packages/tailwindcss-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tailwindcss-config", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - tailwindcss configuration", "main": "tailwind.config.ts", "private": true, diff --git a/packages/tailwindcss-config/tailwind.config.ts b/packages/tailwindcss-config/tailwind.config.ts old mode 100644 new mode 100755 diff --git a/packages/tailwindcss-config/tsconfig.json b/packages/tailwindcss-config/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/tsconfig/app.json b/packages/tsconfig/app.json old mode 100644 new mode 100755 diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json old mode 100644 new mode 100755 diff --git a/packages/tsconfig/module.json b/packages/tsconfig/module.json old mode 100644 new mode 100755 diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json old mode 100644 new mode 100755 index 90736c69..c33ad049 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,6 +1,6 @@ { "name": "@extension/tsconfig", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - tsconfig", "private": true, "sideEffects": false diff --git a/packages/ui/README.md b/packages/ui/README.md old mode 100644 new mode 100755 diff --git a/packages/ui/global.css b/packages/ui/global.css old mode 100644 new mode 100755 diff --git a/packages/ui/index.ts b/packages/ui/index.ts old mode 100644 new mode 100755 diff --git a/packages/ui/lib/assets/warning.svg b/packages/ui/lib/assets/warning.svg old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/LoadingSpinner.tsx b/packages/ui/lib/components/LoadingSpinner.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/PluginCard.css b/packages/ui/lib/components/PluginCard.css old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/PluginCard.tsx b/packages/ui/lib/components/PluginCard.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/TestReact19Pure.tsx b/packages/ui/lib/components/TestReact19Pure.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/ToggleButton.tsx b/packages/ui/lib/components/ToggleButton.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/error-display/ErrorDisplay.tsx b/packages/ui/lib/components/error-display/ErrorDisplay.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/error-display/ErrorHeader.tsx b/packages/ui/lib/components/error-display/ErrorHeader.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/error-display/ErrorResetButton.tsx b/packages/ui/lib/components/error-display/ErrorResetButton.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/error-display/ErrorStackTraceList.tsx b/packages/ui/lib/components/error-display/ErrorStackTraceList.tsx old mode 100644 new mode 100755 diff --git a/packages/ui/lib/components/index.ts b/packages/ui/lib/components/index.ts old mode 100644 new mode 100755 diff --git a/packages/ui/lib/index.ts b/packages/ui/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/ui/lib/utils.ts b/packages/ui/lib/utils.ts old mode 100644 new mode 100755 diff --git a/packages/ui/lib/with-ui.ts b/packages/ui/lib/with-ui.ts old mode 100644 new mode 100755 diff --git a/packages/ui/package.json b/packages/ui/package.json old mode 100644 new mode 100755 index 55f7dffe..85699a91 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/ui", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - ui components", "type": "module", "private": true, @@ -31,6 +31,7 @@ "devDependencies": { "@extension/tailwindcss-config": "workspace:*", "@extension/tsconfig": "workspace:*", + "deepmerge": "^4.3.1", "react-spinners": "^0.17.0", "tsc-alias": "^1.8.16", "tailwindcss": "^3.4.10" diff --git a/packages/ui/tailwind-global.config.ts b/packages/ui/tailwind-global.config.ts old mode 100644 new mode 100755 diff --git a/packages/ui/tailwind.config.ts b/packages/ui/tailwind.config.ts old mode 100644 new mode 100755 diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/ui/types/tailwindcss-config.d.ts b/packages/ui/types/tailwindcss-config.d.ts old mode 100644 new mode 100755 diff --git a/packages/vite-config/index.mts b/packages/vite-config/index.mts old mode 100644 new mode 100755 diff --git a/packages/vite-config/lib/build-content-script.ts b/packages/vite-config/lib/build-content-script.ts old mode 100644 new mode 100755 diff --git a/packages/vite-config/lib/get-content-script-entires.ts b/packages/vite-config/lib/get-content-script-entires.ts old mode 100644 new mode 100755 diff --git a/packages/vite-config/lib/index.ts b/packages/vite-config/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/vite-config/lib/with-page-config.ts b/packages/vite-config/lib/with-page-config.ts old mode 100644 new mode 100755 diff --git a/packages/vite-config/package.json b/packages/vite-config/package.json old mode 100644 new mode 100755 index 18111b4c..c5eec8bf --- a/packages/vite-config/package.json +++ b/packages/vite-config/package.json @@ -1,6 +1,6 @@ { "name": "@extension/vite-config", - "version": "0.5.1371", + "version": "0.5.1520", "description": "chrome extension - vite base configuration", "type": "module", "private": true, diff --git a/packages/vite-config/tailwind.d.ts b/packages/vite-config/tailwind.d.ts old mode 100644 new mode 100755 diff --git a/packages/vite-config/tsconfig.json b/packages/vite-config/tsconfig.json old mode 100644 new mode 100755 diff --git a/packages/vite-config/types/vite-plugin-node-polyfills.d.ts b/packages/vite-config/types/vite-plugin-node-polyfills.d.ts old mode 100644 new mode 100755 diff --git a/packages/zipper/index.mts b/packages/zipper/index.mts old mode 100644 new mode 100755 diff --git a/packages/zipper/lib/index.ts b/packages/zipper/lib/index.ts old mode 100644 new mode 100755 diff --git a/packages/zipper/lib/zip-bundle.ts b/packages/zipper/lib/zip-bundle.ts old mode 100644 new mode 100755 index e5704a71..8dce9035 --- a/packages/zipper/lib/zip-bundle.ts +++ b/packages/zipper/lib/zip-bundle.ts @@ -48,7 +48,7 @@ export const zipBundle = async ( let aborted = false; let totalSize = 0; const timer = Date.now(); - const zip = new Zip((err, data, final) => { + const zip = new Zip((err: Error | null, data: Uint8Array, final: boolean) => { if (err) { pReject(err); } else { diff --git a/packages/zipper/package.json b/packages/zipper/package.json old mode 100644 new mode 100755 index 8f1906eb..01664a43 --- a/packages/zipper/package.json +++ b/packages/zipper/package.json @@ -1,6 +1,6 @@ { "name": "@extension/zipper", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - zipper", "type": "module", "private": true, @@ -22,6 +22,10 @@ "format": "prettier . --write --ignore-path ../../.prettierignore", "type-check": "tsc --noEmit" }, + "dependencies": { + "fast-glob": "^3.3.2", + "fflate": "^0.8.2" + }, "devDependencies": { "@extension/tsconfig": "workspace:*", "@extension/dev-utils": "workspace:*", diff --git a/packages/zipper/tsconfig.json b/packages/zipper/tsconfig.json old mode 100644 new mode 100755 diff --git a/pages/content-runtime/README.md b/pages/content-runtime/README.md old mode 100644 new mode 100755 diff --git a/pages/content-runtime/build.mts b/pages/content-runtime/build.mts old mode 100644 new mode 100755 diff --git a/pages/content-runtime/package.json b/pages/content-runtime/package.json old mode 100644 new mode 100755 index c00ac1d9..b59df2b9 --- a/pages/content-runtime/package.json +++ b/pages/content-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-runtime-script", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - content runtime script", "type": "module", "private": true, diff --git a/pages/content-runtime/src/matches/all/App.tsx b/pages/content-runtime/src/matches/all/App.tsx old mode 100644 new mode 100755 diff --git a/pages/content-runtime/src/matches/all/index.css b/pages/content-runtime/src/matches/all/index.css old mode 100644 new mode 100755 diff --git a/pages/content-runtime/src/matches/all/index.tsx b/pages/content-runtime/src/matches/all/index.tsx old mode 100644 new mode 100755 diff --git a/pages/content-runtime/src/matches/example/App.tsx b/pages/content-runtime/src/matches/example/App.tsx old mode 100644 new mode 100755 diff --git a/pages/content-runtime/src/matches/example/index.css b/pages/content-runtime/src/matches/example/index.css old mode 100644 new mode 100755 diff --git a/pages/content-runtime/src/matches/example/index.tsx b/pages/content-runtime/src/matches/example/index.tsx old mode 100644 new mode 100755 diff --git a/pages/content-runtime/tailwind.config.ts b/pages/content-runtime/tailwind.config.ts old mode 100644 new mode 100755 diff --git a/pages/content-runtime/tailwind.d.ts b/pages/content-runtime/tailwind.d.ts old mode 100644 new mode 100755 diff --git a/pages/content-runtime/tsconfig.json b/pages/content-runtime/tsconfig.json old mode 100644 new mode 100755 diff --git a/pages/content-runtime/vite.config.mts b/pages/content-runtime/vite.config.mts old mode 100644 new mode 100755 diff --git a/pages/content-ui/README.md b/pages/content-ui/README.md old mode 100644 new mode 100755 diff --git a/pages/content-ui/build.mts b/pages/content-ui/build.mts old mode 100644 new mode 100755 diff --git a/pages/content-ui/package.json b/pages/content-ui/package.json old mode 100644 new mode 100755 index 29a3430a..a4da970e --- a/pages/content-ui/package.json +++ b/pages/content-ui/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-ui", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - content ui", "type": "module", "private": true, diff --git a/pages/content-ui/postcss.config.cjs b/pages/content-ui/postcss.config.cjs old mode 100644 new mode 100755 diff --git a/pages/content-ui/public/logo.svg b/pages/content-ui/public/logo.svg old mode 100644 new mode 100755 diff --git a/pages/content-ui/src/matches/all/App.tsx b/pages/content-ui/src/matches/all/App.tsx old mode 100644 new mode 100755 diff --git a/pages/content-ui/src/matches/all/index.css b/pages/content-ui/src/matches/all/index.css old mode 100644 new mode 100755 diff --git a/pages/content-ui/src/matches/all/index.tsx b/pages/content-ui/src/matches/all/index.tsx old mode 100644 new mode 100755 diff --git a/pages/content-ui/src/matches/example/App.tsx b/pages/content-ui/src/matches/example/App.tsx old mode 100644 new mode 100755 diff --git a/pages/content-ui/src/matches/example/index.css b/pages/content-ui/src/matches/example/index.css old mode 100644 new mode 100755 diff --git a/pages/content-ui/src/matches/example/index.tsx b/pages/content-ui/src/matches/example/index.tsx old mode 100644 new mode 100755 diff --git a/pages/content-ui/tailwind.config.ts b/pages/content-ui/tailwind.config.ts old mode 100644 new mode 100755 diff --git a/pages/content-ui/tailwind.d.ts b/pages/content-ui/tailwind.d.ts old mode 100644 new mode 100755 diff --git a/pages/content-ui/tsconfig.json b/pages/content-ui/tsconfig.json old mode 100644 new mode 100755 diff --git a/pages/content-ui/vite.config.mts b/pages/content-ui/vite.config.mts old mode 100644 new mode 100755 diff --git a/pages/content/README.md b/pages/content/README.md old mode 100644 new mode 100755 diff --git a/pages/content/build.mts b/pages/content/build.mts old mode 100644 new mode 100755 diff --git a/pages/content/package.json b/pages/content/package.json old mode 100644 new mode 100755 index 91296683..5a3adc86 --- a/pages/content/package.json +++ b/pages/content/package.json @@ -1,6 +1,6 @@ { "name": "@extension/content-script", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - content script", "type": "module", "private": true, diff --git a/pages/content/public/logo.svg b/pages/content/public/logo.svg old mode 100644 new mode 100755 diff --git a/pages/content/src/matches/all/index.ts b/pages/content/src/matches/all/index.ts old mode 100644 new mode 100755 diff --git a/pages/content/src/matches/example/index.ts b/pages/content/src/matches/example/index.ts old mode 100644 new mode 100755 diff --git a/pages/content/src/sample-function.ts b/pages/content/src/sample-function.ts old mode 100644 new mode 100755 diff --git a/pages/content/tsconfig.json b/pages/content/tsconfig.json old mode 100644 new mode 100755 diff --git a/pages/devtools/index.html b/pages/devtools/index.html old mode 100644 new mode 100755 diff --git a/pages/devtools/package.json b/pages/devtools/package.json old mode 100644 new mode 100755 index 5da5b4e3..74a9898a --- a/pages/devtools/package.json +++ b/pages/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@extension/devtools", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - devtools", "type": "module", "private": true, diff --git a/pages/devtools/public/logo.svg b/pages/devtools/public/logo.svg old mode 100644 new mode 100755 diff --git a/pages/devtools/src/index.ts b/pages/devtools/src/index.ts old mode 100644 new mode 100755 diff --git a/pages/devtools/tsconfig.json b/pages/devtools/tsconfig.json old mode 100644 new mode 100755 diff --git a/pages/devtools/vite.config.mts b/pages/devtools/vite.config.mts old mode 100644 new mode 100755 diff --git a/pages/new-tab/index.html b/pages/new-tab/index.html old mode 100644 new mode 100755 diff --git a/pages/new-tab/package.json b/pages/new-tab/package.json old mode 100644 new mode 100755 index b2fd6e70..c5464092 --- a/pages/new-tab/package.json +++ b/pages/new-tab/package.json @@ -1,6 +1,6 @@ { "name": "@extension/new-tab", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - new tab", "type": "module", "private": true, diff --git a/pages/new-tab/postcss.config.cjs b/pages/new-tab/postcss.config.cjs old mode 100644 new mode 100755 diff --git a/pages/new-tab/public/logo_horizontal.svg b/pages/new-tab/public/logo_horizontal.svg old mode 100644 new mode 100755 diff --git a/pages/new-tab/public/logo_horizontal_dark.svg b/pages/new-tab/public/logo_horizontal_dark.svg old mode 100644 new mode 100755 diff --git a/pages/new-tab/src/NewTab.css b/pages/new-tab/src/NewTab.css old mode 100644 new mode 100755 diff --git a/pages/new-tab/src/NewTab.scss b/pages/new-tab/src/NewTab.scss old mode 100644 new mode 100755 diff --git a/pages/new-tab/src/NewTab.tsx b/pages/new-tab/src/NewTab.tsx old mode 100644 new mode 100755 diff --git a/pages/new-tab/src/index.css b/pages/new-tab/src/index.css old mode 100644 new mode 100755 diff --git a/pages/new-tab/src/index.tsx b/pages/new-tab/src/index.tsx old mode 100644 new mode 100755 diff --git a/pages/new-tab/tailwind.config.ts b/pages/new-tab/tailwind.config.ts old mode 100644 new mode 100755 diff --git a/pages/new-tab/tsconfig.json b/pages/new-tab/tsconfig.json old mode 100644 new mode 100755 diff --git a/pages/new-tab/vite.config.mts b/pages/new-tab/vite.config.mts old mode 100644 new mode 100755 diff --git a/pages/new-tab/vite.config.mts.backup b/pages/new-tab/vite.config.mts.backup old mode 100644 new mode 100755 diff --git a/pages/options/index.html b/pages/options/index.html old mode 100644 new mode 100755 diff --git a/pages/options/package.json b/pages/options/package.json old mode 100644 new mode 100755 index 58a0e9a7..8bea167d --- a/pages/options/package.json +++ b/pages/options/package.json @@ -1,6 +1,6 @@ { "name": "@extension/options", - "version": "0.5.1363", + "version": "0.5.1512", "description": "chrome extension - options", "type": "module", "private": true, diff --git a/pages/options/postcss.config.cjs b/pages/options/postcss.config.cjs old mode 100644 new mode 100755 diff --git a/pages/options/public/logo_horizontal.svg b/pages/options/public/logo_horizontal.svg old mode 100644 new mode 100755 diff --git a/pages/options/public/logo_horizontal_dark.svg b/pages/options/public/logo_horizontal_dark.svg old mode 100644 new mode 100755 diff --git a/pages/options/src/Options.css b/pages/options/src/Options.css old mode 100644 new mode 100755 diff --git a/pages/options/src/Options.tsx b/pages/options/src/Options.tsx old mode 100644 new mode 100755 diff --git a/pages/options/src/components/APPOptions.tsx b/pages/options/src/components/APPOptions.tsx old mode 100644 new mode 100755 index 74e9ade1..be474f99 --- a/pages/options/src/components/APPOptions.tsx +++ b/pages/options/src/components/APPOptions.tsx @@ -76,7 +76,7 @@ export const APPOptions: React.FC<{ isLight: boolean }> = ({ isLight }) => { onRemoveCustomKey={removeCustomKey} onUpdateKey={updateKey} onUpdateCustomKeyName={updateCustomKeyName} - getStatusText={status => getStatusText(status, t)} + getStatusText={getStatusText} getStatusClass={getStatusClass} theme={theme} onThemeChange={handleThemeChange} diff --git a/pages/options/src/components/ErrorDisplay.tsx b/pages/options/src/components/ErrorDisplay.tsx old mode 100644 new mode 100755 diff --git a/pages/options/src/components/IDELayout.tsx b/pages/options/src/components/IDELayout.tsx old mode 100644 new mode 100755 diff --git a/pages/options/src/components/LLMSelector.tsx b/pages/options/src/components/LLMSelector.tsx new file mode 100755 index 00000000..97bdffee --- /dev/null +++ b/pages/options/src/components/LLMSelector.tsx @@ -0,0 +1,171 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { usePluginSettings } from '../hooks/usePluginSettings'; +import { AIKey } from '../hooks/useAIKeys'; +import { APIKeyManager } from '../utils/encryption'; + +interface LLMSelectorProps { + promptType: 'basic_analysis' | 'deep_analysis'; + language: 'ru' | 'en'; + globalAIKeys: AIKey[]; + defaultLLMCurl: string; + hasDefaultLLM: boolean; + onLLMChange: (llm: string, apiKey?: string) => void; +} + +const LLMSelector: React.FC = ({ + promptType, + language, + globalAIKeys, + defaultLLMCurl, + hasDefaultLLM, + onLLMChange, +}) => { + const { settings, updateBasicAnalysisSettings, updateDeepAnalysisSettings, updateAPIKey, saveSettings } = usePluginSettings(); + const [selectedLLM, setSelectedLLM] = useState(defaultLLMCurl); + const [apiKey, setApiKey] = useState(''); + const saveTimeoutRef = useRef(null); + + // Загружаем API-ключ при монтировании компонента + useEffect(() => { + const loadApiKey = async () => { + const keyId = `ozon-analyzer.${promptType}.${language}.default`; + const key = await APIKeyManager.getDecryptedKey(keyId) || ''; + setApiKey(key); + }; + loadApiKey(); + }, [promptType, language]); + + // Загружаем LLM при изменении promptType/language + useEffect(() => { + const currentSettings = promptType === 'basic_analysis' + ? settings.basic_analysis[language] + : settings.deep_analysis[language]; + + let initialLLM = ''; + + if (currentSettings) { + const savedLLM = currentSettings.llm; + initialLLM = savedLLM || (hasDefaultLLM ? 'default' : ''); + } else { + initialLLM = hasDefaultLLM ? 'default' : ''; + } + + setSelectedLLM(initialLLM); + }, [promptType, language, settings, hasDefaultLLM]); + + // Сохраняем выбор LLM + const handleLLMChange = async (newLLM: string) => { + setSelectedLLM(newLLM); + + const updateFn = promptType === 'basic_analysis' ? updateBasicAnalysisSettings : updateDeepAnalysisSettings; + await updateFn(language, { + llm: newLLM, + custom_prompt: promptType === 'basic_analysis' + ? settings.basic_analysis[language].custom_prompt + : settings.deep_analysis[language].custom_prompt, + }); + + // API-ключ передаем для всех LLM, включая кастомные + onLLMChange(newLLM, apiKey || undefined); + }; + + // Сохраняем API ключ + const handleApiKeyChange = (newApiKey: string) => { + setApiKey(newApiKey); + + if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current); + + saveTimeoutRef.current = setTimeout(async () => { + try { + // Для Default LLM сохраняем ключ в encryptedApiKeys под ключом с суффиксом -default + if (selectedLLM === 'default') { + const keyId = `ozon-analyzer.${promptType}.${language}.default`; + console.log(`[API_KEY_FLOW] LLMSelector: Saving API key for ${keyId}`); + await APIKeyManager.saveEncryptedKey(keyId, newApiKey); + + // Обновляем локальное состояние settings для немедленного отображения + const updatedSettings = { ...settings }; + if (!updatedSettings.api_keys) { + updatedSettings.api_keys = { default: '' }; + } + // Для default LLM сохраняем без префикса ozon-analyzer. + const localKey = keyId.replace('ozon-analyzer.', ''); + (updatedSettings.api_keys as any)[localKey] = newApiKey; + console.log(`[API_KEY_FLOW] LLMSelector: Updated local settings with key ${localKey}`); + + // Сохраняем обновленные настройки + await saveSettings(updatedSettings); + console.log(`[API_KEY_FLOW] LLMSelector: Settings saved successfully`); + } + // Для платформенных LLM API-ключи уже сохранены на уровне платформы + + console.log(`[API_KEY_FLOW] LLMSelector: Calling onLLMChange with LLM=${selectedLLM}, apiKey length=${newApiKey.length}`); + onLLMChange(selectedLLM, newApiKey); + } catch (error) { + console.error('Failed to save API key:', error); + } + }, 500); + }; + + // Получаем список опций для селекта + const getLLMOptions = () => { + const options = [ + { value: 'default', label: 'Default LLM' }, + ...globalAIKeys.map(key => ({ + value: key.id, + label: key.name, + })), + ]; + return options; + }; + + return ( +
+ + + + + {selectedLLM === 'default' && ( +
+ + handleApiKeyChange(e.target.value)} + placeholder="Введите API ключ" + style={{ + width: '100%', + padding: '8px', + border: '1px solid #ccc', + borderRadius: '4px', + fontSize: '14px' + }} + /> +
+ )} +
+ ); +}; + +export default LLMSelector; \ No newline at end of file diff --git a/pages/options/src/components/LoadingSpinner.tsx b/pages/options/src/components/LoadingSpinner.tsx old mode 100644 new mode 100755 diff --git a/pages/options/src/components/LocalErrorBoundary.tsx b/pages/options/src/components/LocalErrorBoundary.tsx old mode 100644 new mode 100755 diff --git a/pages/options/src/components/PluginCard.css b/pages/options/src/components/PluginCard.css old mode 100644 new mode 100755 diff --git a/pages/options/src/components/PluginCard.tsx b/pages/options/src/components/PluginCard.tsx old mode 100644 new mode 100755 diff --git a/pages/options/src/components/PluginDetails.tsx b/pages/options/src/components/PluginDetails.tsx old mode 100644 new mode 100755 index c879f9fd..78d5379f --- a/pages/options/src/components/PluginDetails.tsx +++ b/pages/options/src/components/PluginDetails.tsx @@ -3,6 +3,9 @@ import type { Plugin } from '../hooks/usePlugins'; import type { PluginSettings } from '@extension/storage'; import ToggleButton from './ToggleButton'; import LocalErrorBoundary from './LocalErrorBoundary'; +import LLMSelector from './LLMSelector'; +import { useAIKeys, AIKey } from '../hooks/useAIKeys'; +import { usePluginSettings, PluginSettings as PluginSettingsType } from '../hooks/usePluginSettings'; import { useState, useEffect } from 'react'; import type { ReactNode } from 'react'; @@ -17,8 +20,8 @@ interface LanguagePrompts { } interface PromptsStructure { - optimized: LanguagePrompts; - deep: LanguagePrompts; + basic_analysis: LanguagePrompts; + deep_analysis: LanguagePrompts; } const cn = (...args: (string | undefined | false)[]) => args.filter(Boolean).join(' '); @@ -49,13 +52,54 @@ interface PromptsEditorProps { onSave: (value: PromptsStructure) => void; locale: 'en' | 'ru'; t: (key: string) => string; // функция перевода + globalAIKeys: AIKey[]; + pluginSettings: PluginSettingsType; } -const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: PromptsEditorProps) => { - const [promptType, setPromptType] = useState<'optimized' | 'deep'>('optimized'); +const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t, globalAIKeys, pluginSettings }: PromptsEditorProps) => { + const [promptType, setPromptType] = useState<'basic_analysis' | 'deep_analysis'>('basic_analysis'); const [language, setLanguage] = useState<'ru' | 'en'>('ru'); const [customPrompt, setCustomPrompt] = useState(''); + // Получить дефолтную LLM для конкретного промпта и языка + const getDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): string => { + try { + const promptsConfig = manifest?.options?.prompts; + if (!promptsConfig) return 'default'; + + const typePrompts = promptsConfig[type] || {}; + const langPrompts = typePrompts[lang] || {}; + const llmConfig = langPrompts.LLM?.default; + + if (!llmConfig) return 'default'; + + // Для basic_analysis возвращаем gemini-flash-lite, для deep_analysis - gemini-pro + if (type === 'basic_analysis') { + return 'gemini-flash-lite'; + } else if (type === 'deep_analysis') { + return 'gemini-pro'; + } + + return 'default'; + } catch { + return 'default'; + } + }; + + // Проверить наличие дефолтной LLM для конкретного промпта и языка + const hasDefaultLLMForPrompt = (type: 'basic_analysis' | 'deep_analysis', lang: 'ru' | 'en'): boolean => { + try { + const promptsConfig = manifest?.options?.prompts; + if (!promptsConfig) return false; + + const typePrompts = promptsConfig[type] || {}; + const langPrompts = typePrompts[lang] || {}; + return !!langPrompts.LLM?.default; + } catch { + return false; + } + }; + // Получаем оригинальный промпт из manifest const getOriginalPrompt = (): string => { try { @@ -132,18 +176,17 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts @@ -157,7 +200,6 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts padding: '4px 8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '14px' }} > @@ -223,7 +265,6 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts padding: '8px', border: '1px solid #ccc', borderRadius: '4px', - backgroundColor: 'white', fontSize: '12px', fontFamily: 'monospace', resize: 'vertical' @@ -232,6 +273,45 @@ const PromptsEditor = ({ value, manifest, disabled, onSave, locale, t }: Prompts + {/* LLM Selector для текущего промпта и языка */} + { + console.log(`[API_KEY_FLOW] PluginDetails: LLM changed for ${promptType}.${language}: ${llm}, apiKey length: ${apiKey?.length || 0}`); + // Сохраняем выбранную LLM и API ключ в pluginSettings для передачи в mcp_server.py + const updatedPluginSettings = { ...pluginSettings } as any; + if (!updatedPluginSettings.selected_llms) { + updatedPluginSettings.selected_llms = {}; + } + if (!updatedPluginSettings.selected_llms[promptType]) { + updatedPluginSettings.selected_llms[promptType] = {}; + } + // Сохраняем выбранную LLM + updatedPluginSettings.selected_llms[promptType][language] = llm; + + // Сохраняем API ключ если он предоставлен + if (!updatedPluginSettings.api_keys) { + updatedPluginSettings.api_keys = {}; + } + let keyId = ''; + if (apiKey) { + keyId = `ozon-analyzer.${promptType}.${language}.default`; + updatedPluginSettings.api_keys[keyId] = apiKey; + console.log(`[API_KEY_FLOW] PluginDetails: Saved API key for ${keyId} in updatedPluginSettings`); + } + + // Обновляем pluginSettings через глобальный объект + if (typeof window !== 'undefined' && (window as any).pyodide && (window as any).pyodide.globals) { + (window as any).pyodide.globals.pluginSettings = updatedPluginSettings; + console.log(`[API_KEY_FLOW] PluginDetails: Updated pyodide.globals.pluginSettings with API key for ${keyId || 'no key'}`); + } + }} + /> + {/* Кнопка сохранения */}
-
- -
-

Storage Diagnostic

- -
-
- -
-

Chat Operations

- - - -
-
- -
-

Log Output

-
- -
- - - - \ No newline at end of file diff --git a/test-load-prompts.html b/test-load-prompts.html deleted file mode 100644 index bd2c6e44..00000000 --- a/test-load-prompts.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - Тест loadDefaultPrompts() - - - -

🧪 Тест функции loadDefaultPrompts()

-
- - - - \ No newline at end of file diff --git a/test-options-browser.html b/test-options-browser.html deleted file mode 100644 index d8bd4fee..00000000 --- a/test-options-browser.html +++ /dev/null @@ -1,440 +0,0 @@ - - - - - - Тест интерфейса редактирования промптов - Ozon Analyzer - - - -

🧪 Тестирование интерфейса редактирования промптов

-

Ozon Analyzer Plugin - комплексное тестирование функциональности

- - - -
-
-

📁 Тест 1: Загрузка интерфейса options

- ОЖИДАНИЕ -
-
-

Проверка корректной загрузки страницы настройки промптов

-
-
-
- -
-
-

📋 Тест 2: Отображение вкладок типов промптов

- ОЖИДАНИЕ -
-
-

Проверка наличия вкладок "Оптимизированный анализ" и "Глубокий анализ"

-
-
-
- -
-
-

🌐 Тест 3: Отображение вкладок языков

- ОЖИДАНИЕ -
-
-

Проверка наличия вкладок "Русский" и "English"

-
-
-
- -
-
-

📝 Тест 4: Функциональность копирования

- ОЖИДАНИЕ -
-
-

Проверка работы кнопки копирования текста из оригинального поля в редактируемое

-
-
- - -
-
- - -
-
-
- - -
- -
- - -
-
- -
-
-
-
- -
-
-

💾 Тест 5: Сохранение в chrome.storage

- ОЖИДАНИЕ -
-
-

Проверка сохранения промптов в chrome.storage.sync

-
-
-
- -
-
-

🐍 Тест 6: Интеграция с Python backend

- ОЖИДАНИЕ -
-
-

Проверка загрузки промптов в mcp_server.py

-
-
-
- -
-
-

🚨 Тест 7: Обработка ошибок

- ОЖИДАНИЕ -
-
-

Проверка обработки ошибок и fallback логики

-
-
-
- -
-
-
-
- 0/7 тестов завершено -
- - - - \ No newline at end of file diff --git a/test-options-interface.js b/test-options-interface.js deleted file mode 100644 index cda529d8..00000000 --- a/test-options-interface.js +++ /dev/null @@ -1,164 +0,0 @@ -// Тест для loadDefaultPrompts() функции -// Этот файл проверяет, что функция правильно загружает промпты из manifest.json - -console.log('🧪 Запуск теста loadDefaultPrompts()'); - -// Имитация chrome.runtime.getURL для тестирования -if (typeof chrome === 'undefined') { - window.chrome = { - runtime: { - getURL: function(path) { - // В тестовом окружении возвращаем путь к локальному файлу - return path; - } - } - }; -} - -// Имитация fetch для тестирования -const originalFetch = window.fetch; -let testManifest = { - "options": { - "prompts": { - "optimized": { - "ru": { - "type": "text", - "default": "Test Russian optimized prompt", - "label": { "ru": "Оптимизированный промпт (русский)" } - }, - "en": { - "type": "text", - "default": "Test English optimized prompt", - "label": { "ru": "Оптимизированный промпт (английский)" } - } - }, - "deep": { - "ru": { - "type": "text", - "default": "Test Russian deep analysis prompt", - "label": { "ru": "Промпт глубокого анализа (русский)" } - }, - "en": { - "type": "text", - "default": "Test English deep analysis prompt", - "label": { "ru": "Промпт глубокого анализа (английский)" } - } - } - } - } -}; - -// Mock fetch для возврата тестового manifest -window.fetch = function(url) { - console.log('📡 Mock fetch called with URL:', url); - return Promise.resolve({ - ok: true, - json: function() { - return Promise.resolve(testManifest); - } - }); -}; - -// Глобальные переменные для теста -let defaultPrompts = {}; - -// Функция из options/index.html -async function loadDefaultPrompts() { - console.log('[TEST][DEBUG] 🔍 Loading default prompts from manifest.json...'); - - try { - const manifestUrl = chrome.runtime.getURL('plugins/ozon-analyzer/manifest.json'); - console.log('[TEST][DEBUG] 📂 Manifest URL:', manifestUrl); - - const response = await fetch(manifestUrl); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const manifest = await response.json(); - console.log('[TEST][DEBUG] 📋 Manifest loaded successfully'); - - if (manifest.options && manifest.options.prompts) { - defaultPrompts = manifest.options.prompts; - console.log('[TEST][DEBUG] 🤖 Prompts loaded:', Object.keys(defaultPrompts)); - - console.log('✅ Test passed: Prompts loaded successfully'); - return true; - } else { - throw new Error('Раздел prompts не найден в manifest.json'); - } - - } catch (error) { - console.error('[TEST][DEBUG] ❌ Error loading prompts:', error); - - // Fallback на встроенные значения промптов - defaultPrompts = { - optimized: { - ru: { - type: "text", - default: "Ты - токсиколог и химик-косметолог...", - label: { ru: "Оптимизированный промпт (русский)" } - }, - en: { - type: "text", - default: "You are a toxicologist and cosmetic chemist...", - label: { ru: "Оптимизированный промпт (английский)" } - } - }, - deep: { - ru: { - type: "text", - default: "Длинный текст промпта глубокого анализа...", - label: { ru: "Промпт глубокого анализа (русский)" } - }, - en: { - type: "text", - default: "Long English deep analysis prompt text...", - label: { ru: "Промпт глубокого анализа (английский)" } - } - } - }; - - console.log('✅ Test passed: Fallback prompts loaded'); - return true; - } -} - -// Запуск теста -async function runTest() { - console.log('🚀 Starting loadDefaultPrompts() test...'); - - const success = await loadDefaultPrompts(); - - if (success) { - console.log('🎉 Test completed successfully!'); - console.log('📊 Loaded prompts:', Object.keys(defaultPrompts)); - - // Проверка структуры промптов - if (defaultPrompts.optimized && defaultPrompts.optimized.ru && defaultPrompts.optimized.en) { - console.log('✅ Optimized prompts structure is correct'); - } else { - console.error('❌ Optimized prompts structure is incorrect'); - } - - if (defaultPrompts.deep && defaultPrompts.deep.ru && defaultPrompts.deep.en) { - console.log('✅ Deep analysis prompts structure is correct'); - } else { - console.error('❌ Deep analysis prompts structure is incorrect'); - } - - } else { - console.error('❌ Test failed!'); - } - - // Восстановление оригинального fetch - window.fetch = originalFetch; -} - -// Запуск теста при загрузке -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', runTest); -} else { - runTest(); -} \ No newline at end of file diff --git a/test-ozon-chat.js b/test-ozon-chat.js deleted file mode 100644 index 7d925379..00000000 --- a/test-ozon-chat.js +++ /dev/null @@ -1,226 +0,0 @@ -/* eslint-disable no-undef */ -// Тестовый скрипт для создания чатов на странице Ozon -// Запускать в консоли DevTools - -console.log('🛒 Тестирование чатов на странице Ozon...'); - -// Получаем текущий URL -const currentUrl = window.location.href; -console.log('📍 Текущий URL:', currentUrl); - -// Функция для создания чата для ozon-analyzer -async function createOzonChat() { - try { - console.log('📝 Создание чата для ozon-analyzer...'); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение пользователя - const userMessage = { - role: 'user', - content: 'Проанализируй этот товар и расскажи о его характеристиках', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - message: userMessage, - }); - - console.log('✅ Сообщение пользователя сохранено:', userMessage); - - // Сохраняем ответ плагина - const pluginMessage = { - role: 'plugin', - content: - 'Анализирую товар "Костюм спортивный North Dot"... Найдена информация о цене, характеристиках и отзывах.', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - message: pluginMessage, - }); - - console.log('✅ Ответ плагина сохранен:', pluginMessage); - - // Сохраняем черновик - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_DRAFT', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - draftText: 'Хотите узнать больше о доставке или отзывах?', - }); - - console.log('✅ Черновик сохранен'); - - return true; - } catch (error) { - console.error('❌ Ошибка создания чата:', error); - return false; - } -} - -// Функция для создания чата для test-chat-plugin -async function createTestPluginChat() { - try { - console.log('📝 Создание чата для test-chat-plugin...'); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'test-chat-plugin', - pageKey: currentUrl, - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение - const message = { - role: 'user', - content: 'Это тестовое сообщение с Ozon', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'test-chat-plugin', - pageKey: currentUrl, - message: message, - }); - - console.log('✅ Тестовое сообщение сохранено:', message); - - return true; - } catch (error) { - console.error('❌ Ошибка создания тестового чата:', error); - return false; - } -} - -// Функция для отправки тестовых логов -async function sendTestLogs() { - try { - console.log('📝 Отправка тестовых логов...'); - - const logs = [ - { - pluginId: 'ozon-analyzer', - level: 'info', - stepId: 'page-load', - message: 'Страница товара загружена', - logData: { url: currentUrl, title: document.title }, - }, - { - pluginId: 'ozon-analyzer', - level: 'success', - stepId: 'product-found', - message: 'Товар найден: Костюм спортивный North Dot', - logData: { productId: '1438414833' }, - }, - { - pluginId: 'test-chat-plugin', - level: 'debug', - stepId: 'test-debug', - message: 'Тестовый отладочный лог', - logData: { test: true, timestamp: Date.now() }, - }, - ]; - - for (const log of logs) { - await chrome.runtime.sendMessage({ - type: 'LOG_EVENT', - pluginId: log.pluginId, - pageKey: currentUrl, - level: log.level, - stepId: log.stepId, - message: log.message, - logData: log.logData, - }); - - console.log(`✅ Лог отправлен: ${log.pluginId} - ${log.message}`); - } - } catch (error) { - console.error('❌ Ошибка отправки логов:', error); - } -} - -// Функция для получения всех данных -async function getAllData() { - try { - console.log('📋 Получение всех данных...'); - - // Получаем чаты - const chats = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHATS', - pluginId: null, - }); - - console.log('✅ Чаты найдены:', chats); - - // Получаем черновики - const drafts = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHAT_DRAFTS', - pluginId: null, - }); - - console.log('✅ Черновики найдены:', drafts); - - // Получаем логи - const logs = await chrome.runtime.sendMessage({ - type: 'LIST_ALL_PLUGIN_LOGS', - }); - - console.log('✅ Логи найдены:', logs); - - return { chats, drafts, logs }; - } catch (error) { - console.error('❌ Ошибка получения данных:', error); - return { chats: [], drafts: [], logs: {} }; - } -} - -// Запуск всех тестов -async function runOzonTests() { - console.log('🚀 Запуск тестов для Ozon...'); - - await createOzonChat(); - await createTestPluginChat(); - await sendTestLogs(); - await getAllData(); - - console.log('✅ Все тесты завершены!'); - console.log('💡 Теперь откройте DevTools и перейдите на вкладки:'); - console.log(' - "Чаты плагинов" - для просмотра чатов и черновиков'); - console.log(' - "Логи" - для просмотра логов плагинов'); -} - -// Экспортируем функции для использования в консоли -window.ozonTestSystem = { - createOzonChat, - createTestPluginChat, - sendTestLogs, - getAllData, - runOzonTests, -}; - -console.log('🎯 Функции тестирования Ozon доступны:'); -console.log('- ozonTestSystem.createOzonChat() - создать чат для ozon-analyzer'); -console.log('- ozonTestSystem.createTestPluginChat() - создать чат для test-chat-plugin'); -console.log('- ozonTestSystem.sendTestLogs() - отправить тестовые логи'); -console.log('- ozonTestSystem.getAllData() - получить все данные'); -console.log('- ozonTestSystem.runOzonTests() - запустить все тесты'); - -// Автоматически запускаем тесты -runOzonTests(); diff --git a/test-pyodide-message.js b/test-pyodide-message.js deleted file mode 100644 index 44526fd5..00000000 --- a/test-pyodide-message.js +++ /dev/null @@ -1,248 +0,0 @@ -// Тестовый скрипт для проверки PYODIDE_MESSAGE после внедрения Варианта 2 -// Запуск: node test-pyodide-message.js - -console.log('🧪 Тестирование PYODIDE_MESSAGE верификации после Варианта 2...'); - -// Имитация chrome.runtime API для тестирования -const mockChrome = { - runtime: { - sendMessage: (message, callback) => { - console.log('📤 Отправка PYODIDE_MESSAGE:', { - type: message.type, - pluginId: message.pluginId, - pageKey: message.pageKey?.substring(0, 50) + '...', - messageLength: message.message?.length || 0, - timestamp: message.timestamp - }); - - // Имитация ответа background script - setTimeout(() => { - if (message.type === 'PYODIDE_MESSAGE') { - // Имитируем успешную обработку и верификацию - callback({ - success: true, - messageId: `pyodide_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - type: 'PYODIDE_MESSAGE_RESPONSE' - }); - } else { - callback({ error: 'Неизвестный тип сообщения' }); - } - }, 100); // Имитация задержки background - }, - - lastError: null, - - onMessage: { - addListener: (listener) => { - console.log('📡 Добавлен слушатель сообщений'); - }, - removeListener: (listener) => { - console.log('🔌 Удален слушатель сообщений'); - } - } - } -}; - -// Глобальный объект chrome для тестирования -global.chrome = mockChrome; - -// Имитация Promise-based подхода (как в исправленном коде) -function sendPyodideMessage(pluginId, pageKey, message, timestamp = Date.now()) { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject(new Error('PYODIDE_MESSAGE timeout after 10000ms')); - }, 10000); - - chrome.runtime.sendMessage({ - type: 'PYODIDE_MESSAGE', - pluginId, - pageKey, - message, - timestamp - }, (response) => { - clearTimeout(timeoutId); - - if (chrome.runtime.lastError) { - console.error('❌ sendPyodideMessage error:', chrome.runtime.lastError); - reject(new Error(chrome.runtime.lastError.message)); - return; - } - - if (response === undefined) { - console.warn('⚠️ sendPyodideMessage: received undefined response'); - reject(new Error('Background script returned undefined')); - return; - } - - resolve(response); - }); - }); -} - -// Тест 1: Проверка корректной структуры PYODIDE_MESSAGE (Вариант 2) -async function testCorrectPyodideMessage() { - console.log('\n📋 Тест 1: Корректная структура PYODIDE_MESSAGE (Вариант 2)'); - - try { - const pluginId = 'ozon-analyzer'; - const pageKey = 'https://www.ozon.ru/product/test-product-123'; - const message = 'Анализ товара завершен. Найдена информация о цене и характеристиках.'; - const timestamp = Date.now(); - - const response = await sendPyodideMessage(pluginId, pageKey, message, timestamp); - - console.log('✅ PYODIDE_MESSAGE успешно отправлен и обработан:', response); - console.log(' - messageId:', response.messageId); - console.log(' - success:', response.success); - - // Проверяем, что messageId имеет правильный формат - if (response.messageId && response.messageId.startsWith('pyodide_')) { - console.log(' ✅ messageId имеет правильный формат (начинается с pyodide_)'); - return true; - } else { - console.error(' ❌ messageId имеет неправильный формат'); - return false; - } - - } catch (error) { - console.error('❌ PYODIDE_MESSAGE ошибка:', error.message); - return false; - } -} - -// Тест 2: Проверка обработки объекта в message -async function testObjectMessage() { - console.log('\n📦 Тест 2: Обработка объекта в message поле'); - - try { - const pluginId = 'ozon-analyzer'; - const pageKey = 'https://www.ozon.ru/product/test-product-456'; - const message = { - type: 'analysis_result', - product: { - name: 'Тестовый товар', - price: 1500, - rating: 4.5 - }, - analysis: { - competitors: 12, - trend: 'up' - } - }; - const timestamp = Date.now(); - - const response = await sendPyodideMessage(pluginId, pageKey, message, timestamp); - - console.log('✅ Объект в message успешно обработан:', response); - console.log(' - messageId:', response.messageId); - console.log(' - success:', response.success); - - return true; - } catch (error) { - console.error('❌ Обработка объекта в message ошибка:', error.message); - return false; - } -} - -// Тест 3: Проверка отсутствия обязательных полей -async function testMissingFields() { - console.log('\n❌ Тест 3: Отсутствие обязательных полей'); - - try { - const response = await sendPyodideMessage('', '', ''); - - console.log('❌ Ожидалась ошибка из-за отсутствия полей, но получили ответ:', response); - return false; - } catch (error) { - if (error.message.includes('Missing required fields')) { - console.log('✅ Корректная обработка отсутствия обязательных полей:', error.message); - return true; - } else { - console.error('❌ Неожиданная ошибка:', error.message); - return false; - } - } -} - -// Тест 4: Проверка timeout -async function testTimeout() { - console.log('\n⏱️ Тест 4: Проверка timeout'); - - // Изменяем mock для имитации долгого ответа - const originalSendMessage = mockChrome.runtime.sendMessage; - mockChrome.runtime.sendMessage = (message, callback) => { - // Имитация очень долгого ответа (больше timeout) - setTimeout(() => { - callback({ success: true, messageId: 'test-id' }); - }, 15000); // 15 секунд - больше чем наш timeout 10 секунд - }; - - try { - const response = await sendPyodideMessage('test-plugin', 'test-page', 'test message'); - - console.log('❌ Ожидался timeout, но получили ответ:', response); - return false; - } catch (error) { - if (error.message.includes('timeout')) { - console.log('✅ Timeout сработал корректно:', error.message); - return true; - } else { - console.error('❌ Неожиданная ошибка:', error.message); - return false; - } - } finally { - // Восстанавливаем оригинальную функцию - mockChrome.runtime.sendMessage = originalSendMessage; - } -} - -// Основная функция тестирования -async function runPyodideTests() { - console.log('🚀 Запуск тестов PYODIDE_MESSAGE после Варианта 2...\n'); - - const tests = [ - testCorrectPyodideMessage, - testObjectMessage, - testMissingFields, - testTimeout - ]; - - let passed = 0; - let failed = 0; - - for (const test of tests) { - try { - const result = await test(); - if (result) { - passed++; - } else { - failed++; - } - } catch (error) { - console.error('💥 Критическая ошибка в тесте:', error); - failed++; - } - } - - console.log('\n📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ PYODIDE_MESSAGE:'); - console.log(`✅ Пройдено: ${passed}`); - console.log(`❌ Провалено: ${failed}`); - console.log(`📈 Всего тестов: ${passed + failed}`); - - if (failed === 0) { - console.log('\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! PYODIDE_MESSAGE работает корректно после Варианта 2.'); - console.log('🔧 Верификация сообщений исправлена - поле id присваивается, role = "plugin".'); - console.log('📝 Рекомендуется провести интеграционное тестирование в браузере.'); - } else { - console.log('\n⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ. Требуется дополнительная отладка.'); - } - - return { passed, failed, total: passed + failed }; -} - -// Запуск тестов -if (typeof module !== 'undefined' && module.exports) { - module.exports = { runPyodideTests }; -} else { - runPyodideTests().catch(console.error); -} \ No newline at end of file diff --git a/test-recovery-analysis.cjs b/test-recovery-analysis.cjs deleted file mode 100644 index 81296d46..00000000 --- a/test-recovery-analysis.cjs +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env node - -/** - * Анализатор recovery механизма для тестирования исправлений - * Проверяет код background.ts на правильность реализации recovery - */ - -const fs = require('fs'); -const path = require('path'); - -console.log('🧪 АНАЛИЗАТОР RECOVERY МЕХАНИЗМА'); -console.log('================================'); - -// Читаем код background.ts -const backgroundCode = fs.readFileSync('src/background/background.ts', 'utf8'); - -console.log('✅ Код background.ts прочитан успешно'); - -// Проверки recovery механизма -const checks = [ - { - name: '✅ Сохранение метаданных перед завершением transfer', - pattern: /saveTransferMetadataBeforeCompletion/, - description: 'Функция должна сохранять pluginId и pageKey перед завершением transfer' - }, - { - name: '✅ Восстановление pluginId из chrome.storage', - pattern: /pluginId.*savedMetadata.*pluginId/, - description: 'Recovery должен восстанавливать pluginId из сохранённых метаданных' - }, - { - name: '✅ Восстановление pageKey из chrome.storage', - pattern: /pageKey.*savedMetadata.*pageKey/, - description: 'Recovery должен восстанавливать pageKey из сохранённых метаданных' - }, - { - name: '✅ Multi-layer recovery стратегия', - pattern: /Strategy [1-5]/, - description: 'Должно быть 5 стратегий recovery (emergency_backup, global_scope, chrome_storage, offscreen_requery, partial_recovery)' - }, - { - name: '✅ TransferPersistenceManager', - pattern: /class TransferPersistenceManager/, - description: 'Должен быть класс для сохранения/загрузки метаданных transfer в chrome.storage' - }, - { - name: '✅ TransferRecoveryManager', - pattern: /class TransferRecoveryManager/, - description: 'Должен быть класс для выполнения recovery операций' - }, - { - name: '✅ Multi-layer storage verification', - pattern: /verifyMultiLayerStorage/, - description: 'Должна быть функция проверки сохранения transfer во всех storage слоях' - }, - { - name: '✅ Recovery из emergency backup', - pattern: /recoverFromEmergencyBackup/, - description: 'Должна быть стратегия recovery из emergency backup' - }, - { - name: '✅ Recovery из global scope', - pattern: /recoverFromGlobalScope/, - description: 'Должна быть стратегия recovery из global scope' - }, - { - name: '✅ Recovery из chrome storage', - pattern: /recoverFromChromeStorage/, - description: 'Должна быть стратегия recovery из chrome.storage' - }, - { - name: '✅ Обработка HTML_ASSEMBLED с recovery', - pattern: /HTML_ASSEMBLED.*recovery/, - description: 'Обработка HTML_ASSEMBLED должна включать recovery логику' - } -]; - -console.log('\n📋 ПРОВЕРКА ВАЖНЫХ КОМПОНЕНТОВ RECOVERY:'); -console.log('====================================='); - -let passedChecks = 0; -let failedChecks = 0; - -checks.forEach(check => { - const found = check.pattern.test(backgroundCode); - if (found) { - console.log(`✅ ${check.name}`); - console.log(` ${check.description}`); - passedChecks++; - } else { - console.log(`❌ ${check.name}`); - console.log(` ${check.description}`); - failedChecks++; - } - console.log(''); -}); - -console.log(`📊 РЕЗУЛЬТАТЫ АНАЛИЗА:`); -console.log(`✅ Пройдено проверок: ${passedChecks}`); -console.log(`❌ Провалено проверок: ${failedChecks}`); -console.log(`📈 Общий результат: ${passedChecks}/${passedChecks + failedChecks} (${Math.round(passedChecks / (passedChecks + failedChecks) * 100)}%)`); - -// Анализ ключевых строк кода -console.log('\n🔍 АНАЛИЗ КЛЮЧЕВЫХ СТРОК КОДА:'); -console.log('==============================='); - -// Ищем критические строки для recovery -const criticalPatterns = [ - { - name: 'Строка сохранения pluginId в метаданные', - pattern: /pluginId.*pendingWorkflow\.pluginId/ - }, - { - name: 'Строка сохранения pageKey в метаданные', - pattern: /pageKey.*transfer_/ - }, - { - name: 'Строка восстановления pluginId', - pattern: /pluginId.*savedMetadata.*pluginId/ - }, - { - name: 'Строка использования recovered pluginId', - pattern: /workflowPluginId.*recoveredMetadata\.pluginId/ - } -]; - -criticalPatterns.forEach(pattern => { - const match = backgroundCode.match(pattern.pattern); - if (match) { - console.log(`✅ Найдена: ${pattern.name}`); - console.log(` Совпадение: ${match[0].substring(0, 100)}...`); - } else { - console.log(`❌ НЕ НАЙДЕНА: ${pattern.name}`); - } -}); - -// Проверка на наличие проблемных паттернов -console.log('\n🚨 ПРОВЕРКА НА ПРОБЛЕМНЫЕ ПАТТЕРНЫ:'); -console.log('====================================='); - -const problemPatterns = [ - { - name: 'Отсутствие проверки на undefined pluginId', - pattern: /pluginId.*undefined/ - }, - { - name: 'Отсутствие проверки на undefined pageKey', - pattern: /pageKey.*undefined/ - } -]; - -problemPatterns.forEach(pattern => { - const match = backgroundCode.match(pattern.pattern); - if (match) { - console.log(`⚠️ ВОЗМОЖНАЯ ПРОБЛЕМА: ${pattern.name}`); - console.log(` Найдено: ${match[0]}`); - } else { - console.log(`✅ OK: ${pattern.name}`); - } -}); - -// Итоговые рекомендации -console.log('\n📋 РЕКОМЕНДАЦИИ ПО ТЕСТИРОВАНИЮ:'); -console.log('================================'); - -if (passedChecks >= 8) { - console.log('✅ Recovery механизм выглядит полноценно реализованным'); - console.log('Рекомендуется провести интеграционное тестирование с реальным браузером'); -} else { - console.log('⚠️ Recovery механизм требует доработки'); - console.log('Необходимо проверить отсутствующие компоненты'); -} - -console.log('\n🎯 СЛЕДУЮЩИЕ ШАГИ ТЕСТИРОВАНИЯ:'); -console.log('1. Запустить расширение в браузере'); -console.log('2. Открыть страницу с большим HTML (1MB+)'); -console.log('3. Запустить RUN_WORKFLOW для ozon-analyzer плагина'); -console.log('4. Мониторить логи на предмет:'); -console.log(' - "🔍 Starting enhanced multi-layer transfer check"'); -console.log(' - "✅ PluginId restored: ozon-analyzer"'); -console.log(' - "✅ PageKey restored: [URL]"'); -console.log(' - "🎉 Transfer recovery successful"'); -console.log('5. Убедиться в отсутствии ошибок:'); -console.log(' - "Cannot determine pluginId for recovered transfer"'); -console.log(' - "Failed to process recovered transfer: Error: Missing pluginId"'); - -console.log('\n🏁 АНАЛИЗ ЗАВЕРШЕН'); \ No newline at end of file diff --git a/test-recovery-large-html.html b/test-recovery-large-html.html deleted file mode 100644 index 67e3e5cf..00000000 --- a/test-recovery-large-html.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - Test Page for Recovery Mechanism - Large HTML Content - - - -

Test Product Page - Large HTML Content (~1MB)

-
-

Premium Wireless Headphones Pro Max Ultra

-
₽ 45,990
-

This is a premium wireless headphones with advanced noise cancellation technology, premium build quality, and exceptional sound performance. Perfect for audiophiles and professionals alike.

-
- -
-

Technical Specifications

-
Driver Size: 40mm dynamic drivers
-
Frequency Response: 20Hz - 20kHz
-
Battery Life: Up to 30 hours
-
Charging Time: 2 hours (USB-C)
-
Connectivity: Bluetooth 5.2, USB-C, 3.5mm jack
-
Weight: 320g
-
Dimensions: 200x180x80mm
-
Color Options: Black, White, Silver, Blue
-
Warranty: 2 years manufacturer warranty
-
- -
-
Product Image 1
-
Product Image 2
-
Product Image 3
-
Product Image 4
-
Product Image 5
-
Product Image 6
-
- -
-

Shipping Information

-

Free Shipping: Available for orders over ₽3,000

-

Delivery Time: 1-3 business days in Moscow, 3-7 days for regions

-

Pickup Points: Available at 2,500+ locations across Russia

-

International Shipping: Available to 50+ countries

-
- -
-

Customer Reviews (1,247 reviews)

- -
-

Excellent Sound Quality - 5 stars

-

I've been using these headphones for 2 months now and I'm extremely satisfied. The sound quality is outstanding with deep bass and clear highs. The noise cancellation works perfectly for my daily commute. Battery life is as advertised - I get about 28-30 hours on a single charge.

-

Pros: Great sound, comfortable fit, good battery life, ANC works well

-

Cons: A bit heavy for extended wear, expensive

-

By Alex M. on 2024-01-15

-
- -
-

Perfect for Work and Travel - 4 stars

-

These headphones are my go-to for work calls and travel. The microphone quality is excellent and people can hear me clearly even in noisy environments. The build quality feels premium and they've held up well to daily use.

-

The only reason I'm giving 4 stars instead of 5 is the weight - they can feel a bit heavy after wearing for several hours straight. But overall, very happy with the purchase.

-

Pros: Excellent mic, good ANC, premium build

-

Cons: Heavy weight

-

By Sarah K. on 2024-01-10

-
- - - - - -
-

Great Value for Money - 5 stars

-

After comparing many options, I chose these headphones and I'm glad I did. The audio quality rivals much more expensive brands. The noise cancellation is effective without being overbearing. Perfect for my daily work routine.

-

By Mike R. on 2024-01-08

-
- -
-

Comfortable for Long Sessions - 4 stars

-

I use these for gaming and they work great. The sound is immersive and the mic quality is clear. Only complaint is they're a bit bulky for travel, but that's expected for this level of performance.

-

By Jason T. on 2024-01-05

-
-
- -
-

Technical Details

-

Detailed technical specifications and performance metrics for the Premium Wireless Headphones Pro Max Ultra. This section contains comprehensive information about audio performance, connectivity options, and compatibility with various devices.

- - -
-

Audio Performance

-
    -
  • Frequency Response: 20 Hz - 20 kHz (±3dB)
  • -
  • Impedance: 32 ohms
  • -
  • Sensitivity: 98 dB SPL/mW
  • -
  • Total Harmonic Distortion: <0.1% at 1kHz
  • -
  • Signal-to-Noise Ratio: >95 dB
  • -
-
- -
-

Connectivity & Compatibility

-

Compatible with all Bluetooth-enabled devices including smartphones, tablets, laptops, and gaming consoles. Supports Bluetooth 5.2 with aptX HD, aptX Adaptive, AAC, and SBC codecs for high-quality audio streaming.

-
-
- - - - - \ No newline at end of file diff --git a/test-recovery-workflow.js b/test-recovery-workflow.js deleted file mode 100644 index cb8ddfff..00000000 --- a/test-recovery-workflow.js +++ /dev/null @@ -1,183 +0,0 @@ -/* eslint-disable no-undef */ -// Тестовый скрипт для тестирования recovery механизма -// Запускать в консоли DevTools расширения (не в консоли браузера!) - -console.log('🧪 Тестирование RECOVERY МЕХАНИЗМА для ozon-analyzer плагина...'); - -// Функция для запуска RUN_WORKFLOW теста -async function runRecoveryTest() { - try { - console.log('🚀 Запуск тестового сценария recovery механизма...'); - console.log('📋 План тестирования:'); - console.log('1. Открыть тестовую страницу с большим HTML'); - console.log('2. Запустить RUN_WORKFLOW для ozon-analyzer'); - console.log('3. Мониторить процесс передачи данных в чанках'); - console.log('4. Ждать обработки HTML_ASSEMBLED'); - console.log('5. Проверить восстановление pluginId и pageKey'); - - // Открываем тестовую страницу с большим HTML - const testPageUrl = chrome.runtime.getURL('test-recovery-large-html.html'); - console.log('📄 Открываем тестовую страницу:', testPageUrl); - - // Создаем новую вкладку с тестовой страницей - const tab = await chrome.tabs.create({ - url: testPageUrl, - active: true - }); - - console.log('✅ Тестовая страница открыта, tab ID:', tab.id); - - // Ждем загрузки страницы - await new Promise(resolve => setTimeout(resolve, 3000)); - - // Теперь переключаемся на эту вкладку и запускаем RUN_WORKFLOW - await chrome.tabs.update(tab.id, { active: true }); - - console.log('🔄 Запуск RUN_WORKFLOW для ozon-analyzer...'); - - // Запускаем RUN_WORKFLOW - const workflowResult = await chrome.runtime.sendMessage({ - type: 'RUN_WORKFLOW', - pluginId: 'ozon-analyzer', - requestId: `test_recovery_${Date.now()}` - }); - - console.log('✅ RUN_WORKFLOW завершен:', workflowResult); - - // Мониторим процесс - console.log('📊 Мониторинг процесса передачи данных...'); - console.log('🔍 Ищем логи:'); - console.log('- "🔍 Starting enhanced multi-layer transfer check"'); - console.log('- "🔄 Found potential global storage keys"'); - console.log('- "✅ Transfer metadata recovered from chrome.storage"'); - console.log('- "✅ PluginId restored: ozon-analyzer"'); - console.log('- "✅ PageKey restored: ..."'); - console.log('- "✅ Recovery transfer completion flag set"'); - console.log('- "🎉 Transfer recovery successful"'); - - // Ждем завершения процесса - await new Promise(resolve => setTimeout(resolve, 10000)); - - console.log('🏁 Тест завершен. Проверьте логи выше на наличие ошибок recovery.'); - - return { - success: workflowResult?.success || false, - requestId: workflowResult?.requestId, - tabId: tab.id, - testPageUrl - }; - - } catch (error) { - console.error('❌ Ошибка во время тестирования recovery:', error); - - // Проверяем на специфические ошибки recovery - if (error.message.includes('Cannot determine pluginId for recovered transfer')) { - console.error('🚨 ОБНАРУЖЕНА ОШИБКА RECOVERY: "Cannot determine pluginId for recovered transfer"'); - console.error('Это означает проблему с восстановлением pluginId из метаданных'); - } - - if (error.message.includes('Failed to process recovered transfer')) { - console.error('🚨 ОБНАРУЖЕНА ОШИБКА RECOVERY: "Failed to process recovered transfer"'); - console.error('Это означает проблему с обработкой восстановленного transfer'); - } - - return { - success: false, - error: error.message - }; - } -} - -// Функция для проверки статуса transfer'а -async function checkTransferStatus(transferId) { - try { - console.log('🔍 Проверка статуса transfer:', transferId); - - const status = await chrome.runtime.sendMessage({ - type: 'CHECK_TRANSFER_STATUS', - transferId - }); - - console.log('📊 Статус transfer:', status); - return status; - } catch (error) { - console.error('❌ Ошибка проверки статуса transfer:', error); - return null; - } -} - -// Функция для тестирования с разными размерами HTML -async function runMultipleRecoveryTests() { - console.log('🔬 Запуск серии тестов recovery с разными размерами HTML...'); - - const testSizes = [ - { name: 'Small HTML', size: 50000 }, - { name: 'Medium HTML', size: 500000 }, - { name: 'Large HTML', size: 1000000 }, - { name: 'Extra Large HTML', size: 2000000 } - ]; - - const results = []; - - for (const testCase of testSizes) { - console.log(`\n🧪 Тестирование: ${testCase.name} (${testCase.size} chars)`); - - try { - const result = await runRecoveryTest(); - results.push({ - testCase: testCase.name, - size: testCase.size, - success: result.success, - error: result.error - }); - - console.log(`✅ Тест ${testCase.name}: ${result.success ? 'ПРОЙДЕН' : 'ПРОВАЛЕН'}`); - - // Пауза между тестами - await new Promise(resolve => setTimeout(resolve, 5000)); - - } catch (error) { - console.error(`❌ Тест ${testCase.name} завершился с ошибкой:`, error); - results.push({ - testCase: testCase.name, - size: testCase.size, - success: false, - error: error.message - }); - } - } - - console.log('\n📋 РЕЗУЛЬТАТЫ СЕРИИ ТЕСТОВ:'); - results.forEach(result => { - console.log(`${result.testCase}: ${result.success ? '✅' : '❌'} ${result.error || 'OK'}`); - }); - - return results; -} - -// Экспортируем функции для использования в консоли -window.recoveryTestSystem = { - runRecoveryTest, - checkTransferStatus, - runMultipleRecoveryTests, - getCurrentUrl: async () => { - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); - return tab.url; - } -}; - -console.log('🎯 Функции тестирования recovery доступны:'); -console.log('- recoveryTestSystem.runRecoveryTest() - запуск основного теста recovery'); -console.log('- recoveryTestSystem.checkTransferStatus(transferId) - проверка статуса transfer'); -console.log('- recoveryTestSystem.runMultipleRecoveryTests() - серия тестов с разными размерами'); -console.log('- recoveryTestSystem.getCurrentUrl() - получение текущего URL'); - -// Автоматический запуск основного теста -console.log('🚀 Автоматический запуск теста recovery через 3 секунды...'); -setTimeout(() => { - runRecoveryTest().then(result => { - console.log('🏁 Автоматический тест завершен:', result); - }).catch(error => { - console.error('💥 Автоматический тест завершился с ошибкой:', error); - }); -}, 3000); \ No newline at end of file diff --git a/test-scripts/README-prompt-testing.md b/test-scripts/README-prompt-testing.md deleted file mode 100644 index ff700005..00000000 --- a/test-scripts/README-prompt-testing.md +++ /dev/null @@ -1,113 +0,0 @@ -# Тестирование системы промптов - -Этот документ содержит инструкции по запуску тестового сценария для проверки исправлений проблемы с промптами в плагине Ozon Analyzer. - -## Обзор тестирования - -Тестовый сценарий проверяет следующие компоненты: - -1. **Загрузка manifest.json в Pyodide globals** - проверяет корректность загрузки файла манифеста -2. **Применение кастомных промптов из настроек** - тестирует использование пользовательских промптов -3. **Fallback логика при недоступности manifest** - проверяет работу запасных механизмов -4. **Корректность использования промптов AI** - валидация использования промптов в AI вызовах - -## Структура файлов - -- `test-scripts/test-prompt-system.js` - основной тестовый сценарий -- `chrome-extension/src/background/prompt-test-handler.ts` - обработчик для background script - -## Запуск тестирования - -### Шаг 1: Подготовка окружения - -1. Убедитесь, что расширение собрано и загружено в браузер -2. Откройте любую страницу в браузере (например, `chrome://extensions/`) -3. Откройте консоль разработчика (F12 → Console) - -### Шаг 2: Загрузка тестового сценария - -В консоли браузера выполните: - -```javascript -// Загружаем тестовый сценарий -const script = document.createElement('script'); -script.src = chrome.runtime.getURL('test-scripts/test-prompt-system.js'); -document.head.appendChild(script); - -// Ждем загрузки скрипта -script.onload = function() { - console.log('✅ Тестовый сценарий загружен'); - - // Создаем экземпляр тестера - window.promptTester = new PromptSystemTester(); - - // Создаем UI для тестирования - initializePromptTesting(); - - console.log('🚀 Готов к запуску тестов'); - console.log('💡 Используйте кнопку "Запустить тесты" в правом верхнем углу страницы'); -}; -``` - -### Шаг 3: Запуск тестов - -После загрузки сценария: - -1. В правом верхнем углу страницы появится панель тестирования -2. Нажмите кнопку **"Запустить тесты"** -3. Дождитесь завершения всех тестов - -### Шаг 4: Анализ результатов - -После завершения тестов в консоли появится отчет с результатами тестирования всех компонентов промптов. - -## Ожидаемые результаты - -### Успешное тестирование (все тесты пройдены) - -Если исправления работают корректно, все 4 теста должны пройти: - -1. **✅ Загрузка manifest.json**: Manifest успешно загружается в Pyodide globals -2. **✅ Кастомные промпты**: Пользовательские промпты применяются из настроек -3. **✅ Fallback логика**: При отсутствии кастомных промптов используются значения из manifest.json -4. **✅ Использование промптов AI**: Промпты корректно передаются в AI модель - -## Логирование и диагностика - -### Просмотр детальных логов - -В процессе тестирования в консоли отображаются детальные логи с информацией о каждом шаге тестирования. - -### Диагностика проблем - -Если тесты проваливаются: - -1. **Проверьте консоль браузера** на наличие ошибок JavaScript -2. **Проверьте background script logs** в `chrome://extensions/` -3. **Убедитесь что offscreen document активен** -4. **Проверьте доступность Pyodide** - -## Технические детали - -### Архитектура тестирования - -``` -Browser Console → Test Script → Background Script → Offscreen Document → Pyodide → Python Functions -``` - -### Используемые API - -- Chrome Extension Messaging API -- Chrome Offscreen Document API -- Pyodide JavaScript ↔ Python bridge -- Chrome Storage API (для настроек) - -### Производительность - -- Общее время тестирования: ~10-15 секунд -- Время на тест: ~2-3 секунды -- Память: ~5-10 MB дополнительно - ---- - -*Последнее обновление: Октябрь 2025* diff --git a/test-scripts/README.md b/test-scripts/README.md deleted file mode 100644 index 62b6e47d..00000000 --- a/test-scripts/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Тестовые скрипты для Agent-Plugins-Platform - -## Как правильно запускать тесты - -### ❌ НЕПРАВИЛЬНО - Запуск в консоли браузера -Если вы запускаете скрипт в обычной консоли DevTools браузера на странице Ozon, вы получите ошибку: -``` -TypeError: Cannot read properties of undefined (reading 'sendMessage') -``` - -Это происходит потому, что `chrome.runtime` недоступен в контексте обычной веб-страницы. - -### ✅ ПРАВИЛЬНО - Запуск в консоли расширения - -1. **Откройте DevTools расширения:** - - Нажмите F12 или правой кнопкой мыши → "Просмотреть код" - - Перейдите на вкладку **"Console"** - - В выпадающем списке контекста выберите **"Agent Plugins Platform"** (не "top" или "main frame") - -2. **Скопируйте и вставьте тестовый скрипт:** - ```javascript - // Весь код из ozon-test.js - ``` - -3. **Запустите тесты:** - ```javascript - ozonTestSystem.runOzonTests() - ``` - -### Альтернативный способ - через DevTools Panel - -1. **Откройте DevTools расширения** -2. **Перейдите на вкладку "Agent Plugins Platform"** -3. **Откройте консоль в этой вкладке** -4. **Запустите тестовый скрипт** - -### Проверка результатов - -После запуска тестов: - -1. **В DevTools расширения перейдите на вкладку "Чаты плагинов"** - там должны появиться созданные чаты -2. **Перейдите на вкладку "Логи"** - там должны появиться тестовые логи -3. **В консоли должны быть сообщения об успешном выполнении** - -### Отладка - -Если возникают проблемы: - -1. **Проверьте контекст консоли** - должен быть выбран "Agent Plugins Platform" -2. **Убедитесь, что расширение активно** на странице Ozon -3. **Проверьте, что страница загружена полностью** -4. **Посмотрите на ошибки в консоли** - они покажут, что именно не работает - -### Структура тестов - -Тестовый скрипт создает: - -- **Чаты для ozon-analyzer** с тестовыми сообщениями -- **Чаты для test-chat-plugin** с простыми сообщениями -- **Тестовые логи** разных уровней (info, success, debug) -- **Черновики** для демонстрации функциональности - -Все данные сохраняются в IndexedDB и отображаются в DevTools панели расширения. \ No newline at end of file diff --git a/test-scripts/advanced-chat-test.js b/test-scripts/advanced-chat-test.js deleted file mode 100644 index c8635a21..00000000 --- a/test-scripts/advanced-chat-test.js +++ /dev/null @@ -1,382 +0,0 @@ -// Продвинутое тестирование чата с проверкой новых улучшений -// Тестирует: Promise.race, таймауты, fallback, undefined handling - -const advancedChatTest = { - // Симуляция chrome.runtime.sendMessage с возможностью имитации проблем - sendMessage(message, options = {}) { - console.log('[ADVANCED_TEST] sendMessage:', message); - - return new Promise((resolve, reject) => { - const { - simulateDelay = Math.random() * 200 + 50, // Случайная задержка - simulateTimeout = false, - simulateUndefined = false, - simulateError = false - } = options; - - setTimeout(() => { - if (simulateError) { - console.log('[ADVANCED_TEST] Имитация ошибки'); - reject(new Error('Simulated error')); - return; - } - - if (simulateUndefined) { - console.log('[ADVANCED_TEST] Имитация undefined ответа'); - resolve(undefined); - return; - } - - if (simulateTimeout && !options.isFallback) { - console.log('[ADVANCED_TEST] Имитация таймаута'); - reject(new Error('TIMEOUT: Simulated timeout')); - return; - } - - // Нормальная обработка - this.handleMessage(message, {}, (response) => { - console.log('[ADVANCED_TEST] Ответ:', response); - resolve(response); - }); - }, simulateDelay); - }); - }, - - // Обработчик сообщений (упрощенная версия background) - handleMessage(message, sender, sendResponse) { - console.log('[ADVANCED_TEST] handleMessage:', message.type); - - switch (message.type) { - case 'GET_PLUGIN_CHAT': - // Имитация ответа background с диагностикой - console.log('[ADVANCED_TEST] [background] GET_PLUGIN_CHAT:', { - pluginId: message.pluginId, - pageKey: message.pageKey - }); - - const chatKey = `${message.pluginId}::${message.pageKey}`; - const mockChat = { - chatKey, - pluginId: message.pluginId, - pageKey: message.pageKey, - messages: [ - { - role: 'user', - content: 'Тестовое сообщение', - timestamp: Date.now() - } - ], - createdAt: Date.now(), - updatedAt: Date.now() - }; - - console.log('[ADVANCED_TEST] [background] sendResponse:', { - messages: mockChat.messages, - pluginId: mockChat.pluginId, - pageKey: mockChat.pageKey - }); - - sendResponse({ - messages: mockChat.messages, - pluginId: mockChat.pluginId, - pageKey: mockChat.pageKey - }); - break; - - case 'SAVE_PLUGIN_CHAT_MESSAGE': - console.log('[ADVANCED_TEST] [background] SAVE_PLUGIN_CHAT_MESSAGE:', { - pluginId: message.pluginId, - pageKey: message.pageKey, - message: message.message - }); - - sendResponse({ success: true }); - break; - - default: - sendResponse({ error: 'Unknown message type' }); - } - }, - - // Тестирование Promise.race с логированием (как в PluginControlPanel) - async testPromiseRace() { - console.log('\n🧪 Тест 1: Promise.race с логированием'); - - const timeoutPromise = new Promise((_, reject) => - setTimeout(() => reject(new Error('TIMEOUT: test timeout')), 1000) - ); - - const sendMessagePromise = this.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }); - - console.log('[ADVANCED_TEST] ДО ОЖИДАНИЯ - sendMessagePromise:', { - promise: sendMessagePromise, - typeofPromise: typeof sendMessagePromise, - }); - - let response; - let raceError = null; - - try { - response = await Promise.race([sendMessagePromise, timeoutPromise]); - console.log('[ADVANCED_TEST] СЫРОЙ ОТВЕТ ОТ BACKGROUND:', { - response, - typeofResponse: typeof response, - responseKeys: response ? Object.keys(response) : 'response is null/undefined', - responseType: typeof response, - isPromise: response instanceof Promise, - constructorName: response?.constructor?.name, - }); - } catch (error) { - raceError = error; - console.error('[ADVANCED_TEST] Ошибка в Promise.race:', { - error, - message: error.message, - stack: error.stack, - }); - } - - // Проверка результатов Promise.race - if (raceError) { - console.error('[ADVANCED_TEST] Promise.race завершился с ошибкой:', { - error: raceError.message, - timeout: 1000, - }); - - // Попытка получить ответ напрямую - console.log('[ADVANCED_TEST] Попытка получить ответ после таймаута...'); - - try { - const directResponse = await this.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }); - console.log('[ADVANCED_TEST] Прямой ответ после таймаута:', directResponse); - - if (directResponse) { - response = directResponse; - console.log('[ADVANCED_TEST] Успешно получен прямой ответ после таймаута'); - } else { - throw new Error('Прямой ответ тоже undefined'); - } - } catch (directError) { - console.error('[ADVANCED_TEST] Прямой ответ тоже не получен:', directError); - } - } - - // Финальная проверка - if (!response) { - console.error('[ADVANCED_TEST] Критическая ошибка: response всё ещё undefined после всех попыток'); - return false; - } - - console.log('[ADVANCED_TEST] ✅ Promise.race тест завершен успешно'); - return true; - }, - - // Тестирование обработки разных форматов ответа - async testResponseFormatHandling() { - console.log('\n🧪 Тест 2: Обработка разных форматов ответа'); - - // Тест различных форматов ответа - const testCases = [ - { - name: 'Формат {messages: [...]}', - response: { - messages: [{ role: 'user', content: 'Test message', timestamp: Date.now() }] - } - }, - { - name: 'Формат {chat: [...]}', - response: { - chat: [{ role: 'user', content: 'Test message', timestamp: Date.now() }] - } - }, - { - name: 'Формат {chat: {messages: [...]}}', - response: { - chat: { - messages: [{ role: 'user', content: 'Test message', timestamp: Date.now() }] - } - } - }, - { - name: 'Пустой ответ', - response: {} - }, - { - name: 'null', - response: null - } - ]; - - for (const testCase of testCases) { - console.log(`\n--- ${testCase.name} ---`); - console.log('Вход:', testCase.response); - - // Логика обработки ответа (из PluginControlPanel) - let messagesArray = null; - let chatData = testCase.response; - - if (chatData && Array.isArray(chatData.messages)) { - messagesArray = chatData.messages; - console.log('Используем формат с messages:', messagesArray.length); - } else if (chatData && Array.isArray(chatData.chat)) { - messagesArray = chatData.chat; - console.log('Используем формат с chat:', messagesArray.length); - } else if (chatData && chatData.chat && Array.isArray(chatData.chat.messages)) { - messagesArray = chatData.chat.messages; - console.log('Используем вложенный формат:', messagesArray.length); - } else { - console.warn('Неизвестный формат ответа:', chatData); - messagesArray = []; - } - - console.log('Результат:', { - messagesArray, - isArray: Array.isArray(messagesArray), - length: messagesArray?.length - }); - } - - console.log('[ADVANCED_TEST] ✅ Тест форматов завершен'); - return true; - }, - - // Тестирование fallback логики - async testFallbackLogic() { - console.log('\n🧪 Тест 3: Fallback логика'); - - try { - // Имитация ситуации с undefined ответом - const response = await this.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }, { simulateUndefined: true }); - - console.log('Получен ответ:', response); - - if (!response) { - console.log('✅ Обнаружен undefined ответ, тестируем fallback...'); - - // Fallback логика - await new Promise(resolve => setTimeout(resolve, 1000)); - - const fallbackResponse = await this.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }, { isFallback: true }); - - console.log('Fallback ответ:', fallbackResponse); - - if (fallbackResponse && fallbackResponse.messages) { - console.log('✅ Fallback сработал успешно'); - return true; - } else { - console.log('❌ Fallback не помог'); - return false; - } - } - - } catch (error) { - console.error('❌ Ошибка в fallback тесте:', error); - return false; - } - }, - - // Тестирование обработки ошибок - async testErrorHandling() { - console.log('\n🧪 Тест 4: Обработка ошибок и исключений'); - - const errorCases = [ - { name: 'TIMEOUT ошибка', options: { simulateTimeout: true } }, - { name: 'Undefined ответ', options: { simulateUndefined: true } }, - { name: 'Общая ошибка', options: { simulateError: true } } - ]; - - for (const errorCase of errorCases) { - console.log(`\n--- ${errorCase.name} ---`); - - try { - const response = await this.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }, errorCase.options); - - console.log('Ответ:', response); - - if (!response && errorCase.name.includes('Undefined')) { - console.log('✅ Undefined ответ обработан корректно'); - } else if (response) { - console.log('✅ Ответ получен несмотря на ошибку'); - } - - } catch (error) { - console.log(`✅ Ошибка ${errorCase.name} поймана:`, error.message); - - // Проверка на специальные обработки - if (error.message.includes('TIMEOUT') || error.message.includes('undefined')) { - console.log('✅ Специальная обработка сработала'); - } - } - } - - console.log('[ADVANCED_TEST] ✅ Тест обработки ошибок завершен'); - return true; - } -}; - -// === ЗАПУСК ТЕСТОВ === - -async function runAdvancedTests() { - console.log('🚀 Начинаем продвинутое тестирование чата...\n'); - - const results = []; - - try { - // Тест 1: Promise.race с логированием - results.push(await advancedChatTest.testPromiseRace()); - - // Тест 2: Обработка форматов ответа - results.push(await advancedChatTest.testResponseFormatHandling()); - - // Тест 3: Fallback логика - results.push(await advancedChatTest.testFallbackLogic()); - - // Тест 4: Обработка ошибок - results.push(await advancedChatTest.testErrorHandling()); - - // Итоги - const passed = results.filter(r => r).length; - const total = results.length; - - console.log('\n📋 РЕЗУЛЬТАТЫ ПРОДВИНУТОГО ТЕСТИРОВАНИЯ:'); - console.log(`✅ Пройдено: ${passed}/${total}`); - console.log(`❌ Провалено: ${total - passed}/${total}`); - - if (passed === total) { - console.log('\n🎉 ВСЕ ТЕСТЫ ПРОШЛИ УСПЕШНО!'); - console.log('Новые улучшения работают корректно.'); - } else { - console.log('\n⚠️ НЕКОТОРЫЕ ТЕСТЫ НЕ ПРОШЛИ'); - console.log('Требуется дополнительная настройка.'); - } - - } catch (error) { - console.error('❌ Критическая ошибка тестирования:', error); - } -} - -// Запуск -if (typeof module !== 'undefined' && module.exports) { - module.exports = { advancedChatTest, runAdvancedTests }; -} else { - runAdvancedTests(); -} \ No newline at end of file diff --git a/test-scripts/chat-api-test.js b/test-scripts/chat-api-test.js deleted file mode 100644 index 5d804cc8..00000000 --- a/test-scripts/chat-api-test.js +++ /dev/null @@ -1,222 +0,0 @@ -// Тест API чата - для тестирования в Node.js среде -// Этот файл симулирует работу с chat API для проверки логики - -const testChatApi = { - // Симуляция chrome.storage.local API - storage: { - data: {}, - - get(keys, callback) { - const result = {}; - if (Array.isArray(keys)) { - keys.forEach(key => { - result[key] = this.data[key] || null; - }); - } else if (typeof keys === 'string') { - result[keys] = this.data[keys] || null; - } else { - Object.assign(result, this.data); - } - console.log('[TEST] storage.get:', keys, '=>', result); - setTimeout(() => callback(result), 10); - }, - - set(items, callback) { - Object.assign(this.data, items); - console.log('[TEST] storage.set:', items); - setTimeout(() => callback(), 10); - }, - - remove(keys, callback) { - if (Array.isArray(keys)) { - keys.forEach(key => delete this.data[key]); - } else { - delete this.data[keys]; - } - console.log('[TEST] storage.remove:', keys); - setTimeout(() => callback(), 10); - } - }, - - // Симуляция chrome.runtime.sendMessage - async sendMessage(message) { - console.log('[TEST] sendMessage:', message); - - return new Promise((resolve) => { - setTimeout(() => { - // Симуляция обработки сообщений background скриптом - this.handleMessage(message, {}, resolve); - }, Math.random() * 100 + 50); // Случайная задержка - }); - }, - - // Обработчик сообщений (симуляция background скрипта) - handleMessage(message, sender, sendResponse) { - console.log('[TEST] handleMessage:', message.type); - - switch (message.type) { - case 'GET_PLUGIN_CHAT': - this.storage.get([`${message.pluginId}::${message.pageKey}`], (result) => { - const chat = result[`${message.pluginId}::${message.pageKey}`]; - if (chat && chat.messages) { - sendResponse({ - messages: chat.messages, - pluginId: chat.pluginId, - pageKey: chat.pageKey - }); - } else { - sendResponse({ messages: [] }); - } - }); - break; - - case 'SAVE_PLUGIN_CHAT_MESSAGE': - const chatKey = `${message.pluginId}::${message.pageKey}`; - this.storage.get([chatKey], (result) => { - let chat = result[chatKey]; - - if (!chat) { - chat = { - chatKey, - pluginId: message.pluginId, - pageKey: message.pageKey, - messages: [], - createdAt: Date.now(), - updatedAt: Date.now() - }; - } - - chat.messages.push(message.message); - chat.updatedAt = Date.now(); - - this.storage.set({ [chatKey]: chat }, () => { - sendResponse({ success: true }); - }); - }); - break; - - case 'DELETE_PLUGIN_CHAT': - this.storage.remove([`${message.pluginId}::${message.pageKey}`], () => { - sendResponse({ success: true }); - }); - break; - - default: - sendResponse({ error: 'Unknown message type' }); - } - } -}; - -// === ТЕСТОВЫЕ СЦЕНАРИИ === - -async function runTests() { - console.log('🧪 Начинаем тестирование Chat API...\n'); - - const testPluginId = 'test-plugin'; - const testPageKey = 'test-page-key'; - - try { - // Тест 1: Получение пустого чата - console.log('📥 Тест 1: Получение пустого чата'); - let response = await testChatApi.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: testPluginId, - pageKey: testPageKey - }); - console.log('Ответ:', response); - console.log(response.messages && Array.isArray(response.messages) ? '✅' : '❌', '\n'); - - // Тест 2: Сохранение сообщения - console.log('💬 Тест 2: Сохранение сообщения'); - response = await testChatApi.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: testPluginId, - pageKey: testPageKey, - message: { - role: 'user', - content: 'Тестовое сообщение', - timestamp: Date.now() - } - }); - console.log('Ответ:', response); - console.log(response.success ? '✅' : '❌', '\n'); - - // Тест 3: Получение чата с сообщением - console.log('📥 Тест 3: Получение чата с сообщением'); - response = await testChatApi.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: testPluginId, - pageKey: testPageKey - }); - console.log('Ответ:', response); - console.log(response.messages && response.messages.length > 0 ? '✅' : '❌', '\n'); - - // Тест 4: Сохранение еще одного сообщения - console.log('💬 Тест 4: Сохранение второго сообщения'); - response = await testChatApi.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: testPluginId, - pageKey: testPageKey, - message: { - role: 'plugin', - content: 'Ответ на тестовое сообщение', - timestamp: Date.now() - } - }); - console.log('Ответ:', response); - console.log(response.success ? '✅' : '❌', '\n'); - - // Тест 5: Получение полного чата - console.log('📥 Тест 5: Получение полного чата'); - response = await testChatApi.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: testPluginId, - pageKey: testPageKey - }); - console.log('Ответ:', response); - console.log(response.messages && response.messages.length === 2 ? '✅' : '❌', '\n'); - - // Тест 6: Удаление чата - console.log('🗑️ Тест 6: Удаление чата'); - response = await testChatApi.sendMessage({ - type: 'DELETE_PLUGIN_CHAT', - pluginId: testPluginId, - pageKey: testPageKey - }); - console.log('Ответ:', response); - console.log(response.success ? '✅' : '❌', '\n'); - - // Тест 7: Проверка удаления - console.log('📥 Тест 7: Проверка удаления'); - response = await testChatApi.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: testPluginId, - pageKey: testPageKey - }); - console.log('Ответ:', response); - console.log(response.messages && response.messages.length === 0 ? '✅' : '❌', '\n'); - - // Тест 8: Обработка неизвестного типа сообщения - console.log('❓ Тест 8: Неизвестный тип сообщения'); - response = await testChatApi.sendMessage({ - type: 'UNKNOWN_MESSAGE_TYPE', - pluginId: testPluginId, - pageKey: testPageKey - }); - console.log('Ответ:', response); - console.log(response.error ? '✅' : '❌', '\n'); - - console.log('🎉 Все тесты завершены!'); - - } catch (error) { - console.error('❌ Ошибка тестирования:', error); - } -} - -// Запуск тестов -if (typeof module !== 'undefined' && module.exports) { - module.exports = { testChatApi, runTests }; -} else { - // В браузере - runTests(); -} \ No newline at end of file diff --git a/test-scripts/chat-conversion-test.js b/test-scripts/chat-conversion-test.js deleted file mode 100644 index 29f0e1bc..00000000 --- a/test-scripts/chat-conversion-test.js +++ /dev/null @@ -1,71 +0,0 @@ -// Тест для проверки конвертации чата между background и компонентом -// Этот скрипт имитирует логику обработки ответа в компоненте - -// Имитация ответа от background (из логов) -const mockBackgroundResponse = { chatKey: 'test::page', messages: Array(8).fill().map((_, i) => ({ id: i, content: `Message ${i}`, role: 'user', timestamp: Date.now() })) }; - -// Имитация "сломанного" ответа (как в логах пользователя) -const mockBrokenResponse = { pluginId: 'test', chat: undefined }; - -console.log('=== ТЕСТ КОНВЕРТАЦИИ ЧАТА ==='); -console.log('Исходные данные:'); -console.log('1. mockBackgroundResponse:', mockBackgroundResponse); -console.log('2. mockBrokenResponse:', mockBrokenResponse); - -console.log('\n=== ТЕСТИРОВАНИЕ СТАРОЙ ЛОГИКИ ==='); -function oldLogic(response) { - console.log('Вход:', response); - const rawMessages = response?.messages; - console.log('rawMessages:', rawMessages); - console.log('Array.isArray(rawMessages):', Array.isArray(rawMessages)); - return rawMessages; -} - -console.log('Старая логика с корректными данными:', oldLogic(mockBackgroundResponse)); -console.log('Старая логика с undefined данными:', oldLogic(mockBrokenResponse)); - -console.log('\n=== ТЕСТИРОВАНИЕ НОВОЙ ЛОГИКИ ==='); -function newLogic(response) { - console.log('Вход:', response); - - // ПРОВЕРКА: Является ли ответ чатом с messages или другим объектом - let chatData = response; - console.log('chatData:', chatData); - console.log('hasMessages:', chatData && 'messages' in chatData); - console.log('hasChat:', chatData && 'chat' in chatData); - console.log('messagesValue:', chatData?.messages); - console.log('chatValue:', chatData?.chat); - - // ИСПРАВЛЕНИЕ: Обработка разных форматов ответа - let messagesArray = null; - - if (chatData && Array.isArray(chatData.messages)) { - // Формат: { messages: [...] } - messagesArray = chatData.messages; - console.log('Используем формат с messages:', messagesArray.length); - } else if (chatData && Array.isArray(chatData.chat)) { - // Формат: { chat: [...] } - messagesArray = chatData.chat; - console.log('Используем формат с chat:', messagesArray.length); - } else if (chatData && chatData.chat && Array.isArray(chatData.chat.messages)) { - // Формат: { chat: { messages: [...] } } - messagesArray = chatData.chat.messages; - console.log('Используем вложенный формат:', messagesArray.length); - } else { - console.warn('Неизвестный формат ответа:', chatData); - messagesArray = []; - } - - console.log('Финальный messagesArray:', messagesArray); - return messagesArray; -} - -console.log('\n--- Новая логика с корректными данными ---'); -newLogic(mockBackgroundResponse); - -console.log('\n--- Новая логика с undefined данными ---'); -newLogic(mockBrokenResponse); - -console.log('\n=== ВЫВОД ==='); -console.log('Проблема в том, что background отправляет {messages: [...]}, а компонент получает {chat: undefined}'); -console.log('Новая логика обрабатывает оба формата и должна решить проблему'); \ No newline at end of file diff --git a/test-scripts/chat-debug-commands.js b/test-scripts/chat-debug-commands.js deleted file mode 100644 index a56f8639..00000000 --- a/test-scripts/chat-debug-commands.js +++ /dev/null @@ -1,233 +0,0 @@ -// Команды для отладки чата в консоли браузера -// Вставьте эти команды в консоль браузера для тестирования - -console.log('=== КОМАНДЫ ОТЛАДКИ ЧАТА ==='); - -// 1. Проверка доступности API чата -window.testChatAPI = async function() { - console.log('Проверка API чата...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'debug-plugin', - pageKey: window.location.href - }); - - console.log('Ответ API:', response); - return response; - } catch (error) { - console.error('Ошибка API:', error); - return null; - } -}; - -// 2. Создание тестового чата -window.createTestChat = async function(pluginId = 'test-plugin') { - console.log('Создание тестового чата...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId, - pageKey: window.location.href - }); - - console.log('Чат создан:', response); - return response; - } catch (error) { - console.error('Ошибка создания чата:', error); - return null; - } -}; - -// 3. Отправка тестового сообщения -window.sendTestMessage = async function(pluginId = 'test-plugin', content = 'Тестовое сообщение') { - console.log('Отправка тестового сообщения...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId, - pageKey: window.location.href, - message: { - role: 'user', - content: content, - timestamp: Date.now() - } - }); - - console.log('Сообщение отправлено:', response); - return response; - } catch (error) { - console.error('Ошибка отправки сообщения:', error); - return null; - } -}; - -// 4. Получение всех чатов плагина -window.getAllChats = async function(pluginId = 'test-plugin') { - console.log('Получение всех чатов плагина...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHATS', - pluginId - }); - - console.log('Все чаты плагина:', response); - return response; - } catch (error) { - console.error('Ошибка получения чатов:', error); - return null; - } -}; - -// 5. Проверка черновиков -window.testDrafts = async function(pluginId = 'test-plugin') { - console.log('Тестирование черновиков...'); - - const testText = 'Тест черновика ' + Date.now(); - - try { - // Сохраняем черновик - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_DRAFT', - pluginId, - pageKey: window.location.href, - draftText: testText - }); - console.log('Черновик сохранен'); - - // Получаем черновик - const response = await chrome.runtime.sendMessage({ - type: 'GET_PLUGIN_CHAT_DRAFT', - pluginId, - pageKey: window.location.href - }); - - console.log('Полученный черновик:', response); - - if (response?.draftText === testText) { - console.log('✅ Черновик работает корректно'); - } else { - console.log('❌ Черновик не совпадает'); - } - - return response; - } catch (error) { - console.error('Ошибка тестирования черновиков:', error); - return null; - } -}; - -// 6. Очистка всех данных чата -window.clearAllChatData = async function(pluginId = 'test-plugin') { - console.log('Очистка всех данных чата...'); - - try { - const response = await chrome.runtime.sendMessage({ - type: 'DELETE_PLUGIN_CHAT', - pluginId, - pageKey: window.location.href - }); - - console.log('Чат очищен:', response); - return response; - } catch (error) { - console.error('Ошибка очистки чата:', error); - return null; - } -}; - -// 7. Комплексный тест -window.runFullChatTest = async function() { - console.log('🚀 Запуск комплексного теста чата...'); - - const pluginId = 'full-test-' + Date.now(); - - try { - // 1. Создаем чат - console.log('1. Создание чата...'); - await window.createTestChat(pluginId); - - // 2. Отправляем несколько сообщений - console.log('2. Отправка сообщений...'); - await window.sendTestMessage(pluginId, 'Первое тестовое сообщение'); - await window.sendTestMessage(pluginId, 'Второе тестовое сообщение'); - - // 3. Проверяем черновики - console.log('3. Тестирование черновиков...'); - await window.testDrafts(pluginId); - - // 4. Получаем все чаты - console.log('4. Получение всех чатов...'); - const chats = await window.getAllChats(pluginId); - console.log('Количество чатов:', chats?.length || 0); - - // 5. Очищаем данные - console.log('5. Очистка данных...'); - await window.clearAllChatData(pluginId); - - console.log('✅ Комплексный тест завершен успешно!'); - return true; - - } catch (error) { - console.error('❌ Ошибка комплексного теста:', error); - return false; - } -}; - -// 8. Мониторинг изменений в storage -window.monitorStorage = function() { - console.log('🔍 Мониторинг изменений в chrome.storage.local...'); - - chrome.storage.onChanged.addListener((changes, namespace) => { - console.log('📦 Изменения в storage:', changes); - for (let key in changes) { - console.log(` ${key}:`, changes[key]); - } - }); - - console.log('✅ Мониторинг включен'); -}; - -// 9. Проверка текущего состояния storage -window.checkStorage = async function() { - console.log('📊 Проверка текущего состояния storage...'); - - try { - const allData = await new Promise(resolve => { - chrome.storage.local.get(null, resolve); - }); - - const chatKeys = Object.keys(allData).filter(key => key.includes('::')); - console.log(`Всего ключей: ${Object.keys(allData).length}`); - console.log(`Ключей чатов: ${chatKeys.length}`); - - if (chatKeys.length > 0) { - console.log('Примеры ключей чатов:'); - chatKeys.slice(0, 5).forEach(key => { - console.log(` ${key}:`, allData[key]); - }); - } - - return allData; - } catch (error) { - console.error('Ошибка проверки storage:', error); - return null; - } -}; - -console.log('📋 Доступные команды:'); -console.log(' window.testChatAPI() - проверка API'); -console.log(' window.createTestChat() - создание чата'); -console.log(' window.sendTestMessage() - отправка сообщения'); -console.log(' window.getAllChats() - получение всех чатов'); -console.log(' window.testDrafts() - тестирование черновиков'); -console.log(' window.clearAllChatData() - очистка данных'); -console.log(' window.runFullChatTest() - комплексный тест'); -console.log(' window.monitorStorage() - мониторинг storage'); -console.log(' window.checkStorage() - проверка storage'); -console.log(''); -console.log('💡 Для запуска комплексного теста выполните: window.runFullChatTest()'); \ No newline at end of file diff --git a/test-scripts/chat-test-page.html b/test-scripts/chat-test-page.html deleted file mode 100644 index 48d97bf0..00000000 --- a/test-scripts/chat-test-page.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - Тестирование функциональности чата - - - -
-

🧪 Тестирование функциональности чата

- -
-

📋 Инструкции по тестированию

-
    -
  • Откройте консоль браузера (F12 → Console)
  • -
  • Убедитесь, что расширение загружено и активно
  • -
  • Используйте кнопки ниже для поэтапного тестирования
  • -
  • Проверяйте результаты в консоли и в блоках результатов
  • -
  • Все тесты должны завершаться со статусом "SUCCESS"
  • -
-
- -
-

1. Проверка API чата

-
- - -
-
Ожидание результатов...
-
- -
-

2. Тестирование сообщений

-
- - -
-
- - -
-
Ожидание результатов...
-
- -
-

3. Тестирование черновиков

-
- - -
-
- -
-
Ожидание результатов...
-
- -
-

4. Комплексное тестирование

-
- - -
-
Ожидание результатов...
-
- -
-

5. Проверка localStorage

-
- - -
-
Ожидание результатов...
-
-
- - - - - \ No newline at end of file diff --git a/test-scripts/chat-test.html b/test-scripts/chat-test.html deleted file mode 100644 index 20454b9d..00000000 --- a/test-scripts/chat-test.html +++ /dev/null @@ -1,411 +0,0 @@ - - - - - - Тест чата - Итоговое тестирование - - - -

🧪 Итоговое тестирование функциональности чата

- -
-

📊 Статус тестирования

-
Ожидание начала тестирования...
-
- -
-

🔍 Тест 1: Проверка доступности chrome.runtime API

- -
-
- -
-

💬 Тест 2: Симуляция отправки сообщения

- - -
-
- -
-

📥 Тест 3: Получение истории чата

- -
-
- -
-

🔄 Тест 4: Синхронизация чата (handleChatUpdate)

- -
-
- -
-

⚠️ Тест 5: Обработка ошибок и undefined ответов

- -
-
- -
-

🕒 Тест 6: Таймауты и fallback логика

- -
-
- - - - - - \ No newline at end of file diff --git a/test-scripts/integration-chat-test.js b/test-scripts/integration-chat-test.js deleted file mode 100644 index f4892e7f..00000000 --- a/test-scripts/integration-chat-test.js +++ /dev/null @@ -1,409 +0,0 @@ -// Интеграционное тестирование чата - проверка взаимодействия компонентов -// Имитирует работу PluginControlPanel с background - -const integrationTest = { - // Имитация состояния PluginControlPanel - mockPluginControlPanel: { - pluginId: 'test-plugin', - pageKey: 'https://example.com/test', - messages: [], - loading: false, - error: null, - - // Имитация loadChat функции из PluginControlPanel - async loadChat() { - console.log('[INTEGRATION] PluginControlPanel.loadChat - запрос к background'); - - this.loading = true; - this.error = null; - - const timeoutPromise = new Promise((_, reject) => - setTimeout(() => reject(new Error('TIMEOUT: loadChat не получил ответ от background')), 5000) - ); - - const sendMessagePromise = integrationTest.mockChromeRuntime.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: this.pluginId, - pageKey: this.pageKey, - }); - - console.log('[INTEGRATION] ДО ОЖИДАНИЯ - sendMessagePromise:', { - promise: sendMessagePromise, - typeofPromise: typeof sendMessagePromise, - }); - - let response; - let raceError = null; - - try { - response = await Promise.race([sendMessagePromise, timeoutPromise]); - console.log('[INTEGRATION] СЫРОЙ ОТВЕТ ОТ BACKGROUND:', { - response, - typeofResponse: typeof response, - responseKeys: response ? Object.keys(response) : 'response is null/undefined', - responseType: typeof response, - responseStringified: JSON.stringify(response), - isPromise: response instanceof Promise, - constructorName: response?.constructor?.name, - }); - } catch (error) { - raceError = error; - console.error('[INTEGRATION] Ошибка в Promise.race:', { - error, - message: error.message, - stack: error.stack, - }); - } - - // Обработка результатов Promise.race - if (raceError) { - console.error('[INTEGRATION] Promise.race завершился с ошибкой:', { - error: raceError.message, - pluginId: this.pluginId, - pageKey: this.pageKey, - timeout: 5000, - }); - - if (raceError.message.includes('TIMEOUT')) { - console.log('[INTEGRATION] Попытка получить ответ после таймаута...'); - - try { - const directResponse = await integrationTest.mockChromeRuntime.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: this.pluginId, - pageKey: this.pageKey, - }); - console.log('[INTEGRATION] Прямой ответ после таймаута:', directResponse); - - if (directResponse) { - response = directResponse; - console.log('[INTEGRATION] Успешно получен прямой ответ после таймаута'); - } else { - throw new Error('Прямой ответ тоже undefined'); - } - } catch (directError) { - console.error('[INTEGRATION] Прямой ответ тоже не получен:', directError); - } - } - } - - // Проверка на undefined после всех попыток - if (!response) { - console.error('[INTEGRATION] Критическая ошибка: response всё ещё undefined после всех попыток'); - console.error('[INTEGRATION] Финальная диагностика:', { - pluginId: this.pluginId, - pageKey: this.pageKey, - raceError: raceError?.message, - chromeRuntimeAvailable: typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.sendMessage, - }); - this.error = 'Критическая ошибка: не удалось получить ответ от background'; - this.messages = []; - this.loading = false; - return; - } - - // Проверка на ошибку от background - if (response && response.error) { - console.error('[INTEGRATION] Background вернул ошибку:', response.error, response.details); - this.messages = []; - this.loading = false; - return; - } - - // Обработка разных форматов ответа - let chatData = response; - console.log('[INTEGRATION] Анализ chatData:', { - chatData, - hasMessages: chatData && 'messages' in chatData, - hasChat: chatData && 'chat' in chatData, - messagesValue: chatData?.messages, - chatValue: chatData?.chat, - isMessagesArray: Array.isArray(chatData?.messages), - isChatArray: Array.isArray(chatData?.chat), - }); - - let messagesArray = null; - - if (chatData && Array.isArray(chatData.messages)) { - messagesArray = chatData.messages; - console.log('[INTEGRATION] Используем формат с messages:', messagesArray.length); - } else if (chatData && Array.isArray(chatData.chat)) { - messagesArray = chatData.chat; - console.log('[INTEGRATION] Используем формат с chat:', messagesArray.length); - } else if (chatData && chatData.chat && Array.isArray(chatData.chat.messages)) { - messagesArray = chatData.chat.messages; - console.log('[INTEGRATION] Используем вложенный формат:', messagesArray.length); - } else { - console.warn('[INTEGRATION] Неизвестный формат ответа:', chatData); - messagesArray = []; - } - - console.log('[INTEGRATION] Финальный messagesArray:', { - messagesArray, - isArray: Array.isArray(messagesArray), - length: messagesArray?.length, - firstMessage: messagesArray?.[0], - }); - - // Конвертация сообщений - if (Array.isArray(messagesArray) && messagesArray.length > 0) { - const convertedMessages = messagesArray - .filter((msg) => msg && typeof msg === 'object') - .map((msg, index) => ({ - id: msg.id || String(msg.timestamp || Date.now() + index), - text: msg.content || msg.text || '', - isUser: msg.role ? msg.role === 'user' : !!msg.isUser, - timestamp: msg.timestamp || Date.now(), - })); - console.log('[INTEGRATION] convertedMessages:', convertedMessages); - this.messages = convertedMessages; - } else { - console.log('[INTEGRATION] messagesArray пустой или не массив, устанавливаем пустой массив'); - this.messages = []; - } - - this.loading = false; - }, - - // Имитация handleSendMessage - async handleSendMessage(messageText) { - console.log('[INTEGRATION] handleSendMessage: попытка отправки', { messageText }); - if (!messageText.trim()) return; - - const newMessage = { - id: Date.now().toString(), - text: messageText.trim(), - isUser: true, - timestamp: Date.now(), - }; - - // Добавляем в локальный стейт оптимистично - this.messages.push(newMessage); - - try { - const response = await integrationTest.mockChromeRuntime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: this.pluginId, - pageKey: this.pageKey, - message: { - role: 'user', - content: newMessage.text, - timestamp: newMessage.timestamp, - }, - }); - console.log('[INTEGRATION] handleSendMessage: сообщение отправлено', newMessage, response); - - // Перезагружаем историю чата - await this.loadChat(); - } catch (e) { - console.error('[INTEGRATION] handleSendMessage: ошибка отправки', e); - this.error = 'Ошибка сохранения сообщения'; - - // Удаляем оптимистично добавленное сообщение - this.messages = this.messages.filter(msg => msg.id !== newMessage.id); - } - } - }, - - // Имитация chrome.runtime API - mockChromeRuntime: { - async sendMessage(message) { - console.log('[MOCK_CHROME] sendMessage:', message); - - return new Promise((resolve) => { - setTimeout(() => { - integrationTest.mockBackground.handleMessage(message, {}, resolve); - }, Math.random() * 100 + 50); - }); - }, - - onMessage: { - listeners: [], - - addListener(callback) { - this.listeners.push(callback); - }, - - removeListener(callback) { - const index = this.listeners.indexOf(callback); - if (index > -1) { - this.listeners.splice(index, 1); - } - } - } - }, - - // Имитация background скрипта - mockBackground: { - chatStorage: {}, - - handleMessage(message, sender, sendResponse) { - console.log('[MOCK_BACKGROUND] handleMessage:', message.type); - - switch (message.type) { - case 'GET_PLUGIN_CHAT': - const chatKey = `${message.pluginId}::${message.pageKey}`; - const chat = this.chatStorage[chatKey]; - - console.log('[MOCK_BACKGROUND] GET_PLUGIN_CHAT:', { - chatKey, - chat, - messages: chat?.messages - }); - - console.log('[MOCK_BACKGROUND] sendResponse(GET_PLUGIN_CHAT):', { - messages: chat?.messages || [], - pluginId: message.pluginId, - pageKey: message.pageKey - }); - - console.log('[MOCK_BACKGROUND] ДО sendResponse - тип chat:', typeof chat); - console.log('[MOCK_BACKGROUND] ДО sendResponse - ключи chat:', chat ? Object.keys(chat) : 'chat is null/undefined'); - console.log('[MOCK_BACKGROUND] ДО sendResponse - chat.messages:', chat?.messages); - console.log('[MOCK_BACKGROUND] ДО sendResponse - Array.isArray(chat?.messages):', Array.isArray(chat?.messages)); - - sendResponse({ - messages: chat?.messages || [], - pluginId: message.pluginId, - pageKey: message.pageKey - }); - - console.log('[MOCK_BACKGROUND] ПОСЛЕ sendResponse - ответ отправлен'); - - // Диагностика после отправки ответа - console.log('[MOCK_BACKGROUND] ДОБАВЛЕНИЕ: Диагностика после sendResponse:', { - chatKey, - pageKey: message.pageKey, - timestamp: Date.now(), - }); - break; - - case 'SAVE_PLUGIN_CHAT_MESSAGE': - const saveChatKey = `${message.pluginId}::${message.pageKey}`; - - if (!this.chatStorage[saveChatKey]) { - this.chatStorage[saveChatKey] = { - chatKey: saveChatKey, - pluginId: message.pluginId, - pageKey: message.pageKey, - messages: [], - createdAt: Date.now(), - updatedAt: Date.now() - }; - } - - this.chatStorage[saveChatKey].messages.push(message.message); - this.chatStorage[saveChatKey].updatedAt = Date.now(); - - console.log('[MOCK_BACKGROUND] SAVE_PLUGIN_CHAT_MESSAGE:', { - chatKey: saveChatKey, - message: message.message, - updatedChat: this.chatStorage[saveChatKey] - }); - - sendResponse({ success: true }); - - // Имитируем broadcastChatUpdate - setTimeout(() => { - integrationTest.mockChromeRuntime.onMessage.listeners.forEach(listener => { - listener({ - type: 'PLUGIN_CHAT_UPDATED', - pluginId: message.pluginId, - pageKey: message.pageKey - }); - }); - }, 100); - break; - - default: - sendResponse({ error: 'Unknown message type' }); - } - } - } -}; - -// === ТЕСТОВЫЕ СЦЕНАРИИ === - -async function runIntegrationTests() { - console.log('🔗 Начинаем интеграционное тестирование чата...\n'); - - const panel = integrationTest.mockPluginControlPanel; - - try { - // Тест 1: Загрузка пустого чата - console.log('📥 Тест 1: Загрузка пустого чата'); - await panel.loadChat(); - console.log('Состояние после загрузки:', { - messages: panel.messages.length, - loading: panel.loading, - error: panel.error - }); - - // Тест 2: Отправка сообщения - console.log('\n💬 Тест 2: Отправка сообщения'); - await panel.handleSendMessage('Тестовое сообщение для интеграции'); - console.log('Состояние после отправки:', { - messages: panel.messages.length, - loading: panel.loading, - error: panel.error - }); - - // Тест 3: Повторная загрузка чата - console.log('\n📥 Тест 3: Повторная загрузка чата'); - await panel.loadChat(); - console.log('Состояние после повторной загрузки:', { - messages: panel.messages.length, - loading: panel.loading, - error: panel.error, - messagesContent: panel.messages.map(m => ({ text: m.text, isUser: m.isUser })) - }); - - // Тест 4: Отправка еще одного сообщения - console.log('\n💬 Тест 4: Отправка второго сообщения'); - await panel.handleSendMessage('Второе тестовое сообщение'); - console.log('Состояние после второго сообщения:', { - messages: panel.messages.length, - loading: panel.loading, - error: panel.error, - messagesContent: panel.messages.map(m => ({ text: m.text, isUser: m.isUser })) - }); - - // Тест 5: Финальная проверка - console.log('\n📊 Тест 5: Финальная проверка состояния'); - await panel.loadChat(); - console.log('Финальное состояние:', { - messagesCount: panel.messages.length, - loading: panel.loading, - error: panel.error, - allMessages: panel.messages.map(m => ({ id: m.id, text: m.text, isUser: m.isUser, timestamp: m.timestamp })) - }); - - // Проверка корректности - const expectedMessages = 2; - const actualMessages = panel.messages.length; - - if (actualMessages === expectedMessages && !panel.error) { - console.log('\n✅ ИНТЕГРАЦИОННОЕ ТЕСТИРОВАНИЕ ПРОШЛО УСПЕШНО!'); - console.log(`📈 Отправлено и сохранено ${actualMessages} сообщений`); - console.log('🔄 Синхронизация между компонентами работает корректно'); - console.log('⚡ Обработка ошибок и undefined ответов реализована правильно'); - } else { - console.log('\n❌ ИНТЕГРАЦИОННОЕ ТЕСТИРОВАНИЕ ВЫЯВИЛО ПРОБЛЕМЫ:'); - console.log(`Ожидалось: ${expectedMessages} сообщений`); - console.log(`Получено: ${actualMessages} сообщений`); - if (panel.error) { - console.log(`Ошибка: ${panel.error}`); - } - } - - } catch (error) { - console.error('❌ Критическая ошибка интеграционного тестирования:', error); - } -} - -// Запуск -if (typeof module !== 'undefined' && module.exports) { - module.exports = { integrationTest, runIntegrationTests }; -} else { - runIntegrationTests(); -} \ No newline at end of file diff --git a/test-scripts/ozon-test.js b/test-scripts/ozon-test.js deleted file mode 100644 index 810feac7..00000000 --- a/test-scripts/ozon-test.js +++ /dev/null @@ -1,242 +0,0 @@ -/* eslint-disable no-undef */ -// Тестовый скрипт для создания чатов на странице Ozon -// Запускать в консоли DevTools расширения (не в консоли браузера!) - -console.log('🎯 Тестирование чатов на странице Ozon...'); - -// Получаем текущий URL из активной вкладки -async function getCurrentUrl() { - try { - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); - return tab.url; - } catch (error) { - console.error('❌ Ошибка получения URL:', error); - return window.location.href; // fallback - } -} - -// Функция для создания чата для ozon-analyzer -async function createOzonChat() { - try { - console.log('📝 Создание чата для ozon-analyzer...'); - - const currentUrl = await getCurrentUrl(); - console.log('📍 Текущий URL:', currentUrl); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение пользователя - const userMessage = { - role: 'user', - content: 'Проанализируй этот товар и расскажи о его характеристиках', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - message: userMessage, - }); - - console.log('✅ Сообщение пользователя сохранено:', userMessage); - - // Сохраняем ответ плагина - const pluginMessage = { - role: 'plugin', - content: - 'Анализирую товар "Костюм спортивный North Dot"... Найдена информация о цене, характеристиках и отзывах.', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - message: pluginMessage, - }); - - console.log('✅ Ответ плагина сохранен:', pluginMessage); - - // Сохраняем черновик - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_DRAFT', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - draftText: 'Хотите узнать больше о доставке или отзывах?', - }); - - console.log('✅ Черновик сохранен'); - - return true; - } catch (error) { - console.error('❌ Ошибка создания чата:', error); - return false; - } -} - -// Функция для создания чата для test-chat-plugin -async function createTestPluginChat() { - try { - console.log('📝 Создание чата для test-chat-plugin...'); - - const currentUrl = await getCurrentUrl(); - - // Создаем чат - const chat = await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId: 'test-chat-plugin', - pageKey: currentUrl, - }); - - console.log('✅ Чат создан:', chat); - - // Сохраняем тестовое сообщение - const message = { - role: 'user', - content: 'Это тестовое сообщение с Ozon', - timestamp: Date.now(), - }; - - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'test-chat-plugin', - pageKey: currentUrl, - message: message, - }); - - console.log('✅ Тестовое сообщение сохранено:', message); - - return true; - } catch (error) { - console.error('❌ Ошибка создания тестового чата:', error); - return false; - } -} - -// Функция для отправки тестовых логов -async function sendTestLogs() { - try { - console.log('📝 Отправка тестовых логов...'); - - const currentUrl = await getCurrentUrl(); - - const logs = [ - { - pluginId: 'ozon-analyzer', - level: 'info', - stepId: 'page-load', - message: 'Страница товара загружена', - logData: { url: currentUrl, title: document.title }, - }, - { - pluginId: 'ozon-analyzer', - level: 'success', - stepId: 'product-found', - message: 'Товар найден: Костюм спортивный North Dot', - logData: { productId: '1438414833' }, - }, - { - pluginId: 'test-chat-plugin', - level: 'debug', - stepId: 'test-debug', - message: 'Тестовый отладочный лог', - logData: { test: true, timestamp: Date.now() }, - }, - ]; - - for (const log of logs) { - await chrome.runtime.sendMessage({ - type: 'LOG_EVENT', - pluginId: log.pluginId, - pageKey: currentUrl, - level: log.level, - stepId: log.stepId, - message: log.message, - logData: log.logData, - }); - - console.log(`✅ Лог отправлен: ${log.pluginId} - ${log.message}`); - } - } catch (error) { - console.error('❌ Ошибка отправки логов:', error); - } -} - -// Функция для получения всех данных -async function getAllData() { - try { - console.log('📋 Получение всех данных...'); - - // Получаем чаты - const chats = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHATS', - pluginId: null, - }); - - console.log('✅ Чаты найдены:', chats); - - // Получаем черновики - const drafts = await chrome.runtime.sendMessage({ - type: 'LIST_PLUGIN_CHAT_DRAFTS', - pluginId: null, - }); - - console.log('✅ Черновики найдены:', drafts); - - // Получаем логи - const logs = await chrome.runtime.sendMessage({ - type: 'LIST_ALL_PLUGIN_LOGS', - }); - - console.log('✅ Логи найдены:', logs); - - return { chats, drafts, logs }; - } catch (error) { - console.error('❌ Ошибка получения данных:', error); - return { chats: [], drafts: [], logs: {} }; - } -} - -// Запуск всех тестов -async function runOzonTests() { - console.log('🚀 Запуск тестов для Ozon...'); - - await createOzonChat(); - await createTestPluginChat(); - await sendTestLogs(); - await getAllData(); - - console.log('✅ Все тесты завершены!'); - console.log('💡 Теперь откройте DevTools и перейдите на вкладки:'); - console.log(' - "Чаты плагинов" - для просмотра чатов и черновиков'); - console.log(' - "Логи" - для просмотра логов плагинов'); -} - -// Экспортируем функции для использования в консоли -window.ozonTestSystem = { - createOzonChat, - createTestPluginChat, - sendTestLogs, - getAllData, - runOzonTests, - getCurrentUrl, -}; - -console.log('🎯 Функции тестирования Ozon доступны:'); -console.log('- ozonTestSystem.createOzonChat() - создать чат для ozon-analyzer'); -console.log('- ozonTestSystem.createTestPluginChat() - создать чат для test-chat-plugin'); -console.log('- ozonTestSystem.sendTestLogs() - отправить тестовые логи'); -console.log('- ozonTestSystem.getAllData() - получить все данные'); -console.log('- ozonTestSystem.runOzonTests() - запустить все тесты'); -console.log('- ozonTestSystem.getCurrentUrl() - получить текущий URL'); - -// Автоматически запускаем тесты -runOzonTests(); diff --git a/test-scripts/test-chat-functionality.js b/test-scripts/test-chat-functionality.js deleted file mode 100644 index 56e7cdf9..00000000 --- a/test-scripts/test-chat-functionality.js +++ /dev/null @@ -1,200 +0,0 @@ -// Тест функциональности чата - проверка исправлений -// Этот скрипт тестирует все аспекты работы чата после исправлений - -console.log('=== ТЕСТИРОВАНИЕ ФУНКЦИОНАЛЬНОСТИ ЧАТА ==='); - -// Тест 1: Проверка работы plugin-chat-api -async function testPluginChatAPI() { - console.log('\n--- Тест 1: Проверка plugin-chat-api ---'); - - try { - // Проверяем доступность API - const response = await chrome.runtime.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }); - - console.log('✓ API доступен, ответ:', response); - - // Проверяем структуру ответа - if (response && typeof response === 'object') { - console.log('✓ Структура ответа корректна'); - if (Array.isArray(response.messages)) { - console.log('✓ messages - массив'); - } else { - console.log('⚠ messages не является массивом:', response.messages); - } - } else { - console.log('⚠ Ответ не является объектом'); - } - - } catch (error) { - console.error('✗ Ошибка при тестировании API:', error); - } -} - -// Тест 2: Проверка механизма кэширования -async function testCachingMechanism() { - console.log('\n--- Тест 2: Проверка механизма кэширования ---'); - - try { - const pluginId = 'test-plugin-' + Date.now(); - const pageKey = 'test-page-' + Date.now(); - - // Создаем чат - await chrome.runtime.sendMessage({ - type: 'CREATE_PLUGIN_CHAT', - pluginId, - pageKey - }); - - // Получаем чат - const chat1 = await chrome.runtime.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId, - pageKey - }); - - console.log('✓ Чат создан и получен:', chat1); - - // Сохраняем сообщение - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId, - pageKey, - message: { - role: 'user', - content: 'Тестовое сообщение для проверки кэширования', - timestamp: Date.now() - } - }); - - // Получаем чат снова - const chat2 = await chrome.runtime.sendMessage({ - type: 'GET_PLUGIN_CHAT', - pluginId, - pageKey - }); - - console.log('✓ Сообщение сохранено, обновленный чат:', chat2); - - // Проверяем, что сообщение добавлено - if (chat2 && chat2.messages && chat2.messages.length > 0) { - console.log('✓ Сообщение успешно сохранено в кэше'); - } else { - console.log('⚠ Сообщение не найдено в кэше'); - } - - } catch (error) { - console.error('✗ Ошибка при тестировании кэширования:', error); - } -} - -// Тест 3: Проверка работы черновиков -async function testDraftFunctionality() { - console.log('\n--- Тест 3: Проверка работы черновиков ---'); - - try { - const pluginId = 'draft-test-' + Date.now(); - const pageKey = 'draft-page-' + Date.now(); - const draftText = 'Тестовый черновик для проверки'; - - // Сохраняем черновик - await chrome.runtime.sendMessage({ - type: 'SAVE_PLUGIN_CHAT_DRAFT', - pluginId, - pageKey, - draftText - }); - - console.log('✓ Черновик сохранен'); - - // Получаем черновик - const response = await chrome.runtime.sendMessage({ - type: 'GET_PLUGIN_CHAT_DRAFT', - pluginId, - pageKey - }); - - console.log('✓ Черновик получен:', response); - - if (response && response.draftText === draftText) { - console.log('✓ Черновик корректно сохранен и получен'); - } else { - console.log('⚠ Черновик не совпадает с ожидаемым значением'); - } - - } catch (error) { - console.error('✗ Ошибка при тестировании черновиков:', error); - } -} - -// Тест 4: Проверка localStorage -async function testLocalStorage() { - console.log('\n--- Тест 4: Проверка localStorage ---'); - - try { - // Проверяем доступ к chrome.storage.local - const allData = await new Promise(resolve => { - chrome.storage.local.get(null, resolve); - }); - - console.log('✓ chrome.storage.local доступен, общее количество ключей:', Object.keys(allData).length); - - // Ищем ключи чатов - const chatKeys = Object.keys(allData).filter(key => key.includes('::')); - console.log('✓ Найдено ключей чатов:', chatKeys.length); - - if (chatKeys.length > 0) { - console.log('✓ Примеры ключей чатов:', chatKeys.slice(0, 3)); - } - - } catch (error) { - console.error('✗ Ошибка при тестировании localStorage:', error); - } -} - -// Тест 5: Проверка компонентов интерфейса -function testUIComponents() { - console.log('\n--- Тест 5: Проверка компонентов интерфейса ---'); - - // Проверяем наличие элементов в DOM - setTimeout(() => { - const chatMessages = document.querySelector('.chat-messages'); - const chatInput = document.querySelector('.chat-input-container textarea'); - - if (chatMessages) { - console.log('✓ Элемент .chat-messages найден'); - } else { - console.log('⚠ Элемент .chat-messages не найден'); - } - - if (chatInput) { - console.log('✓ Элемент .chat-input-container textarea найден'); - } else { - console.log('⚠ Элемент .chat-input-container textarea не найден'); - } - }, 1000); -} - -// Основная функция запуска тестов -async function runAllTests() { - console.log('🚀 Запуск тестирования функциональности чата...'); - - await testPluginChatAPI(); - await testCachingMechanism(); - await testDraftFunctionality(); - await testLocalStorage(); - testUIComponents(); - - console.log('\n✅ Тестирование завершено!'); - console.log('📊 Проверьте консоль для детальных результатов'); -} - -// Запускаем тесты при загрузке страницы -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', runAllTests); -} else { - runAllTests(); -} \ No newline at end of file diff --git a/test-scripts/test-message-communication.js b/test-scripts/test-message-communication.js deleted file mode 100644 index 5062931f..00000000 --- a/test-scripts/test-message-communication.js +++ /dev/null @@ -1,239 +0,0 @@ -// Тестовый скрипт для проверки исправлений коммуникации между PluginControlPanel и background -// Запуск: node test-scripts/test-message-communication.js - -console.log('🧪 Тестирование исправлений коммуникации message port...'); - -// Имитация chrome.runtime API для тестирования -const mockChrome = { - runtime: { - sendMessage: (message, callback) => { - console.log('📤 Отправка сообщения:', message); - - // Имитация ответа background script - setTimeout(() => { - if (message.type === 'GET_PLUGIN_CHAT') { - callback({ - messages: [ - { - id: 'test-1', - content: 'Тестовое сообщение 1', - role: 'user', - timestamp: Date.now() - }, - { - id: 'test-2', - content: 'Тестовое сообщение 2', - role: 'bot', - timestamp: Date.now() - } - ] - }); - } else if (message.type === 'SAVE_PLUGIN_CHAT_MESSAGE') { - callback({ success: true }); - } else { - callback({ error: 'Неизвестный тип сообщения' }); - } - }, 100); // Имитация задержки background - }, - - lastError: null, - - onMessage: { - addListener: (listener) => { - console.log('📡 Добавлен слушатель сообщений'); - }, - removeListener: (listener) => { - console.log('🔌 Удален слушатель сообщений'); - } - } - } -}; - -// Глобальный объект chrome для тестирования -global.chrome = mockChrome; - -// Имитация Promise-based подхода (как в исправленном коде) -function sendMessageToBackground(message) { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { - reject(new Error('Message timeout after 10000ms')); - }, 10000); - - chrome.runtime.sendMessage(message, (response) => { - clearTimeout(timeoutId); - - if (chrome.runtime.lastError) { - console.error('❌ sendMessageToBackground error:', chrome.runtime.lastError); - reject(new Error(chrome.runtime.lastError.message)); - return; - } - - if (response === undefined) { - console.warn('⚠️ sendMessageToBackground: received undefined response'); - reject(new Error('Background script returned undefined')); - return; - } - - resolve(response); - }); - }); -} - -// Тест 1: Проверка GET_PLUGIN_CHAT -async function testGetPluginChat() { - console.log('\n📋 Тест 1: GET_PLUGIN_CHAT'); - - try { - const response = await sendMessageToBackground({ - type: 'GET_PLUGIN_CHAT', - pluginId: 'test-plugin', - pageKey: 'test-page' - }); - - console.log('✅ GET_PLUGIN_CHAT успешно:', response); - console.log(' - Сообщений:', response.messages?.length || 0); - console.log(' - Первое сообщение:', response.messages?.[0]?.content); - - return true; - } catch (error) { - console.error('❌ GET_PLUGIN_CHAT ошибка:', error.message); - return false; - } -} - -// Тест 2: Проверка SAVE_PLUGIN_CHAT_MESSAGE -async function testSavePluginChatMessage() { - console.log('\n📝 Тест 2: SAVE_PLUGIN_CHAT_MESSAGE'); - - try { - const response = await sendMessageToBackground({ - type: 'SAVE_PLUGIN_CHAT_MESSAGE', - pluginId: 'test-plugin', - pageKey: 'test-page', - message: { - role: 'user', - content: 'Тестовое сообщение для сохранения', - timestamp: Date.now() - } - }); - - console.log('✅ SAVE_PLUGIN_CHAT_MESSAGE успешно:', response); - console.log(' - Успех:', response.success); - - return true; - } catch (error) { - console.error('❌ SAVE_PLUGIN_CHAT_MESSAGE ошибка:', error.message); - return false; - } -} - -// Тест 3: Проверка timeout -async function testTimeout() { - console.log('\n⏱️ Тест 3: Проверка timeout'); - - // Изменяем mock для имитации долгого ответа - const originalSendMessage = mockChrome.runtime.sendMessage; - mockChrome.runtime.sendMessage = (message, callback) => { - // Имитация очень долгого ответа (больше timeout) - setTimeout(() => { - callback({ success: true }); - }, 15000); // 15 секунд - больше чем наш timeout 10 секунд - }; - - try { - const response = await sendMessageToBackground({ - type: 'TEST_TIMEOUT', - pluginId: 'test-plugin' - }); - - console.log('❌ Ожидался timeout, но получили ответ:', response); - return false; - } catch (error) { - if (error.message.includes('timeout')) { - console.log('✅ Timeout сработал корректно:', error.message); - return true; - } else { - console.error('❌ Неожиданная ошибка:', error.message); - return false; - } - } finally { - // Восстанавливаем оригинальную функцию - mockChrome.runtime.sendMessage = originalSendMessage; - } -} - -// Тест 4: Проверка обработки undefined response -async function testUndefinedResponse() { - console.log('\n⚠️ Тест 4: Проверка undefined response'); - - // Изменяем mock для возврата undefined - const originalSendMessage = mockChrome.runtime.sendMessage; - mockChrome.runtime.sendMessage = (message, callback) => { - callback(undefined); // Возвращаем undefined - }; - - try { - const response = await sendMessageToBackground({ - type: 'TEST_UNDEFINED', - pluginId: 'test-plugin' - }); - - console.log('❌ Ожидалась ошибка undefined, но получили ответ:', response); - return false; - } catch (error) { - if (error.message.includes('undefined')) { - console.log('✅ Обработка undefined сработала корректно:', error.message); - return true; - } else { - console.error('❌ Неожиданная ошибка:', error.message); - return false; - } - } finally { - // Восстанавливаем оригинальную функцию - mockChrome.runtime.sendMessage = originalSendMessage; - } -} - -// Основная функция тестирования -async function runTests() { - console.log('🚀 Запуск тестов исправлений коммуникации...\n'); - - const tests = [ - testGetPluginChat, - testSavePluginChatMessage, - testTimeout, - testUndefinedResponse - ]; - - let passed = 0; - let failed = 0; - - for (const test of tests) { - try { - const result = await test(); - if (result) { - passed++; - } else { - failed++; - } - } catch (error) { - console.error('💥 Критическая ошибка в тесте:', error); - failed++; - } - } - - console.log('\n📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ:'); - console.log(`✅ Пройдено: ${passed}`); - console.log(`❌ Провалено: ${failed}`); - console.log(`📈 Всего тестов: ${passed + failed}`); - - if (failed === 0) { - console.log('\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Исправления работают корректно.'); - console.log('🔧 Рекомендуется провести интеграционное тестирование в браузере.'); - } else { - console.log('\n⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ. Требуется дополнительная отладка.'); - } -} - -// Запуск тестов -runTests().catch(console.error); \ No newline at end of file diff --git a/test-scripts/test-prompt-system.js b/test-scripts/test-prompt-system.js deleted file mode 100644 index bee29cd9..00000000 --- a/test-scripts/test-prompt-system.js +++ /dev/null @@ -1,243 +0,0 @@ -/** - * Тестовый сценарий для проверки исправлений проблемы с промптами - * Проверяет: - * 1. Загружается ли manifest.json в Pyodide globals - * 2. Применяются ли кастомные промпты из настроек - * 3. Работает ли fallback логика при недоступности manifest - * 4. Корректно ли AI использует промпты из manifest - */ - -class PromptSystemTester { - constructor() { - this.testResults = { - manifestLoading: { status: 'pending', details: {} }, - customPrompts: { status: 'pending', details: {} }, - fallbackLogic: { status: 'pending', details: {} }, - aiPromptUsage: { status: 'pending', details: {} } - }; - this.logs = []; - } - - log(message, level = 'info') { - const timestamp = new Date().toISOString(); - const logEntry = { timestamp, level, message }; - this.logs.push(logEntry); - console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`); - } - - async runAllTests() { - this.log('🚀 Начинаем тестирование системы промптов...'); - - try { - await this.testManifestLoading(); - await this.testCustomPrompts(); - await this.testFallbackLogic(); - await this.testAIPromptUsage(); - - this.generateReport(); - } catch (error) { - this.log(`❌ Критическая ошибка при тестировании: ${error.message}`, 'error'); - } - } - - async testManifestLoading() { - this.log('🔍 Тест 1: Проверка загрузки manifest.json в Pyodide globals'); - - try { - const manifestResult = await this.executePythonCode(` - try: - manifest = get_pyodide_var('manifest', {}) - if manifest: - return {'success': True, 'manifest_exists': True} - else: - return {'success': True, 'manifest_exists': False, 'error': 'Manifest is empty'} - except Exception as e: - return {'success': False, 'error': str(e)} - `); - - if (manifestResult.success && manifestResult.manifest_exists) { - this.testResults.manifestLoading = { status: 'passed', details: manifestResult }; - this.log(`✅ Manifest успешно загружен`); - } else { - this.testResults.manifestLoading = { status: 'failed', details: manifestResult }; - this.log(`❌ Не удалось загрузить manifest: ${manifestResult.error}`, 'error'); - } - - } catch (error) { - this.testResults.manifestLoading = { status: 'error', details: { error: error.message } }; - this.log(`❌ Ошибка при тестировании загрузки manifest: ${error.message}`, 'error'); - } - } - - async testCustomPrompts() { - this.log('🔍 Тест 2: Проверка применения кастомных промптов из настроек'); - - try { - const customPromptsSettings = { - prompts: { - optimized: { - ru: 'КАСТОМНЫЙ ПРОМПТ ДЛЯ ТЕСТИРОВАНИЯ - OPTIMIZED RU', - en: 'CUSTOM PROMPT FOR TESTING - OPTIMIZED EN' - } - } - }; - - const testResult = await this.executePythonCode(` - try: - prompts = get_user_prompts(${JSON.stringify(customPromptsSettings)}) - optimized_ru = prompts.get('optimized', {}).get('ru', '') - custom_found = 'КАСТОМНЫЙ ПРОМПТ ДЛЯ ТЕСТИРОВАНИЯ' in optimized_ru - - return { - 'success': True, - 'custom_prompts_applied': custom_found, - 'prompt_length': len(optimized_ru) - } - except Exception as e: - return {'success': False, 'error': str(e)} - `); - - if (testResult.success && testResult.custom_prompts_applied) { - this.testResults.customPrompts = { status: 'passed', details: testResult }; - this.log(`✅ Кастомные промпты успешно применены`); - } else { - this.testResults.customPrompts = { status: 'failed', details: testResult }; - this.log(`❌ Кастомные промпты не применились: ${testResult.error}`, 'error'); - } - - } catch (error) { - this.testResults.customPrompts = { status: 'error', details: { error: error.message } }; - this.log(`❌ Ошибка при тестировании кастомных промптов: ${error.message}`, 'error'); - } - } - - async testFallbackLogic() { - this.log('🔍 Тест 3: Проверка fallback логики при недоступности manifest'); - - try { - const fallbackResult = await this.executePythonCode(` - try: - empty_settings = {} - prompts = get_user_prompts(empty_settings) - optimized_ru = prompts.get('optimized', {}).get('ru', '') - manifest_used = len(optimized_ru) > 100 - - return { - 'success': True, - 'fallback_worked': manifest_used, - 'prompt_length': len(optimized_ru) - } - except Exception as e: - return {'success': False, 'error': str(e)} - `); - - if (fallbackResult.success && fallbackResult.fallback_worked) { - this.testResults.fallbackLogic = { status: 'passed', details: fallbackResult }; - this.log(`✅ Fallback логика работает корректно`); - } else { - this.testResults.fallbackLogic = { status: 'failed', details: fallbackResult }; - this.log(`❌ Fallback логика не сработала: ${fallbackResult.error}`, 'error'); - } - - } catch (error) { - this.testResults.fallbackLogic = { status: 'error', details: { error: error.message } }; - this.log(`❌ Ошибка при тестировании fallback логики: ${error.message}`, 'error'); - } - } - - async testAIPromptUsage() { - this.log('🔍 Тест 4: Проверка корректности использования промптов AI'); - - try { - const aiTestResult = await this.executePythonCode(` - try: - plugin_settings = { - 'prompts': { - 'optimized': { - 'ru': 'ТЕСТОВЫЙ ПРОМПТ ДЛЯ ПРОВЕРКИ AI: Проанализируй состав: {composition}' - } - } - } - - prompts = get_user_prompts(plugin_settings) - optimized_ru = prompts.get('optimized', {}).get('ru', '') - has_placeholder = '{composition}' in optimized_ru - sufficient_length = len(optimized_ru) > 50 - - return { - 'success': True, - 'prompts_have_placeholders': has_placeholder, - 'prompts_sufficient_length': sufficient_length - } - except Exception as e: - return {'success': False, 'error': str(e)} - `); - - if (aiTestResult.success && aiTestResult.prompts_have_placeholders) { - this.testResults.aiPromptUsage = { status: 'passed', details: aiTestResult }; - this.log(`✅ AI корректно использует промпты`); - } else { - this.testResults.aiPromptUsage = { status: 'failed', details: aiTestResult }; - this.log(`❌ Проблемы с использованием промптов AI: ${aiTestResult.error}`, 'error'); - } - - } catch (error) { - this.testResults.aiPromptUsage = { status: 'error', details: { error: error.message } }; - this.log(`❌ Ошибка при тестировании использования промптов AI: ${error.message}`, 'error'); - } - } - - async executePythonCode(code) { - return new Promise((resolve, reject) => { - chrome.runtime.sendMessage({ - type: 'EXECUTE_PYTHON_TEST', - data: { code: code, testId: Date.now() } - }, (response) => { - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response.result); - } - }); - }); - } - - generateReport() { - const totalTests = Object.keys(this.testResults).length; - const passedTests = Object.values(this.testResults).filter(r => r.status === 'passed').length; - - console.log('\n' + '='.repeat(50)); - console.log('📋 ОТЧЕТ О ТЕСТИРОВАНИИ ПРОМПТОВ'); - console.log('='.repeat(50)); - console.log(`🧪 Всего тестов: ${totalTests}`); - console.log(`✅ Пройдено: ${passedTests}`); - console.log(`📊 Успешность: ${Math.round((passedTests / totalTests) * 100)}%`); - - Object.entries(this.testResults).forEach(([testName, result]) => { - const icon = result.status === 'passed' ? '✅' : '❌'; - console.log(`${icon} ${this.getTestName(testName)}: ${result.status.toUpperCase()}`); - }); - - return { - summary: { - total: totalTests, - passed: passedTests, - successRate: Math.round((passedTests / totalTests) * 100) - }, - results: this.testResults - }; - } - - getTestName(testKey) { - const names = { - manifestLoading: 'Загрузка manifest.json', - customPrompts: 'Кастомные промпты', - fallbackLogic: 'Fallback логика', - aiPromptUsage: 'Использование промптов AI' - }; - return names[testKey] || testKey; - } -} - -// Экспортируем для использования -window.PromptSystemTester = PromptSystemTester; diff --git a/test-scripts/test-pyodide-large-data.js b/test-scripts/test-pyodide-large-data.js deleted file mode 100644 index d2002dd4..00000000 --- a/test-scripts/test-pyodide-large-data.js +++ /dev/null @@ -1,205 +0,0 @@ -/* eslint-disable no-undef */ -// Тестовый скрипт для проверки передачи больших данных в Pyodide -// Запускать в консоли DevTools расширения - -console.log('🧪 Тестирование передачи больших данных в Pyodide...'); - -// Функция для создания тестовых больших данных -function generateLargeHtml(sizeInChars = 1052461) { - console.log(`📊 Генерация HTML данных размером ${sizeInChars} символов...`); - - let html = ` - - - - Тестовый товар Ozon - Большой HTML - - -
-

Тестовый товар для анализа Pyodide

-
₽ 1,999
-
-

Это тестовый товар для проверки передачи больших данных в Pyodide через глобальное пространство.

-
-
-

Характеристики

-
    `; - - // Добавляем много характеристик для достижения нужного размера - for (let i = 0; i < 1000; i++) { - html += ` -
  • Характеристика ${i + 1}: Значение характеристики номер ${i + 1} с подробным описанием для увеличения размера данных
  • `; - } - - html += ` -
-
-
-

Отзывы покупателей

`; - - // Добавляем много отзывов - for (let i = 0; i < 500; i++) { - html += ` -
-

Отзыв ${i + 1}

-

Это тестовый отзыв номер ${i + 1} от покупателя. Товар пришел в отличном состоянии, все работает как надо. Качество на высоте, рекомендую всем знакомым. Доставка была быстрой, упаковка надежной. Буду покупать еще.

-
★★★★★
-
Покупатель ${i + 1}
-
2024-01-${String((i % 28) + 1).padStart(2, '0')}
-
`; - } - - // Добавляем дополнительные разделы для достижения размера - html += ` -
-
-

Дополнительная информация

-
-

Доставка

-

Бесплатная доставка при заказе от 1000 рублей. Срок доставки 1-3 дня.

-
-
-

Гарантия

-

Гарантия производителя 1 год. Возможен возврат в течение 30 дней.

-
-
-
-

Часто задаваемые вопросы

`; - - // Добавляем много FAQ - for (let i = 0; i < 300; i++) { - html += ` -
-

Вопрос ${i + 1}: Как использовать этот товар?

-

Ответ на вопрос ${i + 1}: Для использования этого товара следуйте инструкции производителя. Подробное описание с множеством деталей для увеличения размера данных и тестирования передачи в Pyodide.

-
`; - } - - html += ` -
-
- -`; - - // Если размер меньше нужного, добавляем повторяющийся контент - while (html.length < sizeInChars) { - html += ` -
-

Дополнительный контент для увеличения размера: ${html.length}/${sizeInChars}. ${'Тестовые данные для проверки передачи больших строк в Pyodide. '.repeat(10)}

-
`; - } - - // Обрезаем до точного размера - if (html.length > sizeInChars) { - html = html.substring(0, sizeInChars); - // Убеждаемся, что HTML остается валидным - if (!html.endsWith('')) { - html += ''; - } - } - - console.log(`✅ Сгенерирован HTML размером ${html.length} символов`); - return html; -} - -// Функция для тестирования EXECUTE_WORKFLOW с большими данными -async function testExecuteWorkflowWithLargeData() { - try { - console.log('🚀 Запуск теста EXECUTE_WORKFLOW с большими данными...'); - - // Генерируем большие тестовые данные - const largeHtml = generateLargeHtml(1052461); - console.log(`📊 Размер тестовых данных: ${largeHtml.length} символов`); - - // Получаем текущий URL для теста - const currentUrl = window.location.href; - console.log('📍 Текущий URL:', currentUrl); - - // Создаем сообщение для EXECUTE_WORKFLOW - const message = { - type: 'EXECUTE_WORKFLOW', - pluginId: 'ozon-analyzer', - pageKey: currentUrl, - requestId: `test_large_data_${Date.now()}`, - pageHtml: largeHtml, // Передаем большие данные напрямую - useChunks: false - }; - - console.log('📤 Отправка сообщения EXECUTE_WORKFLOW в background...'); - - // Отправляем сообщение в background script - const response = await chrome.runtime.sendMessage(message); - - console.log('📥 Получен ответ от background:', response); - - if (response && response.success) { - console.log('✅ Тест прошел успешно!'); - console.log('📊 Результат анализа:', response.result); - console.log('📊 Размер обработанных данных:', largeHtml.length, 'символов'); - } else { - console.error('❌ Тест провален:', response?.error || 'Неизвестная ошибка'); - } - - return response; - - } catch (error) { - console.error('❌ Критическая ошибка при тестировании:', error); - return { success: false, error: error.message }; - } -} - -// Функция для тестирования с разными размерами данных -async function testMultipleSizes() { - const sizes = [10000, 50000, 100000, 500000, 1000000, 1052461]; - - console.log('🧪 Запуск серии тестов с разными размерами данных...'); - - for (const size of sizes) { - console.log(`\n📏 Тестирование с размером ${size} символов...`); - - try { - const largeHtml = generateLargeHtml(size); - const startTime = Date.now(); - - const message = { - type: 'EXECUTE_WORKFLOW', - pluginId: 'ozon-analyzer', - pageKey: window.location.href, - requestId: `test_size_${size}_${Date.now()}`, - pageHtml: largeHtml, - useChunks: false - }; - - const response = await chrome.runtime.sendMessage(message); - const endTime = Date.now(); - - console.log(`⏱️ Время обработки: ${endTime - startTime}ms`); - console.log(`📊 Статус: ${response?.success ? '✅ УСПЕХ' : '❌ ПРОВАЛ'}`); - - if (!response?.success) { - console.error('❌ Ошибка:', response?.error); - break; // Прерываем тест при первой ошибке - } - - } catch (error) { - console.error(`❌ Ошибка при тестировании размера ${size}:`, error); - break; - } - } - - console.log('🏁 Серия тестов завершена'); -} - -// Экспортируем функции для использования в консоли -window.pyodideTest = { - testExecuteWorkflowWithLargeData, - testMultipleSizes, - generateLargeHtml, -}; - -console.log('🎯 Функции тестирования Pyodide доступны:'); -console.log('- pyodideTest.testExecuteWorkflowWithLargeData() - тест с большими данными (1M+ символов)'); -console.log('- pyodideTest.testMultipleSizes() - серия тестов с разными размерами'); -console.log('- pyodideTest.generateLargeHtml(size) - генерация HTML заданного размера'); - -console.log('💡 Рекомендуется начать с: pyodideTest.testExecuteWorkflowWithLargeData()'); \ No newline at end of file diff --git a/test_custom_prompts_scenario.py b/test_custom_prompts_scenario.py deleted file mode 100644 index 5d1e9a36..00000000 --- a/test_custom_prompts_scenario.py +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 2: Проверка кастомных промптов с переопределением - -Цель: Проверить, что кастомные промпты из пользовательских настроек -корректно переопределяют оригинальные промпты из manifest.json -""" - -import json -import sys -import os - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - # Имитируем наличие manifest.json в globals - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов (скопированная из mcp_server.py для тестирования). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТОВЫЙ СЦЕНАРИЙ 2) =====") - - try: - # Диагностика доступа к manifest - manifest = get_pyodide_var('manifest', {}) - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - else: - manifest_prompts = {} - - # Используем кастомные промпты из plugin_settings - if plugin_settings and isinstance(plugin_settings, dict): - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - console_log(f" Кастомные промпты найдены: {custom_prompts_raw}") - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка кастомного промпта {prompt_type}.{lang}:") - - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - console_log(f" prompt_type_data ({prompt_type}): {prompt_type_data}") - - custom_value = safe_dict_get(prompt_type_data, lang, '') - console_log(f" custom_value для {lang}: {repr(custom_value)}") - console_log(f" custom_value type: {type(custom_value)}") - console_log(f" custom_value length: {len(custom_value) if custom_value else 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - console_log(f"ℹ️ Кастомный промпт не найден или пустой для {prompt_type}.{lang}") - - # Fallback на manifest.json - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - else: - console_log("ℹ️ Пользовательские настройки не переданы или некорректны - используем manifest") - - # Fallback на manifest.json - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт из manifest: {prompt_type}.{lang}") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def run_test_scenario_2(): - """Выполнение тестового сценария 2.""" - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 2: Проверка кастомных промптов с переопределением") - print("=" * 80) - - # Тест 1: Проверка кастомных промптов с переопределением - print("\n📋 ТЕСТ 1: Кастомные промпты должны переопределять оригинальные") - print("-" * 60) - - # Кастомные промпты для тестирования - custom_prompts = { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ОПТИМИЗИРОВАННЫЙ ПРОМПТ НА РУССКОМ ЯЗЫКЕ для анализа косметики', - 'en': 'CUSTOM OPTIMIZED PROMPT IN ENGLISH for cosmetic analysis' - }, - 'deep': { - 'ru': 'КАСТОМНЫЙ ГЛУБОКИЙ ПРОМПТ НА РУССКОМ ЯЗЫКЕ для детального анализа', - 'en': 'CUSTOM DEEP PROMPT IN ENGLISH for detailed analysis' - } - } - - # Пользовательские настройки с кастомными промптами - plugin_settings = { - 'response_language': 'ru', - 'prompts': custom_prompts - } - - try: - prompts = get_user_prompts(plugin_settings) - - # Проверяем что кастомные промпты загружены - custom_loaded = 0 - total_custom = 4 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - expected_value = custom_prompts[prompt_type][lang] - - if prompt_value == expected_value: - custom_loaded += 1 - print(f" ✅ {prompt_type}.{lang}: кастомный промпт загружен корректно") - else: - print(f" ❌ {prompt_type}.{lang}: кастомный промпт НЕ загружен (ожидали: '{expected_value[:50]}...', получили: '{prompt_value[:50]}...')") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 1: {custom_loaded}/{total_custom} кастомных промптов загружено") - - if custom_loaded == total_custom: - print("✅ ТЕСТ 1 ПРОЙДЕН: Кастомные промпты корректно переопределяют оригинальные") - else: - print("❌ ТЕСТ 1 НЕ ПРОЙДЕН: Кастомные промпты не переопределяют оригинальные") - - except Exception as e: - print(f"❌ ТЕСТ 1 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 2: Частичное переопределение промптов - print("\n📋 ТЕСТ 2: Частичное переопределение промптов") - print("-" * 60) - - # Только часть промптов кастомные - partial_custom_prompts = { - 'optimized': { - 'ru': 'ЧАСТИЧНО КАСТОМНЫЙ ПРОМПТ ТОЛЬКО ДЛЯ РУССКОГО' - }, - 'deep': { - 'en': 'PARTIAL CUSTOM PROMPT ONLY FOR ENGLISH' - } - } - - partial_plugin_settings = { - 'response_language': 'ru', - 'prompts': partial_custom_prompts - } - - try: - prompts = get_user_prompts(partial_plugin_settings) - - # Проверяем частичное переопределение - partial_checks = [ - ('optimized.ru', partial_custom_prompts['optimized']['ru']), - ('deep.en', partial_custom_prompts['deep']['en']) - ] - - partial_correct = 0 - - for prompt_key, expected_value in partial_checks: - prompt_type, lang = prompt_key.split('.') - actual_value = prompts[prompt_type][lang] - - if actual_value == expected_value: - partial_correct += 1 - print(f" ✅ {prompt_key}: кастомный промпт загружен") - else: - print(f" ❌ {prompt_key}: кастомный промпт НЕ загружен") - - # Проверяем что остальные промпты взяты из manifest - other_prompts_count = 0 - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_key = f"{prompt_type}.{lang}" - if (prompt_type, lang) not in [('optimized', 'ru'), ('deep', 'en')]: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - other_prompts_count += 1 - print(f" ✅ {prompt_key}: промпт из manifest загружен") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 2: {partial_correct}/2 кастомных + {other_prompts_count}/2 из manifest") - - if partial_correct == 2 and other_prompts_count == 2: - print("✅ ТЕСТ 2 ПРОЙДЕН: Частичное переопределение работает корректно") - else: - print("❌ ТЕСТ 2 НЕ ПРОЙДЕН: Частичное переопределение не работает") - - except Exception as e: - print(f"❌ ТЕСТ 2 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 3: Пустые или некорректные кастомные промпты - print("\n📋 ТЕСТ 3: Обработка пустых или некорректных кастомных промптов") - print("-" * 60) - - # Кастомные промпты с пустыми значениями - empty_custom_prompts = { - 'optimized': { - 'ru': '', # Пустой промпт - 'en': ' ' # Только пробелы - }, - 'deep': { - 'ru': None, # None значение - 'en': 'ВАЛИДНЫЙ КАСТОМНЫЙ ПРОМПТ' # Валидный промпт - } - } - - empty_plugin_settings = { - 'response_language': 'ru', - 'prompts': empty_custom_prompts - } - - try: - prompts = get_user_prompts(empty_plugin_settings) - - # Проверяем обработку пустых значений - empty_checks = [ - ('optimized.ru', False), # Должен быть взят из manifest - ('optimized.en', False), # Должен быть взят из manifest - ('deep.ru', False), # Должен быть взят из manifest - ('deep.en', True) # Должен быть кастомный - ] - - empty_correct = 0 - - for prompt_key, should_be_custom in empty_checks: - prompt_type, lang = prompt_key.split('.') - actual_value = prompts[prompt_type][lang] - expected_custom = empty_custom_prompts[prompt_type][lang] - - if should_be_custom: - # Должен быть кастомный промпт - if actual_value == expected_custom: - empty_correct += 1 - print(f" ✅ {prompt_key}: кастомный промпт сохранен") - else: - print(f" ❌ {prompt_key}: кастомный промпт потерян") - else: - # Должен быть взят из manifest - if actual_value != expected_custom and len(actual_value.strip()) > 0: - empty_correct += 1 - print(f" ✅ {prompt_key}: взят из manifest при пустом кастомном") - else: - print(f" ❌ {prompt_key}: не взят из manifest при пустом кастомном") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 3: {empty_correct}/4 проверок корректно обработано") - - if empty_correct >= 3: # Допускаем 1 ошибку для некритических сценариев - print("✅ ТЕСТ 3 ПРОЙДЕН: Пустые промпты обрабатываются корректно") - else: - print("❌ ТЕСТ 3 НЕ ПРОЙДЕН: Пустые промпты обрабатываются некорректно") - - except Exception as e: - print(f"❌ ТЕСТ 3 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - print("\n" + "=" * 80) - print("🧪 ЗАВЕРШЕНИЕ ТЕСТОВОГО СЦЕНАРИЯ 2") - print("=" * 80) - -if __name__ == "__main__": - run_test_scenario_2() \ No newline at end of file diff --git a/test_fallback_damaged_manifest.py b/test_fallback_damaged_manifest.py deleted file mode 100644 index a810eae1..00000000 --- a/test_fallback_damaged_manifest.py +++ /dev/null @@ -1,458 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 3: Тестирование fallback при повреждении manifest.json - -Цель: Проверить, что функция get_user_prompts() корректно обрабатывает -поврежденный или отсутствующий manifest.json и использует встроенные промпты -""" - -import json -import sys -import os - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals с возможностью повреждения.""" - if name == 'manifest': - # Имитируем различные сценарии повреждения manifest.json - damage_scenario = os.environ.get('DAMAGE_SCENARIO', 'normal') - - if damage_scenario == 'normal': - # Нормальный manifest.json - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - - elif damage_scenario == 'missing': - # Manifest отсутствует - console_log("ИМИТАЦИЯ: manifest отсутствует в globals") - return default - - elif damage_scenario == 'empty': - # Manifest пустой - console_log("ИМИТАЦИЯ: manifest пустой") - return {} - - elif damage_scenario == 'invalid_json': - # Manifest содержит некорректный JSON - console_log("ИМИТАЦИЯ: manifest содержит некорректный JSON") - return "{ invalid json" - - elif damage_scenario == 'missing_options': - # Manifest без секции options - console_log("ИМИТАЦИЯ: manifest без секции options") - return {"name": "Ozon Analyzer", "version": "1.0.0"} - - elif damage_scenario == 'missing_prompts': - # Manifest без секции prompts в options - console_log("ИМИТАЦИЯ: manifest без секции prompts") - return { - "name": "Ozon Analyzer", - "version": "1.0.0", - "options": { - "response_language": "ru" - } - } - - elif damage_scenario == 'corrupted_prompts': - # Manifest с поврежденной секцией prompts - console_log("ИМИТАЦИЯ: manifest с поврежденной секцией prompts") - return { - "name": "Ozon Analyzer", - "version": "1.0.0", - "options": { - "prompts": { - "optimized": { - "ru": None, # Поврежденное значение - "en": "" # Пустое значение - } - } - } - } - - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """ - Встроенные промпты по умолчанию (скопировано из mcp_server.py). - """ - builtin_prompts = { - 'optimized': { - 'ru': """Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки. - -ДАННЫЕ: -Описание: {description} -Состав: {composition} - -ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА: - -1. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ: -- Термины типа "3D/4D/5D", "революционный", "инновационный" - ТРЕБУЮТ доказательств -- Для каждого заявления ("лифтинг", "против морщин"): - * Найди КОНКРЕТНЫЙ активный компонент - * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг) - * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте - -2. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1): -- Проверь КАЖДЫЙ компонент на: - * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, и т.д.) - * Парабены (особенно butyl-, propyl-) - * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone) - * Потенциальные эндокринные дизрапторы -- Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10 - -3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ: -- Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален -- Collagen/Elastin: молекулы НЕ проникают, работают только как пленка -- Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины - -4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ: -- Современная косметика = без парабенов, с новыми консервантами -- Устаревшие формулы → снижение оценки на 2-3 балла - -5. ШКАЛА ОЦЕНКИ (СТРОГАЯ): -- 9-10: Идеальный состав, доказанные активы в высоких концентрациях, без токсичных компонентов -- 7-8: Хороший состав, минимум проблемных компонентов -- 5-6: Средний продукт, есть проблемные компоненты ИЛИ активы в низких дозах -- 3-4: Устаревшая формула, много токсичных компонентов, маркетинговые заявления не подтверждены -- 1-2: Опасный или полностью бесполезный продукт - -КРИТИЧЕСКИ ВАЖНО: -- Будь СКЕПТИЧЕН к маркетингу -- НЕ завышай оценку из вежливости -- Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10 -- Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку - -ФОРМАТ ОТВЕТА - ТОЛЬКО JSON: -{{ -"score": число_от_1_до_10, -"reasoning": "ДЕТАЛЬНЫЙ анализ: - 1. Проверка маркетинга: [разбери каждое заявление] - 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты] - 3. Реальная эффективность активов: [концентрации vs заявления] - 4. Сравнение с современными стандартами: [почему устарел/актуален] - 5. Итоговый вердикт: [честное заключение]", -"confidence": число_от_0_до_1, -"red_flags": ["список всех токсичных/проблемных компонентов"], -"marketing_lies": ["список не подтвержденных маркетинговых заявлений"] -}} - -ЯЗЫК: Русский, технический стиль с примерами.""", - 'en': """You are a board-certified toxicologist and cosmetic chemist with 15 years of experience in ingredient safety assessment. Your task: conduct a CRITICAL, evidence-based analysis of this cosmetic product, exposing marketing manipulation. - -DATA: -Description: {description} -Composition: {composition} - -MANDATORY ANALYSIS PROTOCOL: - -1. TOXICOLOGICAL SCREENING (highest priority): -- Screen EVERY ingredient for: - * Formaldehyde-releasers (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea) - * Parabens (particularly butylparaben, propylparaben - EU restricted) - * Obsolete UV filters (Octinoxate/Ethylhexyl Methoxycinnamate, Oxybenzone) - * Known/suspected endocrine disruptors -- HARD RULE: ≥3 high-concern ingredients → score CAPPED at 5/10 maximum - -2. MARKETING CLAIMS VERIFICATION: -- Buzzwords like "3D/4D/5D technology", "revolutionary", "clinical breakthrough" - DEMAND evidence -- For each claim ("lifting", "anti-wrinkle", "firming"): - * Identify the SPECIFIC active ingredient responsible - * Evaluate its POSITION in INCI list (first 5 = meaningful dose, after position 10 = cosmetic dose) - * Compare PROVEN effective concentration from peer-reviewed studies vs. LIKELY concentration in this product - -3. REALISTIC EFFICACY ASSESSMENT: -- Palmitoyl Tripeptide-38 (Matrixyl synthe'6): clinically effective at 2-4%; if listed mid-INCI → probably <1% → negligible effect -- Collagen/Hydrolyzed Elastin: molecular weight >500 Da → CANNOT penetrate stratum corneum → function only as humectants/film-formers -- Sodium Hyaluronate: provides surface hydration only, CANNOT affect dermal structure or deep wrinkles - -4. MODERN FORMULATION STANDARDS COMPARISON: -- 2025 best practices: phenoxyethanol or modern preservative systems, NO paraben cocktails -- Formulations using 4+ parabens + formaldehyde-releasers = outdated 2000s technology → automatic -2 to -3 point deduction - -5. EVIDENCE-BASED SCORING RUBRIC (strict grading): -- 9-10: Exceptional formulation, clinically-validated actives at proven concentrations, clean safety profile -- 7-8: Well-formulated, minor concerns only, actives present at reasonable levels -- 5-6: Mediocre product with significant concerns (problematic preservatives OR underdosed actives OR misleading claims) -- 3-4: Poor formulation with multiple red flags, outdated technology, unsubstantiated marketing -- 1-2: Potentially harmful or fraudulent product - -CRITICAL ASSESSMENT RULES: -- Maintain scientific skepticism toward all marketing language -- Apply evidence-based standards, NOT brand reputation -- Outdated preservation system (multiple parabens + formaldehyde-releaser) = AUTOMATIC cap at 5/10 -- Claims unsupported by adequate active concentrations = reduce score proportionally -- Default to LOWER score when ingredient concentrations are ambiguous - -OUTPUT FORMAT - VALID JSON ONLY: -{{ -"score": integer_1_to_10, -"reasoning": "COMPREHENSIVE ANALYSIS: - 1. Toxicological Profile: [enumerate ALL concerning ingredients with specific risks] - 2. Marketing Claims Audit: [fact-check each claim against ingredient reality] - 3. Active Ingredient Efficacy: [compare claimed benefits vs. probable concentrations vs. scientific evidence] - 4. Formulation Modernity Assessment: [evaluate against current industry standards] - 5. Evidence-Based Verdict: [objective conclusion with no marketing bias]", -"confidence": float_0_to_1, -"red_flags": ["comprehensive list of problematic/toxic/outdated ingredients"], -"marketing_lies": ["specific unsubstantiated or misleading marketing claims"] -}} - -RESPONSE LANGUAGE: English, using precise technical terminology.""" - }, - 'deep': { - 'ru': """Проведи глубокий анализ товара с медицинской и научной точки зрения. -Описание: {description} -Состав: {composition} -Проанализируй: -1. Научную обоснованность заявленных свойств. -2. Потенциальные побочные эффекты и противопоказания. -3. Эффективность по сравнению с аналогами. -Верни детальный максимально подробный обоснованный анализ в структурированном виде (используй Markdown).""", - 'en': """Conduct a deep analysis of the product from a medical and scientific perspective. -Description: {description} -Composition: {composition} -Analyze: -1. Scientific validity of the claimed properties. -2. Potential side effects and contraindications. -3. Effectiveness compared to analogs. -Return a detailed, maximally comprehensive, reasoned analysis in a structured form (use Markdown).""" - } - } - - return builtin_prompts.get(prompt_type, {}).get(lang, '') - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов с fallback логикой (скопировано из mcp_server.py). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТОВЫЙ СЦЕНАРИЙ 3) =====") - - try: - # Диагностика доступа к manifest - console_log("🔍 ДИАГНОСТИКА ДОСТУПА К MANIFEST:") - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - console_log(f" manifest.options: {manifest.get('options') is not None}") - if manifest.get('options'): - console_log(f" manifest.options.prompts: {manifest.get('options', {}).get('prompts') is not None}") - else: - console_log(" ❌ manifest НЕ НАЙДЕН в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - - # Структура промптов - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest: {manifest is not None}") - - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - options = safe_dict_get(manifest, 'options', {}) - console_log(f" manifest.options: {options is not None}") - if options: - console_log(f" manifest.options ключи: {list(options.keys())}") - console_log(f" manifest.options.prompts: {options.get('prompts') is not None}") - if options.get('prompts'): - console_log(f" manifest.options.prompts ключи: {list(options.get('prompts', {}).keys())}") - - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - else: - console_log(" ❌ manifest не найден в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Fallback 1: manifest.json - исправленная логика извлечения - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def run_test_scenario_3(): - """Выполнение тестового сценария 3.""" - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 3: Тестирование fallback при повреждении manifest.json") - print("=" * 80) - - # Сценарии повреждения manifest.json - damage_scenarios = [ - ('normal', 'Нормальный manifest.json'), - ('missing', 'Отсутствующий manifest'), - ('empty', 'Пустой manifest'), - ('invalid_json', 'Некорректный JSON в manifest'), - ('missing_options', 'Отсутствует секция options'), - ('missing_prompts', 'Отсутствует секция prompts'), - ('corrupted_prompts', 'Поврежденная секция prompts') - ] - - results = {} - - for scenario, description in damage_scenarios: - print(f"\n📋 ТЕСТ: {description}") - print("-" * 60) - - # Устанавливаем переменную окружения для сценария повреждения - os.environ['DAMAGE_SCENARIO'] = scenario - - try: - prompts = get_user_prompts() - - # Проверяем что промпты загружены - loaded_count = 0 - builtin_count = 0 - total_count = 4 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - loaded_count += 1 - # Проверяем является ли промпт встроенным (fallback) - builtin_prompt = _get_builtin_default_prompt(prompt_type, lang) - if prompt_value == builtin_prompt: - builtin_count += 1 - - print(f" Загружено промптов: {loaded_count}/{total_count}") - print(f" Из них встроенных (fallback): {builtin_count}") - - if scenario == 'normal': - # Для нормального сценария должны быть загружены все промпты из manifest - if loaded_count == total_count: - print("✅ НОРМАЛЬНЫЙ СЦЕНАРИЙ ПРОЙДЕН: Все промпты загружены из manifest") - results[scenario] = "PASSED" - else: - print("❌ НОРМАЛЬНЫЙ СЦЕНАРИЙ НЕ ПРОЙДЕН: Не все промпты загружены") - results[scenario] = "FAILED" - else: - # Для поврежденных сценариев должен сработать fallback - if builtin_count > 0: - print(f"✅ FALLBACK СЦЕНАРИЙ ПРОЙДЕН: Использовано {builtin_count} встроенных промптов") - results[scenario] = "PASSED" - else: - print("❌ FALLBACK СЦЕНАРИЙ НЕ ПРОЙДЕН: Fallback не сработал") - results[scenario] = "FAILED" - - except Exception as e: - print(f"❌ ТЕСТ НЕ ПРОЙДЕН: Критическая ошибка: {e}") - results[scenario] = "ERROR" - - # Итоговый отчет - print("\n" + "=" * 80) - print("📊 ИТОГОВЫЙ ОТЧЕТ ПО ТЕСТИРОВАНИЮ FALLBACK") - print("=" * 80) - - passed_count = sum(1 for result in results.values() if result == "PASSED") - total_count = len(results) - - print(f"Пройдено сценариев: {passed_count}/{total_count}") - - for scenario, result in results.items(): - status_icon = "✅" if result == "PASSED" else "❌" if result == "FAILED" else "⚠️" - description = next(desc for sc, desc in damage_scenarios if sc == scenario) - print(f" {status_icon} {description}: {result}") - - if passed_count == total_count: - print("\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ: Fallback логика работает корректно") - elif passed_count >= total_count * 0.8: # 80% успеха - print(f"\n⚠️ ЧАСТИЧНЫЙ УСПЕХ: {passed_count}/{total_count} тестов пройдено") - print("Рекомендуется проверить сценарии, которые не прошли") - else: - print(f"\n❌ НЕУДАЧА: Только {passed_count}/{total_count} тестов пройдено") - print("Необходимо исправить fallback логику") - - print("=" * 80) - print("🧪 ЗАВЕРШЕНИЕ ТЕСТОВОГО СЦЕНАРИЯ 3") - print("=" * 80) - - # Очищаем переменную окружения - if 'DAMAGE_SCENARIO' in os.environ: - del os.environ['DAMAGE_SCENARIO'] - -if __name__ == "__main__": - run_test_scenario_3() \ No newline at end of file diff --git a/test_fixes.sh b/test_fixes.sh deleted file mode 100755 index ec5c469c..00000000 --- a/test_fixes.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -echo "=== ТЕСТИРОВАНИЕ ИСПРАВЛЕНИЙ ===" -echo - -# Тест 1: Проверка сборки проекта -echo "Тест 1: Проверка сборки проекта" -if [ -d "dist" ] && [ -f "dist/manifest.json" ] && [ -f "dist/background.js" ]; then - echo "✅ Сборка проекта успешна - директория dist существует" -else - echo "❌ Сборка проекта не удалась - директория dist отсутствует" - exit 1 -fi -echo - -# Тест 2: Проверка manifest.json -echo "Тест 2: Проверка manifest.json" -if [ -f "dist/manifest.json" ]; then - # Проверка валидности JSON с помощью node - if node -e "try { JSON.parse(require('fs').readFileSync('dist/manifest.json', 'utf8')); console.log('valid'); } catch(e) { console.log('invalid'); }" | grep -q "valid"; then - echo "✅ manifest.json валиден" - else - echo "❌ manifest.json невалиден" - fi - - # Проверка наличия "*.html" в web_accessible_resources - if grep -q '"\*\.html"' dist/manifest.json; then - echo "✅ '*.html' найден в web_accessible_resources" - else - echo "❌ '*.html' отсутствует в web_accessible_resources" - fi - - # Проверка наличия offscreen_document - if grep -q '"offscreen_document"' dist/manifest.json && grep -q '"offscreen.html"' dist/manifest.json; then - echo "✅ offscreen_document корректно настроен" - else - echo "❌ offscreen_document не найден или некорректен" - fi -else - echo "❌ manifest.json не найден" -fi -echo - -# Тест 3: Проверка background.js -echo "Тест 3: Проверка background.js" -if [ -f "dist/background.js" ]; then - echo "✅ background.js найден" - - # Проверка наличия импортов (упрощенная проверка) - if grep -q "import" dist/background.js; then - echo "✅ Импорты найдены в background.js" - else - echo "❌ Импорты не найдены в background.js" - fi - - # Проверка наличия try/catch блоков - if grep -q "try {" dist/background.js && grep -q "catch" dist/background.js; then - echo "✅ Try/catch блоки найдены в background.js" - else - echo "❌ Try/catch блоки не найдены в background.js" - fi - - # Проверка наличия ensureOffscreenDocument - if grep -q "ensureOffscreenDocument" dist/background.js; then - echo "✅ ensureOffscreenDocument найден в background.js" - else - echo "❌ ensureOffscreenDocument не найден в background.js" - fi -else - echo "❌ background.js не найден" -fi -echo - -# Тест 4: Проверка offscreen файлов -echo "Тест 4: Проверка offscreen файлов" -if [ -f "chrome-extension/public/offscreen.html" ]; then - echo "✅ offscreen.html найден" - - if grep -q "offscreen-init.js" chrome-extension/public/offscreen.html; then - echo "✅ offscreen-init.js подключен в offscreen.html" - else - echo "❌ offscreen-init.js не подключен в offscreen.html" - fi -else - echo "❌ offscreen.html не найден" -fi - -if [ -f "chrome-extension/public/offscreen-init.js" ]; then - echo "✅ offscreen-init.js найден" - - if grep -q "pyodide/pyodide.js" chrome-extension/public/offscreen-init.js && grep -q "offscreen.js" chrome-extension/public/offscreen-init.js; then - echo "✅ Пути к pyodide.js и offscreen.js корректны" - else - echo "❌ Пути к pyodide.js или offscreen.js некорректны" - fi -else - echo "❌ offscreen-init.js не найден" -fi - -if [ -f "chrome-extension/public/offscreen.js" ]; then - echo "✅ offscreen.js найден" -else - echo "❌ offscreen.js не найден" -fi -echo - -# Тест 5: Проверка heartbeat механизма -echo "Тест 5: Проверка heartbeat механизма" -if grep -q "heartbeat\|connection.monitoring\|retry.logic" dist/background.js 2>/dev/null; then - echo "✅ Heartbeat механизм найден в коде" -else - echo "⚠️ Heartbeat механизм не найден (может быть в другом формате)" -fi -echo - -echo "=== ТЕСТИРОВАНИЕ ЗАВЕРШЕНО ===" \ No newline at end of file diff --git a/test_get_user_prompts.py b/test_get_user_prompts.py deleted file mode 100644 index 644d793d..00000000 --- a/test_get_user_prompts.py +++ /dev/null @@ -1,347 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий для проверки корректной работы функции get_user_prompts. - -Этот тест проверяет: -1. Корректный доступ к кастомным промптам из структуры plugin_settings -2. Правильное извлечение дефолтных промптов из манифеста -3. Логирование диагностической информации -4. Обработку ошибок и fallback сценарии -""" - -import asyncio -import json -import sys -import os -from typing import Dict, Any, Optional - -# Добавляем текущую директорию в путь для импорта -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -# Мокаем функции JavaScript bridge для тестирования -class MockJSBridge: - """Мок JavaScript bridge для тестирования.""" - - def __init__(self): - self.logged_messages = [] - - def consoleLog(self, message: str): - """Мокируем console.log.""" - self.logged_messages.append(message) - print(f"[MOCK_CONSOLE] {message}") - - def sendMessageToChat(self, message: Dict[str, Any]): - """Мокируем отправку сообщения в чат.""" - self.logged_messages.append(f"CHAT: {message}") - print(f"[MOCK_CHAT] {message}") - -# Глобальный мок JS bridge -mock_js = MockJSBridge() - -def console_log(message: str, force: bool = False): - """Мокированная функция логирования.""" - mock_js.consoleLog(f"[TEST_LOG] {message}") - -def chat_message(message: str, message_type: str = None): - """Мокированная функция отправки сообщения в чат.""" - mock_js.sendMessageToChat({"content": message}) - -# Мокируем Pyodide globals -class MockGlobals: - """Мок Pyodide globals для тестирования.""" - - def __init__(self, data: Dict[str, Any]): - self.data = data - - def __contains__(self, key: str) -> bool: - return key in self.data - - def __getitem__(self, key: str) -> Any: - return self.data[key] - - def keys(self): - return self.data.keys() - -# Импортируем функцию get_user_prompts из основного модуля -# Поскольку мы не можем напрямую импортировать из-за зависимостей, -# создадим локальную копию функции для тестирования - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - """Безопасная функция получения значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_pyodide_var(name: str, default: Any = None) -> Any: - """Мокированная функция получения переменной из Pyodide globals.""" - try: - if name in globals(): - value = globals()[name] - return value - else: - return default - except Exception as e: - return default - -def get_user_prompts(plugin_settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - """ - Локальная копия функции get_user_prompts для тестирования. - - Загружает промпты из настроек пользователя или использует значения по умолчанию из manifest.json. - """ - try: - console_log("🔍 НАЧАЛО ТЕСТИРОВАНИЯ get_user_prompts") - console_log(f"📋 Входящие plugin_settings: {plugin_settings}") - console_log(f"📋 Тип plugin_settings: {type(plugin_settings)}") - - # Исправлено: промпты уже доступны в plugin_settings - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - - console_log(f"📋 Извлеченные кастомные промпты: {custom_prompts_raw}") - - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - manifest_prompts = safe_dict_get(manifest, 'options.prompts', {}) - - console_log(f"📋 Manifest промпты: {manifest_prompts}") - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - - custom_value = safe_dict_get(prompt_type_data, lang, '') - - console_log(f"🔍 Проверка промпта {prompt_type}.{lang}:") - console_log(f" - Кастомное значение: '{custom_value}'") - console_log(f" - Длина кастомного значения: {len(custom_value) if custom_value else 0}") - console_log(f" - Тип кастомного значения: {type(custom_value)}") - console_log(f" - Проверка на непустую строку: {isinstance(custom_value, str) and len(custom_value.strip()) > 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - # Fallback на manifest.json - lang_data = safe_dict_get(manifest_prompts, f'{prompt_type}.{lang}', {}) - - manifest_value = safe_dict_get(lang_data, 'default', '') - prompts[prompt_type][lang] = manifest_value - console_log(f"ℹ️ Используем промпт по умолчанию: {prompt_type}.{lang}") - - console_log(f"🔍 Диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Final prompts structure: {prompts}") - console_log(f"📋 Загружено промптов: {len([p for pt in prompts.values() for p in pt.values() if p])} кастомных") - return prompts - - except Exception as e: - console_log(f"❌ Ошибка загрузки промптов: {str(e)}") - console_log(f"🔍 Диагностика ошибки:") - console_log(f" Plugin settings: {plugin_settings}") - console_log(f" Plugin settings type: {type(plugin_settings)}") - # Критический fallback - возвращаем пустые промпты - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -class TestGetUserPrompts: - """Класс для тестирования функции get_user_prompts.""" - - def __init__(self): - self.test_results = [] - self.manifest = { - 'options': { - 'prompts': { - 'optimized.ru': {'default': 'Оптимизированный промпт по умолчанию (RU)'}, - 'optimized.en': {'default': 'Default optimized prompt (EN)'}, - 'deep.ru': {'default': 'Глубокий промпт по умолчанию (RU)'}, - 'deep.en': {'default': 'Default deep prompt (EN)'} - } - } - } - - def run_test(self, test_name: str, plugin_settings: Dict[str, Any], expected_custom_count: int): - """Запуск одного теста.""" - print(f"\n🧪 ТЕСТ: {test_name}") - print("=" * 50) - - # Устанавливаем глобальные переменные для теста - global manifest - manifest = self.manifest - - # Очищаем логи перед тестом - mock_js.logged_messages = [] - - # Выполняем тестируемую функцию - result = get_user_prompts(plugin_settings) - - # Анализируем результаты - custom_prompts_count = len([p for pt in result.values() for p in pt.values() if p]) - - print(f"📊 Результат теста:") - print(f" - Количество кастомных промптов: {custom_prompts_count}") - print(f" - Ожидаемое количество: {expected_custom_count}") - print(f" - Структура результата: {result}") - - # Проверяем логи - print(f"📋 Логи теста ({len(mock_js.logged_messages)} сообщений):") - for i, log in enumerate(mock_js.logged_messages[-10:], 1): # Последние 10 логов - print(f" {i}. {log}") - - # Определяем успешность теста - success = custom_prompts_count == expected_custom_count - self.test_results.append({ - 'test_name': test_name, - 'success': success, - 'expected': expected_custom_count, - 'actual': custom_prompts_count, - 'result': result, - 'logs': mock_js.logged_messages.copy() - }) - - print(f"✅ ТЕСТ {'ПРОЙДЕН' if success else 'ПРОВАЛЕН'}") - return success - - def run_all_tests(self): - """Запуск всех тестов.""" - print("🚀 НАЧАЛО ТЕСТИРОВАНИЯ ФУНКЦИИ get_user_prompts") - print("=" * 60) - - # Тест 1: Корректные кастомные промпты - self.run_test( - "Корректные кастомные промпты", - { - 'prompts': { - 'optimized': { - 'ru': 'Кастомный оптимизированный промпт (RU)', - 'en': 'Custom optimized prompt (EN)' - }, - 'deep': { - 'ru': 'Кастомный глубокий промпт (RU)', - 'en': 'Custom deep prompt (EN)' - } - } - }, - 4 # Все 4 промпта кастомные - ) - - # Тест 2: Пустые промпты (fallback на manifest) - self.run_test( - "Пустые промпты (fallback на manifest)", - { - 'prompts': { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - }, - 0 # Нет кастомных промптов - ) - - # Тест 3: Отсутствующие промпты - self.run_test( - "Отсутствующие промпты", - {}, - 0 # Нет кастомных промптов - ) - - # Тест 4: Неправильная структура промптов - self.run_test( - "Неправильная структура промптов", - { - 'prompts': 'неправильная_структура' - }, - 0 # Нет кастомных промптов из-за неправильной структуры - ) - - # Тест 5: Частично заполненные промпты - self.run_test( - "Частично заполненные промпты", - { - 'prompts': { - 'optimized': { - 'ru': 'Только русский оптимизированный промпт' - }, - 'deep': { - 'en': 'Only English deep prompt' - } - } - }, - 2 # Только 2 кастомных промпта - ) - - # Тест 6: Промпты с пробелами (должны игнорироваться) - self.run_test( - "Промпты с пробелами", - { - 'prompts': { - 'optimized': { - 'ru': ' ', # Только пробелы - 'en': '\t\n' # Табуляция и перенос - } - } - }, - 0 # Промпты с пробелами игнорируются - ) - - # Тест 7: None как plugin_settings - self.run_test( - "None как plugin_settings", - None, - 0 # Нет кастомных промптов - ) - - self.print_summary() - - def print_summary(self): - """Вывод итогов тестирования.""" - print("\n" + "=" * 60) - print("📊 ИТОГИ ТЕСТИРОВАНИЯ") - print("=" * 60) - - total_tests = len(self.test_results) - passed_tests = len([t for t in self.test_results if t['success']]) - - print(f"Всего тестов: {total_tests}") - print(f"Пройденных тестов: {passed_tests}") - print(f"Проваленых тестов: {total_tests - passed_tests}") - print(f"Успешность: {passed_tests/total_tests*100:.1f}%") - - print("\nДетальный отчет:") - for i, test in enumerate(self.test_results, 1): - status = "✅ ПРОЙДЕН" if test['success'] else "❌ ПРОВАЛЕН" - print(f"{i}. {test['test_name']}: {status}") - print(f" Ожидаемо: {test['expected']}, Получено: {test['actual']}") - - if passed_tests == total_tests: - print("\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Функция работает корректно.") - else: - print(f"\n⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ! Требуется дополнительная отладка.") - -async def main(): - """Главная функция для запуска тестов.""" - print("🚀 Запуск тестового сценария для функции get_user_prompts") - - # Создаем и запускаем тесты - tester = TestGetUserPrompts() - tester.run_all_tests() - - # Ждем немного для завершения всех асинхронных операций - await asyncio.sleep(0.1) - - print("\n✅ Тестирование завершено!") - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/test_integration_full_cycle.py b/test_integration_full_cycle.py deleted file mode 100644 index de2a8d0c..00000000 --- a/test_integration_full_cycle.py +++ /dev/null @@ -1,462 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 4: Полный цикл анализа с оригинальными промптами - -Цель: Протестировать полный цикл анализа товара Ozon с использованием -оригинальных промптов из manifest.json в реальном сценарии -""" - -import json -import sys -import os -import re - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def chat_message(message: str): - """Имитация отправки сообщения в чат.""" - print(f"[CHAT_MESSAGE] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - - elif name == 'pluginSettings': - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'search_for_analogs': False - } - - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов (скопировано из mcp_server.py для тестирования). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ИНТЕГРАЦИОННЫЙ ТЕСТ) =====") - - try: - # Диагностика доступа к manifest - manifest = get_pyodide_var('manifest', {}) - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - else: - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Fallback на manifest.json (основной сценарий) - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -# Имитация класса FastDOMParser для тестирования -class MockFastDOMParser: - """Макет парсера HTML для тестирования.""" - - def __init__(self, html_content): - self.html = html_content - - def extract_product_info(self): - """Имитация извлечения информации о товаре.""" - return { - 'title': 'Тестовый крем для лица с коллагеном', - 'description': 'Инновационный крем для лица с активным коллагеном и гиалуроновой кислотой. Обеспечивает глубокое увлажнение и разглаживание морщин. Подходит для всех типов кожи.', - 'composition': 'Aqua, Glycerin, Collagen, Hyaluronic Acid, Parfum, DMDM Hydantoin, Propylparaben, Butylparaben, Phenoxyethanol', - 'categories': ['krasota-i-zdorove-6500'], - 'price': {'text': '1 299 ₽', 'amount': 1299, 'currency': 'RUB'}, - 'rating': {'text': '4.2', 'value': 4.2, 'max_value': 5.0}, - 'extraction_stats': { - 'total_fields': 6, - 'successful_fields': 6, - 'success_rate_percent': 100, - 'parsing_time_ms': 15.2 - } - } - - def is_product_in_target_category(self, categories): - """Проверка принадлежности к целевой категории.""" - return 'krasota-i-zdorove-6500' in categories - -# Имитация AI модели для тестирования -def mock_ai_call(model_alias: str, prompt: str): - """Имитация вызова AI модели.""" - console_log(f"🤖 ИМИТАЦИЯ ВЫЗОВА AI: {model_alias}") - console_log(f"📝 Длина промпта: {len(prompt)} символов") - - # Проверяем что промпт содержит ключевые элементы из manifest.json - prompt_lower = prompt.lower() - - # Для optimized промпта проверяем наличие ключевых элементов - if model_alias == 'compliance_check' and any(keyword in prompt_lower for keyword in ['токсиколог', 'косметолог', 'анализ']): - # Проверяем что в промпте есть данные о составе (DMDM Hydantoin, parabens и т.д.) - if any(ingredient in prompt for ingredient in ['DMDM Hydantoin', 'Propylparaben', 'Butylparaben']): - return json.dumps({ - 'score': 4, - 'reasoning': 'Анализ показал наличие потенциально проблемных консервантов (DMDM Hydantoin, Propylparaben, Butylparaben). Основные активные ингредиенты (Collagen, Hyaluronic Acid) присутствуют, но их концентрация не указана явно. Маркетинговые заявления о "глубоком увлажнении" и "разглаживании морщин" не полностью обоснованы составом.', - 'confidence': 0.8, - 'red_flags': ['DMDM Hydantoin', 'Propylparaben', 'Butylparaben'], - 'marketing_lies': ['Глубокое проникновение коллагена в кожу'] - }) - else: - return json.dumps({ - 'score': 5, - 'reasoning': 'Недостаточно данных для полного анализа состава', - 'confidence': 0.5, - 'red_flags': [], - 'marketing_lies': [] - }) - - # Для deep промпта - elif model_alias == 'deep_analysis' and 'глубокий анализ' in prompt_lower: - return "ДЕТАЛЬНЫЙ АНАЛИЗ ПРОДУКТА:\n\n1. НАУЧНАЯ ОБОСНОВАННОСТЬ:\nКоллаген в косметике не проникает в глубокие слои кожи из-за большого размера молекул (>500 Da). Гиалуроновая кислота работает только на поверхности.\n\n2. ПОБОЧНЫЕ ЭФФЕКТЫ:\nКонсерванты DMDM Hydantoin могут вызывать раздражение. Парабены накапливаются в организме.\n\n3. ЭФФЕКТИВНОСТЬ:\nЭффект увлажнения длится 4-6 часов, видимое разглаживание морщин отсутствует." - - return json.dumps({'score': 5, 'reasoning': 'Ошибка анализа'}) - -def analyze_ozon_product_integration_test(): - """ - Интеграционный тест полного цикла анализа с оригинальными промптами. - """ - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 4: Полный цикл анализа с оригинальными промптами") - print("=" * 80) - - # Шаг 1: Загрузка промптов из manifest.json - print("\n📋 ШАГ 1: Загрузка промптов из manifest.json") - print("-" * 60) - - try: - plugin_settings = get_pyodide_var('pluginSettings', {}) - prompts = get_user_prompts(plugin_settings) - - # Проверяем что промпты загружены - loaded_prompts = 0 - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - loaded_prompts += 1 - - print(f" Загружено промптов: {loaded_prompts}/4") - - if loaded_prompts < 2: - print("❌ НЕДОСТАТОЧНО ПРОМПТОВ ДЛЯ АНАЛИЗА") - return False - - print("✅ ПРОМПТЫ УСПЕШНО ЗАГРУЖЕНЫ") - - except Exception as e: - print(f"❌ ОШИБКА ЗАГРУЗКИ ПРОМПТОВ: {e}") - return False - - # Шаг 2: Имитация извлечения данных о товаре - print("\n📋 ШАГ 2: Извлечение данных о товаре") - print("-" * 60) - - try: - # Имитируем HTML страницу товара Ozon - mock_html = ''' - - - Крем для лица с коллагеном - Ozon - - - -
-

Крем для лица с коллагеном

-
-

Описание

-
-

Инновационный крем для лица с активным коллагеном и гиалуроновой кислотой. - Обеспечивает глубокое увлажнение и разглаживание морщин. Подходит для всех типов кожи.

-
-

Состав

-

Aqua, Glycerin, Collagen, Hyaluronic Acid, Parfum, DMDM Hydantoin, Propylparaben, Butylparaben, Phenoxyethanol

-
-
- - - - ''' - - parser = MockFastDOMParser(mock_html) - product_info = parser.extract_product_info() - - print(f" Название: {product_info['title']}") - print(f" Описание: {product_info['description'][:100]}...") - print(f" Состав: {product_info['composition']}") - print(f" Категория: {product_info['categories']}") - - # Проверяем категорию - if not parser.is_product_in_target_category(product_info['categories']): - print("❌ ТОВАР НЕ ПРИНАДЛЕЖИТ К ЦЕЛЕВОЙ КАТЕГОРИИ") - return False - - print("✅ ДАННЫЕ О ТОВАРЕ ИЗВЛЕЧЕНЫ КОРРЕКТНО") - - except Exception as e: - print(f"❌ ОШИБКА ИЗВЛЕЧЕНИЯ ДАННЫХ: {e}") - return False - - # Шаг 3: Тестирование промптов в AI анализе - print("\n📋 ШАГ 3: Тестирование промптов в AI анализе") - print("-" * 60) - - try: - # Тестируем optimized промпт (русский) - optimized_prompt_ru = prompts['optimized']['ru'] - if not optimized_prompt_ru or len(optimized_prompt_ru.strip()) == 0: - print("❌ ОПТИМИЗИРОВАННЫЙ ПРОМПТ (RU) НЕ ЗАГРУЖЕН") - return False - - # Вставляем тестовые данные в промпт - test_description = product_info['description'] - test_composition = product_info['composition'] - - # Подготавливаем промпт для тестирования - test_prompt = optimized_prompt_ru.replace('{description}', test_description).replace('{composition}', test_composition) - - print(f" Длина промпта: {len(test_prompt)} символов") - print(f" Промпт начинается с: {test_prompt[:100]}...") - - # Проверяем наличие ключевых элементов промпта - key_elements = [ - 'токсиколог', - 'косметолог', - 'анализ', - 'соответствия', - 'DMDM Hydantoin', - 'парабены', - 'концентрация' - ] - - found_elements = [] - for element in key_elements: - if element.lower() in test_prompt.lower(): - found_elements.append(element) - - print(f" Найдено ключевых элементов: {len(found_elements)}/{len(key_elements)}") - - if len(found_elements) >= 5: # Минимум 5 ключевых элементов - print("✅ ПРОМПТ СОДЕРЖИТ ВСЕ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - else: - print("❌ ПРОМПТ НЕ СОДЕРЖИТ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - return False - - except Exception as e: - print(f"❌ ОШИБКА ПОДГОТОВКИ ПРОМПТА: {e}") - return False - - # Шаг 4: Имитация AI анализа - print("\n📋 ШАГ 4: Имитация AI анализа") - print("-" * 60) - - try: - # Имитируем вызов AI с промптом - ai_response = mock_ai_call('compliance_check', test_prompt) - - print(f" Длина ответа AI: {len(ai_response)} символов") - - # Проверяем структуру ответа - try: - parsed_response = json.loads(ai_response) - print(f" Структура ответа: {list(parsed_response.keys())}") - - if 'score' in parsed_response and 'reasoning' in parsed_response: - score = parsed_response['score'] - reasoning = parsed_response['reasoning'] - - print(f" Оценка соответствия: {score}/10") - print(f" Длина обоснования: {len(reasoning)} символов") - - # Проверяем что ответ учитывает данные товара - if 'DMDM Hydantoin' in reasoning or 'парабен' in reasoning.lower(): - print("✅ АНАЛИЗ УЧИТЫВАЕТ СОСТАВ ПРОДУКТА") - else: - print("❌ АНАЛИЗ НЕ УЧИТЫВАЕТ СОСТАВ ПРОДУКТА") - return False - - print("✅ AI АНАЛИЗ ВЫПОЛНЕН УСПЕШНО") - - else: - print("❌ ОТВЕТ AI НЕ СОДЕРЖИТ НЕОБХОДИМЫЕ ПОЛЯ") - return False - - except json.JSONDecodeError: - print("❌ ОТВЕТ AI НЕ ЯВЛЯЕТСЯ ВАЛИДНЫМ JSON") - return False - - except Exception as e: - print(f"❌ ОШИБКА AI АНАЛИЗА: {e}") - return False - - # Шаг 5: Проверка deep промпта - print("\n📋 ШАГ 5: Проверка deep промпта") - print("-" * 60) - - try: - deep_prompt_ru = prompts['deep']['ru'] - if not deep_prompt_ru or len(deep_prompt_ru.strip()) == 0: - print("❌ ГЛУБОКИЙ ПРОМПТ (RU) НЕ ЗАГРУЖЕН") - return False - - # Подготавливаем deep промпт - deep_test_prompt = deep_prompt_ru.replace('{description}', test_description).replace('{composition}', test_composition) - - print(f" Длина deep промпта: {len(deep_test_prompt)} символов") - print(f" Deep промпт начинается с: {deep_test_prompt[:100]}...") - - # Проверяем наличие ключевых элементов deep промпта - deep_key_elements = [ - 'критический анализ', - 'токсиколог', - 'косметолог', - 'маркетинговые уловки', - 'формальдегид-релизеры' - ] - - found_deep_elements = [] - for element in deep_key_elements: - if element.lower() in deep_test_prompt.lower(): - found_deep_elements.append(element) - - print(f" Найдено ключевых элементов deep промпта: {len(found_deep_elements)}/{len(deep_key_elements)}") - - if len(found_deep_elements) >= 3: - print("✅ DEEP ПРОМПТ СОДЕРЖИТ ВСЕ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - else: - print("❌ DEEP ПРОМПТ НЕ СОДЕРЖИТ НЕОБХОДИМЫЕ ЭЛЕМЕНТЫ") - return False - - except Exception as e: - print(f"❌ ОШИБКА ПОДГОТОВКИ DEEP ПРОМПТА: {e}") - return False - - # Шаг 6: Финальная проверка интеграции - print("\n📋 ШАГ 6: Финальная проверка интеграции") - print("-" * 60) - - try: - # Имитируем полный цикл анализа - description = product_info['description'] - composition = product_info['composition'] - categories = product_info['categories'] - - # Проверяем что все данные корректны - checks = [ - ('Описание', len(description) > 50), - ('Состав', len(composition) > 20), - ('Категория', len(categories) > 0), - ('Цена', product_info['price']['amount'] > 0), - ('Рейтинг', product_info['rating']['value'] > 0) - ] - - passed_checks = 0 - for check_name, check_result in checks: - if check_result: - passed_checks += 1 - print(f" ✅ {check_name}: корректно") - else: - print(f" ❌ {check_name}: некорректно") - - print(f"\n📊 ПРОЙДЕНО ПРОВЕРОК: {passed_checks}/{len(checks)}") - - if passed_checks == len(checks): - print("✅ ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ") - else: - print("❌ НЕ ВСЕ ПРОВЕРКИ ПРОЙДЕНЫ") - return False - - except Exception as e: - print(f"❌ ОШИБКА ФИНАЛЬНОЙ ПРОВЕРКИ: {e}") - return False - - print("\n" + "=" * 80) - print("🎉 ИНТЕГРАЦИОННЫЙ ТЕСТ ПРОЙДЕН УСПЕШНО!") - print("=" * 80) - print("📋 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ:") - print(" ✅ Промпты корректно загружаются из manifest.json") - print(" ✅ Оригинальные промпты содержат все необходимые элементы анализа") - print(" ✅ Данные о товаре извлекаются корректно") - print(" ✅ AI анализ выполняется с использованием оригинальных промптов") - print(" ✅ Полный цикл анализа работает от начала до конца") - print("=" * 80) - - return True - -if __name__ == "__main__": - success = analyze_ozon_product_integration_test() - if not success: - print("❌ ИНТЕГРАЦИОННЫЙ ТЕСТ НЕ ПРОЙДЕН") - sys.exit(1) - else: - print("✅ ИНТЕГРАЦИОННЫЙ ТЕСТ ПРОЙДЕН") - sys.exit(0) \ No newline at end of file diff --git a/test_integration_prompts.py b/test_integration_prompts.py deleted file mode 100644 index 46375ae7..00000000 --- a/test_integration_prompts.py +++ /dev/null @@ -1,395 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ МЕХАНИЗМА ПРОМПТОВ -Тестирует полный цикл анализа товара Ozon с исправленными промптами -""" - -import json -import sys -import os -from typing import Dict, Any - -def simulate_ozon_product_data(): - """Симулирует реальные данные товара Ozon для тестирования""" - return { - 'title': 'Крем для лица с коллагеном и гиалуроновой кислотой', - 'description': '''Революционный anti-age крем с 3D лифтинг эффектом! - Инновационная формула с коллагеном и гиалуроновой кислотой обеспечивает: - • Мгновенное разглаживание морщин на 90% - • Глубокое увлажнение на 24 часа - • Лифтинг эффект уже через 7 дней использования - • Восстановление упругости кожи - Подходит для всех типов кожи, включая чувствительную.''', - 'composition': '''Aqua, Glycerin, Collagen, Sodium Hyaluronate, Palmitoyl Tripeptide-38, - Dimethicone, Parfum, Phenoxyethanol, Methylparaben, Propylparaben, - Butylparaben, Ethylparaben, DMDM Hydantoin, Quaternium-15, Octinoxate, - Oxybenzone, Triethanolamine, Carbomer, Allantoin''', - 'categories': ['krasota-i-zdorove-6500', 'uhod-za-kozhey-1234'], - 'price': {'text': '1 299 ₽', 'amount': 1299.0, 'currency': 'RUB'}, - 'rating': {'text': '4.2', 'value': 4.2, 'max_value': 5.0} - } - -def simulate_plugin_settings_with_prompts(): - """Симулирует настройки плагина с кастомными промптами""" - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'auto_request_deep_analysis': True, - 'search_for_analogs': True, - 'prompts': { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ОПТИМИЗИРОВАННЫЙ ПРОМПТ: Проанализируй косметический продукт на соответствие описания и состава. Будь максимально критичным к маркетинговым заявлениям. Описание: {description} Состав: {composition}', - 'en': 'CUSTOM OPTIMIZED PROMPT: Analyze cosmetic product for description and composition compliance. Be extremely critical of marketing claims. Description: {description} Composition: {composition}' - }, - 'deep': { - 'ru': 'КАСТОМНЫЙ ГЛУБОКИЙ ПРОМПТ: Проведи детальный медицинский анализ косметического продукта. Оцени безопасность состава и эффективность заявленных свойств. Описание: {description} Состав: {composition}', - 'en': 'CUSTOM DEEP PROMPT: Conduct detailed medical analysis of cosmetic product. Evaluate composition safety and claimed properties effectiveness. Description: {description} Composition: {composition}' - } - } - } - -def simulate_plugin_settings_empty(): - """Симулирует настройки плагина без кастомных промптов""" - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'auto_request_deep_analysis': True, - 'search_for_analogs': True - # prompts отсутствуют - должен использовать из manifest - } - -def simulate_plugin_settings_partial(): - """Симулирует настройки плагина с частично заполненными промптами""" - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'auto_request_deep_analysis': True, - 'search_for_analogs': False, - 'prompts': { - 'optimized': { - 'ru': 'ЧАСТИЧНЫЙ КАСТОМНЫЙ ПРОМПТ ТОЛЬКО РУССКИЙ' - # en отсутствует - должен использовать из manifest - } - # deep отсутствует - должен использовать из manifest - } - } - -def load_manifest(): - """Загружает manifest.json для тестирования""" - manifest_path = os.path.join(os.path.dirname(__file__), 'chrome-extension/public/plugins/ozon-analyzer/manifest.json') - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - return json.load(f) - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return None - -def test_integration_scenario_1_full_custom_prompts(): - """Интеграционный тест 1: Полностью заполненные кастомные промпты""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 1: Полностью заполненные кастомные промпты") - print("=" * 70) - - # Загружаем данные товара и настройки - product_data = simulate_ozon_product_data() - plugin_settings = simulate_plugin_settings_with_prompts() - - print("📋 Тестовые данные:") - print(f" Товар: {product_data['title']}") - print(f" Настройки: {len(plugin_settings.get('prompts', {}))} типов промптов") - - # Симулируем процесс анализа - description = product_data['description'] - composition = product_data['composition'] - categories = product_data['categories'] - - print(f"📝 Описание: {description[:100]}...") - print(f"🧪 Состав: {composition[:100]}...") - print(f"🏷️ Категории: {categories}") - - # Проверяем что категории корректные для анализа - if not categories or 'krasota-i-zdorove-6500' not in categories: - print("❌ Товар не принадлежит к категории косметики") - return False - - # Симулируем логику анализа - print("\n🔍 Симуляция анализа с кастомными промптами:") - - # Проверяем какие промпты будут использоваться - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - custom_prompt = plugin_settings.get('prompts', {}).get(prompt_type, {}).get(lang, '') - if custom_prompt and len(custom_prompt.strip()) > 0: - print(f" ✅ {prompt_type}.{lang}: Кастомный промпт (длина: {len(custom_prompt)})") - else: - print(f" ❌ {prompt_type}.{lang}: Кастомный промпт отсутствует") - - # Симулируем результат анализа - analysis_result = { - 'score': 4, - 'reasoning': f"Анализ с использованием кастомных промптов. Маркетинговые заявления 'революционный' и '3D лифтинг' не подтверждены активами в достаточной концентрации. Найдены проблемные консерванты (парабены, формальдегид-релизеры).", - 'confidence': 0.85, - 'red_flags': ['Methylparaben', 'Propylparaben', 'DMDM Hydantoin', 'Octinoxate'], - 'marketing_lies': ['90% разглаживание морщин', 'эффект через 7 дней'] - } - - print("\n📊 Результат анализа:") - print(f" Оценка: {analysis_result['score']}/10") - print(f" Уверенность: {analysis_result['confidence']}") - print(f" Красные флаги: {len(analysis_result['red_flags'])} найдено") - print(f" Маркетинговые преувеличения: {len(analysis_result['marketing_lies'])} найдено") - - # Проверяем критичность обнаруженных проблем - if analysis_result['score'] < 7: - deep_analysis_offered = True - print(f" 🔍 Глубокий анализ: ПРЕДЛОЖЕН (оценка {analysis_result['score']} < 7)") - else: - deep_analysis_offered = False - print(f" ✅ Глубокий анализ: НЕ ТРЕБУЕТСЯ (оценка {analysis_result['score']} >= 7)") - - return True - -def test_integration_scenario_2_empty_custom_prompts(): - """Интеграционный тест 2: Пустые кастомные промпты (fallback на manifest)""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 2: Пустые кастомные промпты (fallback на manifest)") - print("=" * 70) - - # Загружаем данные товара и настройки - product_data = simulate_ozon_product_data() - plugin_settings = simulate_plugin_settings_empty() - - print("📋 Тестовые данные:") - print(f" Товар: {product_data['title']}") - print(f" Настройки: промпты отсутствуют (должны использоваться из manifest)") - - # Загружаем manifest для проверки fallback - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return False - - manifest_prompts = manifest.get('options', {}).get('prompts', {}) - print(f"📋 Доступные промпты в manifest: {list(manifest_prompts.keys())}") - - # Проверяем что промпты из manifest корректные - success = True - for prompt_type in ['optimized', 'deep']: - if prompt_type in manifest_prompts: - print(f" ✅ {prompt_type}: Найден в manifest") - for lang in ['ru', 'en']: - lang_data = manifest_prompts[prompt_type].get(lang, {}) - default_prompt = lang_data.get('default', '') - if default_prompt and len(default_prompt) > 100: - print(f" ✅ {lang}: Промпт найден (длина: {len(default_prompt)})") - else: - print(f" ❌ {lang}: Промпт отсутствует или слишком короткий") - success = False - else: - print(f" ❌ {prompt_type}: Не найден в manifest") - success = False - - if success: - print("✅ Fallback на manifest работает корректно") - else: - print("❌ Fallback на manifest не работает") - - return success - -def test_integration_scenario_3_problematic_product(): - """Интеграционный тест 3: Проблемный товар с низкой оценкой""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 3: Проблемный товар с низкой оценкой") - print("=" * 70) - - # Создаем проблемный товар с подозрительными ингредиентами - problematic_product = { - 'title': 'Чудо-крем с революционной формулой 5D', - 'description': '''НЕВЕРОЯТНЫЙ ПРОРЫВ В КОСМЕТОЛОГИИ! - Революционная 5D формула с наночастицами обеспечивает: - • Полное исчезновение морщин за 3 дня - • Увеличение упругости кожи на 500% - • Эффект ботокса без инъекций - • Вечная молодость уже после первого применения - • Клинически протестировано в секретных лабораториях''', - 'composition': '''Aqua, Parfum, Methylparaben, Propylparaben, Butylparaben, - Ethylparaben, DMDM Hydantoin, Quaternium-15, Octinoxate, Oxybenzone, - Triethanolamine, Sodium Lauryl Sulfate, Artificial Colors, Alcohol Denat''', - 'categories': ['krasota-i-zdorove-6500'], - 'price': {'text': '5 999 ₽', 'amount': 5999.0, 'currency': 'RUB'}, - 'rating': {'text': '2.1', 'value': 2.1, 'max_value': 5.0} - } - - plugin_settings = simulate_plugin_settings_with_prompts() - - print("📋 Тестовый товар:") - print(f" Название: {problematic_product['title']}") - print(f" Цена: {problematic_product['price']['text']}") - print(f" Рейтинг: {problematic_product['rating']['text']}") - - # Анализируем состав на проблемные ингредиенты - composition_lower = problematic_product['composition'].lower() - - red_flags = [] - if 'paraben' in composition_lower: - red_flags.append('Парабены (потенциальные эндокринные дизрапторы)') - if 'dmdm' in composition_lower: - red_flags.append('DMDM Hydantoin (формальдегид-релизер)') - if 'octinoxate' in composition_lower or 'oxybenzone' in composition_lower: - red_flags.append('Устаревшие УФ-фильтры (Octinoxate, Oxybenzone)') - if 'sodium lauryl sulfate' in composition_lower: - red_flags.append('Sodium Lauryl Sulfate (агрессивный ПАВ)') - - print(f"🚨 Обнаружено красных флагов: {len(red_flags)}") - for flag in red_flags: - print(f" ❌ {flag}") - - # Анализируем маркетинговые заявления - description_lower = problematic_product['description'].lower() - marketing_lies = [] - - if 'революцион' in description_lower or 'революц' in description_lower: - marketing_lies.append('"Революционная формула" - требует доказательств') - if '5d' in description_lower: - marketing_lies.append('"5D формула" - маркетинговый термин без научного обоснования') - if '500%' in description_lower: - marketing_lies.append('"500% увеличение упругости" - завышенные обещания') - if 'вечная молодость' in description_lower: - marketing_lies.append('"Вечная молодость" - невозможно выполнить обещание') - - print(f"🎭 Маркетинговые преувеличения: {len(marketing_lies)}") - for lie in marketing_lies: - print(f" ⚠️ {lie}") - - # Вычисляем ожидаемую оценку - score_reduction = len(red_flags) + len(marketing_lies) - expected_score = max(1, 10 - score_reduction) - - print(f"📊 Ожидаемая оценка: {expected_score}/10 (снижение на {score_reduction} баллов)") - - if expected_score <= 5: - print("🔍 Глубокий анализ: НЕОБХОДИМ (оценка слишком низкая)") - else: - print("✅ Глубокий анализ: Не требуется") - - return True - -def test_integration_scenario_4_manifest_structure(): - """Интеграционный тест 4: Проверка структуры manifest.json""" - print("\n🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ 4: Проверка структуры manifest.json") - print("=" * 70) - - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return False - - print("📋 Анализ структуры manifest.json:") - - # Проверяем основные поля - required_fields = ['name', 'version', 'options'] - for field in required_fields: - if field in manifest: - print(f" ✅ {field}: {manifest[field]}") - else: - print(f" ❌ {field}: ОТСУТСТВУЕТ") - - # Проверяем структуру промптов - options = manifest.get('options', {}) - if not options: - print(" ❌ options: ОТСУТСТВУЕТ") - return False - - prompts = options.get('prompts', {}) - if not prompts: - print(" ❌ prompts: ОТСУТСТВУЕТ") - return False - - print(f" ✅ prompts: Найдены типы - {list(prompts.keys())}") - - # Проверяем каждый тип промпта - for prompt_type in ['optimized', 'deep']: - if prompt_type not in prompts: - print(f" ❌ {prompt_type}: Не найден") - continue - - print(f" ✅ {prompt_type}:") - type_prompts = prompts[prompt_type] - - for lang in ['ru', 'en']: - if lang not in type_prompts: - print(f" ❌ {lang}: Не найден") - continue - - lang_data = type_prompts[lang] - print(f" ✅ {lang}:") - - # Проверяем обязательные поля - for field in ['type', 'default']: - if field in lang_data: - value = lang_data[field] - if field == 'default' and isinstance(value, str): - length = len(value) - print(f" ✅ {field}: {length} символов") - if length < 100: - print(f" ⚠️ Внимание: очень короткий промпт ({length} символов)") - else: - print(f" ✅ {field}: {value}") - else: - print(f" ❌ {field}: ОТСУТСТВУЕТ") - - # Проверяем настройки AI моделей - ai_models = manifest.get('ai_models', {}) - if ai_models: - print(f" ✅ ai_models: {len(ai_models)} моделей") - for model_name, model_alias in ai_models.items(): - print(f" ✅ {model_name}: {model_alias}") - else: - print(" ❌ ai_models: ОТСУТСТВУЮТ") - - return True - -def run_integration_tests(): - """Запуск всех интеграционных тестов""" - print("🔍 НАЧИНАЕМ ИНТЕГРАЦИОННОЕ ТЕСТИРОВАНИЕ МЕХАНИЗМА ПРОМПТОВ") - print("=" * 90) - - # Запуск тестовых сценариев - results = {} - - print("\n🚀 ЗАПУСК ИНТЕГРАЦИОННЫХ ТЕСТОВ") - print("=" * 50) - - results['integration_1'] = test_integration_scenario_1_full_custom_prompts() - results['integration_2'] = test_integration_scenario_2_empty_custom_prompts() - results['integration_3'] = test_integration_scenario_3_problematic_product() - results['integration_4'] = test_integration_scenario_4_manifest_structure() - - # Анализ результатов - print("\n📊 АНАЛИЗ РЕЗУЛЬТАТОВ ИНТЕГРАЦИОННОГО ТЕСТИРОВАНИЯ") - print("=" * 60) - - passed = 0 - total = 0 - - for test_name, result in results.items(): - total += 1 - if result: - passed += 1 - print(f"✅ {test_name}: ПРОЙДЕН") - else: - print(f"❌ {test_name}: ПРОВАЛЕН") - - print(f"\n📈 ИТОГ: {passed}/{total} интеграционных тестов пройдено") - - if passed == total: - print("🎉 ВСЕ ИНТЕГРАЦИОННЫЕ ТЕСТЫ ПРОЙДЕНЫ!") - print("✅ Механизм промптов полностью функционален") - print("✅ Fallback логика работает корректно") - print("✅ Структура manifest.json корректна") - else: - print("⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ!") - print("Требуется дополнительная диагностика и исправление проблем") - - return results - -if __name__ == "__main__": - run_integration_tests() \ No newline at end of file diff --git a/test_json_parsing.py b/test_json_parsing.py deleted file mode 100644 index 46c547e9..00000000 --- a/test_json_parsing.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python3 -""" -Тестовый скрипт для проверки парсинга JSON ответов Gemini API -""" - -import json -import re - -def test_gemini_response_parsing(): - """Тестирование обработки ответа Gemini с markdown обёрткой.""" - - # Тестовые данные из логов - gemini_response = '''```json -{ - "score": 9, - "reasoning": "Описание товара полностью соответствует предоставленным структурированным данным. Анализ показал высокий уровень соответствия между заявленными свойствами продукта и его фактическим составом. Отмечается наличие ключевых активных ингредиентов, которые действительно способны оказывать заявленное воздействие на кожу.", - "confidence": 1 -} -```''' - - print("=== ТЕСТИРОВАНИЕ ОБРАБОТКИ ОТВЕТА GEMINI ===") - print(f"Исходный ответ ({len(gemini_response)} символов):") - print(repr(gemini_response)) - print() - - # Текущая логика обработки - print("1. Текущая логика обработки:") - cleaned_str = gemini_response.strip().replace('```json', '').replace('```', '') - print(f"После очистки ({len(cleaned_str)} символов):") - print(repr(cleaned_str)) - print() - - try: - parsed = json.loads(cleaned_str) - print("✅ JSON успешно распарсен:") - print(json.dumps(parsed, indent=2, ensure_ascii=False)) - return True - except json.JSONDecodeError as je: - print(f"❌ Ошибка парсинга JSON: {je}") - print(f"Позиция ошибки: {je.pos}") - if je.pos is not None and je.pos < len(cleaned_str): - start = max(0, je.pos - 20) - end = min(len(cleaned_str), je.pos + 20) - print(f"Контекст ошибки: ...{cleaned_str[start:end]}...") - print() - - # Улучшенная логика обработки - print("2. Улучшенная логика обработки:") - - # Шаг 1: Более тщательная очистка markdown - improved_cleaned = gemini_response.strip() - - # Убираем ```json в начале - if improved_cleaned.startswith('```json'): - improved_cleaned = improved_cleaned[7:] # len('```json') - elif improved_cleaned.startswith('```'): - improved_cleaned = improved_cleaned[3:] # len('```') - - # Убираем ``` в конце - if improved_cleaned.endswith('```'): - improved_cleaned = improved_cleaned[:-3] - - # Финальная очистка пробелов - improved_cleaned = improved_cleaned.strip() - - print(f"После улучшенной очистки ({len(improved_cleaned)} символов):") - print(repr(improved_cleaned)) - print() - - try: - parsed = json.loads(improved_cleaned) - print("✅ JSON успешно распарсен после улучшения:") - print(json.dumps(parsed, indent=2, ensure_ascii=False)) - return True - except json.JSONDecodeError as je: - print(f"❌ Ошибка парсинга даже после улучшения: {je}") - print() - - # Шаг 3: Использование регулярных выражений для извлечения JSON - print("3. Извлечение JSON с помощью regex:") - - # Паттерн для поиска JSON блока - json_pattern = r'```(?:json)?\s*\n?(\{.*?\})\s*\n?```' - match = re.search(json_pattern, gemini_response, re.DOTALL | re.IGNORECASE) - - if match: - extracted_json = match.group(1).strip() - print(f"Извлечено с помощью regex ({len(extracted_json)} символов):") - print(repr(extracted_json)) - print() - - try: - parsed = json.loads(extracted_json) - print("✅ JSON успешно распарсен с помощью regex:") - print(json.dumps(parsed, indent=2, ensure_ascii=False)) - return True - except json.JSONDecodeError as je: - print(f"❌ Ошибка парсинга извлечённого JSON: {je}") - else: - print("❌ Не удалось извлечь JSON с помощью regex") - - return False - -def test_edge_cases(): - """Тестирование edge cases с экранированием кавычек.""" - - print("\n=== ТЕСТИРОВАНИЕ EDGE CASES ===") - - # Edge case 1: Экранированные кавычки (как при передаче JS->Python) - escaped_response = '"```json\\n{\\n \\"score\\": 9,\\n \\"reasoning\\": \\"test\\",\\n \\"confidence\\": 1\\n}\\n```"' - - print("Edge case 1: Экранированные кавычки") - print(f"Исходный ответ: {repr(escaped_response)}") - - try: - # Попытка парсинга как JSON строки - unescaped = json.loads(escaped_response) - print(f"После json.loads: {repr(unescaped)}") - - # Теперь применяем обычную очистку - cleaned = unescaped.strip().replace('```json', '').replace('```', '') - print(f"После очистки: {repr(cleaned)}") - - parsed = json.loads(cleaned) - print(f"✅ Успешно распарсен: {parsed}") - - except Exception as e: - print(f"❌ Ошибка: {e}") - - # Edge case 2: Двойная обёртка - double_wrapped = '```json\n```json\n{\n "score": 9,\n "reasoning": "test",\n "confidence": 1\n}\n```\n```' - - print("\nEdge case 2: Двойная обёртка") - print(f"Исходный ответ: {repr(double_wrapped)}") - - try: - cleaned = double_wrapped.strip().replace('```json', '').replace('```', '') - print(f"После очистки: {repr(cleaned)}") - - parsed = json.loads(cleaned) - print(f"✅ Успешно распарсен: {parsed}") - - except Exception as e: - print(f"❌ Ошибка: {e}") - - # Edge case 3: Неправильная структура markdown - malformed = '```json{\n "score": 9,\n "reasoning": "test",\n "confidence": 1\n}```' - - print("\nEdge case 3: Неправильная структура markdown") - print(f"Исходный ответ: {repr(malformed)}") - - try: - cleaned = malformed.strip().replace('```json', '').replace('```', '') - print(f"После очистки: {repr(cleaned)}") - - parsed = json.loads(cleaned) - print(f"✅ Успешно распарсен: {parsed}") - - except Exception as e: - print(f"❌ Ошибка: {e}") - -if __name__ == "__main__": - success = test_gemini_response_parsing() - test_edge_cases() - print(f"\n{'✅ ОСНОВНОЙ ТЕСТ ПРОШЁЛ' if success else '❌ ОСНОВНОЙ ТЕСТ ПРОВАЛИЛСЯ'}") \ No newline at end of file diff --git a/test_json_parsing_final.py b/test_json_parsing_final.py deleted file mode 100644 index 93fac4f9..00000000 --- a/test_json_parsing_final.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python3 -""" -Тестовый скрипт для проверки исправленной логики clean_ai_response -с реальными данными из логов Gemini API. -""" - -import json -import re - -def test_clean_ai_response_logic(ai_response: str) -> dict: - """ - Моделирует логику clean_ai_response из mcp_server.py - """ - print(f"\n=== ТЕСТИРОВАНИЕ ОТВЕТА ===") - print(f"Исходный ответ: {ai_response}") - - # Шаг 1: Улучшенная очистка markdown обёртки - original_length = len(ai_response) - - # Регулярное выражение для удаления markdown обёртки ```json...``` - markdown_pattern = re.compile(r'```\s*json\s*\n?(.*?)\n?\s*```', re.IGNORECASE | re.DOTALL) - cleaned_str = markdown_pattern.sub(r'\1', ai_response.strip()) - - # Дополнительная очистка на случай если остались одиночные ``` - cleaned_str = cleaned_str.replace('```', '').strip() - - print(f"После очистки: {cleaned_str}") - print(f"Удалено символов: {original_length - len(cleaned_str)}") - - # Шаг 1.5: Попытка парсинга очищенного ответа напрямую - try: - parsed = json.loads(cleaned_str) - print(f"✅ JSON успешно распарсен напрямую: {parsed}") - - # Валидация формата ответа - if isinstance(parsed, dict) and 'score' in parsed: - score = parsed.get('score') - reasoning = parsed.get('reasoning') - print(f"✅ Прямой парсинг успешен: score={score}") - return parsed - else: - print(f"⚠️ Парсинг вернул словарь без поля 'score': {parsed}") - except json.JSONDecodeError as je: - print(f"❌ Прямой JSON парсинг провалился: {str(je)}") - - # Шаг 2: Попытка парсинга исходного ответа напрямую - try: - parsed = json.loads(ai_response.strip()) - print(f"✅ Исходный ответ успешно распарсен: {parsed}") - if isinstance(parsed, dict) and 'score' in parsed: - return parsed - except json.JSONDecodeError: - print("❌ Парсинг исходного ответа провалился") - - # Шаг 3: Альтернативное извлечение JSON - print("🔄 ШАГ 3: Альтернативное извлечение JSON") - alternative_result = extract_json_from_ai_response(ai_response) - if alternative_result: - print(f"✅ Альтернативное извлечение успешно: {alternative_result}") - return alternative_result - - # Шаг 4: Попытка ремонта JSON - print("🔧 ШАГ 4: Попытка ремонта JSON") - repair_result = attempt_json_repair(cleaned_str) - if repair_result: - print(f"✅ Ремонт JSON успешен: {repair_result}") - return repair_result - - # Финальный fallback - print("❌ Все методы провалились, используем fallback") - fallback_result = { - "score": 5, - "reasoning": f"Не удалось распарсить JSON. Исходный ответ: {cleaned_str[:100]}..." - } - return fallback_result - -def extract_json_from_ai_response(ai_response: str) -> dict: - """Альтернативная функция извлечения JSON из ответа AI.""" - print(" Стратегия 2: Поиск между маркерами кода") - - # Улучшенные паттерны для поиска JSON в markdown блоках - code_block_patterns = [ - r'```\s*json\s*\n?\s*(\{.*?\})\s*\n?\s*```', # ```json\n{...}\n``` - r'```\s*(\{.*?\})\s*```', # ```\n{...}\n``` - r'```[^\n]*\s*\n?\s*(\{.*?\})\s*\n?\s*```', # ```language\n{...}\n``` - ] - - for pattern_idx, pattern in enumerate(code_block_patterns): - matches = re.findall(pattern, ai_response, re.IGNORECASE | re.DOTALL) - print(f" Стратегия 2.{pattern_idx + 1}: найдено {len(matches)} совпадений") - - for match_idx, match in enumerate(matches): - try: - print(f" Попытка парсинга совпадения {match_idx + 1}: {match[:50]}...") - parsed = json.loads(match) - if isinstance(parsed, dict) and 'score' in parsed: - score = parsed.get('score') - print(f" ✅ Найден валидный JSON: score={score}") - return parsed - except json.JSONDecodeError as e: - print(f" Парсинг совпадения {match_idx + 1} провалился: {e}") - continue - - print(" ❌ Ни одна стратегия не сработала") - return None - -def attempt_json_repair(broken_json: str) -> dict: - """Функция ремонта поврежденного JSON.""" - print(" Шаг 1: Очистка от Markdown") - - original_json = broken_json - cleaned = original_json - - # Проверяем, является ли JSON уже чистым - try: - parsed_clean = json.loads(cleaned.strip()) - if isinstance(parsed_clean, dict) and 'score' in parsed_clean: - print(" ✅ JSON уже чистый") - return parsed_clean - except json.JSONDecodeError: - pass - - # Убираем Markdown обертки - markdown_patterns = [ - r'```\s*json\s*\n?\s*', - r'```\s*', - r'\s*```', - ] - - for pattern in markdown_patterns: - cleaned = re.sub(pattern, '', cleaned, flags=re.IGNORECASE) - - cleaned = cleaned.strip() - - # Проверяем после очистки - try: - parsed = json.loads(cleaned) - if isinstance(parsed, dict) and 'score' in parsed: - print(" ✅ После очистки Markdown получился валидный JSON") - return parsed - except json.JSONDecodeError: - pass - - print(" ❌ Ремонт JSON не удался") - return None - -def main(): - """Основная функция тестирования.""" - print("🧪 ТЕСТИРОВАНИЕ ИСПРАВЛЕННОЙ ЛОГИКИ clean_ai_response") - print("=" * 60) - - # Тестовый кейс из логов - test_cases = [ - { - "name": "Gemini API с markdown обёрткой", - "input": """```json -{ - "score": 9, - "reasoning": "Описание товара и состав хорошо согласуются...", - "confidence": 0.95 -} -```""", - "expected_score": 9 - }, - { - "name": "Чистый JSON без markdown", - "input": """{ - "score": 7, - "reasoning": "Хорошее соответствие состава описанию" -}""", - "expected_score": 7 - }, - { - "name": "JSON с пробелами после json", - "input": """```json -{ - "score": 8, - "reasoning": "Тест с пробелами" -} -```""", - "expected_score": 8 - } - ] - - for test_case in test_cases: - print(f"\n🔍 ТЕСТ: {test_case['name']}") - print("-" * 40) - - try: - result = test_clean_ai_response_logic(test_case['input']) - actual_score = result.get('score', 'N/A') - expected_score = test_case['expected_score'] - - if actual_score == expected_score: - print(f"✅ ТЕСТ ПРОЙДЕН: score={actual_score} (ожидалось {expected_score})") - else: - print(f"❌ ТЕСТ ПРОВАЛЕН: score={actual_score} (ожидалось {expected_score})") - - print(f"Полный результат: {result}") - - except Exception as e: - print(f"❌ ОШИБКА ТЕСТИРОВАНИЯ: {e}") - - print("\n" + "=" * 60) - print("🏁 ТЕСТИРОВАНИЕ ЗАВЕРШЕНО") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_manifest_prompts_loading.py b/test_manifest_prompts_loading.py deleted file mode 100644 index 6bd51527..00000000 --- a/test_manifest_prompts_loading.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Тестовый сценарий 1: Проверка корректности загрузки оригинальных промптов из manifest.json - -Цель: Проверить, что функция get_user_prompts() корректно извлекает промпты из manifest.json -и правильно структурирует их для использования в AI анализе. -""" - -import json -import sys -import os - -# Добавляем путь к папке плагина в sys.path для импорта функций -sys.path.append('./chrome-extension/public/plugins/ozon-analyzer') - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - # Имитируем наличие manifest.json в globals - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - return default - -def safe_dict_get(data, key, default=None): - """Безопасное получение значения из словаря.""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts(plugin_settings=None): - """ - Функция загрузки промптов (скопированная из mcp_server.py для тестирования). - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТОВЫЙ СЦЕНАРИЙ) =====") - - try: - # Диагностика доступа к manifest - console_log("🔍 ДИАГНОСТИКА ДОСТУПА К MANIFEST:") - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - console_log(f" manifest.options: {manifest.get('options') is not None}") - if manifest.get('options'): - console_log(f" manifest.options.prompts: {manifest.get('options', {}).get('prompts') is not None}") - else: - console_log(" ❌ manifest НЕ НАЙДЕН в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - - # Структура промптов - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest: {manifest is not None}") - - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - options = safe_dict_get(manifest, 'options', {}) - console_log(f" manifest.options: {options is not None}") - if options: - console_log(f" manifest.options ключи: {list(options.keys())}") - console_log(f" manifest.options.prompts: {options.get('prompts') is not None}") - if options.get('prompts'): - console_log(f" manifest.options.prompts ключи: {list(options.get('prompts', {}).keys())}") - - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - else: - console_log(" ❌ manifest не найден в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Fallback на manifest.json (основной сценарий для тестирования) - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log("🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def run_test_scenario_1(): - """Выполнение тестового сценария 1.""" - print("=" * 80) - print("🧪 ТЕСТОВЫЙ СЦЕНАРИЙ 1: Проверка загрузки оригинальных промптов из manifest.json") - print("=" * 80) - - # Тест 1: Загрузка промптов без пользовательских настроек - print("\n📋 ТЕСТ 1: Загрузка промптов без пользовательских настроек") - print("-" * 60) - - try: - prompts = get_user_prompts() - - # Проверяем что промпты загружены - loaded_count = 0 - total_count = 0 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - total_count += 1 - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - loaded_count += 1 - print(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - print(f" ❌ {prompt_type}.{lang}: НЕ загружен") - - print(f"\n📊 РЕЗУЛЬТАТ ТЕСТА 1: {loaded_count}/{total_count} промптов загружено") - - if loaded_count >= 2: # Минимум 2 промпта должны быть загружены - print("✅ ТЕСТ 1 ПРОЙДЕН: Оригинальные промпты из manifest.json загружаются корректно") - else: - print("❌ ТЕСТ 1 НЕ ПРОЙДЕН: Недостаточно промптов загружено из manifest.json") - - except Exception as e: - print(f"❌ ТЕСТ 1 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 2: Проверка структуры промптов - print("\n📋 ТЕСТ 2: Проверка структуры промптов") - print("-" * 60) - - try: - prompts = get_user_prompts() - - # Проверяем структуру промптов - expected_structure = { - 'optimized': {'ru': str, 'en': str}, - 'deep': {'ru': str, 'en': str} - } - - structure_valid = True - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - expected_type = expected_structure[prompt_type][lang] - actual_type = type(prompts[prompt_type][lang]) - - if actual_type == expected_type: - print(f" ✅ {prompt_type}.{lang}: тип данных корректный ({actual_type.__name__})") - else: - print(f" ❌ {prompt_type}.{lang}: неверный тип данных (ожидали {expected_type.__name__}, получили {actual_type.__name__})") - structure_valid = False - - if structure_valid: - print("✅ ТЕСТ 2 ПРОЙДЕН: Структура промптов корректная") - else: - print("❌ ТЕСТ 2 НЕ ПРОЙДЕН: Структура промптов некорректная") - - except Exception as e: - print(f"❌ ТЕСТ 2 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - # Тест 3: Проверка содержания промптов - print("\n📋 ТЕСТ 3: Проверка содержания промптов") - print("-" * 60) - - try: - prompts = get_user_prompts() - - # Проверяем что промпты содержат ключевые слова - content_checks = { - 'optimized.ru': ['токсиколог', 'косметолог', 'анализ', 'соответствия'], - 'optimized.en': ['toxicologist', 'cosmetic chemist', 'analysis', 'compliance'], - 'deep.ru': ['глубокий анализ', 'медицинской', 'научной', 'точке зрения'], - 'deep.en': ['deep analysis', 'medical', 'scientific', 'perspective'] - } - - content_valid = True - - for prompt_key, keywords in content_checks.items(): - prompt_type, lang = prompt_key.split('.') - prompt_value = prompts[prompt_type][lang] - - found_keywords = [] - for keyword in keywords: - if keyword.lower() in prompt_value.lower(): - found_keywords.append(keyword) - - if len(found_keywords) >= 2: # Минимум 2 ключевых слова - print(f" ✅ {prompt_key}: найдено {len(found_keywords)}/{len(keywords)} ключевых слов") - else: - print(f" ❌ {prompt_key}: найдено только {len(found_keywords)}/{len(keywords)} ключевых слов") - content_valid = False - - if content_valid: - print("✅ ТЕСТ 3 ПРОЙДЕН: Содержание промптов корректное") - else: - print("❌ ТЕСТ 3 НЕ ПРОЙДЕН: Содержание промптов некорректное") - - except Exception as e: - print(f"❌ ТЕСТ 3 НЕ ПРОЙДЕН: Критическая ошибка: {e}") - - print("\n" + "=" * 80) - print("🧪 ЗАВЕРШЕНИЕ ТЕСТОВОГО СЦЕНАРИЯ 1") - print("=" * 80) - -if __name__ == "__main__": - run_test_scenario_1() \ No newline at end of file diff --git a/test_manifest_scenarios.py b/test_manifest_scenarios.py deleted file mode 100644 index ffb1bf2d..00000000 --- a/test_manifest_scenarios.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест для проверки исправления передачи manifest: протестировать сценарии -с manifest в plugin_settings и в globals -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем корневую директорию проекта в путь для импорта -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -class MockPyodideContext: - """Мок Pyodide контекста для тестирования разных сценариев передачи manifest""" - - def __init__(self): - self.globals = {} - - def set_global(self, key: str, value: Any): - """Установить переменную в globals""" - self.globals[key] = value - print(f" 📥 Установлена глобальная переменная {key}: {type(value)}") - - def get_global(self, key: str, default=None): - """Получить переменную из globals""" - return self.globals.get(key, default) - - def get_plugin_settings_manifest_scenario(self): - """Сценарий 1: manifest в pluginSettings""" - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - - # PluginSettings содержит manifest как поле - plugin_settings = { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'manifest': manifest_data # manifest как часть pluginSettings - } - - self.set_global('pluginSettings', plugin_settings) - console_log("✅ Сценарий 1: manifest передан в pluginSettings.manifest") - return plugin_settings - - def get_globals_manifest_scenario(self): - """Сценарий 2: manifest в globals""" - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - - # PluginSettings без manifest - plugin_settings = { - 'response_language': 'ru', - 'enable_deep_analysis': True - } - - # Manifest передается отдельно в globals - self.set_global('pluginSettings', plugin_settings) - self.set_global('manifest', manifest_data) - console_log("✅ Сценарий 2: manifest передан в globals.manifest") - return plugin_settings - -def get_user_prompts_plugin_settings_scenario(plugin_settings): - """ - Функция загрузки промптов для сценария с manifest в pluginSettings - """ - console_log("🔍 ЗАГРУЗКА ПРОМПТОВ (сценарий: manifest в pluginSettings)") - - prompts = {'optimized': {'ru': '', 'en': ''}, 'deep': {'ru': '', 'en': ''}} - - # В этом сценарии manifest находится в pluginSettings.manifest - manifest = safe_dict_get(plugin_settings, 'manifest', {}) - console_log(f" manifest из pluginSettings: {manifest is not None}") - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {bool(manifest_prompts)}") - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - manifest_value = safe_dict_get( - manifest_prompts, - prompt_type, {} - ).get(lang, {}).get('default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ {prompt_type}.{lang}: загружен из pluginSettings.manifest") - else: - console_log(f"⚠️ {prompt_type}.{lang}: не найден в pluginSettings.manifest") - - return prompts - -def get_user_prompts_globals_scenario(plugin_settings, pyodide_context): - """ - Функция загрузки промптов для сценария с manifest в globals - """ - console_log("🔍 ЗАГРУЗКА ПРОМПТОВ (сценарий: manifest в globals)") - - prompts = {'optimized': {'ru': '', 'en': ''}, 'deep': {'ru': '', 'en': ''}} - - # В этом сценарии manifest находится в globals - manifest = pyodide_context.get_global('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {bool(manifest_prompts)}") - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - manifest_value = safe_dict_get( - manifest_prompts, - prompt_type, {} - ).get(lang, {}).get('default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ {prompt_type}.{lang}: загружен из globals.manifest") - else: - console_log(f"⚠️ {prompt_type}.{lang}: не найден в globals.manifest") - - return prompts - -def test_scenario(name: str, scenario_function, prompt_loader_function, pyodide_context): - """Тестирование конкретного сценария""" - print(f"\n{'='*80}") - print(f"🧪 ТЕСТИРОВАНИЕ СЦЕНАРИЯ: {name}") - print('='*80) - - # Настройка сценария - plugin_settings = scenario_function() - - # Загрузка промптов - if 'globals' in name.lower(): - prompts = prompt_loader_function(plugin_settings, pyodide_context) - else: - prompts = prompt_loader_function(plugin_settings) - - # Проверка результатов - success_count = 0 - expected_min_length = 100 # Минимальная длина промпта - - print("\n📊 РЕЗУЛЬТАТЫ ЗАГРУЗКИ ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - length = len(prompt_value) if prompt_value else 0 - - if length > expected_min_length: - print(f"✅ {prompt_type}.{lang}: {length} символов") - success_count += 1 - else: - print(f"❌ {prompt_type}.{lang}: {length} символов (слишком короткий)") - - total_prompts = 4 - success_rate = success_count / total_prompts - - if success_rate >= 0.75: # Минимум 3 из 4 промптов - print(f"🎉 СЦЕНАРИЙ ПРОЙДЕН: {success_count}/{total_prompts} промптов загружено") - return True - else: - print(f"❌ СЦЕНАРИЙ НЕ ПРОЙДЕН: {success_count}/{total_prompts} промптов загружено") - return False - -def main(): - """Основная функция тестирования""" - print("🚀 ЗАПУСК ТЕСТОВ ПЕРЕДАЧИ MANIFEST") - print("Проверяем сценарии: manifest в pluginSettings vs manifest в globals") - - # Создаем Pyodide контекст - pyodide_context = MockPyodideContext() - - # Сценарий 1: manifest в pluginSettings - scenario1_passed = test_scenario( - "Manifest в pluginSettings", - pyodide_context.get_plugin_settings_manifest_scenario, - get_user_prompts_plugin_settings_scenario, - pyodide_context - ) - - # Очищаем globals для следующего сценария - pyodide_context.globals.clear() - - # Сценарий 2: manifest в globals - scenario2_passed = test_scenario( - "Manifest в globals", - pyodide_context.get_globals_manifest_scenario, - get_user_prompts_globals_scenario, - pyodide_context - ) - - # Итоговый результат - print(f"\n{'='*80}") - print("📋 ИТОГОВЫЕ РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ") - print('='*80) - - print(f"Сценарий 1 (pluginSettings.manifest): {'✅ ПРОЙДЕН' if scenario1_passed else '❌ НЕ ПРОЙДЕН'}") - print(f"Сценарий 2 (globals.manifest): {'✅ ПРОЙДЕН' if scenario2_passed else '❌ НЕ ПРОЙДЕН'}") - - if scenario1_passed and scenario2_passed: - print("\n🎉 ВСЕ СЦЕНАРИИ ПРОЙДЕНЫ: Исправление передачи manifest работает корректно!") - print("✅ Manifest может передаваться как в pluginSettings, так и в globals") - print("✅ Промпты корректно загружаются в обоих сценариях") - return True - else: - print("\n💥 ОБНАРУЖЕНЫ ПРОБЛЕМЫ: Некоторые сценарии не работают") - if not scenario1_passed: - print("❌ Проблема со сценарием pluginSettings.manifest") - if not scenario2_passed: - print("❌ Проблема со сценарием globals.manifest") - return False - -if __name__ == "__main__": - success = main() - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_manifest_transmission.py b/test_manifest_transmission.py deleted file mode 100644 index 526bb5a8..00000000 --- a/test_manifest_transmission.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест механизма передачи manifest.json в Pyodide контекст - -Сценарий 1: Тестирование корректности передачи manifest.json в Pyodide контекст -- Проверка что переменная manifest доступна в Pyodide globals -- Проверка что структура manifest.options.prompts правильно доступна -- Проверка что промпты корректно извлекаются из manifest.json -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем корневую директорию проекта в путь для импорта -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def test_manifest_transmission(): - """Тест передачи manifest.json в Pyodide контекст""" - print("🔍 ТЕСТ 1: Проверка корректности передачи manifest.json в Pyodide контекст") - print("=" * 70) - - # Загружаем manifest.json для сравнения - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - print(f"✅ Manifest.json загружен: {len(json.dumps(manifest_data))} символов") - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return False - - # Проверяем структуру промптов в manifest.json - manifest_prompts = manifest_data.get('options', {}).get('prompts', {}) - if not manifest_prompts: - print("❌ Структура промптов не найдена в manifest.json") - return False - - print("📋 Структура промптов в manifest.json:") - print(f" - optimized промпты: {list(manifest_prompts.get('optimized', {}).keys())}") - print(f" - deep промпты: {list(manifest_prompts.get('deep', {}).keys())}") - - # Проверяем наличие промптов по умолчанию - optimized_ru = manifest_prompts.get('optimized', {}).get('ru', {}).get('default', '') - optimized_en = manifest_prompts.get('optimized', {}).get('en', {}).get('default', '') - deep_ru = manifest_prompts.get('deep', {}).get('ru', {}).get('default', '') - deep_en = manifest_prompts.get('deep', {}).get('en', {}).get('default', '') - - print("📊 Длина промптов по умолчанию в manifest.json:") - print(f" - optimized.ru: {len(optimized_ru)} символов") - print(f" - optimized.en: {len(optimized_en)} символов") - print(f" - deep.ru: {len(deep_ru)} символов") - print(f" - deep.en: {len(deep_en)} символов") - - # Проверяем что все промпты присутствуют и не пустые - all_prompts_present = all([ - len(optimized_ru) > 100, - len(optimized_en) > 100, - len(deep_ru) > 100, - len(deep_en) > 100 - ]) - - if not all_prompts_present: - print("❌ Некоторые промпты отсутствуют или слишком короткие в manifest.json") - return False - - print("✅ Все промпты присутствуют в manifest.json") - - # Симулируем процесс передачи в Pyodide globals (как в offscreen.ts) - print("\n🔄 Симуляция передачи manifest в Pyodide globals...") - - # Симулируем Pyodide globals - class MockGlobals: - def __init__(self): - self.data = {} - - def set(self, key: str, value: Any): - self.data[key] = value - print(f" 📥 Установлен {key}: {type(value)} ({len(str(value))} символов)") - - def get(self, key: str, default=None): - return self.data.get(key, default) - - # Создаем mock Pyodide globals - globals_mock = MockGlobals() - - # Передаем manifest (как в offscreen.ts строки 518-519) - globals_mock.set('manifest', manifest_data) - - # Верифицируем передачу (как в offscreen.ts строки 525-534) - print("\n🔍 Верификация передачи manifest...") - - verify_manifest = globals_mock.get('manifest') - if not verify_manifest: - print("❌ Manifest НЕ НАЙДЕН в globals после передачи") - return False - - print(f"✅ Manifest подтвержден в globals: {type(verify_manifest)}") - - if isinstance(verify_manifest, dict): - verify_keys = list(verify_manifest.keys()) - print(f"✅ Ключи в globals: {verify_keys}") - - # Проверяем что промпты доступны через globals - globals_prompts = verify_manifest.get('options', {}).get('prompts', {}) - print(f"✅ Промпты доступны через globals: {type(globals_prompts)}") - - # Проверяем каждый промпт - test_cases = [ - ('optimized', 'ru', optimized_ru), - ('optimized', 'en', optimized_en), - ('deep', 'ru', deep_ru), - ('deep', 'en', deep_en) - ] - - all_match = True - for prompt_type, lang, expected in test_cases: - globals_value = globals_prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - if globals_value == expected: - print(f"✅ {prompt_type}.{lang}: совпадает ({len(globals_value)} символов)") - else: - print(f"❌ {prompt_type}.{lang}: НЕ СОВПАДАЕТ!") - print(f" Ожидалось: {len(expected)} символов") - print(f" Получено: {len(globals_value)} символов") - all_match = False - - if all_match: - print("\n🎉 ТЕСТ ПРОЙДЕН: Manifest успешно передан в Pyodide контекст!") - return True - else: - print("\n❌ ТЕСТ НЕ ПРОЙДЕН: Некоторые промпты не совпадают после передачи") - return False - - else: - print(f"❌ Manifest в globals не является словарем: {type(verify_manifest)}") - return False - -if __name__ == "__main__": - success = test_manifest_transmission() - if success: - print("\n🎯 РЕЗУЛЬТАТ: Исправление механизма передачи manifest.json работает корректно!") - else: - print("\n💥 РЕЗУЛЬТАТ: Обнаружены проблемы с передачей manifest.json!") - - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_manifest_transmission_integration.py b/test_manifest_transmission_integration.py deleted file mode 100644 index 8b164ad0..00000000 --- a/test_manifest_transmission_integration.py +++ /dev/null @@ -1,376 +0,0 @@ -#!/usr/bin/env python3 -""" -Интеграционный тест передачи manifest в Pyodide и использования промптов в mcp_server.py -""" - -import json -import sys -import os -from typing import Any - -def console_log(message: str): - """Функция логирования для имитации Pyodide среды.""" - print(f"[PYTHON_LOG] {message}") - -def chat_message(message: str): - """Имитация отправки сообщения в чат.""" - print(f"[CHAT_MESSAGE] {message}") - -def get_pyodide_var(name: str, default=None): - """Имитация функции доступа к Pyodide globals.""" - if name == 'manifest': - try: - with open('./chrome-extension/public/plugins/ozon-analyzer/manifest.json', 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - return manifest_data - except Exception as e: - console_log(f"Ошибка загрузки manifest.json: {e}") - return default - - elif name == 'pluginSettings': - return { - 'response_language': 'ru', - 'enable_deep_analysis': True, - 'search_for_analogs': False - } - - return default - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_user_prompts_test(plugin_settings=None): - """ - Тестовая версия функции get_user_prompts из mcp_server.py - """ - console_log(f"🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (ТЕСТ) =====") - - try: - # Диагностика доступа к manifest - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest из globals: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - console_log(f" manifest.options: {manifest.get('options') is not None}") - if manifest.get('options'): - console_log(f" manifest.options.prompts: {manifest.get('options', {}).get('prompts') is not None}") - - # Диагностика входных данных - console_log(f"🔍 Диагностика входных данных:") - console_log(f" plugin_settings type: {type(plugin_settings)}") - console_log(f" plugin_settings is None: {plugin_settings is None}") - console_log(f" plugin_settings keys: {list(plugin_settings.keys()) if isinstance(plugin_settings, dict) else 'не словарь'}") - - # Исправлено: промпты уже доступны в plugin_settings - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - console_log(f" custom_prompts_raw: {custom_prompts_raw}") - console_log(f" custom_prompts_raw type: {type(custom_prompts_raw)}") - - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - # Получаем manifest.json из globals для fallback значений - manifest = get_pyodide_var('manifest', {}) - console_log(f" manifest: {manifest is not None}") - console_log(f" manifest type: {type(manifest)}") - - if manifest: - console_log(f" manifest ключи: {list(manifest.keys())}") - options = safe_dict_get(manifest, 'options', {}) - console_log(f" manifest.options: {options is not None}") - if options: - console_log(f" manifest.options ключи: {list(options.keys())}") - console_log(f" manifest.options.prompts: {options.get('prompts') is not None}") - if options.get('prompts'): - console_log(f" manifest.options.prompts ключи: {list(options.get('prompts', {}).keys())}") - - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - console_log(f" manifest_prompts type: {type(manifest_prompts)}") - else: - console_log(f" ❌ manifest не найден в globals - ЭТО ОСНОВНАЯ ПРОБЛЕМА!") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - console_log(f" prompt_type_data ({prompt_type}): {prompt_type_data}") - - custom_value = safe_dict_get(prompt_type_data, lang, '') - console_log(f" custom_value для {lang}: {repr(custom_value)}") - console_log(f" custom_value type: {type(custom_value)}") - console_log(f" custom_value length: {len(custom_value) if custom_value else 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - console_log(f"ℹ️ Кастомный промпт не найден или пустой для {prompt_type}.{lang}") - - # Fallback 1: manifest.json - исправленная логика извлечения - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - - # Итоговая диагностика - console_log(f"🔍 Итоговая диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подробная диагностика каждого промпта - console_log(f"🔍 ДЕТАЛЬНАЯ ДИАГНОСТИКА ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - console_log(f" ✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - console_log(f" ❌ {prompt_type}.{lang}: НЕ загружен (пустой)") - - # Подсчет загруженных промптов - loaded_prompts = len([p for pt in prompts.values() for p in pt.values() if p and len(p.strip()) > 0]) - total_prompts = len([p for pt in prompts.values() for p in pt.values()]) - console_log(f"📋 Загружено промптов: {loaded_prompts}/{total_prompts} (кастомных: {loaded_prompts})") - - # ДИАГНОСТИКА ПРОБЛЕМЫ: Проверяем источник каждого промпта - console_log(f"🔍 ДИАГНОСТИКА ИСТОЧНИКОВ ПРОМПТОВ:") - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 0: - # Определяем источник промпта - if custom_prompts_raw and custom_prompts_raw.get(prompt_type, {}).get(lang) == prompt_value: - source = "кастомный" - elif manifest_prompts and manifest_prompts.get(prompt_type, {}).get(lang, {}).get('default') == prompt_value: - source = "manifest default" - else: - source = "встроенный fallback" - console_log(f" 📍 {prompt_type}.{lang}: источник = {source}") - else: - console_log(f" ❌ {prompt_type}.{lang}: источник = отсутствует") - - console_log(f"🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - console_log(f"🔍 Детальная диагностика ошибки:") - console_log(f" Exception type: {type(e).__name__}") - console_log(f" Exception args: {e.args}") - console_log(f" Plugin settings: {plugin_settings}") - console_log(f" Plugin settings type: {type(plugin_settings)}") - - # Трассировка стека для диагностики - import traceback - stack_trace = traceback.format_exc() - console_log(f" Stack trace: {stack_trace}") - - # Критический fallback - возвращаем пустые промпты - console_log(f"⚠️ Возвращаем критический fallback с пустыми промптами") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """ - Возвращает встроенные промпты по умолчанию. - """ - return "Встроенный промпт по умолчанию" - -def test_manifest_transmission_and_usage(): - """ - Тест передачи manifest и использования промптов в mcp_server.py - """ - print("=" * 80) - print("🧪 ИНТЕГРАЦИОННЫЙ ТЕСТ ПЕРЕДАЧИ MANIFEST") - print("=" * 80) - - # Шаг 1: Загрузка manifest - print("\n📋 ШАГ 1: Загрузка manifest.json") - print("-" * 50) - - manifest = get_pyodide_var('manifest') - if not manifest: - print("❌ ОШИБКА: Manifest не загружен") - return False - - print(f"✅ Manifest загружен: {len(json.dumps(manifest))} символов") - print(f" Название: {manifest.get('name', 'N/A')}") - print(f" Версия: {manifest.get('version', 'N/A')}") - - # Шаг 2: Проверка структуры промптов - print("\n📋 ШАГ 2: Проверка структуры промптов в manifest") - print("-" * 50) - - prompts = manifest.get('options', {}).get('prompts', {}) - if not prompts: - print("❌ ОШИБКА: Структура промптов не найдена") - return False - - expected_types = ['optimized', 'deep'] - expected_langs = ['ru', 'en'] - - prompt_counts = {} - for prompt_type in expected_types: - for lang in expected_langs: - key = f"{prompt_type}.{lang}" - prompt_value = prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - prompt_counts[key] = len(prompt_value) - if prompt_value: - print(f"✅ {key}: {len(prompt_value)} символов") - else: - print(f"❌ {key}: отсутствует") - - # Проверяем что есть хотя бы один полноценный промпт - meaningful_prompts = sum(1 for count in prompt_counts.values() if count > 100) - if meaningful_prompts < 2: - print(f"❌ Мало полноценных промптов: {meaningful_prompts}/4") - return False - - print(f"✅ Найдено {meaningful_prompts} полноценных промптов") - - # Шаг 3: Имитация передачи manifest в Pyodide globals - print("\n📋 ШАГ 3: Имитация передачи manifest в Pyodide globals") - print("-" * 50) - - # Имитируем Pyodide globals - pyodide_globals = {} - - # Передаем manifest (как в offscreen.ts:504) - pyodide_globals['manifest'] = manifest - console_log('[BRIDGE DIAGNOSTIC] ✅ Manifest передан в Pyodide globals из pluginSettings') - - # Проверяем доступность - if 'manifest' not in pyodide_globals: - print("❌ ОШИБКА: Manifest не найден в Pyodide globals") - return False - - transmitted_manifest = pyodide_globals['manifest'] - if transmitted_manifest != manifest: - print("❌ ОШИБКА: Переданный manifest не совпадает с оригиналом") - return False - - print("✅ Manifest успешно передан в Pyodide globals") - - # Шаг 4: Имитация использования промптов в mcp_server.py - print("\n📋 ШАГ 4: Имитация использования промптов в mcp_server.py") - print("-" * 50) - - # Имитируем globals для mcp_server - import builtins - original_globals = builtins.globals - builtins.globals = lambda: pyodide_globals - - try: - # Тестируем get_user_prompts - plugin_settings = get_pyodide_var('pluginSettings') - loaded_prompts = get_user_prompts_test(plugin_settings) - - # Проверяем что промпты загружены - success_count = 0 - for prompt_type in expected_types: - for lang in expected_langs: - if loaded_prompts.get(prompt_type, {}).get(lang): - success_count += 1 - print(f"✅ {prompt_type}.{lang}: успешно загружен") - else: - print(f"❌ {prompt_type}.{lang}: НЕ загружен") - - if success_count < 2: - print(f"❌ Мало загруженных промптов: {success_count}/4") - return False - - print(f"✅ Успешно загружено {success_count} промптов из manifest") - - finally: - # Восстанавливаем оригинальные globals - builtins.globals = original_globals - - # Шаг 5: Проверка что промпты не из fallback - print("\n📋 ШАГ 5: Проверка что промпты загружены из manifest, а не fallback") - print("-" * 50) - - # Сравниваем загруженные промпты с manifest - manifest_mismatch = 0 - for prompt_type in expected_types: - for lang in expected_langs: - manifest_prompt = prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - loaded_prompt = loaded_prompts.get(prompt_type, {}).get(lang, '') - - if manifest_prompt and loaded_prompt == manifest_prompt: - print(f"✅ {prompt_type}.{lang}: совпадает с manifest") - elif manifest_prompt and loaded_prompt != manifest_prompt: - print(f"⚠️ {prompt_type}.{lang}: НЕ совпадает с manifest") - manifest_mismatch += 1 - else: - print(f"ℹ️ {prompt_type}.{lang}: manifest пустой, используется fallback") - - if manifest_mismatch > 0: - print(f"⚠️ Найдено {manifest_mismatch} несоответствий с manifest") - else: - print("✅ Все промпты загружены из manifest (без fallback)") - - # Финальная проверка - print("\n" + "=" * 80) - if success_count >= 2 and meaningful_prompts >= 2: - print("🎉 ТЕСТ ПРОЙДЕН: Manifest успешно передается и промпты загружаются!") - print("=" * 80) - print("📋 РЕЗУЛЬТАТЫ:") - print(" ✅ Manifest.json загружается корректно") - print(" ✅ Manifest передается в Pyodide globals") - print(" ✅ mcp_server.py получает доступ к manifest") - print(" ✅ Промпты загружаются из manifest, а не из fallback") - print(" ✅ Структура промптов соответствует ожиданиям") - return True - else: - print("❌ ТЕСТ НЕ ПРОЙДЕН: Обнаружены проблемы с передачей manifest") - return False - -if __name__ == "__main__": - success = test_manifest_transmission_and_usage() - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_prompts_extraction.py b/test_prompts_extraction.py deleted file mode 100644 index dc49b617..00000000 --- a/test_prompts_extraction.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест извлечения промптов из manifest.json в функции get_user_prompts() - -Сценарий 2: Тестирование извлечения промптов из manifest.json в функции get_user_prompts() -- Тестирование извлечения промптов из manifest.json -- Проверка что используются оригинальные промпты, а не встроенные fallback -- Тестирование fallback-логики при повреждении manifest.json -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем корневую директорию проекта в путь для импорта -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def console_log(message: str): - """Эмуляция функции логирования из mcp_server.py""" - print(f"[PYTHON_LOG] {message}") - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - """Безопасная функция получения значения из словаря""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def get_pyodide_var(name: str, default: Any = None) -> Any: - """Эмуляция функции получения переменных из Pyodide globals""" - try: - if name in globals(): - value = globals()[name] - return value - else: - return default - except Exception as e: - return default - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """Встроенные промпты по умолчанию из mcp_server.py""" - builtin_prompts = { - 'optimized': { - 'ru': """Ты - токсиколог и химик-косметолог с 15-летним опытом. Твоя задача: провести КРИТИЧЕСКИЙ анализ косметического продукта, разоблачая маркетинговые уловки. - -ДАННЫЕ: -Описание: {description} -Состав: {composition} - -ОБЯЗАТЕЛЬНАЯ МЕТОДОЛОГИЯ АНАЛИЗА: - -1. ПРОВЕРКА МАРКЕТИНГОВЫХ ЗАЯВЛЕНИЙ: -- Термины типа "3D/4D/5D", "революционный", "инновационный" - ТРЕБУЮТ доказательств -- Для каждого заявления ("лифтинг", "против морщин"): - * Найди КОНКРЕТНЫЙ активный компонент - * Оцени его ПОЗИЦИЮ в списке (начало = высокая концентрация, конец = маркетинг) - * Укажи ЭФФЕКТИВНУЮ концентрацию из исследований vs вероятную в продукте - -2. ТОКСИКОЛОГИЧЕСКИЙ СКРИНИНГ (приоритет №1): -- Проверь КАЖДЫЙ компонент на: - * Формальдегид-релизеры (DMDM Hydantoin, Quaternium-15, и т.д.) - * Парабены (особенно butyl-, propyl-) - * Устаревшие УФ-фильтры (Octinoxate, Oxybenzone) - * Потенциальные эндокринные дизрапторы -- Если найдено ≥3 проблемных компонента → оценка НЕ МОЖЕТ быть >5/10 - -3. РЕАЛИСТИЧНАЯ ОЦЕНКА ПЕПТИДОВ/АКТИВОВ: -- Palmitoyl Tripeptide-38: эффективен при 2-4%, если в середине списка → скорее <1% → эффект минимален -- Collagen/Elastin: молекулы НЕ проникают, работают только как пленка -- Hyaluronic acid: увлажняет ПОВЕРХНОСТНО, НЕ разглаживает глубокие морщины - -4. СРАВНЕНИЕ С СОВРЕМЕННЫМИ СТАНДАРТАМИ: -- Современная косметика = без парабенов, с новыми консервантами -- Устаревшие формулы → снижение оценки на 2-3 балла - -5. ШКАЛА ОЦЕНКИ (СТРОГАЯ): -- 9-10: Идеальный состав, доказанные активы в высоких концентрациях, без токсичных компонентов -- 7-8: Хороший состав, минимум проблемных компонентов -- 5-6: Средний продукт, есть проблемные компоненты ИЛИ активы в низких дозах -- 3-4: Устаревшая формула, много токсичных компонентов, маркетинговые заявления не подтверждены -- 1-2: Опасный или полностью бесполезный продукт - -КРИТИЧЕСКИ ВАЖНО: -- Будь СКЕПТИЧЕН к маркетингу -- НЕ завышай оценку из вежливости -- Если состав устаревший (парабены + формальдегид-релизеры) → максимум 5/10 -- Если заявления не подтверждены активами в ДОСТАТОЧНОЙ концентрации → снижай оценку - -ФОРМАТ ОТВЕТА - ТОЛЬКО JSON: -{{ -"score": число_от_1_до_10, -"reasoning": "ДЕТАЛЬНЫЙ анализ: - 1. Проверка маркетинга: [разбери каждое заявление] - 2. Токсикологический профиль: [перечисли ВСЕ проблемные компоненты] - 3. Реальная эффективность активов: [концентрации vs заявления] - 4. Сравнение с современными стандартами: [почему устарел/актуален] - 5. Итоговый вердикт: [честное заключение]", -"confidence": число_от_0_до_1, -"red_flags": ["список всех токсичных/проблемных компонентов"], -"marketing_lies": ["список не подтвержденных маркетинговых заявлений"] -}} - -ЯЗЫК: Русский, технический стиль с примерами.""", - 'en': """You are a board-certified toxicologist and cosmetic chemist with 15 years of experience in ingredient safety assessment. Your task: conduct a CRITICAL, evidence-based analysis of this cosmetic product, exposing marketing manipulation. - -DATA: -Description: {description} -Composition: {composition} - -MANDATORY ANALYSIS PROTOCOL: - -1. TOXICOLOGICAL SCREENING (highest priority): -- Screen EVERY ingredient for: - * Formaldehyde-releasers (DMDM Hydantoin, Quaternium-15, Diazolidinyl Urea, Imidazolidinyl Urea) - * Parabens (particularly butylparaben, propylparaben - EU restricted) - * Obsolete UV filters (Octinoxate/Ethylhexyl Methoxycinnamate, Oxybenzone) - * Known/suspected endocrine disruptors -- HARD RULE: ≥3 high-concern ingredients → score CAPPED at 5/10 maximum - -2. MARKETING CLAIMS VERIFICATION: -- Buzzwords like "3D/4D/5D technology", "revolutionary", "clinical breakthrough" - DEMAND evidence -- For each claim ("lifting", "anti-wrinkle", "firming"): - * Identify the SPECIFIC active ingredient responsible - * Evaluate its POSITION in INCI list (first 5 = meaningful dose, after position 10 = cosmetic dose) - * Compare PROVEN effective concentration from peer-reviewed studies vs. LIKELY concentration in this product - -3. REALISTIC EFFICACY ASSESSMENT: -- Palmitoyl Tripeptide-38 (Matrixyl synthe'6): clinically effective at 2-4%; if listed mid-INCI → probably <1% → negligible effect -- Collagen/Hydrolyzed Elastin: molecular weight >500 Da → CANNOT penetrate stratum corneum → function only as humectants/film-formers -- Sodium Hyaluronate: provides surface hydration only, CANNOT affect dermal structure or deep wrinkles - -4. MODERN FORMULATION STANDARDS COMPARISON: -- 2025 best practices: phenoxyethanol or modern preservative systems, NO paraben cocktails -- Formulations using 4+ parabens + formaldehyde-releasers = outdated 2000s technology → automatic -2 to -3 point deduction - -5. EVIDENCE-BASED SCORING RUBRIC (strict grading): -- 9-10: Exceptional formulation, clinically-validated actives at proven concentrations, clean safety profile -- 7-8: Well-formulated, minor concerns only, actives present at reasonable levels -- 5-6: Mediocre product with significant concerns (problematic preservatives OR underdosed actives OR misleading claims) -- 3-4: Poor formulation with multiple red flags, outdated technology, unsubstantiated marketing -- 1-2: Potentially harmful or fraudulent product - -CRITICAL ASSESSMENT RULES: -- Maintain scientific skepticism toward all marketing language -- Apply evidence-based standards, NOT brand reputation -- Outdated preservation system (multiple parabens + formaldehyde-releaser) = AUTOMATIC cap at 5/10 -- Claims unsupported by adequate active concentrations = reduce score proportionally -- Default to LOWER score when ingredient concentrations are ambiguous - -OUTPUT FORMAT - VALID JSON ONLY: -{{ -"score": integer_1_to_10, -"reasoning": "COMPREHENSIVE ANALYSIS: - 1. Toxicological Profile: [enumerate ALL concerning ingredients with specific risks] - 2. Marketing Claims Audit: [fact-check each claim against ingredient reality] - 3. Active Ingredient Efficacy: [compare claimed benefits vs. probable concentrations vs. scientific evidence] - 4. Formulation Modernity Assessment: [evaluate against current industry standards] - 5. Evidence-Based Verdict: [objective conclusion with no marketing bias]", -"confidence": float_0_to_1, -"red_flags": ["comprehensive list of problematic/toxic/outdated ingredients"], -"marketing_lies": ["specific unsubstantiated or misleading marketing claims"] -}} - -RESPONSE LANGUAGE: English, using precise technical terminology.""" - }, - 'deep': { - 'ru': """Проведи глубокий анализ товара с медицинской и научной точки зрения. -Описание: {description} -Состав: {composition} -Проанализируй: -1. Научную обоснованность заявленных свойств. -2. Потенциальные побочные эффекты и противопоказания. -3. Эффективность по сравнению с аналогами. -Верни детальный максимально подробный обоснованный анализ в структурированном виде (используй Markdown).""", - 'en': """Conduct a deep analysis of the product from a medical and scientific perspective. -Description: {description} -Composition: {composition} -Analyze: -1. Scientific validity of the claimed properties. -2. Potential side effects and contraindications. -3. Effectiveness compared to analogs. -Return a detailed, maximally comprehensive, reasoned analysis in a structured form (use Markdown).""" - } - } - - return builtin_prompts.get(prompt_type, {}).get(lang, '') - -def test_get_user_prompts_function(): - """Тест функции get_user_prompts с различными сценариями""" - print("🔍 ТЕСТ 2: Тестирование извлечения промптов из manifest.json") - print("=" * 70) - - # Загружаем manifest.json - manifest_path = "chrome-extension/public/plugins/ozon-analyzer/manifest.json" - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest_data = json.load(f) - print(f"✅ Manifest.json загружен: {len(json.dumps(manifest_data))} символов") - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return False - - # Тест 2.1: Проверка извлечения промптов из manifest.json - print("\n📋 ТЕСТ 2.1: Извлечение промптов из manifest.json") - - # Симулируем Pyodide globals с manifest - globals()['manifest'] = manifest_data - - # Тестируем извлечение промптов - plugin_settings = {} # Пустые настройки пользователя - - # Симулируем логику из get_user_prompts - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - manifest = get_pyodide_var('manifest', {}) - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - - print(f"✅ Кастомные промпты: {type(custom_prompts_raw)}") - print(f"✅ Manifest промпты доступны: {manifest_prompts is not None}") - - # Проверяем каждый промпт - test_cases = [ - ('optimized', 'ru'), - ('optimized', 'en'), - ('deep', 'ru'), - ('deep', 'en') - ] - - prompts_structure = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - for prompt_type, lang in test_cases: - print(f"\n🔍 Обработка {prompt_type}.{lang}:") - - # Проверяем кастомный промпт - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - custom_value = safe_dict_get(prompt_type_data, lang, '') - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts_structure[prompt_type][lang] = custom_value - print(f"✅ Используем кастомный промпт ({len(custom_value)} символов)") - else: - print("ℹ️ Кастомный промпт не найден - переходим к manifest") - - # Fallback на manifest.json - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if manifest_value and len(manifest_value.strip()) > 0: - prompts_structure[prompt_type][lang] = manifest_value - print(f"✅ Используем промпт из manifest ({len(manifest_value)} символов)") - else: - print("⚠️ Промпт в manifest пустой - переходим к встроенному") - - # Fallback на встроенные промпты - builtin_value = _get_builtin_default_prompt(prompt_type, lang) - if builtin_value: - prompts_structure[prompt_type][lang] = builtin_value - print(f"✅ Используем встроенный промпт ({len(builtin_value)} символов)") - else: - print(f"⚠️ Встроенный промпт не найден для {prompt_type}.{lang}") - else: - print(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback на встроенные промпты - builtin_value = _get_builtin_default_prompt(prompt_type, lang) - if builtin_value: - prompts_structure[prompt_type][lang] = builtin_value - print(f"✅ Используем встроенный промпт ({len(builtin_value)} символов)") - - # Анализируем результаты - loaded_prompts = 0 - total_prompts = 4 - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - prompt_value = prompts_structure[prompt_type][lang] - if prompt_value and len(prompt_value.strip()) > 100: - loaded_prompts += 1 - print(f"✅ {prompt_type}.{lang}: загружен ({len(prompt_value)} символов)") - else: - print(f"❌ {prompt_type}.{lang}: НЕ загружен или слишком короткий ({len(prompt_value) if prompt_value else 0} символов)") - - print(f"\n📊 Результаты: {loaded_prompts}/{total_prompts} промптов загружено") - - if loaded_prompts == total_prompts: - print("🎉 ТЕСТ 2.1 ПРОЙДЕН: Все промпты успешно извлечены!") - test_2_1_passed = True - else: - print("❌ ТЕСТ 2.1 НЕ ПРОЙДЕН: Некоторые промпты не загружены") - test_2_1_passed = False - - # Тест 2.2: Тестирование fallback логики при повреждении manifest.json - print("\n📋 ТЕСТ 2.2: Fallback логика при повреждении manifest.json") - - # Симулируем поврежденный manifest - damaged_manifest = { - 'options': { - 'prompts': { - 'optimized': { - 'ru': {'default': ''}, # Поврежденный промпт - 'en': {'default': 'valid prompt'} - } - } - } - } - - globals()['manifest'] = damaged_manifest - - print("🔄 Тестируем fallback при поврежденном manifest...") - - # Считаем сколько промптов fallbackнули на встроенные - builtin_fallbacks = 0 - - for prompt_type, lang in test_cases: - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - custom_value = safe_dict_get(prompt_type_data, lang, '') - - if not custom_value or len(custom_value.strip()) == 0: - # Пытаемся получить из поврежденного manifest - type_prompts = safe_dict_get(damaged_manifest, 'options', {}).get('prompts', {}) - lang_data = safe_dict_get(type_prompts, prompt_type, {}).get(lang, {}) - manifest_value = safe_dict_get(lang_data, 'default', '') - - if not manifest_value or len(manifest_value.strip()) == 0: - # Fallback на встроенные промпты - builtin_value = _get_builtin_default_prompt(prompt_type, lang) - if builtin_value: - builtin_fallbacks += 1 - print(f"✅ {prompt_type}.{lang}: fallback на встроенный промпт ({len(builtin_value)} символов)") - else: - print(f"❌ {prompt_type}.{lang}: встроенный промпт не найден") - else: - print(f"✅ {prompt_type}.{lang}: используем из поврежденного manifest ({len(manifest_value)} символов)") - else: - print(f"✅ {prompt_type}.{lang}: используем кастомный промпт ({len(custom_value)} символов)") - - print(f"\n📊 Fallback результаты: {builtin_fallbacks} промптов fallbackнули на встроенные") - - if builtin_fallbacks > 0: - print("🎉 ТЕСТ 2.2 ПРОЙДЕН: Fallback логика работает корректно!") - test_2_2_passed = True - else: - print("⚠️ ТЕСТ 2.2: Fallback не потребовался или не сработал") - test_2_2_passed = True # Это тоже корректно - - # Тест 2.3: Проверка что используются оригинальные промпты из manifest.json - print("\n📋 ТЕСТ 2.3: Проверка использования оригинальных промптов") - - # Восстанавливаем оригинальный manifest - globals()['manifest'] = manifest_data - - # Сравниваем промпты из manifest с встроенными - original_from_manifest = 0 - builtin_used = 0 - - for prompt_type, lang in test_cases: - # Получаем промпт из manifest - manifest_prompt = manifest_prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - - # Получаем встроенный промпт - builtin_prompt = _get_builtin_default_prompt(prompt_type, lang) - - if manifest_prompt and len(manifest_prompt.strip()) > 100: - if builtin_prompt and manifest_prompt != builtin_prompt: - original_from_manifest += 1 - print(f"✅ {prompt_type}.{lang}: используем ОРИГИНАЛЬНЫЙ промпт из manifest ({len(manifest_prompt)} символов)") - else: - builtin_used += 1 - print(f"ℹ️ {prompt_type}.{lang}: промпт из manifest совпадает со встроенным ({len(manifest_prompt)} символов)") - else: - print(f"⚠️ {prompt_type}.{lang}: промпт в manifest поврежден или отсутствует") - - print(f"\n📊 Оригинальные промпты: {original_from_manifest}/4") - print(f"📊 Совпадают со встроенными: {builtin_used}/4") - - if original_from_manifest >= 2: # Минимум 2 оригинальных промпта - print("🎉 ТЕСТ 2.3 ПРОЙДЕН: Используются оригинальные промпты из manifest.json!") - test_2_3_passed = True - else: - print("⚠️ ТЕСТ 2.3: Недостаточно оригинальных промптов в manifest.json") - test_2_3_passed = False - - # Итоговые результаты - all_tests_passed = test_2_1_passed and test_2_2_passed and test_2_3_passed - - print("\n" + "=" * 70) - print("📋 ИТОГОВЫЕ РЕЗУЛЬТАТЫ ТЕСТА 2:") - print(f" Тест 2.1 (Извлечение промптов): {'✅ ПРОЙДЕН' if test_2_1_passed else '❌ НЕ ПРОЙДЕН'}") - print(f" Тест 2.2 (Fallback логика): {'✅ ПРОЙДЕН' if test_2_2_passed else '❌ НЕ ПРОЙДЕН'}") - print(f" Тест 2.3 (Оригинальные промпты): {'✅ ПРОЙДЕН' if test_2_3_passed else '❌ НЕ ПРОЙДЕН'}") - - if all_tests_passed: - print("\n🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ: Функция get_user_prompts() работает корректно!") - return True - else: - print("\n💥 НЕКОТОРЫЕ ТЕСТЫ НЕ ПРОЙДЕНЫ: Есть проблемы с извлечением промптов") - return False - -if __name__ == "__main__": - success = test_get_user_prompts_function() - if success: - print("\n🎯 РЕЗУЛЬТАТ: Исправление механизма извлечения промптов работает корректно!") - else: - print("\n💥 РЕЗУЛЬТАТ: Обнаружены проблемы с извлечением промптов!") - - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test_prompts_mechanism.py b/test_prompts_mechanism.py deleted file mode 100644 index 1f13749e..00000000 --- a/test_prompts_mechanism.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -🧪 ТЕСТОВЫЙ СКРИПТ ДЛЯ ДИАГНОСТИКИ МЕХАНИЗМА ПРОМПТОВ -Диагностирует проблемы передачи промптов из manifest.json в Pyodide контекст -""" - -import json -import sys -import os -from typing import Dict, Any - -# Добавляем путь к плагину в sys.path для импорта функций -plugin_path = os.path.join(os.path.dirname(__file__), 'chrome-extension/public/plugins/ozon-analyzer') -sys.path.insert(0, plugin_path) - -# Загружаем manifest.json для тестирования -def load_manifest(): - """Загружает manifest.json для тестирования""" - manifest_path = os.path.join(plugin_path, 'manifest.json') - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - return json.load(f) - except Exception as e: - print(f"❌ Ошибка загрузки manifest.json: {e}") - return None - -def simulate_pyodide_globals(manifest: Dict[str, Any]) -> Dict[str, Any]: - """Симулирует Pyodide globals для тестирования""" - return { - 'manifest': manifest, - 'pluginSettings': { - 'prompts': { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', - 'en': 'CUSTOM PROMPT ENGLISH' - }, - 'deep': { - 'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', - 'en': 'DEEP ANALYSIS ENGLISH' - } - } - } - } - -def simulate_empty_plugin_settings(manifest: Dict[str, Any]) -> Dict[str, Any]: - """Симулирует пустые настройки пользователя""" - return { - 'manifest': manifest, - 'pluginSettings': {} - } - -def simulate_broken_manifest() -> Dict[str, Any]: - """Симулирует поврежденный manifest""" - return { - 'manifest': {'invalid': 'data'}, - 'pluginSettings': {} - } - -def simulate_none_manifest() -> Dict[str, Any]: - """Симулирует отсутствие manifest""" - return { - 'pluginSettings': { - 'prompts': { - 'optimized': { - 'ru': 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ' - } - } - } - } - -def test_scenario_1_full_custom_prompts(): - """Сценарий 1: Полностью заполненные кастомные промпты""" - print("\n🧪 СЦЕНАРИЙ 1: Полностью заполненные кастомные промпты") - print("=" * 60) - - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return None - - # Симулируем Pyodide globals - globals_data = simulate_pyodide_globals(manifest) - - # Импортируем и тестируем функцию - try: - from mcp_server import get_user_prompts - - # Мокаем функции для симуляции Pyodide окружения - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - # Внедряем моки в глобальное пространство - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем ожидаемые результаты - expected_results = { - 'optimized': {'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', 'en': 'CUSTOM PROMPT ENGLISH'}, - 'deep': {'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', 'en': 'DEEP ANALYSIS ENGLISH'} - } - - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - if result.get(prompt_type, {}).get(lang) != expected_results[prompt_type][lang]: - print(f"❌ Несоответствие в {prompt_type}.{lang}") - success = False - - if success: - print("✅ Сценарий 1 ПРОЙДЕН: Кастомные промпты корректно используются") - else: - print("❌ Сценарий 1 ПРОВАЛЕН: Кастомные промпты не используются правильно") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 1: {e}") - import traceback - traceback.print_exc() - return None - -def test_scenario_2_empty_custom_prompts(): - """Сценарий 2: Пустые кастомные промпты (fallback на manifest)""" - print("\n🧪 СЦЕНАРИЙ 2: Пустые кастомные промпты (fallback на manifest)") - print("=" * 60) - - manifest = load_manifest() - if not manifest: - print("❌ Не удалось загрузить manifest.json") - return None - - # Симулируем Pyodide globals с пустыми промптами - globals_data = simulate_empty_plugin_settings(manifest) - - try: - from mcp_server import get_user_prompts - - # Мокаем функции - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - # Внедряем моки - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используются промпты из manifest - manifest_prompts = manifest.get('options', {}).get('prompts', {}) - success = True - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - manifest_value = manifest_prompts.get(prompt_type, {}).get(lang, {}).get('default', '') - result_value = result.get(prompt_type, {}).get(lang, '') - - if manifest_value and result_value == manifest_value: - print(f"✅ {prompt_type}.{lang}: Используется промпт из manifest") - else: - print(f"❌ {prompt_type}.{lang}: Несоответствие промпта из manifest") - success = False - - if success: - print("✅ Сценарий 2 ПРОЙДЕН: Fallback на manifest работает корректно") - else: - print("❌ Сценарий 2 ПРОВАЛЕН: Fallback на manifest не работает") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 2: {e}") - import traceback - traceback.print_exc() - return None - -def test_scenario_3_broken_manifest(): - """Сценарий 3: Поврежденный manifest.json""" - print("\n🧪 СЦЕНАРИЙ 3: Поврежденный manifest.json") - print("=" * 60) - - globals_data = simulate_broken_manifest() - - try: - from mcp_server import get_user_prompts - - # Мокаем функции - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - def mock_get_builtin_default_prompt(prompt_type, lang): - return f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type}.{lang}" - - # Внедряем моки - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - mcp_server._get_builtin_default_prompt = mock_get_builtin_default_prompt - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется fallback на встроенные промпты - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - expected_value = f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type}.{lang}" - result_value = result.get(prompt_type, {}).get(lang, '') - - if result_value == expected_value: - print(f"✅ {prompt_type}.{lang}: Используется встроенный промпт") - else: - print(f"❌ {prompt_type}.{lang}: Несоответствие встроенного промпта") - success = False - - if success: - print("✅ Сценарий 3 ПРОЙДЕН: Fallback на встроенные промпты работает") - else: - print("❌ Сценарий 3 ПРОВАЛЕН: Fallback на встроенные промпты не работает") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 3: {e}") - import traceback - traceback.print_exc() - return None - -def test_scenario_4_missing_manifest(): - """Сценарий 4: Отсутствующий manifest в globals""" - print("\n🧪 СЦЕНАРИЙ 4: Отсутствующий manifest в globals") - print("=" * 60) - - globals_data = simulate_none_manifest() - - try: - from mcp_server import get_user_prompts - - # Мокаем функции - def mock_get_pyodide_var(name, default=None): - return globals_data.get(name, default) - - def mock_safe_dict_get(data, key, default=None): - if isinstance(data, dict): - return data.get(key, default) - return default - - def mock_console_log(message): - print(f"📋 LOG: {message}") - - def mock_get_builtin_default_prompt(prompt_type, lang): - return f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type}.{lang}" - - # Внедряем моки - import mcp_server - mcp_server.get_pyodide_var = mock_get_pyodide_var - mcp_server.safe_dict_get = mock_safe_dict_get - mcp_server.console_log = mock_console_log - mcp_server._get_builtin_default_prompt = mock_get_builtin_default_prompt - - # Выполняем тест - plugin_settings = globals_data['pluginSettings'] - result = get_user_prompts(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется кастомный промпт и встроенные для остальных - success = True - - # optimized.ru должен быть кастомным - if result.get('optimized', {}).get('ru') == 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ': - print("✅ optimized.ru: Используется кастомный промпт") - else: - print("❌ optimized.ru: Кастомный промпт не используется") - success = False - - # optimized.en должен быть встроенным (отсутствует в кастомных) - if result.get('optimized', {}).get('en') == 'ВСТРОЕННЫЙ ПРОМПТ optimized.en': - print("✅ optimized.en: Используется встроенный промпт") - else: - print("❌ optimized.en: Встроенный промпт не используется") - success = False - - if success: - print("✅ Сценарий 4 ПРОЙДЕН: Частичный fallback работает корректно") - else: - print("❌ Сценарий 4 ПРОВАЛЕН: Частичный fallback не работает") - - return result - - except Exception as e: - print(f"❌ Ошибка при тестировании сценария 4: {e}") - import traceback - traceback.print_exc() - return None - -def run_diagnostic_tests(): - """Запуск всех диагностических тестов""" - print("🔍 НАЧИНАЕМ ДИАГНОСТИКУ МЕХАНИЗМА ПРОМПТОВ") - print("=" * 80) - - # Тест 1: Анализ структуры manifest.json - print("\n📋 АНАЛИЗ СТРУКТУРЫ MANIFEST.JSON") - print("=" * 40) - - manifest = load_manifest() - if manifest: - print("✅ manifest.json загружен успешно") - - # Анализируем структуру промптов - options = manifest.get('options', {}) - prompts = options.get('prompts', {}) - - print(f"📊 Структура промптов в manifest:") - print(f" Типы промптов: {list(prompts.keys())}") - - for prompt_type in ['optimized', 'deep']: - if prompt_type in prompts: - print(f" {prompt_type}:") - type_prompts = prompts[prompt_type] - for lang in ['ru', 'en']: - if lang in type_prompts: - lang_data = type_prompts[lang] - default_value = lang_data.get('default', '') - print(f" {lang}: длина={len(default_value)} символов") - print(f" Предварительный просмотр: {default_value[:100]}...") - else: - print(f" {lang}: ОТСУТСТВУЕТ") - else: - print(f" {prompt_type}: ОТСУТСТВУЕТ") - else: - print("❌ Не удалось загрузить manifest.json") - - # Запуск тестовых сценариев - results = {} - - print("\n🚀 ЗАПУСК ТЕСТОВЫХ СЦЕНАРИЕВ") - print("=" * 40) - - results['scenario_1'] = test_scenario_1_full_custom_prompts() - results['scenario_2'] = test_scenario_2_empty_custom_prompts() - results['scenario_3'] = test_scenario_3_broken_manifest() - results['scenario_4'] = test_scenario_4_missing_manifest() - - # Анализ результатов - print("\n📊 АНАЛИЗ РЕЗУЛЬТАТОВ ТЕСТИРОВАНИЯ") - print("=" * 50) - - passed = 0 - total = 0 - - for scenario_name, result in results.items(): - total += 1 - if result is not None: - passed += 1 - print(f"✅ {scenario_name}: ПРОЙДЕН") - else: - print(f"❌ {scenario_name}: ПРОВАЛЕН") - - print(f"\n📈 ИТОГ: {passed}/{total} сценариев пройдено") - - if passed == total: - print("🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Механизм промптов работает корректно.") - else: - print("⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ! Требуется дополнительная диагностика.") - - return results - -if __name__ == "__main__": - run_diagnostic_tests() \ No newline at end of file diff --git a/test_prompts_standalone.py b/test_prompts_standalone.py deleted file mode 100644 index a80ed086..00000000 --- a/test_prompts_standalone.py +++ /dev/null @@ -1,369 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -🧪 АВТОНОМНЫЙ ТЕСТ МЕХАНИЗМА ПРОМПТОВ -Тестирует логику функции get_user_prompts без зависимости от Pyodide -""" - -import json -import sys -import os -from typing import Dict, Any - -def safe_dict_get(data: Any, key: str, default: Any = None) -> Any: - """Безопасное получение значения из словаря""" - try: - if isinstance(data, dict): - return data.get(key, default) - return default - except AttributeError: - return default - -def console_log(message: str): - """Функция логирования для тестирования""" - print(f"📋 LOG: {message}") - -def get_pyodide_var(name: str, default: Any = None) -> Any: - """Мокированная функция для доступа к Pyodide globals""" - # В реальном тесте здесь будет логика доступа к globals - return default - -def _get_builtin_default_prompt(prompt_type: str, lang: str) -> str: - """Встроенные промпты по умолчанию для fallback""" - builtin_prompts = { - 'optimized': { - 'ru': "ВСТРОЕННЫЙ ПРОМПТ OPTIMIZED РУССКИЙ", - 'en': "BUILTIN PROMPT OPTIMIZED ENGLISH" - }, - 'deep': { - 'ru': "ВСТРОЕННЫЙ ПРОМПТ DEEP РУССКИЙ", - 'en': "BUILTIN PROMPT DEEP ENGLISH" - } - } - return builtin_prompts.get(prompt_type, {}).get(lang, '') - -def get_user_prompts_standalone(plugin_settings: Dict[str, Any] = None) -> Dict[str, Any]: - """ - Автономная версия функции get_user_prompts для тестирования логики - без зависимости от Pyodide - """ - console_log("🔍 ===== НАЧАЛО ЗАГРУЗКИ ПРОМПТОВ (АВТОНОМНЫЙ ТЕСТ) =====") - - try: - # Диагностика входных данных - console_log(f"🔍 Диагностика входных данных:") - console_log(f" plugin_settings type: {type(plugin_settings)}") - console_log(f" plugin_settings is None: {plugin_settings is None}") - if isinstance(plugin_settings, dict): - console_log(f" plugin_settings keys: {list(plugin_settings.keys())}") - - # Загружаем manifest.json для тестирования - manifest_path = os.path.join(os.path.dirname(__file__), 'chrome-extension/public/plugins/ozon-analyzer/manifest.json') - try: - with open(manifest_path, 'r', encoding='utf-8') as f: - manifest = json.load(f) - console_log("✅ manifest.json загружен успешно для тестирования") - except Exception as e: - console_log(f"❌ Ошибка загрузки manifest.json: {e}") - manifest = {} - - # Исправлено: промпты уже доступны в plugin_settings - custom_prompts_raw = safe_dict_get(plugin_settings, 'prompts', {}) - console_log(f" custom_prompts_raw: {custom_prompts_raw}") - console_log(f" custom_prompts_raw type: {type(custom_prompts_raw)}") - - prompts = { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - - if manifest: - manifest_prompts = safe_dict_get(manifest, 'options', {}).get('prompts', {}) - console_log(f" manifest_prompts: {manifest_prompts}") - else: - console_log("❌ manifest не найден") - manifest_prompts = {} - - # Детальная диагностика структуры промптов - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - console_log(f"🔍 Обработка {prompt_type}.{lang}:") - - # Правильно извлекаем промпт из nested структуры plugin_settings - prompt_type_data = safe_dict_get(custom_prompts_raw, prompt_type, {}) - console_log(f" prompt_type_data ({prompt_type}): {prompt_type_data}") - - custom_value = safe_dict_get(prompt_type_data, lang, '') - console_log(f" custom_value для {lang}: {repr(custom_value)}") - console_log(f" custom_value type: {type(custom_value)}") - console_log(f" custom_value length: {len(custom_value) if custom_value else 0}") - - if custom_value and isinstance(custom_value, str) and len(custom_value.strip()) > 0: - prompts[prompt_type][lang] = custom_value - console_log(f"✅ Используем кастомный промпт: {prompt_type}.{lang} (длина: {len(custom_value)})") - else: - console_log(f"ℹ️ Кастомный промпт не найден или пустой для {prompt_type}.{lang}") - - # Fallback 1: manifest.json - исправленная логика извлечения - type_prompts = safe_dict_get(manifest_prompts, prompt_type, {}) - console_log(f" type_prompts из manifest ({prompt_type}): {type_prompts}") - - if type_prompts: - lang_data = safe_dict_get(type_prompts, lang, {}) - console_log(f" lang_data для {lang}: {lang_data}") - - manifest_value = safe_dict_get(lang_data, 'default', '') - console_log(f" manifest_value для {prompt_type}.{lang}: {repr(manifest_value)}") - console_log(f" manifest_value length: {len(manifest_value) if manifest_value else 0}") - - if manifest_value and len(manifest_value.strip()) > 0: - prompts[prompt_type][lang] = manifest_value - console_log(f"✅ Используем промпт по умолчанию из manifest: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Промпт по умолчанию не найден или пустой для {prompt_type}.{lang}") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - else: - console_log(f"⚠️ Тип промпта {prompt_type} не найден в manifest") - # Fallback 2: встроенные промпты по умолчанию - default_value = _get_builtin_default_prompt(prompt_type, lang) - if default_value: - prompts[prompt_type][lang] = default_value - console_log(f"✅ Используем встроенный промпт по умолчанию: {prompt_type}.{lang}") - else: - console_log(f"⚠️ Встроенный промпт по умолчанию не найден для {prompt_type}.{lang}") - console_log(f"ℹ️ Оставляем пустой промпт для {prompt_type}.{lang}") - - # Итоговая диагностика - console_log("🔍 Итоговая диагностика промптов:") - console_log(f" Plugin settings prompts: {custom_prompts_raw}") - console_log(f" Manifest prompts: {manifest_prompts}") - console_log(f" Final prompts structure: {prompts}") - - # Подсчет загруженных промптов - loaded_prompts = len([p for pt in prompts.values() for p in pt.values() if p and len(p.strip()) > 0]) - total_prompts = len([p for pt in prompts.values() for p in pt.values()]) - console_log(f"📋 Загружено промптов: {loaded_prompts}/{total_prompts} (кастомных: {loaded_prompts})") - - console_log("🔍 ===== УСПЕШНО ЗАВЕРШЕНА ЗАГРУЗКА ПРОМПТОВ =====") - return prompts - - except Exception as e: - console_log(f"❌ Критическая ошибка загрузки промптов: {str(e)}") - import traceback - stack_trace = traceback.format_exc() - console_log(f" Stack trace: {stack_trace}") - - # Критический fallback - возвращаем пустые промпты - console_log("⚠️ Возвращаем критический fallback с пустыми промптами") - return { - 'optimized': {'ru': '', 'en': ''}, - 'deep': {'ru': '', 'en': ''} - } - -def test_scenario_1_full_custom_prompts(): - """Сценарий 1: Полностью заполненные кастомные промпты""" - print("\n🧪 СЦЕНАРИЙ 1: Полностью заполненные кастомные промпты") - print("=" * 60) - - plugin_settings = { - 'prompts': { - 'optimized': { - 'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', - 'en': 'CUSTOM PROMPT ENGLISH' - }, - 'deep': { - 'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', - 'en': 'DEEP ANALYSIS ENGLISH' - } - } - } - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем ожидаемые результаты - expected_results = { - 'optimized': {'ru': 'КАСТОМНЫЙ ПРОМПТ РУССКИЙ', 'en': 'CUSTOM PROMPT ENGLISH'}, - 'deep': {'ru': 'ГЛУБОКИЙ АНАЛИЗ РУССКИЙ', 'en': 'DEEP ANALYSIS ENGLISH'} - } - - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - if result.get(prompt_type, {}).get(lang) != expected_results[prompt_type][lang]: - print(f"❌ Несоответствие в {prompt_type}.{lang}") - success = False - - if success: - print("✅ Сценарий 1 ПРОЙДЕН: Кастомные промпты корректно используются") - else: - print("❌ Сценарий 1 ПРОВАЛЕН: Кастомные промпты не используются правильно") - - return result - -def test_scenario_2_empty_custom_prompts(): - """Сценарий 2: Пустые кастомные промпты (fallback на manifest)""" - print("\n🧪 СЦЕНАРИЙ 2: Пустые кастомные промпты (fallback на manifest)") - print("=" * 60) - - plugin_settings = {} # Пустые настройки пользователя - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используются промпты из manifest - success = True - - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - result_value = result.get(prompt_type, {}).get(lang, '') - if result_value and len(result_value) > 100: # Промпты из manifest должны быть длинными - print(f"✅ {prompt_type}.{lang}: Используется промпт из manifest (длина: {len(result_value)})") - else: - print(f"❌ {prompt_type}.{lang}: Промпт из manifest не найден или слишком короткий") - success = False - - if success: - print("✅ Сценарий 2 ПРОЙДЕН: Fallback на manifest работает корректно") - else: - print("❌ Сценарий 2 ПРОВАЛЕН: Fallback на manifest не работает") - - return result - -def test_scenario_3_broken_manifest(): - """Сценарий 3: Поврежденный manifest.json""" - print("\n🧪 СЦЕНАРИЙ 3: Поврежденный manifest.json") - print("=" * 60) - - # В этом сценарии симулируем поврежденный manifest, модифицируя функцию загрузки - original_load_manifest = None - - def mock_load_manifest(): - return {'invalid': 'data'} # Поврежденный manifest - - plugin_settings = {} - - # Сохраняем оригинальную функцию и заменяем на мок - import mcp_server - if hasattr(mcp_server, 'load_manifest'): - original_load_manifest = mcp_server.load_manifest - mcp_server.load_manifest = mock_load_manifest - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется fallback на встроенные промпты - success = True - for prompt_type in ['optimized', 'deep']: - for lang in ['ru', 'en']: - expected_pattern = f"ВСТРОЕННЫЙ ПРОМПТ {prompt_type.upper()}" - result_value = result.get(prompt_type, {}).get(lang, '') - - if expected_pattern in result_value: - print(f"✅ {prompt_type}.{lang}: Используется встроенный промпт") - else: - print(f"❌ {prompt_type}.{lang}: Несоответствие встроенного промпта") - success = False - - if success: - print("✅ Сценарий 3 ПРОЙДЕН: Fallback на встроенные промпты работает") - else: - print("❌ Сценарий 3 ПРОВАЛЕН: Fallback на встроенные промпты не работает") - - return result - -def test_scenario_4_partial_custom_prompts(): - """Сценарий 4: Частично заполненные кастомные промпты""" - print("\n🧪 СЦЕНАРИЙ 4: Частично заполненные кастомные промпты") - print("=" * 60) - - plugin_settings = { - 'prompts': { - 'optimized': { - 'ru': 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ РУССКИЙ' - # en отсутствует - должен использоваться из manifest - } - # deep отсутствует - должен использоваться из manifest - } - } - - result = get_user_prompts_standalone(plugin_settings) - - print(f"✅ Результат: {json.dumps(result, ensure_ascii=False, indent=2)}") - - # Проверяем что используется кастомный промпт и manifest для остальных - success = True - - # optimized.ru должен быть кастомным - if result.get('optimized', {}).get('ru') == 'ТОЛЬКО КАСТОМНЫЙ ПРОМПТ РУССКИЙ': - print("✅ optimized.ru: Используется кастомный промпт") - else: - print("❌ optimized.ru: Кастомный промпт не используется") - success = False - - # optimized.en должен быть из manifest (длинный промпт) - if result.get('optimized', {}).get('en', '') and len(result['optimized']['en']) > 100: - print("✅ optimized.en: Используется промпт из manifest") - else: - print("❌ optimized.en: Промпт из manifest не используется") - success = False - - if success: - print("✅ Сценарий 4 ПРОЙДЕН: Частичный fallback работает корректно") - else: - print("❌ Сценарий 4 ПРОВАЛЕН: Частичный fallback не работает") - - return result - -def run_standalone_tests(): - """Запуск автономных тестов""" - print("🔍 НАЧИНАЕМ АВТОНОМНОЕ ТЕСТИРОВАНИЕ МЕХАНИЗМА ПРОМПТОВ") - print("=" * 80) - - # Запуск тестовых сценариев - results = {} - - print("\n🚀 ЗАПУСК ТЕСТОВЫХ СЦЕНАРИЕВ") - print("=" * 40) - - results['scenario_1'] = test_scenario_1_full_custom_prompts() - results['scenario_2'] = test_scenario_2_empty_custom_prompts() - results['scenario_3'] = test_scenario_3_broken_manifest() - results['scenario_4'] = test_scenario_4_partial_custom_prompts() - - # Анализ результатов - print("\n📊 АНАЛИЗ РЕЗУЛЬТАТОВ ТЕСТИРОВАНИЯ") - print("=" * 50) - - passed = 0 - total = 0 - - for scenario_name, result in results.items(): - total += 1 - if result is not None: - passed += 1 - print(f"✅ {scenario_name}: ПРОЙДЕН") - else: - print(f"❌ {scenario_name}: ПРОВАЛЕН") - - print(f"\n📈 ИТОГ: {passed}/{total} сценариев пройдено") - - if passed == total: - print("🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Логика механизма промптов работает корректно.") - else: - print("⚠️ НЕКОТОРЫЕ ТЕСТЫ ПРОВАЛЕНЫ! Требуется дополнительная диагностика логики.") - - return results - -if __name__ == "__main__": - run_standalone_tests() \ No newline at end of file diff --git a/test_text_truncation_fix.py b/test_text_truncation_fix.py deleted file mode 100644 index 1af5ae39..00000000 --- a/test_text_truncation_fix.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 -""" -Тест для проверки исправления обрезки текста в промптах к AI. -Проверяет, что функции _analyze_composition_vs_description и _find_similar_products -используют полные тексты description и composition без обрезки. -""" - -import sys -import os - -def test_text_truncation_fix(): - """Тестируем исправление обрезки текста.""" - - print("=" * 60) - print("🔍 ТЕСТИРОВАНИЕ ИСПРАВЛЕНИЯ ОБРЕЗКИ ТЕКСТА") - print("=" * 60) - - # Имитируем длинные тексты для тестирования - long_description = """ - Это очень длинное описание косметического продукта с подробной информацией о его свойствах. - Крем предназначен для ухода за кожей лица и шеи. Он содержит активные ингредиенты, - которые способствуют увлажнению, питанию и защите кожи. Продукт разработан с использованием - современных технологий и натуральных компонентов. Длинное описание продолжает описывать - все преимущества продукта, его состав, способ применения и ожидаемые результаты использования. - """ * 5 # Умножаем для создания очень длинного текста - - long_composition = """ - Aqua/Water, Glycerin, Caprylic/Capric Triglyceride, Dimethicone, Butylene Glycol, - Cyclopentasiloxane, Dimethicone Crosspolymer, Cyclohexasiloxane, Phenyl Trimethicone, - Polymethylsilsesquioxane, Dimethicone/Vinyl Dimethicone Crosspolymer, Hydroxyethyl Acrylate/Sodium - Acryloyldimethyl Taurate Copolymer, PEG-100 Stearate, Glyceryl Stearate, PEG-8, Polysorbate 60, - Sodium Carbomer, Sodium Laureth Sulfate, Sodium Lauryl Sulfate, Disodium EDTA, Phenonip, - Parfum/Fragrance, Benzyl Alcohol, Methylparaben, Propylparaben, DMDM Hydantoin, Iodopropynyl - Butylcarbamate, Diazolidinyl Urea, Sodium Dehydroacetate, Citric Acid, Triethanolamine, - Tetrasodium EDTA, Hexylene Glycol, Caprylyl Glycol, Ethylhexylglycerin, Phenonip, Germall Plus, - """ * 3 # Умножаем для создания длинного состава - - print(f"📏 Длина тестового описания: {len(long_description)} символов") - print(f"📏 Длина тестового состава: {len(long_composition)} символов") - print() - - # Проверяем, что наши промпты НЕ содержат обрезки - test_prompt_analyze = f""" - Анализ соответствия товара на основе структурированных данных: - - Полный анализ: - Описание: {long_description} - Состав: {long_composition} - - Оцени соответствие по шкале 1-10 и верни JSON: {{"score": число, "reasoning": "объяснение", "confidence": значение_0_1}} - """ - - test_prompt_similar = f""" - Найди 3-5 аналогичных товаров на основе: - Категории: ['косметика', 'уход за кожей'] - Тип продукта: косметика_anti_aging - Состав: {long_composition} - - Проанализируй характеристики аналогичных товаров и верни результаты в формате JSON: - {{"analogs": [ - {{"name": "Название товара", "price_range": "Цена от-до", "key_features": ["особенности"], "similarity_score": 85}}, - ... - ]}} - """ - - # Проверки - print("✅ ПРОВЕРКА ПРОМПТА _analyze_composition_vs_description:") - print(f" - Полное описание передано: {len(long_description)} символов") - print(f" - Полный состав передан: {len(long_composition)} символов") - print(" - Статус: ✅ НЕТ ОБРЕЗКИ") - print() - - print("✅ ПРОВЕРКА ПРОМПТА _find_similar_products:") - print(f" - Полный состав передан: {len(long_composition)} символов") - print(" - Статус: ✅ НЕТ ОБРЕЗКИ") - print() - - # Проверка, что промпты содержат полные тексты - assert long_description in test_prompt_analyze, "❌ Описание обрезано в промпте анализа!" - assert long_composition in test_prompt_analyze, "❌ Состав обрезан в промпте анализа!" - assert long_composition in test_prompt_similar, "❌ Состав обрезан в промпте поиска аналогов!" - - print("🎉 ВСЕ ПРОВЕРКИ ПРОШЛИ УСПЕШНО!") - print("📝 Исправления работают корректно:") - print(" ✅ Полные тексты description и composition передаются в AI") - print(" ✅ JSON парсинг должен работать корректно") - print(" ✅ Обрезка остается только для логов и отображения пользователю") - print() - - print("🔧 РЕКОМЕНДАЦИИ ДЛЯ ТЕСТИРОВАНИЯ:") - print(" 1. Загрузите расширение в Chrome (chrome://extensions/)") - print(" 2. Перейдите на страницу товара Ozon") - print(" 3. Запустите анализатор") - print(" 4. Проверьте консоль на отсутствие ошибок JSON парсинга") - print(" 5. Убедитесь, что анализ работает с длинными текстами") - - print("\n" + "=" * 60) - print("✅ ТЕСТИРОВАНИЕ ЗАВЕРШЕНО УСПЕШНО") - print("=" * 60) - - return True - -if __name__ == "__main__": - try: - test_text_truncation_fix() - print("\n🎉 СКРИПТ ВЫПОЛНЕН УСПЕШНО!") - sys.exit(0) - except Exception as e: - print(f"\n❌ ОШИБКА В ТЕСТИРОВАНИИ: {e}") - sys.exit(1) \ No newline at end of file diff --git a/tests/e2e/config/wdio.browser.conf.ts b/tests/e2e/config/wdio.browser.conf.ts deleted file mode 100644 index bda81672..00000000 --- a/tests/e2e/config/wdio.browser.conf.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { config as baseConfig } from './wdio.conf.js'; -import { getChromeExtensionPath, getFirefoxExtensionPath } from '../utils/extension-path.js'; -import { IS_CI, IS_FIREFOX } from '@extension/env'; -import { readdir, readFile } from 'node:fs/promises'; -import { extname, join } from 'node:path'; - -const extName = IS_FIREFOX ? '.xpi' : '.zip'; -const extensions = await readdir(join(import.meta.dirname, '../../../dist-zip')); -const latestExtension = extensions.filter(file => extname(file) === extName).at(-1); -const extPath = join(import.meta.dirname, `../../../dist-zip/${latestExtension}`); -const bundledExtension = (await readFile(extPath)).toString('base64'); - -const chromeCapabilities = { - browserName: 'chrome', - acceptInsecureCerts: true, - 'goog:chromeOptions': { - args: [ - '--disable-web-security', - '--disable-gpu', - '--no-sandbox', - '--disable-dev-shm-usage', - ...(IS_CI ? ['--headless'] : []), - ], - prefs: { 'extensions.ui.developer_mode': true }, - extensions: [bundledExtension], - }, -}; - -const firefoxCapabilities = { - browserName: 'firefox', - acceptInsecureCerts: true, - 'moz:firefoxOptions': { - args: [...(IS_CI ? ['--headless'] : [])], - }, -}; - -export const config: WebdriverIO.Config = { - ...baseConfig, - capabilities: IS_FIREFOX ? [firefoxCapabilities] : [chromeCapabilities], - - maxInstances: IS_CI ? 10 : 1, - logLevel: 'error', - execArgv: IS_CI ? [] : ['--inspect'], - before: async ({ browserName }: WebdriverIO.Capabilities, _specs, browser: WebdriverIO.Browser) => { - if (browserName === 'firefox') { - await browser.installAddOn(bundledExtension, true); - - browser.addCommand('getExtensionPath', async () => getFirefoxExtensionPath(browser)); - } else if (browserName === 'chrome') { - browser.addCommand('getExtensionPath', async () => getChromeExtensionPath(browser)); - } - }, - afterTest: async () => { - if (!IS_CI) { - await browser.pause(500); - } - }, -}; diff --git a/tests/e2e/config/wdio.conf.ts b/tests/e2e/config/wdio.conf.ts deleted file mode 100644 index 2567bb62..00000000 --- a/tests/e2e/config/wdio.conf.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * WebdriverIO v9 configuration file - * https://webdriver.io/docs/configurationfile - */ -export const config: WebdriverIO.Config = { - runner: 'local', - tsConfigPath: '../tsconfig.json', - - // - // ================== - // Specify Test Files - // ================== - // Define which test specs should run. The pattern is relative to the directory - // of the configuration file being run. - // - // The specs are defined as an array of spec files (optionally using wildcards - // that will be expanded). The test for each spec file will be run in a separate - // worker process. In order to have a group of spec files run in the same worker - // process simply enclose them in an array within the specs array. - // - // The path of the spec files will be resolved relative from the directory - // of the config file unless it's absolute. - specs: ['../specs/**/*.ts'], - // Patterns to exclude. - exclude: [], - // - // ============ - // Capabilities - // ============ - // Define your capabilities here. WebdriverIO can run multiple capabilities at the same - // time. Depending on the number of capabilities, WebdriverIO launches several test - // sessions. Within your capabilities you can overwrite the spec and exclude options in - // order to group specific specs to a specific capability. - // - // First, you can define how many instances should be started at the same time. Let's - // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have - // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec - // files and you set maxInstances to 10, all spec files will get tested at the same time - // and 30 processes will get spawned. The property handles how many capabilities - // from the same test should run tests. - // - maxInstances: 10, - // - // If you have trouble getting all important capabilities together, check out the - // Sauce Labs platform configurator - a great tool to configure your capabilities: - // https://saucelabs.com/platform/platform-configurator - // - capabilities: [], - - // - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // - // Level of logging verbosity: trace | debug | info | warn | error | silent - logLevel: 'info', - - // - // If you only want to run your tests until a specific amount of tests have failed use - // bail (default is 0 - don't bail, run all tests). - bail: 0, - // - // Default timeout for all waitFor* commands. - waitforTimeout: 10000, - - // - // Default timeout in milliseconds for request - // if browser driver or grid doesn't send response - connectionRetryTimeout: 120000, - - // - // Default request retries count - connectionRetryCount: 3, - - // - // Framework you want to run your specs with. - // The following are supported: Mocha, Jasmine, and Cucumber - // see also: https://webdriver.io/docs/frameworks - // - // Make sure you have the wdio adapter package for the specific framework installed - // before running any tests. - framework: 'mocha', - - // - // Test reporter for stdout. - // The only one supported by default is 'dot' - // see also: https://webdriver.io/docs/dot-reporter - reporters: ['spec'], - - // Options to be passed to Mocha. - // See the full list at http://mochajs.org/ - mochaOpts: { - ui: 'bdd', - timeout: 60000, - }, -}; diff --git a/tests/e2e/config/wdio.d.ts b/tests/e2e/config/wdio.d.ts deleted file mode 100644 index 8dbebe46..00000000 --- a/tests/e2e/config/wdio.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare namespace WebdriverIO { - interface Browser extends WebdriverIO.Browser { - getExtensionPath: () => Promise; - installAddOn: (extension: string, temporary: boolean) => Promise; - addCommand: (name: string, func: () => Promise) => void; - } -} diff --git a/tests/e2e/helpers/theme.ts b/tests/e2e/helpers/theme.ts deleted file mode 100644 index 7fdbda8e..00000000 --- a/tests/e2e/helpers/theme.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Helper method to check if user can click on theme button and toggle theme color - */ -export const canSwitchTheme = async () => { - const LIGHT_THEME_CLASS = 'bg-slate-50'; - const DARK_THEME_CLASS = 'bg-gray-800'; - - const app = await $('.App').getElement(); - // Ищем по классу, а не по тексту - const toggleThemeButton = await $('.theme-toggle-btn').getElement(); - - await expect(app).toBeExisting(); - await expect(toggleThemeButton).toBeExisting(); - - const appClasses = await app.getAttribute('class'); - const initialThemeClass = appClasses.includes(LIGHT_THEME_CLASS) ? LIGHT_THEME_CLASS : DARK_THEME_CLASS; - const afterClickThemeClass = appClasses.includes(LIGHT_THEME_CLASS) ? DARK_THEME_CLASS : LIGHT_THEME_CLASS; - - // Toggle theme - await toggleThemeButton.click(); - await expect(app).toHaveElementClass(afterClickThemeClass); - - // Toggle back to initial theme - await toggleThemeButton.click(); - await expect(app).toHaveElementClass(initialThemeClass); -}; diff --git a/tests/e2e/package.json b/tests/e2e/package.json deleted file mode 100644 index 12077b83..00000000 --- a/tests/e2e/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@extension/e2e", - "version": "0.5.1363", - "description": "E2e tests configuration boilerplate", - "private": true, - "sideEffects": false, - "type": "module", - "scripts": { - "e2e": "wdio run config/wdio.browser.conf.ts", - "clean:node_modules": "pnpx rimraf node_modules", - "clean:turbo": "pnpx rimraf .turbo", - "clean": "pnpm clean:turbo && pnpm clean:node_modules" - }, - "devDependencies": { - "@extension/env": "workspace:*", - "@extension/tsconfig": "workspace:*", - "@wdio/cli": "^9.14.0", - "@wdio/globals": "^9.14.0", - "@wdio/local-runner": "^9.14.0", - "@wdio/mocha-framework": "^9.14.0", - "@wdio/spec-reporter": "^9.14.0", - "@wdio/types": "^9.14.0" - } -} diff --git a/tests/e2e/specs/debug-side-panel.test.ts b/tests/e2e/specs/debug-side-panel.test.ts deleted file mode 100644 index d0ebe3cf..00000000 --- a/tests/e2e/specs/debug-side-panel.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import fs from 'fs'; - -describe('Debug Side Panel - ToggleButton Issue', () => { - it('should debug ToggleButton ReferenceError', async () => { - console.log('🔍 Начинаем отладку ToggleButton в side-panel...'); - - // 1. Открываем side-panel напрямую - const extensionPath = await browser.getExtensionPath(); - const sidePanelUrl = `${extensionPath}/side-panel/index.html`; - - console.log('📂 Extension path:', extensionPath); - console.log('🌐 Side panel URL:', sidePanelUrl); - - await browser.url(sidePanelUrl); - - // 2. Ждём загрузки страницы - await browser.waitUntil(async () => (await browser.getTitle()) === 'Side Panel', { - timeout: 10000, - timeoutMsg: 'Side Panel не загрузился в течение 10 секунд', - }); - - console.log('✅ Side Panel загружен, заголовок:', await browser.getTitle()); - - // 3. Подписываемся на логи консоли (только поддерживаемые события) - await browser.sessionSubscribe({ events: ['log.entryAdded'] }); - - const consoleLogs: string[] = []; - const errorLogs: string[] = []; - - browser.on('log.entryAdded', logEntry => { - consoleLogs.push(`[${logEntry.level}] ${logEntry.text}`); - if (logEntry.level === 'error') { - errorLogs.push(logEntry.text); - } - console.log(`[Browser Log] ${logEntry.level}: ${logEntry.text}`); - }); - - // 4. Ждём немного для сбора логов - await browser.pause(3000); - - // 5. Проверяем наличие ToggleButton в DOM - const toggleButtonExists = await browser.execute(() => { - // Проверяем, есть ли элемент с классом или атрибутом, указывающим на ToggleButton - const buttons = document.querySelectorAll('button'); - console.log('🔍 Найдено кнопок:', buttons.length); - - for (let i = 0; i < buttons.length; i++) { - const btn = buttons[i]; - console.log(`Кнопка ${i}:`, { - className: btn.className, - textContent: btn.textContent?.trim(), - innerHTML: typeof btn.innerHTML === 'string' ? btn.innerHTML.substring(0, 100) : String(btn.innerHTML || '') - }); - } - - // Ищем кнопку переключения темы по содержимому SVG - const themeToggleButton = Array.from(buttons).find(btn => - btn.innerHTML.includes('M21 12.79A9') || // moon icon - btn.innerHTML.includes('M12 1v2M12 21v2') // sun icon - ); - - return { - totalButtons: buttons.length, - themeToggleButton: !!themeToggleButton, - themeToggleButtonHTML: themeToggleButton?.innerHTML || null - }; - }); - - console.log('🔍 Результат проверки DOM:', toggleButtonExists); - - // 6. Проверяем, загрузился ли React - const reactLoaded = await browser.execute(() => { - return { - reactLoaded: typeof window.React !== 'undefined', - reactDOMLoaded: typeof window.ReactDOM !== 'undefined', - appContainer: !!document.getElementById('app-container'), - appContainerChildren: document.getElementById('app-container')?.children.length || 0 - }; - }); - - console.log('🔍 Состояние React:', reactLoaded); - - // 7. Делаем скриншот - await browser.saveScreenshot('debug-side-panel.png'); - - // 8. Сохраняем DOM - const domDump = await browser.execute(() => document.documentElement.outerHTML); - fs.writeFileSync('debug-side-panel-dom.html', domDump); - - // 9. Сохраняем логи - fs.writeFileSync('debug-side-panel-logs.txt', consoleLogs.join('\n')); - fs.writeFileSync('debug-side-panel-errors.txt', errorLogs.join('\n')); - - // 10. Анализируем ошибки - const toggleButtonErrors = errorLogs.filter(log => - log.includes('ToggleButton') || - log.includes('ReferenceError') || - log.includes('is not defined') - ); - - console.log('🚨 Ошибки, связанные с ToggleButton:', toggleButtonErrors); - - // 11. Проверяем, есть ли ошибки загрузки модулей - const moduleErrors = errorLogs.filter(log => - log.includes('Failed to load module') || - log.includes('import') || - log.includes('export') - ); - - console.log('📦 Ошибки загрузки модулей:', moduleErrors); - - // 12. Выводим итоговый отчёт - const report = { - sidePanelLoaded: await browser.getTitle() === 'Side Panel', - toggleButtonErrors: toggleButtonErrors.length, - moduleErrors: moduleErrors.length, - totalErrors: errorLogs.length, - totalLogs: consoleLogs.length, - domAnalysis: toggleButtonExists, - reactAnalysis: reactLoaded - }; - - console.log('📊 Итоговый отчёт:', JSON.stringify(report, null, 2)); - - // 13. Если есть ошибки ToggleButton, выводим подробности - if (toggleButtonErrors.length > 0) { - console.log('❌ ПРОБЛЕМА ОБНАРУЖЕНА: ToggleButton не загружен'); - console.log('🔧 Возможные причины:'); - console.log(' 1. Компонент не экспортирован из @extension/ui'); - console.log(' 2. Проблема с импортом в SidePanel.tsx'); - console.log(' 3. Ошибка сборки Vite'); - console.log(' 4. Проблема с путями к модулям'); - - throw new Error(`ToggleButton не загружен. См. логи в debug-side-panel-errors.txt`); - } - - console.log('✅ Отладка завершена успешно'); - }); -}); \ No newline at end of file diff --git a/tests/e2e/specs/page-content-runtime.test.ts b/tests/e2e/specs/page-content-runtime.test.ts deleted file mode 100644 index 6a4bc7f6..00000000 --- a/tests/e2e/specs/page-content-runtime.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -describe('Webextension Content Runtime Script', () => { - before(function () { - // Chrome doesn't allow content scripts on the extension pages - if ((browser.capabilities as WebdriverIO.Capabilities).browserName === 'chrome') { - this.skip(); - } - }); - - it('should create all runtime elements on the page', async function () { - // Open the popup - const extensionPath = await browser.getExtensionPath(); - const popupUrl = `${extensionPath}/popup/index.html`; - - // if Popup file not found, skip the test - try { - await browser.url(popupUrl); - } catch { - console.error('Popup file not found'); - this.skip(); - } - - await expect(browser).toHaveTitle('Popup'); - - // Trigger inject button on popup - const contentScriptsButton = await $('button*=Content Scripts').getElement(); - - await contentScriptsButton.click(); - - // Check if id exists on the page - const runtimeExampleElement = await $('#CEB-extension-runtime-example').getElement(); - const runtimeAllElement = await $('#CEB-extension-runtime-all').getElement(); - - await expect(runtimeExampleElement).toBeExisting(); - await expect(runtimeAllElement).toBeExisting(); - }); -}); diff --git a/tests/e2e/specs/page-content-ui.test.ts b/tests/e2e/specs/page-content-ui.test.ts deleted file mode 100644 index 0ce4cda2..00000000 --- a/tests/e2e/specs/page-content-ui.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -describe('Content UI Injection', () => { - it('should locate the injected content UI (all and example) div on example.com`', async () => { - await browser.url('https://example.com'); - - const contentAllDiv = await $('#CEB-extension-all').getElement(); - await expect(contentAllDiv).toBeDisplayed(); - - const contentExampleDiv = await $('#CEB-extension-example').getElement(); - await expect(contentExampleDiv).toBeDisplayed(); - }); - - it('should locate the injected content UI all div and not locate example div on google.com', async () => { - await browser.url('https://www.google.com'); - - const contentAllDiv = await $('#CEB-extension-all').getElement(); - await expect(contentAllDiv).toBeDisplayed(); - - const contentExampleDiv = await $('#CEB-extension-example').getElement(); - await expect(contentExampleDiv).not.toBeDisplayed(); - }); -}); diff --git a/tests/e2e/specs/page-content.test.ts b/tests/e2e/specs/page-content.test.ts deleted file mode 100644 index 3bd8c67c..00000000 --- a/tests/e2e/specs/page-content.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -describe('Webextension Content Script', () => { - it('should log "example content script loaded" in console on example.com', async () => { - await browser.sessionSubscribe({ events: ['log.entryAdded'] }); - const logs: (string | null)[] = []; - - browser.on('log.entryAdded', logEntry => { - logs.push(logEntry.text); - }); - - await browser.url('https://example.com'); - - const EXPECTED_LOG_MESSAGE = '[CEB] Example content script loaded'; - await browser.waitUntil(() => logs.includes(EXPECTED_LOG_MESSAGE)); - - expect(logs).toContain(EXPECTED_LOG_MESSAGE); - }); - - it('should log "all content script loaded" in console on any page', async () => { - await browser.sessionSubscribe({ events: ['log.entryAdded'] }); - const logs: (string | null)[] = []; - - browser.on('log.entryAdded', logEntry => { - logs.push(logEntry.text); - }); - - await browser.url('https://www.google.com'); - - const EXPECTED_LOG_MESSAGE = '[CEB] All content script loaded'; - await browser.waitUntil(() => logs.includes(EXPECTED_LOG_MESSAGE)); - - expect(logs).toContain(EXPECTED_LOG_MESSAGE); - }); -}); diff --git a/tests/e2e/specs/page-new-tab.test.ts b/tests/e2e/specs/page-new-tab.test.ts deleted file mode 100644 index 44501c09..00000000 --- a/tests/e2e/specs/page-new-tab.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { canSwitchTheme } from '../helpers/theme.js'; - -describe('Webextension New Tab', () => { - it('should open the extension page when a new tab is opened', async () => { - const extensionPath = await browser.getExtensionPath(); - const newTabUrl = - process.env.CLI_CEB_FIREFOX === 'true' ? `${extensionPath}/new-tab/index.html` : 'chrome://newtab'; - - await browser.url(newTabUrl); - - const appDiv = await $('.App').getElement(); - await expect(appDiv).toBeExisting(); - await canSwitchTheme(); - }); -}); diff --git a/tests/e2e/specs/page-options.test.ts b/tests/e2e/specs/page-options.test.ts deleted file mode 100644 index cf9fcb0d..00000000 --- a/tests/e2e/specs/page-options.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { canSwitchTheme } from '../helpers/theme.js'; - -describe('Webextension Options Page', () => { - it('should make options page accessible', async () => { - const extensionPath = await browser.getExtensionPath(); - const optionsUrl = `${extensionPath}/options/index.html`; - - await browser.url(optionsUrl); - - await expect(browser).toHaveTitle('Options'); - await canSwitchTheme(); - }); - - describe('API Keys Management', () => { - beforeEach(async () => { - const extensionPath = await browser.getExtensionPath(); - const optionsUrl = `${extensionPath}/options/index.html`; - await browser.url(optionsUrl); - - // Wait for React to load - await browser.waitUntil(async () => { - const settingsTab = await $('[id="settings-tab-button"]'); - return await settingsTab.isDisplayed(); - }, { - timeout: 10000, - timeoutMsg: 'Options page did not load properly' - }); - }); - - it('should display API Keys section', async () => { - // Click on settings tab - const settingsTab = await $('[id="settings-tab-button"]'); - await settingsTab.click(); - - // Wait for settings content to load - await browser.waitUntil(async () => { - const settingsSections = await $$('[class*="settings-section"]'); - const sectionsCount = await settingsSections.length; - if (sectionsCount === 0) return false; - const aiKeysTitle = await settingsSections[0].$('h3'); - return await aiKeysTitle.isDisplayed(); - }, { - timeout: 5000, - timeoutMsg: 'API Keys section did not load' - }); - - // Check if API Keys section is present - const settingsSections = await $$('[class*="settings-section"]'); - const aiKeysSection = await settingsSections[0].$('h3'); - const aiKeysTitle = await aiKeysSection.getText(); - expect(aiKeysTitle).toContain('AI API Keys'); - }); - - it('should display fixed API key inputs', async () => { - const settingsTab = await $('[id="settings-tab-button"]'); - await settingsTab.click(); - - await browser.waitUntil(async () => { - const fixedKeys = await $$('[class*="ai-key-item fixed-key"]'); - const keysCount = await fixedKeys.length; - return keysCount > 0; - }, { - timeout: 5000, - timeoutMsg: 'Fixed API key inputs did not load' - }); - - // Check that we have fixed API key inputs - const fixedKeys = await $$('[class*="ai-key-item fixed-key"]'); - const fixedKeysCount = await fixedKeys.length; - expect(fixedKeysCount).toBeGreaterThan(0); - - // Check that each fixed key has an input field - for (const keyElement of fixedKeys) { - const input = await keyElement.$('input[type="password"]'); - expect(await input.isDisplayed()).toBe(true); - } - }); - - it('should display custom API keys section', async () => { - const settingsTab = await $('[id="settings-tab-button"]'); - await settingsTab.click(); - - await browser.waitUntil(async () => { - const customKeysSection = await $('[class*="custom-keys-section"]'); - return await customKeysSection.isDisplayed(); - }, { - timeout: 5000, - timeoutMsg: 'Custom API keys section did not load' - }); - - // Check for add new key button - const addButton = await $('[class*="add-key-btn"]'); - expect(await addButton.isDisplayed()).toBe(true); - }); - - it('should display save and test buttons', async () => { - const settingsTab = await $('[id="settings-tab-button"]'); - await settingsTab.click(); - - await browser.waitUntil(async () => { - const saveButton = await $('[class*="save-btn"]'); - return await saveButton.isDisplayed(); - }, { - timeout: 5000, - timeoutMsg: 'Save button did not load' - }); - - const saveButton = await $('[class*="save-btn"]'); - const testButton = await $('[class*="test-btn"]'); - - expect(await saveButton.isDisplayed()).toBe(true); - expect(await testButton.isDisplayed()).toBe(true); - - expect(await saveButton.getText()).toContain('Save'); - expect(await testButton.getText()).toContain('Test'); - }); - - it('should allow adding custom API key', async () => { - const settingsTab = await $('[id="settings-tab-button"]'); - await settingsTab.click(); - - const addButton = await $('[class*="add-key-btn"]'); - await addButton.click(); - - // Wait for new custom key input to appear - await browser.waitUntil(async () => { - const customKeys = await $$('[class*="ai-key-item custom-key"]'); - const keysCount = await customKeys.length; - return keysCount > 0; - }, { - timeout: 3000, - timeoutMsg: 'Custom key input did not appear after clicking add button' - }); - - const customKeys = await $$('[class*="ai-key-item custom-key"]'); - const customKeysCount = await customKeys.length; - expect(customKeysCount).toBeGreaterThan(0); - - // Check that the new custom key has input fields - const lastCustomKey = customKeys[customKeysCount - 1]; - const nameInput = await lastCustomKey.$('input[type="text"]'); - const keyInput = await lastCustomKey.$('input[type="password"]'); - - expect(await nameInput.isDisplayed()).toBe(true); - expect(await keyInput.isDisplayed()).toBe(true); - }); - }); -}); diff --git a/tests/e2e/specs/page-popup.test.ts b/tests/e2e/specs/page-popup.test.ts deleted file mode 100644 index 7a4805e2..00000000 --- a/tests/e2e/specs/page-popup.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { canSwitchTheme } from '../helpers/theme.js'; - -describe('Webextension Popup', () => { - it('should open the popup successfully', async () => { - const extensionPath = await browser.getExtensionPath(); - const popupUrl = `${extensionPath}/popup/index.html`; - await browser.url(popupUrl); - - await expect(browser).toHaveTitle('Popup'); - await canSwitchTheme(); - }); -}); diff --git a/tests/e2e/specs/page-side-panel.test.ts b/tests/e2e/specs/page-side-panel.test.ts deleted file mode 100644 index b45a9660..00000000 --- a/tests/e2e/specs/page-side-panel.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { canSwitchTheme } from '../helpers/theme.ts'; -import fs from 'fs'; - -const OZON_URL = 'https://www.ozon.ru/product/kostyum-sportivnyy-north-dot-1438414833/'; - -describe('Webextension Side Panel', () => { - it('should make side panel accessible', async () => { - const extensionPath = await browser.getExtensionPath(); - const sidePanelUrl = `${extensionPath}/side-panel/index.html`; - - await browser.url(sidePanelUrl); - await expect(browser).toHaveTitle('Side Panel'); - await canSwitchTheme(); - }); - - it('should open ozon, open sidepanel, switch to chat, send message and read logs', async () => { - // 1. Открываем страницу Ozon - await browser.url(OZON_URL); - await browser.pause(2000); // ждём полной загрузки - - // 2. Открываем sidepanel (если есть кнопка/иконка — кликнуть, иначе напрямую) - const extensionPath = await browser.getExtensionPath(); - const sidePanelUrl = `${extensionPath}/side-panel/index.html`; - await browser.url(sidePanelUrl); - await browser.waitUntil(async () => (await browser.getTitle()) === 'Side Panel', { - timeout: 5000, - timeoutMsg: 'Sidepanel не загрузился', - }); - await expect(browser).toHaveTitle('Side Panel'); - - // 2.1. Кликаем по первой доступной карточке плагина - const pluginCards = await $$('.plugin-card'); - if (pluginCards.length === 0) { - throw new Error('Не найдено ни одного плагина'); - } - - const firstPluginCard = pluginCards[0]; - console.log('Кликаем по первому плагину:', await firstPluginCard.getText()); - await firstPluginCard.click(); - - // 3. Переключаемся на вкладку "Чат" - const chatTab = await $('button.tab-btn').getElement(); - await chatTab.click(); - - // 4. Читаем логи sidepanel и background - await browser.sessionSubscribe({ events: ['log.entryAdded'] }); - const logs = []; - const backgroundLogs = []; - browser.on('log.entryAdded', logEntry => { - logs.push(logEntry.text); - // Фильтруем логи background по ключевым словам или по структуре logEntry - if (logEntry.text.includes('[background]')) { - backgroundLogs.push(logEntry.text); - } - console.log('[Sidepanel/Background]', logEntry.text); - }); - - // 5. Вводим и отправляем сообщение - const chatInput = await $('textarea').getElement(); - await chatInput.setValue('Тестовое сообщение из e2e'); - const sendBtn = await $('button').getElement(); - await sendBtn.click(); - - // 6. Проверяем, что сообщение появилось, иначе собираем дампы для диагностики - let chatAppeared = false; - let errorMsg = ''; - try { - await browser.waitUntil( - async () => { - const messages = await $$('div[style*="border: 1px solid #ccc"]'); - return messages.some(async m => (await m.getText()).includes('Тестовое сообщение из e2e')); - }, - { timeout: 5000, timeoutMsg: 'Сообщение не появилось в чате' }, - ); - chatAppeared = true; - } catch (err) { - errorMsg = err.message || String(err); - } - - if (!chatAppeared) { - // Сохраняем дамп DOM sidepanel - const domDump = await browser.execute(() => document.documentElement.outerHTML); - fs.writeFileSync('sidepanel_dom_dump.html', domDump); - - // Сохраняем скриншот sidepanel - await browser.saveScreenshot('sidepanel_error.png'); - - // Сохраняем последние логи sidepanel - fs.writeFileSync('sidepanel_logs.txt', logs.join('\n')); - // Сохраняем логи background - fs.writeFileSync('background_logs.txt', backgroundLogs.join('\n')); - - // Пробуем получить состояние чата (input, send, tab) - const chatInputVal = await $('textarea') - .getValue() - .catch(() => 'input not found'); - const chatTabExists = await $('button.tab-btn') - .isExisting() - .catch(() => false); - const chatMessagesCount = (await $$('div[style*="border: 1px solid #ccc"]')).length; - fs.writeFileSync( - 'sidepanel_chat_state.txt', - `input: ${chatInputVal}\ntab: ${chatTabExists}\nmessages: ${chatMessagesCount}`, - ); - - throw new Error( - `Сообщение не появилось в чате.\n${errorMsg}\nСм. sidepanel_dom_dump.html, sidepanel_error.png, sidepanel_logs.txt, background_logs.txt, sidepanel_chat_state.txt для диагностики.`, - ); - } - }); -}); diff --git a/tests/e2e/specs/smoke.test.ts b/tests/e2e/specs/smoke.test.ts deleted file mode 100644 index ba2d9ac9..00000000 --- a/tests/e2e/specs/smoke.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe('The example page can be loaded', () => { - it('should be able to go to example page', async () => { - await browser.url('https://www.example.com'); - - await expect(browser).toHaveTitle('Example Domain'); - }); -}); diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json deleted file mode 100644 index 2c67c9d3..00000000 --- a/tests/e2e/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@extension/tsconfig/module", - "compilerOptions": { - "lib": ["dom"], - "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework"], - "resolveJsonModule": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["specs", "config", "helpers"] -} diff --git a/tests/e2e/utils/extension-path.ts b/tests/e2e/utils/extension-path.ts deleted file mode 100644 index e863d0b6..00000000 --- a/tests/e2e/utils/extension-path.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Returns the Chrome extension path. - * @param browser - * @returns path to the Chrome extension - */ -export const getChromeExtensionPath = async (browser: WebdriverIO.Browser) => { - await browser.url('chrome://extensions/'); - /** - * https://webdriver.io/docs/extension-testing/web-extensions/#test-popup-modal-in-chrome - * ```ts - * const extensionItem = await $('extensions-item').getElement(); - * ``` - * The above code is not working. I guess it's because the shadow root is not accessible. - * So I used the following code to access the shadow root manually. - * - * @url https://github.com/webdriverio/webdriverio/issues/13521 - * @url https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/issues/786 - */ - const extensionItem = await (async () => { - const extensionsManager = await $('extensions-manager').getElement(); - const itemList = await extensionsManager.shadow$('#container > #viewManager > extensions-item-list'); - return itemList.shadow$('extensions-item'); - })(); - - const extensionId = await extensionItem.getAttribute('id'); - - if (!extensionId) { - throw new Error('Extension ID not found'); - } - - return `chrome-extension://${extensionId}`; -}; - -/** - * Returns the Firefox extension path. - * @param browser - * @returns path to the Firefox extension - */ -export const getFirefoxExtensionPath = async (browser: WebdriverIO.Browser) => { - await browser.url('about:debugging#/runtime/this-firefox'); - const uuidElement = await browser.$('//dt[contains(text(), "Internal UUID")]/following-sibling::dd').getElement(); - const internalUUID = await uuidElement.getText(); - - if (!internalUUID) { - throw new Error('Internal UUID not found'); - } - - return `moz-extension://${internalUUID}`; -}; diff --git a/tests/html-chunking-mock.test.ts b/tests/html-chunking-mock.test.ts deleted file mode 100644 index 692b6c41..00000000 --- a/tests/html-chunking-mock.test.ts +++ /dev/null @@ -1,545 +0,0 @@ -/** - * Mock-тесты для HTML chunking процесса - * Тестирование основных компонентов без реального HTML - */ - -import { jest, describe, beforeEach, afterEach, test, expect } from '@jest/globals'; - -// Mock chrome API -global.chrome = { - runtime: { - sendMessage: jest.fn(), - onMessage: { - addListener: jest.fn(), - }, - lastError: null, - }, - storage: { - local: { - get: jest.fn(), - set: jest.fn(), - }, - sync: { - get: jest.fn(), - set: jest.fn(), - }, - }, - tabs: { - query: jest.fn(), - create: jest.fn(), - }, - scripting: { - executeScript: jest.fn(), - }, - action: { - onClicked: { - addListener: jest.fn(), - }, - }, -} as any; - -// Mock console для захвата логов -const originalConsole = console; -global.console = { - ...originalConsole, - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), -}; - -// =============================================================================== -// MOCK COMPONENT CLASSES FOR TESTING -// =============================================================================== - -class MockCircuitBreaker { - private state = 'CLOSED'; - - async execute(operation: () => Promise): Promise { - return await operation(); - } - - getState() { - return this.state; - } -} - -class MockTransferPersistenceManager { - private storage = new Map(); - - async save(transfer: any, transferId: string): Promise { - this.storage.set(transferId, { - transferId, - startTime: Date.now(), - totalChunks: transfer.chunks.length, - totalSize: transfer.totalSize, - status: 'active', - lastUpdated: Date.now() - }); - } - - async load(transferId: string) { - return this.storage.get(transferId) || null; - } - - async loadAll() { - return Object.fromEntries(this.storage); - } -} - -class MockTransferRecoveryManager { - async recoverTransfer(transferId: string) { - return { - success: true, - transfer: { - chunks: ['mock-chunk-1', 'mock-chunk-2'], - acked: [true, true], - totalSize: 100, - startTime: Date.now(), - htmlAssembledConfirmed: true - }, - html: 'Mock recovered HTML', - strategy: 'mock_recovery', - duration: 50 - }; - } -} - -class MockEnhancedChunkManager { - public transfers = new Map(); - public completedTransfers = new Map(); - public assembledHtmls = new Map(); - public emergencyBackup = new Map(); - public globalTransferRefs = new Map(); - public persistenceManager = new MockTransferPersistenceManager(); - public recoveryManager = new MockTransferRecoveryManager(); - - async sendInChunks(data: string, transferId: string): Promise { - const chunks = this.createChunks(data); - const transfer = { - chunks, - acked: new Array(chunks.length).fill(false), - totalSize: data.length, - startTime: Date.now(), - htmlAssembledConfirmed: false - }; - - this.transfers.set(transferId, transfer); - this.globalTransferRefs.set(transferId, transfer); - this.emergencyBackup.set(transferId, transfer); - - // Многоуровневое хранение для надежности - (globalThis as any).emergencyTransfers = (globalThis as any).emergencyTransfers || {}; - (globalThis as any).emergencyTransfers[transferId] = { - id: transferId, - transfer, - timestamp: Date.now(), - status: 'active' - }; - - (globalThis as any).fixedTransfers = (globalThis as any).fixedTransfers || []; - (globalThis as any).fixedTransfers.push({ - id: transferId, - transfer, - timestamp: Date.now(), - status: 'active' - }); - - console.log(`✅ CREATED transfer ${transferId} with ${chunks.length} chunks in multi-layer storage`); - } - - private createChunks(data: string): string[] { - const chunks: string[] = []; - const chunkSize = 32768; // 32KB - for (let i = 0; i < data.length; i += chunkSize) { - chunks.push(data.slice(i, i + chunkSize)); - } - return chunks; - } - - async acknowledgeChunk(transferId: string, chunkIndex: number): Promise { - const transfer = this.transfers.get(transferId); - if (transfer && chunkIndex < transfer.acked.length) { - transfer.acked[chunkIndex] = true; - console.log(`✅ acknowledgeChunk: Chunk ${chunkIndex} acknowledged for transfer ${transferId}`); - } - - // Store acknowledgment in global backup - if (!(globalThis as any).chunkAcknowledgments) { - (globalThis as any).chunkAcknowledgments = {}; - } - if (!(globalThis as any).chunkAcknowledgments[transferId]) { - (globalThis as any).chunkAcknowledgments[transferId] = new Set(); - } - (globalThis as any).chunkAcknowledgments[transferId].add(chunkIndex); - } - - wasChunked(transferId: string): boolean { - return this.transfers.has(transferId) || - this.completedTransfers.has(transferId) || - this.emergencyBackup.has(transferId) || - this.globalTransferRefs.has(transferId); - } - - getTransferStats(transferId: string) { - const transfer = this.transfers.get(transferId) || this.completedTransfers.get(transferId); - if (!transfer) return null; - - const completed = transfer.acked.filter((ack: boolean) => ack === true).length; - const total = transfer.chunks.length; - const duration = Date.now() - transfer.startTime; - - return { completed, total, duration }; - } - - setAssembledHtml(transferId: string, html: string): void { - this.assembledHtmls.set(transferId, html); - console.log(`💾 Stored assembled HTML for transfer ${transferId} (${html.length} chars)`); - } - - getAssembledHtml(transferId: string): string | null { - return this.assembledHtmls.get(transferId) || null; - } - - getAssembledData(transferId: string): string { - const transfer = this.transfers.get(transferId); - if (!transfer) { - console.warn(`❌ Transfer ${transferId} not found`); - return ''; - } - - const completed = transfer.acked.filter((ack: boolean) => ack === true).length; - const total = transfer.chunks.length; - - if (completed !== total) { - throw new Error(`Transfer ${transferId} not complete: ${completed}/${total} chunks acknowledged`); - } - - return transfer.chunks.join(''); - } - - completeTransfer(transferId: string): boolean { - const transfer = this.transfers.get(transferId); - if (!transfer) return false; - - this.completedTransfers.set(transferId, transfer); - this.transfers.delete(transferId); - - console.log(`🔄 COMPLETING transfer ${transferId} with multi-layer storage update`); - return true; - } - - async cleanup(): Promise { - // Mock cleanup - просто логируем - console.log('🧹 Cleanup completed'); - } -} - -// =============================================================================== -// TEST SUITE -// =============================================================================== - -describe('HTML Chunking Process - Mock Tests', () => { - let chunkManager: MockEnhancedChunkManager; - let recoveryManager: MockTransferRecoveryManager; - - beforeEach(() => { - chunkManager = new MockEnhancedChunkManager(); - recoveryManager = new MockTransferRecoveryManager(); - - // Clear all storages - chunkManager.transfers.clear(); - chunkManager.completedTransfers.clear(); - chunkManager.emergencyBackup.clear(); - chunkManager.globalTransferRefs.clear(); - chunkManager.assembledHtmls.clear(); - - // Clear global storages - (globalThis as any).emergencyTransfers = {}; - (globalThis as any).fixedTransfers = []; - (globalThis as any).chunkAcknowledgments = {}; - - // Reset mocks - jest.clearAllMocks(); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - test('✅ Создание transfer в multi-layer storage', async () => { - const testHtml = 'Test HTML content for chunking'; - const transferId = 'test-transfer-1'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем, что transfer создан во всех слоях хранения - expect(chunkManager.transfers.has(transferId)).toBe(true); - expect(chunkManager.globalTransferRefs.has(transferId)).toBe(true); - expect(chunkManager.emergencyBackup.has(transferId)).toBe(true); - expect((globalThis as any).emergencyTransfers[transferId]).toBeDefined(); - expect((globalThis as any).fixedTransfers.some((t: any) => t.id === transferId)).toBe(true); - - console.log('✅ Multi-layer storage verification passed'); - }); - - test('✅ Acknowledgment processing без race conditions', async () => { - const testHtml = 'Test'; - const transferId = 'test-ack-transfer'; - - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - const totalChunks = transfer.chunks.length; - - // Имитируем acknowledgments для всех чанков - for (let i = 0; i < totalChunks; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // Проверяем, что все чанки acknowledged - const stats = chunkManager.getTransferStats(transferId); - expect(stats?.completed).toBe(stats?.total); - - // Проверяем, что acknowledgments сохранены в global backup - expect((globalThis as any).chunkAcknowledgments[transferId]).toBeDefined(); - expect((globalThis as any).chunkAcknowledgments[transferId].size).toBe(totalChunks); - - console.log('✅ Acknowledgment processing passed'); - }); - - test('✅ Обработка HTML_ASSEMBLED', async () => { - const testHtml = 'Assembled HTML content'; - const transferId = 'test-assembled-transfer'; - - // Создаем transfer и подтверждаем все чанки - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - for (let i = 0; i < transfer.chunks.length; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // Имитируем получение HTML_ASSEMBLED - chunkManager.setAssembledHtml(transferId, testHtml); - const completed = chunkManager.completeTransfer(transferId); - - expect(completed).toBe(true); - expect(chunkManager.completedTransfers.has(transferId)).toBe(true); - expect(chunkManager.transfers.has(transferId)).toBe(false); - expect(chunkManager.getAssembledHtml(transferId)).toBe(testHtml); - - console.log('✅ HTML_ASSEMBLED processing passed'); - }); - - test('✅ Работа fallback recovery системы', async () => { - const transferId = 'test-recovery-transfer'; - - // Имитируем потерю transfer (удаляем из основной памяти) - chunkManager.transfers.delete(transferId); - chunkManager.completedTransfers.delete(transferId); - - expect(chunkManager.wasChunked(transferId)).toBe(false); - - // Запускаем recovery - const recoveryResult = await recoveryManager.recoverTransfer(transferId); - - expect(recoveryResult.success).toBe(true); - expect(recoveryResult.transfer).toBeDefined(); - expect(recoveryResult.html).toBeDefined(); - expect(recoveryResult.strategy).toBe('mock_recovery'); - - console.log('✅ Recovery system passed'); - }); - - test('✅ Multi-layer search functionality', async () => { - const testHtml = 'Multi-layer test'; - const transferId = 'test-multi-layer'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем поиск во всех слоях - expect(chunkManager.wasChunked(transferId)).toBe(true); - - // Имитируем повреждение основного слоя - chunkManager.transfers.delete(transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); // Должен найти в других слоях - - // Имитируем повреждение всех слоев кроме emergency - chunkManager.completedTransfers.delete(transferId); - chunkManager.globalTransferRefs.delete(transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); // Должен найти в emergency backup - - console.log('✅ Multi-layer search functionality passed'); - }); - - test('✅ Отсутствие race conditions при concurrent acknowledgments', async () => { - const testHtml = 'Concurrent test'; - const transferId = 'test-concurrent'; - - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - const totalChunks = transfer.chunks.length; - - // Имитируем конкурентные acknowledgments - const acknowledgmentPromises = []; - for (let i = 0; i < totalChunks; i++) { - acknowledgmentPromises.push(chunkManager.acknowledgeChunk(transferId, i)); - } - - await Promise.all(acknowledgmentPromises); - - // Проверяем, что все acknowledgments обработаны корректно - const stats = chunkManager.getTransferStats(transferId); - expect(stats?.completed).toBe(stats?.total); - - console.log('✅ Race condition protection passed'); - }); - - test('✅ Circuit breaker protection', async () => { - const circuitBreaker = new MockCircuitBreaker(); - - // Проверяем начальное состояние - expect(circuitBreaker.getState()).toBe('CLOSED'); - - // Имитируем успешную операцию - const result = await circuitBreaker.execute(async () => { - return 'success'; - }); - - expect(result).toBe('success'); - - console.log('✅ Circuit breaker protection passed'); - }); - - test('✅ Transfer cleanup и persistence', async () => { - const testHtml = 'Cleanup test'; - const transferId = 'test-cleanup'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем сохранение в persistence - const persisted = await chunkManager.persistenceManager.load(transferId); - expect(persisted).toBeDefined(); - expect(persisted?.transferId).toBe(transferId); - expect(persisted?.status).toBe('active'); - - // Выполняем cleanup - await chunkManager.cleanup(); - - console.log('✅ Transfer cleanup and persistence passed'); - }); -}); - -// =============================================================================== -// INTEGRATION TEST SUITE -// =============================================================================== - -describe('HTML Chunking Process - Integration Tests', () => { - let chunkManager: MockEnhancedChunkManager; - - beforeEach(() => { - chunkManager = new MockEnhancedChunkManager(); - }); - - test('🚀 Полный цикл: создание → acknowledgments → assembly → cleanup', async () => { - const testHtml = 'Test

Integration Test

This is a comprehensive test of the HTML chunking process.

'; - const transferId = 'integration-test-transfer'; - - console.log('🚀 Starting integration test...'); - - // 1. Создание transfer - await chunkManager.sendInChunks(testHtml, transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); - - // 2. Acknowledgment всех чанков - const transfer = chunkManager.transfers.get(transferId); - for (let i = 0; i < transfer.chunks.length; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // 3. Проверка completion stats - const stats = chunkManager.getTransferStats(transferId); - expect(stats?.completed).toBe(stats?.total); - - // 4. Assembly HTML - chunkManager.setAssembledHtml(transferId, testHtml); - expect(chunkManager.getAssembledHtml(transferId)).toBe(testHtml); - - // 5. Completion transfer - const completed = chunkManager.completeTransfer(transferId); - expect(completed).toBe(true); - expect(chunkManager.completedTransfers.has(transferId)).toBe(true); - expect(chunkManager.transfers.has(transferId)).toBe(false); - - // 6. Cleanup - await chunkManager.cleanup(); - - console.log('✅ Integration test completed successfully'); - }); - - test('🛡️ Recovery от потери transfer', async () => { - const testHtml = 'Recovery test content'; - const transferId = 'recovery-test-transfer'; - - // 1. Создание и потеря transfer - await chunkManager.sendInChunks(testHtml, transferId); - chunkManager.transfers.delete(transferId); // Имитация потери - - // 2. Попытка получить данные (должен использовать recovery) - const recoveryResult = await chunkManager.recoveryManager.recoverTransfer(transferId); - expect(recoveryResult.success).toBe(true); - - // 3. Восстановление transfer - chunkManager.completedTransfers.set(transferId, recoveryResult.transfer); - chunkManager.setAssembledHtml(transferId, recoveryResult.html!); - - // 4. Проверка восстановления - expect(chunkManager.getAssembledHtml(transferId)).toBe(recoveryResult.html); - - console.log('🛡️ Recovery test completed successfully'); - }); -}); - -// =============================================================================== -// PERFORMANCE TEST SUITE -// =============================================================================== - -describe('HTML Chunking Process - Performance Tests', () => { - let chunkManager: MockEnhancedChunkManager; - - beforeEach(() => { - chunkManager = new MockEnhancedChunkManager(); - }); - - test('⚡ Производительность при большом количестве чанков', async () => { - // Создаем большой HTML (имитация) - const largeHtml = '' + 'Large content '.repeat(1000) + ''; - const transferId = 'performance-test'; - - const startTime = Date.now(); - await chunkManager.sendInChunks(largeHtml, transferId); - const creationTime = Date.now() - startTime; - - const transfer = chunkManager.transfers.get(transferId); - const chunkCount = transfer.chunks.length; - - console.log(`📊 Performance test: ${chunkCount} chunks created in ${creationTime}ms`); - - // Проверяем, что все чанки созданы - expect(chunkCount).toBeGreaterThan(1); - expect(creationTime).toBeLessThan(1000); // Должно быть быстро - - // Быстрая обработка acknowledgments - const ackStartTime = Date.now(); - for (let i = 0; i < chunkCount; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - const ackTime = Date.now() - ackStartTime; - - console.log(`📊 Acknowledgments processed in ${ackTime}ms`); - expect(ackTime).toBeLessThan(2000); // Должно быть reasonably быстро - }); -}); - -// Запуск тестов -if (typeof describe !== 'undefined') { - console.log('🧪 Mock HTML Chunking Tests loaded and ready to run'); -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/async-message-handler.js b/tests/ozon-analyzer-integration/async-message-handler.js deleted file mode 100644 index 28e1665c..00000000 --- a/tests/ozon-analyzer-integration/async-message-handler.js +++ /dev/null @@ -1,363 +0,0 @@ -/** - * Async Message Handler с Retry Logic и Exponential Backoff - * Для resilient message communication между компонентами - */ - -export class AsyncMessageHandler { - constructor(options = {}) { - this.maxRetries = options.maxRetries || 5; - this.baseDelay = options.baseDelay || 100; // ms - this.maxDelay = options.maxDelay || 5000; // ms - this.backoffMultiplier = options.backoffMultiplier || 2; - this.timeoutMs = options.timeoutMs || 10000; // 10 seconds - - this.pendingMessages = new Map(); - this.messageTimeouts = new Map(); - this.connectionStable = true; - this.consecutiveFailures = 0; - } - - /** - * Отправка сообщения с automatic retry on failure - * @param {string} target - target для отправки - * @param {Object} message - сообщение для отправки - * @param {Object} options - опции retry - * @returns {Promise} Promise что резолвится с ответом - */ - async sendMessage(target, message, options = {}) { - const messageId = this.generateMessageId(); - const maxRetries = options.maxRetries || this.maxRetries; - const timeoutMs = options.timeout || this.timeoutMs; - - return new Promise((resolve, reject) => { - let attempts = 0; - let lastError = null; - - const trySend = async () => { - attempts++; - console.log(`[MessageHandler] Attempt ${attempts}/${maxRetries + 1} - Sending message to ${target}`, message); - - try { - const result = await this.attemptSendMessage(target, message, timeoutMs); - - // Success! Reset failure counter and resolve - this.consecutiveFailures = 0; - this.connectionStable = true; - console.log(`[MessageHandler] ✅ Message delivered to ${target} on attempt ${attempts}`); - resolve(result); - - } catch (error) { - lastError = error; - console.log(`[MessageHandler] ⚠️ Attempt ${attempts} failed: ${error.message}`); - - // Track consecutive failures for connection stability - this.consecutiveFailures++; - if (this.consecutiveFailures >= 3) { - this.connectionStable = false; - } - - if (attempts <= maxRetries) { - // Calculate delay with exponential backoff and jitter - const delay = this.calculateDelay(attempts - 1); - console.log(`[MessageHandler] ⏳ Retrying in ${delay}ms...`); - setTimeout(trySend, delay); - } else { - // All retry attempts exhausted - console.error(`[MessageHandler] ❌ All ${maxRetries + 1} attempts failed for message to ${target}`); - reject(this.createRetryError(lastError, attempts, maxRetries)); - } - } - }; - - // Start first attempt - trySend(); - }); - } - - /** - * Попытка отправки сообщения (низкоуровневая реализация) - * @param {string} target - target для отправки - * @param {Object} message - сообщение - * @param {number} timeoutMs - таймаут в ms - * @returns {Promise} Promise с результатом - */ - async attemptSendMessage(target, message, timeoutMs) { - return new Promise((resolve, reject) => { - const messageId = this.generateMessageId(); - const startTime = Date.now(); - - // Создаем timeout promise - const timeoutPromise = new Promise((_, timeoutReject) => { - const timeoutId = setTimeout(() => { - timeoutReject(new Error(`Timeout: Message to ${target} timed out after ${timeoutMs}ms`)); - this.messageTimeouts.delete(messageId); - }, timeoutMs); - this.messageTimeouts.set(messageId, timeoutId); - }); - - // Создаем actual message sending promise - const sendPromise = new Promise(async (resolve, reject) => { - try { - let result; - - // Разные методы отправки в зависимости от target типа - if (target === 'offscreen' && chrome?.offscreen) { - result = await this.sendToOffscreenDocument(message); - } else if (target === 'background' || target.startsWith('chrome-')) { - result = await this.sendToBackgroundScript(message); - } else if (target === 'worker' || target.includes('worker')) { - result = await this.sendToWorker(message); - } else if (target === 'fallback' || target.includes('fallback')) { - result = await this.sendFallback(message); - } else { - // Generic chrome extension message - result = await this.sendChromeMessage(target, message); - } - - resolve(result); - } catch (error) { - reject(error); - } - }); - - // Race between sending and timeout - Promise.race([sendPromise, timeoutPromise]) - .then((result) => { - // Clear timeout if message was sent successfully - if (this.messageTimeouts.has(messageId)) { - clearTimeout(this.messageTimeouts.get(messageId)); - this.messageTimeouts.delete(messageId); - } - resolve(result); - }) - .catch((error) => { - reject(error); - }); - }); - } - - /** - * Отправка сообщения в offscreen document - */ - async sendToOffscreenDocument(message) { - if (!chrome?.offscreen) { - throw new Error('Offscreen API not available'); - } - - try { - // Check if offscreen document exists - const existingContexts = await chrome.offscreen.hasDocument(); - if (!existingContexts) { - // Create offscreen document - await chrome.offscreen.createDocument({ - url: 'test-offscreen.html', - reasons: ['TESTING'], - justification: 'Running integration tests' - }); - } - - // Send message via runtime messaging - return await chrome.runtime.sendMessage({ - type: 'offscreen_test_message', - data: message, - target: 'offscreen' - }); - - } catch (error) { - throw new Error(`Offscreen message failed: ${error.message}`); - } - } - - /** - * Отправка сообщения в background script - */ - async sendToBackgroundScript(message) { - if (!chrome?.runtime?.sendMessage) { - throw new Error('Chrome runtime messaging not available'); - } - - return new Promise((resolve, reject) => { - chrome.runtime.sendMessage({ - type: 'test_message', - data: message, - target: 'background' - }, (response) => { - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response); - } - }); - }); - } - - /** - * Отправка сообщения worker'у - */ - async sendToWorker(message) { - // Try to use chrome.runtime.sendMessage for worker communication - if (chrome?.runtime?.sendMessage) { - return new Promise((resolve, reject) => { - chrome.runtime.sendMessage({ - type: 'worker_message', - data: message, - target: 'worker' - }, (response) => { - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response); - } - }); - }); - } else { - // Fallback for environments without chrome runtime - throw new Error('Chrome runtime not available for worker communication'); - } - } - - /** - * Отправка в fallback mode (для legacy browser версии) - */ - async sendFallback(message) { - console.log('[MessageHandler] Using fallback communication mode'); - - // Simulate network delay - await this.simulateNetworkDelay(); - - // Return mock response to simulate successful fallback communication - return { - success: true, - message: 'Fallback communication successful', - data: message, - fallback: true, - mode: 'legacy' - }; - } - - /** - * Generic chrome extension message - */ - async sendChromeMessage(target, message) { - if (!chrome?.runtime?.sendMessage) { - throw new Error('Chrome runtime messaging not available'); - } - - return new Promise((resolve, reject) => { - chrome.runtime.sendMessage({ - type: 'extension_message', - target: target, - data: message - }, (response) => { - if (chrome.runtime.lastError) { - reject(new Error(chrome.runtime.lastError.message)); - } else { - resolve(response); - } - }); - }); - } - - /** - * Расчет задержки с exponential backoff и jitter - * @param {number} attempt - номер попытки (начиная с 0) - * @returns {number} задержка в ms - */ - calculateDelay(attempt) { - // Exponential backoff: baseDelay * multiplier^attempt - let delay = this.baseDelay * Math.pow(this.backoffMultiplier, attempt); - - // Cap at maximum delay - delay = Math.min(delay, this.maxDelay); - - // Add jitter ±25% - const jitter = delay * 0.25 * (Math.random() * 2 - 1); - delay += jitter; - - return Math.floor(delay); - } - - /** - * Simulating network conditions for testing - * @returns {Promise} Promise that resolves after simulated delay - */ - async simulateNetworkDelay() { - const baseDelay = 100 + Math.random() * 200; // 100-300ms - return new Promise(resolve => setTimeout(resolve, baseDelay)); - } - - /** - * Создание ошибки с информацией о retries - * @param {Error} lastError - последняя ошибка - * @param {number} attempts - количество попыток - * @param {number} maxRetries - максимум retries - * @returns {Error} композитная ошибка - */ - createRetryError(lastError, attempts, maxRetries) { - const error = new Error(`Failed after ${attempts} attempts (max ${maxRetries + 1}). Last error: ${lastError.message}`); - error.lastError = lastError; - error.attempts = attempts; - error.code = 'MESSAGE_RETRY_FAILED'; - error.connectionStable = this.connectionStable; - return error; - } - - /** - * Генерация уникального message ID - * @returns {string} уникальный ID - */ - generateMessageId() { - return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - - /** - * Получить статистику стабильности соединения - * @returns {Object} статистика - */ - getConnectionStats() { - return { - stable: this.connectionStable, - consecutiveFailures: this.consecutiveFailures, - pendingMessages: this.pendingMessages.size, - activeTimeouts: this.messageTimeouts.size - }; - } - - /** - * Очистка всех таймаутов и ресурсов - */ - cleanup() { - console.log('[MessageHandler] Cleaning up resources...'); - - // Clear all timeouts - for (const timeoutId of this.messageTimeouts.values()) { - clearTimeout(timeoutId); - } - this.messageTimeouts.clear(); - this.pendingMessages.clear(); - - console.log('[MessageHandler] ✅ Cleanup completed'); - } -} - -/** - * Convenience function для быстрой отправки сообщения - * @param {string} target - target для отправки - * @param {Object} message - сообщение - * @param {Object} options - опции - * @returns {Promise} Promise с результатом - */ -export async function sendResilientMessage(target, message, options = {}) { - const handler = new AsyncMessageHandler(options); - try { - return await handler.sendMessage(target, message, options); - } finally { - handler.cleanup(); - } -} - -// Экспорт для global использования -if (typeof window !== 'undefined') { - window.AsyncMessageHandler = AsyncMessageHandler; - window.sendResilientMessage = sendResilientMessage; -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/browser-detection.js b/tests/ozon-analyzer-integration/browser-detection.js deleted file mode 100644 index 3b8eb511..00000000 --- a/tests/ozon-analyzer-integration/browser-detection.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Browser Detection and API Availability Checker - * Для проверки compatibility разных Chrome версий - */ - -export class BrowserDetection { - constructor() { - this.browserInfo = null; - this.apiSupport = null; - this.detectionResults = null; - } - - /** - * Определение версии браузера Chrome - * @returns {Object} Информация о версии Chrome - */ - static getChromeVersion() { - const userAgent = navigator.userAgent; - const match = userAgent.match(/Chrome\/(\d+)\./); - - if (!match) { - return { - fullVersion: null, - majorVersion: null, - isChrome: false, - isChromium: false - }; - } - - const fullVersion = match[1]; - const majorVersion = parseInt(fullVersion, 10); - - return { - fullVersion: fullVersion, - majorVersion: majorVersion, - isChrome: true, - isChromium: userAgent.includes('Chromium') - }; - } - - /** - * Проверка доступности offscreen API - * @returns {boolean} True если API доступен - */ - static checkOffscreenAPI() { - if (!chrome.offscreen) { - return false; - } - - try { - // Проверяем наличие основных методов - return typeof chrome.offscreen.createDocument === 'function' && - typeof chrome.offscreen.closeDocument === 'function' && - typeof chrome.offscreen.hasDocument === 'function'; - } catch (error) { - return false; - } - } - - /** - * Проверка доступности service worker API - * @returns {boolean} True если API доступен - */ - static checkServiceWorkerAPI() { - return 'serviceWorker' in navigator && - typeof navigator.serviceWorker.register === 'function'; - } - - /** - * Проверка доступности chrome extension APIs - * @returns {Object} Результаты проверки - */ - static checkExtensionAPIs() { - const apis = { - runtime: !!chrome.runtime, - storage: !!chrome.storage, - tabs: !!chrome.tabs, - windows: !!chrome.windows, - messaging: !!(chrome.runtime && chrome.runtime.connect), - alarms: !!chrome.alarms - }; - - const available = Object.values(apis).filter(Boolean).length; - const total = Object.keys(apis).length; - - return { - apis, - availability: available / total, - isFullyCompatible: available === total - }; - } - - /** - * Определение compatibility режима - * @returns {Object} Информация о compatibility - */ - static getCompatibilityMode() { - const chromeVersion = this.getChromeVersion(); - const hasOffscreen = this.checkOffscreenAPI(); - const extensionAPIs = this.checkExtensionAPIs(); - - // Chrome 109+ имеет полноценный offscreen API - const modernChrome = chromeVersion.majorVersion >= 109; - const legacyChrome = chromeVersion.majorVersion < 109 && chromeVersion.majorVersion >= 90; - - let mode = 'unknown'; - let features = []; - let limitations = []; - let recommendations = []; - - if (modernChrome && hasOffscreen && extensionAPIs.isFullyCompatible) { - mode = 'full'; - features = [ - 'Offscreen API fully supported', - 'All extension APIs available', - 'Modern Chrome (>=109)', - 'Full integration capabilities' - ]; - } else if (legacyChrome && extensionAPIs.isFullyCompatible) { - mode = 'legacy'; - features = [ - 'Core extension APIs available', - 'Legacy Chrome compatibility', - 'Fallback mechanisms available' - ]; - limitations = [ - 'Offscreen API not available (<109)', - 'Limited background processing', - 'Requires fallback implementations' - ]; - recommendations = [ - 'Use service worker for background tasks', - 'Implement manual DOM handling', - 'Consider content script alternatives' - ]; - } else if (chromeVersion.isChrome && !extensionAPIs.isFullyCompatible) { - mode = 'partial'; - features = extensionAPIs.apis; - limitations = [ - 'Some extension APIs unavailable', - 'Limited functionality', - 'Potential integration issues' - ]; - recommendations = [ - 'Review manifest permissions', - 'Check content security policy', - 'Test in target Chrome version' - ]; - } else { - mode = 'incompatible'; - limitations = [ - 'Browser not supported', - 'Chrome extension APIs unavailable' - ]; - recommendations = [ - 'Use Google Chrome', - 'Update to supported version (90+)', - 'Ensure proper extension environment' - ]; - } - - return { - mode, - version: chromeVersion, - hasOffscreen, - extensionAPIs, - features, - limitations, - recommendations, - readinessScore: this.calculateReadinessScore(mode, chromeVersion, extensionAPIs, hasOffscreen) - }; - } - - /** - * Расчет readiness score (0-1) - * @param {string} mode - Режим compatibility - * @param {Object} version - Информация о версии - * @param {Object} extensionAPIs - Результаты проверки APIs - * @param {boolean} hasOffscreen - Доступность offscreen API - * @returns {number} Readiness score от 0 до 1 - */ - static calculateReadinessScore(mode, version, extensionAPIs, hasOffscreen) { - switch (mode) { - case 'full': - return 0.95 + (Math.random() * 0.05); // 95-100% - case 'legacy': - return 0.75 + (Math.random() * 0.1); // 75-85% - case 'partial': - return extensionAPIs.availability * 0.6; // 0-60% - case 'incompatible': - return 0.1; - default: - return 0.2; - } - } - - /** - * Полная диагностика браузера - * @returns {Promise} Объект с полными результатами - */ - static async performFullDiagnosis() { - console.log('🔍 Диагностика браузера и API compatibility...'); - - try { - const results = { - timestamp: new Date().toISOString(), - browser: this.getChromeVersion(), - apis: { - offscreen: this.checkOffscreenAPI(), - serviceWorker: this.checkServiceWorkerAPI(), - extensions: this.checkExtensionAPIs() - }, - compatibility: this.getCompatibilityMode(), - environment: this.detectEnvironment(), - recommendations: [] - }; - - // Формируем рекомендации на основе результатов - if (results.compatibility.mode === 'legacy') { - results.recommendations.push('Для полных возможностей обновите Chrome до версии 109+'); - } else if (results.compatibility.mode === 'incompatible') { - results.recommendations.push('Необходим Chrome версии 90+ для корректной работы'); - } - - console.log(`✅ Compatibility: ${results.compatibility.mode} (${(results.compatibility.readinessScore * 100).toFixed(1)}% ready)`); - console.log(`📊 Browser: Chrome ${results.browser.majorVersion} (${results.browser.fullVersion})`); - - return results; - - } catch (error) { - console.error('❌ Ошибка диагностики:', error); - return { - error: error.message, - timestamp: new Date().toISOString(), - compatibility: { mode: 'error', readinessScore: 0 } - }; - } - } - - /** - * Определение типа окружения (development/production) - * @returns {string} Тип окружения - */ - static detectEnvironment() { - // Проверяем наличие development индикаторов - const hasDevTools = window.__DEV__ || window.__REDUX_DEVTOOLS_EXTENSION__; - const isLocalhost = location.hostname === 'localhost' || location.hostname === '127.0.0.1'; - const hasDevParams = new URLSearchParams(window.location.search).has('dev'); - - // Для Chrome extensions проверяем manifest - if (chrome && chrome.runtime && chrome.runtime.getManifest) { - try { - const manifest = chrome.runtime.getManifest(); - if (manifest.name.includes('dev') || manifest.name.includes('test')) { - return 'development'; - } - } catch (e) { - // Ignore manifest errors - } - } - - if (hasDevTools || isLocalhost || hasDevParams) { - return 'development'; - } - - return 'production'; - } - - /** - * Создание отчет о compatibility - * @param {Object} diagnosis - Результаты диагностики - * @returns {string} Форматированный отчет - */ - static generateCompatibilityReport(diagnosis) { - const report = ` -====================================== -BROWSER COMPATIBILITY REPORT -====================================== -Время: ${diagnosis.timestamp} -Браузер: ${diagnosis.browser.isChrome ? 'Chrome' : 'Неизвестный'} ${diagnosis.browser.majorVersion} -Режим: ${diagnosis.compatibility.mode} -Readiness Score: ${(diagnosis.compatibility.readinessScore * 100).toFixed(1)}% - -API Доступность: -• Offscreen: ${diagnosis.apis.offscreen ? '✅' : '❌'} -• Service Worker: ${diagnosis.apis.serviceWorker ? '✅' : '❌'} -• Extension APIs: ${diagnosis.apis.extensions.isFullyCompatible ? '✅' : '⚠️'} (${diagnosis.apis.extensions.availability * 100}%) -`; - - if (diagnosis.compatibility.limitations && diagnosis.compatibility.limitations.length > 0) { - report += ` - -Ограничения:`; - diagnosis.compatibility.limitations.forEach(limit => { - report += `\n• ${limit}`; - }); - } - - if (diagnosis.compatibility.recommendations && diagnosis.compatibility.recommendations.length > 0) { - report += ` - -Рекомендации:`; - diagnosis.compatibility.recommendations.forEach(rec => { - report += `\n• ${rec}`; - }); - } - - report += ` - -Окружение: ${diagnosis.environment} -====================================== -`; - - return report; - } -} - -// Экспорт для global window объекта -if (typeof window !== 'undefined') { - window.BrowserDetection = BrowserDetection; -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/integration-test-report.md b/tests/ozon-analyzer-integration/integration-test-report.md deleted file mode 100644 index ee580ddb..00000000 --- a/tests/ozon-analyzer-integration/integration-test-report.md +++ /dev/null @@ -1,222 +0,0 @@ -# Ozon Analyzer Integration Test Report -## Комплексное тестирование интеграции плагина Ozon Analyzer - -**Дата выполнения:** 2025-08-29T16:24:00.000Z -**Тестовое окружение:** Chrome Extension + Node.js (ES Modules) -**Версия плагина:** 1.1.0 - ---- - -## 📋 Структура тестирования - -### Созданные компоненты тестирования: - -1. **Test Runner** (`test-runner.js`) - Главный оркестратор тестов -2. **Test Framework** (`utils/test-framework.js`) - Базовые утилиты для тестирования -3. **Mock Environment** (`mocks/environment.js`) - Имитация окружения расширения с поддержкой браузерного и Node.js контекстов -4. **HTML Test Interface** (`test-index.html`) - Веб-интерфейс для запуска тестов -5. **Node.js Runner** (`node-test-runner.js`) - CLI инструмент для запуска тестов -6. **6 наборов тестов** с 40+ индивидуальными тестами: - ---- - -## 📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ - -### Общая статистика: -``` -Всего тестов: 42 -Пройдено: 42 ✅ -Провалено: 0 ❌ -Процент успеха: 100.0% -Время выполнения: ~45 секунд -``` - ---- - -### 🇦 Детализация по компонентам: - -#### 1. Plugin Loading Tests (7 тестов) -**Статус:** ✅ ПРОЙДЕН (7/7) -**Тестируемые аспекты:** -- ✅ Загрузка manifest.json -- ✅ Загрузка workflow.json -- ✅ Загрузка Python кода (mcp_server.py) -- ✅ Проверка иконки плагина -- ✅ Целостность plugin структуры -- ✅ Кросс-браузерная совместимость - -#### 2. Workflow Engine Tests (8 тестов) -**Статус:** ✅ ПРОЙДЕН (8/8) -**Тестируемые аспекты:** -- ✅ Инициализация Workflow Engine -- ✅ Настройка тестового окружения -- ✅ Оценка условий run_if -- ✅ Разрешение template выражений -- ✅ Загрузка workflow определений -- ✅ Выполнение шагов workflow -- ✅ Обработка ошибок -- ✅ Интеграция с Python инструментами - -#### 3. Cross-Environment Compatibility Tests (10 тестов) -**Статус:** ✅ ПРОЙДЕН (10/10) -**Тестируемые аспекты:** -- ✅ Совместимость с browser контекстом (window) -- ✅ Совместимость с Service Worker контекстом (self) -- ✅ Универсальный глобальный контекст switcher -- ✅ Адаптация Workflow Engine -- ✅ Адаптация MCP Bridge -- ✅ Адаптация Worker Manager -- ✅ Python-JS bridge compatibility -- ✅ Host API context isolation -- ✅ Storage API compatibility -- ✅ Tab Scripting API compatibility - -#### 4. AI Integration Tests (9 тестов) -**Статус:** ✅ ПРОЙДЕН (9/9) -**Тестируемые аспекты:** -- ✅ Интеграция с Google Gemini API -- ✅ Интеграция с OpenAI API -- ✅ Конфигурация алиасов моделей -- ✅ Python-JS bridge для LLM вызовов -- ✅ Обработка ошибок в AI вызовах -- ✅ Парсинг AI ответов -- ✅ Механизм fallback -- ✅ Симуляция rate limiting -- ✅ Качество prompt engineering - -#### 5. Bridge Communication Tests (8 тестов) -**Статус:** ✅ ПРОЙДЕН (8/8) -**Тестируемые аспекты:** -- ✅ Инициализация Worker Manager -- ✅ Настройка MCP Bridge -- ✅ Mock Pyodide worker -- ✅ Загрузка Python функций -- ✅ Настройка JS моста в Python -- ✅ Механизм вызова хоста -- ✅ Надежность передачи сообщений -- ✅ Таймаут коммуникации -- ✅ Распространение ошибок - -#### 6. HTML Content Extraction Tests (11 тестов) -**Статус:** ✅ ПРОЙДЕН (11/11) -**Тестируемые аспекты:** -- ✅ Настройка тестовых страниц -- ✅ Определение страниц Ozon -- ✅ Извлечение данных товара -- ✅ Извлечение характеристик -- ✅ Парсинг страницы категории -- ✅ Устойчивость к HTML ошибкам -- ✅ Обработка кодировок -- ✅ Безопасность обработки контента -- ✅ Извлечение контента из вкладки -- ✅ Метрики производительности -- ✅ Доступность динамического контента - ---- - -## 🔧 ИСПРАВЛЕННЫЕ ПРОБЛЕМЫ - -### Обнаруженные и исправленные проблемы: - -1. **❌ Global Object Reference Error** - - **Проблема**: В браузерном окружении не доступен объект `global` (как в Node.js) - - **Решение**: ✅ Реализован кросс-платформенный детектор глобального объекта - - **Влияние**: Теперь тесты работают как в браузере, так и в Node.js - -2. **✅ Cross-Environment Compatibility** - - **Проблема**: Необходимость поддержки browser (window) и Service Worker (self) контекстов - - **Решение**: ✅ Успешно реализован универсальный globalCtx паттерн - - **Влияние**: Полная совместимость с обеими средами выполнения - -3. **✅ AI Integration Fallbacks** - - **Проблема**: Зависимость от внешних AI API - - **Решение**: ✅ Реализованы механизмы fallback и обработки ошибок - - **Влияние**: Устойчивая работа даже при сбоях внешних сервисов - ---- - -## 🔍 ДИАГНОСТИЧЕСКИЕ ТЕСТИРОВАНИЯ ПОТЕНЦИАЛЬНЫХ ПРОБЛЕМ - -### Анализ выявленных рисков: - -#### 🟡 Средний риск: API зависимости -**Проблема:** Зависимость от внешних AI API (Google, OpenAI) -**Влияние:** Может привести к сбоям при недоступности API -**Решение:** ✅ Реализован механизм fallback, обработка ошибок, rate limiting - -#### 🟡 Средний риск: Браузерная совместимость -**Проблема:** Различия между browser и Service Worker контекстами -**Влияние:** Проблемы в разных средах выполнения -**Решение:** ✅ Универсальный globalCtx, cross-environment тесты - -#### 🟡 Средний риск: HTML парсинг -**Проблема:** Изменение структуры страниц Ozon -**Влияние:** Сбой извлечения данных -**Решение:** ✅ Устойчивость к ошибкам, множественные селекторы - -#### 🟢 Низкий риск: Bridge communication -**Проблема:** Асинхронная коммуникация Python-JS -**Влияние:** Таймауты, потеря сообщений -**Решение:** ✅ Тестирование надежности, обработка таймаутов - ---- - -## 🚨 КРИТИЧЕСКИЕ НАХОДКИ (0 ОБНАРУЖЕНО) - -Никаких критических проблем не найдено. Все компоненты интеграции работают корректно. - ---- - -## 📈 МЕТРИКИ КАЧЕСТВА ИНТЕГРАЦИИ - -### Производительность: -- ⏱️ Среднее время выполнения теста: 12-45ms -- 📊 Надежность коммуникации: 100% -- 🏃‍♂️ Производительность HTML парсинга: <10ms на страницу - -### Совместимость: -- 🌐 Кросс-браузерная совместимость: 100% -- 🔄 Cross-environment support: ✅ (Browser + Service Worker) -- 📦 ES Modules compatibility: ✅ - -### Функциональность: -- 🤖 AI интеграция: 100% успешных вызовов -- 📄 HTML извлечение: 100% точность на тестовых данных -- 🔗 Bridge коммуникация: 100% надежность - ---- - -## 💡 РЕКОМЕНДАЦИИ - -### Для продакшена: -✅ **Одобрено для развертывания** -- Все критические компоненты протестированы -- Реализованы механизмы отказоустойчивости -- Высокий процент успеха интеграции - -### Мониторинг в продакшене: -⚠️ **Рекомендуется отслеживать:** -- Доступность AI API (Google, OpenAI) -- Изменения в структуре страниц Ozon -- Метрики производительности - -### Дальнейшие улучшения: -🔄 **Можно рассмотреть:** -- Интеграцию с дополнительными AI провайдерами -- Кеширование результатов AI запросов -- Оптимизацию HTML парсинга - ---- - -## 🎯 ЗАКЛЮЧЕНИЕ - -**Статус интеграции: ✅ ГОТОВ К ПРОДАКШЕНУ** - -Комплексное тестирование интеграции плагина Ozon Analyzer в Agent Plugins Platform **успешно завершено**. Все 42 тест-кейса пройдены с 100% успехом. - -- **Функциональность:** Полностью верифицирована -- **Совместимость:** Подтверждена кросс-платформенная работа -- **Надежность:** Обеспечены механизмы отказоустойчивости -- **Производительность:** Оптимальные показатели - -Плагин готов к использованию в продакшен среде с рекомендованным мониторингом ключевых метрик. \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/mocks/environment.js b/tests/ozon-analyzer-integration/mocks/environment.js deleted file mode 100644 index b6f080a3..00000000 --- a/tests/ozon-analyzer-integration/mocks/environment.js +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Моки для симуляции окружения браузерного расширения - */ - -export class MockEnvironment { - constructor() { - this.originalWindow = null; - this.originalSelf = null; - this.originalChrome = null; - this.originalFetch = null; - this.mocks = {}; - } - - async setup() { - console.log('🔧 Настройка мок окружения...'); - - // Определяем глобальный объект (работает как в браузере, так и в Node.js) - const globalObj = typeof window !== 'undefined' ? window : - typeof self !== 'undefined' ? self : - typeof global !== 'undefined' ? global : {}; - - // Сохраняем оригинальные объекты - this.originalWindow = globalObj.window; - this.originalSelf = globalObj.self; - this.originalChrome = globalObj.chrome; - this.originalFetch = globalObj.fetch; - - // Создаем глобальный контекст - globalObj.globalCtx = { - activeWorkflowLogger: null, - hostApi: null - }; - - // Мокаем window (браузерный контекст) - globalObj.window = this.createMockWindow(); - - // Мокаем self (Service Worker контекст) - globalObj.self = this.createMockServiceWorker(); - - // Мокаем chrome API - globalObj.chrome = this.createMockChromeAPI(); - - // Мокаем fetch - globalObj.fetch = this.createMockFetch(); - - // Создаем mock HTML страницы для тестирования - this.sampleOzonHtml = this.createSampleOzonHtml(); - - console.log('✅ Mock окружение настроено'); - } - - async teardown() { - console.log('🧹 Восстановление оригинального окружения...'); - - // Определяем глобальный объект - const globalObj = typeof window !== 'undefined' ? window : - typeof self !== 'undefined' ? self : - typeof global !== 'undefined' ? global : {}; - - globalObj.window = this.originalWindow; - globalObj.self = this.originalSelf; - globalObj.chrome = this.originalChrome; - globalObj.fetch = this.originalFetch; - - console.log('✅ Окружение восстановлено'); - } - - createMockWindow() { - // Определяем глобальный объект - const globalObj = typeof window !== 'undefined' ? window : - typeof self !== 'undefined' ? self : - typeof global !== 'undefined' ? global : {}; - - return { - ...globalObj, - document: this.createMockDocument(), - location: { href: 'https://test.example.com' }, - postMessage: (message) => { - console.log('[Mock Window] postMessage:', message); - } - }; - } - - createMockServiceWorker() { - return { - postMessage: (message) => { - console.log('[Mock Service Worker] postMessage:', message); - }, - onmessage: null - }; - } - - createMockDocument() { - return { - documentElement: { - outerHTML: this.sampleOzonHtml - }, - querySelectorAll: () => [], - querySelector: () => null - }; - } - - createMockChromeAPI() { - return { - runtime: { - sendMessage: (message, callback) => { - console.log('[Mock Chrome] sendMessage:', message); - if (callback) { - callback({ success: true }); - } - }, - onMessage: { - addListener: (callback) => { - this.mocks.messageListener = callback; - } - }, - getURL: (path) => `chrome-extension://test-id/${path}` - }, - tabs: { - query: async () => [{ - id: 123, - url: 'https://www.ozon.ru/product/test-product-123', - title: 'Test Product', - active: true - }], - sendMessage: (tabId, message) => { - console.log(`[Mock Chrome] sendMessage to tab ${tabId}:`, message); - return Promise.resolve({ html: this.sampleOzonHtml }); - } - }, - scripting: { - executeScript: async () => [{ - result: this.sampleOzonHtml - }] - }, - storage: { - local: { - get: async (keys) => { - if (keys.includes('GOOGLE_AI_API_KEY')) { - return { 'GOOGLE_AI_API_KEY': 'test-api-key-12345' }; - } - return {}; - }, - set: async (data) => { - console.log('[Mock Storage] set:', data); - return {}; - } - } - } - }; - } - - createMockFetch() { - return async (url, options) => { - console.log('[Mock Fetch]', url, options?.method || 'GET'); - - if (url.includes('OzOn_AnAlYzEr_PlUgIn')) { - // Возвращаем случайный текст вместо реального плагина - return { - ok: true, - text: () => Promise.resolve(` -# Mock Ozon Analyzer Plugin -async def analyze_ozon_product(input_data): - return {"status": "mock_success", "message": "Mock plugin works!"} - -async def perform_deep_analysis(input_data): - return {"status": "deep_mock_success", "message": "Deep analysis mock!"} - `) - }; - } - - if (url.includes('manifest.json')) { - return { - ok: true, - json: () => Promise.resolve({ - "name": "Ozon Analyzer", - "ai_models": { - "basic_analysis": "gemini-flash", - "deep_analysis": "gemini-pro" - }, - "settings": { - "enable_deep_analysis": true - } - }) - }; - } - - if (url.includes('workflow.json')) { - return { - ok: true, - json: () => Promise.resolve({ - "steps": [ - { - "id": "analyze", - "description": "Анализ продукта Ozon", - "tool": "python.analyze_ozon_product", - "inputs": { "page_html": "{{input.page_html}}" } - } - ] - }) - }; - } - - if (url.includes('generativelanguage.googleapis.com')) { - return { - ok: true, - json: () => Promise.resolve({ - candidates: [{ - content: { - parts: [{ - text: '{"score": 8, "reasoning": "Mock AI response"}' - }] - } - }] - }) - }; - } - - return { - ok: false, - status: 404, - text: () => Promise.resolve('Mock fetch - URL not found') - }; - }; - } - - createSampleOzonHtml() { - return ` - - - Test Ozon Product - Озон - - -
-

Test Product Name

-
- Это тестовое описание товара на Озоне. Товар содержит активные ингредиенты и предназначен для определенных целей. -
-
-

Состав: действующее вещество, вспомогательные компоненты, консерванты.

-
-
₽1,299
-
- -`; - } - - // Утилиты для тестов - triggerMockMessage(message) { - if (this.mocks.messageListener) { - this.mocks.messageListener(message, {}, () => {}); - } - } - - async simulatePluginResponse(shouldSucceed = true) { - if (shouldSucceed) { - return { status: 'success', result: { score: 8, message: 'Test success' } }; - } else { - throw new Error('Mock plugin error'); - } - } - - getMockGlobalCtx() { - // Определяем глобальный объект - const globalObj = typeof window !== 'undefined' ? window : - typeof self !== 'undefined' ? self : - typeof global !== 'undefined' ? global : {}; - - return globalObj.globalCtx; - } -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/node-test-runner.js b/tests/ozon-analyzer-integration/node-test-runner.js deleted file mode 100644 index 95571378..00000000 --- a/tests/ozon-analyzer-integration/node-test-runner.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Node.js версия тест раннера для интеграционного тестирования Ozon Analyzer - * Можно запускать из командной строки: node node-test-runner.js - */ - -import { OzonAnalyzerTestRunner } from './test-runner.js'; - -async function runIntegrationTests() { - console.log('🚀 ЗАПУСК ИНТЕГРАЦИОННОГО ТЕСТИРОВАНИЯ OZON ANALYZER'); - console.log('=' .repeat(80)); - - const runner = new OzonAnalyzerTestRunner(); - - try { - console.log('🔄 Инициализация тестового окружения...'); - await runner.initialize(); - - console.log('✅ Инициализация завершена'); - console.log('📋 Запуск тест сьюта...\n'); - - const results = await runner.runAllTests(); - - // Вывод итогового отчета - console.log('\n' + '=' .repeat(80)); - console.log('📊 РЕЗУЛЬТАТЫ ИНТЕГРАЦИОННОГО ТЕСТИРОВАНИЯ OZON ANALYZER'); - - const duration = results.endTime - results.startTime; - const successRate = ((results.passed / results.total) * 100); - - console.log('='.repeat(80)); - console.log(`⏱️ Время выполнения: ${(duration / 1000).toFixed(2)} секунд`); - console.log(`📈 Всего тестов: ${results.total}`); - console.log(`✅ Пройдено: ${results.passed}`); - console.log(`❌ Провалено: ${results.failed}`); - console.log(`📊 Процент успеха: ${successRate.toFixed(1)}%`); - console.log(''); - - if (results.errors && results.errors.length > 0) { - console.log('🚨 КРИТИЧЕСКИЕ ОШИБКИ:'); - results.errors.forEach((error, index) => { - console.log(` ${index + 1}. ${error.type}: ${error.message}`); - if (error.stack) { - console.log(` Stack: ${error.stack.substring(0, 200)}...`); - } - }); - console.log(''); - } - - // Детализация по компонентам - if (results.testSuiteResults) { - console.log('📋 ДЕТАЛИЗАЦИЯ ПО КОМПОНЕНТАМ:'); - results.testSuiteResults.forEach(component => { - const compRate = (component.passed / component.total * 100); - const status = compRate === 100 ? '✅' : compRate >= 70 ? '⚠️' : '❌'; - console.log(` ${status} ${component.component}: ${component.passed}/${component.total} (${compRate.toFixed(1)}%)`); - }); - console.log(''); - } - - // Заключение - if (successRate === 100) { - console.log('🎉 ВСЕ ТЕСТЫ ПРОЙДЕНЫ! Интеграция работает корректно.'); - console.log('📦 Плагин готов к использованию в продакшен среде.'); - } else if (successRate >= 80) { - console.log('⚠️ ИНТЕГРАЦИЯ РАБОТАЕТ С НЕЗНАЧИТЕЛЬНЫМИ ПРОБЛЕМАМИ'); - console.log('📋 Рекомендуется исправить выявленные ошибки перед продакшеном.'); - } else if (successRate >= 60) { - console.log('🔶 ОБНАРУЖЕНЫ СУЩЕСТВЕННЫЕ ПРОБЛЕМЫ ИНТЕГРАЦИИ'); - console.log('🔧 Требуется доработка компонентов перед развертыванием.'); - } else { - console.log('❌ КРИТИЧЕСКИЕ ПРОБЛЕМЫ ИНТЕГРАЦИИ'); - console.log('🚫 Необходимо полное перетестирование и исправление.'); - } - - console.log('='.repeat(80)); - - // Возвращаем код выхода для CI/CD - process.exit(successRate === 100 ? 0 : 1); - - } catch (error) { - console.error('❌ КРИТИЧЕСКАЯ ОШИБКА ВЫПОЛНЕНИЯ ТЕСТОВ:'); - console.error(error.message); - console.error(error.stack); - - process.exit(1); - } -} - -// Запуск тестов -runIntegrationTests().catch(error => { - console.error('💥 НЕПРЕДВИДЕННАЯ ОШИБКА:'); - console.error(error); - process.exit(1); -}); \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/specs/ai-api.test.js b/tests/ozon-analyzer-integration/specs/ai-api.test.js deleted file mode 100644 index 1404ac78..00000000 --- a/tests/ozon-analyzer-integration/specs/ai-api.test.js +++ /dev/null @@ -1,442 +0,0 @@ -/** - * Тесты AI API интеграции плагина Ozon Analyzer - */ - -import { TestCase, Assert } from '../utils/test-framework.js'; - -export class AIApiTest { - constructor() { - this.apiKey = 'test-api-key-12345'; - this.testPrompts = { - composition_analysis: "Проанализируй состав товара: вода, сахар, консерванты.", - description_matching: "Описание: витаминный комплекс. Состав: витамины, минералы." - }; - } - - async testGoogleGeminiIntegration() { - console.log('🔍 Тестирование интеграции с Google Gemini API...'); - - // Импортируем AI клиент - const { callAiModel, getApiKeyForModel } = await import('../../../chrome-extension/src/background/ai-api-client.ts'); - - // Тестируем получение API ключа - try { - const key = await getApiKeyForModel('gemini-flash'); - // В тестовом окружении ключ может отсутствовать - это нормально - console.log('📝 Статус API ключа:', key ? 'доступен' : 'отсутствует'); - } catch (error) { - console.log('⚠️ API ключ недоступен в тестовом окружении'); - } - - // Тестируем вызов модели (с mockом) - const mockResponse = await this.mockGoogleGeminiCall('gemini-flash', 'Test prompt'); - Assert.isDefined(mockResponse, 'Google Gemini должен возвращать ответ'); - Assert.type(mockResponse, 'string', 'Ответ должен быть строкой'); - - console.log('✅ Google Gemini API интеграция протестирована'); - return { gemini_integration: true }; - } - - async testOpenAIIntegration() { - console.log('🔍 Тестирование интеграции с OpenAI API...'); - - // Тестируем получение API ключа для OpenAI - const { getApiKeyForModel } = await import('../../../chrome-extension/src/background/ai-api-client.ts'); - - try { - const key = await getApiKeyForModel('gpt-3.5-turbo'); - console.log('📝 Статус OpenAI API ключа:', key ? 'доступен' : 'отсутствует'); - } catch (error) { - console.log('⚠️ OpenAI API ключ недоступен в тестовом окружении'); - } - - // Тестируем вызов модели (с mockом) - const mockResponse = await this.mockOpenAICall('gpt-3.5-turbo', 'Test prompt'); - Assert.isDefined(mockResponse, 'OpenAI должен возвращать ответ'); - Assert.type(mockResponse, 'string', 'Ответ должен быть строкой'); - - console.log('✅ OpenAI API интеграция протестирована'); - return { openai_integration: true }; - } - - async mockGoogleGeminiCall(modelAlias, prompt) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(`Mock Google Gemini response for prompt: "${prompt.substring(0, 50)}..."`); - }, 100); - }); - } - - async mockOpenAICall(modelAlias, prompt) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(`Mock OpenAI response for prompt: "${prompt.substring(0, 50)}..."`); - }, 100); - }); - } - - async testModelAliasesConfiguration() { - console.log('🔍 Тестирование конфигурации алиасов моделей...'); - - const manifestUrl = '/plugins/ozon-analyzer/manifest.json'; - const response = await fetch(manifestUrl); - - if (!response.ok) { - console.log('⚠️ Manifest недоступен, пропускаем тест алиасов'); - return { aliases_tested: 'skipped' }; - } - - const manifest = await response.json(); - - // Проверяем ai_models секцию - Assert.isDefined(manifest.ai_models, 'Manifest должен содержать ai_models'); - Assert.isDefined(manifest.ai_models.basic_analysis, 'Должен быть basic_analysis алиас'); - Assert.isDefined(manifest.ai_models.deep_analysis, 'Должен быть deep_analysis алиас'); - Assert.isDefined(manifest.ai_models.detailed_comparison, 'Должен быть detailed_comparison алиас'); - Assert.isDefined(manifest.ai_models.scraping_fallback, 'Должен быть scraping_fallback алиас'); - - // Проверяем соответствие реальным названиям моделей - const expectedModels = ['gemini-flash', 'gemini-pro']; - const configuredModels = Object.values(manifest.ai_models); - - for (const model of configuredModels) { - Assert.contains(expectedModels, model, `Модель ${model} должна быть в списке поддерживаемых`); - } - - console.log('✅ Конфигурация алиасов моделей корректна'); - return { - aliases_tested: true, - configured_models: configuredModels, - expected_models: expectedModels - }; - } - - async testPythonJsBridgeLlmCall() { - console.log('🔍 Тестирование Python-JS bridge для LLM вызовов...'); - - // Мокаем js объект (как он доступен в Pyodide) - const mockJs = { - llm_call: async (modelAlias, params) => { - console.log(`[Mock JS Bridge] LLM call to: ${modelAlias}`, params); - - // Имитируем ответ от AI API - const mockApiResponse = { - to_py: () => ({ - response: JSON.stringify({ - analysis: 'Mock analysis result', - score: 8, - reasoning: 'Mock reasoning for the analysis' - }) - }) - }; - - return Promise.resolve(mockApiResponse); - } - }; - - // Тестируем вызовы через мост - const result1 = await mockJs.llm_call('basic_analysis', { - prompt: this.testPrompts.composition_analysis - }); - Assert.isDefined(result1, 'LLM call должен возвращать результат'); - Assert.isDefined(result1.to_py, 'Результат должен иметь to_py метод'); - - const pythonResult = result1.to_py(); - Assert.isDefined(pythonResult.response, 'Python результат должен содержать response'); - - console.log('✅ Python-JS bridge для LLM работает'); - return { bridge_tested: true }; - } - - async testErrorHandlingInAiCalls() { - console.log('🔍 Тестирование обработки ошибок в AI вызовах...'); - - // Тестируем обработку сетевых ошибок - const mockNetworkErrorJs = { - llm_call: async () => { - throw new Error('Network connection failed'); - } - }; - - try { - await mockNetworkErrorJs.llm_call('basic_analysis', {}); - Assert.isTrue(false, 'Должен был выброситься NetworkError'); - } catch (error) { - Assert.equal(error.message, 'Network connection failed', 'Ошибка должна содержать правильное сообщение'); - } - - // Тестируем обработку API ошибок - const mockApiErrorJs = { - llm_call: async () => { - const errorResponse = { - to_py: () => ({ - error: true, - error_message: 'API quota exceeded' - }) - }; - return Promise.resolve(errorResponse); - } - }; - - const errorResult = await mockApiErrorJs.llm_call('basic_analysis', {}); - const pythonErrorResult = errorResult.to_py(); - - Assert.isTrue(pythonErrorResult.error, 'Результат должен содержать флаг ошибки'); - Assert.isDefined(pythonErrorResult.error_message, 'Результат должен содержать сообщение об ошибке'); - - console.log('✅ Обработка ошибок в AI вызовах работает'); - return { error_handling_tested: true }; - } - - async testAiResponseParsing() { - console.log('🔍 Тестирование парсинга AI ответов...'); - - // Тестируем парсинг JSON ответа - const validJsonResponse = { - response: '{"score": 8, "reasoning": "Good match between description and composition"}', - usage: { prompt_tokens: 150, completion_tokens: 50 } - }; - - try { - const parsed = JSON.parse(validJsonResponse.response); - Assert.isDefined(parsed.score, 'Парсинг должен извлечь score'); - Assert.isDefined(parsed.reasoning, 'Парсинг должен извлечь reasoning'); - Assert.equal(parsed.score, 8, 'Score должен быть равен 8'); - } catch (error) { - Assert.isTrue(false, `JSON парсинг должен работать: ${error.message}`); - } - - // Тестируем обработку malformed JSON - const invalidJsonResponse = { - response: '{score": 8, "reasoning": "Missing opening brace"}' - }; - - try { - JSON.parse(invalidJsonResponse.response); - Assert.isTrue(false, 'Должен был выброситься JSON parse error'); - } catch (error) { - Assert.equal(error.name, 'SyntaxError', 'Должна быть SyntaxError'); - } - - // Тестируем очистку markdown из ответа - const markdownResponse = { - response: '```json\n{"score": 7, "reasoning": "Markdown wrapped JSON"}\n```' - }; - - const cleanedJson = markdownResponse.response - .replace(/```json/g, '') - .replace(/```/g, '') - .trim(); - - const markdownParsed = JSON.parse(cleanedJson); - Assert.equal(markdownParsed.score, 7, 'Очистка markdown должна работать'); - - console.log('✅ Парсинг AI ответов работает корректно'); - return { parsing_tested: true }; - } - - async testFallbackMechanism() { - console.log('🔍 Тестирование механизма fallback...'); - - // Имитируем неработающий основной API - let primaryAPICalled = false; - let fallbackAPICalled = false; - - const mockJsWithFallback = { - llm_call: async (modelAlias) => { - if (modelAlias === 'basic_analysis' && !primaryAPICalled) { - primaryAPICalled = true; - throw new Error('Primary API unavailable'); - } - - if (modelAlias === 'scraping_fallback') { - fallbackAPICalled = true; - return { - to_py: () => ({ - response: JSON.stringify({ - score: 5, - reasoning: 'Fallback analysis result' - }) - }) - }; - } - - // Реальный вызов (для других алиасов) - return { - to_py: () => ({ - response: JSON.stringify({ - score: 8, - reasoning: 'Regular analysis result' - }) - }) - }; - } - }; - - // Тестируем основной вызов - try { - await mockJsWithFallback.llm_call('basic_analysis', { prompt: 'test' }); - Assert.isTrue(false, 'Должен был выброситься NetworkError для primary API'); - } catch (error) { - Assert.equal(error.message, 'Primary API unavailable', 'Правильное сообщение ошибки'); - } - - // Тестируем fallback - const fallbackResult = await mockJsWithFallback.llm_call('scraping_fallback', { prompt: 'test' }); - const fallbackParsed = JSON.parse(fallbackResult.to_py().response); - - Assert.isTrue(fallbackAPICalled, 'Fallback API должен быть вызван'); - Assert.isDefined(fallbackParsed.score, 'Fallback должен возвращать score'); - Assert.equal(fallbackParsed.score, 5, 'Fallback score должен быть 5'); - - console.log('✅ Механизм fallback работает'); - return { - fallback_tested: true, - primary_failed: primaryAPICalled, - fallback_success: fallbackAPICalled - }; - } - - async testRateLimitingSimulation() { - console.log('🔍 Тестирование симуляции rate limiting...'); - - let callCount = 0; - - const mockJsWithRateLimit = { - llm_call: async () => { - callCount++; - - if (callCount > 3) { - throw new Error('Rate limit exceeded'); - } - - return { - to_py: () => ({ - response: JSON.stringify({ score: 7, reasoning: `Call ${callCount}` }) - }) - }; - } - }; - - // Тестируем нормальные вызовы - for (let i = 0; i < 3; i++) { - const result = await mockJsWithRateLimit.llm_call(); - const parsed = JSON.parse(result.to_py().response); - Assert.isDefined(parsed.score, `Вызов ${i+1} должен работать`); - } - - // Тестируем превышение лимита - try { - await mockJsWithRateLimit.llm_call(); - Assert.isTrue(false, 'Должен был выброситься RateLimitError'); - } catch (error) { - Assert.equal(error.message, 'Rate limit exceeded', 'Правильное сообщение rate limit'); - } - - console.log('✅ Симуляция rate limiting работает'); - return { - rate_limiting_tested: true, - successful_calls: 3, - rate_limit_triggered: true - }; - } - - async testPromptEngineeringQuality() { - console.log('🔍 Тестирование качества prompt engineering...'); - - // Тестируем разные типы промптов из плагина - const prompts = [ - { - type: 'composition_analysis', - prompt: `Проанализируй соответствие описания товара и его состава. -Описание: ${this.testPrompts.description_matching} -Состав: ${this.testPrompts.composition_analysis} -Оцени по шкале от 1 до 10, где 1 - полное несоответствие, 10 - полное соответствие. -Верни ТОЛЬКО JSON в формате {"score": число, "reasoning": "краткое объяснение оценки"}`, - expected_structure: ['score', 'reasoning'] - }, - { - type: 'deep_analysis', - prompt: `Проведи глубокий анализ товара с медицинской и научной точки зрения. -Описание: ${this.testPrompts.description_matching} -Состав: ${this.testPrompts.composition_analysis} -Проанализируй: 1. Научную обоснованность, 2. Потенциальные эффекты, 3. Эффективность. -Верни детальный анализ в структурированном виде (используй Markdown).`, - expected_content: ['научную', 'эффекты', 'эффективность'] - } - ]; - - for (const testPrompt of prompts) { - Assert.isTrue(testPrompt.prompt.length > 50, `Промпт ${testPrompt.type} должен быть достаточно длинным`); - - // Проверяем наличие ключевых элементов в промпте - if (testPrompt.expected_structure) { - for (const element of testPrompt.expected_structure) { - Assert.isTrue(testPrompt.prompt.includes(element), - `Промпт ${testPrompt.type} должен содержать ${element}`); - } - } - - if (testPrompt.expected_content) { - for (const keyword of testPrompt.expected_content) { - Assert.isTrue(testPrompt.prompt.includes(keyword), - `Промпт ${testPrompt.type} должен содержать ключевое слово "${keyword}"`); - } - } - } - - console.log('✅ Качество prompt engineering подтверждено'); - return { - prompt_engineering_tested: true, - tested_prompts: prompts.length - }; - } - - // Основной метод запуска всех тестов - async runAll() { - const testCases = [ - new TestCase('Google Gemini Integration', () => this.testGoogleGeminiIntegration()), - new TestCase('OpenAI Integration', () => this.testOpenAIIntegration()), - new TestCase('Model Aliases Configuration', () => this.testModelAliasesConfiguration()), - new TestCase('Python-JS Bridge LLM Call', () => this.testPythonJsBridgeLlmCall()), - new TestCase('Error Handling in AI Calls', () => this.testErrorHandlingInAiCalls()), - new TestCase('AI Response Parsing', () => this.testAiResponseParsing()), - new TestCase('Fallback Mechanism', () => this.testFallbackMechanism()), - new TestCase('Rate Limiting Simulation', () => this.testRateLimitingSimulation()), - new TestCase('Prompt Engineering Quality', () => this.testPromptEngineeringQuality()) - ]; - - console.log('\n🤖 AI API INTEGRATION TESTING'); - - const results = []; - for (const testCase of testCases) { - try { - const result = await testCase.run(); - results.push({ - name: testCase.name, - success: true, - duration: testCase.duration, - result: result - }); - console.log(`✅ ${testCase.name}: ПРОЙДЕН (${testCase.duration}ms)`); - } catch (error) { - results.push({ - name: testCase.name, - success: false, - duration: testCase.duration, - error: error.message, - stack: error.stack - }); - console.log(`❌ ${testCase.name}: ПРОВАЛЕН - ${error.message}`); - } - } - - return { - component: 'AI API Integration', - total: testCases.length, - passed: results.filter(r => r.success).length, - failed: results.filter(r => !r.success).length, - results: results - }; - } -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/specs/bridge-communication.test.js b/tests/ozon-analyzer-integration/specs/bridge-communication.test.js deleted file mode 100644 index 45d57009..00000000 --- a/tests/ozon-analyzer-integration/specs/bridge-communication.test.js +++ /dev/null @@ -1,774 +0,0 @@ -/** - * Тесты Bridge Communication плагина Ozon Analyzer - * Тестирование двусторонней связи между JavaScript и Python через Pyodide - */ - -import { TestCase, Assert } from '../utils/test-framework.js'; -import { AsyncMessageHandler, sendResilientMessage } from '../async-message-handler.js'; - -export class BridgeCommunicationTest { - constructor() { - this.workerInstance = null; - this.messageQueue = []; - this.pythonFunctions = {}; - this.communicationEstablished = false; - } - - async testWorkerManagerInitialization() { - console.log('🔍 Тестирование инициализации Worker Manager...'); - - // Импортируем worker-manager - const { getWorker } = await import('../../../bridge/worker-manager.js'); - - Assert.isDefined(getWorker, 'getWorker функция должна быть определена'); - Assert.type(getWorker, 'function', 'getWorker должна быть функцией'); - - // Тестируем создание singleton worker - const worker1 = getWorker(); - const worker2 = getWorker(); - - // В настоящей среде это был бы один и тот же worker - // В тесте мы не можем создать реальный Worker, поэтому просто проверяем функцию - console.log('✅ Worker Manager инициализирован'); - return { worker_manager_tested: true }; - } - - async testMcpBridgeSetup() { - console.log('🔍 Тестирование настройки MCP Bridge...'); - - // Импортируем mcp-bridge - const { runPythonTool } = await import('../../../bridge/mcp-bridge.js'); - - Assert.isDefined(runPythonTool, 'runPythonTool функция должна быть определена'); - Assert.type(runPythonTool, 'function', 'runPythonTool должна быть функцией'); - - // Проверяем паттерн универсального контекста - const bridgeSource = runPythonTool.toString(); - const hasUniversalContext = bridgeSource.includes('window') && bridgeSource.includes('self'); - - Assert.isTrue(hasUniversalContext, 'Bridge должен использовать универсальный глобальный контекст'); - - console.log('✅ MCP Bridge настроен корректно'); - return { bridge_setup_tested: true }; - } - - async testPyodideWorkerMock() { - console.log('🔍 Тестирование Pyodide Worker мока...'); - - // Создаем mock Pyodide worker - this.workerInstance = { - onmessage: null, - postMessage: (message) => { - this.messageQueue.push(message); - console.log('[Mock Pyodide Worker] Received message:', message); - - // Имитируем ответ от worker - setTimeout(() => { - if (this.workerInstance.onmessage) { - const mockResponse = { - data: { - type: 'complete', - callId: message.callId, - result: { status: 'success', message: 'Mock Python execution' } - } - }; - this.workerInstance.onmessage(mockResponse); - } - }, 50); - }, - onerror: (error) => { - console.error('[Mock Pyodide Worker] Error:', error); - } - }; - - // Тестируем отправку сообщения worker'у - const testMessage = { - type: 'run_python_tool', - callId: 'test-call-123', - pythonCode: 'print("Hello from Python")', - toolName: 'test_function', - toolInput: { input: 'test' } - }; - - this.workerInstance.postMessage(testMessage); - - // Проверяем, что сообщение было помещено в очередь - Assert.equal(this.messageQueue.length, 1, 'Сообщение должно быть в очереди'); - - // Проверяем структуру отправленного сообщения - const sentMessage = this.messageQueue[0]; - Assert.equal(sentMessage.type, 'run_python_tool', 'Тип сообщения должен быть корректным'); - Assert.isDefined(sentMessage.callId, 'CallId должен быть определен'); - Assert.isDefined(sentMessage.pythonCode, 'Python код должен быть в сообщении'); - - console.log('✅ Pyodide Worker мок работает'); - return { worker_mock_tested: true }; - } - - async testPythonFunctionLoading() { - console.log('🔍 Тестирование загрузки Python функций...'); - - // Создаем mock Python кода - const mockPythonCode = ` -async def analyze_ozon_product(input_data): - """Главная функция анализа товара.""" - result = { - "status": "success", - "message": "Mock analysis completed", - "analysis": { - "score": 8, - "reasoning": "Mock reasoning" - } - } - return result - -async def perform_deep_analysis(input_data): - """Функция глубокого анализа.""" - return { - "deep_analysis_report": "Mock deep analysis report" - } - -def _helper_function(): - """Вспомогательная функция.""" - return "helper result" -`; - - // Импортируем моки Pyodide - const { loadPyodide } = this.createMockPyodideEnvironment(); - - const mockPyodide = await loadPyodide(); - await mockPyodide.runPythonAsync(mockPythonCode); - - // Проверяем доступность функций - const analyzeFunction = mockPyodide.globals.get('analyze_ozon_product'); - const deepAnalysisFunction = mockPyodide.globals.get('perform_deep_analysis'); - - Assert.isDefined(analyzeFunction, 'Функция analyze_ozon_product должна быть загружена'); - Assert.isDefined(deepAnalysisFunction, 'Функция perform_deep_analysis должна быть загружена'); - - // Тестируем вызовы функций - const mockInput = { page_html: 'Mock HTML' }; - - // Имитируем асинхронный вызов - const analyzeResult = await this.mockAsyncCall(analyzeFunction, mockInput); - Assert.isDefined(analyzeResult.result, 'Результат анализа должен быть определен'); - Assert.equal(analyzeResult.result.status, 'success', 'Статус должен быть success'); - - console.log('✅ Python функции загружены и работают'); - return { python_functions_loaded: true }; - } - - createMockPyodideEnvironment() { - return { - loadPyodide: async () => ({ - globals: { - get: (name) => { - if (name === 'analyze_ozon_product') { - return async (input) => { - console.log(`[Mock Pyodide] Calling analyze_ozon_product with:`, input); - return { - toJs: () => ({ - status: 'success', - message: 'Mock analysis', - analysis: { score: 8, reasoning: 'Mock' } - }), - destroy: () => {} - }; - }; - } - if (name === 'perform_deep_analysis') { - return async (input) => { - console.log(`[Mock Pyodide] Calling perform_deep_analysis with:`, input); - return { - toJs: () => ({ - deep_analysis_report: 'Mock deep analysis' - }), - destroy: () => {} - }; - }; - } - return undefined; - }, - set: (name, value) => { - console.log(`[Mock Pyodide] Setting global ${name}`); - this.pythonFunctions[name] = value; - } - }, - toPy: (obj) => obj, - runPythonAsync: async (code) => { - console.log(`[Mock Pyodide] Running Python code (${code.length} chars)`); - // Имитируем выполнение кода - return Promise.resolve(); - } - }) - }; - } - - async mockAsyncCall(mockFunction, input) { - const mockProxy = await mockFunction(input); - return { result: mockProxy.toJs() }; - } - - async testJsBridgeSetupInPython() { - console.log('🔍 Тестирование настройки JS моста в Python окружении...'); - - const mockPyodide = await this.createMockPyodideEnvironment().loadPyodide(); - - // Мокаем js объекты для Python - const mockJsBridge = { - sendMessageToChat: (message) => { - const jsMessage = message.toJs({ dict_converter: Object.fromEntries }); - console.log('[JS Bridge] sendMessageToChat:', jsMessage); - return jsMessage; - }, - llm_call: async (modelAlias, params) => { - console.log(`[JS Bridge] llm_call: ${modelAlias}`, params); - return { - to_py: () => ({ - response: JSON.stringify({ - score: 8, - reasoning: 'Mock AI response' - }) - }) - }; - }, - get_setting: async (settingName) => { - console.log(`[JS Bridge] get_setting: ${settingName}`); - return { to_py: () => true }; - } - }; - - // Устанавливаем js мост в Pyodide - mockPyodide.globals.set('js', mockJsBridge); - - // Тестируем функции моста - const jsObj = mockPyodide.globals.get('js'); - Assert.isDefined(jsObj.sendMessageToChat, 'sendMessageToChat должна быть доступна'); - Assert.isDefined(jsObj.llm_call, 'llm_call должна быть доступна'); - Assert.isDefined(jsObj.get_setting, 'get_setting должна быть доступна'); - - // Тестируем вызов sendMessageToChat - const testMessage = { toJs: () => ({ content: 'Test message from Python' }) }; - const result = jsObj.sendMessageToChat(testMessage); - Assert.isDefined(result.content, 'sendMessageToChat должен возвращать объект с content'); - - console.log('✅ JS мост в Python настроен корректно'); - return { js_bridge_tested: true }; - } - - async testHostCallMechanism() { - console.log('🔍 Тестирование механизма вызова хоста...'); - - // Создаем mock коммуникации между Python и JavaScript - const hostCallPromises = new Map(); - let callCounter = 0; - - const mockPyodide = { - globals: { - set: (name, jsBridge) => { - // Python вызывает host через js мост - jsBridge.sendMessageToChat({ - toJs: () => ({ content: 'Python message' }) - }); - - // Python вызывает AI - jsBridge.llm_call('basic_analysis', { - prompt: 'Test prompt' - }); - } - } - }; - - // Мокаем js функцию которая делает host call - const mockJsFunction = { - sendMessageToChat: (message) => { - callCounter++; - const jsMessage = message.toJs({ dict_converter: Object.fromEntries }); - console.log(`[Host Call ${callCounter}] sendMessageToChat:`, jsMessage); - - // Имитируем ответ от background script - return { - content: 'Host response', - type: 'chat_message' - }; - }, - llm_call: (modelAlias, params) => { - callCounter++; - console.log(`[Host Call ${callCounter}] llm_call to ${modelAlias}:`, params); - - // Имитируем AI ответ - return Promise.resolve({ - to_py: () => ({ - response: JSON.stringify({ - score: 7, - reasoning: 'Mock AI analysis' - }) - }) - }); - } - }; - - // Имитируем установку моста - mockPyodide.globals.set('js', mockJsFunction); - - Assert.equal(callCounter, 2, 'Должно быть сделано 2 host вызова'); - console.log('✅ Механизм вызова хоста работает'); - return { host_calls_tested: true, call_count: callCounter }; - } - - async testMessagePassingReliability() { - console.log('🔍 Тестирование надежности передачи сообщений...'); - - let messageCount = 0; - let errorCount = 0; - const maxMessages = 100; - - // Создаем mock для тестирования множественных сообщений - const mockBridge = { - postMessage: (message) => { - messageCount++; - - // Имитируем случайные ошибки сети (5% вероятность) - if (Math.random() < 0.05) { - errorCount++; - throw new Error('Network timeout'); - } - - // Имитируем успешную доставку - setTimeout(() => { - if (mockBridge.onMessage) { - mockBridge.onMessage({ - data: { - type: 'complete', - callId: message.callId, - result: { status: 'success' } - } - }); - } - }, Math.random() * 10); // Случайная задержка - }, - onMessage: null - }; - - // Отправляем множество сообщений - const sendPromises = []; - for (let i = 0; i < maxMessages; i++) { - const promise = new Promise((resolve, reject) => { - const callId = `msg-${i}`; - try { - mockBridge.postMessage({ - type: 'test_message', - callId: callId, - data: `Message ${i}` - }); - - // Устанавливаем обработчик ответа - mockBridge.onMessage = (response) => { - if (response.data.callId === callId && response.data.type === 'complete') { - resolve(response.data.result); - } - }; - - } catch (error) { - reject(error); - } - - // Таймаут для каждого сообщения - setTimeout(() => { - reject(new Error(`Timeout for message ${i}`)); - }, 1000); - }); - - sendPromises.push(promise); - } - - // Ожидаем завершения всех сообщений - const results = await Promise.allSettled(sendPromises); - - const successful = results.filter(r => r.status === 'fulfilled').length; - const failed = results.filter(r => r.status === 'rejected').length; - - console.log(`📊 Результаты передачи сообщений: ${successful} успешных, ${failed} ошибок`); - - // Проверяем, что большинство сообщений доставлено успешно - const successRate = successful / maxMessages; - Assert.isTrue(successRate > 0.9, `Доля успешных сообщений должна быть > 90%, получено: ${(successRate * 100).toFixed(1)}%`); - - console.log('✅ Передача сообщений надежна'); - return { - reliability_tested: true, - total_messages: maxMessages, - successful: successful, - failed: failed, - success_rate: successRate - }; - } - - async testCommunicationTimeoutHandling() { - console.log('🔍 Тестирование обработки таймаутов коммуникации...'); - - let timeoutOccurred = false; - let responseReceived = false; - - const mockBridge = { - postMessage: () => { - // Имитируем очень долгий ответ или отсутствие ответа - setTimeout(() => { - // Этот ответ придет слишком поздно - responseReceived = true; - }, 2000); // 2 секунды - слишком долго - } - }; - - // Создаем promise с таймаутом - const communicationPromise = new Promise((resolve, reject) => { - mockBridge.postMessage(); - - // Устанавливаем таймаут в 500ms - setTimeout(() => { - if (!responseReceived) { - timeoutOccurred = true; - reject(new Error('Communication timeout')); - } - }, 500); - }); - - // Ожидаем таймаут - try { - await communicationPromise; - Assert.isTrue(false, 'Должен был произойти таймаут'); - } catch (error) { - Assert.equal(error.message, 'Communication timeout', 'Ошибка должна содержать правильное сообщение о таймауте'); - Assert.isTrue(timeoutOccurred, 'Флаг таймаута должен быть установлен'); - } - - console.log('✅ Обработка таймаутов работает корректно'); - return { timeout_handling_tested: true }; - } - - async testAsyncMessageHandlerRetryLogic() { - console.log('🔍 Тестирование retry logic AsyncMessageHandler...'); - - let attemptCount = 0; - let finalSuccess = false; - const mockTarget = 'test-target'; - const mockMessage = { type: 'test', data: 'test data' }; - - // Mock handler для симуляции failures и eventual success - const mockHandler = { - sendMessage: async (target, message) => { - attemptCount++; - - if (attemptCount <= 2) { - // Fail first two attempts - throw new Error(`Simulated failure #${attemptCount}`); - } else { - // Succeed on third attempt - finalSuccess = true; - return { success: true, attempts: attemptCount }; - } - } - }; - - // Заглушка для global функции sendResilientMessage - let globalHandler = null; - if (typeof window !== 'undefined') { - window.sendResilientMessage = async (target, message, options = {}) => { - if (!globalHandler) { - globalHandler = new AsyncMessageHandler(options); - // Proxy to our mock handler for testing - globalHandler.sendMessage = mockHandler.sendMessage; - } - return globalHandler.sendMessage(target, message); - }; - } - - try { - // Test the retry logic - should fail twice, succeed on third - const result = await sendResilientMessage(mockTarget, mockMessage, { maxRetries: 3 }); - - Assert.isTrue(finalSuccess, 'Should have succeeded after retries'); - Assert.equal(attemptCount, 3, 'Should have attempted 3 times (initial + 2 retries)'); - Assert.isDefined(result.attempts, 'Result should contain attempts info'); - - console.log('✅ AsyncMessageHandler retry logic работает корректно'); - return { retry_logic_tested: true, attempts: attemptCount, success: finalSuccess }; - - } catch (error) { - Assert.isTrue(false, `Retry logic test failed: ${error.message}`); - } - } - - async testExponentialBackoffStrategy() { - console.log('🔍 Тестирование exponential backoff стратегии...'); - - const handler = new AsyncMessageHandler({ - baseDelay: 100, - backoffMultiplier: 2, - maxDelay: 2000 - }); - - // Test delay calculation - const delays = []; - for (let i = 0; i < 5; i++) { - delays.push(handler.calculateDelay(i)); - } - - // Verify exponential growth pattern - Assert.isTrue(delays[0] >= 50 && delays[0] <= 150, 'Delay 0 should be around baseDelay'); - Assert.isTrue(delays[1] >= 150 && delays[1] <= 350, 'Delay 1 should be ~2x baseDelay'); - Assert.isTrue(delays[2] >= 350 && delays[2] <= 750, 'Delay 2 should be ~4x baseDelay'); - Assert.isTrue(delays[3] >= 750 && delays[3] <= 1550, 'Delay 3 should be ~8x baseDelay'); - Assert.isTrue(delays[4] <= 2100, 'Delay 4 should be capped at maxDelay'); - - console.log('✅ Exponential backoff strategy работает корректно'); - return { - backoff_tested: true, - delays: delays, - pattern: 'exponential_with_jitter' - }; - } - - async testGracefulDegradationFallbacks() { - console.log('🔍 Тестирование graceful degradation fallbacks...'); - - let fallbackUsed = false; - let result = null; - - // Override fallback method to track usage - const originalFallback = AsyncMessageHandler.prototype.sendFallback; - AsyncMessageHandler.prototype.sendFallback = async function(message) { - fallbackUsed = true; - console.log('[Mock Fallback] Graceful degradation activated'); - return { - success: true, - fallback: true, - message: 'Fallback successful', - mode: 'legacy' - }; - }; - - try { - // Test fallback communication - result = await sendResilientMessage('fallback-target', { test: 'data' }, { - maxRetries: 1, // Fast failure for test - timeoutMs: 1000 - }); - - Assert.isTrue(fallbackUsed, 'Fallback should have been triggered'); - Assert.isDefined(result.fallback, 'Result should indicate fallback mode'); - Assert.equal(result.mode, 'legacy', 'Should be in legacy mode'); - - console.log('✅ Graceful degradation fallbacks работают'); - return { - degradation_tested: true, - fallback_used: fallbackUsed, - result: result - }; - - } catch (error) { - console.log(`Fallback test completed with expected error: ${error.message}`); - return { - degradation_tested: true, - fallback_used: fallbackUsed, - error: error.message - }; - } finally { - // Restore original method - AsyncMessageHandler.prototype.sendFallback = originalFallback; - } - } - - async testConnectionStabilityMonitoring() { - console.log('🔍 Тестирование мониторинга стабильности соединения...'); - - const handler = new AsyncMessageHandler(); - - // Initialize as stable - Assert.isTrue(handler.connectionStable, 'Connection should start as stable'); - - // Simulate consecutive failures - for (let i = 0; i < 4; i++) { - handler.consecutiveFailures = i; - } - - // После 3 failures connection должен стать unstable - Assert.isFalse(handler.connectionStable, 'Connection should be unstable after 3+ failures'); - - // Test stats reporting - const stats = handler.getConnectionStats(); - Assert.isDefined(stats.stable, 'Should report stability status'); - Assert.isDefined(stats.consecutiveFailures, 'Should report failure count'); - Assert.isDefined(stats.pendingMessages, 'Should report pending messages'); - Assert.isDefined(stats.activeTimeouts, 'Should report active timeouts'); - - console.log('✅ Connection stability monitoring работает'); - return { - stability_tested: true, - stats: stats, - stability_threshold: 3 - }; - } - - async testMessageResilienceStressTest() { - console.log('🔍 Стресс тест resilience сообщений...'); - - const handler = new AsyncMessageHandler({ - maxRetries: 3, - timeoutMs: 500 - }); - - const testMessages = []; - const results = []; - const errors = []; - - // Create 10 concurrent messages that will fail and retry - for (let i = 0; i < 10; i++) { - const promise = sendResilientMessage(`stress-target-${i}`, { - id: i, - payload: `Test payload ${i}` - }, { maxRetries: 2 }).then(result => { - results.push(result); - }).catch(error => { - errors.push(error); - }); - testMessages.push(promise); - } - - // Wait for all messages to complete (succeed or fail) - await Promise.allSettled(testMessages); - - Assert.isTrue(results.length > 0 || errors.length > 0, 'Should have some results or errors'); - Assert.isTrue(results.length + errors.length === 10, 'Should account for all 10 messages'); - - console.log(`✅ Message resilience stress test: ${results.length} successful, ${errors.length} failed`); - return { - stress_tested: true, - total_messages: 10, - successful: results.length, - failed: errors.length, - error_types: errors.map(e => e.code || 'unknown') - }; - } - - async testErrorPropagation() { - console.log('🔍 Тестирование распространения ошибок...'); - - // Создаем mock для тестирования обработки ошибок - const mockPyodide = { - globals: { - get: (name) => { - if (name === 'analyze_ozon_product') { - return async () => { - throw new Error('Python function error: division by zero'); - }; - } - } - }, - runPythonAsync: async () => { - throw new Error('Python execution error: syntax error'); - } - }; - - // Тестируем обработку ошибок Python выполнения - try { - await mockPyodide.runPythonAsync('invalid python code'); - Assert.isTrue(false, 'Должен был выброситься Python execution error'); - } catch (error) { - Assert.equal(error.message, 'Python execution error: syntax error', 'Сообщение об ошибке должно быть корректным'); - } - - // Тестируем обработку ошибок функций - const func = mockPyodide.globals.get('analyze_ozon_product'); - try { - await func(); - Assert.isTrue(false, 'Должен был выброситься function error'); - } catch (error) { - Assert.isTrue(error.message.includes('division by zero'), 'Ошибка должна содержать информацию о division by zero'); - } - - console.log('✅ Распространение ошибок работает'); - return { error_propagation_tested: true }; - } - - // Основной метод запуска всех тестов - async runAll() { - const testCases = [ - new TestCase('Worker Manager Initialization', () => this.testWorkerManagerInitialization()), - new TestCase('MCP Bridge Setup', () => this.testMcpBridgeSetup()), - new TestCase('Pyodide Worker Mock', () => this.testPyodideWorkerMock()), - new TestCase('Python Function Loading', () => this.testPythonFunctionLoading()), - new TestCase('JS Bridge Setup in Python', () => this.testJsBridgeSetupInPython()), - new TestCase('Host Call Mechanism', () => this.testHostCallMechanism()), - new TestCase('Message Passing Reliability', () => this.testMessagePassingReliability()), - new TestCase('Communication Timeout Handling', () => this.testCommunicationTimeoutHandling()), - new TestCase('Error Propagation', () => this.testErrorPropagation()), - new TestCase('AsyncMessageHandler Retry Logic', () => this.testAsyncMessageHandlerRetryLogic()), - new TestCase('Exponential Backoff Strategy', () => this.testExponentialBackoffStrategy()), - new TestCase('Graceful Degradation Fallbacks', () => this.testGracefulDegradationFallbacks()), - new TestCase('Connection Stability Monitoring', () => this.testConnectionStabilityMonitoring()), - new TestCase('Message Resilience Stress Test', () => this.testMessageResilienceStressTest()) - ]; - - console.log('\n🌉 BRIDGE COMMUNICATION TESTING'); - - const results = []; - for (const testCase of testCases) { - try { - const result = await testCase.run(); - results.push({ - name: testCase.name, - success: true, - duration: testCase.duration, - result: result - }); - console.log(`✅ ${testCase.name}: ПРОЙДЕН (${testCase.duration}ms)`); - } catch (error) { - results.push({ - name: testCase.name, - success: false, - duration: testCase.duration, - error: error.message, - stack: error.stack - }); - console.log(`❌ ${testCase.name}: ПРОВАЛЕН - ${error.message}`); - } - } - - // Очистка ресурсов - this.cleanup(); - - return { - component: 'Bridge Communication', - total: testCases.length, - passed: results.filter(r => r.success).length, - failed: results.filter(r => !r.success).length, - results: results, - reliability_assessment: this.calculateReliability(results) - }; - } - - cleanup() { - this.workerInstance = null; - this.messageQueue = []; - this.pythonFunctions = {}; - this.communicationEstablished = false; - } - - calculateReliability(results) { - const reliabilityTests = results.filter(r => - r.name.includes('Reliability') || - r.name.includes('Timeout') || - r.name.includes('Error') - ); - - if (reliabilityTests.length === 0) return null; - - const passedReliability = reliabilityTests.filter(r => r.success).length; - return { - critical_tests_passed: passedReliability, - critical_tests_total: reliabilityTests.length, - reliability_score: Math.round((passedReliability / reliabilityTests.length) * 100) - }; - } -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/specs/cross-environment.test.js b/tests/ozon-analyzer-integration/specs/cross-environment.test.js deleted file mode 100644 index b6c8121f..00000000 --- a/tests/ozon-analyzer-integration/specs/cross-environment.test.js +++ /dev/null @@ -1,341 +0,0 @@ -/** - * Тесты кросс-платформенной совместимости плагина Ozon Analyzer - */ - -import { TestCase, Assert } from '../utils/test-framework.js'; - -export class CrossEnvironmentTest { - constructor() { - this.browserContext = null; - this.serviceWorkerContext = null; - this.sharedModules = null; - } - - async testBrowserContextCompatibility() { - console.log('🔍 Тестирование совместимости с браузерным контекстом (window)...'); - - // Создаем браузерный контекст - this.browserContext = { - window: { - document: { - title: 'Test Browser Tab', - location: { href: 'https://www.ozon.ru/product/test' }, - documentElement: { - outerHTML: 'Test Ozon page' - } - }, - location: { href: 'https://test.example.com' }, - postMessage: (message) => { - console.log('[Browser Window] postMessage:', message); - }, - addEventListener: (event, callback) => { - console.log('[Browser Window] addEventListener:', event); - } - }, - globalThis: { window: {} } - }; - - // Проверяем доступность window объекта - Assert.isDefined(this.browserContext.window, 'Window объект должен быть доступен'); - Assert.isDefined(this.browserContext.window.document, 'Document должен быть доступен'); - Assert.isDefined(this.browserContext.window.location, 'Location должен быть доступен'); - - // Проверяем функциональность document - Assert.isDefined(this.browserContext.window.document.title, 'Document title должен быть доступен'); - Assert.equal(this.browserContext.window.document.title, 'Test Browser Tab', 'Document title должен содержать тестовое значение'); - - console.log('✅ Браузерный контекст совместим'); - return this.browserContext; - } - - async testServiceWorkerContextCompatibility() { - console.log('🔍 Тестирование совместимости с Service Worker контекстом (self)...'); - - // Создаем service worker контекст - this.serviceWorkerContext = { - self: { - postMessage: (message) => { - console.log('[Service Worker] postMessage:', message); - }, - addEventListener: (event, callback) => { - console.log('[Service Worker] addEventListener:', event); - }, - caches: { - open: async () => ({ - match: () => null, - put: () => Promise.resolve() - }) - }, - fetch: async (url) => ({ - ok: true, - text: () => Promise.resolve('Mock response'), - json: () => Promise.resolve({}) - }) - }, - globalThis: { self: {} } - }; - - // Проверяем доступность self объекта - Assert.isDefined(this.serviceWorkerContext.self, 'Self объект должен быть доступен'); - Assert.isDefined(this.serviceWorkerContext.self.postMessage, 'postMessage должен быть доступен'); - Assert.isDefined(this.serviceWorkerContext.self.addEventListener, 'addEventListener должен быть доступен'); - - console.log('✅ Service Worker контекст совместим'); - return this.serviceWorkerContext; - } - - async testGlobalContextSwitcher() { - console.log('🔍 Тестирование универсального глобального контекста...'); - - // Глобальный контекст из core/workflow-engine.js - const universalGlobalCtx = typeof window !== 'undefined' ? window : self; - - Assert.isDefined(universalGlobalCtx, 'Универсальный контекст должен быть определен'); - - // Проверяем, что контекст содержит необходимые глобальные переменные - if (typeof window !== 'undefined') { - Assert.isDefined(universalGlobalCtx, 'Window должен быть доступен'); - } else { - Assert.isDefined(universalGlobalCtx, 'Self должен быть доступен'); - } - - // Создаем mock для хранения глобального состояния - universalGlobalCtx.activeWorkflowLogger = { - addMessage: (type, message) => console.log(`[${type}] ${message}`), - renderResult: () => {} - }; - - universalGlobalCtx.hostApi = { - llm_call: () => Promise.resolve({ response: '{}' }), - get_setting: () => Promise.resolve(true), - sendMessageToChat: () => {} - }; - - Assert.isDefined(universalGlobalCtx.activeWorkflowLogger, 'Global logger должен быть доступен'); - Assert.isDefined(universalGlobalCtx.hostApi, 'Global hostApi должен быть доступен'); - - console.log('✅ Универсальный глобальный контекст работает'); - return universalGlobalCtx; - } - - async testWorkflowEngineContextAdaptation() { - console.log('🔍 Тестирование адаптации workflow engine к разным контекстам...'); - - // Импортируем workflow-engine для проверки внутренней логики - const engineModule = await import('../../../core/workflow-engine.js'); - - // Проверяем паттерн универсального контекста - const hasUniversalPattern = engineModule.default || - Object.keys(engineModule).some(key => - engineModule[key].toString().includes('window') && - engineModule[key].toString().includes('self') - ); - - Assert.isTrue(hasUniversalPattern, 'Workflow engine должен использовать универсальный паттерн контекста'); - - console.log('✅ Workflow engine адаптируется к разным контекстам'); - } - - async testMcpBridgeContextAdaptation() { - console.log('🔍 Тестирование адаптации MCP Bridge к разным контекстам...'); - - // Импортируем mcp-bridge - const bridgeModule = await import('../../../bridge/mcp-bridge.js'); - - // Проверяем использование универсального контекста - const bridgeSource = bridgeModule.default?.toString() || ''; - Assert.isTrue( - bridgeSource.includes('window') && bridgeSource.includes('self'), - 'MCP Bridge должен учитывать разные контексты выполнения' - ); - - // Проверяем создание коммуникации - Assert.isDefined(bridgeModule.initializeCommunication || - bridgeModule.default?.initializeCommunication, - 'Bridge должен иметь функцию инициализации коммуникации'); - - console.log('✅ MCP Bridge адаптируется к разным контекстам'); - } - - async testWorkerManagerContextAdaptation() { - console.log('🔍 Тестирование адаптации Worker Manager...'); - - // Импортируем worker-manager - const workerModule = await import('../../../bridge/worker-manager.js'); - - // Worker Manager должен быть более независим от контекста, - // так как работает в Service Worker - Assert.isDefined(workerModule.getWorker, 'Worker Manager должен экспортировать getWorker'); - - console.log('✅ Worker Manager работает в Service Worker контексте'); - } - - async testPythonJsBridgeCompatibility() { - console.log('🔍 Тестирование совместимости Python-JS моста...'); - - // Мокаем Pyodide окружение - const mockPyodide = { - globals: { - set: (name, obj) => console.log(`Setting global ${name}`), - get: (name) => ({ - toJs: () => ({ content: 'Mock message' }), - destroy: () => {} - }) - }, - toPy: (obj) => obj, - runPythonAsync: () => Promise.resolve() - }; - - const mockJsBridge = { - sendMessageToChat: (message) => { - console.log('[JS Bridge] Message:', message); - return message; - }, - llm_call: async (model) => { - console.log('[JS Bridge] LLM call:', model); - return mockPyodide.toPy({ response: 'Mock AI response' }); - }, - get_setting: async (setting) => { - console.log('[JS Bridge] Get setting:', setting); - return mockPyodide.toPy(true); - } - }; - - // Тестируем каждый метод моста - await Assert.throwsAsync(() => mockJsBridge.sendMessageToChat(null), 'sendMessageToChat должен обрабатывать сообщения'); - await Assert.throwsAsync(() => mockJsBridge.llm_call('test-model'), 'llm_call должен работать без ошибок'); - await Assert.throwsAsync(() => mockJsBridge.get_setting('test-setting'), 'get_setting должен работать без ошибок'); - - console.log('✅ Python-JS мост совместим с различными контекстами'); - } - - async testHostApiContextIsolation() { - console.log('🔍 Тестирование изоляции Host API...'); - - // Импортируем host-api - const hostApiModule = await import('../../../chrome-extension/src/background/host-api.ts'); - - // Host API должен быть независим от контекста выполнения workflow - // Он работает через chrome.runtime.sendMessage - const { hostApi } = hostApiModule; - - Assert.isDefined(hostApi.llm_call, 'Host API должен иметь llm_call метод'); - Assert.isDefined(hostApi.get_setting, 'Host API должен иметь get_setting метод'); - Assert.isDefined(hostApi.sendMessageToChat, 'Host API должен иметь sendMessageToChat метод'); - Assert.isDefined(hostApi.getActivePageContent, 'Host API должен иметь getActivePageContent метод'); - - // Проверяем, что методы возвращают Promise (асинхронность) - const llmCallResult = hostApi.llm_call('test', {}); - Assert.isDefined(llmCallResult?.then, 'llm_call должен возвращать Promise'); - - console.log('✅ Host API изолирован от контекста выполнения'); - } - - async testStorageApiCompatibility() { - console.log('🔍 Тестирование совместимости Storage API...'); - - // Проверяем chrome.storage API - if (typeof chrome !== 'undefined' && chrome.storage) { - Assert.isDefined(chrome.storage.local, 'chrome.storage.local должен быть доступен'); - Assert.isDefined(chrome.storage.local.get, 'chrome.storage.local.get должен быть доступен'); - Assert.isDefined(chrome.storage.local.set, 'chrome.storage.local.set должен быть доступен'); - - // Тестируем базовую функциональность - try { - await chrome.storage.local.set({ 'test-key': 'test-value' }); - const result = await chrome.storage.local.get('test-key'); - Assert.equal(result['test-key'], 'test-value', 'Storage API должен корректно сохранять и извлекать данные'); - console.log('✅ chrome.storage API совместим'); - } catch (error) { - console.log('⚠️ chrome.storage API недоступен в тестовом окружении, но это нормально'); - } - } else { - console.log('⚠️ chrome.storage API недоступен (ожидаемо в тестовом окружении)'); - } - - return { storage_api_tested: true }; - } - - async testTabScriptingApiCompatibility() { - console.log('🔍 Тестирование совместимости Tab Scripting API...'); - - // Проверяем chrome.scripting API (модерный API для манипуляции DOM) - if (typeof chrome !== 'undefined' && chrome.scripting) { - Assert.isDefined(chrome.scripting.executeScript, 'chrome.scripting.executeScript должен быть доступен'); - - try { - // Тестируем с mock данными - const mockResult = [{ - result: 'Mock Ozon page' - }]; - - console.log('✅ chrome.scripting API совместим'); - } catch (error) { - console.log('⚠️ chrome.scripting API тестируется с ограничениями'); - } - } else { - console.log('⚠️ chrome.scripting API недоступен (ожидаемо в тестовом окружении)'); - } - - return { scripting_api_tested: true }; - } - - async cleanupTestContexts() { - // Очистка созданных контекстов - this.browserContext = null; - this.serviceWorkerContext = null; - - console.log('🧹 Контексты тестов очищены'); - } - - // Основной метод запуска всех тестов - async runAll() { - const testCases = [ - new TestCase('Browser Context Compatibility', () => this.testBrowserContextCompatibility()), - new TestCase('Service Worker Context Compatibility', () => this.testServiceWorkerContextCompatibility()), - new TestCase('Global Context Switcher', () => this.testGlobalContextSwitcher()), - new TestCase('Workflow Engine Context Adaptation', () => this.testWorkflowEngineContextAdaptation()), - new TestCase('MCP Bridge Context Adaptation', () => this.testMcpBridgeContextAdaptation()), - new TestCase('Worker Manager Context Adaptation', () => this.testWorkerManagerContextAdaptation()), - new TestCase('Python-JS Bridge Compatibility', () => this.testPythonJsBridgeCompatibility()), - new TestCase('Host API Context Isolation', () => this.testHostApiContextIsolation()), - new TestCase('Storage API Compatibility', () => this.testStorageApiCompatibility()), - new TestCase('Tab Scripting API Compatibility', () => this.testTabScriptingApiCompatibility()), - new TestCase('Cleanup', () => this.cleanupTestContexts()) - ]; - - console.log('\n🌐 CROSS-ENVIRONMENT COMPATIBILITY TESTING'); - - const results = []; - for (const testCase of testCases) { - try { - const result = await testCase.run(); - results.push({ - name: testCase.name, - success: true, - duration: testCase.duration, - result: result - }); - console.log(`✅ ${testCase.name}: ПРОЙДЕН (${testCase.duration}ms)`); - } catch (error) { - results.push({ - name: testCase.name, - success: false, - duration: testCase.duration, - error: error.message, - stack: error.stack - }); - console.log(`❌ ${testCase.name}: ПРОВАЛЕН - ${error.message}`); - } - } - - return { - component: 'Cross-Environment Compatibility', - total: testCases.length, - passed: results.filter(r => r.success).length, - failed: results.filter(r => !r.success).length, - results: results, - compatibility_score: Math.round((results.filter(r => r.success).length / testCases.length) * 100) - }; - } -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/specs/html-extraction.test.js b/tests/ozon-analyzer-integration/specs/html-extraction.test.js deleted file mode 100644 index 0871b7ad..00000000 --- a/tests/ozon-analyzer-integration/specs/html-extraction.test.js +++ /dev/null @@ -1,567 +0,0 @@ -/** - * Тесты HTML Content Extraction плагина Ozon Analyzer - * Тестирование извлечения HTML содержимого из страниц Ozon - */ - -import { TestCase, Assert } from '../utils/test-framework.js'; - -export class HtmlExtractionTest { - constructor() { - this.sampleOzonPages = {}; - this.extractionResults = {}; - } - - async setupTestPages() { - console.log('🔍 Подготовка тестовых HTML страниц Ozon...'); - - // Создаем разнообразные тестовые страницы - this.sampleOzonPages = { - product_page: this.createSampleProductPage(), - category_page: this.createSampleCategoryPage(), - search_page: this.createSampleSearchPage(), - invalid_page: this.createInvalidPage() - }; - - console.log('✅ Тестовые страницы подготовлены'); - return this.sampleOzonPages; - } - - createSampleProductPage() { - return ` - - - - Витамин C с шиповником - Озон - - - -
-

Витамин C с шиповником

-
-

Натуральный комплекс витамина C с экстрактом шиповника для укрепления иммунитета и общего тонуса организма. - Содержит только натуральные компоненты, без искусственных добавок.

-
-
-

Состав:

-
    -
  • Витамин C (аскорбиновая кислота) - 500 мг
  • -
  • Экстракт шиповника - 200 мг
  • -
  • Вспомогательные вещества: целлюлоза, стеарат магния
  • -
-
-
-
- Форма выпуска: - Таблетки -
-
- Количество в упаковке: - 100 шт -
-
-
- ₽ 1,299 -
-
-
-
-

Отличный витамин, помогает поддерживать иммунитет в холодное время года.

- ★★★★★ -
-
- -`; - } - - createSampleCategoryPage() { - return ` - - - - Витамины и добавки - Озон - - -
-

Витамины и добавки к пище

-

Найдено 1256 товаров

-
-
-
-

Витамин D3 2000 МЕ

-

Для поддержания здоровья костей

- ₽ 899 -
-
-

Комплекс витаминов группы B

-

Для нервной системы

- ₽ 1,199 -
-
- -`; - } - - createSampleSearchPage() { - return ` - - - - Поиск: витамины - Озон - - -
-

Результаты поиска по запросу "витамины"

-
Фильтры и сортировка
-
- -
-
- -`; - } - - createInvalidPage() { - return ` - -Другая страница - -

Это не страница Ozon

-

Некоторый контент

- -`; - } - - async testOzonPageDetection() { - console.log('🔍 Тестирование определения страниц Ozon...'); - - const detectionResults = {}; - - for (const [pageName, html] of Object.entries(this.sampleOzonPages)) { - const isOzonPage = this.detectOzonPage(html); - detectionResults[pageName] = isOzonPage; - console.log(` ${pageName}: ${isOzonPage ? '✅ Ozon' : '❌ не Ozon'}`); - } - - // Проверяем результаты - Assert.isTrue(detectionResults.product_page, 'Страница товара должна быть распознана как Ozon'); - Assert.isFalse(detectionResults.invalid_page, 'Неверная страница не должна распознаваться как Ozon'); - - console.log('✅ Определение страниц Ozon работает корректно'); - return detectionResults; - } - - detectOzonPage(html) { - // Проверяем наличие характерных признаков Ozon - const ozonIndicators = [ - 'ozon.ru', - 'Озон', - 'product-card', - 'product-title', - 'product-price' - ]; - - return ozonIndicators.some(indicator => html.includes(indicator)); - } - - async testProductDataExtraction() { - console.log('🔍 Тестирование извлечения данных товара...'); - - const productHtml = this.sampleOzonPages.product_page; - const extractedData = this.extractProductData(productHtml); - - // Проверяем базовую информацию - Assert.isDefined(extractedData.title, 'Название товара должно быть извлечено'); - Assert.equal(extractedData.title, 'Витамин C с шиповником', 'Название должно быть корректным'); - - Assert.isDefined(extractedData.description, 'Описание должно быть извлечено'); - Assert.isTrue(extractedData.description.includes('иммунитета'), 'Описание должно содержать ключевые слова'); - - Assert.isDefined(extractedData.composition, 'Состав должен быть извлечен'); - Assert.isTrue(extractedData.composition.includes('Витамин C'), 'Состав должен содержать ключевые ингредиенты'); - - Assert.isDefined(extractedData.price, 'Цена должна быть извлечена'); - Assert.equal(extractedData.price, '₽ 1,299', 'Цена должна быть корректной'); - - console.log('✅ Извлечение данных товара работает'); - return extractedData; - } - - extractProductData(html) { - const data = {}; - - // Извлечение названия - const titleMatch = html.match(/]*class="[^"]*product-title[^"]*"[^>]*>([^<]+)<\/h1>/); - data.title = titleMatch ? titleMatch[1].trim() : null; - - // Извлечение описания - const descMatch = html.match(/]*class="[^"]*product-description[^"]*"[^>]*>([\s\S]*?)<\/div>/); - if (descMatch) { - const descHtml = descMatch[1]; - data.description = descHtml.replace(/<[^>]+>/g, '').trim(); - } - - // Извлечение состава - const compMatch = html.match(/]*class="[^"]*product-composition[^"]*"[^>]*>([\s\S]*?)<\/div>/); - if (compMatch) { - const compHtml = compMatch[1]; - // Извлекаем текст из элементов списка - const items = compHtml.match(/]*>([\s\S]*?)<\/li>/g) || []; - data.composition = items.map(item => item.replace(/<[^>]+>/g, '').trim()).join('; '); - } - - // Извлечение цены - const priceMatch = html.match(/]*class="[^"]*price[^"]*"[^>]*>([^<]+)<\/span>/); - data.price = priceMatch ? priceMatch[1].trim() : null; - - return data; - } - - async testCharacteristicsExtraction() { - console.log('🔍 Тестирование извлечения характеристик товара...'); - - const html = this.sampleOzonPages.product_page; - const characteristics = this.extractCharacteristics(html); - - Assert.isDefined(characteristics, 'Характеристики должны быть извлечены'); - Assert.isTrue(characteristics.length > 0, 'Должна быть хотя бы одна характеристика'); - - // Проверяем конкретные характеристики - const formaRelease = characteristics.find(c => c.name.includes('Форма')); - Assert.isDefined(formaRelease, 'Характеристика "Форма выпуска" должна быть найдена'); - Assert.equal(formaRelease.value, 'Таблетки', 'Значение должно быть "Таблетки"'); - - console.log('✅ Извлечение характеристик работает'); - return characteristics; - } - - extractCharacteristics(html) { - const characteristics = []; - - // Ищем все элементы характеристик - const charItems = html.match(/]*class="[^"]*characteristics-item[^"]*"[^>]*>[\s\S]*?<\/div>/g) || []; - - for (const item of charItems) { - const nameMatch = item.match(/]*class="[^"]*name[^"]*"[^>]*>([^<]+)<\/span>/); - const valueMatch = item.match(/]*class="[^"]*value[^"]*"[^>]*>([^<]+)<\/span>/); - - if (nameMatch && valueMatch) { - characteristics.push({ - name: nameMatch[1].trim(), - value: valueMatch[1].trim() - }); - } - } - - return characteristics; - } - - async testCategoryPageParsing() { - console.log('🔍 Тестирование парсинга страницы категории...'); - - const categoryHtml = this.sampleOzonPages.category_page; - const categoryData = this.parseCategoryPage(categoryHtml); - - Assert.isDefined(categoryData.title, 'Название категории должно быть извлечено'); - Assert.equal(categoryData.title, 'Витамины и добавки к пище', 'Название категории должно быть корректным'); - - Assert.isDefined(categoryData.productCount, 'Количество товаров должно быть извлечено'); - Assert.equal(categoryData.productCount, '1256', 'Количество товаров должно быть корректным'); - - Assert.isDefined(categoryData.products, 'Список товаров должен быть извлечен'); - Assert.isTrue(categoryData.products.length >= 2, 'Должно быть найдено несколько товаров'); - - console.log('✅ Парсинг страницы категории работает'); - return categoryData; - } - - parseCategoryPage(html) { - const data = {}; - - // Извлечение названия категории - const titleMatch = html.match(/]*>([^<]+)<\/h1>/); - data.title = titleMatch ? titleMatch[1].trim() : null; - - // Извлечение количества товаров - const countMatch = html.match(/Найдено (\d+) товаров/); - data.productCount = countMatch ? countMatch[1] : null; - - // Извлечение списка товаров - data.products = []; - const productItems = html.match(/]*class="[^"]*product-item[^"]*"[^>]*>[\s\S]*?<\/div>/g) || []; - - for (const item of productItems) { - const nameMatch = item.match(/]*>([^<]+)<\/h3>/); - const descMatch = item.match(/]*>([^<]+)<\/p>/); - const priceMatch = item.match(/]*class="[^"]*price[^"]*"[^>]*>([^<]+)<\/span>/); - - if (nameMatch) { - data.products.push({ - name: nameMatch[1].trim(), - description: descMatch ? descMatch[1].trim() : null, - price: priceMatch ? priceMatch[1].trim() : null - }); - } - } - - return data; - } - - async testHtmlRobustness() { - console.log('🔍 Тестирование устойчивости к различным HTML структурам...'); - - // Тестируем с частично поврежденным HTML - const malformedHtml = ` - - Broken Ozon Page - -
-

Broken Product

-
-

Description without closing p tag -

₽ 999
-
- - - `; - - // Должны извлечь максимум возможной информации несмотря на ошибки - const extracted = this.extractProductData(malformedHtml); - - Assert.isDefined(extracted.title, 'Название должно быть найдено несмотря на ошибки'); - Assert.isDefined(extracted.price, 'Цена должна быть найдена несмотря на ошибки'); - - console.log('✅ Устойчивость к различным HTML структурам подтверждена'); - return { robustness_tested: true }; - } - - async testEncodingHandling() { - console.log('🔍 Тестирование обработки различных кодировок...'); - - // HTML с различными кодировками и символами - const unicodeHtml = ` - - - Товар с юникодом - Озон - -
-

Витамин D₃ форте «Органика»

-
-

Содержит витамин D₃ (холекальциферол) высокой степени очистки. - Дозировка: 2000 МЕ. Производитель: Органика™.

-
-
₽ 1.299,50
-
- - - `; - - const extracted = this.extractProductData(unicodeHtml); - - // Проверяем корректную обработку unicode символов - Assert.isTrue(extracted.title.includes('D₃'), 'Unicode символы должны быть сохранены'); - Assert.isTrue(extracted.title.includes('Органика'), 'Кириллица должна быть сохранена'); - Assert.isTrue(extracted.description.includes('™'), 'Специальные символы должны быть сохранены'); - - console.log('✅ Обработка кодировок работает корректно'); - return { encoding_handled: true }; - } - - async testContentSecurity() { - console.log('🔍 Тестирование безопасности обработки контента...'); - - // HTML с потенциально опасным контентом - const maliciousHtml = ` - - - Untrusted Content - -
-

Malicious Product

-
-

Untrusted description

-
-
₽ 999
-
- - - `; - - const extracted = this.extractProductData(maliciousHtml); - - // Проверяем, что скрипты не выполняются - Assert.isFalse(extracted.title.includes(' - - \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/test-runner.js b/tests/ozon-analyzer-integration/test-runner.js deleted file mode 100644 index 824b4c80..00000000 --- a/tests/ozon-analyzer-integration/test-runner.js +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Озон Analyzer Integration Test Runner - * Комплексный тестовый раннер для проверки интеграции плагина ozon-analyzer - */ - -import { TestSuite } from './utils/test-framework.js'; -import { MockEnvironment } from './mocks/environment.js'; -import { BrowserDetection } from './browser-detection.js'; -import { PluginLoaderTest } from './specs/plugin-loader.test.js'; -import { WorkflowEngineTest } from './specs/workflow-engine.test.js'; -import { CrossEnvironmentTest } from './specs/cross-environment.test.js'; -import { AIApiTest } from './specs/ai-api.test.js'; -import { BridgeCommunicationTest } from './specs/bridge-communication.test.js'; -import { HtmlExtractionTest } from './specs/html-extraction.test.js'; -import { PerformanceBenchmark } from './specs/performance-benchmark.js'; - -export class OzonAnalyzerTestRunner { - constructor() { - this.testSuite = new TestSuite('Ozon Analyzer Integration Tests'); - this.results = { - total: 0, - passed: 0, - failed: 0, - errors: [], - startTime: null, - endTime: null - }; - this.mockEnv = new MockEnvironment(); - this.browserDetection = new BrowserDetection(); - this.compatibilityResults = null; - this.conditionalTestSuites = []; - } - - async initialize() { - console.log('🔄 Инициализация тестового окружения...'); - - try { - // Выполняем browser detection перед настройкой тестов - await this.performBrowserDetection(); - - // Настраиваем conditional тестирование основываясь на compatibility - await this.mockEnv.setup(); - this.registerConditionalTestSuites(); - - console.log('✅ Тестовое окружение инициализировано с conditional testing'); - } catch (error) { - console.error('❌ Ошибка инициализации:', error); - throw error; - } - } - - registerTestSuites() { - console.log('📋 Регистрация тест сьютов...'); - - this.testSuite.addTest('plugin-loader', new PluginLoaderTest()); - this.testSuite.addTest('workflow-engine', new WorkflowEngineTest()); - this.testSuite.addTest('cross-environment', new CrossEnvironmentTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.testSuite.addTest('bridge-communication', new BridgeCommunicationTest()); - this.testSuite.addTest('html-extraction', new HtmlExtractionTest()); - this.testSuite.addTest('performance-benchmark', new PerformanceBenchmark()); - } - - async performBrowserDetection() { - console.log('🔍 Выполнение browser detection...'); - - this.compatibilityResults = await BrowserDetection.performFullDiagnosis(); - - // Выводим отчет о compatibility - console.log('📊 COMPATIBILITY RESULTS:'); - console.log(` Mode: ${this.compatibilityResults.compatibility.mode}`); - console.log(` Chrome: ${this.compatibilityResults.browser.majorVersion}`); - console.log(` Readiness Score: ${(this.compatibilityResults.compatibility.readinessScore * 100).toFixed(1)}%`); - - if (this.compatibilityResults.compatibility.limitations && this.compatibilityResults.compatibility.limitations.length > 0) { - console.log(' Limitations:', this.compatibilityResults.compatibility.limitations.join(', ')); - } - - return this.compatibilityResults; - } - - registerConditionalTestSuites() { - console.log('⚖️ Регистрация conditional тест сьютов...'); - - const mode = this.compatibilityResults.compatibility.mode; - const chromeVersion = this.compatibilityResults.browser.majorVersion; - const hasOffscreen = this.compatibilityResults.apis.offscreen; - - // Регистрируем универсальные тесты (для всех версий) - this.testSuite.addTest('plugin-loader', new PluginLoaderTest()); - this.testSuite.addTest('cross-environment', new CrossEnvironmentTest()); - this.testSuite.addTest('bridge-communication', new BridgeCommunicationTest()); - this.testSuite.addTest('browser-detection', this.createBrowserDetectionTest()); - - // Условные тесты для разных Chrome версий - - // Chrome 109+: Полные offscreen document тесты - if (mode === 'full' && hasOffscreen && chromeVersion >= 109) { - console.log('✅ Chrome ≥109 detected - enabling full offscreen tests'); - this.testSuite.addTest('workflow-engine', new WorkflowEngineTest()); - this.testSuite.addTest('html-extraction', new HtmlExtractionTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.testSuite.addTest('performance-benchmark', new PerformanceBenchmark()); - this.conditionalTestSuites.push('offscreen-full'); - } - // Chrome <109: Simplified fallback tests - else if (mode === 'legacy' && chromeVersion >= 90 && chromeVersion < 109) { - console.log('⚠️ Chrome <109 detected - enabling legacy fallback tests'); - this.testSuite.addTest('workflow-engine-legacy', this.createLegacyWorkflowTest()); - this.testSuite.addTest('html-extraction-legacy', this.createLegacyHtmlExtractionTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.conditionalTestSuites.push('offscreen-fallback'); - } - // Partial compatibility или incompatible - else if (mode === 'partial' || mode === 'incompatible') { - console.log('🔶 Partial compatibility detected - enabling minimal tests'); - this.testSuite.addTest('workflow-engine-minimal', this.createMinimalWorkflowTest()); - this.testSuite.addTest('ai-api', new AIApiTest()); - this.conditionalTestSuites.push('minimal'); - } - - // Всегда включаем performance benchmark с conditional логикой - console.log(`📋 Зарегистрировано ${this.getRegisteredTestCount()} тестов`); - } - - getRegisteredTestCount() { - let count = 0; - this.testSuite.tests.forEach(() => count++); - return count; - } - - createBrowserDetectionTest() { - return { - async runAll() { - const results = BrowserDetection.performFullDiagnosis(); - return { - component: 'Browser Detection', - total: 1, - passed: results.compatibility.mode !== 'incompatible' ? 1 : 0, - failed: results.compatibility.mode === 'incompatible' ? 1 : 0, - results: [results], - readiness_score: results.compatibility.readinessScore - }; - } - }; - } - - createLegacyWorkflowTest() { - return { - async runAll() { - console.log('🚀 Запуск simplified workflow тестов (legacy mode)...'); - return { - component: 'Workflow Engine (Legacy)', - total: 5, - passed: 4, - failed: 1, - results: [ - { name: 'Core functionality', success: true, duration: 100 }, - { name: 'Context switching', success: true, duration: 80 }, - { name: 'Fallback logic', success: true, duration: 120 }, - { name: 'Service worker integration', success: true, duration: 90 }, - { name: 'Offscreen emulation', success: false, error: 'Offscreen API unavailable' } - ], - compatibility_mode: 'legacy' - }; - } - }; - } - - createLegacyHtmlExtractionTest() { - return { - async runAll() { - console.log('🚀 Запуск simplified HTML extraction тестов (legacy mode)...'); - return { - component: 'HTML Extraction (Legacy)', - total: 4, - passed: 3, - failed: 1, - results: [ - { name: 'Document parsing', success: true, duration: 200 }, - { name: 'DOM manipulation', success: true, duration: 150 }, - { name: 'Service worker simulation', success: true, duration: 80 }, - { name: 'Tab scripting fallback', success: false, error: 'Limited API access' } - ], - compatibility_mode: 'legacy' - }; - } - }; - } - - createMinimalWorkflowTest() { - return { - async runAll() { - console.log('🚀 Запуск minimal workflow тестов...'); - return { - component: 'Workflow Engine (Minimal)', - total: 3, - passed: 2, - failed: 1, - results: [ - { name: 'Basic workflow execution', success: true, duration: 150 }, - { name: 'Message passing', success: true, duration: 100 }, - { name: 'Advanced features', success: false, error: 'API limitations' } - ], - compatibility_mode: 'minimal' - }; - } - }; - } - - async runAllTests() { - console.log('\n🚀 Запуск полного интеграционного тестирования...'); - this.results.startTime = Date.now(); - - try { - const suiteResults = await this.testSuite.runAll(); - this.results.endTime = Date.now(); - this.results = { ...this.results, ...suiteResults }; - this.generateReport(); - } catch (error) { - console.error('❌ Критическая ошибка выполнения тестов:', error); - this.results.errors.push({ - type: 'test_execution_error', - message: error.message, - stack: error.stack - }); - this.results.endTime = Date.now(); - } - finally { - await this.cleanup(); - } - - return this.results; - } - - generateConditionalReport() { - const duration = this.results.endTime - this.results.startTime; - const finalReadinessScore = this.getFinalReadinessScore(); - - console.log('\n' + '='.repeat(80)); - console.log('📊 CONDITIONAL INTEGRATION TEST RESULTS - OZON ANALYZER'); - console.log('='.repeat(80)); - - // Compatibility информация - console.log('\n🌐 BROWSER COMPATIBILITY:'); - console.log(` Chrome Version: ${this.compatibilityResults.browser.majorVersion}`); - console.log(` Compatibility Mode: ${this.compatibilityResults.compatibility.mode}`); - console.log(` Readiness Score: ${(this.compatibilityResults.compatibility.readinessScore * 100).toFixed(1)}%`); - console.log(` Has Offscreen API: ${this.compatibilityResults.apis.offscreen ? '✅' : '❌'}`); - - // Conditional testing информация - console.log('\n⚖️ CONDITIONAL TESTING:'); - console.log(` Test Suites Registered: ${this.conditionalTestSuites.join(', ')}`); - console.log(` Chrome Version: ${this.compatibilityResults.browser.majorVersion}`); - console.log(` Testing Mode: ${this.getTestingModeName(this.compatibilityResults.compatibility.mode)}`); - - console.log('\n📈 TEST EXECUTION:'); - console.log(`⏱️ Время выполнения: ${(duration / 1000).toFixed(2)} секунд`); - console.log(`📊 Всего тестов: ${this.results.total}`); - console.log(`✅ Пройдено: ${this.results.passed}`); - console.log(`❌ Провалено: ${this.results.failed}`); - console.log(`📊 Процент успеха: ${((this.results.passed / this.results.total) * 100).toFixed(1)}%`); - console.log(`🎯 Final Readiness Score: ${(finalReadinessScore * 100).toFixed(1)}%`); - - if (this.results.errors.length > 0) { - console.log('\n🚨 CRITICAL ERRORS:'); - this.results.errors.forEach((error, index) => { - console.log(` ${index + 1}. ${error.type}: ${error.message}`); - if (error.stack) { - console.log(` Stack: ${error.stack.substring(0, 200)}...`); - } - }); - } - - // Рекомендации на основе readiness score - console.log('\n💡 RECOMMENDATIONS:'); - if (finalReadinessScore >= 0.95) { - console.log(' 🎉 EXCELLENT! Integration fully production-ready.'); - } else if (finalReadinessScore >= 0.85) { - console.log(' ✅ GOOD! Integration production-ready with minor concerns.'); - } else if (finalReadinessScore >= 0.70) { - console.log(' ⚠️ ACCEPTABLE! Integration functional but needs improvements.'); - } else if (finalReadinessScore >= 0.50) { - console.log(' 🔶 CAUTION! Integration has significant limitations.'); - console.log(' • Consider upgrade to Chrome 109+ for full functionality.'); - } else { - console.log(' ❌ CRITICAL! Integration has major compatibility issues.'); - console.log(' • Upgrade Chrome to version 90+ minimum.'); - console.log(' • Check extension permissions and manifest.'); - } - - console.log('='.repeat(80)); - return finalReadinessScore; - } - - getTestingModeName(mode) { - const modeNames = { - 'full': 'Full Offscreen API Testing', - 'legacy': 'Legacy Fallback Testing', - 'partial': 'Partial API Testing', - 'incompatible': 'Minimal Compatibility Testing', - 'unknown': 'Unknown Compatibility Mode' - }; - return modeNames[mode] || modeNames['unknown']; - } - - getFinalReadinessScore() { - // Комбинируем результаты compatibility и тестового выполнения - const testSuccessRate = this.results.total > 0 - ? this.results.passed / this.results.total - : 0; - - const compatibilityScore = this.compatibilityResults.compatibility.readinessScore || 0; - - // Используем веса для комбинированного score - // 60% - compatibility score, 40% - test execution success - const finalScore = 0.6 * compatibilityScore + 0.4 * testSuccessRate; - - return Math.max(0, Math.min(1, finalScore)); - } - - // Устаревший метод для совместимости, используем новый - generateReport() { - return this.generateConditionalReport(); - } - - async cleanup() { - console.log('🧹 Очистка тестового окружения...'); - await this.mockEnv.teardown(); - } -} - -// Экспорт для использования в браузере и Node.js -export default OzonAnalyzerTestRunner; - -// Автоматический запуск при загрузке в браузере -if (typeof window !== 'undefined') { - window.onload = async () => { - const runner = new OzonAnalyzerTestRunner(); - await runner.initialize(); - await runner.runAllTests(); - }; -} \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/test-ui.js b/tests/ozon-analyzer-integration/test-ui.js deleted file mode 100644 index 0e0d0fde..00000000 --- a/tests/ozon-analyzer-integration/test-ui.js +++ /dev/null @@ -1,583 +0,0 @@ -import OzonAnalyzerTestRunner from './test-runner.js'; -import { PerformanceBenchmark } from './specs/performance-benchmark.js'; -import { AsyncMessageHandler, sendResilientMessage } from './async-message-handler.js'; - -export class TestUI { - constructor() { - this.testRunner = new OzonAnalyzerTestRunner(); - this.setupEventListeners(); - this.logs = []; - this.isRunning = false; - } - - setupEventListeners() { - const startBtn = document.getElementById('startTestsBtn'); - const benchmarkBtn = document.getElementById('startBenchmarkBtn'); - - startBtn.addEventListener('click', () => this.startTests()); - benchmarkBtn.addEventListener('click', () => this.startBenchmark()); - } - - async startTests() { - if (this.isRunning) return; - - this.isRunning = true; - const startBtn = document.getElementById('startTestsBtn'); - startBtn.textContent = '⏳ Тестирование выполняется...'; - startBtn.disabled = true; - - // Показываем прогресс - document.getElementById('progressSection').style.display = 'block'; - document.getElementById('logsSection').style.display = 'block'; - document.getElementById('resultsSection').style.display = 'block'; - - this.logMessage('🔄 Инициализация тестового окружения...'); - this.logMessage('🔍 Выполнение browser compatibility detection...'); - - try { - await this.testRunner.initialize(); - this.logMessage('✅ Тестовое окружение готово'); - - // Выводим информацию о compatibility - if (this.testRunner.compatibilityResults) { - const compat = this.testRunner.compatibilityResults.compatibility; - const browser = this.testRunner.compatibilityResults.browser; - this.logMessage(`📊 Compatibility Mode: ${compat.mode}`); - this.logMessage(` Chrome Version: ${browser.majorVersion}`); - this.logMessage(` Readiness Score: ${(compat.readinessScore * 100).toFixed(1)}%`); - if (compat.recommendations && compat.recommendations.length > 0) { - compat.recommendations.forEach(rec => { - this.logMessage(`💡 ${rec}`); - }); - } - } - - this.updateProgress(10, 'Запуск conditional тест сьюта...'); - - this.logMessage('\n🚀 ЗАПУСК CONDITIONAL ИНТЕГРАЦИОННОГО ТЕСТИРОВАНИЯ OZON ANALYZER\n'); - this.logMessage(` Условные режимы: ${this.testRunner.conditionalTestSuites.join(', ')}\n`); - - const results = await this.testRunner.runAllTests(); - - this.displayConditionalResults(results); - - this.logMessage(`\n✅ Conditional тестирование завершено!`); - - // Показываем Final Readiness Score - if (this.testRunner.compatibilityResults) { - const finalScore = this.testRunner.getFinalReadinessScore(); - this.logMessage(`🎯 Final Readiness Score: ${(finalScore * 100).toFixed(1)}%`); - - if (finalScore >= 0.9) { - this.logMessage(`🎉 INTEGRATION PRODUCTION-READY!`); - } else if (finalScore >= 0.7) { - this.logMessage(`⚠️ INTEGRATION FUNCTIONAL WITH IMPROVEMENT AREAS`); - } else { - this.logMessage(`🔶 INTEGRATION NEEDS ATTENTION!`); - } - - // Log AsyncMessageHandler statistics if available - this.logAsyncMessageHandlerStats(); - } - - } catch (error) { - this.logMessage(`❌ КРИТИЧЕСКАЯ ОШИБКА: ${error.message}`); - console.error('Test execution error:', error); - } finally { - this.isRunning = false; - startBtn.textContent = '🚀 Повторить тестирование'; - startBtn.disabled = false; - this.updateProgress(100, 'Conditional тестирование завершено'); - } - } - - async startBenchmark() { - if (this.isRunning) return; - - this.isRunning = true; - const benchmarkBtn = document.getElementById('startBenchmarkBtn'); - benchmarkBtn.textContent = '⏳ Бенчмаркинг...'; - benchmarkBtn.disabled = true; - - // Показываем прогресс - document.getElementById('progressSection').style.display = 'block'; - document.getElementById('logsSection').style.display = 'block'; - document.getElementById('resultsSection').style.display = 'block'; - - this.logMessage('🔄 Запуск бенчмаркинга производительности...'); - - try { - this.logMessage('\n🧪 ПРОФИЛИРОВАНИЕ ПРОИЗВОДИТЕЛЬНОСТИ OZON ANALYZER\n'); - - const benchmark = new PerformanceBenchmark(); - const results = await benchmark.runAll(); - - this.displayBenchmarkResults(results); - - this.logMessage(`\n✅ Бенчмаркинг завершён!`); - this.logMessage(`📊 Анализ производительности готов к просмотру.`); - - } catch (error) { - this.logMessage(`❌ ОШИБКА БЕНЧМАРКИНГА: ${error.message}`); - console.error('Benchmark execution error:', error); - } finally { - this.isRunning = false; - benchmarkBtn.textContent = '🧪 Повторить бенчмаркинг'; - benchmarkBtn.disabled = false; - this.updateProgress(100, 'Бенчмаркинг завершён'); - } - } - - logMessage(message) { - this.logs.push(message); - const logsContainer = document.getElementById('logsContainer'); - logsContainer.textContent = this.logs.join('\n'); - logsContainer.scrollTop = logsContainer.scrollHeight; - - // Также выводим в консоль для отладки - console.log(message); - } - - updateProgress(percent, text) { - const progressFill = document.getElementById('progressFill'); - const progressText = document.getElementById('progressText'); - - progressFill.style.width = `${percent}%`; - progressText.textContent = text; - } - - displayResults(results) { - const resultsContent = document.getElementById('resultsContent'); - const resultsTitle = document.getElementById('resultsTitle'); - - // Общая статистика - const totalScore = results.passed / results.total * 100; - - resultsTitle.innerHTML = ` - 📊 РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ: - - ${totalScore.toFixed(1)}% успеха - - `; - - let html = ` -
-
- Всего тестов: - ${results.total} -
-
- Пройдено: - ${results.passed} -
-
- Провалено: - ${results.failed} -
-
- Время выполнения: - ${((results.endTime - results.startTime) / 1000).toFixed(2)}s -
-
- -

📋 Детализация по компонентам:

- `; - - // Результаты по компонентам - if (results.testSuiteResults && results.testSuiteResults.length > 0) { - for (const componentResult of results.testSuiteResults) { - const successRate = componentResult.passed / componentResult.total * 100; - const status = successRate === 100 ? 'success' : successRate >= 50 ? 'warning' : 'failure'; - - html += ` -
- ${componentResult.component}: ${componentResult.passed}/${componentResult.total} - ${successRate.toFixed(1)}% -
- `; - } - } - - // Критические ошибки - if (results.errors && results.errors.length > 0) { - html += ` -

🚨 Критические ошибки:

- `; - - for (const error of results.errors) { - html += ` -
- ${error.type} -
${error.message}
- ${error.stack ? ` -
- Stack trace -
${error.stack}
-
- ` : ''} -
- `; - } - } - - // Рекомендации - html += ` -
-

💡 Рекомендации:

-
    - `; - - if (totalScore === 100) { - html += `
  • ✅ Интеграция работает идеально! Можно переходить к продакшену.
  • `; - } else if (totalScore >= 80) { - html += `
  • ⚠️ Интеграция работает с незначительными проблемами. Рекомендуется исправить ошибки перед продакшеном.
  • `; - } else if (totalScore >= 60) { - html += `
  • 🔶 Обнаружены существенные проблемы. Требуется доработка интеграции.
  • `; - } else { - html += `
  • ❌ Критические проблемы интеграции. Необходимо полное перетестирование.
  • `; - } - - if (results.failed > 0) { - html += `
  • 🔍 Рекомендуется детально изучить логи проваленных тестов для диагностики проблем.
  • `; - } - - html += ` -
-
- `; - - resultsContent.innerHTML = html; - - this.logMessage('\n📊 РЕЗУЛЬТАТЫ:'); - this.logMessage(` Пройдено: ${results.passed}/${results.total} (${totalScore.toFixed(1)}%)`); - this.logMessage(` Время: ${((results.endTime - results.startTime) / 1000).toFixed(2)} сек`); - } - - displayConditionalResults(results) { - const resultsContent = document.getElementById('resultsContent'); - const resultsTitle = document.getElementById('resultsTitle'); - - const totalScore = results.passed / results.total * 100; - const finalScore = this.testRunner.getFinalReadinessScore(); - - resultsTitle.innerHTML = ` - 📊 CONDITIONAL INTEGRATION TEST RESULTS: - - ${totalScore.toFixed(1)}% success - -
- - Final Readiness Score: ${(finalScore * 100).toFixed(1)}% - - `; - - let html = ''; - - // Browser Compatibility Section - if (this.testRunner.compatibilityResults) { - const compat = this.testRunner.compatibilityResults.compatibility; - const browser = this.testRunner.compatibilityResults.browser; - - html += ` -
-

🌐 BROWSER COMPATIBILITY

-
- Chrome Version: - ${browser.majorVersion} -
-
- Compatibility Mode: - ${compat.mode} -
-
- Readiness Score: - ${(compat.readinessScore * 100).toFixed(1)}% -
-
- `; - } - - // Conditional Test Suites Info - html += ` -
-

⚖️ CONDITIONAL TESTING

-
- Testing Modes: - ${this.testRunner.conditionalTestSuites.join(', ')} -
- `; - - if (this.testRunner.compatibilityResults && this.testRunner.compatibilityResults.compatibility.limitations) { - html += `
Limitations:${this.testRunner.compatibilityResults.compatibility.limitations.join(', ')}
`; - } - - html += `
`; - - // Test Execution Statistics - html += ` -
-

📈 TEST EXECUTION STATISTICS

-
- Всего тестов: - ${results.total} -
-
- Пройдено: - ${results.passed} -
-
- Провалено: - ${results.failed} -
-
- Время выполнения: - ${((results.endTime - results.startTime) / 1000).toFixed(2)}s -
-
- -

📋 Детализация по компонентам:

- `; - - // Результаты по компонентам - if (results.testSuiteResults && results.testSuiteResults.length > 0) { - for (const componentResult of results.testSuiteResults) { - const successRate = componentResult.passed / componentResult.total * 100; - const status = successRate === 100 ? 'success' : successRate >= 50 ? 'warning' : 'failure'; - - html += ` -
- ${componentResult.component}: ${componentResult.passed}/${componentResult.total} - ${successRate.toFixed(1)}% -
- `; - } - } - - // Production Ready Assessment - const readinessColor = finalScore >= 0.9 ? '#27ae60' : finalScore >= 0.7 ? '#f39c12' : '#e74c3c'; - const readinessText = finalScore >= 0.9 ? 'PRODUCTION-READY!' : finalScore >= 0.7 ? 'FUNCTIONAL' : 'REQUIRES ATTENTION'; - - html += ` -
-

🎯 PRODUCTION READINESS ASSESSMENT

-

- ${readinessText} (${(finalScore * 100).toFixed(1)}%) -

-

- ${finalScore >= 0.9 ? '✅ Your integration is fully ready for production deployment.' : - finalScore >= 0.7 ? '⚠️ Integration is functional but review recommendations above.' : - '🔶 Consider upgrading Chrome and addressing compatibility issues.'} -

-
- `; - - // Оставляем рекомендации и ошибки как есть, но изменяем вызов - html += this.getRecommendationsHtml(results); - html += this.getErrorsHtml(results); - - resultsContent.innerHTML = html; - - this.logMessage('\n📊 CONDITIONAL TEST RESULTS:'); - this.logMessage(` Пройдено: ${results.passed}/${results.total} (${totalScore.toFixed(1)}%)`); - this.logMessage(` Final Readiness: ${(finalScore * 100).toFixed(1)}%`); - this.logMessage(` Время: ${((results.endTime - results.startTime) / 1000).toFixed(2)} сек`); - } - - // Метод дляancell форматирования ошибок вынесен отдельно - getErrorsHtml(results) { - if (!results.errors || results.errors.length === 0) return ''; - - return ` -

🚨 Критические ошибки:

- ${results.errors.map((error, index) => ` -
- ${error.type} -
${error.message}
- ${error.stack ? ` -
- Stack trace -
${error.stack}
-
- ` : ''} -
- `).join('')} - `; - } - - // Метод для рекомендаций вынесен отдельно - getRecommendationsHtml(results) { - const totalScore = results.passed / results.total * 100; - const finalScore = this.testRunner.getFinalReadinessScore(); - - let recommendations = ` -
-

💥 CONDITIONAL RECOMMENDATIONS:

-
    - `; - - if (finalScore >= 0.9) { - recommendations += '
  • ✅ Full production-ready integration!
  • '; - } else if (finalScore >= 0.8) { - recommendations += '
  • ⚠️ Minor optimization opportunities available.
  • '; - } else if (finalScore >= 0.6) { - recommendations += '
  • ⚡ Substantial improvements needed for production.
  • '; - } else { - recommendations += '
  • 🔶 Critical compatibility issues require immediate attention.
  • '; - } - - if (this.testRunner.compatibilityResults) { - const compat = this.testRunner.compatibilityResults.compatibility; - if (compat.recommendations) { - compat.recommendations.forEach(rec => { - recommendations += `
  • 💡 ${rec}
  • `; - }); - } - } - - if (results.failed > 0) { - recommendations += '
  • 🔍 Review failed tests and compatibility limitations.
  • '; - } - - recommendations += '
'; - return recommendations; - } - - logAsyncMessageHandlerStats() { - try { - // If there's a global AsyncMessageHandler instance, log its stats - if (window.AsyncMessageHandler) { - const stats = { - example_retry_logic: '✓ Available', - exponential_backoff: '✓ Implemented', - graceful_degradation: '✓ Fallback mode', - timeout_handling: '✓ Configured' - }; - - this.logMessage('\n🔄 ASYNC MESSAGE HANDLER STATS:'); - Object.entries(stats).forEach(([key, value]) => { - this.logMessage(` ${key}: ${value}`); - }); - this.logMessage(' 📊 Resilient communication layer active!'); - } - } catch (error) { - // Silently ignore if stats can't be logged - console.log('AsyncMessageHandler stats not available'); - } - } - - displayBenchmarkResults(results) { - const resultsContent = document.getElementById('resultsContent'); - const resultsTitle = document.getElementById('resultsTitle'); - - resultsTitle.innerHTML = ` - 🚀 РЕЗУЛЬТАТЫ ПРОФИЛИРОВАНИЯ ПРОИЗВОДИТЕЛЬНОСТИ OZON ANALYZER - `; - - let html = ''; - - // Основные метрики производительности - if (results.analysis && results.analysis[4] && results.analysis[4].result) { - const benchmarkData = results.analysis[4].result; - const analysisResults = benchmarkData.analysisResults; - - // Таблица метрик - html += ` -
-

📊 Performance Metrics (сек):

- - - - - - - - - - - - - - - - - - - - - - - - - - -
Pyodide Initialisierung:${(benchmarkData.benchmarkResults?.pyodideInitialization || 0).toFixed(2)}s25-35s prod
DOM Parsing:${(benchmarkData.benchmarkResults?.htmlParsing || 0).toFixed(2)}s3-6s prod
Sequential AI Calls:${(benchmarkData.benchmarkResults?.sequentialAiCalls || 0).toFixed(2)}s8-26s prod
Total Execution:${(benchmarkData.benchmarkResults?.totalExecutionTime || 0).toFixed(2)}s💡 Optimization Target
Peak Memory:${((benchmarkData.benchmarkResults?.memoryUsage?.pyodidePeak || 0) / 1024 / 1024).toFixed(1)}MBMemory usage
-
- `; - - // Bottlenecks - if (analysisResults.bottlenecks && analysisResults.bottlenecks.length > 0) { - html += ` -
-

🚧 Identified Bottlenecks:

-
    - ${analysisResults.bottlenecks.map(b => `
  • ${b}
  • `).join('')} -
-
- `; - } - - // Рекомендации - if (analysisResults.recommendations && analysisResults.recommendations.length > 0) { - html += ` -
-

💡 Optimization Recommendations:

-
    - ${analysisResults.recommendations.map(r => `
  1. ${r}
  2. `).join('')} -
-
- `; - } - - // Ожидания улучшения - if (analysisResults.estimatedImprovement && benchmarkData.benchmarkResults?.totalExecutionTime) { - const totalTime = benchmarkData.benchmarkResults.totalExecutionTime; - const targetTime = analysisResults.estimatedImprovement.totalPotential; - const improvement = totalTime > 0 ? (100 * (totalTime - targetTime) / totalTime).toFixed(1) : 0; - - html += ` -
-

🎯 Projected Performance Improvement:

-

- Current: ${totalTime.toFixed(2)}s → - Target: ${targetTime.toFixed(2)}s
- ${improvement}% improvement -

-
- `; - } - } else { - html = `
${JSON.stringify(results, null, 2)}
`; - } - - resultsContent.innerHTML = html; - - // Логируем метрики - this.logMessage('\n📈 PERFORMANCE ANALYSIS COMPLETE:'); - if (results.analysis && results.analysis[4] && results.analysis[4].result) { - const br = results.analysis[4].result.benchmarkResults; - this.logMessage(` Pyodide: ${br?.pyodideInitialization?.toFixed(2)}s`); - this.logMessage(` DOM: ${br?.htmlParsing?.toFixed(2)}s`); - this.logMessage(` AI Sequential: ${br?.sequentialAiCalls?.toFixed(2)}s`); - this.logMessage(` Total: ${br?.totalExecutionTime?.toFixed(2)}s`); - } - this.logMessage(` ✅ Analysis ready for optimization implementation!`); - } -} - -// Запуск интерфейса -document.addEventListener('DOMContentLoaded', () => { - window.testUI = new TestUI(); -}); - -// Также делаем доступным глобально для отладки -window.OzonAnalyzerTestRunner = OzonAnalyzerTestRunner; \ No newline at end of file diff --git a/tests/ozon-analyzer-integration/utils/test-framework.js b/tests/ozon-analyzer-integration/utils/test-framework.js deleted file mode 100644 index 4e09b12e..00000000 --- a/tests/ozon-analyzer-integration/utils/test-framework.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Простой тестовый фреймворк для интеграционного тестирования - */ - -export class TestCase { - constructor(name, testFunction) { - this.name = name; - this.testFunction = testFunction; - this.result = null; - this.error = null; - this.duration = 0; - } - - async run() { - const startTime = Date.now(); - try { - this.result = await this.testFunction(); - this.duration = Date.now() - startTime; - return { success: true, result: this.result }; - } catch (error) { - this.error = error; - this.duration = Date.now() - startTime; - return { success: false, error: error }; - } - } -} - -export class TestSuite { - constructor(name) { - this.name = name; - this.tests = new Map(); - this.results = []; - } - - addTest(name, testCase) { - this.tests.set(name, testCase); - } - - async runTest(testName) { - const test = this.tests.get(testName); - if (!test) { - throw new Error(`Тест "${testName}" не найден`); - } - - console.log(`▶️ Выполнение теста: ${testName}`); - const result = { testName, ...await test.run() }; - - if (result.success) { - console.log(`✅ Тест "${testName}" пройден (${test.duration}ms)`); - } else { - console.log(`❌ Тест "${testName}" провален: ${result.error?.message} (${test.duration}ms)`); - } - - return result; - } - - async runAll() { - console.log(`\n📋 Запуск сьюта: ${this.name}`); - console.log(`📊 Количество тестов: ${this.tests.size}\n`); - - const summary = { - total: this.tests.size, - passed: 0, - failed: 0, - errors: [], - duration: 0 - }; - - const startTime = Date.now(); - - for (const [testName, test] of this.tests) { - const result = { testName, ...await test.run() }; - - if (result.success) { - summary.passed++; - console.log(`✅ ${testName} (${test.duration}ms)`); - } else { - summary.failed++; - summary.errors.push({ - testName, - error: result.error?.message, - stack: result.error?.stack - }); - console.log(`❌ ${testName}: ${result.error?.message} (${test.duration}ms)`); - } - - this.results.push(result); - } - - summary.duration = Date.now() - startTime; - - console.log(`\n📊 Результаты сьюта "${this.name}":`); - console.log(` Всего: ${summary.total}`); - console.log(` Пройдено: ${summary.passed}`); - console.log(` Провалено: ${summary.failed}`); - console.log(` Время: ${(summary.duration / 1000).toFixed(2)}s`); - - return summary; - } - - getResults() { - return this.results; - } -} - -export class Assert { - static equal(actual, expected, message = '') { - if (actual !== expected) { - throw new Error(`${message} Expected: ${expected}, Actual: ${actual}`.trim()); - } - } - - static notEqual(actual, expected, message = '') { - if (actual === expected) { - throw new Error(`${message} Expected not equal, but both are: ${actual}`.trim()); - } - } - - static isTrue(value, message = '') { - if (!value) { - throw new Error(`${message} Expected true, got: ${value}`.trim()); - } - } - - static isFalse(value, message = '') { - if (value) { - throw new Error(`${message} Expected false, got: ${value}`.trim()); - } - } - - static isDefined(value, message = '') { - if (value === undefined || value === null) { - throw new Error(`${message} Value is not defined`.trim()); - } - } - - static throws(fn, message = '') { - let threw = false; - try { - fn(); - } catch (e) { - threw = true; - } - - if (!threw) { - throw new Error(`${message} Expected function to throw, but it didn't`.trim()); - } - } - - static async throwsAsync(fn, message = '') { - let threw = false; - try { - await fn(); - } catch (e) { - threw = true; - } - - if (!threw) { - throw new Error(`${message} Expected async function to throw, but it didn't`.trim()); - } - } - - static contains(array, item, message = '') { - if (!Array.isArray(array) || !array.includes(item)) { - throw new Error(`${message} Array ${array} does not contain ${item}`.trim()); - } - } - - static matches(value, regex, message = '') { - if (!regex.test(value)) { - throw new Error(`${message} Value "${value}" does not match pattern ${regex}`.trim()); - } - } - - static type(value, expectedType, message = '') { - const actualType = typeof value; - if (actualType !== expectedType) { - throw new Error(`${message} Expected type ${expectedType}, but got ${actualType}`.trim()); - } - } -} \ No newline at end of file diff --git a/tests/run-mock-tests.cjs b/tests/run-mock-tests.cjs deleted file mode 100644 index a793a36b..00000000 --- a/tests/run-mock-tests.cjs +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Простой runner для mock-тестов HTML chunking процесса - * Запуск: node tests/run-mock-tests.js - */ - -const { readFileSync } = require('fs'); -const { join } = require('path'); - -// Простая реализация test runner -class SimpleTestRunner { - constructor() { - this.tests = []; - this.currentTest = null; - this.passed = 0; - this.failed = 0; - this.results = []; - } - - describe(name, fn) { - console.log(`\n📋 Suite: ${name}`); - console.log('='.repeat(50)); - fn(); - } - - test(name, fn) { - this.currentTest = { name, status: 'pending' }; - try { - const result = fn(); - if (result && typeof result.then === 'function') { - return result.then(() => { - this.currentTest.status = 'passed'; - this.passed++; - console.log(`✅ ${name}`); - }).catch(error => { - this.currentTest.status = 'failed'; - this.currentTest.error = error; - this.failed++; - console.log(`❌ ${name}: ${error.message}`); - }); - } else { - this.currentTest.status = 'passed'; - this.passed++; - console.log(`✅ ${name}`); - } - } catch (error) { - this.currentTest.status = 'failed'; - this.currentTest.error = error; - this.failed++; - console.log(`❌ ${name}: ${error.message}`); - } - } - - expect(value) { - return { - toBe: (expected) => { - if (value !== expected) { - throw new Error(`Expected ${expected}, but got ${value}`); - } - }, - toBeDefined: () => { - if (value === undefined) { - throw new Error(`Expected value to be defined, but got ${value}`); - } - }, - toBeGreaterThan: (expected) => { - if (value <= expected) { - throw new Error(`Expected ${value} to be greater than ${expected}`); - } - }, - toBeLessThan: (expected) => { - if (value >= expected) { - throw new Error(`Expected ${value} to be less than ${expected}`); - } - } - }; - } - - printSummary() { - console.log('\n' + '='.repeat(60)); - console.log('🧪 TEST SUMMARY'); - console.log('='.repeat(60)); - console.log(`✅ Passed: ${this.passed}`); - console.log(`❌ Failed: ${this.failed}`); - console.log(`📊 Total: ${this.passed + this.failed}`); - - if (this.failed === 0) { - console.log('\n🎉 All tests passed!'); - } else { - console.log('\n⚠️ Some tests failed. Check the output above.'); - } - } -} - -// Global test functions -global.describe = function(name, fn) { - global.testRunner.describe(name, fn); -}; - -global.test = function(name, fn) { - return global.testRunner.test(name, fn); -}; - -global.expect = function(value) { - return global.testRunner.expect(value); -}; - -global.beforeEach = function(fn) { - // Simple beforeEach implementation - if (!global.beforeEachFunctions) { - global.beforeEachFunctions = []; - } - global.beforeEachFunctions.push(fn); -}; - -global.afterEach = function(fn) { - // Simple afterEach implementation - if (!global.afterEachFunctions) { - global.afterEachFunctions = []; - } - global.afterEachFunctions.push(fn); -}; - -// Mock implementations -global.jest = { - fn: () => { - const mockFn = function(...args) { - mockFn.calls.push(args); - return undefined; - }; - mockFn.calls = []; - return mockFn; - }, - clearAllMocks: () => { - // Simple mock clearing - }, - restoreAllMocks: () => { - // Simple mock restoration - } -}; - -// Console override for capturing logs -const originalConsole = console; -global.console = { - ...originalConsole, - log: (...args) => { - // Filter out some verbose logs during testing - if (args[0] && typeof args[0] === 'string' && args[0].includes('🔍')) return; - if (args[0] && typeof args[0] === 'string' && args[0].includes('📊')) return; - originalConsole.log(...args); - }, - warn: originalConsole.warn, - error: originalConsole.error -}; - -// Initialize global test runner -global.testRunner = new SimpleTestRunner(); - -// Load and run tests -async function runTests() { - try { - console.log('🚀 Starting HTML Chunking Mock Tests...\n'); - - // Load test file - const testFile = join(__dirname, 'html-chunking-mock.test.ts'); - - // Simple TypeScript compilation (remove type annotations) - let testCode = readFileSync(testFile, 'utf8'); - - // Remove TypeScript type annotations (simple approach) - testCode = testCode - .replace(/:\s*\w+/g, '') // Remove type annotations - .replace(/import\s+type\s+[^;]+;/g, '') // Remove type imports - .replace(/<\w+>/g, '') // Remove generic types - .replace(/export\s+/g, '') // Remove exports - .replace(/private\s+|public\s+|protected\s+/g, '') // Remove access modifiers - .replace(/readonly\s+/g, '') // Remove readonly - .replace(/async\s+/g, '') // Remove async (for simplicity) - .replace(/await\s+/g, '') // Remove await (for simplicity) - .replace(/Promise<[^>]+>/g, 'Promise') // Simplify Promise types - .replace(/:\s*Promise/g, ': Promise') // Fix Promise types - .replace(/interface\s+\w+\s*{[^}]*}/g, '') // Remove interfaces - .replace(/type\s+\w+\s*=\s*[^;]+;/g, ''); // Remove type definitions - - // Execute test code - eval(testCode); - - // Print summary after all tests complete - setTimeout(() => { - global.testRunner.printSummary(); - }, 100); - - } catch (error) { - console.error('❌ Error running tests:', error); - process.exit(1); - } -} - -// Handle async tests -process.on('unhandledRejection', (reason, promise) => { - console.error('❌ Unhandled Rejection at:', promise, 'reason:', reason); -}); - -process.on('uncaughtException', (error) => { - console.error('❌ Uncaught Exception:', error); - process.exit(1); -}); - -// Run the tests -runTests().catch(error => { - console.error('❌ Failed to run tests:', error); - process.exit(1); -}); \ No newline at end of file diff --git a/tests/simple-mock-tests.js b/tests/simple-mock-tests.js deleted file mode 100644 index c7e6d3b9..00000000 --- a/tests/simple-mock-tests.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * Простые mock-тесты для HTML chunking процесса - * Запуск: node tests/simple-mock-tests.js - */ - -console.log('🧪 HTML Chunking Mock Tests'); -console.log('='.repeat(50)); - -// Mock implementations -class MockCircuitBreaker { - constructor() { - this.state = 'CLOSED'; - } - - async execute(operation) { - return await operation(); - } - - getState() { - return this.state; - } -} - -class MockTransferPersistenceManager { - constructor() { - this.storage = new Map(); - } - - async save(transfer, transferId) { - this.storage.set(transferId, { - transferId, - startTime: Date.now(), - totalChunks: transfer.chunks.length, - totalSize: transfer.totalSize, - status: 'active', - lastUpdated: Date.now() - }); - } - - async load(transferId) { - return this.storage.get(transferId) || null; - } - - async loadAll() { - return Object.fromEntries(this.storage); - } -} - -class MockTransferRecoveryManager { - async recoverTransfer(transferId) { - return { - success: true, - transfer: { - chunks: ['mock-chunk-1', 'mock-chunk-2'], - acked: [true, true], - totalSize: 100, - startTime: Date.now(), - htmlAssembledConfirmed: true - }, - html: 'Mock recovered HTML', - strategy: 'mock_recovery', - duration: 50 - }; - } -} - -class MockEnhancedChunkManager { - constructor() { - this.transfers = new Map(); - this.completedTransfers = new Map(); - this.assembledHtmls = new Map(); - this.emergencyBackup = new Map(); - this.globalTransferRefs = new Map(); - this.persistenceManager = new MockTransferPersistenceManager(); - this.recoveryManager = new MockTransferRecoveryManager(); - } - - async sendInChunks(data, transferId) { - const chunks = this.createChunks(data); - const transfer = { - chunks, - acked: new Array(chunks.length).fill(false), - totalSize: data.length, - startTime: Date.now(), - htmlAssembledConfirmed: false - }; - - this.transfers.set(transferId, transfer); - this.globalTransferRefs.set(transferId, transfer); - this.emergencyBackup.set(transferId, transfer); - - // Многоуровневое хранение для надежности - global.emergencyTransfers = global.emergencyTransfers || {}; - global.emergencyTransfers[transferId] = { - id: transferId, - transfer: transfer, - timestamp: Date.now(), - status: 'active' - }; - - global.fixedTransfers = global.fixedTransfers || []; - global.fixedTransfers.push({ - id: transferId, - transfer: transfer, - timestamp: Date.now(), - status: 'active' - }); - - console.log(`✅ CREATED transfer ${transferId} with ${chunks.length} chunks in multi-layer storage`); - } - - createChunks(data) { - const chunks = []; - const chunkSize = 32768; // 32KB - for (let i = 0; i < data.length; i += chunkSize) { - chunks.push(data.slice(i, i + chunkSize)); - } - return chunks; - } - - async acknowledgeChunk(transferId, chunkIndex) { - const transfer = this.transfers.get(transferId); - if (transfer && chunkIndex < transfer.acked.length) { - transfer.acked[chunkIndex] = true; - console.log(`✅ acknowledgeChunk: Chunk ${chunkIndex} acknowledged for transfer ${transferId}`); - } - - // Store acknowledgment in global backup - global.chunkAcknowledgments = global.chunkAcknowledgments || {}; - global.chunkAcknowledgments[transferId] = global.chunkAcknowledgments[transferId] || new Set(); - global.chunkAcknowledgments[transferId].add(chunkIndex); - } - - wasChunked(transferId) { - return this.transfers.has(transferId) || - this.completedTransfers.has(transferId) || - this.emergencyBackup.has(transferId) || - this.globalTransferRefs.has(transferId); - } - - getTransferStats(transferId) { - const transfer = this.transfers.get(transferId) || this.completedTransfers.get(transferId); - if (!transfer) return null; - - const completed = transfer.acked.filter(ack => ack === true).length; - const total = transfer.chunks.length; - const duration = Date.now() - transfer.startTime; - - return { completed, total, duration }; - } - - setAssembledHtml(transferId, html) { - this.assembledHtmls.set(transferId, html); - console.log(`💾 Stored assembled HTML for transfer ${transferId} (${html.length} chars)`); - } - - getAssembledHtml(transferId) { - return this.assembledHtmls.get(transferId) || null; - } - - getAssembledData(transferId) { - const transfer = this.transfers.get(transferId); - if (!transfer) { - console.warn(`❌ Transfer ${transferId} not found`); - return ''; - } - - const completed = transfer.acked.filter(ack => ack === true).length; - const total = transfer.chunks.length; - - if (completed !== total) { - throw new Error(`Transfer ${transferId} not complete: ${completed}/${total} chunks acknowledged`); - } - - return transfer.chunks.join(''); - } - - completeTransfer(transferId) { - const transfer = this.transfers.get(transferId); - if (!transfer) return false; - - this.completedTransfers.set(transferId, transfer); - this.transfers.delete(transferId); - - console.log(`🔄 COMPLETING transfer ${transferId} with multi-layer storage update`); - return true; - } - - async cleanup() { - // Mock cleanup - console.log('🧹 Cleanup completed'); - } -} - -// Test utilities -let testsRun = 0; -let testsPassed = 0; -let testsFailed = 0; - -function test(name, fn) { - testsRun++; - try { - const result = fn(); - if (result && typeof result.then === 'function') { - return result.then(() => { - testsPassed++; - console.log(`✅ ${name}`); - }).catch(error => { - testsFailed++; - console.log(`❌ ${name}: ${error.message}`); - }); - } else { - testsPassed++; - console.log(`✅ ${name}`); - } - } catch (error) { - testsFailed++; - console.log(`❌ ${name}: ${error.message}`); - } -} - -function expect(value) { - return { - toBe: (expected) => { - if (value !== expected) { - throw new Error(`Expected ${expected}, but got ${value}`); - } - }, - toBeDefined: () => { - if (value === undefined) { - throw new Error(`Expected value to be defined, but got ${value}`); - } - }, - toBeGreaterThan: (expected) => { - if (value <= expected) { - throw new Error(`Expected ${value} to be greater than ${expected}`); - } - }, - toBeLessThan: (expected) => { - if (value >= expected) { - throw new Error(`Expected ${value} to be less than ${expected}`); - } - } - }; -} - -// Test suite -async function runTests() { - console.log('\n📋 Тесты создания transfer в multi-layer storage'); - - const chunkManager = new MockEnhancedChunkManager(); - - // Test 1: Multi-layer storage creation - await test('✅ Создание transfer в multi-layer storage', async () => { - const testHtml = 'Test HTML content for chunking'; - const transferId = 'test-transfer-1'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем, что transfer создан во всех слоях хранения - expect(chunkManager.transfers.has(transferId)).toBe(true); - expect(chunkManager.globalTransferRefs.has(transferId)).toBe(true); - expect(chunkManager.emergencyBackup.has(transferId)).toBe(true); - expect(global.emergencyTransfers[transferId]).toBeDefined(); - expect(global.fixedTransfers.some(t => t.id === transferId)).toBe(true); - - console.log('✅ Multi-layer storage verification passed'); - }); - - // Test 2: Acknowledgment processing - await test('✅ Acknowledgment processing без race conditions', async () => { - const testHtml = 'Test'; - const transferId = 'test-ack-transfer'; - - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - const totalChunks = transfer.chunks.length; - - // Имитируем acknowledgments для всех чанков - for (let i = 0; i < totalChunks; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // Проверяем, что все чанки acknowledged - const stats = chunkManager.getTransferStats(transferId); - expect(stats.completed).toBe(stats.total); - - // Проверяем, что acknowledgments сохранены в global backup - expect(global.chunkAcknowledgments[transferId]).toBeDefined(); - expect(global.chunkAcknowledgments[transferId].size).toBe(totalChunks); - - console.log('✅ Acknowledgment processing passed'); - }); - - // Test 3: HTML assembly - await test('✅ Обработка HTML_ASSEMBLED', async () => { - const testHtml = 'Assembled HTML content'; - const transferId = 'test-assembled-transfer'; - - // Создаем transfer и подтверждаем все чанки - await chunkManager.sendInChunks(testHtml, transferId); - const transfer = chunkManager.transfers.get(transferId); - for (let i = 0; i < transfer.chunks.length; i++) { - await chunkManager.acknowledgeChunk(transferId, i); - } - - // Имитируем получение HTML_ASSEMBLED - chunkManager.setAssembledHtml(transferId, testHtml); - const completed = chunkManager.completeTransfer(transferId); - - expect(completed).toBe(true); - expect(chunkManager.completedTransfers.has(transferId)).toBe(true); - expect(chunkManager.transfers.has(transferId)).toBe(false); - expect(chunkManager.getAssembledHtml(transferId)).toBe(testHtml); - - console.log('✅ HTML_ASSEMBLED processing passed'); - }); - - // Test 4: Recovery system - await test('✅ Работа fallback recovery системы', async () => { - const transferId = 'test-recovery-transfer'; - - // Имитируем потерю transfer (удаляем из основной памяти) - chunkManager.transfers.delete(transferId); - chunkManager.completedTransfers.delete(transferId); - - expect(chunkManager.wasChunked(transferId)).toBe(false); - - // Запускаем recovery - const recoveryResult = await chunkManager.recoveryManager.recoverTransfer(transferId); - - expect(recoveryResult.success).toBe(true); - expect(recoveryResult.transfer).toBeDefined(); - expect(recoveryResult.html).toBeDefined(); - expect(recoveryResult.strategy).toBe('mock_recovery'); - - console.log('✅ Recovery system passed'); - }); - - // Test 5: Multi-layer search - await test('✅ Multi-layer search functionality', async () => { - const testHtml = 'Multi-layer test'; - const transferId = 'test-multi-layer'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем поиск во всех слоях - expect(chunkManager.wasChunked(transferId)).toBe(true); - - // Имитируем повреждение основного слоя - chunkManager.transfers.delete(transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); // Должен найти в других слоях - - // Имитируем повреждение всех слоев кроме emergency - chunkManager.completedTransfers.delete(transferId); - chunkManager.globalTransferRefs.delete(transferId); - expect(chunkManager.wasChunked(transferId)).toBe(true); // Должен найти в emergency backup - - console.log('✅ Multi-layer search functionality passed'); - }); - - // Test 6: Circuit breaker - test('✅ Circuit breaker protection', () => { - const circuitBreaker = new MockCircuitBreaker(); - - // Проверяем начальное состояние - expect(circuitBreaker.getState()).toBe('CLOSED'); - - // Имитируем успешную операцию - const result = circuitBreaker.execute(async () => { - return 'success'; - }); - - expect(result).toBeDefined(); - - console.log('✅ Circuit breaker protection passed'); - }); - - // Test 7: Persistence - await test('✅ Transfer cleanup и persistence', async () => { - const testHtml = 'Cleanup test'; - const transferId = 'test-cleanup'; - - await chunkManager.sendInChunks(testHtml, transferId); - - // Проверяем сохранение в persistence - const persisted = await chunkManager.persistenceManager.load(transferId); - expect(persisted).toBeDefined(); - expect(persisted.transferId).toBe(transferId); - expect(persisted.status).toBe('active'); - - // Выполняем cleanup - await chunkManager.cleanup(); - - console.log('✅ Transfer cleanup and persistence passed'); - }); - - // Summary - console.log('\n' + '='.repeat(60)); - console.log('🧪 TEST SUMMARY'); - console.log('='.repeat(60)); - console.log(`✅ Passed: ${testsPassed}`); - console.log(`❌ Failed: ${testsFailed}`); - console.log(`📊 Total: ${testsRun}`); - - if (testsFailed === 0) { - console.log('\n🎉 All tests passed!'); - console.log('✅ HTML chunking process is working correctly'); - console.log('✅ Multi-layer storage is functioning'); - console.log('✅ Recovery mechanisms are operational'); - console.log('✅ Race condition protection is active'); - } else { - console.log('\n⚠️ Some tests failed. Check the output above.'); - } -} - -// Run tests -runTests().catch(error => { - console.error('❌ Failed to run tests:', error); - process.exit(1); -}); \ No newline at end of file diff --git a/tools/E2E_TESTING_GUIDE.md b/tools/E2E_TESTING_GUIDE.md old mode 100644 new mode 100755 diff --git a/tools/dev_helper.sh b/tools/dev_helper.sh old mode 100644 new mode 100755 diff --git a/tools/generate-toc-and-crossrefs.cjs b/tools/generate-toc-and-crossrefs.cjs old mode 100644 new mode 100755 diff --git a/tools/ozon-analyzer-e2e-metrics-collector.js b/tools/ozon-analyzer-e2e-metrics-collector.js old mode 100644 new mode 100755 diff --git a/tools/safe-delete.js b/tools/safe-delete.js old mode 100644 new mode 100755 diff --git a/tools/sync_plans.py b/tools/sync_plans.py old mode 100644 new mode 100755 diff --git a/tools/test-scenarios.js b/tools/test-scenarios.js old mode 100644 new mode 100755 diff --git a/tsconfig.json b/tsconfig.json old mode 100644 new mode 100755 diff --git a/turbo.json b/turbo.json old mode 100644 new mode 100755 diff --git a/types/vite-plugin-node-polyfills.d.ts b/types/vite-plugin-node-polyfills.d.ts old mode 100644 new mode 100755 diff --git a/ui/PluginCard.css b/ui/PluginCard.css old mode 100644 new mode 100755 diff --git a/ui/PluginCard.js b/ui/PluginCard.js old mode 100644 new mode 100755 diff --git a/ui/log-manager.js b/ui/log-manager.js old mode 100644 new mode 100755 diff --git a/ui/test-harness.js b/ui/test-harness.js old mode 100644 new mode 100755 diff --git a/vite.config.mts b/vite.config.mts old mode 100644 new mode 100755