Skip to content

Commit 4e5ebfc

Browse files
authored
카테고리 관리 목록에 Figma 디자인을 적용한다 (#934)
* chore: UIComposable 0.3.0 적용 * ui: 카드형 컬렉션 뷰 컴포넌트 추가 * ui: 카테고리 관리 목록에 Figma 디자인 적용 * chore: 사용하지 않는 CheckBox 제거 * chore: pre-push 코드 리뷰 훅 추가 * chore: 코드 리뷰 봇용 AGENTS.md 제거
1 parent 40dade9 commit 4e5ebfc

12 files changed

Lines changed: 603 additions & 199 deletions

File tree

‎.githooks/pre-push‎

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
remote=$1
5+
schema=$(dirname "$0")/review-result.schema.json
6+
result_dir=
7+
8+
trap 'if [ -n "$result_dir" ]; then rm -rf "$result_dir"; fi' 0
9+
trap 'exit 1' HUP INT TERM
10+
11+
while read -r local_ref local_sha remote_ref remote_sha
12+
do
13+
case "$remote_ref" in
14+
refs/heads/*) ;;
15+
*) continue ;;
16+
esac
17+
18+
case "$local_sha" in
19+
*[!0]*) ;;
20+
*) continue ;;
21+
esac
22+
23+
if [ "$local_sha" = "$remote_sha" ]; then
24+
continue
25+
fi
26+
27+
case "$remote_sha" in
28+
*[!0]*)
29+
base=$remote_sha
30+
if ! git cat-file -e "$base^{commit}" 2>/dev/null; then
31+
printf '%s\n' "리뷰 기준 커밋을 찾을 수 없습니다. git fetch $remote 실행 후 다시 푸시하세요." >&2
32+
exit 1
33+
fi
34+
;;
35+
*)
36+
if ! base_ref=$(git symbolic-ref --quiet "refs/remotes/$remote/HEAD"); then
37+
printf '%s\n' "원격 기본 브랜치를 찾을 수 없습니다. git remote set-head $remote -a 실행 후 다시 푸시하세요." >&2
38+
exit 1
39+
fi
40+
if ! base=$(git merge-base "$base_ref" "$local_sha"); then
41+
printf '%s\n' '리뷰 기준이 될 공통 조상 커밋을 찾을 수 없습니다.' >&2
42+
exit 1
43+
fi
44+
;;
45+
esac
46+
47+
if git diff --quiet "$base" "$local_sha" --; then
48+
continue
49+
else
50+
status=$?
51+
if [ "$status" -ne 1 ]; then
52+
exit "$status"
53+
fi
54+
fi
55+
56+
if ! command -v claude >/dev/null 2>&1; then
57+
printf '%s\n' 'Claude Code CLI를 찾을 수 없습니다. 설치와 로그인을 확인하세요.' >&2
58+
exit 1
59+
fi
60+
if ! command -v python3 >/dev/null 2>&1; then
61+
printf '%s\n' '리뷰 결과 판독에 필요한 python3를 찾을 수 없습니다.' >&2
62+
exit 1
63+
fi
64+
if [ -z "$result_dir" ]; then
65+
result_dir=$(mktemp -d "${TMPDIR:-/tmp}/devlog-pre-push.XXXXXX")
66+
fi
67+
result=$result_dir/result.json
68+
rm -f "$result"
69+
schema_json=$(cat "$schema")
70+
71+
printf '%s\n' "Claude 리뷰 시작: $local_ref ($base..$local_sha)" >&2
72+
if ! claude -p \
73+
--model claude-opus-5-5 \
74+
--effort high \
75+
--permission-mode plan \
76+
--permission-prompts none \
77+
--no-session-persistence \
78+
--output-format json \
79+
--json-schema "$schema_json" \
80+
<<EOF > "$result"
81+
You are code_reviewer for a user-authorized pre-push review in $(pwd).
82+
This is a separate Claude Code CLI review. Perform the review yourself without spawning subagents or other agents.
83+
84+
Read CLAUDE.md and fetch its DevLog Agent Policy index through the connected Notion MCP before inspecting code. Verify that the index is under the DevLog page and use only policies marked Active. Load the Active General, iOS, and Claude Code policies and every Active task-specific policy whose route matches this review. Apply the current code_reviewer contract.
85+
If Notion MCP, the policy index, any required Active policy, or required code is unavailable, set verdict to "BLOCKED" and report the missing source. Do not use historical memory as a policy fallback.
86+
87+
Review only the committed changes from $base to $local_sha for $local_ref being pushed to $remote_ref.
88+
Use git diff $base $local_sha -- for the exact change set. Inspect supporting files at $local_sha with git show. Do not substitute HEAD or include unrelated staged, unstaged, or untracked changes.
89+
Use the current request or issue and acceptance criteria when identifiable. Keep the review within the actual diff and applicable scope exclusions.
90+
91+
Inspect correctness, regressions, error and cancellation handling, concurrency and resource lifetimes, persistence and event integrity, architecture and dependency boundaries, and meaningful test coverage against existing contracts.
92+
Inspect layout that can break on compact iPhones such as mini and SE models, including hardcoded dimensions, fixed widths or heights, and fixed offsets that do not adapt to the available width, height, or Safe Area. Report a finding only with the concrete screen condition and the element that overflows, clips, or overlaps.
93+
Read only. Do not create, edit, or delete files, policies, Git state, or external records. Do not request escalation, build, execute tests, or run, launch, install, boot, or open the app or Simulator.
94+
95+
Return a JSON object matching the provided JSON schema, with only "verdict" and "review" fields.
96+
Set verdict to "FAIL" when an actionable defect requires correction. Set verdict to "PASS" only when no blocking finding exists and every required policy and code source was available. Set verdict to "BLOCKED" if a required source or execution setting is unavailable.
97+
In review, state the inspected scope, available verification evidence, unexecuted checks, and remaining verification limits. For PASS, explicitly state that there are no blocking findings. A PASS does not establish that tests passed or runtime behavior was verified.
98+
Write review in Korean and preserve implementation names. Include file paths and line numbers for actionable findings.
99+
EOF
100+
then
101+
printf '%s\n' 'Claude 리뷰 실행에 실패하여 푸시를 중단합니다.' >&2
102+
exit 1
103+
fi
104+
105+
if ! python3 - "$result" <<'PY'
106+
import json
107+
import sys
108+
109+
110+
def unique_object(pairs):
111+
result = dict(pairs)
112+
if len(result) != len(pairs):
113+
raise ValueError("duplicate keys")
114+
return result
115+
116+
117+
try:
118+
with open(sys.argv[1], encoding="utf-8") as file:
119+
envelope = json.load(file, object_pairs_hook=unique_object)
120+
if (
121+
not isinstance(envelope, dict)
122+
or envelope.get("type") != "result"
123+
or envelope.get("subtype") != "success"
124+
or envelope.get("is_error") is not False
125+
):
126+
raise ValueError("unsuccessful claude result")
127+
result = envelope.get("structured_output")
128+
if result is None:
129+
text = envelope.get("result")
130+
if not isinstance(text, str):
131+
raise ValueError("missing review result")
132+
result = json.loads(text, object_pairs_hook=unique_object)
133+
if (
134+
not isinstance(result, dict)
135+
or set(result) != {"verdict", "review"}
136+
or not isinstance(result["verdict"], str)
137+
or result["verdict"] not in {"PASS", "FAIL", "BLOCKED"}
138+
or not isinstance(result["review"], str)
139+
or not result["review"].strip()
140+
):
141+
raise ValueError("invalid review result")
142+
except (OSError, UnicodeError, ValueError):
143+
print("리뷰 결과가 없거나 형식이 올바르지 않습니다.", file=sys.stderr)
144+
sys.exit(1)
145+
146+
print(result["review"], file=sys.stderr)
147+
sys.exit(0 if result["verdict"] == "PASS" else 1)
148+
PY
149+
then
150+
printf '%s\n' 'PASS 판정을 확인하지 못하여 푸시를 중단합니다.' >&2
151+
exit 1
152+
fi
153+
done
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"verdict": {
5+
"type": "string",
6+
"enum": ["PASS", "FAIL", "BLOCKED"]
7+
},
8+
"review": {
9+
"type": "string"
10+
}
11+
},
12+
"required": ["verdict", "review"],
13+
"additionalProperties": false
14+
}

‎AGENTS.md‎

Lines changed: 0 additions & 92 deletions
This file was deleted.

‎Application/Presentation/Development/Sources/Goal/Create/GoalCreateView.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private struct GoalCreateDescriptionEditor: View {
140140
Text(RecordPresentation.text("development_goal_create_markdown_hint"))
141141
.font(.caption)
142142
.foregroundStyle(Color.textTertiary)
143-
UIKitTextEditor()
143+
UIKitTextEditor
144144
.composable(
145145
update: { textEditor in
146146
textEditor.updateInput(

‎Application/Presentation/Development/Sources/Record/Editor/RecordEditorView.swift‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public struct RecordEditorView: View {
134134
Text(RecordPresentation.text("development_record_markdown_hint"))
135135
.font(.caption)
136136
.foregroundStyle(Color.textTertiary)
137-
UIKitTextEditor()
137+
UIKitTextEditor
138138
.composable(
139139
update: { textEditor in
140140
textEditor.updateInput(

0 commit comments

Comments
 (0)