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.
- package/README.md +162 -49
- package/bin/clawflowhub.js +152 -66
- package/package.json +20 -8
- package/src/commands/create.js +286 -0
- package/src/commands/cron.js +46 -21
- package/src/commands/doctor.js +146 -0
- package/src/commands/explore.js +49 -0
- package/src/commands/register.js +83 -0
- package/src/commands/update.js +35 -0
- package/src/core/Installer.js +226 -109
- package/src/core/TerminalUI.js +12 -17
- package/.env.example +0 -14
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
- package/.github/pull_request_template.md +0 -19
- package/.github/workflows/publish.yml +0 -46
- package/.github/workflows/test.yml +0 -31
- package/CODE_OF_CONDUCT.md +0 -29
- package/SECURITY.md +0 -20
- package/SUPPORT.md +0 -21
|
@@ -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
|
+
}
|
package/src/commands/cron.js
CHANGED
|
@@ -2,23 +2,24 @@
|
|
|
2
2
|
* Cron commands - ŕ¸ŕ¸ąŕ¸ŕ¸ŕ¸˛ŕ¸Ł cronjobs
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const chalk = require(
|
|
6
|
-
const ClawFlow = require(
|
|
7
|
-
const { normalizeCronExpression } = require(
|
|
8
|
-
const YAML = require(
|
|
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 ===
|
|
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("'")) ||
|
|
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 ===
|
|
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 ===
|
|
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 ||
|
|
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(
|
|
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(
|
|
75
|
+
console.log(chalk.gray(" No cronjobs configured"));
|
|
75
76
|
} else {
|
|
76
77
|
crons.forEach((job) => {
|
|
77
|
-
const status =
|
|
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(
|
|
80
|
-
console.log(
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 ===
|
|
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(
|
|
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
|
+
};
|