name: session-to-skill-and-blog description: > Turn a completed non-trivial engineering session into a paired durable artifact: (1) a reusable skill under Innei's personal SKILL repo, and (2) a published blog post that narrates the journey and embeds the skill URL. Triggers on "把这个过程写成 skill 再写一篇 blog"、"沉淀一下这次的折腾"、 "productize this session"、"publish this as a skill and a writeup". metadata: author: innei version: "0.10.0"
session-to-skill-and-blog
Capture a hard-won session as a pair: an operational skill (durable artifact) and a narrative blog (discoverability layer linking to it).
Use when the session had ≥ 1 non-obvious pitfall, a novel workflow worth
keeping, or Innei said "写成 skill / productize this". Skip for pure
interactive Q&A or project-specific lessons (those go in the project's
CLAUDE.md).
Iron rule: skill first, blog second. The blog needs the skill URL, which only exists after the skill is pushed. And SKILL.md's format forces operational completeness before narrative drama distorts the lessons.
References
This file is a thin index. Load the matching reference when you start the actual work:
| File | When to load |
|---|---|
references/writing-style.md |
Before drafting prose — persona selection, Willison/Abramov/Antirez voice, punctuation, structure. |
references/node-usage.md |
Before using any extension node — deletion test, escalation ladder, scenario → node map, <dynamic> catalog rule, budgets, anti-patterns. |
references/visuals.md |
When planning diagrams or uploading image assets — Excalidraw vs Mermaid, palette, ≥3-diagram rule, mxs file upload. |
references/publish-flow.md |
When previewing / creating / editing the post — mxs preview, draft create, round-trip, stage/apply. |
references/widget-template/ |
When authoring a new <dynamic> widget — Yohaku-language UI/UX guide (DESIGN.md) + working vanilla skeleton (template.mjs, self-contained tokens, protocol plumbing). |
references/envelope.template.xml |
Copy as the post envelope before pasting the LiteXML body. |
For LiteXML tag syntax itself, load the litexml-authoring skill (fresh via
load-litexml.sh) — this skill governs whether/when, that one governs how.
Configuration
~/.config/innei-skills/config.json (see references/config.example.json):
{ "skill_repo_dir": "~/git/innei-repo/skill" }
Missing key → fallback to ~/git/innei-repo/skill.
Domains: infrastructure / automation / writing / research / content.
Prereqs once per machine: npm i -g @mx-space/cli (Node ≥ 22); mxs auth login.
Scripts
Define $S once per session:
S="$(realpath ~/.claude/skills/session-to-skill-and-blog)/scripts"
# Codex: swap ~/.claude for ~/.codex
| Script | What it does |
|---|---|
resolve-skill-repo.sh |
Print absolute path to the SKILL repo (config-driven, with fallback). |
scaffold-skill.sh |
Create dir + stub SKILL.md + README row (alphabetical) + both flat symlinks; git add staged. |
load-litexml.sh |
degit the latest litexml-authoring subtree (SKILL.md + references/) into ~/.cache/. |
push-skill.sh |
Idempotent mxs snippet create|update --type skill from a SKILL.md path. Emits the snowflake id on stdout. |
publish-post.sh |
mxs post create as draft, with aiGen=2, --open admin preview, --silent response; optional --skill-id <id> repeated arg threads ids into meta.skillIds. |
get-post.sh |
mxs post get <slug> --output xml — round-trip step 1. |
update-post.sh |
mxs post update <slug> --file … — round-trip step 2. |
Workflow
[1] Inventory session
[2] Scaffold + author SKILL.md
[3] Commit + push skill (to GitHub)
[4] Push skill to mx-core (returns snowflake id)
[5] Write blog
[6] Publish via mxs with --skill-id
[1] Inventory
Scan the session for: decision points, pitfalls (symptom → cause → fix),
inline code ≥ 15 lines (extract → scripts/), JSON/YAML ≥ 20 lines
(extract → references/ with <PLACEHOLDER> markers), verification
commands, "I was wrong about X" moments (alert callouts in the blog).
[2] Scaffold + author
bash "$S/scaffold-skill.sh" <domain> <skill-name> "<one-line purpose>"
Fill in the stub. SKILL.md sections in order: frontmatter (name,
description starting "Use when…", ≤ 500 chars) → overview → scope →
inputs → files provided → workflow ASCII → per-step → Common Pitfalls
table (mandatory) → rules → verification checklist. Target ≤ 250 lines.
[3] Commit + push
REPO="$(bash "$S/resolve-skill-repo.sh")"
cd "$REPO" && git commit -m "feat: add <skill-name> skill" && git push
The pre-commit hook enforces: README row exists; both flat symlinks
present and resolved. Skill URL (used at blog top + bottom):
https://github.com/Innei/SKILL/tree/main/skills/<domain>/<skill-name>
[4] Push skill to mx-core
The blog post will reference this skill via meta.skillIds. Push the
SKILL.md to mx-core's snippet store so the reader-facing <SkillCardList>
on the article page has something to render and the public raw URL
(${MXS_API_URL}/api/v3/s/sk/<name>) exists.
SKILL_ID=$(bash "$S/push-skill.sh" "$REPO/skills/<domain>/<skill-name>/SKILL.md")
The script is idempotent — re-runs update the snippet at
reference=skill, name=<frontmatter-name> instead of creating a duplicate.
Capture the returned id; step [6] threads it through publish-post.sh.
If the skill never reaches mx-core, the blog still publishes — readers just won't see the install card. That trade-off is fine when mx-core is offline; do not block the blog on it.
[5] Write the blog
Load writing-style.md (persona + voice),
node-usage.md (which nodes the content
earns), and visuals.md (diagram plan) before
drafting. The two rules that govern node use:
- Deletion test: rewrite the candidate node as a plain paragraph; if no information is lost, the node was decoration — don't use it.
- Escalate, never skip: prose → verbatim artifacts → structured facts →
semantic signal → diagram/media → containers → interactive. Interactive
enters only when the reader's action carries the lesson (parameter
exploration, algorithm step-through, perceptible difference, self-check —
see "When interaction teaches" in
node-usage.md).
Medium: default LiteXML (for Innei's blog). Load the authoring guide fresh:
LITEXML_CACHE=$(bash "$S/load-litexml.sh")
# Read $LITEXML_CACHE/SKILL.md and its references as needed.
Plain Markdown is fine when no haklex-specific tags are needed.
[6] Publish via mxs
Follow publish-flow.md: preview →
create as draft with the skill id from step [4]
(bash "$S/publish-post.sh" /tmp/blog/article.xml --skill-id "$SKILL_ID") →
Innei approves → mxs post publish <slug>. Edits round-trip via
get-post.sh / update-post.sh; published posts prefer stage /
apply. Paste the final URL back into the originating session.
Common pitfalls
| Mistake | Fix |
|---|---|
| Blog before skill | Skill first. Always. |
| SKILL.md too long (all code inline) | Extract ≥ 15-line code to scripts/. Target ≤ 250 lines. |
| Picking a persona by reflex instead of subject | Default pattern (distilled judgments). Use agent only when the process is the point; site-owner only when ownership is Innei's. Apply the selection rule in writing-style.md. |
Writing a pattern post that reads as a session transcript |
The session is evidence, not spine. If sections are chronological "I did X, Innei said Y", switch to agent or extract harder. |
| Switching personas mid-post | Pick one and stay. If a pattern post needs Innei's ownership voice for one component, demote ownership to third person rather than switch. |
| Node sprinkling ("use more node types") | Node variety is not a quality metric. Apply the deletion test per node (node-usage.md). |
| Pitfalls in prose only, no table | Pitfalls table is mandatory; it's the most-grep'd section. |
| Skill URL not embedded in blog (both top + bottom) | Banner at top, CTA at bottom. |
--no-verify to bypass pre-commit hook |
Pre-commit invariants must all be in the same commit; fix the root cause instead. |
Hardcoding ~/git/innei-repo/skill in shell |
bash "$S/resolve-skill-repo.sh" — config-driven with fallback. |
Stale local litexml-authoring clone |
bash "$S/load-litexml.sh" refreshes via degit on every call. |
pnpm --silent litexml … from a haklex worktree |
mxs preview <file> — envelope-aware, no local clone, opens browser by default. |
| Skill written in Chinese | Skill in English (artifact). Blog in Innei's chosen language (default Chinese). |
--state publish on post create |
Always create as draft. mxs post publish <slug> only after Innei approves preview. |
Skipping push-skill.sh and embedding only the GitHub URL |
The blog <SkillCardList> reads meta.skillIds. Without --skill-id, the install card never renders even if the GitHub link is right. |
Hand-writing --meta '{"aiGen":2,"skillIds":[…]}' |
Use publish-post.sh --skill-id <id>; it jq-assembles meta and survives multiple ids without quoting bugs. |
Re-running post create to edit |
Round-trip: get-post.sh → edit → update-post.sh. |
| Updating a published post with the create envelope | Its <state>draft</state> unpublishes the live post — readers see it vanish. update-post.sh strips <state>; publish state changes only via mxs post publish|unpublish. |
LiteXML body passed straight to mxs --file |
Wrap in references/envelope.template.xml first. |
Previewing a bare LiteXML fragment (no <doc>) |
Inter-block whitespace becomes root-level text nodes → Lexical error #282. Keep authoring sources <doc>-wrapped; mxs wraps server-side. |
<dynamic> with an invented or adapted URL |
Catalog-gated: only URLs from ${MXS_API_URL}/s/dynamic-widgets-catalog. No catalog → no <dynamic>; degrade per node-usage.md. |
Hand-writing <summary> |
Omit. Server AI auto-generates and may overwrite. |
Picking <category> without checking what exists |
mxs category list --output llm first; reuse existing slug. |
| Auto-creating a new category | Requires explicit second confirmation from Innei before mxs category create. |
Forgetting aiGen=2 |
publish-post.sh already passes --meta '{"aiGen":2}'; on first update of a legacy post, re-attach with mxs post update <slug> --meta '{"aiGen":2}'. |
Verification
-
bash "$S/resolve-skill-repo.sh"resolves before any write. - Skill dir at
$REPO/skills/<domain>/<skill-name>/; long code inscripts/, long configs inreferences/. - SKILL.md has frontmatter + scope + inputs + workflow + pitfalls table + verification checklist.
- Pre-commit hook passed;
git pushsucceeded. -
bash "$S/push-skill.sh" …returned a snowflake id; the public URL${MXS_API_URL}/api/v3/s/sk/<name>resolves to the raw markdown. - Skill URL resolves in a browser; embedded in blog at top + bottom.
- Voice follows
writing-style.md: one persona throughout (defaultpattern;agentonly when the process is the point;site-owneronly when Innei owns the subject);patternposts use the four-block section template + self-audit closing; Willison transparency, Abramov arc, Antirez 断语; em-dashes sparing. - Visuals follow
visuals.md: ≥ 3 Excalidraw diagrams for substantialsite-ownerposts, each answering a named question. - Node audit passed (
node-usage.md): every non-prose node holds a one-clause deletion-test justification; alerts ≤ 3, banner ≤ 1, interactive nodes enter only via a "When interaction teaches" scenario. -
mxs auth whoamireturned the expected user;<category>reuses an existing slug (or Innei explicitly approved a new one). - Envelope
<state>draft</state>on first push;mxs post publish <slug>only after Innei approves;aiGen=2set. - Final post URL pasted back into the originating session.