jinhak-ai-standard 2.6.0 → 2.7.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ Claude Code의 `/session-start` 스킬이 이 파일을 참조하여 표준 업
5
5
 
6
6
  ---
7
7
 
8
+ ## [2.7] - 2026-03-04
9
+
10
+ ### CLI 기술 스택 자동 감지 + CLAUDE.md 완전 자동 생성
11
+
12
+ `npx jinhak-ai-standard` 실행 시 프로젝트의 기술 스택을 자동 감지하여 CLAUDE.md와 .ai/ 폴더를 의미있는 내용으로 생성합니다.
13
+
14
+ ### 추가
15
+ - `detectTechStack()` — package.json에서 프레임워크, 언어, 패키지매니저, 상태관리, 스타일링, DB, ORM, 빌드도구, 테스트도구 자동 감지
16
+ - `applyStackToTemplate()` — templates/project-claude.md 템플릿의 `[대괄호]` 플레이스홀더를 감지된 기술 스택으로 자동 치환
17
+ - `generateFolderTree()` — 프로젝트 1-depth 폴더 트리 자동 생성
18
+ - `generateAiContent()` — .ai/ 폴더 파일에 감지된 기술 스택 기반 의미있는 초기 내용 생성 (ADR-001 포함)
19
+
20
+ ### 변경
21
+ - `bin/cli.cjs`: apply() 흐름에 기술 스택 감지 → 템플릿 치환 → 의미있는 .ai/ 생성 단계 추가
22
+ - `package.json`: 버전 2.6.0 → 2.7.0
23
+ - `CLAUDE.md`: 버전 2.6 → 2.7
24
+
25
+ ### Migration Guide (v2.6 → v2.7)
26
+
27
+ 기존 v2.6 프로젝트에서 업데이트 시:
28
+ 1. `npx jinhak-ai-standard` 재실행 — 기존 CLAUDE.md가 있으면 덮어쓰지 않고 버전만 안내
29
+ 2. 새 프로젝트에서는 자동으로 기술 스택이 감지되어 CLAUDE.md에 반영됨
30
+
31
+ ---
32
+
8
33
  ## [2.6] - 2026-03-02
9
34
 
10
35
  ### npm 패키지 + GitHub Releases 배포 시스템
@@ -17,9 +42,10 @@ Claude Code의 `/session-start` 스킬이 이 파일을 참조하여 표준 업
17
42
  - `.github/workflows/release.yml` — 태그 push 시 GitHub Release + npm publish 자동화
18
43
 
19
44
  ### 변경
45
+ - `bin/cli.cjs`: CLAUDE.md 자동 생성(신규) 및 버전 메타데이터 업데이트(기존), prompts/ 디렉토리 복사 추가
20
46
  - `.claude/skills/apply-standard/SKILL.md`: 0단계에 npm 패키지 → Release tarball → git clone 3단계 폴백 추가
21
47
  - `QUICK_START_PROMPT.md`: 방법 1(npx), 방법 2(프롬프트), 방법 3(Release tarball) 구분
22
- - `README.md`: v2.5 → v2.6, 적용 방법 1~4로 재정리, 문서 구조에 새 파일 추가
48
+ - `README.md`: v2.5 → v2.6, 적용 방법 1~4로 재정리, 문서 구조에 새 파일 추가, npx 설명에 CLAUDE.md 자동 생성 반영
23
49
  - `CLAUDE.md`: 버전 2.5 → 2.6, 프로젝트 구조에 package.json/bin/cli.cjs 추가
24
50
 
25
51
  ### 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
 
@@ -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.6.tar.gz \
103
103
  | tar -xz -C /tmp/jinhak-standards --strip-components=1
104
104
  ```
105
105
 
package/README.md CHANGED
@@ -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.6 -->
109
109
  > <!-- jinhak_standard_repo: https://github.com/JinhakStandard/ai-vibecoding -->
110
- > <!-- applied_date: 2026-02-28 -->
110
+ > <!-- applied_date: 2026-03-02 -->
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/ # 코드 리뷰 프롬프트
@@ -167,7 +167,7 @@ JinhakStandard/
167
167
  │ ├── settings.json # 권한, hooks 설정
168
168
  │ ├── scripts/
169
169
  │ │ └── session-briefing.cjs # 세션 자동 브리핑 Hook 스크립트
170
- │ └── skills/ # 슬래시 명령어 (13개)
170
+ │ └── skills/ # 슬래시 명령어 (14개)
171
171
  │ ├── apply-standard/ # /apply-standard - 표준 적용/업데이트
172
172
  │ ├── commit/ # /commit - 커밋 생성
173
173
  │ ├── debug/ # /debug - 체계적 디버깅 (v2.3)
@@ -175,9 +175,10 @@ JinhakStandard/
175
175
  │ ├── orchestrate/ # /orchestrate - Agent Teams + 2단계 검증 (v2.3)
176
176
  │ ├── review-pr/ # /review-pr - PR 리뷰
177
177
  │ ├── 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)
178
+ │ ├── prompt-register/ # /prompt-register - 프롬프트 등록
179
+ │ ├── prompt-search/ # /prompt-search - 프롬프트 검색
180
+ │ ├── prompt-quality-check/ # /prompt-quality-check - 품질 검증
181
+ │ ├── prompt-report/ # /prompt-report - 사용량 리포트 (v2.5)
181
182
  │ ├── session-end/ # /session-end - 세션 종료 (v2.0.2)
182
183
  │ ├── session-start/ # /session-start - 세션 시작
183
184
  │ └── test/ # /test - 테스트 + Red-Green 검증 (v2.3)
@@ -213,26 +214,20 @@ JinhakStandard/
213
214
 
214
215
  ## 빠른 시작
215
216
 
216
- > **자동 적용(권장):** [QUICK_START_PROMPT.md](./QUICK_START_PROMPT.md)의 프롬프트를 사용하세요.
217
+ > **자동 적용(권장):** `npx jinhak-ai-standard` 또는 [QUICK_START_PROMPT.md](./QUICK_START_PROMPT.md)의 프롬프트를 사용하세요.
217
218
 
218
219
  ### 수동 적용 (필요 시)
219
220
 
220
221
  ```bash
221
- # 1. 저장소를 클론하여 참고
222
- git clone https://github.com/JinhakStandard/ai-vibecoding.git /tmp/jinhak-standards
222
+ # 방법 A: npx로 번에 적용 (권장)
223
+ npx jinhak-ai-standard
223
224
 
224
- # 2. CLAUDE.md 템플릿 복사프로젝트 정보 수정
225
+ # 방법 B: 저장소 클론수동 복사
226
+ git clone https://github.com/JinhakStandard/ai-vibecoding.git /tmp/jinhak-standards
225
227
  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에 추가
228
+ cp -r /tmp/jinhak-standards/.claude .
229
+ cp -r /tmp/jinhak-standards/security .
230
+ mkdir -p .ai
236
231
  echo "CLAUDE.local.md" >> .gitignore
237
232
  ```
238
233
 
@@ -308,7 +303,8 @@ claude
308
303
 
309
304
  | 버전 | 날짜 | 변경 내용 |
310
305
  |------|------|----------|
311
- | **2.5** | **2026-03-01** | **프롬프트 라이브러리 Phase 2 + npm/GitHub Releases 배포: JABIS API 연동, `/prompt-report` 스킬, `npx jinhak-ai-standard` CLI** |
306
+ | **2.6** | **2026-03-02** | **npm 패키지 + GitHub Releases 배포: `npx jinhak-ai-standard` CLI (CLAUDE.md 자동 생성/업데이트, prompts/ 복사 포함)** |
307
+ | 2.5 | 2026-03-01 | 프롬프트 라이브러리 Phase 2: JABIS API 연동, `/prompt-report` 스킬, 사용량 추적 |
312
308
  | 2.4 | 2026-02-28 | 프롬프트 라이브러리 Phase 1: 등록/검색/품질검증 시스템, 예시 프롬프트 3개, `/prompt-register` `/prompt-search` `/prompt-quality-check` 스킬 |
313
309
  | 2.3 | 2026-02-28 | 적응적 추천 + skills.sh 모범사례 + 멀티 에이전트 패턴: 가중치 비평, C6 Hard Gate, State Contract, 스킬 조합 가이드, 2단계 검증, `/debug` 스킬, AI 합리화 방지 |
314
310
  | 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.0",
4
4
  "description": "JINHAK 전사 AI 개발 표준 — Claude Code 프로젝트 자동 적용 도구",
5
5
  "bin": {
6
6
  "jinhak-ai-standard": "./bin/cli.cjs"