@sleighmaster/bmad 1.1.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/bmad.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +73 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/install.d.ts +8 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +64 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/update.d.ts +6 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +111 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/services/config-manager.d.ts +44 -0
- package/dist/services/config-manager.d.ts.map +1 -0
- package/dist/services/config-manager.js +113 -0
- package/dist/services/config-manager.js.map +1 -0
- package/dist/services/github.d.ts +25 -0
- package/dist/services/github.d.ts.map +1 -0
- package/dist/services/github.js +119 -0
- package/dist/services/github.js.map +1 -0
- package/dist/services/installer.d.ts +16 -0
- package/dist/services/installer.d.ts.map +1 -0
- package/dist/services/installer.js +165 -0
- package/dist/services/installer.js.map +1 -0
- package/dist/services/version-checker.d.ts +44 -0
- package/dist/services/version-checker.d.ts.map +1 -0
- package/dist/services/version-checker.js +116 -0
- package/dist/services/version-checker.js.map +1 -0
- package/dist/utils/fs-utils.d.ts +47 -0
- package/dist/utils/fs-utils.d.ts.map +1 -0
- package/dist/utils/fs-utils.js +98 -0
- package/dist/utils/fs-utils.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +38 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import got from 'got';
|
|
2
|
+
import decompress from 'decompress';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { createTempDir } from '../utils/fs-utils.js';
|
|
6
|
+
const REPO_OWNER = 'sleighmaster99';
|
|
7
|
+
const REPO_NAME = 'BMad6GitHub';
|
|
8
|
+
const API_BASE = 'https://api.github.com';
|
|
9
|
+
/**
|
|
10
|
+
* 최신 릴리즈 정보 가져오기
|
|
11
|
+
*/
|
|
12
|
+
export async function getLatestRelease() {
|
|
13
|
+
const url = `${API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;
|
|
14
|
+
try {
|
|
15
|
+
const response = await got(url, {
|
|
16
|
+
headers: {
|
|
17
|
+
Accept: 'application/vnd.github.v3+json',
|
|
18
|
+
'User-Agent': 'bmad-cli',
|
|
19
|
+
},
|
|
20
|
+
responseType: 'json',
|
|
21
|
+
});
|
|
22
|
+
const release = response.body;
|
|
23
|
+
return {
|
|
24
|
+
tagName: release.tag_name,
|
|
25
|
+
name: release.name,
|
|
26
|
+
publishedAt: release.published_at,
|
|
27
|
+
body: release.body,
|
|
28
|
+
zipballUrl: release.zipball_url,
|
|
29
|
+
htmlUrl: release.html_url,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof Error && 'response' in error) {
|
|
34
|
+
const httpError = error;
|
|
35
|
+
if (httpError.response?.statusCode === 404) {
|
|
36
|
+
throw new Error('No releases found. The repository may not have any releases yet.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`Failed to fetch latest release: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 특정 버전의 릴리즈 정보 가져오기
|
|
44
|
+
*/
|
|
45
|
+
export async function getReleaseByTag(tag) {
|
|
46
|
+
// v 접두사 확인 및 추가
|
|
47
|
+
const normalizedTag = tag.startsWith('v') ? tag : `v${tag}`;
|
|
48
|
+
const url = `${API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${normalizedTag}`;
|
|
49
|
+
try {
|
|
50
|
+
const response = await got(url, {
|
|
51
|
+
headers: {
|
|
52
|
+
Accept: 'application/vnd.github.v3+json',
|
|
53
|
+
'User-Agent': 'bmad-cli',
|
|
54
|
+
},
|
|
55
|
+
responseType: 'json',
|
|
56
|
+
});
|
|
57
|
+
const release = response.body;
|
|
58
|
+
return {
|
|
59
|
+
tagName: release.tag_name,
|
|
60
|
+
name: release.name,
|
|
61
|
+
publishedAt: release.published_at,
|
|
62
|
+
body: release.body,
|
|
63
|
+
zipballUrl: release.zipball_url,
|
|
64
|
+
htmlUrl: release.html_url,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof Error && 'response' in error) {
|
|
69
|
+
const httpError = error;
|
|
70
|
+
if (httpError.response?.statusCode === 404) {
|
|
71
|
+
throw new Error(`Release ${normalizedTag} not found.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Failed to fetch release ${normalizedTag}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 릴리즈 다운로드 및 압축 해제
|
|
79
|
+
*/
|
|
80
|
+
export async function downloadRelease(release) {
|
|
81
|
+
const tempDir = await createTempDir('bmad-download-');
|
|
82
|
+
const zipPath = path.join(tempDir, 'release.zip');
|
|
83
|
+
const extractDir = path.join(tempDir, 'extracted');
|
|
84
|
+
try {
|
|
85
|
+
// zipball 다운로드
|
|
86
|
+
const response = await got(release.zipballUrl, {
|
|
87
|
+
headers: {
|
|
88
|
+
Accept: 'application/vnd.github.v3+json',
|
|
89
|
+
'User-Agent': 'bmad-cli',
|
|
90
|
+
},
|
|
91
|
+
followRedirect: true,
|
|
92
|
+
});
|
|
93
|
+
await fs.writeFile(zipPath, response.rawBody);
|
|
94
|
+
// 압축 해제
|
|
95
|
+
await decompress(zipPath, extractDir);
|
|
96
|
+
// GitHub zipball은 {owner}-{repo}-{sha} 형태의 폴더로 압축 해제됨
|
|
97
|
+
const extractedFolders = await fs.readdir(extractDir);
|
|
98
|
+
if (extractedFolders.length === 0) {
|
|
99
|
+
throw new Error('Downloaded archive is empty');
|
|
100
|
+
}
|
|
101
|
+
// 첫 번째 폴더가 실제 콘텐츠
|
|
102
|
+
const contentDir = path.join(extractDir, extractedFolders[0]);
|
|
103
|
+
return contentDir;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
// 정리
|
|
107
|
+
await fs.remove(tempDir).catch(() => { });
|
|
108
|
+
throw new Error(`Failed to download release: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 임시 다운로드 디렉토리 정리
|
|
113
|
+
*/
|
|
114
|
+
export async function cleanupDownload(downloadDir) {
|
|
115
|
+
// downloadDir의 상위 2단계 (temp 폴더)를 삭제
|
|
116
|
+
const tempDir = path.dirname(path.dirname(downloadDir));
|
|
117
|
+
await fs.remove(tempDir).catch(() => { });
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/services/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ReleaseInfo } from './github.js';
|
|
2
|
+
export interface InstallResult {
|
|
3
|
+
success: boolean;
|
|
4
|
+
version: string;
|
|
5
|
+
backupPath?: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* BMad6GitHub 설치 수행
|
|
10
|
+
*/
|
|
11
|
+
export declare function performInstall(projectRoot: string, release: ReleaseInfo, options?: {
|
|
12
|
+
force?: boolean;
|
|
13
|
+
skipGithub?: boolean;
|
|
14
|
+
skipHooks?: boolean;
|
|
15
|
+
}): Promise<InstallResult>;
|
|
16
|
+
//# sourceMappingURL=installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/services/installer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoC,WAAW,EAAE,MAAM,aAAa,CAAC;AAK5E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CAChB,GACL,OAAO,CAAC,aAAa,CAAC,CA4FxB"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { downloadRelease, cleanupDownload } from './github.js';
|
|
5
|
+
import { initializeConfig } from './config-manager.js';
|
|
6
|
+
import { writeText, createBackupDir } from '../utils/fs-utils.js';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* BMad6GitHub 설치 수행
|
|
10
|
+
*/
|
|
11
|
+
export async function performInstall(projectRoot, release, options = {}) {
|
|
12
|
+
const bmadPath = path.join(projectRoot, '_bmad');
|
|
13
|
+
let downloadDir = null;
|
|
14
|
+
let backupPath;
|
|
15
|
+
try {
|
|
16
|
+
// 1. 기존 _bmad 확인
|
|
17
|
+
if (await fs.pathExists(bmadPath)) {
|
|
18
|
+
if (!options.force) {
|
|
19
|
+
throw new Error('_bmad folder already exists. Use --force to overwrite.');
|
|
20
|
+
}
|
|
21
|
+
// 백업 생성
|
|
22
|
+
logger.step(1, 7, 'Backing up existing _bmad folder...');
|
|
23
|
+
backupPath = await createBackupDir(projectRoot, '_bmad');
|
|
24
|
+
await fs.copy(bmadPath, path.join(backupPath, '_bmad'));
|
|
25
|
+
await fs.remove(bmadPath);
|
|
26
|
+
}
|
|
27
|
+
// 2. 릴리즈 다운로드
|
|
28
|
+
logger.step(2, 7, 'Downloading release...');
|
|
29
|
+
downloadDir = await downloadRelease(release);
|
|
30
|
+
// 3. _bmad 폴더 복사
|
|
31
|
+
logger.step(3, 7, 'Installing _bmad folder...');
|
|
32
|
+
const srcBmad = path.join(downloadDir, '_bmad');
|
|
33
|
+
if (!(await fs.pathExists(srcBmad))) {
|
|
34
|
+
throw new Error('Downloaded release does not contain _bmad folder');
|
|
35
|
+
}
|
|
36
|
+
await fs.copy(srcBmad, bmadPath);
|
|
37
|
+
// 4. .claude/commands 복사
|
|
38
|
+
logger.step(4, 7, 'Installing Claude commands...');
|
|
39
|
+
const srcClaude = path.join(downloadDir, '.claude', 'commands');
|
|
40
|
+
const destClaude = path.join(projectRoot, '.claude', 'commands');
|
|
41
|
+
if (await fs.pathExists(srcClaude)) {
|
|
42
|
+
await fs.ensureDir(path.dirname(destClaude));
|
|
43
|
+
await fs.copy(srcClaude, destClaude, { overwrite: true });
|
|
44
|
+
}
|
|
45
|
+
// 5. .github 템플릿 복사 (선택)
|
|
46
|
+
if (!options.skipGithub) {
|
|
47
|
+
logger.step(5, 7, 'Installing GitHub templates...');
|
|
48
|
+
const srcGithub = path.join(downloadDir, '_bmad', 'bmm', 'data', 'github-templates', '.github');
|
|
49
|
+
const destGithub = path.join(projectRoot, '.github');
|
|
50
|
+
if (await fs.pathExists(srcGithub)) {
|
|
51
|
+
await fs.copy(srcGithub, destGithub, { overwrite: false });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
logger.step(5, 7, 'Skipping GitHub templates...');
|
|
56
|
+
}
|
|
57
|
+
// 6. Git hooks 설치 (선택)
|
|
58
|
+
if (!options.skipHooks) {
|
|
59
|
+
logger.step(6, 7, 'Installing Git hooks...');
|
|
60
|
+
await installGitHooks(projectRoot, downloadDir);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
logger.step(6, 7, 'Skipping Git hooks...');
|
|
64
|
+
}
|
|
65
|
+
// 7. config 초기화 및 버전 파일 생성
|
|
66
|
+
logger.step(7, 7, 'Initializing configuration...');
|
|
67
|
+
const projectName = path.basename(projectRoot);
|
|
68
|
+
await initializeConfig(projectRoot, projectName);
|
|
69
|
+
await writeText(path.join(bmadPath, '.version'), release.tagName);
|
|
70
|
+
// .gitignore 업데이트
|
|
71
|
+
await updateGitignore(projectRoot);
|
|
72
|
+
// 정리
|
|
73
|
+
if (downloadDir) {
|
|
74
|
+
await cleanupDownload(downloadDir);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
version: release.tagName,
|
|
79
|
+
backupPath,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
// 정리
|
|
84
|
+
if (downloadDir) {
|
|
85
|
+
await cleanupDownload(downloadDir).catch(() => { });
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
version: release.tagName,
|
|
90
|
+
backupPath,
|
|
91
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Git hooks 설치
|
|
97
|
+
*/
|
|
98
|
+
async function installGitHooks(projectRoot, downloadDir) {
|
|
99
|
+
const gitDir = path.join(projectRoot, '.git');
|
|
100
|
+
if (!(await fs.pathExists(gitDir))) {
|
|
101
|
+
logger.warning('No .git directory found. Skipping Git hooks installation.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const hooksDir = path.join(gitDir, 'hooks');
|
|
105
|
+
await fs.ensureDir(hooksDir);
|
|
106
|
+
// pre-commit hook
|
|
107
|
+
const srcPreCommit = path.join(downloadDir, '_bmad', 'bmm', 'data', 'git-hooks', 'pre-commit');
|
|
108
|
+
if (await fs.pathExists(srcPreCommit)) {
|
|
109
|
+
const destPreCommit = path.join(hooksDir, 'pre-commit');
|
|
110
|
+
await fs.copy(srcPreCommit, destPreCommit);
|
|
111
|
+
// 실행 권한 부여 (Unix)
|
|
112
|
+
if (process.platform !== 'win32') {
|
|
113
|
+
try {
|
|
114
|
+
execSync(`chmod +x "${destPreCommit}"`);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// 권한 설정 실패 무시
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// commit-msg hook
|
|
122
|
+
const srcCommitMsg = path.join(downloadDir, '_bmad', 'bmm', 'data', 'git-hooks', 'commit-msg');
|
|
123
|
+
if (await fs.pathExists(srcCommitMsg)) {
|
|
124
|
+
const destCommitMsg = path.join(hooksDir, 'commit-msg');
|
|
125
|
+
await fs.copy(srcCommitMsg, destCommitMsg);
|
|
126
|
+
if (process.platform !== 'win32') {
|
|
127
|
+
try {
|
|
128
|
+
execSync(`chmod +x "${destCommitMsg}"`);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// 권한 설정 실패 무시
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* .gitignore 업데이트
|
|
138
|
+
*/
|
|
139
|
+
async function updateGitignore(projectRoot) {
|
|
140
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
141
|
+
const entriesToAdd = [
|
|
142
|
+
'# Claude Code local settings',
|
|
143
|
+
'.claude/settings.local.json',
|
|
144
|
+
'.claude/todos.local.json',
|
|
145
|
+
'',
|
|
146
|
+
];
|
|
147
|
+
let content = '';
|
|
148
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
149
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
150
|
+
}
|
|
151
|
+
// 이미 추가되어 있는지 확인
|
|
152
|
+
if (content.includes('.claude/settings.local.json')) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// 줄바꿈 추가
|
|
156
|
+
if (content && !content.endsWith('\n')) {
|
|
157
|
+
content += '\n';
|
|
158
|
+
}
|
|
159
|
+
if (content && !content.endsWith('\n\n')) {
|
|
160
|
+
content += '\n';
|
|
161
|
+
}
|
|
162
|
+
content += entriesToAdd.join('\n');
|
|
163
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/services/installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ReleaseInfo } from './github.js';
|
|
2
|
+
export interface VersionCheckCache {
|
|
3
|
+
lastChecked: string;
|
|
4
|
+
currentVersion: string;
|
|
5
|
+
latestVersion: string;
|
|
6
|
+
updateAvailable: boolean;
|
|
7
|
+
releaseUrl?: string;
|
|
8
|
+
releaseNotes?: string;
|
|
9
|
+
cacheTTL: number;
|
|
10
|
+
}
|
|
11
|
+
export interface VersionCheckResult {
|
|
12
|
+
installed: boolean;
|
|
13
|
+
currentVersion: string | null;
|
|
14
|
+
latestVersion: string;
|
|
15
|
+
updateAvailable: boolean;
|
|
16
|
+
releaseInfo?: ReleaseInfo;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 버전 비교
|
|
20
|
+
*/
|
|
21
|
+
export declare function compareVersions(current: string, latest: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* 버전 체크 수행
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkVersion(projectRoot: string): Promise<VersionCheckResult>;
|
|
26
|
+
/**
|
|
27
|
+
* 버전 캐시 읽기
|
|
28
|
+
*/
|
|
29
|
+
export declare function readVersionCache(projectRoot: string): Promise<VersionCheckCache | null>;
|
|
30
|
+
/**
|
|
31
|
+
* 버전 캐시 업데이트
|
|
32
|
+
*/
|
|
33
|
+
export declare function updateVersionCache(projectRoot: string, data: {
|
|
34
|
+
currentVersion: string;
|
|
35
|
+
latestVersion: string;
|
|
36
|
+
updateAvailable: boolean;
|
|
37
|
+
releaseUrl?: string;
|
|
38
|
+
releaseNotes?: string;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* 릴리즈 노트 요약 생성
|
|
42
|
+
*/
|
|
43
|
+
export declare function summarizeReleaseNotes(notes: string, maxLength?: number): string;
|
|
44
|
+
//# sourceMappingURL=version-checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-checker.d.ts","sourceRoot":"","sources":["../../src/services/version-checker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAKD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAWxE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2CnF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAkB7F;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IACJ,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,MAAY,GAAG,MAAM,CAiBpF"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { getCurrentVersion, readJson, writeJson } from '../utils/fs-utils.js';
|
|
4
|
+
import { getLatestRelease } from './github.js';
|
|
5
|
+
const CACHE_TTL = 24 * 60 * 60; // 24시간
|
|
6
|
+
const CACHE_FILE = '.version-check.json';
|
|
7
|
+
/**
|
|
8
|
+
* 버전 비교
|
|
9
|
+
*/
|
|
10
|
+
export function compareVersions(current, latest) {
|
|
11
|
+
// v 접두사 제거
|
|
12
|
+
const cleanCurrent = current.replace(/^v/, '');
|
|
13
|
+
const cleanLatest = latest.replace(/^v/, '');
|
|
14
|
+
try {
|
|
15
|
+
return semver.gt(cleanLatest, cleanCurrent);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// semver 파싱 실패 시 문자열 비교
|
|
19
|
+
return cleanCurrent !== cleanLatest;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 버전 체크 수행
|
|
24
|
+
*/
|
|
25
|
+
export async function checkVersion(projectRoot) {
|
|
26
|
+
const currentVersion = await getCurrentVersion(projectRoot);
|
|
27
|
+
const installed = currentVersion !== null;
|
|
28
|
+
try {
|
|
29
|
+
const releaseInfo = await getLatestRelease();
|
|
30
|
+
const latestVersion = releaseInfo.tagName;
|
|
31
|
+
const updateAvailable = installed
|
|
32
|
+
? compareVersions(currentVersion, latestVersion)
|
|
33
|
+
: false;
|
|
34
|
+
// 캐시 업데이트
|
|
35
|
+
await updateVersionCache(projectRoot, {
|
|
36
|
+
currentVersion: currentVersion || 'not installed',
|
|
37
|
+
latestVersion,
|
|
38
|
+
updateAvailable,
|
|
39
|
+
releaseUrl: releaseInfo.htmlUrl,
|
|
40
|
+
releaseNotes: releaseInfo.body,
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
installed,
|
|
44
|
+
currentVersion,
|
|
45
|
+
latestVersion,
|
|
46
|
+
updateAvailable,
|
|
47
|
+
releaseInfo,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
// 네트워크 오류 등 - 캐시된 정보 사용 시도
|
|
52
|
+
const cache = await readVersionCache(projectRoot);
|
|
53
|
+
if (cache) {
|
|
54
|
+
return {
|
|
55
|
+
installed,
|
|
56
|
+
currentVersion,
|
|
57
|
+
latestVersion: cache.latestVersion,
|
|
58
|
+
updateAvailable: cache.updateAvailable,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 버전 캐시 읽기
|
|
66
|
+
*/
|
|
67
|
+
export async function readVersionCache(projectRoot) {
|
|
68
|
+
const cachePath = path.join(projectRoot, '_bmad', CACHE_FILE);
|
|
69
|
+
const cache = await readJson(cachePath);
|
|
70
|
+
if (!cache) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
// 캐시 유효성 확인
|
|
74
|
+
const lastChecked = new Date(cache.lastChecked);
|
|
75
|
+
const now = new Date();
|
|
76
|
+
const ageInSeconds = (now.getTime() - lastChecked.getTime()) / 1000;
|
|
77
|
+
if (ageInSeconds > cache.cacheTTL) {
|
|
78
|
+
return null; // 캐시 만료
|
|
79
|
+
}
|
|
80
|
+
return cache;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 버전 캐시 업데이트
|
|
84
|
+
*/
|
|
85
|
+
export async function updateVersionCache(projectRoot, data) {
|
|
86
|
+
const cachePath = path.join(projectRoot, '_bmad', CACHE_FILE);
|
|
87
|
+
const cache = {
|
|
88
|
+
lastChecked: new Date().toISOString(),
|
|
89
|
+
currentVersion: data.currentVersion,
|
|
90
|
+
latestVersion: data.latestVersion,
|
|
91
|
+
updateAvailable: data.updateAvailable,
|
|
92
|
+
releaseUrl: data.releaseUrl,
|
|
93
|
+
releaseNotes: data.releaseNotes,
|
|
94
|
+
cacheTTL: CACHE_TTL,
|
|
95
|
+
};
|
|
96
|
+
await writeJson(cachePath, cache);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 릴리즈 노트 요약 생성
|
|
100
|
+
*/
|
|
101
|
+
export function summarizeReleaseNotes(notes, maxLength = 200) {
|
|
102
|
+
if (!notes) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
// 마크다운 헤더 제거
|
|
106
|
+
const cleaned = notes
|
|
107
|
+
.split('\n')
|
|
108
|
+
.filter(line => !line.startsWith('#'))
|
|
109
|
+
.join('\n')
|
|
110
|
+
.trim();
|
|
111
|
+
if (cleaned.length <= maxLength) {
|
|
112
|
+
return cleaned;
|
|
113
|
+
}
|
|
114
|
+
return cleaned.substring(0, maxLength - 3) + '...';
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=version-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-checker.js","sourceRoot":"","sources":["../../src/services/version-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프로젝트 루트인지 확인 (package.json 또는 .git 존재)
|
|
3
|
+
*/
|
|
4
|
+
export declare function isProjectRoot(dir: string): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* _bmad 폴더가 설치되어 있는지 확인
|
|
7
|
+
*/
|
|
8
|
+
export declare function isBmadInstalled(dir: string): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* 현재 설치된 버전 읽기
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCurrentVersion(dir: string): Promise<string | null>;
|
|
13
|
+
/**
|
|
14
|
+
* 임시 디렉토리 생성
|
|
15
|
+
*/
|
|
16
|
+
export declare function createTempDir(prefix?: string): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* 백업 디렉토리 생성
|
|
19
|
+
*/
|
|
20
|
+
export declare function createBackupDir(dir: string, name: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* 디렉토리 복사 (덮어쓰기 옵션)
|
|
23
|
+
*/
|
|
24
|
+
export declare function copyDirectory(src: string, dest: string, options?: {
|
|
25
|
+
overwrite?: boolean;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* 파일 존재 여부 확인
|
|
29
|
+
*/
|
|
30
|
+
export declare function fileExists(filePath: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* JSON 파일 읽기
|
|
33
|
+
*/
|
|
34
|
+
export declare function readJson<T>(filePath: string): Promise<T | null>;
|
|
35
|
+
/**
|
|
36
|
+
* JSON 파일 쓰기
|
|
37
|
+
*/
|
|
38
|
+
export declare function writeJson(filePath: string, data: unknown): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* 텍스트 파일 읽기
|
|
41
|
+
*/
|
|
42
|
+
export declare function readText(filePath: string): Promise<string | null>;
|
|
43
|
+
/**
|
|
44
|
+
* 텍스트 파일 쓰기
|
|
45
|
+
*/
|
|
46
|
+
export declare function writeText(filePath: string, content: string): Promise<void>;
|
|
47
|
+
//# sourceMappingURL=fs-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-utils.d.ts","sourceRoot":"","sources":["../../src/utils/fs-utils.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIjE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnE;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ3E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,GAAE,MAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAK7E;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKhF;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAMrE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9E;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMvE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
/**
|
|
5
|
+
* 프로젝트 루트인지 확인 (package.json 또는 .git 존재)
|
|
6
|
+
*/
|
|
7
|
+
export async function isProjectRoot(dir) {
|
|
8
|
+
const hasPackageJson = await fs.pathExists(path.join(dir, 'package.json'));
|
|
9
|
+
const hasGit = await fs.pathExists(path.join(dir, '.git'));
|
|
10
|
+
return hasPackageJson || hasGit;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* _bmad 폴더가 설치되어 있는지 확인
|
|
14
|
+
*/
|
|
15
|
+
export async function isBmadInstalled(dir) {
|
|
16
|
+
return fs.pathExists(path.join(dir, '_bmad'));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 현재 설치된 버전 읽기
|
|
20
|
+
*/
|
|
21
|
+
export async function getCurrentVersion(dir) {
|
|
22
|
+
const versionFile = path.join(dir, '_bmad', '.version');
|
|
23
|
+
try {
|
|
24
|
+
const content = await fs.readFile(versionFile, 'utf-8');
|
|
25
|
+
return content.trim();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 임시 디렉토리 생성
|
|
33
|
+
*/
|
|
34
|
+
export async function createTempDir(prefix = 'bmad-') {
|
|
35
|
+
const tempBase = os.tmpdir();
|
|
36
|
+
const tempDir = path.join(tempBase, `${prefix}${Date.now()}`);
|
|
37
|
+
await fs.ensureDir(tempDir);
|
|
38
|
+
return tempDir;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 백업 디렉토리 생성
|
|
42
|
+
*/
|
|
43
|
+
export async function createBackupDir(dir, name) {
|
|
44
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
45
|
+
const backupDir = path.join(dir, `.backup.${name}.${timestamp}`);
|
|
46
|
+
await fs.ensureDir(backupDir);
|
|
47
|
+
return backupDir;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 디렉토리 복사 (덮어쓰기 옵션)
|
|
51
|
+
*/
|
|
52
|
+
export async function copyDirectory(src, dest, options = {}) {
|
|
53
|
+
await fs.copy(src, dest, {
|
|
54
|
+
overwrite: options.overwrite ?? false,
|
|
55
|
+
errorOnExist: !options.overwrite,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 파일 존재 여부 확인
|
|
60
|
+
*/
|
|
61
|
+
export async function fileExists(filePath) {
|
|
62
|
+
return fs.pathExists(filePath);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* JSON 파일 읽기
|
|
66
|
+
*/
|
|
67
|
+
export async function readJson(filePath) {
|
|
68
|
+
try {
|
|
69
|
+
return await fs.readJson(filePath);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* JSON 파일 쓰기
|
|
77
|
+
*/
|
|
78
|
+
export async function writeJson(filePath, data) {
|
|
79
|
+
await fs.writeJson(filePath, data, { spaces: 2 });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 텍스트 파일 읽기
|
|
83
|
+
*/
|
|
84
|
+
export async function readText(filePath) {
|
|
85
|
+
try {
|
|
86
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 텍스트 파일 쓰기
|
|
94
|
+
*/
|
|
95
|
+
export async function writeText(filePath, content) {
|
|
96
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=fs-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-utils.js","sourceRoot":"","sources":["../../src/utils/fs-utils.ts"],"names":[],"mappings":"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"}
|