@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.18.13",
3
+ "version": "0.18.14",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -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
- // 한국어 도메인 키워드 영문 slug 매핑
1014
- const KOREAN_DOMAIN_MAP: Record<string, string> = {
1015
- "채팅": "chat", "메시지": "message", "사용자": "user", "유저": "user",
1016
- "인증": "auth", "로그인": "login", "로그아웃": "logout", "회원가입": "signup",
1017
- "결제": "payment", "주문": "order", "상품": "product", "장바구니": "cart",
1018
- "알림": "notification", "푸시": "push",
1019
- "게시글": "post", "게시판": "board", "댓글": "comment", "좋아요": "like",
1020
- "검색": "search", "필터": "filter", "정렬": "sort",
1021
- "파일": "file", "업로드": "upload", "다운로드": "download", "이미지": "image",
1022
- "대시보드": "dashboard", "통계": "stats", "분석": "analytics",
1023
- "설정": "settings", "프로필": "profile", "계정": "account",
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] || [];