jinhak-ai-standard 2.6.0 → 2.7.1

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.
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * README Guard Hook
4
+ *
5
+ * git commit 시점에 README.md 업데이트 누락을 감지합니다.
6
+ * PreToolUse (Bash - git commit) 에서 실행됩니다.
7
+ *
8
+ * 동작:
9
+ * 1. staged 파일 목록을 확인
10
+ * 2. "중요 파일"이 변경되었는데 README.md가 staged에 없으면 경고
11
+ * 3. additionalContext로 Claude에게 README 업데이트를 요청
12
+ */
13
+
14
+ const { execSync } = require('child_process');
15
+
16
+ // 커밋 명령이 아니면 무시
17
+ const command = process.env.command || '';
18
+ if (!command.includes('commit')) {
19
+ process.exit(0);
20
+ }
21
+
22
+ try {
23
+ // staged 파일 목록
24
+ const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
25
+ .trim()
26
+ .split('\n')
27
+ .filter(Boolean);
28
+
29
+ if (staged.length === 0) {
30
+ process.exit(0);
31
+ }
32
+
33
+ // README.md가 이미 staged에 포함되어 있으면 OK
34
+ const readmeIncluded = staged.some(f =>
35
+ f.toLowerCase() === 'readme.md'
36
+ );
37
+
38
+ if (readmeIncluded) {
39
+ process.exit(0);
40
+ }
41
+
42
+ // README 업데이트가 필요할 수 있는 "중요 변경" 패턴
43
+ const significantPatterns = [
44
+ // 구조/설정 변경
45
+ /^CLAUDE\.md$/,
46
+ /^CHANGELOG\.md$/,
47
+ /^RELEASE_NOTES\.md$/,
48
+ /^QUICK_START_PROMPT\.md$/,
49
+ /^package\.json$/,
50
+ /^bin\//,
51
+ // 새 기능/스킬 추가
52
+ /^\.claude\/skills\//,
53
+ /^\.claude\/settings\.json$/,
54
+ /^\.claude\/scripts\//,
55
+ // 문서 구조 변경
56
+ /^templates\//,
57
+ /^security\//,
58
+ /^prompts\//,
59
+ // 주요 가이드 문서
60
+ /^VIBE_CODING_GUIDE\.md$/,
61
+ /^CODING_CONVENTIONS\.md$/,
62
+ /^ARCHITECTURE\.md$/,
63
+ /^PROJECT_STRUCTURE\.md$/,
64
+ /^SECURITY_ISMS\.md$/,
65
+ /^PROMPT-LIBRARY\.md$/,
66
+ /^PROMPT_LIBRARY_USAGE\.md$/,
67
+ // 스크립트/자동화
68
+ /^scripts\//,
69
+ ];
70
+
71
+ const significantChanges = staged.filter(file =>
72
+ significantPatterns.some(pattern => pattern.test(file))
73
+ );
74
+
75
+ if (significantChanges.length === 0) {
76
+ // 중요 변경이 아니면 무시
77
+ process.exit(0);
78
+ }
79
+
80
+ // 경고 출력 (additionalContext로 Claude에게 전달)
81
+ const fileList = significantChanges.map(f => ` - ${f}`).join('\n');
82
+
83
+ console.log(`[README Guard] ⚠️ README.md 업데이트 필요 가능성 감지
84
+
85
+ 다음 중요 파일이 변경되었지만 README.md가 커밋에 포함되지 않았습니다:
86
+ ${fileList}
87
+
88
+ README.md에 반영이 필요한 변경인지 확인하세요.
89
+ - 새 기능/스킬 추가 → README에 설명 추가
90
+ - 버전 변경 → README 버전 번호 갱신
91
+ - 구조 변경 → README 구조 설명 업데이트
92
+ - 적용 방법 변경 → README 설치/적용 가이드 업데이트
93
+
94
+ 커밋을 진행하기 전에 사용자에게 README.md 업데이트 여부를 확인하세요.`);
95
+
96
+ } catch (e) {
97
+ // git 명령 실패 시 조용히 무시 (hook이 커밋을 막지 않도록)
98
+ process.exit(0);
99
+ }
@@ -71,6 +71,15 @@
71
71
  "command": "node scripts/security-check-hook.cjs"
72
72
  }
73
73
  ]
74
+ },
75
+ {
76
+ "matcher": "Bash",
77
+ "hooks": [
78
+ {
79
+ "type": "command",
80
+ "command": "node .claude/scripts/readme-guard.cjs"
81
+ }
82
+ ]
74
83
  }
75
84
  ],
76
85
  "PostToolUse": [
@@ -141,7 +141,7 @@ Auto Memory가 활성화되어 있습니다.
141
141
 
142
142
  > **중요**: `deny` 규칙은 프로젝트 전체에 **강제 적용**됩니다. `settings.local.json`이나 `~/.claude/settings.json`으로 우회할 수 없으므로, 위험 명령 차단에 가장 확실한 방법입니다. `deny`가 `allow`보다 우선합니다.
143
143
 
144
- > Hook은 Node.js 기반으로 작성되어 OS별 변환이 필요 없습니다. Windows/macOS/Linux 모두 동일한 설정을 사용합니다.
144
+ > Hook은 bash 셸에서 실행됩니다. Unix 문법을 사용할 있으며, Windows 전용 문법(`> nul`, `powershell`)만 피하면 OS 무관하게 동작합니다.
145
145
 
146
146
  **Scripts 복사** - 표준 저장소의 세션 브리핑 스크립트를 복사:
147
147
  - `/tmp/jinhak-standards/.claude/scripts/session-briefing.cjs` → `.claude/scripts/session-briefing.cjs`
package/CHANGELOG.md CHANGED
@@ -5,6 +5,48 @@ Claude Code의 `/session-start` 스킬이 이 파일을 참조하여 표준 업
5
5
 
6
6
  ---
7
7
 
8
+ ## [2.7.1] - 2026-03-12
9
+
10
+ ### README Guard Hook — 커밋 시 README.md 업데이트 누락 자동 감지
11
+
12
+ 커밋 시점에 중요 파일이 변경되었지만 README.md가 staged에 포함되지 않은 경우 Claude에게 경고를 전달하여 자동으로 README 업데이트를 유도합니다.
13
+
14
+ ### 추가
15
+ - `.claude/scripts/readme-guard.cjs` — PreToolUse Hook 스크립트 (git commit 시 README.md 누락 감지)
16
+ - `.claude/settings.json` — PreToolUse Hook에 readme-guard.cjs 등록
17
+
18
+ ### 동작 방식
19
+ - `git commit` 실행 전 staged 파일 분석
20
+ - CLAUDE.md, skills, templates, scripts, security 등 중요 파일 변경 감지
21
+ - README.md 미포함 시 additionalContext로 경고 → Claude가 사용자에게 확인 요청
22
+
23
+ ---
24
+
25
+ ## [2.7] - 2026-03-04
26
+
27
+ ### CLI 기술 스택 자동 감지 + CLAUDE.md 완전 자동 생성
28
+
29
+ `npx jinhak-ai-standard` 실행 시 프로젝트의 기술 스택을 자동 감지하여 CLAUDE.md와 .ai/ 폴더를 의미있는 내용으로 생성합니다.
30
+
31
+ ### 추가
32
+ - `detectTechStack()` — package.json에서 프레임워크, 언어, 패키지매니저, 상태관리, 스타일링, DB, ORM, 빌드도구, 테스트도구 자동 감지
33
+ - `applyStackToTemplate()` — templates/project-claude.md 템플릿의 `[대괄호]` 플레이스홀더를 감지된 기술 스택으로 자동 치환
34
+ - `generateFolderTree()` — 프로젝트 1-depth 폴더 트리 자동 생성
35
+ - `generateAiContent()` — .ai/ 폴더 파일에 감지된 기술 스택 기반 의미있는 초기 내용 생성 (ADR-001 포함)
36
+
37
+ ### 변경
38
+ - `bin/cli.cjs`: apply() 흐름에 기술 스택 감지 → 템플릿 치환 → 의미있는 .ai/ 생성 단계 추가
39
+ - `package.json`: 버전 2.6.0 → 2.7.0
40
+ - `CLAUDE.md`: 버전 2.6 → 2.7
41
+
42
+ ### Migration Guide (v2.6 → v2.7)
43
+
44
+ 기존 v2.6 프로젝트에서 업데이트 시:
45
+ 1. `npx jinhak-ai-standard` 재실행 — 기존 CLAUDE.md가 있으면 덮어쓰지 않고 버전만 안내
46
+ 2. 새 프로젝트에서는 자동으로 기술 스택이 감지되어 CLAUDE.md에 반영됨
47
+
48
+ ---
49
+
8
50
  ## [2.6] - 2026-03-02
9
51
 
10
52
  ### npm 패키지 + GitHub Releases 배포 시스템
@@ -17,9 +59,10 @@ Claude Code의 `/session-start` 스킬이 이 파일을 참조하여 표준 업
17
59
  - `.github/workflows/release.yml` — 태그 push 시 GitHub Release + npm publish 자동화
18
60
 
19
61
  ### 변경
62
+ - `bin/cli.cjs`: CLAUDE.md 자동 생성(신규) 및 버전 메타데이터 업데이트(기존), prompts/ 디렉토리 복사 추가
20
63
  - `.claude/skills/apply-standard/SKILL.md`: 0단계에 npm 패키지 → Release tarball → git clone 3단계 폴백 추가
21
64
  - `QUICK_START_PROMPT.md`: 방법 1(npx), 방법 2(프롬프트), 방법 3(Release tarball) 구분
22
- - `README.md`: v2.5 → v2.6, 적용 방법 1~4로 재정리, 문서 구조에 새 파일 추가
65
+ - `README.md`: v2.5 → v2.6, 적용 방법 1~4로 재정리, 문서 구조에 새 파일 추가, npx 설명에 CLAUDE.md 자동 생성 반영
23
66
  - `CLAUDE.md`: 버전 2.5 → 2.6, 프로젝트 구조에 package.json/bin/cli.cjs 추가
24
67
 
25
68
  ### Migration Guide (v2.5 → v2.6)
package/CLAUDE.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <!-- JINHAK Standard Metadata - 이 메타 정보는 자동 버전 관리에 사용됩니다. 삭제하지 마세요. -->
2
- <!-- jinhak_standard_version: 2.6 -->
2
+ <!-- jinhak_standard_version: 2.7 -->
3
3
  <!-- jinhak_standard_repo: https://github.com/JinhakStandard/ai-vibecoding -->
4
- <!-- applied_date: 2026-03-02 -->
4
+ <!-- applied_date: 2026-03-04 -->
5
5
 
6
- # JINHAK 전사 AI 개발 표준 v2.6
6
+ # JINHAK 전사 AI 개발 표준 v2.7
7
7
 
8
8
  이 문서는 JINHAK의 모든 프로젝트에서 AI(Claude Code / Claude.ai)와 협업할 때 따라야 하는 전사 표준입니다.
9
9
 
@@ -62,8 +62,8 @@
62
62
 
63
63
  ```
64
64
  프로젝트루트/
65
- ├── package.json # npm 패키지 설정 — npx jinhak-ai-standard (v2.6)
66
- ├── bin/cli.cjs # CLI 진입점 (apply/info/link/help) (v2.6)
65
+ ├── package.json # npm 패키지 설정 — npx jinhak-ai-standard (v2.7)
66
+ ├── bin/cli.cjs # CLI 진입점 (apply/info/link/help + 기술 스택 자동 감지) (v2.7)
67
67
  ├── .github/workflows/ # GitHub Actions (release.yml → 태그 push 시 Release + npm publish) (v2.6)
68
68
  ├── CLAUDE.md # Claude Code 메인 설정 파일 (필수)
69
69
  ├── CLAUDE.local.md # 로컬 개발자 설정 (git 제외, 선택사항)
@@ -204,19 +204,18 @@ Claude는 다음 안티패턴을 감지하면 **즉시 경고하고 대안을
204
204
  > 상세 내용: [security/AI_SECURITY_GUARDRAILS.md](./security/AI_SECURITY_GUARDRAILS.md)
205
205
  > 상세 안티패턴 목록과 대화 예시는 [VIBE_CODING_GUIDE.md](./VIBE_CODING_GUIDE.md) 섹션 6.4~6.5를 참조하세요.
206
206
 
207
- ### 2.5 Hook 크로스 플랫폼 원칙
207
+ ### 2.5 Hook 실행 환경 및 규칙
208
208
 
209
- **모든 Hook 명령은 Node.js 기반으로 작성**하여 OS(Windows/macOS/Linux)에 관계없이 동일하게 동작하도록 합니다. Claude Code는 Node.js를 필수 의존성으로 요구하므로, `node` 명령은 모든 환경에서 사용 가능합니다.
209
+ **Claude Code의 Hook bash 셸에서 실행**됩니다. 따라서 Unix 문법(`2>/dev/null`, `||`, `&&` 등)을 사용할 있으며, `node`, `npx`, `git` bash PATH에 있는 모든 명령을 사용할 수 있습니다.
210
210
 
211
211
  **필수 규칙:**
212
- - Hook command는 `node` 또는 `node -e` 로 시작할 것
213
- - `echo`, `cat`, `head`, `sed`, `powershell` OS 종속 명령을 Hook에서 사용하지 말 것
214
- - 출력 리다이렉션(`> nul`, `2>/dev/null`) 대신 Node.js 내부에서 에러를 처리할
215
- - 파일 경로는 `path.join()`으로 생성하고 슬래시(`/`)로 통일할 것
212
+ - Windows 전용 문법 사용 금지: `> nul`, `2>nul`, `powershell -Command`
213
+ - 파일 경로는 슬래시(`/`)로 통일할 (백슬래시 `\` 금지)
214
+ - 복잡한 로직은 `.cjs` 스크립트로 분리하여 `node script.cjs`로 실행 권장
216
215
 
217
216
  **Windows `nul` 파일 방지:**
218
- - `> nul`, `2>nul` 사용 금지 (예약 디바이스 이름 충돌로 `nul` 파일 생성)
219
- - `settings.local.json`의 PostToolUse Hook으로 도구 실행 후 `nul` 파일 자동 삭제
217
+ - `> nul`, `2>nul` 사용 금지 (Windows 예약 디바이스 이름 충돌로 `nul` 파일 생성)
218
+ - bash 환경에서는 `> /dev/null 2>&1` 사용
220
219
 
221
220
  ### 2.6 Windows 개발 환경 규칙
222
221
 
@@ -507,7 +506,7 @@ body: { action: 'delete', id: '123' }
507
506
  }
508
507
  ```
509
508
 
510
- > Hook은 Node.js 기반으로 OS에 무관하게 동작합니다. 세션 브리핑, 보안 경고모든 Hook command가 `node`로 시작하도록 작성하세요.
509
+ > Hook은 bash 셸에서 실행됩니다. `node`, `npx` bash에서 사용 가능한 명령과 Unix 문법(`2>/dev/null`, `||` )을 모두 사용할 있습니다. Windows 전용 문법(`> nul`, `powershell`)은 사용하지 마세요.
511
510
 
512
511
  ### 6.1.1 글로벌 Hook (자동 표준 감지)
513
512
 
@@ -1165,4 +1164,4 @@ AI가 생성해서는 안 되는 12가지 위험 패턴:
1165
1164
  ---
1166
1165
 
1167
1166
  *마지막 업데이트: 2026-03-01*
1168
- *버전: 2.6*
1167
+ *버전: 2.7*
@@ -340,4 +340,4 @@ Claude Code 없이 **Claude.ai** 웹에서 프롬프트를 사용하는 방법
340
340
 
341
341
  ---
342
342
 
343
- *이 문서는 [JINHAK 전사 AI 개발 표준](./CLAUDE.md) v2.4의 상세 문서입니다.*
343
+ *이 문서는 [JINHAK 전사 AI 개발 표준](./CLAUDE.md)의 상세 문서입니다.*
@@ -99,7 +99,7 @@ CLAUDE.local.md
99
99
 
100
100
  ```bash
101
101
  mkdir -p /tmp/jinhak-standards
102
- curl -sL https://github.com/JinhakStandard/ai-vibecoding/archive/refs/tags/v2.5.tar.gz \
102
+ curl -sL https://github.com/JinhakStandard/ai-vibecoding/archive/refs/tags/v2.7.tar.gz \
103
103
  | tar -xz -C /tmp/jinhak-standards --strip-components=1
104
104
  ```
105
105
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # JINHAK AI 개발 표준 v2.6 (AI Vibe Coding Standards)
1
+ # JINHAK AI 개발 표준 v2.7 (AI Vibe Coding Standards)
2
2
 
3
3
  JINHAK 전사에서 AI(Claude Code / Claude.ai)와 협업할 때 따라야 하는 개발 표준 문서입니다.
4
4
 
@@ -13,8 +13,8 @@ cd my-project
13
13
  npx jinhak-ai-standard
14
14
  ```
15
15
 
16
- `.claude/`, `.ai/`, `security/` 등 표준 파일이 자동으로 복사됩니다.
17
- 이후 `CLAUDE.md`를 프로젝트에 맞게 작성하고 Claude Code에서 `/session-start`로 시작하세요.
16
+ `CLAUDE.md`, `.claude/`, `.ai/`, `security/`, `prompts/` 등 표준 파일이 자동으로 생성됩니다.
17
+ `CLAUDE.md`의 `[대괄호]` 내용을 프로젝트 정보로 수정한 뒤 Claude Code에서 `/session-start`로 시작하세요.
18
18
 
19
19
  ### 방법 2: Claude Code 프롬프트 (복사-붙여넣기)
20
20
 
@@ -105,9 +105,9 @@ claude
105
105
 
106
106
  > 각 프로젝트의 CLAUDE.md에 다음 메타 정보가 기록되어 추적됩니다:
107
107
  > ```html
108
- > <!-- jinhak_standard_version: 2.4 -->
108
+ > <!-- jinhak_standard_version: 2.7 -->
109
109
  > <!-- jinhak_standard_repo: https://github.com/JinhakStandard/ai-vibecoding -->
110
- > <!-- applied_date: 2026-02-28 -->
110
+ > <!-- applied_date: 2026-03-04 -->
111
111
  > ```
112
112
 
113
113
  ---
@@ -138,9 +138,9 @@ JinhakStandard/
138
138
  ├── VIBE_CODING_GUIDE.md # 바이브 코딩 방법론 (비개발자 포함)
139
139
  ├── PROJECT_STRUCTURE.md # 표준 프로젝트 구조
140
140
  ├── SECURITY_ISMS.md # ISMS 보안 가이드
141
- ├── PROMPT-LIBRARY.md # 프롬프트 라이브러리 시스템 가이드 (v2.4)
142
- ├── PROMPT_LIBRARY_USAGE.md # 프롬프트 라이브러리 사용법 (v2.4)
143
- ├── prompts/ # 프롬프트 라이브러리 (v2.4)
141
+ ├── PROMPT-LIBRARY.md # 프롬프트 라이브러리 시스템 가이드
142
+ ├── PROMPT_LIBRARY_USAGE.md # 프롬프트 라이브러리 사용법
143
+ ├── prompts/ # 프롬프트 라이브러리
144
144
  │ ├── _template/ # 새 프롬프트 작성용 템플릿
145
145
  │ ├── code-gen/ # 코드 생성 프롬프트
146
146
  │ ├── code-review/ # 코드 리뷰 프롬프트
@@ -166,8 +166,9 @@ JinhakStandard/
166
166
  ├── .claude/ # Claude Code 설정 (표준 템플릿)
167
167
  │ ├── settings.json # 권한, hooks 설정
168
168
  │ ├── scripts/
169
- │ │ └── session-briefing.cjs # 세션 자동 브리핑 Hook 스크립트
170
- │ └── skills/ # 슬래시 명령어 (13개)
169
+ │ │ ├── session-briefing.cjs # 세션 자동 브리핑 Hook 스크립트
170
+ └── readme-guard.cjs # 커밋 README.md 누락 감지 (v2.7.1)
171
+ │ └── skills/ # 슬래시 명령어 (14개)
171
172
  │ ├── apply-standard/ # /apply-standard - 표준 적용/업데이트
172
173
  │ ├── commit/ # /commit - 커밋 생성
173
174
  │ ├── debug/ # /debug - 체계적 디버깅 (v2.3)
@@ -175,9 +176,10 @@ JinhakStandard/
175
176
  │ ├── orchestrate/ # /orchestrate - Agent Teams + 2단계 검증 (v2.3)
176
177
  │ ├── review-pr/ # /review-pr - PR 리뷰
177
178
  │ ├── security-check/ # /security-check - 보안 점검 (v2.0)
178
- │ ├── prompt-register/ # /prompt-register - 프롬프트 등록 (v2.4)
179
- │ ├── prompt-search/ # /prompt-search - 프롬프트 검색 (v2.4)
180
- │ ├── prompt-quality-check/ # /prompt-quality-check - 품질 검증 (v2.4)
179
+ │ ├── prompt-register/ # /prompt-register - 프롬프트 등록
180
+ │ ├── prompt-search/ # /prompt-search - 프롬프트 검색
181
+ │ ├── prompt-quality-check/ # /prompt-quality-check - 품질 검증
182
+ │ ├── prompt-report/ # /prompt-report - 사용량 리포트 (v2.5)
181
183
  │ ├── session-end/ # /session-end - 세션 종료 (v2.0.2)
182
184
  │ ├── session-start/ # /session-start - 세션 시작
183
185
  │ └── test/ # /test - 테스트 + Red-Green 검증 (v2.3)
@@ -213,26 +215,20 @@ JinhakStandard/
213
215
 
214
216
  ## 빠른 시작
215
217
 
216
- > **자동 적용(권장):** [QUICK_START_PROMPT.md](./QUICK_START_PROMPT.md)의 프롬프트를 사용하세요.
218
+ > **자동 적용(권장):** `npx jinhak-ai-standard` 또는 [QUICK_START_PROMPT.md](./QUICK_START_PROMPT.md)의 프롬프트를 사용하세요.
217
219
 
218
220
  ### 수동 적용 (필요 시)
219
221
 
220
222
  ```bash
221
- # 1. 저장소를 클론하여 참고
222
- git clone https://github.com/JinhakStandard/ai-vibecoding.git /tmp/jinhak-standards
223
+ # 방법 A: npx로 번에 적용 (권장)
224
+ npx jinhak-ai-standard
223
225
 
224
- # 2. CLAUDE.md 템플릿 복사프로젝트 정보 수정
226
+ # 방법 B: 저장소 클론수동 복사
227
+ git clone https://github.com/JinhakStandard/ai-vibecoding.git /tmp/jinhak-standards
225
228
  cp /tmp/jinhak-standards/templates/project-claude.md ./CLAUDE.md
226
-
227
- # 3. AI 문서화 폴더 및 파일 생성
228
- mkdir -p .ai .claude/skills/commit .claude/skills/review-pr .claude/skills/session-start .claude/skills/test
229
- touch .ai/SESSION_LOG.md .ai/CURRENT_SPRINT.md .ai/DECISIONS.md .ai/ARCHITECTURE.md .ai/CONVENTIONS.md
230
-
231
- # 4. 스킬 파일 및 settings.json 복사
232
- cp /tmp/jinhak-standards/.claude/skills/*/SKILL.md 각_스킬_폴더/
233
- cp /tmp/jinhak-standards/.claude/settings.json .claude/
234
-
235
- # 5. .gitignore에 추가
229
+ cp -r /tmp/jinhak-standards/.claude .
230
+ cp -r /tmp/jinhak-standards/security .
231
+ mkdir -p .ai
236
232
  echo "CLAUDE.local.md" >> .gitignore
237
233
  ```
238
234
 
@@ -308,7 +304,10 @@ claude
308
304
 
309
305
  | 버전 | 날짜 | 변경 내용 |
310
306
  |------|------|----------|
311
- | **2.5** | **2026-03-01** | **프롬프트 라이브러리 Phase 2 + npm/GitHub Releases 배포: JABIS API 연동, `/prompt-report` 스킬, `npx jinhak-ai-standard` CLI** |
307
+ | **2.7.1** | **2026-03-12** | **README Guard Hook: 커밋 중요 파일 변경 README.md 업데이트 누락 자동 감지** |
308
+ | 2.7 | 2026-03-04 | CLI 기술 스택 자동 감지 + CLAUDE.md 완전 자동 생성: `npx jinhak-ai-standard` 실행 시 package.json 분석하여 기술 스택 자동 반영 |
309
+ | 2.6 | 2026-03-02 | npm 패키지 + GitHub Releases 배포: `npx jinhak-ai-standard` CLI (CLAUDE.md 자동 생성/업데이트, prompts/ 복사 포함) |
310
+ | 2.5 | 2026-03-01 | 프롬프트 라이브러리 Phase 2: JABIS API 연동, `/prompt-report` 스킬, 사용량 추적 |
312
311
  | 2.4 | 2026-02-28 | 프롬프트 라이브러리 Phase 1: 등록/검색/품질검증 시스템, 예시 프롬프트 3개, `/prompt-register` `/prompt-search` `/prompt-quality-check` 스킬 |
313
312
  | 2.3 | 2026-02-28 | 적응적 추천 + skills.sh 모범사례 + 멀티 에이전트 패턴: 가중치 비평, C6 Hard Gate, State Contract, 스킬 조합 가이드, 2단계 검증, `/debug` 스킬, AI 합리화 방지 |
314
313
  | 2.2 | 2026-02-28 | Planner-Critic 듀얼 에이전트 `/deep-plan` 스킬, Auto Memory 보강, memory-templates 추가 |
package/bin/cli.cjs CHANGED
@@ -83,6 +83,143 @@ function ensureGitignoreEntries(entries) {
83
83
  return added;
84
84
  }
85
85
 
86
+ // ─────────────────────────────────────────
87
+ // 기술 스택 자동 감지
88
+ // ─────────────────────────────────────────
89
+
90
+ function detectTechStack() {
91
+ const stack = { projectName: path.basename(TARGET), description: '', framework: [], language: 'JavaScript', packageManager: 'npm', stateManagement: '', styling: '', database: '', orm: '', buildTool: '', testTool: '', scripts: {} };
92
+ let pkg = null;
93
+ try { pkg = JSON.parse(fs.readFileSync(path.join(TARGET, 'package.json'), 'utf8')); } catch { /* ignore */ }
94
+
95
+ if (pkg) {
96
+ if (pkg.name) stack.projectName = pkg.name;
97
+ if (pkg.description) stack.description = pkg.description;
98
+ if (pkg.scripts) stack.scripts = pkg.scripts;
99
+ const deps = Object.keys(pkg.dependencies || {}), devDeps = Object.keys(pkg.devDependencies || {}), allDeps = [...deps, ...devDeps];
100
+
101
+ if (deps.includes('next')) stack.framework.push('Next.js');
102
+ if (deps.includes('react') && !deps.includes('next')) stack.framework.push('React');
103
+ if (deps.includes('@nestjs/core')) stack.framework.push('NestJS');
104
+ if (deps.includes('express')) stack.framework.push('Express');
105
+ if (deps.includes('fastify')) stack.framework.push('Fastify');
106
+ if (deps.includes('nuxt')) stack.framework.push('Nuxt');
107
+ if (deps.includes('vue')) stack.framework.push('Vue');
108
+
109
+ if (allDeps.includes('typescript') || fs.existsSync(path.join(TARGET, 'tsconfig.json'))) stack.language = 'TypeScript';
110
+
111
+ if (deps.includes('zustand')) stack.stateManagement = 'Zustand';
112
+ else if (deps.includes('@reduxjs/toolkit') || deps.includes('redux')) stack.stateManagement = 'Redux';
113
+ else if (deps.includes('recoil')) stack.stateManagement = 'Recoil';
114
+ else if (deps.includes('jotai')) stack.stateManagement = 'Jotai';
115
+
116
+ if (allDeps.includes('tailwindcss')) stack.styling = 'Tailwind CSS';
117
+ else if (allDeps.includes('styled-components')) stack.styling = 'styled-components';
118
+ else if (allDeps.includes('@emotion/react')) stack.styling = 'Emotion';
119
+ else if (allDeps.includes('sass') || allDeps.includes('node-sass')) stack.styling = 'Sass/SCSS';
120
+
121
+ if (deps.includes('pg')) stack.database = 'PostgreSQL';
122
+ else if (deps.includes('mssql') || deps.includes('tedious')) stack.database = 'MSSQL';
123
+ else if (deps.includes('mysql2') || deps.includes('mysql')) stack.database = 'MySQL';
124
+ else if (deps.includes('mongodb') || deps.includes('mongoose')) stack.database = 'MongoDB';
125
+
126
+ if (deps.includes('@prisma/client') || devDeps.includes('prisma')) stack.orm = 'Prisma';
127
+ else if (deps.includes('drizzle-orm')) stack.orm = 'Drizzle';
128
+ else if (deps.includes('typeorm')) stack.orm = 'TypeORM';
129
+
130
+ if (allDeps.includes('vite')) stack.buildTool = 'Vite';
131
+ else if (allDeps.includes('webpack')) stack.buildTool = 'Webpack';
132
+ else if (allDeps.includes('esbuild')) stack.buildTool = 'esbuild';
133
+
134
+ if (allDeps.includes('vitest')) stack.testTool = 'Vitest';
135
+ else if (allDeps.includes('jest')) stack.testTool = 'Jest';
136
+ else if (allDeps.includes('mocha')) stack.testTool = 'Mocha';
137
+ else if (allDeps.includes('playwright')) stack.testTool = 'Playwright';
138
+ }
139
+
140
+ if (fs.existsSync(path.join(TARGET, 'pnpm-lock.yaml'))) stack.packageManager = 'pnpm';
141
+ else if (fs.existsSync(path.join(TARGET, 'yarn.lock'))) stack.packageManager = 'yarn';
142
+ else if (fs.existsSync(path.join(TARGET, 'bun.lockb')) || fs.existsSync(path.join(TARGET, 'bun.lock'))) stack.packageManager = 'bun';
143
+ if (fs.existsSync(path.join(TARGET, 'tsconfig.json'))) stack.language = 'TypeScript';
144
+
145
+ return stack;
146
+ }
147
+
148
+ function generateFolderTree() {
149
+ try {
150
+ const entries = fs.readdirSync(TARGET, { withFileTypes: true })
151
+ .filter(e => !(e.name.startsWith('.') && e.name !== '.ai' && e.name !== '.claude') && e.name !== 'node_modules')
152
+ .sort((a, b) => a.isDirectory() === b.isDirectory() ? a.name.localeCompare(b.name) : a.isDirectory() ? -1 : 1);
153
+ return entries.map((e, i) => (i === entries.length - 1 ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 ') + e.name + (e.isDirectory() ? '/' : '')).join('\n');
154
+ } catch { return '[프로젝트 폴더 구조를 여기에 작성]'; }
155
+ }
156
+
157
+ function applyStackToTemplate(template, stack, version) {
158
+ const today = new Date().toISOString().split('T')[0];
159
+ template = template.replace(/\r\n/g, '\n');
160
+ const metaStart = template.indexOf('<!-- JINHAK Standard Metadata');
161
+ if (metaStart > 0) template = template.substring(metaStart);
162
+
163
+ template = template.replace(/jinhak_standard_version:\s*[\d.]+/, 'jinhak_standard_version: ' + version);
164
+ template = template.replace('[YYYY-MM-DD]', today);
165
+ template = template.replace(/\[표준 저장소 URL[^\]]*\]/, 'https://github.com/JinhakStandard/ai-vibecoding');
166
+ template = template.replace(/\[프로젝트명\]/g, stack.projectName);
167
+ template = template.replace(/\[프로젝트 한 줄 설명\]/, stack.description || '<!-- TODO: 프로젝트 설명을 작성하세요 -->');
168
+ template = template.replace(/\[프로젝트 폴더 구조를 여기에 작성\]/, generateFolderTree());
169
+
170
+ const fw = stack.framework.length > 0 ? stack.framework.join(' + ') : '-';
171
+ template = template.replace(/\[React \/ Next\.js \/ Express \/ NestJS 등\]/, fw);
172
+ template = template.replace(/\[JavaScript \/ TypeScript\]/, stack.language);
173
+ template = template.replace(/\[pnpm \/ npm \/ yarn\]/, stack.packageManager);
174
+ template = template.replace(/\[Zustand \/ Redux 등\]/, stack.stateManagement || '-');
175
+ template = template.replace(/\[Tailwind CSS \/ CSS Modules 등\]/, stack.styling || '-');
176
+ template = template.replace(/\[PostgreSQL \/ MySQL \/ MongoDB 등\]/, stack.database || '-');
177
+ template = template.replace(/\[Prisma \/ Drizzle \/ TypeORM 등\]/, stack.orm || '-');
178
+ template = template.replace(/\[Vite \/ Webpack \/ Turbopack 등\]/, stack.buildTool || '-');
179
+
180
+ const pm = stack.packageManager, s = stack.scripts, run = pm === 'npm' ? 'npm run' : pm;
181
+ template = template.replace(/\[pnpm install\]/g, pm === 'npm' ? 'npm install' : `${pm} install`);
182
+ template = template.replace(/\[pnpm dev\]/, s.dev ? `${run} dev` : `# ${run} dev (스크립트 미정의)`);
183
+ template = template.replace(/\[pnpm build\]/g, s.build ? `${run} build` : `# ${run} build (스크립트 미정의)`);
184
+ template = template.replace(/\[pnpm test\]/, s.test ? `${run} test` : `# ${run} test (스크립트 미정의)`);
185
+ template = template.replace(/\[pnpm typecheck\]/, s.typecheck ? `${run} typecheck` : (stack.language === 'TypeScript' ? 'npx tsc --noEmit' : `# TypeScript 미사용`));
186
+ template = template.replace(/\[pnpm lint\]/, s.lint ? `${run} lint` : `# ${run} lint (스크립트 미정의)`);
187
+ template = template.replace(/### 5\. \[프로젝트 특화 규칙\]\n- \[규칙 1 설명\]\n- \[규칙 2 설명\]\n- \[규칙 3 설명\]/, '### 5. 프로젝트 특화 규칙\n<!-- TODO: 프로젝트에 맞는 규칙을 추가하세요 -->\n- (규칙 추가 필요)');
188
+ template = template.replace(/\[apps\/\*\/node_modules packages\/\*\/node_modules\]/, '');
189
+
190
+ return template;
191
+ }
192
+
193
+ function generateAiContent(filename, stack, version) {
194
+ const today = new Date().toISOString().split('T')[0];
195
+ const fw = stack.framework.length > 0 ? stack.framework.join(' + ') : '미감지';
196
+
197
+ switch (filename) {
198
+ case 'SESSION_LOG.md':
199
+ return `# 세션 작업 기록\n\n> 세션 종료 시 반드시 업데이트하세요.\n\n---\n\n## ${today}\n\n### 세션 요약\n- JINHAK AI 개발 표준 v${version} 적용\n\n### 주요 변경\n- \`CLAUDE.md\` - AI 협업 설정 파일 생성\n- \`.claude/\` - Claude Code 설정 복사\n- \`.ai/\` - 프로젝트 문서화 폴더 초기화\n\n### 커밋\n- (표준 적용 커밋 필요)\n\n---\n`;
200
+ case 'CURRENT_SPRINT.md':
201
+ return `# 현재 진행 중인 작업\n\n> 마지막 업데이트: ${today}\n\n---\n\n## 진행 중 (In Progress)\n\n없음\n\n---\n\n## 대기 중 (Pending)\n\n### 우선순위 1: CLAUDE.md 프로젝트 특화 내용 보완\n- [ ] 프로젝트 설명 작성\n- [ ] 프로젝트 특화 규칙 추가\n\n---\n\n## 최근 완료\n\n### ${today}\n- [x] JINHAK AI 개발 표준 v${version} 적용\n\n---\n`;
202
+ case 'DECISIONS.md':
203
+ return `# 아키텍처 의사결정 기록 (ADR)\n\n---\n\n## ADR-001: 기술 스택 선정\n\n### 상태\n승인됨 (${today.substring(0, 7)})\n\n### 컨텍스트\n- ${stack.projectName} 프로젝트의 기술 스택 결정\n\n### 결정\n| 항목 | 선택 |\n|------|------|\n| 프레임워크 | ${fw} |\n| 언어 | ${stack.language} |\n| 패키지 매니저 | ${stack.packageManager} |${stack.stateManagement ? '\n| 상태관리 | ' + stack.stateManagement + ' |' : ''}${stack.database ? '\n| DB | ' + stack.database + ' |' : ''}${stack.orm ? '\n| ORM | ' + stack.orm + ' |' : ''}${stack.buildTool ? '\n| 빌드 | ' + stack.buildTool + ' |' : ''}${stack.testTool ? '\n| 테스트 | ' + stack.testTool + ' |' : ''}\n\n---\n\n## 의사결정 변경 이력\n\n| 날짜 | ADR | 변경 내용 |\n|------|-----|----------|\n| ${today} | ADR-001 | 초기 작성 (CLI 자동 감지) |\n`;
204
+ case 'ARCHITECTURE.md': {
205
+ const lines = [`# ${stack.projectName} 시스템 아키텍처\n\n> ${stack.description || '<!-- TODO: 프로젝트 설명 -->'}\n\n## 기술 스택\n`];
206
+ const front = stack.framework.filter(f => ['React', 'Next.js', 'Vue', 'Nuxt', 'Svelte'].includes(f));
207
+ const back = stack.framework.filter(f => ['Express', 'Fastify', 'NestJS'].includes(f));
208
+ if (front.length) lines.push(`### 프론트엔드\n- **프레임워크**: ${front.join(' + ')}${stack.styling ? '\n- **스타일링**: ' + stack.styling : ''}${stack.stateManagement ? '\n- **상태관리**: ' + stack.stateManagement : ''}\n`);
209
+ if (back.length) lines.push(`### 백엔드\n- **런타임**: Node.js\n- **프레임워크**: ${back.join(' + ')}\n`);
210
+ if (stack.database) lines.push(`### 데이터베이스\n- **DBMS**: ${stack.database}${stack.orm ? '\n- **ORM**: ' + stack.orm : ''}\n`);
211
+ lines.push(`### 빌드 & 개발\n- **언어**: ${stack.language}\n- **패키지 매니저**: ${stack.packageManager}${stack.buildTool ? '\n- **빌드 도구**: ' + stack.buildTool : ''}${stack.testTool ? '\n- **테스트**: ' + stack.testTool : ''}\n\n---\n`);
212
+ return lines.join('\n');
213
+ }
214
+ case 'CONVENTIONS.md': {
215
+ const ext = stack.language === 'TypeScript' ? '.tsx' : '.jsx';
216
+ return `# ${stack.projectName} 코딩 컨벤션\n\nJINHAK 전사 표준 기반.\n\n---\n\n## 네이밍 규칙\n\n| 유형 | 규칙 | 예시 |\n|------|------|------|\n| 컴포넌트 | PascalCase | \`MyComponent${ext}\` |\n| 함수/변수 | camelCase | \`handleSubmit\` |\n| 상수 | UPPER_SNAKE_CASE | \`MAX_RETRY\` |${stack.language === 'TypeScript' ? '\n| 타입 | PascalCase | `UserProfile` |' : ''}\n\n---\n\n## 금지 사항\n\n1. ${stack.language === 'TypeScript' ? '`any` 타입 사용 금지' : '불필요한 타입 강제 변환 금지'}\n2. \`console.log\` 프로덕션 코드 사용 금지\n3. 하드코딩된 URL/포트/비밀키 사용 금지\n4. PUT/PATCH/DELETE HTTP 메서드 사용 금지\n\n---\n`;
217
+ }
218
+ default:
219
+ return `# ${filename.replace('.md', '').replace(/_/g, ' ')}\n\n> 마지막 업데이트: ${today}\n\n---\n`;
220
+ }
221
+ }
222
+
86
223
  // ─────────────────────────────────────────
87
224
  // 메인 커맨드: apply
88
225
  // ─────────────────────────────────────────
@@ -90,6 +227,7 @@ function ensureGitignoreEntries(entries) {
90
227
  function apply() {
91
228
  const version = getLatestVersion();
92
229
  const current = getCurrentVersion();
230
+ const stack = detectTechStack();
93
231
 
94
232
  log('');
95
233
  log(c.bold('JINHAK AI 개발 표준 적용 도구'));
@@ -97,6 +235,18 @@ function apply() {
97
235
  log(`대상 경로: ${c.cyan(TARGET)}`);
98
236
  log('');
99
237
 
238
+ log(c.bold('감지된 기술 스택:'));
239
+ log(` 프로젝트: ${c.cyan(stack.projectName)}`);
240
+ if (stack.description) log(` 설명: ${stack.description}`);
241
+ log(` 프레임워크: ${stack.framework.length > 0 ? c.cyan(stack.framework.join(' + ')) : '-'}`);
242
+ log(` 언어: ${c.cyan(stack.language)} | 패키지 매니저: ${c.cyan(stack.packageManager)}`);
243
+ if (stack.stateManagement) log(` 상태관리: ${stack.stateManagement}`);
244
+ if (stack.styling) log(` 스타일링: ${stack.styling}`);
245
+ if (stack.database) log(` DB: ${stack.database}${stack.orm ? ' + ' + stack.orm : ''}`);
246
+ if (stack.buildTool) log(` 빌드: ${stack.buildTool}`);
247
+ if (stack.testTool) log(` 테스트: ${stack.testTool}`);
248
+ log('');
249
+
100
250
  if (current) {
101
251
  if (current === version) {
102
252
  log(`이미 최신 버전 (v${current})이 적용되어 있습니다.`);
@@ -144,20 +294,14 @@ function apply() {
144
294
  created.push('scripts/security-check-hook.cjs');
145
295
  }
146
296
 
147
- // 6. .ai/ 폴더
148
- const aiFiles = [
149
- 'SESSION_LOG.md',
150
- 'CURRENT_SPRINT.md',
151
- 'DECISIONS.md',
152
- 'ARCHITECTURE.md',
153
- 'CONVENTIONS.md',
154
- ];
297
+ // 6. .ai/ 폴더 (기술 스택 기반 의미있는 초기 내용)
298
+ const aiFiles = ['SESSION_LOG.md', 'CURRENT_SPRINT.md', 'DECISIONS.md', 'ARCHITECTURE.md', 'CONVENTIONS.md'];
155
299
  const aiDir = path.join(TARGET, '.ai');
156
300
  fs.mkdirSync(aiDir, { recursive: true });
157
301
  for (const f of aiFiles) {
158
302
  const dest = path.join(aiDir, f);
159
303
  if (!fs.existsSync(dest)) {
160
- fs.writeFileSync(dest, `# ${f.replace('.md', '').replace(/_/g, ' ')}\n\n> 마지막 업데이트: ${new Date().toISOString().split('T')[0]}\n\n---\n`, 'utf8');
304
+ fs.writeFileSync(dest, generateAiContent(f, stack, version), 'utf8');
161
305
  created.push(`.ai/${f}`);
162
306
  }
163
307
  }
@@ -175,6 +319,33 @@ function apply() {
175
319
  created.push(`.gitignore (+${gitignoreAdded.length}개 항목)`);
176
320
  }
177
321
 
322
+ // 8. CLAUDE.md (기술 스택 자동 치환)
323
+ const claudeMdDest = path.join(TARGET, 'CLAUDE.md');
324
+ const claudeTemplateSrc = path.join(STANDARD_ROOT, 'templates', 'project-claude.md');
325
+ if (!fs.existsSync(claudeMdDest)) {
326
+ if (fs.existsSync(claudeTemplateSrc)) {
327
+ let template = fs.readFileSync(claudeTemplateSrc, 'utf8');
328
+ template = applyStackToTemplate(template, stack, version);
329
+ fs.writeFileSync(claudeMdDest, template, 'utf8');
330
+ created.push('CLAUDE.md (기술 스택 자동 감지 적용)');
331
+ }
332
+ } else if (current && current !== version) {
333
+ let claudeContent = fs.readFileSync(claudeMdDest, 'utf8');
334
+ const today = new Date().toISOString().split('T')[0];
335
+ claudeContent = claudeContent.replace(/jinhak_standard_version:\s*[\d.]+/, 'jinhak_standard_version: ' + version);
336
+ claudeContent = claudeContent.replace(/applied_date:\s*[\d-]+/, 'applied_date: ' + today);
337
+ fs.writeFileSync(claudeMdDest, claudeContent, 'utf8');
338
+ created.push('CLAUDE.md (버전 ' + current + ' -> ' + version + ' 업데이트)');
339
+ }
340
+
341
+ // 9. prompts/
342
+ const promptsSrc = path.join(STANDARD_ROOT, 'prompts');
343
+ const promptsDest = path.join(TARGET, 'prompts');
344
+ if (fs.existsSync(promptsSrc)) {
345
+ const promptCount = copyDir(promptsSrc, promptsDest);
346
+ if (promptCount > 0) created.push('prompts/ (' + promptCount + '개 파일)');
347
+ }
348
+
178
349
  // 결과 출력
179
350
  log('');
180
351
  if (created.length > 0) {
@@ -188,7 +359,7 @@ function apply() {
188
359
 
189
360
  log('');
190
361
  log(c.bold('다음 단계:'));
191
- log(' 1. CLAUDE.md 프로젝트에 맞게 작성/수정');
362
+ log(' 1. CLAUDE.md TODO 항목 확인 및 프로젝트에 맞게 수정');
192
363
  log(' 2. Claude Code 세션 재시작 (settings.json 반영)');
193
364
  log(' 3. /session-start 로 세션 시작');
194
365
  log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinhak-ai-standard",
3
- "version": "2.6.0",
3
+ "version": "2.7.1",
4
4
  "description": "JINHAK 전사 AI 개발 표준 — Claude Code 프로젝트 자동 적용 도구",
5
5
  "bin": {
6
6
  "jinhak-ai-standard": "./bin/cli.cjs"
@@ -5,7 +5,7 @@
5
5
  ---
6
6
 
7
7
  <!-- JINHAK Standard Metadata - 이 메타 정보는 자동 버전 관리에 사용됩니다. 삭제하지 마세요. -->
8
- <!-- jinhak_standard_version: 2.0 -->
8
+ <!-- jinhak_standard_version: 2.7 -->
9
9
  <!-- jinhak_standard_repo: [표준 저장소 URL - https://github.com/JinhakStandard/ai-vibecoding 로 교체] -->
10
10
  <!-- applied_date: [YYYY-MM-DD] -->
11
11