@blastlabs/utils 1.14.0 → 1.15.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/init-ai-rules.cjs +117 -110
- package/package.json +1 -1
package/bin/init-ai-rules.cjs
CHANGED
|
@@ -3,53 +3,42 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
// 규칙 카테고리별 정의
|
|
7
|
+
const RULE_CATEGORIES = {
|
|
8
|
+
// 기본 규칙 (항상 포함)
|
|
9
|
+
base: [
|
|
10
|
+
"typescript-standards.mdc",
|
|
11
|
+
"react-hooks.mdc",
|
|
12
|
+
"testing.mdc",
|
|
13
|
+
"documentation.mdc",
|
|
14
|
+
"git-commit.mdc",
|
|
15
|
+
],
|
|
16
|
+
// FSD 아키텍처 규칙
|
|
17
|
+
fsd: [
|
|
18
|
+
"fsd-architecture.mdc",
|
|
19
|
+
"entities-layer.mdc",
|
|
20
|
+
"shared-layer.mdc",
|
|
21
|
+
"views-layer.mdc",
|
|
22
|
+
],
|
|
14
23
|
};
|
|
15
24
|
|
|
16
25
|
function parseArgs(args) {
|
|
17
26
|
const options = {
|
|
18
|
-
projectName: null,
|
|
19
|
-
projectDescription: "프로젝트 설명을 작성해주세요.",
|
|
20
|
-
framework: null, // 'nextjs' or 'vite'
|
|
21
27
|
fsd: false,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
packageManager: "pnpm",
|
|
28
|
+
all: false,
|
|
29
|
+
list: false,
|
|
25
30
|
help: false,
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
for (
|
|
29
|
-
const arg = args[i];
|
|
30
|
-
|
|
33
|
+
for (const arg of args) {
|
|
31
34
|
if (arg === "--help" || arg === "-h") {
|
|
32
35
|
options.help = true;
|
|
33
|
-
} else if (arg === "--next" || arg === "--nextjs") {
|
|
34
|
-
options.framework = "nextjs";
|
|
35
|
-
} else if (arg === "--vite") {
|
|
36
|
-
options.framework = "vite";
|
|
37
36
|
} else if (arg === "--fsd") {
|
|
38
37
|
options.fsd = true;
|
|
39
|
-
} else if (arg === "--
|
|
40
|
-
options.
|
|
41
|
-
} else if (arg === "--
|
|
42
|
-
options.
|
|
43
|
-
} else if (arg === "--npm") {
|
|
44
|
-
options.packageManager = "npm";
|
|
45
|
-
} else if (arg === "--yarn") {
|
|
46
|
-
options.packageManager = "yarn";
|
|
47
|
-
} else if (arg === "--pnpm") {
|
|
48
|
-
options.packageManager = "pnpm";
|
|
49
|
-
} else if (!arg.startsWith("--") && !options.projectName) {
|
|
50
|
-
options.projectName = arg;
|
|
51
|
-
} else if (!arg.startsWith("--") && options.projectName && !options.projectDescription) {
|
|
52
|
-
options.projectDescription = arg;
|
|
38
|
+
} else if (arg === "--all") {
|
|
39
|
+
options.all = true;
|
|
40
|
+
} else if (arg === "--list" || arg === "-l") {
|
|
41
|
+
options.list = true;
|
|
53
42
|
}
|
|
54
43
|
}
|
|
55
44
|
|
|
@@ -57,111 +46,129 @@ function parseArgs(args) {
|
|
|
57
46
|
}
|
|
58
47
|
|
|
59
48
|
function showHelp() {
|
|
60
|
-
console.log("@blastlabs/utils -
|
|
61
|
-
console.log("프로젝트에 .
|
|
62
|
-
console.log("사용법: npx blastlabs
|
|
49
|
+
console.log("@blastlabs/utils - Cursor Rules Installer\n");
|
|
50
|
+
console.log("프로젝트에 .cursor/rules/ 규칙 파일들을 설치합니다.\n");
|
|
51
|
+
console.log("사용법: npx @blastlabs/utils init-rules [options]\n");
|
|
63
52
|
console.log("Options:");
|
|
64
|
-
console.log(" --
|
|
65
|
-
console.log(" --
|
|
66
|
-
console.log(" --
|
|
67
|
-
console.log(" --
|
|
68
|
-
console.log(" --monorepo 모노레포 규칙 포함");
|
|
69
|
-
console.log(" --npm npm 사용 (기본값: pnpm)");
|
|
70
|
-
console.log(" --yarn yarn 사용");
|
|
71
|
-
console.log(" --pnpm pnpm 사용 (기본값)");
|
|
53
|
+
console.log(" --fsd FSD 아키텍처 규칙 포함");
|
|
54
|
+
console.log(" --all 모든 규칙 설치 (base + fsd)");
|
|
55
|
+
console.log(" --list, -l 설치 가능한 규칙 목록 보기");
|
|
56
|
+
console.log(" --help, -h 도움말 보기");
|
|
72
57
|
console.log("\n예시:");
|
|
73
|
-
console.log(
|
|
74
|
-
console.log(
|
|
75
|
-
console.log(
|
|
58
|
+
console.log(" npx @blastlabs/utils init-rules # 기본 규칙만");
|
|
59
|
+
console.log(" npx @blastlabs/utils init-rules --fsd # 기본 + FSD 규칙");
|
|
60
|
+
console.log(" npx @blastlabs/utils init-rules --all # 모든 규칙");
|
|
76
61
|
console.log("\n⚠️ 프로젝트 루트 디렉토리에서 실행해주세요!");
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
console.
|
|
85
|
-
|
|
64
|
+
function showList() {
|
|
65
|
+
console.log("📋 설치 가능한 규칙 목록\n");
|
|
66
|
+
|
|
67
|
+
console.log("📦 기본 규칙 (base) - 항상 포함:");
|
|
68
|
+
for (const rule of RULE_CATEGORIES.base) {
|
|
69
|
+
console.log(` ├── ${rule}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log("\n🏗️ FSD 아키텍처 규칙 (--fsd):");
|
|
73
|
+
for (const rule of RULE_CATEGORIES.fsd) {
|
|
74
|
+
console.log(` ├── ${rule}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log("\n💡 각 규칙은 globs 패턴으로 자동 적용됩니다.");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getRulesSourceDir() {
|
|
81
|
+
// 패키지 내 .cursor/rules 경로
|
|
82
|
+
return path.join(__dirname, "..", ".cursor", "rules");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function copyRule(ruleName, sourceDir, targetDir) {
|
|
86
|
+
const sourcePath = path.join(sourceDir, ruleName);
|
|
87
|
+
const targetPath = path.join(targetDir, ruleName);
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(sourcePath)) {
|
|
90
|
+
console.log(` ⚠️ ${ruleName} - 소스 파일 없음, 건너뜀`);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(targetPath)) {
|
|
95
|
+
console.log(` ⏭️ ${ruleName} - 이미 존재함, 건너뜀`);
|
|
96
|
+
return false;
|
|
86
97
|
}
|
|
98
|
+
|
|
99
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
100
|
+
console.log(` ✅ ${ruleName}`);
|
|
101
|
+
return true;
|
|
87
102
|
}
|
|
88
103
|
|
|
89
104
|
function main() {
|
|
90
105
|
const args = process.argv.slice(2);
|
|
91
106
|
const options = parseArgs(args);
|
|
92
107
|
|
|
93
|
-
if (options.help
|
|
108
|
+
if (options.help) {
|
|
94
109
|
showHelp();
|
|
95
110
|
process.exit(0);
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let content = loadTemplate(TEMPLATES.base);
|
|
102
|
-
|
|
103
|
-
if (options.fsd) {
|
|
104
|
-
content += "\n" + loadTemplate(TEMPLATES.fsd);
|
|
113
|
+
if (options.list) {
|
|
114
|
+
showList();
|
|
115
|
+
process.exit(0);
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
const targetDir = process.cwd();
|
|
119
|
+
const rulesTargetDir = path.join(targetDir, ".cursor", "rules");
|
|
120
|
+
const rulesSourceDir = getRulesSourceDir();
|
|
121
|
+
|
|
122
|
+
// 소스 디렉토리 확인
|
|
123
|
+
if (!fs.existsSync(rulesSourceDir)) {
|
|
124
|
+
console.error("❌ 규칙 소스 디렉토리를 찾을 수 없습니다.");
|
|
125
|
+
console.error(` 경로: ${rulesSourceDir}`);
|
|
126
|
+
process.exit(1);
|
|
109
127
|
}
|
|
110
128
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
// .cursor/rules 디렉토리 생성
|
|
130
|
+
if (!fs.existsSync(rulesTargetDir)) {
|
|
131
|
+
fs.mkdirSync(rulesTargetDir, { recursive: true });
|
|
132
|
+
console.log("📁 .cursor/rules/ 디렉토리 생성됨\n");
|
|
115
133
|
}
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
|
|
135
|
+
// 설치할 규칙 목록 결정
|
|
136
|
+
let rulesToInstall = [...RULE_CATEGORIES.base];
|
|
137
|
+
|
|
138
|
+
if (options.all || options.fsd) {
|
|
139
|
+
rulesToInstall = [...rulesToInstall, ...RULE_CATEGORIES.fsd];
|
|
119
140
|
}
|
|
120
141
|
|
|
121
|
-
//
|
|
122
|
-
|
|
142
|
+
// 중복 제거
|
|
143
|
+
rulesToInstall = [...new Set(rulesToInstall)];
|
|
123
144
|
|
|
124
|
-
|
|
125
|
-
const frameworkName = options.framework === "nextjs" ? "Next.js" :
|
|
126
|
-
options.framework === "vite" ? "Vite" : "React";
|
|
127
|
-
|
|
128
|
-
content = content
|
|
129
|
-
.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName)
|
|
130
|
-
.replace(/\{\{PROJECT_DESCRIPTION\}\}/g, options.projectDescription)
|
|
131
|
-
.replace(/\{\{PACKAGE_MANAGER\}\}/g, options.packageManager)
|
|
132
|
-
.replace(/\{\{FRAMEWORK\}\}/g, frameworkName);
|
|
133
|
-
|
|
134
|
-
// .cursorrules 파일 생성
|
|
135
|
-
const cursorrulesPath = path.join(targetDir, ".cursorrules");
|
|
136
|
-
if (fs.existsSync(cursorrulesPath)) {
|
|
137
|
-
console.log("⚠️ .cursorrules 파일이 이미 존재합니다. 덮어쓰기를 건너뜁니다.");
|
|
138
|
-
} else {
|
|
139
|
-
fs.writeFileSync(cursorrulesPath, content);
|
|
140
|
-
console.log("✅ .cursorrules 생성 완료");
|
|
141
|
-
}
|
|
145
|
+
console.log("📥 규칙 설치 중...\n");
|
|
142
146
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
let installedCount = 0;
|
|
148
|
+
let skippedCount = 0;
|
|
149
|
+
|
|
150
|
+
for (const rule of rulesToInstall) {
|
|
151
|
+
const installed = copyRule(rule, rulesSourceDir, rulesTargetDir);
|
|
152
|
+
if (installed) {
|
|
153
|
+
installedCount++;
|
|
154
|
+
} else {
|
|
155
|
+
skippedCount++;
|
|
156
|
+
}
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
console.log("\n
|
|
153
|
-
console.log(
|
|
154
|
-
console.log("└── CLAUDE.md");
|
|
159
|
+
console.log("\n" + "─".repeat(40));
|
|
160
|
+
console.log(`\n✨ 완료! ${installedCount}개 설치, ${skippedCount}개 건너뜀`);
|
|
155
161
|
|
|
156
|
-
console.log("\n📦
|
|
157
|
-
console.log("├── base (
|
|
158
|
-
if (options.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (options.monorepo) console.log("├── monorepo");
|
|
162
|
-
console.log("└── project-specific");
|
|
162
|
+
console.log("\n📦 설치된 규칙 카테고리:");
|
|
163
|
+
console.log(" ├── base (기본 규칙)");
|
|
164
|
+
if (options.all || options.fsd) {
|
|
165
|
+
console.log(" └── fsd (Feature-Sliced Design)");
|
|
166
|
+
}
|
|
163
167
|
|
|
164
|
-
console.log("\n💡 Tip:
|
|
168
|
+
console.log("\n💡 Tip:");
|
|
169
|
+
console.log(" - 각 .mdc 파일의 globs 패턴에 따라 자동 적용됩니다");
|
|
170
|
+
console.log(" - alwaysApply: true 규칙은 항상 적용됩니다");
|
|
171
|
+
console.log(" - 프로젝트에 맞게 규칙을 수정하세요!");
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
main();
|