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/.
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.
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).
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
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) |