crystallize

Capture the current session as a reusable skill — staged as a candidate by default, or live only when the user explicitly asks for a live/active skill.

SKILL.md

GitHub

Crystallize a session into a skill

Use this whenever the user asks to capture work as a reusable skill — "crystallize this", "create a skill", "save this as a skill", "make this reusable", or "turn this into a skill". This is the on-demand counterpart to the automatic post-session skill generation: the user is telling you now that the work is worth keeping.

Two modes, chosen from the user's wording:

  • Candidate (default). Stage the skill in the pending queue for human approval. Every phrasing above means this unless the user says otherwise.
  • Live (explicit only). Write the skill straight to a live, immediately-loadable location, bypassing approval. Take this path ONLY when the user explicitly says "create a live skill" or "create an active skill" (or confirms it when asked). Never infer it from a plain "create a skill" — that stays a candidate.

When to use

  • The user says any trigger phrase above (candidate mode), or explicitly asks for a "live" / "active" skill (live mode).
  • The session demonstrated a procedure that will recur — one a future session, working on a DIFFERENT target, would run again substantially unchanged: a repeatable debugging method for a class of error, a fixed command/API sequence, a verification technique, a research-synthesis flow.

Apply the same recurrence test the automatic pass uses: name the future session that would load this skill and the different target it would run against. If the only honest answer reuses this session's own artifact — this bug, this file, this component, this one question — the procedure does not recur. Effort is not evidence of recurrence: a long, many-step, genuinely difficult session is still one-off if its steps were chosen for one target.

Do not crystallize a task done once and now finished (a specific bug's fix, a one-time audit or trace of one component, a migration, a probe run to answer a question that is now answered), a design or planning discussion, a narrative of what happened in this session, a trivial one-shot answer, a one-off failure, or a session that touched credentials / sensitive paths. Being asked does not make a one-off reusable — but the call is the user's, not yours. When the session carries no recurring procedure, say so and name what makes it one-off, then let them decide; if they still want it captured, crystallize it. Never silently decline a direct request.

Procedure

  1. Reconstruct the procedure from the whole session — including sub-agents. Read back over the conversation and, critically, parse any [Subagent completion event] messages: each carries what a sub-agent was tasked with and the working path it found. Fold those into the procedure so the skill captures the successful route, not the dead ends.

  2. Check for an existing skill first (cross-source dedup). Look at the current auto-generated skills (Skills tab → the auto/ group, or ask). If this procedure essentially duplicates one that already exists, freshen that existing skill instead of creating a near-duplicate — and if a consolidation pass would also capture this same session, don't stage a second copy.

  3. Write prose by default; add a script only when determinism earns it. Most skills are judgment or workflow guidance and should be plain prose steps — that is the expected shape. Reach for a helper script only when part of the procedure is genuinely deterministic and error-prone to re-improvise: a fixed multi-command chain, a set API sequence, or a fiddly file transform. If prose captures it clearly, do not write a script. When a script truly is warranted, it must be Python (so it runs on macOS/Linux/Windows), must not access credentials, wipe files, or call unknown network hosts, and must stay under 4 KB. A staged candidate's script is statically validated and requires human approval before it can run; a live-mode script (step 4b) gets no such check, so you must hold it to these same limits yourself.

  4. Choose the destination — candidate by default, live only on an explicit request. First resolve your KiroCrew skills directory — the SAME directory that holds the auto/ group you inspected in step 2 (honor $KIROCREW_HOME if set; do not assume a literal ~/.kirocrew, since migrated installs live elsewhere).

    (a) Candidate — the default. For "crystallize", "create a skill", "save this as a skill", "make this reusable" and every other phrasing, stage to the pending queue so a human approves before anything loads. Create <skills-dir>/auto/.pending/<slug>/ (<slug> kebab-case, 3–64 chars, starting and ending with a letter or digit — a name outside that shape is silently skipped by the pending list and cannot be approved) with SKILL.md:

    ---
    name: auto/<slug>
    description: <=150 chars, starts with a verb
    triggers: <3-8 comma-separated keywords/phrases>
    source: auto
    session_key: <this session>
    created_at: <ISO-8601 UTC>
    ---
    
    # <slug> (auto-generated)
    
    ## When to use
    ...
    ## Steps
    ...
    ## Gotchas
    ...
    

    Always add a .meta.json next to SKILL.md — the pending list/detail API reads the candidate's description, triggers, name, and source from it (there is no SKILL.md-frontmatter fallback), so without it the candidate shows blank in Skills → Pending review and dedup loses its match data: {"slug": "<slug>", "name": "auto/<slug>", "source": "crystallize", "created_at": "<ISO>", "description": "...", "triggers": "...", "has_scripts": <bool>, "scripts": [...], "kind": "new"}. Only scripts/ is conditional: if you generated a script, put it under scripts/<name>.py in that folder, set "has_scripts": true, and list it in scripts; for a prose-only candidate use "has_scripts": false, "scripts": [].

    Freshening an existing live auto-skill is an UPDATE candidate, not a new one. Same pending folder, but set "kind": "update", "target": "auto/<the live slug>" and "base_version": <N> — a JSON integer, the version: frontmatter of the live SKILL.md you merged from (1 when it has none). A quoted or missing value silently disables the approval-time check that refuses an update merged from a stale base. Approving an update snapshots the live file to auto/<slug>/.versions/v<N>-SKILL.md before overwriting it and keeps the 20 newest snapshots, so the human can roll back.

    You write these files yourself, so no slug guard runs for you: the distinct-slug allocation (<slug>-2, <slug>-3, …) belongs to the automatic pass only. Before creating the folder, check that <skills-dir>/auto/.pending/<slug>/ does not already exist (writing into it overwrites a candidate awaiting review) and, for a NEW candidate, that <skills-dir>/auto/<slug>/ does not either (approval refuses a new candidate whose live name is taken, and it is later pruned unreviewed). If either exists, pick the next free <slug>-2, <slug>-3, … yourself.

    (b) Live: ONLY when the user explicitly says "live" / "active" or confirms it when asked. Write <skills-dir>/<slug>/SKILL.md directly, with no auto/ prefix, .meta.json, or pending stage.

    Reuse (a)'s template with name: <slug>, source: crystallize, and title # <slug>; omit session_key and created_at. Keep the same description, triggers, and When to use / Steps / Gotchas sections.

    Do not overwrite an existing skill: if <skills-dir>/<slug>/ already exists (a live or builtin skill), pick a different slug or ask the user — the live path has no collision guard, so writing blindly clobbers it, and unlike the pending path nothing claims a distinct slug for you. Put any script under scripts/<name>.py and, since no approval step runs for you, mark it executable yourself — on POSIX, chmod +x; skip that on Windows, where the executable bit is a no-op.

    In BOTH cases: do not include absolute paths, credentials, tokens, or user PII in the body or the script.

  5. Hand off.

    • Candidate: tell the user it is staged and they can review it in Skills → Pending review — approve to make it live (and mark any script executable), or dismiss it. Nothing loads until they approve.
    • Live: tell the user it is active immediately and discoverable by its triggers (no approval needed), and point them at the file in case they want to edit or remove it.

Gotchas

  • Default to the pending queue. Only write directly to a live location (<skills-dir>/<slug>/) when the user explicitly asked for a "live" or "active" skill — otherwise always stage under auto/.pending/ so a human reviews it first.
  • One skill per distinct procedure — don't bundle unrelated workflows.
  • Keep the description trigger-class-focused (it is matched on, and truncated in the system-prompt skill index).