@keke1933/vibe-dictionary-mcp 0.7.1 → 0.8.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 (3) hide show
  1. package/README.md +9 -3
  2. package/dist/index.js +422 -41
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vibe Dictionary MCP
2
2
 
3
- 바이브 코딩 사전의 기법을 검색하고 구현용 프롬프트 원문을 가져오는 읽기 전용 로컬 MCP다. Node.js 22.12 이상이 필요하며, 현재 검증 환경은 Node.js 24다.
3
+ 바이브 코딩 사전의 디자인 기법과 도메인 지식을 검색하고 참고 원문을 가져오는 읽기 전용 로컬 MCP다. Node.js 22.12 이상이 필요하며, 현재 검증 환경은 Node.js 24다. 아래 도메인 지식 도구는 현재 소스 기준이며, npm 배포 버전은 별도로 확인해야 한다.
4
4
 
5
5
  ## 제공하는 도구
6
6
 
@@ -8,10 +8,16 @@
8
8
  | --- | --- | --- |
9
9
  | `search_techniques` | 선택: `query`, `domain`, `category`, `target`, `trigger`, `locale`, `limit`, `offset` | 요약·사용 상황·검색 근거·점수·페이지 정보 |
10
10
  | `get_technique` | 필수: `id`, 선택: `locale` | 기법 상세·별칭·키워드·해당 언어의 `promptSnippet` 원문 |
11
+ | `search_domain_knowledge` | 선택: `query`, `locale`, `limit`, `offset` | 도메인 지식 요약·사용 상황·관련도·매칭 필드와 페이지 정보 |
12
+ | `get_domain_knowledge` | 필수: `id`, 선택: `locale` | 개념·결정할 질문·예시·관련 기법 ID·참고 자료·프롬프트 |
11
13
 
12
- 두 도구의 `locale`은 `ko`(기본) 또는 `en`이다. 이름·요약·태그·사용 상황·프롬프트를 선택 언어로 반환하며, 호환성을 위해 `titleKo`·`titleEn`은 두 이름을 유지한다. 검색 언어는 출력 언어와 독립적이므로 한국어로 검색한 뒤 영어 프롬프트를 받을 수 있다.
14
+ 모든 도구의 `locale`은 `ko`(기본) 또는 `en`이다. 기법 조회의 `titleKo`·`titleEn`은 호환성을 위해 두 이름을 유지한다. 검색 언어는 출력 언어와 독립적이므로 한국어로 검색한 뒤 영어 원문을 받을 수 있다.
13
15
 
14
- 검색은 한영 이름·별칭·효과 키워드·사용 상황·요약·프롬프트를 대상으로 한다. 대소문자를 구분하지 않으며, 동의어를 연결하고 이름·별칭·사용 상황을 본문보다 우선한다. 모든 단어의 일치를 요구하지 않고 검색어 충족 비율로 후보를 좁힌다.
16
+ 도메인 지식은 한영 이름·별칭·사용 상황과 개념·흐름·결정 질문을 함께 검색한다. `LMS`, `온라인 강의`, `재시도`, `회원 전용`, `신고 처리`, `private group`처럼 짧은 서비스명이나 업무 표현으로 `search_domain_knowledge`를 호출한다. 한영 동의어·대소문자·문장 부호를 정규화하고 매칭 범위와 필드 중요도로 정렬하는 키워드 검색이며, 의미를 추론하는 AI 검색은 아니다. 결과의 `useCases`와 `matchedOn`을 비교해 선택하고 반환된 ID를 `get_domain_knowledge`에 전달한다. `score`는 신뢰 확률이 아니며, 결과가 맞지 않으면 동의어나 더 짧은 검색어로 다시 찾는다. 검색어가 비어 있으면 목록 순서로 조회한다.
17
+
18
+ 상세 조회에는 개념·서비스 흐름·자주 놓치는 상황·질문·예시·프롬프트·출처를 제공한다. 흐름과 예시는 참고이며 결정할 질문은 정해진 규칙이 아니다. 관련 기법은 화면 표현을 돕는 참고 자료다. 코드 실행이나 교육 평가 기능을 제공하지 않는다.
19
+
20
+ 기법 검색은 한영 이름·별칭·효과 키워드·사용 상황·요약·프롬프트를 대상으로 한다. 대소문자를 구분하지 않으며, 동의어를 연결하고 이름·별칭·사용 상황을 본문보다 우선한다. 모든 단어의 일치를 요구하지 않고 검색어 충족 비율로 후보를 좁힌다.
15
21
 
16
22
  자연어 요청은 호출하는 AI가 해석한다. AI가 한 효과를 나타내는 짧은 한영 검색어를 생성 → MCP가 키워드로 후보 조회 → AI가 요약·사용 상황을 비교하고 필요하면 동의어·다른 언어·수식어를 줄인 표현으로 재검색 → 선택한 ID의 프롬프트를 조회한다. 예를 들어 “글을 고친 뒤 아직 저장하지 않았다는 표시”는 `저장 상태` 또는 `unsaved changes`로 검색한다. 필수 조건은 유지하고 불필요한 필터만 넓히며, 검색어나 필터를 바꾸면 `offset: 0`부터 조회한다. 순위가 높아도 요구에 맞지 않으면 선택하지 않는다.
17
23
 
package/dist/index.js CHANGED
@@ -17859,6 +17859,360 @@ function getSearchMetadata(item) {
17859
17859
  return SEARCH_METADATA[item.id] ?? { targets: [], triggers: [], aliases: { ko: [], en: [] }, useCases: { ko: [], en: [] } };
17860
17860
  }
17861
17861
 
17862
+ // ../../packages/catalog/src/domain-knowledge/education.ts
17863
+ var EDUCATION_KNOWLEDGE = [{
17864
+ // Preserve the ID so existing MCP references still resolve after broadening the topic.
17865
+ id: "coding-lesson-objectives-and-practice",
17866
+ aliases: {
17867
+ ko: ["\uAD50\uC721 \uC11C\uBE44\uC2A4", "\uAD50\uC721 \uD50C\uB7AB\uD3FC", "\uC628\uB77C\uC778 \uAC15\uC758", "\uC628\uB77C\uC778 \uD559\uC2B5", "\uD559\uC2B5 \uAD00\uB9AC \uC2DC\uC2A4\uD15C", "\uC9C4\uB3C4 \uAD00\uB9AC", "\uCF54\uB529 \uAD50\uC721"],
17868
+ en: ["education platform", "online course", "online learning", "learning management system", "LMS", "progress tracking"]
17869
+ },
17870
+ useCases: {
17871
+ ko: ["\uAC15\uC758\xB7\uD034\uC988\xB7\uACFC\uC81C\uB97C \uC81C\uACF5\uD558\uB294 \uAD50\uC721 \uC11C\uBE44\uC2A4\uB97C \uB9CC\uB4E4 \uB54C", "\uD65C\uB3D9\uBCC4 \uC9C4\uB3C4\uC640 \uC644\uB8CC\xB7\uC218\uB8CC \uAE30\uC900\uC744 \uC815\uD560 \uB54C", "\uCD08\uC548\xB7\uCD5C\uC885 \uC81C\uCD9C\xB7\uAC15\uC0AC \uD53C\uB4DC\uBC31\uC744 \uAD6C\uBD84\uD560 \uB54C", "\uB9C8\uAC10\uACFC \uC9C0\uAC01 \uC81C\uCD9C\xB7\uC7AC\uC2DC\uB3C4\xB7\uC7AC\uC81C\uCD9C \uC815\uCC45\uC744 \uC124\uACC4\uD560 \uB54C"],
17872
+ en: ["Building a service with lessons, quizzes and assignments", "Defining activity progress tracking and completion criteria", "Separating drafts, final submissions and instructor feedback", "Designing deadlines, late submissions, retries and resubmissions"]
17873
+ },
17874
+ keywords: {
17875
+ ko: ["\uAD50\uC721", "\uD559\uC2B5", "\uC218\uC5C5", "\uAC15\uC758", "\uC218\uAC15", "\uD559\uC2B5\uC790", "\uD559\uC0DD", "\uAD50\uC0AC", "\uAC15\uC0AC", "\uD559\uC2B5 \uBAA9\uD45C", "\uD559\uC2B5 \uD65C\uB3D9", "\uC9C4\uB3C4", "\uACFC\uC81C", "\uC81C\uCD9C", "\uD53C\uB4DC\uBC31", "\uD3C9\uAC00", "\uC644\uB8CC \uAE30\uC900", "\uC7AC\uC81C\uCD9C", "\uAD50\uC721 \uCF58\uD150\uCE20", "\uCF54\uB529 \uAD50\uC721", "\uCF54\uB529\uAD50\uC721"],
17876
+ en: ["education", "learning", "course", "lesson", "learner", "student", "teacher", "instructor", "learning objectives", "learning activity", "progress", "assignment", "submission", "feedback", "assessment", "completion criteria", "resubmission", "coding education"]
17877
+ },
17878
+ text: {
17879
+ ko: {
17880
+ domain: "\uAD50\uC721",
17881
+ title: "\uD559\uC2B5 \uD65C\uB3D9\xB7\uC81C\uCD9C\xB7\uD53C\uB4DC\uBC31\uACFC \uC644\uB8CC \uAE30\uC900",
17882
+ summary: "\uCF58\uD150\uCE20\uB97C \uBCF8 \uAC83\uACFC \uD559\uC2B5\uC744 \uB9C8\uCE5C \uAC83\uC744 \uAD6C\uBD84\uD558\uACE0, \uD65C\uB3D9\xB7\uC81C\uCD9C\xB7\uD53C\uB4DC\uBC31\xB7\uC9C4\uB3C4\uB97C \uC5F0\uACB0\uD569\uB2C8\uB2E4.",
17883
+ overview: "\uAC15\uC758, \uB3C5\uC11C, \uD034\uC988, \uC2E4\uC2B5, \uD1A0\uB860\uCC98\uB7FC \uC11C\uB85C \uB2E4\uB978 \uD559\uC2B5 \uD65C\uB3D9\uC5D0\uB294 \uC11C\uB85C \uB2E4\uB978 \uACB0\uACFC\uBB3C\uC774 \uC0DD\uAE41\uB2C8\uB2E4. \uAD50\uC721 \uC11C\uBE44\uC2A4\uB97C \uB9CC\uB4E4 \uB54C\uB294 \uD559\uC2B5 \uBAA9\uD45C\uC5D0 \uB9DE\uB294 \uD65C\uB3D9\uACFC \uC774\uD574\uB97C \uD655\uC778\uD560 \uC99D\uAC70\uB97C \uC815\uD55C \uB4A4, \uB204\uAC00 \uC5B4\uB5A4 \uC870\uAC74\uC73C\uB85C \uC644\uB8CC\uB97C \uD310\uB2E8\uD560\uC9C0 \uC5F0\uACB0\uD574\uC57C \uD569\uB2C8\uB2E4. \uC544\uB798 \uD750\uB984\uC740 \uC124\uACC4\uB97C \uC704\uD55C \uCC38\uACE0\uC774\uBA70, \uB300\uC0C1 \uD559\uC2B5\uC790\uC640 \uC218\uC5C5 \uBC29\uC2DD\uC5D0 \uB9DE\uAC8C \uACB0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4.",
17884
+ concepts: [
17885
+ { name: "\uD559\uC2B5 \uBAA9\uD45C\uC640 \uD655\uC778\uD560 \uC99D\uAC70", description: "\uBC30\uC6B4 \uB4A4 \uD560 \uC218 \uC788\uC5B4\uC57C \uD558\uB294 \uD589\uB3D9\uACFC \uADF8\uAC83\uC744 \uD655\uC778\uD560 \uACB0\uACFC\uBB3C\uC744 \uC9DD\uC9D3\uC2B5\uB2C8\uB2E4. \uC608\uB97C \uB4E4\uC5B4 \uAC1C\uB150 \uC124\uBA85, \uD480\uC774 \uACFC\uC815, \uBC1C\uD45C\uB098 \uC2E4\uC2B5 \uACB0\uACFC\uAC00 \uC99D\uAC70\uAC00 \uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4." },
17886
+ { name: "\uD559\uC2B5 \uD65C\uB3D9", description: "\uC5F4\uB78C\xB7\uC2DC\uCCAD\xB7\uBB38\uC81C \uD480\uC774\xB7\uC791\uC131\xB7\uD1A0\uB860 \uB4F1 \uD559\uC2B5\uC790\uAC00 \uC218\uD589\uD558\uB294 \uB2E8\uC704\uC785\uB2C8\uB2E4. \uD65C\uB3D9\uB9C8\uB2E4 \uC785\uB825\uACFC \uACB0\uACFC\uAC00 \uB2E4\uB974\uBBC0\uB85C \uBAA8\uB450 \uAC19\uC740 \uC644\uB8CC \uBC84\uD2BC\uC73C\uB85C \uCC98\uB9AC\uD560 \uD544\uC694\uB294 \uC5C6\uC2B5\uB2C8\uB2E4." },
17887
+ { name: "\uC81C\uCD9C\uACFC \uD3C9\uAC00", description: "\uCD08\uC548 \uC800\uC7A5, \uCD5C\uC885 \uC81C\uCD9C, \uD3C9\uAC00 \uACB0\uACFC\uB97C \uAD6C\uBD84\uD569\uB2C8\uB2E4. \uD3C9\uAC00 \uBC29\uC2DD\uC740 \uC790\uAE30 \uD655\uC778\xB7\uB3D9\uB8CC \uD53C\uB4DC\uBC31\xB7\uAC15\uC0AC \uD655\uC778\xB7\uC790\uB3D9 \uD3C9\uAC00 \uC911 \uBB34\uC5C7\uC744 \uC0AC\uC6A9\uD560\uC9C0 \uC815\uD574\uC57C \uD569\uB2C8\uB2E4." },
17888
+ { name: "\uC9C4\uB3C4\uC640 \uC644\uB8CC", description: "\uC9C4\uB3C4\uB294 \uD65C\uB3D9\uC744 \uC5BC\uB9C8\uB098 \uC9C4\uD589\uD588\uB294\uC9C0, \uC644\uB8CC\uB294 \uC815\uD55C \uC870\uAC74\uC744 \uCDA9\uC871\uD588\uB294\uC9C0\uB97C \uB098\uD0C0\uB0C5\uB2C8\uB2E4. \uC5F4\uB78C, \uC81C\uCD9C, \uD1B5\uACFC\uB97C \uAC19\uC740 \uC0C1\uD0DC\uB85C \uCDE8\uAE09\uD558\uC9C0 \uC54A\uB3C4\uB85D \uAE30\uC900\uC744 \uBA85\uC2DC\uD569\uB2C8\uB2E4." },
17889
+ { name: "\uC7AC\uC2DC\uB3C4\uC640 \uB2E4\uC74C \uB2E8\uACC4 \uC811\uADFC", description: "\uB2E4\uC2DC \uD480\uAC70\uB098 \uC7AC\uC81C\uCD9C\uD560 \uC218 \uC788\uB294 \uC870\uAC74, \uC774\uC804 \uACB0\uACFC\uC758 \uBCF4\uC874, \uB2E4\uC74C \uD65C\uB3D9\uC744 \uC5F4 \uC870\uAC74\uC740 \uAC01\uAC01 \uACB0\uC815\uD560 \uC815\uCC45\uC785\uB2C8\uB2E4. \uBAA8\uB4E0 \uC218\uC5C5\uC5D0 \uC21C\uCC28 \uC7A0\uAE08\uC744 \uC801\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." }
17890
+ ],
17891
+ workflow: [
17892
+ { name: "\uBAA9\uD45C\uC640 \uD65C\uB3D9 \uC815\uD558\uAE30", description: "\uD559\uC2B5 \uB300\uC0C1\uACFC \uBAA9\uD45C\uB97C \uC815\uD558\uACE0 \uC77D\uAE30\xB7\uD034\uC988\xB7\uACFC\uC81C \uB4F1 \uD544\uC694\uD55C \uD65C\uB3D9 \uBC0F \uACB0\uACFC\uBB3C\uC744 \uACE0\uB985\uB2C8\uB2E4." },
17893
+ { name: "\uC9C4\uD589\uACFC \uC81C\uCD9C \uAD6C\uBD84\uD558\uAE30", description: "\uC791\uC131 \uC911\uC778 \uCD08\uC548\uACFC \uC81C\uCD9C\uD55C \uACB0\uACFC\uBB3C\uC744 \uAD6C\uBD84\uD574 \uBCF4\uC5EC \uC90D\uB2C8\uB2E4. \uC800\uC7A5\uC774\uB098 \uC81C\uCD9C\uC5D0 \uC2E4\uD328\uD588\uC744 \uB54C\uB294 \uC2E4\uD328 \uC0C1\uD0DC\uC640 \uB2E4\uC2DC \uD560 \uC218 \uC788\uB294 \uD589\uB3D9\uC744 \uC548\uB0B4\uD569\uB2C8\uB2E4." },
17894
+ { name: "\uACB0\uACFC\uC640 \uD53C\uB4DC\uBC31 \uC5F0\uACB0\uD558\uAE30", description: "\uB204\uAC00 \uC5B4\uB5A4 \uAE30\uC900\uC73C\uB85C \uD655\uC778\uD558\uB294\uC9C0, \uACB0\uACFC\uC640 \uD53C\uB4DC\uBC31\uC744 \uC5B8\uC81C \uACF5\uAC1C\uD558\uB294\uC9C0 \uC815\uD569\uB2C8\uB2E4. \uD3C9\uAC00 \uB300\uAE30\uC640 \uD1B5\uACFC\uB97C \uAD6C\uBD84\uD569\uB2C8\uB2E4." },
17895
+ { name: "\uC644\uB8CC\uC640 \uD6C4\uC18D \uD589\uB3D9 \uC548\uB0B4\uD558\uAE30", description: "\uD569\uC758\uD55C \uC870\uAC74\uC744 \uCDA9\uC871\uD558\uBA74 \uC644\uB8CC\uB85C \uD45C\uC2DC\uD569\uB2C8\uB2E4. \uC7AC\uC2DC\uB3C4\uB098 \uB2E4\uC74C \uD65C\uB3D9\uC774 \uAC00\uB2A5\uD558\uB2E4\uBA74 \uADF8 \uC870\uAC74\uACFC \uC774\uB3D9 \uACBD\uB85C\uB97C \uD568\uAED8 \uBCF4\uC5EC \uC90D\uB2C8\uB2E4." }
17896
+ ],
17897
+ pitfalls: [
17898
+ { name: "\uC5F4\uB78C\uC744 \uC774\uD574\uB098 \uD1B5\uACFC\uB85C \uD574\uC11D\uD558\uAE30", description: "\uD398\uC774\uC9C0\uB97C \uC5F4\uAC70\uB098 \uC601\uC0C1\uC744 \uB05D\uAE4C\uC9C0 \uBCF8 \uAE30\uB85D\uB9CC\uC73C\uB85C \uC774\uD574\uB97C \uC99D\uBA85\uD560 \uC218\uB294 \uC5C6\uC2B5\uB2C8\uB2E4. \uC5F4\uB78C \uC644\uB8CC\uC640 \uD3C9\uAC00 \uD1B5\uACFC\uC758 \uC758\uBBF8\uB97C \uD654\uBA74\uACFC \uB370\uC774\uD130\uC5D0\uC11C \uAD6C\uBD84\uD558\uC138\uC694." },
17899
+ { name: "\uCD08\uC548\xB7\uC81C\uCD9C\xB7\uD3C9\uAC00\uB97C \uD558\uB098\uB85C \uD569\uCE58\uAE30", description: "\uC790\uB3D9 \uC800\uC7A5\uB410\uB2E4\uB294 \uC548\uB0B4\uAC00 \uCD5C\uC885 \uC81C\uCD9C\uC744 \uB73B\uD558\uBA74 \uD63C\uB780\uC774 \uC0DD\uAE41\uB2C8\uB2E4. \uB9C8\uAC10 \uD6C4 \uC81C\uCD9C, \uC7AC\uC81C\uCD9C, \uD3C9\uAC00 \uB300\uAE30 \uC2DC \uAC00\uB2A5\uD55C \uD589\uB3D9\uB3C4 \uBA3C\uC800 \uACB0\uC815\uD558\uC138\uC694." },
17900
+ { name: "\uC9C4\uB3C4 \uBE44\uC728\uB9CC \uBCF4\uC5EC \uC8FC\uAE30", description: "\uBB34\uC5C7\uC744 \uAE30\uC900\uC73C\uB85C \uACC4\uC0B0\uD55C \uBE44\uC728\uC778\uC9C0, \uB0A8\uC740 \uD65C\uB3D9\uC740 \uBB34\uC5C7\uC778\uC9C0 \uD568\uAED8 \uC54C\uB824\uC57C \uD569\uB2C8\uB2E4. \uD65C\uB3D9 \uC218\xB7\uBC30\uC810\xB7\uC2DC\uCCAD\uB7C9 \uC911 \uC5B4\uB5A4 \uAE30\uC900\uC778\uC9C0 \uD655\uC778\uD558\uC138\uC694." }
17901
+ ],
17902
+ questions: [
17903
+ "\uB204\uAC00 \uBC30\uC6B0\uBA70 \uC5B4\uB5A4 \uC120\uC218 \uC9C0\uC2DD\xB7\uC811\uADFC \uD658\uACBD\uC744 \uAC00\uC9C0\uACE0 \uC788\uB098\uC694? \uD559\uC2B5 \uB4A4 \uBB34\uC5C7\uC744 \uD560 \uC218 \uC788\uC5B4\uC57C \uD558\uB098\uC694?",
17904
+ "\uAC15\uC758\xB7\uD034\uC988\xB7\uACFC\uC81C\xB7\uD1A0\uB860 \uC911 \uC5B4\uB5A4 \uD65C\uB3D9\uC774 \uD544\uC694\uD558\uBA70, \uD559\uC2B5 \uACB0\uACFC\uB97C \uBB34\uC5C7\uC73C\uB85C \uD655\uC778\uD558\uB098\uC694?",
17905
+ "\uC5F4\uB78C\xB7\uC81C\uCD9C\xB7\uD3C9\uAC00 \uD1B5\uACFC \uC911 \uBB34\uC5C7\uC744 \uC644\uB8CC\uB85C \uBCF4\uB098\uC694? \uC9C4\uB3C4 \uBE44\uC728\uC758 \uBD84\uBAA8\uC640 \uACC4\uC0B0 \uAE30\uC900\uC740 \uBB34\uC5C7\uC778\uAC00\uC694?",
17906
+ "\uB204\uAC00 \uD3C9\uAC00\uD558\uACE0 \uC5B8\uC81C \uACB0\uACFC\xB7\uD53C\uB4DC\uBC31\uC744 \uACF5\uAC1C\uD558\uB098\uC694? \uD559\uC2B5\uC790\uC640 \uAC15\uC0AC\uAC00 \uBCFC \uC218 \uC788\uB294 \uC815\uBCF4\uB294 \uBB34\uC5C7\uC778\uAC00\uC694?",
17907
+ "\uB9C8\uAC10\xB7\uC9C0\uAC01 \uC81C\uCD9C\xB7\uC7AC\uC2DC\uB3C4\xB7\uC7AC\uC81C\uCD9C\uC740 \uC5B4\uB5BB\uAC8C \uCC98\uB9AC\uD558\uB098\uC694? \uB2E4\uC74C \uD65C\uB3D9\uC740 \uC790\uC720\uB86D\uAC8C \uC5F4\uB9AC\uB098\uC694, \uC870\uAC74\uC774 \uC788\uB098\uC694?",
17908
+ "\uC9C4\uB3C4 \uC800\uC7A5\xB7\uC81C\uCD9C\xB7\uD3C9\uAC00\uAC00 \uC2E4\uD328\uD558\uAC70\uB098 \uC9C0\uC5F0\uB420 \uB54C \uBB34\uC5C7\uC744 \uBCF4\uC874\uD558\uACE0 \uC5B4\uB5A4 \uC0C1\uD0DC\uC640 \uC7AC\uC2DC\uB3C4 \uBC29\uBC95\uC744 \uBCF4\uC5EC \uC8FC\uB098\uC694?"
17909
+ ],
17910
+ example: "\uC608\uC2DC: \uAE00\uC4F0\uAE30 \uC218\uC5C5\uC5D0\uC11C \uC790\uB8CC \uC77D\uAE30\uC640 \uAE00 \uC81C\uCD9C\uC744 \uBCC4\uB3C4 \uD65C\uB3D9\uC73C\uB85C \uB461\uB2C8\uB2E4. \uC77D\uAE30\uB294 \uD559\uC2B5\uC790\uAC00 \uD655\uC778\uD558\uACE0, \uAE00\uC740 \uCD08\uC548 \u2192 \uC81C\uCD9C \u2192 \uAC15\uC0AC \uD53C\uB4DC\uBC31\uC73C\uB85C \uC9C4\uD589\uD55C\uB2E4\uACE0 \uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC81C\uCD9C\uC744 \uC644\uB8CC\uB85C \uBCFC\uC9C0 \uD53C\uB4DC\uBC31 \uC774\uD6C4 \uC218\uC815\uC744 \uB9C8\uCCD0\uC57C \uC644\uB8CC\uC778\uC9C0 \uBA3C\uC800 \uACB0\uC815\uD55C \uB4A4 \uC0C1\uD0DC \uBC30\uC9C0\uC640 \uB2E4\uC74C \uD589\uB3D9\uC744 \uAD6C\uC131\uD569\uB2C8\uB2E4. \uC774\uB294 \uD558\uB098\uC758 \uC608\uC2DC\uC774\uBA70 \uC2E4\uC81C \uC218\uC5C5\uC758 \uD655\uC815 \uADDC\uCE59\uC740 \uC544\uB2D9\uB2C8\uB2E4.",
17911
+ relatedDesigns: [
17912
+ { id: "onboarding-progress-checklist", reason: "\uD544\uC218 \uD559\uC2B5 \uD65C\uB3D9\uACFC \uB0A8\uC740 \uB2E8\uACC4\uB97C \uBAA9\uB85D\uC73C\uB85C \uD45C\uD604\uD560 \uB54C \uCC38\uACE0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uB370\uBAA8\uC758 \uCCB4\uD06C \uB3D9\uC791\uC774 \uC2E4\uC81C \uD559\uC2B5 \uC644\uB8CC\uB97C \uD310\uB2E8\uD558\uC9C0\uB294 \uC54A\uC2B5\uB2C8\uB2E4." },
17913
+ { id: "inline-edit-field", reason: "\uD65C\uB3D9 \uC81C\uBAA9\uC774\uB098 \uC9E7\uC740 \uB2F5\uBCC0\uC758 \uD3B8\uC9D1\xB7\uC800\uC7A5 \uC0C1\uD0DC \uD45C\uD604\uC5D0 \uCC38\uACE0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uACFC\uC81C \uC800\uC7A5\xB7\uC81C\uCD9C\xB7\uD3C9\uAC00 \uAE30\uB2A5\uC740 \uBCC4\uB3C4\uB85C \uAD6C\uD604\uD574\uC57C \uD569\uB2C8\uB2E4." }
17914
+ ],
17915
+ promptSnippet: "\uAD50\uC721 \uC11C\uBE44\uC2A4\uB97C \uC124\uACC4\uD574 \uC918. \uBA3C\uC800 \uD559\uC2B5 \uB300\uC0C1\xB7\uBAA9\uD45C\xB7\uD65C\uB3D9 \uC885\uB958\xB7\uC774\uD574\uB97C \uD655\uC778\uD560 \uACB0\uACFC\uBB3C\xB7\uD3C9\uAC00 \uC8FC\uCCB4\xB7\uC644\uB8CC \uAE30\uC900\xB7\uC9C4\uB3C4 \uACC4\uC0B0\uBC95\xB7\uB9C8\uAC10\uACFC \uC7AC\uC81C\uCD9C \uC815\uCC45\uC744 \uC9C8\uBB38\uD558\uACE0, \uC815\uD574\uC9C0\uC9C0 \uC54A\uC740 \uC815\uCC45\uC744 \uAC00\uC815\uD558\uC9C0 \uB9D0\uC544 \uC918.\n\uD655\uC778\uD55C \uB0B4\uC6A9\uC744 \uBC14\uD0D5\uC73C\uB85C \uB2E4\uC74C\uC744 \uC815\uB9AC\uD574 \uC918.\n1. \uD559\uC2B5\uC790\uC640 \uAC15\uC0AC\uC758 \uC5ED\uD560 \uBC0F \uAC01\uC790 \uBCFC \uC815\uBCF4\uC640 \uAC00\uB2A5\uD55C \uD589\uB3D9.\n2. \uD65C\uB3D9\uBCC4 \uC9C4\uD589 \uC0C1\uD0DC\uC640, \uD574\uB2F9\uD558\uB294 \uACBD\uC6B0 \uCD08\uC548\xB7\uC81C\uCD9C\xB7\uD3C9\uAC00 \uB300\uAE30\xB7\uACB0\uACFC\uC758 \uAD6C\uBD84.\n3. \uACB0\uACFC \uACF5\uAC1C, \uD53C\uB4DC\uBC31, \uC7AC\uC2DC\uB3C4 \uBC0F \uB2E4\uC74C \uD65C\uB3D9 \uC811\uADFC \uC870\uAC74.\n4. \uC800\uC7A5\xB7\uC81C\uCD9C\xB7\uD3C9\uAC00 \uC2E4\uD328\uB098 \uC9C0\uC5F0\uC5D0\uC11C \uBCF4\uC874\uD560 \uC815\uBCF4\uC640 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uC5EC \uC904 \uC0C1\uD0DC.\n5. \uC704 \uADDC\uCE59\uC5D0 \uB9DE\uB294 \uD65C\uB3D9 \uBAA9\uB85D\xB7\uC0C1\uD0DC \uBC30\uC9C0\xB7\uD53C\uB4DC\uBC31 \uD654\uBA74\uACFC \uB514\uC790\uC778 \uC120\uD0DD \uC774\uC720.\n\uC5F4\uB78C\uC744 \uD3C9\uAC00 \uD1B5\uACFC\uB85C \uAC04\uC8FC\uD558\uC9C0 \uB9D0\uACE0, \uC608\uC2DC \uC815\uCC45\uC744 \uD655\uC815 \uC694\uAD6C\uC0AC\uD56D\uC73C\uB85C \uC4F0\uC9C0 \uB9D0\uC544 \uC918. \uC2E4\uC81C \uC800\uC7A5\xB7\uD3C9\uAC00\xB7\uCF54\uB4DC \uC2E4\uD589 \uB4F1\uC740 \uC9C0\uC6D0 \uD658\uACBD\uC744 \uD655\uC778\uD574 \uAD6C\uD604\uD558\uACE0, \uB370\uBAA8\uB9CC \uC788\uB2E4\uBA74 \uC2E4\uC81C \uAE30\uB2A5\uACFC \uAD6C\uBD84\uD574 \uC918."
17916
+ },
17917
+ en: {
17918
+ domain: "Education",
17919
+ title: "Learning activities, submissions, feedback and completion",
17920
+ summary: "Distinguish viewing content from completing learning, and connect activities, submissions, feedback and progress.",
17921
+ overview: "Lessons, reading, quizzes, practice and discussions produce different kinds of learner work. When designing an education service, connect a learning outcome with an activity and evidence of understanding, then decide who judges completion and under which conditions. The workflow below is a design reference to adapt to the learners and course format.",
17922
+ concepts: [
17923
+ { name: "Learning outcome and evidence", description: "Pair what learners should be able to do with evidence that demonstrates it, such as an explanation, worked solution, presentation or practice result." },
17924
+ { name: "Learning activity", description: "A unit such as reading, watching, solving, writing or discussing. Inputs and results differ, so activities do not all need the same completion button." },
17925
+ { name: "Submission and assessment", description: "Distinguish saving a draft, submitting final work and receiving an assessment result. Decide whether assessment uses self-checks, peers, instructors or automation." },
17926
+ { name: "Progress and completion", description: "Progress describes how far an activity has advanced; completion means agreed criteria have been met. Define criteria so viewing, submitting and passing do not become one status." },
17927
+ { name: "Retries and access to the next activity", description: "Retry and resubmission conditions, preservation of previous results and access to later activities are separate policy decisions. Sequential locking is not required for every course." }
17928
+ ],
17929
+ workflow: [
17930
+ { name: "Choose an outcome and activity", description: "Identify the learners and outcome, then select activities and evidence such as reading, quizzes or assignments." },
17931
+ { name: "Separate work in progress from submission", description: "Show drafts separately from submitted work. If saving or submitting fails, show the failure and the actions learners can take next." },
17932
+ { name: "Connect results and feedback", description: "Decide who assesses work, against which criteria, and when results and feedback become visible. Separate pending assessment from passing." },
17933
+ { name: "Explain completion and next actions", description: "Mark completion when agreed criteria are met. If retries or later activities are available, explain the conditions and route to them." }
17934
+ ],
17935
+ pitfalls: [
17936
+ { name: "Treating viewing as understanding or passing", description: "Opening a page or finishing a video does not by itself demonstrate understanding. Distinguish viewing completion from assessment success in the interface and data." },
17937
+ { name: "Merging drafts, submissions and assessment", description: "An autosave message should not imply final submission. Decide which actions remain available after deadlines, during resubmission and while assessment is pending." },
17938
+ { name: "Showing only a progress percentage", description: "Explain the basis of the percentage and which activities remain. Confirm whether it uses activity counts, weights or viewing time." }
17939
+ ],
17940
+ questions: [
17941
+ "Who are the learners, what prior knowledge and access environment do they have, and what should they be able to do afterwards?",
17942
+ "Which lessons, quizzes, assignments or discussions are needed, and what evidence will demonstrate learning?",
17943
+ "Does completion require viewing, submission or passing? What denominator and calculation define progress?",
17944
+ "Who assesses work and when are results and feedback released? What can learners and instructors see?",
17945
+ "How are deadlines, late work, retries and resubmissions handled? Are later activities freely accessible or conditional?",
17946
+ "If progress saving, submission or assessment fails or is delayed, what is preserved and what status and retry path are shown?"
17947
+ ],
17948
+ example: "Example: a writing course could separate reading materials from submitting an essay. Learners confirm reading, while essays move through draft, submission and instructor feedback. Decide whether submission or revision after feedback counts as completion before designing status badges and next actions. This is one example, not an established policy for the service being built.",
17949
+ relatedDesigns: [
17950
+ { id: "onboarding-progress-checklist", reason: "A reference for listing required activities and remaining steps. The demo checkbox does not determine actual learning completion." },
17951
+ { id: "inline-edit-field", reason: "A reference for editing and save-status presentation of activity titles or short answers. Assignment persistence, submission and assessment need separate implementation." }
17952
+ ],
17953
+ promptSnippet: "Help design an education service. First ask about learners, outcomes, activity types, evidence of understanding, assessors, completion criteria, progress calculation, deadlines and resubmission policy. Do not assume undecided policies.\nBased on the answers, describe:\n1. Learner and instructor roles, visible information and available actions.\n2. Activity progress and, where applicable, distinct draft, submitted, pending assessment and result states.\n3. Result release, feedback, retries and access conditions for later activities.\n4. Information to preserve and status to show when saving, submission or assessment fails or is delayed.\n5. Activity lists, status badges and feedback screens that express these rules, with reasons for the design choices.\nDo not equate viewing with passing or turn example policies into agreed requirements. Confirm the supported environment for real persistence, assessment or code execution, and distinguish any demo from actual functionality."
17954
+ }
17955
+ },
17956
+ sources: [
17957
+ { title: "MoodleDocs \u2014 Activity completion", url: "https://docs.moodle.org/en/Activity_completion" },
17958
+ { title: "MoodleDocs \u2014 Assignment settings", url: "https://docs.moodle.org/en/Assignment_settings" }
17959
+ ]
17960
+ }];
17961
+
17962
+ // ../../packages/catalog/src/domain-knowledge/community.ts
17963
+ var COMMUNITY_KNOWLEDGE = [{
17964
+ id: "community-post-visibility-and-moderation",
17965
+ aliases: {
17966
+ ko: ["\uAC8C\uC2DC\uD310 \uC11C\uBE44\uC2A4", "\uD68C\uC6D0 \uCEE4\uBBA4\uB2C8\uD2F0", "\uD68C\uC6D0 \uC804\uC6A9 \uAC8C\uC2DC\uD310", "\uBE44\uACF5\uAC1C \uADF8\uB8F9", "\uAC8C\uC2DC\uAE00 \uC6B4\uC601", "\uC2E0\uACE0 \uCC98\uB9AC"],
17967
+ en: ["community forum", "members-only forum", "private group", "post moderation", "report review"]
17968
+ },
17969
+ useCases: {
17970
+ ko: ["\uAC8C\uC2DC\uBB3C\uACFC \uB313\uAE00\uC774 \uC788\uB294 \uCEE4\uBBA4\uB2C8\uD2F0\uB97C \uB9CC\uB4E4 \uB54C", "\uD68C\uC6D0 \uC804\uC6A9\xB7\uADF8\uB8F9\uBCC4 \uACF5\uAC1C \uBC94\uC704\uC640 \uC811\uADFC \uAD8C\uD55C\uC744 \uC124\uACC4\uD560 \uB54C", "\uC2E0\uACE0 \uC811\uC218\xB7\uAC80\uD1A0\xB7\uC6B4\uC601 \uC870\uCE58\uB97C \uAD6C\uBD84\uD560 \uB54C", "\uAC8C\uC2DC\uBB3C \uC228\uAE40\xB7\uC0AD\uC81C\xB7\uBCF5\uAD6C\uC640 \uB313\uAE00 \uC7A0\uAE08\uC744 \uC124\uACC4\uD560 \uB54C"],
17971
+ en: ["Building a community with posts and comments", "Designing members-only or private-group visibility and access permissions", "Separating reports, review and moderation actions", "Designing post hiding, deletion, restoration and comment locking"]
17972
+ },
17973
+ keywords: {
17974
+ ko: ["\uCEE4\uBBA4\uB2C8\uD2F0", "\uAC8C\uC2DC\uD310", "\uAC8C\uC2DC\uBB3C", "\uB313\uAE00", "\uACF5\uAC1C \uBC94\uC704", "\uAD8C\uD55C", "\uD68C\uC6D0", "\uC6B4\uC601\uC790", "\uC2E0\uACE0", "\uC228\uAE40", "\uC0AD\uC81C", "\uC7A0\uAE08", "\uC6B4\uC601", "\uC811\uADFC \uC81C\uC5B4"],
17975
+ en: ["community", "forum", "post", "discussion", "comment", "visibility", "permissions", "member", "moderator", "report", "hide", "delete", "lock", "moderation", "access control"]
17976
+ },
17977
+ text: {
17978
+ ko: {
17979
+ domain: "\uCEE4\uBBA4\uB2C8\uD2F0",
17980
+ title: "\uAC8C\uC2DC\uBB3C\uC758 \uACF5\uAC1C \uBC94\uC704\xB7\uAD8C\uD55C\uACFC \uC6B4\uC601 \uC0C1\uD0DC",
17981
+ summary: "\uB204\uAC00 \uAE00\uC744 \uBCF4\uACE0 \uD589\uB3D9\uD560 \uC218 \uC788\uB294\uC9C0 \uC815\uD558\uACE0, \uAC8C\uC2DC\xB7\uC2E0\uACE0\xB7\uC228\uAE40\xB7\uC7A0\uAE08\uC744 \uC11C\uB85C \uB2E4\uB978 \uC0C1\uD0DC\uB85C \uB2E4\uB8F9\uB2C8\uB2E4.",
17982
+ overview: "\uCEE4\uBBA4\uB2C8\uD2F0\uC758 \uAE00\uC740 \uACF5\uAC1C \uC5EC\uBD80\uB9CC\uC73C\uB85C \uC124\uBA85\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB204\uAC00 \uBCFC \uC218 \uC788\uB294\uC9C0, \uB204\uAC00 \uC791\uC131\xB7\uC218\uC815\xB7\uB313\uAE00\uC744 \uB2EC \uC218 \uC788\uB294\uC9C0, \uC6B4\uC601 \uC911 \uC5B4\uB5A4 \uC0C1\uD0DC\uB85C \uBC14\uB00C\uB294\uC9C0\uB97C \uB098\uB204\uC5B4 \uC124\uACC4\uD574\uC57C \uD569\uB2C8\uB2E4. \uC544\uB798 \uAC1C\uB150\uC740 \uAC8C\uC2DC\uBB3C\uACFC \uB313\uAE00\uC744 \uC124\uACC4\uD558\uAE30 \uC704\uD55C \uCC38\uACE0\uC774\uBA70 \uD2B9\uC815 \uC11C\uBE44\uC2A4\uC758 \uAD8C\uD55C\uC774\uB098 \uC81C\uC7AC \uC815\uCC45\uC744 \uAE30\uBCF8\uAC12\uC73C\uB85C \uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
17983
+ concepts: [
17984
+ { name: "\uACF5\uAC1C \uBC94\uC704\uC640 \uC811\uADFC \uAD8C\uD55C", description: "\uC804\uCCB4 \uACF5\uAC1C\xB7\uD68C\uC6D0\xB7\uD2B9\uC815 \uADF8\uB8F9\uCC98\uB7FC \uB204\uAC00 \uB0B4\uC6A9\uC744 \uBCFC \uC218 \uC788\uB294\uC9C0 \uC815\uD569\uB2C8\uB2E4. \uC5F4\uB78C \uAD8C\uD55C\uACFC \uAE00 \uC791\uC131\xB7\uB313\uAE00 \uC791\uC131 \uAD8C\uD55C\uC740 \uBCC4\uB3C4\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4." },
17985
+ { name: "\uC5ED\uD560\uACFC \uD589\uB3D9", description: "\uBC29\uBB38\uC790\xB7\uD68C\uC6D0\xB7\uC791\uC131\uC790\xB7\uC6B4\uC601\uC790\uBCC4 \uAC00\uB2A5\uD55C \uD589\uB3D9\uC744 \uAD6C\uBD84\uD569\uB2C8\uB2E4. \uBC84\uD2BC\uC744 \uC228\uAE30\uB294 \uD654\uBA74 \uCC98\uB9AC\uB9CC\uC73C\uB85C \uC2E4\uC81C \uB370\uC774\uD130 \uC811\uADFC\uC774\uB098 \uC218\uC815\uC744 \uB9C9\uC744 \uC218\uB294 \uC5C6\uC2B5\uB2C8\uB2E4." },
17986
+ { name: "\uAC8C\uC2DC \uC0C1\uD0DC", description: "\uCD08\uC548\xB7\uAC8C\uC2DC\uB428\xB7\uC228\uAE40\xB7\uC0AD\uC81C\uCC98\uB7FC \uAE00\uC758 \uB178\uCD9C\uACFC \uBCF4\uAD00 \uC0C1\uD0DC\uB97C \uAD6C\uBD84\uD569\uB2C8\uB2E4. \uC228\uAE40\uACFC \uC601\uAD6C \uC0AD\uC81C\uB294 \uBCF5\uAD6C \uAC00\uB2A5\uC131\uACFC \uAE30\uC874 \uB9C1\uD06C \uCC98\uB9AC\uC5D0\uC11C \uB2E4\uB974\uBBC0\uB85C \uC758\uBBF8\uB97C \uC815\uD574\uC57C \uD569\uB2C8\uB2E4." },
17987
+ { name: "\uC2E0\uACE0\uC640 \uC6B4\uC601 \uD310\uB2E8", description: "\uC2E0\uACE0 \uC811\uC218\uB294 \uC704\uBC18 \uD655\uC815\uACFC \uB2E4\uB985\uB2C8\uB2E4. \uD655\uC778 \uC911\xB7\uCC98\uB9AC\uB428 \uB4F1 \uAC80\uD1A0 \uC0C1\uD0DC, \uB2F4\uB2F9\uC790\uC640 \uAC00\uB2A5\uD55C \uC870\uCE58\uB97C \uC815\uD558\uACE0 \uAC8C\uC2DC \uC0C1\uD0DC\uC640 \uAD6C\uBD84\uD569\uB2C8\uB2E4." },
17988
+ { name: "\uB313\uAE00 \uC7A0\uAE08\uACFC \uBCC0\uACBD \uAE30\uB85D", description: "\uB313\uAE00 \uC7A0\uAE08\uC740 \uC77D\uAE30\uB098 \uC0AD\uC81C\uC640 \uB2E4\uB978 \uC870\uCE58\uC785\uB2C8\uB2E4. \uB204\uAC00 \uC7A0\uAE08\xB7\uD574\uC81C\xB7\uC218\uC815\uC744 \uD560 \uC218 \uC788\uB294\uC9C0, \uBCC0\uACBD \uC774\uC720\uC640 \uAE30\uB85D\uC744 \uB204\uAD6C\uC5D0\uAC8C \uBCF4\uC5EC \uC904\uC9C0 \uACB0\uC815\uD569\uB2C8\uB2E4." }
17989
+ ],
17990
+ workflow: [
17991
+ { name: "\uC791\uC131 \uC804\uC5D0 \uBC94\uC704\uC640 \uAD8C\uD55C \uD655\uC778\uD558\uAE30", description: "\uC791\uC131\uC790\uAC00 \uAC8C\uC2DC\uD560 \uC704\uCE58\uC640 \uACF5\uAC1C \uBC94\uC704\uB97C \uC774\uD574\uD558\uAC8C \uD558\uACE0, \uC77D\uAE30\xB7\uC791\uC131\xB7\uB313\uAE00 \uAD8C\uD55C\uC744 \uAC01\uAC01 \uC815\uD569\uB2C8\uB2E4." },
17992
+ { name: "\uC791\uC131\xB7\uAC8C\uC2DC\xB7\uC218\uC815 \uC5F0\uACB0\uD558\uAE30", description: "\uCD08\uC548 \uC800\uC7A5\uACFC \uAC8C\uC2DC\uB97C \uAD6C\uBD84\uD558\uACE0, \uAC8C\uC2DC \uD6C4 \uC218\uC815 \uAC00\uB2A5 \uBC94\uC704\uC640 \uACF5\uAC1C \uBC94\uC704 \uBCC0\uACBD \uC2DC \uAE30\uC874 \uB9C1\uD06C\xB7\uAC80\uC0C9 \uB178\uCD9C \uCC98\uB9AC\uB97C \uC815\uD569\uB2C8\uB2E4." },
17993
+ { name: "\uC2E0\uACE0\uB97C \uAC80\uD1A0\uC640 \uC870\uCE58\uB85C \uC5F0\uACB0\uD558\uAE30", description: "\uC2E0\uACE0 \uB0B4\uC6A9 \uD655\uC778 \uD6C4 \uC815\uCC45\uC5D0 \uB530\uB77C \uC720\uC9C0\xB7\uC228\uAE40\xB7\uC7A0\uAE08 \uB4F1 \uD544\uC694\uD55C \uC870\uCE58\uB97C \uC120\uD0DD\uD569\uB2C8\uB2E4. \uC2E0\uACE0 \uD69F\uC218\uB9CC\uC73C\uB85C \uC790\uB3D9 \uC81C\uC7AC\uD560\uC9C0\uB294 \uBCC4\uB3C4 \uACB0\uC815 \uC0AC\uD56D\uC785\uB2C8\uB2E4." },
17994
+ { name: "\uACB0\uACFC\uC640 \uBCF5\uAD6C \uAC00\uB2A5\uC131 \uC548\uB0B4\uD558\uAE30", description: "\uCC98\uB9AC \uACB0\uACFC\uB97C \uB204\uAD6C\uC5D0\uAC8C \uC54C\uB9B4\uC9C0, \uC228\uAE40 \uD574\uC81C\xB7\uC0AD\uC81C \uBCF5\uAD6C\xB7\uC774\uC758 \uC81C\uAE30 \uACBD\uB85C\uB97C \uC81C\uACF5\uD560\uC9C0 \uC815\uD569\uB2C8\uB2E4. \uC2E4\uD328 \uC2DC \uC870\uCE58\uAC00 \uC644\uB8CC\uB41C \uAC83\uCC98\uB7FC \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." }
17995
+ ],
17996
+ pitfalls: [
17997
+ { name: "\uD654\uBA74\uC5D0\uC11C\uB9CC \uC228\uAE30\uAE30", description: "\uAD8C\uD55C \uC5C6\uB294 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBAA9\uB85D\uC744 \uC228\uACA8\uB3C4 \uC0C1\uC138 \uC8FC\uC18C\xB7\uAC80\uC0C9\xB7\uCCA8\uBD80 \uD30C\uC77C \uACBD\uB85C\uC5D0\uC11C \uB0B4\uC6A9\uC774 \uB178\uCD9C\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC2E4\uC81C \uC81C\uACF5 \uB370\uC774\uD130\uC758 \uAD8C\uD55C \uAC80\uC0AC\uAE4C\uC9C0 \uC5F0\uACB0\uD558\uC138\uC694." },
17998
+ { name: "\uC2E0\uACE0\xB7\uC228\uAE40\xB7\uC0AD\uC81C\xB7\uC7A0\uAE08\uC744 \uD55C \uC0C1\uD0DC\uB85C \uCC98\uB9AC\uD558\uAE30", description: "\uC2E0\uACE0 \uC811\uC218 \uD6C4\uC5D0\uB3C4 \uAE00\uC740 \uACF5\uAC1C\uB420 \uC218 \uC788\uACE0, \uB313\uAE00\uC744 \uC7A0\uADFC \uAE00\uB3C4 \uC77D\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uAC01 \uC870\uCE58\uC758 \uC758\uBBF8\uC640 \uC870\uD569\uC744 \uD655\uC778\uD558\uC138\uC694." },
17999
+ { name: "\uACF5\uAC1C \uBC94\uC704 \uBCC0\uACBD\uC758 \uD30C\uAE09\uC744 \uB193\uCE58\uAE30", description: "\uC804\uCCB4 \uACF5\uAC1C \uAE00\uC744 \uADF8\uB8F9 \uC804\uC6A9\uC73C\uB85C \uBC14\uAFB8\uBA74 \uC774\uC804 \uB9C1\uD06C\xB7\uAC80\uC0C9 \uACB0\uACFC\xB7\uC54C\uB9BC \uBBF8\uB9AC\uBCF4\uAE30\xB7\uCCA8\uBD80\uC5D0\uB3C4 \uAC19\uC740 \uC815\uCC45\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uC774\uBBF8 \uC678\uBD80\uC5D0 \uACF5\uC720\uB41C \uBCF5\uC0AC\uBCF8\uAE4C\uC9C0 \uD68C\uC218\uB41C\uB2E4\uACE0 \uC548\uB0B4\uD558\uC9C0 \uB9C8\uC138\uC694." }
18000
+ ],
18001
+ questions: [
18002
+ "\uBC29\uBB38\uC790\xB7\uD68C\uC6D0\xB7\uD2B9\uC815 \uADF8\uB8F9 \uC911 \uB204\uAC00 \uAE00\xB7\uB313\uAE00\xB7\uCCA8\uBD80\uB97C \uBCFC \uC218 \uC788\uACE0, \uB204\uAC00 \uC791\uC131\uD560 \uC218 \uC788\uB098\uC694?",
18003
+ "\uC791\uC131\uC790\uC640 \uC6B4\uC601\uC790\uB294 \uAC01\uAC01 \uC5B8\uC81C \uBB34\uC5C7\uC744 \uC218\uC815\xB7\uC228\uAE40\xB7\uC0AD\uC81C\xB7\uC7A0\uAE08\uD560 \uC218 \uC788\uB098\uC694?",
18004
+ "\uACF5\uAC1C \uBC94\uC704\uB97C \uBCC0\uACBD\uD558\uBA74 \uAE30\uC874 \uC8FC\uC18C\xB7\uAC80\uC0C9 \uACB0\uACFC\xB7\uC54C\uB9BC\xB7\uCCA8\uBD80 \uD30C\uC77C\uC740 \uC5B4\uB5BB\uAC8C \uBCF4\uC774\uB098\uC694?",
18005
+ "\uC2E0\uACE0\uB294 \uB204\uAC00 \uAC80\uD1A0\uD558\uACE0 \uC5B4\uB5A4 \uC0C1\uD0DC\uC640 \uC870\uCE58\uAC00 \uD544\uC694\uD55C\uAC00\uC694? \uC790\uB3D9 \uC228\uAE40 \uAE30\uC900\uC774 \uC788\uB2E4\uBA74 \uBB34\uC5C7\uC778\uAC00\uC694?",
18006
+ "\uC228\uAE40\xB7\uC0AD\uC81C \uD6C4 \uBCF5\uAD6C\uB098 \uC774\uC758 \uC81C\uAE30\uB97C \uD5C8\uC6A9\uD558\uB098\uC694? \uB313\uAE00\uACFC \uBCC0\uACBD \uAE30\uB85D\uC740 \uB204\uAD6C\uC5D0\uAC8C \uB0A8\uC544 \uBCF4\uC774\uB098\uC694?",
18007
+ "\uB2E4\uB978 \uC6B4\uC601\uC790\uC640 \uB3D9\uC2DC\uC5D0 \uCC98\uB9AC\uD558\uAC70\uB098 \uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC744 \uB54C \uC5B4\uB5A4 \uCD5C\uC885 \uC0C1\uD0DC\uC640 \uC548\uB0B4\uB97C \uBCF4\uC5EC \uC8FC\uB098\uC694?"
18008
+ ],
18009
+ example: "\uC608\uC2DC: \uB3C5\uC11C \uBAA8\uC784\uC758 \uAE00\uC740 \uAC00\uC785\uD55C \uADF8\uB8F9 \uAD6C\uC131\uC6D0\uB9CC \uC77D\uACE0 \uB313\uAE00\uC744 \uB2EC \uC218 \uC788\uAC8C \uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC2E0\uACE0\uAC00 \uB4E4\uC5B4\uC624\uBA74 \uAC80\uD1A0 \uB300\uAE30\uB9CC \uD45C\uC2DC\uD558\uACE0, \uC6B4\uC601\uC790\uAC00 \uD1A0\uB860\uC744 \uC7A0\uADF8\uBA74 \uAE30\uC874 \uAE00\uC740 \uC77D\uC744 \uC218 \uC788\uC9C0\uB9CC \uC0C8 \uB313\uAE00\uC740 \uC81C\uD55C\uD558\uB294 \uBC29\uC2DD\uC785\uB2C8\uB2E4. \uC2E4\uC81C \uACF5\uAC1C \uBC94\uC704\xB7\uC790\uB3D9 \uC870\uCE58\xB7\uBCF5\uAD6C \uC815\uCC45\uC740 \uC6B4\uC601 \uBAA9\uC801\uC5D0 \uB9DE\uAC8C \uBA3C\uC800 \uD655\uC778\uD574\uC57C \uD569\uB2C8\uB2E4.",
18010
+ relatedDesigns: [
18011
+ { id: "faceted-filter-chips", reason: "\uC6B4\uC601 \uBAA9\uB85D\uC5D0\uC11C \uAC8C\uC2DC \uC0C1\uD0DC\uC640 \uC2E0\uACE0 \uAC80\uD1A0 \uC0C1\uD0DC\uB97C \uBCC4\uB3C4 \uC870\uAC74\uC73C\uB85C \uC881\uD788\uB294 \uD45C\uD604\uC5D0 \uCC38\uACE0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC2E4\uC81C \uC811\uADFC \uAD8C\uD55C \uAC80\uC0AC\uB294 \uAD6C\uD604\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." },
18012
+ { id: "inline-delete-confirmation", reason: "\uAC8C\uC2DC\uBB3C \uC0AD\uC81C\uC758 \uC601\uD5A5\uC744 \uD655\uC778\uD558\uB294 \uD654\uBA74\uC5D0 \uCC38\uACE0\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC2E4\uC81C \uC0AD\uC81C\xB7\uBCF5\uAD6C \uC815\uCC45\uACFC \uC800\uC7A5 \uCC98\uB9AC\uB294 \uBCC4\uB3C4\uB85C \uAD6C\uD604\uD574\uC57C \uD569\uB2C8\uB2E4." }
18013
+ ],
18014
+ promptSnippet: "\uCEE4\uBBA4\uB2C8\uD2F0\uC758 \uAC8C\uC2DC\uBB3C\xB7\uB313\uAE00\xB7\uC6B4\uC601 \uD750\uB984\uC744 \uC124\uACC4\uD574 \uC918. \uBA3C\uC800 \uACF5\uAC1C \uBC94\uC704, \uC5ED\uD560\uBCC4 \uC5F4\uB78C\xB7\uC791\uC131\xB7\uC218\uC815 \uAD8C\uD55C, \uAC8C\uC2DC \uC0C1\uD0DC, \uC2E0\uACE0 \uAC80\uD1A0 \uACFC\uC815, \uC228\uAE40\xB7\uC0AD\uC81C\xB7\uC7A0\uAE08\uACFC \uBCF5\uAD6C \uC815\uCC45\uC744 \uD655\uC778\uD558\uACE0 \uBBF8\uC815\uC778 \uADDC\uCE59\uC744 \uAC00\uC815\uD558\uC9C0 \uB9D0\uC544 \uC918.\n\uD655\uC778\uD55C \uB0B4\uC6A9\uC744 \uBC14\uD0D5\uC73C\uB85C \uB2E4\uC74C\uC744 \uC815\uB9AC\uD574 \uC918.\n1. \uC5ED\uD560\uBCC4 \uAC00\uB2A5\uD55C \uD589\uB3D9\uACFC \uAE00\xB7\uB313\uAE00\xB7\uCCA8\uBD80\uC5D0 \uB300\uD55C \uC811\uADFC \uAD8C\uD55C.\n2. \uACF5\uAC1C \uBC94\uC704, \uAC8C\uC2DC \uC0C1\uD0DC, \uC2E0\uACE0 \uAC80\uD1A0 \uC0C1\uD0DC\uB97C \uAD6C\uBD84\uD55C \uB370\uC774\uD130\uC640 \uC0C1\uD0DC \uC804\uD658.\n3. \uAC01 \uC870\uCE58\uAC00 \uAE30\uC874 \uB9C1\uD06C\xB7\uAC80\uC0C9\xB7\uC54C\uB9BC\xB7\uCCA8\uBD80\xB7\uB313\uAE00\uC5D0 \uBBF8\uCE58\uB294 \uC601\uD5A5.\n4. \uC870\uCE58 \uC2E4\uD328\xB7\uC911\uBCF5 \uC694\uCCAD\xB7\uB3D9\uC2DC \uCC98\uB9AC \uC2DC \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uC77C \uACB0\uACFC\uC640 \uC2E4\uC81C \uCD5C\uC885 \uC0C1\uD0DC.\n5. \uBAA9\uB85D\xB7\uC0C1\uC138\xB7\uC791\uC131\xB7\uC2E0\uACE0\xB7\uC6B4\uC601 \uD654\uBA74\uACFC \uC0C1\uD0DC \uBC30\uC9C0\xB7\uD544\uD130\xB7\uD655\uC778 \uB3D9\uC791\uC758 \uC5F0\uACB0.\n\uC2E0\uACE0\uB97C \uC704\uBC18 \uD655\uC815\uC73C\uB85C \uCDE8\uAE09\uD558\uAC70\uB098 \uBC84\uD2BC \uC228\uAE40\uB9CC\uC73C\uB85C \uAD8C\uD55C\uC744 \uAD6C\uD604\uD558\uC9C0 \uB9D0\uC544 \uC918. \uC608\uC2DC \uC815\uCC45\uACFC \uB514\uC790\uC778 \uB370\uBAA8\uB97C \uC2E4\uC81C \uC6B4\uC601 \uADDC\uCE59\uC774\uB098 \uC800\uC7A5 \uAE30\uB2A5\uC73C\uB85C \uAC04\uC8FC\uD558\uC9C0 \uB9D0\uACE0, \uD544\uC694\uD55C \uC778\uC99D\xB7\uC800\uC7A5 \uD658\uACBD\uC740 \uBCC4\uB3C4\uB85C \uD655\uC778\uD574 \uC918."
18015
+ },
18016
+ en: {
18017
+ domain: "Community",
18018
+ title: "Post visibility, permissions and moderation states",
18019
+ summary: "Decide who can read and act, and separate publication, reports, hiding and locking.",
18020
+ overview: "A community post needs more than a public/private flag. Design who can read it, who can create, edit or comment, and which moderation states it can enter. These concepts are references for posts and comments, not default access or enforcement policies for a particular service.",
18021
+ concepts: [
18022
+ { name: "Visibility and access permissions", description: "Decide who can read content: everyone, members or specific groups, for example. Reading, creating posts and replying can be separate permissions." },
18023
+ { name: "Roles and actions", description: "Separate actions available to visitors, members, authors and moderators. Hiding a button alone cannot prevent actual data access or modification." },
18024
+ { name: "Publication state", description: "Distinguish draft, published, hidden and deleted content. Define hiding versus permanent deletion, including recoverability and handling of existing links." },
18025
+ { name: "Reports and moderation decisions", description: "Receiving a report does not establish a violation. Define review states, responsible people and available actions separately from publication state." },
18026
+ { name: "Comment locking and change history", description: "Locking comments is a different action from restricting reading or deleting content. Decide who can lock, unlock or edit, and who can see reasons and history." }
18027
+ ],
18028
+ workflow: [
18029
+ { name: "Confirm scope and permissions before posting", description: "Help authors understand the destination and visibility of their post, and define reading, posting and reply permissions separately." },
18030
+ { name: "Connect drafting, publication and editing", description: "Separate saving drafts from publishing. Decide what can change afterwards and how visibility changes affect existing links and search exposure." },
18031
+ { name: "Connect reports to review and action", description: "Review a report, then follow the agreed policy to retain, hide or lock content. Automatic enforcement based on report counts is a separate decision." },
18032
+ { name: "Explain outcomes and recoverability", description: "Decide who is notified and whether unhiding, restoring deleted content or appeals are available. Do not present failed actions as completed." }
18033
+ ],
18034
+ pitfalls: [
18035
+ { name: "Hiding content only in the interface", description: "Removing a list item can still leave content exposed through detail URLs, search or attachment paths. Connect the design to actual data-access checks." },
18036
+ { name: "Merging reports, hiding, deletion and locking", description: "A reported post may remain published, and a comment-locked post may remain readable. Confirm the meaning and permitted combinations of these states." },
18037
+ { name: "Missing the effects of visibility changes", description: "Changing a public post to group-only also needs a policy for old links, search results, notification previews and attachments. Do not promise to recall copies already shared externally." }
18038
+ ],
18039
+ questions: [
18040
+ "Which visitors, members or groups can read posts, comments and attachments, and who can create them?",
18041
+ "When can authors and moderators edit, hide, delete or lock content, and which content can they affect?",
18042
+ "After visibility changes, what do existing URLs, search results, notifications and attachments show?",
18043
+ "Who reviews reports, which states and actions are needed, and is there an agreed automatic-hiding rule?",
18044
+ "Can hidden or deleted content be restored or appealed? Who can still see comments and change history?",
18045
+ "If moderators act concurrently or a request fails, what final state and explanation are shown?"
18046
+ ],
18047
+ example: "Example: a reading group could allow only its members to read and comment on posts. A report would create a pending review, while a moderator could lock further comments and keep the existing discussion readable. Confirm actual visibility, automatic actions and recovery policies for the community before adopting this example.",
18048
+ relatedDesigns: [
18049
+ { id: "faceted-filter-chips", reason: "A reference for filtering a moderation list separately by publication and report-review states. It does not implement actual access checks." },
18050
+ { id: "inline-delete-confirmation", reason: "A reference for confirming the impact of deleting a post. Real deletion, recovery policy and persistence need separate implementation." }
18051
+ ],
18052
+ promptSnippet: "Help design community posts, comments and moderation. First confirm visibility, role-based read/create/edit permissions, publication states, report review, hiding, deletion, locking and recovery policies. Do not assume undecided rules.\nBased on the answers, describe:\n1. Actions for each role and access to posts, comments and attachments.\n2. Data and transitions that separate visibility, publication state and report-review state.\n3. How each action affects existing links, search, notifications, attachments and comments.\n4. User-visible outcomes and actual final states after failures, duplicate requests or concurrent moderation.\n5. How lists, detail pages, editors, reports and moderation screens connect to badges, filters and confirmation actions.\nDo not treat a report as proof of a violation or implement permissions by hiding buttons alone. Do not mistake example policies or design demos for real moderation rules or persistence. Separately confirm the required authentication and storage environment."
18053
+ }
18054
+ },
18055
+ sources: [
18056
+ { title: "Discourse \u2014 Create a category", url: "https://meta.discourse.org/t/create-a-category-in-discourse/197224" },
18057
+ { title: "GitHub Docs \u2014 Moderating discussions", url: "https://docs.github.com/en/discussions/managing-discussions-for-your-community/moderating-discussions" }
18058
+ ]
18059
+ }];
18060
+
18061
+ // ../../packages/catalog/src/domain-knowledge.ts
18062
+ var DOMAIN_KNOWLEDGE = [
18063
+ ...EDUCATION_KNOWLEDGE,
18064
+ ...COMMUNITY_KNOWLEDGE
18065
+ ];
18066
+
18067
+ // ../../packages/catalog/src/domain-knowledge/search-vocabulary.ts
18068
+ var KNOWLEDGE_SYNONYMS = [
18069
+ ["education", "\uAD50\uC721", "\uAD50\uC721\uC11C\uBE44\uC2A4", "\uAD50\uC721 \uC11C\uBE44\uC2A4", "\uAD50\uC721 \uD50C\uB7AB\uD3FC", "\uAD50\uC721\uD50C\uB7AB\uD3FC", "lms", "learning management system", "\uD559\uC2B5 \uAD00\uB9AC \uC2DC\uC2A4\uD15C", "\uD559\uC2B5\uAD00\uB9AC\uC2DC\uC2A4\uD15C", "\uC774\uB7EC\uB2DD", "e-learning", "elearning"],
18070
+ ["course", "courses", "lesson", "lessons", "\uAC15\uC758", "\uC218\uC5C5", "\uAC15\uC88C", "\uB808\uC2A8", "\uC628\uB77C\uC778 \uAC15\uC758", "\uC628\uB77C\uC778\uAC15\uC758", "online course", "online courses"],
18071
+ ["learning", "\uD559\uC2B5"],
18072
+ ["quiz", "quizzes", "\uD034\uC988"],
18073
+ ["assignment", "assignments", "homework", "\uACFC\uC81C"],
18074
+ ["submission", "submissions", "submit", "\uC81C\uCD9C"],
18075
+ ["resubmission", "resubmit", "\uC7AC\uC81C\uCD9C", "\uB2E4\uC2DC \uC81C\uCD9C"],
18076
+ ["latework", "late submission", "late work", "\uC9C0\uAC01 \uC81C\uCD9C", "\uC9C0\uAC01\uC81C\uCD9C", "\uAE30\uD55C \uD6C4 \uC81C\uCD9C"],
18077
+ ["retry", "retries", "\uC7AC\uC2DC\uB3C4", "\uB2E4\uC2DC \uD480\uAE30"],
18078
+ ["feedback", "\uD53C\uB4DC\uBC31"],
18079
+ ["assessment", "\uD3C9\uAC00"],
18080
+ ["progress", "\uC9C4\uB3C4", "\uC9C4\uD589\uB960"],
18081
+ ["completion", "\uC644\uB8CC", "\uC218\uB8CC"],
18082
+ ["deadline", "deadlines", "\uB9C8\uAC10"],
18083
+ ["draft", "drafts", "\uCD08\uC548"],
18084
+ ["ratio", "\uBE44\uC728", "percentage", "percent"],
18085
+ ["community", "communities", "forum", "forums", "\uCEE4\uBBA4\uB2C8\uD2F0", "\uAC8C\uC2DC\uD310", "\uD3EC\uB7FC"],
18086
+ ["post", "posts", "\uAC8C\uC2DC\uBB3C", "\uAC8C\uC2DC\uAE00"],
18087
+ ["comment", "comments", "\uB313\uAE00"],
18088
+ ["visibility", "\uACF5\uAC1C \uBC94\uC704", "\uACF5\uAC1C\uBC94\uC704"],
18089
+ ["permission", "permissions", "\uAD8C\uD55C", "\uC811\uADFC \uAD8C\uD55C", "\uC811\uADFC\uAD8C\uD55C", "access control", "\uC811\uADFC \uC81C\uC5B4", "\uC811\uADFC\uC81C\uC5B4"],
18090
+ ["memberonly", "members only", "members-only", "member only", "\uD68C\uC6D0 \uC804\uC6A9", "\uD68C\uC6D0\uC804\uC6A9", "\uD68C\uC6D0\uB9CC", "private group", "\uBE44\uACF5\uAC1C \uADF8\uB8F9", "\uADF8\uB8F9 \uC804\uC6A9"],
18091
+ ["report", "reports", "\uC2E0\uACE0"],
18092
+ ["moderation", "\uC6B4\uC601", "\uBAA8\uB354\uB808\uC774\uC158", "\uC2E0\uACE0 \uCC98\uB9AC", "\uC2E0\uACE0\uCC98\uB9AC", "\uC2E0\uACE0 \uAC80\uD1A0"],
18093
+ ["hide", "hidden", "hiding", "\uC228\uAE40", "\uC228\uAE30\uAE30"],
18094
+ ["restore", "restoration", "\uBCF5\uAD6C", "\uBCF5\uC6D0"],
18095
+ ["lock", "locking", "locked", "\uC7A0\uAE08"]
18096
+ ];
18097
+ var KNOWLEDGE_STOP_WORDS = [
18098
+ "\uC11C\uBE44\uC2A4",
18099
+ "\uC0AC\uC774\uD2B8",
18100
+ "\uD50C\uB7AB\uD3FC",
18101
+ "\uB9CC\uB4E4\uACE0",
18102
+ "\uB9CC\uB4E4\uB824\uACE0",
18103
+ "\uB9CC\uB4E4\uB824\uB294",
18104
+ "\uC2F6\uC5B4",
18105
+ "\uC2F6\uC5B4\uC694",
18106
+ "\uC2F6\uB2E4",
18107
+ "\uC2F6\uC740\uB370",
18108
+ "\uC54C\uB824\uC918",
18109
+ "\uC54C\uACE0",
18110
+ "\uB300\uD55C",
18111
+ "\uAD00\uB828",
18112
+ "\uD544\uC694\uD55C",
18113
+ "\uC9C0\uC2DD",
18114
+ "\uC5B4\uB5BB\uAC8C",
18115
+ "\uD560",
18116
+ "\uC218",
18117
+ "\uC788\uB294",
18118
+ "\uBCFC",
18119
+ "service",
18120
+ "website",
18121
+ "platform",
18122
+ "build",
18123
+ "building",
18124
+ "would",
18125
+ "like",
18126
+ "need",
18127
+ "about",
18128
+ "how"
18129
+ ];
18130
+
18131
+ // ../../packages/catalog/src/search-ranking.ts
18132
+ var normalizeSearchText = (text2) => text2.normalize("NFKC").trim().toLowerCase();
18133
+ function createSearchTokenizer(synonyms, extraStopWords = []) {
18134
+ const stopWords = /* @__PURE__ */ new Set([
18135
+ ..."a an the to of in on at with for and or i my want please make create implement design effect this that me it is as can you add show using \uB514\uC790\uC778 \uD6A8\uACFC \uAD6C\uD604 \uAD6C\uD604\uD574 \uAD6C\uD604\uD574\uC918 \uB9CC\uB4E4\uC5B4 \uB9CC\uB4E4\uC5B4\uC918 \uD574\uC918 \uC8FC\uC138\uC694 \uC918 \uC774 \uAC00 \uC740 \uB294 \uC744 \uB97C \uC5D0 \uC5D0\uC11C \uC73C\uB85C \uCC98\uB7FC \uB54C \uD558\uBA74 \uB418\uAC8C \uD558\uAC8C \uC0B4\uC9DD".split(" "),
18136
+ ...extraStopWords
18137
+ ]);
18138
+ const escapeRegex = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18139
+ const aliases = synonyms.flatMap((group) => group.map((alias) => ({ alias: normalizeSearchText(alias), canonical: group[0] }))).sort((a, b) => b.alias.length - a.alias.length);
18140
+ const aliasMap = new Map(aliases.map(({ alias, canonical }) => [alias, canonical]));
18141
+ const aliasPattern = new RegExp(aliases.map(({ alias }) => /[a-z]/.test(alias) ? `(?<![a-z0-9])${escapeRegex(alias)}(?![a-z0-9])` : escapeRegex(alias)).join("|"), "gu");
18142
+ return (text2) => {
18143
+ const canonical = normalizeSearchText(text2).replace(aliasPattern, (match) => ` ${aliasMap.get(match)} `);
18144
+ return [...new Set(canonical.split(/[^\p{L}\p{N}.%-]+/u).filter((token) => token && !stopWords.has(token)))];
18145
+ };
18146
+ }
18147
+ function rankSearchEntries(entries3, queryTokens, weights3, exact, exactBonus = 100) {
18148
+ if (!queryTokens.length && !exact) return null;
18149
+ const matchedOn = [];
18150
+ const best = /* @__PURE__ */ new Map();
18151
+ for (const entry of entries3) {
18152
+ const matched = queryTokens.filter((token) => entry.tokens.has(token));
18153
+ if (!matched.length) continue;
18154
+ for (const token of matched) best.set(token, Math.max(best.get(token) ?? 0, weights3[entry.field]));
18155
+ if (!matchedOn.some((match) => match.field === entry.field)) matchedOn.push({ field: entry.field, value: entry.value });
18156
+ }
18157
+ if (!exact && best.size < Math.max(1, Math.ceil(queryTokens.length * 0.6))) return null;
18158
+ let score = [...best.values()].reduce((sum, weight) => sum + weight, 0);
18159
+ score += queryTokens.length ? Math.round(20 * best.size / queryTokens.length) : 0;
18160
+ if (exact) {
18161
+ score += exactBonus;
18162
+ if (!matchedOn.some((match) => match.field === exact.field)) matchedOn.unshift({ field: exact.field, value: exact.value });
18163
+ }
18164
+ return { score, matchedOn: matchedOn.slice(0, 4) };
18165
+ }
18166
+
18167
+ // ../../packages/catalog/src/domain-knowledge-search.ts
18168
+ var tokens = createSearchTokenizer(KNOWLEDGE_SYNONYMS, KNOWLEDGE_STOP_WORDS);
18169
+ var weights = {
18170
+ domain: 35,
18171
+ title: 30,
18172
+ alias: 28,
18173
+ keyword: 20,
18174
+ useCase: 18,
18175
+ summary: 12,
18176
+ concept: 10,
18177
+ workflow: 8,
18178
+ pitfall: 8,
18179
+ question: 6,
18180
+ example: 3
18181
+ };
18182
+ var index = /* @__PURE__ */ new Map();
18183
+ function entries(item) {
18184
+ const cached = index.get(item);
18185
+ if (cached) return cached;
18186
+ const texts = [item.text.ko, item.text.en];
18187
+ const fields = [
18188
+ ["domain", texts.map((text2) => text2.domain)],
18189
+ ["title", [item.id, ...texts.map((text2) => text2.title)]],
18190
+ ["alias", [...item.aliases.ko, ...item.aliases.en]],
18191
+ ["keyword", [...item.keywords.ko, ...item.keywords.en]],
18192
+ ["useCase", [...item.useCases.ko, ...item.useCases.en]],
18193
+ ["summary", texts.flatMap((text2) => [text2.summary, text2.overview])],
18194
+ ...["concept", "workflow", "pitfall"].map((field) => [field, texts.flatMap((text2) => (field === "concept" ? text2.concepts : field === "workflow" ? text2.workflow : text2.pitfalls).flatMap((point) => [point.name, point.description]))]),
18195
+ ["question", texts.flatMap((text2) => text2.questions)],
18196
+ ["example", texts.map((text2) => text2.example)]
18197
+ ];
18198
+ const result2 = fields.flatMap(([field, values]) => values.map((value) => ({ field, value, tokens: new Set(tokens(value)) })));
18199
+ index.set(item, result2);
18200
+ return result2;
18201
+ }
18202
+ function rankDomainKnowledge(query = "") {
18203
+ if (!query.trim()) return DOMAIN_KNOWLEDGE.map((item) => ({ item, score: 0, matchedOn: [] }));
18204
+ const queryTokens = tokens(query);
18205
+ const normalizedQuery = normalizeSearchText(query);
18206
+ const results = [];
18207
+ for (const item of DOMAIN_KNOWLEDGE) {
18208
+ const allEntries = entries(item);
18209
+ const exact = allEntries.find((entry) => normalizeSearchText(entry.value) === normalizedQuery);
18210
+ const result2 = rankSearchEntries(allEntries, queryTokens, weights, exact, exact?.field === "domain" || exact?.field === "title" ? 150 : 100);
18211
+ if (result2) results.push({ item, ...result2 });
18212
+ }
18213
+ return results.sort((a, b) => b.score - a.score);
18214
+ }
18215
+
17862
18216
  // ../../packages/catalog/src/search.ts
17863
18217
  var SEARCH_SYNONYMS = [
17864
18218
  ["button", "buttons", "\uBC84\uD2BC", "cta"],
@@ -17968,19 +18322,11 @@ var SEARCH_SYNONYMS = [
17968
18322
  ["spectrum", "\uC2A4\uD399\uD2B8\uB7FC"],
17969
18323
  ["ticker", "\uD2F0\uCEE4", "\uB274\uC2A4\uB760"]
17970
18324
  ];
17971
- var stopWords = new Set("a an the to of in on at with for and or i my want please make create implement design effect this that me it is as can you add show using \uB514\uC790\uC778 \uD6A8\uACFC \uAD6C\uD604 \uAD6C\uD604\uD574 \uAD6C\uD604\uD574\uC918 \uB9CC\uB4E4\uC5B4 \uB9CC\uB4E4\uC5B4\uC918 \uD574\uC918 \uC8FC\uC138\uC694 \uC918 \uC774 \uAC00 \uC740 \uB294 \uC744 \uB97C \uC5D0 \uC5D0\uC11C \uC73C\uB85C \uCC98\uB7FC \uB54C \uD558\uBA74 \uB418\uAC8C \uD558\uAC8C \uC0B4\uC9DD".split(" "));
17972
- var escapeRegex = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17973
- var aliases = SEARCH_SYNONYMS.flatMap((group) => group.map((alias) => ({ alias: alias.normalize("NFKC").toLowerCase(), canonical: group[0] }))).sort((a, b) => b.alias.length - a.alias.length);
17974
- var aliasMap = new Map(aliases.map(({ alias, canonical }) => [alias, canonical]));
17975
- var aliasPattern = new RegExp(aliases.map(({ alias }) => /[a-z]/.test(alias) ? `(?<![a-z0-9])${escapeRegex(alias)}(?![a-z0-9])` : escapeRegex(alias)).join("|"), "gu");
17976
- function tokens(text2) {
17977
- const canonical = text2.normalize("NFKC").toLowerCase().replace(aliasPattern, (match) => ` ${aliasMap.get(match)} `);
17978
- return [...new Set(canonical.split(/[^\p{L}\p{N}.%-]+/u).map((token) => token.replace(/^(?:을|를|이|가|은|는)$/, "")).filter((token) => token && !stopWords.has(token)))];
17979
- }
17980
- var weights = { title: 30, alias: 28, keyword: 20, useCase: 18, target: 8, trigger: 6, summary: 12, prompt: 1 };
17981
- var index = /* @__PURE__ */ new Map();
17982
- function entries(item) {
17983
- const cached = index.get(item);
18325
+ var tokens2 = createSearchTokenizer(SEARCH_SYNONYMS);
18326
+ var weights2 = { title: 30, alias: 28, keyword: 20, useCase: 18, target: 8, trigger: 6, summary: 12, prompt: 1 };
18327
+ var index2 = /* @__PURE__ */ new Map();
18328
+ function entries2(item) {
18329
+ const cached = index2.get(item);
17984
18330
  if (cached) return cached;
17985
18331
  const en = getTechniqueText(item, "en");
17986
18332
  const meta = getSearchMetadata(item);
@@ -17994,40 +18340,21 @@ function entries(item) {
17994
18340
  ["summary", [item.summary, en.summary]],
17995
18341
  ["prompt", [item.promptSnippet, en.promptSnippet]]
17996
18342
  ];
17997
- const result2 = fields.flatMap(([field, values]) => values.map((value) => ({ field, value, tokens: new Set(tokens(value)) })));
17998
- index.set(item, result2);
18343
+ const result2 = fields.flatMap(([field, values]) => values.map((value) => ({ field, value, tokens: new Set(tokens2(value)) })));
18344
+ index2.set(item, result2);
17999
18345
  return result2;
18000
18346
  }
18001
18347
  function rank(item, query, queryTokens) {
18002
18348
  if (!query.trim()) return { item, score: 0, matchedOn: [] };
18003
- const normalizedQuery = query.normalize("NFKC").trim().toLowerCase();
18004
- const allEntries = entries(item);
18005
- const exact = allEntries.find((entry) => entry.field !== "prompt" && entry.value.normalize("NFKC").trim().toLowerCase() === normalizedQuery);
18006
- if (!queryTokens.length && !exact) return null;
18007
- const matchedOn = [];
18008
- const covered = /* @__PURE__ */ new Set();
18009
- const best = /* @__PURE__ */ new Map();
18010
- for (const entry of allEntries) {
18011
- const matched = queryTokens.filter((token) => entry.tokens.has(token));
18012
- if (!matched.length) continue;
18013
- for (const token of matched) {
18014
- covered.add(token);
18015
- best.set(token, Math.max(best.get(token) ?? 0, weights[entry.field]));
18016
- }
18017
- if (!matchedOn.some((match) => match.field === entry.field)) matchedOn.push({ field: entry.field, value: entry.value });
18018
- }
18019
- if (!exact && covered.size < Math.max(1, Math.ceil(queryTokens.length * 0.6))) return null;
18020
- let score = [...best.values()].reduce((sum, weight) => sum + weight, 0);
18021
- score += queryTokens.length ? Math.round(20 * covered.size / queryTokens.length) : 0;
18022
- if (exact) {
18023
- score += exact.field === "title" ? 150 : 100;
18024
- if (!matchedOn.some((match) => match.field === exact.field)) matchedOn.unshift({ field: exact.field, value: exact.value });
18025
- }
18026
- return { item, score, matchedOn: matchedOn.slice(0, 4) };
18349
+ const normalizedQuery = normalizeSearchText(query);
18350
+ const allEntries = entries2(item);
18351
+ const exact = allEntries.find((entry) => entry.field !== "prompt" && normalizeSearchText(entry.value) === normalizedQuery);
18352
+ const result2 = rankSearchEntries(allEntries, queryTokens, weights2, exact, exact?.field === "title" ? 150 : 100);
18353
+ return result2 ? { item, ...result2 } : null;
18027
18354
  }
18028
18355
  function searchTechniques(options = {}) {
18029
18356
  const query = options.query ?? "";
18030
- const queryTokens = tokens(query);
18357
+ const queryTokens = tokens2(query);
18031
18358
  const results = [];
18032
18359
  for (const item of TECHNIQUES) {
18033
18360
  if (options.domain && item.domain !== options.domain || options.category && item.category !== options.category) continue;
@@ -18042,7 +18369,7 @@ function searchTechniques(options = {}) {
18042
18369
  // package.json
18043
18370
  var package_default = {
18044
18371
  name: "@keke1933/vibe-dictionary-mcp",
18045
- version: "0.7.1",
18372
+ version: "0.8.0",
18046
18373
  license: "MIT",
18047
18374
  author: "leejh96",
18048
18375
  description: "Read-only local MCP for searching Vibe Dictionary techniques and retrieving implementation prompts",
@@ -18106,6 +18433,7 @@ var summaryShape = {
18106
18433
  useCases: z.array(z.string())
18107
18434
  };
18108
18435
  var readOnlyAnnotations = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
18436
+ var knowledgeSummaryShape = { id: z.string(), locale: z.enum(["ko", "en"]), domain: z.string(), title: z.string(), summary: z.string() };
18109
18437
  function summarize(item, locale) {
18110
18438
  const text2 = getTechniqueText(item, locale);
18111
18439
  const metadata = getSearchMetadata(item);
@@ -18132,6 +18460,7 @@ function createServer() {
18132
18460
  const server = new McpServer({ name: package_default.name, version: package_default.version }, {
18133
18461
  instructions: [
18134
18462
  "Find UI, graphics, layout, style and video techniques. Search for a few candidates, compare summaries, useCases and matchedOn fields, then get_technique by exact ID only for the selected prompts.",
18463
+ 'Before choosing designs for an education or community service, use search_domain_knowledge with a short domain or workflow phrase such as "LMS", "late submission", "\uD68C\uC6D0 \uC804\uC6A9" or "\uC2E0\uACE0 \uCC98\uB9AC". Compare summaries, useCases and matchedOn fields, then call get_domain_knowledge by a returned ID for concepts, workflows, pitfalls, open decisions, sources, related technique IDs and a reference prompt. Search supports bilingual aliases and ranked keyword matching, not semantic AI. If no entry fits, retry fewer terms or a synonym, or browse with an empty query. Workflows and examples are design references, not established policies. Do not assume answers to the open questions. Related designs do not implement the domain rules.',
18135
18464
  "The calling assistant interprets the user request and generates short Korean or English search phrases for one effect at a time. The MCP performs bilingual weighted keyword/alias matching and runs no AI model. Extract concrete effects and targets; omit uncertain filters. For broad requests such as a clean or premium webpage, use the stated purpose and preferences; do not equate an adjective with one fixed style. Ask for clarification when missing context prevents a meaningful choice.",
18136
18465
  "For requests combining techniques, search distinct needs such as style, layout and interaction separately. Retrieve the selected prompts, then decide where each technique applies and compare their visual treatment, inputs and timing for conflicts. The tools do not compose techniques or check compatibility; the calling assistant makes those judgments.",
18137
18466
  "Use target/trigger filters only for required demo inputs. Compare summaries and useCases against the original request. If candidates are missing or unsuitable, retry a synonym, the other language or fewer modifiers, and remove only filters that are not required. Reset offset to zero when changing the query or filters. Do not select an unrelated result merely because it ranks first; clarify if useful candidates still cannot be found. Scores are not probabilities. locale selects output language independently of search language. These read-only tools return reference data only."
@@ -18182,6 +18511,58 @@ function createServer() {
18182
18511
  const text2 = getTechniqueText(item, locale);
18183
18512
  return result({ ...summarize(item, locale), aliases: getSearchMetadata(item).aliases[locale], vibeKeywords: text2.vibeKeywords, promptSnippet: text2.promptSnippet });
18184
18513
  });
18514
+ server.registerTool("search_domain_knowledge", {
18515
+ title: "Search domain knowledge / \uB3C4\uBA54\uC778 \uC9C0\uC2DD \uAC80\uC0C9",
18516
+ description: 'Search Korean and English domain knowledge by domain, title, aliases, keywords, use cases, concepts, workflows, pitfalls and decision questions. Rewrite the user request into a short service or workflow phrase, e.g. "LMS", "\uC7AC\uC2DC\uB3C4", "\uD68C\uC6D0 \uC804\uC6A9" or "\uC2E0\uACE0 \uCC98\uB9AC". Bilingual normalization and weighted term coverage rank results; this is not semantic search. Nonempty queries return score (not confidence), matchedOn field names and useCases; empty queries browse catalog order. Results exclude full prompts. If results do not fit, retry fewer terms or synonyms and reset offset. Use get_domain_knowledge with a returned ID for the full reference.',
18517
+ inputSchema: {
18518
+ query: z.string().max(500).default("").describe("Short Korean/English domain, service or workflow phrase; omit to browse. Search independent needs separately."),
18519
+ locale: localeSchema,
18520
+ limit: z.number().int().min(1).max(50).default(20),
18521
+ offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).default(0)
18522
+ },
18523
+ outputSchema: { total: z.number().int(), offset: z.number().int(), nextOffset: z.number().int().nullable(), items: z.array(z.object({
18524
+ ...knowledgeSummaryShape,
18525
+ useCases: z.array(z.string()),
18526
+ score: z.number(),
18527
+ matchedOn: z.array(z.enum(["domain", "title", "alias", "keyword", "useCase", "summary", "concept", "workflow", "pitfall", "question", "example"]))
18528
+ })) },
18529
+ annotations: readOnlyAnnotations
18530
+ }, async ({ query, locale, limit, offset }) => {
18531
+ const matches = rankDomainKnowledge(query);
18532
+ const items = matches.slice(offset, offset + limit).map(({ item, score, matchedOn }) => ({
18533
+ id: item.id,
18534
+ locale,
18535
+ domain: item.text[locale].domain,
18536
+ title: item.text[locale].title,
18537
+ summary: item.text[locale].summary,
18538
+ useCases: item.useCases[locale],
18539
+ score,
18540
+ matchedOn: matchedOn.map((match) => match.field)
18541
+ }));
18542
+ return result({ total: matches.length, offset, nextOffset: offset + items.length < matches.length ? offset + items.length : null, items });
18543
+ });
18544
+ server.registerTool("get_domain_knowledge", {
18545
+ title: "Get domain knowledge / \uB3C4\uBA54\uC778 \uC9C0\uC2DD \uC870\uD68C",
18546
+ description: "Retrieve a domain knowledge entry by exact ID. Workflows and examples are design references; questions are decisions for the user, not established rules. Pitfalls highlight situations to consider. Related technique IDs refer to design demos and do not implement the domain behavior. The reference prompt and sources are returned unchanged in the requested language.",
18547
+ inputSchema: { id: z.string().trim().min(1).max(200), locale: localeSchema },
18548
+ outputSchema: {
18549
+ ...knowledgeSummaryShape,
18550
+ overview: z.string(),
18551
+ concepts: z.array(z.object({ name: z.string(), description: z.string() })),
18552
+ workflow: z.array(z.object({ name: z.string(), description: z.string() })),
18553
+ pitfalls: z.array(z.object({ name: z.string(), description: z.string() })),
18554
+ questions: z.array(z.string()),
18555
+ example: z.string(),
18556
+ relatedDesigns: z.array(z.object({ id: z.string(), reason: z.string() })),
18557
+ promptSnippet: z.string(),
18558
+ sources: z.array(z.object({ title: z.string(), url: z.string() }))
18559
+ },
18560
+ annotations: readOnlyAnnotations
18561
+ }, async ({ id, locale }) => {
18562
+ const item = DOMAIN_KNOWLEDGE.find((candidate) => candidate.id === id);
18563
+ if (!item) return { isError: true, content: [{ type: "text", text: locale === "en" ? "Domain knowledge ID not found. Use search_domain_knowledge to find a valid ID." : "\uB3C4\uBA54\uC778 \uC9C0\uC2DD ID\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. search_domain_knowledge\uB85C \uC720\uD6A8\uD55C ID\uB97C \uD655\uC778\uD574 \uC8FC\uC138\uC694." }] };
18564
+ return result({ id: item.id, locale, ...item.text[locale], sources: item.sources });
18565
+ });
18185
18566
  return server;
18186
18567
  }
18187
18568
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keke1933/vibe-dictionary-mcp",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "author": "leejh96",
6
6
  "description": "Read-only local MCP for searching Vibe Dictionary techniques and retrieving implementation prompts",