Claude
Skills
Sign in
Back

cpf-workflow-author

Included with Lifetime
$97 forever

Create and edit checkpointflow workflow YAML files. Use this skill whenever the user wants to create a workflow, automate a process as a YAML pipeline, define steps that mix CLI commands with human or agent checkpoints, turn a conversation into a reusable runbook, or asks about writing cpf/checkpointflow workflows. Also use it when you see keywords like "workflow", "runbook", "approval flow", "pause and resume", or "await event" in the context of automation.

AI Agents

What this skill does


# cpf-workflow-author

Create checkpointflow workflow YAML files that are valid, idiomatic, and ready to run with `cpf run`.

## Before you start

Read the checkpointflow guide to get the full reference:
```bash
cpf guide
```

If you need to see existing examples in the project, check `examples/*.yaml` in the checkpointflow repo.

## Authoring process

1. **Understand what the user wants to automate.** Ask clarifying questions if needed: What commands or tools are involved? Where does a human or agent need to weigh in? What are the inputs?

2. **Design the step sequence.** Map each action to a step kind:
   - A shell command or script invocation -> `cli`
   - A point where execution must pause for human/agent input -> `await_event`
   - A terminal outcome -> `end`

3. **Decide where to put the file.** If the user specifies a path, use it. Otherwise, prompt the user to choose:
   - **Local** (`.checkpointflow/<id>.yaml`) — scoped to the current project, checked into the repo
   - **Global** (`~/.checkpointflow/<id>.yaml`) — available everywhere via `cpf flows`
   - **Examples** (`examples/<id>.yaml`) — only offer this if inside the checkpointflow repo itself

   Skip the question when the location is obvious from context (e.g. "add a global workflow", "create a workflow in this repo", or an explicit file path).

4. **Validate immediately** after writing:
   ```bash
   cpf validate -f <path-to-workflow.yaml>
   ```
   If validation fails, fix the YAML and re-validate before telling the user the workflow is ready.

5. **Offer to run it** if the user seems ready. When the workflow hits an `await_event` step (exit code 40), collect the required input via a structured prompt, then `cpf resume` to continue.

## Workflow file structure

Every workflow file follows this skeleton:

```yaml
schema_version: checkpointflow/v1
workflow:
  id: snake_case_identifier
  name: Human Readable Name
  version: 0.1.0
  description: >
    A clear description of what this workflow does, why it exists,
    and what the user/agent will experience when running it.

  inputs:
    type: object
    required: [field_name]
    properties:
      field_name:
        type: string
        description: What this field is for and how it will be used
        examples: ["realistic-value-1", "realistic-value-2"]

  steps:
    - id: first_step
      kind: cli
      command: echo "doing something with ${inputs.field_name}"

    - id: done
      kind: end
      result:
        status: completed
```

Key rules:
- `schema_version` is always `checkpointflow/v1`
- `workflow.id` must be present, snake_case, and unique
- `workflow.inputs` uses JSON Schema to define what the caller must provide
- Every step needs a unique `id`
- Steps execute sequentially unless a transition jumps elsewhere

### Writing great names and descriptions

The `name` and `description` fields are emitted in every JSON envelope at runtime. When an agent runs or resumes this workflow, these are the **only context it has** about what the workflow does — it may not have read the YAML file. Write them as if they are the briefing for an agent that knows nothing else.

**`name`** — a short, specific title that tells the agent what process it is driving. Avoid generic names like "Build Pipeline" or "Review Flow". Instead, be specific: "PR Review with Security Gate", "TDD Feature Development for CPF".

**`description`** — a 1-3 sentence explanation that covers:
1. What the workflow does end-to-end (the happy path)
2. Who is involved (human approvals? agent steps? parallel checks?)
3. What methodology or constraints it follows (e.g. "TDD: tests first, then implementation")

Bad: `description: A workflow for deployments.`
Good: `description: >
  Deploys a service to production with a two-phase rollout. An agent runs
  pre-deploy checks (lint, test, canary), then pauses for human approval
  before promoting to full traffic. Rolls back automatically on health check failure.`

### Writing great input properties

Input `description` and `examples` directly shape the UX when an agent or skill collects input from the user. The runner skill uses `examples` as selectable options in its prompt UI — without them, the agent has to guess what values look like.

**Always include `examples`** on string inputs that don't have an `enum`. Provide 2-3 realistic values that illustrate the expected format:

```yaml
properties:
  feature_name:
    type: string
    description: Short kebab-case name for the feature
    examples: ["run-delete", "gui-filter", "batch-resume"]
  target_url:
    type: string
    description: The URL to deploy to
    examples: ["https://staging.example.com", "https://prod.example.com"]
```

**`description`** on input properties should say what the value is *for*, not just what type it is. "The environment name" is worse than "Target deployment environment — determines which config and secrets are loaded."

For `enum` inputs, `examples` are unnecessary — the enum values themselves become the options. But write a clear `description` explaining what each choice means if it's not obvious from the value names.

## Step kinds

All step kinds below are fully supported at runtime.

### cli

Runs a shell command. Supports `${inputs.x}` and `${steps.<id>.outputs.x}` interpolation. Use `cli` for steps that run real shell commands. Do NOT use `cli` with `echo` as a placeholder for agent work — use `await_event` with `audience: agent` instead.

`command` accepts a string or a list of strings. Lists are joined with `&&` at runtime — use lists to avoid YAML multi-line scalar pitfalls:

```yaml
- id: build
  kind: cli
  cwd: ${inputs.project_dir}
  command:
    - npm run lint
    - npm run build --project ${inputs.project_name}
    - npm test
  timeout_seconds: 120
```

Use `cwd` to set the working directory (supports interpolation). Use `shell` to pick a specific shell (`bash`, `sh`, `powershell`, `pwsh`), or set `defaults.shell` at the workflow level:

```yaml
workflow:
  defaults:
    shell: bash
```

If the command produces structured JSON on stdout, declare `outputs` so the runtime validates it and makes it available to later steps:

```yaml
- id: analyze
  kind: cli
  command: analyze-tool --format json ${inputs.target}
  outputs:
    type: object
    required: [score, report_path]
    properties:
      score: { type: number }
      report_path: { type: string }
```

Use `if` for conditional execution (no `${}` wrapper needed):

```yaml
- id: deep_scan
  kind: cli
  command: scan --deep ${inputs.target}
  if: inputs.mode == "full"
```

### await_event

Pauses the workflow and waits for external input. The CLI exits with code 40 and emits a waiting envelope that tells the caller exactly what input is needed and how to resume.

**`audience: user`** — The step pauses for a human to make a decision. The `prompt` presents context and choices. The `input_schema` defines what the user should provide.

```yaml
- id: review
  kind: await_event
  audience: user
  event_name: review_decision
  prompt: Review the results and decide whether to proceed.
  input_schema:
    type: object
    required: [decision]
    properties:
      decision:
        type: string
        enum: [approve, reject]
      notes:
        type: string
```

**`audience: agent`** — The step pauses for an AI agent to do real work. The `prompt` is the work assignment — describe what to analyze, build, or run. The `input_schema` defines the structured output the agent must deliver. The workflow pauses, the agent does the work, and resumes with results. Use this instead of `cli` with `echo` for agent work.

```yaml
- id: analyze
  kind: await_event
  audience: agent
  event_name: analysis_results
  name: "Agent: Analyze Codebase"
  prompt: |
    Scan src/${inputs.area}/ and identify:
    - Source files and their responsibilities
    - Existing test files
    - Key dependencies
  input_schema:
    type: object
    required: [files_found, notes]
    properties:
      files_found: { t

Related in AI Agents