Turn the current session into a reusable workspace skill.
You orchestrate a two-phase flow:
materialize_skill.Do not call write_file to save the SKILL.md directly. Always go through
materialize_skill, which runs the security scanner and writes the manifest
atomically.
Two invocation paths:
/make-skill <focus>. The focus follows the command verbatim.Derive the skill name from focus with this exact rule:
skill_name = "-".join(focus.split())
Internal whitespace (space, tab, full-width space, multiple spaces) collapses
to a single -. Other characters stay as is.
Examples:
cooking → cooking
view image debug → view-image-debug
烹饪 食谱 → 烹饪-食谱
Stock Price → Stock-Price (case preserved)Use this skill_name consistently as plan.name in Step 1 and as the
name= argument to materialize_skill in Step 3.
Call create_plan with all four required arguments
(name, description, expected_outcome, subtasks):
name: the normalised skill_name from Step 0.description: a COMPACT preview the user reviews. Two parts:
## sub-headings. Just the shape, so the user can
judge ordering and scope and refine. Example layout (do NOT copy
this content):
1. <verb phrase, ~5-10 words>
2. <verb phrase, ~5-10 words>
3. <…>
Draw step names from what actually happened in THIS conversation.
Don't fabricate; omit anything not grounded in the conversation.expected_outcome (plan-level, REQUIRED — distinct from the
subtask's expected_outcome): one concrete sentence about what
success looks like for the whole skill creation. Use the literal
string "A new workspace skill <skill_name> is created, enabled, and invocable via /<skill_name>." with <skill_name> substituted.subtasks: a list with a single subtask:
name: "Write and materialize skill"
description: "Write the SKILL.md body and call materialize_skill."
expected_outcome: "Skill created and visible via /skills."
Write plan.name and plan.description in the same language as the user's
recent messages. expected_outcome can stay in English.
After create_plan returns, yield the turn. The user will reply approve,
refine, or cancel. The /plan mode's standard machinery handles the rest:
revise_current_plan with feedback baked into name,
description, or step outline.finish_plan with state="abandoned".When presenting the plan, render the standard plan card. Do NOT add ad-hoc
fields like Subtask: … or Focus: … in the chat message. Use the
normalised plan.name, not the raw focus.
If create_plan is not in your toolkit (plan mode disabled in this
workspace), fall back to a text-based plan:
finish_subtask / finish_plan calls in Step 5; they don't
apply when there's no plan.Once the user approves the plan and the single subtask is in-progress, write a complete, detailed SKILL.md body grounded in THIS conversation. Length is fine when content is load-bearing.
Writing style:
MUSTs.Body sections align 1-to-1 with plan.description Part 2: same order, same
scope. Use the step's verb phrase as the section heading. If the user
refined Part 2 during approval, follow the refined version.
For every step, answer four concrete questions grounded in what actually happened in the session, not in common knowledge:
delay=2 from the start to
avoid the retry loop we saw earlier."
avoid X reminders, not as full sub-procedures.If the conversation doesn't contain a real answer for a question, omit it instead of inventing one. Inventing parameters or error notes is the most common failure mode of this skill.
Add these only when they help a future agent. No fixed schema:
Skip anything that doesn't apply. Empty sections are worse than omitted ones.
If the session settled on a stable output shape (table, JSON schema,
markdown template), document it once at the top of the producing step
with an ALWAYS use this template: block. Example:
ALWAYS use this exact template:
| Ticker | Last close | Currency | Source |
|--------|-----------|----------|--------|
| <symbol> | <price> | <iso-4217> | <api-name> |
Skip this for skills whose output is genuinely free-form.
Re-read the body once and verify ALL THREE. Single pass, no second round:
plan.description Part 2
is present in the body and substantiated by session facts.If any check fails, revise the body.
materialize_skillCall materialize_skill with:
name: the same normalised skill_name you used for plan.name.description: a tight Use this skill when … string distilled from
plan.description Part 1. ≤ 200 characters. Preserve synonyms and
adjacent phrasings from the preview (LLMs tend to under-trigger skills,
so a slightly pushy description is better than a narrow one).body: the reviewed SKILL.md body. No frontmatter; the tool renders
it.Do not call write_file to save SKILL.md directly.
materialize_skillThe tool returns the conflicting name and a suggested rename. Recover automatically; don't gate this on a user question.
cooking: cooking-v2, cooking-2, cooking-new.revise_current_plan to set plan.name to the chosen name. (In
the text-plan fallback, just update your working name in memory.)materialize_skill again with the new name.cooking-v2 because
cooking was already in your workspace. Delete the old one and re-run
if you want the original name back."
Fix the SKILL.md content (frontmatter fields, body sections, etc.) and
call materialize_skill again. Do NOT call finish_subtask until it
returns success.
Remove the flagged patterns from the body and retry.
Adjust inputs and retry, or abandon the plan if the failure is not recoverable.
Once materialize_skill returns success:
finish_subtask for the single subtask.finish_plan with state="completed"./<skill_name>.