
Korean Middle Korean
- 1.5k installs
- 7k repo stars
- Updated August 2, 2026
- nomadamas/k-skill
Korean Middle Korean is an agent skill that converts incoming Korean text into a deterministic Middle Korean-style rewrite with archaic particles, endings, Hanja hints, and tone-mark flavor.
About
Korean Middle Korean is an agent skill that converts contemporary Korean sentences into a consistent Middle Korean-inspired literary style. It replaces modern particles and verb endings with archaic forms, mixes in Hanja where appropriate, updates dates to classical notation, and leaves technical elements such as code, URLs, and proper names untouched. The transformation is rule-based and deterministic, making it ideal for consistent creative output. It is designed for fun, world-building, meme writing, and stylistic experimentation rather than rigorous historical scholarship or safety-critical documents. The skill runs locally via a Node.js helper script and requires no API keys.
- Deterministic conversion of modern Korean to Middle Korean-style with archaic particles and verb endings
- Transforms endings such as '했다' → 'ᄒᆞ엿다〮' and '하는' → 'ᄒᆞᄂᆞᆫ'
- Preserves URLs, code spans, Markdown links, emails, names and numbers with best-effort rules
- Applies Hanja hints and classical date notation (年, 月, 日)
- Scholarly recreation for creative use, not academic translation
Korean Middle Korean by the numbers
- 1,545 all-time installs (skills.sh)
- +237 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #205 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nomadamas/k-skill --skill korean-middle-koreanAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 7k |
| Last updated | August 2, 2026 |
| Repository | nomadamas/k-skill ↗ |
What it does
Instantly convert modern Korean text into stylized Middle Korean prose for creative writing, memes, world-building, or historical-flavored content.
Who is it for?
Best when you're creating Korean-language fiction, historical fantasy, memes, game lore, or stylized social content and want repeatable archaic tone without manual rewriting.
Skip if: Academic translation of historical texts, legal, medical or contractual documents where semantic precision is critical, or any use involving hate speech or defamation.
When should I use this skill?
When a user asks to rewrite Korean text in Middle Korean, Hunminjeongeum, or archaic meme style.
What you get
You receive a consistently transformed Middle Korean-flavored version of the input text that can be used directly in creative projects or passed to the next content-generation step.
- Transformed Korean text in Middle Korean stylistic output
By the numbers
- Preserves URLs, code spans, Markdown links and emails
- Best-effort handling of names and numbers
Files
Korean Middle Korean Style Converter
What this skill does
사용자가 한국어 문장을 "한국 중세 국어처럼", "훈민정음/중세국어 느낌으로", "옛 국어 밈체로" 바꾸어 달라고 할 때, 입력문을 결정론적 Middle Korean-style 문체로 바꾼다.
이 스킬은 학술적 복원이 아니라 창작용 스타일 변환이다.
- 현대 한국어 조사 일부를
ᄋᆞᆫ,ᄋᆞᆯ,애같은 중세국어풍 표기로 바꾼다. 했다,하는,말하는같은 현대 어미를ᄒᆞ엿다〮,ᄒᆞᄂᆞᆫ,ᄆᆞᆯᄒᆞᄂᆞᆫ처럼 바꾼다.- 날짜 단위는
年,月,日로 바꾼다. - 일부 한자어/밈 예시는
熱愛說,俳優,學校처럼 Hanja 힌트를 섞는다. - URL, 이메일, Markdown 링크, inline/fenced code span은 구조 토큰으로 보고 변환하지 않는다.
- 인명·숫자·고유명사는 완전 보존이 아니라, 규칙이 맞지 않을 때 원문을 남기는 best-effort 방식으로 처리한다.
- 한자어 힌트는 넓은 전역 치환으로 적용되므로 합성어·고유명사처럼 보이는 문자열 안에서도 바뀔 수 있다.
When to use
- "이 문장을 한국 중세 국어로 바꿔줘"
- "훈민정음 느낌 나는 밈 문장으로 변환해줘"
- "중세국어풍으로 농담을 써줘"
- "아래 글을 옛 국어 말투로 바꿔줘"
When NOT to use
- 학술 논문, 고문헌 번역, 훈민정음 해례본식 엄밀 표기가 필요한 작업
- 법률·의학·계약 문서처럼 의미 오해가 위험한 문서
- 혐오·괴롭힘·명예훼손 목적의 조롱성 변환
Prerequisites
node18+- 설치된
korean-middle-koreanskill 디렉터리 안에scripts/korean_middle_korean.jshelper 포함 - 별도 API 키 없음
Workflow
1. 변환할 한국어 텍스트를 받는다. 2. 설치된 korean-middle-korean skill 디렉터리를 기준으로 node scripts/korean_middle_korean.js 를 실행한다.
- URL, 이메일, Markdown 링크, inline/fenced code span은 보호되어 원문 그대로 복원된다.
3. 기본 JSON 출력에서 output을 사용자에게 반환한다. 4. 사용자가 근거를 원하면 replacements 배열의 규칙 적용 내역을 요약한다. 5. 학술적 정확성이 필요하다고 보이면 이 스킬은 창작용 스타일 변환임을 먼저 밝힌다.
CLI examples
node scripts/korean_middle_korean.js --text "민수는 3월 5일 학교에서 공부했다."
node scripts/korean_middle_korean.js --text "열애설을 인정했다." --format text
cat input.txt | node scripts/korean_middle_korean.js --stdin --format json
node scripts/korean_middle_korean.js --file ./input.txt --format textResponse policy
output을 중심으로 답한다.- "정확한 중세국어 번역"이라고 단정하지 말고, "중세국어풍/창작용 변환"이라고 설명한다.
- 사용자가 원문 의미 보존을 중요하게 말하면, 변환문 뒤에 "의미 보존 확인"을 짧게 덧붙인다.
Output schema
{
"profile": "middle-korean-style-v1",
"input": "열애설을 인정했다.",
"output": "熱愛說ᄋᆞᆯ 인졍ᄒᆞ엿다〮.",
"replacements": [
{ "kind": "lexicon", "from": "열애설", "to": "熱愛說", "count": 1 }
],
"contract": "Deterministic Korean Middle Korean-style rewrite..."
}Done when
node scripts/korean_middle_korean.js --help가 동작한다.--text,--file,--stdin입력이 모두 동작한다.- JSON과 text 출력이 모두 동작한다.
- 이슈 #270의 예시처럼 날짜/Hanja/중세국어풍 조사·어미·성조점이 나타난다.
#!/usr/bin/env node
"use strict";
const fs = require("node:fs");
const PROFILE = "middle-korean-style-v1";
const CONTRACT =
"Deterministic Korean Middle Korean-style rewrite: public-domain orthographic flavor rules, fixed broad lexicon replacements, archaic particles/endings, Sino-Korean Hanja hints, protected URL/email/Markdown-code spans, and best-effort preservation for names/numbers when no rule matches.";
const LEXICON = [
["야 이", "이"],
["맛국노야", "맛國노〮야"],
["설마", "쇼ᄆᆞ"],
["새벽", "샛ᄇᆡ긔〮"],
["배우", "俳優"],
["구자욱이랑", "구자욱과"],
["거리", "街里"],
["손잡고", "손ᄋᆞᆯ 자ᇙ고"],
["걸어다니는", "거러다니ᄂᆞᆫ"],
["모습", "모ᄉᆡᆸ〮"],
["찍혀", "찍히야"],
["열애설", "熱愛說"],
["터지고", "터ᄂᆞᆺ고"],
["인정했지만", "인졍ᄒᆞ엿거ᄂᆞᆫ"],
["인정했다", "인졍ᄒᆞ엿다〮"],
["인정", "인졍"],
["맛보기한", "맛보기〮ᄒᆞᆫ"],
["느낌이랄까", "닏믁이ᄅᆞᆯ가〯"],
["기분이구나", "기븐〮이로다"],
["말해", "ᄆᆞᆯᄒᆞ야"],
["사건", "일"],
["학교", "學校"],
];
function record(replacements, kind, from, to, count) {
if (count > 0) {
replacements.push({ kind, from, to, count });
}
}
function replaceLiteral(text, from, to, replacements) {
const count = text.split(from).length - 1;
if (count === 0) return text;
record(replacements, "lexicon", from, to, count);
return text.split(from).join(to);
}
function replaceRegex(text, pattern, to, replacements, kind, label) {
const matches = text.match(pattern);
const count = matches ? matches.length : 0;
const next = text.replace(pattern, to);
record(replacements, kind, label ?? String(pattern), typeof to === "string" ? to : "<rule>", count);
return next;
}
function protectSpans(input) {
const protectedSpans = [];
let text = input;
function protect(pattern) {
text = text.replace(pattern, (match) => {
const token = `\uE000${protectedSpans.length}\uE001`;
protectedSpans.push(match);
return token;
});
}
protect(/```[\s\S]*?```/g);
protect(/`[^`\n]*`/g);
protect(/\[[^\]\n]*\]\([^\s)]+(?:\s+"[^"]*")?\)/g);
protect(/\bhttps?:\/\/[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+/g);
protect(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g);
return { text, protectedSpans };
}
function restoreSpans(text, protectedSpans) {
let output = text;
protectedSpans.forEach((span, index) => {
output = output.replaceAll(`\uE000${index}\uE001`, span);
});
return output;
}
function convertToMiddleKoreanStyle(input) {
return createReport(input).output;
}
function createReport(input) {
if (typeof input !== "string") {
throw new TypeError("input must be a string");
}
const replacements = [];
const protectedInput = protectSpans(input);
let output = protectedInput.text;
output = replaceRegex(output, /(\d+)년/g, "$1年", replacements, "date", "년→年");
output = replaceRegex(output, /(\d+)월/g, "$1月", replacements, "date", "월→月");
output = replaceRegex(output, /(\d+)일/g, "$1日", replacements, "date", "일→日");
for (const [from, to] of LEXICON) {
output = replaceLiteral(output, from, to, replacements);
}
output = replaceRegex(output, /말하는/g, "ᄆᆞᆯᄒᆞᄂᆞᆫ", replacements, "ending", "말하는→ᄆᆞᆯᄒᆞᄂᆞᆫ");
output = replaceRegex(output, /공부했다〮?/g, "공부ᄒᆞ엿다〮", replacements, "ending", "공부했다→공부ᄒᆞ엿다〮");
output = replaceRegex(output, /했다〮?/g, "ᄒᆞ엿다〮", replacements, "ending", "했다→ᄒᆞ엿다〮");
output = replaceRegex(output, /하는(?=\s|[",.?!]|$)/g, "ᄒᆞᄂᆞᆫ", replacements, "ending", "하는→ᄒᆞᄂᆞᆫ");
output = replaceRegex(output, /된(?=\s|[",.?!]|$)/g, "ᄃᆞᆫ", replacements, "ending", "된→ᄃᆞᆫ");
output = replaceRegex(output, /것이냐(?=[.?!。]|$)/g, "것이냐〮", replacements, "ending", "것이냐→것이냐〮");
output = replaceRegex(output, /([가-힣ᄀ-ᇿA-Za-z0-9一-龥]+)(은|는)(?=\s|[",.?!]|$)/g, "$1ᄋᆞᆫ", replacements, "particle", "은/는→ᄋᆞᆫ");
output = replaceRegex(output, /([가-힣ᄀ-ᇿA-Za-z0-9一-龥]+)(을|를)(?=\s|[",.?!]|$)/g, "$1ᄋᆞᆯ", replacements, "particle", "을/를→ᄋᆞᆯ");
output = replaceRegex(output, /([가-힣ᄀ-ᇿA-Za-z0-9一-龥]+)에서(?=\s|[",.?!]|$)/g, "$1애", replacements, "particle", "에서→애");
output = replaceRegex(output, /([가-힣ᄀ-ᇿA-Za-z0-9一-龥]+)와(?=\s|[",.?!]|$)/g, "$1와", replacements, "particle", "와 보존");
output = output.replace(/\s+([,.;:?!])/g, "$1");
output = restoreSpans(output, protectedInput.protectedSpans);
return {
profile: PROFILE,
input,
output,
replacements,
contract: CONTRACT,
};
}
function parseArgs(argv) {
const parsed = {
format: "json",
inputMode: null,
text: undefined,
};
let sourceCount = 0;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "--text") {
sourceCount += 1;
parsed.inputMode = "text";
parsed.text = argv[++index];
if (parsed.text === undefined) throw new Error("--text requires a value");
} else if (arg === "--file") {
sourceCount += 1;
parsed.inputMode = "file";
parsed.file = argv[++index];
if (!parsed.file) throw new Error("--file requires a path");
} else if (arg === "--stdin") {
sourceCount += 1;
parsed.inputMode = "stdin";
} else if (arg === "--format") {
parsed.format = argv[++index];
if (!parsed.format) throw new Error("--format requires a value");
} else if (arg === "--help" || arg === "-h") {
parsed.help = true;
} else {
throw new Error(`unknown argument: ${arg}`);
}
}
if (!parsed.help && sourceCount !== 1) {
throw new Error("provide exactly one input source: --text, --file, or --stdin");
}
if (!["json", "text"].includes(parsed.format)) {
throw new Error(`unknown format: ${parsed.format}`);
}
return parsed;
}
function readInput(options) {
if (options.inputMode === "text") return options.text;
if (options.inputMode === "file") return fs.readFileSync(options.file, "utf8");
if (options.inputMode === "stdin") return fs.readFileSync(0, "utf8");
throw new Error("missing input source");
}
function helpText() {
return `Usage: node scripts/korean_middle_korean.js (--text TEXT | --file PATH | --stdin) [--format json|text]\n\nConverts Korean input into a deterministic Korean Middle Korean-style rewrite.\n`;
}
function main(argv = process.argv.slice(2)) {
const options = parseArgs(argv);
if (options.help) {
process.stdout.write(helpText());
return;
}
const report = createReport(readInput(options));
if (options.format === "text") {
process.stdout.write(`${report.output}\n`);
} else {
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
}
}
if (require.main === module) {
try {
main();
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
}
module.exports = {
CONTRACT,
PROFILE,
convertToMiddleKoreanStyle,
createReport,
parseArgs,
main,
};
Related skills
How it compares
Use instead of ad-hoc chat prompting when you need deterministic, rule-based stylistic output rather than varied LLM interpretations.
FAQ
Who is Korean Middle Korean for?
Developers and creators working in Korean who want a repeatable way to generate archaic, Hunminjeongeum-flavored prose for stories, memes, world-building, or creative experiments.
When should I use Korean Middle Korean?
Use it whenever you need to turn modern Korean copy into stylized Middle Korean tone for fiction, game dialogue, humorous historical memes, or consistent world-building voice. It is especially useful when you want the same archaic flavor across many pieces without manually editin
Is Korean Middle Korean safe to install?
Yes. The skill runs locally with no external API calls. Users should review the Security Audits panel on this page before installing.