@joshualelon/clawdbot-skill-flow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Clawdbot Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,265 @@
1
+ # Skill Flow
2
+
3
+ Multi-step workflow orchestration plugin for [Clawdbot](https://github.com/clawdbot/clawdbot).
4
+
5
+ Build deterministic, button-driven conversation flows without AI inference overhead. Perfect for scheduled workouts, surveys, onboarding wizards, approval workflows, and any wizard-style interactions.
6
+
7
+ ## Features
8
+
9
+ - **Deterministic Execution** - Telegram button callbacks route directly to plugin commands, bypassing LLM entirely
10
+ - **Multi-Step Workflows** - Chain steps with conditional branching and variable capture
11
+ - **Channel Rendering** - Telegram inline keyboards with automatic fallback to text-based menus
12
+ - **Input Validation** - Built-in validators for numbers, emails, and phone numbers
13
+ - **Variable Interpolation** - Use `{{variableName}}` in messages to display captured data
14
+ - **Session Management** - Automatic timeout handling (30 minutes) with in-memory state
15
+ - **History Tracking** - JSONL append-only log for completed flows
16
+ - **Cron Integration** - Schedule flows to run automatically via Clawdbot's cron system
17
+
18
+ ## Quick Start
19
+
20
+ ### 1. Install Plugin
21
+
22
+ ```bash
23
+ clawdbot plugins install @joshualelon/clawdbot-skill-flow
24
+ ```
25
+
26
+ ### 2. Create a Flow
27
+
28
+ ```bash
29
+ clawdbot message send "/flow-create import $(cat <<'EOF'
30
+ {
31
+ "name": "daily-checkin",
32
+ "description": "Daily wellness check-in",
33
+ "version": "1.0.0",
34
+ "steps": [
35
+ {
36
+ "id": "mood",
37
+ "message": "How are you feeling today?",
38
+ "buttons": ["😊 Great", "😐 Okay", "😔 Not great"],
39
+ "capture": "mood",
40
+ "next": "sleep"
41
+ },
42
+ {
43
+ "id": "sleep",
44
+ "message": "How many hours did you sleep?",
45
+ "buttons": [4, 5, 6, 7, 8, 9, 10],
46
+ "capture": "sleep",
47
+ "validate": "number"
48
+ }
49
+ ]
50
+ }
51
+ EOF
52
+ )"
53
+ ```
54
+
55
+ ### 3. Run the Flow
56
+
57
+ ```bash
58
+ /flow-start daily-checkin
59
+ ```
60
+
61
+ ## Commands
62
+
63
+ | Command | Description | Example |
64
+ |---------|-------------|---------|
65
+ | `/flow-start <name>` | Start a flow | `/flow-start pushups` |
66
+ | `/flow-list` | List all flows | `/flow-list` |
67
+ | `/flow-create import <json>` | Create flow from JSON | See Quick Start |
68
+ | `/flow-delete <name>` | Delete a flow | `/flow-delete pushups` |
69
+ | `/flow-step` | Internal command (callback handler) | N/A |
70
+
71
+ ## Example Flows
72
+
73
+ ### Pushups Workout
74
+
75
+ 4-set pushup tracker with rep counting:
76
+
77
+ ```bash
78
+ clawdbot message send "/flow-create import $(cat src/examples/pushups.json)"
79
+ ```
80
+
81
+ ### Customer Survey
82
+
83
+ Satisfaction survey with conditional branching (high vs low scores):
84
+
85
+ ```bash
86
+ clawdbot message send "/flow-create import $(cat src/examples/survey.json)"
87
+ ```
88
+
89
+ ### Onboarding Wizard
90
+
91
+ Multi-step setup with email validation and variable interpolation:
92
+
93
+ ```bash
94
+ clawdbot message send "/flow-create import $(cat src/examples/onboarding.json)"
95
+ ```
96
+
97
+ ## Flow Schema
98
+
99
+ See [API Documentation](./docs/api.md) for complete schema reference.
100
+
101
+ ### Minimal Example
102
+
103
+ ```json
104
+ {
105
+ "name": "hello-world",
106
+ "description": "Simple greeting flow",
107
+ "version": "1.0.0",
108
+ "steps": [
109
+ {
110
+ "id": "greeting",
111
+ "message": "What's your name?",
112
+ "capture": "name",
113
+ "next": "farewell"
114
+ },
115
+ {
116
+ "id": "farewell",
117
+ "message": "Nice to meet you, {{name}}! 👋"
118
+ }
119
+ ]
120
+ }
121
+ ```
122
+
123
+ ## Cron Integration
124
+
125
+ Schedule flows to run automatically:
126
+
127
+ ```bash
128
+ clawdbot cron add \
129
+ --name "daily-pushups" \
130
+ --schedule "45 13 * * *" \
131
+ --session-target isolated \
132
+ --message "/flow-start pushups" \
133
+ --channel telegram \
134
+ --to "+1234567890"
135
+ ```
136
+
137
+ Verify:
138
+
139
+ ```bash
140
+ clawdbot cron list
141
+ ```
142
+
143
+ ## Advanced Features
144
+
145
+ ### Conditional Branching
146
+
147
+ Route users to different steps based on captured variables:
148
+
149
+ ```json
150
+ {
151
+ "id": "nps",
152
+ "message": "Rate us 0-10",
153
+ "buttons": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
154
+ "capture": "nps",
155
+ "validate": "number",
156
+ "condition": {
157
+ "variable": "nps",
158
+ "greaterThan": 7,
159
+ "next": "positive-feedback"
160
+ },
161
+ "next": "negative-feedback"
162
+ }
163
+ ```
164
+
165
+ ### Input Validation
166
+
167
+ Enforce data types with built-in validators:
168
+
169
+ - `"validate": "number"` - Numeric input only
170
+ - `"validate": "email"` - Valid email format
171
+ - `"validate": "phone"` - Phone number format
172
+
173
+ ### Variable Interpolation
174
+
175
+ Display captured data in subsequent messages:
176
+
177
+ ```json
178
+ {
179
+ "id": "summary",
180
+ "message": "Thanks {{name}}! You scored {{nps}}/10."
181
+ }
182
+ ```
183
+
184
+ ### Button-Specific Routing
185
+
186
+ Override default `next` on a per-button basis:
187
+
188
+ ```json
189
+ {
190
+ "id": "confirm",
191
+ "message": "Continue?",
192
+ "buttons": [
193
+ { "text": "Yes", "value": "yes", "next": "step2" },
194
+ { "text": "No", "value": "no", "next": "cancel" }
195
+ ]
196
+ }
197
+ ```
198
+
199
+ ## How It Works
200
+
201
+ ### Telegram Button Callbacks
202
+
203
+ When you render a Telegram inline keyboard button:
204
+
205
+ ```typescript
206
+ {
207
+ text: "20",
208
+ callback_data: "/flow-step pushups set1:20"
209
+ }
210
+ ```
211
+
212
+ Telegram sends the `callback_data` string back to Clawdbot, which routes it directly to the `/flow-step` command—**no LLM inference required**.
213
+
214
+ This enables deterministic, instant responses for structured workflows.
215
+
216
+ ### Session Management
217
+
218
+ - Sessions stored in-memory with 30-minute timeout
219
+ - Session key: `${senderId}-${flowName}`
220
+ - Automatic cleanup every 5 minutes
221
+ - History saved to `~/.clawdbot/flows/<name>/history.jsonl` on completion
222
+
223
+ ### File Structure
224
+
225
+ ```
226
+ ~/.clawdbot/flows/
227
+ ├── pushups/
228
+ │ ├── metadata.json
229
+ │ └── history.jsonl
230
+ └── survey/
231
+ ├── metadata.json
232
+ └── history.jsonl
233
+ ```
234
+
235
+ ## Roadmap
236
+
237
+ ### v0.2.0
238
+ - LLM-driven steps (e.g., "Ask user for feedback, then summarize")
239
+ - External service integrations (Google Sheets, webhooks)
240
+ - Flow analytics dashboard (completion rates, drop-off points)
241
+
242
+ ### v0.3.0
243
+ - Visual flow builder (web UI)
244
+ - Loops and repeats
245
+ - Sub-flows (call another flow as a step)
246
+
247
+ ### v1.0.0
248
+ - Production-ready comprehensive tests
249
+ - Performance optimizations
250
+ - Migration guides
251
+
252
+ ## Contributing
253
+
254
+ Issues and PRs welcome! This plugin follows Clawdbot's coding conventions.
255
+
256
+ ## License
257
+
258
+ MIT - See [LICENSE](./LICENSE) file
259
+
260
+ ## Links
261
+
262
+ - [Clawdbot](https://github.com/clawdbot/clawdbot)
263
+ - [Plugin SDK Docs](https://docs.clawd.bot/plugins)
264
+ - [API Reference](./docs/api.md)
265
+ - [Tutorial](./docs/tutorial.md)
@@ -0,0 +1,8 @@
1
+ {
2
+ "id": "skill-flow",
3
+ "configSchema": {
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "properties": {}
7
+ }
8
+ }
package/index.ts ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Skill Flow Plugin - Multi-step workflow orchestration for Clawdbot
3
+ */
4
+
5
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
6
+ import { createFlowStartCommand } from "./src/commands/flow-start.js";
7
+ import { createFlowStepCommand } from "./src/commands/flow-step.js";
8
+ import { createFlowCreateCommand } from "./src/commands/flow-create.js";
9
+ import { createFlowListCommand } from "./src/commands/flow-list.js";
10
+ import { createFlowDeleteCommand } from "./src/commands/flow-delete.js";
11
+
12
+ const plugin = {
13
+ id: "skill-flow",
14
+ name: "Skill Flow",
15
+ description:
16
+ "Multi-step workflow orchestration for deterministic command execution",
17
+ version: "0.1.0",
18
+
19
+ register(api: ClawdbotPluginApi) {
20
+ // Register commands
21
+ api.registerCommand({
22
+ name: "flow-start",
23
+ description: "Start a workflow",
24
+ acceptsArgs: true,
25
+ requireAuth: true,
26
+ handler: createFlowStartCommand(api),
27
+ });
28
+
29
+ api.registerCommand({
30
+ name: "flow-step",
31
+ description: "Handle flow step transition (internal)",
32
+ acceptsArgs: true,
33
+ requireAuth: true,
34
+ handler: createFlowStepCommand(api),
35
+ });
36
+
37
+ api.registerCommand({
38
+ name: "flow-create",
39
+ description: "Create a new flow from JSON",
40
+ acceptsArgs: true,
41
+ requireAuth: true,
42
+ handler: createFlowCreateCommand(api),
43
+ });
44
+
45
+ api.registerCommand({
46
+ name: "flow-list",
47
+ description: "List all available flows",
48
+ acceptsArgs: false,
49
+ requireAuth: true,
50
+ handler: createFlowListCommand(api),
51
+ });
52
+
53
+ api.registerCommand({
54
+ name: "flow-delete",
55
+ description: "Delete a flow",
56
+ acceptsArgs: true,
57
+ requireAuth: true,
58
+ handler: createFlowDeleteCommand(api),
59
+ });
60
+
61
+ api.logger.info("Skill Flow plugin registered successfully");
62
+ },
63
+ };
64
+
65
+ export default plugin;
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@joshualelon/clawdbot-skill-flow",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Multi-step workflow orchestration plugin for Clawdbot",
6
+ "keywords": [
7
+ "clawdbot",
8
+ "clawdbot-plugin",
9
+ "workflow",
10
+ "automation",
11
+ "skill",
12
+ "flow",
13
+ "telegram"
14
+ ],
15
+ "author": "Joshua Le Long <joshualelon>",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/joshualelon/clawdbot-skill-flow.git"
20
+ },
21
+ "homepage": "https://github.com/joshualelon/clawdbot-skill-flow#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/joshualelon/clawdbot-skill-flow/issues"
24
+ },
25
+ "files": [
26
+ "src/**/*",
27
+ "index.ts",
28
+ "types.d.ts",
29
+ "README.md",
30
+ "LICENSE",
31
+ "clawdbot.plugin.json"
32
+ ],
33
+ "clawdbot": {
34
+ "extensions": [
35
+ "./index.ts"
36
+ ]
37
+ },
38
+ "dependencies": {
39
+ "lockfile": "^1.0.4",
40
+ "zod": "^3.22.4"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.0.0",
44
+ "@vitest/coverage-v8": "^1.0.0",
45
+ "dependency-cruiser": "^17.0.0",
46
+ "husky": "^9.0.0",
47
+ "jscpd": "^4.0.0",
48
+ "knip": "^5.0.0",
49
+ "lint-staged": "^15.0.0",
50
+ "oxlint": "^0.15.0",
51
+ "typescript": "^5.3.0",
52
+ "vitest": "^1.0.0"
53
+ },
54
+ "peerDependencies": {
55
+ "clawdbot": ">=2026.1.0"
56
+ },
57
+ "scripts": {
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "test:coverage": "vitest run --coverage",
61
+ "typecheck": "tsc --noEmit",
62
+ "lint": "oxlint src tests index.ts",
63
+ "lint:fix": "oxlint --fix src tests index.ts",
64
+ "check:deps": "depcruise src index.ts --config .dependency-cruiser.cjs",
65
+ "check:dead-code": "knip",
66
+ "check:duplicates": "jscpd src",
67
+ "check": "pnpm lint && pnpm typecheck && pnpm check:deps && pnpm check:dead-code && pnpm check:duplicates && pnpm test:coverage",
68
+ "prepare": "husky"
69
+ },
70
+ "lint-staged": {
71
+ "*.ts": [
72
+ "oxlint --fix",
73
+ "tsc --noEmit"
74
+ ]
75
+ }
76
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * /flow-create command - Create a new flow
3
+ */
4
+
5
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
6
+ import { saveFlow } from "../state/flow-store.js";
7
+ import { FlowMetadataSchema } from "../validation.js";
8
+ import type { FlowMetadata } from "../types.js";
9
+
10
+ export function createFlowCreateCommand(api: ClawdbotPluginApi) {
11
+ return async (args: { args?: string }) => {
12
+ const input = args.args?.trim();
13
+
14
+ if (!input) {
15
+ return {
16
+ text: "Usage: /flow-create import <json>\n\nExample:\n/flow-create import {...}",
17
+ };
18
+ }
19
+
20
+ // Check for 'import' subcommand
21
+ const importMatch = input.match(/^import\s+(.+)$/s);
22
+
23
+ if (!importMatch) {
24
+ return {
25
+ text: "Currently only 'import' mode is supported.\n\nUsage: /flow-create import <json>",
26
+ };
27
+ }
28
+
29
+ const jsonStr = importMatch[1];
30
+ if (!jsonStr) {
31
+ return {
32
+ text: "Error: No JSON provided",
33
+ };
34
+ }
35
+
36
+ try {
37
+ // Parse JSON
38
+ const data = JSON.parse(jsonStr) as unknown;
39
+
40
+ // Validate schema
41
+ const flow: FlowMetadata = FlowMetadataSchema.parse(data);
42
+
43
+ // Save flow
44
+ await saveFlow(api, flow);
45
+
46
+ api.logger.info(`Created flow "${flow.name}"`);
47
+
48
+ return {
49
+ text: `✅ Flow "${flow.name}" created successfully!\n\nStart it with: /flow-start ${flow.name}`,
50
+ };
51
+ } catch (error) {
52
+ if (error instanceof SyntaxError) {
53
+ return {
54
+ text: `Error: Invalid JSON\n\n${error.message}`,
55
+ };
56
+ }
57
+
58
+ if (error instanceof Error) {
59
+ return {
60
+ text: `Error: Invalid flow definition\n\n${error.message}`,
61
+ };
62
+ }
63
+
64
+ return {
65
+ text: "Error: Failed to create flow",
66
+ };
67
+ }
68
+ };
69
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * /flow-delete command - Delete a flow
3
+ */
4
+
5
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
6
+ import { deleteFlow, loadFlow } from "../state/flow-store.js";
7
+
8
+ export function createFlowDeleteCommand(api: ClawdbotPluginApi) {
9
+ return async (args: { args?: string }) => {
10
+ const flowName = args.args?.trim();
11
+
12
+ if (!flowName) {
13
+ return {
14
+ text: "Usage: /flow-delete <flow-name>\n\nExample: /flow-delete pushups",
15
+ };
16
+ }
17
+
18
+ // Check if flow exists
19
+ const flow = await loadFlow(api, flowName);
20
+
21
+ if (!flow) {
22
+ return {
23
+ text: `Flow "${flowName}" not found.\n\nUse /flow-list to see available flows.`,
24
+ };
25
+ }
26
+
27
+ // Delete flow
28
+ await deleteFlow(api, flowName);
29
+
30
+ api.logger.info(`Deleted flow "${flowName}"`);
31
+
32
+ return {
33
+ text: `✅ Flow "${flowName}" deleted successfully.`,
34
+ };
35
+ };
36
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * /flow-list command - List all available flows
3
+ */
4
+
5
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
6
+ import { listFlows } from "../state/flow-store.js";
7
+
8
+ export function createFlowListCommand(api: ClawdbotPluginApi) {
9
+ return async () => {
10
+ const flows = await listFlows(api);
11
+
12
+ if (flows.length === 0) {
13
+ return {
14
+ text: "No flows found.\n\nCreate one with: /flow-create import {...}",
15
+ };
16
+ }
17
+
18
+ let message = `📋 Available Flows (${flows.length}):\n\n`;
19
+
20
+ for (const flow of flows) {
21
+ message += `• ${flow.name}\n`;
22
+ message += ` ${flow.description}\n`;
23
+
24
+ if (flow.triggers?.cron) {
25
+ message += ` 🕒 Cron: ${flow.triggers.cron}\n`;
26
+ }
27
+
28
+ message += ` Start: /flow-start ${flow.name}\n\n`;
29
+ }
30
+
31
+ return { text: message };
32
+ };
33
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * /flow-start command - Start a flow
3
+ */
4
+
5
+ import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
6
+ import { loadFlow } from "../state/flow-store.js";
7
+ import { createSession, getSessionKey } from "../state/session-store.js";
8
+ import { startFlow } from "../engine/executor.js";
9
+
10
+ export function createFlowStartCommand(api: ClawdbotPluginApi) {
11
+ return async (args: {
12
+ args?: string;
13
+ senderId: string;
14
+ channel: string;
15
+ }) => {
16
+ const flowName = args.args?.trim();
17
+
18
+ // Validate input
19
+ if (!flowName) {
20
+ return {
21
+ text: "Usage: /flow-start <flow-name>\n\nExample: /flow-start pushups\n\nUse /flow-list to see available flows.",
22
+ };
23
+ }
24
+
25
+ // Load flow
26
+ const flow = await loadFlow(api, flowName);
27
+
28
+ if (!flow) {
29
+ return {
30
+ text: `Flow "${flowName}" not found.\n\nUse /flow-list to see available flows.`,
31
+ };
32
+ }
33
+
34
+ // Verify flow has steps
35
+ if (!flow.steps || flow.steps.length === 0) {
36
+ return {
37
+ text: `Flow "${flowName}" has no steps.`,
38
+ };
39
+ }
40
+
41
+ // Create session
42
+ const session = createSession({
43
+ flowName: flow.name,
44
+ currentStepId: flow.steps[0]!.id,
45
+ senderId: args.senderId,
46
+ channel: args.channel,
47
+ });
48
+
49
+ api.logger.info(
50
+ `Started flow "${flowName}" for user ${args.senderId} (session: ${getSessionKey(args.senderId, flowName)})`
51
+ );
52
+
53
+ // Render first step
54
+ return startFlow(api, flow, session);
55
+ };
56
+ }