@hazeljs/cli 0.6.0 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,387 @@
1
+ "use strict";
2
+ /**
3
+ * Mustache templates for all simple (single-file) generators.
4
+ *
5
+ * Each template uses three standard variables provided by the Generator base class:
6
+ * - {{className}} — PascalCase name (e.g. "Users")
7
+ * - {{fileName}} — kebab-case name (e.g. "users")
8
+ * - {{camelName}} — camelCase name (e.g. "users")
9
+ *
10
+ * Some templates accept additional data via `extraData` in SimpleGeneratorConfig:
11
+ * - AGENT_TEMPLATE — {{description}}
12
+ * - REPOSITORY_TEMPLATE — {{modelName}} (defaults to fileName)
13
+ *
14
+ * Templates are imported by generate-simple.ts and referenced in the
15
+ * SIMPLE_GENERATORS config array. To add a new generator, create a template
16
+ * here and add a config entry in generate-simple.ts.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.SERVERLESS_CLOUD_FUNCTION_TEMPLATE = exports.SERVERLESS_LAMBDA_TEMPLATE = exports.CONFIG_TEMPLATE = exports.DISCOVERY_TEMPLATE = exports.RAG_SERVICE_TEMPLATE = exports.CRON_SERVICE_TEMPLATE = exports.CACHE_SERVICE_TEMPLATE = exports.AGENT_TEMPLATE = exports.AI_SERVICE_TEMPLATE = exports.WEBSOCKET_GATEWAY_TEMPLATE = exports.REPOSITORY_TEMPLATE = exports.EXCEPTION_FILTER_TEMPLATE = exports.PIPE_TEMPLATE = exports.MIDDLEWARE_TEMPLATE = exports.INTERCEPTOR_TEMPLATE = exports.GUARD_TEMPLATE = exports.SERVICE_TEMPLATE = exports.CONTROLLER_TEMPLATE = void 0;
20
+ // ── Core framework generators ────────────────────────────────────────────────
21
+ exports.CONTROLLER_TEMPLATE = `import { Controller, Get, Post, Body, Param, Delete, Put } from '@hazeljs/core';
22
+ import { {{className}}Service } from './{{fileName}}.service';
23
+ import { Create{{className}}Dto } from './dto/create-{{fileName}}.dto';
24
+ import { Update{{className}}Dto } from './dto/update-{{fileName}}.dto';
25
+
26
+ @Controller('{{fileName}}')
27
+ export class {{className}}Controller {
28
+ constructor(private readonly {{camelName}}Service: {{className}}Service) {}
29
+
30
+ @Get()
31
+ findAll() {
32
+ return this.{{camelName}}Service.findAll();
33
+ }
34
+
35
+ @Get(':id')
36
+ findOne(@Param('id') id: string) {
37
+ return this.{{camelName}}Service.findOne(id);
38
+ }
39
+
40
+ @Post()
41
+ create(@Body(Create{{className}}Dto) createDto: Create{{className}}Dto) {
42
+ return this.{{camelName}}Service.create(createDto);
43
+ }
44
+
45
+ @Put(':id')
46
+ update(@Param('id') id: string, @Body(Update{{className}}Dto) updateDto: Update{{className}}Dto) {
47
+ return this.{{camelName}}Service.update(id, updateDto);
48
+ }
49
+
50
+ @Delete(':id')
51
+ remove(@Param('id') id: string) {
52
+ return this.{{camelName}}Service.remove(id);
53
+ }
54
+ }
55
+ `;
56
+ exports.SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
57
+
58
+ @Service()
59
+ export class {{className}}Service {
60
+ constructor() {}
61
+
62
+ async findAll() {
63
+ return [];
64
+ }
65
+
66
+ async findOne(id: string) {
67
+ return { id };
68
+ }
69
+
70
+ async create(createDto: any) {
71
+ return createDto;
72
+ }
73
+
74
+ async update(id: string, updateDto: any) {
75
+ return { id, ...updateDto };
76
+ }
77
+
78
+ async remove(id: string) {
79
+ return { id };
80
+ }
81
+ }
82
+ `;
83
+ exports.GUARD_TEMPLATE = `import { Injectable, type CanActivate, type ExecutionContext } from '@hazeljs/core';
84
+
85
+ @Injectable()
86
+ export class {{className}}Guard implements CanActivate {
87
+ canActivate(context: ExecutionContext): boolean {
88
+ const request = context.switchToHttp().getRequest();
89
+ // Add your guard logic here
90
+ return true;
91
+ }
92
+ }
93
+ `;
94
+ exports.INTERCEPTOR_TEMPLATE = `import { Injectable, Interceptor, type ExecutionContext } from '@hazeljs/core';
95
+
96
+ @Injectable()
97
+ export class {{className}}Interceptor implements Interceptor {
98
+ async intercept(context: ExecutionContext, next: () => Promise<unknown>): Promise<unknown> {
99
+ // Pre-processing logic here (before handler execution)
100
+ const result = await next();
101
+ // Post-processing logic here (after handler execution)
102
+ return result;
103
+ }
104
+ }
105
+ `;
106
+ exports.MIDDLEWARE_TEMPLATE = `import { Injectable, type MiddlewareHandler, type Request, type Response, type NextFunction } from '@hazeljs/core';
107
+
108
+ @Injectable()
109
+ export class {{className}}Middleware implements MiddlewareHandler {
110
+ use(req: Request, res: Response, next: NextFunction) {
111
+ // Add your middleware logic here
112
+ console.log(\`[{{className}}Middleware] \${req.method} \${req.url}\`);
113
+
114
+ // Continue to next middleware
115
+ next();
116
+ }
117
+ }
118
+ `;
119
+ exports.PIPE_TEMPLATE = `import { type PipeTransform, type RequestContext } from '@hazeljs/core';
120
+
121
+ export class {{className}}Pipe implements PipeTransform {
122
+ transform(value: unknown, context: RequestContext): unknown {
123
+ // Transform logic here
124
+ return value;
125
+ }
126
+ }
127
+ `;
128
+ exports.EXCEPTION_FILTER_TEMPLATE = `import { Catch, type ExceptionFilter, type ArgumentsHost, HttpError, logger } from '@hazeljs/core';
129
+
130
+ @Catch(HttpError)
131
+ export class {{className}}ExceptionFilter implements ExceptionFilter<HttpError> {
132
+ catch(exception: HttpError, host: ArgumentsHost): void {
133
+ const ctx = host.switchToHttp();
134
+ const response = ctx.getResponse();
135
+ const request = ctx.getRequest();
136
+
137
+ const status = exception.statusCode || 500;
138
+ const message = exception.message || 'Internal server error';
139
+
140
+ logger.error(\`[\${request.method}] \${request.url} - \${message} (\${status})\`);
141
+
142
+ response.status(status).json({
143
+ statusCode: status,
144
+ message,
145
+ timestamp: new Date().toISOString(),
146
+ path: request.url,
147
+ });
148
+ }
149
+ }
150
+ `;
151
+ // ── Package-specific generators ──────────────────────────────────────────────
152
+ exports.REPOSITORY_TEMPLATE = `import { Repository, BaseRepository, PrismaService } from '@hazeljs/prisma';
153
+
154
+ // @Repository implies @Injectable() — no need for both decorators
155
+ @Repository({ model: '{{modelName}}' })
156
+ export class {{className}}Repository extends BaseRepository<any> {
157
+ constructor(prisma: PrismaService) {
158
+ super(prisma, '{{modelName}}');
159
+ }
160
+
161
+ // Add custom repository methods here
162
+ async findByName(name: string) {
163
+ return this.findMany({ where: { name } });
164
+ }
165
+ }
166
+ `;
167
+ exports.WEBSOCKET_GATEWAY_TEMPLATE = `import { Realtime, OnConnect, OnDisconnect, OnMessage, Subscribe, Client, Data, WebSocketClient } from '@hazeljs/websocket';
168
+
169
+ @Realtime('/{{fileName}}')
170
+ export class {{className}}Gateway {
171
+ @OnConnect()
172
+ handleConnection(@Client() client: WebSocketClient) {
173
+ console.log('Client connected:', client.id);
174
+ }
175
+
176
+ @OnDisconnect()
177
+ handleDisconnect(@Client() client: WebSocketClient) {
178
+ console.log('Client disconnected:', client.id);
179
+ }
180
+
181
+ @Subscribe('message')
182
+ @OnMessage('message')
183
+ handleMessage(@Client() client: WebSocketClient, @Data() data: unknown) {
184
+ console.log('Message received from', client.id, ':', data);
185
+ // Handle message logic here
186
+ }
187
+ }
188
+ `;
189
+ exports.AI_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
190
+ import { AIService, AIFunction, AIPrompt } from '@hazeljs/ai';
191
+
192
+ @Service()
193
+ export class {{className}}AIService {
194
+ constructor(private readonly aiService: AIService) {}
195
+
196
+ @AIFunction({
197
+ provider: 'openai',
198
+ model: 'gpt-4',
199
+ streaming: false,
200
+ })
201
+ async {{camelName}}Task(@AIPrompt() prompt: string): Promise<unknown> {
202
+ const result = await this.aiService.complete({
203
+ provider: 'openai',
204
+ model: 'gpt-4',
205
+ messages: [{ role: 'user', content: prompt }],
206
+ });
207
+
208
+ return result;
209
+ }
210
+ }
211
+ `;
212
+ exports.AGENT_TEMPLATE = `import { Agent, Tool } from '@hazeljs/agent';
213
+
214
+ @Agent({
215
+ name: '{{fileName}}',
216
+ description: '{{description}}',
217
+ systemPrompt: 'You are a helpful {{className}} agent.',
218
+ enableMemory: true,
219
+ enableRAG: true,
220
+ })
221
+ export class {{className}}Agent {
222
+ @Tool({
223
+ description: 'Example tool for {{fileName}}',
224
+ parameters: [
225
+ {
226
+ name: 'input',
227
+ type: 'string',
228
+ description: 'Input parameter',
229
+ required: true,
230
+ },
231
+ ],
232
+ })
233
+ async exampleTool(input: { input: string }): Promise<{ result: string }> {
234
+ // Implement your tool logic here
235
+ return {
236
+ result: \`Processed: \${input.input}\`,
237
+ };
238
+ }
239
+ }
240
+ `;
241
+ exports.CACHE_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
242
+ import { CacheService, Cacheable, CacheEvict } from '@hazeljs/cache';
243
+
244
+ @Service()
245
+ export class {{className}}CacheService {
246
+ constructor(private readonly cacheService: CacheService) {}
247
+
248
+ @Cacheable({ key: '{{fileName}}:all', ttl: 60 })
249
+ async findAll() {
250
+ // This result will be cached for 60 seconds
251
+ return [];
252
+ }
253
+
254
+ @Cacheable({ key: '{{fileName}}:{{=<% %>=}}#{id}<%={{ }}=%>', ttl: 300 })
255
+ async findOne(id: string) {
256
+ // This result will be cached for 5 minutes
257
+ return { id };
258
+ }
259
+
260
+ @CacheEvict({ key: '{{fileName}}:all' })
261
+ async create(data: any) {
262
+ // Creating a new item evicts the list cache
263
+ return data;
264
+ }
265
+
266
+ async clearAll() {
267
+ await this.cacheService.clear();
268
+ }
269
+ }
270
+ `;
271
+ exports.CRON_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
272
+ import { Cron, CronExpression } from '@hazeljs/cron';
273
+
274
+ @Service()
275
+ export class {{className}}CronService {
276
+ @Cron(CronExpression.EVERY_MINUTE)
277
+ handleEveryMinute() {
278
+ console.log('[{{className}}Cron] Running every minute...');
279
+ // Add your cron job logic here
280
+ }
281
+
282
+ @Cron('0 0 * * *') // Every day at midnight
283
+ handleDaily() {
284
+ console.log('[{{className}}Cron] Running daily task...');
285
+ // Add your daily task logic here
286
+ }
287
+
288
+ @Cron(CronExpression.EVERY_HOUR)
289
+ handleHourly() {
290
+ console.log('[{{className}}Cron] Running hourly cleanup...');
291
+ // Add your hourly task logic here
292
+ }
293
+ }
294
+ `;
295
+ exports.RAG_SERVICE_TEMPLATE = `import { Service } from '@hazeljs/core';
296
+ import { RAGPipeline, MemoryVectorStore } from '@hazeljs/rag';
297
+
298
+ @Service()
299
+ export class {{className}}RagService {
300
+ private pipeline: RAGPipeline;
301
+
302
+ constructor() {
303
+ // Initialize with a memory vector store (swap for Pinecone, Qdrant, etc. in production)
304
+ const vectorStore = new MemoryVectorStore();
305
+
306
+ this.pipeline = new RAGPipeline({
307
+ vectorStore,
308
+ topK: 5,
309
+ });
310
+ }
311
+
312
+ async addDocument(content: string, metadata?: Record<string, unknown>) {
313
+ // Add a document to the vector store for retrieval
314
+ await this.pipeline.addDocument({
315
+ content,
316
+ metadata: metadata || {},
317
+ });
318
+ }
319
+
320
+ async query(question: string) {
321
+ // Retrieve relevant documents and generate a response
322
+ const results = await this.pipeline.query(question);
323
+ return results;
324
+ }
325
+ }
326
+ `;
327
+ exports.DISCOVERY_TEMPLATE = `import { Service } from '@hazeljs/core';
328
+ import { ServiceRegistry, DiscoveryClient } from '@hazeljs/discovery';
329
+
330
+ @Service()
331
+ export class {{className}}DiscoveryService {
332
+ constructor(
333
+ private readonly registry: ServiceRegistry,
334
+ private readonly client: DiscoveryClient,
335
+ ) {}
336
+
337
+ async registerService() {
338
+ await this.registry.register({
339
+ name: '{{fileName}}-service',
340
+ host: 'localhost',
341
+ port: 3000,
342
+ metadata: {
343
+ version: '1.0.0',
344
+ },
345
+ });
346
+ }
347
+
348
+ async discoverService(serviceName: string) {
349
+ const instances = await this.client.getInstances(serviceName);
350
+ return instances;
351
+ }
352
+ }
353
+ `;
354
+ exports.CONFIG_TEMPLATE = `import { HazelModule } from '@hazeljs/core';
355
+ import { ConfigModule, ConfigService } from '@hazeljs/config';
356
+
357
+ // Import ConfigModule.forRoot() in your app module:
358
+ //
359
+ // @HazelModule({
360
+ // imports: [
361
+ // ConfigModule.forRoot({
362
+ // envFilePath: '.env',
363
+ // }),
364
+ // ],
365
+ // })
366
+ //
367
+ // Then inject ConfigService wherever you need it:
368
+ //
369
+ // constructor(private readonly config: ConfigService) {}
370
+ //
371
+ // Usage:
372
+ // this.config.get('DATABASE_URL');
373
+ // this.config.get('PORT', '3000'); // with default value
374
+
375
+ export { ConfigModule, ConfigService };
376
+ `;
377
+ // ── Serverless generators ────────────────────────────────────────────────────
378
+ exports.SERVERLESS_LAMBDA_TEMPLATE = `import { createLambdaHandler } from '@hazeljs/serverless';
379
+ import { AppModule } from './app.module';
380
+
381
+ export const handler = createLambdaHandler(AppModule);
382
+ `;
383
+ exports.SERVERLESS_CLOUD_FUNCTION_TEMPLATE = `import { createCloudFunctionHandler } from '@hazeljs/serverless';
384
+ import { AppModule } from './app.module';
385
+
386
+ export const handler = createCloudFunctionHandler(AppModule);
387
+ `;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,42 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * HazelJS CLI — Entry point and command registration
4
+ *
5
+ * Architecture Overview:
6
+ * =====================
7
+ *
8
+ * index.ts — Entry point, registers all commands with Commander
9
+ * ├─ commands/
10
+ * │ ├─ generate-app.ts — `hazel new` (full scaffolding) and `hazel g app` (skeleton)
11
+ * │ ├─ add.ts — `hazel add [pkg]` with --setup flag (replaces generate-setup)
12
+ * │ ├─ info.ts — `hazel info` (project diagnostics)
13
+ * │ ├─ generate-simple.ts — Config-driven single-file generators (18 types)
14
+ * │ ├─ generate-module.ts — Multi-file module generator
15
+ * │ ├─ generate-dto.ts — DTO pair generator
16
+ * │ ├─ generate-crud.ts — Full CRUD resource generator
17
+ * │ ├─ generate-auth.ts — Auth module with JWT guard
18
+ * │ └─ templates.ts — Mustache templates for all simple generators
19
+ * └─ utils/
20
+ * ├─ generator.ts — Base Generator class, shared types, string utils
21
+ * ├─ generator-registry.ts — Unified registry + runGenerator dispatcher
22
+ * └─ packages-registry.ts — All HazelJS package metadata (single source of truth)
23
+ *
24
+ * Command Structure:
25
+ * =================
26
+ *
27
+ * hazel new <app> — Full interactive scaffolding (packages, git, install)
28
+ * hazel g app <name> — Minimal skeleton app (no install/git)
29
+ * hazel g <type> <name> — Unified generator for 23+ types (see --list)
30
+ * hazel add <pkg> [--setup] — Install HazelJS packages + optional setup file
31
+ * hazel info — Project diagnostics
32
+ *
33
+ * Design Principles:
34
+ * ===================
35
+ *
36
+ * 1. Config-driven generators: SIMPLE_GENERATORS array defines 18+ single-file generators
37
+ * 2. Centralized package registry: HAZEL_PACKAGES drives `hazel add` and `hazel new -i`
38
+ * 3. Machine-readable output: --json and --list --list-json for LLM tool-use
39
+ * 4. Consistent CLI options: --path, --dry-run, --json available everywhere
40
+ * 5. Separation of concerns: Templates live separately from logic; registry is data-driven
41
+ */
2
42
  export {};
package/dist/index.js CHANGED
@@ -1,7 +1,49 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ /**
4
+ * HazelJS CLI — Entry point and command registration
5
+ *
6
+ * Architecture Overview:
7
+ * =====================
8
+ *
9
+ * index.ts — Entry point, registers all commands with Commander
10
+ * ├─ commands/
11
+ * │ ├─ generate-app.ts — `hazel new` (full scaffolding) and `hazel g app` (skeleton)
12
+ * │ ├─ add.ts — `hazel add [pkg]` with --setup flag (replaces generate-setup)
13
+ * │ ├─ info.ts — `hazel info` (project diagnostics)
14
+ * │ ├─ generate-simple.ts — Config-driven single-file generators (18 types)
15
+ * │ ├─ generate-module.ts — Multi-file module generator
16
+ * │ ├─ generate-dto.ts — DTO pair generator
17
+ * │ ├─ generate-crud.ts — Full CRUD resource generator
18
+ * │ ├─ generate-auth.ts — Auth module with JWT guard
19
+ * │ └─ templates.ts — Mustache templates for all simple generators
20
+ * └─ utils/
21
+ * ├─ generator.ts — Base Generator class, shared types, string utils
22
+ * ├─ generator-registry.ts — Unified registry + runGenerator dispatcher
23
+ * └─ packages-registry.ts — All HazelJS package metadata (single source of truth)
24
+ *
25
+ * Command Structure:
26
+ * =================
27
+ *
28
+ * hazel new <app> — Full interactive scaffolding (packages, git, install)
29
+ * hazel g app <name> — Minimal skeleton app (no install/git)
30
+ * hazel g <type> <name> — Unified generator for 23+ types (see --list)
31
+ * hazel add <pkg> [--setup] — Install HazelJS packages + optional setup file
32
+ * hazel info — Project diagnostics
33
+ *
34
+ * Design Principles:
35
+ * ===================
36
+ *
37
+ * 1. Config-driven generators: SIMPLE_GENERATORS array defines 18+ single-file generators
38
+ * 2. Centralized package registry: HAZEL_PACKAGES drives `hazel add` and `hazel new -i`
39
+ * 3. Machine-readable output: --json and --list --list-json for LLM tool-use
40
+ * 4. Consistent CLI options: --path, --dry-run, --json available everywhere
41
+ * 5. Separation of concerns: Templates live separately from logic; registry is data-driven
42
+ */
3
43
  Object.defineProperty(exports, "__esModule", { value: true });
4
44
  const commander_1 = require("commander");
45
+ const fs_1 = require("fs");
46
+ const path_1 = require("path");
5
47
  const generate_app_1 = require("./commands/generate-app");
6
48
  const generate_module_1 = require("./commands/generate-module");
7
49
  const generate_dto_1 = require("./commands/generate-dto");
@@ -11,11 +53,13 @@ const generate_simple_1 = require("./commands/generate-simple");
11
53
  const generator_registry_1 = require("./utils/generator-registry");
12
54
  const info_1 = require("./commands/info");
13
55
  const add_1 = require("./commands/add");
56
+ // Read version from package.json to ensure consistency
57
+ const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf8'));
14
58
  const program = new commander_1.Command();
15
59
  program
16
60
  .name('hazel')
17
61
  .description('CLI for generating HazelJS components and applications')
18
- .version('0.2.0');
62
+ .version(packageJson.version);
19
63
  // New app command
20
64
  (0, generate_app_1.generateApp)(program);
21
65
  // Utility commands
@@ -12,4 +12,9 @@ export declare const GENERATOR_LIST: GeneratorMeta[];
12
12
  * For types that don't require a name (auth, config), pass a placeholder (e.g. 'auth').
13
13
  */
14
14
  export declare function runGenerator(type: string, name: string, options: GenerateCLIOptions): Promise<GenerateResult>;
15
+ /**
16
+ * Get an array of all available generator type names.
17
+ *
18
+ * @returns Array of generator types (e.g. ['controller', 'service', 'module', ...])
19
+ */
15
20
  export declare function getGeneratorTypes(): string[];
@@ -56,6 +56,11 @@ async function runGenerator(type, name, options) {
56
56
  error: `Unknown generator type: "${type}". Use "hazel generate --list" to see available types.`,
57
57
  };
58
58
  }
59
+ /**
60
+ * Get an array of all available generator type names.
61
+ *
62
+ * @returns Array of generator types (e.g. ['controller', 'service', 'module', ...])
63
+ */
59
64
  function getGeneratorTypes() {
60
65
  return exports.GENERATOR_LIST.map((g) => g.type);
61
66
  }
@@ -19,7 +19,16 @@ export interface HazelPackageMeta {
19
19
  setupTemplate: string | null;
20
20
  }
21
21
  export declare const HAZEL_PACKAGES: HazelPackageMeta[];
22
- /** Lookup by short name (e.g. 'ai') or full npm name (e.g. '@hazeljs/ai') */
22
+ /**
23
+ * Look up a HazelJS package by its short CLI name or full npm package name.
24
+ *
25
+ * @param nameOrNpm - Either the short name ('ai', 'auth') or full npm name ('@hazeljs/ai')
26
+ * @returns The matching package metadata, or undefined if not found
27
+ */
23
28
  export declare function findPackage(nameOrNpm: string): HazelPackageMeta | undefined;
24
- /** Map of shortName → HazelPackageMeta for quick lookup */
29
+ /**
30
+ * Map of short CLI name → HazelPackageMeta for O(1) lookups.
31
+ *
32
+ * Example: PACKAGES_BY_NAME['ai'] returns the @hazeljs/ai package metadata.
33
+ */
25
34
  export declare const PACKAGES_BY_NAME: Record<string, HazelPackageMeta>;
@@ -363,9 +363,18 @@ SwaggerModule.setRootModule(AppModule);
363
363
  setupTemplate: null,
364
364
  },
365
365
  ];
366
- /** Lookup by short name (e.g. 'ai') or full npm name (e.g. '@hazeljs/ai') */
366
+ /**
367
+ * Look up a HazelJS package by its short CLI name or full npm package name.
368
+ *
369
+ * @param nameOrNpm - Either the short name ('ai', 'auth') or full npm name ('@hazeljs/ai')
370
+ * @returns The matching package metadata, or undefined if not found
371
+ */
367
372
  function findPackage(nameOrNpm) {
368
373
  return exports.HAZEL_PACKAGES.find((p) => p.shortName === nameOrNpm || p.npm === nameOrNpm);
369
374
  }
370
- /** Map of shortName → HazelPackageMeta for quick lookup */
375
+ /**
376
+ * Map of short CLI name → HazelPackageMeta for O(1) lookups.
377
+ *
378
+ * Example: PACKAGES_BY_NAME['ai'] returns the @hazeljs/ai package metadata.
379
+ */
371
380
  exports.PACKAGES_BY_NAME = Object.fromEntries(exports.HAZEL_PACKAGES.map((p) => [p.shortName, p]));
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "@hazeljs/cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.5",
4
4
  "description": "Command-line interface for scaffolding and generating HazelJS applications and components",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
8
8
  "dist",
9
9
  "@template",
10
- "@template-ai-native"
10
+ "@template-ai-native",
11
+ "cli-manifest.json"
11
12
  ],
12
13
  "bin": {
13
14
  "hazel": "./dist/index.js"
14
15
  },
15
16
  "scripts": {
16
- "build": "tsc",
17
+ "build": "tsc && node scripts/generate-manifest.js",
18
+ "build:types": "tsc",
17
19
  "test": "jest",
18
20
  "lint": "eslint src --ext .ts",
19
21
  "format": "prettier --write \"src/**/*.ts\"",
@@ -68,5 +70,5 @@
68
70
  "type": "opencollective",
69
71
  "url": "https://opencollective.com/hazeljs"
70
72
  },
71
- "gitHead": "27bc86031befce190871d3f07be4156544f37f94"
73
+ "gitHead": "58872147a05275f470b563d4d4784c7efd53596e"
72
74
  }