@hazeljs/cli 2.0.5 → 2.0.7

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
@@ -1,8 +1,8 @@
1
1
  # @hazeljs/cli
2
2
 
3
- **From zero to AI-powered apps in one command.**
3
+ **Scaffold Agent OS apps and HazelJS backends.**
4
4
 
5
- Generate controllers, services, modules, AI agents, RAG systems or create a complete AI-Native application with PostgreSQL, Redis, and Docker. Interactive project setup, package management, and code that follows best practices.
5
+ DNA / Store / Skillgate / HITL live in [Meridian](https://github.com/hazel-js/hazeljs-meridian-ops) and `hazel agent new`. HTTP + HCEL templates (`--template=ai-native`) are a secondary framework path not the Agent OS flagship.
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@hazeljs/cli.svg)](https://www.npmjs.com/package/@hazeljs/cli)
8
8
  [![npm downloads](https://img.shields.io/npm/dm/@hazeljs/cli)](https://www.npmjs.com/package/@hazeljs/cli)
@@ -10,13 +10,13 @@ Generate controllers, services, modules, AI agents, RAG systems — or create a
10
10
 
11
11
  ## Features
12
12
 
13
- - 🤖 **AI-Native Templates** - Create complete AI applications with agents, RAG, PostgreSQL, and Docker
14
- - 🚀 **Project Scaffolding** - Create new HazelJS applications instantly with interactive setup
15
- - 🎨 **Code Generation** - Generate controllers, services, AI agents, RAG systems, and 20+ component types
16
- - 📦 **Package Management** - Add HazelJS packages interactively with optional setup file generation
17
- - 🏗️ **Best Practices** - Generated code follows HazelJS conventions and AI patterns
18
- - 🎯 **TypeScript First** - All generated code is fully typed
19
- - **Fast Development** - Reduce boilerplate and speed up development
13
+ - 🧬 **Agent OS** `hazel agent new` (bare / agent-os / skillgate), Store, DNA install, runs, apply
14
+ - 🚪 **Skillgate / Gatekeeper** `hazel skillgate from-openapi`, `hazel gatekeeper validate|simulate|explain`
15
+ - 🧪 **Eval / benchmark** `hazel eval`, `hazel benchmark`
16
+ - 🚀 **App scaffolding** `hazel new`, `hazel g app` (HTTP skeleton or `--template=ai-native`)
17
+ - 🎨 **Code generation** controllers, services, agents, RAG, 20+ component types
18
+ - 📦 **Package management** `hazel add` with optional setup files
19
+ - Generated apps do **not** list or import `reflect-metadata` `@hazeljs/core` loads it
20
20
 
21
21
  ## Installation
22
22
 
@@ -34,25 +34,45 @@ npm install --save-dev @hazeljs/cli
34
34
 
35
35
  ## Commands
36
36
 
37
- ### Create a New Application
37
+ ### Agent OS (start here)
38
38
 
39
- **AI-Native Application (Recommended for AI Projects):**
39
+ **Flagship teaching path** — clone [Meridian](https://github.com/hazel-js/hazeljs-meridian-ops) (`store:sync` → `platform:sync` → `dev`). The CLI is not a substitute for that app.
40
+
41
+ ```bash
42
+ # Smaller Agent OS / DNA scaffold (not Meridian)
43
+ hazel agent new my-desk --template=agent-os
44
+ # templates: bare | agent-os | skillgate
45
+ # agent-os includes: DNA + real @Tool app + ops stack (self-healing + predictive-scaling) + K8s manifests
46
+ cd my-desk && npm install && npm run dev
47
+
48
+ hazel agent install ./packages/support.dna.json
49
+ hazel agent runs list
50
+ hazel store publish | install | list
51
+ hazel skillgate from-openapi ./openapi.yaml
52
+ hazel gatekeeper validate
53
+ hazel benchmark
54
+ hazel eval
55
+ ```
56
+
57
+ `hazel agent run` is a DNA smoke with stubs. Real tools and HITL = your app’s `runtime.execute` / Meridian `/api/chat`. `hazel agent apply` declares desired state — it does not restart Node.
58
+
59
+ ### Create a framework application (secondary)
60
+
61
+ HTTP + HCEL / RAG scaffold — **not** the Agent OS flagship:
40
62
 
41
63
  ```bash
42
64
  hazel g app my-ai-app --template=ai-native
43
65
  cd my-ai-app && npm install && cp .env.example .env && docker-compose up -d && npm run dev
44
66
  ```
45
67
 
46
- Creates a complete AI-powered application with agents, RAG (PostgreSQL + pgvector), Redis, and Docker.
47
-
48
- **Skeleton app (quick start):**
68
+ Skeleton API:
49
69
 
50
70
  ```bash
51
71
  hazel g app my-app
52
72
  cd my-app && npm install && npm run dev
53
73
  ```
54
74
 
55
- **Full setup with interactive package selection:**
75
+ Interactive package selection:
56
76
 
57
77
  ```bash
58
78
  hazel new my-app -i
@@ -218,6 +238,17 @@ hazel g serverless handler --platform lambda
218
238
  ## Quick Reference
219
239
 
220
240
  ```bash
241
+ # Agent OS
242
+ hazel agent new <name> [--template agent-os|bare|skillgate]
243
+ hazel agent install <dna>
244
+ hazel agent run | doctor | logs
245
+ hazel agent runs list | inspect | cancel | resume | approve
246
+ hazel store publish | install | list
247
+ hazel skillgate from-openapi <spec>
248
+ hazel gatekeeper validate | simulate | explain
249
+ hazel benchmark
250
+ hazel eval
251
+
221
252
  # Project Management
222
253
  hazel new <name> [-i] # Create new project
223
254
  hazel info # Show project info
package/cli-manifest.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "description": "Machine-readable manifest of all CLI commands and options for LLM agent tool-use",
5
5
  "cli": {
6
6
  "name": "hazel",
7
- "version": "2.0.5",
7
+ "version": "2.0.7",
8
8
  "description": "CLI for generating HazelJS components and applications"
9
9
  },
10
10
  "commands": [
@@ -746,6 +746,12 @@
746
746
  "label": "Configuration (@hazeljs/config)",
747
747
  "description": "Environment-based configuration management"
748
748
  },
749
+ {
750
+ "shortName": "config-server",
751
+ "npm": "@hazeljs/config-server",
752
+ "label": "Git config server (@hazeljs/config-server)",
753
+ "description": "Git-backed distributed configuration (Spring Cloud Config style)"
754
+ },
749
755
  {
750
756
  "shortName": "cron",
751
757
  "npm": "@hazeljs/cron",
@@ -54,7 +54,7 @@ exports.AGENT_TEMPLATES = [
54
54
  {
55
55
  id: 'agent-os',
56
56
  label: 'Agent OS mini-app',
57
- description: 'DNA + real @Agent/@Tool TypeScript app (HITL refund demo) + store lock',
57
+ description: 'DNA + real @Agent/@Tool app + ops stack (self-healing + predictive scaling) + K8s manifests',
58
58
  },
59
59
  {
60
60
  id: 'skillgate',
@@ -156,6 +156,235 @@ Wire real \`@Tool\` / Skillgate handlers in your Hazel app for production behavi
156
156
  `,
157
157
  };
158
158
  }
159
+ function agentOsOpsFiles(npm, _projectName) {
160
+ const deployment = npm;
161
+ const hpaName = `${npm}-hpa`;
162
+ return {
163
+ '.env.example': `# Agent runtime
164
+ HITL=0
165
+
166
+ # Ops stack (proactive scaling + reactive self-healing)
167
+ ENABLE_OPS_STACK=0
168
+ K8S_DEPLOYMENT=${deployment}
169
+ K8S_NAMESPACE=default
170
+ K8S_HPA_NAME=${hpaName}
171
+ PROMETHEUS_URL=http://prometheus.monitoring.svc:9090
172
+ SLACK_BOT_TOKEN=
173
+ PAGERDUTY_ROUTING_KEY=
174
+ `,
175
+ 'src/ops/stack.ts': `import { createOperationsStack } from '@hazeljs/predictive-scaling';
176
+ import {
177
+ FetchKubernetesRestartClient,
178
+ FetchKubernetesScalingClient,
179
+ createSlackHealingNotifier,
180
+ createHealingNotifierChain,
181
+ InMemoryKubernetesRestartClient,
182
+ InMemoryKubernetesScalingClient,
183
+ } from '@hazeljs/self-healing';
184
+
185
+ export function createAgentOpsStack(appName: string) {
186
+ const deployment = process.env.K8S_DEPLOYMENT ?? appName;
187
+ const namespace = process.env.K8S_NAMESPACE ?? 'default';
188
+ const hpaName = process.env.K8S_HPA_NAME ?? \`\${deployment}-hpa\`;
189
+ const inCluster = Boolean(process.env.KUBERNETES_SERVICE_HOST);
190
+
191
+ const scalingClient = inCluster
192
+ ? new FetchKubernetesScalingClient()
193
+ : new InMemoryKubernetesScalingClient();
194
+ const restartClient = inCluster
195
+ ? new FetchKubernetesRestartClient()
196
+ : new InMemoryKubernetesRestartClient();
197
+
198
+ const notifications = [];
199
+ if (process.env.SLACK_BOT_TOKEN) {
200
+ notifications.push(
201
+ createSlackHealingNotifier({
202
+ token: process.env.SLACK_BOT_TOKEN,
203
+ channel: process.env.SLACK_ALERT_CHANNEL ?? '#ops-alerts',
204
+ })
205
+ );
206
+ }
207
+
208
+ return createOperationsStack({
209
+ healing: {
210
+ strategies: ['config-rollback', 'hpa-boost', 'pod-restart', 'safe-mode'],
211
+ aiDiagnostics: true,
212
+ drain: { timeoutMs: 30_000 },
213
+ notifyOn: ['critical-healing', 'healing-failed', 'hpa-boost', 'pod-restart'],
214
+ notifications: notifications.length ? createHealingNotifierChain(notifications) : undefined,
215
+ kubernetes: {
216
+ deployment,
217
+ namespace,
218
+ client: restartClient,
219
+ drainBeforeRestart: true,
220
+ hpa: {
221
+ name: hpaName,
222
+ namespace,
223
+ client: scalingClient,
224
+ boostMinReplicas: Number(process.env.HPA_BOOST_MIN ?? 4),
225
+ restoreAfterMs: Number(process.env.HPA_RESTORE_MS ?? 300_000),
226
+ },
227
+ },
228
+ performance: {
229
+ enabled: true,
230
+ autoScaleOnDegradation: true,
231
+ thresholds: { criticalLatencyMs: 2000, sampleSize: 20 },
232
+ },
233
+ },
234
+ scaling: {
235
+ model: 'time-series-forecast',
236
+ metrics: ['requests', 'latency', 'cpu'],
237
+ horizon: '30m',
238
+ confidence: 0.85,
239
+ capacityPerReplica: Number(process.env.CAPACITY_PER_REPLICA ?? 120),
240
+ hpa: {
241
+ name: hpaName,
242
+ namespace,
243
+ client: scalingClient,
244
+ maxReplicas: Number(process.env.HPA_MAX_REPLICAS ?? 50),
245
+ },
246
+ },
247
+ prometheus: process.env.PROMETHEUS_URL
248
+ ? {
249
+ baseUrl: process.env.PROMETHEUS_URL,
250
+ pollIntervalMs: 60_000,
251
+ queries: {
252
+ requests: \`sum(rate(http_requests_total{service="${deployment}"}[5m]))\`,
253
+ latency:
254
+ \`histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{service="${deployment}"}[5m])) by (le))\`,
255
+ cpu: \`avg(rate(container_cpu_usage_seconds_total{pod=~"${deployment}-.*"}[5m]))\`,
256
+ },
257
+ }
258
+ : undefined,
259
+ });
260
+ }
261
+
262
+ let opsStack: ReturnType<typeof createAgentOpsStack> | null = null;
263
+
264
+ export function startAgentOpsStack(appName: string) {
265
+ if (opsStack) return opsStack;
266
+ opsStack = createAgentOpsStack(appName);
267
+ opsStack.start();
268
+ return opsStack;
269
+ }
270
+
271
+ export function stopAgentOpsStack() {
272
+ opsStack?.stop();
273
+ opsStack = null;
274
+ }
275
+ `,
276
+ 'deploy/kubernetes/deployment.yaml': `apiVersion: apps/v1
277
+ kind: Deployment
278
+ metadata:
279
+ name: ${deployment}
280
+ labels:
281
+ app: ${deployment}
282
+ spec:
283
+ replicas: 2
284
+ selector:
285
+ matchLabels:
286
+ app: ${deployment}
287
+ template:
288
+ metadata:
289
+ labels:
290
+ app: ${deployment}
291
+ spec:
292
+ serviceAccountName: ${deployment}
293
+ containers:
294
+ - name: agent
295
+ image: ${deployment}:latest
296
+ env:
297
+ - name: ENABLE_OPS_STACK
298
+ value: "1"
299
+ - name: K8S_DEPLOYMENT
300
+ value: ${deployment}
301
+ - name: K8S_HPA_NAME
302
+ value: ${hpaName}
303
+ - name: PROMETHEUS_URL
304
+ value: http://prometheus.monitoring.svc:9090
305
+ resources:
306
+ requests:
307
+ cpu: 250m
308
+ memory: 512Mi
309
+ limits:
310
+ cpu: "1"
311
+ memory: 1Gi
312
+ `,
313
+ 'deploy/kubernetes/hpa.yaml': `apiVersion: autoscaling/v2
314
+ kind: HorizontalPodAutoscaler
315
+ metadata:
316
+ name: ${hpaName}
317
+ spec:
318
+ scaleTargetRef:
319
+ apiVersion: apps/v1
320
+ kind: Deployment
321
+ name: ${deployment}
322
+ minReplicas: 2
323
+ maxReplicas: 50
324
+ metrics:
325
+ - type: Resource
326
+ resource:
327
+ name: cpu
328
+ target:
329
+ type: Utilization
330
+ averageUtilization: 70
331
+ - type: Resource
332
+ resource:
333
+ name: memory
334
+ target:
335
+ type: Utilization
336
+ averageUtilization: 80
337
+ `,
338
+ 'deploy/kubernetes/service.yaml': `apiVersion: v1
339
+ kind: Service
340
+ metadata:
341
+ name: ${deployment}
342
+ spec:
343
+ selector:
344
+ app: ${deployment}
345
+ ports:
346
+ - port: 80
347
+ targetPort: 3000
348
+ `,
349
+ 'deploy/kubernetes/serviceaccount.yaml': `apiVersion: v1
350
+ kind: ServiceAccount
351
+ metadata:
352
+ name: ${deployment}
353
+ ---
354
+ apiVersion: rbac.authorization.k8s.io/v1
355
+ kind: Role
356
+ metadata:
357
+ name: ${deployment}-ops
358
+ rules:
359
+ - apiGroups: ["autoscaling"]
360
+ resources: ["horizontalpodautoscalers"]
361
+ verbs: ["get", "patch", "update"]
362
+ - apiGroups: ["apps"]
363
+ resources: ["deployments"]
364
+ verbs: ["get", "patch", "update"]
365
+ ---
366
+ apiVersion: rbac.authorization.k8s.io/v1
367
+ kind: RoleBinding
368
+ metadata:
369
+ name: ${deployment}-ops
370
+ subjects:
371
+ - kind: ServiceAccount
372
+ name: ${deployment}
373
+ roleRef:
374
+ apiGroup: rbac.authorization.k8s.io
375
+ kind: Role
376
+ name: ${deployment}-ops
377
+ `,
378
+ 'deploy/kubernetes/kustomization.yaml': `apiVersion: kustomize.config.k8s.io/v1beta1
379
+ kind: Kustomization
380
+ resources:
381
+ - serviceaccount.yaml
382
+ - deployment.yaml
383
+ - service.yaml
384
+ - hpa.yaml
385
+ `,
386
+ };
387
+ }
159
388
  function agentOsFiles(projectName) {
160
389
  const dnaName = agentDnaName(projectName);
161
390
  const npm = sanitizeNpmName(projectName);
@@ -197,6 +426,8 @@ Use lookupOrder for facts. Call processRefund only after lookup. Be concise.`,
197
426
  },
198
427
  dependencies: {
199
428
  '@hazeljs/agent': '^1.0.6',
429
+ '@hazeljs/self-healing': '^2.0.5',
430
+ '@hazeljs/predictive-scaling': '^2.0.5',
200
431
  },
201
432
  devDependencies: {
202
433
  '@types/node': '^20.19.39',
@@ -279,9 +510,16 @@ import {
279
510
  type AgentEvent,
280
511
  } from '@hazeljs/agent';
281
512
  import { SupportAgent } from './support.agent';
513
+ import { startAgentOpsStack, stopAgentOpsStack } from './ops/stack';
282
514
 
283
515
  async function main() {
284
516
  const input = process.argv.slice(2).join(' ') || 'Where is ORD-1001?';
517
+
518
+ if (process.env.ENABLE_OPS_STACK === '1') {
519
+ startAgentOpsStack('${npm}');
520
+ process.on('SIGINT', () => stopAgentOpsStack());
521
+ process.on('SIGTERM', () => stopAgentOpsStack());
522
+ }
285
523
  // Demo uses mock LLM. Wire OpenAI (or another provider) in production apps.
286
524
  const llm = createMockLlmProvider(
287
525
  'Used real @Tool handlers on AgentRuntime. (Replace createMockLlmProvider with your LLM.)'
@@ -326,6 +564,8 @@ main().catch((e) => {
326
564
  | \`dna/agent.marketplace.json\` | DNA / marketplace package (OpenAPI-for-agents) |
327
565
  | \`src/support.agent.ts\` | Real tool handlers |
328
566
  | \`src/main.ts\` | AgentRuntime bootstrap + optional DNA overlay |
567
+ | \`src/ops/stack.ts\` | Predictive scaling + self-healing ops stack |
568
+ | \`deploy/kubernetes/\` | K8s Deployment, HPA, RBAC for in-cluster ops |
329
569
 
330
570
  ## Quick start
331
571
 
@@ -343,7 +583,21 @@ npm run store:install
343
583
  \`\`\`
344
584
 
345
585
  Set \`HITL=1\` to pause on \`processRefund\` approvals.
586
+
587
+ ## Production ops (predictive + self-healing)
588
+
589
+ \`\`\`bash
590
+ cp .env.example .env
591
+ ENABLE_OPS_STACK=1 npm run dev
592
+
593
+ # Deploy to Kubernetes (requires kubectl + kustomize)
594
+ kubectl apply -k deploy/kubernetes
595
+ \`\`\`
596
+
597
+ - **Proactive:** \`@hazeljs/predictive-scaling\` forecasts traffic and adjusts HPA min replicas
598
+ - **Reactive:** \`@hazeljs/self-healing\` heals failures, drains pods, opens Slack/Jira alerts
346
599
  `,
600
+ ...agentOsOpsFiles(npm, projectName),
347
601
  };
348
602
  }
349
603
  function skillgateFiles(projectName) {
@@ -88,8 +88,12 @@ describe('agent templates scaffold', () => {
88
88
  (0, agent_templates_1.scaffoldAgentProject)({ name: 'os-demo', destDir: dest, template: 'agent-os' });
89
89
  expect(fs.existsSync(path.join(dest, 'src/support.agent.ts'))).toBe(true);
90
90
  expect(fs.existsSync(path.join(dest, 'src/main.ts'))).toBe(true);
91
+ expect(fs.existsSync(path.join(dest, 'src/ops/stack.ts'))).toBe(true);
92
+ expect(fs.existsSync(path.join(dest, 'deploy/kubernetes/hpa.yaml'))).toBe(true);
91
93
  expect(fs.existsSync(path.join(dest, 'dna/agent.marketplace.json'))).toBe(true);
92
94
  const pkg = JSON.parse(fs.readFileSync(path.join(dest, 'package.json'), 'utf8'));
95
+ expect(pkg.dependencies['@hazeljs/self-healing']).toBe('^2.0.5');
96
+ expect(pkg.dependencies['@hazeljs/predictive-scaling']).toBe('^2.0.5');
93
97
  expect(pkg.dependencies['reflect-metadata']).toBeUndefined();
94
98
  const main = fs.readFileSync(path.join(dest, 'src/main.ts'), 'utf8');
95
99
  const agent = fs.readFileSync(path.join(dest, 'src/support.agent.ts'), 'utf8');
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * `hazel organism` — inspect / control organism snapshots (Phase 1).
4
+ * Live runtime control uses the programmatic API; CLI reads/writes local snapshots.
5
+ */
6
+ export declare function registerOrganismCommand(program: Command): void;
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerOrganismCommand = registerOrganismCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const DEFAULT_STORE = path.join('.hazel', 'organisms.json');
40
+ function loadStore(storePath) {
41
+ const resolved = path.resolve(storePath);
42
+ if (!fs.existsSync(resolved))
43
+ return {};
44
+ try {
45
+ return JSON.parse(fs.readFileSync(resolved, 'utf8'));
46
+ }
47
+ catch {
48
+ return {};
49
+ }
50
+ }
51
+ function saveStore(storePath, data) {
52
+ const resolved = path.resolve(storePath);
53
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
54
+ fs.writeFileSync(resolved, JSON.stringify(data, null, 2));
55
+ }
56
+ /**
57
+ * `hazel organism` — inspect / control organism snapshots (Phase 1).
58
+ * Live runtime control uses the programmatic API; CLI reads/writes local snapshots.
59
+ */
60
+ function registerOrganismCommand(program) {
61
+ const org = program.command('organism').description('Agentic Organism Runtime commands');
62
+ org
63
+ .command('list')
64
+ .description('List known organisms from the local snapshot store')
65
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
66
+ .option('--json', 'Print JSON')
67
+ .action((opts) => {
68
+ const data = loadStore(opts.store);
69
+ const rows = Object.values(data);
70
+ if (opts.json) {
71
+ console.log(JSON.stringify({ organisms: rows }, null, 2));
72
+ return;
73
+ }
74
+ if (!rows.length) {
75
+ console.log('No organisms found. Use createOrganism() then persist a snapshot.');
76
+ return;
77
+ }
78
+ for (const r of rows) {
79
+ console.log(`${r.id}\t${r.status}\t${r.mission.objective}`);
80
+ }
81
+ });
82
+ org
83
+ .command('inspect <id>')
84
+ .description('Inspect an organism snapshot')
85
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
86
+ .option('--json', 'Print JSON')
87
+ .action((id, opts) => {
88
+ const data = loadStore(opts.store);
89
+ const snap = data[id];
90
+ if (!snap) {
91
+ console.error(`Organism not found: ${id}`);
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ console.log(opts.json ? JSON.stringify(snap, null, 2) : formatInspect(snap));
96
+ });
97
+ org
98
+ .command('agents <id>')
99
+ .description('List agents for an organism')
100
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
101
+ .option('--json', 'Print JSON')
102
+ .action((id, opts) => {
103
+ const snap = loadStore(opts.store)[id];
104
+ if (!snap) {
105
+ console.error(`Organism not found: ${id}`);
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ if (opts.json) {
110
+ console.log(JSON.stringify({ agents: snap.agents }, null, 2));
111
+ return;
112
+ }
113
+ for (const a of snap.agents) {
114
+ console.log(`${a.id}\t${a.status}\tgen=${a.generation}\t${a.name ?? ''}`);
115
+ }
116
+ });
117
+ org
118
+ .command('genealogy <id>')
119
+ .description('Show agent genealogy')
120
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
121
+ .option('--json', 'Print JSON')
122
+ .action((id, opts) => {
123
+ const snap = loadStore(opts.store)[id];
124
+ if (!snap) {
125
+ console.error(`Organism not found: ${id}`);
126
+ process.exitCode = 1;
127
+ return;
128
+ }
129
+ console.log(opts.json
130
+ ? JSON.stringify(snap.genealogy ?? [], null, 2)
131
+ : JSON.stringify(snap.genealogy ?? [], null, 2));
132
+ });
133
+ org
134
+ .command('resources <id>')
135
+ .description('Show organism resource pool')
136
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
137
+ .option('--json', 'Print JSON')
138
+ .action((id, opts) => {
139
+ const snap = loadStore(opts.store)[id];
140
+ if (!snap) {
141
+ console.error(`Organism not found: ${id}`);
142
+ process.exitCode = 1;
143
+ return;
144
+ }
145
+ console.log(JSON.stringify(snap.resources ?? {}, null, 2));
146
+ });
147
+ org
148
+ .command('events <id>')
149
+ .description('Show recent organism events')
150
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
151
+ .option('--json', 'Print JSON')
152
+ .action((id, opts) => {
153
+ const snap = loadStore(opts.store)[id];
154
+ if (!snap) {
155
+ console.error(`Organism not found: ${id}`);
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+ console.log(JSON.stringify(snap.events ?? [], null, 2));
160
+ });
161
+ for (const action of ['pause', 'resume', 'stop']) {
162
+ org
163
+ .command(`${action} <id>`)
164
+ .description(`${action} an organism snapshot (status flag only in Phase 1 CLI)`)
165
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
166
+ .action((id, opts) => {
167
+ const data = loadStore(opts.store);
168
+ const snap = data[id];
169
+ if (!snap) {
170
+ console.error(`Organism not found: ${id}`);
171
+ process.exitCode = 1;
172
+ return;
173
+ }
174
+ snap.status =
175
+ action === 'pause' ? 'paused' : action === 'resume' ? 'operating' : 'terminated';
176
+ snap.updatedAt = new Date().toISOString();
177
+ data[id] = snap;
178
+ saveStore(opts.store, data);
179
+ console.log(`Organism ${id} marked ${snap.status}`);
180
+ });
181
+ }
182
+ org
183
+ .command('save-snapshot')
184
+ .description('Helper: write a minimal demo snapshot for CLI testing')
185
+ .option('--store <path>', 'Snapshot store path', DEFAULT_STORE)
186
+ .option('--id <id>', 'Organism id', 'demo-org')
187
+ .action((opts) => {
188
+ const data = loadStore(opts.store);
189
+ data[opts.id] = {
190
+ id: opts.id,
191
+ status: 'operating',
192
+ mission: {
193
+ id: 'demo',
194
+ objective: 'Operate customer support while maintaining 90% CSAT',
195
+ },
196
+ agents: [],
197
+ genealogy: [],
198
+ resources: { tokensRemaining: 5000000 },
199
+ events: [],
200
+ updatedAt: new Date().toISOString(),
201
+ };
202
+ saveStore(opts.store, data);
203
+ console.log(`Wrote snapshot ${opts.id} to ${opts.store}`);
204
+ });
205
+ }
206
+ function formatInspect(snap) {
207
+ return [
208
+ `id: ${snap.id}`,
209
+ `status: ${snap.status}`,
210
+ `mission: ${snap.mission.objective}`,
211
+ `agents: ${snap.agents.length}`,
212
+ `updatedAt: ${snap.updatedAt}`,
213
+ ].join('\n');
214
+ }
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ const agent_1 = require("./commands/agent");
59
59
  const skillgate_1 = require("./commands/skillgate");
60
60
  const gatekeeper_1 = require("./commands/gatekeeper");
61
61
  const store_1 = require("./commands/store");
62
+ const organism_1 = require("./commands/organism");
62
63
  // Read version from package.json to ensure consistency
63
64
  const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf8'));
64
65
  const program = new commander_1.Command();
@@ -77,6 +78,7 @@ program
77
78
  (0, skillgate_1.registerSkillgateCommand)(program);
78
79
  (0, gatekeeper_1.registerGatekeeperCommand)(program);
79
80
  (0, store_1.registerStoreCommand)(program);
81
+ (0, organism_1.registerOrganismCommand)(program);
80
82
  // Generate command group (unified: hazel g <type> <name> [--path] [--dry-run] [--json], or hazel g --list)
81
83
  const generateCommand = program
82
84
  .command('generate')
@@ -91,6 +91,22 @@ export const oauthImports = [
91
91
  moduleExpression: "ConfigModule.forRoot({ envFilePath: '.env' })",
92
92
  setupTemplate: null,
93
93
  },
94
+ {
95
+ shortName: 'config-server',
96
+ npm: '@hazeljs/config-server',
97
+ label: 'Git config server (@hazeljs/config-server)',
98
+ hint: 'import { ConfigServer } from "@hazeljs/config-server";\n // await new ConfigServer({ git: { uri: process.env.CONFIG_GIT_URI! }, port: 8888 }).start()',
99
+ moduleImport: "import { ConfigServerModule } from '@hazeljs/config-server';",
100
+ moduleExpression: "ConfigServerModule.forRoot({ git: { uri: process.env.CONFIG_GIT_URI || '' }, port: 8888 })",
101
+ setupTemplate: `import { ConfigServerModule } from '@hazeljs/config-server';
102
+
103
+ // Add ConfigServerModule.forRoot(...) to your HazelModule imports, then start:
104
+ // await new ConfigServer({ git: { uri: process.env.CONFIG_GIT_URI! }, port: 8888 }).start();
105
+ export const configServerImports = [
106
+ ConfigServerModule.forRoot({ git: { uri: process.env.CONFIG_GIT_URI || '' }, port: 8888 }),
107
+ ];
108
+ `,
109
+ },
94
110
  {
95
111
  shortName: 'cron',
96
112
  npm: '@hazeljs/cron',
@@ -269,6 +285,26 @@ export const gatekeeper = new AgentGatekeeper({
269
285
  defaultDecision: 'deny',
270
286
  policies: [],
271
287
  });
288
+ `,
289
+ },
290
+ {
291
+ shortName: 'organism',
292
+ npm: '@hazeljs/organism',
293
+ label: 'Agentic Organism Runtime (@hazeljs/organism)',
294
+ hint: 'import { createOrganism, OrganismRuntime } from "@hazeljs/organism";\n // const organism = await createOrganism({ mission, genes, constitution })',
295
+ moduleImport: null,
296
+ moduleExpression: null,
297
+ setupTemplate: `import { createOrganism } from '@hazeljs/organism';
298
+
299
+ export async function startOrganism() {
300
+ const organism = await createOrganism({
301
+ mission: { id: 'ops', objective: 'Operate within budget and constraints' },
302
+ genes: [],
303
+ limits: { maxAgents: 10, maxGenerationDepth: 3 },
304
+ });
305
+ await organism.start();
306
+ return organism;
307
+ }
272
308
  `,
273
309
  },
274
310
  {
@@ -363,6 +399,37 @@ export class ExampleResilienceService {
363
399
  return 'ok';
364
400
  }
365
401
  }
402
+ `,
403
+ },
404
+ {
405
+ shortName: 'self-healing',
406
+ npm: '@hazeljs/self-healing',
407
+ label: 'Self-Healing Microservices (@hazeljs/self-healing)',
408
+ hint: 'import { SelfHealing, SelfHeal, createHealingCoordinator } from "@hazeljs/self-healing";',
409
+ moduleImport: null,
410
+ moduleExpression: null,
411
+ setupTemplate: `import { createHealingCoordinator } from '@hazeljs/self-healing';
412
+
413
+ export const healing = createHealingCoordinator({
414
+ strategies: ['config-rollback', 'hpa-boost', 'pod-restart'],
415
+ });
416
+ `,
417
+ },
418
+ {
419
+ shortName: 'predictive-scaling',
420
+ npm: '@hazeljs/predictive-scaling',
421
+ label: 'Predictive Auto-Scaling (@hazeljs/predictive-scaling)',
422
+ hint: 'import { PredictiveScaling, createPredictiveScaler } from "@hazeljs/predictive-scaling";',
423
+ moduleImport: null,
424
+ moduleExpression: null,
425
+ setupTemplate: `import { createPredictiveScaler } from '@hazeljs/predictive-scaling';
426
+
427
+ // Wire hpa.client to your Kubernetes scaling client
428
+ export const scaler = createPredictiveScaler({
429
+ horizon: '30m',
430
+ metrics: ['requests', 'latency'],
431
+ hpa: { name: 'api-hpa', client: scalingClient },
432
+ });
366
433
  `,
367
434
  },
368
435
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/cli",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
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",
@@ -31,11 +31,11 @@
31
31
  "mustache": "^4.2.0"
32
32
  },
33
33
  "peerDependencies": {
34
- "@hazeljs/eval": "^2.0.5",
35
- "@hazeljs/benchmark": "^2.0.5",
36
- "@hazeljs/agent": "^2.0.5",
37
- "@hazeljs/skillgate": "^2.0.5",
38
- "@hazeljs/agent-gatekeeper": "^2.0.5"
34
+ "@hazeljs/eval": "^2.0.7",
35
+ "@hazeljs/benchmark": "^2.0.7",
36
+ "@hazeljs/agent": "^2.0.7",
37
+ "@hazeljs/skillgate": "^2.0.7",
38
+ "@hazeljs/agent-gatekeeper": "^2.0.7"
39
39
  },
40
40
  "peerDependenciesMeta": {
41
41
  "@hazeljs/eval": {
@@ -55,11 +55,11 @@
55
55
  }
56
56
  },
57
57
  "devDependencies": {
58
- "@hazeljs/eval": "^2.0.5",
59
- "@hazeljs/benchmark": "^2.0.5",
60
- "@hazeljs/agent": "^2.0.5",
61
- "@hazeljs/skillgate": "^2.0.5",
62
- "@hazeljs/agent-gatekeeper": "^2.0.5",
58
+ "@hazeljs/eval": "^2.0.7",
59
+ "@hazeljs/benchmark": "^2.0.7",
60
+ "@hazeljs/agent": "^2.0.7",
61
+ "@hazeljs/skillgate": "^2.0.7",
62
+ "@hazeljs/agent-gatekeeper": "^2.0.7",
63
63
  "@types/inquirer": "^8.2.10",
64
64
  "@types/mustache": "^4.2.5",
65
65
  "typescript": "^6.0.3"