l-convert
Convert a draft article to a blog-compatible MDX file. Use when: (1) User wants to publish a draft article to the blog, (2) User says 'convert to article', 'convert', 'publish article', or 'move to bl...
Convert Draft to Blog Article
Convert a doc site draft article from doc/src/content/docs/articles/ to a blog-compatible MDX file in blog/src/articles/.
Model requirement: This skill performs Japanese translation of English conversation blocks (### Takazudo / ### Claude sections become <ConvTakazudo> / <ConvClaude> with a Japanese translation after <TransSep />). The frontmatter pins model: opus so translation always runs on Opus — do not downgrade. If you find yourself running this skill on a smaller model, stop and re-invoke /l-convert so it runs on Opus.
Arguments
--auto: Automatically apply all suggested tags (including new ones) from/l-tag-attach-to-articlewithout asking for confirmation. Still report what tags were applied.
Steps
1. Identify the draft article
Determine which draft to convert:
- Check
git diff --name-onlyfor uncommitted changes indoc/src/content/docs/articles/ - Check
git log --oneline -10 --diff-filter=AM -- doc/src/content/docs/articles/for recently added or modified articles - If the user specified an article name or path, use that
- If multiple candidates exist or the target is unclear, ask the user which article to convert
2. Read the draft
Read the full content of the target Markdown file from doc/src/content/docs/articles/.
3. Determine the output filename
The output file goes to blog/src/articles/{slug}.mdx.
- Strip date/time prefixes from draft filenames before using as slug. Draft files often have prefixes like
20260301-1500-— remove theYYYYMMDD-HHMM-portion. For example:20260301-1500-claude-code-agent-teams-usage→claude-code-agent-teams-usage - If the remaining name is suitable as a slug (kebab-case, descriptive), use it
- Otherwise, derive a slug from the article title (lowercase, hyphens, ASCII only, max 50 chars)
- If a file with the same slug already exists, ask the user before overwriting
4. Build the frontmatter
The blog MDX requires this frontmatter format:
---
title: "記事のタイトル"
description: "記事の概要説明(1-2文)"
author: Takazudo
tags: []
createdAt: "YYYY-MM-DD"
---
- title: Use the draft’s
titlefrontmatter, or the first# headingif no frontmatter title exists. If neither exists, ask the user. - description: Use the draft’s
descriptionfrontmatter if available. Otherwise, generate a concise summary (1-2 sentences) based on the article content. - author: Always
Takazudo - tags: Leave as empty array
[]for now (tags will be attached via/l-tag-attach-to-articlein the next step) - createdAt: Today’s date in
"YYYY-MM-DD"format
Do not include doc site-specific frontmatter fields (sidebar_position, sidebar_label, pagination_next, pagination_prev).
5. Convert doc site admonitions to MDX components
Convert doc site admonition blocks to the corresponding blog MDX components:
:::note → <Note>
<!-- doc site input -->
:::note 注記
Content here.
Multiple lines supported.
:::
<!-- Blog MDX output -->
<Note>
Content here.
Multiple lines supported.
</Note>
:::info → <Info>
<!-- doc site input -->
:::info 備考
Content here.
:::
<!-- Blog MDX output -->
<Info>
Content here.
</Info>
Custom titles
If the admonition has a custom title (other than the standard 注記/備考):
:::note カスタムタイトル
Content here.
:::
<Note title="カスタムタイトル">
Content here.
</Note>
The standard titles are:
:::note 注記→<Note>(no title prop needed, defaults to 注記):::info 備考→<Info>(no title prop needed, defaults to 備考)
Other admonition types
If :::tip, :::warning, :::danger, or :::caution are found (they shouldn’t be per writing rules, but just in case):
:::tip→<Info>:::warning→<Note>:::danger→<Note>:::caution→<Note>
6. Handle other doc site-specific syntax
- Internal doc links: Convert doc site relative links (
[text](./other-doc.md)) that point to other docs to appropriate form. If the link target is another article, convert to the blog URL format (/articles/slug). If it’s a reference doc, remove the link and keep the text. - Import statements: Remove any doc site-specific import statements (e.g.,
import CategoryNav from ...) - doc site components: Remove doc site-specific JSX components (e.g.,
<CategoryNav />) - Image paths: See step 6a below.
- Fix internal links in the doc draft: See step 6b below.
- Approximation tildes (
~): See step 6c below.
6a. Copy and rewrite image paths
doc site serves static files from doc/public/, so images in drafts use paths like /img/articles/.... The blog (Next.js) serves static files from blog/public/ with basePath: '/pj/zpaper', so images must be both copied and path-rewritten.
For each image reference found in the article (markdown  or JSX <Img src="/img/..." />):
- Copy the image file from
doc/public/{path}toblog/public/{path}
- Example:
doc/public/img/articles/20260217-foo/bar.png→blog/public/img/articles/20260217-foo/bar.png - Create intermediate directories as needed (
mkdir -p)
- Rewrite the path in the MDX to include the blog basePath prefix
/pj/zpaper/
- Example:
/img/articles/20260217-foo/bar.png→/pj/zpaper/img/articles/20260217-foo/bar.png
This is necessary because:
- The blog uses
basePath: '/pj/zpaper'innext.config.mjs - Raw
<img>tags (from markdown images) are NOT automatically prefixed by Next.js — onlynext/imagegets basePath prefixing - The blog’s
Imgcomponent renders a plain<img>tag without basePath handling
If the draft article has no images, skip this step.
6b. Fix internal links in the doc draft
The doc draft (doc/src/content/docs/articles/) and the blog output (blog/src/articles/) use different link formats for internal article references. A link that works in the blog may break the doc site build, and vice versa. This step ensures both files have correct links.
Scan all markdown links in the article and identify internal links — links pointing to other articles or pages within the site (not external URLs like https://...).
Fix the doc draft source file (edit in place):
- Links to other articles must use doc site-relative paths:
./other-article-slug(no/articles/prefix, no.mdextension needed) - Blog-style absolute paths like
/articles/slugwill cause doc site broken link error — convert them to./slug - Verify the link target actually exists in
doc/src/content/docs/articles/by checking for a matching.mdfile
Fix the blog output:
- Links to other articles should use the blog URL format:
/articles/slug - doc site-relative paths like
./other-article-slugshould be converted to/articles/other-article-slug - Verify the link target actually exists in
blog/src/articles/by checking for a matching.mdxfile
If a link target doesn’t exist in either location, warn the user and ask how to handle it (remove the link, keep as-is, or fix the path).
6c. Sanitize approximation tildes (~)
Bare ASCII ~ used as an “approximately” marker (e.g., ~80 LOC, ~1–3s, ~500K) is dangerous in the blog’s MDX rendering. The blog parser treats ~text~ (single tildes) as GFM strikethrough, so when two such tildes appear in the same paragraph or bold span, the text between them gets rendered with a strike-through line.
This often shows up because Claude’s English responses use ~ casually, and a literal Japanese translation preserves it.
Rules:
-
In Japanese paragraphs (after
<TransSep />): Replace approximation~N(whereNis a digit) with約N. This is the most natural reading in Japanese and avoids the markdown ambiguity entirely.~80 LOC→約80 LOC~1-3秒→約1-3秒~500トークン→約500トークン
-
In English paragraphs (inside
<ConvClaude>/<ConvTakazudo>before<TransSep />): Keep~as-is in most cases — single tilde per paragraph renders fine. But if a paragraph or bold span contains two or more~Npatterns, escape them with\~to prevent strikethrough pairing.- Safe (single tilde):
Total: ~200–300 LOC of pure Rust. - Broken (two tildes pair as strikethrough):
**Rebuild time goes from ~1–3s (Astro) to ~1–10ms (Rust per file).** - Fixed:
**Rebuild time goes from \~1–3s (Astro) to \~1–10ms (Rust per file).**
- Safe (single tilde):
-
Inside backticks / code blocks: Leave
~untouched. Backticks disable markdown parsing, so~/.claude/and~80insidecodeare fine. -
Inside table cells: Single
~per cell is safe (the|separator breaks pairing). No change needed.
After writing the output, grep for ~[0-9] in the file and audit each occurrence to confirm it’s either inside backticks, inside a table cell, or a single occurrence per paragraph.
6d. Fix clear typos in conversation English
Takazudo’s English turns in conversation drafts are rough chat text typed quickly. They contain a mix of:
- Intentional rough chat voice — informal grammar, missing articles, unusual phrasing, em-dashes mid-thought, lowercase sentence starts, dropped words. Keep as-is. This is the author’s voice and not an error.
- Clear typos / errors — misspellings, subject-verb agreement errors, archaic word forms used by mistake, obvious wrong-word errors. Fix these silently.
Apply the same fixes to both the doc draft (in place) and the blog MDX output, so the two sources stay consistent.
Fix examples (do fix):
usefullness→usefulness(spelling)paragraph get dim→paragraph gets dim(subject-verb agreement)inconsistence→inconsistency(archaic / wrong word form for the intended meaning)recieve→receive,seperate→separate, etc. (common misspellings)
Do NOT fix (rough chat voice — leave alone):
- Missing articles:
it's almost same,it's same thing— chat style - Lowercase sentence starts after a period:
but nested lists are just lists.— chat style - Reordered modifiers:
the big another topic's start— chat style - Trailing tag-question forms:
..., do you?— chat style - Dropped helpers / non-standard verb forms:
seems helping,Is it like see my drawn picture— chat style - Em-dash thinking pauses:
zoom out --hum.— chat style - Concept→pronoun number agreement quirks:
Modular synths... it has many knobs— chat style
Heuristic: Fix only what a copy editor would mark as an unambiguous error. If you have to think for more than a second about whether it’s an error or a stylistic choice, leave it.
Translation impact: Typo fixes in the English source do not require re-translating the matching <TransSep /> block — the Japanese translation should already convey the intended meaning. Only re-translate if the typo materially changed the meaning conveyed in the English (rare).
This step applies only to Takazudo’s turns (<ConvTakazudo> blocks). Claude’s turns (<ConvClaude>) come from the model and don’t need typo fixing.
7. Write the output file
Write the converted MDX to blog/src/articles/{slug}.mdx.
8. Verify the output
After writing, read the output file and verify:
- Frontmatter has all required fields (
title,description,author,tags,createdAt) - No doc site-specific syntax remains (
:::, doc site imports, doc site components) - Admonitions are properly converted to
<Note>/<Info>components - The markdown structure is intact
- Tilde audit: run
grep -nP "~[0-9]"on the output and confirm each match is either inside backticks, inside a table cell, or a single occurrence per paragraph. Two~in the same paragraph or bold span will render as strikethrough — fix per step 6c.
9. Generate thumbnail and OGP image
Generate the kumiko-pattern thumbnail SVG and OGP PNG for the article:
pnpm kumiko-gen {slug}
pnpm svg-to-png {slug}
The root script already includes --color-scheme random --zoom 5, so no extra flags are needed.
This creates:
blog/public/thumbnails/{slug}.svg— Thumbnail displayed in article headerblog/public/thumbnails/{slug}.png— OGP image for social media (1200x630px)
No frontmatter changes are needed — the blog resolves thumbnails automatically by slug.
10. Tag the article
After successful conversion, run the tag attachment workflow:
- Use
/l-tag-attach-to-articleto suggest and attach tags - Use
/l-tag-update-listif new tags are needed
If --auto was passed: Automatically apply all suggested tags (including new ones) without asking for confirmation. Still report what tags were applied.
11. Report
Tell the user:
- The output file path
- A summary of conversions performed (number of admonitions converted, etc.)
- Remind them to review the article and commit when ready
Available Blog MDX Components
For reference, these are the custom MDX components available in the blog:
| Component | Purpose | Props |
|---|---|---|
<Note> | Supplementary note (注記) | title?: string (default: “注記”) |
<Info> | Additional info/aside (備考) | title?: string (default: “備考”) |
<InfoBox> | Highlighted info box | (children only) |
<Column> | Bordered column block | (children only) |
<Outro> | End-of-article section | (children only) |
<Youtube> | YouTube embed | (see component) |
<Img> | Image with caption | (see component) |
<ExImg> | Extended image | (see component) |
<ImgsGrid> | Image grid layout | (see component) |
<FlowRoot> | Float clearing container | (children only) |
<LayoutDivide> | Multi-column layout | (children only) |
<LayoutDivideItem> | Column item | (children only) |