| name | write-recipe |
| description | Generate YAML recipes for .autoskillit/recipes/. Use when user says "make script skill", "generate script", "script a workflow", "write a script", "create a script", "new recipe", "write a pipeline", or when loaded by other skills for script formatting. |
| hooks | {"PreToolUse":[{"matcher":"*","hooks":[{"type":"command","command":"echo '[SKILL: write-recipe] Writing recipe...'","once":true}]}]} |
Make Script Skill
Format a workflow into a YAML recipe following the workflow schema.
When to Use
- Standalone: User wants to create a new recipe from scratch
- Loaded by another skill: Another skill (e.g., setup-project) loads this via the Skill tool to format a workflow it has already discovered.
Arguments (standalone mode)
/autoskillit:write-recipe
No positional arguments. The skill prompts interactively for workflow details.
Critical Constraints
NEVER:
- Create SKILL.md files (not in
.claude/commands/, .claude/skills/, or anywhere else)
- Create Markdown companion files alongside the YAML script
- Create files outside
.autoskillit/recipes/ directory
- Tell the user to run a script with
/autoskillit:<name> syntax
ALWAYS:
- Save the script to
.autoskillit/recipes/{name}.yaml as the ONLY output
- Call
validate_recipe after saving and fix any errors
- Use "recipe" terminology (not "skill script")
Output Path Isolation (Required)
Every recipe that produces output files MUST scope its write destinations through a
per-run context variable captured in the init step:
- Use
init to create a unique timestamped directory and capture its path as
${{ context.work_dir }}, ${{ context.planner_dir }}, or similar.
- All downstream steps reference their output directories via
${{ context.<var> }}/subdir/ — never via bare {{AUTOSKILLIT_TEMP}}/name/....
- The
non-unique-output-path lint rule (severity: ERROR) rejects any recipe step
that uses a bare {{AUTOSKILLIT_TEMP}}/ path without a context-variable prefix.
Valid patterns:
${{ context.planner_dir }}/phases/ (run-scoped via captured unique dir)
${{ context.work_dir }}/output/ (clone-scoped)
Invalid pattern (rejected by lint):
{{AUTOSKILLIT_TEMP}}/myrecipe/data/ (bare — shared across runs, causes stale artifacts)
How Scripts Are Loaded
Recipes have their own discovery and invocation mechanism — completely
separate from the skill system. You do not need to create anything else for the
script to be usable. The lifecycle is:
- You save the YAML file to
.autoskillit/recipes/{name}.yaml
- The user discovers it via
list_recipes MCP tool (lists all scripts in that directory)
- The user loads it via
load_recipe("{name}") MCP tool (returns raw YAML)
- An agent executes it by interpreting the YAML steps and calling MCP tools directly
No SKILL.md, no slash command registration, no Markdown companion file — the YAML file
in .autoskillit/recipes/ is the only artifact needed. The MCP tools handle everything else.
The Script Format
Every generated script MUST follow the workflow YAML schema:
name: {script-name}
autoskillit_version: "{version}"
description: {One line description.}
summary: {Concise pipeline chain, e.g. "plan > verify > implement > test > merge"}
ingredients:
var_name:
description: {What this input is for}
required: true
default: {value}
steps:
step_name:
tool: {mcp_tool_name}
with:
arg1: "${{ inputs.var_name }}"
arg2: "literal value"
capture:
var_name: "${{ result.field_name }}"
on_success: next_step
on_failure: escalate
retry:
max_attempts: 3
on: needs_retry
on_exhausted: escalate
done:
action: stop
message: "Pipeline complete."
escalate:
action: stop
message: "Failed — human intervention needed."
Format Rules
- Top-level fields:
name, autoskillit_version (stamped), description, summary (required), inputs, steps
- Inputs: each with
description, optional required (default false) and default
- Steps: each has either
tool (MCP tool call) or action (terminal: stop)
- Tool steps: use
with: for arguments, on_success/on_failure for routing
- Terminal steps: have
action: stop and a message:
- Routing targets: must reference other step names defined in the same file
- Variable substitution: use
${{ inputs.var_name }} for declared inputs and ${{ context.var_name }} for values captured by preceding steps
- Retry blocks: optional, specify
max_attempts, on (condition field), on_exhausted (step name)
- Summary: one line, use
> to chain steps (e.g., "plan > verify > implement > test > merge")
Complete Schema Reference
Top-Level Fields
| Field | Required | Type | Notes |
|---|
name | Yes | string | Unique identifier; validation fails if empty |
autoskillit_version | No | string | Package version that generated this script. Set from kitchen_status.package_version. Used by migration system to detect outdated scripts. |
description | Yes | string | Human-readable, shown in listings |
summary | Yes | string | Pipeline chain shown in list_recipes output |
inputs | No | mapping | Omit if the script has no configurable values |
kitchen_rules | Yes | list[str] | Orchestrator discipline rules. Must enumerate forbidden native tools (Read, Grep, Glob, Edit, Write, Bash, Task, Explore, WebFetch, WebSearch, NotebookEdit). |
steps | Yes | mapping | At least one step required |
Input Fields
| Field | Required | Default | Notes |
|---|
description | No | "" | What this input is for |
required | No | false | Whether the agent must prompt for it |
default | No | null | Value used when not provided |
Tool Step Fields
| Field | Required | Notes |
|---|
tool | Yes (xor action) | MCP tool name (see Tool Reference below) |
with | No | Arguments passed to the tool; values support ${{ inputs.X }} and ${{ context.X }} |
on_success | No | Step name to route to on success, or "done" |
on_failure | No | Step name to route to on failure |
capture | No | Map of context_var → ${{ result.field }} expressions. Captured values available to later steps via ${{ context.var }} |
retry | No | Retry block (see below) |
note | No | Human-readable annotation for the agent; not executed |
Terminal Step Fields
| Field | Required | Notes |
|---|
action | Yes (xor tool) | Must be "stop" |
message | Yes | Displayed to the agent when this step is reached |
Retry Block Fields
| Field | Default | Notes |
|---|
max_attempts | 3 | How many times to retry before giving up |
on | null | Response field to check. Valid values: exit_code, is_error, needs_retry, result, retry_reason, session_id, subtype |
on_exhausted | "escalate" | Step name to jump to when retries run out |
Capture Field
Extracts values from tool results into a pipeline-scoped context dict. Subsequent steps reference captured values via ${{ context.var_name }}.
| Field | Type | Notes |
|---|
capture | mapping | Keys are context variable names; values must be ${{ result.field }} expressions |
Rules:
- Values must contain
${{ result.* }} expressions (literals and other namespaces are rejected)
- Dotted result paths are valid (e.g.,
${{ result.data.path }})
- Captured variables become available to steps that appear after the capturing step
- A step cannot reference its own capture — only preceding steps' captures
Example:
steps:
implement:
tool: run_skill
with:
skill_command: "/autoskillit:implement-worktree-no-merge ${{ context.plan_path }}"
cwd: "."
capture:
worktree_path: "${{ result.worktree_path }}"
on_success: test
test:
tool: test_check
with:
worktree_path: "${{ context.worktree_path }}"
Validation Rules
The system validates scripts against these rules:
name must be non-empty
steps must contain at least one step
- Each step must have exactly one of
tool or action (not both, not neither)
- Terminal steps (
action: stop) must have a message
on_success and on_failure targets must reference a step name defined in the file, or the literal "done"
retry.on_exhausted must reference a defined step name
retry.on must be one of the valid response fields listed above
- All
${{ inputs.X }} references must match a declared input name
capture values must contain ${{ result.* }} expressions
capture values must only use the result.* namespace
${{ context.X }} references must point to a variable captured by a preceding step
run_skill steps should have a capture: block to explicitly wire outputs (warning: IMPLICIT_HANDOFF)
- All captured variables should be consumed by at least one reachable downstream step via
${{ context.X }} (warning: DEAD_OUTPUT)
MCP Tool Reference
Available tools for use in tool: fields:
| Tool | Arguments (with:) | Purpose |
|---|
run_skill | skill_command, cwd, model (optional), step_name (optional) | Run a Claude Code headless session with a skill |
test_check | worktree_path | Run test suite, returns PASS/FAIL |
merge_worktree | worktree_path, base_branch | Merge after test gate |
reset_test_dir | test_dir, force (optional, default false) | Clear test directory (requires reset guard marker) |
classify_fix | worktree_path, base_branch | Analyze diff for restart scope (full vs partial) |
reset_workspace | test_dir | Reset workspace, preserving configured directories |
run_cmd | cmd, cwd, timeout (optional) | Execute arbitrary shell command |
validate_recipe | script_path | Validate a script file against the workflow schema |
Bundled AutoSkillit Skills
These skills ship with the autoskillit plugin and are invoked as /autoskillit:<name>:
analyze-prs, arch-lens-c4-container, arch-lens-concurrency, arch-lens-data-lineage, arch-lens-deployment,
arch-lens-development, arch-lens-error-resilience, arch-lens-module-dependency, arch-lens-operational,
arch-lens-process-flow, arch-lens-repository-access, arch-lens-scenarios, arch-lens-security, arch-lens-state-lifecycle,
audit-arch, audit-bugs, audit-claims, audit-cohesion, audit-defense-standards, audit-docs, audit-feature-gates, audit-friction, audit-impl, audit-review-decisions, audit-tests,
build-execution-map, bundle-local-report, close-kitchen, collapse-issues, compose-pr, compose-research-pr, design-guards, diagnose-ci, dry-walkthrough, elaborate-phase,
enrich-issues, exp-lens-benchmark-representativeness, exp-lens-causal-assumptions, exp-lens-comparator-construction,
exp-lens-error-budget, exp-lens-estimand-clarity, exp-lens-exploratory-confirmatory, exp-lens-fair-comparison,
exp-lens-governance-risk, exp-lens-iterative-learning, exp-lens-measurement-validity, exp-lens-pipeline-integrity,
exp-lens-randomization-blocking, exp-lens-reproducibility-artifacts, exp-lens-sensitivity-robustness,
exp-lens-severity-testing, exp-lens-unit-interference, exp-lens-validity-threats, exp-lens-variance-stability,
implement-experiment, implement-worktree, implement-worktree-no-merge, investigate, issue-splitter, make-arch-diag,
make-campaign, make-experiment-diag, make-groups, make-plan, make-req, merge-pr, mermaid, migrate-recipes, open-integration-pr,
open-kitchen, pipeline-summary, plan-experiment, plan-visualization, planner-analyze, planner-assess-review-approach, planner-consolidate-wps, planner-elaborate-assignments, planner-elaborate-phase, planner-elaborate-wps, planner-extract-domain, planner-generate-phases, planner-reconcile-deps, planner-refine, planner-refine-assignments, planner-refine-phases, planner-refine-wps, planner-validate-task-alignment, prepare-issue, prepare-pr, prepare-research-pr, process-issues, promote-to-main, rectify, reload-session, report-bug,
resolve-claims-review, resolve-design-review, resolve-failures, resolve-merge-conflicts, resolve-research-review, resolve-review, retry-worktree, review-approach, review-design, review-pr, review-research-pr, run-experiment,
scope, setup-environment, setup-project, smoke-task, stage-data, triage-issues, troubleshoot-experiment,
validate-audit, validate-review-decisions, validate-test-audit, verify-diag,
vis-lens-always-on, vis-lens-antipattern, vis-lens-caption-annot, vis-lens-chart-select,
vis-lens-color-access, vis-lens-methodology-norms, vis-lens-figure-table, vis-lens-multi-compare,
vis-lens-reproducibility, vis-lens-story-arc, vis-lens-temporal, vis-lens-uncertainty,
generate-report, write-recipe
Skill Reference Disambiguation
When the user describes a workflow using bare skill names (e.g., "use make-plan",
"then run investigate"), you MUST resolve each name before writing it into the YAML.
Resolution procedure
For each bare skill name the user mentions:
- Check local: Does
.claude/skills/<name>/SKILL.md exist in the project directory?
- Check bundled: Is
<name> in the Bundled AutoSkillit Skills list above?
- Resolve:
- Local only → use
/<name> (bare slash command)
- Bundled only → use
/autoskillit:<name>
- Both exist → prompt the user:
"I see <name> exists as both a local project skill (/<name>) and a
bundled AutoSkillit skill (/autoskillit:<name>). Which should this script
use? The local version is recommended since it's tailored to your project."
- Neither exists → warn the user that the skill wasn't found and ask them
to clarify the correct name or path
Defaults
- Local always takes priority when the user doesn't express a preference.
- Only prompt when both sources provide the same name. Don't prompt for names
that exist in only one source.
Example: Standard Implementation Pipeline
This is the reference format. All generated scripts should match this style:
name: implementation
description: Plan, verify, implement, test, and merge a task.
summary: make-plan > dry-walk > implement > test > merge
kitchen_rules:
- "NEVER use native Claude Code tools (Read, Grep, Glob, Edit, Write,
Bash, Task, Explore, WebFetch, WebSearch, NotebookEdit) from the
orchestrator. All work is delegated through run_skill."
- "Route to on_failure when a step fails — do not investigate directly."
ingredients:
task:
description: What to implement
required: true
project_dir:
description: Path to the project
required: true
work_dir:
description: Working directory (can be same as project_dir)
default: "."
base_branch:
description: Branch to merge into
default: develop
steps:
plan:
tool: run_skill
with:
skill_command: "/autoskillit:make-plan ${{ inputs.task }}"
cwd: "${{ inputs.work_dir }}"
capture:
plan_path: "${{ result.plan_path }}"
on_success: verify
on_failure: escalate
verify:
tool: run_skill
with:
skill_command: "/autoskillit:dry-walkthrough ${{ context.plan_path }}"
cwd: "${{ inputs.work_dir }}"
on_success: implement
on_failure: escalate
implement:
tool: run_skill
with:
skill_command: "/autoskillit:implement-worktree-no-merge ${{ context.plan_path }}"
cwd: "${{ inputs.work_dir }}"
capture:
worktree_path: "${{ result.worktree_path }}"
retries: 0
on_context_limit: retry_worktree
on_success: test
on_failure: escalate
retry_worktree:
tool: run_skill
with:
skill_command: "/autoskillit:retry-worktree ${{ context.plan_path }} ${{ context.worktree_path }}"
cwd: "${{ context.worktree_path }}"
on_success: test
on_failure: escalate
test:
tool: test_check
with:
worktree_path: "${{ context.worktree_path }}"
on_success: merge
on_failure: fix
merge:
tool: merge_worktree
with:
worktree_path: "${{ context.worktree_path }}"
base_branch: "${{ inputs.base_branch }}"
on_success: done
on_failure: escalate
fix:
tool: run_skill
with:
skill_command: "/autoskillit:resolve-failures ${{ context.worktree_path }} ${{ context.plan_path }} ${{ inputs.base_branch }}"
cwd: "${{ inputs.work_dir }}"
on_success: done
on_failure: escalate
done:
action: stop
message: "Implementation complete."
escalate:
action: stop
message: "Failed — human intervention needed."
Example: Loop with Fix Step
A condensed bugfix loop showing retry, classify, and routing patterns:
name: example-loop
description: Test, fix, and merge with automatic retry.
summary: test > investigate > plan > implement > verify > merge
kitchen_rules:
- "NEVER use native Claude Code tools (Read, Grep, Glob, Edit, Write,
Bash, Task, Explore, WebFetch, WebSearch, NotebookEdit) from the
orchestrator. All work is delegated through run_skill."
- "Route to on_failure when a step fails — do not investigate directly."
ingredients:
test_dir:
description: Directory containing the project to test
required: true
base_branch:
description: Branch to merge fixes into
default: develop
helper_dir:
description: Directory for helper agent sessions
required: true
steps:
test:
tool: test_check
with:
worktree_path: "${{ inputs.test_dir }}"
on_success: done
on_failure: investigate
investigate:
tool: run_skill
with:
skill_command: "/autoskillit:investigate the test failures"
cwd: "${{ inputs.helper_dir }}"
on_success: plan
on_failure: escalate
plan:
tool: run_skill
with:
skill_command: "/autoskillit:rectify the investigation findings"
cwd: "${{ inputs.helper_dir }}"
capture:
plan_path: "${{ result.plan_path }}"
on_success: implement
on_failure: escalate
implement:
tool: run_skill
with:
skill_command: "/autoskillit:implement-worktree-no-merge ${{ context.plan_path }}"
cwd: "${{ inputs.helper_dir }}"
retries: 0
on_context_limit: escalate
on_success: verify
on_failure: escalate
verify:
tool: test_check
with:
worktree_path: "${{ inputs.test_dir }}"
on_success: merge
on_failure: classify
note: Re-test after implementation. If still failing, classify the fix scope.
classify:
tool: classify_fix
with:
worktree_path: "${{ inputs.test_dir }}"
base_branch: "${{ inputs.base_branch }}"
note: If full_restart, go back to investigate. If partial_restart, go back to implement.
on_success: merge
on_failure: escalate
merge:
tool: merge_worktree
with:
worktree_path: "${{ inputs.test_dir }}"
base_branch: "${{ inputs.base_branch }}"
on_success: done
on_failure: escalate
done:
action: stop
message: "All tests passing. Fix merged successfully."
escalate:
action: stop
message: "Human intervention needed. Review the latest output for details."
Converting Legacy Markdown Commands to YAML
When converting old .claude/commands/ or .claude/skills/ Markdown recipes to YAML:
Mapping Table
| Markdown Pattern | YAML Equivalent |
|---|
SETUP: block with var = value | inputs: block with description, required, default |
| Hardcoded paths in SETUP | required: true inputs (never hardcode paths) |
PIPELINE: numbered steps | steps: keyed by descriptive name |
run_skill("/skill-name ...", cwd=...) | tool: run_skill with with: {skill_command: "...", cwd: "..."} |
run_skill(...) with retry | tool: run_skill with retries: field |
→ ESCALATE / prose failure routing | on_failure: escalate |
PASS → next step | on_success: next_step |
FAIL → fix attempt | on_failure: fix |
Repeat up to 3x, then ESCALATE | retry: {max_attempts: 3, on: needs_retry, on_exhausted: escalate} |
IF condition: branching | Multiple steps with on_success/on_failure routing |
FOR each part: loops | Not representable in YAML schema — add a note: explaining the loop for the agent |
Prose Notes: section | note: field on individual steps, or comments in YAML |
AskUserQuestion prompts | Not in schema — the agent handles prompting before executing the script |
review_approach = false (optional) | Input with required: false and default: "false" |
Local skill refs (bare skill-name) | Follow the Skill Reference Disambiguation procedure above to resolve |
What Cannot Be Directly Represented
Some Markdown patterns require agent interpretation rather than YAML structure:
- Multi-part plan loops (
FOR each plan_part): Add a note: to the implement step explaining that the agent should glob for plan parts and iterate
- Conditional steps (
IF review_approach == true): Use an input with a default and add a note: explaining the conditional
Conversion Checklist
- Extract inputs from
SETUP: block — remove hardcoded paths, make them required: true
- Map each numbered pipeline step to a named YAML step
- Resolve skill references — for each skill name, follow the Skill Reference Disambiguation procedure
- Identify which MCP tool each step calls (see Tool Reference above)
- Set
on_success / on_failure routing for every tool step
- Add
retry: blocks where the Markdown says "repeat" or "retry"
- Add terminal
done and escalate steps
- Write a
summary: line capturing the pipeline chain
- Add
note: fields for agent-interpreted logic (loops, conditionals)
Standalone Invocation Flow
When called directly as /autoskillit:write-recipe:
- Ask the user what workflow they want to script (name, what it does)
- Ask whether it's a linear pipeline or a loop with a fix step
- Ask for the tool calls and routing (which MCP tools, what order, what conditions)
- Ask for inputs (what's configurable)
- Generate the script in the YAML format above
- Before saving, call
kitchen_status to get package_version and stamp autoskillit_version: "{package_version}" as the second top-level field (after name). This is required for the migration system to track script age.
- Save to
.autoskillit/recipes/{name}.yaml (create the directory if needed)
- Call
validate_recipe with the saved file path. If errors are returned, fix them and re-validate until clean. Review the quality.warnings in the response:
DEAD_OUTPUT: A capture: key is never referenced by any reachable downstream step via ${{ context.X }}. Either add a ${{ context.X }} reference in the downstream step's with: block, or remove the unused capture.
IMPLICIT_HANDOFF: A run_skill step has no capture: block. Add a capture: block to explicitly wire outputs to downstream steps via ${{ context.X }}, or confirm the skill's output is intentionally unused.
- Present the quality summary to the user and fix any warnings that indicate broken wiring.
- After validation passes, generate the pipeline contract file by calling
generate_recipe_card on the saved script. This creates .autoskillit/recipes/contracts/{name}.yaml alongside the recipe. Use run_python with autoskillit.recipe.contracts.generate_recipe_card passing the script path and scripts directory, or rely on load_recipe which auto-generates contracts on first load.
- Tell the user: "Saved to
.autoskillit/recipes/{name}.yaml. Load it with load_recipe("{name}") via the MCP tool."
After telling the user, emit the structured output token as the very last line of your
text output:
IMPORTANT: Emit the structured output tokens as literal plain text with no
markdown formatting on the token names. Do not wrap token names in **bold**,
*italic*, or any other markdown. The adjudicator performs a regex match on the
exact token name — decorators cause match failure.
recipe_path = {absolute_path_to_saved_recipe_file}
CRITICAL: Scripts Are NOT Skills
Recipes are YAML workflow files in .autoskillit/recipes/. They are:
- Loaded via the
load_recipe MCP tool
- Executed by the agent interpreting the YAML steps
They are NOT:
- Slash commands (cannot be invoked as
/autoskillit:<name>)
- Stored in
.autoskillit/skills/ or any other directory
- Markdown files (they are
.yaml files)
Never tell the user to run a script with /autoskillit:<name>. The correct
invocation is always via load_recipe("<name>").
Loaded by Another Skill
When loaded via the Skill tool by another skill (e.g., setup-project), the calling agent already has all the workflow context in its conversation. Use that context directly:
- Workflow name and description are already known
- Tool calls and routing are already determined
- Inputs are already identified
Apply the format rules above to produce the YAML script. Do not re-ask for information the calling agent has already gathered.
Edit Mode (Loaded with Existing Script Content)
When the agent is given an existing script's YAML content and a requested change:
- Parse the existing YAML to understand the current structure
- Apply the requested modifications while preserving all existing fields
- Resolve any skill references per the disambiguation procedure
- Write the modified YAML to the target path — ask the user whether to:
- Save changes to the original file
- Save as a new script (prompt for name)
- Use temporarily without saving
- Call
validate_recipe on the saved path
- If errors, fix and re-validate until clean
- Review quality warnings and fix data-flow issues before reporting changes
- After validation passes, regenerate the pipeline contract file to reflect the changes. Use
run_python with autoskillit.recipe.contracts.generate_recipe_card or rely on load_recipe auto-generation on next load.
- Report the changes made
This edit mode is invoked when load_recipe routes the user's modification request through this skill. The skill receives the existing YAML as context.