jun-claude-code 0.0.11 → 0.0.13
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/README.md +122 -83
- package/dist/cli.js +40 -0
- package/dist/copy.js +92 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/init-context.d.ts +4 -0
- package/dist/init-context.js +143 -0
- package/dist/init-project.js +4 -4
- package/dist/validate.d.ts +4 -0
- package/dist/validate.js +105 -0
- package/package.json +2 -2
- package/{.claude → templates/global}/CLAUDE.md +50 -21
- package/{.claude → templates/global}/agents/architect.md +23 -11
- package/{.claude → templates/global}/agents/code-reviewer.md +17 -7
- package/{.claude → templates/global}/agents/code-writer.md +27 -15
- package/{.claude → templates/global}/agents/context-collector.md +11 -3
- package/{.claude → templates/global}/agents/context-manager.md +21 -9
- package/{.claude → templates/global}/agents/designer.md +32 -20
- package/{.claude → templates/global}/agents/director.md +43 -31
- package/{.claude → templates/global}/agents/explore.md +13 -5
- package/{.claude → templates/global}/agents/git-manager.md +39 -22
- package/{.claude → templates/global}/agents/impact-analyzer.md +17 -7
- package/{.claude → templates/global}/agents/qa-tester.md +22 -8
- package/{.claude → templates/global}/agents/simple-code-writer.md +13 -5
- package/{.claude → templates/global}/agents/task-planner.md +18 -10
- package/{.claude → templates/global}/hooks/skill-forced.sh +56 -56
- package/{.claude → templates/global}/hooks/workflow-enforced.sh +49 -31
- package/{.claude → templates/global}/skills/Backend/SKILL.md +38 -9
- package/{.claude → templates/global}/skills/Coding/SKILL.md +69 -29
- package/{.claude → templates/global}/skills/Director/SKILL.md +9 -5
- package/{.claude → templates/global}/skills/Documentation/SKILL.md +68 -1
- package/{.claude → templates/global}/skills/Git/SKILL.md +8 -4
- package/{.claude → templates/global}/skills/Git/git.md +60 -28
- package/{.claude → templates/global}/skills/Git/pr-apply.md +18 -6
- package/{.claude → templates/global}/skills/Git/pr-review.md +4 -0
- package/templates/global/skills/PromptStructuring/SKILL.md +67 -0
- package/templates/global/skills/PromptStructuring/positive-phrasing.md +206 -0
- package/templates/global/skills/PromptStructuring/xml-tags.md +330 -0
- package/{.claude → templates/global}/skills/React/SKILL.md +28 -8
- package/{.claude → templates/global}/skills/React/react-hook-form.md +20 -12
- package/{.claude → templates/global}/skills/React/tailwind-styled.md +29 -7
- package/{.claude → templates/global}/skills/React/tanstack-router.md +21 -9
- package/templates/project/agents/context-generator.md +66 -0
- package/{.claude → templates/project}/agents/project-task-manager.md +19 -7
- package/templates/project/skills/ContextGeneration/SKILL.md +160 -0
- package/templates/project/workflows/context-gen.yml +75 -0
- /package/{.claude → templates/global}/settings.json +0 -0
- /package/{.claude → templates/project}/hooks/task-loader.sh +0 -0
- /package/{.claude → templates/project}/project.env.example +0 -0
package/README.md
CHANGED
|
@@ -1,140 +1,179 @@
|
|
|
1
1
|
# jun-claude-code
|
|
2
2
|
|
|
3
|
-
Claude Code 설정 템플릿 CLI
|
|
3
|
+
Claude Code 설정 템플릿 CLI. 미리 정의된 Agents, Skills, Hooks, Workflow를 프로젝트에 설치하여 Claude Code 환경을 빠르게 구축합니다.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# npx로 바로 실행
|
|
9
|
-
npx jun-claude-code
|
|
10
|
-
|
|
11
|
-
# 또는 전역 설치
|
|
12
|
-
npm install -g jun-claude-code
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## 사용법
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# 기본 실행 (기존 파일이 있으면 덮어쓰기 확인)
|
|
19
|
-
jun-claude-code
|
|
20
|
-
|
|
21
|
-
# 미리보기 (실제 복사 없이 복사될 파일 목록 확인)
|
|
22
|
-
jun-claude-code --dry-run
|
|
5
|
+
## 포함 내용
|
|
23
6
|
|
|
24
|
-
|
|
25
|
-
jun-claude-code --force
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## 포함된 설정
|
|
7
|
+
### Agents (`templates/global/agents/`)
|
|
29
8
|
|
|
30
|
-
|
|
9
|
+
작업별 전문 Subagent 13종. Main Agent의 Context Window를 절약하면서 각 작업을 위임합니다.
|
|
31
10
|
|
|
32
|
-
| Agent |
|
|
11
|
+
| Agent | 역할 |
|
|
33
12
|
|-------|------|
|
|
34
|
-
| `explore` |
|
|
13
|
+
| `explore` | 코드베이스 탐색 |
|
|
35
14
|
| `task-planner` | 작업 계획 수립 |
|
|
36
|
-
| `code-writer` | 코드 작성 |
|
|
15
|
+
| `code-writer` | 코드 작성 (Opus) |
|
|
16
|
+
| `simple-code-writer` | 단순 수정 (Haiku) |
|
|
37
17
|
| `code-reviewer` | 셀프 코드 리뷰 |
|
|
38
|
-
| `git-manager` | Git
|
|
18
|
+
| `git-manager` | Git 커밋/PR |
|
|
39
19
|
| `impact-analyzer` | 사이드이펙트 분석 |
|
|
40
20
|
| `qa-tester` | 테스트/빌드 검증 |
|
|
41
21
|
| `architect` | 아키텍처 설계 |
|
|
42
22
|
| `designer` | UI/UX 스타일링 |
|
|
23
|
+
| `director` | 작업 총괄 디렉터 |
|
|
43
24
|
| `context-collector` | Context 수집 |
|
|
44
25
|
| `context-manager` | Context 문서 관리 |
|
|
45
26
|
|
|
46
|
-
### Skills (
|
|
27
|
+
### Skills (`templates/global/skills/`)
|
|
47
28
|
|
|
48
29
|
| Skill | 설명 |
|
|
49
30
|
|-------|------|
|
|
50
31
|
| `Coding` | 공통 코딩 원칙 (SRP, 응집도, 가독성) |
|
|
51
|
-
| `Git` | Git 커밋/PR
|
|
32
|
+
| `Git` | Git 커밋/PR 규칙, PR 리뷰, 피드백 적용 |
|
|
52
33
|
| `Backend` | 백엔드 개발 원칙 (레이어, TypeORM) |
|
|
34
|
+
| `React` | React 개발 (TanStack Router, React Hook Form, Tailwind) |
|
|
35
|
+
| `Documentation` | .claude 문서 작성 가이드 |
|
|
36
|
+
| `Director` | 디렉터 Agent 운영 스킬 |
|
|
53
37
|
|
|
54
|
-
### Hooks (
|
|
38
|
+
### Hooks (`templates/global/hooks/`)
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
| Hook | 설명 |
|
|
41
|
+
|------|------|
|
|
42
|
+
| `workflow-enforced.sh` | 워크플로우 순서 강제 프로토콜 |
|
|
43
|
+
| `skill-forced.sh` | Skill/Agent 평가 프로토콜 |
|
|
58
44
|
|
|
59
|
-
|
|
45
|
+
### Project Agents (`templates/project/agents/`)
|
|
60
46
|
|
|
61
|
-
|
|
47
|
+
프로젝트 `.claude/`에 설치되는 프로젝트별 Agent.
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
├── skills/ # Skill 가이드 수정/추가
|
|
68
|
-
├── hooks/ # 자동 실행 스크립트
|
|
69
|
-
└── context/ # 프로젝트별 Context (직접 추가)
|
|
70
|
-
```
|
|
49
|
+
| Agent | 역할 |
|
|
50
|
+
|-------|------|
|
|
51
|
+
| `project-task-manager` | GitHub Project 태스크 관리 |
|
|
52
|
+
| `context-generator` | Context 자동 생성 |
|
|
71
53
|
|
|
72
|
-
###
|
|
54
|
+
### Project Skills (`templates/project/skills/`)
|
|
73
55
|
|
|
74
|
-
|
|
56
|
+
| Skill | 설명 |
|
|
57
|
+
|-------|------|
|
|
58
|
+
| `ContextGeneration` | Context 자동 생성 스킬 |
|
|
75
59
|
|
|
60
|
+
### Workflow
|
|
61
|
+
|
|
62
|
+
Planning -> Validation -> Implementation -> Review 순서의 작업 워크플로우와 Context 절약 원칙(Subagent 위임 규칙)을 정의합니다.
|
|
63
|
+
|
|
64
|
+
## 설치
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# npx로 바로 실행
|
|
68
|
+
npx jun-claude-code
|
|
69
|
+
|
|
70
|
+
# 또는 전역 설치
|
|
71
|
+
npm install -g jun-claude-code
|
|
76
72
|
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
|
|
74
|
+
## 사용법
|
|
75
|
+
|
|
76
|
+
### 기본 명령어: 설정 복사
|
|
77
|
+
|
|
78
|
+
`templates/global` 설정 파일을 `~/.claude`로 복사합니다.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
jun-claude-code
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
| 옵션 | 설명 |
|
|
85
|
+
|------|------|
|
|
86
|
+
| `--dry-run`, `-d` | 실제 복사 없이 복사될 파일 목록만 확인 |
|
|
87
|
+
| `--force`, `-f` | 확인 없이 모든 파일 덮어쓰기 |
|
|
85
88
|
|
|
86
|
-
`init-project
|
|
89
|
+
### `init-project`: GitHub Project 연동
|
|
90
|
+
|
|
91
|
+
프로젝트 디렉토리에서 GitHub Project 태스크 관리를 설정합니다.
|
|
87
92
|
|
|
88
93
|
```bash
|
|
89
|
-
# 프로젝트 루트에서 실행
|
|
90
94
|
jun-claude-code init-project
|
|
91
95
|
```
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
3. **Repository** (owner/repo 형식)
|
|
97
|
+
인터랙티브로 다음을 설정합니다:
|
|
98
|
+
- GitHub Owner (org 또는 user)
|
|
99
|
+
- Project Number
|
|
100
|
+
- Repository (owner/repo 형식)
|
|
98
101
|
|
|
99
|
-
설정
|
|
102
|
+
설정 후 생성되는 파일:
|
|
100
103
|
|
|
101
104
|
```
|
|
102
|
-
|
|
103
|
-
├── .
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
│
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
│ └── project-task-manager.md # 태스크 관리 Agent
|
|
105
|
+
.claude/
|
|
106
|
+
├── project.env # GitHub Project 환경변수
|
|
107
|
+
├── settings.json # StartSession hook 설정
|
|
108
|
+
├── hooks/
|
|
109
|
+
│ └── task-loader.sh # 태스크 조회 스크립트
|
|
110
|
+
└── agents/
|
|
111
|
+
└── project-task-manager.md # 태스크 관리 Agent
|
|
110
112
|
```
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
> `read:project` 스코프 필요: `gh auth refresh -s read:project,project`
|
|
115
|
+
|
|
116
|
+
### `init-context`: Context 자동 생성 설정
|
|
113
117
|
|
|
114
|
-
|
|
118
|
+
GitHub Actions를 통한 Context 문서 자동 생성을 설정합니다.
|
|
115
119
|
|
|
116
120
|
```bash
|
|
117
|
-
|
|
118
|
-
GITHUB_PROJECT_OWNER=your-org
|
|
119
|
-
GITHUB_PROJECT_NUMBER=1
|
|
120
|
-
GITHUB_PROJECT_REPO=your-org/your-repo
|
|
121
|
+
jun-claude-code init-context
|
|
121
122
|
```
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
설정 후 생성되는 파일:
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
```
|
|
127
|
+
.github/workflows/
|
|
128
|
+
└── context-gen.yml # Context 생성 GitHub Actions 워크플로우
|
|
129
|
+
|
|
130
|
+
.claude/
|
|
131
|
+
├── agents/
|
|
132
|
+
│ └── context-generator.md # Context 자동 생성 Agent
|
|
133
|
+
├── skills/
|
|
134
|
+
│ └── ContextGeneration/
|
|
135
|
+
│ └── SKILL.md # Context 자동 생성 Skill
|
|
136
|
+
└── context/
|
|
137
|
+
├── codebase/
|
|
138
|
+
│ └── INDEX.md # 코드베이스 모듈 참조 목록
|
|
139
|
+
└── business/
|
|
140
|
+
└── INDEX.md # 비즈니스 도메인 참조 목록
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
> `CLAUDE_CODE_OAUTH_TOKEN`를 GitHub repository secrets에 추가해야 합니다.
|
|
144
|
+
|
|
145
|
+
## 프로젝트 구조
|
|
126
146
|
|
|
127
|
-
```bash
|
|
128
|
-
gh auth refresh -s read:project,project
|
|
129
147
|
```
|
|
148
|
+
templates/
|
|
149
|
+
├── global/ # ~/.claude/에 설치되는 전역 설정
|
|
150
|
+
│ ├── CLAUDE.md # 작업 가이드 (워크플로우, Context 절약 원칙)
|
|
151
|
+
│ ├── agents/ # Subagent 정의 (13종)
|
|
152
|
+
│ ├── skills/ # 스킬 가이드 (코딩, Git, BE, FE 등)
|
|
153
|
+
│ ├── hooks/ # 자동 실행 스크립트 (워크플로우 강제, 스킬 평가)
|
|
154
|
+
│ └── settings.json # Claude Code 전역 설정
|
|
155
|
+
└── project/ # 프로젝트 .claude/에 설치되는 프로젝트별 설정
|
|
156
|
+
├── agents/ # project-task-manager, context-generator Agent
|
|
157
|
+
├── skills/ # ContextGeneration Skill
|
|
158
|
+
├── hooks/ # task-loader Hook
|
|
159
|
+
├── workflows/ # context-gen Workflow
|
|
160
|
+
└── project.env.example
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 커스터마이징
|
|
130
164
|
|
|
131
|
-
|
|
165
|
+
설치 후 `~/.claude/`에서 필요에 맞게 수정할 수 있습니다. 프로젝트별로는 프로젝트 루트에 `.claude/`를 만들어 설정을 추가합니다.
|
|
132
166
|
|
|
133
|
-
|
|
167
|
+
```
|
|
168
|
+
your-project/
|
|
169
|
+
├── .claude/
|
|
170
|
+
│ ├── context/ # 프로젝트 아키텍처, 도메인 지식
|
|
171
|
+
│ └── skills/ # 프로젝트 전용 스킬
|
|
172
|
+
└── CLAUDE.md # 프로젝트 설명
|
|
173
|
+
```
|
|
134
174
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
3. **Git 작업 위임**: 모든 Git 작업은 `git-manager` Agent 사용
|
|
175
|
+
- **context/**: 프로젝트의 사실/배경 정보 (아키텍처, 도메인 모델, API 스펙 등)
|
|
176
|
+
- **skills/**: 프로젝트 고유의 작업 절차 (배포 방법, 테스트 규칙 등)
|
|
138
177
|
|
|
139
178
|
## License
|
|
140
179
|
|
package/dist/cli.js
CHANGED
|
@@ -33,9 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
36
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
40
|
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
38
42
|
const copy_1 = require("./copy");
|
|
43
|
+
const init_context_1 = require("./init-context");
|
|
44
|
+
const validate_1 = require("./validate");
|
|
39
45
|
const program = new commander_1.Command();
|
|
40
46
|
program
|
|
41
47
|
.name('jun-claude-code')
|
|
@@ -78,4 +84,38 @@ program
|
|
|
78
84
|
process.exit(1);
|
|
79
85
|
}
|
|
80
86
|
});
|
|
87
|
+
program
|
|
88
|
+
.command('init-context')
|
|
89
|
+
.description('Setup context auto-generation with GitHub Actions')
|
|
90
|
+
.action(async () => {
|
|
91
|
+
try {
|
|
92
|
+
await (0, init_context_1.initContext)();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error instanceof Error) {
|
|
96
|
+
console.error('Error:', error.message);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.error('An unexpected error occurred');
|
|
100
|
+
}
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
program
|
|
105
|
+
.command('validate')
|
|
106
|
+
.description('Validate template directory structure')
|
|
107
|
+
.action(async () => {
|
|
108
|
+
try {
|
|
109
|
+
await (0, validate_1.validateTemplates)();
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error instanceof Error) {
|
|
113
|
+
console.error(chalk_1.default.red('Validation failed:'), error.message);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.error(chalk_1.default.red('Validation failed'));
|
|
117
|
+
}
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
81
121
|
program.parse();
|
package/dist/copy.js
CHANGED
|
@@ -93,12 +93,12 @@ function copyFile(src, dest) {
|
|
|
93
93
|
fs.copyFileSync(src, dest);
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
|
-
* Get the source
|
|
96
|
+
* Get the source templates/global directory path (from package installation)
|
|
97
97
|
*/
|
|
98
|
-
function
|
|
98
|
+
function getSourceGlobalDir() {
|
|
99
99
|
// When installed as npm package, __dirname is in dist/
|
|
100
|
-
//
|
|
101
|
-
return path.resolve(__dirname, '..', '
|
|
100
|
+
// templates/global folder is at package root
|
|
101
|
+
return path.resolve(__dirname, '..', 'templates', 'global');
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Get the destination .claude directory path (user's home)
|
|
@@ -110,26 +110,100 @@ function getDestClaudeDir() {
|
|
|
110
110
|
}
|
|
111
111
|
return path.join(homeDir, '.claude');
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Merge settings.json from source into destination.
|
|
115
|
+
* Hooks are merged per event key; duplicate hook entries (by deep equality) are skipped.
|
|
116
|
+
* Non-hook keys are shallow-merged (source wins for new keys, dest preserved for existing).
|
|
117
|
+
*/
|
|
118
|
+
function mergeSettingsJson(sourceDir, destDir) {
|
|
119
|
+
const sourcePath = path.join(sourceDir, 'settings.json');
|
|
120
|
+
const destPath = path.join(destDir, 'settings.json');
|
|
121
|
+
if (!fs.existsSync(sourcePath)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let sourceSettings;
|
|
125
|
+
try {
|
|
126
|
+
sourceSettings = JSON.parse(fs.readFileSync(sourcePath, 'utf-8'));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
console.log(chalk_1.default.yellow(' Warning: Could not parse source settings.json, skipping merge.'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
let destSettings = {};
|
|
133
|
+
if (fs.existsSync(destPath)) {
|
|
134
|
+
try {
|
|
135
|
+
destSettings = JSON.parse(fs.readFileSync(destPath, 'utf-8'));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
console.log(chalk_1.default.yellow(' Warning: Could not parse destination settings.json, creating fresh.'));
|
|
139
|
+
destSettings = {};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Merge top-level keys (source fills in missing keys, dest's existing keys preserved)
|
|
143
|
+
for (const key of Object.keys(sourceSettings)) {
|
|
144
|
+
if (key === 'hooks') {
|
|
145
|
+
continue; // hooks are merged separately below
|
|
146
|
+
}
|
|
147
|
+
if (!(key in destSettings)) {
|
|
148
|
+
destSettings[key] = sourceSettings[key];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Merge hooks
|
|
152
|
+
const sourceHooks = sourceSettings.hooks || {};
|
|
153
|
+
if (!destSettings.hooks) {
|
|
154
|
+
destSettings.hooks = {};
|
|
155
|
+
}
|
|
156
|
+
const destHooks = destSettings.hooks;
|
|
157
|
+
for (const event of Object.keys(sourceHooks)) {
|
|
158
|
+
const sourceEntries = sourceHooks[event] || [];
|
|
159
|
+
if (!destHooks[event]) {
|
|
160
|
+
destHooks[event] = [];
|
|
161
|
+
}
|
|
162
|
+
const destEntries = destHooks[event];
|
|
163
|
+
// Extract a command-based key from a hook entry for duplicate detection.
|
|
164
|
+
// Type 1: { type: "command", command: "..." } → returns "type:command"
|
|
165
|
+
// Type 2: { hooks: [{ type: "command", command: "..." }, ...] } → returns sorted "type:command" joined
|
|
166
|
+
const getHookKey = (entry) => {
|
|
167
|
+
if (entry.hooks && Array.isArray(entry.hooks)) {
|
|
168
|
+
return entry.hooks
|
|
169
|
+
.map((h) => `${h.type || ''}:${h.command || ''}`)
|
|
170
|
+
.sort()
|
|
171
|
+
.join('\n');
|
|
172
|
+
}
|
|
173
|
+
return `${entry.type || ''}:${entry.command || ''}`;
|
|
174
|
+
};
|
|
175
|
+
// Build a Set of command keys from existing entries for fast duplicate detection
|
|
176
|
+
const existingKeys = new Set(destEntries.map((entry) => getHookKey(entry)));
|
|
177
|
+
for (const entry of sourceEntries) {
|
|
178
|
+
const key = getHookKey(entry);
|
|
179
|
+
if (!existingKeys.has(key)) {
|
|
180
|
+
destEntries.push(entry);
|
|
181
|
+
existingKeys.add(key);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
ensureDir(destDir);
|
|
186
|
+
fs.writeFileSync(destPath, JSON.stringify(destSettings, null, 2) + '\n', 'utf-8');
|
|
187
|
+
console.log(` ${chalk_1.default.blue('[merged]')} settings.json`);
|
|
188
|
+
}
|
|
113
189
|
/**
|
|
114
190
|
* Copy .claude files to user's home directory
|
|
115
191
|
*/
|
|
116
192
|
async function copyClaudeFiles(options = {}) {
|
|
117
193
|
const { dryRun = false, force = false } = options;
|
|
118
|
-
const sourceDir =
|
|
194
|
+
const sourceDir = getSourceGlobalDir();
|
|
119
195
|
const destDir = getDestClaudeDir();
|
|
120
196
|
console.log(chalk_1.default.blue('Source:'), sourceDir);
|
|
121
197
|
console.log(chalk_1.default.blue('Destination:'), destDir);
|
|
122
198
|
console.log();
|
|
123
199
|
// Check if source exists
|
|
124
200
|
if (!fs.existsSync(sourceDir)) {
|
|
125
|
-
console.error(chalk_1.default.red('Error:'), 'Source
|
|
201
|
+
console.error(chalk_1.default.red('Error:'), 'Source templates/global directory not found');
|
|
126
202
|
process.exit(1);
|
|
127
203
|
}
|
|
128
|
-
// Files to exclude from global copy (
|
|
204
|
+
// Files to exclude from global copy (merge-handled separately)
|
|
129
205
|
const EXCLUDE_FROM_GLOBAL = [
|
|
130
|
-
'
|
|
131
|
-
'agents/project-task-manager.md',
|
|
132
|
-
'project.env.example',
|
|
206
|
+
'settings.json',
|
|
133
207
|
];
|
|
134
208
|
// Get all files to copy
|
|
135
209
|
const allFiles = getAllFiles(sourceDir);
|
|
@@ -137,7 +211,7 @@ async function copyClaudeFiles(options = {}) {
|
|
|
137
211
|
return !EXCLUDE_FROM_GLOBAL.includes(file);
|
|
138
212
|
});
|
|
139
213
|
if (files.length === 0) {
|
|
140
|
-
console.log(chalk_1.default.yellow('No files found in
|
|
214
|
+
console.log(chalk_1.default.yellow('No files found in templates/global directory'));
|
|
141
215
|
return;
|
|
142
216
|
}
|
|
143
217
|
console.log(chalk_1.default.cyan(`Found ${files.length} files to copy:`));
|
|
@@ -152,6 +226,11 @@ async function copyClaudeFiles(options = {}) {
|
|
|
152
226
|
const status = exists ? chalk_1.default.yellow('[overwrite]') : chalk_1.default.green('[new]');
|
|
153
227
|
console.log(` ${status} ${file}`);
|
|
154
228
|
}
|
|
229
|
+
// settings.json merge indicator
|
|
230
|
+
const sourceSettingsExists = fs.existsSync(path.join(sourceDir, 'settings.json'));
|
|
231
|
+
if (sourceSettingsExists) {
|
|
232
|
+
console.log(` ${chalk_1.default.blue('[merge]')} settings.json`);
|
|
233
|
+
}
|
|
155
234
|
console.log();
|
|
156
235
|
console.log(chalk_1.default.yellow('No files were copied (dry run mode)'));
|
|
157
236
|
return;
|
|
@@ -177,6 +256,8 @@ async function copyClaudeFiles(options = {}) {
|
|
|
177
256
|
console.log(` ${status} ${file}`);
|
|
178
257
|
copiedCount++;
|
|
179
258
|
}
|
|
259
|
+
// Merge settings.json (hooks are merged, not overwritten)
|
|
260
|
+
mergeSettingsJson(sourceDir, destDir);
|
|
180
261
|
console.log();
|
|
181
262
|
console.log(chalk_1.default.green(`Done! Copied ${copiedCount} files, skipped ${skippedCount} files.`));
|
|
182
263
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.initProject = exports.copyClaudeFiles = void 0;
|
|
3
|
+
exports.initContext = exports.initProject = exports.copyClaudeFiles = void 0;
|
|
4
4
|
var copy_1 = require("./copy");
|
|
5
5
|
Object.defineProperty(exports, "copyClaudeFiles", { enumerable: true, get: function () { return copy_1.copyClaudeFiles; } });
|
|
6
6
|
var init_project_1 = require("./init-project");
|
|
7
7
|
Object.defineProperty(exports, "initProject", { enumerable: true, get: function () { return init_project_1.initProject; } });
|
|
8
|
+
var init_context_1 = require("./init-context");
|
|
9
|
+
Object.defineProperty(exports, "initContext", { enumerable: true, get: function () { return init_context_1.initContext; } });
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.initContext = initContext;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
/**
|
|
44
|
+
* Get the templates/project directory path (from package installation)
|
|
45
|
+
*/
|
|
46
|
+
function getTemplatesDir() {
|
|
47
|
+
return path.resolve(__dirname, '..', 'templates', 'project');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Initialize context auto-generation with GitHub Actions
|
|
51
|
+
*/
|
|
52
|
+
async function initContext() {
|
|
53
|
+
const cwd = process.cwd();
|
|
54
|
+
console.log(chalk_1.default.bold('\n📄 Initializing Context Auto-Generation...\n'));
|
|
55
|
+
// 1. templates/project/workflows/context-gen.yml → .github/workflows/context-gen.yml 복사
|
|
56
|
+
const templateSrc = path.join(getTemplatesDir(), 'workflows', 'context-gen.yml');
|
|
57
|
+
const workflowDest = path.join(cwd, '.github', 'workflows', 'context-gen.yml');
|
|
58
|
+
fs.mkdirSync(path.dirname(workflowDest), { recursive: true });
|
|
59
|
+
if (fs.existsSync(workflowDest)) {
|
|
60
|
+
console.log(chalk_1.default.yellow(' ⚠ .github/workflows/context-gen.yml already exists, skipping'));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
fs.copyFileSync(templateSrc, workflowDest);
|
|
64
|
+
console.log(chalk_1.default.green(' ✓ Created .github/workflows/context-gen.yml'));
|
|
65
|
+
}
|
|
66
|
+
// 2. context-generator Agent 복사
|
|
67
|
+
const agentSrc = path.join(getTemplatesDir(), 'agents', 'context-generator.md');
|
|
68
|
+
const agentDest = path.join(cwd, '.claude', 'agents', 'context-generator.md');
|
|
69
|
+
fs.mkdirSync(path.dirname(agentDest), { recursive: true });
|
|
70
|
+
if (fs.existsSync(agentDest)) {
|
|
71
|
+
console.log(chalk_1.default.yellow(' ⚠ .claude/agents/context-generator.md already exists, skipping'));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
fs.copyFileSync(agentSrc, agentDest);
|
|
75
|
+
console.log(chalk_1.default.green(' ✓ Created .claude/agents/context-generator.md'));
|
|
76
|
+
}
|
|
77
|
+
// 3. ContextGeneration Skill 복사
|
|
78
|
+
const skillSrc = path.join(getTemplatesDir(), 'skills', 'ContextGeneration', 'SKILL.md');
|
|
79
|
+
const skillDest = path.join(cwd, '.claude', 'skills', 'ContextGeneration', 'SKILL.md');
|
|
80
|
+
fs.mkdirSync(path.dirname(skillDest), { recursive: true });
|
|
81
|
+
if (fs.existsSync(skillDest)) {
|
|
82
|
+
console.log(chalk_1.default.yellow(' ⚠ .claude/skills/ContextGeneration/SKILL.md already exists, skipping'));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
fs.copyFileSync(skillSrc, skillDest);
|
|
86
|
+
console.log(chalk_1.default.green(' ✓ Created .claude/skills/ContextGeneration/SKILL.md'));
|
|
87
|
+
}
|
|
88
|
+
// 4. .claude/context/codebase/ 디렉토리 + stub INDEX.md
|
|
89
|
+
const codebaseDirPath = path.join(cwd, '.claude', 'context', 'codebase');
|
|
90
|
+
const codebaseIndex = path.join(codebaseDirPath, 'INDEX.md');
|
|
91
|
+
fs.mkdirSync(codebaseDirPath, { recursive: true });
|
|
92
|
+
if (fs.existsSync(codebaseIndex)) {
|
|
93
|
+
console.log(chalk_1.default.yellow(' ⚠ .claude/context/codebase/INDEX.md already exists, skipping'));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
fs.writeFileSync(codebaseIndex, `---
|
|
97
|
+
name: Codebase Index
|
|
98
|
+
description: 코드베이스 모듈 참조 목록
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
# Codebase Context Index
|
|
102
|
+
|
|
103
|
+
## 모듈 목록
|
|
104
|
+
|
|
105
|
+
| 모듈 | 설명 | 문서 |
|
|
106
|
+
|------|------|------|
|
|
107
|
+
`);
|
|
108
|
+
console.log(chalk_1.default.green(' ✓ Created .claude/context/codebase/INDEX.md'));
|
|
109
|
+
}
|
|
110
|
+
// 5. .claude/context/business/ 디렉토리 + stub INDEX.md
|
|
111
|
+
const businessDirPath = path.join(cwd, '.claude', 'context', 'business');
|
|
112
|
+
const businessIndex = path.join(businessDirPath, 'INDEX.md');
|
|
113
|
+
fs.mkdirSync(businessDirPath, { recursive: true });
|
|
114
|
+
if (fs.existsSync(businessIndex)) {
|
|
115
|
+
console.log(chalk_1.default.yellow(' ⚠ .claude/context/business/INDEX.md already exists, skipping'));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
fs.writeFileSync(businessIndex, `---
|
|
119
|
+
name: Business Index
|
|
120
|
+
description: 비즈니스 도메인 참조 목록
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Business Context Index
|
|
124
|
+
|
|
125
|
+
## 프로젝트 개요
|
|
126
|
+
|
|
127
|
+
<!-- 프로젝트가 해결하는 문제와 대상 사용자를 작성하세요 -->
|
|
128
|
+
|
|
129
|
+
## 도메인 목록
|
|
130
|
+
|
|
131
|
+
| 도메인 | 설명 | 문서 |
|
|
132
|
+
|--------|------|------|
|
|
133
|
+
`);
|
|
134
|
+
console.log(chalk_1.default.green(' ✓ Created .claude/context/business/INDEX.md'));
|
|
135
|
+
}
|
|
136
|
+
// 6. 안내 메시지
|
|
137
|
+
console.log(chalk_1.default.bold('\n✅ Context auto-generation setup complete!\n'));
|
|
138
|
+
console.log(chalk_1.default.cyan('Next steps:'));
|
|
139
|
+
console.log(chalk_1.default.cyan(' 1. Add CLAUDE_CODE_OAUTH_TOKEN to your repository secrets'));
|
|
140
|
+
console.log(chalk_1.default.cyan(' → Settings > Secrets and variables > Actions > New repository secret'));
|
|
141
|
+
console.log(chalk_1.default.cyan(' 2. Create a PR to trigger context auto-generation'));
|
|
142
|
+
console.log('');
|
|
143
|
+
}
|
package/dist/init-project.js
CHANGED
|
@@ -57,10 +57,10 @@ function askQuestion(question) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* Get the source
|
|
60
|
+
* Get the source templates/project directory path (from package installation)
|
|
61
61
|
*/
|
|
62
|
-
function
|
|
63
|
-
return path.resolve(__dirname, '..', '
|
|
62
|
+
function getSourceProjectDir() {
|
|
63
|
+
return path.resolve(__dirname, '..', 'templates', 'project');
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* Get the project .claude directory path (current working directory)
|
|
@@ -101,7 +101,7 @@ function createProjectEnv(destDir, config) {
|
|
|
101
101
|
* Copy a project-specific file from package source to project directory
|
|
102
102
|
*/
|
|
103
103
|
function copyProjectFile(srcRelative, destDir) {
|
|
104
|
-
const srcPath = path.join(
|
|
104
|
+
const srcPath = path.join(getSourceProjectDir(), srcRelative);
|
|
105
105
|
const destPath = path.join(destDir, srcRelative);
|
|
106
106
|
if (!fs.existsSync(srcPath)) {
|
|
107
107
|
console.log(chalk_1.default.yellow(` ⚠ 소스 파일 없음: ${srcRelative}`));
|