docs: update AGENTS and README to include guidelines for Conventional Commits
- Added a section on Conventional Commits in AGENTS.md, detailing the process for generating commit messages. - Enhanced README.md with references to the Conventional Commits rules and the necessary scripts for generating commit messages. - Clarified the format for commit messages, specifying the language requirements for headers and bodies.
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
description: >-
|
||||||
|
Conventional Commits EvoBGP. Триггеры commit/коммит/закоммить/staged/commit message —
|
||||||
|
ОБЯЗАТЕЛЬНО сначала Shell scripts/commit/staged-context.ps1, скилл commit-message.
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# Conventional Commits (EvoBGP)
|
||||||
|
|
||||||
|
## Триггеры (применить правило + скилл)
|
||||||
|
|
||||||
|
Любой запрос на коммит или сообщение коммита: `commit`, `коммит`, `закоммить`, `git commit`, `commit message`, `conventional commit`, `staged`, «сгенерируй коммит» — в т.ч. если это указано в плане или [AGENTS.md](../../AGENTS.md).
|
||||||
|
|
||||||
|
## Обязательный запуск скрипта (MUST)
|
||||||
|
|
||||||
|
1. Прочитать скилл [`.cursor/skills/commit-message/SKILL.md`](../skills/commit-message/SKILL.md).
|
||||||
|
2. **Первым действием** выполнить Shell (из корня репо):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -NoProfile -File scripts/commit/staged-context.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Сообщение коммита строить **только** по JSON из stdout скрипта (`groups`, `diff_excerpt`, `stat`).
|
||||||
|
4. **Запрещено** генерировать commit message без успешного (exit 0) запуска скрипта; не заменять скрипт одним `git diff --cached`.
|
||||||
|
|
||||||
|
## Формат (строго)
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <short summary in English>
|
||||||
|
|
||||||
|
<тело на русском: что изменено и зачем>
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Заголовок** — только английский; императив, без точки в конце; ≤72 символов.
|
||||||
|
- **Тело** — только русский; полные предложения; пустая строка после заголовка.
|
||||||
|
- Запрещены vague-сообщения: `fix bug`, `update code`, `wip`, `misc`.
|
||||||
|
|
||||||
|
## Типы (semantic-release)
|
||||||
|
|
||||||
|
| type | Когда | Версия |
|
||||||
|
|------|--------|--------|
|
||||||
|
| `feat` | новая функциональность | minor |
|
||||||
|
| `fix` | исправление бага | patch |
|
||||||
|
| `perf` | ускорение без смены API | patch |
|
||||||
|
| `refactor` | реструктуризация без смены поведения | — |
|
||||||
|
| `docs` | только документация | — |
|
||||||
|
| `test` | тесты | — |
|
||||||
|
| `ci` | CI/CD (`.gitea/`, workflows) | — |
|
||||||
|
| `chore` | обслуживание, deps, `.cursor/` | — |
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
- Заголовок: `feat!` / `fix!` **или** в теле строка `BREAKING CHANGE:` (на английском ключевое слово) + описание impact **на русском**.
|
||||||
|
|
||||||
|
## Scope (EvoBGP)
|
||||||
|
|
||||||
|
Выбирать по доминирующему пути из staged diff:
|
||||||
|
|
||||||
|
| Префикс | scope |
|
||||||
|
|---------|-------|
|
||||||
|
| `internal/httpapi/` | `httpapi` |
|
||||||
|
| `internal/store/`, `internal/repository/`, `internal/db/` | `store` |
|
||||||
|
| `internal/jobs/` | `jobs` |
|
||||||
|
| `internal/pipeline/` | `pipeline` |
|
||||||
|
| `internal/birdfmt/` | `birdfmt` |
|
||||||
|
| `internal/birddeploy/` | `birddeploy` |
|
||||||
|
| `internal/bundle/`, `internal/signing/` | `bundle` |
|
||||||
|
| `cmd/` | `cmd` |
|
||||||
|
| `web/` | `web` |
|
||||||
|
| `docs/openapi.yaml`, `redocly.yaml` | `openapi` |
|
||||||
|
| `docs/` (остальное) | `docs` |
|
||||||
|
| `migrations/` | `db` |
|
||||||
|
| `.gitea/` | `ci` |
|
||||||
|
| `deploy/` | `deploy` |
|
||||||
|
| `.cursor/` | `chore` |
|
||||||
|
| прочее в корне | `chore` |
|
||||||
|
|
||||||
|
`type` определять по **содержимому diff**, не только по пути.
|
||||||
|
|
||||||
|
## Multi-change
|
||||||
|
|
||||||
|
- Несвязанные области → **отдельные коммиты** (auto-split по скиллу).
|
||||||
|
- Одна логическая фича через несколько scope (OpenAPI + httpapi + web) → **один** коммит, scope по главной области.
|
||||||
|
|
||||||
|
## Обязательный вывод агенту
|
||||||
|
|
||||||
|
Для каждого коммита:
|
||||||
|
|
||||||
|
**1. Готовое сообщение** (копировать в `git commit -m` / HEREDOC):
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <summary>
|
||||||
|
|
||||||
|
<тело RU>
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Пояснение (RU):** почему выбран type; риск/impact; был ли split.
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(httpapi): add version endpoint and wire web footer
|
||||||
|
|
||||||
|
Добавлен GET /v1/version. В Web UI версия в footer берётся из API вместо захардкоженного значения.
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
fix(auth): correct token validation edge case
|
||||||
|
|
||||||
|
Исправлена ложная 401 при истёкшем refresh-токене с валидной сессией.
|
||||||
|
```
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
---
|
||||||
|
name: commit-message
|
||||||
|
description: >-
|
||||||
|
ОБЯЗАТЕЛЬНО при commit, коммит, закоммить, commit message, conventional commit,
|
||||||
|
staged, semantic-release, «сгенерируй коммит», git commit: ПЕРВЫМ делом Shell —
|
||||||
|
scripts/commit/staged-context.ps1; затем Conventional Commit (заголовок EN, тело RU).
|
||||||
|
---
|
||||||
|
|
||||||
|
# Commit message (EvoBGP)
|
||||||
|
|
||||||
|
## Когда применять (сразу читать этот скилл)
|
||||||
|
|
||||||
|
Триггеры в промпте пользователя или в плане/AGENTS.md:
|
||||||
|
|
||||||
|
- `commit`, `коммит`, `закоммить`, `commit message`, `conventional commit`
|
||||||
|
- `git commit`, `staged`, `сообщение коммита`, `сгенерируй коммит`
|
||||||
|
- агент собирается выполнить `git commit` или предложить текст коммита
|
||||||
|
|
||||||
|
## Шаг 0 — ОБЯЗАТЕЛЬНО (до любого текста коммита)
|
||||||
|
|
||||||
|
**Первый вызов инструментов** в этой задаче — Shell из корня репозитория:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -NoProfile -File scripts/commit/staged-context.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
| Правило | Деталь |
|
||||||
|
|---------|--------|
|
||||||
|
| **MUST** | Запустить скрипт до генерации заголовка/тела коммита |
|
||||||
|
| **MUST NOT** | Строить сообщение только по `git diff --cached` / `git status` без скрипта |
|
||||||
|
| **MUST NOT** | Пропускать скрипт, даже если diff «и так понятен» |
|
||||||
|
| Exit `1` | Index пуст — сообщить пользователю, **не коммитить** |
|
||||||
|
| Exit `0` | Разобрать JSON stdout: `staged_count`, `groups[]` (`scope`, `files`, `stat`, `diff_excerpt`) |
|
||||||
|
|
||||||
|
Дополнительно: [.cursor/rules/conventional-commits.mdc](../../rules/conventional-commits.mdc).
|
||||||
|
|
||||||
|
## Слияние групп (одна фича)
|
||||||
|
|
||||||
|
Скрипт группирует **только по путям**. Перед split проверьте логическую связность:
|
||||||
|
|
||||||
|
**Объединить в один коммит**, если это одна задача:
|
||||||
|
|
||||||
|
- `docs/openapi.yaml` + `internal/httpapi/` (+ опционально `web/`) — один endpoint/контракт;
|
||||||
|
- `migrations/` + `internal/store/` / `repository/` — одна схема;
|
||||||
|
- правки теста рядом с кодом той же фичи.
|
||||||
|
|
||||||
|
При объединении: один scope (доминирующий пакет, часто `httpapi` или `openapi`), один type, одно тело RU.
|
||||||
|
|
||||||
|
**Auto-split** — если группы **не связаны** (например `web/` + `internal/birdfmt/` без общего смысла): **отдельный коммит на группу**, без вопроса пользователю.
|
||||||
|
|
||||||
|
## Порядок коммитов при split
|
||||||
|
|
||||||
|
1. `openapi`
|
||||||
|
2. `httpapi`, `store`, `jobs`
|
||||||
|
3. `pipeline`, `birdfmt`, `birddeploy`, `bundle`
|
||||||
|
4. `web`
|
||||||
|
5. `ci`, `deploy`, `db`
|
||||||
|
6. `docs`
|
||||||
|
7. `chore`, `cmd`
|
||||||
|
|
||||||
|
## Алгоритм auto-split (уровень файлов)
|
||||||
|
|
||||||
|
1. `git status` и `git diff --cached --stat` — зафиксировать полный список staged-файлов.
|
||||||
|
2. `git reset HEAD` — снять всё из index (working tree не трогать).
|
||||||
|
3. Для каждой (объединённой) группы по порядку выше:
|
||||||
|
- `git add -- <paths…>`
|
||||||
|
- Сгенерировать сообщение по правилу conventional-commits.
|
||||||
|
- Закоммитить (см. ниже).
|
||||||
|
4. После всех коммитов — `git status` для проверки.
|
||||||
|
|
||||||
|
**Ограничение:** частичный stage одного файла с разной семантикой — предупредить; split по hunk'ам не делать; предложить разнести правки по файлам.
|
||||||
|
|
||||||
|
## Создание коммита
|
||||||
|
|
||||||
|
Только если пользователь **явно** просил закоммитить. Иначе — только вывести готовые сообщения.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git commit -m "$( @'
|
||||||
|
<type>(<scope>): <summary in English>
|
||||||
|
|
||||||
|
<Тело на русском.>
|
||||||
|
'@ )"
|
||||||
|
```
|
||||||
|
|
||||||
|
В PowerShell для многострочного тела используйте here-string как выше или `-m` для заголовка и `-m` для тела.
|
||||||
|
|
||||||
|
Перед коммитом: `git status`, `git diff --cached` для группы — убедиться, что stage соответствует сообщению.
|
||||||
|
|
||||||
|
## Генерация текста
|
||||||
|
|
||||||
|
По `groups[].diff_excerpt`, `stat`, `files`:
|
||||||
|
|
||||||
|
- **type** — по смыслу diff (`feat` / `fix` / …), не по умолчанию `chore`.
|
||||||
|
- **scope** — из JSON группы или доминирующий при merge.
|
||||||
|
- **summary** — конкретный, английский, императив.
|
||||||
|
- **body** — русский: что, зачем, edge cases, breaking impact.
|
||||||
|
|
||||||
|
## Вывод пользователю
|
||||||
|
|
||||||
|
Для **каждого** коммита:
|
||||||
|
|
||||||
|
### 1. Готовое сообщение
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <summary>
|
||||||
|
|
||||||
|
<тело>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Пояснение (RU)
|
||||||
|
|
||||||
|
- Почему выбран type/scope.
|
||||||
|
- Риски и impact.
|
||||||
|
- Split: сколько коммитов и почему.
|
||||||
|
|
||||||
|
## Не смешивать
|
||||||
|
|
||||||
|
Один commit message — одна primary intent. Не объединять несвязанный `fix` и `feat` в один заголовок.
|
||||||
|
|
||||||
|
## Ссылки
|
||||||
|
|
||||||
|
- Правило: `.cursor/rules/conventional-commits.mdc`
|
||||||
|
- Скрипт: `scripts/commit/staged-context.ps1`
|
||||||
|
- Просмотр групп: `powershell -File scripts/commit/staged-context.ps1 | ConvertFrom-Json`
|
||||||
@@ -36,6 +36,15 @@
|
|||||||
- **Повторное использование:** если [docs/architecture.md](docs/architecture.md) уже описывает поток — не пересказывайте его длинно; укажите документ и конкретный подпункт задачи.
|
- **Повторное использование:** если [docs/architecture.md](docs/architecture.md) уже описывает поток — не пересказывайте его длинно; укажите документ и конкретный подпункт задачи.
|
||||||
- **Длинные планы:** `.cursor/plans/*.plan.md` — для истории решений; для навигации пользователю достаточно `docs/`; не читайте план целиком без причины.
|
- **Длинные планы:** `.cursor/plans/*.plan.md` — для истории решений; для навигации пользователю достаточно `docs/`; не читайте план целиком без причины.
|
||||||
|
|
||||||
|
## Коммиты (Conventional Commits)
|
||||||
|
|
||||||
|
Если пользователь просит **коммит**, **commit message**, **закоммить**, **git commit** или это следует из плана — **сразу**:
|
||||||
|
|
||||||
|
1. Shell: `powershell -NoProfile -File scripts/commit/staged-context.ps1` (первый вызов, до текста коммита).
|
||||||
|
2. Скилл [.cursor/skills/commit-message/SKILL.md](.cursor/skills/commit-message/SKILL.md) и правило [.cursor/rules/conventional-commits.mdc](.cursor/rules/conventional-commits.mdc).
|
||||||
|
|
||||||
|
Без вывода скрипта (exit 0) **не** придумывать сообщение коммита. Заголовок — EN, тело — RU; несвязанные области — auto-split (скилл).
|
||||||
|
|
||||||
## Команды и среда
|
## Команды и среда
|
||||||
|
|
||||||
- Консоль пользователя: **PowerShell**; пути в стиле `deploy\compose`.
|
- Консоль пользователя: **PowerShell**; пути в стиле `deploy\compose`.
|
||||||
|
|||||||
@@ -31,6 +31,11 @@
|
|||||||
| [../.cursor/rules/engineering.mdc](../.cursor/rules/engineering.mdc) | Go, API, migrations, security, enforcement |
|
| [../.cursor/rules/engineering.mdc](../.cursor/rules/engineering.mdc) | Go, API, migrations, security, enforcement |
|
||||||
| [../.cursor/rules/web-shadcn.mdc](../.cursor/rules/web-shadcn.mdc) | SvelteKit, shadcn-svelte |
|
| [../.cursor/rules/web-shadcn.mdc](../.cursor/rules/web-shadcn.mdc) | SvelteKit, shadcn-svelte |
|
||||||
| [../.cursor/rules/networking-bird.mdc](../.cursor/rules/networking-bird.mdc) | BIRD2, BGP policy, IP/CIDR |
|
| [../.cursor/rules/networking-bird.mdc](../.cursor/rules/networking-bird.mdc) | BIRD2, BGP policy, IP/CIDR |
|
||||||
|
| [../.cursor/rules/conventional-commits.mdc](../.cursor/rules/conventional-commits.mdc) | Conventional Commits (заголовок EN, тело RU) |
|
||||||
|
|
||||||
|
### Сообщения коммитов (Cursor)
|
||||||
|
|
||||||
|
После `git add` попросите агента: **«сгенерируй коммит по staged»**, **«закоммить»**, **«commit message»** — агент **обязан первым делом** запустить `scripts/commit/staged-context.ps1`, затем скилл [commit-message](../.cursor/skills/commit-message/SKILL.md) (заголовок EN, тело RU, auto-split). Просмотр групп вручную: `powershell -NoProfile -File scripts/commit/staged-context.ps1 | ConvertFrom-Json`.
|
||||||
|
|
||||||
## Репозиторий и CI
|
## Репозиторий и CI
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#Requires -Version 5.1
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Staged git changes grouped by EvoBGP scope for Conventional Commit generation.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Outputs JSON to stdout: file groups, --stat, and truncated patch per group.
|
||||||
|
Exit 1 if nothing is staged. Does not write commit messages.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
powershell -NoProfile -File scripts/commit/staged-context.ps1
|
||||||
|
powershell -NoProfile -File scripts/commit/staged-context.ps1 | ConvertFrom-Json
|
||||||
|
#>
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$MaxLinesPerGroup = 120
|
||||||
|
$MaxLinesTotal = 500
|
||||||
|
|
||||||
|
function Test-GitRepository {
|
||||||
|
$null = git rev-parse --git-dir 2>$null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error 'staged-context: not a git repository'
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ScopeForPath {
|
||||||
|
param([string]$Path)
|
||||||
|
$p = $Path -replace '\\', '/'
|
||||||
|
|
||||||
|
# Longest / most specific prefixes first
|
||||||
|
$rules = @(
|
||||||
|
@{ Prefix = 'docs/openapi.yaml'; Scope = 'openapi' }
|
||||||
|
@{ Prefix = 'redocly.yaml'; Scope = 'openapi' }
|
||||||
|
@{ Prefix = 'internal/httpapi/'; Scope = 'httpapi' }
|
||||||
|
@{ Prefix = 'internal/store/'; Scope = 'store' }
|
||||||
|
@{ Prefix = 'internal/repository/'; Scope = 'store' }
|
||||||
|
@{ Prefix = 'internal/db/'; Scope = 'store' }
|
||||||
|
@{ Prefix = 'internal/jobs/'; Scope = 'jobs' }
|
||||||
|
@{ Prefix = 'internal/pipeline/'; Scope = 'pipeline' }
|
||||||
|
@{ Prefix = 'internal/birdfmt/'; Scope = 'birdfmt' }
|
||||||
|
@{ Prefix = 'internal/birddeploy/'; Scope = 'birddeploy' }
|
||||||
|
@{ Prefix = 'internal/bundle/'; Scope = 'bundle' }
|
||||||
|
@{ Prefix = 'internal/signing/'; Scope = 'bundle' }
|
||||||
|
@{ Prefix = 'cmd/'; Scope = 'cmd' }
|
||||||
|
@{ Prefix = 'web/'; Scope = 'web' }
|
||||||
|
@{ Prefix = 'migrations/'; Scope = 'db' }
|
||||||
|
@{ Prefix = 'docs/'; Scope = 'docs' }
|
||||||
|
@{ Prefix = '.gitea/'; Scope = 'ci' }
|
||||||
|
@{ Prefix = 'deploy/'; Scope = 'deploy' }
|
||||||
|
@{ Prefix = '.cursor/'; Scope = 'chore' }
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($rule in $rules) {
|
||||||
|
if ($p -eq $rule.Prefix.TrimEnd('/') -or $p.StartsWith($rule.Prefix)) {
|
||||||
|
return $rule.Scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'chore'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ScopeSortOrder {
|
||||||
|
param([string]$Scope)
|
||||||
|
$order = @{
|
||||||
|
openapi = 10
|
||||||
|
httpapi = 20
|
||||||
|
store = 21
|
||||||
|
jobs = 22
|
||||||
|
pipeline = 30
|
||||||
|
birdfmt = 31
|
||||||
|
birddeploy = 32
|
||||||
|
bundle = 33
|
||||||
|
web = 40
|
||||||
|
ci = 50
|
||||||
|
deploy = 51
|
||||||
|
db = 52
|
||||||
|
docs = 60
|
||||||
|
cmd = 70
|
||||||
|
chore = 80
|
||||||
|
}
|
||||||
|
if ($order.ContainsKey($Scope)) { return $order[$Scope] }
|
||||||
|
return 99
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Git {
|
||||||
|
param([string[]]$GitArgs)
|
||||||
|
$out = & git @GitArgs 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
$msg = ($out | Out-String).Trim()
|
||||||
|
throw "git $($GitArgs -join ' ') failed: $msg"
|
||||||
|
}
|
||||||
|
return ($out | Out-String).TrimEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
function New-GitArgs {
|
||||||
|
param([string[]]$Base, [string[]]$Paths)
|
||||||
|
if ($Paths.Count -eq 0) { return $Base }
|
||||||
|
return $Base + '--' + $Paths
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-TruncatedDiff {
|
||||||
|
param(
|
||||||
|
[string[]]$Files,
|
||||||
|
[int]$MaxLines,
|
||||||
|
[ref]$TotalLinesUsed
|
||||||
|
)
|
||||||
|
if ($Files.Count -eq 0) { return '' }
|
||||||
|
|
||||||
|
$remaining = $MaxLinesTotal - $TotalLinesUsed.Value
|
||||||
|
if ($remaining -le 0) {
|
||||||
|
return '[diff truncated: global line budget exceeded]'
|
||||||
|
}
|
||||||
|
|
||||||
|
$cap = [Math]::Min($MaxLines, $remaining)
|
||||||
|
$diff = Invoke-Git -GitArgs (New-GitArgs -Base @(
|
||||||
|
'diff', '--cached', '--no-color', '--unified=3'
|
||||||
|
) -Paths $Files)
|
||||||
|
if ([string]::IsNullOrWhiteSpace($diff)) { return '' }
|
||||||
|
|
||||||
|
$lines = $diff -split "`n", -1
|
||||||
|
if ($lines.Count -le $cap) {
|
||||||
|
$TotalLinesUsed.Value += $lines.Count
|
||||||
|
return $diff
|
||||||
|
}
|
||||||
|
|
||||||
|
$truncated = ($lines[0..($cap - 1)] -join "`n") + "`n... [truncated: $($lines.Count - $cap) more lines]"
|
||||||
|
$TotalLinesUsed.Value += $cap
|
||||||
|
return $truncated
|
||||||
|
}
|
||||||
|
|
||||||
|
Test-GitRepository
|
||||||
|
|
||||||
|
$stagedRaw = Invoke-Git -GitArgs @('diff', '--cached', '--name-only')
|
||||||
|
if ([string]::IsNullOrWhiteSpace($stagedRaw)) {
|
||||||
|
[Console]::Error.WriteLine('staged-context: no staged changes (git index is empty)')
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$stagedFiles = @(
|
||||||
|
$stagedRaw -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
|
||||||
|
)
|
||||||
|
$stagedFiles = $stagedFiles | Sort-Object -Unique
|
||||||
|
|
||||||
|
$scopeBuckets = @{}
|
||||||
|
foreach ($f in $stagedFiles) {
|
||||||
|
$scope = Get-ScopeForPath -Path $f
|
||||||
|
if (-not $scopeBuckets.ContainsKey($scope)) {
|
||||||
|
$scopeBuckets[$scope] = [System.Collections.Generic.List[string]]::new()
|
||||||
|
}
|
||||||
|
$scopeBuckets[$scope].Add($f) | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalLinesUsed = 0
|
||||||
|
$groups = New-Object System.Collections.Generic.List[object]
|
||||||
|
|
||||||
|
$scopeKeys = @($scopeBuckets.Keys | Sort-Object { Get-ScopeSortOrder $_ })
|
||||||
|
|
||||||
|
foreach ($scope in $scopeKeys) {
|
||||||
|
$files = @($scopeBuckets[$scope] | Sort-Object)
|
||||||
|
$stat = Invoke-Git -GitArgs (New-GitArgs -Base @('diff', '--cached', '--stat') -Paths $files)
|
||||||
|
$diffExcerpt = Get-TruncatedDiff -Files $files -MaxLines $MaxLinesPerGroup -TotalLinesUsed ([ref]$totalLinesUsed)
|
||||||
|
|
||||||
|
$groups.Add([ordered]@{
|
||||||
|
scope = $scope
|
||||||
|
files = $files
|
||||||
|
stat = $stat
|
||||||
|
diff_excerpt = $diffExcerpt
|
||||||
|
}) | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = [ordered]@{
|
||||||
|
staged_count = $stagedFiles.Count
|
||||||
|
groups = $groups.ToArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = $result | ConvertTo-Json -Depth 6 -Compress:$false
|
||||||
|
# UTF-8 stdout for agents / ConvertFrom-Json
|
||||||
|
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
||||||
|
Write-Output $json
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user