@mandujs/core 0.18.13 → 0.18.14
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/package.json +1 -1
- package/src/guard/negotiation.ts +28 -52
package/package.json
CHANGED
package/src/guard/negotiation.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface NegotiationRequest {
|
|
|
37
37
|
/** 구현하려는 기능의 의도 */
|
|
38
38
|
intent: string;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* 영문 feature name slug (에이전트가 제공).
|
|
42
|
+
* 제공되면 extractFeatureName()을 건너뛰고 이 값을 그대로 사용.
|
|
43
|
+
* 예: "chat", "user-auth", "payment", "file-upload"
|
|
44
|
+
*/
|
|
45
|
+
featureName?: string;
|
|
46
|
+
|
|
40
47
|
/** 요구사항 목록 */
|
|
41
48
|
requirements?: string[];
|
|
42
49
|
|
|
@@ -1010,24 +1017,26 @@ function toCamelCase(str: string): string {
|
|
|
1010
1017
|
.replace(/^(.)/, (_, c) => c.toLowerCase());
|
|
1011
1018
|
}
|
|
1012
1019
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
"팀": "team", "프로젝트": "project", "태스크": "task", "일정": "schedule",
|
|
1025
|
-
"컴포넌트": "component", "레이아웃": "layout", "네비게이션": "navigation",
|
|
1026
|
-
"실시간": "realtime", "스트리밍": "streaming", "웹소켓": "websocket",
|
|
1027
|
-
};
|
|
1020
|
+
/** 에이전트 제공 slug 정리: lowercase kebab-case, 빈 문자열이면 falsy */
|
|
1021
|
+
function sanitizeSlug(slug: string | undefined): string {
|
|
1022
|
+
if (!slug) return "";
|
|
1023
|
+
return slug
|
|
1024
|
+
.trim()
|
|
1025
|
+
.toLowerCase()
|
|
1026
|
+
.replace(/\s+/g, "-")
|
|
1027
|
+
.replace(/[^a-z0-9-]/g, "")
|
|
1028
|
+
.replace(/-{2,}/g, "-")
|
|
1029
|
+
.replace(/^-+|-+$/g, "");
|
|
1030
|
+
}
|
|
1028
1031
|
|
|
1032
|
+
/**
|
|
1033
|
+
* intent에서 feature name 추출 (featureName 미제공 시 fallback)
|
|
1034
|
+
*
|
|
1035
|
+
* MCP 에이전트는 항상 featureName을 영문으로 제공하므로,
|
|
1036
|
+
* 이 함수는 CLI/프로그래밍 직접 호출 시 fallback으로만 사용.
|
|
1037
|
+
*/
|
|
1029
1038
|
function extractFeatureName(intent: string): string {
|
|
1030
|
-
// 1. 영문 패턴
|
|
1039
|
+
// 1. 영문 패턴 추출
|
|
1031
1040
|
const englishPatterns = [
|
|
1032
1041
|
/(?:add|implement|create|build)\s+(.+)/i,
|
|
1033
1042
|
/(.+?)\s+(?:feature|system|module|service)/i,
|
|
@@ -1046,46 +1055,13 @@ function extractFeatureName(intent: string): string {
|
|
|
1046
1055
|
}
|
|
1047
1056
|
}
|
|
1048
1057
|
|
|
1049
|
-
// 2.
|
|
1050
|
-
const koreanPatterns = [
|
|
1051
|
-
/(?:추가|구현|만들|개발|작성|생성)(?:어|해|하)[줘요]?\s*[:\-]?\s*(.+)/,
|
|
1052
|
-
/(.+?)\s*(?:기능|시스템|모듈|서비스|페이지|컴포넌트)\s*(?:추가|구현|만들|개발)?/,
|
|
1053
|
-
/(.+?)\s*(?:을|를|의|에)\s/,
|
|
1054
|
-
];
|
|
1055
|
-
|
|
1056
|
-
for (const pattern of koreanPatterns) {
|
|
1057
|
-
const match = intent.match(pattern);
|
|
1058
|
-
if (match) {
|
|
1059
|
-
const captured = match[1].trim();
|
|
1060
|
-
// 캡처된 텍스트에서 도메인 키워드 매칭
|
|
1061
|
-
const slug = koreanToSlug(captured);
|
|
1062
|
-
if (slug) return slug;
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
// 3. intent 전체에서 도메인 키워드 직접 탐색
|
|
1067
|
-
const slug = koreanToSlug(intent);
|
|
1068
|
-
if (slug) return slug;
|
|
1069
|
-
|
|
1070
|
-
// 4. intent에서 영문 단어 추출 시도
|
|
1058
|
+
// 2. intent 내 영문 단어 추출
|
|
1071
1059
|
const englishWord = intent.match(/[a-z][a-z0-9-]{2,}/i)?.[0]?.toLowerCase();
|
|
1072
1060
|
if (englishWord) return englishWord;
|
|
1073
1061
|
|
|
1074
1062
|
return "feature";
|
|
1075
1063
|
}
|
|
1076
1064
|
|
|
1077
|
-
function koreanToSlug(text: string): string {
|
|
1078
|
-
const matches: string[] = [];
|
|
1079
|
-
for (const [korean, english] of Object.entries(KOREAN_DOMAIN_MAP)) {
|
|
1080
|
-
if (text.includes(korean)) {
|
|
1081
|
-
matches.push(english);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
if (matches.length === 0) return "";
|
|
1085
|
-
// 여러 키워드 매칭 시 하이폰으로 연결 (최대 2개)
|
|
1086
|
-
return matches.slice(0, 2).join("-");
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
1065
|
/**
|
|
1090
1066
|
* 프리셋에 따라 구조를 조정
|
|
1091
1067
|
* FSD, Clean, Hexagonal 등 프리셋별 레이어 매핑 적용
|
|
@@ -1181,8 +1157,8 @@ export async function negotiate(
|
|
|
1181
1157
|
// 1. 카테고리 감지
|
|
1182
1158
|
const category = request.category || detectCategory(intent);
|
|
1183
1159
|
|
|
1184
|
-
// 2. 기능
|
|
1185
|
-
const featureName = extractFeatureName(intent);
|
|
1160
|
+
// 2. 기능 이름: 에이전트 제공 값 우선, 없으면 자동 추출
|
|
1161
|
+
const featureName = sanitizeSlug(request.featureName) || extractFeatureName(intent);
|
|
1186
1162
|
|
|
1187
1163
|
// 3. 관련 결정 검색
|
|
1188
1164
|
const categoryTags = CATEGORY_KEYWORDS[category] || [];
|