@djangocfg/ext-base 1.0.0 → 1.0.1

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 CHANGED
@@ -6,17 +6,16 @@ Base utilities and common code for building DjangoCFG extensions.
6
6
 
7
7
  ## What is this?
8
8
 
9
- `@djangocfg/ext-base` is a foundation library for DjangoCFG extensions. It provides:
9
+ `@djangocfg/ext-base` provides the foundation for DjangoCFG extensions with:
10
10
 
11
- - **Extension registration system** - automatic extension metadata tracking
12
- - **Common React hooks** - pagination, infinite scroll, data fetching patterns
13
- - **Environment utilities** - isDevelopment, isProduction, isStaticBuild helpers
14
- - **Type-safe context helpers** - factory functions for creating contexts
15
- - **Logger utilities** - structured logging with tags
16
- - **Auth integration** - convenient re-exports of useAuth
17
- - **TypeScript types** - shared types for all extensions
11
+ - Extension registration system and metadata tracking
12
+ - React hooks for pagination, infinite scroll, and data fetching
13
+ - Environment utilities (isDevelopment, isProduction, isStaticBuild)
14
+ - Type-safe context helpers and logger utilities
15
+ - Auth integration and API factory
16
+ - **CLI tool** for managing extensions
18
17
 
19
- This package is used internally by all official DjangoCFG extensions (newsletter, payments, support, leads, knowbase) and can be used to build your own custom extensions.
18
+ Used internally by official extensions (newsletter, payments, support, leads, knowbase) and for building custom extensions.
20
19
 
21
20
  ## Install
22
21
 
@@ -24,6 +23,35 @@ This package is used internally by all official DjangoCFG extensions (newsletter
24
23
  pnpm add @djangocfg/ext-base
25
24
  ```
26
25
 
26
+ ## CLI Usage
27
+
28
+ The package includes a CLI tool for managing DjangoCFG extensions:
29
+
30
+ ```bash
31
+ # List available extensions
32
+ djangocfg-ext list
33
+
34
+ # Show extension info
35
+ djangocfg-ext info ext-leads
36
+
37
+ # Interactive installation wizard
38
+ djangocfg-ext install
39
+
40
+ # Initialize extension in your project
41
+ djangocfg-ext init
42
+
43
+ # Show help
44
+ djangocfg-ext help
45
+ ```
46
+
47
+ ### Available Extensions
48
+
49
+ - **@djangocfg/ext-leads** - Lead management and contact forms
50
+ - **@djangocfg/ext-payments** - Payment processing with multiple providers
51
+ - **@djangocfg/ext-newsletter** - Newsletter and email campaigns
52
+ - **@djangocfg/ext-support** - Customer support and ticketing
53
+ - **@djangocfg/ext-knowbase** - Knowledge base and documentation
54
+
27
55
  ## Quick Start
28
56
 
29
57
  ### 1. Create extension metadata
@@ -35,16 +63,13 @@ import type { ExtensionMetadata } from '@djangocfg/ext-base';
35
63
  export const extensionConfig: ExtensionMetadata = {
36
64
  name: 'my-extension',
37
65
  version: '1.0.0',
38
- author: 'Your Name',
39
66
  displayName: 'My Extension',
40
67
  description: 'Amazing extension functionality',
41
68
  icon: '🚀',
42
- license: 'MIT',
43
- keywords: ['extension', 'feature'],
44
69
  };
45
70
  ```
46
71
 
47
- ### 2. Create your extension provider
72
+ ### 2. Create extension provider
48
73
 
49
74
  ```typescript
50
75
  // src/contexts/MyExtensionProvider.tsx
@@ -56,7 +81,6 @@ import { extensionConfig } from '../config';
56
81
  export function MyExtensionProvider({ children }) {
57
82
  return (
58
83
  <ExtensionProvider metadata={extensionConfig}>
59
- {/* Your extension contexts here */}
60
84
  {children}
61
85
  </ExtensionProvider>
62
86
  );
@@ -77,60 +101,27 @@ export default function RootLayout({ children }) {
77
101
  }
78
102
  ```
79
103
 
80
- The extension will automatically register itself and log in development mode:
81
- ```
82
- [ext-base] Extension registered: My Extension v1.0.0
83
- ```
84
-
85
104
  ## Core Features
86
105
 
87
106
  ### Environment Configuration
88
107
 
89
108
  ```typescript
90
- import { isDevelopment, isProduction, isStaticBuild, isClient, isServer } from '@djangocfg/ext-base';
109
+ import { isDevelopment, isProduction, isStaticBuild } from '@djangocfg/ext-base';
91
110
 
92
111
  if (isDevelopment) {
93
112
  console.log('Running in development mode');
94
113
  }
95
-
96
- if (isStaticBuild) {
97
- // Special handling for Next.js static export
98
- }
99
114
  ```
100
115
 
101
116
  ### API Factory
102
117
 
103
- Create extension API instances with shared authentication in one line:
118
+ Create extension API instances with automatic configuration:
104
119
 
105
- ```typescript
106
- // src/api/index.ts
107
- import { API } from './generated/ext_myextension';
108
- import { createExtensionAPI } from '@djangocfg/ext-base/api';
109
-
110
- // That's it! All configuration is handled automatically:
111
- // - API URL from environment
112
- // - Static build detection
113
- // - Shared authentication storage from @djangocfg/api
114
- export const apiMyExtension = createExtensionAPI(API);
115
- ```
116
-
117
- **Before (manual setup):**
118
- ```typescript
119
- import { API } from './generated/ext_myextension';
120
- import { api as accountsApi } from '@djangocfg/api';
121
-
122
- const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
123
- const apiUrl = isStaticBuild ? '' : process.env.NEXT_PUBLIC_API_URL || '';
124
- const storage = (accountsApi as any)._storage;
125
-
126
- export const apiMyExtension = new API(apiUrl, { storage });
127
- ```
128
-
129
- **After (with factory):**
130
120
  ```typescript
131
121
  import { API } from './generated/ext_myextension';
132
122
  import { createExtensionAPI } from '@djangocfg/ext-base/api';
133
123
 
124
+ // Handles API URL, static build detection, and shared auth automatically
134
125
  export const apiMyExtension = createExtensionAPI(API);
135
126
  ```
136
127
 
@@ -140,23 +131,19 @@ export const apiMyExtension = createExtensionAPI(API);
140
131
  import { usePagination, useInfinitePagination } from '@djangocfg/ext-base/hooks';
141
132
 
142
133
  // Standard pagination
143
- const { items, page, pageSize, totalPages, goToPage, nextPage, prevPage } = usePagination({
134
+ const { items, page, totalPages, goToPage, nextPage, prevPage } = usePagination({
144
135
  keyPrefix: 'articles',
145
136
  fetcher: async (page, pageSize) => {
146
137
  const response = await api.articles.list({ page, page_size: pageSize });
147
138
  return response.data;
148
139
  },
149
- initialPage: 1,
150
140
  pageSize: 20,
151
141
  });
152
142
 
153
143
  // Infinite scroll
154
144
  const { items, isLoading, hasMore, loadMore } = useInfinitePagination({
155
145
  keyPrefix: 'articles',
156
- fetcher: async (page, pageSize) => {
157
- const response = await api.articles.list({ page, page_size: pageSize });
158
- return response.data;
159
- },
146
+ fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }).then(r => r.data),
160
147
  pageSize: 20,
161
148
  });
162
149
  ```
@@ -175,12 +162,6 @@ const { Provider, useContext: useMyContext } = createExtensionContext<MyContextV
175
162
  displayName: 'MyContext',
176
163
  errorMessage: 'useMyContext must be used within MyProvider',
177
164
  });
178
-
179
- // In components
180
- export function MyComponent() {
181
- const { data, refresh } = useMyContext();
182
- return <div>{data.length} items</div>;
183
- }
184
165
  ```
185
166
 
186
167
  ### Logger
@@ -188,14 +169,11 @@ export function MyComponent() {
188
169
  ```typescript
189
170
  import { createExtensionLogger } from '@djangocfg/ext-base';
190
171
 
191
- const logger = createExtensionLogger({
192
- tag: 'my-extension',
193
- level: 'info',
194
- });
172
+ const logger = createExtensionLogger({ tag: 'my-extension' });
195
173
 
196
174
  logger.info('Extension initialized');
197
- logger.error('Something went wrong', error);
198
- logger.success('Operation completed!');
175
+ logger.error('Operation failed', error);
176
+ logger.success('Completed!');
199
177
  ```
200
178
 
201
179
  ### Auth Integration
@@ -206,135 +184,45 @@ import { useAuth } from '@djangocfg/ext-base/auth';
206
184
  function MyComponent() {
207
185
  const { user, isAuthenticated, login, logout } = useAuth();
208
186
 
209
- if (!isAuthenticated) {
210
- return <button onClick={login}>Login</button>;
211
- }
212
-
213
- return <div>Welcome, {user?.email}</div>;
214
- }
215
- ```
216
-
217
- ### Error Handling
218
-
219
- ```typescript
220
- import { handleError, formatError } from '@djangocfg/ext-base';
221
-
222
- try {
223
- await someOperation();
224
- } catch (error) {
225
- handleError(error, (formattedError) => {
226
- logger.error('Operation failed:', formattedError);
227
- });
187
+ return isAuthenticated ? (
188
+ <div>Welcome, {user?.email}</div>
189
+ ) : (
190
+ <button onClick={login}>Login</button>
191
+ );
228
192
  }
229
193
  ```
230
194
 
231
195
  ## Package Exports
232
196
 
233
- ### Main entry (`@djangocfg/ext-base`)
234
- Server-safe exports - can be used in both client and server components:
235
- - All TypeScript types
236
- - Environment configuration (isDevelopment, isProduction, etc.)
237
- - API factory (createExtensionAPI)
238
- - Logger utilities
239
- - Error handling utilities
240
-
241
- ### Hooks entry (`@djangocfg/ext-base/hooks`)
242
- Client-only exports:
243
- - `ExtensionProvider` - main provider component
244
- - `usePagination` - standard pagination hook
245
- - `useInfinitePagination` - infinite scroll hook
246
- - `createExtensionContext` - context factory
247
-
248
- ### Auth entry (`@djangocfg/ext-base/auth`)
249
- Auth re-exports:
250
- - `useAuth` - authentication hook
251
- - `UserProfile` type
252
- - `AuthContextType` type
253
-
254
- ### API entry (`@djangocfg/ext-base/api`)
255
- API utilities:
256
- - `createExtensionAPI` - API instance factory
257
- - `getSharedAuthStorage` - Get shared auth storage
197
+ | Export | Description | Usage |
198
+ |--------|-------------|-------|
199
+ | `@djangocfg/ext-base` | Server-safe exports (types, environment, API factory, logger, error handling) | Server & client components |
200
+ | `@djangocfg/ext-base/hooks` | Client-only exports (ExtensionProvider, pagination hooks, context factory) | Client components only |
201
+ | `@djangocfg/ext-base/auth` | Auth re-exports (useAuth, types) | Client components only |
202
+ | `@djangocfg/ext-base/api` | API utilities (createExtensionAPI, getSharedAuthStorage) | Server & client components |
258
203
 
259
204
  ## TypeScript Types
260
205
 
261
206
  ```typescript
262
207
  import type {
263
- // Extension metadata
264
208
  ExtensionMetadata,
265
209
  ExtensionProviderProps,
266
-
267
- // Pagination
268
210
  PaginatedResponse,
269
211
  PaginationParams,
270
212
  PaginationState,
271
213
  InfinitePaginationReturn,
272
-
273
- // Logger
274
214
  ExtensionLogger,
275
- LoggerOptions,
276
-
277
- // Errors
278
215
  ExtensionError,
279
- ErrorHandler,
280
-
281
- // Context
282
- ExtensionContextOptions,
283
216
  } from '@djangocfg/ext-base';
284
217
  ```
285
218
 
286
- ## Example: Complete Extension
287
-
288
- ```typescript
289
- // config.ts
290
- export const extensionConfig: ExtensionMetadata = {
291
- name: 'blog',
292
- version: '1.0.0',
293
- author: 'Your Company',
294
- displayName: 'Blog',
295
- description: 'Blog management system',
296
- icon: '📝',
297
- keywords: ['blog', 'articles'],
298
- };
299
-
300
- // contexts/BlogProvider.tsx
301
- 'use client';
302
-
303
- import { ExtensionProvider } from '@djangocfg/ext-base/hooks';
304
- import { useInfinitePagination } from '@djangocfg/ext-base/hooks';
305
- import { createExtensionLogger } from '@djangocfg/ext-base';
306
- import { extensionConfig } from '../config';
307
-
308
- const logger = createExtensionLogger({ tag: 'blog' });
309
-
310
- export function BlogProvider({ children }) {
311
- const { items, loadMore, hasMore } = useInfinitePagination({
312
- keyPrefix: 'blog-articles',
313
- fetcher: async (page, pageSize) => {
314
- const response = await api.articles.list({ page, page_size: pageSize });
315
- return response.data;
316
- },
317
- });
318
-
319
- logger.info('Blog provider initialized with', items.length, 'articles');
320
-
321
- return (
322
- <ExtensionProvider metadata={extensionConfig}>
323
- {/* Your contexts here */}
324
- {children}
325
- </ExtensionProvider>
326
- );
327
- }
328
- ```
329
-
330
219
  ## Best Practices
331
220
 
332
- 1. **Always use ExtensionProvider** - Wrap your extension with it for proper registration
333
- 2. **Define metadata** - Create a `config.ts` file with complete metadata
334
- 3. **Use provided hooks** - Leverage `usePagination` and `useInfinitePagination`
335
- 4. **Structured logging** - Use `createExtensionLogger` with consistent tags
336
- 5. **Type safety** - Import types from `@djangocfg/ext-base`
337
- 6. **Separate entry points** - Use `/hooks` for client code, main entry for server-safe code
221
+ - Always wrap your extension with `ExtensionProvider` for proper registration
222
+ - Define complete metadata in `config.ts`
223
+ - Use provided pagination hooks for consistent data fetching
224
+ - Use `createExtensionLogger` with consistent tags for structured logging
225
+ - Separate client-only code using `/hooks` entry point
338
226
 
339
227
  ## License
340
228
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ext-base",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Base utilities and common code for DjangoCFG extensions",
5
5
  "keywords": [
6
6
  "django",
@@ -55,26 +55,40 @@
55
55
  "dist",
56
56
  "src"
57
57
  ],
58
+ "bin": {
59
+ "djangocfg-ext": "./dist/cli.mjs"
60
+ },
58
61
  "scripts": {
59
62
  "build": "tsup",
60
63
  "dev": "tsup --watch",
61
- "check": "tsc --noEmit"
64
+ "check": "tsc --noEmit",
65
+ "cli": "tsx src/cli/index.ts",
66
+ "cli:help": "tsx src/cli/index.ts --help",
67
+ "cli:list": "tsx src/cli/index.ts list",
68
+ "cli:info": "tsx src/cli/index.ts info"
62
69
  },
63
70
  "peerDependencies": {
64
- "@djangocfg/api": "^2.1.14",
71
+ "@djangocfg/api": "^2.1.15",
65
72
  "consola": "^3.4.2",
66
73
  "react": "^18 || ^19",
67
74
  "react-dom": "^18 || ^19",
68
75
  "swr": "^2.3.7"
69
76
  },
77
+ "dependencies": {
78
+ "chalk": "^5.3.0",
79
+ "consola": "^3.4.2",
80
+ "picocolors": "^1.1.1",
81
+ "prompts": "^2.4.2"
82
+ },
70
83
  "devDependencies": {
71
- "@djangocfg/api": "^2.1.14",
72
- "@djangocfg/typescript-config": "^2.1.14",
84
+ "@djangocfg/api": "^2.1.15",
85
+ "@djangocfg/typescript-config": "^2.1.15",
73
86
  "@types/node": "^24.7.2",
87
+ "@types/prompts": "^2.4.9",
74
88
  "@types/react": "^19.0.0",
75
- "consola": "^3.4.2",
76
89
  "swr": "^2.3.7",
77
90
  "tsup": "^8.5.0",
91
+ "tsx": "^4.19.2",
78
92
  "typescript": "^5.9.3"
79
93
  }
80
94
  }
@@ -0,0 +1,277 @@
1
+ /**
2
+ * DjangoCFG Extension CLI
3
+ *
4
+ * Interactive CLI for managing DjangoCFG extensions
5
+ */
6
+
7
+ import { consola } from 'consola';
8
+ import chalk from 'chalk';
9
+ import prompts from 'prompts';
10
+ import { readFileSync } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+
16
+ // Read package version
17
+ function getVersion(): string {
18
+ try {
19
+ const pkgPath = join(__dirname, '../../package.json');
20
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
21
+ return pkg.version;
22
+ } catch {
23
+ return '1.0.0';
24
+ }
25
+ }
26
+
27
+ // CLI Commands
28
+ const COMMANDS = {
29
+ install: 'Install an extension',
30
+ list: 'List available extensions',
31
+ info: 'Show extension info',
32
+ init: 'Initialize extension in your project',
33
+ help: 'Show help',
34
+ } as const;
35
+
36
+ type Command = keyof typeof COMMANDS;
37
+
38
+ // Available extensions
39
+ const EXTENSIONS = {
40
+ 'ext-leads': {
41
+ name: '@djangocfg/ext-leads',
42
+ description: 'Lead management and contact forms',
43
+ features: ['Contact forms', 'Lead tracking', 'Email integration'],
44
+ },
45
+ 'ext-payments': {
46
+ name: '@djangocfg/ext-payments',
47
+ description: 'Payment processing with multiple providers',
48
+ features: ['NowPayments', 'Crypto payments', 'Payment tracking'],
49
+ },
50
+ 'ext-newsletter': {
51
+ name: '@djangocfg/ext-newsletter',
52
+ description: 'Newsletter and email campaigns',
53
+ features: ['Subscription management', 'Email templates', 'Campaign tracking'],
54
+ },
55
+ 'ext-support': {
56
+ name: '@djangocfg/ext-support',
57
+ description: 'Customer support and ticketing',
58
+ features: ['Ticket system', 'Chat support', 'Knowledge base'],
59
+ },
60
+ 'ext-knowbase': {
61
+ name: '@djangocfg/ext-knowbase',
62
+ description: 'Knowledge base and documentation',
63
+ features: ['Articles', 'Categories', 'Search', 'Markdown support'],
64
+ },
65
+ } as const;
66
+
67
+ // Print banner
68
+ function printBanner() {
69
+ console.log();
70
+ console.log(chalk.cyan.bold(' 🚀 DjangoCFG Extension Manager'));
71
+ console.log(chalk.gray(` v${getVersion()}`));
72
+ console.log();
73
+ }
74
+
75
+ // Print help
76
+ function printHelp() {
77
+ printBanner();
78
+
79
+ console.log(chalk.yellow.bold('Usage:'));
80
+ console.log(` ${chalk.cyan('djangocfg-ext')} ${chalk.gray('[command]')}`);
81
+ console.log();
82
+
83
+ console.log(chalk.yellow.bold('Commands:'));
84
+ Object.entries(COMMANDS).forEach(([cmd, desc]) => {
85
+ console.log(` ${chalk.cyan(cmd.padEnd(12))} ${chalk.gray(desc)}`);
86
+ });
87
+ console.log();
88
+
89
+ console.log(chalk.yellow.bold('Examples:'));
90
+ console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext install')}`);
91
+ console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext list')}`);
92
+ console.log(` ${chalk.gray('$')} ${chalk.cyan('djangocfg-ext info ext-leads')}`);
93
+ console.log();
94
+ }
95
+
96
+ // List extensions
97
+ function listExtensions() {
98
+ printBanner();
99
+ console.log(chalk.yellow.bold('Available Extensions:'));
100
+ console.log();
101
+
102
+ Object.entries(EXTENSIONS).forEach(([key, ext]) => {
103
+ console.log(chalk.cyan.bold(` 📦 ${ext.name}`));
104
+ console.log(chalk.gray(` ${ext.description}`));
105
+ console.log(chalk.gray(` Features: ${ext.features.join(', ')}`));
106
+ console.log();
107
+ });
108
+ }
109
+
110
+ // Show extension info
111
+ function showInfo(extensionKey: string) {
112
+ const ext = EXTENSIONS[extensionKey as keyof typeof EXTENSIONS];
113
+
114
+ if (!ext) {
115
+ consola.error(`Extension not found: ${extensionKey}`);
116
+ consola.info('Run `djangocfg-ext list` to see available extensions');
117
+ return;
118
+ }
119
+
120
+ printBanner();
121
+ console.log(chalk.cyan.bold(`📦 ${ext.name}`));
122
+ console.log();
123
+ console.log(chalk.white(ext.description));
124
+ console.log();
125
+ console.log(chalk.yellow.bold('Features:'));
126
+ ext.features.forEach(feature => {
127
+ console.log(chalk.gray(` • ${feature}`));
128
+ });
129
+ console.log();
130
+ console.log(chalk.yellow.bold('Installation:'));
131
+ console.log(chalk.gray(` pnpm add ${ext.name}`));
132
+ console.log();
133
+ }
134
+
135
+ // Install extension
136
+ async function installExtension() {
137
+ printBanner();
138
+
139
+ const response = await prompts({
140
+ type: 'select',
141
+ name: 'extension',
142
+ message: 'Which extension would you like to install?',
143
+ choices: Object.entries(EXTENSIONS).map(([key, ext]) => ({
144
+ title: ext.name,
145
+ description: ext.description,
146
+ value: key,
147
+ })),
148
+ });
149
+
150
+ if (!response.extension) {
151
+ consola.info('Installation cancelled');
152
+ return;
153
+ }
154
+
155
+ const ext = EXTENSIONS[response.extension as keyof typeof EXTENSIONS];
156
+
157
+ console.log();
158
+ consola.info(`Installing ${chalk.cyan(ext.name)}...`);
159
+ console.log();
160
+
161
+ // Show installation command
162
+ console.log(chalk.yellow.bold('Run this command:'));
163
+ console.log(chalk.cyan(` pnpm add ${ext.name}`));
164
+ console.log();
165
+
166
+ console.log(chalk.yellow.bold('Then import in your app:'));
167
+ console.log(chalk.gray(` import { ExtensionProvider } from '${ext.name}';`));
168
+ console.log();
169
+
170
+ consola.success('See documentation at https://djangocfg.com/docs');
171
+ }
172
+
173
+ // Init extension in project
174
+ async function initExtension() {
175
+ printBanner();
176
+
177
+ console.log(chalk.yellow('This will create extension configuration in your project'));
178
+ console.log();
179
+
180
+ const response = await prompts([
181
+ {
182
+ type: 'select',
183
+ name: 'extension',
184
+ message: 'Which extension do you want to configure?',
185
+ choices: Object.entries(EXTENSIONS).map(([key, ext]) => ({
186
+ title: ext.name,
187
+ description: ext.description,
188
+ value: key,
189
+ })),
190
+ },
191
+ {
192
+ type: 'confirm',
193
+ name: 'confirm',
194
+ message: 'Generate extension configuration?',
195
+ initial: true,
196
+ },
197
+ ]);
198
+
199
+ if (!response.confirm) {
200
+ consola.info('Initialization cancelled');
201
+ return;
202
+ }
203
+
204
+ const ext = EXTENSIONS[response.extension as keyof typeof EXTENSIONS];
205
+
206
+ console.log();
207
+ consola.info(`Initializing ${chalk.cyan(ext.name)}...`);
208
+ console.log();
209
+
210
+ // Show what to do next
211
+ console.log(chalk.yellow.bold('Next steps:'));
212
+ console.log(chalk.gray(' 1. Install the extension: ') + chalk.cyan(`pnpm add ${ext.name}`));
213
+ console.log(chalk.gray(' 2. Add provider to your layout:'));
214
+ console.log();
215
+ console.log(chalk.gray(' ') + chalk.white('import { ExtensionProvider } from \'' + ext.name + '\';'));
216
+ console.log();
217
+ console.log(chalk.gray(' ') + chalk.white('<ExtensionProvider>'));
218
+ console.log(chalk.gray(' ') + chalk.white('{children}'));
219
+ console.log(chalk.gray(' ') + chalk.white('</ExtensionProvider>'));
220
+ console.log();
221
+
222
+ consola.success('Initialization complete!');
223
+ }
224
+
225
+ // Main CLI
226
+ async function main() {
227
+ const args = process.argv.slice(2);
228
+ const command = args[0] as Command;
229
+
230
+ // No command - show help
231
+ if (!command) {
232
+ printHelp();
233
+ return;
234
+ }
235
+
236
+ // Handle commands
237
+ switch (command) {
238
+ case 'list':
239
+ listExtensions();
240
+ break;
241
+
242
+ case 'info':
243
+ const extKey = args[1];
244
+ if (!extKey) {
245
+ consola.error('Please specify an extension');
246
+ consola.info('Example: djangocfg-ext info ext-leads');
247
+ return;
248
+ }
249
+ showInfo(extKey);
250
+ break;
251
+
252
+ case 'install':
253
+ await installExtension();
254
+ break;
255
+
256
+ case 'init':
257
+ await initExtension();
258
+ break;
259
+
260
+ case 'help':
261
+ case '--help':
262
+ case '-h':
263
+ printHelp();
264
+ break;
265
+
266
+ default:
267
+ consola.error(`Unknown command: ${command}`);
268
+ console.log();
269
+ printHelp();
270
+ process.exit(1);
271
+ }
272
+ }
273
+
274
+ main().catch((error) => {
275
+ consola.error('CLI error:', error);
276
+ process.exit(1);
277
+ });