@team-semicolon/semo-cli 3.7.1 → 3.7.3
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/dist/index.js +159 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -995,17 +995,17 @@ program
|
|
|
995
995
|
spinner.fail("Git 레포지토리가 아닙니다. 'git init'을 먼저 실행하세요.");
|
|
996
996
|
process.exit(1);
|
|
997
997
|
}
|
|
998
|
-
// 2. Extension 패키지 처리
|
|
999
|
-
|
|
998
|
+
// 2. Extension 패키지 처리
|
|
999
|
+
// semo-hooks는 기본 포함 (Claude Code Hooks 로깅 시스템)
|
|
1000
|
+
let extensionsToInstall = ["semo-hooks"];
|
|
1000
1001
|
if (options.with) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
console.log(chalk_1.default.cyan("\n📦 추가 Extension 설치:"));
|
|
1004
|
-
extensionsToInstall.forEach(pkg => {
|
|
1005
|
-
console.log(chalk_1.default.gray(` - ${EXTENSION_PACKAGES[pkg].name}: ${EXTENSION_PACKAGES[pkg].desc}`));
|
|
1006
|
-
});
|
|
1007
|
-
}
|
|
1002
|
+
const additionalPkgs = options.with.split(",").map((p) => p.trim()).filter((p) => p in EXTENSION_PACKAGES);
|
|
1003
|
+
extensionsToInstall = [...new Set([...extensionsToInstall, ...additionalPkgs])];
|
|
1008
1004
|
}
|
|
1005
|
+
console.log(chalk_1.default.cyan("\n📦 Extension 설치:"));
|
|
1006
|
+
extensionsToInstall.forEach(pkg => {
|
|
1007
|
+
console.log(chalk_1.default.gray(` - ${EXTENSION_PACKAGES[pkg].name}: ${EXTENSION_PACKAGES[pkg].desc}`));
|
|
1008
|
+
});
|
|
1009
1009
|
// 3. .claude 디렉토리 생성
|
|
1010
1010
|
const claudeDir = path.join(cwd, ".claude");
|
|
1011
1011
|
if (!fs.existsSync(claudeDir)) {
|
|
@@ -1072,7 +1072,8 @@ program
|
|
|
1072
1072
|
console.log(chalk_1.default.gray(" 1. Claude Code에서 프로젝트 열기"));
|
|
1073
1073
|
console.log(chalk_1.default.gray(" 2. 자연어로 요청하기 (예: \"댓글 기능 구현해줘\")"));
|
|
1074
1074
|
console.log(chalk_1.default.gray(" 3. /SEMO:help로 도움말 확인"));
|
|
1075
|
-
|
|
1075
|
+
// semo-hooks만 설치된 경우 추가 패키지 안내
|
|
1076
|
+
if (extensionsToInstall.length === 1 && extensionsToInstall[0] === "semo-hooks") {
|
|
1076
1077
|
console.log(chalk_1.default.gray("\n💡 추가 패키지: semo add <package> (예: semo add meta)"));
|
|
1077
1078
|
}
|
|
1078
1079
|
console.log();
|
|
@@ -1421,7 +1422,8 @@ async function downloadExtensions(cwd, packages, force) {
|
|
|
1421
1422
|
}
|
|
1422
1423
|
// 개별 패키지 복사
|
|
1423
1424
|
for (const pkg of packages) {
|
|
1424
|
-
|
|
1425
|
+
// Extension 패키지는 semo-system/ 폴더에 있음
|
|
1426
|
+
const srcPath = path.join(tempDir, "semo-system", pkg);
|
|
1425
1427
|
const destPath = path.join(semoSystemDir, pkg);
|
|
1426
1428
|
if (fs.existsSync(srcPath)) {
|
|
1427
1429
|
if (fs.existsSync(destPath) && !force) {
|
|
@@ -1497,6 +1499,26 @@ function createMergedOrchestrator(claudeAgentsDir, orchestratorSources) {
|
|
|
1497
1499
|
}
|
|
1498
1500
|
}
|
|
1499
1501
|
}
|
|
1502
|
+
// meta 패키지 포함 여부 확인
|
|
1503
|
+
const hasMetaInSources = orchestratorSources.some(s => s.pkg === "meta");
|
|
1504
|
+
// Meta 자동 체이닝 섹션
|
|
1505
|
+
const metaAutoChainSection = hasMetaInSources ? `
|
|
1506
|
+
## 🔴 Meta 환경 자동 체이닝 (NON-NEGOTIABLE)
|
|
1507
|
+
|
|
1508
|
+
> **조건**: semo-system/ 내 파일 수정이 감지되면
|
|
1509
|
+
> **동작**: 작업 종료 전 자동으로 \`skill:meta-workflow\` 호출
|
|
1510
|
+
|
|
1511
|
+
\`\`\`text
|
|
1512
|
+
semo-system/ 파일 수정 감지
|
|
1513
|
+
↓
|
|
1514
|
+
[자동] skill:meta-workflow 호출
|
|
1515
|
+
↓
|
|
1516
|
+
버저닝 → 배포 → 로컬 동기화
|
|
1517
|
+
\`\`\`
|
|
1518
|
+
|
|
1519
|
+
**이 규칙은 우회할 수 없습니다.**
|
|
1520
|
+
|
|
1521
|
+
` : "";
|
|
1500
1522
|
// 병합된 orchestrator.md 생성
|
|
1501
1523
|
const mergedContent = `---
|
|
1502
1524
|
name: orchestrator
|
|
@@ -1565,7 +1587,7 @@ ${routingTables.join("\n\n---\n\n")}
|
|
|
1565
1587
|
3. **Package Priority**: 라우팅 충돌 시 설치 순서대로 우선순위 적용
|
|
1566
1588
|
4. **Cross-Package**: 다른 패키지 전문 영역 요청 시 인계 권유
|
|
1567
1589
|
|
|
1568
|
-
${crossPackageRouting.length > 0 ? `## 🔄 Cross-Package Routing
|
|
1590
|
+
${metaAutoChainSection}${crossPackageRouting.length > 0 ? `## 🔄 Cross-Package Routing
|
|
1569
1591
|
|
|
1570
1592
|
${crossPackageRouting[0]}` : ""}
|
|
1571
1593
|
|
|
@@ -2346,6 +2368,8 @@ async function setupClaudeMd(cwd, extensions, force) {
|
|
|
2346
2368
|
: "";
|
|
2347
2369
|
// 패키지별 CLAUDE.md 병합 섹션 생성
|
|
2348
2370
|
let packageClaudeMdSections = "";
|
|
2371
|
+
// meta 패키지 설치 여부 확인
|
|
2372
|
+
const hasMetaPackage = extensions.includes("meta");
|
|
2349
2373
|
// 개별 패키지 CLAUDE.md 병합 - 중복 제거 적용
|
|
2350
2374
|
for (const pkg of extensions) {
|
|
2351
2375
|
const pkgClaudeMdPath = path.join(semoSystemDir, pkg, "CLAUDE.md");
|
|
@@ -2391,6 +2415,47 @@ ${orchestratorPaths.map(p => `- \`${p}\``).join("\n")}
|
|
|
2391
2415
|
: `**반드시 읽어야 할 파일**: \`semo-system/semo-core/agents/orchestrator/orchestrator.md\`
|
|
2392
2416
|
|
|
2393
2417
|
이 파일에서 라우팅 테이블, 의도 분류, 메시지 포맷을 확인하세요.`;
|
|
2418
|
+
// Meta 패키지 설치 시 자동 워크플로우 강제 규칙
|
|
2419
|
+
const metaWorkflowSection = hasMetaPackage ? `
|
|
2420
|
+
### 3. Meta 환경 자동 워크플로우 (NON-NEGOTIABLE)
|
|
2421
|
+
|
|
2422
|
+
> **⚠️ Meta 패키지가 설치된 환경에서는 반드시 아래 규칙이 적용됩니다.**
|
|
2423
|
+
> **이 규칙을 우회하거나 무시하는 것은 금지됩니다.**
|
|
2424
|
+
|
|
2425
|
+
#### 자동 트리거 조건
|
|
2426
|
+
|
|
2427
|
+
\`semo-system/\` 디렉토리 내 파일이 수정되면:
|
|
2428
|
+
1. 작업 종료 전 반드시 \`skill:meta-workflow\` 호출
|
|
2429
|
+
2. 버저닝 → 배포 → 로컬 동기화 체인 자동 실행
|
|
2430
|
+
|
|
2431
|
+
#### 감지 패턴
|
|
2432
|
+
|
|
2433
|
+
다음 경로의 파일 수정 시 자동 트리거:
|
|
2434
|
+
- \`semo-system/semo-core/**\`
|
|
2435
|
+
- \`semo-system/semo-skills/**\`
|
|
2436
|
+
- \`semo-system/meta/**\`
|
|
2437
|
+
- \`semo-system/semo-remote/**\`
|
|
2438
|
+
- \`semo-system/semo-hooks/**\`
|
|
2439
|
+
- \`packages/cli/**\` (CLI 수정 시)
|
|
2440
|
+
|
|
2441
|
+
#### 강제 동작 흐름
|
|
2442
|
+
|
|
2443
|
+
\`\`\`text
|
|
2444
|
+
[작업 완료 감지]
|
|
2445
|
+
↓
|
|
2446
|
+
semo-system/ 또는 packages/ 파일 수정 여부 확인
|
|
2447
|
+
↓
|
|
2448
|
+
수정됨? → [SEMO] Skill 호출: meta-workflow
|
|
2449
|
+
버저닝 → 배포 → 동기화 자동 실행
|
|
2450
|
+
↓
|
|
2451
|
+
수정 안됨? → 정상 종료
|
|
2452
|
+
\`\`\`
|
|
2453
|
+
|
|
2454
|
+
**금지 사항**:
|
|
2455
|
+
- semo-system/ 수정 후 버저닝 없이 종료
|
|
2456
|
+
- "버저닝 나중에 해줘" 요청 수락
|
|
2457
|
+
- meta-workflow 스킬 호출 건너뛰기
|
|
2458
|
+
` : "";
|
|
2394
2459
|
const claudeMdContent = `# SEMO Project Configuration
|
|
2395
2460
|
|
|
2396
2461
|
> SEMO (Semicolon Orchestrate) - AI Agent Orchestration Framework v${VERSION}
|
|
@@ -2444,7 +2509,7 @@ npm run build # 3. 빌드 검증 (Next.js/TypeScript 프로젝트)
|
|
|
2444
2509
|
- \`--no-verify\` 플래그 사용 금지
|
|
2445
2510
|
- Quality Gate 우회 시도 거부
|
|
2446
2511
|
- "그냥 커밋해줘", "빌드 생략해줘" 등 거부
|
|
2447
|
-
|
|
2512
|
+
${metaWorkflowSection}
|
|
2448
2513
|
---
|
|
2449
2514
|
|
|
2450
2515
|
## 설치된 구성
|
|
@@ -3035,8 +3100,87 @@ program
|
|
|
3035
3100
|
else {
|
|
3036
3101
|
console.log(chalk_1.default.red(" ❌ .claude/ 디렉토리 없음"));
|
|
3037
3102
|
}
|
|
3038
|
-
// 4.
|
|
3039
|
-
console.log(chalk_1.default.cyan("\n4.
|
|
3103
|
+
// 4. semo-hooks 상태 확인
|
|
3104
|
+
console.log(chalk_1.default.cyan("\n4. semo-hooks (Claude Code Hooks)"));
|
|
3105
|
+
const hooksDir = path.join(semoSystemDir, "semo-hooks");
|
|
3106
|
+
const hooksDistDir = path.join(hooksDir, "dist");
|
|
3107
|
+
const hooksIndexJs = path.join(hooksDistDir, "index.js");
|
|
3108
|
+
if (!fs.existsSync(hooksDir)) {
|
|
3109
|
+
console.log(chalk_1.default.gray(" ⏭️ semo-hooks 미설치 (선택 패키지)"));
|
|
3110
|
+
console.log(chalk_1.default.gray(" 💡 설치: semo add hooks"));
|
|
3111
|
+
}
|
|
3112
|
+
else {
|
|
3113
|
+
// hooks 버전 확인
|
|
3114
|
+
const hooksVersionPath = path.join(hooksDir, "VERSION");
|
|
3115
|
+
const hooksVersion = fs.existsSync(hooksVersionPath)
|
|
3116
|
+
? fs.readFileSync(hooksVersionPath, "utf-8").trim()
|
|
3117
|
+
: "?";
|
|
3118
|
+
console.log(chalk_1.default.green(` ✅ semo-hooks v${hooksVersion} 설치됨`));
|
|
3119
|
+
// 빌드 상태 확인
|
|
3120
|
+
if (!fs.existsSync(hooksDistDir) || !fs.existsSync(hooksIndexJs)) {
|
|
3121
|
+
console.log(chalk_1.default.red(" ❌ 빌드되지 않음 (dist/index.js 없음)"));
|
|
3122
|
+
console.log(chalk_1.default.gray(" 💡 해결: semo hooks enable"));
|
|
3123
|
+
}
|
|
3124
|
+
else {
|
|
3125
|
+
console.log(chalk_1.default.green(" ✅ 빌드 완료 (dist/index.js 존재)"));
|
|
3126
|
+
}
|
|
3127
|
+
// settings.local.json hooks 설정 확인
|
|
3128
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
3129
|
+
const settingsPath = path.join(homeDir, ".claude", "settings.local.json");
|
|
3130
|
+
if (!fs.existsSync(settingsPath)) {
|
|
3131
|
+
console.log(chalk_1.default.yellow(" ⚠️ settings.local.json 없음"));
|
|
3132
|
+
console.log(chalk_1.default.gray(" 💡 해결: semo hooks enable"));
|
|
3133
|
+
}
|
|
3134
|
+
else {
|
|
3135
|
+
try {
|
|
3136
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
3137
|
+
const hooksConfig = settings.hooks;
|
|
3138
|
+
if (!hooksConfig) {
|
|
3139
|
+
console.log(chalk_1.default.yellow(" ⚠️ hooks 설정 없음"));
|
|
3140
|
+
console.log(chalk_1.default.gray(" 💡 해결: semo hooks enable"));
|
|
3141
|
+
}
|
|
3142
|
+
else {
|
|
3143
|
+
const requiredHooks = ["SessionStart", "UserPromptSubmit", "Stop", "SessionEnd"];
|
|
3144
|
+
const missingHooks = [];
|
|
3145
|
+
const invalidPathHooks = [];
|
|
3146
|
+
for (const hookName of requiredHooks) {
|
|
3147
|
+
const hookArray = hooksConfig[hookName];
|
|
3148
|
+
if (!hookArray || !Array.isArray(hookArray) || hookArray.length === 0) {
|
|
3149
|
+
missingHooks.push(hookName);
|
|
3150
|
+
}
|
|
3151
|
+
else {
|
|
3152
|
+
// 경로 검증
|
|
3153
|
+
const hookEntry = hookArray[0];
|
|
3154
|
+
const innerHooks = hookEntry?.hooks;
|
|
3155
|
+
if (innerHooks && Array.isArray(innerHooks) && innerHooks.length > 0) {
|
|
3156
|
+
const command = innerHooks[0]?.command || "";
|
|
3157
|
+
// 현재 프로젝트의 semo-hooks 경로와 비교
|
|
3158
|
+
if (!command.includes(hooksDir) && !command.includes("semo-hooks")) {
|
|
3159
|
+
invalidPathHooks.push(hookName);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
if (missingHooks.length > 0) {
|
|
3165
|
+
console.log(chalk_1.default.yellow(` ⚠️ 누락된 hooks: ${missingHooks.join(", ")}`));
|
|
3166
|
+
console.log(chalk_1.default.gray(" 💡 해결: semo hooks enable"));
|
|
3167
|
+
}
|
|
3168
|
+
else if (invalidPathHooks.length > 0) {
|
|
3169
|
+
console.log(chalk_1.default.yellow(` ⚠️ 경로 불일치: ${invalidPathHooks.join(", ")}`));
|
|
3170
|
+
console.log(chalk_1.default.gray(" 💡 해결: semo hooks enable (다른 프로젝트 설정 감지)"));
|
|
3171
|
+
}
|
|
3172
|
+
else {
|
|
3173
|
+
console.log(chalk_1.default.green(" ✅ hooks 설정 완료 (4개 hook 등록됨)"));
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
catch {
|
|
3178
|
+
console.log(chalk_1.default.red(" ❌ settings.local.json 파싱 오류"));
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
// 5. 전체 설치 검증
|
|
3183
|
+
console.log(chalk_1.default.cyan("\n5. 전체 설치 검증"));
|
|
3040
3184
|
const verificationResult = verifyInstallation(cwd, []);
|
|
3041
3185
|
if (verificationResult.success) {
|
|
3042
3186
|
console.log(chalk_1.default.green(" ✅ 설치 상태 정상"));
|