npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@joshualelon/clawdbot-skill-flow

v2.4.1

Published

Multi-step workflow orchestration plugin for Clawdbot

Readme

Skill Flow

Multi-step workflow orchestration plugin for Clawdbot.

Build deterministic, button-driven conversation flows without AI inference overhead. Perfect for scheduled workouts, surveys, onboarding wizards, approval workflows, and any wizard-style interactions.

Features

  • Deterministic Execution - Telegram button callbacks route directly to plugin commands, bypassing LLM entirely
  • LLM-Powered Flow Generation - Generate complete flows from natural language descriptions
  • Adaptive Step Modification - AI-powered hooks that personalize messages based on user context
  • Multi-Step Workflows - Chain steps with conditional branching and variable capture
  • Channel Rendering - Telegram inline keyboards with automatic fallback to text-based menus
  • Input Validation - Built-in validators for numbers, emails, and phone numbers
  • Variable Interpolation - Use {{variableName}} in messages to display captured data
  • Session Management - Automatic timeout handling (30 minutes) with in-memory state
  • History Tracking - JSONL append-only log for completed flows
  • Cron Integration - Schedule flows to run automatically via Clawdbot's cron system
  • Hooks Utility Library - Pre-built integrations for Google Sheets, dynamic buttons, scheduling, and more

Requirements

  • Clawdbot: v2026.1.25 or later (requires Telegram sendPayload support)
    • PR: https://github.com/clawdbot/clawdbot/pull/1917
    • Important: Telegram inline keyboard buttons will not work with older Clawdbot versions
    • Text-based fallback will work on all versions
  • Node.js: 22+ (same as Clawdbot)
  • Channels: Currently optimized for Telegram (other channels use text-based menus)

Quick Start

1. Install Plugin

clawdbot plugins install @joshualelon/clawdbot-skill-flow

2. Create a Flow

clawdbot message send "/flow_create import $(cat <<'EOF'
{
  "name": "daily-checkin",
  "description": "Daily wellness check-in",
  "version": "1.0.0",
  "steps": [
    {
      "id": "mood",
      "message": "How are you feeling today?",
      "buttons": ["😊 Great", "😐 Okay", "😔 Not great"],
      "capture": "mood",
      "next": "sleep"
    },
    {
      "id": "sleep",
      "message": "How many hours did you sleep?",
      "buttons": [4, 5, 6, 7, 8, 9, 10],
      "capture": "sleep",
      "validate": "number"
    }
  ]
}
EOF
)"

3. Run the Flow

/flow_start daily-checkin

Configuration

The plugin supports several configuration options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | flowsDir | string | ~/.clawdbot/flows | Custom directory for flow definitions | | sessionTimeoutMinutes | number | 30 | Session timeout (1-1440 minutes) | | sessionCleanupIntervalMinutes | number | 5 | Cleanup check interval (1-60 minutes) | | enableBuiltinHistory | boolean | true | Save completed flows to .jsonl files | | maxFlowsPerUser | number | unlimited | Limit concurrent flows per user | | llm.flowGenerationTimeout | number | 30000 | Flow generation timeout (ms) | | llm.adaptationTimeout | number | 5000 | Step adaptation timeout (ms) | | llm.maxTokens | number | 4096 | Max tokens for responses | | llm.temperature | number | 0.7 | Response creativity (0-2) |

Note: LLM features use Clawdbot's Claude configuration automatically. Provider/model settings are inherited from Clawdbot.

Setting Configuration

# Use custom flows directory (e.g., for job tracking system)
clawdbot config set plugins.entries.clawdbot-skill-flow.config.flowsDir "~/clawd/jobs"

# Adjust session timeout to 60 minutes
clawdbot config set plugins.entries.clawdbot-skill-flow.config.sessionTimeoutMinutes 60

# Disable built-in history (if using custom storage backend)
clawdbot config set plugins.entries.clawdbot-skill-flow.config.enableBuiltinHistory false

# Restart gateway to apply changes
systemctl --user restart clawdbot-gateway  # Linux
# or restart via Clawdbot Mac app menu

Commands

| Command | Description | Example | |---------|-------------|---------| | /flow_start <name> | Start a flow | /flow_start pushups | | /flow_list | List all flows | /flow_list | | /flow_create import <json> | Create flow from JSON | See Quick Start | | /flow_generate <description> | Generate flow from natural language (AI) | /flow_generate Create a mood tracker | | /flow_delete <name> | Delete a flow | /flow_delete pushups | | /flow_step | Internal command (callback handler) | N/A |

Example Flows

Pushups Workout

4-set pushup tracker with rep counting:

clawdbot message send "/flow_create import $(cat src/examples/pushups.json)"

Customer Survey

Satisfaction survey with conditional branching (high vs low scores):

clawdbot message send "/flow_create import $(cat src/examples/survey.json)"

Onboarding Wizard

Multi-step setup with email validation and variable interpolation:

clawdbot message send "/flow_create import $(cat src/examples/onboarding.json)"

Flow Schema

See API Documentation for complete schema reference.

Minimal Example

{
  "name": "hello-world",
  "description": "Simple greeting flow",
  "version": "1.0.0",
  "steps": [
    {
      "id": "greeting",
      "message": "What's your name?",
      "capture": "name",
      "next": "farewell"
    },
    {
      "id": "farewell",
      "message": "Nice to meet you, {{name}}! 👋"
    }
  ]
}

Cron Integration

Schedule flows to run automatically:

clawdbot cron add \
  --name "daily-pushups" \
  --schedule "45 13 * * *" \
  --session-target isolated \
  --message "/flow_start pushups" \
  --channel telegram \
  --to "+1234567890"

Verify:

clawdbot cron list

LLM-Powered Features

Skill Flow now supports AI-powered features that bridge deterministic execution with adaptive intelligence:

Flow Generation

Generate complete flows from natural language descriptions:

/flow_generate Create a 4-set pushup tracker with progressive difficulty

The AI will:

  • Design appropriate steps and branching logic
  • Add validation for captured data
  • Create natural, conversational messages
  • Structure the flow according to best practices

Preview before saving:

# Generate flow
/flow_generate Create a daily wellness check-in with mood and sleep tracking

# Review preview and JSON
# If satisfied, save it:
/flow_generate save

# Or cancel:
/flow_generate cancel

# Start the flow
/flow_start wellness-checkin

Adaptive Step Modification

Make your flows adaptive with LLM-powered hooks that personalize messages based on user context:

// ~/.clawdbot/flows/pushups/hooks.js

export default (api) => {
  const { createLLMAdapter } = api.hooks;

  return {
    onStepRender: createLLMAdapter(api, {
      adaptMessage: true,
      adaptButtons: true,
      includeVariables: true
    })
  };
};

What it does:

  • Personalizes messages based on captured variables
  • Adapts button labels to be more contextual
  • Makes conversations feel natural and engaging
  • Falls back gracefully if LLM unavailable

Example transformation:

Original: "Set 2: How many pushups?"
Adapted: "Nice work on those 25 reps! Ready for set 2?"

Compose with other hooks:

export default (api) => {
  const { composeHooks, createDynamicButtons, createLLMAdapter } = api.hooks;

  return {
    onStepRender: composeHooks(
      // First: Generate button values from history
      createDynamicButtons({
        variable: 'reps',
        strategy: 'centered'
      }),
      // Then: Adapt message and labels with AI
      createLLMAdapter(api, {
        adaptMessage: true,
        adaptButtons: true,
        preserveButtonValues: true
      })
    )
  };
};

Configuration:

LLM features use Clawdbot's configured Claude instance (no separate API keys needed). You can optionally adjust performance settings:

# Adjust timeouts (optional)
clawdbot config set plugins.entries.clawdbot-skill-flow.config.llm.flowGenerationTimeout 30000
clawdbot config set plugins.entries.clawdbot-skill-flow.config.llm.adaptationTimeout 5000

# Adjust creativity/token limits (optional)
clawdbot config set plugins.entries.clawdbot-skill-flow.config.llm.temperature 0.7
clawdbot config set plugins.entries.clawdbot-skill-flow.config.llm.maxTokens 4096

Documentation:

Advanced Features

Conditional Branching

Route users to different steps based on captured variables:

{
  "id": "nps",
  "message": "Rate us 0-10",
  "buttons": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  "capture": "nps",
  "validate": "number",
  "condition": {
    "variable": "nps",
    "greaterThan": 7,
    "next": "positive-feedback"
  },
  "next": "negative-feedback"
}

Input Validation

Enforce data types with built-in validators:

  • "validate": "number" - Numeric input only
  • "validate": "email" - Valid email format
  • "validate": "phone" - Phone number format

Variable Interpolation

Display captured data in subsequent messages:

{
  "id": "summary",
  "message": "Thanks {{name}}! You scored {{nps}}/10."
}

Button-Specific Routing

Override default next on a per-button basis:

{
  "id": "confirm",
  "message": "Continue?",
  "buttons": [
    { "text": "Yes", "value": "yes", "next": "step2" },
    { "text": "No", "value": "no", "next": "cancel" }
  ]
}

Step-Level Actions System

Skill-flow uses explicit step-level actions instead of implicit global hooks. This makes flows self-documenting, LLM-generatable, and easier to understand.

Actions in Flow JSON

Each step can declare what actions to execute:

{
  "name": "pushups",
  "description": "4-set pushup workout",
  "version": "1.0.0",
  "hooks": "./hooks.js",
  "steps": [
    {
      "id": "set1",
      "message": "Set 1: How many pushups?",
      "actions": {
        "fetch": {
          "historicalAverage": "getHistoricalAverage"
        },
        "beforeRender": ["generateDynamicButtons"],
        "afterCapture": ["logToSheets"]
      },
      "capture": "set1",
      "validate": "number",
      "next": "set2"
    }
  ]
}

Action Types:

  1. fetch - Get data before rendering (returns variables to inject)

    • Format: { "varName": "actionFunctionName" }
    • Example: Fetch historical workout data to calculate averages
  2. beforeRender - Modify step before display (returns modified step)

    • Format: ["actionFunctionName", ...]
    • Example: Generate dynamic buttons based on fetched data
  3. afterCapture - Side effects after capturing variable

    • Format: ["actionFunctionName", ...]
    • Example: Log to Google Sheets, send notifications

Hooks File Structure

Action functions are exported as named exports from your hooks file. All actions receive an api parameter as their final argument - plugin utilities are available via api.hooks.

Note: No imports needed! Just destructure what you need from api.hooks.

Action Signatures:

// Fetch actions - retrieve data before rendering
export async function myFetch(session: FlowSession, api: EnhancedPluginApi) {
  const { querySheetHistory } = api.hooks;
  return { variableName: value };
}

// BeforeRender actions - modify step before display
export async function myBeforeRender(step: FlowStep, session: FlowSession, api: EnhancedPluginApi) {
  const { generateButtonRange } = api.hooks;
  return { ...step, buttons: [...] };
}

// AfterCapture actions - side effects after capturing input
export async function myAfterCapture(variable: string, value: string | number, session: FlowSession, api: EnhancedPluginApi) {
  const { appendToSheet } = api.hooks;
  await appendToSheet('spreadsheet-id', { [variable]: value });
}

// Create a new spreadsheet (typically in a fetch action)
export async function createWorkoutLog(session, api) {
  const { createSpreadsheet } = api.hooks;

  const { spreadsheetId, spreadsheetUrl } = await createSpreadsheet({
    title: `${session.flowName} Log - ${new Date().getFullYear()}`,
    worksheetName: 'Sessions',
    headers: ['timestamp', 'userId', 'set1', 'set2', 'set3', 'set4', 'total'],
    folderId: process.env.GOOGLE_DRIVE_FOLDER_ID, // Optional: move to specific folder
    useGogOAuth: true // Use gog CLI OAuth to avoid service account quota issues (requires GOG_ACCOUNT, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET env vars)
  });

  return { spreadsheetId, spreadsheetUrl };
}

Complete Example:

// ~/.clawdbot/flows/pushups/hooks.js

/**
 * Fetch action - returns variables to inject
 */
export async function getHistoricalAverage(session, api) {
  // Access plugin utilities via api.hooks (no imports needed!)
  const { querySheetHistory } = api.hooks;

  const history = await querySheetHistory('spreadsheet-id', { limit: 10 });
  const avg = history.reduce((sum, row) => sum + row.reps, 0) / history.length;
  return { historicalAverage: Math.round(avg) };
}

/**
 * BeforeRender action - returns modified step
 */
export async function generateDynamicButtons(step, session, api) {
  const { generateButtonRange } = api.hooks;

  const avg = session.variables.historicalAverage || 25;
  return {
    ...step,
    buttons: generateButtonRange(avg, { count: 5, spread: 5 })
  };
}

/**
 * AfterCapture action - side effects only
 */
export async function logToSheets(variable, value, session, api) {
  const { appendToSheet } = api.hooks;

  await appendToSheet('spreadsheet-id', {
    date: new Date().toISOString(),
    [variable]: value
  });
}

/**
 * Global lifecycle hooks (default export)
 * Use factory function to access api.hooks
 */
export default (api) => {
  const { createClawdBotScheduler } = api.hooks;
  const scheduler = createClawdBotScheduler(api);

  return {
    async onFlowComplete(session) {
      console.log('Workout complete!', session.variables);

      // Schedule next session using plugin utilities
      await scheduler.schedule({
        name: `${session.flowName}-next`,
        schedule: '0 9 * * *', // Daily at 9am
        message: `/flow_start ${session.flowName}`,
      });
    },

    async onFlowAbandoned(session, reason) {
      console.log('Flow abandoned:', reason);
    }
  };
};

See the Hooks API Reference for complete documentation of available utilities.

Benefits of Step-Level Actions

Before (Implicit Global Hooks):

// Flow JSON doesn't show what happens
{ "id": "set1", "capture": "set1" }

// Hook has to filter by step ID
export default {
  async onStepRender(step, session) {
    if (step.id === 'set1') {
      // Hidden logic...
    }
  }
}

After (Explicit Step-Level Actions):

{
  "id": "set1",
  "actions": {
    "fetch": { "avg": "getAverage" },
    "beforeRender": ["generateButtons"],
    "afterCapture": ["logToSheets"]
  }
}

Benefits:

  • ✅ Self-documenting (JSON shows what happens)
  • ✅ LLM-generatable (AI can see available actions)
  • ✅ Git-friendly (changes visible in JSON diffs)
  • ✅ Testable (test actions in isolation)
  • ✅ No step.id filtering needed

Complete Example

See src/examples/pushups/ for a complete working example with:

  • Fetch actions for historical data
  • Dynamic button generation
  • Google Sheets logging (mock)
  • Lifecycle hooks for scheduling

Hooks Utility Library

The plugin includes pre-built integrations (work in progress):

  • Google Sheets - Log flow data and query history
  • Dynamic Buttons - Generate buttons based on historical data
  • Scheduling - Schedule recurring workflow sessions
  • Common Utilities - Compose actions, retry logic, validation

Note: The utility library is being updated to work with the new step-level actions system.

Documentation:

  • Hooks & Actions Reference - Complete API documentation
  • src/examples/pushups/ - Complete working example
  • src/examples/survey/ - Survey with conditional actions

Custom Storage Backends

Replace or supplement the built-in JSONL storage:

{
  "name": "pushups",
  "storage": {
    "backend": "./storage.js",
    "builtin": false
  },
  "steps": [...]
}

StorageBackend Interface:

export default {
  async saveSession(session) {
    // Write to Google Sheets, database, etc.
  },

  async loadHistory(flowName, options) {
    // Return historical sessions for analytics
    return [];
  },
};

Set "builtin": false to disable JSONL storage and only use the custom backend. Omit it (or set to true) to use both.

See src/examples/sheets-storage.example.js for a complete reference.

Security

Hooks & Storage Backend Safety

The plugin validates that all dynamically loaded files (hooks, storage backends) remain within the ~/.clawdbot/flows/ directory. This prevents directory traversal attacks.

Valid hook paths:

{
  "name": "myflow",
  "hooks": "./hooks.js",           // ✅ Relative to flow directory
  "hooks": "hooks/custom.js"        // ✅ Subdirectory within flow
}

Invalid hook paths (will be rejected):

{
  "hooks": "/etc/passwd",           // ❌ Absolute path outside flows
  "hooks": "../../../etc/passwd",   // ❌ Directory traversal
  "hooks": "~/malicious.js"         // ❌ Tilde expansion outside flows
}

The plugin uses path validation similar to Clawdbot's core security patterns to ensure hooks and storage backends can only access files within their designated flow directory.

How It Works

Telegram Button Callbacks

When you render a Telegram inline keyboard button:

{
  text: "20",
  callback_data: "/flow-step pushups set1:20"
}

Telegram sends the callback_data string back to Clawdbot, which routes it directly to the /flow-step command—no LLM inference required.

This enables deterministic, instant responses for structured workflows.

Session Management

  • Sessions stored in-memory with 30-minute timeout
  • Session key: ${senderId}-${flowName}
  • Automatic cleanup every 5 minutes
  • History saved to ~/.clawdbot/flows/<name>/history.jsonl on completion

File Structure

~/.clawdbot/flows/
├── pushups/
│   ├── metadata.json
│   └── history.jsonl
└── survey/
    ├── metadata.json
    └── history.jsonl

Troubleshooting

Plugin Failed to Load - Missing Dependencies

Symptom:

Error: Cannot find module 'zod'
Plugin "clawdbot-skill-flow" failed to load

Cause: The clawdbot plugins install command timed out during npm install, leaving node_modules partially corrupted.

Fix:

# 1. Find your plugin directory
cd ~/.clawdbot/extensions/@joshualelon/clawdbot-skill-flow

# 2. Clean and reinstall dependencies
rm -rf node_modules package-lock.json
npm install --omit=optional

# 3. Restart gateway
systemctl --user restart clawdbot-gateway  # Linux
# OR
# Restart from Clawdbot menu bar app (macOS)

Plugin Install Timeout

Symptom:

npm install timed out
Plugin installation incomplete

Cause: Large dependency tree or slow network connection.

Solutions:

  1. Manual install:

    cd ~/.clawdbot/extensions/@joshualelon/clawdbot-skill-flow
    npm install --timeout=120000
  2. Use faster registry (optional):

    npm config set registry https://registry.npmjs.org/
    npm install
  3. Skip optional dependencies:

    npm install --omit=optional --no-audit

Flow Not Found

Symptom:

Flow "my-flow" not found

Solutions:

  1. Check flows directory:

    ls ~/.clawdbot/flows/
    # Or your custom flowsDir
  2. Verify flow created:

    /flow_list
  3. Check file permissions:

    chmod 644 ~/.clawdbot/flows/my-flow/metadata.json

Telegram Buttons Not Working

Symptom: Buttons don't appear or don't respond when clicked.

Cause: Clawdbot version doesn't support sendPayload.

Solution: Upgrade Clawdbot to v2026.1.25+ which includes PR #1917.

Text-based menus will work as fallback on older versions.

Session Timeout Issues

Symptom: "Session expired" messages appear too quickly.

Solution: Increase timeout in plugin config:

clawdbot config set plugins.entries.clawdbot-skill-flow.config.sessionTimeoutMinutes 60

Hook Execution Timeout

Symptom:

Action "myAction" timed out after 5000ms

Solutions:

  1. Increase action timeout:

    clawdbot config set plugins.entries.clawdbot-skill-flow.config.security.actionTimeout 10000
  2. Optimize slow hooks: Move heavy computation to background jobs.

  3. Use conditional execution:

    {
      "actions": {
        "fetch": {
          "data": { "action": "fetchData", "if": "needsData" }
        }
      }
    }

LLM Features Not Working

Symptom: /flow_generate or LLM adapters fail.

Cause: No Claude API configured in Clawdbot.

Solution: Configure Claude in Clawdbot settings. The plugin inherits Clawdbot's LLM configuration automatically.

Debugging Tips

  1. Check plugin logs:

    journalctl --user -u clawdbot-gateway -f  # Linux
    # OR
    tail -f ~/Library/Logs/Clawdbot/gateway.log  # macOS
  2. Enable debug logging:

    clawdbot config set plugins.entries.clawdbot-skill-flow.config.debug true
  3. Test flow JSON manually:

    cat ~/.clawdbot/flows/my-flow/metadata.json | jq .
  4. Verify plugin loaded:

    /flow_list  # Should list available flows

Contributing

Issues and PRs welcome! This plugin follows Clawdbot's coding conventions.

License

MIT - See LICENSE file

Links