@team-semicolon/semo-cli 3.0.23 → 3.0.27
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 +286 -69
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -166,6 +166,7 @@ async function showVersionComparison(cwd) {
|
|
|
166
166
|
local: currentCliVersion,
|
|
167
167
|
remote: latestCliVersion,
|
|
168
168
|
needsUpdate: latestCliVersion ? isVersionLower(currentCliVersion, latestCliVersion) : false,
|
|
169
|
+
level: 0,
|
|
169
170
|
});
|
|
170
171
|
// semo-core (루트 또는 semo-system 내부)
|
|
171
172
|
const corePathRoot = path.join(cwd, "semo-core", "VERSION");
|
|
@@ -179,6 +180,7 @@ async function showVersionComparison(cwd) {
|
|
|
179
180
|
local: localCore,
|
|
180
181
|
remote: remoteCore,
|
|
181
182
|
needsUpdate: remoteCore ? isVersionLower(localCore, remoteCore) : false,
|
|
183
|
+
level: 0,
|
|
182
184
|
});
|
|
183
185
|
}
|
|
184
186
|
// semo-skills (루트 또는 semo-system 내부)
|
|
@@ -193,11 +195,51 @@ async function showVersionComparison(cwd) {
|
|
|
193
195
|
local: localSkills,
|
|
194
196
|
remote: remoteSkills,
|
|
195
197
|
needsUpdate: remoteSkills ? isVersionLower(localSkills, remoteSkills) : false,
|
|
198
|
+
level: 0,
|
|
196
199
|
});
|
|
197
200
|
}
|
|
198
|
-
//
|
|
201
|
+
// 그룹 패키지 (eng, biz, ops) 및 하위 Extension - semo-system 내부
|
|
202
|
+
// 그룹별로 묶어서 계층 구조로 출력
|
|
199
203
|
if (hasSemoSystem) {
|
|
200
|
-
for (const
|
|
204
|
+
for (const group of PACKAGE_GROUPS) {
|
|
205
|
+
const groupVersionPath = path.join(semoSystemDir, group, "VERSION");
|
|
206
|
+
const hasGroupVersion = fs.existsSync(groupVersionPath);
|
|
207
|
+
// 해당 그룹의 하위 패키지 찾기
|
|
208
|
+
const groupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => key.startsWith(`${group}/`));
|
|
209
|
+
const installedGroupExtensions = groupExtensions.filter(key => fs.existsSync(path.join(semoSystemDir, key, "VERSION")));
|
|
210
|
+
// 그룹 버전이 있거나 하위 패키지가 설치된 경우에만 표시
|
|
211
|
+
if (hasGroupVersion || installedGroupExtensions.length > 0) {
|
|
212
|
+
// 그룹 패키지 버전 추가
|
|
213
|
+
if (hasGroupVersion) {
|
|
214
|
+
const localGroup = fs.readFileSync(groupVersionPath, "utf-8").trim();
|
|
215
|
+
const remoteGroup = await getRemotePackageVersion(group);
|
|
216
|
+
versionInfos.push({
|
|
217
|
+
name: group,
|
|
218
|
+
local: localGroup,
|
|
219
|
+
remote: remoteGroup,
|
|
220
|
+
needsUpdate: remoteGroup ? isVersionLower(localGroup, remoteGroup) : false,
|
|
221
|
+
level: 1,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// 하위 Extension 패키지들 추가
|
|
225
|
+
for (const key of installedGroupExtensions) {
|
|
226
|
+
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
227
|
+
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
228
|
+
const remoteExt = await getRemotePackageVersion(key);
|
|
229
|
+
versionInfos.push({
|
|
230
|
+
name: key,
|
|
231
|
+
local: localExt,
|
|
232
|
+
remote: remoteExt,
|
|
233
|
+
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
234
|
+
level: 2,
|
|
235
|
+
group: group,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// 그룹에 속하지 않는 Extension (meta 등)
|
|
241
|
+
const nonGroupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => !PACKAGE_GROUPS.some(g => key.startsWith(`${g}/`)));
|
|
242
|
+
for (const key of nonGroupExtensions) {
|
|
201
243
|
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
202
244
|
if (fs.existsSync(extVersionPath)) {
|
|
203
245
|
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
@@ -207,40 +249,61 @@ async function showVersionComparison(cwd) {
|
|
|
207
249
|
local: localExt,
|
|
208
250
|
remote: remoteExt,
|
|
209
251
|
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
252
|
+
level: 1,
|
|
210
253
|
});
|
|
211
254
|
}
|
|
212
255
|
}
|
|
213
256
|
}
|
|
214
|
-
// packages/ 디렉토리의 설치된 패키지들 (로컬 버전만 표시)
|
|
257
|
+
// packages/ 디렉토리의 설치된 패키지들 (로컬 버전만 표시) - 개발 환경용
|
|
215
258
|
const packagesDir = path.join(cwd, "packages");
|
|
216
259
|
if (fs.existsSync(packagesDir)) {
|
|
217
|
-
// 패키지
|
|
218
|
-
const
|
|
219
|
-
"packages/core": "core",
|
|
220
|
-
"packages/meta": "meta",
|
|
221
|
-
"packages/eng
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"packages/
|
|
231
|
-
|
|
260
|
+
// 그룹별 패키지 매핑
|
|
261
|
+
const packageGroups = {
|
|
262
|
+
"packages/core": { level: 0, packages: [{ name: "packages/core", path: "core" }] },
|
|
263
|
+
"packages/meta": { level: 0, packages: [{ name: "packages/meta", path: "meta" }] },
|
|
264
|
+
"packages/eng": {
|
|
265
|
+
level: 1,
|
|
266
|
+
packages: [
|
|
267
|
+
{ name: "packages/eng/nextjs", path: "eng/nextjs" },
|
|
268
|
+
{ name: "packages/eng/spring", path: "eng/spring" },
|
|
269
|
+
{ name: "packages/eng/ms", path: "eng/ms" },
|
|
270
|
+
{ name: "packages/eng/infra", path: "eng/infra" },
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
"packages/biz": {
|
|
274
|
+
level: 1,
|
|
275
|
+
packages: [
|
|
276
|
+
{ name: "packages/biz/discovery", path: "biz/discovery" },
|
|
277
|
+
{ name: "packages/biz/management", path: "biz/management" },
|
|
278
|
+
{ name: "packages/biz/design", path: "biz/design" },
|
|
279
|
+
{ name: "packages/biz/poc", path: "biz/poc" },
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
"packages/ops": {
|
|
283
|
+
level: 1,
|
|
284
|
+
packages: [
|
|
285
|
+
{ name: "packages/ops/qa", path: "ops/qa" },
|
|
286
|
+
{ name: "packages/ops/monitor", path: "ops/monitor" },
|
|
287
|
+
{ name: "packages/ops/improve", path: "ops/improve" },
|
|
288
|
+
],
|
|
289
|
+
},
|
|
232
290
|
};
|
|
233
|
-
for (const [
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
291
|
+
for (const [groupKey, groupData] of Object.entries(packageGroups)) {
|
|
292
|
+
for (const pkg of groupData.packages) {
|
|
293
|
+
const pkgVersionPath = path.join(packagesDir, pkg.path, "VERSION");
|
|
294
|
+
if (fs.existsSync(pkgVersionPath)) {
|
|
295
|
+
const localPkg = fs.readFileSync(pkgVersionPath, "utf-8").trim();
|
|
296
|
+
const remotePkg = await getRemotePackageVersion(`packages/${pkg.path}`);
|
|
297
|
+
const isSubPackage = pkg.path.includes("/");
|
|
298
|
+
versionInfos.push({
|
|
299
|
+
name: pkg.name,
|
|
300
|
+
local: localPkg,
|
|
301
|
+
remote: remotePkg,
|
|
302
|
+
needsUpdate: remotePkg ? isVersionLower(localPkg, remotePkg) : false,
|
|
303
|
+
level: isSubPackage ? 2 : groupData.level,
|
|
304
|
+
group: isSubPackage ? pkg.path.split("/")[0] : undefined,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
244
307
|
}
|
|
245
308
|
}
|
|
246
309
|
}
|
|
@@ -251,7 +314,20 @@ async function showVersionComparison(cwd) {
|
|
|
251
314
|
console.log(chalk_1.default.gray(" │ 패키지 │ 설치됨 │ 최신 │ 상태 │"));
|
|
252
315
|
console.log(chalk_1.default.gray(" ├────────────────────────┼──────────┼──────────┼────────┤"));
|
|
253
316
|
for (const info of versionInfos) {
|
|
254
|
-
|
|
317
|
+
// 계층 구조 표시를 위한 접두사
|
|
318
|
+
let prefix = "";
|
|
319
|
+
let displayName = info.name;
|
|
320
|
+
if (info.level === 1) {
|
|
321
|
+
// 그룹 패키지 (eng, biz, ops)
|
|
322
|
+
prefix = "📦 ";
|
|
323
|
+
}
|
|
324
|
+
else if (info.level === 2) {
|
|
325
|
+
// 하위 패키지
|
|
326
|
+
prefix = " └─ ";
|
|
327
|
+
// 그룹명 제거하고 하위 이름만 표시 (예: biz/discovery → discovery)
|
|
328
|
+
displayName = info.name.includes("/") ? info.name.split("/").pop() || info.name : info.name;
|
|
329
|
+
}
|
|
330
|
+
const name = (prefix + displayName).padEnd(22);
|
|
255
331
|
const local = (info.local || "-").padEnd(8);
|
|
256
332
|
const remote = (info.remote || "-").padEnd(8);
|
|
257
333
|
const status = info.needsUpdate
|
|
@@ -464,6 +540,7 @@ async function showVersionInfo() {
|
|
|
464
540
|
local: VERSION,
|
|
465
541
|
remote: latestCliVersion,
|
|
466
542
|
needsUpdate: latestCliVersion ? isVersionLower(VERSION, latestCliVersion) : false,
|
|
543
|
+
level: 0,
|
|
467
544
|
});
|
|
468
545
|
// 2. semo-core 버전 (루트 또는 semo-system 내부)
|
|
469
546
|
const corePathRoot = path.join(cwd, "semo-core", "VERSION");
|
|
@@ -477,6 +554,7 @@ async function showVersionInfo() {
|
|
|
477
554
|
local: localCore,
|
|
478
555
|
remote: remoteCore,
|
|
479
556
|
needsUpdate: remoteCore ? isVersionLower(localCore, remoteCore) : false,
|
|
557
|
+
level: 0,
|
|
480
558
|
});
|
|
481
559
|
}
|
|
482
560
|
// 3. semo-skills 버전 (루트 또는 semo-system 내부)
|
|
@@ -491,12 +569,50 @@ async function showVersionInfo() {
|
|
|
491
569
|
local: localSkills,
|
|
492
570
|
remote: remoteSkills,
|
|
493
571
|
needsUpdate: remoteSkills ? isVersionLower(localSkills, remoteSkills) : false,
|
|
572
|
+
level: 0,
|
|
494
573
|
});
|
|
495
574
|
}
|
|
496
|
-
// 4. Extension
|
|
575
|
+
// 4. 그룹 패키지 (eng, biz, ops) 및 하위 Extension - semo-system 내부
|
|
497
576
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
498
577
|
if (fs.existsSync(semoSystemDir)) {
|
|
499
|
-
for (const
|
|
578
|
+
for (const group of PACKAGE_GROUPS) {
|
|
579
|
+
const groupVersionPath = path.join(semoSystemDir, group, "VERSION");
|
|
580
|
+
const hasGroupVersion = fs.existsSync(groupVersionPath);
|
|
581
|
+
// 해당 그룹의 하위 패키지 찾기
|
|
582
|
+
const groupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => key.startsWith(`${group}/`));
|
|
583
|
+
const installedGroupExtensions = groupExtensions.filter(key => fs.existsSync(path.join(semoSystemDir, key, "VERSION")));
|
|
584
|
+
if (hasGroupVersion || installedGroupExtensions.length > 0) {
|
|
585
|
+
// 그룹 패키지 버전 추가
|
|
586
|
+
if (hasGroupVersion) {
|
|
587
|
+
const localGroup = fs.readFileSync(groupVersionPath, "utf-8").trim();
|
|
588
|
+
const remoteGroup = await getRemotePackageVersion(group);
|
|
589
|
+
versionInfos.push({
|
|
590
|
+
name: group,
|
|
591
|
+
local: localGroup,
|
|
592
|
+
remote: remoteGroup,
|
|
593
|
+
needsUpdate: remoteGroup ? isVersionLower(localGroup, remoteGroup) : false,
|
|
594
|
+
level: 1,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
// 하위 Extension 패키지들 추가
|
|
598
|
+
for (const key of installedGroupExtensions) {
|
|
599
|
+
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
600
|
+
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
601
|
+
const remoteExt = await getRemotePackageVersion(key);
|
|
602
|
+
versionInfos.push({
|
|
603
|
+
name: key,
|
|
604
|
+
local: localExt,
|
|
605
|
+
remote: remoteExt,
|
|
606
|
+
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
607
|
+
level: 2,
|
|
608
|
+
group: group,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// 그룹에 속하지 않는 Extension (meta 등)
|
|
614
|
+
const nonGroupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => !PACKAGE_GROUPS.some(g => key.startsWith(`${g}/`)));
|
|
615
|
+
for (const key of nonGroupExtensions) {
|
|
500
616
|
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
501
617
|
if (fs.existsSync(extVersionPath)) {
|
|
502
618
|
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
@@ -506,39 +622,60 @@ async function showVersionInfo() {
|
|
|
506
622
|
local: localExt,
|
|
507
623
|
remote: remoteExt,
|
|
508
624
|
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
625
|
+
level: 1,
|
|
509
626
|
});
|
|
510
627
|
}
|
|
511
628
|
}
|
|
512
629
|
}
|
|
513
|
-
// 5. packages/ 디렉토리의 설치된 패키지들
|
|
630
|
+
// 5. packages/ 디렉토리의 설치된 패키지들 - 개발 환경용
|
|
514
631
|
const packagesDir = path.join(cwd, "packages");
|
|
515
632
|
if (fs.existsSync(packagesDir)) {
|
|
516
|
-
const
|
|
517
|
-
"packages/core": "core",
|
|
518
|
-
"packages/meta": "meta",
|
|
519
|
-
"packages/eng
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
"packages/
|
|
529
|
-
|
|
633
|
+
const packageGroups = {
|
|
634
|
+
"packages/core": { level: 0, packages: [{ name: "packages/core", path: "core" }] },
|
|
635
|
+
"packages/meta": { level: 0, packages: [{ name: "packages/meta", path: "meta" }] },
|
|
636
|
+
"packages/eng": {
|
|
637
|
+
level: 1,
|
|
638
|
+
packages: [
|
|
639
|
+
{ name: "packages/eng/nextjs", path: "eng/nextjs" },
|
|
640
|
+
{ name: "packages/eng/spring", path: "eng/spring" },
|
|
641
|
+
{ name: "packages/eng/ms", path: "eng/ms" },
|
|
642
|
+
{ name: "packages/eng/infra", path: "eng/infra" },
|
|
643
|
+
],
|
|
644
|
+
},
|
|
645
|
+
"packages/biz": {
|
|
646
|
+
level: 1,
|
|
647
|
+
packages: [
|
|
648
|
+
{ name: "packages/biz/discovery", path: "biz/discovery" },
|
|
649
|
+
{ name: "packages/biz/management", path: "biz/management" },
|
|
650
|
+
{ name: "packages/biz/design", path: "biz/design" },
|
|
651
|
+
{ name: "packages/biz/poc", path: "biz/poc" },
|
|
652
|
+
],
|
|
653
|
+
},
|
|
654
|
+
"packages/ops": {
|
|
655
|
+
level: 1,
|
|
656
|
+
packages: [
|
|
657
|
+
{ name: "packages/ops/qa", path: "ops/qa" },
|
|
658
|
+
{ name: "packages/ops/monitor", path: "ops/monitor" },
|
|
659
|
+
{ name: "packages/ops/improve", path: "ops/improve" },
|
|
660
|
+
],
|
|
661
|
+
},
|
|
530
662
|
};
|
|
531
|
-
for (const [
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
663
|
+
for (const [, groupData] of Object.entries(packageGroups)) {
|
|
664
|
+
for (const pkg of groupData.packages) {
|
|
665
|
+
const pkgVersionPath = path.join(packagesDir, pkg.path, "VERSION");
|
|
666
|
+
if (fs.existsSync(pkgVersionPath)) {
|
|
667
|
+
const localPkg = fs.readFileSync(pkgVersionPath, "utf-8").trim();
|
|
668
|
+
const remotePkg = await getRemotePackageVersion(`packages/${pkg.path}`);
|
|
669
|
+
const isSubPackage = pkg.path.includes("/");
|
|
670
|
+
versionInfos.push({
|
|
671
|
+
name: pkg.name,
|
|
672
|
+
local: localPkg,
|
|
673
|
+
remote: remotePkg,
|
|
674
|
+
needsUpdate: remotePkg ? isVersionLower(localPkg, remotePkg) : false,
|
|
675
|
+
level: isSubPackage ? 2 : groupData.level,
|
|
676
|
+
group: isSubPackage ? pkg.path.split("/")[0] : undefined,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
542
679
|
}
|
|
543
680
|
}
|
|
544
681
|
}
|
|
@@ -567,7 +704,20 @@ async function showVersionInfo() {
|
|
|
567
704
|
console.log(chalk_1.default.gray(" │ 패키지 │ 설치됨 │ 최신 │ 상태 │"));
|
|
568
705
|
console.log(chalk_1.default.gray(" ├────────────────────────┼──────────┼──────────┼────────┤"));
|
|
569
706
|
for (const info of versionInfos) {
|
|
570
|
-
|
|
707
|
+
// 계층 구조 표시를 위한 접두사
|
|
708
|
+
let prefix = "";
|
|
709
|
+
let displayName = info.name;
|
|
710
|
+
if (info.level === 1) {
|
|
711
|
+
// 그룹 패키지 (eng, biz, ops)
|
|
712
|
+
prefix = "📦 ";
|
|
713
|
+
}
|
|
714
|
+
else if (info.level === 2) {
|
|
715
|
+
// 하위 패키지
|
|
716
|
+
prefix = " └─ ";
|
|
717
|
+
// 그룹명 제거하고 하위 이름만 표시
|
|
718
|
+
displayName = info.name.includes("/") ? info.name.split("/").pop() || info.name : info.name;
|
|
719
|
+
}
|
|
720
|
+
const name = (prefix + displayName).padEnd(22);
|
|
571
721
|
const local = (info.local || "-").padEnd(8);
|
|
572
722
|
const remote = (info.remote || "-").padEnd(8);
|
|
573
723
|
const status = info.needsUpdate ? "⬆ 업데이트" : "✓ 최신 ";
|
|
@@ -1727,6 +1877,7 @@ function updateGitignore(cwd) {
|
|
|
1727
1877
|
.claude/*
|
|
1728
1878
|
!.claude/memory/
|
|
1729
1879
|
!.claude/memory/**
|
|
1880
|
+
semo-system/
|
|
1730
1881
|
`;
|
|
1731
1882
|
if (fs.existsSync(gitignorePath)) {
|
|
1732
1883
|
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
@@ -1913,7 +2064,68 @@ _SEMO 기본 규칙의 예외 사항을 여기에 추가하세요._
|
|
|
1913
2064
|
console.log(chalk_1.default.green("✓ .claude/memory/rules/project-specific.md 생성됨"));
|
|
1914
2065
|
}
|
|
1915
2066
|
}
|
|
1916
|
-
// === CLAUDE.md
|
|
2067
|
+
// === CLAUDE.md 중복 섹션 감지 ===
|
|
2068
|
+
// "Core Rules (상속)" 패턴을 사용하는 Extension은 고유 섹션만 추출
|
|
2069
|
+
function extractUniqueContent(content, pkgName) {
|
|
2070
|
+
// "Core Rules (상속)" 섹션이 있는지 확인
|
|
2071
|
+
const hasCoreRulesRef = /## Core Rules \(상속\)/i.test(content);
|
|
2072
|
+
if (hasCoreRulesRef) {
|
|
2073
|
+
// "고유:" 패턴이 포함된 섹션만 추출
|
|
2074
|
+
const uniqueSectionPattern = /## [^\n]* 고유:/g;
|
|
2075
|
+
const sections = [];
|
|
2076
|
+
// 섹션별로 분리
|
|
2077
|
+
const allSections = content.split(/(?=^## )/gm);
|
|
2078
|
+
for (const section of allSections) {
|
|
2079
|
+
// "고유:" 키워드가 있는 섹션만 포함
|
|
2080
|
+
if (/고유:/i.test(section)) {
|
|
2081
|
+
sections.push(section.trim());
|
|
2082
|
+
}
|
|
2083
|
+
// References 섹션도 포함
|
|
2084
|
+
if (/^## References/i.test(section)) {
|
|
2085
|
+
sections.push(section.trim());
|
|
2086
|
+
}
|
|
2087
|
+
// 패키지 구조, Keywords 섹션 포함
|
|
2088
|
+
if (/^## (패키지 구조|Keywords|Routing)/i.test(section)) {
|
|
2089
|
+
sections.push(section.trim());
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (sections.length > 0) {
|
|
2093
|
+
return sections.join("\n\n");
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
// 공유 규칙 패턴 감지 (이 패턴이 있으면 중복 가능성 높음)
|
|
2097
|
+
const sharedPatterns = [
|
|
2098
|
+
/Orchestrator-First Policy/i,
|
|
2099
|
+
/Quality Gate|Pre-Commit/i,
|
|
2100
|
+
/세션 초기화|Session Init/i,
|
|
2101
|
+
/버저닝 규칙|Versioning/i,
|
|
2102
|
+
/패키지 접두사|PREFIX_ROUTING/i,
|
|
2103
|
+
/SEMO Core 필수 참조/i,
|
|
2104
|
+
/NON-NEGOTIABLE.*Orchestrator/i,
|
|
2105
|
+
];
|
|
2106
|
+
// 공유 패턴이 많이 발견되면 간소화된 참조만 반환
|
|
2107
|
+
let sharedPatternCount = 0;
|
|
2108
|
+
for (const pattern of sharedPatterns) {
|
|
2109
|
+
if (pattern.test(content)) {
|
|
2110
|
+
sharedPatternCount++;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
// 3개 이상의 공유 패턴이 발견되면 중복이 많은 것으로 판단
|
|
2114
|
+
if (sharedPatternCount >= 3) {
|
|
2115
|
+
// 기본 헤더와 References만 추출
|
|
2116
|
+
const headerMatch = content.match(/^# .+\n\n>[^\n]+/);
|
|
2117
|
+
const referencesMatch = content.match(/## References[\s\S]*$/);
|
|
2118
|
+
let simplified = headerMatch ? headerMatch[0] : `# ${pkgName}`;
|
|
2119
|
+
simplified += "\n\n> Core Rules는 semo-core/principles/를 참조합니다.";
|
|
2120
|
+
if (referencesMatch) {
|
|
2121
|
+
simplified += "\n\n" + referencesMatch[0];
|
|
2122
|
+
}
|
|
2123
|
+
return simplified;
|
|
2124
|
+
}
|
|
2125
|
+
// 그 외에는 전체 내용 반환
|
|
2126
|
+
return content;
|
|
2127
|
+
}
|
|
2128
|
+
// === CLAUDE.md 생성 (패키지 CLAUDE.md 병합 지원 + 중복 제거) ===
|
|
1917
2129
|
async function setupClaudeMd(cwd, extensions, force) {
|
|
1918
2130
|
console.log(chalk_1.default.cyan("\n📄 CLAUDE.md 설정"));
|
|
1919
2131
|
const claudeMdPath = path.join(cwd, ".claude", "CLAUDE.md");
|
|
@@ -1932,31 +2144,36 @@ async function setupClaudeMd(cwd, extensions, force) {
|
|
|
1932
2144
|
let packageClaudeMdSections = "";
|
|
1933
2145
|
// 1. 설치된 패키지에서 그룹 추출 (중복 제거)
|
|
1934
2146
|
const installedGroups = [...new Set(extensions.map(pkg => pkg.split("/")[0]).filter(g => PACKAGE_GROUPS.includes(g)))];
|
|
1935
|
-
// 2. 그룹 레벨 CLAUDE.md 먼저 병합 (biz, eng, ops)
|
|
2147
|
+
// 2. 그룹 레벨 CLAUDE.md 먼저 병합 (biz, eng, ops) - 중복 제거 적용
|
|
1936
2148
|
for (const group of installedGroups) {
|
|
1937
2149
|
const groupClaudeMdPath = path.join(semoSystemDir, group, "CLAUDE.md");
|
|
1938
2150
|
if (fs.existsSync(groupClaudeMdPath)) {
|
|
1939
2151
|
const groupContent = fs.readFileSync(groupClaudeMdPath, "utf-8");
|
|
1940
|
-
//
|
|
1941
|
-
const
|
|
2152
|
+
// 중복 제거 후 고유 콘텐츠만 추출
|
|
2153
|
+
const uniqueContent = extractUniqueContent(groupContent, group);
|
|
2154
|
+
// 헤더 레벨 조정 (# → ##, ## → ###)
|
|
2155
|
+
const adjustedContent = uniqueContent
|
|
1942
2156
|
.replace(/^# /gm, "## ")
|
|
1943
2157
|
.replace(/^## /gm, "### ")
|
|
1944
2158
|
.replace(/^### /gm, "#### ");
|
|
1945
2159
|
packageClaudeMdSections += `\n\n---\n\n${adjustedContent}`;
|
|
1946
|
-
console.log(chalk_1.default.green(` + ${group}/ 그룹 CLAUDE.md
|
|
2160
|
+
console.log(chalk_1.default.green(` + ${group}/ 그룹 CLAUDE.md 병합됨 (고유 섹션만)`));
|
|
1947
2161
|
}
|
|
1948
2162
|
}
|
|
1949
|
-
// 3. 개별 패키지 CLAUDE.md 병합
|
|
2163
|
+
// 3. 개별 패키지 CLAUDE.md 병합 - 중복 제거 적용
|
|
1950
2164
|
for (const pkg of extensions) {
|
|
1951
2165
|
const pkgClaudeMdPath = path.join(semoSystemDir, pkg, "CLAUDE.md");
|
|
1952
2166
|
if (fs.existsSync(pkgClaudeMdPath)) {
|
|
1953
2167
|
const pkgContent = fs.readFileSync(pkgClaudeMdPath, "utf-8");
|
|
1954
|
-
|
|
1955
|
-
|
|
2168
|
+
const pkgName = EXTENSION_PACKAGES[pkg]?.name || pkg;
|
|
2169
|
+
// 중복 제거 후 고유 콘텐츠만 추출
|
|
2170
|
+
const uniqueContent = extractUniqueContent(pkgContent, pkgName);
|
|
2171
|
+
// 헤더 레벨 조정
|
|
2172
|
+
const adjustedContent = uniqueContent
|
|
1956
2173
|
.replace(/^# /gm, "### ")
|
|
1957
2174
|
.replace(/^## /gm, "#### ");
|
|
1958
|
-
packageClaudeMdSections += `\n\n---\n\n## ${
|
|
1959
|
-
console.log(chalk_1.default.gray(` + ${pkg}/CLAUDE.md
|
|
2175
|
+
packageClaudeMdSections += `\n\n---\n\n## ${pkgName} 패키지 컨텍스트\n\n${adjustedContent}`;
|
|
2176
|
+
console.log(chalk_1.default.gray(` + ${pkg}/CLAUDE.md 병합됨 (고유 섹션만)`));
|
|
1960
2177
|
}
|
|
1961
2178
|
}
|
|
1962
2179
|
// 4. Orchestrator 참조 경로 결정 (Extension 패키지 우선)
|