clawflowbang 1.0.2 → 1.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.
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Create command - Generate templates for skills and agents
3
+ */
4
+
5
+ const chalk = require("chalk");
6
+ const fs = require("fs-extra");
7
+ const path = require("path");
8
+ const inquirer = require("inquirer");
9
+ const yaml = require("yaml");
10
+
11
+ module.exports = async function createCommand(type, name, _options) {
12
+ console.log(chalk.cyan.bold("\n🛠️ ClawFlow Create\n"));
13
+
14
+ // 1. Determine Type
15
+ let targetType = type;
16
+ if (!targetType || !["skill", "agent"].includes(targetType.toLowerCase())) {
17
+ const { typeAnswer } = await inquirer.prompt([
18
+ {
19
+ type: "list",
20
+ name: "typeAnswer",
21
+ message: "What do you want to create?",
22
+ choices: [
23
+ { name: "Skill (Executable tool/function)", value: "skill" },
24
+ { name: "Agent (High-level LLM orchestrator)", value: "agent" },
25
+ ],
26
+ },
27
+ ]);
28
+ targetType = typeAnswer;
29
+ } else {
30
+ targetType = targetType.toLowerCase();
31
+ }
32
+
33
+ // 2. Determine Name
34
+ let targetName = name;
35
+ if (!targetName) {
36
+ const { nameAnswer } = await inquirer.prompt([
37
+ {
38
+ type: "input",
39
+ name: "nameAnswer",
40
+ message: `Enter ${targetType} name:`,
41
+ default: `my-cool-${targetType}`,
42
+ validate: (input) => {
43
+ if (/^[a-z0-9-_]+$/.test(input)) return true;
44
+ return "Name must be lowercase and contain only alphanumeric characters, dashes, or underscores.";
45
+ },
46
+ },
47
+ ]);
48
+ targetName = nameAnswer;
49
+ }
50
+
51
+ const targetDir = path.resolve(process.cwd(), targetName);
52
+
53
+ // 3. Check if directory exists
54
+ if (fs.existsSync(targetDir)) {
55
+ const { overwrite } = await inquirer.prompt([
56
+ {
57
+ type: "confirm",
58
+ name: "overwrite",
59
+ message: `Directory ${targetName} already exists. Overwrite?`,
60
+ default: false,
61
+ },
62
+ ]);
63
+ if (!overwrite) {
64
+ console.log(chalk.yellow("Aborted."));
65
+ return;
66
+ }
67
+ await fs.remove(targetDir);
68
+ }
69
+
70
+ // 4. Gather Metadata
71
+ const metadata = await inquirer.prompt([
72
+ {
73
+ type: "input",
74
+ name: "description",
75
+ message: "Description:",
76
+ default: `A new ${targetType} created with ClawFlow`,
77
+ },
78
+ {
79
+ type: "input",
80
+ name: "author",
81
+ message: "Author:",
82
+ default: process.env.USER || process.env.USERNAME || "anonymous",
83
+ },
84
+ {
85
+ type: "list",
86
+ name: "language",
87
+ message: "Language:",
88
+ choices: ["TypeScript", "Python"],
89
+ when: targetType === "skill",
90
+ },
91
+ {
92
+ type: "confirm",
93
+ name: "gitInit",
94
+ message: "Initialize a git repository?",
95
+ default: true,
96
+ },
97
+ ]);
98
+
99
+ // 5. Generate Files
100
+ try {
101
+ if (targetType === "skill") {
102
+ await generateSkill(targetDir, targetName, metadata);
103
+ } else {
104
+ await generateAgent(targetDir, targetName, metadata);
105
+ }
106
+
107
+ // Git Init
108
+ if (metadata.gitInit) {
109
+ try {
110
+ const { execSync } = require("child_process");
111
+ execSync("git init", { cwd: targetDir, stdio: "ignore" });
112
+ console.log(chalk.gray(" ✓ Git repository initialized"));
113
+ } catch (e) {
114
+ console.log(chalk.yellow(" ⚠️ Failed to initialize git repository"));
115
+ }
116
+ }
117
+
118
+ console.log(
119
+ chalk.green(`\n✅ ${targetType} "${targetName}" created successfully!`),
120
+ );
121
+ console.log(chalk.gray(`Path: ${targetDir}\n`));
122
+ console.log(chalk.cyan("Next steps:"));
123
+ console.log(chalk.white(` cd ${targetName}`));
124
+ if (targetType === "skill" && metadata.language === "TypeScript") {
125
+ console.log(chalk.white(" npm install"));
126
+ }
127
+ console.log(
128
+ chalk.white(" Edit skill.yaml/agent.yaml to define your logic\n"),
129
+ );
130
+ } catch (err) {
131
+ console.error(chalk.red("\n❌ Error generating files:"), err.message);
132
+ }
133
+ };
134
+
135
+ async function generateSkill(dir, name, metadata) {
136
+ const isTS = metadata.language === "TypeScript";
137
+ const ext = isTS ? "ts" : "py";
138
+
139
+ await fs.ensureDir(dir);
140
+ await fs.ensureDir(path.join(dir, "examples"));
141
+
142
+ // skill.yaml
143
+ const skillYaml = {
144
+ name: name,
145
+ version: "1.0.0",
146
+ description: metadata.description,
147
+ author: metadata.author,
148
+ type: "tool",
149
+ runtime: metadata.language.toLowerCase(),
150
+ entry: `index.${ext}`,
151
+ parameters: {
152
+ type: "object",
153
+ properties: {
154
+ input: {
155
+ type: "string",
156
+ description: "Input for the skill",
157
+ },
158
+ },
159
+ required: ["input"],
160
+ },
161
+ };
162
+
163
+ await fs.writeFile(path.join(dir, "skill.yaml"), yaml.stringify(skillYaml));
164
+
165
+ // Entry file
166
+ let content = "";
167
+ if (isTS) {
168
+ content = `
169
+ // ${name} - Generated by ClawFlow
170
+ export interface Input {
171
+ input: string;
172
+ }
173
+
174
+ export async function run(params: Input) {
175
+ console.log(\`Running ${name} with input: \${params.input}\`);
176
+ return {
177
+ success: true,
178
+ message: \`Processed: \${params.input}\`,
179
+ timestamp: new Date().toISOString()
180
+ };
181
+ }
182
+ `;
183
+ // package.json for TS
184
+ const pkgJson = {
185
+ name: name,
186
+ version: "1.0.0",
187
+ description: metadata.description,
188
+ main: "index.ts",
189
+ scripts: {
190
+ test: 'echo "Error: no test specified" && exit 1',
191
+ },
192
+ dependencies: {
193
+ "@types/node": "^20.0.0",
194
+ typescript: "^5.0.0",
195
+ },
196
+ };
197
+ await fs.writeJson(path.join(dir, "package.json"), pkgJson, { spaces: 2 });
198
+ } else {
199
+ content = `
200
+ # ${name} - Generated by ClawFlow
201
+ import sys
202
+ import json
203
+ import datetime
204
+
205
+ def run(params):
206
+ input_str = params.get('input', '')
207
+ print(f"Running ${name} with input: {input_str}")
208
+
209
+ return {
210
+ "success": True,
211
+ "message": f"Processed: {input_str}",
212
+ "timestamp": datetime.datetime.now().isoformat()
213
+ }
214
+
215
+ if __name__ == "__main__":
216
+ # For local testing
217
+ test_params = {"input": "Hello from ClawFlow"}
218
+ print(json.dumps(run(test_params), indent=2))
219
+ `;
220
+ // requirements.txt for Python
221
+ await fs.writeFile(
222
+ path.join(dir, "requirements.txt"),
223
+ "# Add your dependencies here\n",
224
+ );
225
+ }
226
+
227
+ await fs.writeFile(path.join(dir, `index.${ext}`), content.trim());
228
+
229
+ // Example
230
+ const example = {
231
+ input: "Hello world",
232
+ };
233
+ await fs.writeJson(path.join(dir, "examples", "basic.json"), example, {
234
+ spaces: 2,
235
+ });
236
+
237
+ // README
238
+ await fs.writeFile(
239
+ path.join(dir, "README.md"),
240
+ `# ${name}\n\n${metadata.description}`,
241
+ );
242
+ }
243
+
244
+ async function generateAgent(dir, name, metadata) {
245
+ await fs.ensureDir(dir);
246
+ await fs.ensureDir(path.join(dir, "prompts"));
247
+ await fs.ensureDir(path.join(dir, "tools"));
248
+
249
+ // agent.yaml
250
+ const agentYaml = {
251
+ name: name,
252
+ version: "1.0.0",
253
+ description: metadata.description,
254
+ author: metadata.author,
255
+ model: "gpt-4-turbo",
256
+ prompts: {
257
+ system: "prompts/system.md",
258
+ user: "prompts/user.md",
259
+ },
260
+ tools: [
261
+ // 'example-skill'
262
+ ],
263
+ config: {
264
+ temperature: 0.7,
265
+ max_tokens: 1000,
266
+ },
267
+ };
268
+
269
+ await fs.writeFile(path.join(dir, "agent.yaml"), yaml.stringify(agentYaml));
270
+
271
+ // Prompts
272
+ await fs.writeFile(
273
+ path.join(dir, "prompts", "system.md"),
274
+ `# System Prompt for ${name}\n\nYou are a helpful assistant specialized in...`,
275
+ );
276
+ await fs.writeFile(
277
+ path.join(dir, "prompts", "user.md"),
278
+ `# User Prompt for ${name}\n\nTask: {{input}}`,
279
+ );
280
+
281
+ // README
282
+ await fs.writeFile(
283
+ path.join(dir, "README.md"),
284
+ `# ${name} (Agent)\n\n${metadata.description}`,
285
+ );
286
+ }
@@ -2,23 +2,24 @@
2
2
  * Cron commands - จัดการ cronjobs
3
3
  */
4
4
 
5
- const chalk = require('chalk');
6
- const ClawFlow = require('../index');
7
- const { normalizeCronExpression } = require('../core/CronFormat');
8
- const YAML = require('yaml');
5
+ const chalk = require("chalk");
6
+ const ClawFlow = require("../index");
7
+ const { normalizeCronExpression } = require("../core/CronFormat");
8
+ const YAML = require("yaml");
9
9
 
10
10
  function parseJsonParams(params) {
11
- if (params === undefined || params === null || params === '') {
11
+ if (params === undefined || params === null || params === "") {
12
12
  return {};
13
13
  }
14
14
 
15
- if (typeof params === 'object') {
15
+ if (typeof params === "object") {
16
16
  return params;
17
17
  }
18
18
 
19
19
  const raw = String(params).trim();
20
20
  const unquoted =
21
- (raw.startsWith("'") && raw.endsWith("'")) || (raw.startsWith('"') && raw.endsWith('"'))
21
+ (raw.startsWith("'") && raw.endsWith("'")) ||
22
+ (raw.startsWith('"') && raw.endsWith('"'))
22
23
  ? raw.slice(1, -1).trim()
23
24
  : raw;
24
25
 
@@ -28,7 +29,7 @@ function parseJsonParams(params) {
28
29
  if (!candidate) continue;
29
30
  try {
30
31
  const parsed = JSON.parse(candidate);
31
- if (parsed && typeof parsed === 'object') {
32
+ if (parsed && typeof parsed === "object") {
32
33
  return parsed;
33
34
  }
34
35
  } catch (_error) {
@@ -40,7 +41,7 @@ function parseJsonParams(params) {
40
41
  if (!candidate) continue;
41
42
  try {
42
43
  const parsed = YAML.parse(candidate);
43
- if (parsed && typeof parsed === 'object') {
44
+ if (parsed && typeof parsed === "object") {
44
45
  return parsed;
45
46
  }
46
47
  } catch (_error) {
@@ -58,7 +59,7 @@ function resolveScheduleInput(options) {
58
59
  return normalizeCronExpression(`every ${options.every}`);
59
60
  }
60
61
 
61
- return normalizeCronExpression(options.schedule || '*/5 * * * *');
62
+ return normalizeCronExpression(options.schedule || "*/5 * * * *");
62
63
  }
63
64
 
64
65
  module.exports = {
@@ -66,18 +67,21 @@ module.exports = {
66
67
  const hub = new ClawFlow();
67
68
 
68
69
  try {
69
- console.log(chalk.cyan.bold('\n⏰ Configured Cronjobs:\n'));
70
+ console.log(chalk.cyan.bold("\n⏰ Configured Cronjobs:\n"));
70
71
 
71
72
  const crons = await hub.listCrons();
72
73
 
73
74
  if (crons.length === 0) {
74
- console.log(chalk.gray(' No cronjobs configured'));
75
+ console.log(chalk.gray(" No cronjobs configured"));
75
76
  } else {
76
77
  crons.forEach((job) => {
77
- const status = job.enabled !== false ? chalk.green('●') : chalk.gray('○');
78
+ const status =
79
+ job.enabled !== false ? chalk.green("●") : chalk.gray("○");
78
80
  console.log(` ${status} ${chalk.bold(job.skill || job.name)}`);
79
- console.log(` ${chalk.blue('Schedule:')} ${job.schedule}`);
80
- console.log(` ${chalk.blue('Description:')} ${job.description || '-'}`);
81
+ console.log(` ${chalk.blue("Schedule:")} ${job.schedule}`);
82
+ console.log(
83
+ ` ${chalk.blue("Description:")} ${job.description || "-"}`,
84
+ );
81
85
  console.log();
82
86
  });
83
87
  }
@@ -88,7 +92,7 @@ module.exports = {
88
92
  },
89
93
 
90
94
  async add(skillName, options) {
91
- const { params = '{}', description = '' } = options;
95
+ const { params = "{}", description = "" } = options;
92
96
 
93
97
  const hub = new ClawFlow();
94
98
 
@@ -96,9 +100,26 @@ module.exports = {
96
100
  const parsedParams = parseJsonParams(params);
97
101
  const schedule = resolveScheduleInput(options);
98
102
 
99
- console.log(chalk.cyan(`\n➕ Adding cronjob for ${chalk.bold(skillName)}...\n`));
103
+ if (options.dryRun) {
104
+ console.log(chalk.yellow("\n📋 Cronjob Preview (Dry Run):\n"));
105
+ console.log(` Skill: ${chalk.bold(skillName)}`);
106
+ console.log(` Schedule: ${schedule}`);
107
+ console.log(` Description: ${description || "-"}`);
108
+ console.log(` Params: ${JSON.stringify(parsedParams)}`);
109
+ console.log(chalk.gray("\n(Nothing was added)"));
110
+ return;
111
+ }
112
+
113
+ console.log(
114
+ chalk.cyan(`\n➕ Adding cronjob for ${chalk.bold(skillName)}...\n`),
115
+ );
100
116
 
101
- const job = await hub.cronManager.add(skillName, schedule, parsedParams, description);
117
+ const job = await hub.cronManager.add(
118
+ skillName,
119
+ schedule,
120
+ parsedParams,
121
+ description,
122
+ );
102
123
 
103
124
  console.log(chalk.green(`\n✓ Cronjob added successfully`));
104
125
  console.log(` ID: ${job.id}`);
@@ -113,7 +134,9 @@ module.exports = {
113
134
  const hub = new ClawFlow();
114
135
 
115
136
  try {
116
- console.log(chalk.cyan(`\n🗑️ Removing cronjob ${chalk.bold(jobId)}...\n`));
137
+ console.log(
138
+ chalk.cyan(`\n🗑️ Removing cronjob ${chalk.bold(jobId)}...\n`),
139
+ );
117
140
 
118
141
  await hub.cronManager.remove(jobId);
119
142
 
@@ -132,7 +155,7 @@ module.exports = {
132
155
  if (options.schedule || options.every) {
133
156
  updates.schedule = resolveScheduleInput(options);
134
157
  }
135
- if (typeof options.description === 'string') {
158
+ if (typeof options.description === "string") {
136
159
  updates.description = options.description;
137
160
  }
138
161
  if (options.params !== undefined) {
@@ -140,7 +163,9 @@ module.exports = {
140
163
  }
141
164
 
142
165
  if (Object.keys(updates).length === 0) {
143
- throw new Error('No update data provided (use --schedule/--every/--description/--params)');
166
+ throw new Error(
167
+ "No update data provided (use --schedule/--every/--description/--params)",
168
+ );
144
169
  }
145
170
 
146
171
  const updated = await hub.cronManager.edit(jobId, updates);
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Doctor command - Check system compatibility and configuration
3
+ */
4
+
5
+ const chalk = require("chalk");
6
+ const fs = require("fs-extra");
7
+ const path = require("path");
8
+ const { execSync } = require("child_process");
9
+
10
+ module.exports = async function doctorCommand() {
11
+ console.log(chalk.cyan.bold("\n🩺 ClawFlow Doctor - System Diagnostic\n"));
12
+
13
+ const checks = [
14
+ { name: "Node.js version", check: checkNode },
15
+ { name: "OpenClaw CLI", check: checkOpenClaw },
16
+ { name: "ClawHub CLI", check: checkClawHub },
17
+ { name: "Git CLI", check: checkGit },
18
+ { name: "Configuration", check: checkConfig },
19
+ { name: "Skills Path", check: checkSkillsPath },
20
+ ];
21
+
22
+ let errors = 0;
23
+ let warnings = 0;
24
+
25
+ for (const item of checks) {
26
+ process.stdout.write(chalk.white(` Checking ${item.name}... `));
27
+ try {
28
+ const result = await item.check();
29
+ if (result.status === "ok") {
30
+ process.stdout.write(chalk.green("✓\n"));
31
+ if (result.message) console.log(chalk.gray(` ${result.message}`));
32
+ } else if (result.status === "warn") {
33
+ process.stdout.write(chalk.yellow("⚠️\n"));
34
+ console.log(chalk.yellow(` ${result.message}`));
35
+ warnings++;
36
+ } else {
37
+ process.stdout.write(chalk.red("✗\n"));
38
+ console.log(chalk.red(` ${result.message}`));
39
+ errors++;
40
+ }
41
+ } catch (err) {
42
+ process.stdout.write(chalk.red("✗\n"));
43
+ console.log(chalk.red(` Error: ${err.message}`));
44
+ errors++;
45
+ }
46
+ }
47
+
48
+ console.log("\n" + "─".repeat(50));
49
+ if (errors === 0 && warnings === 0) {
50
+ console.log(
51
+ chalk.green.bold("✨ Everything looks great! You are ready to go."),
52
+ );
53
+ } else if (errors === 0) {
54
+ console.log(
55
+ chalk.yellow.bold(`⚠️ System ready but with ${warnings} warning(s).`),
56
+ );
57
+ } else {
58
+ console.log(
59
+ chalk.red.bold(`❌ Found ${errors} error(s) and ${warnings} warning(s).`),
60
+ );
61
+ console.log(
62
+ chalk.gray(" Please fix the errors above before continuing."),
63
+ );
64
+ }
65
+ console.log();
66
+ };
67
+
68
+ async function checkNode() {
69
+ const version = process.version;
70
+ const major = parseInt(version.slice(1).split(".")[0], 10);
71
+ if (major < 16) {
72
+ return {
73
+ status: "error",
74
+ message: `Node.js ${version} is too old. Please use v16 or newer.`,
75
+ };
76
+ }
77
+ return { status: "ok", message: version };
78
+ }
79
+
80
+ async function checkOpenClaw() {
81
+ try {
82
+ const out = execSync("openclaw --version", { stdio: "pipe" })
83
+ .toString()
84
+ .trim();
85
+ return { status: "ok", message: out };
86
+ } catch (e) {
87
+ return {
88
+ status: "warn",
89
+ message: "OpenClaw CLI not found in PATH. Make sure it is installed.",
90
+ };
91
+ }
92
+ }
93
+
94
+ async function checkClawHub() {
95
+ try {
96
+ const out = execSync("clawhub --version", { stdio: "pipe" })
97
+ .toString()
98
+ .trim();
99
+ return { status: "ok", message: out };
100
+ } catch (e) {
101
+ return {
102
+ status: "warn",
103
+ message: "ClawHub CLI not found. Registry installs might fail.",
104
+ };
105
+ }
106
+ }
107
+
108
+ async function checkGit() {
109
+ try {
110
+ const out = execSync("git --version", { stdio: "pipe" }).toString().trim();
111
+ return { status: "ok", message: out };
112
+ } catch (e) {
113
+ return {
114
+ status: "error",
115
+ message: "Git is required for many ClawFlow features.",
116
+ };
117
+ }
118
+ }
119
+
120
+ async function checkConfig() {
121
+ const configFile = path.join(process.cwd(), ".clawflowhub", "config.json");
122
+ if (fs.existsSync(configFile)) {
123
+ return { status: "ok", message: "Local configuration found." };
124
+ }
125
+ return {
126
+ status: "warn",
127
+ message: 'No local .clawflowhub folder. Run "clawflow init".',
128
+ };
129
+ }
130
+
131
+ async function checkSkillsPath() {
132
+ const configFile = path.join(process.cwd(), ".clawflowhub", "config.json");
133
+ if (!fs.existsSync(configFile))
134
+ return { status: "warn", message: "Unknown skills path." };
135
+
136
+ try {
137
+ const config = fs.readJsonSync(configFile);
138
+ const skillsPath = config.openclaw?.skillsPath;
139
+ if (skillsPath && fs.existsSync(skillsPath)) {
140
+ return { status: "ok", message: skillsPath };
141
+ }
142
+ return { status: "error", message: `Skills path not found: ${skillsPath}` };
143
+ } catch (e) {
144
+ return { status: "error", message: "Failed to read config.json" };
145
+ }
146
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Explore command - Discover new skills
3
+ */
4
+
5
+ const chalk = require("chalk");
6
+ const boxen = require("boxen");
7
+
8
+ module.exports = async function exploreCommand() {
9
+ console.log(chalk.magenta.bold("\n✨ Explore ClawHub Ecosystem\n"));
10
+
11
+ const featured = [
12
+ {
13
+ name: "ClawGraph-Visualizer",
14
+ desc: "Visualize your agent node relations",
15
+ author: "clawflow-team",
16
+ },
17
+ {
18
+ name: "Crypto-Whale-Watcher",
19
+ desc: "Monitor large transactions on-chain",
20
+ author: "crypto-dev",
21
+ },
22
+ {
23
+ name: "Research-Pro-Agent",
24
+ desc: "Multi-step research orchestrator",
25
+ author: "openkrab",
26
+ },
27
+ ];
28
+
29
+ console.log(chalk.white("Featured Skills & Agents:"));
30
+
31
+ featured.forEach((item) => {
32
+ const content = `${chalk.bold.cyan(item.name)}\n${chalk.gray(item.desc)}\n${chalk.yellow("By: " + item.author)}`;
33
+ console.log(
34
+ boxen(content, {
35
+ padding: 1,
36
+ margin: { top: 1, bottom: 0 },
37
+ borderStyle: "round",
38
+ borderColor: "magenta",
39
+ }),
40
+ );
41
+ });
42
+
43
+ console.log(
44
+ chalk.gray('\nRun "clawflow search <query>" to find more packages!'),
45
+ );
46
+ console.log(
47
+ chalk.gray("Or visit https://hub.clawflow.dev for the full registry.\n"),
48
+ );
49
+ };