# Create Workflow

> Collaborative workflow design - guides users through creating well-structured crayon workflows with embedded descriptions.

- Skill: `timescale/create-workflow` (Agent Skill)
- Install (CLI): `npx skillmds@latest add timescale/create-workflow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/timescale/create-workflow/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: timescale (https://skillmd.com/u/timescale)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/timescale/create-workflow

---


# Create Workflow

Design and write a workflow with embedded description fields for crayon.

---

## Pre-flight check

### 1. Detect Project Type

Check the current working directory and handle one of three cases:

**Case 1: Empty directory** (no files, or only dotfiles like `.git`)
→ Tell the user this skill must be run inside a crayon project. **Stop here.**

**Case 2: Existing crayon project** (has `src/crayon/workflows/` or `src/crayon/agents/` directories)
→ Good to go. Continue to step 2.

**Case 3: Existing non-crayon directory** (has files/projects but no crayon structure — e.g., home directory, another repo)
→ Tell the user this skill must be run from inside a crayon project directory. **Stop here.**

### 2. Discover Integrations

**IMPORTANT** Do this step only after step 1 (scaffolding) is done.

Call `mcp__plugin_crayon_crayon-local-tools__list_integrations` to see what external integrations are available (Salesforce, Slack, HubSpot, etc.). This authenticates via crayon cloud and may open a browser for login if not yet authenticated.

**CRITICAL: If the tool returns an error (authentication failure, empty response, etc.), you MUST stop immediately. Do NOT continue with any further steps. Do NOT offer to create the workflow without integrations. Do NOT skip this step.**

Tell the user the `list_integrations` call failed and suggest they try running `crayon login` from their terminal if the automatic browser auth didn't work, then run `/crayon:create-workflow` again.

Then stop. Do not proceed to any subsequent steps.

Once the tool succeeds, the returned integration IDs are what nodes declare in their `integrations: [...]` arrays.

**IMPORTANT — stay in design mode:** Do NOT read integration-specific guides (e.g., `integrations/salesforce`), call `get_connection`, or research SDKs/libraries during this phase. You only need the integration IDs for node stub `integrations: [...]` arrays. All integration research and connection checks happen later in `/crayon:refine-node`.

### 3. Read Existing Context

- Read `src/crayon/workflows/*.ts` — existing workflows to reuse or reference
- Read `src/crayon/agents/*.md` — existing agents that could be reused
- Ensure `src/crayon/workflows/` and `src/crayon/agents/` directories exist

---

## Writing the Workflow (Incremental — file watcher updates live)

Once the flow is approved, write the workflow **incrementally** as TypeScript with embedded description fields. The file watcher picks up changes automatically — no compile step needed per-task.

This phase focuses on **WHAT** each node does, not **HOW**. Capture purpose and intent in plain language. Implementation details (exact fields, API schemas, tool configs) are handled later by `/crayon:refine-node`.

### Step-by-step

**IMPORTANT: Write one node at a time, not all at once.** The file watcher picks up changes — each save updates the graph live. Batch-writing all nodes defeats this and gives the user no chance to see the workflow take shape.

1. **Write the workflow file** — create `src/crayon/workflows/<name>.ts` with the scaffold template below, including a flow-only `description` field and an empty `run()` method. Import nothing yet.

2. **For each task in order** (one at a time — save the workflow file after each):
   a. Create the node/agent stub file with its own `description` field (see Node Stub Creation below)
   b. Add the import to the workflow file
   c. Add the task to the workflow's `description` field
   d. Add the `ctx.run()` call to the workflow's `run()` method
   e. **Save the workflow file** — the graph gains a new node immediately

3. After all tasks are written:
   a. **Save a version** — Call the `create_version` MCP tool with a message like:
      ```
      Scaffold workflow: <workflow-name>

      <Describe what changed — like a good git commit message body.>
      ```
   b. **Immediately** invoke `/crayon:refine-node` to add typed schemas and implementation details. Do NOT ask the user — just proceed directly.
       
### Workflow Scaffold Template

```typescript
// src/crayon/workflows/<name>.ts
// Auto-generated by create-workflow skill
import { z } from "zod";
import { Workflow } from "runcrayon";
// ... node/agent imports (NEVER use .js extensions — use extensionless imports) ...

const <Name>InputSchema = z.object({
  // ... from workflow inputs (use z.string(), z.number(), etc. — best guess from descriptions)
});
type <Name>Input = z.infer<typeof <Name>InputSchema>;

const <Name>OutputSchema = z.object({});
type <Name>Output = z.infer<typeof <Name>OutputSchema>;

export const <camelCaseName> = Workflow.create({
  name: "<kebab-case-name>",
  version: 1,
  description: `
<One-line summary of what the workflow does.>

## Tasks

### 1. Task Name
**Node:** \`node-name\` (node|agent)

### 2. Another Task
**Node:** \`another-node\` (agent)
**Loop:** for each item in items
`,
  inputSchema: <Name>InputSchema,
  outputSchema: <Name>OutputSchema,

  async run(ctx, inputs: <Name>Input): Promise<<Name>Output> {
    // Task 1: Task Name
    const result1 = await ctx.run(<nodeRef>, { /* inputs */ });

    // Task 2: Another Task
    // ...

    return {} as <Name>Output;
  },
});
```

### Workflow Description Format (flow-only)

The workflow `description` field captures **only flow-level concerns** — task ordering, conditions, decisions, loops. Node-specific details live in the node's own `description` field.

```markdown
<One-line summary>

## Tasks

### 1. Task Name
**Node:** `node-name` (agent|node)

### 2. Decision Name
**Condition:** `variable.field >= value`
**If true:** continue to task 3
**If false:** return early with outputs

### 3. Loop Task
**Node:** `loop-node` (agent)
**Loop:** for each lead in leads

### 4. Final Task
**Node:** `final-node` (node)
**Return:** (describe what the workflow returns)
```

### Node Description Format (node-specific)

Each node/agent's `description` field captures **what that node does**, its inputs and outputs in plain language.

```markdown
<What this node does — one or two sentences.>

**Input Description:** <what information this task needs>

**Output Description:** <what this task produces>
```

**For nodes with side effects** (sending messages, updating records, writing data), use this extended format:

```markdown
<What this node does — one or two sentences.>

**Side Effect:** <what external action is performed and the target>

**Test Mode:** <what happens when ctx.testMode is true — typically "Returns the <action details> without actually <performing the action>">

**Input Description:** <what information this task needs — include target if dynamic>

**Output Description:** <what this task produces — MUST include action details (e.g., messageSent, recipient, fieldsUpdated, testMode)>
```

The `**Side Effect:**` tag marks the node as having side effects so the compiler generates `ctx.testMode` guards. The `**Test Mode:**` description spells out the exact expected behavior to reduce ambiguity in code generation.

### Node Stub Creation

For each task in the workflow, create a stub file:

**For `(node)` tasks → `src/crayon/nodes/<name>.ts`:**

```typescript
// src/crayon/nodes/<name>.ts
import { z } from "zod";
import { Node } from "runcrayon";

export const <camelCaseName> = Node.create({
  name: "<kebab-case-name>",
  // integrations: ["salesforce"],  // declare if this node needs external credentials
  description: `
<What this node does.>

**Input Description:** <plain language>

**Output Description:** <plain language>
`,
  inputSchema: z.object({}),
  outputSchema: z.object({}),
  async execute(ctx, input) {
    // TODO: implement
    return {} as any;
  },
});
```

**For `(agent)` tasks → `src/crayon/agents/<name>.ts` + `src/crayon/agents/<name>.md`:**

```typescript
// src/crayon/agents/<name>.ts
import { z } from "zod";
import { Agent } from "runcrayon";
import { fileURLToPath } from "url";
import path from "path";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export const <camelCaseName> = Agent.create({
  name: "<kebab-case-name>",
  integrations: ["openai"],  // declare AI provider + any external services; keys fetched from Nango at runtime
  description: `
<What this agent does.>

**Input Description:** <plain language>

**Output Description:** <plain language>
`,
  inputSchema: z.object({}),
  outputSchema: z.object({}),
  tools: {},
  specPath: path.resolve(__dirname, "./<name>.md"),
});
```

Also create the agent spec file `src/crayon/agents/<name>.md` (colocated with the agent code):

```markdown
---
name: <agent-name>
---

# <Agent Title>

<Brief description of what the agent does>

## Task

<Derived from description>

## Guidelines

- Prefer primary sources over aggregators
- If information is unavailable, say so rather than guessing
- Keep output structured and consistent

## Output Format

Return a JSON object with:
- TODO: define fields during refinement
```

### Node Types

| Type | When to use |
|------|-------------|
| `(agent)` | Needs AI reasoning/judgment |
| `(node)` | Deterministic function or API call |

Built-in nodes: `web_read`

### Naming Conventions

- Workflow names: lowercase with hyphens (e.g., `lead-scoring`)
- Node names: lowercase with hyphens (e.g., `company-researcher`)

---

## Principles

1. **Draft first, ask later** — make your best guess and let the user correct, rather than interrogating upfront
2. **Concrete over abstract** — push for specific conditions, not vague descriptions (e.g., `score >= 80` not "if it's good")
3. **Leverage existing** — prefer reusing existing agents/nodes over creating new ones
4. **What, not how** — capture intent in plain language; implementation details come during refinement
5. **Run it yourself** — when the user wants to test a workflow or node, use the `runWorkflow` / `runNode` MCP tools or run CLI commands yourself.
6. **Side effects require intentional targets** — nodes that send messages, update records, or write to external systems must name WHO/WHERE the action targets. The target should either be traced to a workflow input / upstream node output, or be a deliberate constant with clear rationale. Never fabricate or randomly choose a target.

