Initial commit
This commit is contained in:
136
.claude/hooks/check-javadoc.ps1
Normal file
136
.claude/hooks/check-javadoc.ps1
Normal file
@@ -0,0 +1,136 @@
|
||||
<#
|
||||
.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
|
||||
38
.claude/settings.json
Normal file
38
.claude/settings.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(./gradlew test:*)",
|
||||
"Bash(./gradlew clean test:*)",
|
||||
"Bash(./gradlew build:*)",
|
||||
"Bash(./gradlew check:*)",
|
||||
"Bash(./gradlew clean check:*)",
|
||||
"Bash(./gradlew ideaFormat:*)",
|
||||
"PowerShell(.\\gradlew.bat test:*)",
|
||||
"PowerShell(.\\gradlew.bat clean test:*)",
|
||||
"PowerShell(.\\gradlew.bat build:*)",
|
||||
"PowerShell(.\\gradlew.bat check:*)",
|
||||
"PowerShell(.\\gradlew.bat clean check:*)",
|
||||
"PowerShell(.\\gradlew.bat ideaFormat:*)",
|
||||
"Bash(git status:*)",
|
||||
"Bash(git diff:*)",
|
||||
"Bash(git log:*)",
|
||||
"PowerShell(git status:*)",
|
||||
"PowerShell(git diff:*)",
|
||||
"PowerShell(git log:*)"
|
||||
]
|
||||
},
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File .claude/hooks/check-javadoc.ps1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
25
.claude/skills/verify-mcp-server-change/SKILL.md
Normal file
25
.claude/skills/verify-mcp-server-change/SKILL.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: verify-mcp-server-change
|
||||
description: Review, implement, and verify changes to the AX HUB Java MCP server, including mandatory Korean class and method comments for added or modified production source. Use for JSON-RPC parsing or errors, MCP method handlers, Tool Registry metadata or fallback, tool execution planning and routing, streaming responses, trace or audit behavior, Spring profiles, and OpenShift deployment changes. Do not use for documentation-only edits unrelated to server behavior.
|
||||
---
|
||||
|
||||
# Verify MCP Server Change
|
||||
|
||||
이 skill의 정본은 `.agents/skills/verify-mcp-server-change/`다. Codex와 Claude Code가 같은 절차를 쓰도록
|
||||
내용을 복제하지 않고 참조한다.
|
||||
|
||||
## 시작할 때 읽을 파일
|
||||
|
||||
1. [.agents/skills/verify-mcp-server-change/SKILL.md](../../../.agents/skills/verify-mcp-server-change/SKILL.md)
|
||||
— workflow 9단계와 필수 한글 소스 주석 규칙.
|
||||
2. [.agents/skills/verify-mcp-server-change/references/change-checklist.md](../../../.agents/skills/verify-mcp-server-change/references/change-checklist.md)
|
||||
— 변경 성격에 해당하는 절만 골라 읽는다.
|
||||
|
||||
두 파일을 읽고 그 절차를 그대로 따른다. 아래는 Claude Code 환경에서만 다른 점이다.
|
||||
|
||||
## Claude Code 실행 메모
|
||||
|
||||
- 검증 명령: `.\gradlew.bat test` (의존성·packaging·profile·배포 변경은 `clean test`)
|
||||
- 공개 응답 모양을 바꾸면 `docs/contracts/agent-builder-mcp/examples/agentbuilder-v0.3/`의 JSON과
|
||||
`AgentBuilderContractExampleTest`가 함께 바뀌어야 한다. 예제만 고치고 코드를 두거나 그 반대로 두지 않는다.
|
||||
- 한글 Javadoc 누락은 `.claude/hooks/check-javadoc.ps1`이 편집 직후 알려 준다. hook 경고를 무시한 채 완료 보고하지 않는다.
|
||||
Reference in New Issue
Block a user