From crayon
Collaborative workflow design - guides users through creating well-structured crayon workflows with embedded descriptions.
npx claudepluginhub timescale/crayon --plugin crayonThis skill uses the workspace's default tool permissions.
Design and write a workflow with embedded description fields for crayon.
Verifies tests pass on completed feature branch, presents options to merge locally, create GitHub PR, keep as-is or discard; executes choice and cleans up worktree.
Guides root cause investigation for bugs, test failures, unexpected behavior, performance issues, and build failures before proposing fixes.
Writes implementation plans from specs for multi-step tasks, mapping files and breaking into TDD bite-sized steps before coding.
Design and write a workflow with embedded description fields for crayon.
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.
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.
src/crayon/workflows/*.ts — existing workflows to reuse or referencesrc/crayon/agents/*.md — existing agents that could be reusedsrc/crayon/workflows/ and src/crayon/agents/ directories existOnce 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.
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.
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.
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
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.
// 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;
},
});
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.
<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)
Each node/agent's description field captures what that node does, its inputs and outputs in plain language.
<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:
<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.
For each task in the workflow, create a stub file:
For (node) tasks → src/crayon/nodes/<name>.ts:
// 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:
// 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):
---
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
| Type | When to use |
|---|---|
(agent) | Needs AI reasoning/judgment |
(node) | Deterministic function or API call |
Built-in nodes: web_read
lead-scoring)company-researcher)score >= 80 not "if it's good")runWorkflow / runNode MCP tools or run CLI commands yourself.