# agents-gitflow-guard

> **Устали от того, что ИИ-агенты игнорируют ваш GitFlow?**

Конфигурируемый страж ролей веток Git для ИИ-агентов написания кода — [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview), [Codex](https://github.com/openai/codex), [OpenCode](https://github.com/opencode-ai/opencode), [Antigravity](https://github.com/google-deepmind), [CodeBuddy](https://codebuddy.ai), [ZCode](https://zcode.ai), [Cursor](https://cursor.com), [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) и [Pi](https://github.com/mariozechner/pi).
Вы сами определяете свои ветки —
**integration** (фичи вливаются через PR/MR), **preview** (окружения тестирования), **production**, **archive** — каждая со своими правилами обновления. Агенты не могут обойти процесс, а критические слияния остаются под вашим контролем.

[English](README.md) · [简体中文](README.zh.md) · [繁體中文](README.zh-tw.md) · [日本語](README.ja.md) · [한국어](README.ko.md) · [Deutsch](README.de.md) · [Français](README.fr.md) · [Italiano](README.it.md) · [Português](README.pt.md) · [Español](README.es.md) · [Русский](README.ru.md) · [Лицензия](LICENSE)

[![Support on Ko-fi](https://img.shields.io/badge/Support_on_Ko--fi-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white)](https://ko-fi.com/keanz21)
[![npm total downloads](https://img.shields.io/npm/dt/agents-gitflow-guard.svg)](https://www.npmjs.com/package/agents-gitflow-guard) [![npm weekly downloads](https://img.shields.io/npm/dw/agents-gitflow-guard.svg)](https://www.npmjs.com/package/agents-gitflow-guard)

---

## Оглавление

- [Быстрый старт — 30 секунд до защиты репозитория](#быстрый-старт--30-секунд-до-защиты-репозитория)
- [Зачем — Проблема, которую решает этот плагин](#зачем--проблема-которую-решает-этот-плагин)
- [Для кого — Сценарии и команды](#для-кого--сценарии-и-команды)
- [Что он делает — Возможности](#что-он-делает--возможности)
- [Чего он НЕ делает — Честные ограничения](#чего-он-не-делает--честные-ограничения)
- [Защита на стороне сервера vs этот плагин](#защита-на-стороне-сервера-vs-этот-плагин)
- [Как это работает — Механизм в трех строках](#как-это-работает--механизм-в-трех-строках)
- [Справочник по конфигурации](#справочник-по-конфигурации)
- [Матрица проверок (Gate Matrix) — Что блокируется, а что разрешено](#матрица-проверок-gate-matrix--что-блокируется-а-что-разрешено)
- [Где контроль остается за человеком](#где-контроль-остается-за-человеком)
- [Подробная установка](#подробная-установка)
- [FAQ](#faq)
- [Глоссарий](#глоссарий)
- [Планы развития (Roadmap)](#планы-развития-roadmap)
- [Разработка](#разработка)
- [Поддержка](#поддержка)
- [Лицензия](#лицензия)

---

## Быстрый старт — 30 секунд до защиты репозитория

**Шаг 1 — установка.** Все девять клиентов используют один и тот же npm-пакет `agents-gitflow-guard` — выберите режим установки, соответствующий вашему агенту:

```bash
# Режим A: Клиенты CLI Hook (Claude Code · Codex · OpenCode · Antigravity · CodeBuddy · ZCode · Cursor)
npm i -g agents-gitflow-guard
```

```bash
# Режим B: Внутрипроцессный плагин DSH (перезапустите DSH после установки; плагины загружаются при старте)
dsh plugin --profile web add agents-gitflow-guard
```

```bash
# Режим C: Внутрипроцессное расширение Pi
npm i -D agents-gitflow-guard
```

> **Примечание**: Обычная команда `add` или `npm i` устанавливает последнюю версию из реестра npm. Если зеркало реестра имеет задержку кэша или вам требуется зафиксировать определенную версию, укажите `@<версия>` (например, `npm i -g agents-gitflow-guard@<версия>`). Специфичные для DSH peer-зависимости (`@deepseek-ai/cordis` / `@deepseek-ai/dsh-tools`) объявлены **необязательными** — они нужны только интегрированному в DSH плагину, и DSH предоставляет их через общий модуль профиля во время выполнения; пользователи CLI / Pi / OpenCode не обязаны их устанавливать.
>
> Клиентам CLI Hook требуется одна команда подключения после установки (см. Шаг 2); для Pi достаточно скопировать файл расширения; DSH монтируется автоматически при установке плагина.

**Шаг 2 — подключение клиента (конфигурационный файл не требуется).** Страж поставляется со **встроенными настройками по умолчанию, защищающими `develop` (integration) + `main` (archive)** — ноль конфигурации, включен по умолчанию. Единственное, что нужно сделать — указать вашему ИИ-клиенту вызывать страж с помощью одной команды для каждого клиента stdin-hook (DSH подключается автоматически; для Pi копируется файл, см. ниже):

```bash
# Claude Code → файл .claude/settings.json текущего репозитория
gitflow-guard wire --client claude --project --yes
```

```bash
# Codex / OpenCode / Antigravity / CodeBuddy / ZCode / Cursor (у каждого собственный конфигурационный файл; --yes пропускает подтверждение y/N)
gitflow-guard wire --client codex --project --yes
gitflow-guard wire --client opencode --project --yes
gitflow-guard wire --client antigravity --project --yes
gitflow-guard wire --client codebuddy --project --yes
gitflow-guard wire --client zcode --project --yes
gitflow-guard wire --client cursor --project --yes
```


```bash
# Предпросмотр (без записи) / удаление / интерактивный мастер:
gitflow-guard wire --client claude --dry-run
gitflow-guard wire --client claude --unwire
gitflow-guard setup
```

Команда `wire` вносит изменения в существующую конфигурацию **неразрушающим образом** (уже присутствующие хуки остаются без изменений; устаревшие записи gitflow-guard при повторном запуске мигрируют в текущую самозаякоренную форму) и по умолчанию записывает их в **каталог проекта** — `--global` (для всех репозиториев на этой машине) всегда запрашивает подтверждение или требует флага `--yes`. Конкретные файлы и форматы для каждого клиента приведены в разделе [Подробная установка](#подробная-установка).

> ⚠️ **main защищена по умолчанию.** Разработчики, использующие Trunk-based разработку или работу в одной ветке (прямой пуш в единственную ветку), будут заблокированы при попытке прямого пуша в `main`, пока явно не отключат защиту — создайте `gitflow-guard.config.json` с `{ "enabled": false }` или настройте собственную карту веток (см. [Справочник по конфигурации](#справочник-по-конфигурации)). Команда `gitflow-guard status` повторяет это предупреждение всякий раз, когда действуют встроенные настройки по умолчанию.

**Шаг 3 — проверка.** Попросите агента выполнить `git push origin develop`. Ожидается отклонение вызова инструмента:

```text
Error: [gitflow-guard] blocked: Protected branch "develop" forbids direct push
Next: Integration branch (develop) is updated via PR/MR from a feature branch: push the feature first, then `gh pr create --base develop` / `glab mr create --target-branch develop`.
```

Сообщения по умолчанию выводятся на английском языке; создайте конфигурацию с `"locale": "zh"` для переключения на китайский язык — сообщения будут выглядеть так: *已拦截: 受保护分支「develop」禁止直推 / 下一步: 集成分支(develop)由 PR/MR 合入 feature……* (см. [Справочник по конфигурации](#справочник-по-конфигурации)).

**Готово.** Страж активен для данного репозитория со встроенными настройками по умолчанию. Требуются дополнительные этапы (`preview` / `production`) или другие имена веток? Создайте файл `gitflow-guard.config.json` и укажите только нужные поля — все остальные параметры сохранят значения по умолчанию. Полную таблицу решений см. в [Матрице проверок (Gate Matrix)](#матрица-проверок-gate-matrix--что-блокируется-а-что-разрешено).

### Пошаговое руководство — одна фича от начала до конца

Сценарий: ваша команда выпускает страницу входа (`feature/login-page`); `develop` — ветка интеграции, `main` — архив. Что вы и агент видите на каждом шаге:

| # | действие агента | решение плагина | что вы видите |
|---|---|---|---|
| 1 | `git checkout -b feature/login-page` (от develop) | ✅ разрешено (работа над фичей свободна) | ветка создана |
| 2 | `git add . && git commit -m "feat: login"` | ✅ разрешено | коммит создан |
| 3 | `git push -u origin feature/login-page` | ✅ разрешено (пуш фичи безопасен) | выполнен пуш |
| 4 | `git checkout develop && git merge feature/login-page` | 🚫 **заблокировано** — ветка интеграции обновляется только через PR/MR | необходимо открыть PR/MR в develop |
| 5 | `gh pr create --base develop` | ✅ разрешено (фича → интеграция через PR) | PR создан, вы проверяете и сливаете |
| 6 | `git push origin main` или слияние в main | 🚫 **заблокировано** — архив доступен только для ручных действий человека | вы сами архивируете develop → main после релиза |

Обратите внимание на то, чего агент *не может* сделать: влить фичу напрямую в `develop` или как-либо затронуть `main`. Каждое ответственное слияние — это осознанное действие человека на странице PR/MR или в собственном терминале.

---

## Зачем — Проблема, которую решает этот плагин

ИИ-агенты написания кода работают прямо в вашем репозитории. Им *предписывается* — через системные промпты, файлы инструкций проекта (`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules` и подобные) и документацию — следовать процессу слияния: разработка в ветке фичи, слияние в интеграционную ветку (и этапы preview/production, если они есть), а слияния в архив и прод оставлять человеку.

**Это мягкое правило (soft rule).** Агенты срезают углы, меняют порядок действий или просто «забывают» о нем — не из злого умысла, а потому что текстовые инструкции для языковой модели являются необязательными.

Этот плагин превращает мягкое правило в **жесткий системный механизм (hard mechanism)**. Каждая git-операция, которую пытается выполнить агент, сверяется с *реальным состоянием локального репозитория*. Нарушения блокируются до запуска команды с подробным объяснением причины и подсказкой следующего шага.

Никому не нужно помнить правила — правила исполняются принудительно.

---

## Для кого — Сценарии и команды

### Признаки того, что плагин вам подходит

- У вас есть — или вы хотите внедрить — четкий процесс ветвления: от единственной интеграционной ветки `develop` до многоэтапных пайплайнов preview/production.
- Агент уже совершал срезку: выполнял прямой пуш в защищенную ветку или делал слияние туда, куда не следовало. Если это произошло один раз, это повторится — плагин обеспечивает структурное исправление.
- Вы защищаете интеграционные и архивные ветки, но не хотите полагаться только на ручной ревью для отлова каждой срезки.
- Несколько фичей разрабатываются параллельно и попадают в единое общее preview-окружение, и вы хотите контролировать каждый переход на более строгий этап.

### Конкретные сценарии

1. **Одиночный разработчик + агент на проектах заказчиков.** Вы даете агенту задачу; он «помогает», отправляя коммиты прямо в интеграционную ветку. С небольшим конфигурационным файлом агент физически не сможет затронуть защищенные ветки без PR/MR — даже если вы не следите за ним.
2. **Небольшая команда (3–10 человек) с автодеплоем preview по CI.** Тестовое окружение автоматически развертывается при слиянии; однажды агент без ревью влил фичу в `develop`. С этого момента любой переход в защищенную ветку требует создания PR/MR — осознанного и регистрируемого действия.
3. **Крупная компания с многоуровневыми пайплайнами.** Множество preview-окружений, контролируемые ветки продакшена и архива — каждая роль настраивается декларативно, и страж масштабируется без усложнения логики.
4. **Асинхронная совместная работа.** Вы не всегда находитесь в сети. Страж поддерживает дисциплину ветвления между вашими сессиями; слияния в прод и архив остаются исключительно вашей прерогативой.

**Вам НЕ подходит** (см. также [Чего он НЕ делает — Честные ограничения](#чего-он-не-делает--честные-ограничения)):

- **Trunk-based разработка** — все сливают напрямую в одну ветку: плагин будет непрерывно блокировать операции.
- **Личный репозиторий без установленного процесса** — нечего контролировать, нет пользы.
- **Команда, не желающая назначать роли веткам** — плагину необходима как минимум одна ветка `integration` для защиты.

---

## Что он делает — Возможности

- **Блокировка до выполнения**: прямой пуш / force-push / удаление веток с защищенными ролями (integration / preview / production / archive); попытка агента влить код в production или archive.
- **Ориентация на роли, полная настраиваемость**: `integration` (встроенное значение: `develop`) является базовой ролью; `preview` / `production` / `archive` — опциональные массивы точных имен или регулярных выражений, каждое со своими правилами обновления (`pr` / `flexible`, `mergeBy`).
- **Слияние человеком там, где это важно (Merge-by-user)**: слияния в прод и архив остаются в ваших руках — плагин запрещает агенту нажимать кнопку merge, поэтому ваше действие и *является* подтверждением.
- **Поддержка любых соглашений об именах**: имена веток сопоставляются через конфигурацию и никогда не зашиты жестко в код (см. [Справочник по конфигурации](#справочник-по-конфигурации)).
- **Полный аудит**: каждая блокировка добавляется в журнал аудита в каталоге состояния пользователя (`~/.local/state/gitflow-guard/`, `%LOCALAPPDATA%\gitflow-guard` в Windows) — вне репозитория, никогда не коммитится, находится за пределами доступной агенту песочницы и разделяется между всеми worktree одного репозитория.
- **Платформонезависимое ядро**: чистый локальный git; опционально обращается к `gh` (GitHub) или `glab` (GitLab) для разрешения целевых веток PR/MR, но прекрасно работает и без них.

---

## Чего он НЕ делает — Честные ограничения

- **Это не абсолютный периметр безопасности.** Парсинг команд основан на эвристиках (best-effort); агент, целенаправленно обфусцирующий команды, способен обойти текстовый анализ.
- **Он не является шлюзом в CI-системах.** Статус CI регистрируется только для справки, но не как жесткое ограничение. Настоящая защита веток должна настраиваться в параметрах GitHub/GitLab.
- **Он не заменяет сам рабочий процесс.** В вашем проекте должна быть хотя бы одна ветка `integration`; если все пушат прямо в одну ветку, плагин будет блокировать постоянно — не включайте его в таком случае.
- **Прод и архив не автоматизируются** — они намеренно оставлены под ручное нажатие человека; плагин лишь отвечает агентам отказом.

---

## Защита на стороне сервера vs этот плагин

Серверная защита веток (правила защиты веток в GitHub, защищенные ветки в GitLab) и данный плагин решают **разные задачи**. Они дополняют друг друга, а не заменяют.

| параметр | серверная защита | данный плагин |
|---|---|---|
| что регулирует | *кто* может пушить / сливать в защищенные ветки (права доступа) | *как* агенты входят в рабочий процесс (workflow) — в какую роль направлено слияние |
| запрещает агентам слияние в прод/архив | нет — сервер не отличает действия агента от действий человека | да — слияние в прод/архив заблокировано для агентов по умолчанию |
| гибкость по ролям | одно правило на ветку на хостинге | `update` (`pr`/`flexible`) + `mergeBy` (`user`/`anyone`) для каждой роли в едином конфиге |
| область действия | все пользователи репозитория, включая людей | агенты с настроенным хуком или плагином (люди не ограничены) |
| точка применения | на стороне сервера, во время пуша / слияния | локально, до выполнения команды |
| платформа | привязана к сервису хостинга | чистый локальный git, независим от платформы (`gh` / `glab` опциональны) |
| возможность обхода | пользователи с правами администратора | любой агент, не подключенный к стражу, или намеренно вредоносный агент |

Почему это важно: серверная защита отвечает на вопрос *"может ли этот пуш состояться в принципе?"*; плагин отвечает на вопрос *"может ли данный агент выполнить действие в рамках назначенной роли согласно конфигу?"*. Наиболее надежная конфигурация использует **оба механизма** — плагин обеспечивает соблюдение процесса агентами, а серверная защита гарантирует, что никто не сможет отправить прямой пуш в защищенную ветку.

---

## Как это работает — Механизм в трех строках

1. Агент вызывает инструмент командной строки (`pwsh` / `bash`) с git-командой.
2. Плагин классифицирует команду, определяет роли веток по `gitflow-guard.config.json` и применяет матрицу проверок.
3. Нарушение → вызов инструмента **отклоняется до его запуска** с объяснением причины и подсказкой следующего действия. Разрешено → команда выполняется; каждое отклонение записывается в журнал аудита (`~/.local/state/gitflow-guard/repos/<repo>-<hash>/audit.jsonl`).

Без подтверждений в чате и хранилищ разрешений: критические слияния (прод / архив) доступны **только пользователю** — агент может подготовить PR/MR, но нажатие кнопки слияния остается за вами.

### Принципы проектирования — почему это работает

#### 1. Конфигурация — единственный источник истины

Ни имена веток, ни правила не зашиты в коде. `integration` поставляется со встроенным значением по умолчанию (`develop`); `preview` / `production` / `archive` — опциональные массивы точных имен или регулярных выражений со своими `update` и `mergeBy`, объединяемые с настройками по умолчанию методом deep-merge. Один и тот же исполняемый файл подходит как для одиночного `develop`, так и для корпоративного пайплайна с множеством окружений.

#### 2. Блокировка происходит до выполнения, а не после

Страж перехватывает предварительное событие инструмента на каждой платформе — DSH `tools/pre-execute`, Pi `tool_call` и хук `PreToolUse` клиентов CLI — точку принятия решений *до* отправки команды на исполнение. Ответ `deny` означает, что команда **никогда не запустится**; агент увидит только отказ. Ретроспективный анализ (проверка логов постфактум) не может служить контролем — ущерб уже был бы нанесен.

#### 3. Критические слияния невозможно подделать без человека

Код плагина не принимает решений о том, допустимо ли слияние в прод или архив. Проверка просто запрещает *агенту* выполнять эти слияния, оставляя единственный путь — веб-интерфейс PR/MR, где **вы** нажимаете кнопку merge, что и является подтверждением. Не существует токена, разрешения или сообщения в чате, которое агент мог бы подделать для обхода этого правила.

---

## Справочник по конфигурации

### Встроенные значения по умолчанию и переопределение через deep-merge

Страж **включен по умолчанию** — файл `gitflow-guard.config.json` не требуется. Защищаются:

| по умолчанию | роль | правило |
|---|---|---|
| `develop` | **integration** | прямой пуш запрещен; обновление через PR/MR (`update: "pr"`) |
| `main` | **archive** | прямой пуш и слияние агентом запрещены; слияние в архив выполняет человек (`mergeBy: "user"`) |

При создании `gitflow-guard.config.json` его поля **объединяются с настройками по умолчанию методом deep-merge**: каждое указанное вами поле/роль переопределяет стандартное значение, а все неуказанные поля сохраняют значения по умолчанию. Указывайте только то, что хотите изменить:

```jsonc
{
  "branches": { "production": ["release-[\\w-]+"] }  // develop+main сохраняются; production добавляется
}
```

**Полное отключение** (для Trunk-based разработки): `{ "enabled": false }`. Устранение случайной блокировки выполняется правкой одного файла, а команда `gitflow-guard status` всегда наглядно показывает текущую действующую конфигурацию (включая встроенные настройки).

### Роли веток — модель проверки

**Роль** сопоставляет имена веток (или регулярные выражения) с набором правил. `integration` задана по умолчанию; все остальные роли опциональны.

```text
feature-ветки ──(свободно)──> integration (ветка интеграции; обновление через PR/MR)
                                    │
                                    ├──> preview (опционально; тестовые окружения; через PR/MR)
                                    │
                                    └──> production (опционально; PR/MR + слияние только вручную)
archive (опционально; архивируется вами после релиза)
```

| роль | ключ конфигурации | обязательна? | обеспечиваемое поведение |
|---|---|---|---|
| **feature** | `featurePattern` | — | свободно: commit / push / sync / rebase |
| **integration** | `branches.integration` | по умолчанию (`develop`) | прямой пуш запрещен (`pr`); фичи вливаются через PR/MR |
| **preview** | `branches.preview` (массив) | опционально | прямой пуш запрещен; обновление только через PR/MR (тестовые стенды) |
| **production** | `branches.production` (массив) | опционально | только PR/MR; слияние исключительно человеком (`mergeBy: "user"`) |
| **archive** | `branches.archive` (массив) | по умолчанию (`main`) | PR/MR в архив может создавать агент; слияние выполняется только человеком |

### Настройка имен веток и правил — поддерживаются любые имена

**Небольшая команда (один / 2–3 разработчика) — минимальный вариант: только интеграция:**

```jsonc
{
  "enabled": true,
  "featurePattern": "feature/[\\w-]+",
  "branches": { "integration": ["develop"] }
}
```

**Большая команда (несколько preview-окружений + production + archive):**

```jsonc
{
  "enabled": true,
  "featurePattern": "(topic|feature)/[\\w-]+",
  "branches": {
    "integration": ["develop", "topic/[\\w-]+"],
    "preview": {
      "branches": ["ita1", "itb1", "itb2", "sg", "vb", "r1-conf", "r1-ope", "r2-conf", "r2-ope"],
      "update": "pr"
    },
    "production": {
      "branches": ["prd-conf", "prd-ope"],
      "update": "pr",
      "mergeBy": "user"
    },
    "archive": ["main"]
  }
}
```

### Полный справочник полей

```jsonc
{
  "enabled": true,                     // по умолчанию true — установите false для отключения стража
  "featurePattern": "feature/[\\w-]+", // регулярное выражение JS для сопоставления рабочих веток/фичей
  "branches": {
    "integration": { "branches": ["develop"], "update": "pr" },  // по умолчанию: ["develop"] — опустите для сохранения
    "preview":     { "branches": ["ita1"], "update": "pr" },     // опционально
    "production":  { "branches": ["prd"], "update": "pr", "mergeBy": "user" }, // опционально
    "archive":     ["main"]                                      // опционально
  },
  "worktree": {                        // опционально: защита рабочего дерева и апстрим-базовой линии
    "requireCleanOnPr": false,         // требовать чистых staged/unstaged изменений перед созданием PR (по умолчанию false)
    "requireCleanOnMerge": false,      // требовать чистого рабочего дерева перед слиянием (по умолчанию false)
    "allowUntracked": true,            // разрешать неотслеживаемые файлы (??); false блокирует при их наличии (по умолчанию true)
    "requireUpstreamSynced": false     // требовать синхронизации с апстрим-веткой перед созданием PR (по умолчанию false)
  },
  "locale": "en",                      // опционально: язык сообщений — любой зарегистрированный ('en'/'zh' встроенные); при неизвестных значениях выводится предупреждение в status и используется английский
  "strict": false,                     // опционально: fail-closed — невалидный конфиг / внутренние ошибки блокируют вместо предупреждения и пропуска
  "ci": { "enabled": true }            // опционально: проверки gh pr логируются для справки
}
```

- Роли принимают либо **массив** (краткая запись), либо **объект** `{ branches, update?, mergeBy? }`.
- `update`: `pr` (по умолчанию) = обновление только через PR/MR; `flexible` = разрешить прямые/локальные слияния (для малых команд).
- `mergeBy` (production): `user` (по умолчанию) = слияние выполняет только человек; `anyone` = разрешить автоматическое слияние PR.
- **Защита рабочего дерева и апстрим-базовой линии (`worktree`)**: опциональные проверки состояния и расхождения —— `requireCleanOnPr: true` блокирует создание PR при наличии незакоммиченных изменений (staged/unstaged); `requireCleanOnMerge: true` блокирует локальные слияния и слияния PR при грязном рабочем дереве; `allowUntracked` (по умолчанию `true`) разрешает неотслеживаемые файлы (`??`) без трения, либо может быть установлен в `false` для строгого взаимодействия человека и агента; `requireUpstreamSynced: true` блокирует создание PR, если ветка отстает от апстрим-базовой линии. В многоэтапных составных командах (например, `git add . && git commit && gh pr create`) для последующих сегментов динамически моделируется чистое состояние.
- Каждая запись ветки — это точное имя или регулярное выражение (определяется автоматически). **Безопасность регулярных выражений**: шаблоны веток задаются вами и компилируются как есть; `featurePattern` сопоставляется с полным именем ветки (компилируется как `^(?:<pattern>)$`), поэтому пишите выражения, покрывающие имя целиком, — шаблоны-подстроки вроде `feature/` или просто `fix` не совпадут, и такие ветки попадут в `other`. Избегайте конструкций с катастрофическим возвратом (например, вложенных квантификаторов `(\w+)+`) в `featurePattern` и именах веток.
- **Язык сообщений**: по умолчанию английский; укажите `"locale": "zh"` для китайского или передайте `--locale <en|zh>` любой подкоманде `gitflow-guard` (приоритет: флаг CLI > конфигурация проекта > английский). Весь пользовательский текст адаптируется под локаль — включая сообщения фреймворка CLI (`--help`, сообщения о неизвестных командах, пустой лог аудита).
- **Пользовательские локали**: сторонние пакеты могут добавлять локали во время выполнения — `import { registerLocale } from 'agents-gitflow-guard'`, вызовите `registerLocale('fr', frDict)` со словарем, содержащим ровно те же ключи, что и встроенный английский (валидируется при регистрации), затем укажите `"locale": "fr"` в конфигурации проекта.

  ```js
  import { registerLocale, MESSAGE_KEYS } from 'agents-gitflow-guard'
  // MESSAGE_KEYS перечисляет все ключи, которые должен определять словарь (аналогично встроенному английскому);
  // при регистрации выбрасывается исключение, если ключ пропущен или является лишним.
  const fr = { /* по одной записи на каждый ключ из MESSAGE_KEYS, например: */ 'deny.header': ({ why }) => `[gitflow-guard] bloqué : ${why}` }
  registerLocale('fr', fr)
  ```
- **Неизвестные локали**: незарегистрированное значение `"locale"` при перехвате автоматически переключается на английский (по дизайну хуки не должны зависать из-за формулировок), поэтому опечатку легко не заметить; предупреждение отображается в `gitflow-guard status`.
- **Валидация**: пересекающиеся ветки в разных ролях отклоняются; невалидные регулярные выражения отклоняются. **Любая ошибка конфигурации переводит страж проекта в состояние "отключен"** (с выводом ошибки), исключая работу по полуугаданной конфигурации; обратите внимание, что переопределение роли веткой, совпадающей с ролью по умолчанию (например, назначение `main` в integration, когда стандартный archive остается `main`), вызывает ошибку пересечения — переопределите или удалите другую роль.
- **Строгий режим (strict mode)**: по умолчанию при поврежденном конфиге в stderr выводится предупреждение и команда пропускается (fail-open, чтобы опечатка не сломала рабочий процесс). `"strict": true` переводит ошибки конфигурации и внутренние ошибки в **блокировку** (fail-closed) — для проектов с повышенными требованиями к надежности. Явное `enabled: false` работает бесшумно; *отсутствие* файла конфигурации ошибкой не считается — действуют встроенные настройки по умолчанию (develop+main).

---

## Матрица проверок (Gate Matrix) — Что блокируется, а что разрешено

| действие агента | решение |
|---|---|
| commit / push фичи / sync / rebase / команды только для чтения | ✅ разрешено |
| прямой push / force-push / удаление integration / preview / production / archive | 🚫 блокируется (прямой push разрешен при `flexible` в integration/preview) |
| PR/MR: feature → integration / preview | ✅ разрешено |
| PR/MR: feature → production | ✅ создание разрешено; **слияние блокируется** (слияние вручную в UI) |
| PR/MR в archive | ✅ создание разрешено; 🚫 слияние блокируется (слияние вручную в UI) |
| локальный `git merge feature/x` находясь на integration / preview | 🚫 блокируется (требуется PR/MR); разрешено при `update: flexible` |
| цепочки команд (`checkout develop && merge feature/x`) | 🚫 блокируется — переключения веток эмулируются посегментно, обход невозможен |
| принудительное пересоздание защищенной ветки (`git checkout -B/-C <ветка>` / `git switch -C`) | 🚫 блокируется (проверка прямой модификации ссылок) |
| перенаправление/удаление защищенной ветки через `git symbolic-ref` | 🚫 блокируется (проверка прямой модификации ссылок) |
| `git cherry-pick` / `git revert` на integration / preview / production / archive | 🚫 блокируется (перезапись истории в защищенной ветке); флаги `-n` / `--no-commit` и команды `--abort`/`--continue`/`--skip`/`--quit` разрешены |
| git-команды, обернутые в `sudo` (повышение привилегий) | 🚫 обертка снимается (включая `sudo -u …`), проверяется базовая команда |

> Два намеренных исключения, предотвращающих регрессии: `git tag -f` (перемещение тега, даже указывающего на защищенную ветку) остается разрешенным — теги находятся вне области ролей веток, аналогично `push --tags`; обычный `git commit` на защищенной ветке остается разрешенным — страж контролирует роли веток и пути слияния, а не контент, при этом последующий `git push` будет заблокирован (удаленный репозиторий останется чистым).

Целевая ветка PR/MR определяется через `gh pr view` (GitHub) или `glab mr view` (GitLab). Без установленного CLI платформы плагин действует консервативно.

---

## Где контроль остается за человеком
- **Слияние в прод** и **архив** по умолчанию разрешены только человеку: агент может помочь подготовить PR/MR, но **кнопку слияния нажимаете вы** — это нажатие и *является* подтверждением. Отдельное хранилище разрешений для делегирования этого решения не используется.
- Каждое отклонение команды сохраняется в журнале аудита пользователя (`gitflow-guard audit`).

---

## Подробная установка

**Требование**: **Node.js ≥ 22** в переменной окружения `PATH` (минимальная версия согласно `engines` пакета и базовый уровень матрицы CI). Все клиенты используют **один и тот же npm-пакет** `agents-gitflow-guard` — различается только шаг подключения.

| Тип клиента / Платформа | Команда установки | Шаг монтирования и подключения |
|---|---|---|
| Claude Code · Codex · OpenCode · Antigravity · CodeBuddy · ZCode · Cursor | `npm i -g agents-gitflow-guard` | `gitflow-guard wire --client <имя> --project --yes` |
| DeepSeek Harness (DSH) | `dsh plugin --profile web add agents-gitflow-guard` | Перезапустить DSH — плагин автоматически монтируется в слой профиля |
| Pi | `npm i -D agents-gitflow-guard` | Скопировать `pi/gitflow-guard.ts` в `.pi/extensions/` |

### 1. Автономные клиенты CLI Hook (Claude Code · Codex · OpenCode · Antigravity · CodeBuddy · ZCode · Cursor)

Установите CLI глобально один раз, затем **подключите каждого клиента одной командой** (страж уже включен по умолчанию со встроенными настройками):

```bash
npm i -g agents-gitflow-guard   # предоставляет исполняемый файл `gitflow-guard`
gitflow-guard wire --client claude --project --yes
gitflow-guard wire --client codex --project --yes
gitflow-guard wire --client opencode --project --yes
gitflow-guard wire --client antigravity --project --yes
gitflow-guard wire --client codebuddy --project --yes
gitflow-guard wire --client zcode --project --yes
gitflow-guard wire --client cursor --project --yes
```

Команда `wire` считывает существующий файл конфигурации (при наличии), добавляет запись хука без изменения других параметров, является идемпотентной (повторный вызов пропускается), поддерживает режим `--dry-run` для предпросмотра и `--unwire` для удаления, а также запрашивает подтверждение перед записью в `--global`. Конфигурационные файлы клиентов (для справки и ручной настройки):

```jsonc
// Claude Code — .claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash", "hooks": [{ "type": "command", "command": "node <npm-global>/agents-gitflow-guard/bin/gitflow-guard.mjs check --platform claude" }] }
    ]
  }
}
```

```jsonc
// Codex — .codex/hooks.json
{
  "hooks": {
    "PreToolUse": [
      { "matcher": "^Bash$", "hooks": [{ "type": "command", "command": "node <npm-global>/agents-gitflow-guard/bin/gitflow-guard.mjs check --platform codex" }] }
    ]
  }
}
```

```ts
// OpenCode — `.opencode/plugins/gitflow-guard.ts` (копия поставляемого в пакете `opencode/gitflow-guard.ts`;
// в OpenCode 1.18+ удален hooks.yaml — точкой расширения теперь служит каталог plugins, событие
// `tool.execute.before`, где выброс исключения = отказ; `wire --client opencode` копирует этот файл за вас)
```
`gitflow-guard wire --client opencode` записывает этот файл из пакета; писать его вручную не рекомендуется.

```jsonc
// Antigravity (Google) — .agents/hooks.json
// (процесс хука agy запускается с cwd = каталог файла конфигурации хука, поэтому относительный путь bin/… не разрешается;
// `wire` записывает абсолютный путь на уровне проекта, а на глобальном — установленный в PATH gitflow-guard.
// Здесь показан вариант глобальной установки.)
{
  "gitflow-guard": {
    "PreToolUse": [
      { "matcher": "run_command", "hooks": [ { "type": "command", "command": "node <npm-global>/agents-gitflow-guard/bin/gitflow-guard.mjs check --platform antigravity" } ] }
    ]
  }
}
```

Остальные три клиента CLI используют ту же форму хука — `wire` записывает их файлы за вас:

- **CodeBuddy** — `.codebuddy/settings.json`
- **ZCode** — `.zcode/config.json` (также устанавливает `hooks.enabled: true`)
- **Cursor** — `.cursor/hooks.json` (`hooks.beforeShellExecution`)

> `<npm-global>/agents-gitflow-guard/bin/...` — это заполнитель: `wire` при подключении подставляет абсолютный путь к раннеру самого установленного пакета (полностью самозаякоренная команда: без раскрытия переменных на стороне клиента, без предположений о cwd hook-процесса, без зависимости от PATH, без развертывания чего-либо в целевом репозитории). Обновляетесь с ≤ 0.0.41? Перезапустите `wire` один раз для каждого клиента — старые записи (шаблоны переменных / относительные пути / форма PATH) мигрируют на месте.

### 2. Внутрипроцессные плагины и расширения (DSH · Pi)

- **DeepSeek Harness (DSH)**:
  ```bash
  dsh plugin --profile web add agents-gitflow-guard
  ```
  После этого перезапустите DSH. Пакет объявляет директиву `dsh.bundle.patch`, поэтому `dsh plugin add` автоматически подключает его в слой профиля без ручного редактирования. Обновление выполняется аналогичной командой и перезапуском.

- **Pi**:
  Pi загружает расширения внутри процесса (без передачи данных в stdin, без вызова дочерних процессов). Установите точку входа в проект и сохраните пакет в devDependencies:
  ```bash
  npm i -D agents-gitflow-guard
  mkdir -p .pi/extensions
  cp node_modules/agents-gitflow-guard/pi/gitflow-guard.ts .pi/extensions/gitflow-guard.ts
  ```
  Настройте `.pi/settings.json`:
  ```jsonc
  // Pi — .pi/settings.json (пути расширений разрешаются относительно .pi)
  { "extensions": ["extensions/gitflow-guard.ts"] }
  ```

### 3. Сборка из исходников и локальная разработка

Для контрибьюторов и разработчиков, желающих запускать и отлаживать последнюю версию из исходного кода:

```bash
# Клонирование и сборка
git clone https://github.com/FeatureAgents/AgentsGitFlowController.git
cd AgentsGitFlowController
npm install && npm run build
```

Подключите локальную сборку к целевой платформе агента:

```bash
# A. Клиенты CLI Hook (Claude Code · Codex · OpenCode · Antigravity · CodeBuddy · ZCode · Cursor)
npm link # или npm install -g .
gitflow-guard wire --client <claude|codex|opencode|antigravity|codebuddy|zcode|cursor> --project --yes

# B. DeepSeek Harness (DSH)
dsh plugin --profile web add file:/path/to/AgentsGitFlowController
# или выполните: node scripts/install-dsh.mjs web (затем перезапустите DSH)

# C. Pi
npm link
# или скопируйте файл pi/gitflow-guard.ts репозитория напрямую в .pi/extensions/
```

### 4. Примечание о GitHub Copilot

**GitHub Copilot — хук намеренно не предоставляется.** Copilot содержит встроенные механизмы защиты: разрешения **allow/deny/ask** для инструментов и **правила** проекта (`rules.json` + `AGENTS.md`). Рекомендуется использовать официальную документацию:

- [Разрешение и запрет использования инструментов (GitHub Docs)](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools)
- [Добавление пользовательских правил для агента Copilot (GitHub Docs)](https://docs.github.com/en/copilot/customizing-copilot/adding-custom-rules-for-the-copilot-coding-agent)
- Опционально: Copilot также предоставляет [систему хуков](https://docs.github.com/en/copilot/reference/hooks-reference) (`preToolUse` → `permissionDecision:"deny"`), если вам требуется перехват команд.

### 5. Механизм хуков и технические детали

- **Протокол платформы**: Хук считывает данные из stdin и отвечает в соответствии с протоколом целевой платформы:
  - **Claude Code / OpenCode / CodeBuddy / ZCode**: `exit 2` (в stderr передается причина и инструкция по дальнейшим действиям).
  - **Codex**: stdout JSON `{"hookSpecificOutput":{"permissionDecision":"deny",...}}`.
  - **Antigravity**: stdout JSON `{"decision":"deny","reason":...}` с кодом `exit 0` (требование Antigravity).
  - **Cursor**: stdout JSON `{"permission":"deny","user_message":...,"agent_message":...}` с кодом `exit 0`.
  - **Pi**: Внутрипроцессное расширение слушает событие `tool_call` и отклоняет вызов через `{ block: true, reason }`.

- **Перехват до выполнения (Pre-tool)**: Перехватывается только предварительное событие; страж блокирует команды *до* их запуска, поэтому пост-хуки и очистка разрешений не требуются.
- **Разрешение переменной PATH**: Глобальная установка (`npm i -g`) предоставляет бинарник `gitflow-guard`. `wire` привязывает каждый хук к абсолютному пути раннера самого установленного пакета, поэтому хук продолжает работать, даже когда среда агента не наследует ваш интерактивный `PATH`.
- **Включено по умолчанию**: Встроенные настройки (`integration: ["develop"]`, `archive: ["main"]`) действуют без создания конфигурационного файла. Пользовательские настройки в `gitflow-guard.config.json` объединяются с базовыми через deep-merge.
- **Неразрушающее подключение**: `gitflow-guard wire` объединяет хуки идемпотентно без изменения существующих правил (устаревшие записи gitflow-guard при повторном запуске мигрируют в текущую форму), а `wire --unwire` удаляет исключительно запись стража.

---

## FAQ

### Мои ветки называются иначе — могу ли я использовать плагин?

Да — имена веток нигде не зашиты жестко. `integration` предоставляется со значением по умолчанию (`develop`), а любая пользовательская конфигурация объединяется с ним; ее элементы (как и элементы `preview` / `production` / `archive`) могут быть любыми именами или регулярными выражениями. Параметр `featurePattern` указывает плагину, как распознавать ваши рабочие ветки.

Команда, называющая интеграционную ветку `master`, тестовую — `beta`, а ветки фичей начинающая с префикса `fix/`, просто указывает это в конфигурации; все блокировки, отчеты и аудит будут использовать именно эти имена. Вам не нужно подстраиваться под чужие соглашения — вы сами объявляете правила сопоставления. См. [Настройка имен веток и правил — поддерживаются любые имена](#настройка-имен-веток-и-правил--поддерживаются-любые-имена).

---

### Нужны ли мне вообще ветки preview/production/archive?

Нет. Добавляйте только те роли, которые реально присутствуют в вашем процессе. Для индивидуального проекта с веткой `develop` достаточно указать `integration: ["develop"]`; компания с десятью тестовыми средами добавит массив `preview` и роль `production`. Остальные роли останутся выключенными.

---

### Является ли это инструментом безопасности?

Нет, и крайне важно не воспринимать его как таковой. Это страж рабочего процесса (workflow guard): он делает согласованный процесс механически принудительным. Распознавание текстовых команд по своей природе работает на основе эвристик (best-effort) — агент, целенаправленно пытающийся скрыть команду, может обойти синтаксический анализатор.

В рамках поддерживаемых форматов команд границы ролей обеспечиваются локально: слияние в защищенную роль (integration / preview / production / archive) требует настроенного пути (PR/MR или ручное слияние человеком для production/archive). Стандартные методы обфускации классифицируются и блокируются — вызовы через оболочки (`sh -c` / `bash -lc`), подоболочки и вложенные конструкции с обратными кавычками/`$()`, префиксы `env`/`command`/`nohup`/`xargs`/`sudo` и присвоения `VAR=x`, абсолютные пути, конвейеры и цепочки `||`, глобальные опции git (`-C .`, `--git-dir=…`), псевдонимы git, заданные прямо в командной строке (`-c alias.<имя>=push …`, `git config alias.<имя> …` — включая формы в кавычках, с другим регистром и цепочки), refspec с масками (`refs/heads/*:refs/heads/*`), использование `git pull` как fetch+merge, а также низкоуровневые команды `send-pack`/`update-ref`/`symbolic-ref`; принудительное пересоздание защищенных веток (`checkout -B`/`switch -C`) и cherry-pick/revert на защищенных ветках блокируются проверками ref-update / ref-move. Исполняемый набор тестовых сценариев находится в `tests/accuracy-audit.spec.ts`.

Что остается **незащитимым на локальном уровне**: прямые вызовы API хостинга (`gh api repos/…/pulls/N/merge`, `curl`) и запуск команд из дочерних процессов сред выполнения (`node -e "child_process.exec('git push …')"`), а также псевдонимы, которые текстовый анализатор не видит — уже присутствующие в `.git/config`, внедрённые через каналы переменных окружения, собранные с помощью переменных оболочки или подстановки команд, записанные с экранированием ANSI-C или обратным слэшем, либо определённые в одном сегменте команды и вызванные в более позднем сегменте; произвольно глубокое экранирование или смена кодировок по своей сути остаются в рамках эвристического анализа; вложенность `$()` или обратных кавычек глубже 10 уровней больше не раскрывается (анализатор останавливает раскрытие, а не аварийно завершается на патологическом вводе). Настоящий непреодолимый рубеж защиты — это правила защиты веток на вашем git-хостинге. Используйте оба подхода: страж обеспечивает мгновенную обратную связь и журнал аудита, но не заменяет периметр безопасности.

---

### Почему агент не может сам выполнить слияние в production/archive?

Потому что система классифицирует эти действия как **доступные только человеку**. Плагин отклоняет *слияние* в прод и архив — при этом создание PR/MR остается разрешенным, и агент может подготовить для вас архивный PR из `develop` в `main`. Само слияние выполняется единственным способом: **вы** нажимаете кнопку merge — не существует токена, разрешения или команды в чате, с помощью которой агент мог бы наделить себя этим правом.

---

### Нужен ли мне CLI `gh` или `glab`?

Нет. Они являются опциональными адаптерами, используемыми только для определения целевой ветки при выполнении `pr merge` / `mr merge`, позволяя системе отличить разрешенное «слияние в integration/preview» от запрещенного «слияния в production/archive». Если ни один CLI не может подтвердить целевую ветку (утилита не установлена, не авторизована, нет сети или запрос завершился ошибкой), система **отклоняет слияние**, даже если оно запущено из ветки фичи: такой PR потенциально может вести в прод или архив. Повторите попытку после настройки CLI или выполните слияние вручную. Все остальные функции работают без изменений. Основные проверки не обращаются к внешним сервисам, поэтому плагин работает одинаково на GitHub, GitLab, собственных серверах и в офлайн-режиме.

---

### Будет ли плагин мешать моей обычной работе?

Намеренно нет. Все стандартные операции в ветке фичи — коммиты, пуши, синхронизация с `integration`, rebase, команды чтения и запуск `gitflow-guard status` — разрешены без задержек.

Блокировки срабатывают только в двух случаях: (1) прямая запись в ветки с защищенными ролями и (2) попытка агента выполнить слияние в прод или архив. Если вы столкнулись с блокировкой, которую считаете ошибочной, выполните `gitflow-guard status` — команда покажет, какая роль назначена каждой локальной ветке, позволяя легко найти и исправить несоответствие.

---

### Что если в моей конфигурации допущена ошибка?

Некорректная конфигурация никогда не применяется случайно: любая ошибка валидации отключает страж для проекта и выводит список ошибок.

Типичные ошибки: переопределение роли веткой, совпадающей с ролью по умолчанию (например, назначение `main` в integration, когда стандартный archive остается `main` — явная ошибка пересечения; переопределите или удалите другую роль), указание одной ветки в двух разных ролях (отклоняется) и невалидное регулярное выражение в `featurePattern` (отклоняется при компиляции). Сообщения об ошибках предельно понятны, а конфигурация представляет собой простой JSON-объект, поэтому исправление обычно занимает полминуты.

---

### Что именно проверяется в локальном репозитории?

Текущая ветка (`git branch --show-current`) и — только при `pr merge` / `mr merge` — целевая ветка PR/MR через `gh pr view` / `glab mr view`. При включенной опциональной защите `worktree` он также считывает `git status --porcelain` (состояние незакоммиченных / неотслеживаемых файлов) и, если задан `requireUpstreamSynced`, `git rev-list --left-right --count HEAD...@{upstream}`. Анализ дерева коммитов не требуется, поскольку модель **ориентирована на роли** (какая ветка *является* целевой), а не на хронологический порядок.

Плагин ничего не записывает, не обращается к удаленным серверам и не требует специальных возможностей хостинга для базовых проверок. Слияния в прод и архив просто отклоняются для агентов, а слияние человеком выполняется через интерфейс.

---

### Лицензия / стоимость?

MIT, бесплатно, без скрытых условий. Используйте, модифицируйте, распространяйте — единственным требованием является сохранение уведомления об авторских правах.

Если плагин спас вашу команду от неприятных последствий срезки процесса, кнопка чаевых вверху страницы всегда доступна, но не обязательна. См. [Лицензия](#лицензия).

---
## Глоссарий

| термин | значение |
|---|---|
| **integration** | базовая роль (по умолчанию: `develop`); фичи вливаются через PR/MR; защищена |
| **preview** | опциональные ветки тестовых сред (`branches.preview`, массив); обновление только через PR/MR |
| **production** | опциональные ветки продакшена (`branches.production`, массив); PR/MR + слияние только человеком |
| **archive** | опциональная ветка архива после релиза (`branches.archive`, массив); агенты могут создавать PR/MR, но слияние выполняет только человек |
| **feature branch** | ваша рабочая ветка, определяемая по `featurePattern`; свободная зона |
| **gate matrix** | таблица решений, сопоставляющая каждую классифицированную команду с разрешением/запретом |
| **pre-execute** | хук в конвейере инструментов, на котором происходит отклонение — до запуска команды |
| **merge-by-user** | слияния в прод и архив остаются за вами — ваше действие в PR/MR является подтверждением |

---

## Планы развития (Roadmap)

Будущие возможности и направления активных исследований:

- **Поддержка новых агентов**: Исследование и адаптация под хуки и расширения новых ИИ-агентов (например, Windsurf, новые CLI-агенты).
- **Агрегация аудита**: Синхронизация журналов аудита между машинами и форматы экспорта для проверки соответствия стандартам безопасности команд.
- **Готовые шаблоны процессов**: Пресеты конфигурации для распространенных моделей ветвления Git (Trunk-based разработка, многоуровневый корпоративный GitFlow).
- **Интеграция с CI**: Нативные хуки для CI-пайплайнов и интеграция с проверками PR при сохранении локального запуска без зависимостей.

Выпущенные функции и история версий представлены в [CHANGELOG.md](CHANGELOG.md).

---

## Разработка

```bash
npm install
npm test                # модульные тесты: classify / gate / config / cli / repo / platform / i18n / index / accuracy-audit / pi
npm run typecheck       # tsc --noEmit, 0 ошибок
npm run build           # tsdown → lib/ (CLI и плагин используют общую сборку)
npm run check:pins      # проверка совпадения версии package.json с заголовком CHANGELOG и версиями в README
npm run check:readmes   # проверка структурной симметрии всех 11 README (44 заголовка / 7 таблиц / 17 пунктов оглавления)
npm run verify:matrix   # непрерывное кросс-агентное тестирование: логика DSH + локаль zh + хуки клиентов + расширение Pi
npm run test:git-matrix # матрица git-решений из 169 случаев на реальных репозиториях
npm run test:realflow   # жизненный цикл ветки фичи end-to-end на реальном удаленном репозитории
npm run test:pi         # расширение Pi end-to-end (требуется локальная установка Pi)
npm run test:all        # typecheck + модульные тесты + матрица платформ + git-матрица + realflow
```

- **Стандарт качества**: Любое изменение логики требует успешного прохождения тайпчека (0 ошибок), всех тестов и матрицы `verify:matrix`.
- **Добавление клиентов**: При добавлении поддержки новой платформы агентов следуйте чек-листу синхронизации в [AGENTS.md](AGENTS.md) §8.

---

## Поддержка

Плагин является бесплатным с открытым исходным кодом (MIT). Если он помог вам и вашей команде избежать критических сбоев, вы можете поддержать проект чашкой кофе:

[![Support on Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/keanz21)

---

## Лицензия

[MIT](LICENSE) © FeatureAgents
