@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.
Files changed (2) hide show
  1. package/bin/init-ai-rules.cjs +117 -110
  2. package/package.json +1 -1
@@ -3,53 +3,42 @@
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
5
 
6
- const TEMPLATES = {
7
- base: "base.md",
8
- fsd: "fsd.md",
9
- testing: "testing.md",
10
- nextjs: "nextjs.md",
11
- vite: "vite.md",
12
- monorepo: "monorepo.md",
13
- project: "project-specific.md",
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
- testing: false,
23
- monorepo: false,
24
- packageManager: "pnpm",
28
+ all: false,
29
+ list: false,
25
30
  help: false,
26
31
  };
27
32
 
28
- for (let i = 0; i < args.length; i++) {
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 === "--testing" || arg === "--test") {
40
- options.testing = true;
41
- } else if (arg === "--monorepo") {
42
- options.monorepo = true;
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 - AI Rules Generator\n");
61
- console.log("프로젝트에 .cursorrules와 CLAUDE.md 파일을 생성합니다.\n");
62
- console.log("사용법: npx blastlabs-init-ai-rules <project-name> [options]\n");
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(" --next, --nextjs Next.js 프로젝트 규칙 포함");
65
- console.log(" --vite Vite 프로젝트 규칙 포함");
66
- console.log(" --fsd Feature-Sliced Design 규칙 포함");
67
- console.log(" --testing, --test 테스트 전략 규칙 포함");
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(' npx blastlabs-init-ai-rules "My Project" --vite --fsd');
74
- console.log(' npx blastlabs-init-ai-rules "Admin" --next --fsd --testing --monorepo');
75
- console.log(' npx blastlabs-init-ai-rules "Web App" "React 웹 애플리케이션" --vite');
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 loadTemplate(name) {
80
- const templatePath = path.join(__dirname, "..", "templates", name);
81
- try {
82
- return fs.readFileSync(templatePath, "utf-8");
83
- } catch (error) {
84
- console.error(`❌ 템플릿 파일을 찾을 수 없습니다: ${name}`);
85
- return "";
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 || !options.projectName) {
108
+ if (options.help) {
94
109
  showHelp();
95
110
  process.exit(0);
96
111
  }
97
112
 
98
- const targetDir = process.cwd();
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
- if (options.testing) {
108
- content += "\n" + loadTemplate(TEMPLATES.testing);
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
- if (options.framework === "nextjs") {
112
- content += "\n" + loadTemplate(TEMPLATES.nextjs);
113
- } else if (options.framework === "vite") {
114
- content += "\n" + loadTemplate(TEMPLATES.vite);
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
- if (options.monorepo) {
118
- content += "\n" + loadTemplate(TEMPLATES.monorepo);
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
- // 항상 project-specific 섹션 추가
122
- content += "\n" + loadTemplate(TEMPLATES.project);
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
- // CLAUDE.md 파일 생성 (루트)
144
- const claudePath = path.join(targetDir, "CLAUDE.md");
145
- if (fs.existsSync(claudePath)) {
146
- console.log("⚠️ CLAUDE.md 파일이 이미 존재합니다. 덮어쓰기를 건너뜁니다.");
147
- } else {
148
- fs.writeFileSync(claudePath, content);
149
- console.log("✅ CLAUDE.md 생성 완료");
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("├── .cursorrules");
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.fsd) console.log("├── fsd (Feature-Sliced Design)");
159
- if (options.testing) console.log("├── testing (테스트 전략)");
160
- if (options.framework) console.log(`├── ${options.framework}`);
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blastlabs/utils",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",