@axboot-mcp/mcp-server 1.0.0

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.
Files changed (78) hide show
  1. package/CLAUDE.md +119 -0
  2. package/MCP_TOOL_PLAN.md +710 -0
  3. package/MCP_USAGE.md +914 -0
  4. package/README.md +168 -0
  5. package/REPOSITORY_CONVENTIONS.md +250 -0
  6. package/SEARCH_PARAMS_MCP_TOOL_COMPLETE_PLAN.md +646 -0
  7. package/SEARCH_PARAMS_PLAN.md +2570 -0
  8. package/STORE_PATTERNS.md +1178 -0
  9. package/debug-dto.js +72 -0
  10. package/generate-banner-store.js +62 -0
  11. package/generation-plan.json +2176 -0
  12. package/generation-results.json +1817 -0
  13. package/package.json +45 -0
  14. package/scripts/batch-generate-all.js +159 -0
  15. package/scripts/batch-generate-mcp.js +329 -0
  16. package/scripts/batch-generate-stores-v2.js +272 -0
  17. package/scripts/batch-generate-stores.js +179 -0
  18. package/scripts/batch-plan.json +3810 -0
  19. package/scripts/batch-process.py +90 -0
  20. package/scripts/batch-regenerate.js +356 -0
  21. package/scripts/direct-generate.js +227 -0
  22. package/scripts/execute-batches.js +1911 -0
  23. package/scripts/generate-all-stores.js +144 -0
  24. package/scripts/generate-stores-mcp.js +161 -0
  25. package/scripts/generate-stores-v2.js +450 -0
  26. package/scripts/generate-stores-v3.js +412 -0
  27. package/scripts/generate-stores-v4.js +521 -0
  28. package/scripts/generate-stores.js +382 -0
  29. package/scripts/repos-to-process.json +1899 -0
  30. package/src/config/nh-layout-patterns.ts +166 -0
  31. package/src/docs/HOOK_GENERATION_PLAN.md +2226 -0
  32. package/src/docs/NH_STORE_PATTERNS.md +297 -0
  33. package/src/docs/README.md +216 -0
  34. package/src/docs/index.ts +28 -0
  35. package/src/docs/loader.ts +568 -0
  36. package/src/docs/patterns.json +419 -0
  37. package/src/docs/practical-examples.md +732 -0
  38. package/src/docs/quick-start.md +257 -0
  39. package/src/docs/requirements-analysis-guide.md +364 -0
  40. package/src/docs/rules.json +321 -0
  41. package/src/docs/store-pattern-analysis.md +664 -0
  42. package/src/docs/store-patterns-rules.md +1168 -0
  43. package/src/docs/store-patterns-usage-guide.md +1835 -0
  44. package/src/docs/troubleshooting.md +544 -0
  45. package/src/docs/type-selection-guide.md +572 -0
  46. package/src/docs//354/202/254/354/232/251/353/262/225/AntD-/354/273/264/355/217/254/353/204/214/355/212/270-/354/202/254/354/232/251/353/262/225.md +1515 -0
  47. package/src/docs//354/202/254/354/232/251/353/262/225/DataGrid-/354/202/254/354/232/251/353/262/225.md +866 -0
  48. package/src/docs//354/202/254/354/232/251/353/262/225/FormItem-/354/202/254/354/232/251/353/262/225.md +903 -0
  49. package/src/docs//354/202/254/354/232/251/353/262/225/FormModal-/354/202/254/354/232/251/353/262/225.md +1155 -0
  50. package/src/docs//354/202/254/354/232/251/353/262/225/MCP-/353/260/224/354/235/264/353/270/214/354/275/224/353/224/251-/352/260/200/354/235/264/353/223/234.md +1133 -0
  51. package/src/docs//354/202/254/354/232/251/353/262/225/MSW-Mock-/353/215/260/354/235/264/355/204/260-/354/202/254/354/232/251/353/262/225.md +579 -0
  52. package/src/docs//354/202/254/354/232/251/353/262/225/Search-/354/273/264/355/217/254/353/204/214/355/212/270-/354/202/254/354/232/251/353/262/225.md +738 -0
  53. package/src/docs//354/202/254/354/232/251/353/262/225/Store-/355/214/250/355/204/264-/354/202/254/354/232/251/353/262/225.md +1135 -0
  54. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/224/353/251/264/352/265/254/354/204/261-/355/203/200/354/236/205/353/263/204-/352/260/234/353/260/234/354/210/234/354/204/234.md +1805 -0
  55. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/224/353/251/264/355/203/200/354/236/205/353/263/204-/352/260/234/353/260/234-/355/224/204/353/241/254/355/224/204/355/212/270-/352/260/200/354/235/264/353/223/234.md +946 -0
  56. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/225/354/236/245/355/231/224/353/251/264/355/203/200/354/236/205/353/263/204-/354/203/201/354/204/270-/355/224/204/353/241/254/355/224/204/355/212/270/352/260/200/354/235/264/353/223/234.md +2422 -0
  57. package/src/features/store-features.ts +232 -0
  58. package/src/handlers/analyze-requirements.ts +403 -0
  59. package/src/handlers/analyze.ts +1373 -0
  60. package/src/handlers/generate-from-requirements.ts +250 -0
  61. package/src/handlers/generate-hook.ts +950 -0
  62. package/src/handlers/generate-interactive.ts +840 -0
  63. package/src/handlers/generate-listdatagrid.ts +521 -0
  64. package/src/handlers/generate-multi-stores.ts +577 -0
  65. package/src/handlers/generate-requirements-from-layout.ts +160 -0
  66. package/src/handlers/generate-search-params.ts +717 -0
  67. package/src/handlers/generate.ts +911 -0
  68. package/src/handlers/list-templates.ts +104 -0
  69. package/src/handlers/scan-metadata.ts +485 -0
  70. package/src/handlers/suggest-layout.ts +326 -0
  71. package/src/index.ts +959 -0
  72. package/src/prompts/search-params.md +793 -0
  73. package/src/templates/index.ts +107 -0
  74. package/src/templates/unified.ts +462 -0
  75. package/store-generation-error-patterns.md +225 -0
  76. package/test/useAgentStore.ts +136 -0
  77. package/test-server.js +78 -0
  78. package/tsconfig.json +20 -0
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Store 피처/태그 정의
3
+ * 각 피처는 State와 Action을 포함
4
+ */
5
+
6
+ export interface StoreFeature {
7
+ name: string;
8
+ description: string;
9
+ keywords: string[]; // 자연어 분석용 키워드
10
+ states: string[];
11
+ actions: string[];
12
+ }
13
+
14
+ export const FEATURES: Record<string, StoreFeature> = {
15
+ // 기본 기능
16
+ LIST: {
17
+ name: '리스트',
18
+ description: '기본 리스트 표시',
19
+ keywords: ['리스트', '목록', '목록보기', '조회', '표시', '나열'],
20
+ states: ['listData', 'listPage', 'listSpinning'],
21
+ actions: ['callListApi', 'changeListPage'],
22
+ },
23
+
24
+ SEARCH: {
25
+ name: '검색',
26
+ description: '검색 기능',
27
+ keywords: ['검색', '찾기', '필터', '서치'],
28
+ states: ['searchValue'],
29
+ actions: ['onSearch'],
30
+ },
31
+
32
+ // 선택 기능
33
+ SELECTED_ITEM: {
34
+ name: '선택 아이템',
35
+ description: '선택된 항목 저장',
36
+ keywords: ['선택', '클릭', '선택된', '하나 선택', '단일 선택'],
37
+ states: ['selectedItem'],
38
+ actions: ['setSelectedItem'],
39
+ },
40
+
41
+ CHECKBOX: {
42
+ name: '체크박스',
43
+ description: '다중 선택 체크박스',
44
+ keywords: ['체크', '체크박스', '다중 선택', '여러개', '복수 선택', 'checkbox'],
45
+ states: ['checkedRowKeys'],
46
+ actions: ['setCheckedRowKeys'],
47
+ },
48
+
49
+ // 상세 영역
50
+ DETAIL_PANEL: {
51
+ name: '상세 영역',
52
+ description: '별도의 상세 표시 영역',
53
+ keywords: ['상세', '디테일', '상세보기', 'detail', '우측 상세', '오른쪽 상세'],
54
+ states: ['detail', 'detailSpinning'],
55
+ actions: ['setDetail'],
56
+ },
57
+
58
+ DETAIL_API: {
59
+ name: '상세 조회 API',
60
+ description: '상세 조회 API 호출',
61
+ keywords: ['상세조회', '상세 api', '디테일 조회', '상세 정보 가져오기'],
62
+ states: ['detailSpinning'],
63
+ actions: ['callDetailApi'],
64
+ },
65
+
66
+ // CRUD 기능
67
+ SAVE: {
68
+ name: '저장',
69
+ description: '저장/수정 기능',
70
+ keywords: ['저장', '수정', '등록', '추가', 'create', 'update', 'save'],
71
+ states: ['saveRequestValue', 'saveSpinning'],
72
+ actions: ['callSaveApi'],
73
+ },
74
+
75
+ DELETE: {
76
+ name: '삭제',
77
+ description: '삭제 기능',
78
+ keywords: ['삭제', '제거', 'delete', 'remove'],
79
+ states: ['deleteSpinning'],
80
+ actions: ['callDeleteApi'],
81
+ },
82
+
83
+ // UI 기능
84
+ MODAL: {
85
+ name: '모달',
86
+ description: '모달/팝업 창',
87
+ keywords: ['모달', '팝업', 'modal', 'popup', '다이얼로그', 'dialog'],
88
+ states: ['modalOpen'],
89
+ actions: ['setModalOpen'],
90
+ },
91
+
92
+ EXCEL_DOWNLOAD: {
93
+ name: '엑셀 다운로드',
94
+ description: '엑셀 다운로드 기능',
95
+ keywords: ['엑셀', 'excel', '다운로드', '내보내기', 'export'],
96
+ states: ['excelSpinning'],
97
+ actions: ['callExcelDownloadApi'],
98
+ },
99
+
100
+ // 특수 패턴
101
+ TREE: {
102
+ name: '트리',
103
+ description: '트리 구조',
104
+ keywords: ['트리', 'tree', '계층', '폴더', '카테고리', '메뉴'],
105
+ states: ['treeData', 'treeSpinning', 'expandedKeys'],
106
+ actions: ['callTreeApi', 'setExpandedKeys'],
107
+ },
108
+
109
+ REORDER: {
110
+ name: '재정렬',
111
+ description: '드래그 앤 드롭 재정렬',
112
+ keywords: ['순서', '재정렬', '드래그', '정렬', '위치 변경', 'reorder', 'drag', 'drop'],
113
+ states: ['isReordered'],
114
+ actions: ['onDrop'],
115
+ },
116
+
117
+ DASHBOARD: {
118
+ name: '대시보드',
119
+ description: '대시보드 요약 정보',
120
+ keywords: ['대시보드', '요약', 'summary', '통계', '집계'],
121
+ states: ['summaryData', 'summarySpinning'],
122
+ actions: ['callSummaryApi'],
123
+ },
124
+
125
+ TAB: {
126
+ name: '탭 전환',
127
+ description: '탭 전환 기능',
128
+ keywords: ['탭', 'tab', '전환'],
129
+ states: ['activeTabKey'],
130
+ actions: ['setActiveTabKey'],
131
+ },
132
+ };
133
+
134
+ /**
135
+ * Type별 피처 조합
136
+ */
137
+ export const TYPE_FEATURES: Record<number, string[]> = {
138
+ 1: ['LIST', 'SEARCH', 'SELECTED_ITEM', 'SAVE', 'CHECKBOX'],
139
+ 2: ['LIST', 'SEARCH', 'SELECTED_ITEM', 'DETAIL_PANEL', 'MODAL', 'DELETE', 'CHECKBOX'],
140
+ 3: ['TREE', 'SAVE'],
141
+ 4: ['LIST', 'DASHBOARD', 'TAB'],
142
+ 5: ['LIST'],
143
+ 6: ['LIST', 'REORDER'],
144
+ 7: ['LIST', 'SEARCH', 'SELECTED_ITEM', 'DETAIL_PANEL', 'MODAL', 'DELETE', 'SAVE', 'EXCEL_DOWNLOAD', 'CHECKBOX'],
145
+ 8: ['LIST', 'SELECTED_ITEM', 'CHECKBOX', 'EXCEL_DOWNLOAD'],
146
+ 9: ['LIST', 'SELECTED_ITEM', 'DETAIL_PANEL', 'MODAL', 'DETAIL_API'],
147
+ 10: ['LIST', 'SELECTED_ITEM', 'EXCEL_DOWNLOAD'],
148
+ };
149
+
150
+ /**
151
+ * Type 정보
152
+ */
153
+ export interface TypeInfo {
154
+ type: number;
155
+ name: string;
156
+ description: string;
157
+ features: string[];
158
+ useCases: string[];
159
+ }
160
+
161
+ export const TYPE_INFOS: TypeInfo[] = [
162
+ {
163
+ type: 1,
164
+ name: '기본 리스트 + 상세',
165
+ description: '선택된 아이템의 상세 정보 표시, 저장/수정 지원',
166
+ features: TYPE_FEATURES[1],
167
+ useCases: ['회원 목록', '상품 목록', '쿠폰 목록'],
168
+ },
169
+ {
170
+ type: 2,
171
+ name: '마스터-디테일 + 모달 + 삭제',
172
+ description: '왼쪽 리스트, 오른쪽 상세, 모달, 삭제 기능 (B2B 패턴)',
173
+ features: TYPE_FEATURES[2],
174
+ useCases: ['B2B 상품', '견적 상담', '구독 관리'],
175
+ },
176
+ {
177
+ type: 3,
178
+ name: '트리 기반',
179
+ description: '트리 구조 데이터, 펼치기/접기, 저장 지원',
180
+ features: TYPE_FEATURES[3],
181
+ useCases: ['카테고리 관리', '메뉴 관리', '조직도'],
182
+ },
183
+ {
184
+ type: 4,
185
+ name: '대시보드',
186
+ description: '여러 개의 리스트와 요약 정보, 탭 전환',
187
+ features: TYPE_FEATURES[4],
188
+ useCases: ['회원 대시보드', '주문 대시보드', '매출 대시보드'],
189
+ },
190
+ {
191
+ type: 5,
192
+ name: '단순 리스트',
193
+ description: '최소한의 기능만 있는 단순 리스트',
194
+ features: TYPE_FEATURES[5],
195
+ useCases: ['참조 데이터 조회', '드롭다운 목록'],
196
+ },
197
+ {
198
+ type: 6,
199
+ name: '재정렬 가능 리스트',
200
+ description: '드래그 앤 드롭으로 순서 변경 가능',
201
+ features: TYPE_FEATURES[6],
202
+ useCases: ['팝업 메뉴', '배너 순서', '메뉴 순서'],
203
+ },
204
+ {
205
+ type: 7,
206
+ name: '마스터-디테일 + 모달 + 삭제 + 엑셀',
207
+ description: '왼쪽 리스트, 오른쪽 상세, 모달, 삭제, 엑셀 다운로드',
208
+ features: TYPE_FEATURES[7],
209
+ useCases: ['B2B 견적 상담', '상담 관리', '주문 관리'],
210
+ },
211
+ {
212
+ type: 8,
213
+ name: '리스트 + 선택 + 엑셀 다운로드',
214
+ description: '선택 아이템, 체크박스, 엑셀 다운로드 (상세 없음)',
215
+ features: TYPE_FEATURES[8],
216
+ useCases: ['주문 전체 조회', '결제 조회', '배송 조회'],
217
+ },
218
+ {
219
+ type: 9,
220
+ name: '마스터-디테일 + 모달 + 상세조회 API',
221
+ description: '왼쪽 리스트, 오른쪽 상세, 모달, 상세 조회 API 호출',
222
+ features: TYPE_FEATURES[9],
223
+ useCases: ['직배송 구독', '상담 내역', '주문 상세'],
224
+ },
225
+ {
226
+ type: 10,
227
+ name: '마스터-디테일 + 엑셀 다운로드',
228
+ description: '선택된 행 표시, 엑셀 다운로드 (상세/모달 없음)',
229
+ features: TYPE_FEATURES[10],
230
+ useCases: ['상품 그룹 마스터', '분류 관리', '카테고리 마스터'],
231
+ },
232
+ ];
@@ -0,0 +1,403 @@
1
+ import { FEATURES, TYPE_FEATURES, TYPE_INFOS, TypeInfo } from '../features/store-features.js';
2
+ import {
3
+ getStoreTypeInfo,
4
+ getAllFields,
5
+ suggestStoreType,
6
+ getContextualDocumentation
7
+ } from '../docs/index.js';
8
+
9
+ /**
10
+ * 요구사항 분석 결과
11
+ */
12
+ export interface RequirementsAnalysis {
13
+ success: boolean;
14
+ requirements: string;
15
+ extractedFeatures: string[];
16
+ matchedTypes: TypeMatch[];
17
+ recommendedType?: TypeMatch;
18
+ canUseExistingType: boolean;
19
+ isComplexRequest: boolean;
20
+ contextualDocs?: string;
21
+ relevantDocs?: string[];
22
+ }
23
+
24
+ /**
25
+ * 타입 매칭 결과
26
+ */
27
+ export interface TypeMatch {
28
+ type: number;
29
+ name: string;
30
+ description: string;
31
+ matchScore: number;
32
+ matchedFeatures: string[];
33
+ missingFeatures: string[];
34
+ extraFeatures: string[];
35
+ }
36
+
37
+ /**
38
+ * 자연어 요구사항을 분석하여 필요한 피처 추출
39
+ */
40
+ export async function analyzeRequirements(args: {
41
+ requirements: string;
42
+ }): Promise<{
43
+ content: Array<{ type: string; text: string }>;
44
+ }> {
45
+ const { requirements } = args;
46
+
47
+ try {
48
+ // 1. 요구사항에서 피처 추출
49
+ const extractedFeatures = extractFeaturesFromRequirements(requirements);
50
+
51
+ // 2. 각 타입과 매칭 점수 계산
52
+ const matchedTypes = matchTypes(extractedFeatures);
53
+
54
+ // 3. 가장 적합한 타입 추천
55
+ const recommendedType = findBestMatch(matchedTypes);
56
+
57
+ // 4. 기존 타입 사용 가능 여부 판단 (매칭 점수 0.8 이상이면 사용 가능)
58
+ const canUseExistingType = recommendedType ? recommendedType.matchScore >= 0.8 : false;
59
+
60
+ // 5. 복잡한 요청 여부 판단 및 문서 컨텍스트 생성
61
+ const matchScore = recommendedType?.matchScore || 0;
62
+ const docsContext = getContextualDocumentation(
63
+ matchScore,
64
+ extractedFeatures,
65
+ recommendedType?.type
66
+ );
67
+
68
+ const analysis: RequirementsAnalysis = {
69
+ success: true,
70
+ requirements,
71
+ extractedFeatures,
72
+ matchedTypes,
73
+ recommendedType,
74
+ canUseExistingType,
75
+ isComplexRequest: docsContext.isComplex,
76
+ contextualDocs: docsContext.isComplex ? docsContext.context : undefined,
77
+ relevantDocs: docsContext.relevantDocs,
78
+ };
79
+
80
+ return {
81
+ content: [
82
+ {
83
+ type: 'text',
84
+ text: JSON.stringify(analysis, null, 2),
85
+ },
86
+ ],
87
+ };
88
+ } catch (error) {
89
+ return {
90
+ content: [
91
+ {
92
+ type: 'text',
93
+ text: JSON.stringify({
94
+ success: false,
95
+ error: error instanceof Error ? error.message : String(error),
96
+ }),
97
+ },
98
+ ],
99
+ };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * 요구사항에서 피처 추출 (키워드 매칭)
105
+ */
106
+ export function extractFeaturesFromRequirements(requirements: string): string[] {
107
+ const extracted: string[] = [];
108
+ const lowerRequirements = requirements.toLowerCase();
109
+
110
+ for (const [featureKey, feature] of Object.entries(FEATURES)) {
111
+ // 키워드 매칭
112
+ for (const keyword of feature.keywords) {
113
+ if (lowerRequirements.includes(keyword.toLowerCase())) {
114
+ if (!extracted.includes(featureKey)) {
115
+ extracted.push(featureKey);
116
+ }
117
+ break;
118
+ }
119
+ }
120
+ }
121
+
122
+ // 항상 LIST는 기본으로 포함 (명시적 언급이 없어도)
123
+ if (!extracted.includes('LIST')) {
124
+ extracted.unshift('LIST');
125
+ }
126
+
127
+ return extracted;
128
+ }
129
+
130
+ /**
131
+ * 추출된 피처와 각 타입을 매칭하여 점수 계산
132
+ */
133
+ export function matchTypes(extractedFeatures: string[]): TypeMatch[] {
134
+ const matches: TypeMatch[] = [];
135
+
136
+ for (const typeInfo of TYPE_INFOS) {
137
+ const typeFeatures = TYPE_FEATURES[typeInfo.type] || [];
138
+
139
+ // 일치하는 피처
140
+ const matchedFeatures = extractedFeatures.filter((f) => typeFeatures.includes(f));
141
+
142
+ // 부족한 피처 (요구사항에는 있는데 타입에는 없는)
143
+ const missingFeatures = extractedFeatures.filter((f) => !typeFeatures.includes(f));
144
+
145
+ // 추가 피처 (타입에는 있는데 요구사항에는 없는)
146
+ const extraFeatures = typeFeatures.filter((f) => !extractedFeatures.includes(f));
147
+
148
+ // 매칭 점수 계산
149
+ // - 일치하는 피처: +1점
150
+ // - 부족한 피처: -0.5점
151
+ // - 추가 피처: -0.2점 (불필요한 기능이 있음)
152
+ let matchScore = 0;
153
+ matchScore += matchedFeatures.length * 1;
154
+ matchScore += missingFeatures.length * -0.5;
155
+ matchScore += extraFeatures.length * -0.1;
156
+
157
+ // 정규화 (0-1 사이)
158
+ matchScore = Math.max(0, Math.min(1, (matchScore + 1) / (extractedFeatures.length + 1)));
159
+
160
+ matches.push({
161
+ type: typeInfo.type,
162
+ name: typeInfo.name,
163
+ description: typeInfo.description,
164
+ matchScore,
165
+ matchedFeatures,
166
+ missingFeatures,
167
+ extraFeatures,
168
+ });
169
+ }
170
+
171
+ // 매칭 점수순 정렬
172
+ return matches.sort((a, b) => b.matchScore - a.matchScore);
173
+ }
174
+
175
+ /**
176
+ * 가장 적합한 타입 찾기
177
+ */
178
+ export function findBestMatch(matchedTypes: TypeMatch[]): TypeMatch | undefined {
179
+ if (matchedTypes.length === 0) return undefined;
180
+
181
+ const best = matchedTypes[0];
182
+
183
+ // 최소 매칭 기준: 적어도 하나의 피처는 일치해야 함
184
+ if (best.matchedFeatures.length === 0) {
185
+ return undefined;
186
+ }
187
+
188
+ return best;
189
+ }
190
+
191
+ /**
192
+ * 동적 Store 생성을 위한 피처 조합 추천
193
+ * 기존 타입으로 커버되지 않을 때 사용
194
+ */
195
+ export function suggestFeatureCombination(extractedFeatures: string[]): {
196
+ features: string[];
197
+ recommendedTemplate: string;
198
+ } {
199
+ // 기본 템플릿 (Type 1)
200
+ let recommendedType = 1;
201
+ let bestScore = 0;
202
+
203
+ // 가장 가까운 타입 찾기
204
+ for (const typeInfo of TYPE_INFOS) {
205
+ const typeFeatures = TYPE_FEATURES[typeInfo.type] || [];
206
+ const intersection = extractedFeatures.filter((f) => typeFeatures.includes(f));
207
+ const score = intersection.length;
208
+
209
+ if (score > bestScore) {
210
+ bestScore = score;
211
+ recommendedType = typeInfo.type;
212
+ }
213
+ }
214
+
215
+ return {
216
+ features: extractedFeatures,
217
+ recommendedTemplate: `type${recommendedType}`,
218
+ };
219
+ }
220
+
221
+ /**
222
+ * docs/patterns.json 기반 정밀 타입 매칭
223
+ * 기존 키워드 매칭보다 더 정확한 타입 추천 제공
224
+ */
225
+ export function matchTypesWithJsonPatterns(extractedFeatures: string[]): TypeMatch[] {
226
+ const matches: TypeMatch[] = [];
227
+
228
+ // types 1-12 순회
229
+ for (let typeNum = 1; typeNum <= 12; typeNum++) {
230
+ const typeInfo = getStoreTypeInfo(typeNum);
231
+
232
+ if (!typeInfo) {
233
+ // 패턴 정보가 없는 타�은 기본 TYPE_INFOS 사용
234
+ const baseTypeInfo = TYPE_INFOS.find(t => t.type === typeNum);
235
+ if (baseTypeInfo) {
236
+ const typeFeatures = TYPE_FEATURES[baseTypeInfo.type] || [];
237
+ const matchedFeatures = extractedFeatures.filter((f) => typeFeatures.includes(f));
238
+ const missingFeatures = extractedFeatures.filter((f) => !typeFeatures.includes(f));
239
+ const extraFeatures = typeFeatures.filter((f) => !extractedFeatures.includes(f));
240
+
241
+ let matchScore = matchedFeatures.length * 1;
242
+ matchScore += missingFeatures.length * -0.5;
243
+ matchScore += extraFeatures.length * -0.1;
244
+ matchScore = Math.max(0, Math.min(1, (matchScore + 1) / (extractedFeatures.length + 1)));
245
+
246
+ matches.push({
247
+ type: baseTypeInfo.type,
248
+ name: baseTypeInfo.name,
249
+ description: baseTypeInfo.description,
250
+ matchScore,
251
+ matchedFeatures,
252
+ missingFeatures,
253
+ extraFeatures,
254
+ });
255
+ }
256
+ continue;
257
+ }
258
+
259
+ // JSON 패턴에서 필드 정보 추출
260
+ const allFields = [
261
+ ...typeInfo.metadata.required,
262
+ ...typeInfo.metadata.optional,
263
+ ...typeInfo.states.required,
264
+ ...typeInfo.states.optional,
265
+ ...typeInfo.actions.required,
266
+ ...typeInfo.actions.optional,
267
+ ];
268
+
269
+ // 필드 이름을 피처 키워드와 매칭
270
+ const matchedFeatures: string[] = [];
271
+ const allFieldsLower = allFields.map(f => f.toLowerCase());
272
+
273
+ for (const feature of extractedFeatures) {
274
+ const featureLower = feature.toLowerCase();
275
+ // 직접 필드명 매칭
276
+ if (allFieldsLower.some(field => field.includes(featureLower))) {
277
+ matchedFeatures.push(feature);
278
+ continue;
279
+ }
280
+
281
+ // 특수 매칭 규칙
282
+ switch (feature) {
283
+ case 'LIST':
284
+ if (allFieldsLower.some(f => f.includes('list'))) matchedFeatures.push(feature);
285
+ break;
286
+ case 'SEARCH':
287
+ if (allFieldsLower.some(f => f.includes('request'))) matchedFeatures.push(feature);
288
+ break;
289
+ case 'DETAIL':
290
+ case 'DETAIL_PANEL':
291
+ if (allFieldsLower.some(f => f.includes('detail') || f.includes('save'))) matchedFeatures.push(feature);
292
+ break;
293
+ case 'MODAL':
294
+ if (allFieldsLower.some(f => f.includes('modal'))) matchedFeatures.push(feature);
295
+ break;
296
+ case 'SAVE':
297
+ if (allFieldsLower.some(f => f.includes('save'))) matchedFeatures.push(feature);
298
+ break;
299
+ case 'DELETE':
300
+ if (allFieldsLower.some(f => f.includes('delete'))) matchedFeatures.push(feature);
301
+ break;
302
+ case 'EXCEL':
303
+ case 'EXCEL_DOWNLOAD':
304
+ if (allFieldsLower.some(f => f.includes('excel'))) matchedFeatures.push(feature);
305
+ break;
306
+ case 'CHECKBOX':
307
+ if (allFieldsLower.some(f => f.includes('checked') || f.includes('check'))) matchedFeatures.push(feature);
308
+ break;
309
+ case 'TREE':
310
+ if (allFieldsLower.some(f => f.includes('tree'))) matchedFeatures.push(feature);
311
+ break;
312
+ }
313
+ }
314
+
315
+ const missingFeatures = extractedFeatures.filter((f) => !matchedFeatures.includes(f));
316
+ const extraFeatures: string[] = [];
317
+
318
+ // 매칭 점수 계산 (JSON 기반 가중치)
319
+ let matchScore = 0;
320
+ matchScore += matchedFeatures.length * 1.2; // JSON 기반 매칭에 더 높은 가중치
321
+ matchScore += missingFeatures.length * -0.3;
322
+ matchScore = Math.max(0, Math.min(1, (matchScore + 1) / (extractedFeatures.length + 1)));
323
+
324
+ matches.push({
325
+ type: typeNum,
326
+ name: typeInfo.name || `Type ${typeNum}`,
327
+ description: typeInfo.description || '',
328
+ matchScore,
329
+ matchedFeatures,
330
+ missingFeatures,
331
+ extraFeatures,
332
+ });
333
+ }
334
+
335
+ return matches.sort((a, b) => b.matchScore - a.matchScore);
336
+ }
337
+
338
+ /**
339
+ * 요구사항으로부터 최적의 Store 타입 추천 (JSON 패턴 활용)
340
+ */
341
+ export function recommendStoreTypeFromRequirements(requirements: string): {
342
+ type: number;
343
+ name: string;
344
+ description: string;
345
+ confidence: number;
346
+ } {
347
+ const extractedFeatures = extractFeaturesFromRequirements(requirements);
348
+ const matches = matchTypesWithJsonPatterns(extractedFeatures);
349
+
350
+ if (matches.length === 0 || matches[0].matchedFeatures.length === 0) {
351
+ return {
352
+ type: 1,
353
+ name: 'Basic List',
354
+ description: '단순 조회 페이지',
355
+ confidence: 0.5,
356
+ };
357
+ }
358
+
359
+ const best = matches[0];
360
+ return {
361
+ type: best.type,
362
+ name: best.name,
363
+ description: best.description,
364
+ confidence: best.matchScore,
365
+ };
366
+ }
367
+
368
+ /**
369
+ * 특정 Store 타입에 필요한 필드 정보 반환
370
+ */
371
+ export function getStoreTypeFields(type: number): {
372
+ metadata: { required: string[]; optional: string[] };
373
+ states: { required: string[]; optional: string[] };
374
+ actions: { required: string[]; optional: string[] };
375
+ subscribeSelector: string[];
376
+ } {
377
+ const typeInfo = getStoreTypeInfo(type);
378
+
379
+ if (!typeInfo) {
380
+ return {
381
+ metadata: { required: [], optional: [] },
382
+ states: { required: [], optional: [] },
383
+ actions: { required: [], optional: [] },
384
+ subscribeSelector: [],
385
+ };
386
+ }
387
+
388
+ return {
389
+ metadata: {
390
+ required: typeInfo.metadata.required || [],
391
+ optional: typeInfo.metadata.optional || [],
392
+ },
393
+ states: {
394
+ required: typeInfo.states.required || [],
395
+ optional: typeInfo.states.optional || [],
396
+ },
397
+ actions: {
398
+ required: typeInfo.actions.required || [],
399
+ optional: typeInfo.actions.optional || [],
400
+ },
401
+ subscribeSelector: typeInfo.subscribePattern?.selector || [],
402
+ };
403
+ }