
Joseon Sillok Search
Query the official Joseon Sillok (Annals of the Joseon Dynasty) web archive and return structured article snippets for historical or cultural research in agent workflows.
Overview
joseon-sillok-search is an agent skill most often used in Idea (also Build integrations, Grow content) that searches the Joseon Sillok official web archive and returns cleaned article excerpts.
Install
npx skills add https://github.com/nomadamas/k-skill --skill joseon-sillok-searchWhat is this skill?
- Python client for sillok.history.go.kr search and article detail URLs.
- POST-based search with pagination (default limit 5, max 20 pages) and HTML parsing helpers.
- Optional requests dependency with urllib fallback and cookie-aware fetching.
- Normalizes article text by stripping tags, comments, and archive footer boilerplate.
- CLI-oriented argparse entry for agent-driven batch lookups.
- Default search result limit: 5
- Maximum pagination: 20 pages
- Default HTTP timeout: 30 seconds
Adoption & trust: 2.3k installs on skills.sh; 5.4k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need verifiable Joseon-era primary text for a project but manual Korean archive search is slow and hard to automate inside an agent loop.
Who is it for?
Builders doing Korean history research, edtech, or cultural content who want scripted annal lookup inside Claude Code or Cursor.
Skip if: Users who need licensed bulk corpora offline, English-only sources with no Korean context, or production crawling at scale without respecting site policy.
When should I use this skill?
You need automated search or detail fetch from the Joseon Sillok official site inside an agent workflow.
What do I get? / Deliverables
You get structured search hits and readable article text from sillok.history.go.kr suitable for notes, citations, or downstream RAG indexing steps.
- JSON or text-normalized search results with article IDs
- Cleaned passage text suitable for notes or indexing
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Primary shelf is Idea / research because the skill’s first value is discovering and citing primary-source Korean history before you commit content, features, or narrative product direction. It automates search against sillok.history.go.kr—classic research discovery—not shipping, SEO, or infra operations.
Where it fits
Pull annal excerpts on a policy topic before scoping a history education mini-app.
Prototype a citation widget that resolves article_id detail pages from search queries.
Gather verified quotes for a newsletter series on Joseon governance themes.
How it compares
Use this targeted archive integration instead of generic web search when answers must cite Joseon Sillok article IDs and passages.
Common Questions / FAQ
Who is joseon-sillok-search for?
Solo builders and researchers automating lookups on the official Joseon Sillok site for agent-assisted writing, apps, or fact gathering.
When should I use joseon-sillok-search?
In Idea / research for primary-source discovery; in Build / integrations when prototyping citation or RAG features; in Grow / content when drafting historically grounded articles.
Is joseon-sillok-search safe to install?
It performs outbound HTTP to a government history site—review the Security Audits panel on this Prism page and avoid storing credentials or violating archive usage rules.
SKILL.md
READMESKILL.md - Joseon Sillok Search
from __future__ import annotations import argparse import html import json import math import re import ssl import sys import urllib.error import urllib.parse import urllib.request from dataclasses import asdict, dataclass from http.cookiejar import CookieJar from typing import Any, Iterable try: import requests except ImportError: # pragma: no cover - optional runtime dependency requests = None BASE_URL = "https://sillok.history.go.kr" SEARCH_URL = f"{BASE_URL}/search/searchResultList.do" DETAIL_URL_TEMPLATE = f"{BASE_URL}/id/{{article_id}}" DEFAULT_TIMEOUT = 30 DEFAULT_LIMIT = 5 MAX_PAGES = 20 DEFAULT_HEADERS = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7", "Content-Type": "application/x-www-form-urlencoded", "Origin": BASE_URL, "Referer": f"{BASE_URL}/main/main.do", "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36" ), } COMMENT_PATTERN = re.compile(r"<!--.*?-->", re.S) BR_PATTERN = re.compile(r"<br\s*/?>", re.I) TAG_PATTERN = re.compile(r"<[^>]+>") WHITESPACE_PATTERN = re.compile(r"\s+") DIGITS_PATTERN = re.compile(r"\d+") DETAIL_FOOTER_PATTERN = re.compile( r"\s*(?:[【〖](?:태백산사고본|국편영인본|분류)[】〗]|ⓒ\s*세종대왕기념사업회).*", re.S, ) CATEGORY_PATTERN = re.compile( r"<a\s+href=\"javascript:searchCategory\('([^']*)'\);\"\s+class=\"cate-item[^\"]*\">(.*?)</a>", re.S, ) RESULT_PATTERN = re.compile( r"<div\s+class=\"result-box\">.*?" r"<a\s+href=\"javascript:goView\('([^']+)',\s*\d+\);\"\s+class=\"subject\">(.*?)</a>\s*" r"<p\s+class=\"text\">(.*?)</p>", re.S, ) TITLE_HEAD_PATTERN = re.compile( r"<div\s+class=\"title\">\s*<p\s+class=\"date\">(.*?)</p>\s*<h3>(.*?)</h3>", re.S, ) LEFT_VIEW_PATTERN = re.compile( r"<div\s+class=\"view-item\s+left\">.*?<div\s+class=\"view-text\">(.*?)</div>", re.S, ) RIGHT_VIEW_PATTERN = re.compile( r"<div\s+class=\"view-item\s+right\">.*?<div\s+class=\"view-text\">(.*?)</div>", re.S, ) CLASSIFICATION_PATTERN = re.compile(r"<li\s+class=\"view_font02\">\s*[【〖]분류[】〗]\s*(.*?)</li>", re.S) TOTAL_COUNT_PATTERN = re.compile(r"검색결과\s*<strong>(\d+)</strong>개") HIDDEN_VALUE_TEMPLATE = '<input[^>]+id="{field}"[^>]+value="([^"]*)"' KING_ACCESSION_YEARS = { "태조": 1392, "정종": 1399, "태종": 1401, "세종": 1418, "문종": 1450, "단종": 1452, "세조": 1455, "예종": 1468, "성종": 1469, "연산군": 1494, "중종": 1506, "인종": 1545, "명종": 1545, "선조": 1567, "선조수정": 1567, "광해군중초본": 1608, "광해군정초본": 1608, "인조": 1623, "효종": 1649, "현종": 1659, "현종개수": 1659, "숙종": 1674, "숙종보궐정오": 1674, "경종": 1720, "경종수정": 1720, "영조": 1724, "정조": 1776, "순조": 1800, "헌종": 1834, "철종": 1849, "고종": 1863, "순종": 1907, "순종부록": 1910, } KING_ALIASES = { "태조실록": "태조", "정종실록": "정종", "태종실록": "태종", "세종실록": "세종", "문종실록": "문종", "단종실록": "단종", "세조실록": "세조", "예종실록": "예종", "성종실록": "성종", "연산군일기": "연산군", "중종실록": "중종", "인종실록": "인종", "명종실록": "명종", "선조실록": "선조", "선조수정실록": "선조수정", "광해군중초본": "광해군중초본", "광해군정초본": "광해군정초본", "인조실록": "인조", "효종실록": "효종", "현종실록": "현종", "현종개수실록": "현종개수", "숙종실록": "숙종", "숙종보궐정오": "숙종보궐정오", "경종실록": "경종", "경종수정실록": "경종수정", "영조실록": "영조", "정조실록": "정조", "순조실록": "순조", "헌종실록": "헌종", "철종실록": "철종", "고종실록": "고종", "순종실록": "순종", "순종실록부록": "순종부록", "순종부록": "순종부록", } for canonical in KING_ACCESSION_YEARS: KING_ALIASES.setdefault(canonical, canonical) @dataclass(frozen=True) class SearchCategory: label: str count: int token: str @dataclass(frozen=True) class ResultTitleMetadata: king: str | None regnal_year: int | None gregorian_year: int | None article_title: str @dataclass(frozen=True) class SearchResult: