@jackclaw/create 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.
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-jackclaw — Scaffold a complete JackClaw node project.
4
+ *
5
+ * Usage:
6
+ * npm create jackclaw@latest
7
+ * npx create-jackclaw
8
+ * npx create-jackclaw --name my-node --role worker --provider openai --yes
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG"}
package/dist/index.js ADDED
@@ -0,0 +1,365 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * create-jackclaw — Scaffold a complete JackClaw node project.
5
+ *
6
+ * Usage:
7
+ * npm create jackclaw@latest
8
+ * npx create-jackclaw
9
+ * npx create-jackclaw --name my-node --role worker --provider openai --yes
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const readline_1 = __importDefault(require("readline"));
18
+ // ─── Banner ──────────────────────────────────────────────────────────────────
19
+ const BANNER = `
20
+ 🦞 create-jackclaw — Scaffold your AI node
21
+ ───────────────────────────────────────────
22
+ `;
23
+ function parseArgs(argv) {
24
+ const args = {};
25
+ for (let i = 2; i < argv.length; i++) {
26
+ const a = argv[i];
27
+ if (a === '--yes' || a === '-y') {
28
+ args.yes = true;
29
+ }
30
+ else if (a === '--name' && argv[i + 1]) {
31
+ args.name = argv[++i];
32
+ }
33
+ else if (a === '--role' && argv[i + 1]) {
34
+ args.role = argv[++i];
35
+ }
36
+ else if (a === '--provider' && argv[i + 1]) {
37
+ args.provider = argv[++i];
38
+ }
39
+ else if (!a.startsWith('-') && !args.name) {
40
+ // positional: treat as project name
41
+ args.name = a;
42
+ }
43
+ }
44
+ return args;
45
+ }
46
+ // ─── Prompts ─────────────────────────────────────────────────────────────────
47
+ function ask(rl, question, defaultVal) {
48
+ return new Promise(resolve => {
49
+ rl.question(` ${question} (${defaultVal}): `, answer => {
50
+ resolve(answer.trim() || defaultVal);
51
+ });
52
+ });
53
+ }
54
+ function askChoice(rl, question, choices, defaultVal) {
55
+ return new Promise(resolve => {
56
+ const choiceStr = choices.map(c => (c === defaultVal ? `[${c}]` : c)).join(' / ');
57
+ rl.question(` ${question} (${choiceStr}): `, answer => {
58
+ const val = answer.trim().toLowerCase();
59
+ if (val && choices.includes(val)) {
60
+ resolve(val);
61
+ }
62
+ else {
63
+ resolve(defaultVal);
64
+ }
65
+ });
66
+ });
67
+ }
68
+ // ─── Validators ──────────────────────────────────────────────────────────────
69
+ const ROLES = ['worker', 'engineer', 'analyst', 'ceo'];
70
+ const PROVIDERS = ['openai', 'anthropic', 'ollama', 'custom'];
71
+ function sanitizeName(name) {
72
+ return name
73
+ .toLowerCase()
74
+ .replace(/[^a-z0-9-]/g, '-')
75
+ .replace(/-+/g, '-')
76
+ .replace(/^-|-$/g, '');
77
+ }
78
+ // ─── Templates ───────────────────────────────────────────────────────────────
79
+ function genPackageJson(name, safeName, role) {
80
+ const pkg = {
81
+ name: safeName,
82
+ version: '0.1.0',
83
+ private: true,
84
+ type: 'module',
85
+ scripts: {
86
+ build: 'tsc',
87
+ dev: 'tsc --watch',
88
+ start: 'npx jackclaw start',
89
+ typecheck: 'tsc --noEmit',
90
+ },
91
+ dependencies: {
92
+ '@jackclaw/node': '^0.1.0',
93
+ '@jackclaw/sdk': '^0.1.0',
94
+ },
95
+ devDependencies: {
96
+ '@types/node': '^20.0.0',
97
+ typescript: '^5.4.0',
98
+ },
99
+ jackclaw: {
100
+ role,
101
+ },
102
+ };
103
+ return JSON.stringify(pkg, null, 2) + '\n';
104
+ }
105
+ function genTsConfig() {
106
+ const config = {
107
+ compilerOptions: {
108
+ target: 'ES2022',
109
+ module: 'NodeNext',
110
+ moduleResolution: 'NodeNext',
111
+ outDir: 'dist',
112
+ rootDir: 'src',
113
+ strict: true,
114
+ esModuleInterop: true,
115
+ skipLibCheck: true,
116
+ declaration: true,
117
+ sourceMap: true,
118
+ },
119
+ include: ['src'],
120
+ exclude: ['node_modules', 'dist'],
121
+ };
122
+ return JSON.stringify(config, null, 2) + '\n';
123
+ }
124
+ function genSrcIndex(name, role) {
125
+ return `import { definePlugin } from '@jackclaw/sdk'
126
+
127
+ /**
128
+ * ${name} — A JackClaw ${role} node plugin.
129
+ *
130
+ * This plugin registers basic commands that your node exposes
131
+ * to the Hub and other nodes in the team.
132
+ */
133
+ export default definePlugin({
134
+ name: '${name}',
135
+ version: '0.1.0',
136
+ description: 'A ${role} node for JackClaw',
137
+
138
+ commands: {
139
+ /**
140
+ * /ping — Simple health check.
141
+ */
142
+ ping: async (ctx) => {
143
+ return { text: \`🦞 pong from \${ctx.node.name} (role: ${role})\` }
144
+ },
145
+
146
+ /**
147
+ * /status — Report node status.
148
+ */
149
+ status: async (ctx) => {
150
+ return {
151
+ text: \`Node \${ctx.node.name} is online\`,
152
+ items: [
153
+ { label: 'Role', value: '${role}' },
154
+ { label: 'Version', value: ctx.plugin.version },
155
+ { label: 'Uptime', value: \`\${Math.floor(process.uptime())}s\` },
156
+ ],
157
+ }
158
+ },
159
+
160
+ /**
161
+ * /hello <name> — Greet someone.
162
+ */
163
+ hello: async (ctx) => {
164
+ const who = ctx.args[0] || ctx.userName || 'world'
165
+ return { text: \`👋 Hello, \${who}! I'm \${ctx.node.name}.\` }
166
+ },
167
+ },
168
+
169
+ schedules: {
170
+ /**
171
+ * Daily report — runs every day at 08:00.
172
+ */
173
+ dailyReport: {
174
+ cron: '0 8 * * *',
175
+ handler: async (ctx) => {
176
+ await ctx.report({
177
+ summary: \`Daily report from \${ctx.node.name}\`,
178
+ items: [
179
+ { label: 'Status', value: 'healthy' },
180
+ { label: 'Tasks completed', value: 0 },
181
+ ],
182
+ })
183
+ },
184
+ },
185
+ },
186
+ })
187
+ `;
188
+ }
189
+ function genEnvExample(provider) {
190
+ const lines = [
191
+ '# JackClaw Node Configuration',
192
+ '#',
193
+ '# Copy this file to .env and fill in your values.',
194
+ '',
195
+ '# Hub connection',
196
+ 'JACKCLAW_HUB_URL=http://localhost:3100',
197
+ '',
198
+ '# LLM Provider',
199
+ `LLM_PROVIDER=${provider}`,
200
+ '',
201
+ ];
202
+ if (provider === 'openai') {
203
+ lines.push('OPENAI_API_KEY=sk-...');
204
+ lines.push('OPENAI_MODEL=gpt-4o');
205
+ }
206
+ else if (provider === 'anthropic') {
207
+ lines.push('ANTHROPIC_API_KEY=sk-ant-...');
208
+ lines.push('ANTHROPIC_MODEL=claude-sonnet-4-20250514');
209
+ }
210
+ else if (provider === 'ollama') {
211
+ lines.push('OLLAMA_HOST=http://localhost:11434');
212
+ lines.push('OLLAMA_MODEL=llama3');
213
+ }
214
+ else {
215
+ lines.push('# Configure your custom LLM provider');
216
+ lines.push('LLM_API_BASE=http://localhost:8000');
217
+ lines.push('LLM_API_KEY=');
218
+ lines.push('LLM_MODEL=');
219
+ }
220
+ lines.push('');
221
+ return lines.join('\n');
222
+ }
223
+ function genReadme(name, role, provider) {
224
+ return `# ${name}
225
+
226
+ > A **${role}** node for [JackClaw](https://github.com/nicepkg/JackClawOS) 🦞
227
+
228
+ ## Quick Start
229
+
230
+ \`\`\`bash
231
+ # Install dependencies
232
+ npm install
233
+
234
+ # Start the node
235
+ npx jackclaw start
236
+ \`\`\`
237
+
238
+ ## Configuration
239
+
240
+ 1. Copy \`.env.example\` to \`.env\` and fill in your API keys.
241
+ 2. The node connects to a JackClaw Hub at \`JACKCLAW_HUB_URL\`.
242
+
243
+ ## Plugin Commands
244
+
245
+ | Command | Description |
246
+ |-----------|----------------------|
247
+ | \`/ping\` | Health check |
248
+ | \`/status\` | Report node status |
249
+ | \`/hello\` | Greet someone |
250
+
251
+ ## Project Structure
252
+
253
+ \`\`\`
254
+ ${name}/
255
+ ├── src/
256
+ │ └── index.ts # Plugin definition (commands, schedules)
257
+ ├── package.json
258
+ ├── tsconfig.json
259
+ ├── .env.example
260
+ └── README.md
261
+ \`\`\`
262
+
263
+ ## Development
264
+
265
+ \`\`\`bash
266
+ # Watch mode
267
+ npm run dev
268
+
269
+ # Type check
270
+ npm run typecheck
271
+
272
+ # Build
273
+ npm run build
274
+ \`\`\`
275
+
276
+ ## LLM Provider: ${provider}
277
+
278
+ ${provider === 'openai' ? 'Set `OPENAI_API_KEY` in your `.env` file.' : ''}${provider === 'anthropic' ? 'Set `ANTHROPIC_API_KEY` in your `.env` file.' : ''}${provider === 'ollama' ? 'Make sure Ollama is running at `http://localhost:11434`.' : ''}${provider === 'custom' ? 'Configure your custom LLM endpoint in `.env`.' : ''}
279
+
280
+ ## Learn More
281
+
282
+ - [JackClaw Documentation](https://github.com/nicepkg/JackClawOS)
283
+ - [Plugin SDK Reference](https://github.com/nicepkg/JackClawOS/tree/main/packages/jackclaw-sdk)
284
+ `;
285
+ }
286
+ function genGitignore() {
287
+ return `node_modules/
288
+ dist/
289
+ .env
290
+ *.log
291
+ .DS_Store
292
+ `;
293
+ }
294
+ // ─── Main ────────────────────────────────────────────────────────────────────
295
+ async function main() {
296
+ console.log(BANNER);
297
+ const cliArgs = parseArgs(process.argv);
298
+ const skipPrompts = cliArgs.yes === true;
299
+ let projectName;
300
+ let role;
301
+ let provider;
302
+ if (skipPrompts) {
303
+ // Non-interactive mode: use defaults for anything not provided
304
+ projectName = cliArgs.name || 'my-jackclaw-node';
305
+ role = cliArgs.role && ROLES.includes(cliArgs.role)
306
+ ? cliArgs.role
307
+ : 'worker';
308
+ provider = cliArgs.provider && PROVIDERS.includes(cliArgs.provider)
309
+ ? cliArgs.provider
310
+ : 'openai';
311
+ }
312
+ else {
313
+ // Interactive mode
314
+ const rl = readline_1.default.createInterface({
315
+ input: process.stdin,
316
+ output: process.stdout,
317
+ });
318
+ projectName = await ask(rl, 'Project name', cliArgs.name || 'my-jackclaw-node');
319
+ role = await askChoice(rl, 'Node role', [...ROLES], cliArgs.role || 'worker');
320
+ provider = await askChoice(rl, 'LLM provider', [...PROVIDERS], cliArgs.provider || 'openai');
321
+ rl.close();
322
+ }
323
+ const safeName = sanitizeName(projectName);
324
+ const targetDir = path_1.default.resolve(process.cwd(), projectName);
325
+ // Check if directory already exists
326
+ if (fs_1.default.existsSync(targetDir)) {
327
+ console.error(`\n ✗ Directory "${projectName}" already exists. Pick a different name.\n`);
328
+ process.exit(1);
329
+ }
330
+ console.log(`\n 📁 Scaffolding ${projectName}...\n`);
331
+ // Create directories
332
+ fs_1.default.mkdirSync(path_1.default.join(targetDir, 'src'), { recursive: true });
333
+ // Write files
334
+ const files = [
335
+ ['package.json', genPackageJson(projectName, safeName, role)],
336
+ ['tsconfig.json', genTsConfig()],
337
+ [path_1.default.join('src', 'index.ts'), genSrcIndex(projectName, role)],
338
+ ['.env.example', genEnvExample(provider)],
339
+ ['README.md', genReadme(projectName, role, provider)],
340
+ ['.gitignore', genGitignore()],
341
+ ];
342
+ for (const [filePath, content] of files) {
343
+ const fullPath = path_1.default.join(targetDir, filePath);
344
+ fs_1.default.writeFileSync(fullPath, content, 'utf-8');
345
+ console.log(` ✔ ${filePath}`);
346
+ }
347
+ // Done!
348
+ console.log(`
349
+ ✅ Project "${projectName}" created successfully!
350
+
351
+ Next steps:
352
+
353
+ cd ${projectName}
354
+ npm install
355
+ npx jackclaw start
356
+
357
+ 🦞 Role: ${role} | LLM: ${provider}
358
+ 📖 Edit src/index.ts to add commands and schedules.
359
+ `);
360
+ }
361
+ main().catch(err => {
362
+ console.error(`\n ✗ Error: ${err.message}\n`);
363
+ process.exit(1);
364
+ });
365
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@jackclaw/create",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a JackClaw team project in one command",
5
+ "bin": {
6
+ "create-jackclaw": "./dist/index.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "start": "node dist/index.js"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^20.0.0",
14
+ "typescript": "^5.4.0"
15
+ },
16
+ "engines": {
17
+ "node": ">=20.0.0"
18
+ },
19
+ "keywords": [
20
+ "jackclaw",
21
+ "scaffold",
22
+ "cli",
23
+ "create"
24
+ ],
25
+ "license": "MIT",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * create-jackclaw — Scaffold a complete JackClaw node project.
5
+ *
6
+ * Usage:
7
+ * npm create jackclaw@latest
8
+ * npx create-jackclaw
9
+ * npx create-jackclaw --name my-node --role worker --provider openai --yes
10
+ */
11
+
12
+ import fs from 'fs'
13
+ import path from 'path'
14
+ import readline from 'readline'
15
+
16
+ // ─── Banner ──────────────────────────────────────────────────────────────────
17
+
18
+ const BANNER = `
19
+ 🦞 create-jackclaw — Scaffold your AI node
20
+ ───────────────────────────────────────────
21
+ `
22
+
23
+ // ─── CLI arg parsing ─────────────────────────────────────────────────────────
24
+
25
+ interface CliArgs {
26
+ name?: string
27
+ role?: string
28
+ provider?: string
29
+ yes?: boolean
30
+ }
31
+
32
+ function parseArgs(argv: string[]): CliArgs {
33
+ const args: CliArgs = {}
34
+ for (let i = 2; i < argv.length; i++) {
35
+ const a = argv[i]
36
+ if (a === '--yes' || a === '-y') {
37
+ args.yes = true
38
+ } else if (a === '--name' && argv[i + 1]) {
39
+ args.name = argv[++i]
40
+ } else if (a === '--role' && argv[i + 1]) {
41
+ args.role = argv[++i]
42
+ } else if (a === '--provider' && argv[i + 1]) {
43
+ args.provider = argv[++i]
44
+ } else if (!a.startsWith('-') && !args.name) {
45
+ // positional: treat as project name
46
+ args.name = a
47
+ }
48
+ }
49
+ return args
50
+ }
51
+
52
+ // ─── Prompts ─────────────────────────────────────────────────────────────────
53
+
54
+ function ask(rl: readline.Interface, question: string, defaultVal: string): Promise<string> {
55
+ return new Promise(resolve => {
56
+ rl.question(` ${question} (${defaultVal}): `, answer => {
57
+ resolve(answer.trim() || defaultVal)
58
+ })
59
+ })
60
+ }
61
+
62
+ function askChoice(
63
+ rl: readline.Interface,
64
+ question: string,
65
+ choices: string[],
66
+ defaultVal: string,
67
+ ): Promise<string> {
68
+ return new Promise(resolve => {
69
+ const choiceStr = choices.map(c => (c === defaultVal ? `[${c}]` : c)).join(' / ')
70
+ rl.question(` ${question} (${choiceStr}): `, answer => {
71
+ const val = answer.trim().toLowerCase()
72
+ if (val && choices.includes(val)) {
73
+ resolve(val)
74
+ } else {
75
+ resolve(defaultVal)
76
+ }
77
+ })
78
+ })
79
+ }
80
+
81
+ // ─── Validators ──────────────────────────────────────────────────────────────
82
+
83
+ const ROLES = ['worker', 'engineer', 'analyst', 'ceo'] as const
84
+ const PROVIDERS = ['openai', 'anthropic', 'ollama', 'custom'] as const
85
+
86
+ function sanitizeName(name: string): string {
87
+ return name
88
+ .toLowerCase()
89
+ .replace(/[^a-z0-9-]/g, '-')
90
+ .replace(/-+/g, '-')
91
+ .replace(/^-|-$/g, '')
92
+ }
93
+
94
+ // ─── Templates ───────────────────────────────────────────────────────────────
95
+
96
+ function genPackageJson(name: string, safeName: string, role: string): string {
97
+ const pkg = {
98
+ name: safeName,
99
+ version: '0.1.0',
100
+ private: true,
101
+ type: 'module',
102
+ scripts: {
103
+ build: 'tsc',
104
+ dev: 'tsc --watch',
105
+ start: 'npx jackclaw start',
106
+ typecheck: 'tsc --noEmit',
107
+ },
108
+ dependencies: {
109
+ '@jackclaw/node': '^0.1.0',
110
+ '@jackclaw/sdk': '^0.1.0',
111
+ },
112
+ devDependencies: {
113
+ '@types/node': '^20.0.0',
114
+ typescript: '^5.4.0',
115
+ },
116
+ jackclaw: {
117
+ role,
118
+ },
119
+ }
120
+ return JSON.stringify(pkg, null, 2) + '\n'
121
+ }
122
+
123
+ function genTsConfig(): string {
124
+ const config = {
125
+ compilerOptions: {
126
+ target: 'ES2022',
127
+ module: 'NodeNext',
128
+ moduleResolution: 'NodeNext',
129
+ outDir: 'dist',
130
+ rootDir: 'src',
131
+ strict: true,
132
+ esModuleInterop: true,
133
+ skipLibCheck: true,
134
+ declaration: true,
135
+ sourceMap: true,
136
+ },
137
+ include: ['src'],
138
+ exclude: ['node_modules', 'dist'],
139
+ }
140
+ return JSON.stringify(config, null, 2) + '\n'
141
+ }
142
+
143
+ function genSrcIndex(name: string, role: string): string {
144
+ return `import { definePlugin } from '@jackclaw/sdk'
145
+
146
+ /**
147
+ * ${name} — A JackClaw ${role} node plugin.
148
+ *
149
+ * This plugin registers basic commands that your node exposes
150
+ * to the Hub and other nodes in the team.
151
+ */
152
+ export default definePlugin({
153
+ name: '${name}',
154
+ version: '0.1.0',
155
+ description: 'A ${role} node for JackClaw',
156
+
157
+ commands: {
158
+ /**
159
+ * /ping — Simple health check.
160
+ */
161
+ ping: async (ctx) => {
162
+ return { text: \`🦞 pong from \${ctx.node.name} (role: ${role})\` }
163
+ },
164
+
165
+ /**
166
+ * /status — Report node status.
167
+ */
168
+ status: async (ctx) => {
169
+ return {
170
+ text: \`Node \${ctx.node.name} is online\`,
171
+ items: [
172
+ { label: 'Role', value: '${role}' },
173
+ { label: 'Version', value: ctx.plugin.version },
174
+ { label: 'Uptime', value: \`\${Math.floor(process.uptime())}s\` },
175
+ ],
176
+ }
177
+ },
178
+
179
+ /**
180
+ * /hello <name> — Greet someone.
181
+ */
182
+ hello: async (ctx) => {
183
+ const who = ctx.args[0] || ctx.userName || 'world'
184
+ return { text: \`👋 Hello, \${who}! I'm \${ctx.node.name}.\` }
185
+ },
186
+ },
187
+
188
+ schedules: {
189
+ /**
190
+ * Daily report — runs every day at 08:00.
191
+ */
192
+ dailyReport: {
193
+ cron: '0 8 * * *',
194
+ handler: async (ctx) => {
195
+ await ctx.report({
196
+ summary: \`Daily report from \${ctx.node.name}\`,
197
+ items: [
198
+ { label: 'Status', value: 'healthy' },
199
+ { label: 'Tasks completed', value: 0 },
200
+ ],
201
+ })
202
+ },
203
+ },
204
+ },
205
+ })
206
+ `
207
+ }
208
+
209
+ function genEnvExample(provider: string): string {
210
+ const lines = [
211
+ '# JackClaw Node Configuration',
212
+ '#',
213
+ '# Copy this file to .env and fill in your values.',
214
+ '',
215
+ '# Hub connection',
216
+ 'JACKCLAW_HUB_URL=http://localhost:3100',
217
+ '',
218
+ '# LLM Provider',
219
+ `LLM_PROVIDER=${provider}`,
220
+ '',
221
+ ]
222
+
223
+ if (provider === 'openai') {
224
+ lines.push('OPENAI_API_KEY=sk-...')
225
+ lines.push('OPENAI_MODEL=gpt-4o')
226
+ } else if (provider === 'anthropic') {
227
+ lines.push('ANTHROPIC_API_KEY=sk-ant-...')
228
+ lines.push('ANTHROPIC_MODEL=claude-sonnet-4-20250514')
229
+ } else if (provider === 'ollama') {
230
+ lines.push('OLLAMA_HOST=http://localhost:11434')
231
+ lines.push('OLLAMA_MODEL=llama3')
232
+ } else {
233
+ lines.push('# Configure your custom LLM provider')
234
+ lines.push('LLM_API_BASE=http://localhost:8000')
235
+ lines.push('LLM_API_KEY=')
236
+ lines.push('LLM_MODEL=')
237
+ }
238
+
239
+ lines.push('')
240
+ return lines.join('\n')
241
+ }
242
+
243
+ function genReadme(name: string, role: string, provider: string): string {
244
+ return `# ${name}
245
+
246
+ > A **${role}** node for [JackClaw](https://github.com/nicepkg/JackClawOS) 🦞
247
+
248
+ ## Quick Start
249
+
250
+ \`\`\`bash
251
+ # Install dependencies
252
+ npm install
253
+
254
+ # Start the node
255
+ npx jackclaw start
256
+ \`\`\`
257
+
258
+ ## Configuration
259
+
260
+ 1. Copy \`.env.example\` to \`.env\` and fill in your API keys.
261
+ 2. The node connects to a JackClaw Hub at \`JACKCLAW_HUB_URL\`.
262
+
263
+ ## Plugin Commands
264
+
265
+ | Command | Description |
266
+ |-----------|----------------------|
267
+ | \`/ping\` | Health check |
268
+ | \`/status\` | Report node status |
269
+ | \`/hello\` | Greet someone |
270
+
271
+ ## Project Structure
272
+
273
+ \`\`\`
274
+ ${name}/
275
+ ├── src/
276
+ │ └── index.ts # Plugin definition (commands, schedules)
277
+ ├── package.json
278
+ ├── tsconfig.json
279
+ ├── .env.example
280
+ └── README.md
281
+ \`\`\`
282
+
283
+ ## Development
284
+
285
+ \`\`\`bash
286
+ # Watch mode
287
+ npm run dev
288
+
289
+ # Type check
290
+ npm run typecheck
291
+
292
+ # Build
293
+ npm run build
294
+ \`\`\`
295
+
296
+ ## LLM Provider: ${provider}
297
+
298
+ ${provider === 'openai' ? 'Set `OPENAI_API_KEY` in your `.env` file.' : ''}${provider === 'anthropic' ? 'Set `ANTHROPIC_API_KEY` in your `.env` file.' : ''}${provider === 'ollama' ? 'Make sure Ollama is running at `http://localhost:11434`.' : ''}${provider === 'custom' ? 'Configure your custom LLM endpoint in `.env`.' : ''}
299
+
300
+ ## Learn More
301
+
302
+ - [JackClaw Documentation](https://github.com/nicepkg/JackClawOS)
303
+ - [Plugin SDK Reference](https://github.com/nicepkg/JackClawOS/tree/main/packages/jackclaw-sdk)
304
+ `
305
+ }
306
+
307
+ function genGitignore(): string {
308
+ return `node_modules/
309
+ dist/
310
+ .env
311
+ *.log
312
+ .DS_Store
313
+ `
314
+ }
315
+
316
+ // ─── Main ────────────────────────────────────────────────────────────────────
317
+
318
+ async function main() {
319
+ console.log(BANNER)
320
+
321
+ const cliArgs = parseArgs(process.argv)
322
+ const skipPrompts = cliArgs.yes === true
323
+
324
+ let projectName: string
325
+ let role: string
326
+ let provider: string
327
+
328
+ if (skipPrompts) {
329
+ // Non-interactive mode: use defaults for anything not provided
330
+ projectName = cliArgs.name || 'my-jackclaw-node'
331
+ role = cliArgs.role && (ROLES as readonly string[]).includes(cliArgs.role)
332
+ ? cliArgs.role
333
+ : 'worker'
334
+ provider = cliArgs.provider && (PROVIDERS as readonly string[]).includes(cliArgs.provider)
335
+ ? cliArgs.provider
336
+ : 'openai'
337
+ } else {
338
+ // Interactive mode
339
+ const rl = readline.createInterface({
340
+ input: process.stdin,
341
+ output: process.stdout,
342
+ })
343
+
344
+ projectName = await ask(rl, 'Project name', cliArgs.name || 'my-jackclaw-node')
345
+ role = await askChoice(rl, 'Node role', [...ROLES], cliArgs.role || 'worker')
346
+ provider = await askChoice(rl, 'LLM provider', [...PROVIDERS], cliArgs.provider || 'openai')
347
+
348
+ rl.close()
349
+ }
350
+
351
+ const safeName = sanitizeName(projectName)
352
+ const targetDir = path.resolve(process.cwd(), projectName)
353
+
354
+ // Check if directory already exists
355
+ if (fs.existsSync(targetDir)) {
356
+ console.error(`\n ✗ Directory "${projectName}" already exists. Pick a different name.\n`)
357
+ process.exit(1)
358
+ }
359
+
360
+ console.log(`\n 📁 Scaffolding ${projectName}...\n`)
361
+
362
+ // Create directories
363
+ fs.mkdirSync(path.join(targetDir, 'src'), { recursive: true })
364
+
365
+ // Write files
366
+ const files: Array<[string, string]> = [
367
+ ['package.json', genPackageJson(projectName, safeName, role)],
368
+ ['tsconfig.json', genTsConfig()],
369
+ [path.join('src', 'index.ts'), genSrcIndex(projectName, role)],
370
+ ['.env.example', genEnvExample(provider)],
371
+ ['README.md', genReadme(projectName, role, provider)],
372
+ ['.gitignore', genGitignore()],
373
+ ]
374
+
375
+ for (const [filePath, content] of files) {
376
+ const fullPath = path.join(targetDir, filePath)
377
+ fs.writeFileSync(fullPath, content, 'utf-8')
378
+ console.log(` ✔ ${filePath}`)
379
+ }
380
+
381
+ // Done!
382
+ console.log(`
383
+ ✅ Project "${projectName}" created successfully!
384
+
385
+ Next steps:
386
+
387
+ cd ${projectName}
388
+ npm install
389
+ npx jackclaw start
390
+
391
+ 🦞 Role: ${role} | LLM: ${provider}
392
+ 📖 Edit src/index.ts to add commands and schedules.
393
+ `)
394
+ }
395
+
396
+ main().catch(err => {
397
+ console.error(`\n ✗ Error: ${err.message}\n`)
398
+ process.exit(1)
399
+ })
@@ -0,0 +1,33 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ > {{DESCRIPTION}}
4
+
5
+ A JackClaw plugin built with [@jackclaw/sdk](https://jackclaw.dev/docs/sdk).
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm install
11
+ npm run build
12
+ jackclaw plugin load .
13
+ ```
14
+
15
+ ## Commands
16
+
17
+ | Command | Description |
18
+ |---------|-------------|
19
+ | `/hello` | Say hello from this node |
20
+ | `/ping [message]` | Echo a message back |
21
+
22
+ ## Development
23
+
24
+ ```bash
25
+ npm run dev # watch mode
26
+ npm run typecheck # type-check only
27
+ npm run build # production build
28
+ ```
29
+
30
+ ## Docs
31
+
32
+ - [Plugin Development Guide](https://jackclaw.dev/docs/plugin-development)
33
+ - [SDK API Reference](https://jackclaw.dev/docs/sdk)
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "description": "{{DESCRIPTION}}",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@jackclaw/sdk": "^0.1.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^20.0.0",
17
+ "typescript": "^5.4.0"
18
+ },
19
+ "author": "{{AUTHOR}}",
20
+ "license": "MIT",
21
+ "engines": {
22
+ "node": ">=20.0.0"
23
+ },
24
+ "openclaw": {
25
+ "extensions": ["./dist/index.js"]
26
+ }
27
+ }
@@ -0,0 +1,55 @@
1
+ import { defineNode } from '@jackclaw/sdk'
2
+
3
+ export default defineNode({
4
+ name: '{{PROJECT_NAME}}',
5
+ version: '0.1.0',
6
+ description: '{{DESCRIPTION}}',
7
+
8
+ // Node capabilities advertised to the hub
9
+ capabilities: ['report', 'command', 'schedule'],
10
+
11
+ commands: {
12
+ status: async (ctx) => {
13
+ return {
14
+ text: `Node ${ctx.node.name} is online ✅`,
15
+ data: {
16
+ uptime: process.uptime(),
17
+ memory: process.memoryUsage().heapUsed,
18
+ pid: process.pid,
19
+ },
20
+ }
21
+ },
22
+
23
+ info: async (ctx) => {
24
+ return {
25
+ text: `Node: ${ctx.node.name} | Version: ${ctx.node.version}`,
26
+ }
27
+ },
28
+ },
29
+
30
+ schedule: {
31
+ daily: async (ctx) => {
32
+ await ctx.report({
33
+ summary: `Daily check-in from ${ctx.node.name}`,
34
+ items: [
35
+ { label: 'Status', value: '✅ Online' },
36
+ { label: 'Uptime', value: `${Math.floor(process.uptime() / 3600)}h` },
37
+ ],
38
+ })
39
+ },
40
+
41
+ hourly: async (ctx) => {
42
+ ctx.log.debug(`Heartbeat from ${ctx.node.name}`)
43
+ },
44
+ },
45
+
46
+ hooks: {
47
+ onLoad: async (ctx) => {
48
+ ctx.log.info(`Node ${ctx.node.name} v${ctx.node.version} started`)
49
+ },
50
+
51
+ onShutdown: async (ctx) => {
52
+ ctx.log.info(`Node ${ctx.node.name} shutting down`)
53
+ },
54
+ },
55
+ })
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true,
12
+ "sourceMap": true
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }
@@ -0,0 +1,33 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ > {{DESCRIPTION}}
4
+
5
+ A JackClaw plugin built with [@jackclaw/sdk](https://jackclaw.dev/docs/sdk).
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm install
11
+ npm run build
12
+ jackclaw plugin load .
13
+ ```
14
+
15
+ ## Commands
16
+
17
+ | Command | Description |
18
+ |---------|-------------|
19
+ | `/hello` | Say hello from this node |
20
+ | `/ping [message]` | Echo a message back |
21
+
22
+ ## Development
23
+
24
+ ```bash
25
+ npm run dev # watch mode
26
+ npm run typecheck # type-check only
27
+ npm run build # production build
28
+ ```
29
+
30
+ ## Docs
31
+
32
+ - [Plugin Development Guide](https://jackclaw.dev/docs/plugin-development)
33
+ - [SDK API Reference](https://jackclaw.dev/docs/sdk)
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "description": "{{DESCRIPTION}}",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@jackclaw/sdk": "^0.1.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^20.0.0",
17
+ "typescript": "^5.4.0"
18
+ },
19
+ "author": "{{AUTHOR}}",
20
+ "license": "MIT",
21
+ "engines": {
22
+ "node": ">=20.0.0"
23
+ },
24
+ "openclaw": {
25
+ "extensions": ["./dist/index.js"]
26
+ }
27
+ }
@@ -0,0 +1,44 @@
1
+ import { definePlugin } from '@jackclaw/sdk'
2
+
3
+ export default definePlugin({
4
+ name: '{{PROJECT_NAME}}',
5
+ version: '0.1.0',
6
+ description: '{{DESCRIPTION}}',
7
+
8
+ commands: {
9
+ // Basic command: /hello
10
+ hello: async (ctx) => {
11
+ return {
12
+ text: `Hello from ${ctx.node.name}! 👋`,
13
+ }
14
+ },
15
+
16
+ // Command with arguments: /ping [message]
17
+ ping: async (ctx) => {
18
+ const msg = ctx.args[0] ?? 'world'
19
+ return {
20
+ text: `Pong! You said: ${msg}`,
21
+ }
22
+ },
23
+ },
24
+
25
+ schedule: {
26
+ // Runs every day at 09:00
27
+ daily: async (ctx) => {
28
+ await ctx.report({
29
+ summary: `Daily report from ${ctx.node.name}`,
30
+ items: [
31
+ { label: 'Status', value: '✅ Healthy' },
32
+ { label: 'Time', value: new Date().toLocaleString() },
33
+ ],
34
+ })
35
+ },
36
+ },
37
+
38
+ hooks: {
39
+ // Called when the plugin is loaded
40
+ onLoad: async (ctx) => {
41
+ ctx.log.info(`${ctx.plugin.name} loaded on node ${ctx.node.name}`)
42
+ },
43
+ },
44
+ })
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true,
12
+ "sourceMap": true
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true,
12
+ "declarationMap": true,
13
+ "sourceMap": true
14
+ },
15
+ "include": ["src"],
16
+ "exclude": ["node_modules", "dist", "templates"]
17
+ }