algokit-mcp 1.0.2 → 1.0.4
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 +139 -17
- package/dist/api/boj-scraper.js +1 -1
- package/dist/api/boj-scraper.js.map +1 -1
- package/dist/api/programmers-scraper.d.ts +0 -5
- package/dist/api/programmers-scraper.d.ts.map +1 -1
- package/dist/api/programmers-scraper.js +59 -90
- package/dist/api/programmers-scraper.js.map +1 -1
- package/dist/index.js +38 -35
- package/dist/index.js.map +1 -1
- package/dist/tools/boj/analyze-code-submission-boj.d.ts +67 -0
- package/dist/tools/boj/analyze-code-submission-boj.d.ts.map +1 -0
- package/dist/tools/boj/analyze-code-submission-boj.js +91 -0
- package/dist/tools/boj/analyze-code-submission-boj.js.map +1 -0
- package/dist/tools/boj/analyze-problem-boj.d.ts +48 -0
- package/dist/tools/boj/analyze-problem-boj.d.ts.map +1 -0
- package/dist/tools/boj/analyze-problem-boj.js +52 -0
- package/dist/tools/boj/analyze-problem-boj.js.map +1 -0
- package/dist/tools/boj/fetch-problem-content-boj.d.ts +49 -0
- package/dist/tools/boj/fetch-problem-content-boj.d.ts.map +1 -0
- package/dist/tools/boj/fetch-problem-content-boj.js +95 -0
- package/dist/tools/boj/fetch-problem-content-boj.js.map +1 -0
- package/dist/tools/boj/generate-hint-boj.d.ts +42 -0
- package/dist/tools/boj/generate-hint-boj.d.ts.map +1 -0
- package/dist/tools/boj/generate-hint-boj.js +82 -0
- package/dist/tools/boj/generate-hint-boj.js.map +1 -0
- package/dist/tools/boj/generate-review-template-boj.d.ts +48 -0
- package/dist/tools/boj/generate-review-template-boj.d.ts.map +1 -0
- package/dist/tools/boj/generate-review-template-boj.js +52 -0
- package/dist/tools/boj/generate-review-template-boj.js.map +1 -0
- package/dist/tools/boj/get-problem-boj.d.ts +22 -0
- package/dist/tools/boj/get-problem-boj.d.ts.map +1 -0
- package/dist/tools/boj/get-problem-boj.js +89 -0
- package/dist/tools/boj/get-problem-boj.js.map +1 -0
- package/dist/tools/boj/search-problems-boj.d.ts +42 -0
- package/dist/tools/boj/search-problems-boj.d.ts.map +1 -0
- package/dist/tools/boj/search-problems-boj.js +143 -0
- package/dist/tools/boj/search-problems-boj.js.map +1 -0
- package/dist/tools/boj/search-tags-boj.d.ts +22 -0
- package/dist/tools/boj/search-tags-boj.d.ts.map +1 -0
- package/dist/tools/boj/search-tags-boj.js +70 -0
- package/dist/tools/boj/search-tags-boj.js.map +1 -0
- package/dist/tools/get-problem-boj.d.ts +22 -0
- package/dist/tools/get-problem-boj.d.ts.map +1 -0
- package/dist/tools/get-problem-boj.js +89 -0
- package/dist/tools/get-problem-boj.js.map +1 -0
- package/dist/tools/programmers/analyze-code-submission-programmers.d.ts +60 -0
- package/dist/tools/programmers/analyze-code-submission-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/analyze-code-submission-programmers.js +85 -0
- package/dist/tools/programmers/analyze-code-submission-programmers.js.map +1 -0
- package/dist/tools/programmers/analyze-problem-programmers.d.ts +48 -0
- package/dist/tools/programmers/analyze-problem-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/analyze-problem-programmers.js +53 -0
- package/dist/tools/programmers/analyze-problem-programmers.js.map +1 -0
- package/dist/tools/programmers/fetch-problem-content-programmers.d.ts +46 -0
- package/dist/tools/programmers/fetch-problem-content-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/fetch-problem-content-programmers.js +76 -0
- package/dist/tools/programmers/fetch-problem-content-programmers.js.map +1 -0
- package/dist/tools/programmers/generate-hint-programmers.d.ts +42 -0
- package/dist/tools/programmers/generate-hint-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/generate-hint-programmers.js +80 -0
- package/dist/tools/programmers/generate-hint-programmers.js.map +1 -0
- package/dist/tools/programmers/generate-review-template-programmers.d.ts +48 -0
- package/dist/tools/programmers/generate-review-template-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/generate-review-template-programmers.js +53 -0
- package/dist/tools/programmers/generate-review-template-programmers.js.map +1 -0
- package/dist/tools/programmers/get-problem-programmers.d.ts +53 -0
- package/dist/tools/programmers/get-problem-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/get-problem-programmers.js +163 -0
- package/dist/tools/programmers/get-problem-programmers.js.map +1 -0
- package/dist/tools/programmers/search-problems-programmers.d.ts +73 -0
- package/dist/tools/programmers/search-problems-programmers.d.ts.map +1 -0
- package/dist/tools/programmers/search-problems-programmers.js +173 -0
- package/dist/tools/programmers/search-problems-programmers.js.map +1 -0
- package/dist/tools/search-problems-boj.d.ts +42 -0
- package/dist/tools/search-problems-boj.d.ts.map +1 -0
- package/dist/tools/search-problems-boj.js +143 -0
- package/dist/tools/search-problems-boj.js.map +1 -0
- package/dist/tools/search-tags-boj.d.ts +22 -0
- package/dist/tools/search-tags-boj.d.ts.map +1 -0
- package/dist/tools/search-tags-boj.js +70 -0
- package/dist/tools/search-tags-boj.js.map +1 -0
- package/dist/utils/agent-skill-installer.d.ts +9 -0
- package/dist/utils/agent-skill-installer.d.ts.map +1 -0
- package/dist/utils/agent-skill-installer.js +94 -0
- package/dist/utils/agent-skill-installer.js.map +1 -0
- package/dist/utils/claudecode-skill-installer.d.ts +6 -0
- package/dist/utils/claudecode-skill-installer.d.ts.map +1 -0
- package/dist/utils/claudecode-skill-installer.js +70 -0
- package/dist/utils/claudecode-skill-installer.js.map +1 -0
- package/dist/utils/skill-installer.d.ts +6 -0
- package/dist/utils/skill-installer.d.ts.map +1 -0
- package/dist/utils/skill-installer.js +70 -0
- package/dist/utils/skill-installer.js.map +1 -0
- package/dist/utils/tier-converter.d.ts.map +1 -1
- package/dist/utils/tier-converter.js +3 -0
- package/dist/utils/tier-converter.js.map +1 -1
- package/package.json +3 -4
- package/skills/algo:code-review/SKILL.md +134 -0
- package/skills/algo:fetch/SKILL.md +102 -0
- package/skills/algo:hint/SKILL.md +111 -0
- package/skills/algo:review/SKILL.md +123 -0
- package/skills/algo:search/SKILL.md +122 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fetch_problem_content_boj MCP 도구
|
|
3
|
+
*
|
|
4
|
+
* Phase 6 - P6-004: BOJ 문제 본문 스크래핑 MCP 도구 구현
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { BOJScraper, BojFetchError } from '../../api/boj-scraper.js';
|
|
8
|
+
import { parseProblemContent, HtmlParseError } from '../../utils/html-parser.js';
|
|
9
|
+
/**
|
|
10
|
+
* 입력 스키마
|
|
11
|
+
*/
|
|
12
|
+
export const FetchProblemContentBOJInputSchema = z.object({
|
|
13
|
+
problem_id: z.number().int().positive()
|
|
14
|
+
.describe('백준 문제 번호'),
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* fetch_problem_content_boj 도구 핸들러
|
|
18
|
+
*
|
|
19
|
+
* @param args - 입력 인자 (problem_id)
|
|
20
|
+
* @returns ProblemContent JSON 문자열
|
|
21
|
+
* @throws {Error} 검증 실패, 스크래핑 실패, 파싱 실패 시
|
|
22
|
+
*/
|
|
23
|
+
export async function handleFetchProblemContentBOJ(args) {
|
|
24
|
+
try {
|
|
25
|
+
// 1. 입력 검증
|
|
26
|
+
const { problem_id } = FetchProblemContentBOJInputSchema.parse(args);
|
|
27
|
+
// 2. BOJ 페이지 스크래핑
|
|
28
|
+
const scraper = new BOJScraper();
|
|
29
|
+
const html = await scraper.fetchProblemPage(problem_id);
|
|
30
|
+
// 3. HTML 파싱
|
|
31
|
+
const content = parseProblemContent(html, problem_id);
|
|
32
|
+
// 4. JSON 문자열로 반환
|
|
33
|
+
return {
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify(content, null, 2),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// Zod 검증 에러
|
|
40
|
+
if (error instanceof z.ZodError) {
|
|
41
|
+
throw new Error(`입력 검증 실패: ${error.issues[0].message}`);
|
|
42
|
+
}
|
|
43
|
+
// BOJ 스크래핑 에러
|
|
44
|
+
if (error instanceof BojFetchError) {
|
|
45
|
+
if (error.code === 'NOT_FOUND') {
|
|
46
|
+
throw new Error(`문제를 찾을 수 없습니다: ${args.problem_id}번`);
|
|
47
|
+
}
|
|
48
|
+
if (error.code === 'TIMEOUT') {
|
|
49
|
+
throw new Error(`문제 페이지 요청이 타임아웃되었습니다: ${args.problem_id}번`);
|
|
50
|
+
}
|
|
51
|
+
if (error.code === 'NETWORK_ERROR') {
|
|
52
|
+
throw new Error(`네트워크 에러가 발생했습니다: ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
if (error.code === 'PARSE_ERROR') {
|
|
55
|
+
throw new Error(`문제 페이지 응답을 처리할 수 없습니다: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// HTML 파싱 에러
|
|
59
|
+
if (error instanceof HtmlParseError) {
|
|
60
|
+
throw new Error(`HTML 파싱 실패 (${error.field}): ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
// 기타 에러
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* MCP 도구 정의
|
|
68
|
+
*/
|
|
69
|
+
export function fetchProblemContentBOJTool() {
|
|
70
|
+
return {
|
|
71
|
+
name: 'fetch_problem_content_boj',
|
|
72
|
+
description: `백준(BOJ) 문제 본문 스크래핑 (힌트 제외).
|
|
73
|
+
|
|
74
|
+
BOJ 페이지에서 문제 제목, 설명, 입출력 형식, 예제, 제한사항을 가져옵니다.
|
|
75
|
+
**힌트는 포함되지 않습니다** - generate_hint_boj 도구를 사용하세요.
|
|
76
|
+
|
|
77
|
+
응답 구조: problemId, title, description, inputFormat, outputFormat, examples, limits, metadata
|
|
78
|
+
|
|
79
|
+
**사용 시나리오**:
|
|
80
|
+
- 문제 풀이 전 문제 본문 확인
|
|
81
|
+
- 문제 복습 시 문제 내용 참조
|
|
82
|
+
- 코드 분석 시 문제 요구사항 비교
|
|
83
|
+
|
|
84
|
+
**제한사항**:
|
|
85
|
+
- 스크래핑 대상: https://www.acmicpc.net/problem/{problem_id}
|
|
86
|
+
- 타임아웃: 10초
|
|
87
|
+
- 재시도: 최대 2회
|
|
88
|
+
- 캐시: 30일
|
|
89
|
+
|
|
90
|
+
⚠️ 플랫폼 판별: 문제 번호만 입력된 경우 대화 맥락에서 플랫폼을 파악하거나, 맥락이 없으면 반드시 BOJ/프로그래머스 중 어느 플랫폼인지 사용자에게 확인 후 호출하세요.`,
|
|
91
|
+
inputSchema: FetchProblemContentBOJInputSchema,
|
|
92
|
+
handler: handleFetchProblemContentBOJ,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=fetch-problem-content-boj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-problem-content-boj.js","sourceRoot":"","sources":["../../../src/tools/boj/fetch-problem-content-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjF;;GAEG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACpC,QAAQ,CAAC,UAAU,CAAC;CACxB,CAAC,CAAC;AAYH;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,IAAa;IAC9D,IAAI,CAAC;QACH,WAAW;QACX,MAAM,EAAE,UAAU,EAAE,GAAG,iCAAiC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAErE,kBAAkB;QAClB,MAAM,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAExD,aAAa;QACb,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAEtD,kBAAkB;QAClB,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;SACvC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,YAAY;QACZ,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,cAAc;QACd,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;YACnC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,kBAAmB,IAAoC,CAAC,UAAU,GAAG,CAAC,CAAC;YACzF,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,yBAA0B,IAAoC,CAAC,UAAU,GAAG,CAAC,CAAC;YAChG,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,oBAAoB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,aAAa;QACb,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,CAAC,KAAK,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,QAAQ;QACR,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B;IACxC,OAAO;QACL,IAAI,EAAE,2BAA2B;QACjC,WAAW,EAAE;;;;;;;;;;;;;;;;;;iGAkBgF;QAC7F,WAAW,EAAE,iCAAiC;QAC9C,OAAO,EAAE,4BAA4B;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generate_hint_boj MCP 도구
|
|
3
|
+
*
|
|
4
|
+
* BOJ 문제 힌트 가이드 생성 (SRP: 힌트만)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import type { ProblemAnalyzer } from '../../services/problem-analyzer.js';
|
|
8
|
+
/**
|
|
9
|
+
* 입력 스키마
|
|
10
|
+
*/
|
|
11
|
+
export declare const GenerateHintBOJInputSchema: z.ZodObject<{
|
|
12
|
+
problem_id: z.ZodNumber;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
problem_id: number;
|
|
15
|
+
}, {
|
|
16
|
+
problem_id: number;
|
|
17
|
+
}>;
|
|
18
|
+
export type GenerateHintBOJInput = z.infer<typeof GenerateHintBOJInputSchema>;
|
|
19
|
+
/**
|
|
20
|
+
* MCP TextContent 타입
|
|
21
|
+
*/
|
|
22
|
+
interface TextContent {
|
|
23
|
+
type: 'text';
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* generate-hint 도구 핸들러
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateHintBOJTool(analyzer: ProblemAnalyzer): {
|
|
30
|
+
name: string;
|
|
31
|
+
description: string;
|
|
32
|
+
inputSchema: z.ZodObject<{
|
|
33
|
+
problem_id: z.ZodNumber;
|
|
34
|
+
}, "strip", z.ZodTypeAny, {
|
|
35
|
+
problem_id: number;
|
|
36
|
+
}, {
|
|
37
|
+
problem_id: number;
|
|
38
|
+
}>;
|
|
39
|
+
handler: (input: GenerateHintBOJInput) => Promise<TextContent>;
|
|
40
|
+
};
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=generate-hint-boj.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-hint-boj.d.ts","sourceRoot":"","sources":["../../../src/tools/boj/generate-hint-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAG1E;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;EAGrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;GAEG;AACH,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe;;;;;;;;;;qBAqClC,oBAAoB,KAAG,OAAO,CAAC,WAAW,CAAC;EA6BrE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generate_hint_boj MCP 도구
|
|
3
|
+
*
|
|
4
|
+
* BOJ 문제 힌트 가이드 생성 (SRP: 힌트만)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { ProblemNotFoundError } from '../../api/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* 입력 스키마
|
|
10
|
+
*/
|
|
11
|
+
export const GenerateHintBOJInputSchema = z.object({
|
|
12
|
+
problem_id: z.number().int().positive()
|
|
13
|
+
.describe('백준 문제 번호'),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* generate-hint 도구 핸들러
|
|
17
|
+
*/
|
|
18
|
+
export function generateHintBOJTool(analyzer) {
|
|
19
|
+
return {
|
|
20
|
+
name: 'generate_hint_boj',
|
|
21
|
+
description: `백준(BOJ) 문제 힌트 생성. 3단계 가이드 프롬프트 제공.
|
|
22
|
+
|
|
23
|
+
🎯 **핵심: 한 번에 1개 레벨 힌트만 제공. 1,2,3 단계를 동시에 제시하지 마세요.**
|
|
24
|
+
|
|
25
|
+
📋 응답에 포함된 정보:
|
|
26
|
+
- hint_levels[0]: Level 1 - 문제 분석 (처음 시도)
|
|
27
|
+
- hint_levels[1]: Level 2 - 핵심 아이디어 (부분 구현)
|
|
28
|
+
- hint_levels[2]: Level 3 - 상세 풀이 (거의 완성)
|
|
29
|
+
|
|
30
|
+
🤖 **사용자 상황 판단 → 적절한 1개 레벨만 제시**:
|
|
31
|
+
|
|
32
|
+
1️⃣ Level 1 제시 (처음/막힐 때):
|
|
33
|
+
"사용자가 코드 없거나 어디서부터 시작해야 할지 모를 때"
|
|
34
|
+
→ hint_levels[0].prompt로 문제 접근법 제시
|
|
35
|
+
|
|
36
|
+
2️⃣ Level 2 제시 (다시 요청하거나 부분 구현 언급):
|
|
37
|
+
"사용자가 '더 필요해', '더 자세히' 요청하거나, 이미 코드를 시작했다고 언급"
|
|
38
|
+
→ hint_levels[1].prompt로 핵심 로직 제시
|
|
39
|
+
|
|
40
|
+
3️⃣ Level 3 제시 (상세 풀이 명시 요청):
|
|
41
|
+
"사용자가 '정답', '풀이', '코드' 등 최종 답변 요청"
|
|
42
|
+
→ hint_levels[2].prompt로 상세 구현 가이드 제시
|
|
43
|
+
|
|
44
|
+
📖 **[권장] 어려운 문제는 본문 먼저 확인**:
|
|
45
|
+
- 난이도 높은 문제(Gold 이상, level ≥ 11)는 메타데이터만으로 부족할 수 있음
|
|
46
|
+
- fetch_problem_content로 실제 문제 본문 확인 후 힌트 생성 권장
|
|
47
|
+
- 문제 본문을 보면 더 정확하고 맞춤형 힌트 제공 가능
|
|
48
|
+
- 예: mcp-cli call algokit/fetch_problem_content '{"problem_id": 1234}'
|
|
49
|
+
- 본문 확인은 권장사항이며 필수는 아님
|
|
50
|
+
|
|
51
|
+
**정답 정책**: 힌트만 기본 제공. 사용자가 "정답", "풀이", "코드" 명시 요청 시만 전체 풀이 제공.
|
|
52
|
+
|
|
53
|
+
⚠️ 플랫폼 판별: 문제 번호만 입력된 경우 대화 맥락에서 플랫폼을 파악하거나, 맥락이 없으면 반드시 BOJ/프로그래머스 중 어느 플랫폼인지 사용자에게 확인 후 호출하세요.`,
|
|
54
|
+
inputSchema: GenerateHintBOJInputSchema,
|
|
55
|
+
handler: async (input) => {
|
|
56
|
+
try {
|
|
57
|
+
// 입력 검증
|
|
58
|
+
const { problem_id } = GenerateHintBOJInputSchema.parse(input);
|
|
59
|
+
// 힌트 가이드 생성 (유사 문제 제외)
|
|
60
|
+
const result = await analyzer.analyze(problem_id, false);
|
|
61
|
+
// JSON 문자열로 반환 (Claude Code가 파싱)
|
|
62
|
+
return {
|
|
63
|
+
type: 'text',
|
|
64
|
+
text: JSON.stringify(result, null, 2),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
// Zod 검증 에러
|
|
69
|
+
if (error instanceof z.ZodError) {
|
|
70
|
+
throw new Error(`입력 검증 실패: ${error.issues[0].message}`);
|
|
71
|
+
}
|
|
72
|
+
// ProblemNotFoundError
|
|
73
|
+
if (error instanceof ProblemNotFoundError) {
|
|
74
|
+
throw new Error(`문제를 찾을 수 없습니다: ${input.problem_id}번`);
|
|
75
|
+
}
|
|
76
|
+
// 기타 에러
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=generate-hint-boj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-hint-boj.js","sourceRoot":"","sources":["../../../src/tools/boj/generate-hint-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACpC,QAAQ,CAAC,UAAU,CAAC;CACxB,CAAC,CAAC;AAYH;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAyB;IAC3D,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iGAgCgF;QAC7F,WAAW,EAAE,0BAA0B;QACvC,OAAO,EAAE,KAAK,EAAE,KAA2B,EAAwB,EAAE;YACnE,IAAI,CAAC;gBACH,QAAQ;gBACR,MAAM,EAAE,UAAU,EAAE,GAAG,0BAA0B,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAE/D,uBAAuB;gBACvB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAEzD,iCAAiC;gBACjC,OAAO;oBACL,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;iBACtC,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,YAAY;gBACZ,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;oBAChC,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC1D,CAAC;gBAED,uBAAuB;gBACvB,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBAC1C,MAAM,IAAI,KAAK,CAAC,kBAAmB,KAA8B,CAAC,UAAU,GAAG,CAAC,CAAC;gBACnF,CAAC;gBAED,QAAQ;gBACR,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generate_review_template_boj MCP 도구
|
|
3
|
+
*
|
|
4
|
+
* BOJ 복습 템플릿 및 가이드 제공 (Keyless Architecture)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import type { ReviewTemplateGenerator } from '../../services/review-template-generator.js';
|
|
8
|
+
/**
|
|
9
|
+
* 입력 스키마
|
|
10
|
+
*/
|
|
11
|
+
export declare const GenerateReviewTemplateBOJInputSchema: z.ZodObject<{
|
|
12
|
+
problem_id: z.ZodNumber;
|
|
13
|
+
user_notes: z.ZodOptional<z.ZodString>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
problem_id: number;
|
|
16
|
+
user_notes?: string | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
problem_id: number;
|
|
19
|
+
user_notes?: string | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
export type GenerateReviewTemplateBOJInput = z.infer<typeof GenerateReviewTemplateBOJInputSchema>;
|
|
22
|
+
/**
|
|
23
|
+
* MCP TextContent 타입
|
|
24
|
+
*/
|
|
25
|
+
interface TextContent {
|
|
26
|
+
type: 'text';
|
|
27
|
+
text: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* generate_review_template_boj 도구 핸들러
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateReviewTemplateBOJTool(generator: ReviewTemplateGenerator): {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
inputSchema: z.ZodObject<{
|
|
36
|
+
problem_id: z.ZodNumber;
|
|
37
|
+
user_notes: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
problem_id: number;
|
|
40
|
+
user_notes?: string | undefined;
|
|
41
|
+
}, {
|
|
42
|
+
problem_id: number;
|
|
43
|
+
user_notes?: string | undefined;
|
|
44
|
+
}>;
|
|
45
|
+
handler: (input: GenerateReviewTemplateBOJInput) => Promise<TextContent>;
|
|
46
|
+
};
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=generate-review-template-boj.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-review-template-boj.d.ts","sourceRoot":"","sources":["../../../src/tools/boj/generate-review-template-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AAG3F;;GAEG;AACH,eAAO,MAAM,oCAAoC;;;;;;;;;EAK/C,CAAC;AAEH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAC;AAElG;;GAEG;AACH,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,uBAAuB;;;;;;;;;;;;;qBAKrD,8BAA8B,KAAG,OAAO,CAAC,WAAW,CAAC;EA6B/E"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* generate_review_template_boj MCP 도구
|
|
3
|
+
*
|
|
4
|
+
* BOJ 복습 템플릿 및 가이드 제공 (Keyless Architecture)
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { ProblemNotFoundError } from '../../api/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* 입력 스키마
|
|
10
|
+
*/
|
|
11
|
+
export const GenerateReviewTemplateBOJInputSchema = z.object({
|
|
12
|
+
problem_id: z.number().int().positive()
|
|
13
|
+
.describe('백준 문제 번호'),
|
|
14
|
+
user_notes: z.string().optional()
|
|
15
|
+
.describe('사용자가 미리 작성한 메모 (선택)'),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* generate_review_template_boj 도구 핸들러
|
|
19
|
+
*/
|
|
20
|
+
export function generateReviewTemplateBOJTool(generator) {
|
|
21
|
+
return {
|
|
22
|
+
name: 'generate_review_template_boj',
|
|
23
|
+
description: '백준(BOJ) 문제에 대한 사용자의 제출 코드와 문제 본문을 분석해서 사용자 맞춤형 복기용 가이드를 제공합니다. 마크다운 템플릿, 문제 분석, 관련 문제, 작성 프롬프트를 포함합니다.\n\n⚠️ 플랫폼 판별: 문제 번호만 입력된 경우 대화 맥락에서 플랫폼을 파악하거나, 맥락이 없으면 반드시 BOJ/프로그래머스 중 어느 플랫폼인지 사용자에게 확인 후 호출하세요.',
|
|
24
|
+
inputSchema: GenerateReviewTemplateBOJInputSchema,
|
|
25
|
+
handler: async (input) => {
|
|
26
|
+
try {
|
|
27
|
+
// 입력 검증
|
|
28
|
+
const { problem_id, user_notes } = GenerateReviewTemplateBOJInputSchema.parse(input);
|
|
29
|
+
// 템플릿 생성
|
|
30
|
+
const template = await generator.generate(problem_id, user_notes);
|
|
31
|
+
// JSON 문자열로 반환
|
|
32
|
+
return {
|
|
33
|
+
type: 'text',
|
|
34
|
+
text: JSON.stringify(template, null, 2),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// Zod 검증 에러
|
|
39
|
+
if (error instanceof z.ZodError) {
|
|
40
|
+
throw new Error(`입력 검증 실패: ${error.issues[0].message}`);
|
|
41
|
+
}
|
|
42
|
+
// ProblemNotFoundError
|
|
43
|
+
if (error instanceof ProblemNotFoundError) {
|
|
44
|
+
throw new Error(`문제를 찾을 수 없습니다: ${input.problem_id}번`);
|
|
45
|
+
}
|
|
46
|
+
// 기타 에러
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=generate-review-template-boj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-review-template-boj.js","sourceRoot":"","sources":["../../../src/tools/boj/generate-review-template-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACpC,QAAQ,CAAC,UAAU,CAAC;IACvB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC9B,QAAQ,CAAC,qBAAqB,CAAC;CACnC,CAAC,CAAC;AAYH;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,SAAkC;IAC9E,OAAO;QACL,IAAI,EAAE,8BAA8B;QACpC,WAAW,EAAE,4MAA4M;QACzN,WAAW,EAAE,oCAAoC;QACjD,OAAO,EAAE,KAAK,EAAE,KAAqC,EAAwB,EAAE;YAC7E,IAAI,CAAC;gBACH,QAAQ;gBACR,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,oCAAoC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAErF,SAAS;gBACT,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBAElE,eAAe;gBACf,OAAO;oBACL,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,YAAY;gBACZ,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;oBAChC,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC1D,CAAC;gBAED,uBAAuB;gBACvB,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBAC1C,MAAM,IAAI,KAAK,CAAC,kBAAmB,KAAwC,CAAC,UAAU,GAAG,CAAC,CAAC;gBAC7F,CAAC;gBAED,QAAQ;gBACR,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_problem 도구
|
|
3
|
+
*
|
|
4
|
+
* 특정 BOJ 문제의 상세 정보를 조회합니다.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* 입력 스키마
|
|
9
|
+
*/
|
|
10
|
+
export declare const GetProblemInputSchema: z.ZodObject<{
|
|
11
|
+
problem_id: z.ZodNumber;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
problem_id: number;
|
|
14
|
+
}, {
|
|
15
|
+
problem_id: number;
|
|
16
|
+
}>;
|
|
17
|
+
export type GetProblemInput = z.infer<typeof GetProblemInputSchema>;
|
|
18
|
+
/**
|
|
19
|
+
* 문제 상세 조회 도구 핸들러
|
|
20
|
+
*/
|
|
21
|
+
export declare function getProblem(input: GetProblemInput): Promise<string>;
|
|
22
|
+
//# sourceMappingURL=get-problem-boj.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-problem-boj.d.ts","sourceRoot":"","sources":["../../../src/tools/boj/get-problem-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;EAEhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAgEpE;;GAEG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBxE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get_problem 도구
|
|
3
|
+
*
|
|
4
|
+
* 특정 BOJ 문제의 상세 정보를 조회합니다.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { SolvedAcClient } from '../../api/solvedac-client.js';
|
|
8
|
+
import { ProblemNotFoundError } from '../../api/types.js';
|
|
9
|
+
import { getTierBadge, levelToTier } from '../../utils/tier-converter.js';
|
|
10
|
+
/**
|
|
11
|
+
* 입력 스키마
|
|
12
|
+
*/
|
|
13
|
+
export const GetProblemInputSchema = z.object({
|
|
14
|
+
problem_id: z.number().int().positive().describe('BOJ 문제 번호 (양의 정수)'),
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* 한글 태그명 추출 (우선순위: 한글 > 영어 > 키)
|
|
18
|
+
*/
|
|
19
|
+
function getKoreanTagName(tag) {
|
|
20
|
+
const korean = tag.displayNames.find(dn => dn.language === 'ko');
|
|
21
|
+
if (korean)
|
|
22
|
+
return korean.name;
|
|
23
|
+
const english = tag.displayNames.find(dn => dn.language === 'en');
|
|
24
|
+
if (english)
|
|
25
|
+
return english.name;
|
|
26
|
+
return tag.key;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 문제 상세 정보를 마크다운으로 포맷팅
|
|
30
|
+
*/
|
|
31
|
+
function formatProblemDetail(problem) {
|
|
32
|
+
const tierBadge = getTierBadge(problem.level);
|
|
33
|
+
const tierName = levelToTier(problem.level);
|
|
34
|
+
const bojLink = `https://www.acmicpc.net/problem/${problem.problemId}`;
|
|
35
|
+
let result = `# [${problem.problemId}] ${problem.titleKo}\n\n`;
|
|
36
|
+
// 기본 정보
|
|
37
|
+
result += `## 📋 기본 정보\n\n`;
|
|
38
|
+
result += `- **문제 번호**: ${problem.problemId}\n`;
|
|
39
|
+
result += `- **제목**: ${problem.titleKo}\n`;
|
|
40
|
+
result += `- **난이도**: ${tierBadge} (레벨 ${problem.level})\n`;
|
|
41
|
+
result += `- **링크**: [BOJ ${problem.problemId}](${bojLink})\n\n`;
|
|
42
|
+
// 태그
|
|
43
|
+
if (problem.tags.length > 0) {
|
|
44
|
+
result += `## 🏷️ 알고리즘 태그\n\n`;
|
|
45
|
+
const tagList = problem.tags.map(tag => {
|
|
46
|
+
const koreanName = getKoreanTagName(tag);
|
|
47
|
+
return `- **${koreanName}** (\`${tag.key}\`)`;
|
|
48
|
+
}).join('\n');
|
|
49
|
+
result += `${tagList}\n\n`;
|
|
50
|
+
}
|
|
51
|
+
// 통계
|
|
52
|
+
result += `## 📊 통계\n\n`;
|
|
53
|
+
result += `- **해결한 사용자**: ${problem.acceptedUserCount.toLocaleString()}명\n`;
|
|
54
|
+
result += `- **평균 시도 횟수**: ${problem.averageTries.toFixed(1)}회\n`;
|
|
55
|
+
// 풀이 가능 여부
|
|
56
|
+
if (problem.isSolvable !== undefined) {
|
|
57
|
+
result += `- **풀이 가능**: ${problem.isSolvable ? '✅ 예' : '❌ 아니오'}\n`;
|
|
58
|
+
}
|
|
59
|
+
if (problem.isPartial !== undefined) {
|
|
60
|
+
result += `- **부분 점수**: ${problem.isPartial ? '✅ 있음' : '❌ 없음'}\n`;
|
|
61
|
+
}
|
|
62
|
+
result += `\n---\n`;
|
|
63
|
+
result += `💡 **팁**: 위 링크를 클릭하면 BOJ에서 문제를 확인할 수 있습니다.\n`;
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 문제 상세 조회 도구 핸들러
|
|
68
|
+
*/
|
|
69
|
+
export async function getProblem(input) {
|
|
70
|
+
const client = new SolvedAcClient();
|
|
71
|
+
try {
|
|
72
|
+
// API 호출
|
|
73
|
+
const problem = await client.getProblem(input.problem_id);
|
|
74
|
+
// 결과 포맷팅
|
|
75
|
+
return formatProblemDetail(problem);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// 에러 처리
|
|
79
|
+
if (error instanceof ProblemNotFoundError) {
|
|
80
|
+
throw new Error(`문제 ${input.problem_id}를 찾을 수 없습니다. 문제 번호를 확인해주세요.\n` +
|
|
81
|
+
`BOJ 링크: https://www.acmicpc.net/problem/${input.problem_id}`);
|
|
82
|
+
}
|
|
83
|
+
if (error instanceof Error) {
|
|
84
|
+
throw new Error(`문제 조회 중 오류가 발생했습니다: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
throw new Error('문제 조회 중 알 수 없는 오류가 발생했습니다.');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=get-problem-boj.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-problem-boj.js","sourceRoot":"","sources":["../../../src/tools/boj/get-problem-boj.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_problems 도구
|
|
3
|
+
*
|
|
4
|
+
* BOJ 문제를 티어, 태그, 키워드로 검색합니다.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* 입력 스키마
|
|
9
|
+
*
|
|
10
|
+
* level_min/level_max는 숫자(1-30) 또는 티어 문자열(예: "실버 3", "Gold I") 모두 지원
|
|
11
|
+
*/
|
|
12
|
+
export declare const SearchProblemsInputSchema: z.ZodObject<{
|
|
13
|
+
query: z.ZodOptional<z.ZodString>;
|
|
14
|
+
level_min: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodEffects<z.ZodString, number, string>]>>;
|
|
15
|
+
level_max: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodEffects<z.ZodString, number, string>]>>;
|
|
16
|
+
tags: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
17
|
+
sort: z.ZodOptional<z.ZodEnum<["level", "id", "average_try"]>>;
|
|
18
|
+
direction: z.ZodOptional<z.ZodEnum<["asc", "desc"]>>;
|
|
19
|
+
page: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
page: number;
|
|
22
|
+
sort?: "level" | "id" | "average_try" | undefined;
|
|
23
|
+
tags?: string | string[] | undefined;
|
|
24
|
+
query?: string | undefined;
|
|
25
|
+
direction?: "asc" | "desc" | undefined;
|
|
26
|
+
level_min?: number | undefined;
|
|
27
|
+
level_max?: number | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
sort?: "level" | "id" | "average_try" | undefined;
|
|
30
|
+
tags?: string | string[] | undefined;
|
|
31
|
+
query?: string | undefined;
|
|
32
|
+
direction?: "asc" | "desc" | undefined;
|
|
33
|
+
page?: number | undefined;
|
|
34
|
+
level_min?: string | number | undefined;
|
|
35
|
+
level_max?: string | number | undefined;
|
|
36
|
+
}>;
|
|
37
|
+
export type SearchProblemsInput = z.infer<typeof SearchProblemsInputSchema>;
|
|
38
|
+
/**
|
|
39
|
+
* 문제 검색 도구 핸들러
|
|
40
|
+
*/
|
|
41
|
+
export declare function searchProblems(input: SearchProblemsInput): Promise<string>;
|
|
42
|
+
//# sourceMappingURL=search-problems-boj.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-problems-boj.d.ts","sourceRoot":"","sources":["../../../src/tools/boj/search-problems-boj.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;GAIG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;EA+DpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAiD5E;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiChF"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* search_problems 도구
|
|
3
|
+
*
|
|
4
|
+
* BOJ 문제를 티어, 태그, 키워드로 검색합니다.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { SolvedAcClient } from '../../api/solvedac-client.js';
|
|
8
|
+
import { getTierBadge, parseTierString } from '../../utils/tier-converter.js';
|
|
9
|
+
/**
|
|
10
|
+
* 입력 스키마
|
|
11
|
+
*
|
|
12
|
+
* level_min/level_max는 숫자(1-30) 또는 티어 문자열(예: "실버 3", "Gold I") 모두 지원
|
|
13
|
+
*/
|
|
14
|
+
export const SearchProblemsInputSchema = z.object({
|
|
15
|
+
query: z.string().optional().describe('검색 키워드 (제목, 번호, 태그 등)'),
|
|
16
|
+
level_min: z
|
|
17
|
+
.union([
|
|
18
|
+
z.number().int().min(1).max(30),
|
|
19
|
+
z.string().transform((val, ctx) => {
|
|
20
|
+
// 숫자 문자열이면 숫자로 변환 (예: "16" → 16)
|
|
21
|
+
const numVal = Number(val);
|
|
22
|
+
if (!isNaN(numVal) && Number.isInteger(numVal)) {
|
|
23
|
+
if (numVal < 1 || numVal > 30) {
|
|
24
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: '레벨은 1-30 사이여야 합니다.' });
|
|
25
|
+
return z.NEVER;
|
|
26
|
+
}
|
|
27
|
+
return numVal;
|
|
28
|
+
}
|
|
29
|
+
// 티어 문자열 파싱 (예: "실버 3", "Gold I")
|
|
30
|
+
try {
|
|
31
|
+
return parseTierString(val);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
ctx.addIssue({
|
|
35
|
+
code: z.ZodIssueCode.custom,
|
|
36
|
+
message: error instanceof Error ? error.message : '티어 파싱 실패',
|
|
37
|
+
});
|
|
38
|
+
return z.NEVER;
|
|
39
|
+
}
|
|
40
|
+
}),
|
|
41
|
+
])
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('최소 난이도 (숫자 1-30 또는 "실버 3", "Gold I" 형식)'),
|
|
44
|
+
level_max: z
|
|
45
|
+
.union([
|
|
46
|
+
z.number().int().min(1).max(30),
|
|
47
|
+
z.string().transform((val, ctx) => {
|
|
48
|
+
// 숫자 문자열이면 숫자로 변환 (예: "17" → 17)
|
|
49
|
+
const numVal = Number(val);
|
|
50
|
+
if (!isNaN(numVal) && Number.isInteger(numVal)) {
|
|
51
|
+
if (numVal < 1 || numVal > 30) {
|
|
52
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: '레벨은 1-30 사이여야 합니다.' });
|
|
53
|
+
return z.NEVER;
|
|
54
|
+
}
|
|
55
|
+
return numVal;
|
|
56
|
+
}
|
|
57
|
+
// 티어 문자열 파싱 (예: "실버 3", "Gold I")
|
|
58
|
+
try {
|
|
59
|
+
return parseTierString(val);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
ctx.addIssue({
|
|
63
|
+
code: z.ZodIssueCode.custom,
|
|
64
|
+
message: error instanceof Error ? error.message : '티어 파싱 실패',
|
|
65
|
+
});
|
|
66
|
+
return z.NEVER;
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
])
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('최대 난이도 (숫자 1-30 또는 "실버 3", "Gold I" 형식)'),
|
|
72
|
+
tags: z
|
|
73
|
+
.union([z.string(), z.array(z.string())])
|
|
74
|
+
.optional()
|
|
75
|
+
.describe('알고리즘 태그 (예: "dp" 또는 ["dp", "greedy", "bfs"])'),
|
|
76
|
+
sort: z.enum(['level', 'id', 'average_try']).optional().describe('정렬 기준'),
|
|
77
|
+
direction: z.enum(['asc', 'desc']).optional().describe('정렬 방향'),
|
|
78
|
+
page: z.number().int().min(1).optional().default(1).describe('페이지 번호 (기본: 1)'),
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* 한글 태그명 추출
|
|
82
|
+
*/
|
|
83
|
+
function getKoreanTagName(tag) {
|
|
84
|
+
const korean = tag.displayNames.find(dn => dn.language === 'ko');
|
|
85
|
+
return korean?.name || tag.key;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 문제 검색 결과를 마크다운 테이블로 포맷팅
|
|
89
|
+
*/
|
|
90
|
+
function formatSearchResults(items, count, page) {
|
|
91
|
+
if (items.length === 0) {
|
|
92
|
+
return '검색 결과가 없습니다.';
|
|
93
|
+
}
|
|
94
|
+
let result = `# 문제 검색 결과\n\n`;
|
|
95
|
+
result += `**총 ${count}개 문제** (${page}페이지)\n\n`;
|
|
96
|
+
result += `| 번호 | 제목 | 난이도 | 태그 | 해결자 수 | 평균 시도 |\n`;
|
|
97
|
+
result += `|------|------|--------|------|-----------|----------|\n`;
|
|
98
|
+
for (const problem of items) {
|
|
99
|
+
const tierBadge = getTierBadge(problem.level);
|
|
100
|
+
const tags = problem.tags.slice(0, 3).map(getKoreanTagName).join(', ');
|
|
101
|
+
const tagsDisplay = problem.tags.length > 3 ? `${tags}...` : tags;
|
|
102
|
+
const bojLink = `https://www.acmicpc.net/problem/${problem.problemId}`;
|
|
103
|
+
result += `| [${problem.problemId}](${bojLink}) | ${problem.titleKo} | ${tierBadge} | ${tagsDisplay} | ${problem.acceptedUserCount.toLocaleString()}명 | ${problem.averageTries.toFixed(1)}회 |\n`;
|
|
104
|
+
}
|
|
105
|
+
result += `\n---\n`;
|
|
106
|
+
result += `💡 **팁**: 문제 번호를 클릭하면 BOJ 페이지로 이동합니다.\n`;
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 문제 검색 도구 핸들러
|
|
111
|
+
*/
|
|
112
|
+
export async function searchProblems(input) {
|
|
113
|
+
// 입력 검증
|
|
114
|
+
if (input.level_min !== undefined && input.level_max !== undefined) {
|
|
115
|
+
if (input.level_min > input.level_max) {
|
|
116
|
+
throw new Error('level_min은 level_max보다 작거나 같아야 합니다.');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const client = new SolvedAcClient();
|
|
120
|
+
try {
|
|
121
|
+
// API 호출
|
|
122
|
+
const params = {
|
|
123
|
+
query: input.query,
|
|
124
|
+
level_min: input.level_min,
|
|
125
|
+
level_max: input.level_max,
|
|
126
|
+
tags: input.tags,
|
|
127
|
+
sort: input.sort,
|
|
128
|
+
direction: input.direction,
|
|
129
|
+
page: input.page,
|
|
130
|
+
};
|
|
131
|
+
const result = await client.searchProblems(params);
|
|
132
|
+
// 결과 포맷팅
|
|
133
|
+
return formatSearchResults(result.items, result.count, input.page);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// 에러 메시지 포맷팅
|
|
137
|
+
if (error instanceof Error) {
|
|
138
|
+
throw new Error(`문제 검색 중 오류가 발생했습니다: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
throw new Error('문제 검색 중 알 수 없는 오류가 발생했습니다.');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=search-problems-boj.js.map
|