@dwizi/create-dzx 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/README.md +41 -0
- package/bin/index.js +524 -0
- package/package.json +34 -0
- package/templates/basic/README.md +8 -0
- package/templates/basic/mcp.json +13 -0
- package/templates/basic/package.json +17 -0
- package/templates/basic/prompts/summarize.md +9 -0
- package/templates/basic/resources/getting-started.md +7 -0
- package/templates/basic/src/server.ts +3 -0
- package/templates/basic/tools/hello.ts +17 -0
- package/templates/full/README.md +8 -0
- package/templates/full/mcp.json +17 -0
- package/templates/full/package.json +17 -0
- package/templates/full/prompts/rewrite.md +9 -0
- package/templates/full/resources/usage.md +7 -0
- package/templates/full/src/server.ts +3 -0
- package/templates/full/tools/convert-text.ts +17 -0
- package/templates/tools-only/README.md +8 -0
- package/templates/tools-only/mcp.json +13 -0
- package/templates/tools-only/package.json +17 -0
- package/templates/tools-only/src/server.ts +3 -0
- package/templates/tools-only/tools/echo.ts +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# create-dzx
|
|
2
|
+
|
|
3
|
+
Scaffold a new dzx MCP server project.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-dzx
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with options:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx create-dzx my-agent --template basic --runtime node
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Options
|
|
18
|
+
|
|
19
|
+
- `--dir <path>` - Target directory (default: `my-agent`)
|
|
20
|
+
- `--template <basic|tools-only|full>` - Template to scaffold
|
|
21
|
+
- `--runtime <node|deno>` - Runtime to configure
|
|
22
|
+
- `--install` - Install dependencies after scaffolding
|
|
23
|
+
- `--no-install` - Skip dependency installation
|
|
24
|
+
- `--yes` - Accept defaults
|
|
25
|
+
- `--force` - Overwrite existing files
|
|
26
|
+
|
|
27
|
+
## Templates
|
|
28
|
+
|
|
29
|
+
- **basic** - Includes tools, resources, and prompts
|
|
30
|
+
- **tools-only** - Minimal template with tools only
|
|
31
|
+
- **full** - Full-featured template with all features
|
|
32
|
+
|
|
33
|
+
## Package Manager Detection
|
|
34
|
+
|
|
35
|
+
`create-dzx` automatically detects your package manager by checking for lockfiles:
|
|
36
|
+
- `pnpm-lock.yaml` → pnpm
|
|
37
|
+
- `package-lock.json` → npm
|
|
38
|
+
- `yarn.lock` → yarn
|
|
39
|
+
- `bun.lockb` → bun
|
|
40
|
+
|
|
41
|
+
If no lockfile is found, it defaults to `pnpm`.
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import * as clack from "@clack/prompts";
|
|
7
|
+
|
|
8
|
+
const TEMPLATES = ["basic", "tools-only", "full"];
|
|
9
|
+
const RUNTIMES = ["node", "deno"];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a string into a filesystem-safe slug.
|
|
13
|
+
*/
|
|
14
|
+
function slugify(value) {
|
|
15
|
+
return value
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9-_]/g, "-")
|
|
18
|
+
.replace(/--+/g, "-")
|
|
19
|
+
.replace(/^-+/, "")
|
|
20
|
+
.replace(/-+$/, "");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse CLI argv into a simple key/value map.
|
|
25
|
+
*/
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const args = { positional: [] };
|
|
28
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
29
|
+
const arg = argv[i];
|
|
30
|
+
if (!arg.startsWith("--")) {
|
|
31
|
+
args.positional.push(arg);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const key = arg.slice(2);
|
|
35
|
+
const next = argv[i + 1];
|
|
36
|
+
if (next && !next.startsWith("--")) {
|
|
37
|
+
args[key] = next;
|
|
38
|
+
i += 1;
|
|
39
|
+
} else {
|
|
40
|
+
args[key] = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return args;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Return true when the directory is missing or empty.
|
|
48
|
+
*/
|
|
49
|
+
function isEmptyDir(dir) {
|
|
50
|
+
if (!fs.existsSync(dir)) return true;
|
|
51
|
+
return fs.readdirSync(dir).length === 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Recursively list files relative to the base directory.
|
|
56
|
+
*/
|
|
57
|
+
function listFiles(dir, baseDir = dir) {
|
|
58
|
+
if (!fs.existsSync(dir)) return [];
|
|
59
|
+
const entries = fs
|
|
60
|
+
.readdirSync(dir, { withFileTypes: true })
|
|
61
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
62
|
+
const files = [];
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const entryPath = path.join(dir, entry.name);
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
files.push(...listFiles(entryPath, baseDir));
|
|
67
|
+
} else {
|
|
68
|
+
files.push(path.relative(baseDir, entryPath));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Copy a directory recursively.
|
|
76
|
+
*/
|
|
77
|
+
function copyDir(src, dest) {
|
|
78
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
79
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const srcPath = path.join(src, entry.name);
|
|
82
|
+
const destPath = path.join(dest, entry.name);
|
|
83
|
+
if (entry.isDirectory()) {
|
|
84
|
+
copyDir(srcPath, destPath);
|
|
85
|
+
} else {
|
|
86
|
+
fs.copyFileSync(srcPath, destPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Detect package manager from lockfiles or environment.
|
|
93
|
+
*/
|
|
94
|
+
function detectPackageManager(cwd) {
|
|
95
|
+
const lockfiles = {
|
|
96
|
+
"pnpm-lock.yaml": "pnpm",
|
|
97
|
+
"package-lock.json": "npm",
|
|
98
|
+
"yarn.lock": "yarn",
|
|
99
|
+
"bun.lockb": "bun",
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
for (const [lockfile, pm] of Object.entries(lockfiles)) {
|
|
103
|
+
if (fs.existsSync(path.join(cwd, lockfile))) {
|
|
104
|
+
return pm;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check parent directories (monorepo context)
|
|
109
|
+
let current = cwd;
|
|
110
|
+
for (let i = 0; i < 5; i++) {
|
|
111
|
+
const parent = path.dirname(current);
|
|
112
|
+
if (parent === current) break;
|
|
113
|
+
for (const [lockfile, pm] of Object.entries(lockfiles)) {
|
|
114
|
+
if (fs.existsSync(path.join(parent, lockfile))) {
|
|
115
|
+
return pm;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
current = parent;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Default to pnpm
|
|
122
|
+
return "pnpm";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get install command for a package manager.
|
|
127
|
+
*/
|
|
128
|
+
function getInstallCommand(pm) {
|
|
129
|
+
return `${pm} install`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Run a shell command in a given working directory.
|
|
134
|
+
*/
|
|
135
|
+
async function runCommand(command, cwd) {
|
|
136
|
+
const { spawn } = await import("node:child_process");
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const child = spawn(command, { cwd, stdio: "inherit", shell: true });
|
|
139
|
+
child.on("error", (err) => reject(err));
|
|
140
|
+
child.on("exit", (code) => {
|
|
141
|
+
if (code === 0) resolve();
|
|
142
|
+
else reject(new Error(`Command failed (${code}): ${command}`));
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Resolve the templates directory for create-dzx.
|
|
149
|
+
*/
|
|
150
|
+
function resolveTemplatesRoot() {
|
|
151
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
152
|
+
const localTemplatesRoot = path.resolve(here, "..", "templates");
|
|
153
|
+
if (fs.existsSync(localTemplatesRoot)) return localTemplatesRoot;
|
|
154
|
+
// Fallback for when installed as npm package
|
|
155
|
+
return path.resolve(process.cwd(), "node_modules", "create-dzx", "templates");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Color utilities for terminal output.
|
|
160
|
+
*/
|
|
161
|
+
const useColor = Boolean(process.stdout.isTTY);
|
|
162
|
+
function color(code) {
|
|
163
|
+
return (text) => (useColor ? `\x1b[${code}m${text}\x1b[0m` : text);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const colorize = {
|
|
167
|
+
green: color("32"),
|
|
168
|
+
cyan: color("36"),
|
|
169
|
+
blue: color("34"),
|
|
170
|
+
gray: color("90"),
|
|
171
|
+
bold: color("1"),
|
|
172
|
+
dim: color("2"),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const symbols = {
|
|
176
|
+
check: useColor ? "✔" : "OK",
|
|
177
|
+
step: useColor ? "●" : "*",
|
|
178
|
+
brand: useColor ? "▲" : ">",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create a terminal spinner controller.
|
|
183
|
+
*/
|
|
184
|
+
function createSpinner(enabled) {
|
|
185
|
+
const frames = ["◐", "◓", "◑", "◒"];
|
|
186
|
+
let timer = null;
|
|
187
|
+
let message = "";
|
|
188
|
+
let frameIndex = 0;
|
|
189
|
+
|
|
190
|
+
const clearLine = () => {
|
|
191
|
+
if (!enabled) return;
|
|
192
|
+
process.stdout.write("\r\x1b[2K");
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const render = () => {
|
|
196
|
+
if (!enabled) return;
|
|
197
|
+
const frame = frames[frameIndex % frames.length];
|
|
198
|
+
frameIndex += 1;
|
|
199
|
+
process.stdout.write(`\r${colorize.gray(frame)} ${message}`);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const start = (nextMessage) => {
|
|
203
|
+
message = nextMessage;
|
|
204
|
+
if (!enabled || timer) return;
|
|
205
|
+
render();
|
|
206
|
+
timer = setInterval(render, 80);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const update = (nextMessage) => {
|
|
210
|
+
message = nextMessage;
|
|
211
|
+
if (!enabled || !timer) return;
|
|
212
|
+
render();
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const pause = () => {
|
|
216
|
+
if (!enabled || !timer) return;
|
|
217
|
+
clearInterval(timer);
|
|
218
|
+
timer = null;
|
|
219
|
+
clearLine();
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const resume = () => {
|
|
223
|
+
if (!enabled || timer || !message) return;
|
|
224
|
+
render();
|
|
225
|
+
timer = setInterval(render, 80);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const stop = () => {
|
|
229
|
+
pause();
|
|
230
|
+
message = "";
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return { start, update, pause, resume, stop, isEnabled: enabled };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Print a aligned key/value summary list.
|
|
238
|
+
*/
|
|
239
|
+
function printKeyValueList(items) {
|
|
240
|
+
if (items.length === 0) return;
|
|
241
|
+
const maxLabel = items.reduce((max, item) => Math.max(max, item.label.length), 0);
|
|
242
|
+
for (const item of items) {
|
|
243
|
+
const padded = item.label.padEnd(maxLabel);
|
|
244
|
+
// eslint-disable-next-line no-console
|
|
245
|
+
console.log(
|
|
246
|
+
`${colorize.gray(symbols.step)} ${colorize.gray(padded)} : ${colorize.cyan(item.value)}`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get the latest version of @dwizi/dzx from npm registry.
|
|
253
|
+
*/
|
|
254
|
+
async function getDzxVersion() {
|
|
255
|
+
try {
|
|
256
|
+
const { promisify } = await import("node:util");
|
|
257
|
+
const { exec } = promisify(await import("node:child_process"));
|
|
258
|
+
const { stdout } = await exec("npm view @dwizi/dzx version", { encoding: "utf8" });
|
|
259
|
+
return stdout.trim();
|
|
260
|
+
} catch {
|
|
261
|
+
// Fallback version if npm query fails
|
|
262
|
+
return "latest";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Main scaffolding function.
|
|
268
|
+
*/
|
|
269
|
+
async function main() {
|
|
270
|
+
const args = parseArgs(process.argv.slice(2));
|
|
271
|
+
const force = Boolean(args.force);
|
|
272
|
+
const isYes = Boolean(args.yes);
|
|
273
|
+
const shouldInstall = args.install
|
|
274
|
+
? true
|
|
275
|
+
: args["no-install"]
|
|
276
|
+
? false
|
|
277
|
+
: true; // Default to installing for scaffold mode
|
|
278
|
+
|
|
279
|
+
if (args.help || args.h) {
|
|
280
|
+
// eslint-disable-next-line no-console
|
|
281
|
+
console.log(`
|
|
282
|
+
${colorize.blue(symbols.brand)} ${colorize.bold("create-dzx")}
|
|
283
|
+
|
|
284
|
+
${colorize.bold("Usage")} ${colorize.gray("create-dzx [options]")}
|
|
285
|
+
|
|
286
|
+
${colorize.bold("Options")}
|
|
287
|
+
${colorize.cyan("--dir <path>".padEnd(20))} ${colorize.gray("target directory (default: my-agent)")}
|
|
288
|
+
${colorize.cyan("--template <basic|tools-only|full>".padEnd(20))} ${colorize.gray("template to scaffold")}
|
|
289
|
+
${colorize.cyan("--runtime <node|deno>".padEnd(20))} ${colorize.gray("runtime to configure")}
|
|
290
|
+
${colorize.cyan("--install".padEnd(20))} ${colorize.gray("install dependencies after scaffolding")}
|
|
291
|
+
${colorize.cyan("--no-install".padEnd(20))} ${colorize.gray("skip dependency installation")}
|
|
292
|
+
${colorize.cyan("--yes".padEnd(20))} ${colorize.gray("accept defaults")}
|
|
293
|
+
${colorize.cyan("--force".padEnd(20))} ${colorize.gray("overwrite existing files")}
|
|
294
|
+
`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
clack.intro("create-dzx");
|
|
299
|
+
|
|
300
|
+
const dirArg = args.dir ?? args.positional[0];
|
|
301
|
+
const defaultDir = "my-agent";
|
|
302
|
+
|
|
303
|
+
let targetDir = path.resolve(process.cwd(), dirArg || defaultDir);
|
|
304
|
+
if (!isYes) {
|
|
305
|
+
const dirResponse = await clack.text({
|
|
306
|
+
message: "Project directory",
|
|
307
|
+
initialValue: dirArg || defaultDir,
|
|
308
|
+
});
|
|
309
|
+
if (clack.isCancel(dirResponse)) {
|
|
310
|
+
clack.cancel("Aborted.");
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
targetDir = path.resolve(process.cwd(), dirResponse || defaultDir);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let template = args.template || (isYes ? "basic" : undefined);
|
|
317
|
+
if (!template) {
|
|
318
|
+
const templateResponse = await clack.select({
|
|
319
|
+
message: "Template",
|
|
320
|
+
options: [
|
|
321
|
+
{ value: "basic", label: "basic" },
|
|
322
|
+
{ value: "tools-only", label: "tools-only" },
|
|
323
|
+
{ value: "full", label: "full" },
|
|
324
|
+
],
|
|
325
|
+
initialValue: "basic",
|
|
326
|
+
});
|
|
327
|
+
if (clack.isCancel(templateResponse)) {
|
|
328
|
+
clack.cancel("Aborted.");
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
template = templateResponse;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let runtime = args.runtime || (isYes ? "node" : undefined);
|
|
335
|
+
if (!runtime) {
|
|
336
|
+
const runtimeResponse = await clack.select({
|
|
337
|
+
message: "Runtime",
|
|
338
|
+
options: [
|
|
339
|
+
{ value: "node", label: "node" },
|
|
340
|
+
{ value: "deno", label: "deno" },
|
|
341
|
+
],
|
|
342
|
+
initialValue: "node",
|
|
343
|
+
});
|
|
344
|
+
if (clack.isCancel(runtimeResponse)) {
|
|
345
|
+
clack.cancel("Aborted.");
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
runtime = runtimeResponse;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
template = template ?? "basic";
|
|
352
|
+
runtime = runtime ?? "node";
|
|
353
|
+
|
|
354
|
+
if (!TEMPLATES.includes(template)) {
|
|
355
|
+
throw new Error(`Unknown template: ${template}`);
|
|
356
|
+
}
|
|
357
|
+
if (!RUNTIMES.includes(runtime)) {
|
|
358
|
+
throw new Error(`Unknown runtime: ${runtime}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!force && !isEmptyDir(targetDir)) {
|
|
362
|
+
throw new Error(`Target directory is not empty: ${targetDir}. Use --force to overwrite.`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const templatesRoot = resolveTemplatesRoot();
|
|
366
|
+
const templateDir = path.join(templatesRoot, template);
|
|
367
|
+
if (!fs.existsSync(templateDir)) {
|
|
368
|
+
throw new Error(`Template not found: ${template}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (force && !isYes) {
|
|
372
|
+
const confirmation = await clack.confirm({
|
|
373
|
+
message: "This will overwrite existing files. Continue?",
|
|
374
|
+
initialValue: false,
|
|
375
|
+
});
|
|
376
|
+
if (clack.isCancel(confirmation) || confirmation === false) {
|
|
377
|
+
clack.cancel("Aborted.");
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const spinner = createSpinner(process.stdout.isTTY);
|
|
383
|
+
const stepLabels = [
|
|
384
|
+
"Validating destination",
|
|
385
|
+
"Copying template",
|
|
386
|
+
"Configuring manifest",
|
|
387
|
+
...(shouldInstall ? ["Installing dependencies"] : []),
|
|
388
|
+
"Finalizing",
|
|
389
|
+
];
|
|
390
|
+
const stepLabelWidth = stepLabels.reduce((max, label) => Math.max(max, label.length), 0);
|
|
391
|
+
const stepTimes = [];
|
|
392
|
+
let stepStart = Date.now();
|
|
393
|
+
let lastStep = "";
|
|
394
|
+
let spinnerStarted = false;
|
|
395
|
+
|
|
396
|
+
const logStep = (label, ms) => {
|
|
397
|
+
const paddedLabel = label.padEnd(stepLabelWidth);
|
|
398
|
+
const paddedMs = `${ms}ms`.padStart(6);
|
|
399
|
+
const line = `${colorize.cyan(symbols.step)} ${colorize.gray(paddedLabel)} ${colorize.dim(paddedMs)}`;
|
|
400
|
+
if (spinner.isEnabled) {
|
|
401
|
+
spinner.pause();
|
|
402
|
+
// eslint-disable-next-line no-console
|
|
403
|
+
console.log(line);
|
|
404
|
+
spinner.resume();
|
|
405
|
+
} else {
|
|
406
|
+
// eslint-disable-next-line no-console
|
|
407
|
+
console.log(line);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const step = (message) => {
|
|
412
|
+
const now = Date.now();
|
|
413
|
+
if (lastStep) {
|
|
414
|
+
const ms = now - stepStart;
|
|
415
|
+
stepTimes.push({ label: lastStep, ms });
|
|
416
|
+
logStep(lastStep, ms);
|
|
417
|
+
}
|
|
418
|
+
lastStep = message;
|
|
419
|
+
stepStart = now;
|
|
420
|
+
if (spinner.isEnabled) {
|
|
421
|
+
if (spinnerStarted) {
|
|
422
|
+
spinner.update(message);
|
|
423
|
+
} else {
|
|
424
|
+
spinner.start(message);
|
|
425
|
+
spinnerStarted = true;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const dzxVersion = await getDzxVersion();
|
|
431
|
+
const banner = `${colorize.blue(symbols.brand)} ${colorize.bold("create-dzx")} ${colorize.gray("scaffold")}`;
|
|
432
|
+
// eslint-disable-next-line no-console
|
|
433
|
+
console.log(banner);
|
|
434
|
+
|
|
435
|
+
step("Validating destination");
|
|
436
|
+
if (!force) {
|
|
437
|
+
const templateFiles = listFiles(templateDir);
|
|
438
|
+
const collisions = templateFiles.filter((file) => fs.existsSync(path.join(targetDir, file)));
|
|
439
|
+
if (collisions.length > 0) {
|
|
440
|
+
const preview = collisions
|
|
441
|
+
.slice(0, 8)
|
|
442
|
+
.map((file) => `- ${file}`)
|
|
443
|
+
.join("\n");
|
|
444
|
+
const suffix = collisions.length > 8 ? "\n- ..." : "";
|
|
445
|
+
throw new Error(
|
|
446
|
+
`Refusing to overwrite existing files. Use --force to proceed.\n${preview}${suffix}`,
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
step("Copying template");
|
|
452
|
+
copyDir(templateDir, targetDir);
|
|
453
|
+
|
|
454
|
+
step("Configuring manifest");
|
|
455
|
+
const manifestPath = path.join(targetDir, "mcp.json");
|
|
456
|
+
if (fs.existsSync(manifestPath)) {
|
|
457
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
458
|
+
manifest.name = slugify(path.basename(targetDir));
|
|
459
|
+
manifest.runtime = runtime;
|
|
460
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Update package.json with correct @dwizi/dzx version
|
|
464
|
+
const pkgPath = path.join(targetDir, "package.json");
|
|
465
|
+
if (fs.existsSync(pkgPath)) {
|
|
466
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
467
|
+
if (pkg.dependencies && pkg.dependencies["@dwizi/dzx"]) {
|
|
468
|
+
pkg.dependencies["@dwizi/dzx"] = `^${dzxVersion}`;
|
|
469
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (shouldInstall) {
|
|
474
|
+
step("Installing dependencies");
|
|
475
|
+
if (!fs.existsSync(pkgPath)) {
|
|
476
|
+
if (spinner.isEnabled) spinner.stop();
|
|
477
|
+
throw new Error("Missing package.json in template. Cannot install dependencies.");
|
|
478
|
+
}
|
|
479
|
+
const pm = detectPackageManager(targetDir);
|
|
480
|
+
const installCommand = getInstallCommand(pm);
|
|
481
|
+
try {
|
|
482
|
+
await runCommand(installCommand, targetDir);
|
|
483
|
+
} catch {
|
|
484
|
+
if (spinner.isEnabled) spinner.stop();
|
|
485
|
+
throw new Error(`Dependency installation failed. Run \`${installCommand}\` manually.`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
step("Finalizing");
|
|
490
|
+
if (lastStep) {
|
|
491
|
+
const ms = Date.now() - stepStart;
|
|
492
|
+
stepTimes.push({ label: lastStep, ms });
|
|
493
|
+
logStep(lastStep, ms);
|
|
494
|
+
}
|
|
495
|
+
spinner.stop();
|
|
496
|
+
|
|
497
|
+
const totalMs = stepTimes.reduce((sum, item) => sum + item.ms, 0);
|
|
498
|
+
const summaryLines = [
|
|
499
|
+
{ label: "dir", value: targetDir },
|
|
500
|
+
{ label: "template", value: template },
|
|
501
|
+
{ label: "runtime", value: runtime },
|
|
502
|
+
{ label: "install", value: shouldInstall ? "yes" : "no" },
|
|
503
|
+
{ label: "ready", value: `${totalMs}ms` },
|
|
504
|
+
];
|
|
505
|
+
// eslint-disable-next-line no-console
|
|
506
|
+
console.log("");
|
|
507
|
+
// eslint-disable-next-line no-console
|
|
508
|
+
console.log(`${colorize.green(symbols.check)} ${colorize.bold("Project ready")}`);
|
|
509
|
+
printKeyValueList(summaryLines);
|
|
510
|
+
const pm = shouldInstall ? detectPackageManager(targetDir) : "pnpm";
|
|
511
|
+
const nextSteps = [
|
|
512
|
+
`cd ${path.basename(targetDir)}`,
|
|
513
|
+
shouldInstall ? "dzx dev" : `${pm} install`,
|
|
514
|
+
shouldInstall ? "" : "dzx dev",
|
|
515
|
+
].filter(Boolean);
|
|
516
|
+
// eslint-disable-next-line no-console
|
|
517
|
+
console.log(`${colorize.gray("next")} ${colorize.cyan(nextSteps.join(" && "))}`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
main().catch((err) => {
|
|
521
|
+
// eslint-disable-next-line no-console
|
|
522
|
+
console.error(err);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dwizi/create-dzx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public",
|
|
8
|
+
"registry": "https://registry.npmjs.org/"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/dwizi/create-dzx.git",
|
|
13
|
+
"directory": "packages/create-dzx"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/dwizi/create-dzx/issues"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"create-dzx": "./bin/index.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin",
|
|
23
|
+
"templates"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@clack/prompts": "^1.0.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=24"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "node scripts/build.mjs"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "basic-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"runtime": "node",
|
|
5
|
+
"entry": "src/server.ts",
|
|
6
|
+
"toolsDir": "tools",
|
|
7
|
+
"resourcesDir": "resources",
|
|
8
|
+
"promptsDir": "prompts",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": false,
|
|
11
|
+
"filesystem": { "read": ["./resources", "./prompts"], "write": [] }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "basic-agent",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "dzx dev",
|
|
7
|
+
"inspect": "dzx inspect --json",
|
|
8
|
+
"build": "dzx build --split-tools"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@dwizi/dzx": "^0.0.0",
|
|
12
|
+
"zod": "^4.3.6"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsx": "^4.21.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineSchema } from "@dwizi/dzx/schema";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a friendly greeting.
|
|
6
|
+
* @param {object} input
|
|
7
|
+
* @param {string} input.name
|
|
8
|
+
* @returns {{ greeting: string }}
|
|
9
|
+
*/
|
|
10
|
+
export default async function hello(input: { name: string }) {
|
|
11
|
+
return { greeting: `Hello, ${input.name}!` };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const schema = {
|
|
15
|
+
input: defineSchema(z.object({ name: z.string() })),
|
|
16
|
+
output: defineSchema(z.object({ greeting: z.string() })),
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "full-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"runtime": "node",
|
|
5
|
+
"entry": "src/server.ts",
|
|
6
|
+
"toolsDir": "tools",
|
|
7
|
+
"resourcesDir": "resources",
|
|
8
|
+
"promptsDir": "prompts",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": false,
|
|
11
|
+
"filesystem": { "read": ["./resources", "./prompts"], "write": [] }
|
|
12
|
+
},
|
|
13
|
+
"build": {
|
|
14
|
+
"command": "pnpm build",
|
|
15
|
+
"output": "dist"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "full-agent",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "dzx dev",
|
|
7
|
+
"inspect": "dzx inspect --json",
|
|
8
|
+
"build": "dzx build --split-tools"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@dwizi/dzx": "^0.0.0",
|
|
12
|
+
"zod": "^4.3.6"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsx": "^4.21.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineSchema } from "@dwizi/dzx/schema";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Converts text to uppercase.
|
|
6
|
+
* @param {object} input
|
|
7
|
+
* @param {string} input.text
|
|
8
|
+
* @returns {{ upper: string }}
|
|
9
|
+
*/
|
|
10
|
+
export default async function convertText(input: { text: string }) {
|
|
11
|
+
return { upper: input.text.toUpperCase() };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const schema = {
|
|
15
|
+
input: defineSchema(z.object({ text: z.string() })),
|
|
16
|
+
output: defineSchema(z.object({ upper: z.string() })),
|
|
17
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tools-only-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"runtime": "node",
|
|
5
|
+
"entry": "src/server.ts",
|
|
6
|
+
"toolsDir": "tools",
|
|
7
|
+
"resourcesDir": "resources",
|
|
8
|
+
"promptsDir": "prompts",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": false,
|
|
11
|
+
"filesystem": { "read": [], "write": [] }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tools-only-agent",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "dzx dev",
|
|
7
|
+
"inspect": "dzx inspect --json",
|
|
8
|
+
"build": "dzx build --split-tools"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@dwizi/dzx": "^0.0.0",
|
|
12
|
+
"zod": "^4.3.6"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsx": "^4.21.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineSchema } from "@dwizi/dzx/schema";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the input as output.
|
|
6
|
+
* @param {object} input
|
|
7
|
+
* @param {string} input.text
|
|
8
|
+
* @returns {{ text: string }}
|
|
9
|
+
*/
|
|
10
|
+
export default async function echo(input: { text: string }) {
|
|
11
|
+
return { text: input.text };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const schema = {
|
|
15
|
+
input: defineSchema(z.object({ text: z.string() })),
|
|
16
|
+
output: defineSchema(z.object({ text: z.string() })),
|
|
17
|
+
};
|