@team-semicolon/semo-cli 3.0.7 → 3.0.10
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 +168 -55
- package/dist/index.js +189 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,70 +1,71 @@
|
|
|
1
1
|
# @team-semicolon/semo-cli
|
|
2
2
|
|
|
3
|
-
> SEMO CLI - AI Agent Orchestration Framework Installer
|
|
3
|
+
> SEMO CLI v3.0 - AI Agent Orchestration Framework Installer
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
Gemini의 하이브리드 전략에 따라 SEMO를 자동 설치하는 CLI 도구입니다.
|
|
5
|
+
## 설치
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
|
|
8
|
+
npm install -g @team-semicolon/semo-cli
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
`semo init` 명령어는 다음을 자동으로 수행합니다:
|
|
11
|
+
## 빠른 시작
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
```bash
|
|
14
|
+
# 프로젝트에 SEMO 설치
|
|
15
|
+
semo init
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
# Extension 패키지 추가
|
|
18
|
+
semo add eng/nextjs
|
|
20
19
|
|
|
20
|
+
# 버전 확인 및 업데이트 체크
|
|
21
|
+
semo -v
|
|
21
22
|
```
|
|
22
|
-
semo-system/
|
|
23
|
-
├── semo-core/ # Layer 0: 원칙, 오케스트레이션
|
|
24
|
-
└── semo-skills/ # Layer 1: coder, tester, planner
|
|
25
|
-
```
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
## 명령어
|
|
25
|
+
|
|
26
|
+
### `semo init`
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
현재 프로젝트에 SEMO를 설치합니다.
|
|
30
29
|
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"command": "npx",
|
|
37
|
-
"args": ["-y", "@team-semicolon/semo-mcp"]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
30
|
+
```bash
|
|
31
|
+
semo init # 기본 설치 (프로젝트 유형 자동 감지)
|
|
32
|
+
semo init --force # 기존 설정 덮어쓰기
|
|
33
|
+
semo init --skip-mcp # MCP 설정 생략
|
|
34
|
+
semo init --with next,infra # 특정 패키지와 함께 설치
|
|
41
35
|
```
|
|
42
36
|
|
|
43
|
-
###
|
|
37
|
+
### `semo add <packages>`
|
|
44
38
|
|
|
45
|
-
|
|
39
|
+
Extension 패키지를 추가로 설치합니다.
|
|
46
40
|
|
|
41
|
+
```bash
|
|
42
|
+
# 개별 패키지 설치
|
|
43
|
+
semo add eng/nextjs
|
|
44
|
+
semo add biz/discovery
|
|
45
|
+
|
|
46
|
+
# 그룹 일괄 설치
|
|
47
|
+
semo add biz # Business 전체 (discovery, design, management, poc)
|
|
48
|
+
semo add eng # Engineering 전체 (nextjs, spring, ms, infra)
|
|
49
|
+
semo add ops # Operations 전체 (qa, monitor, improve)
|
|
50
|
+
|
|
51
|
+
# 여러 패키지 동시 설치
|
|
52
|
+
semo add eng/nextjs,eng/infra
|
|
53
|
+
|
|
54
|
+
# 레거시 별칭 지원
|
|
55
|
+
semo add next # → eng/nextjs
|
|
56
|
+
semo add backend # → eng/spring
|
|
57
|
+
semo add mvp # → biz/poc
|
|
47
58
|
```
|
|
48
|
-
.claude/memory/
|
|
49
|
-
├── context.md # 프로젝트 상태
|
|
50
|
-
├── decisions.md # 아키텍처 결정
|
|
51
|
-
└── rules/ # 프로젝트별 규칙
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## 명령어
|
|
55
59
|
|
|
56
|
-
###
|
|
60
|
+
### `semo list`
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
사용 가능한 모든 패키지를 표시합니다.
|
|
59
63
|
|
|
60
64
|
```bash
|
|
61
|
-
semo
|
|
62
|
-
semo init --force # 기존 설정 덮어쓰기
|
|
63
|
-
semo init --skip-mcp # MCP 설정 생략
|
|
64
|
-
semo init --skip-subtree # Git Subtree 생략
|
|
65
|
+
semo list
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
### status
|
|
68
|
+
### `semo status`
|
|
68
69
|
|
|
69
70
|
SEMO 설치 상태를 확인합니다.
|
|
70
71
|
|
|
@@ -72,31 +73,117 @@ SEMO 설치 상태를 확인합니다.
|
|
|
72
73
|
semo status
|
|
73
74
|
```
|
|
74
75
|
|
|
75
|
-
###
|
|
76
|
+
### `semo version` / `semo -v`
|
|
77
|
+
|
|
78
|
+
버전 정보 및 업데이트 확인을 표시합니다.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
semo version
|
|
82
|
+
semo -v
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
출력 예시:
|
|
86
|
+
```
|
|
87
|
+
📦 SEMO CLI 버전 정보
|
|
88
|
+
|
|
89
|
+
현재 버전: 3.0.7
|
|
90
|
+
최신 버전: 3.0.7
|
|
91
|
+
|
|
92
|
+
✓ 최신 버전을 사용 중입니다.
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `semo update`
|
|
76
96
|
|
|
77
97
|
SEMO를 최신 버전으로 업데이트합니다.
|
|
78
98
|
|
|
79
99
|
```bash
|
|
80
|
-
semo update
|
|
100
|
+
semo update # CLI + semo-system 전체 업데이트
|
|
101
|
+
semo update --self # CLI만 업데이트
|
|
102
|
+
semo update --system # semo-system만 업데이트
|
|
103
|
+
semo update --skip-cli # CLI 업데이트 건너뛰기
|
|
81
104
|
```
|
|
82
105
|
|
|
106
|
+
## 패키지 구조
|
|
107
|
+
|
|
108
|
+
### Standard (필수)
|
|
109
|
+
|
|
110
|
+
모든 프로젝트에 기본 설치됩니다.
|
|
111
|
+
|
|
112
|
+
| 패키지 | 설명 |
|
|
113
|
+
|--------|------|
|
|
114
|
+
| `semo-core` | 원칙, 오케스트레이터, 공통 커맨드 |
|
|
115
|
+
| `semo-skills` | 13개 통합 스킬 (coder, tester, planner, deployer 등) |
|
|
116
|
+
|
|
117
|
+
### Extensions (선택)
|
|
118
|
+
|
|
119
|
+
프로젝트 유형에 맞게 선택적으로 설치합니다.
|
|
120
|
+
|
|
121
|
+
#### Business Layer (`biz`)
|
|
122
|
+
|
|
123
|
+
| 패키지 | 설명 | 설치 |
|
|
124
|
+
|--------|------|------|
|
|
125
|
+
| `biz/discovery` | 아이템 발굴, 시장 조사, Epic/Task | `semo add biz/discovery` |
|
|
126
|
+
| `biz/design` | 컨셉 설계, 목업, UX | `semo add biz/design` |
|
|
127
|
+
| `biz/management` | 일정/인력/스프린트 관리 | `semo add biz/management` |
|
|
128
|
+
| `biz/poc` | 빠른 PoC, 패스트트랙 | `semo add biz/poc` |
|
|
129
|
+
|
|
130
|
+
#### Engineering Layer (`eng`)
|
|
131
|
+
|
|
132
|
+
| 패키지 | 설명 | 자동 감지 | 설치 |
|
|
133
|
+
|--------|------|----------|------|
|
|
134
|
+
| `eng/nextjs` | Next.js 프론트엔드 개발 | `next.config.*` | `semo add eng/nextjs` |
|
|
135
|
+
| `eng/spring` | Spring Boot 백엔드 개발 | `pom.xml`, `build.gradle` | `semo add eng/spring` |
|
|
136
|
+
| `eng/ms` | 마이크로서비스 아키텍처 | - | `semo add eng/ms` |
|
|
137
|
+
| `eng/infra` | 인프라/배포 관리 | `Dockerfile`, `docker-compose.yml` | `semo add eng/infra` |
|
|
138
|
+
|
|
139
|
+
#### Operations Layer (`ops`)
|
|
140
|
+
|
|
141
|
+
| 패키지 | 설명 | 설치 |
|
|
142
|
+
|--------|------|------|
|
|
143
|
+
| `ops/qa` | 테스트/품질 관리 | `semo add ops/qa` |
|
|
144
|
+
| `ops/monitor` | 서비스 현황 모니터링 | `semo add ops/monitor` |
|
|
145
|
+
| `ops/improve` | 개선 제안 | `semo add ops/improve` |
|
|
146
|
+
|
|
147
|
+
#### Meta
|
|
148
|
+
|
|
149
|
+
| 패키지 | 설명 | 설치 |
|
|
150
|
+
|--------|------|------|
|
|
151
|
+
| `meta` | SEMO 프레임워크 자체 개발/관리 | `semo add meta` |
|
|
152
|
+
|
|
83
153
|
## 설치 후 구조
|
|
84
154
|
|
|
85
155
|
```
|
|
86
156
|
your-project/
|
|
87
157
|
├── .claude/
|
|
88
|
-
│ ├── CLAUDE.md
|
|
89
|
-
│ ├── settings.json
|
|
90
|
-
│ ├── memory/
|
|
91
|
-
│ ├──
|
|
92
|
-
│
|
|
158
|
+
│ ├── CLAUDE.md # 프로젝트 설정 (Extension CLAUDE.md 병합)
|
|
159
|
+
│ ├── settings.json # MCP 서버 설정
|
|
160
|
+
│ ├── memory/ # Context Mesh (세션 간 컨텍스트)
|
|
161
|
+
│ │ ├── context.md # 프로젝트 상태
|
|
162
|
+
│ │ ├── decisions.md # 아키텍처 결정
|
|
163
|
+
│ │ └── rules/ # 프로젝트별 규칙
|
|
164
|
+
│ ├── agents/ # 에이전트 심볼릭 링크
|
|
165
|
+
│ ├── skills/ # 스킬 심볼릭 링크
|
|
166
|
+
│ └── commands/SEMO/ # SEMO 커맨드
|
|
93
167
|
│
|
|
94
|
-
└── semo-system/
|
|
95
|
-
├── semo-core/
|
|
96
|
-
|
|
168
|
+
└── semo-system/ # White Box (읽기 전용)
|
|
169
|
+
├── semo-core/ # Layer 0: 원칙, 오케스트레이션
|
|
170
|
+
├── semo-skills/ # Layer 1: 통합 스킬
|
|
171
|
+
├── biz/ # Business Layer (선택)
|
|
172
|
+
├── eng/ # Engineering Layer (선택)
|
|
173
|
+
└── ops/ # Operations Layer (선택)
|
|
97
174
|
```
|
|
98
175
|
|
|
99
|
-
##
|
|
176
|
+
## MCP 서버
|
|
177
|
+
|
|
178
|
+
SEMO CLI는 다음 MCP 서버를 자동으로 등록합니다:
|
|
179
|
+
|
|
180
|
+
| 서버 | 설명 |
|
|
181
|
+
|------|------|
|
|
182
|
+
| `semo-integrations` | GitHub, Slack, Supabase 연동 |
|
|
183
|
+
| `context7` | 라이브러리 문서 조회 |
|
|
184
|
+
| `sequential-thinking` | 순차적 사고 지원 |
|
|
185
|
+
|
|
186
|
+
### 환경변수
|
|
100
187
|
|
|
101
188
|
MCP 연동을 위해 다음 환경변수를 설정하세요:
|
|
102
189
|
|
|
@@ -107,11 +194,37 @@ MCP 연동을 위해 다음 환경변수를 설정하세요:
|
|
|
107
194
|
| `SUPABASE_URL` | Supabase 프로젝트 URL |
|
|
108
195
|
| `SUPABASE_KEY` | Supabase 서비스 키 |
|
|
109
196
|
|
|
197
|
+
## 프로젝트 유형 자동 감지
|
|
198
|
+
|
|
199
|
+
`semo init` 실행 시 프로젝트 파일을 분석하여 적절한 패키지를 추천합니다:
|
|
200
|
+
|
|
201
|
+
| 감지 파일 | 추천 패키지 |
|
|
202
|
+
|----------|-------------|
|
|
203
|
+
| `next.config.js`, `next.config.mjs`, `next.config.ts` | `eng/nextjs` |
|
|
204
|
+
| `pom.xml`, `build.gradle` | `eng/spring` |
|
|
205
|
+
| `Dockerfile`, `docker-compose.yml` | `eng/infra` |
|
|
206
|
+
| `semo-core`, `semo-skills` | `meta` |
|
|
207
|
+
|
|
208
|
+
## 레거시 명령어 호환
|
|
209
|
+
|
|
210
|
+
이전 버전 사용자를 위해 레거시 패키지명도 지원합니다:
|
|
211
|
+
|
|
212
|
+
| 레거시 | 현재 |
|
|
213
|
+
|--------|------|
|
|
214
|
+
| `semo add next` | `semo add eng/nextjs` |
|
|
215
|
+
| `semo add backend` | `semo add eng/spring` |
|
|
216
|
+
| `semo add ms` | `semo add eng/ms` |
|
|
217
|
+
| `semo add infra` | `semo add eng/infra` |
|
|
218
|
+
| `semo add qa` | `semo add ops/qa` |
|
|
219
|
+
| `semo add po` | `semo add biz/discovery` |
|
|
220
|
+
| `semo add pm` | `semo add biz/management` |
|
|
221
|
+
| `semo add design` | `semo add biz/design` |
|
|
222
|
+
| `semo add mvp` | `semo add biz/poc` |
|
|
223
|
+
|
|
110
224
|
## 참조
|
|
111
225
|
|
|
112
226
|
- [SEMO 레포지토리](https://github.com/semicolon-devteam/semo)
|
|
113
|
-
- [SEMO MCP Server](
|
|
114
|
-
- [Gemini 하이브리드 전략](../../docs/SEMO_ARCHITECTURE_REVIEW.md)
|
|
227
|
+
- [SEMO MCP Server](https://www.npmjs.com/package/@team-semicolon/semo-mcp)
|
|
115
228
|
|
|
116
229
|
## 라이선스
|
|
117
230
|
|
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ const child_process_1 = require("child_process");
|
|
|
59
59
|
const fs = __importStar(require("fs"));
|
|
60
60
|
const path = __importStar(require("path"));
|
|
61
61
|
const os = __importStar(require("os"));
|
|
62
|
-
const VERSION = "3.0.
|
|
62
|
+
const VERSION = "3.0.10";
|
|
63
63
|
const PACKAGE_NAME = "@team-semicolon/semo-cli";
|
|
64
64
|
// === 버전 비교 유틸리티 ===
|
|
65
65
|
/**
|
|
@@ -313,17 +313,104 @@ function detectProjectType(cwd) {
|
|
|
313
313
|
}
|
|
314
314
|
return detected;
|
|
315
315
|
}
|
|
316
|
+
// === 설치된 Extension 패키지 스캔 ===
|
|
317
|
+
function getInstalledExtensions(cwd) {
|
|
318
|
+
const semoSystemDir = path.join(cwd, "semo-system");
|
|
319
|
+
const installed = [];
|
|
320
|
+
for (const key of Object.keys(EXTENSION_PACKAGES)) {
|
|
321
|
+
const pkgPath = path.join(semoSystemDir, key);
|
|
322
|
+
if (fs.existsSync(pkgPath)) {
|
|
323
|
+
installed.push(key);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return installed;
|
|
327
|
+
}
|
|
328
|
+
function checkRequiredTools() {
|
|
329
|
+
const tools = [
|
|
330
|
+
{
|
|
331
|
+
name: "GitHub CLI (gh)",
|
|
332
|
+
installed: false,
|
|
333
|
+
installCmd: isWindows ? "winget install GitHub.cli" : "brew install gh",
|
|
334
|
+
description: "GitHub API 연동 (이슈, PR, 배포)",
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "Supabase CLI",
|
|
338
|
+
installed: false,
|
|
339
|
+
installCmd: isWindows ? "npm install -g supabase" : "brew install supabase/tap/supabase",
|
|
340
|
+
description: "Supabase 데이터베이스 연동",
|
|
341
|
+
},
|
|
342
|
+
];
|
|
343
|
+
// GitHub CLI 확인
|
|
344
|
+
try {
|
|
345
|
+
const ghVersion = (0, child_process_1.execSync)("gh --version", { stdio: "pipe", encoding: "utf-8" });
|
|
346
|
+
tools[0].installed = true;
|
|
347
|
+
tools[0].version = ghVersion.split("\n")[0].replace("gh version ", "").trim();
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// gh not installed
|
|
351
|
+
}
|
|
352
|
+
// Supabase CLI 확인
|
|
353
|
+
try {
|
|
354
|
+
const supabaseVersion = (0, child_process_1.execSync)("supabase --version", { stdio: "pipe", encoding: "utf-8" });
|
|
355
|
+
tools[1].installed = true;
|
|
356
|
+
tools[1].version = supabaseVersion.trim();
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
// supabase not installed
|
|
360
|
+
}
|
|
361
|
+
return tools;
|
|
362
|
+
}
|
|
363
|
+
async function showToolsStatus() {
|
|
364
|
+
console.log(chalk_1.default.cyan("\n🔍 필수 도구 확인"));
|
|
365
|
+
const tools = checkRequiredTools();
|
|
366
|
+
const missingTools = tools.filter(t => !t.installed);
|
|
367
|
+
for (const tool of tools) {
|
|
368
|
+
if (tool.installed) {
|
|
369
|
+
console.log(chalk_1.default.green(` ✓ ${tool.name} ${tool.version ? `(${tool.version})` : ""}`));
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
console.log(chalk_1.default.yellow(` ✗ ${tool.name} - 미설치`));
|
|
373
|
+
console.log(chalk_1.default.gray(` ${tool.description}`));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (missingTools.length > 0) {
|
|
377
|
+
console.log(chalk_1.default.yellow("\n⚠ 일부 도구가 설치되어 있지 않습니다."));
|
|
378
|
+
console.log(chalk_1.default.gray(" SEMO의 일부 기능이 제한될 수 있습니다.\n"));
|
|
379
|
+
console.log(chalk_1.default.cyan("📋 설치 명령어:"));
|
|
380
|
+
for (const tool of missingTools) {
|
|
381
|
+
console.log(chalk_1.default.white(` ${tool.installCmd}`));
|
|
382
|
+
}
|
|
383
|
+
console.log();
|
|
384
|
+
const { continueWithout } = await inquirer_1.default.prompt([
|
|
385
|
+
{
|
|
386
|
+
type: "confirm",
|
|
387
|
+
name: "continueWithout",
|
|
388
|
+
message: "도구 없이 계속 설치를 진행할까요?",
|
|
389
|
+
default: true,
|
|
390
|
+
},
|
|
391
|
+
]);
|
|
392
|
+
return continueWithout;
|
|
393
|
+
}
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
316
396
|
// === init 명령어 ===
|
|
317
397
|
program
|
|
318
398
|
.command("init")
|
|
319
399
|
.description("현재 프로젝트에 SEMO를 설치합니다")
|
|
320
400
|
.option("-f, --force", "기존 설정 덮어쓰기")
|
|
321
401
|
.option("--skip-mcp", "MCP 설정 생략")
|
|
402
|
+
.option("--no-gitignore", ".gitignore 수정 생략")
|
|
322
403
|
.option("--with <packages>", "추가 설치할 패키지 (쉼표 구분: next,backend)")
|
|
323
404
|
.action(async (options) => {
|
|
324
405
|
console.log(chalk_1.default.cyan.bold("\n🚀 SEMO 설치 시작\n"));
|
|
325
406
|
console.log(chalk_1.default.gray("Gemini 하이브리드 전략: White Box + Black Box\n"));
|
|
326
407
|
const cwd = process.cwd();
|
|
408
|
+
// 0. 필수 도구 확인
|
|
409
|
+
const shouldContinue = await showToolsStatus();
|
|
410
|
+
if (!shouldContinue) {
|
|
411
|
+
console.log(chalk_1.default.yellow("\n설치가 취소되었습니다. 필수 도구 설치 후 다시 시도하세요.\n"));
|
|
412
|
+
process.exit(0);
|
|
413
|
+
}
|
|
327
414
|
// 1. Git 레포지토리 확인
|
|
328
415
|
const spinner = (0, ora_1.default)("Git 레포지토리 확인 중...").start();
|
|
329
416
|
try {
|
|
@@ -375,9 +462,13 @@ program
|
|
|
375
462
|
}
|
|
376
463
|
// 7. Context Mesh 초기화
|
|
377
464
|
await setupContextMesh(cwd);
|
|
378
|
-
// 8.
|
|
465
|
+
// 8. .gitignore 업데이트
|
|
466
|
+
if (options.gitignore !== false) {
|
|
467
|
+
updateGitignore(cwd);
|
|
468
|
+
}
|
|
469
|
+
// 9. CLAUDE.md 생성
|
|
379
470
|
await setupClaudeMd(cwd, extensionsToInstall, options.force);
|
|
380
|
-
//
|
|
471
|
+
// 10. Extensions 심볼릭 링크 (agents/skills 병합)
|
|
381
472
|
if (extensionsToInstall.length > 0) {
|
|
382
473
|
await setupExtensionSymlinks(cwd, extensionsToInstall);
|
|
383
474
|
}
|
|
@@ -488,13 +579,20 @@ async function createStandardSymlinks(cwd) {
|
|
|
488
579
|
const commandsDir = path.join(claudeDir, "commands");
|
|
489
580
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
490
581
|
const semoCommandsLink = path.join(commandsDir, "SEMO");
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
582
|
+
const commandsTarget = path.join(semoSystemDir, "semo-core", "commands", "SEMO");
|
|
583
|
+
// 기존 링크가 있으면 삭제 후 재생성 (업데이트 시에도 최신 반영)
|
|
584
|
+
if (fs.existsSync(semoCommandsLink)) {
|
|
585
|
+
if (fs.lstatSync(semoCommandsLink).isSymbolicLink()) {
|
|
586
|
+
fs.unlinkSync(semoCommandsLink);
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
removeRecursive(semoCommandsLink);
|
|
496
590
|
}
|
|
497
591
|
}
|
|
592
|
+
if (fs.existsSync(commandsTarget)) {
|
|
593
|
+
createSymlinkOrJunction(commandsTarget, semoCommandsLink);
|
|
594
|
+
console.log(chalk_1.default.green(" ✓ .claude/commands/SEMO → semo-system/semo-core/commands/SEMO"));
|
|
595
|
+
}
|
|
498
596
|
}
|
|
499
597
|
// === Extensions 다운로드 (심볼릭 링크 제외) ===
|
|
500
598
|
async function downloadExtensions(cwd, packages, force) {
|
|
@@ -511,6 +609,27 @@ async function downloadExtensions(cwd, packages, force) {
|
|
|
511
609
|
(0, child_process_1.execSync)(`git clone --depth 1 ${SEMO_REPO} "${tempDir}"`, { stdio: "pipe" });
|
|
512
610
|
}
|
|
513
611
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
612
|
+
// 그룹 추출 (중복 제거) - 그룹 레벨 CLAUDE.md 복사용
|
|
613
|
+
const groups = [...new Set(packages.map(pkg => pkg.split("/")[0]).filter(g => ["biz", "eng", "ops"].includes(g)))];
|
|
614
|
+
// 그룹 레벨 파일 복사 (CLAUDE.md, VERSION 등)
|
|
615
|
+
for (const group of groups) {
|
|
616
|
+
const groupSrcDir = path.join(tempDir, "packages", group);
|
|
617
|
+
const groupDestDir = path.join(semoSystemDir, group);
|
|
618
|
+
// 그룹 디렉토리의 루트 파일만 복사 (CLAUDE.md, VERSION)
|
|
619
|
+
if (fs.existsSync(groupSrcDir)) {
|
|
620
|
+
fs.mkdirSync(groupDestDir, { recursive: true });
|
|
621
|
+
const groupFiles = fs.readdirSync(groupSrcDir);
|
|
622
|
+
for (const file of groupFiles) {
|
|
623
|
+
const srcFile = path.join(groupSrcDir, file);
|
|
624
|
+
const destFile = path.join(groupDestDir, file);
|
|
625
|
+
if (fs.statSync(srcFile).isFile()) {
|
|
626
|
+
fs.copyFileSync(srcFile, destFile);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
console.log(chalk_1.default.green(` ✓ ${group}/ 그룹 파일 복사 (CLAUDE.md 등)`));
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// 개별 패키지 복사
|
|
514
633
|
for (const pkg of packages) {
|
|
515
634
|
const srcPath = path.join(tempDir, "packages", pkg);
|
|
516
635
|
const destPath = path.join(semoSystemDir, pkg);
|
|
@@ -1022,6 +1141,33 @@ async function mergeExtensionSettings(cwd, packages) {
|
|
|
1022
1141
|
}
|
|
1023
1142
|
}
|
|
1024
1143
|
}
|
|
1144
|
+
// === .gitignore 업데이트 ===
|
|
1145
|
+
function updateGitignore(cwd) {
|
|
1146
|
+
console.log(chalk_1.default.cyan("\n📝 .gitignore 업데이트"));
|
|
1147
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
1148
|
+
const semoIgnoreBlock = `
|
|
1149
|
+
# === SEMO ===
|
|
1150
|
+
.claude/*
|
|
1151
|
+
!.claude/memory/
|
|
1152
|
+
!.claude/memory/**
|
|
1153
|
+
`;
|
|
1154
|
+
if (fs.existsSync(gitignorePath)) {
|
|
1155
|
+
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
1156
|
+
// 이미 SEMO 블록이 있으면 스킵
|
|
1157
|
+
if (content.includes("# === SEMO ===")) {
|
|
1158
|
+
console.log(chalk_1.default.gray(" → SEMO 블록 이미 존재 (건너뜀)"));
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
// 기존 파일에 추가
|
|
1162
|
+
fs.appendFileSync(gitignorePath, semoIgnoreBlock);
|
|
1163
|
+
console.log(chalk_1.default.green("✓ .gitignore에 SEMO 규칙 추가됨"));
|
|
1164
|
+
}
|
|
1165
|
+
else {
|
|
1166
|
+
// 새로 생성
|
|
1167
|
+
fs.writeFileSync(gitignorePath, semoIgnoreBlock.trim() + "\n");
|
|
1168
|
+
console.log(chalk_1.default.green("✓ .gitignore 생성됨 (SEMO 규칙 포함)"));
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1025
1171
|
// === Context Mesh 초기화 ===
|
|
1026
1172
|
async function setupContextMesh(cwd) {
|
|
1027
1173
|
console.log(chalk_1.default.cyan("\n🧠 Context Mesh 초기화"));
|
|
@@ -1142,8 +1288,25 @@ async function setupClaudeMd(cwd, extensions, force) {
|
|
|
1142
1288
|
const extensionsList = extensions.length > 0
|
|
1143
1289
|
? extensions.map(pkg => `├── ${pkg}/ # ${EXTENSION_PACKAGES[pkg].name}`).join("\n")
|
|
1144
1290
|
: "";
|
|
1145
|
-
// 패키지별 CLAUDE.md 병합 섹션 생성
|
|
1291
|
+
// 그룹 및 패키지별 CLAUDE.md 병합 섹션 생성
|
|
1146
1292
|
let packageClaudeMdSections = "";
|
|
1293
|
+
// 1. 설치된 패키지에서 그룹 추출 (중복 제거)
|
|
1294
|
+
const installedGroups = [...new Set(extensions.map(pkg => pkg.split("/")[0]).filter(g => PACKAGE_GROUPS.includes(g)))];
|
|
1295
|
+
// 2. 그룹 레벨 CLAUDE.md 먼저 병합 (biz, eng, ops)
|
|
1296
|
+
for (const group of installedGroups) {
|
|
1297
|
+
const groupClaudeMdPath = path.join(semoSystemDir, group, "CLAUDE.md");
|
|
1298
|
+
if (fs.existsSync(groupClaudeMdPath)) {
|
|
1299
|
+
const groupContent = fs.readFileSync(groupClaudeMdPath, "utf-8");
|
|
1300
|
+
// 그룹 CLAUDE.md 헤더 레벨 조정 (# → ##, ## → ###)
|
|
1301
|
+
const adjustedContent = groupContent
|
|
1302
|
+
.replace(/^# /gm, "## ")
|
|
1303
|
+
.replace(/^## /gm, "### ")
|
|
1304
|
+
.replace(/^### /gm, "#### ");
|
|
1305
|
+
packageClaudeMdSections += `\n\n---\n\n${adjustedContent}`;
|
|
1306
|
+
console.log(chalk_1.default.green(` + ${group}/ 그룹 CLAUDE.md 병합됨`));
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
// 3. 개별 패키지 CLAUDE.md 병합
|
|
1147
1310
|
for (const pkg of extensions) {
|
|
1148
1311
|
const pkgClaudeMdPath = path.join(semoSystemDir, pkg, "CLAUDE.md");
|
|
1149
1312
|
if (fs.existsSync(pkgClaudeMdPath)) {
|
|
@@ -1337,7 +1500,9 @@ program
|
|
|
1337
1500
|
}
|
|
1338
1501
|
console.log();
|
|
1339
1502
|
}
|
|
1340
|
-
//
|
|
1503
|
+
// 기존에 설치된 모든 Extension 패키지 스캔
|
|
1504
|
+
const previouslyInstalled = getInstalledExtensions(cwd);
|
|
1505
|
+
// 요청한 패키지 중 이미 설치된 것과 새로 설치할 것 분류
|
|
1341
1506
|
const alreadyInstalled = [];
|
|
1342
1507
|
const toInstall = [];
|
|
1343
1508
|
for (const pkg of packages) {
|
|
@@ -1364,8 +1529,8 @@ program
|
|
|
1364
1529
|
await downloadExtensions(cwd, toInstall, options.force);
|
|
1365
1530
|
// 2. settings.json 병합
|
|
1366
1531
|
await mergeExtensionSettings(cwd, toInstall);
|
|
1367
|
-
// 3. 심볼릭 링크 설정 (
|
|
1368
|
-
const allInstalledPackages = [...new Set([...
|
|
1532
|
+
// 3. 심볼릭 링크 설정 (기존 + 새로 설치한 모든 패키지 포함)
|
|
1533
|
+
const allInstalledPackages = [...new Set([...previouslyInstalled, ...toInstall])];
|
|
1369
1534
|
await setupExtensionSymlinks(cwd, allInstalledPackages);
|
|
1370
1535
|
// 4. CLAUDE.md 재생성 (모든 설치된 패키지 반영)
|
|
1371
1536
|
await setupClaudeMd(cwd, allInstalledPackages, options.force);
|
|
@@ -1594,7 +1759,18 @@ program
|
|
|
1594
1759
|
}
|
|
1595
1760
|
}
|
|
1596
1761
|
}
|
|
1597
|
-
//
|
|
1762
|
+
// commands 링크도 정리 (신규 commands 반영 위해)
|
|
1763
|
+
const claudeCommandsDir = path.join(claudeDir, "commands");
|
|
1764
|
+
const semoCommandsLink = path.join(claudeCommandsDir, "SEMO");
|
|
1765
|
+
if (fs.existsSync(semoCommandsLink)) {
|
|
1766
|
+
if (fs.lstatSync(semoCommandsLink).isSymbolicLink()) {
|
|
1767
|
+
fs.unlinkSync(semoCommandsLink);
|
|
1768
|
+
}
|
|
1769
|
+
else {
|
|
1770
|
+
removeRecursive(semoCommandsLink);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
// Standard 심볼릭 링크 재생성 (agents, skills, commands 포함)
|
|
1598
1774
|
await createStandardSymlinks(cwd);
|
|
1599
1775
|
// Extensions 심볼릭 링크 재생성
|
|
1600
1776
|
if (installedExtensions.length > 0) {
|