@ncds/ui-admin-mcp 1.0.0-alpha.2 → 1.0.0-alpha.21
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/README.md +3 -3
- package/bin/components.bundle.js +18 -9
- package/bin/definitions/external/editor.d.ts +50 -0
- package/bin/definitions/external/editor.js +53 -0
- package/bin/definitions/external/step-guide.d.ts +61 -0
- package/bin/definitions/external/step-guide.js +52 -0
- package/bin/definitions/instructions.md +194 -10
- package/bin/definitions/js-api.json +352 -0
- package/bin/definitions/rules.json +36 -4
- package/bin/definitions/tool-definitions.json +33 -9
- package/bin/overrides/composition.json +2473 -0
- package/bin/server.d.ts +5 -0
- package/bin/server.js +97 -33
- package/bin/server.mjs +0 -0
- package/bin/tools/external/editor.d.ts +18 -0
- package/bin/tools/external/editor.js +88 -0
- package/bin/tools/external/step-guide.d.ts +19 -0
- package/bin/tools/external/step-guide.js +79 -0
- package/bin/tools/getComponentProps.d.ts +3 -0
- package/bin/tools/getComponentProps.js +12 -3
- package/bin/tools/listCompositionOverrides.d.ts +61 -0
- package/bin/tools/listCompositionOverrides.js +156 -0
- package/bin/tools/listSidecarOverrides.d.ts +54 -0
- package/bin/tools/listSidecarOverrides.js +96 -0
- package/bin/tools/ping.d.ts +1 -1
- package/bin/tools/renderToHtml.d.ts +38 -7
- package/bin/tools/renderToHtml.js +785 -110
- package/bin/tools/searchComponent.d.ts +5 -0
- package/bin/tools/searchComponent.js +3 -3
- package/bin/tools/validateHtml.d.ts +8 -6
- package/bin/tools/validateHtml.js +74 -6
- package/bin/types.d.ts +60 -1
- package/bin/utils/bemValidator.d.ts +16 -8
- package/bin/utils/bemValidator.js +16 -4
- package/bin/utils/compliance.d.ts +7 -6
- package/bin/utils/compliance.js +8 -4
- package/bin/utils/dataLoader.d.ts +43 -14
- package/bin/utils/dataLoader.js +125 -22
- package/bin/utils/domEnvironment.js +51 -0
- package/bin/utils/fuzzyMatch.d.ts +4 -0
- package/bin/utils/fuzzyMatch.js +13 -3
- package/bin/utils/logger.d.ts +5 -5
- package/bin/utils/logger.js +5 -5
- package/bin/utils/response.d.ts +4 -2
- package/bin/utils/response.js +15 -4
- package/bin/utils/tokenValidator.d.ts +4 -3
- package/bin/utils/tokenValidator.js +13 -11
- package/bin/version.d.ts +4 -2
- package/bin/version.js +4 -2
- package/data/_icons.json +357 -2
- package/data/_meta.json +4 -5
- package/data/_tokens.json +8 -8
- package/data/badge-group.json +181 -4
- package/data/badge.json +146 -14
- package/data/block-container.json +95 -0
- package/data/block-header.json +208 -0
- package/data/bread-crumb.json +38 -2
- package/data/button-group.json +59 -0
- package/data/button.json +124 -1
- package/data/carousel-arrow.json +6 -11
- package/data/carousel-number-group.json +2 -12
- package/data/checkbox.json +1 -1
- package/data/combo-box.json +36 -8
- package/data/data-grid.json +240 -0
- package/data/date-picker.json +22 -2
- package/data/divider.json +1 -1
- package/data/dot.json +2 -2
- package/data/dropdown.json +187 -20
- package/data/editor.json +85 -0
- package/data/empty-state.json +168 -3
- package/data/featured-icon.json +20 -5
- package/data/file-input.json +176 -10
- package/data/horizontal-tab.json +219 -3
- package/data/image-file-input.json +177 -10
- package/data/input-base.json +165 -4
- package/data/modal.json +266 -5
- package/data/notification.json +56 -40
- package/data/number-input.json +164 -4
- package/data/page-title.json +135 -0
- package/data/pagination.json +8 -4
- package/data/password-input.json +252 -13
- package/data/progress-bar.json +28 -8
- package/data/progress-circle.json +9 -6
- package/data/radio.json +4 -3
- package/data/range-date-picker-with-buttons.json +187 -7
- package/data/range-date-picker.json +186 -6
- package/data/select-box.json +52 -16
- package/data/select.json +35 -25
- package/data/slider.json +1 -1
- package/data/spinner.json +3 -4
- package/data/step-guide.json +130 -0
- package/data/switch.json +66 -6
- package/data/table.json +293 -0
- package/data/tag.json +68 -6
- package/data/textarea.json +1 -1
- package/data/toggle.json +2 -2
- package/data/tooltip.json +20 -3
- package/data/vertical-tab.json +220 -3
- package/package.json +28 -26
- package/templates/README.md +1 -1
- package/bin/instructions.d.ts +0 -1
- package/bin/instructions.js +0 -14
- package/bin/tools/getComponentHtml.d.ts +0 -3
- package/bin/tools/getComponentHtml.js +0 -30
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listCompositionOverrides = exports.buildListCompositionOverridesResponse = exports.buildCompositionOverridesSummary = void 0;
|
|
4
|
+
const response_js_1 = require("../utils/response.js");
|
|
5
|
+
/**
|
|
6
|
+
* coverageScore 가중치 — 각 보강 영역의 점수 기여도.
|
|
7
|
+
* 시나리오(canonicalExamples)가 가장 큰 가중치를 가지는 이유: AI hallucination 차단력이 가장 큼.
|
|
8
|
+
*/
|
|
9
|
+
const COVERAGE_WEIGHTS = {
|
|
10
|
+
CANONICAL_EXAMPLES_FULL: 0.5,
|
|
11
|
+
CANONICAL_EXAMPLES_PARTIAL: 0.25,
|
|
12
|
+
ALLOWED_CHILDREN_FULL: 0.2,
|
|
13
|
+
ALLOWED_CHILDREN_PARTIAL: 0.1,
|
|
14
|
+
BEM_CLASSES_EXTRA: 0.1,
|
|
15
|
+
DESCRIPTION_EXTRA: 0.1,
|
|
16
|
+
ALIASES_EXTRA: 0.1,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* 전체 가중치 합 (5 영역 모두 적용 가능한 경우의 최대).
|
|
20
|
+
* applicableMax = TOTAL_WEIGHT - Σ(notApplicable 영역 가중치) 로 동적 계산.
|
|
21
|
+
*/
|
|
22
|
+
const TOTAL_WEIGHT = 1.0;
|
|
23
|
+
/**
|
|
24
|
+
* 영역별 가중치 맵 — applicableMax 동적 계산 시 사용.
|
|
25
|
+
* canonicalExamples 는 항상 적용 가능하므로 포함하지 않음 (분모에서 제외되지 않음).
|
|
26
|
+
*/
|
|
27
|
+
const AREA_WEIGHT_MAP = {
|
|
28
|
+
allowedChildren: COVERAGE_WEIGHTS.ALLOWED_CHILDREN_FULL,
|
|
29
|
+
bemClassesExtra: COVERAGE_WEIGHTS.BEM_CLASSES_EXTRA,
|
|
30
|
+
descriptionExtra: COVERAGE_WEIGHTS.DESCRIPTION_EXTRA,
|
|
31
|
+
aliasesExtra: COVERAGE_WEIGHTS.ALIASES_EXTRA,
|
|
32
|
+
};
|
|
33
|
+
/** coverageNote tier 임계값. */
|
|
34
|
+
const TIER_THRESHOLD_HIGH = 0.7;
|
|
35
|
+
const TIER_THRESHOLD_MEDIUM = 0.4;
|
|
36
|
+
/** 점수 cap (이론적 최대값). */
|
|
37
|
+
const SCORE_CAP = 1;
|
|
38
|
+
/** 소수점 2자리 반올림용 — `Math.round(x * 100) / 100`. */
|
|
39
|
+
const TWO_DECIMAL_BASE = 100;
|
|
40
|
+
/** AI 가 UI 를 얼마나 정확히 그릴 수 있는지 추정 — 각 composition overrides 보강이 다른 정확도 영역을 커버한다 */
|
|
41
|
+
const METRIC_DESCRIPTIONS = {
|
|
42
|
+
canonicalExamples: 'AI 가 시나리오별로 분기 가능한 정답 트리 수 (form / data-grid / with-tooltip 등). 가장 강한 정확도 신호 — 0 이면 AI 가 prop 조합을 추측해야 한다.',
|
|
43
|
+
hasCanonicalExample: 'legacy 단일 시나리오 존재 여부 — 진입용 표준 트리 1건. canonicalExamples 가 비어있는 컴포넌트의 fallback.',
|
|
44
|
+
bemClassesExtra: 'extract 가 못 잡은 BEM 클래스 화이트리스트 수. validate_html 이 합법/위법을 정확히 판정 — 발명 클래스 사용 차단력.',
|
|
45
|
+
allowedChildrenKeys: 'compound 부모-자식 제약 노드 수. 잘못된 자식 끼우기 / 컨텍스트 위반 (Table.Pagination in DataGrid 등) 차단력.',
|
|
46
|
+
allowedParentsKeys: '역방향 제약 노드 수 — 자식이 어느 부모 안에만 허용되는지 명시.',
|
|
47
|
+
descriptionExtra: 'list_components 응답에 자연어 용도/모드 명시 여부. 같은 컴포넌트가 다른 모드(horizontal/vertical) 로 쓰일 때 AI 가 모드를 인지하는 정확도.',
|
|
48
|
+
aliasesExtra: 'search_component 한국어/유사 키워드 매칭 별칭 수. AI 가 "폼 레이아웃", "세로 테이블" 같은 비공식 용어로 검색해도 정답 컴포넌트에 도달.',
|
|
49
|
+
coverageScore: '자기 카테고리 천장 대비 정규화 점수 (0.0 ~ 1.0). 1.0 이면 컴포넌트 구조상 가능한 모든 보강 영역을 완료. applicableMax = 1.0 − Σ(notApplicable 영역 가중치)로 동적 계산되어 모든 컴포넌트가 동일 척도. 절대 가중합이 아니라 정규화 비율이므로 컴포넌트 종류 무관하게 직접 비교 가능.',
|
|
50
|
+
notApplicable: '컴포넌트 구조상 적용 불가한 보강 영역 목록. non-compound 는 자동으로 ["allowedChildren"] 포함 (자식 없어 부모-자식 제약 의미 없음). composition.json notApplicableAreas 선언(예: extract 가 BEM 을 완전히 잡는 컴포넌트 → ["bemClassesExtra"])과 union 되어 최종 notApplicable 결정. 점수 분모에서 자동 제외되어 천장이 1.0 으로 통일된다.',
|
|
51
|
+
};
|
|
52
|
+
const COVERAGE_FORMULA = 'rawScore (가중 합) = canonicalExamples>=2: +0.50 (>=1 또는 hasCanonicalExample: +0.25) + allowedChildrenKeys>=2: +0.20 (>=1: +0.10, compound 만 적용) + bemClassesExtra>=1: +0.10 + descriptionExtra: +0.10 + aliasesExtra>=1: +0.10. applicableMax = 1.0 − Σ(notApplicable 영역 가중치) — non-compound 는 자동으로 allowedChildren(0.20) 제외, composition.json notApplicableAreas 선언 시 해당 가중치 추가 차감. coverageScore = rawScore / applicableMax (Math.min(1.0, ...)). 임계값: high>=0.70, medium>=0.40, 그 외 low. coverageScore 1.0 = 자기 카테고리에서 더 채울 영역 없음.';
|
|
53
|
+
/** 적용 가능 영역의 절대 가중치 합 (정규화 전 raw 점수). */
|
|
54
|
+
const computeRawScore = (s) => {
|
|
55
|
+
let score = 0;
|
|
56
|
+
if (s.canonicalExamples.length >= 2)
|
|
57
|
+
score += COVERAGE_WEIGHTS.CANONICAL_EXAMPLES_FULL;
|
|
58
|
+
else if (s.canonicalExamples.length >= 1 || s.hasCanonicalExample)
|
|
59
|
+
score += COVERAGE_WEIGHTS.CANONICAL_EXAMPLES_PARTIAL;
|
|
60
|
+
if (s.allowedChildrenKeys.length >= 2)
|
|
61
|
+
score += COVERAGE_WEIGHTS.ALLOWED_CHILDREN_FULL;
|
|
62
|
+
else if (s.allowedChildrenKeys.length >= 1)
|
|
63
|
+
score += COVERAGE_WEIGHTS.ALLOWED_CHILDREN_PARTIAL;
|
|
64
|
+
if (s.bemClassesExtra >= 1)
|
|
65
|
+
score += COVERAGE_WEIGHTS.BEM_CLASSES_EXTRA;
|
|
66
|
+
if (s.descriptionExtra)
|
|
67
|
+
score += COVERAGE_WEIGHTS.DESCRIPTION_EXTRA;
|
|
68
|
+
if (s.aliasesExtra >= 1)
|
|
69
|
+
score += COVERAGE_WEIGHTS.ALIASES_EXTRA;
|
|
70
|
+
return score;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* 컴포넌트의 notApplicable 영역 목록.
|
|
74
|
+
* 구조적 제외(non-compound → allowedChildren) + entry 의 notApplicableAreas 선언을 union.
|
|
75
|
+
*/
|
|
76
|
+
const getNotApplicable = (isCompound, entryNotApplicableAreas) => {
|
|
77
|
+
const structural = isCompound ? [] : ['allowedChildren'];
|
|
78
|
+
return [...new Set([...structural, ...entryNotApplicableAreas])];
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* applicableMax = TOTAL_WEIGHT - Σ(notApplicable 영역 가중치).
|
|
82
|
+
* compound 기본: 1.0. non-compound 기본: 0.8 (allowedChildren 0.2 제외).
|
|
83
|
+
* notApplicableAreas 추가 선언 시 해당 가중치만큼 추가 차감 → coverageScore 1.0 = 실제 달성 가능 최대.
|
|
84
|
+
*/
|
|
85
|
+
const getApplicableMax = (notApplicable) => {
|
|
86
|
+
const excluded = notApplicable.reduce((sum, area) => sum + (AREA_WEIGHT_MAP[area] ?? 0), 0);
|
|
87
|
+
return Math.round((TOTAL_WEIGHT - excluded) * TWO_DECIMAL_BASE) / TWO_DECIMAL_BASE;
|
|
88
|
+
};
|
|
89
|
+
/** rawScore → coverageScore (자기 카테고리 천장 기준 0~1.0 정규화). */
|
|
90
|
+
const normalizeScore = (rawScore, applicableMax) => Math.min(SCORE_CAP, Math.round((rawScore / applicableMax) * TWO_DECIMAL_BASE) / TWO_DECIMAL_BASE);
|
|
91
|
+
/** coverageScore 를 high/medium/low tier 로 분류. */
|
|
92
|
+
const getTier = (score) => {
|
|
93
|
+
if (score >= TIER_THRESHOLD_HIGH)
|
|
94
|
+
return 'high';
|
|
95
|
+
if (score >= TIER_THRESHOLD_MEDIUM)
|
|
96
|
+
return 'medium';
|
|
97
|
+
return 'low';
|
|
98
|
+
};
|
|
99
|
+
const noteFor = (s, score) => {
|
|
100
|
+
const strengths = [];
|
|
101
|
+
if (s.canonicalExamples.length >= 2)
|
|
102
|
+
strengths.push(`시나리오 ${s.canonicalExamples.length}`);
|
|
103
|
+
else if (s.canonicalExamples.length >= 1 || s.hasCanonicalExample)
|
|
104
|
+
strengths.push('시나리오 1');
|
|
105
|
+
if (s.allowedChildrenKeys.length >= 1)
|
|
106
|
+
strengths.push(`compound 제약 ${s.allowedChildrenKeys.length}`);
|
|
107
|
+
if (s.bemClassesExtra >= 1)
|
|
108
|
+
strengths.push(`BEM 화이트리스트 ${s.bemClassesExtra}`);
|
|
109
|
+
if (s.descriptionExtra)
|
|
110
|
+
strengths.push('모드 인지 보강');
|
|
111
|
+
if (s.aliasesExtra >= 1)
|
|
112
|
+
strengths.push(`검색 별칭 ${s.aliasesExtra}`);
|
|
113
|
+
const tier = getTier(score);
|
|
114
|
+
return strengths.length > 0
|
|
115
|
+
? `${tier} · ${strengths.join(' + ')}`
|
|
116
|
+
: `${tier} · 보강 거의 없음 — 직접 작성 hallucination 위험`;
|
|
117
|
+
};
|
|
118
|
+
/** 단일 entry 를 row 로 변환 — compound 여부에 따라 applicableMax 가 결정되고 coverageScore 가 정규화된다. */
|
|
119
|
+
const toSummary = (component, entry, componentMap) => {
|
|
120
|
+
const base = {
|
|
121
|
+
component,
|
|
122
|
+
canonicalExamples: Object.keys(entry.canonicalExamples ?? {}).filter((k) => !k.startsWith('_')),
|
|
123
|
+
hasCanonicalExample: !!entry.canonicalExample,
|
|
124
|
+
bemClassesExtra: (entry.bemClassesExtra ?? []).length,
|
|
125
|
+
allowedChildrenKeys: Object.keys(entry.allowedChildren ?? {}),
|
|
126
|
+
allowedParentsKeys: Object.keys(entry.allowedParents ?? {}),
|
|
127
|
+
descriptionExtra: !!entry.descriptionExtra,
|
|
128
|
+
aliasesExtra: (entry.aliasesExtra ?? []).length,
|
|
129
|
+
};
|
|
130
|
+
const subComponentCount = Object.keys(componentMap.get(component)?.subComponents ?? {}).length;
|
|
131
|
+
const isCompound = subComponentCount > 0;
|
|
132
|
+
const notApplicable = getNotApplicable(isCompound, entry.notApplicableAreas ?? []);
|
|
133
|
+
const applicableMax = getApplicableMax(notApplicable);
|
|
134
|
+
const rawScore = computeRawScore(base);
|
|
135
|
+
const coverageScore = normalizeScore(rawScore, applicableMax);
|
|
136
|
+
return { ...base, coverageScore, notApplicable, coverageNote: noteFor(base, coverageScore) };
|
|
137
|
+
};
|
|
138
|
+
/** composition overrides map → 요약 결과 (순수 함수). coverageScore 내림차순 + 동률은 이름 알파벳. */
|
|
139
|
+
const buildCompositionOverridesSummary = (compositionOverrides, componentMap) => {
|
|
140
|
+
const overrides = Object.entries(compositionOverrides)
|
|
141
|
+
.map(([component, entry]) => toSummary(component, entry, componentMap))
|
|
142
|
+
.sort((a, b) => b.coverageScore - a.coverageScore || a.component.localeCompare(b.component));
|
|
143
|
+
return {
|
|
144
|
+
total: overrides.length,
|
|
145
|
+
metrics: METRIC_DESCRIPTIONS,
|
|
146
|
+
coverageScoreFormula: COVERAGE_FORMULA,
|
|
147
|
+
overrides,
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
exports.buildCompositionOverridesSummary = buildCompositionOverridesSummary;
|
|
151
|
+
/** server.ts 부팅 시 1회 호출 — 응답 사전 직렬화 */
|
|
152
|
+
const buildListCompositionOverridesResponse = (compositionOverrides, componentMap) => (0, response_js_1.successResponse)((0, exports.buildCompositionOverridesSummary)(compositionOverrides, componentMap));
|
|
153
|
+
exports.buildListCompositionOverridesResponse = buildListCompositionOverridesResponse;
|
|
154
|
+
/** list_composition_overrides tool — 사전 직렬화된 응답을 그대로 반환 */
|
|
155
|
+
const listCompositionOverrides = (prebuilt) => prebuilt;
|
|
156
|
+
exports.listCompositionOverrides = listCompositionOverrides;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list_sidecar_overrides tool — composition.json sidecar 적용 현황 요약
|
|
3
|
+
*
|
|
4
|
+
* 신규 프로젝트에서 NCDS MCP 와 연결한 뒤, 어느 컴포넌트에 어떤 sidecar 보강이
|
|
5
|
+
* 들어가 있는지 표 형태(컴포넌트별 row)로 한 번에 확인하는 용도. 응답이 크지 않도록
|
|
6
|
+
* 시나리오 키 목록 + 카운트 위주로 구성하고, 상세는 get_component_props 로 유도.
|
|
7
|
+
*
|
|
8
|
+
* 순수 함수 — server.ts 부팅 시 1회 buildListSidecarOverridesResponse() 로 사전 직렬화.
|
|
9
|
+
*/
|
|
10
|
+
import type { ComponentData } from '../types.js';
|
|
11
|
+
import type { CompositionEntry } from '../utils/dataLoader.js';
|
|
12
|
+
import { type McpToolResponse } from '../utils/response.js';
|
|
13
|
+
export interface SidecarOverrideSummary {
|
|
14
|
+
/** 컴포넌트 이름 (kebab-case) */
|
|
15
|
+
component: string;
|
|
16
|
+
/** canonicalExamples 시나리오 key 목록 — 비어있으면 [] */
|
|
17
|
+
canonicalExamples: string[];
|
|
18
|
+
/** legacy 단일 시나리오 canonicalExample 존재 여부 */
|
|
19
|
+
hasCanonicalExample: boolean;
|
|
20
|
+
/** bemClassesExtra 항목 수 */
|
|
21
|
+
bemClassesExtra: number;
|
|
22
|
+
/** allowedChildren 정의된 부모 키 목록 (예: ["Table", "Table.Header", ...]) */
|
|
23
|
+
allowedChildrenKeys: string[];
|
|
24
|
+
/** allowedParents 정의된 자식 키 목록 */
|
|
25
|
+
allowedParentsKeys: string[];
|
|
26
|
+
/** descriptionExtra 존재 여부 */
|
|
27
|
+
descriptionExtra: boolean;
|
|
28
|
+
/** aliasesExtra 항목 수 */
|
|
29
|
+
aliasesExtra: number;
|
|
30
|
+
/** AI 의 UI 작성 정확도 추정 점수 (0.0 ~ 1.0). 시나리오 + compound 제약 + validate 정확도 + 자연어 보강의 가중 합. */
|
|
31
|
+
coverageScore: number;
|
|
32
|
+
/** 컴포넌트 구조상 의미 있게 달성 가능한 최대 점수. compound 컴포넌트(subComponents 보유): 1.0, 그 외: 0.8. */
|
|
33
|
+
maxAchievable: number;
|
|
34
|
+
/** coverageScore / maxAchievable — 컴포넌트별 천장 대비 충족율 (0.0 ~ 1.0). 절대 점수가 낮아도 비율이 1.0 이면 더 보강할 게 없다. */
|
|
35
|
+
coverageRatio: number;
|
|
36
|
+
/** coverageScore 한 줄 해석 — high/medium/low 와 강점 영역 요약 */
|
|
37
|
+
coverageNote: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ListSidecarOverridesResult {
|
|
40
|
+
/** sidecar 적용 컴포넌트 수 */
|
|
41
|
+
total: number;
|
|
42
|
+
/** 각 척도가 무엇을 의미하고 AI 의 UI 작성 정확도에 어떻게 영향을 주는지 (AI 가 본 응답을 바로 해석하도록 동봉) */
|
|
43
|
+
metrics: Record<string, string>;
|
|
44
|
+
/** coverageScore 산식 (가중치 명시) */
|
|
45
|
+
coverageScoreFormula: string;
|
|
46
|
+
/** 컴포넌트별 요약 row 배열 — coverageScore 내림차순, 동률은 컴포넌트 이름 알파벳 */
|
|
47
|
+
overrides: SidecarOverrideSummary[];
|
|
48
|
+
}
|
|
49
|
+
/** sidecar map → 요약 결과 (순수 함수). coverageRatio 내림차순 + 동률은 coverageScore + 이름 알파벳 */
|
|
50
|
+
export declare const buildSidecarOverridesSummary: (sidecar: Record<string, CompositionEntry>, componentMap: Map<string, ComponentData>) => ListSidecarOverridesResult;
|
|
51
|
+
/** server.ts 부팅 시 1회 호출 — 응답 사전 직렬화 */
|
|
52
|
+
export declare const buildListSidecarOverridesResponse: (sidecar: Record<string, CompositionEntry>, componentMap: Map<string, ComponentData>) => McpToolResponse;
|
|
53
|
+
/** list_sidecar_overrides tool — 사전 직렬화된 응답을 그대로 반환 */
|
|
54
|
+
export declare const listSidecarOverrides: (prebuilt: McpToolResponse) => McpToolResponse;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listSidecarOverrides = exports.buildListSidecarOverridesResponse = exports.buildSidecarOverridesSummary = void 0;
|
|
4
|
+
const response_js_1 = require("../utils/response.js");
|
|
5
|
+
/** AI 가 UI 를 얼마나 정확히 그릴 수 있는지 추정 — 각 sidecar 보강이 다른 정확도 영역을 커버한다 */
|
|
6
|
+
const METRIC_DESCRIPTIONS = {
|
|
7
|
+
canonicalExamples: 'AI 가 시나리오별로 분기 가능한 정답 트리 수 (form / data-grid / with-tooltip 등). 가장 강한 정확도 신호 — 0 이면 AI 가 prop 조합을 추측해야 한다.',
|
|
8
|
+
hasCanonicalExample: 'legacy 단일 시나리오 존재 여부 — 진입용 표준 트리 1건. canonicalExamples 가 비어있는 컴포넌트의 fallback.',
|
|
9
|
+
bemClassesExtra: 'extract 가 못 잡은 BEM 클래스 화이트리스트 수. validate_html 이 합법/위법을 정확히 판정 — 발명 클래스 사용 차단력.',
|
|
10
|
+
allowedChildrenKeys: 'compound 부모-자식 제약 노드 수. 잘못된 자식 끼우기 / 컨텍스트 위반 (Table.Pagination in DataGrid 등) 차단력.',
|
|
11
|
+
allowedParentsKeys: '역방향 제약 노드 수 — 자식이 어느 부모 안에만 허용되는지 명시.',
|
|
12
|
+
descriptionExtra: 'list_components 응답에 자연어 용도/모드 명시 여부. 같은 컴포넌트가 다른 모드(horizontal/vertical) 로 쓰일 때 AI 가 모드를 인지하는 정확도.',
|
|
13
|
+
aliasesExtra: 'search_component 한국어/유사 키워드 매칭 별칭 수. AI 가 "폼 레이아웃", "세로 테이블" 같은 비공식 용어로 검색해도 정답 컴포넌트에 도달.',
|
|
14
|
+
maxAchievable: '구조상 의미 있게 도달 가능한 최대 점수. compound 컴포넌트(subComponents 보유): 1.0, 그 외(prop 배열로 항목 받는 컴포넌트 — menus/groups/optionItems 등): 0.8. allowedChildren 가중치(+0.20) 가 compound 가 아닌 컴포넌트에는 적용 불가하기 때문.',
|
|
15
|
+
coverageRatio: 'coverageScore / maxAchievable. 절대 점수보다 더 정확한 "보강 충족율" — 1.0 이면 해당 컴포넌트는 더 채울 영역이 없다. 0.8 미만이면 의미 있는 보강 여지 있음.',
|
|
16
|
+
};
|
|
17
|
+
const COVERAGE_FORMULA = 'canonicalExamples>=2: +0.50 (>=1 또는 hasCanonicalExample: +0.25) · allowedChildrenKeys>=2: +0.20 (>=1: +0.10) · bemClassesExtra>=1: +0.10 · descriptionExtra: +0.10 · aliasesExtra>=1: +0.10 · max 1.0. 임계값: high>=0.70, medium>=0.40, 그 외 low. 시나리오 가중치가 가장 큼 — AI hallucination 의 가장 강한 차단 신호이기 때문.';
|
|
18
|
+
const computeCoverageScore = (s) => {
|
|
19
|
+
let score = 0;
|
|
20
|
+
if (s.canonicalExamples.length >= 2)
|
|
21
|
+
score += 0.5;
|
|
22
|
+
else if (s.canonicalExamples.length >= 1 || s.hasCanonicalExample)
|
|
23
|
+
score += 0.25;
|
|
24
|
+
if (s.allowedChildrenKeys.length >= 2)
|
|
25
|
+
score += 0.2;
|
|
26
|
+
else if (s.allowedChildrenKeys.length >= 1)
|
|
27
|
+
score += 0.1;
|
|
28
|
+
if (s.bemClassesExtra >= 1)
|
|
29
|
+
score += 0.1;
|
|
30
|
+
if (s.descriptionExtra)
|
|
31
|
+
score += 0.1;
|
|
32
|
+
if (s.aliasesExtra >= 1)
|
|
33
|
+
score += 0.1;
|
|
34
|
+
return Math.min(1, Math.round(score * 100) / 100);
|
|
35
|
+
};
|
|
36
|
+
/** 컴포넌트 구조상 의미 있게 도달 가능한 최대 점수.
|
|
37
|
+
* compound (subComponents 보유): canonicalExamples 0.5 + allowedChildren 0.2 + bem 0.1 + desc 0.1 + alias 0.1 = 1.0.
|
|
38
|
+
* non-compound: allowedChildren 항목이 의미 없음 → 0.8 cap. */
|
|
39
|
+
const computeMaxAchievable = (isCompound) => (isCompound ? 1.0 : 0.8);
|
|
40
|
+
const noteFor = (s, score) => {
|
|
41
|
+
const strengths = [];
|
|
42
|
+
if (s.canonicalExamples.length >= 2)
|
|
43
|
+
strengths.push(`시나리오 ${s.canonicalExamples.length}`);
|
|
44
|
+
else if (s.canonicalExamples.length >= 1 || s.hasCanonicalExample)
|
|
45
|
+
strengths.push('시나리오 1');
|
|
46
|
+
if (s.allowedChildrenKeys.length >= 1)
|
|
47
|
+
strengths.push(`compound 제약 ${s.allowedChildrenKeys.length}`);
|
|
48
|
+
if (s.bemClassesExtra >= 1)
|
|
49
|
+
strengths.push(`BEM 화이트리스트 ${s.bemClassesExtra}`);
|
|
50
|
+
if (s.descriptionExtra)
|
|
51
|
+
strengths.push('모드 인지 보강');
|
|
52
|
+
if (s.aliasesExtra >= 1)
|
|
53
|
+
strengths.push(`검색 별칭 ${s.aliasesExtra}`);
|
|
54
|
+
const tier = score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low';
|
|
55
|
+
return strengths.length > 0 ? `${tier} · ${strengths.join(' + ')}` : `${tier} · 보강 거의 없음 — 직접 작성 hallucination 위험`;
|
|
56
|
+
};
|
|
57
|
+
/** 단일 entry 를 row 로 변환 — componentMap 으로 compound 여부 결정해 maxAchievable 계산 */
|
|
58
|
+
const toSummary = (component, entry, componentMap) => {
|
|
59
|
+
const base = {
|
|
60
|
+
component,
|
|
61
|
+
canonicalExamples: Object.keys(entry.canonicalExamples ?? {}).filter((k) => !k.startsWith('_')),
|
|
62
|
+
hasCanonicalExample: !!entry.canonicalExample,
|
|
63
|
+
bemClassesExtra: (entry.bemClassesExtra ?? []).length,
|
|
64
|
+
allowedChildrenKeys: Object.keys(entry.allowedChildren ?? {}),
|
|
65
|
+
allowedParentsKeys: Object.keys(entry.allowedParents ?? {}),
|
|
66
|
+
descriptionExtra: !!entry.descriptionExtra,
|
|
67
|
+
aliasesExtra: (entry.aliasesExtra ?? []).length,
|
|
68
|
+
};
|
|
69
|
+
const coverageScore = computeCoverageScore(base);
|
|
70
|
+
const subComponentCount = Object.keys(componentMap.get(component)?.subComponents ?? {}).length;
|
|
71
|
+
const isCompound = subComponentCount > 0;
|
|
72
|
+
const maxAchievable = computeMaxAchievable(isCompound);
|
|
73
|
+
const coverageRatio = Math.round((coverageScore / maxAchievable) * 100) / 100;
|
|
74
|
+
return { ...base, coverageScore, maxAchievable, coverageRatio, coverageNote: noteFor(base, coverageScore) };
|
|
75
|
+
};
|
|
76
|
+
/** sidecar map → 요약 결과 (순수 함수). coverageRatio 내림차순 + 동률은 coverageScore + 이름 알파벳 */
|
|
77
|
+
const buildSidecarOverridesSummary = (sidecar, componentMap) => {
|
|
78
|
+
const overrides = Object.entries(sidecar)
|
|
79
|
+
.map(([component, entry]) => toSummary(component, entry, componentMap))
|
|
80
|
+
.sort((a, b) => b.coverageRatio - a.coverageRatio ||
|
|
81
|
+
b.coverageScore - a.coverageScore ||
|
|
82
|
+
a.component.localeCompare(b.component));
|
|
83
|
+
return {
|
|
84
|
+
total: overrides.length,
|
|
85
|
+
metrics: METRIC_DESCRIPTIONS,
|
|
86
|
+
coverageScoreFormula: COVERAGE_FORMULA,
|
|
87
|
+
overrides,
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
exports.buildSidecarOverridesSummary = buildSidecarOverridesSummary;
|
|
91
|
+
/** server.ts 부팅 시 1회 호출 — 응답 사전 직렬화 */
|
|
92
|
+
const buildListSidecarOverridesResponse = (sidecar, componentMap) => (0, response_js_1.successResponse)((0, exports.buildSidecarOverridesSummary)(sidecar, componentMap));
|
|
93
|
+
exports.buildListSidecarOverridesResponse = buildListSidecarOverridesResponse;
|
|
94
|
+
/** list_sidecar_overrides tool — 사전 직렬화된 응답을 그대로 반환 */
|
|
95
|
+
const listSidecarOverrides = (prebuilt) => prebuilt;
|
|
96
|
+
exports.listSidecarOverrides = listSidecarOverrides;
|
package/bin/tools/ping.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ping tool — NCUA MCP 서버 health check + capabilities/rules 제공 (순수 함수)
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type { Capability, ComponentData } from '../types.js';
|
|
5
5
|
import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
|
|
6
6
|
import { type McpToolResponse } from '../utils/response.js';
|
|
7
7
|
/** ping tool 파라미터 — server.ts에서 모든 데이터를 소유하고 전달한다 */
|
|
@@ -1,21 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
* render_to_html tool — 컴포넌트 속성을 전달하면 정확한 HTML + React 매핑을 반환 (순수 함수)
|
|
3
|
-
*
|
|
4
|
-
* DOM 환경과 React/ReactDOM은 server.ts에서 초기화하고 파라미터로 전달한다.
|
|
5
|
-
* 이 파일에는 fs, path, require, let이 없다.
|
|
6
|
-
*/
|
|
7
|
-
import type { ComponentData, ReactRuntime } from '../types.js';
|
|
1
|
+
import type { ComponentData, JsApiInfo, ReactRuntime } from '../types.js';
|
|
8
2
|
import type { CdnMeta, IconMeta } from '../utils/dataLoader.js';
|
|
9
3
|
import { type McpToolResponse } from '../utils/response.js';
|
|
10
4
|
/** render_to_html tool 파라미터 */
|
|
11
5
|
export interface RenderToHtmlParams {
|
|
12
6
|
componentMap: Map<string, ComponentData>;
|
|
13
7
|
bundle: Record<string, unknown>;
|
|
8
|
+
iconBundle: Record<string, unknown>;
|
|
14
9
|
cdnMeta: CdnMeta | null;
|
|
15
10
|
iconMeta: IconMeta | null;
|
|
16
11
|
reactRuntime: ReactRuntime;
|
|
12
|
+
jsApiMap: Map<string, JsApiInfo>;
|
|
17
13
|
name: string;
|
|
18
14
|
props?: Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* CDN init 패턴(Story 5.8) 생성 시 `<div id>` 접미사로 사용되는 고유 식별자.
|
|
17
|
+
* server.ts가 매 호출마다 `crypto.randomUUID().slice(0, 6)` 방식으로 생성해 주입한다.
|
|
18
|
+
* renderToHtml.ts는 순수 함수 원칙(파일 헤더 주석)을 위해 내부에서 랜덤을 생성하지 않는다.
|
|
19
|
+
*/
|
|
20
|
+
instanceId: string;
|
|
19
21
|
}
|
|
22
|
+
/** Story 5.9: 사용자 props 에서 함수 prop 재귀 제거 (JSON 직렬화·deep merge 안전).
|
|
23
|
+
* cdnDefaults 는 JSON 이라 함수 없음 → 사용자 props 만 정리.
|
|
24
|
+
* Story 5.10: 외부 분기 모듈(`tools/external/editor.ts`)에서도 재사용 → export. */
|
|
25
|
+
export declare const stripFunctionProps: (obj: unknown) => unknown;
|
|
26
|
+
/** Story 5.9: cdnDefaults 와 사용자 props 를 deep merge.
|
|
27
|
+
* - 사용자 값 우선 (lodash merge 의 두 번째 인자가 우선)
|
|
28
|
+
* - 함수 prop 은 사용자 props 에서 stripFunctionProps 로 사전 제거
|
|
29
|
+
* - DatePicker `datePickerOptions` 배열 한정: 각 항목의 `options` 에 cdnDefaults 의 첫 항목 options 를 per-element 부분 merge
|
|
30
|
+
* - 다른 배열(`buttons` 등) 은 사용자 값 그대로
|
|
31
|
+
* 반환: { merged, defaultsApplied } — defaultsApplied 는 cdnDefaults 에 있고 사용자 props 에 없는 top-level 키 (defaultsUsed 응답용)
|
|
32
|
+
* Story 5.10: 외부 분기 모듈(`tools/external/editor.ts`)에서도 재사용 → export. */
|
|
33
|
+
export declare const mergeCdnDefaults: (componentName: string, cdnDefaults: Record<string, unknown> | undefined, userProps: Record<string, unknown>) => {
|
|
34
|
+
merged: Record<string, unknown>;
|
|
35
|
+
defaultsApplied: string[];
|
|
36
|
+
};
|
|
37
|
+
/** jsRequired에 따라 js 필드를 생성 */
|
|
38
|
+
export declare const buildJsField: (componentData: ComponentData, jsApiMap: Map<string, JsApiInfo>) => {
|
|
39
|
+
required: false;
|
|
40
|
+
} | {
|
|
41
|
+
api?: {
|
|
42
|
+
className: string;
|
|
43
|
+
constructor: string;
|
|
44
|
+
constructorParams: Record<string, string>;
|
|
45
|
+
methods: string[];
|
|
46
|
+
example: string;
|
|
47
|
+
} | undefined;
|
|
48
|
+
required: true;
|
|
49
|
+
description: string;
|
|
50
|
+
};
|
|
20
51
|
/** render_to_html tool — props로 React 컴포넌트를 렌더링하여 HTML + React 매핑 반환 */
|
|
21
52
|
export declare const renderToHtml: (params: RenderToHtmlParams) => McpToolResponse;
|