get_alio_annexes
Retrieve annex sections from Korean regulations. Filter by annex number or list only annex titles without content.
Instructions
[ALIO] 규정 본문에서 [별표 N] 섹션만 추출. annexNumber 로 특정 별표 한정 가능. listOnly 로 목록만 조회.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| institution | Yes | 기관코드 또는 기관명 | |
| regId | No | 규정 ID | |
| title | No | 규정 제목 부분일치 (regId 대신) | |
| annexNumber | No | 특정 별표 번호만 (예: 1 → [별표 1] 만). 생략 시 전체 별표 | |
| listOnly | Yes | 본문 없이 별표 목록만 |
Implementation Reference
- src/tools/alio/annexes.ts:38-105 (handler)Main handler function for get_alio_annexes. Loads the institution index, finds the matching regulation, reads its markdown body, extracts [별표 N] (annex) sections using regex, filters by annex number if specified, and returns either a list or full annex content.
export async function getAlioAnnexes( _api: LawApiClient, input: GetAlioAnnexesInput ): Promise<ToolResponse> { try { const idx = await loadIndex() const inst = findInstitution(idx, input.institution) if (!inst) { return { content: [{ type: "text", text: `기관을 찾을 수 없습니다: ${input.institution}` }], isError: true, } } const manifest = idx.manifests.get(inst.apbaId) const entry = manifest?.regulations.find((r) => input.regId ? r.regId === input.regId : input.title ? r.title.includes(input.title) : false ) if (!entry) { return { content: [ { type: "text", text: `규정을 찾을 수 없습니다. institution=${inst.apbaId}` }, ], isError: true, } } const md = await readRegulationMd(inst.apbaId, entry.regId) if (!md) { return { content: [{ type: "text", text: `본문 파일을 읽을 수 없습니다: ${entry.mdPath}` }], isError: true, } } const annexes = extractAnnexes(md) const filtered = input.annexNumber ? annexes.filter((a) => a.number === input.annexNumber) : annexes const lines: string[] = [] lines.push(`# [${inst.apbaId}] ${inst.apbaNa} — ${entry.title}`) lines.push(`> 별표 ${annexes.length}개 발견${input.annexNumber ? ` (#${input.annexNumber}만 표시)` : ""}`) lines.push("") if (filtered.length === 0) { lines.push("- 별표가 없거나 해당 번호의 별표가 없습니다.") if (annexes.length > 0) { lines.push("") lines.push("발견된 별표 목록:") annexes.forEach((a) => lines.push(`- ${a.heading}`)) } } else if (input.listOnly) { filtered.forEach((a) => lines.push(`- ${a.heading}`)) } else { for (const a of filtered) { lines.push("---") lines.push(`## ${a.heading}`) lines.push("") lines.push(a.body.trim()) lines.push("") } } return { content: [{ type: "text", text: truncateResponse(lines.join("\n")) }] } } catch (err) { return formatToolError(err, "get_alio_annexes") } } - src/tools/alio/annexes.ts:14-28 (schema)Zod schema defining the input: institution (required), regId or title (one required), optional annexNumber, and listOnly flag (defaults false) with a refine validator to ensure regId or title is provided.
export const GetAlioAnnexesSchema = z .object({ institution: z.string().describe("기관코드 또는 기관명"), regId: z.string().optional().describe("규정 ID"), title: z.string().optional().describe("규정 제목 부분일치 (regId 대신)"), annexNumber: z .number() .optional() .describe("특정 별표 번호만 (예: 1 → [별표 1] 만). 생략 시 전체 별표"), listOnly: z.boolean().default(false).describe("본문 없이 별표 목록만"), }) .refine((v) => !!(v.regId || v.title), { message: "regId 또는 title 중 하나는 필수", path: ["regId"], }) - src/tool-registry.ts:764-769 (registration)Registration entry in the tool registry: imports getAlioAnnexes and GetAlioAnnexesSchema from src/tools/alio/annexes.ts, registers the tool with name 'get_alio_annexes', description, schema, and handler.
{ name: "get_alio_annexes", description: "[ALIO] 규정 본문에서 [별표 N] 섹션만 추출. annexNumber 로 특정 별표 한정 가능. listOnly 로 목록만 조회.", schema: GetAlioAnnexesSchema, handler: getAlioAnnexes }, - src/tools/alio/annexes.ts:108-135 (helper)Helper function extractAnnexes that parses the markdown body using a regex to find [별표 N] headings and splits the document into annex sections with number, heading, and body.
function extractAnnexes(md: string): AnnexSection[] { // 헤딩 패턴: "## [별표 1]" 또는 "[별표 1] 제목" 또는 "[별표1]" / "[별 표 1]" const headRe = /^#{0,6}\s*\[\s*별\s*표\s*(\d+)?\s*\]([^\n]*)$/gm const matches: Array<{ idx: number; num: number | null; head: string }> = [] let m: RegExpExecArray | null while ((m = headRe.exec(md)) !== null) { matches.push({ idx: m.index, num: m[1] ? Number(m[1]) : null, head: m[0].replace(/^#+\s*/, "").trim(), }) } if (matches.length === 0) return [] const sections: AnnexSection[] = [] for (let i = 0; i < matches.length; i++) { const cur = matches[i] const next = matches[i + 1] const start = cur.idx + matches[i].head.length // heading 직후 const end = next ? next.idx : md.length sections.push({ number: cur.num, heading: cur.head, body: md.slice(start, end).trim(), }) } return sections }