137 lines
5.0 KiB
PowerShell
137 lines
5.0 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
src/main/java의 추가·수정 소스에 한글 Javadoc이 붙어 있는지 확인하는 PostToolUse hook.
|
|
|
|
.DESCRIPTION
|
|
`.agents/skills/verify-mcp-server-change/SKILL.md`의 "필수 한글 소스 주석" 규칙을 지침이 아니라
|
|
결정적 검사로 강제한다. 최상위 type 선언과 indent 4의 method/constructor 선언 바로 위에
|
|
Javadoc(`*/`로 끝나는 블록)이 있는지만 본다. 파일을 고치지 않으며 build에도 관여하지 않는다.
|
|
|
|
Hook 모드: Claude Code가 stdin으로 넘긴 JSON에서 tool_input.file_path를 읽는다.
|
|
누락이 있으면 stderr로 알리고 exit 2로 Claude에게 되돌린다.
|
|
|
|
수동 모드: -Path 로 파일 또는 디렉터리를 직접 검사한다. 예)
|
|
pwsh .claude/hooks/check-javadoc.ps1 -Path src/main/java
|
|
#>
|
|
param(
|
|
[string]$Path
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Get-MissingJavadoc {
|
|
param([string]$File)
|
|
|
|
$lines = Get-Content -LiteralPath $File -Encoding utf8
|
|
$findings = @()
|
|
|
|
for ($i = 0; $i -lt $lines.Count; $i++) {
|
|
$line = $lines[$i]
|
|
if ([string]::IsNullOrWhiteSpace($line)) { continue }
|
|
|
|
$kind = $null
|
|
|
|
# 최상위 type 선언 (indent 0). 중첩 type은 skill 규칙의 강제 대상이 아니다.
|
|
if ($line -match '^(?:(?:public|final|abstract|sealed|non-sealed)\s+)*(class|interface|record|enum)\s+\w') {
|
|
$kind = 'type'
|
|
}
|
|
# 정확히 indent 4인 선언만 본다. 8칸 이상은 method 본문이나 이어지는 인자 목록이다.
|
|
elseif ($line -match '^ {4}\S') {
|
|
$body = $line.Substring(4)
|
|
# 중첩 type 선언은 건너뛴다.
|
|
if ($body -match '^(?:(?:public|protected|private|static|final|abstract|sealed|non-sealed)\s+)*(class|interface|record|enum)\s+\w') {
|
|
continue
|
|
}
|
|
# compact record constructor: `public Protocol {`
|
|
if ($body -match '^(public|protected|private)\s+\w+\s*\{\s*$') {
|
|
$kind = 'method'
|
|
}
|
|
# method/constructor: `(` 앞에 토큰이 둘 이상이라 enum 상수(`NAME(...)`)와 구분된다.
|
|
elseif ($body -match '^([^(){};=]*\S)\s*\(' -and $matches[1] -match '\s' -and
|
|
($body -match '\{\s*$' -or $body -match ';\s*$' -or $body -match '\(\s*$')) {
|
|
$kind = 'method'
|
|
}
|
|
}
|
|
|
|
if (-not $kind) { continue }
|
|
|
|
# 선언 위로 올라가며 Javadoc 블록의 끝(`*/`)을 찾는다.
|
|
# 빈 줄, annotation, 그리고 선언보다 깊게 들여쓴 줄(여러 줄 annotation의 이어지는 인자)은 건너뛴다.
|
|
$declIndent = $line.Length - $line.TrimStart(' ').Length
|
|
$hasJavadoc = $false
|
|
$j = $i - 1
|
|
while ($j -ge 0) {
|
|
$prev = $lines[$j]
|
|
$trimmed = $prev.Trim()
|
|
if ($trimmed -match '\*/$') { $hasJavadoc = $true; break }
|
|
$prevIndent = $prev.Length - $prev.TrimStart(' ').Length
|
|
if ($trimmed -eq '' -or $trimmed -match '^@\w' -or $prevIndent -gt $declIndent) { $j--; continue }
|
|
break
|
|
}
|
|
|
|
if (-not $hasJavadoc) {
|
|
$findings += [pscustomobject]@{
|
|
File = $File
|
|
Line = $i + 1
|
|
Kind = $kind
|
|
Text = $line.Trim()
|
|
}
|
|
}
|
|
}
|
|
return $findings
|
|
}
|
|
|
|
function Test-Target {
|
|
param([string]$Candidate)
|
|
if ([string]::IsNullOrWhiteSpace($Candidate)) { return $false }
|
|
if ($Candidate -notmatch '\.java$') { return $false }
|
|
return ($Candidate -replace '\\', '/') -match 'src/main/java/'
|
|
}
|
|
|
|
# --- 대상 수집 ---
|
|
$targets = @()
|
|
if ($Path) {
|
|
if (Test-Path -LiteralPath $Path -PathType Container) {
|
|
$targets = Get-ChildItem -LiteralPath $Path -Recurse -Filter *.java |
|
|
ForEach-Object { $_.FullName } | Where-Object { Test-Target $_ }
|
|
}
|
|
elseif (Test-Target $Path) {
|
|
$targets = @($Path)
|
|
}
|
|
}
|
|
else {
|
|
try {
|
|
$raw = [Console]::In.ReadToEnd()
|
|
if ([string]::IsNullOrWhiteSpace($raw)) { exit 0 }
|
|
$event = $raw | ConvertFrom-Json
|
|
$file = $event.tool_input.file_path
|
|
if (Test-Target $file) { $targets = @($file) }
|
|
}
|
|
catch {
|
|
# Hook은 편집 흐름을 막지 않는다. 입력을 못 읽으면 조용히 통과시킨다.
|
|
exit 0
|
|
}
|
|
}
|
|
|
|
if ($targets.Count -eq 0) { exit 0 }
|
|
|
|
$all = @()
|
|
foreach ($t in $targets) {
|
|
if (Test-Path -LiteralPath $t) { $all += Get-MissingJavadoc -File $t }
|
|
}
|
|
|
|
if ($all.Count -eq 0) {
|
|
if ($Path) { Write-Host "javadoc ok: $($targets.Count) file(s)" }
|
|
exit 0
|
|
}
|
|
|
|
$report = ($all | ForEach-Object {
|
|
" {0}:{1} [{2}] {3}" -f (Resolve-Path -LiteralPath $_.File -Relative), $_.Line, $_.Kind, $_.Text
|
|
}) -join "`n"
|
|
|
|
[Console]::Error.WriteLine(
|
|
"한글 Javadoc 누락 ($($all.Count)건). " +
|
|
".agents/skills/verify-mcp-server-change/SKILL.md의 '필수 한글 소스 주석' 규칙에 따라 " +
|
|
"역할·처리 단계·협력 객체를 설명하는 주석을 선언 바로 위에 추가하세요.`n$report")
|
|
exit 2
|