@exilonstudios/cli 1.0.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/dist/index.js ADDED
@@ -0,0 +1,1275 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import { intro, outro } from "@clack/prompts";
6
+ import chalk6 from "chalk";
7
+ import gradient from "gradient-string";
8
+
9
+ // src/commands/new.ts
10
+ import { select, text, confirm, multiselect, cancel } from "@clack/prompts";
11
+ import chalk from "chalk";
12
+ import fs from "fs-extra";
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ import { execa } from "execa";
16
+ import { Listr } from "listr2";
17
+ var __filename = fileURLToPath(import.meta.url);
18
+ var __dirname = path.dirname(__filename);
19
+ var PLUGIN_STARTERS = [
20
+ { value: "basic", label: "\u{1F4E6} Basic Plugin", hint: "Minimal plugin structure" },
21
+ { value: "blog", label: "\u{1F4DD} Blog Extension", hint: "Extend blog functionality" },
22
+ { value: "shop", label: "\u{1F6D2} Shop Integration", hint: "E-commerce integration" },
23
+ { value: "analytics", label: "\u{1F4CA} Analytics Provider", hint: "Custom analytics tracking" },
24
+ { value: "notification", label: "\u{1F514} Notification Channel", hint: "Custom notification channel" }
25
+ ];
26
+ var THEME_STARTERS = [
27
+ { value: "minimal", label: "\u{1F3A8} Minimal Theme", hint: "Clean, minimal design" },
28
+ { value: "blog", label: "\u{1F4F0} Blog Theme", hint: "Content-focused blog theme" },
29
+ { value: "gaming", label: "\u{1F3AE} Gaming Theme", hint: "Gaming server theme" },
30
+ { value: "portfolio", label: "\u{1F4BC} Portfolio Theme", hint: "Personal portfolio theme" }
31
+ ];
32
+ var PLUGIN_FEATURES = [
33
+ { value: "admin", label: "\u{1F527} Admin Panel", hint: "Backend admin interface" },
34
+ { value: "public", label: "\u{1F310} Public Pages", hint: "Frontend user-facing pages" },
35
+ { value: "api", label: "\u{1F50C} API Routes", hint: "REST API endpoints" },
36
+ { value: "models", label: "\u{1F4CA} Database Models", hint: "Eloquent models" },
37
+ { value: "middleware", label: "\u{1F6E1}\uFE0F Middleware", hint: "Custom middleware" },
38
+ { value: "commands", label: "\u2328\uFE0F Artisan Commands", hint: "Console commands" },
39
+ { value: "react", label: "\u269B\uFE0F React Components", hint: "React + Inertia pages" },
40
+ { value: "settings", label: "\u2699\uFE0F Settings Page", hint: "Configurable settings" }
41
+ ];
42
+ var THEME_FEATURES = [
43
+ { value: "templates", label: "\u{1F4C4} Blade Templates", hint: "Blade view templates" },
44
+ { value: "assets", label: "\u{1F3A8} CSS/JS Assets", hint: "Frontend assets" },
45
+ { value: "config", label: "\u2699\uFE0F Theme Config", hint: "Customizable theme options" },
46
+ { value: "widgets", label: "\u{1F9E9} Widget Areas", hint: "Dynamic widget areas" },
47
+ { value: "colors", label: "\u{1F3A8} Color Schemes", hint: "Multiple color schemes" }
48
+ ];
49
+ async function newCommand(typeArg) {
50
+ try {
51
+ let type;
52
+ if (typeArg && ["plugin", "theme"].includes(typeArg)) {
53
+ type = typeArg;
54
+ } else {
55
+ const typeSelect = await select({
56
+ message: "What do you want to create?",
57
+ options: [
58
+ { value: "plugin", label: "\u{1F50C} Plugin", hint: "Extend CMS functionality" },
59
+ { value: "theme", label: "\u{1F3A8} Theme", hint: "Customize the appearance" }
60
+ ]
61
+ });
62
+ if (typeof typeSelect !== "string") {
63
+ cancel("Operation cancelled");
64
+ process.exit(0);
65
+ }
66
+ type = typeSelect;
67
+ }
68
+ const name = await text({
69
+ message: `Project name (e.g., ${type === "plugin" ? "my-awesome-plugin" : "my-awesome-theme"}):`,
70
+ placeholder: type === "plugin" ? "my-awesome-plugin" : "my-awesome-theme",
71
+ validate: (value) => {
72
+ if (!value) return "Name is required";
73
+ if (!/^[a-z0-9-]+$/.test(value)) return "Use only lowercase letters, numbers, and hyphens";
74
+ if (value.length < 3) return "Name must be at least 3 characters";
75
+ }
76
+ });
77
+ if (typeof name !== "string") {
78
+ cancel("Operation cancelled");
79
+ process.exit(0);
80
+ }
81
+ const description = await text({
82
+ message: "Description:",
83
+ placeholder: `An amazing ${type} for ExilonCMS`
84
+ });
85
+ if (typeof description !== "string") {
86
+ cancel("Operation cancelled");
87
+ process.exit(0);
88
+ }
89
+ const author = await text({
90
+ message: "Author name:",
91
+ placeholder: "Your Name"
92
+ });
93
+ if (typeof author !== "string") {
94
+ cancel("Operation cancelled");
95
+ process.exit(0);
96
+ }
97
+ let namespace = "ExilonCMS\\Plugins\\" + toPascalCase(name);
98
+ if (type === "plugin") {
99
+ const namespaceInput = await text({
100
+ message: "PHP Namespace:",
101
+ placeholder: namespace,
102
+ initialValue: namespace
103
+ });
104
+ if (typeof namespaceInput !== "string") {
105
+ cancel("Operation cancelled");
106
+ process.exit(0);
107
+ }
108
+ namespace = namespaceInput;
109
+ }
110
+ const useStarter = await confirm({
111
+ message: `Use a starter ${type}?`,
112
+ initialValue: true
113
+ });
114
+ if (typeof useStarter !== "boolean") {
115
+ cancel("Operation cancelled");
116
+ process.exit(0);
117
+ }
118
+ let starter;
119
+ if (useStarter) {
120
+ const starters = type === "plugin" ? PLUGIN_STARTERS : THEME_STARTERS;
121
+ const starterSelect = await select({
122
+ message: `Choose a ${type} starter:`,
123
+ options: starters
124
+ });
125
+ if (typeof starterSelect !== "string") {
126
+ cancel("Operation cancelled");
127
+ process.exit(0);
128
+ }
129
+ starter = starterSelect;
130
+ }
131
+ const features = type === "plugin" ? PLUGIN_FEATURES : THEME_FEATURES;
132
+ const selectedFeatures = await multiselect({
133
+ message: `Select features (press space to select, enter to continue):`,
134
+ options: features,
135
+ required: false
136
+ });
137
+ if (!Array.isArray(selectedFeatures)) {
138
+ cancel("Operation cancelled");
139
+ process.exit(0);
140
+ }
141
+ const options = {
142
+ type,
143
+ name,
144
+ description: description || `An amazing ${type} for ExilonCMS`,
145
+ author: author || "Unknown",
146
+ version: "1.0.0",
147
+ license: "MIT",
148
+ useStarter,
149
+ starter,
150
+ features: selectedFeatures,
151
+ namespace
152
+ };
153
+ const shouldContinue = await confirm({
154
+ message: "Ready to create?",
155
+ initialValue: true
156
+ });
157
+ if (typeof shouldContinue !== "boolean" || !shouldContinue) {
158
+ cancel("Operation cancelled");
159
+ process.exit(0);
160
+ }
161
+ await createProject(options);
162
+ } catch (error) {
163
+ if (error instanceof Error && error.message !== "Operation cancelled") {
164
+ console.error(chalk.red("Error:"), error.message);
165
+ }
166
+ }
167
+ }
168
+ async function createProject(options) {
169
+ const tasks = new Listr([
170
+ {
171
+ title: "Creating project structure",
172
+ task: async () => {
173
+ const projectPath = path.join(process.cwd(), options.name);
174
+ await fs.ensureDir(projectPath);
175
+ if (options.type === "plugin") {
176
+ await createPluginStructure(projectPath, options);
177
+ } else {
178
+ await createThemeStructure(projectPath, options);
179
+ }
180
+ }
181
+ },
182
+ {
183
+ title: "Generating configuration files",
184
+ task: async () => {
185
+ const projectPath = path.join(process.cwd(), options.name);
186
+ await generateConfigFiles(projectPath, options);
187
+ }
188
+ },
189
+ {
190
+ title: "Creating source files",
191
+ task: async () => {
192
+ const projectPath = path.join(process.cwd(), options.name);
193
+ await generateSourceFiles(projectPath, options);
194
+ }
195
+ },
196
+ {
197
+ title: "Installing dependencies",
198
+ task: async () => {
199
+ const projectPath = path.join(process.cwd(), options.name);
200
+ if (options.type === "theme" && !options.features.includes("react")) {
201
+ return "Skipped (no npm dependencies)";
202
+ }
203
+ await execa("npm", ["install"], { cwd: projectPath });
204
+ }
205
+ },
206
+ {
207
+ title: "Initializing git repository",
208
+ task: async () => {
209
+ const projectPath = path.join(process.cwd(), options.name);
210
+ await execa("git", ["init"], { cwd: projectPath });
211
+ }
212
+ }
213
+ ]);
214
+ await tasks.run();
215
+ }
216
+ async function createPluginStructure(projectPath, options) {
217
+ const dirs = [
218
+ "src",
219
+ "src/Models",
220
+ "src/Http/Controllers",
221
+ "src/Http/Middleware",
222
+ "src/Http/Requests",
223
+ "database/migrations",
224
+ "routes",
225
+ "resources/views",
226
+ "resources/js",
227
+ "resources/css",
228
+ "config",
229
+ "tests"
230
+ ];
231
+ for (const dir of dirs) {
232
+ await fs.ensureDir(path.join(projectPath, dir));
233
+ }
234
+ }
235
+ async function createThemeStructure(projectPath, options) {
236
+ const dirs = [
237
+ "resources/views",
238
+ "resources/views/layouts",
239
+ "resources/views/components",
240
+ "resources/css",
241
+ "resources/js",
242
+ "config"
243
+ ];
244
+ for (const dir of dirs) {
245
+ await fs.ensureDir(path.join(projectPath, dir));
246
+ }
247
+ }
248
+ async function generateConfigFiles(projectPath, options) {
249
+ if (options.type === "plugin") {
250
+ const pluginJson = {
251
+ name: options.name,
252
+ version: options.version,
253
+ description: options.description,
254
+ author: options.author,
255
+ license: options.license,
256
+ type: "plugin",
257
+ namespace: options.namespace,
258
+ service_provider: `${options.namespace}\\${toPascalCase(options.name)}ServiceProvider`,
259
+ autoload: {
260
+ "psr-4": {
261
+ [options.namespace + "\\"]: "src/"
262
+ }
263
+ },
264
+ extras: {
265
+ "laravel": {
266
+ "providers": [
267
+ `${options.namespace}\\${toPascalCase(options.name)}ServiceProvider`
268
+ ]
269
+ }
270
+ }
271
+ };
272
+ await fs.writeJson(path.join(projectPath, "plugin.json"), pluginJson, { spaces: 2 });
273
+ }
274
+ if (options.type === "theme") {
275
+ const themeJson = {
276
+ name: options.name,
277
+ version: options.version,
278
+ description: options.description,
279
+ author: options.author,
280
+ license: options.license,
281
+ type: "theme",
282
+ screenshot: "screenshot.png",
283
+ supports: {
284
+ "exiloncms": "^1.0.0"
285
+ }
286
+ };
287
+ await fs.writeJson(path.join(projectPath, "theme.json"), themeJson, { spaces: 2 });
288
+ }
289
+ const packageJson = {
290
+ name: `exiloncms-${options.type}-${options.name}`,
291
+ version: options.version,
292
+ description: options.description,
293
+ author: options.author,
294
+ license: options.license,
295
+ private: true,
296
+ scripts: {
297
+ dev: "vite",
298
+ build: "vite build",
299
+ preview: "vite preview"
300
+ },
301
+ dependencies: {},
302
+ devDependencies: {
303
+ vite: "^5.0.0",
304
+ "@vitejs/plugin-react": "^4.2.0"
305
+ }
306
+ };
307
+ if (options.features.includes("react")) {
308
+ packageJson.dependencies = {
309
+ "react": "^18.2.0",
310
+ "react-dom": "^18.2.0",
311
+ "@inertiajs/react": "^1.0.0"
312
+ };
313
+ packageJson.devDependencies = {
314
+ ...packageJson.devDependencies,
315
+ "@types/react": "^18.2.0",
316
+ "@types/react-dom": "^18.2.0",
317
+ "typescript": "^5.0.0"
318
+ };
319
+ }
320
+ await fs.writeJson(path.join(projectPath, "package.json"), packageJson, { spaces: 2 });
321
+ if (options.type === "plugin") {
322
+ const composerJson = {
323
+ name: `exiloncms/${options.name}`,
324
+ description: options.description,
325
+ type: "exiloncms-plugin",
326
+ version: options.version,
327
+ license: options.license,
328
+ authors: [
329
+ { name: options.author }
330
+ ],
331
+ require: {
332
+ "php": "^8.2",
333
+ "laravel/framework": "^11.0"
334
+ },
335
+ autoload: {
336
+ "psr-4": {
337
+ [options.namespace + "\\"]: "src/"
338
+ }
339
+ },
340
+ "extra": {
341
+ "exiloncms": {
342
+ "name": options.name,
343
+ "namespace": options.namespace
344
+ }
345
+ },
346
+ minimumStability: "dev",
347
+ preferStable: true
348
+ };
349
+ await fs.writeJson(path.join(projectPath, "composer.json"), composerJson, { spaces: 2 });
350
+ }
351
+ }
352
+ async function generateSourceFiles(projectPath, options) {
353
+ if (options.type === "plugin") {
354
+ await generatePluginServiceProvider(projectPath, options);
355
+ await generatePluginRoutes(projectPath, options);
356
+ } else {
357
+ await generateThemeFiles(projectPath, options);
358
+ }
359
+ await generateReadme(projectPath, options);
360
+ await generateLicense(projectPath, options);
361
+ await generateGitignore(projectPath, options);
362
+ }
363
+ async function generatePluginServiceProvider(projectPath, options) {
364
+ const className = toPascalCase(options.name) + "ServiceProvider";
365
+ const content = `<?php
366
+
367
+ namespace ${options.namespace};
368
+
369
+ use Illuminate\\Support\\ServiceProvider;
370
+ use Illuminate\\Support\\Facades\\Route;
371
+
372
+ class ${className} extends ServiceProvider
373
+ {
374
+ /**
375
+ * Bootstrap any package services.
376
+ */
377
+ public function boot(): void
378
+ {
379
+ // Load routes
380
+ $this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
381
+ $this->loadRoutesFrom(__DIR__ . '/../routes/admin.php');
382
+
383
+ // Load views
384
+ $this->loadViewsFrom(__DIR__ . '/../resources/views', '${options.name}');
385
+
386
+ // Load migrations
387
+ $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
388
+
389
+ // Publish assets
390
+ $this->publishes([
391
+ __DIR__ . '/../resources/js' => public_path('vendor/${options.name}'),
392
+ ], ['${options.name}', '${options.name}-assets']);
393
+
394
+ // Publish config
395
+ if ($this->app->runningInConsole()) {
396
+ $this->publishes([
397
+ __DIR__ . '/../config/${options.name}.php' => config_path('${options.name}.php'),
398
+ ], '${options.name}-config');
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Register any package services.
404
+ */
405
+ public function register(): void
406
+ {
407
+ // Merge config
408
+ $this->mergeConfigFrom(
409
+ __DIR__ . '/../config/${options.name}.php',
410
+ '${options.name}'
411
+ );
412
+ }
413
+ }
414
+ `;
415
+ await fs.writeFile(path.join(projectPath, "src", className + ".php"), content);
416
+ }
417
+ async function generatePluginRoutes(projectPath, options) {
418
+ const webRoutes = `<?php
419
+
420
+ use Illuminate\\Support\\Facades\\Route;
421
+ use ${options.namespace}\\Http\\Controllers\\ExampleController;
422
+
423
+ // Public routes
424
+ Route::prefix('${options.name}')->group(function () {
425
+ Route::get('/', [ExampleController::class, 'index'])->name('${options.name}.index');
426
+ });
427
+ `;
428
+ await fs.writeFile(path.join(projectPath, "routes", "web.php"), webRoutes);
429
+ const adminRoutes = `<?php
430
+
431
+ use Illuminate\\Support\\Facades\\Route;
432
+ use ${options.namespace}\\Http\\Controllers\\Admin\\ExampleController;
433
+
434
+ // Admin routes
435
+ Route::prefix('admin/${options.name}')
436
+ ->middleware(['web', 'auth', 'admin'])
437
+ ->group(function () {
438
+ Route::get('/', [ExampleController::class, 'index'])->name('admin.${options.name}.index');
439
+ });
440
+ `;
441
+ await fs.writeFile(path.join(projectPath, "routes", "admin.php"), adminRoutes);
442
+ }
443
+ async function generateThemeFiles(projectPath, options) {
444
+ const layout = `<!DOCTYPE html>
445
+ <html lang="{{ app()->getLocale() }}">
446
+ <head>
447
+ <meta charset="utf-8">
448
+ <meta name="viewport" content="width=device-width, initial-scale=1">
449
+ <title>@yield('title') - {{ setting('name') }}</title>
450
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
451
+ </head>
452
+ <body class="antialiased">
453
+ @yield('content')
454
+
455
+ @stack('scripts')
456
+ </body>
457
+ </html>
458
+ `;
459
+ await fs.writeFile(path.join(projectPath, "resources/views/layouts/app.blade.php"), layout);
460
+ const home = `@extends('layouts.app')
461
+
462
+ @section('title', 'Home')
463
+
464
+ @section('content')
465
+ <div class="container mx-auto px-4 py-8">
466
+ <h1 class="text-4xl font-bold">Welcome to {{ $page->title ?? theme('name') }}</h1>
467
+ <p class="mt-4 text-gray-600">
468
+ {{ $page->content ?? 'This is the ' . theme('name') . ' theme.' }}
469
+ </p>
470
+ </div>
471
+ @endsection
472
+ `;
473
+ await fs.writeFile(path.join(projectPath, "resources/views/home.blade.php"), home);
474
+ }
475
+ async function generateReadme(projectPath, options) {
476
+ const content = `# ${toPascalCase(options.name)}
477
+
478
+ ${options.description}
479
+
480
+ ## Author
481
+ ${options.author}
482
+
483
+ ## Version
484
+ ${options.version}
485
+
486
+ ## License
487
+ ${options.license}
488
+
489
+ ## Installation
490
+
491
+ ### For Plugins
492
+ \`\`\`bash
493
+ composer require exiloncms/${options.name}
494
+ php artisan vendor:publish --tag="${options.name}"
495
+ php artisan migrate
496
+ \`\`\`
497
+
498
+ ### For Themes
499
+ \`\`\`bash
500
+ # Copy to themes directory
501
+ cp -r ${options.name} /path/to/cms/themes/${options.name}
502
+
503
+ # Or use the CLI
504
+ exiloncms install ${options.name}
505
+ \`\`\`
506
+
507
+ ## Usage
508
+
509
+ ${options.type === "plugin" ? '### Configuration\n\nPublish the config file:\n```bash\nphp artisan vendor:publish --tag="${options.name}-config"\n```' : "### Screenshot\n\n![Screenshot](screenshot.png)"}
510
+
511
+ ## Features
512
+
513
+ ${options.features.map((f) => `- ${f}`).join("\n")}
514
+
515
+ ## Development
516
+
517
+ \`\`\`bash
518
+ # Install dependencies
519
+ npm install
520
+
521
+ # Build assets
522
+ npm run build
523
+
524
+ # Watch for changes
525
+ npm run dev
526
+ \`\`\`
527
+
528
+ ## Support
529
+
530
+ For issues and feature requests, please create an issue on the repository.
531
+ `;
532
+ await fs.writeFile(path.join(projectPath, "README.md"), content);
533
+ }
534
+ async function generateLicense(projectPath, options) {
535
+ const year = (/* @__PURE__ */ new Date()).getFullYear();
536
+ const content = `MIT License
537
+
538
+ Copyright (c) ${year} ${options.author}
539
+
540
+ Permission is hereby granted, free of charge, to any person obtaining a copy
541
+ of this software and associated documentation files (the "Software"), to deal
542
+ in the Software without restriction, including without limitation the rights
543
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
544
+ copies of the Software, and to permit persons to whom the Software is
545
+ furnished to do so, subject to the following conditions:
546
+
547
+ The above copyright notice and this permission notice shall be included in all
548
+ copies or substantial portions of the Software.
549
+
550
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
551
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
552
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
553
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
554
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
555
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
556
+ SOFTWARE.
557
+ `;
558
+ await fs.writeFile(path.join(projectPath, "LICENSE"), content);
559
+ }
560
+ async function generateGitignore(projectPath, options) {
561
+ const content = `node_modules/
562
+ dist/
563
+ build/
564
+ vendor/
565
+ .env
566
+ .env.local
567
+ .DS_Store
568
+ *.log
569
+ coverage/
570
+ .nyc_output/
571
+ .cache/
572
+ `;
573
+ await fs.writeFile(path.join(projectPath, ".gitignore"), content);
574
+ }
575
+ function toPascalCase(str) {
576
+ return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
577
+ }
578
+
579
+ // src/commands/install.ts
580
+ import { select as select2, text as text2, confirm as confirm2, cancel as cancel2 } from "@clack/prompts";
581
+ import chalk2 from "chalk";
582
+ import fs2 from "fs-extra";
583
+ import path2 from "path";
584
+ import { execa as execa2 } from "execa";
585
+ import axios from "axios";
586
+ import { Listr as Listr2 } from "listr2";
587
+ var MARKETPLACE_API = "https://marketplace.exiloncms.fr/api/v1";
588
+ async function installCommand(nameArg) {
589
+ try {
590
+ let packageName;
591
+ if (nameArg) {
592
+ packageName = nameArg;
593
+ } else {
594
+ const searchTerm = await text2({
595
+ message: "Enter package name or search term:",
596
+ placeholder: "e.g., blog"
597
+ });
598
+ if (typeof searchTerm !== "string") {
599
+ cancel2("Operation cancelled");
600
+ process.exit(0);
601
+ }
602
+ const packages = await searchPackages(searchTerm);
603
+ if (packages.length === 0) {
604
+ console.log(chalk2.yellow("No packages found"));
605
+ return;
606
+ }
607
+ const selected = await select2({
608
+ message: "Select a package:",
609
+ options: packages.map((pkg) => ({
610
+ value: pkg.name,
611
+ label: pkg.name,
612
+ hint: pkg.description
613
+ }))
614
+ });
615
+ if (typeof selected !== "string") {
616
+ cancel2("Operation cancelled");
617
+ process.exit(0);
618
+ }
619
+ packageName = selected;
620
+ }
621
+ const packageInfo = await getPackageInfo(packageName);
622
+ console.log(chalk2.cyan("\n\u{1F4E6} Package Details:"));
623
+ console.log(chalk2.gray("\u2500".repeat(50)));
624
+ console.log(`${chalk2.bold("Name:")} ${packageInfo.name}`);
625
+ console.log(`${chalk2.bold("Version:")} ${packageInfo.version}`);
626
+ console.log(`${chalk2.bold("Description:")} ${packageInfo.description}`);
627
+ console.log(`${chalk2.bold("Author:")} ${packageInfo.author}`);
628
+ console.log(`${chalk2.bold("Type:")} ${packageInfo.type}`);
629
+ console.log(`${chalk2.bold("Requires:")} ${packageInfo.requires}`);
630
+ console.log(chalk2.gray("\u2500".repeat(50)));
631
+ const shouldInstall = await confirm2({
632
+ message: "Install this package?",
633
+ initialValue: true
634
+ });
635
+ if (typeof shouldInstall !== "boolean" || !shouldInstall) {
636
+ cancel2("Operation cancelled");
637
+ process.exit(0);
638
+ }
639
+ await installPackage(packageInfo);
640
+ } catch (error) {
641
+ if (error instanceof Error && error.message !== "Operation cancelled") {
642
+ console.error(chalk2.red("Error:"), error.message);
643
+ }
644
+ }
645
+ }
646
+ async function searchPackages(query) {
647
+ try {
648
+ const response = await axios.get(`${MARKETPLACE_API}/search`, {
649
+ params: { q: query }
650
+ });
651
+ return response.data.data || [];
652
+ } catch (error) {
653
+ console.log(chalk2.yellow("Note: Marketplace not available, searching local templates..."));
654
+ return getLocalTemplates();
655
+ }
656
+ }
657
+ async function getPackageInfo(name) {
658
+ try {
659
+ const response = await axios.get(`${MARKETPLACE_API}/packages/${name}`);
660
+ return response.data.data;
661
+ } catch (error) {
662
+ const local = getLocalTemplates().find((p) => p.name === name);
663
+ if (!local) {
664
+ throw new Error(`Package "${name}" not found`);
665
+ }
666
+ return local;
667
+ }
668
+ }
669
+ async function installPackage(packageInfo) {
670
+ const tasks = new Listr2([
671
+ {
672
+ title: "Downloading package",
673
+ task: async () => {
674
+ const downloadDir = path2.join(process.cwd(), ".exiloncms-temp");
675
+ await fs2.ensureDir(downloadDir);
676
+ const zipPath = path2.join(downloadDir, `${packageInfo.name}.zip`);
677
+ if (packageInfo.download_url) {
678
+ const response = await axios({
679
+ method: "GET",
680
+ url: packageInfo.download_url,
681
+ responseType: "stream"
682
+ });
683
+ const writer = fs2.createWriteStream(zipPath);
684
+ response.data.pipe(writer);
685
+ return new Promise((resolve, reject) => {
686
+ writer.on("finish", resolve);
687
+ writer.on("error", reject);
688
+ });
689
+ } else {
690
+ throw new Error("This package must be installed manually");
691
+ }
692
+ }
693
+ },
694
+ {
695
+ title: "Extracting package",
696
+ task: async () => {
697
+ const targetDir = packageInfo.type === "plugin" ? path2.join(process.cwd(), "plugins", packageInfo.name) : path2.join(process.cwd(), "themes", packageInfo.name);
698
+ await fs2.ensureDir(targetDir);
699
+ await execa2("unzip", ["-o", path2.join(process.cwd(), ".exiloncms-temp", `${packageInfo.name}.zip`), "-d", targetDir]);
700
+ }
701
+ },
702
+ {
703
+ title: "Installing dependencies",
704
+ task: async () => {
705
+ const packageDir = packageInfo.type === "plugin" ? path2.join(process.cwd(), "plugins", packageInfo.name) : path2.join(process.cwd(), "themes", packageInfo.name);
706
+ const composerJson = path2.join(packageDir, "composer.json");
707
+ const packageJson = path2.join(packageDir, "package.json");
708
+ if (await fs2.pathExists(composerJson)) {
709
+ await execa2("composer", ["install"], { cwd: packageDir });
710
+ }
711
+ if (await fs2.pathExists(packageJson)) {
712
+ await execa2("npm", ["install"], { cwd: packageDir });
713
+ }
714
+ }
715
+ },
716
+ {
717
+ title: "Running migrations",
718
+ task: async () => {
719
+ if (packageInfo.type === "plugin") {
720
+ await execa2("php", ["artisan", "migrate"], { cwd: process.cwd() });
721
+ }
722
+ }
723
+ },
724
+ {
725
+ title: "Cleaning up",
726
+ task: async () => {
727
+ await fs2.remove(path2.join(process.cwd(), ".exiloncms-temp"));
728
+ }
729
+ }
730
+ ]);
731
+ await tasks.run();
732
+ }
733
+ function getLocalTemplates() {
734
+ return [
735
+ {
736
+ name: "blog",
737
+ version: "1.0.0",
738
+ description: "Full-featured blog plugin",
739
+ author: "ExilonCMS",
740
+ type: "plugin",
741
+ download_url: "",
742
+ requires: ">=1.0.0"
743
+ },
744
+ {
745
+ name: "gaming-theme",
746
+ version: "1.0.0",
747
+ description: "Gaming server theme",
748
+ author: "ExilonCMS",
749
+ type: "theme",
750
+ download_url: "",
751
+ requires: ">=1.0.0"
752
+ }
753
+ ];
754
+ }
755
+
756
+ // src/commands/update.ts
757
+ import { confirm as confirm3, cancel as cancel3, multiselect as multiselect2 } from "@clack/prompts";
758
+ import chalk3 from "chalk";
759
+ import { execa as execa3 } from "execa";
760
+ import fs3 from "fs-extra";
761
+ import path3 from "path";
762
+ import axios2 from "axios";
763
+ import { Listr as Listr3 } from "listr2";
764
+ var MARKETPLACE_API2 = "https://marketplace.exiloncms.fr/api/v1";
765
+ async function updateCommand(options) {
766
+ try {
767
+ let updatesAvailable = [];
768
+ const tasks = new Listr3([
769
+ {
770
+ title: "Checking for updates",
771
+ task: async () => {
772
+ updatesAvailable = await checkForUpdates();
773
+ }
774
+ }
775
+ ]);
776
+ await tasks.run();
777
+ if (updatesAvailable.length === 0) {
778
+ console.log(chalk3.green("\u2713 Everything is up to date!"));
779
+ return;
780
+ }
781
+ console.log(chalk3.cyan("\n\u{1F4E6} Available Updates:"));
782
+ console.log(chalk3.gray("\u2500".repeat(60)));
783
+ updatesAvailable.forEach((update) => {
784
+ const icon = update.type === "core" ? "\u2699\uFE0F" : update.type === "plugin" ? "\u{1F50C}" : "\u{1F3A8}";
785
+ console.log(`${icon} ${chalk3.bold(update.name)}`);
786
+ console.log(` ${chalk3.gray(update.currentVersion)} \u2192 ${chalk3.green(update.latestVersion)}`);
787
+ console.log();
788
+ });
789
+ const selectedUpdates = await multiselect2({
790
+ message: "Select updates to install (space to select, enter to continue):",
791
+ options: updatesAvailable.map((update) => ({
792
+ value: update.name,
793
+ label: update.name,
794
+ hint: `${update.currentVersion} \u2192 ${update.latestVersion}`
795
+ })),
796
+ required: false
797
+ });
798
+ if (!Array.isArray(selectedUpdates)) {
799
+ cancel3("Operation cancelled");
800
+ process.exit(0);
801
+ }
802
+ if (selectedUpdates.length === 0) {
803
+ console.log(chalk3.yellow("No updates selected"));
804
+ return;
805
+ }
806
+ const shouldProceed = await confirm3({
807
+ message: `Install ${selectedUpdates.length} update(s)?`,
808
+ initialValue: true
809
+ });
810
+ if (typeof shouldProceed !== "boolean" || !shouldProceed) {
811
+ cancel3("Operation cancelled");
812
+ process.exit(0);
813
+ }
814
+ await installUpdates(updatesAvailable.filter((u) => selectedUpdates.includes(u.name)));
815
+ } catch (error) {
816
+ if (error instanceof Error && error.message !== "Operation cancelled") {
817
+ console.error(chalk3.red("Error:"), error.message);
818
+ }
819
+ }
820
+ }
821
+ async function checkForUpdates() {
822
+ const updates = [];
823
+ try {
824
+ const coreUpdate = await checkCoreUpdate();
825
+ if (coreUpdate) {
826
+ updates.push(coreUpdate);
827
+ }
828
+ } catch (error) {
829
+ }
830
+ try {
831
+ const pluginsDir = path3.join(process.cwd(), "plugins");
832
+ if (await fs3.pathExists(pluginsDir)) {
833
+ const plugins = await fs3.readdir(pluginsDir);
834
+ for (const plugin of plugins) {
835
+ const pluginJson = path3.join(pluginsDir, plugin, "plugin.json");
836
+ if (await fs3.pathExists(pluginJson)) {
837
+ const info = await fs3.readJson(pluginJson);
838
+ const update = await checkPackageUpdate(plugin, info.version, "plugin");
839
+ if (update) {
840
+ updates.push(update);
841
+ }
842
+ }
843
+ }
844
+ }
845
+ } catch (error) {
846
+ }
847
+ try {
848
+ const themesDir = path3.join(process.cwd(), "themes");
849
+ if (await fs3.pathExists(themesDir)) {
850
+ const themes = await fs3.readdir(themesDir);
851
+ for (const theme of themes) {
852
+ const themeJson = path3.join(themesDir, theme, "theme.json");
853
+ if (await fs3.pathExists(themeJson)) {
854
+ const info = await fs3.readJson(themeJson);
855
+ const update = await checkPackageUpdate(theme, info.version, "theme");
856
+ if (update) {
857
+ updates.push(update);
858
+ }
859
+ }
860
+ }
861
+ }
862
+ } catch (error) {
863
+ }
864
+ return updates;
865
+ }
866
+ async function checkCoreUpdate() {
867
+ try {
868
+ const response = await axios2.get(`${MARKETPLACE_API2}/core/latest`);
869
+ const latest = response.data.data;
870
+ const currentVersion = await getCurrentCoreVersion();
871
+ if (currentVersion !== latest.version) {
872
+ return {
873
+ name: "ExilonCMS Core",
874
+ currentVersion,
875
+ latestVersion: latest.version,
876
+ type: "core"
877
+ };
878
+ }
879
+ } catch (error) {
880
+ }
881
+ return null;
882
+ }
883
+ async function checkPackageUpdate(name, currentVersion, type) {
884
+ try {
885
+ const response = await axios2.get(`${MARKETPLACE_API2}/packages/${name}`);
886
+ const latest = response.data.data;
887
+ if (currentVersion !== latest.version) {
888
+ return {
889
+ name,
890
+ currentVersion,
891
+ latestVersion: latest.version,
892
+ type
893
+ };
894
+ }
895
+ } catch (error) {
896
+ }
897
+ return null;
898
+ }
899
+ async function getCurrentCoreVersion() {
900
+ try {
901
+ const composerJson = await fs3.readJson(path3.join(process.cwd(), "composer.json"));
902
+ const packageJson = await fs3.readJson(path3.join(process.cwd(), "package.json"));
903
+ return packageJson.version || "0.0.0";
904
+ } catch (error) {
905
+ return "0.0.0";
906
+ }
907
+ }
908
+ async function installUpdates(updates) {
909
+ for (const update of updates) {
910
+ const tasks = new Listr3([
911
+ {
912
+ title: `Updating ${update.name}`,
913
+ task: async () => {
914
+ if (update.type === "core") {
915
+ await updateCore(update);
916
+ } else {
917
+ await updatePackage(update);
918
+ }
919
+ }
920
+ }
921
+ ]);
922
+ await tasks.run();
923
+ }
924
+ }
925
+ async function updateCore(update) {
926
+ try {
927
+ await execa3("git", ["pull", "origin", "main"], { cwd: process.cwd() });
928
+ await execa3("composer", ["install"], { cwd: process.cwd() });
929
+ await execa3("npm", ["install"], { cwd: process.cwd() });
930
+ await execa3("php", ["artisan", "migrate", "--force"], { cwd: process.cwd() });
931
+ } catch (error) {
932
+ throw new Error("Failed to update core. Make sure you have git installed.");
933
+ }
934
+ }
935
+ async function updatePackage(update) {
936
+ const packageDir = update.type === "plugin" ? path3.join(process.cwd(), "plugins", update.name) : path3.join(process.cwd(), "themes", update.name);
937
+ if (!await fs3.pathExists(packageDir)) {
938
+ throw new Error(`${update.name} is not installed`);
939
+ }
940
+ console.log(chalk3.yellow(`Please update ${update.name} manually or reinstall from marketplace`));
941
+ }
942
+
943
+ // src/commands/list.ts
944
+ import { select as select3 } from "@clack/prompts";
945
+ import chalk4 from "chalk";
946
+ import fs4 from "fs-extra";
947
+ import path4 from "path";
948
+ async function listCommand() {
949
+ try {
950
+ const packages = await getInstalledPackages();
951
+ if (packages.length === 0) {
952
+ console.log(chalk4.yellow("No plugins or themes installed"));
953
+ return;
954
+ }
955
+ const filter = await select3({
956
+ message: "Show:",
957
+ options: [
958
+ { value: "all", label: "\u{1F4E6} All Packages" },
959
+ { value: "plugins", label: "\u{1F50C} Plugins Only" },
960
+ { value: "themes", label: "\u{1F3A8} Themes Only" }
961
+ ],
962
+ initialValue: "all"
963
+ });
964
+ if (typeof filter !== "string") {
965
+ return;
966
+ }
967
+ const filtered = filter === "all" ? packages : packages.filter((p) => p.type === (filter === "plugins" ? "plugin" : "theme"));
968
+ console.log(chalk4.cyan("\n\u{1F4E6} Installed Packages:"));
969
+ console.log(chalk4.gray("\u2500".repeat(70)));
970
+ const plugins = filtered.filter((p) => p.type === "plugin");
971
+ const themes = filtered.filter((p) => p.type === "theme");
972
+ if (plugins.length > 0 && (filter === "all" || filter === "plugins")) {
973
+ console.log(chalk4.bold("\n\u{1F50C} Plugins:"));
974
+ plugins.forEach((pkg) => {
975
+ const status = pkg.enabled ? chalk4.green("\u2713 Enabled") : chalk4.gray("\u2717 Disabled");
976
+ console.log(`
977
+ ${chalk4.bold(pkg.name)} ${status}`);
978
+ console.log(` ${chalk4.gray("Version:")} ${pkg.version}`);
979
+ console.log(` ${chalk4.gray("Description:")} ${pkg.description}`);
980
+ console.log(` ${chalk4.gray("Author:")} ${pkg.author}`);
981
+ });
982
+ }
983
+ if (themes.length > 0 && (filter === "all" || filter === "themes")) {
984
+ console.log(chalk4.bold("\n\u{1F3A8} Themes:"));
985
+ themes.forEach((pkg) => {
986
+ const active = pkg.enabled ? chalk4.green("\u2713 Active") : chalk4.gray("\u2717 Inactive");
987
+ console.log(`
988
+ ${chalk4.bold(pkg.name)} ${active}`);
989
+ console.log(` ${chalk4.gray("Version:")} ${pkg.version}`);
990
+ console.log(` ${chalk4.gray("Description:")} ${pkg.description}`);
991
+ console.log(` ${chalk4.gray("Author:")} ${pkg.author}`);
992
+ });
993
+ }
994
+ console.log(chalk4.gray("\n" + "\u2500".repeat(70)));
995
+ console.log(chalk4.gray(`Total: ${filtered.length} package(s)`));
996
+ } catch (error) {
997
+ if (error instanceof Error) {
998
+ console.error(chalk4.red("Error:"), error.message);
999
+ }
1000
+ }
1001
+ }
1002
+ async function getInstalledPackages() {
1003
+ const packages = [];
1004
+ const pluginsDir = path4.join(process.cwd(), "plugins");
1005
+ if (await fs4.pathExists(pluginsDir)) {
1006
+ const plugins = await fs4.readdir(pluginsDir);
1007
+ for (const plugin of plugins) {
1008
+ const pluginJson = path4.join(pluginsDir, plugin, "plugin.json");
1009
+ if (await fs4.pathExists(pluginJson)) {
1010
+ try {
1011
+ const info = await fs4.readJson(pluginJson);
1012
+ packages.push({
1013
+ name: info.name || plugin,
1014
+ version: info.version || "unknown",
1015
+ description: info.description || "",
1016
+ author: info.author || "Unknown",
1017
+ type: "plugin",
1018
+ enabled: true
1019
+ // Would check from database
1020
+ });
1021
+ } catch (error) {
1022
+ }
1023
+ }
1024
+ }
1025
+ }
1026
+ const themesDir = path4.join(process.cwd(), "themes");
1027
+ if (await fs4.pathExists(themesDir)) {
1028
+ const themes = await fs4.readdir(themesDir);
1029
+ for (const theme of themes) {
1030
+ const themeJson = path4.join(themesDir, theme, "theme.json");
1031
+ if (await fs4.pathExists(themeJson)) {
1032
+ try {
1033
+ const info = await fs4.readJson(themeJson);
1034
+ packages.push({
1035
+ name: info.name || theme,
1036
+ version: info.version || "unknown",
1037
+ description: info.description || "",
1038
+ author: info.author || "Unknown",
1039
+ type: "theme",
1040
+ enabled: false
1041
+ // Would check from database
1042
+ });
1043
+ } catch (error) {
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ return packages;
1049
+ }
1050
+
1051
+ // src/commands/build.ts
1052
+ import chalk5 from "chalk";
1053
+ import fs5 from "fs-extra";
1054
+ import path5 from "path";
1055
+ import { execa as execa4 } from "execa";
1056
+ import archiver from "archiver";
1057
+ import { Listr as Listr4 } from "listr2";
1058
+ async function buildCommand(options) {
1059
+ try {
1060
+ const packageType = await detectPackageType();
1061
+ if (!packageType) {
1062
+ console.log(chalk5.yellow("Not a valid plugin or theme directory"));
1063
+ console.log(chalk5.gray("Run this command from within a plugin/theme directory"));
1064
+ return;
1065
+ }
1066
+ const packageInfo = await getPackageInfo2();
1067
+ if (!packageInfo) {
1068
+ console.log(chalk5.yellow("Could not find package.json or plugin.json/theme.json"));
1069
+ return;
1070
+ }
1071
+ console.log(chalk5.cyan(`
1072
+ \u{1F528} Building ${packageType}: ${packageInfo.name}`));
1073
+ console.log(chalk5.gray(`Version: ${packageInfo.version}`));
1074
+ console.log();
1075
+ const tasks = new Listr4([
1076
+ {
1077
+ title: "Installing dependencies",
1078
+ task: async () => {
1079
+ const packageJsonPath = path5.join(process.cwd(), "package.json");
1080
+ if (await fs5.pathExists(packageJsonPath)) {
1081
+ await execa4("npm", ["install"], { cwd: process.cwd() });
1082
+ } else {
1083
+ return "Skipped (no package.json)";
1084
+ }
1085
+ }
1086
+ },
1087
+ {
1088
+ title: "Building assets",
1089
+ task: async () => {
1090
+ const packageJsonPath = path5.join(process.cwd(), "package.json");
1091
+ if (await fs5.pathExists(packageJsonPath)) {
1092
+ const packageJson = await fs5.readJson(packageJsonPath);
1093
+ if (packageJson.scripts?.build) {
1094
+ await execa4("npm", ["run", "build"], { cwd: process.cwd() });
1095
+ } else {
1096
+ return "Skipped (no build script)";
1097
+ }
1098
+ } else {
1099
+ return "Skipped (no package.json)";
1100
+ }
1101
+ }
1102
+ },
1103
+ {
1104
+ title: "Creating distribution package",
1105
+ task: async () => {
1106
+ await createDistPackage(packageInfo, packageType, options.format);
1107
+ }
1108
+ }
1109
+ ]);
1110
+ await tasks.run();
1111
+ console.log(chalk5.green("\n\u2713 Build complete!"));
1112
+ console.log(chalk5.gray(`Package created: dist/${packageInfo.name}-${packageInfo.version}.${options.format}`));
1113
+ } catch (error) {
1114
+ if (error instanceof Error && error.message !== "Operation cancelled") {
1115
+ console.error(chalk5.red("Error:"), error.message);
1116
+ }
1117
+ }
1118
+ }
1119
+ async function detectPackageType() {
1120
+ const pluginJson = path5.join(process.cwd(), "plugin.json");
1121
+ const themeJson = path5.join(process.cwd(), "theme.json");
1122
+ if (await fs5.pathExists(pluginJson)) {
1123
+ return "plugin";
1124
+ }
1125
+ if (await fs5.pathExists(themeJson)) {
1126
+ return "theme";
1127
+ }
1128
+ return null;
1129
+ }
1130
+ async function getPackageInfo2() {
1131
+ const pluginJson = path5.join(process.cwd(), "plugin.json");
1132
+ const themeJson = path5.join(process.cwd(), "theme.json");
1133
+ const packageJson = path5.join(process.cwd(), "package.json");
1134
+ let info = {};
1135
+ if (await fs5.pathExists(pluginJson)) {
1136
+ info = { ...info, ...await fs5.readJson(pluginJson) };
1137
+ }
1138
+ if (await fs5.pathExists(themeJson)) {
1139
+ info = { ...info, ...await fs5.readJson(themeJson) };
1140
+ }
1141
+ if (await fs5.pathExists(packageJson)) {
1142
+ const pkg = await fs5.readJson(packageJson);
1143
+ info = {
1144
+ ...info,
1145
+ name: info.name || pkg.name,
1146
+ version: info.version || pkg.version,
1147
+ description: info.description || pkg.description,
1148
+ author: info.author || pkg.author
1149
+ };
1150
+ }
1151
+ if (!info.name || !info.version) {
1152
+ return null;
1153
+ }
1154
+ return info;
1155
+ }
1156
+ async function createDistPackage(packageInfo, packageType, format) {
1157
+ await fs5.ensureDir(path5.join(process.cwd(), "dist"));
1158
+ const outputFileName = `${packageInfo.name}-${packageInfo.version}.${format}`;
1159
+ const outputPath = path5.join(process.cwd(), "dist", outputFileName);
1160
+ const exclude = [
1161
+ "node_modules",
1162
+ "dist",
1163
+ ".git",
1164
+ ".gitignore",
1165
+ "coverage",
1166
+ ".env",
1167
+ ".env.local",
1168
+ "*.log",
1169
+ ".DS_Store",
1170
+ "Thumbs.db"
1171
+ ];
1172
+ if (format === "zip") {
1173
+ await createZip(outputPath, exclude);
1174
+ } else {
1175
+ await createTar(outputPath, exclude);
1176
+ }
1177
+ }
1178
+ async function createZip(outputPath, exclude) {
1179
+ return new Promise((resolve, reject) => {
1180
+ const output = fs5.createWriteStream(outputPath);
1181
+ const archive = archiver("zip", { zlib: { level: 9 } });
1182
+ output.on("close", () => resolve(null));
1183
+ archive.on("error", (err) => reject(err));
1184
+ archive.pipe(output);
1185
+ const rootPath = process.cwd();
1186
+ archive.directory(rootPath, false, {
1187
+ filter: (entry) => {
1188
+ const relativePath = path5.relative(rootPath, entry.path);
1189
+ const parts = relativePath.split(path5.sep);
1190
+ for (const excluded of exclude) {
1191
+ if (parts.some((p) => p === excluded.replace("*", ""))) {
1192
+ return false;
1193
+ }
1194
+ if (relativePath.startsWith(excluded)) {
1195
+ return false;
1196
+ }
1197
+ }
1198
+ return true;
1199
+ }
1200
+ });
1201
+ archive.finalize();
1202
+ });
1203
+ }
1204
+ async function createTar(outputPath, exclude) {
1205
+ try {
1206
+ const excludeArgs = exclude.flatMap((e) => ["--exclude", e]);
1207
+ await execa4("tar", [
1208
+ "-czf",
1209
+ outputPath,
1210
+ ...excludeArgs,
1211
+ "."
1212
+ ], { cwd: process.cwd() });
1213
+ } catch (error) {
1214
+ throw new Error("tar command not available. Please use --format zip instead.");
1215
+ }
1216
+ }
1217
+
1218
+ // src/index.ts
1219
+ var VERSION = "1.0.0";
1220
+ function showBanner() {
1221
+ const banner = gradient("cyan", "magenta")(`
1222
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1223
+ \u2551 \u2551
1224
+ \u2551 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2551
1225
+ \u2551 \u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2551
1226
+ \u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2551
1227
+ \u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2551
1228
+ \u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2551
1229
+ \u2551 \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u2551
1230
+ \u2551 \u2551
1231
+ \u2551 Developer CLI v${VERSION} \u2551
1232
+ \u2551 \u2551
1233
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1234
+ `);
1235
+ console.log(banner);
1236
+ }
1237
+ var program = new Command();
1238
+ program.name("exiloncms").description(chalk6.cyan("ExilonCMS Developer CLI - Create plugins and themes with ease")).version(VERSION);
1239
+ program.command("new").description("Create a new plugin or theme").argument("[type]", "Type of project (plugin or theme)").action(async (type) => {
1240
+ intro(chalk6.cyan("\u{1F680} ExilonCMS CLI"));
1241
+ showBanner();
1242
+ await newCommand(type);
1243
+ outro(chalk6.green("\u2713 Done!"));
1244
+ });
1245
+ program.command("install").description("Install a plugin or theme from marketplace").argument("[name]", "Package name").action(async (name) => {
1246
+ intro(chalk6.cyan("\u{1F4E6} ExilonCMS Package Manager"));
1247
+ showBanner();
1248
+ await installCommand(name);
1249
+ outro(chalk6.green("\u2713 Package installed!"));
1250
+ });
1251
+ program.command("update").description("Update ExilonCMS core or packages").option("--core", "Update CMS core only").option("--packages", "Update installed packages only").action(async (options) => {
1252
+ intro(chalk6.cyan("\u{1F504} ExilonCMS Updater"));
1253
+ showBanner();
1254
+ await updateCommand(options);
1255
+ outro(chalk6.green("\u2713 Update complete!"));
1256
+ });
1257
+ program.command("list").description("List installed plugins and themes").action(async () => {
1258
+ intro(chalk6.cyan("\u{1F4CB} ExilonCMS Packages"));
1259
+ showBanner();
1260
+ await listCommand();
1261
+ });
1262
+ program.command("build").description("Build a plugin or theme for distribution").option("--format <format>", "Output format (zip|tar)", "zip").action(async (options) => {
1263
+ intro(chalk6.cyan("\u{1F528} ExilonCMS Builder"));
1264
+ showBanner();
1265
+ await buildCommand(options);
1266
+ outro(chalk6.green("\u2713 Build complete!"));
1267
+ });
1268
+ program.parse();
1269
+ if (!process.argv.slice(2).length) {
1270
+ intro(chalk6.cyan("\u{1F680} ExilonCMS CLI"));
1271
+ showBanner();
1272
+ program.outputHelp();
1273
+ outro(chalk6.gray('Tip: Run "exiloncms new" to create a new plugin or theme'));
1274
+ }
1275
+ //# sourceMappingURL=index.js.map