solmate-skills 2.0.5 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -0
- package/README.md +5 -3
- package/ext-k-skill/scripts/check-setup.sh +29 -0
- package/ext-k-skill/scripts/run-k-skill-proxy.sh +15 -0
- package/ext-k-skill/scripts/validate-skills.sh +56 -0
- package/hooks/install.sh +157 -0
- package/hooks/suggest-skills.sh +109 -0
- package/hooks/verify-suggest.sh +42 -0
- package/hooks/watch-files.sh +96 -0
- package/package.json +1 -1
- package/rules-react/scripts/fetch-stitch.sh +30 -0
- package/tools-shadcn/scripts/verify-setup.sh +134 -0
package/AGENTS.md
CHANGED
|
@@ -56,6 +56,15 @@
|
|
|
56
56
|
- **파일 네이밍**: 모든 파일명 앞에는 `01_`과 같은 **순번(Numbering)**을 반드시 붙여 생성 순서와 계층을 명확히 한다. (예: `01_VISION.md`, `02_UI_DESIGN.md`)
|
|
57
57
|
- **Interactive Process**: 문서를 작성할 때 AI가 독단적으로 내용을 채우지 않는다. 초안 작성 전, **반드시 사용자에게 핵심 질문을 던지고 답변을 바탕으로 문서를 작성(Ask before Write)**한다.
|
|
58
58
|
|
|
59
|
+
### 2.7. SVG 다이어그램 산출물 표준 (User Journey & Data Flow)
|
|
60
|
+
- **사용자 동선 SVG 필수화**: 주요 기능·화면 흐름 문서에는 사용자 동선(User Journey / Screen Flow)을 설명하는 SVG 다이어그램을 포함한다.
|
|
61
|
+
- **저장 위치**: 사용자 동선 SVG는 `docs/02_UI_Screens/assets/`에 저장하고, 관련 UI 스크린 문서에서 상대 경로로 참조한다.
|
|
62
|
+
- **데이터 흐름 SVG 필수화**: 주요 기능의 기술 명세에는 데이터 흐름(Data Flow / API-DB-External Service Flow)을 설명하는 SVG 다이어그램을 포함한다.
|
|
63
|
+
- **저장 위치**: 데이터 흐름 SVG는 `docs/03_Technical_Specs/assets/`에 저장하고, 관련 기술 명세 문서에서 상대 경로로 참조한다.
|
|
64
|
+
- **작업 관리 위치**: SVG 작성·수정 작업은 `docs/04_Logic_Progress/00_BACKLOG.md` 또는 `docs/04_Logic_Progress/02_EXECUTION_PLAN.md`에 태스크로 관리한다.
|
|
65
|
+
- **검증 위치**: SVG가 최신 UI·API·DB 구조와 일치하는지 여부는 `docs/05_QA_Validation/`의 QA 체크리스트 또는 검증 문서에서 확인한다.
|
|
66
|
+
- **파일 네이밍**: SVG 파일명에도 순번과 목적을 포함한다. (예: `01_user_journey.svg`, `02_auth_data_flow.svg`)
|
|
67
|
+
|
|
59
68
|
## 3. 개발 표준 및 품질
|
|
60
69
|
- **UI 중심 개발 전략 (UI-First)**: Concept_Design -> UI_Screens -> Technical_Specs -> Logic_Progress 순서를 따른다.
|
|
61
70
|
- **git commit 필수**: 중요 작업 전 반드시 git commit을 수행한다.
|
package/README.md
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Curated skills for Solmate projects. Easily share and install AI tool skills across your team.
|
|
4
4
|
|
|
5
|
-
## What's New in 2.0.
|
|
5
|
+
## What's New in 2.0.6
|
|
6
6
|
|
|
7
|
-
`solmate-skills@2.0.
|
|
7
|
+
`solmate-skills@2.0.6` keeps **Backlog Context Lock** as the current documentation guardrail, adds **UI-First Gate** expectations, formalizes SVG diagram outputs, and fixes package hygiene so installable skill scripts are included in the npm tarball.
|
|
8
8
|
|
|
9
9
|
Key changes:
|
|
10
10
|
|
|
11
11
|
- Every backlog task must link to related Concept, UI, Technical Spec, and QA documents.
|
|
12
12
|
- UI, user paths, data flow, loading states, empty states, and error states must be confirmed before coding.
|
|
13
|
+
- User journey SVG files belong in `docs/02_UI_Screens/assets/`.
|
|
14
|
+
- Data flow SVG files belong in `docs/03_Technical_Specs/assets/`.
|
|
13
15
|
- `/rules-workflow` now treats linked backlog documents as implementation inputs before coding starts.
|
|
14
16
|
- `/verify-docs` fails backlog items that omit required related-document fields.
|
|
15
|
-
- Local Codex settings under `.codex/` are excluded from the npm package.
|
|
17
|
+
- Local Codex settings under `.codex/` are excluded from the npm package, while skill-owned shell scripts remain publishable.
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
secrets_file="${1:-$HOME/.config/k-skill/secrets.env}"
|
|
5
|
+
|
|
6
|
+
missing=0
|
|
7
|
+
|
|
8
|
+
if [[ ! -f "$secrets_file" ]]; then
|
|
9
|
+
echo "missing secrets file: $secrets_file"
|
|
10
|
+
missing=1
|
|
11
|
+
else
|
|
12
|
+
perms=$(stat -f '%Lp' "$secrets_file" 2>/dev/null || stat -c '%a' "$secrets_file" 2>/dev/null)
|
|
13
|
+
if [[ "$perms" != "600" ]]; then
|
|
14
|
+
echo "insecure permissions on $secrets_file: $perms (expected 600)"
|
|
15
|
+
missing=1
|
|
16
|
+
fi
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [[ "$missing" -ne 0 ]]; then
|
|
20
|
+
cat <<EOF
|
|
21
|
+
next steps:
|
|
22
|
+
1. create ~/.config/k-skill/secrets.env with your credentials
|
|
23
|
+
2. chmod 0600 ~/.config/k-skill/secrets.env
|
|
24
|
+
3. run this check again
|
|
25
|
+
EOF
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo "k-skill setup looks usable"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
SECRETS_FILE="${KSKILL_SECRETS_FILE:-$HOME/.config/k-skill/secrets.env}"
|
|
6
|
+
|
|
7
|
+
if [[ -f "$SECRETS_FILE" ]]; then
|
|
8
|
+
set -a
|
|
9
|
+
# shellcheck disable=SC1090
|
|
10
|
+
source "$SECRETS_FILE"
|
|
11
|
+
set +a
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
cd "$ROOT_DIR"
|
|
15
|
+
exec node packages/k-skill-proxy/src/server.js
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
status=0
|
|
6
|
+
|
|
7
|
+
while IFS= read -r -d '' skill_dir; do
|
|
8
|
+
skill_name="$(basename "$skill_dir")"
|
|
9
|
+
skill_file="$skill_dir/SKILL.md"
|
|
10
|
+
|
|
11
|
+
if [[ ! -f "$skill_file" ]]; then
|
|
12
|
+
echo "missing SKILL.md: $skill_name"
|
|
13
|
+
status=1
|
|
14
|
+
continue
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if ! head -n 1 "$skill_file" | grep -qx -- "---"; then
|
|
18
|
+
echo "missing frontmatter start: $skill_file"
|
|
19
|
+
status=1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if ! grep -q '^name: ' "$skill_file"; then
|
|
23
|
+
echo "missing name field: $skill_file"
|
|
24
|
+
status=1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if ! grep -q '^description: ' "$skill_file"; then
|
|
28
|
+
echo "missing description field: $skill_file"
|
|
29
|
+
status=1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
declared_name="$(sed -n 's/^name: //p' "$skill_file" | head -n 1 | tr -d '"')"
|
|
33
|
+
if [[ "$declared_name" != "$skill_name" ]]; then
|
|
34
|
+
echo "name mismatch: $skill_file declares '$declared_name' but directory is '$skill_name'"
|
|
35
|
+
status=1
|
|
36
|
+
fi
|
|
37
|
+
done < <(
|
|
38
|
+
find "$root" -mindepth 1 -maxdepth 1 -type d \
|
|
39
|
+
! -name .git \
|
|
40
|
+
! -name .github \
|
|
41
|
+
! -name .omx \
|
|
42
|
+
! -name .changeset \
|
|
43
|
+
! -name docs \
|
|
44
|
+
! -name node_modules \
|
|
45
|
+
! -name packages \
|
|
46
|
+
! -name python-packages \
|
|
47
|
+
! -name scripts \
|
|
48
|
+
! -name examples \
|
|
49
|
+
-print0
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if [[ "$status" -ne 0 ]]; then
|
|
53
|
+
exit "$status"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
echo "skill layout looks valid"
|
package/hooks/install.sh
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# solmate-skills: install.sh
|
|
3
|
+
# Purpose: Install Solmate hook scripts into the current project's .claude/ directory
|
|
4
|
+
# and merge hook configuration into .claude/settings.json.
|
|
5
|
+
# Usage: bash .agent/skills/hooks/install.sh
|
|
6
|
+
# (or run from any location: bash <path-to-hooks>/install.sh)
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
PROJECT_ROOT="$(pwd)"
|
|
12
|
+
CLAUDE_DIR="$PROJECT_ROOT/.claude"
|
|
13
|
+
HOOKS_DIR="$CLAUDE_DIR/hooks"
|
|
14
|
+
SETTINGS_FILE="$CLAUDE_DIR/settings.json"
|
|
15
|
+
|
|
16
|
+
echo "Solmate Skills — Hook Installer"
|
|
17
|
+
echo "Project: $PROJECT_ROOT"
|
|
18
|
+
echo ""
|
|
19
|
+
|
|
20
|
+
# --- Step 1: Create .claude/hooks/ ---
|
|
21
|
+
mkdir -p "$HOOKS_DIR"
|
|
22
|
+
echo "[1/4] Created $HOOKS_DIR"
|
|
23
|
+
|
|
24
|
+
# --- Step 2: Copy hook scripts ---
|
|
25
|
+
cp "$SCRIPT_DIR/suggest-skills.sh" "$HOOKS_DIR/solmate-suggest.sh"
|
|
26
|
+
cp "$SCRIPT_DIR/watch-files.sh" "$HOOKS_DIR/solmate-watch.sh"
|
|
27
|
+
cp "$SCRIPT_DIR/verify-suggest.sh" "$HOOKS_DIR/solmate-verify-suggest.sh"
|
|
28
|
+
chmod +x "$HOOKS_DIR/solmate-suggest.sh"
|
|
29
|
+
chmod +x "$HOOKS_DIR/solmate-watch.sh"
|
|
30
|
+
chmod +x "$HOOKS_DIR/solmate-verify-suggest.sh"
|
|
31
|
+
echo "[2/4] Copied hook scripts:"
|
|
32
|
+
echo " .claude/hooks/solmate-suggest.sh (UserPromptSubmit)"
|
|
33
|
+
echo " .claude/hooks/solmate-watch.sh (PreToolUse)"
|
|
34
|
+
echo " .claude/hooks/solmate-verify-suggest.sh (Stop)"
|
|
35
|
+
|
|
36
|
+
# --- Step 3: Merge hook config into settings.json ---
|
|
37
|
+
echo "[3/4] Merging hook config into $SETTINGS_FILE"
|
|
38
|
+
|
|
39
|
+
python3 - "$SETTINGS_FILE" <<'PYEOF'
|
|
40
|
+
import sys, json, os
|
|
41
|
+
|
|
42
|
+
settings_path = sys.argv[1]
|
|
43
|
+
|
|
44
|
+
# Load existing settings or start fresh
|
|
45
|
+
if os.path.exists(settings_path):
|
|
46
|
+
with open(settings_path, 'r') as f:
|
|
47
|
+
try:
|
|
48
|
+
settings = json.load(f)
|
|
49
|
+
except json.JSONDecodeError:
|
|
50
|
+
print(f" WARNING: {settings_path} has invalid JSON. Backing up and starting fresh.")
|
|
51
|
+
os.rename(settings_path, settings_path + '.bak')
|
|
52
|
+
settings = {}
|
|
53
|
+
else:
|
|
54
|
+
settings = {}
|
|
55
|
+
|
|
56
|
+
hooks = settings.setdefault('hooks', {})
|
|
57
|
+
|
|
58
|
+
# --- UserPromptSubmit hook ---
|
|
59
|
+
suggest_cmd = "bash .claude/hooks/solmate-suggest.sh"
|
|
60
|
+
submit_hooks = hooks.setdefault('UserPromptSubmit', [])
|
|
61
|
+
|
|
62
|
+
# Check for duplicate
|
|
63
|
+
already_has_suggest = any(
|
|
64
|
+
h.get('command') == suggest_cmd
|
|
65
|
+
for entry in submit_hooks
|
|
66
|
+
for h in entry.get('hooks', [])
|
|
67
|
+
)
|
|
68
|
+
if not already_has_suggest:
|
|
69
|
+
submit_hooks.append({
|
|
70
|
+
"hooks": [{
|
|
71
|
+
"type": "command",
|
|
72
|
+
"command": suggest_cmd,
|
|
73
|
+
"timeout": 5
|
|
74
|
+
}]
|
|
75
|
+
})
|
|
76
|
+
print(" Added: UserPromptSubmit → solmate-suggest.sh")
|
|
77
|
+
else:
|
|
78
|
+
print(" Skipped (already exists): UserPromptSubmit → solmate-suggest.sh")
|
|
79
|
+
|
|
80
|
+
# --- PreToolUse hook ---
|
|
81
|
+
watch_cmd = "bash .claude/hooks/solmate-watch.sh"
|
|
82
|
+
pre_hooks = hooks.setdefault('PreToolUse', [])
|
|
83
|
+
|
|
84
|
+
already_has_watch = any(
|
|
85
|
+
h.get('command') == watch_cmd
|
|
86
|
+
for entry in pre_hooks
|
|
87
|
+
for h in entry.get('hooks', [])
|
|
88
|
+
)
|
|
89
|
+
if not already_has_watch:
|
|
90
|
+
pre_hooks.append({
|
|
91
|
+
"matcher": "Read|Write|Edit|Bash",
|
|
92
|
+
"hooks": [{
|
|
93
|
+
"type": "command",
|
|
94
|
+
"command": watch_cmd,
|
|
95
|
+
"timeout": 5
|
|
96
|
+
}]
|
|
97
|
+
})
|
|
98
|
+
print(" Added: PreToolUse (Read|Write|Edit|Bash) → solmate-watch.sh")
|
|
99
|
+
else:
|
|
100
|
+
print(" Skipped (already exists): PreToolUse → solmate-watch.sh")
|
|
101
|
+
|
|
102
|
+
# --- Stop hook ---
|
|
103
|
+
verify_cmd = "bash .claude/hooks/solmate-verify-suggest.sh"
|
|
104
|
+
stop_hooks = hooks.setdefault('Stop', [])
|
|
105
|
+
|
|
106
|
+
already_has_verify = any(
|
|
107
|
+
h.get('command') == verify_cmd
|
|
108
|
+
for entry in stop_hooks
|
|
109
|
+
for h in entry.get('hooks', [])
|
|
110
|
+
)
|
|
111
|
+
if not already_has_verify:
|
|
112
|
+
stop_hooks.append({
|
|
113
|
+
"hooks": [{
|
|
114
|
+
"type": "command",
|
|
115
|
+
"command": verify_cmd,
|
|
116
|
+
"timeout": 10,
|
|
117
|
+
"statusMessage": "변경 파일 분석 중..."
|
|
118
|
+
}]
|
|
119
|
+
})
|
|
120
|
+
print(" Added: Stop → solmate-verify-suggest.sh")
|
|
121
|
+
else:
|
|
122
|
+
print(" Skipped (already exists): Stop → solmate-verify-suggest.sh")
|
|
123
|
+
|
|
124
|
+
# Write back
|
|
125
|
+
os.makedirs(os.path.dirname(settings_path), exist_ok=True)
|
|
126
|
+
with open(settings_path, 'w') as f:
|
|
127
|
+
json.dump(settings, f, indent=2, ensure_ascii=False)
|
|
128
|
+
f.write('\n')
|
|
129
|
+
|
|
130
|
+
print(f" Saved: {settings_path}")
|
|
131
|
+
PYEOF
|
|
132
|
+
|
|
133
|
+
# --- Step 4: Add .claude/hooks/ to .gitignore if not already there ---
|
|
134
|
+
GITIGNORE="$PROJECT_ROOT/.gitignore"
|
|
135
|
+
if [ -f "$GITIGNORE" ]; then
|
|
136
|
+
if ! grep -q "\.claude/hooks/" "$GITIGNORE" 2>/dev/null; then
|
|
137
|
+
echo "" >> "$GITIGNORE"
|
|
138
|
+
echo "# Solmate hook scripts (project-local)" >> "$GITIGNORE"
|
|
139
|
+
echo ".claude/hooks/" >> "$GITIGNORE"
|
|
140
|
+
echo "[4/4] Added .claude/hooks/ to .gitignore"
|
|
141
|
+
else
|
|
142
|
+
echo "[4/4] .gitignore already excludes .claude/hooks/"
|
|
143
|
+
fi
|
|
144
|
+
else
|
|
145
|
+
echo "[4/4] No .gitignore found — skipping"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
echo ""
|
|
149
|
+
echo "Done. Hooks are active in this project."
|
|
150
|
+
echo ""
|
|
151
|
+
echo "What was installed:"
|
|
152
|
+
echo " UserPromptSubmit → 프롬프트 키워드 감지 → 관련 스킬 제안"
|
|
153
|
+
echo " PreToolUse → 편집 중인 파일 패턴 감지 → 관련 스킬 제안"
|
|
154
|
+
echo " Stop → 작업 완료 후 변경 파일 분석 → verify-* 스킬 실행 시점 알림"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "To review or disable hooks, open /hooks in Claude Code."
|
|
157
|
+
echo "To uninstall, remove .claude/hooks/ and the 'hooks' section from .claude/settings.json."
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# solmate-skills: suggest-skills.sh
|
|
3
|
+
# Event: UserPromptSubmit
|
|
4
|
+
# Purpose: Detect keywords in user prompt and inject skill suggestions as context.
|
|
5
|
+
# Output: JSON with hookSpecificOutput.additionalContext (non-blocking)
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# Read stdin JSON
|
|
10
|
+
INPUT=$(cat)
|
|
11
|
+
|
|
12
|
+
# Extract the user prompt text
|
|
13
|
+
PROMPT=$(echo "$INPUT" | python3 -c "
|
|
14
|
+
import sys, json
|
|
15
|
+
data = json.load(sys.stdin)
|
|
16
|
+
# UserPromptSubmit provides 'prompt' field
|
|
17
|
+
print(data.get('prompt', ''))
|
|
18
|
+
" 2>/dev/null || echo "")
|
|
19
|
+
|
|
20
|
+
if [ -z "$PROMPT" ]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
SUGGESTIONS=""
|
|
25
|
+
|
|
26
|
+
# --- Keyword matching (Korean + English) ---
|
|
27
|
+
|
|
28
|
+
# Decision / Design → manage-decisions
|
|
29
|
+
if echo "$PROMPT" | grep -qiE '결정|설계|어떻게 할|어떤 방식|DB 스키마|테이블|API 구조|아키텍처|폴더 구조|기능 범위|MVP|스택|라이브러리|어떤 거|어떤게'; then
|
|
30
|
+
SUGGESTIONS="$SUGGESTIONS\n- 결정이 필요한 상황입니다. \`/manage-decisions\`를 실행하면 유형별 질문 템플릿으로 대화를 통해 결정을 이끌어냅니다."
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Security → verify-security
|
|
34
|
+
if echo "$PROMPT" | grep -qiE '보안|security|취약|OWASP|인증|인가|token|jwt|secret|API ?key|sql|injection|xss|csrf'; then
|
|
35
|
+
SUGGESTIONS="$SUGGESTIONS\n- 보안 관련 작업입니다. \`/verify-security\`로 OWASP Top 10 기준 점검을 실행하세요."
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Performance → verify-performance
|
|
39
|
+
if echo "$PROMPT" | grep -qiE '성능|performance|lighthouse|느리|느림|최적화|LCP|CLS|FID|번들|bundle|이미지 최적|lazy|loading'; then
|
|
40
|
+
SUGGESTIONS="$SUGGESTIONS\n- 성능 관련 작업입니다. \`/verify-performance\`로 Lighthouse 및 Core Web Vitals 점검을 실행하세요."
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# PR / Commit / Code review → verify-code
|
|
44
|
+
if echo "$PROMPT" | grep -qiE 'PR|pull ?request|코드 ?리뷰|review|commit|머지|merge|배포 전|pre-deploy'; then
|
|
45
|
+
SUGGESTIONS="$SUGGESTIONS\n- PR 또는 배포 전 점검입니다. \`/verify-code\`로 코드 품질을 종합 리뷰하고, \`/verify-implementation\`으로 전체 verify-* 스킬을 통합 실행하세요."
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Documentation → verify-docs / docs-plan / docs-dev
|
|
49
|
+
if echo "$PROMPT" | grep -qiE '문서|docs?|README|VISION|LEAN_CANVAS|PRODUCT_SPECS|API_SPECS|DB_SCHEMA|ROADMAP|백로그|backlog'; then
|
|
50
|
+
SUGGESTIONS="$SUGGESTIONS\n- 문서 작업입니다. 기획·UI 문서는 \`/docs-plan\`, 기술·진행·QA 문서는 \`/docs-dev\`, 문서 구조 검증은 \`/verify-docs\`를 사용하세요."
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Feature implementation / workflow → rules-workflow
|
|
54
|
+
if echo "$PROMPT" | grep -qiE '기능 ?구현|구현|implement|feature|작업 ?시작|어디서 ?시작|어떻게 ?시작|개발 ?시작|시작할게'; then
|
|
55
|
+
SUGGESTIONS="$SUGGESTIONS\n- 기능 구현을 시작하려 합니다. 먼저 \`/rules-product\`로 현재 Phase를 진단하고 Flow Status Block을 확인한 뒤, \`/rules-workflow\`로 18단계 구현 워크플로우를 진행하세요."
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Flow position question → rules-product
|
|
59
|
+
if echo "$PROMPT" | grep -qiE '지금.*어디|현재.*단계|현재.*위치|다음.*뭐|어느.*Phase|flow status|플로우.*상태|단계.*확인'; then
|
|
60
|
+
SUGGESTIONS="$SUGGESTIONS\n- 현재 위치 확인 요청입니다. \`/rules-product\` 기준 Flow Status Block으로 현재 Phase, Gate, 다음 액션을 먼저 보고하세요."
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# React / UI component → rules-react
|
|
64
|
+
if echo "$PROMPT" | grep -qiE 'React|컴포넌트|component|페이지|page|UI|화면|shadcn|tailwind'; then
|
|
65
|
+
SUGGESTIONS="$SUGGESTIONS\n- React/UI 작업입니다. \`/rules-react\`로 컴포넌트 설계 기준을 확인하고, 구현 후 \`/verify-ui\`로 화면 문서와 사용자 동선 정합성을 검증하세요."
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# UI verification explicit request → verify-ui
|
|
69
|
+
if echo "$PROMPT" | grep -qiE 'UI ?검증|UX ?검증|화면 ?검증|화면.*맞|동선.*검증|상태별 UI|empty state|loading state|error state'; then
|
|
70
|
+
SUGGESTIONS="$SUGGESTIONS\n- UI 검증 요청입니다. \`/verify-ui\`로 화면 구조, 사용자 동선, 데이터 흐름, 상태별 UI를 점검하세요."
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Skill package changes → verify-skills
|
|
74
|
+
if echo "$PROMPT" | grep -qiE '스킬.*검증|skill.*verify|SKILL\.md|openai\.yaml|solmate-skills|npm pack|패키지.*검증'; then
|
|
75
|
+
SUGGESTIONS="$SUGGESTIONS\n- 스킬 패키지 작업입니다. \`/verify-skills\`로 SKILL.md, agents/openai.yaml, CLI 목록, README/AGENTS, npm pack을 검증하세요."
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Drizzle / DB schema → verify-drizzle-schema
|
|
79
|
+
if echo "$PROMPT" | grep -qiE 'drizzle|schema\.ts|마이그레이션|migration|pgTable|sqliteTable'; then
|
|
80
|
+
SUGGESTIONS="$SUGGESTIONS\n- Drizzle 스키마 작업입니다. \`/verify-drizzle-schema\`로 스키마 정합성을 검증하세요."
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# New project / product pipeline → rules-product
|
|
84
|
+
if echo "$PROMPT" | grep -qiE '새 프로젝트|신규 프로젝트|프로젝트 시작|어디서 시작|뭐부터|처음부터|from scratch'; then
|
|
85
|
+
SUGGESTIONS="$SUGGESTIONS\n- 새 프로젝트입니다. \`/rules-product\`를 실행하면 현재 단계를 자동 진단하고 올바른 스킬로 안내합니다."
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Pitch deck / business plan → docs-pitch / docs-business
|
|
89
|
+
if echo "$PROMPT" | grep -qiE '피치|pitch|투자|investor|사업 ?계획|business ?plan|데모데이|해커톤'; then
|
|
90
|
+
SUGGESTIONS="$SUGGESTIONS\n- 발표·투자 자료 작업입니다. 피치덱은 \`/docs-pitch\`, 사업계획서는 \`/docs-business\`를 사용하세요."
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# If no suggestions, exit silently
|
|
94
|
+
if [ -z "$SUGGESTIONS" ]; then
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Output JSON with additionalContext (non-blocking)
|
|
99
|
+
python3 -c "
|
|
100
|
+
import json, sys
|
|
101
|
+
suggestions = sys.argv[1]
|
|
102
|
+
output = {
|
|
103
|
+
'hookSpecificOutput': {
|
|
104
|
+
'hookEventName': 'UserPromptSubmit',
|
|
105
|
+
'additionalContext': '[Solmate Skills 제안]\n' + suggestions.strip()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
print(json.dumps(output))
|
|
109
|
+
" "$SUGGESTIONS"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Claude Stop 훅: git 변경 파일을 분석하여 적절한 verify-* 스킬을 제안합니다
|
|
3
|
+
|
|
4
|
+
python3 - <<'PYEOF'
|
|
5
|
+
import subprocess, json
|
|
6
|
+
|
|
7
|
+
r = subprocess.run(['git', 'diff', '--name-only', 'HEAD'], capture_output=True, text=True)
|
|
8
|
+
files = [f for f in r.stdout.strip().split('\n') if f]
|
|
9
|
+
|
|
10
|
+
if not files:
|
|
11
|
+
exit()
|
|
12
|
+
|
|
13
|
+
s = []
|
|
14
|
+
|
|
15
|
+
if any('schema' in f and (f.endswith('.ts') or f.endswith('.sql')) for f in files):
|
|
16
|
+
s.append(' DB 스키마 변경 감지 → /verify-drizzle-schema')
|
|
17
|
+
|
|
18
|
+
if any(f.endswith(('.tsx', '.jsx')) or f.startswith('docs/02_UI_Screens/') for f in files):
|
|
19
|
+
s.append(' UI/화면 변경 감지 → /verify-ui')
|
|
20
|
+
|
|
21
|
+
if any(any(k in f.lower() for k in ('auth', 'middleware', 'api', 'route', 'token', 'session'))
|
|
22
|
+
and (f.endswith('.ts') or f.endswith('.tsx')) for f in files):
|
|
23
|
+
s.append(' 인증/API 파일 변경 감지 → /verify-security')
|
|
24
|
+
|
|
25
|
+
if any(f.endswith('.ts') or f.endswith('.tsx') for f in files):
|
|
26
|
+
s.append(' 코드 파일 변경 감지 → /verify-code')
|
|
27
|
+
|
|
28
|
+
if any(f.endswith('.md') for f in files):
|
|
29
|
+
s.append(' 문서 파일 변경 감지 → /verify-docs')
|
|
30
|
+
|
|
31
|
+
if any(
|
|
32
|
+
f.endswith('/SKILL.md')
|
|
33
|
+
or f.endswith('/agents/openai.yaml')
|
|
34
|
+
or f in ('bin/cli.js', 'package.json', 'README.md', 'AGENTS.md')
|
|
35
|
+
for f in files
|
|
36
|
+
):
|
|
37
|
+
s.append(' 스킬 패키지 변경 감지 → /verify-skills')
|
|
38
|
+
|
|
39
|
+
if s:
|
|
40
|
+
msg = '[ 검증 시점 알림 ]\n' + '\n'.join(s)
|
|
41
|
+
print(json.dumps({'systemMessage': msg}))
|
|
42
|
+
PYEOF
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# solmate-skills: watch-files.sh
|
|
3
|
+
# Event: PreToolUse (matcher: Read|Write|Edit|Bash)
|
|
4
|
+
# Purpose: Detect file patterns being modified and inject relevant skill suggestions.
|
|
5
|
+
# Output: JSON with hookSpecificOutput.additionalContext (non-blocking)
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract file path and tool name
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | python3 -c "
|
|
13
|
+
import sys, json
|
|
14
|
+
data = json.load(sys.stdin)
|
|
15
|
+
inp = data.get('tool_input', {})
|
|
16
|
+
# Write/Edit use file_path; Bash use command
|
|
17
|
+
print(inp.get('file_path', inp.get('command', '')))" 2>/dev/null || echo "")
|
|
18
|
+
|
|
19
|
+
TOOL_NAME=$(echo "$INPUT" | python3 -c "
|
|
20
|
+
import sys, json
|
|
21
|
+
data = json.load(sys.stdin)
|
|
22
|
+
print(data.get('tool_name', ''))" 2>/dev/null || echo "")
|
|
23
|
+
|
|
24
|
+
if [ -z "$FILE_PATH" ]; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
SUGGESTIONS=""
|
|
29
|
+
|
|
30
|
+
# --- File pattern matching ---
|
|
31
|
+
|
|
32
|
+
# Drizzle schema file → verify-drizzle-schema
|
|
33
|
+
if echo "$FILE_PATH" | grep -qiE 'schema\.(ts|js)|drizzle'; then
|
|
34
|
+
SUGGESTIONS="$SUGGESTIONS\n- Drizzle 스키마 파일을 수정 중입니다. 작업 후 \`/verify-drizzle-schema\`로 스키마 정합성을 검증하세요."
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# .env files → verify-security
|
|
38
|
+
if echo "$FILE_PATH" | grep -qiE '\.env|\.env\.local|\.env\.production|\.env\.example'; then
|
|
39
|
+
SUGGESTIONS="$SUGGESTIONS\n- 환경변수 파일을 수정 중입니다. \`/verify-security\` Check 1(시크릿 노출)을 실행하여 민감 정보 노출 여부를 확인하세요."
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# SKILL.md files → manage-skills
|
|
43
|
+
if echo "$FILE_PATH" | grep -qiE 'SKILL\.md'; then
|
|
44
|
+
SUGGESTIONS="$SUGGESTIONS\n- 스킬 파일을 수정 중입니다. \`/manage-skills\`로 verify 스킬과의 정합성 드리프트를 점검하고, 작업 후 \`/verify-skills\`로 패키지 메타데이터를 검증하세요."
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Skill metadata / package files → verify-skills
|
|
48
|
+
if echo "$FILE_PATH" | grep -qiE 'agents/openai\.yaml|bin/cli\.js|package\.json|README\.md|AGENTS\.md'; then
|
|
49
|
+
SUGGESTIONS="$SUGGESTIONS\n- 스킬 패키지 메타데이터 또는 배포 파일을 수정 중입니다. 작업 후 \`/verify-skills\`를 실행하세요."
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# UI implementation files → verify-ui
|
|
53
|
+
if echo "$FILE_PATH" | grep -qiE '\.(tsx|jsx)$|docs/02_UI_Screens/.*\.md'; then
|
|
54
|
+
SUGGESTIONS="$SUGGESTIONS\n- UI 구현 또는 화면 문서를 수정 중입니다. 작업 후 \`/verify-ui\`로 화면 구조·동선·상태별 UI 정합성을 검증하세요."
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Page/route files → verify-performance
|
|
58
|
+
if echo "$FILE_PATH" | grep -qiE 'page\.(tsx|jsx|ts|js)|route\.(tsx|jsx|ts|js)|layout\.(tsx|jsx)'; then
|
|
59
|
+
SUGGESTIONS="$SUGGESTIONS\n- 페이지·라우트 파일을 수정 중입니다. 작업 후 \`/verify-performance\`로 Core Web Vitals 및 렌더링 전략을 점검하세요."
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# API route files → verify-security
|
|
63
|
+
if echo "$FILE_PATH" | grep -qiE 'api/.*route\.(ts|js)|api/.*index\.(ts|js)|\bapi\b.*\.(ts|js)'; then
|
|
64
|
+
SUGGESTIONS="$SUGGESTIONS\n- API 라우트 파일을 수정 중입니다. 작업 후 \`/verify-security\` Check 2(인증·인가) 및 Check 5(CSRF)를 점검하세요."
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Auth-related files → verify-security
|
|
68
|
+
if echo "$FILE_PATH" | grep -qiE 'auth\.(ts|js|tsx)|middleware\.(ts|js)|session\.(ts|js)'; then
|
|
69
|
+
SUGGESTIONS="$SUGGESTIONS\n- 인증·미들웨어 파일을 수정 중입니다. \`/verify-security\`로 인증·인가 취약점을 점검하세요."
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Documentation files → verify-docs
|
|
73
|
+
if echo "$FILE_PATH" | grep -qiE 'docs/(01|02|03|04|05)_.*\.md'; then
|
|
74
|
+
SUGGESTIONS="$SUGGESTIONS\n- 문서 레이어 파일을 수정 중입니다. 작업 후 \`/verify-docs\`로 메타데이터 및 구조 정합성을 검증하세요."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# AGENTS.md → remind about skill count
|
|
78
|
+
if echo "$FILE_PATH" | grep -qiE 'AGENTS\.md'; then
|
|
79
|
+
SUGGESTIONS="$SUGGESTIONS\n- AGENTS.md를 수정 중입니다. 스킬 목록·개수가 변경된 경우 Section 4의 테이블과 개수를 함께 업데이트하세요."
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if [ -z "$SUGGESTIONS" ]; then
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
python3 -c "
|
|
87
|
+
import json, sys
|
|
88
|
+
suggestions = sys.argv[1]
|
|
89
|
+
output = {
|
|
90
|
+
'hookSpecificOutput': {
|
|
91
|
+
'hookEventName': 'PreToolUse',
|
|
92
|
+
'additionalContext': '[Solmate Skills 파일 감지]\n' + suggestions.strip()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
print(json.dumps(output))
|
|
96
|
+
" "$SUGGESTIONS"
|
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
URL=$1
|
|
17
|
+
OUTPUT=$2
|
|
18
|
+
if [ -z "$URL" ] || [ -z "$OUTPUT" ]; then
|
|
19
|
+
echo "Usage: $0 <url> <output_path>"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
echo "Initiating high-reliability fetch for Stitch HTML..."
|
|
23
|
+
curl -L -f -sS --connect-timeout 10 --compressed "$URL" -o "$OUTPUT"
|
|
24
|
+
if [ $? -eq 0 ]; then
|
|
25
|
+
echo "✅ Successfully retrieved HTML at: $OUTPUT"
|
|
26
|
+
exit 0
|
|
27
|
+
else
|
|
28
|
+
echo "❌ Error: Failed to retrieve content. Check TLS/SNI or URL expiration."
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shadcn/ui Setup Verification Script
|
|
3
|
+
# Validates that a project is correctly configured for shadcn/ui
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
RED='\033[0;31m'
|
|
9
|
+
YELLOW='\033[1;33m'
|
|
10
|
+
NC='\033[0m' # No Color
|
|
11
|
+
|
|
12
|
+
echo "🔍 Verifying shadcn/ui setup..."
|
|
13
|
+
echo ""
|
|
14
|
+
|
|
15
|
+
# Check if components.json exists
|
|
16
|
+
if [ -f "components.json" ]; then
|
|
17
|
+
echo -e "${GREEN}✓${NC} components.json found"
|
|
18
|
+
else
|
|
19
|
+
echo -e "${RED}✗${NC} components.json not found"
|
|
20
|
+
echo -e " ${YELLOW}Run:${NC} npx shadcn@latest init"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Check if tailwind.config exists
|
|
25
|
+
if [ -f "tailwind.config.js" ] || [ -f "tailwind.config.ts" ]; then
|
|
26
|
+
echo -e "${GREEN}✓${NC} Tailwind config found"
|
|
27
|
+
else
|
|
28
|
+
echo -e "${RED}✗${NC} tailwind.config.js not found"
|
|
29
|
+
echo -e " ${YELLOW}Install Tailwind:${NC} npm install -D tailwindcss postcss autoprefixer"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Check if tsconfig.json has path aliases
|
|
34
|
+
if [ -f "tsconfig.json" ]; then
|
|
35
|
+
if grep -q '"@/\*"' tsconfig.json; then
|
|
36
|
+
echo -e "${GREEN}✓${NC} Path aliases configured in tsconfig.json"
|
|
37
|
+
else
|
|
38
|
+
echo -e "${YELLOW}⚠${NC} Path aliases not found in tsconfig.json"
|
|
39
|
+
echo " Add to compilerOptions.paths:"
|
|
40
|
+
echo ' "@/*": ["./src/*"]'
|
|
41
|
+
fi
|
|
42
|
+
else
|
|
43
|
+
echo -e "${YELLOW}⚠${NC} tsconfig.json not found (TypeScript not configured)"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Check if globals.css or equivalent exists
|
|
47
|
+
if [ -f "src/index.css" ] || [ -f "src/globals.css" ] || [ -f "app/globals.css" ]; then
|
|
48
|
+
echo -e "${GREEN}✓${NC} Global CSS file found"
|
|
49
|
+
|
|
50
|
+
# Check for Tailwind directives
|
|
51
|
+
CSS_FILE=$(find . -name "globals.css" -o -name "index.css" | head -n 1)
|
|
52
|
+
if grep -q "@tailwind base" "$CSS_FILE"; then
|
|
53
|
+
echo -e "${GREEN}✓${NC} Tailwind directives present"
|
|
54
|
+
else
|
|
55
|
+
echo -e "${RED}✗${NC} Tailwind directives missing"
|
|
56
|
+
echo " Add to your CSS file:"
|
|
57
|
+
echo " @tailwind base;"
|
|
58
|
+
echo " @tailwind components;"
|
|
59
|
+
echo " @tailwind utilities;"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Check for CSS variables
|
|
63
|
+
if grep -q "^:root" "$CSS_FILE" || grep -q "@layer base" "$CSS_FILE"; then
|
|
64
|
+
echo -e "${GREEN}✓${NC} CSS variables defined"
|
|
65
|
+
else
|
|
66
|
+
echo -e "${YELLOW}⚠${NC} CSS variables not found"
|
|
67
|
+
echo " shadcn/ui requires CSS variables for theming"
|
|
68
|
+
fi
|
|
69
|
+
else
|
|
70
|
+
echo -e "${RED}✗${NC} Global CSS file not found"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Check if components/ui directory exists
|
|
74
|
+
if [ -d "src/components/ui" ] || [ -d "components/ui" ]; then
|
|
75
|
+
echo -e "${GREEN}✓${NC} components/ui directory exists"
|
|
76
|
+
|
|
77
|
+
# Count components
|
|
78
|
+
COMPONENT_COUNT=$(find . -path "*/components/ui/*.tsx" -o -path "*/components/ui/*.jsx" | wc -l)
|
|
79
|
+
echo -e " ${COMPONENT_COUNT} components installed"
|
|
80
|
+
else
|
|
81
|
+
echo -e "${YELLOW}⚠${NC} components/ui directory not found"
|
|
82
|
+
echo " Add your first component: npx shadcn@latest add button"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Check if lib/utils exists
|
|
86
|
+
if [ -f "src/lib/utils.ts" ] || [ -f "lib/utils.ts" ]; then
|
|
87
|
+
echo -e "${GREEN}✓${NC} lib/utils.ts exists"
|
|
88
|
+
|
|
89
|
+
# Check for cn function
|
|
90
|
+
UTILS_FILE=$(find . -name "utils.ts" | grep "lib" | head -n 1)
|
|
91
|
+
if grep -q "export function cn" "$UTILS_FILE"; then
|
|
92
|
+
echo -e "${GREEN}✓${NC} cn() utility function present"
|
|
93
|
+
else
|
|
94
|
+
echo -e "${RED}✗${NC} cn() utility function missing"
|
|
95
|
+
fi
|
|
96
|
+
else
|
|
97
|
+
echo -e "${RED}✗${NC} lib/utils.ts not found"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Check package.json dependencies
|
|
101
|
+
if [ -f "package.json" ]; then
|
|
102
|
+
echo ""
|
|
103
|
+
echo "📦 Checking dependencies..."
|
|
104
|
+
|
|
105
|
+
# Required dependencies
|
|
106
|
+
REQUIRED_DEPS=("react" "tailwindcss")
|
|
107
|
+
RECOMMENDED_DEPS=("class-variance-authority" "clsx" "tailwind-merge" "tailwindcss-animate")
|
|
108
|
+
|
|
109
|
+
for dep in "${REQUIRED_DEPS[@]}"; do
|
|
110
|
+
if grep -q "\"$dep\"" package.json; then
|
|
111
|
+
echo -e "${GREEN}✓${NC} $dep installed"
|
|
112
|
+
else
|
|
113
|
+
echo -e "${RED}✗${NC} $dep not installed"
|
|
114
|
+
fi
|
|
115
|
+
done
|
|
116
|
+
|
|
117
|
+
echo ""
|
|
118
|
+
echo "Recommended dependencies:"
|
|
119
|
+
for dep in "${RECOMMENDED_DEPS[@]}"; do
|
|
120
|
+
if grep -q "\"$dep\"" package.json; then
|
|
121
|
+
echo -e "${GREEN}✓${NC} $dep installed"
|
|
122
|
+
else
|
|
123
|
+
echo -e "${YELLOW}⚠${NC} $dep not installed (recommended)"
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
echo ""
|
|
129
|
+
echo -e "${GREEN}✓${NC} Setup verification complete!"
|
|
130
|
+
echo ""
|
|
131
|
+
echo "Next steps:"
|
|
132
|
+
echo " 1. Add components: npx shadcn@latest add [component]"
|
|
133
|
+
echo " 2. View catalog: npx shadcn@latest add --help"
|
|
134
|
+
echo " 3. Browse docs: https://ui.shadcn.com"
|