bmad-setup 1.7.2 → 1.8.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/bin/cli.js +149 -32
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
-
const { execSync } = require('child_process');
|
|
5
|
+
const { execSync, execFileSync } = require('child_process');
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
@@ -32,6 +32,14 @@ function runSafe(cmd, stepName) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function runFile(file, args, stepName) {
|
|
36
|
+
try {
|
|
37
|
+
execFileSync(file, args, { stdio: 'inherit' });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
throw new Error(`${stepName} 실패: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
// --- --help / --version ---
|
|
36
44
|
function handleFlags() {
|
|
37
45
|
if (args.includes('--version') || args.includes('-v')) {
|
|
@@ -45,8 +53,8 @@ bmad-setup v${VERSION}
|
|
|
45
53
|
BMAD Framework 서브모듈을 한 줄로 설치합니다.
|
|
46
54
|
|
|
47
55
|
Usage:
|
|
48
|
-
npx bmad-setup 전체 설치 실행
|
|
49
|
-
npx bmad-setup --update 서브모듈 최신화 + 심링크 재생성
|
|
56
|
+
npx bmad-setup 전체 설치 실행 (worktree 자동 감지)
|
|
57
|
+
npx bmad-setup --update 서브모듈 최신화 + 심링크 재생성 + 부모 참조 갱신
|
|
50
58
|
npx bmad-setup --help 도움말 표시
|
|
51
59
|
npx bmad-setup --version 버전 표시
|
|
52
60
|
|
|
@@ -55,13 +63,21 @@ Install steps:
|
|
|
55
63
|
2. git submodule init & update
|
|
56
64
|
3. .gitmodules ignore=dirty 설정
|
|
57
65
|
4. install.sh 실행 (심볼릭 링크)
|
|
58
|
-
5.
|
|
59
|
-
6.
|
|
66
|
+
5. post-checkout hook 설치 (worktree 자동 지원)
|
|
67
|
+
6. .gitignore 패치
|
|
68
|
+
7. package.json 스크립트 패치
|
|
69
|
+
|
|
70
|
+
Worktree 감지 시 (자동):
|
|
71
|
+
1. git submodule init & update
|
|
72
|
+
2. install.sh 실행 (심볼릭 링크)
|
|
60
73
|
|
|
61
74
|
Update steps (--update):
|
|
62
|
-
1. git submodule
|
|
63
|
-
2.
|
|
75
|
+
1. git -C bmad-submodule fetch + checkout origin/master
|
|
76
|
+
2. 부모 repo submodule 참조 갱신 (git add)
|
|
64
77
|
3. install.sh 재실행 (심링크 갱신)
|
|
78
|
+
|
|
79
|
+
Requirements:
|
|
80
|
+
- git 2.13+
|
|
65
81
|
`);
|
|
66
82
|
process.exit(0);
|
|
67
83
|
}
|
|
@@ -87,40 +103,72 @@ function validateGitRepo() {
|
|
|
87
103
|
}
|
|
88
104
|
}
|
|
89
105
|
|
|
106
|
+
// --- Task 1: git version check ---
|
|
107
|
+
function validateGitVersion() {
|
|
108
|
+
try {
|
|
109
|
+
const versionOutput = runCapture('git --version');
|
|
110
|
+
const match = versionOutput.match(/(\d+)\.(\d+)/);
|
|
111
|
+
if (!match) {
|
|
112
|
+
log('\u26a0', `Warning: git 버전을 파싱할 수 없습니다. (출력: ${versionOutput})`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const major = parseInt(match[1], 10);
|
|
116
|
+
const minor = parseInt(match[2], 10);
|
|
117
|
+
if (major < 2 || (major === 2 && minor < 13)) {
|
|
118
|
+
log('\u274c', `Error: git 2.13 이상이 필요합니다. (현재: ${versionOutput})`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
log('\u274c', 'Error: git 버전을 확인할 수 없습니다.');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- Task 2: worktree detection ---
|
|
128
|
+
function isWorktree() {
|
|
129
|
+
try {
|
|
130
|
+
return fs.existsSync('.git') && fs.statSync('.git').isFile();
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
90
136
|
// --- Update mode ---
|
|
91
|
-
function
|
|
92
|
-
logStep(1, 3, 'Submodule 동기화');
|
|
137
|
+
function pullLatest() {
|
|
93
138
|
if (!fs.existsSync(SUBMODULE_DIR)) {
|
|
94
139
|
log(' \u274c', `${SUBMODULE_DIR}/ 디렉토리가 없습니다. 먼저 \`npx bmad-setup\`으로 설치하세요.`);
|
|
95
140
|
process.exit(1);
|
|
96
141
|
}
|
|
97
|
-
runSafe(
|
|
98
|
-
|
|
142
|
+
runSafe(`git -C ${SUBMODULE_DIR} fetch origin master`, 'Submodule fetch');
|
|
143
|
+
runSafe(`git -C ${SUBMODULE_DIR} checkout origin/master`, 'Submodule checkout');
|
|
144
|
+
log(' \u2714', '최신 버전으로 업데이트 완료');
|
|
99
145
|
return 'done';
|
|
100
146
|
}
|
|
101
147
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
148
|
+
// --- Task 4: update parent ref ---
|
|
149
|
+
function updateParentRef() {
|
|
150
|
+
if (isWorktree()) {
|
|
151
|
+
log(' \u2139', 'Worktree 환경에서는 부모 참조 갱신을 건너뜁니다. 메인 worktree에서 --update를 실행하세요.');
|
|
152
|
+
return 'skipped';
|
|
153
|
+
}
|
|
154
|
+
runSafe(`git add ${SUBMODULE_DIR}`, '부모 repo 참조 갱신');
|
|
155
|
+
log(' \u2714', '부모 repo의 submodule 참조가 staged 되었습니다. 필요 시 commit 하세요.');
|
|
106
156
|
return 'done';
|
|
107
157
|
}
|
|
108
158
|
|
|
109
159
|
function reinstallSymlinks() {
|
|
110
|
-
logStep(3, 3, 'install.sh 재실행 (심링크 갱신)');
|
|
111
160
|
const scriptPath = `./${SUBMODULE_DIR}/install.sh`;
|
|
112
161
|
if (!fs.existsSync(scriptPath)) {
|
|
113
162
|
log(' \u26a0', `${scriptPath} 파일을 찾을 수 없습니다. 스킵합니다.`);
|
|
114
163
|
return 'skipped';
|
|
115
164
|
}
|
|
116
|
-
|
|
165
|
+
runFile('bash', [scriptPath, process.cwd()], 'install.sh 실행');
|
|
117
166
|
log(' \u2714', '심링크 갱신 완료');
|
|
118
167
|
return 'done';
|
|
119
168
|
}
|
|
120
169
|
|
|
121
170
|
// --- Step 1: Submodule 추가 ---
|
|
122
171
|
function addSubmodule() {
|
|
123
|
-
logStep(1, 6, 'Submodule 추가');
|
|
124
172
|
if (fs.existsSync(SUBMODULE_DIR)) {
|
|
125
173
|
log(' \u2714', `${SUBMODULE_DIR}/ 이미 존재합니다. 스킵합니다.`);
|
|
126
174
|
return 'skipped';
|
|
@@ -132,7 +180,6 @@ function addSubmodule() {
|
|
|
132
180
|
|
|
133
181
|
// --- Step 2: Submodule 초기화 ---
|
|
134
182
|
function initSubmodule() {
|
|
135
|
-
logStep(2, 6, 'Submodule 초기화');
|
|
136
183
|
runSafe('git submodule init && git submodule update', 'Submodule 초기화');
|
|
137
184
|
log(' \u2714', '초기화 완료');
|
|
138
185
|
return 'done';
|
|
@@ -140,7 +187,6 @@ function initSubmodule() {
|
|
|
140
187
|
|
|
141
188
|
// --- Step 3: dirty ignore 설정 ---
|
|
142
189
|
function configureDirtyIgnore() {
|
|
143
|
-
logStep(3, 6, 'dirty ignore 설정');
|
|
144
190
|
runSafe(
|
|
145
191
|
`git config -f .gitmodules submodule.${SUBMODULE_DIR}.ignore dirty`,
|
|
146
192
|
'dirty ignore 설정',
|
|
@@ -151,21 +197,74 @@ function configureDirtyIgnore() {
|
|
|
151
197
|
|
|
152
198
|
// --- Step 4: install.sh 실행 ---
|
|
153
199
|
function runInstallScript() {
|
|
154
|
-
logStep(4, 6, 'install.sh 실행 (심볼릭 링크 생성)');
|
|
155
200
|
const scriptPath = `./${SUBMODULE_DIR}/install.sh`;
|
|
156
201
|
if (!fs.existsSync(scriptPath)) {
|
|
157
202
|
log(' \u26a0', `${scriptPath} 파일을 찾을 수 없습니다. 스킵합니다.`);
|
|
158
203
|
return 'skipped';
|
|
159
204
|
}
|
|
160
|
-
|
|
205
|
+
runFile('bash', [scriptPath, process.cwd()], 'install.sh 실행');
|
|
161
206
|
log(' \u2714', '심볼릭 링크 생성 완료');
|
|
162
207
|
return 'done';
|
|
163
208
|
}
|
|
164
209
|
|
|
210
|
+
// --- Task 8: post-checkout hook 설치 ---
|
|
211
|
+
function installPostCheckoutHook() {
|
|
212
|
+
const MARKER_START = '# BMAD-POST-CHECKOUT-START';
|
|
213
|
+
const MARKER_END = '# BMAD-POST-CHECKOUT-END';
|
|
214
|
+
|
|
215
|
+
let hooksDir;
|
|
216
|
+
try {
|
|
217
|
+
const gitCommonDir = runCapture('git rev-parse --git-common-dir');
|
|
218
|
+
hooksDir = path.join(gitCommonDir, 'hooks');
|
|
219
|
+
} catch (e) {
|
|
220
|
+
log(' \u26a0', 'git hooks 디렉토리를 찾을 수 없습니다. 스킵합니다.');
|
|
221
|
+
return 'skipped';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!fs.existsSync(hooksDir)) {
|
|
225
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const hookPath = path.join(hooksDir, 'post-checkout');
|
|
229
|
+
let content = '';
|
|
230
|
+
|
|
231
|
+
if (fs.existsSync(hookPath)) {
|
|
232
|
+
content = fs.readFileSync(hookPath, 'utf8');
|
|
233
|
+
if (content.includes(MARKER_START)) {
|
|
234
|
+
log(' \u2714', 'post-checkout hook에 BMAD 섹션이 이미 존재합니다. 스킵합니다.');
|
|
235
|
+
return 'skipped';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const bmadSection = `
|
|
240
|
+
${MARKER_START}
|
|
241
|
+
# Auto-generated by bmad-setup. Do not edit this section.
|
|
242
|
+
if [ "$3" = "1" ] && [ -d "${SUBMODULE_DIR}" ]; then
|
|
243
|
+
# Branch checkout (including worktree creation)
|
|
244
|
+
git submodule update --init --recursive 2>/dev/null
|
|
245
|
+
if [ -f "${SUBMODULE_DIR}/install.sh" ]; then
|
|
246
|
+
bash ${SUBMODULE_DIR}/install.sh "$(pwd)" 2>/dev/null
|
|
247
|
+
fi
|
|
248
|
+
fi
|
|
249
|
+
${MARKER_END}
|
|
250
|
+
`;
|
|
251
|
+
|
|
252
|
+
if (content) {
|
|
253
|
+
// Append to existing hook
|
|
254
|
+
fs.writeFileSync(hookPath, content.trimEnd() + '\n' + bmadSection, 'utf8');
|
|
255
|
+
} else {
|
|
256
|
+
// Create new hook
|
|
257
|
+
fs.writeFileSync(hookPath, '#!/bin/bash\n' + bmadSection, 'utf8');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Make executable
|
|
261
|
+
fs.chmodSync(hookPath, 0o755);
|
|
262
|
+
log(' \u2714', 'post-checkout hook 설치 완료');
|
|
263
|
+
return 'done';
|
|
264
|
+
}
|
|
265
|
+
|
|
165
266
|
// --- Step 5: .gitignore 패치 ---
|
|
166
267
|
function patchGitignore() {
|
|
167
|
-
logStep(5, 6, '.gitignore 패치');
|
|
168
|
-
|
|
169
268
|
const MARKER_START = '# BMAD symlinks (auto-generated)';
|
|
170
269
|
const MARKER_END = '# End BMAD';
|
|
171
270
|
const entries = ['_bmad', '.claude/commands/bmad-*'];
|
|
@@ -198,8 +297,6 @@ function patchGitignore() {
|
|
|
198
297
|
|
|
199
298
|
// --- Step 6: package.json 패치 ---
|
|
200
299
|
function patchPackageJson() {
|
|
201
|
-
logStep(6, 6, 'package.json 패치');
|
|
202
|
-
|
|
203
300
|
const pkgPath = 'package.json';
|
|
204
301
|
if (!fs.existsSync(pkgPath)) {
|
|
205
302
|
log(' \u26a0', 'package.json이 없습니다. 이 단계를 스킵합니다.');
|
|
@@ -226,9 +323,9 @@ function patchPackageJson() {
|
|
|
226
323
|
|
|
227
324
|
const scriptsToAdd = {
|
|
228
325
|
postinstall:
|
|
229
|
-
'[ -z "$CI" ] && git submodule
|
|
230
|
-
'bmad:install': './bmad-submodule/install.sh',
|
|
231
|
-
'bmad:uninstall': './bmad-submodule/uninstall.sh',
|
|
326
|
+
'[ -z "$CI" ] && git -C bmad-submodule fetch origin master && git -C bmad-submodule checkout origin/master && git add bmad-submodule && ./bmad-submodule/install.sh "$(pwd)" || true',
|
|
327
|
+
'bmad:install': './bmad-submodule/install.sh "$(pwd)"',
|
|
328
|
+
'bmad:uninstall': './bmad-submodule/uninstall.sh "$(pwd)"',
|
|
232
329
|
};
|
|
233
330
|
|
|
234
331
|
let added = 0;
|
|
@@ -261,6 +358,7 @@ function main() {
|
|
|
261
358
|
handleFlags();
|
|
262
359
|
|
|
263
360
|
validateGitRepo();
|
|
361
|
+
validateGitVersion();
|
|
264
362
|
|
|
265
363
|
if (isUpdate) {
|
|
266
364
|
console.log('');
|
|
@@ -268,14 +366,29 @@ function main() {
|
|
|
268
366
|
console.log('');
|
|
269
367
|
|
|
270
368
|
const steps = [
|
|
271
|
-
{ key: 'sync', label: 'Submodule 동기화', fn: updateSubmodule },
|
|
272
369
|
{ key: 'pull', label: 'Submodule 최신화', fn: pullLatest },
|
|
370
|
+
{ key: 'parentRef', label: '부모 참조 갱신', fn: updateParentRef },
|
|
273
371
|
{ key: 'reinstall', label: '심링크 갱신', fn: reinstallSymlinks },
|
|
274
372
|
];
|
|
275
373
|
|
|
276
374
|
return runSteps(steps, 'BMAD 업데이트가 완료되었습니다!');
|
|
277
375
|
}
|
|
278
376
|
|
|
377
|
+
// Task 7: worktree auto-detect
|
|
378
|
+
if (isWorktree()) {
|
|
379
|
+
console.log('');
|
|
380
|
+
console.log('=== BMAD Worktree Setup ===');
|
|
381
|
+
console.log('\u2139 Git worktree 환경이 감지되었습니다.');
|
|
382
|
+
console.log('');
|
|
383
|
+
|
|
384
|
+
const steps = [
|
|
385
|
+
{ key: 'init', label: 'Submodule 초기화', fn: initSubmodule },
|
|
386
|
+
{ key: 'installSh', label: '심볼릭 링크 생성', fn: runInstallScript },
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
return runSteps(steps, 'Worktree BMAD 설정이 완료되었습니다!');
|
|
390
|
+
}
|
|
391
|
+
|
|
279
392
|
console.log('');
|
|
280
393
|
console.log('=== BMAD Submodule Setup ===');
|
|
281
394
|
console.log('');
|
|
@@ -284,7 +397,8 @@ function main() {
|
|
|
284
397
|
{ key: 'submodule', label: 'Submodule 추가', fn: addSubmodule },
|
|
285
398
|
{ key: 'init', label: 'Submodule 초기화', fn: initSubmodule },
|
|
286
399
|
{ key: 'dirtyIgnore', label: 'dirty ignore 설정', fn: configureDirtyIgnore },
|
|
287
|
-
{ key: 'installSh', label: '
|
|
400
|
+
{ key: 'installSh', label: '심볼릭 링크 생성', fn: runInstallScript },
|
|
401
|
+
{ key: 'hook', label: 'post-checkout hook 설치', fn: installPostCheckoutHook },
|
|
288
402
|
{ key: 'gitignore', label: '.gitignore 패치', fn: patchGitignore },
|
|
289
403
|
{ key: 'packageJson', label: 'package.json 패치', fn: patchPackageJson },
|
|
290
404
|
];
|
|
@@ -292,9 +406,12 @@ function main() {
|
|
|
292
406
|
runSteps(steps, 'BMAD 설치가 완료되었습니다!');
|
|
293
407
|
}
|
|
294
408
|
|
|
409
|
+
// Task 3: dynamic logStep in runSteps
|
|
295
410
|
function runSteps(steps, doneMessage) {
|
|
296
411
|
const results = {};
|
|
297
|
-
for (
|
|
412
|
+
for (let i = 0; i < steps.length; i++) {
|
|
413
|
+
const step = steps[i];
|
|
414
|
+
logStep(i + 1, steps.length, step.label);
|
|
298
415
|
try {
|
|
299
416
|
results[step.key] = step.fn();
|
|
300
417
|
} catch (e) {
|