@donkeylabs/cli 0.4.1 → 0.4.3
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/package.json +4 -1
- package/src/commands/generate.ts +122 -1
- package/src/commands/init.ts +184 -33
- package/src/commands/mcp.ts +305 -0
- package/src/index.ts +6 -0
- package/templates/starter/src/index.ts +21 -4
- package/templates/sveltekit-app/package.json +3 -3
- package/templates/sveltekit-app/src/server/index.ts +14 -20
- package/templates/sveltekit-app/src/server/routes/demo.ts +268 -0
- package/templates/sveltekit-app/src/server/routes/cache/handlers/delete.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/handlers/get.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/handlers/keys.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/handlers/set.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/cache/index.ts +0 -46
- package/templates/sveltekit-app/src/server/routes/counter/handlers/decrement.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/handlers/get.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/handlers/increment.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/handlers/reset.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/counter/index.ts +0 -39
- package/templates/sveltekit-app/src/server/routes/cron/handlers/list.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/cron/index.ts +0 -24
- package/templates/sveltekit-app/src/server/routes/events/handlers/emit.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/events/index.ts +0 -19
- package/templates/sveltekit-app/src/server/routes/index.ts +0 -8
- package/templates/sveltekit-app/src/server/routes/jobs/handlers/enqueue.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/jobs/handlers/stats.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/jobs/index.ts +0 -28
- package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/check.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/ratelimit/handlers/reset.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/ratelimit/index.ts +0 -29
- package/templates/sveltekit-app/src/server/routes/sse/handlers/broadcast.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/sse/handlers/clients.ts +0 -15
- package/templates/sveltekit-app/src/server/routes/sse/index.ts +0 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@donkeylabs/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for @donkeylabs/server - project scaffolding and code generation",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"typecheck": "bun --bun tsc --noEmit"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"kysely": "^0.27.0",
|
|
28
|
+
"kysely-bun-sqlite": "^0.3.0",
|
|
29
|
+
"kysely-codegen": "^0.17.0",
|
|
27
30
|
"picocolors": "^1.1.1",
|
|
28
31
|
"prompts": "^2.4.2"
|
|
29
32
|
},
|
package/src/commands/generate.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { readdir, writeFile, readFile, mkdir } from "node:fs/promises";
|
|
1
|
+
import { readdir, writeFile, readFile, mkdir, unlink } from "node:fs/promises";
|
|
2
2
|
import { join, relative, dirname, basename } from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import pc from "picocolors";
|
|
6
|
+
import { Kysely, Migrator, FileMigrationProvider } from "kysely";
|
|
7
|
+
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
|
8
|
+
import { Database } from "bun:sqlite";
|
|
9
|
+
import { generate, KyselyBunSqliteDialect } from "kysely-codegen";
|
|
6
10
|
|
|
7
11
|
interface DonkeylabsConfig {
|
|
8
12
|
plugins: string[];
|
|
@@ -325,6 +329,8 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
325
329
|
return [];
|
|
326
330
|
}
|
|
327
331
|
|
|
332
|
+
const TIMEOUT_MS = 10000; // 10 second timeout
|
|
333
|
+
|
|
328
334
|
return new Promise((resolve) => {
|
|
329
335
|
const child = spawn("bun", [fullPath], {
|
|
330
336
|
env: { ...process.env, DONKEYLABS_GENERATE: "1" },
|
|
@@ -334,6 +340,16 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
334
340
|
|
|
335
341
|
let stdout = "";
|
|
336
342
|
let stderr = "";
|
|
343
|
+
let timedOut = false;
|
|
344
|
+
|
|
345
|
+
// Timeout handler
|
|
346
|
+
const timeout = setTimeout(() => {
|
|
347
|
+
timedOut = true;
|
|
348
|
+
child.kill("SIGTERM");
|
|
349
|
+
console.warn(pc.yellow(`Route extraction timed out after ${TIMEOUT_MS / 1000}s`));
|
|
350
|
+
console.warn(pc.dim("Make sure your entry file handles DONKEYLABS_GENERATE=1 and calls process.exit(0)"));
|
|
351
|
+
resolve([]);
|
|
352
|
+
}, TIMEOUT_MS);
|
|
337
353
|
|
|
338
354
|
child.stdout?.on("data", (data) => {
|
|
339
355
|
stdout += data.toString();
|
|
@@ -344,6 +360,9 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
344
360
|
});
|
|
345
361
|
|
|
346
362
|
child.on("close", (code) => {
|
|
363
|
+
clearTimeout(timeout);
|
|
364
|
+
if (timedOut) return; // Already resolved
|
|
365
|
+
|
|
347
366
|
if (code !== 0) {
|
|
348
367
|
console.warn(pc.yellow(`Failed to extract routes from server (exit code ${code})`));
|
|
349
368
|
if (stderr) console.warn(pc.dim(stderr));
|
|
@@ -374,12 +393,105 @@ async function extractRoutesFromServer(entryPath: string): Promise<RouteInfo[]>
|
|
|
374
393
|
});
|
|
375
394
|
|
|
376
395
|
child.on("error", (err) => {
|
|
396
|
+
clearTimeout(timeout);
|
|
377
397
|
console.warn(pc.yellow(`Failed to run entry file: ${err.message}`));
|
|
378
398
|
resolve([]);
|
|
379
399
|
});
|
|
380
400
|
});
|
|
381
401
|
}
|
|
382
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Generate schema.ts from plugin migrations using kysely-codegen
|
|
405
|
+
*/
|
|
406
|
+
async function generatePluginSchemas(
|
|
407
|
+
plugins: { name: string; path: string; exportName: string }[]
|
|
408
|
+
): Promise<string[]> {
|
|
409
|
+
const generated: string[] = [];
|
|
410
|
+
|
|
411
|
+
for (const plugin of plugins) {
|
|
412
|
+
const pluginDir = dirname(join(process.cwd(), plugin.path));
|
|
413
|
+
const migrationsDir = join(pluginDir, "migrations");
|
|
414
|
+
|
|
415
|
+
// Skip plugins without migrations folder
|
|
416
|
+
if (!existsSync(migrationsDir)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check if there are any migration files
|
|
421
|
+
const migrationFiles = await readdir(migrationsDir);
|
|
422
|
+
const hasMigrations = migrationFiles.some(
|
|
423
|
+
(f) => f.endsWith(".ts") && !f.endsWith(".d.ts")
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (!hasMigrations) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
console.log(pc.dim(` Generating schema for ${plugin.name}...`));
|
|
431
|
+
|
|
432
|
+
const dbPath = join(process.cwd(), `.temp_schema_${plugin.name}.db`);
|
|
433
|
+
|
|
434
|
+
try {
|
|
435
|
+
// Create temp SQLite database
|
|
436
|
+
const db = new Kysely<any>({
|
|
437
|
+
dialect: new BunSqliteDialect({
|
|
438
|
+
database: new Database(dbPath),
|
|
439
|
+
}),
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Run migrations using Kysely's FileMigrationProvider
|
|
443
|
+
const migrator = new Migrator({
|
|
444
|
+
db,
|
|
445
|
+
provider: new FileMigrationProvider({
|
|
446
|
+
fs: await import("node:fs/promises"),
|
|
447
|
+
path: await import("node:path"),
|
|
448
|
+
migrationFolder: migrationsDir,
|
|
449
|
+
}),
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const { error } = await migrator.migrateToLatest();
|
|
453
|
+
|
|
454
|
+
if (error) {
|
|
455
|
+
console.warn(
|
|
456
|
+
pc.yellow(` Warning: Migration failed for ${plugin.name}: ${error}`)
|
|
457
|
+
);
|
|
458
|
+
await db.destroy();
|
|
459
|
+
try {
|
|
460
|
+
await unlink(dbPath);
|
|
461
|
+
} catch {}
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Generate schema.ts using kysely-codegen
|
|
466
|
+
const schemaPath = join(pluginDir, "schema.ts");
|
|
467
|
+
|
|
468
|
+
await generate({
|
|
469
|
+
db,
|
|
470
|
+
outFile: schemaPath,
|
|
471
|
+
dialect: new KyselyBunSqliteDialect(),
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
generated.push(plugin.name);
|
|
475
|
+
|
|
476
|
+
// Cleanup
|
|
477
|
+
await db.destroy();
|
|
478
|
+
try {
|
|
479
|
+
await unlink(dbPath);
|
|
480
|
+
} catch {}
|
|
481
|
+
} catch (err: any) {
|
|
482
|
+
console.warn(
|
|
483
|
+
pc.yellow(` Warning: Schema generation failed for ${plugin.name}: ${err.message}`)
|
|
484
|
+
);
|
|
485
|
+
// Cleanup on error
|
|
486
|
+
try {
|
|
487
|
+
await unlink(dbPath);
|
|
488
|
+
} catch {}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return generated;
|
|
493
|
+
}
|
|
494
|
+
|
|
383
495
|
export async function generateCommand(_args: string[]): Promise<void> {
|
|
384
496
|
const config = await loadConfig();
|
|
385
497
|
const outDir = config.outDir || ".@donkeylabs/server";
|
|
@@ -390,6 +502,15 @@ export async function generateCommand(_args: string[]): Promise<void> {
|
|
|
390
502
|
const plugins = await findPlugins(config.plugins);
|
|
391
503
|
const fileRoutes = await findRoutes(config.routes || "./src/routes/**/schema.ts");
|
|
392
504
|
|
|
505
|
+
// Generate schema.ts from migrations for plugins that have them
|
|
506
|
+
const schemaPlugins = await generatePluginSchemas(plugins);
|
|
507
|
+
if (schemaPlugins.length > 0) {
|
|
508
|
+
console.log(
|
|
509
|
+
pc.green("Generated schemas:"),
|
|
510
|
+
schemaPlugins.map((p) => pc.dim(p)).join(", ")
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
393
514
|
// Extract routes by running the server with DONKEYLABS_GENERATE=1
|
|
394
515
|
const entryPath = config.entry || "./src/index.ts";
|
|
395
516
|
const serverRoutes = await extractRoutesFromServer(entryPath);
|
package/src/commands/init.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { mkdir, writeFile, readFile, readdir, copyFile, stat } from "node:fs/pro
|
|
|
8
8
|
import { join, resolve, dirname, basename } from "node:path";
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
11
12
|
import pc from "picocolors";
|
|
12
13
|
import prompts from "prompts";
|
|
13
14
|
|
|
@@ -38,21 +39,45 @@ const RENAME_MAP: Record<string, string> = {
|
|
|
38
39
|
|
|
39
40
|
export async function initCommand(args: string[]) {
|
|
40
41
|
// Parse --type flag if provided
|
|
41
|
-
let projectDir =
|
|
42
|
+
let projectDir: string | null = null;
|
|
42
43
|
let typeArg: string | null = null;
|
|
43
44
|
|
|
44
45
|
for (let i = 0; i < args.length; i++) {
|
|
45
46
|
if (args[i] === "--type" && args[i + 1]) {
|
|
46
47
|
typeArg = args[i + 1];
|
|
47
48
|
i++; // skip next arg
|
|
48
|
-
} else if (!args[i]
|
|
49
|
-
projectDir = args[i];
|
|
49
|
+
} else if (!args[i]?.startsWith("-")) {
|
|
50
|
+
projectDir = args[i] ?? null;
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
console.log(pc.bold("\n🚀 Create a new @donkeylabs/server project\n"));
|
|
55
|
+
|
|
56
|
+
// If no project directory provided, prompt for it
|
|
57
|
+
if (!projectDir) {
|
|
58
|
+
const { name } = await prompts({
|
|
59
|
+
type: "text",
|
|
60
|
+
name: "name",
|
|
61
|
+
message: "Project name:",
|
|
62
|
+
initial: "my-donkeylabs-app",
|
|
63
|
+
validate: (value) => {
|
|
64
|
+
if (!value) return "Project name is required";
|
|
65
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
|
|
66
|
+
return "Project name can only contain letters, numbers, dashes, and underscores";
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!name) {
|
|
73
|
+
console.log(pc.yellow("Cancelled."));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
projectDir = name;
|
|
78
|
+
}
|
|
54
79
|
|
|
55
|
-
|
|
80
|
+
const targetDir = resolve(process.cwd(), projectDir!);
|
|
56
81
|
|
|
57
82
|
let projectType: ProjectType;
|
|
58
83
|
|
|
@@ -130,45 +155,143 @@ export async function initCommand(args: string[]) {
|
|
|
130
155
|
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
131
156
|
}
|
|
132
157
|
|
|
133
|
-
|
|
134
|
-
if (projectType === "server") {
|
|
135
|
-
console.log(`
|
|
136
|
-
${pc.bold(pc.green("Success!"))} Server project initialized.
|
|
137
|
-
|
|
138
|
-
${pc.bold("Next steps:")}
|
|
139
|
-
1. Install dependencies:
|
|
140
|
-
${pc.cyan("bun install")}
|
|
158
|
+
console.log(pc.green("\n✓ Project files created\n"));
|
|
141
159
|
|
|
142
|
-
|
|
143
|
-
|
|
160
|
+
// Auto-install dependencies
|
|
161
|
+
console.log(pc.cyan("Installing dependencies...\n"));
|
|
162
|
+
const installSuccess = await runCommand("bun", ["install"], targetDir);
|
|
144
163
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
if (!installSuccess) {
|
|
165
|
+
console.log(pc.yellow("\n⚠ Dependency installation failed."));
|
|
166
|
+
console.log(pc.dim(" Run 'bun install' manually to install dependencies.\n"));
|
|
148
167
|
} else {
|
|
149
|
-
console.log(
|
|
150
|
-
${pc.bold(pc.green("Success!"))} SvelteKit project initialized.
|
|
168
|
+
console.log(pc.green("\n✓ Dependencies installed\n"));
|
|
151
169
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
170
|
+
// Copy CLAUDE.md and docs/ from @donkeylabs/server to project root
|
|
171
|
+
await copyDocsFromServer(targetDir);
|
|
172
|
+
}
|
|
155
173
|
|
|
156
|
-
|
|
157
|
-
|
|
174
|
+
// Ask about MCP setup
|
|
175
|
+
const { setupMcp } = await prompts({
|
|
176
|
+
type: "confirm",
|
|
177
|
+
name: "setupMcp",
|
|
178
|
+
message: `Setup MCP for AI-assisted development? ${pc.dim("(Highly recommended)")}`,
|
|
179
|
+
initial: true,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
if (setupMcp) {
|
|
183
|
+
// Ask which IDE
|
|
184
|
+
const { ide } = await prompts({
|
|
185
|
+
type: "select",
|
|
186
|
+
name: "ide",
|
|
187
|
+
message: "Which AI IDE are you using?",
|
|
188
|
+
choices: [
|
|
189
|
+
{ title: "Claude Code", value: "claude", description: "Anthropic's Claude Code CLI" },
|
|
190
|
+
{ title: "Cursor", value: "cursor", description: "Cursor AI IDE" },
|
|
191
|
+
{ title: "Windsurf", value: "windsurf", description: "Codeium Windsurf" },
|
|
192
|
+
{ title: "Other / Skip instructions", value: "skip" },
|
|
193
|
+
],
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Install @donkeylabs/mcp
|
|
197
|
+
console.log(pc.cyan("\nInstalling @donkeylabs/mcp...\n"));
|
|
198
|
+
const mcpInstallSuccess = await runCommand("bun", ["add", "-d", "@donkeylabs/mcp"], targetDir);
|
|
158
199
|
|
|
159
|
-
|
|
160
|
-
|
|
200
|
+
if (mcpInstallSuccess) {
|
|
201
|
+
console.log(pc.green("✓ Installed @donkeylabs/mcp\n"));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Create .mcp.json
|
|
205
|
+
const mcpConfig = {
|
|
206
|
+
mcpServers: {
|
|
207
|
+
donkeylabs: {
|
|
208
|
+
command: "bunx",
|
|
209
|
+
args: ["@donkeylabs/mcp"],
|
|
210
|
+
cwd: "${workspaceFolder}",
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
await writeFile(join(targetDir, ".mcp.json"), JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
216
|
+
console.log(pc.green("✓ Created .mcp.json\n"));
|
|
217
|
+
|
|
218
|
+
// Show IDE-specific instructions
|
|
219
|
+
if (ide === "claude") {
|
|
220
|
+
console.log(pc.cyan("Claude Code Setup:"));
|
|
221
|
+
console.log(pc.dim("─".repeat(40)));
|
|
222
|
+
console.log(`
|
|
223
|
+
The .mcp.json file has been created in your project.
|
|
224
|
+
Claude Code will automatically detect and use this configuration.
|
|
225
|
+
|
|
226
|
+
${pc.bold("To verify:")}
|
|
227
|
+
1. Open Claude Code in this project directory
|
|
228
|
+
2. The MCP tools should be available automatically
|
|
229
|
+
3. Try asking Claude to "list plugins" or "get project info"
|
|
230
|
+
`);
|
|
231
|
+
} else if (ide === "cursor") {
|
|
232
|
+
console.log(pc.cyan("Cursor Setup:"));
|
|
233
|
+
console.log(pc.dim("─".repeat(40)));
|
|
234
|
+
console.log(`
|
|
235
|
+
${pc.bold("To configure Cursor:")}
|
|
236
|
+
1. Open Cursor Settings (Cmd/Ctrl + ,)
|
|
237
|
+
2. Search for "MCP" or "Model Context Protocol"
|
|
238
|
+
3. Add the donkeylabs server from .mcp.json
|
|
239
|
+
4. Restart Cursor to apply changes
|
|
240
|
+
`);
|
|
241
|
+
} else if (ide === "windsurf") {
|
|
242
|
+
console.log(pc.cyan("Windsurf Setup:"));
|
|
243
|
+
console.log(pc.dim("─".repeat(40)));
|
|
244
|
+
console.log(`
|
|
245
|
+
${pc.bold("To configure Windsurf:")}
|
|
246
|
+
1. Open Windsurf settings
|
|
247
|
+
2. Navigate to AI / MCP configuration
|
|
248
|
+
3. Add the donkeylabs server from .mcp.json
|
|
249
|
+
`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
161
252
|
|
|
162
|
-
|
|
163
|
-
|
|
253
|
+
// Print final success message
|
|
254
|
+
console.log(pc.bold(pc.green("\n🎉 Project ready!\n")));
|
|
255
|
+
|
|
256
|
+
if (projectType === "server") {
|
|
257
|
+
console.log(`${pc.bold("Start development:")}
|
|
258
|
+
${pc.cyan("cd " + (projectDir !== "." ? projectDir : ""))}
|
|
259
|
+
${pc.cyan("bun run dev")}
|
|
260
|
+
`);
|
|
261
|
+
} else {
|
|
262
|
+
console.log(`${pc.bold("Start development:")}
|
|
263
|
+
${projectDir !== "." ? pc.cyan("cd " + projectDir) + "\n " : ""}${pc.cyan("bun run dev")}
|
|
164
264
|
|
|
165
265
|
${pc.bold("Project structure:")}
|
|
166
|
-
src/server/
|
|
167
|
-
src/lib/api.ts
|
|
168
|
-
src/routes/
|
|
169
|
-
src/hooks.server.ts - Server hooks for SSR
|
|
266
|
+
src/server/ - @donkeylabs/server API
|
|
267
|
+
src/lib/api.ts - Typed API client
|
|
268
|
+
src/routes/ - SvelteKit pages
|
|
170
269
|
`);
|
|
171
270
|
}
|
|
271
|
+
|
|
272
|
+
if (setupMcp) {
|
|
273
|
+
console.log(pc.dim("MCP is configured. Your AI assistant can now help you build!"));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Run a command and return success status
|
|
279
|
+
*/
|
|
280
|
+
async function runCommand(cmd: string, args: string[], cwd: string): Promise<boolean> {
|
|
281
|
+
return new Promise((resolve) => {
|
|
282
|
+
const child = spawn(cmd, args, {
|
|
283
|
+
stdio: "inherit",
|
|
284
|
+
cwd,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
child.on("close", (code) => {
|
|
288
|
+
resolve(code === 0);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
child.on("error", () => {
|
|
292
|
+
resolve(false);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
172
295
|
}
|
|
173
296
|
|
|
174
297
|
/**
|
|
@@ -199,3 +322,31 @@ async function copyDirectory(src: string, dest: string): Promise<void> {
|
|
|
199
322
|
}
|
|
200
323
|
}
|
|
201
324
|
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Copy CLAUDE.md and docs/ from @donkeylabs/server to project root
|
|
328
|
+
* for AI-assisted development
|
|
329
|
+
*/
|
|
330
|
+
async function copyDocsFromServer(targetDir: string): Promise<void> {
|
|
331
|
+
const serverPkgPath = join(targetDir, "node_modules", "@donkeylabs", "server");
|
|
332
|
+
|
|
333
|
+
if (!existsSync(serverPkgPath)) {
|
|
334
|
+
return; // Server package not installed
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Copy CLAUDE.md
|
|
338
|
+
const claudeMdSrc = join(serverPkgPath, "CLAUDE.md");
|
|
339
|
+
if (existsSync(claudeMdSrc)) {
|
|
340
|
+
const claudeMdDest = join(targetDir, "CLAUDE.md");
|
|
341
|
+
await copyFile(claudeMdSrc, claudeMdDest);
|
|
342
|
+
console.log(pc.green(" Created:"), "CLAUDE.md (AI instructions)");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Copy docs/ directory
|
|
346
|
+
const docsSrc = join(serverPkgPath, "docs");
|
|
347
|
+
if (existsSync(docsSrc)) {
|
|
348
|
+
const docsDest = join(targetDir, "docs");
|
|
349
|
+
await copyDirectory(docsSrc, docsDest);
|
|
350
|
+
console.log(pc.green(" Created:"), "docs/ (detailed documentation)");
|
|
351
|
+
}
|
|
352
|
+
}
|