diff --git a/.claude/rules/changelog.md b/.claude/rules/changelog.md new file mode 100644 index 0000000..5a088c8 --- /dev/null +++ b/.claude/rules/changelog.md @@ -0,0 +1,35 @@ +# Ведение CHANGELOG.md + +При каждом коммите AI обязан обновить файл `CHANGELOG.md` в корне репозитория, +а также его английский перевод `CHANGELOG.en.md` — обе версии правятся в одном +коммите (см. правило documentation-translations). + +## Что фиксировать + +Записывать только изменения, видимые пользователю утилиты: + +- **Добавлено** — новая функциональность, новые команды, новые параметры CLI +- **Изменено** — изменения в поведении существующей функциональности, изменения формата вывода +- **Исправлено** — исправленные ошибки, влияющие на работу утилиты +- **Удалено** — удалённая функциональность или параметры + +Не фиксировать: + +- внутренние рефакторинги без влияния на пользователя +- изменения в тестах, CI/CD, документации правил +- обновления зависимостей (если не меняют поведение) + +## Формат файла + +- Язык: `CHANGELOG.md` — русский (канон), `CHANGELOG.en.md` — английский перевод +- Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.0.0/) +- Нерелизованные изменения добавляются в секцию `## [Unreleased]` +- При выпуске версии секция `[Unreleased]` переименовывается в `## [X.Y.Z] — YYYY-MM-DD` + +## Правила обновления + +1. Новые записи добавлять в начало соответствующей подсекции `[Unreleased]` +2. Каждая запись — одна строка с кратким описанием изменения +3. Если подсекция (`### Добавлено`, `### Изменено` и т.д.) ещё не существует — создать её +4. Не дублировать информацию из сообщения коммита — формулировка должна быть ориентирована на пользователя +5. Обновление CHANGELOG выполняется в том же коммите, что и само изменение diff --git a/.claude/rules/documentation-translations.md b/.claude/rules/documentation-translations.md new file mode 100644 index 0000000..c268289 --- /dev/null +++ b/.claude/rules/documentation-translations.md @@ -0,0 +1,50 @@ +# Двуязычная документация + +Вся пользовательская документация проекта ведётся на двух языках: **русский — +основной (канонический), английский — перевод**. Русские файлы (`*.md`) +отображаются на главной репозитория и в папках; английские лежат рядом как +`*.en.md`. + +## Парные файлы + +| Русский (канонический) | Английский (перевод) | +|---|---| +| `README.md` | `README.en.md` | +| `CHANGELOG.md` | `CHANGELOG.en.md` | +| `docs/ai-rules/README.md` | `docs/ai-rules/README.en.md` | +| `docs/mcp/README.md` | `docs/mcp/README.en.md` | + +В начале каждого файла — переключатель языка сразу под заголовком `#`: + +- в русском файле: `**Русский** | [English](<имя>.en.md)` +- в английском файле: `[Русский](<имя>.md) | **English**` + +## Главное правило + +**При любой правке одного файла из пары его перевод правится в том же +коммите.** Нельзя оставлять переводы рассинхронизированными: + +1. Правка содержания (новый раздел, изменённая команда, исправленный факт) — + внесите её в обе версии. +2. Сначала пишется/обновляется русская версия (канон), затем синхронно + английская. +3. Структура, заголовки, таблицы, блоки кода и ссылки должны совпадать; + во внутренних ссылках английской версии ведите на английские файлы + (`*.en.md`) там, где у целевого файла есть перевод. +4. Технические термины (`update`/`merge`, `conflict`, имена команд, флагов, + кодов ошибок, классов) сохраняются как есть — переводится только + поясняющий текст. + +## Файлы-исключения (перевод не нужен) + +Эти файлы по назначению потребляются ИИ-инструментами и ведутся **только на +английском**; русских версий у них нет и не требуется: + +- `docs/ai-rules/local-mirror-format.mdc` +- `docs/mcp/agent-instructions.md` + +## Релиз + +В релизные архивы включаются **обе** версии README — `README.md` (русская) и +`README.en.md` (английская). См. шаг `Stage release files` в +`.github/workflows/release.yml`. diff --git a/.claude/rules/dotnet-maintenance.md b/.claude/rules/dotnet-maintenance.md new file mode 100644 index 0000000..6c49fcc --- /dev/null +++ b/.claude/rules/dotnet-maintenance.md @@ -0,0 +1,129 @@ +# Сопровождение утилиты Confluence Page Exporter (.NET) + +## Роль + +Senior .NET разработчик / архитектор в режиме **сопровождения зрелого проекта** +(v2.18.0+). Проект уже построен — не генерируй его с нуля и не переписывай +структуру; расширяй существующую. При архитектурных решениях учитывай +расширяемость, тестируемость и долгосрочную поддерживаемость; объясняй +trade-offs; предлагай альтернативы; не давай «быстрых хаков» без объяснения +рисков; избегай антипаттернов. + +## Что это за проект + +CLI-утилита (**.NET 10**, `System.CommandLine`) **и** MCP-сервер (stdio) для +двусторонней синхронизации дерева страниц Confluence с локальными папками. +Git-подобная модель: `update` (force) и `merge` (smart) с детектом конфликтов. +Работает одинаково на Confluence **Server/DC и Cloud**. + +Реальные команды CLI: `download update`, `download merge`, `upload update`, +`upload create`, `upload merge`, `compare`, `config show`. (Названий +`export`/`import`/`sync` в проекте нет — не используй их.) + +## Локальный формат зеркала + +Одна папка = одна страница. `index.html` = тело в **Confluence Storage Format** +(`body.storage.value`). Маркер `.id_` (в теле — JSON +`{title, space}`) = стабильная идентификация страницы + версия на сервере + +точка отсчёта конфликта (`LastWriteTimeUtc`). Все прочие файлы в папке — +вложения. Полное описание формата — в [`docs/ai-rules/local-mirror-format.mdc`](../../docs/ai-rules/local-mirror-format.mdc) +(тот же файл поставляется пользователям как подключаемое правило — **не путать +его с правилами разработки самой утилиты**). + +## КРИТИЧНО: контракт нормализации (детект двойного редактирования) + +Это самый важный инвариант всего кода. + +- Нормализованный storage format хешируется (SHA-256) и хранится в маркерах + (`.id*`, JSON-поля `h`/`ne`), чтобы отличить **реальную** локальную правку от + mtime-only касания (пересохранение в редакторе, pretty-print, копирование, + `touch`, checkout из VCS). +- **ЛЮБОЕ** изменение нормализации контента — `XmlContentNormalizer` / + `RegexContentNormalizer` / таблица `HtmlEntities` / правила атрибутов или + пробелов / алгоритм хеша, либо смена активного `IContentNormalizer` — + **ОБЯЗАНО** поднять `NormalizationContract.CurrentEpoch` и обновить его + golden-значение. Иначе хеши, посчитанные по старому рецепту, молча разойдутся + с новыми; golden-vector тест `ContentHasherTests` падает, пока не обновишь и + эпоху, и golden. + +## Confluence Storage Format + +XHTML-подмножество с namespaces `ac:` (макросы), `ri:` (ресурсы), `at:` +(шаблоны). Правила: + +- Парсить через `XDocument`/`XmlDocument`, **не** строковыми заменами для + сложных трансформаций. +- Корректно обрабатывать макросы, таблицы, ссылки (``), + вложения-картинки (``). +- Валидировать XML перед отправкой в API; не генерировать несовместимый HTML. +- Спека: [Cloud](https://developer.atlassian.com/cloud/confluence/storage-format/) · + [Server/DC](https://developer.atlassian.com/server/confluence/confluence-storage-format/). + +## Confluence REST API + +- Обновление страницы: получить текущую версию → `version.number + 1` → + обработать `409 Conflict`. Тело: `body.storage.value` + + `representation = "storage"`. +- Явно обрабатывать: `401` / `403` / `404` / `409` / `429` / `5xx`. Учитывать + пагинацию (`limit`/`start`), версионирование, rate limits. +- **Cloud vs Server.** Cloud автоопределяется по `*.atlassian.net` (или + `--auth-type cloud`). На Cloud страницы идут через REST **v2** (числовой + `spaceId` резолвится из ключа автоматически, конфликт версий приходит чистым + `409`), но upload вложений, CQL и ping живут на **сохранившихся v1-эндпоинтах** + — v1 content API на Cloud удалён. Клиентские реализации Server/Cloud + разделены за абстракциями; базовый URL конфигурируется. +- Спека: [Cloud v1](https://developer.atlassian.com/cloud/confluence/rest/v1/) · + [Server/DC](https://developer.atlassian.com/server/confluence/confluence-rest-api-examples/). + +## MCP-сервер + +Утилита запускается как MCP-сервер по stdio (`McpServerRunner`, обёртки в +`Tools/`) — инструменты дублируют операции синка. Инструкции для агентов лежат +в [`docs/mcp/agent-instructions.md`](../../docs/mcp/agent-instructions.md), +встроены в сборку как `EmbeddedResource` и отдаются клиенту через +`InitializeResult.Instructions`. **Меняя поведение MCP-инструментов — +синхронно правь `agent-instructions.md`** (единый источник истины). + +Sandbox/безопасность: сервер стартует с `--root-dir` (песочница); путь вне неё +→ `OUT_OF_SANDBOX`. Флаг `--read-only` запрещает `upload`-инструменты +(`READ_ONLY_VIOLATION`). Учитывай оба ограничения при изменении инструментов. + +## Стек и конвенции (как в коде — не додумывать) + +- **.NET 10**, C# последней версии. `Nullable` + `ImplicitUsings` + + `TreatWarningsAsErrors=true` — **любой варнинг валит билд**. +- **DI:** `Microsoft.Extensions.Hosting`/`DependencyInjection`; composition root — + `Infrastructure/ServiceCollectionExtensions.cs`. Сервисы не создавать через + `new`, абстракции — через интерфейсы. +- **HTTP:** `IHttpClientFactory` (`Microsoft.Extensions.Http`). Retry — + **кастомный `RetryingHttpHandler : DelegatingHandler`** (экспоненциальный + backoff, уважает `Retry-After`; POST ретраится только на 429). **Polly не + используется — не добавляй его.** Не использовать `new HttpClient()`. +- **Логирование:** `Microsoft.Extensions.Logging`, структурное. Никогда не + логировать токены/секреты. +- **Async:** `async`/`await` + `CancellationToken` сквозным образом. Не + использовать `.Result`/`.Wait()`. +- **Сериализация:** `Newtonsoft.Json` (подключён в csproj). +- **Конфигурация:** `Microsoft.Extensions.Configuration`, приоритет + **CLI > env > файл > default**. Секреты не хардкодить. +- **Тесты:** **xunit.v3** на Microsoft Testing Platform + **Moq** + **Shouldly**. + Это НЕ NUnit, НЕ NSubstitute, НЕ FluentAssertions — не тащи их. HTTP мокать + кастомным `HttpMessageHandler`; реальные вызовы API в юнит-тестах запрещены. + Как запускать — см. `CLAUDE.md`. +- **Зависимости:** новые пакеты — только зрелые и поддерживаемые; предпочитать + `Microsoft.Extensions.*` и то, что уже есть в проекте. + +## Релиз и версионирование + +Версия — в `Directory.Build.props` (``). Релиз — тег `vX.Y.Z` +(GitHub Actions `release.yml` собирает артефакты и включает **оба** README). +CHANGELOG перекатывается `[Unreleased]` → `[X.Y.Z] — YYYY-MM-DD` в обоих языках. + +## Поведение при изменениях + +1. Держись существующей структуры (`Services/ Infrastructure/ Models/ Options/ + Commands/ Tools/`) — не вводи новые слои без причины. +2. Читай окружающий код и повторяй его идиомы (именование, плотность + комментариев, стиль). +3. Не ломай архитектурные границы; внешние API — за абстракциями. +4. Сложные места — объясняй; временные компромиссы — только с объяснением риска. diff --git a/.gitignore b/.gitignore index 27f88ae..ad86652 100644 --- a/.gitignore +++ b/.gitignore @@ -107,7 +107,8 @@ _ReSharper*/ # AI assistants ############################ .cursor/ -.claude/ -CLAUDE.md -AGENTS.md +# .claude/ is local by default; the shared agent rules under .claude/rules/ are committed +.claude/* +!.claude/rules/ +# CLAUDE.md / AGENTS.md are committed project AI-agent instructions graphify-out/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6359880 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,18 @@ +# AGENTS.md + +Инструкции для ИИ-агентов в этом репозитории. **Единый источник** — `CLAUDE.md` +(карта проекта, критические инварианты, запуск тестов) и правила в +`.claude/rules/`: + +- `.claude/rules/dotnet-maintenance.md` — сопровождение .NET-кода, Confluence + Storage Format / REST API, MCP-сервер, стек и конвенции, релиз +- `.claude/rules/changelog.md` — ведение CHANGELOG +- `.claude/rules/documentation-translations.md` — двуязычная документация + +Claude Code читает `CLAUDE.md`. Этот файл существует для агентов, читающих +стандарт `AGENTS.md` (Codex и др.); инструменты с поддержкой `@import` +подхватят правила ниже, остальным — открыть файлы по путям выше. + +@.claude/rules/dotnet-maintenance.md +@.claude/rules/changelog.md +@.claude/rules/documentation-translations.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..66088e0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,58 @@ +# Confluence Page Exporter — инструкции для ИИ-агентов + +CLI-утилита **и** MCP-сервер на **.NET 10** для двусторонней синхронизации +дерева страниц Confluence (Server/DC и Cloud) с локальным деревом папок. +Зрелый проект в стадии сопровождения (v2.18.0+) — **расширяй существующее, не +генерируй с нуля**. + +## Карта проекта + +- `src/ConfluencePageExporter/` + - `Commands/` — CLI-команды (`download`/`upload`/`compare`/`config`), `System.CommandLine` + - `Services/` — логика синхронизации (download/upload/merge/compare, анализ конфликтов) + - `Infrastructure/` — HTTP (`RetryingHttpHandler`), DI (`ServiceCollectionExtensions`), нормализация, `CommandDispatcher`, `McpServerRunner` + - `Models/` — DTO Confluence и доменные модели + - `Options/` — конфигурация (приоритет CLI > env > файл > default) + - `Tools/` — обёртки MCP-инструментов + - `Program.cs` → `CommandDispatcher` (CLI) либо `McpServerRunner` (MCP по stdio) +- `tests/ConfluencePageExporter.Tests/` — xunit.v3 (MTP) + Moq + Shouldly +- `docs/` — двуязычная документация + поставляемые пользователям артефакты + +**Двойная поверхность:** одна и та же логика синка доступна как CLI и как +MCP-сервер. `docs/mcp/agent-instructions.md` встроен в сборку как +`EmbeddedResource`. + +## Критические инварианты (не нарушать) + +- **Контракт нормализации.** Любое изменение нормализации контента + (`XmlContentNormalizer` / `RegexContentNormalizer` / `HtmlEntities` / правила + атрибутов и пробелов / алгоритм хеша / смена активного `IContentNormalizer`) + ОБЯЗАНО поднять `NormalizationContract.CurrentEpoch` и обновить golden-значение, + иначе `ContentHasherTests` падает и хеши молча расходятся. Детали — в + [dotnet-maintenance](.claude/rules/dotnet-maintenance.md). +- **Двуязычная документация.** Правка любого `*.md` из пары синхронно правится + в `*.en.md` в том же коммите. +- **CHANGELOG.** Видимые пользователю изменения фиксируются в `CHANGELOG.md` + + `CHANGELOG.en.md` в том же коммите. +- **Сборка.** `TreatWarningsAsErrors=true` — любой варнинг валит билд. + `Nullable` и `ImplicitUsings` включены. +- **Язык.** Документация и сообщения коммитов/PR — на русском (канон). Правила + и комментарии в коде допустимы на английском. + +## Как гонять тесты + +xunit.v3 на Microsoft Testing Platform: `dotnet test` **не печатает результаты**. +Собери тест-проект и запусти `.exe` напрямую: + +```powershell +dotnet build tests/ConfluencePageExporter.Tests/ConfluencePageExporter.Tests.csproj +tests/ConfluencePageExporter.Tests/bin/Debug/net10.0/ConfluencePageExporter.Tests.exe +# фильтр по имени метода: +# ... ConfluencePageExporter.Tests.exe --filter-method "*ShouldEscapeQuotesInCql*" +``` + +## Детальные правила + +@.claude/rules/dotnet-maintenance.md +@.claude/rules/changelog.md +@.claude/rules/documentation-translations.md