@zhive/cli 0.5.5 → 0.6.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.
@@ -0,0 +1,15 @@
1
+ import { loadAgentConfig } from '../config/agent.js';
2
+ import { loadMemory } from '@zhive/sdk';
3
+ import { getModel } from '../config/ai-providers.js';
4
+ import { getAllTools, getExecuteSkillTool, initializeSkills } from './tools/index.js';
5
+ export async function initializeAgentRuntime(agentDir) {
6
+ const config = await loadAgentConfig(agentDir);
7
+ const memory = await loadMemory(agentDir);
8
+ const model = await getModel();
9
+ const skillRegistry = await initializeSkills(agentDir);
10
+ const tools = getAllTools();
11
+ const executeSkillTool = getExecuteSkillTool(skillRegistry, model);
12
+ const allTools = { ...tools, executeSkillTool };
13
+ const runtime = { config, memory, tools: allTools, skills: skillRegistry, model };
14
+ return runtime;
15
+ }
@@ -1,3 +1,4 @@
1
+ import { loadCredentials, } from '@zhive/sdk';
1
2
  import axios from 'axios';
2
3
  import fsExtra from 'fs-extra';
3
4
  import * as fs from 'fs/promises';
@@ -174,3 +175,21 @@ function parseTimeframes(raw) {
174
175
  }
175
176
  return parsed;
176
177
  }
178
+ export async function findAgentByName(name) {
179
+ const agents = await scanAgents();
180
+ const agent = agents.find((a) => a.name === name);
181
+ if (!agent) {
182
+ return null;
183
+ }
184
+ return agent;
185
+ }
186
+ export function getCredentialsPath(agentDir, agentName) {
187
+ const sanitized = agentName.replace(/[^a-zA-Z0-9-_]/g, '-');
188
+ const credPath = path.join(agentDir, `hive-${sanitized}.json`);
189
+ return credPath;
190
+ }
191
+ export async function loadAgentCredentials(agentDir, agentName) {
192
+ const credPath = getCredentialsPath(agentDir, agentName);
193
+ const credentials = await loadCredentials(credPath);
194
+ return credentials;
195
+ }
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { fileURLToPath } from 'node:url';
3
+ import * as path from 'node:path';
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const FIXTURES_DIR = path.join(__dirname, '../../../__fixtures__/mock-hive');
6
+ vi.mock('./constant.js', () => ({
7
+ getHiveDir: vi.fn(() => FIXTURES_DIR),
8
+ HIVE_API_URL: 'http://localhost:6969',
9
+ }));
10
+ vi.mock('./ai-providers.js', () => ({
11
+ AI_PROVIDERS: [
12
+ { label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' },
13
+ { label: 'Anthropic', package: '@ai-sdk/anthropic', envVar: 'ANTHROPIC_API_KEY' },
14
+ ],
15
+ }));
16
+ vi.mock('@zhive/sdk', () => ({
17
+ loadCredentials: vi.fn(),
18
+ }));
19
+ import { loadCredentials } from '@zhive/sdk';
20
+ import { findAgentByName, getCredentialsPath, loadAgentCredentials, scanAgents, } from './agent.js';
21
+ const mockLoadCredentials = loadCredentials;
22
+ describe('getCredentialsPath', () => {
23
+ it('constructs correct path with simple agent name', () => {
24
+ const result = getCredentialsPath('/mock/.zhive/agents/my-agent', 'my-agent');
25
+ expect(result).toBe('/mock/.zhive/agents/my-agent/hive-my-agent.json');
26
+ });
27
+ it('sanitizes special characters in agent name', () => {
28
+ const result = getCredentialsPath('/mock/.zhive/agents/my-agent', 'my@agent!test');
29
+ expect(result).toBe('/mock/.zhive/agents/my-agent/hive-my-agent-test.json');
30
+ });
31
+ it('allows underscores and hyphens in agent name', () => {
32
+ const result = getCredentialsPath('/mock/.zhive/agents/test_agent', 'test_agent-v2');
33
+ expect(result).toBe('/mock/.zhive/agents/test_agent/hive-test_agent-v2.json');
34
+ });
35
+ });
36
+ describe('loadAgentCredentials', () => {
37
+ beforeEach(() => {
38
+ vi.clearAllMocks();
39
+ });
40
+ it('delegates to loadCredentials with correct path', async () => {
41
+ const mockCredentials = { apiKey: 'test-api-key' };
42
+ mockLoadCredentials.mockResolvedValue(mockCredentials);
43
+ const result = await loadAgentCredentials('/mock/.zhive/agents/test-agent', 'test-agent');
44
+ expect(mockLoadCredentials).toHaveBeenCalledWith('/mock/.zhive/agents/test-agent/hive-test-agent.json');
45
+ expect(result).toEqual(mockCredentials);
46
+ });
47
+ it('returns null when loadCredentials returns null', async () => {
48
+ mockLoadCredentials.mockResolvedValue(null);
49
+ const result = await loadAgentCredentials('/mock/.zhive/agents/test-agent', 'test-agent');
50
+ expect(result).toBeNull();
51
+ });
52
+ });
53
+ describe('scanAgents', () => {
54
+ beforeEach(() => {
55
+ vi.clearAllMocks();
56
+ });
57
+ it('discovers all valid agents from fixtures directory', async () => {
58
+ const result = await scanAgents();
59
+ expect(result).toHaveLength(3);
60
+ const names = result.map((a) => a.name).sort();
61
+ expect(names).toEqual(['agent-no-skills', 'empty-agent', 'test-agent']);
62
+ });
63
+ it('loads agent config with correct properties', async () => {
64
+ const result = await scanAgents();
65
+ const testAgent = result.find((a) => a.name === 'test-agent');
66
+ expect(testAgent).toBeDefined();
67
+ expect(testAgent?.bio).toBe('Test agent for CLI testing');
68
+ expect(testAgent?.avatarUrl).toBe('https://example.com/avatar.png');
69
+ expect(testAgent?.agentProfile.sentiment).toBe('bullish');
70
+ expect(testAgent?.agentProfile.timeframes).toEqual(['1h', '4h']);
71
+ expect(testAgent?.agentProfile.sectors).toEqual(['defi', 'gaming']);
72
+ });
73
+ it('loads agent with different sentiment', async () => {
74
+ const result = await scanAgents();
75
+ const bearishAgent = result.find((a) => a.name === 'agent-no-skills');
76
+ expect(bearishAgent).toBeDefined();
77
+ expect(bearishAgent?.agentProfile.sentiment).toBe('bearish');
78
+ expect(bearishAgent?.agentProfile.timeframes).toEqual(['1h']);
79
+ expect(bearishAgent?.agentProfile.sectors).toEqual(['infrastructure']);
80
+ });
81
+ it('handles agent with empty sectors', async () => {
82
+ const result = await scanAgents();
83
+ const emptyAgent = result.find((a) => a.name === 'empty-agent');
84
+ expect(emptyAgent).toBeDefined();
85
+ expect(emptyAgent?.agentProfile.sentiment).toBe('neutral');
86
+ expect(emptyAgent?.agentProfile.sectors).toEqual([]);
87
+ });
88
+ });
89
+ describe('findAgentByName', () => {
90
+ beforeEach(() => {
91
+ vi.clearAllMocks();
92
+ });
93
+ it('returns null when agent name does not match', async () => {
94
+ const result = await findAgentByName('non-existent-agent');
95
+ expect(result).toBeNull();
96
+ });
97
+ it('returns agent when name matches', async () => {
98
+ const result = await findAgentByName('test-agent');
99
+ expect(result).not.toBeNull();
100
+ expect(result?.name).toBe('test-agent');
101
+ expect(result?.dir).toBe(path.join(FIXTURES_DIR, 'agents', 'test-agent'));
102
+ });
103
+ it('finds empty-agent by name', async () => {
104
+ const result = await findAgentByName('empty-agent');
105
+ expect(result).not.toBeNull();
106
+ expect(result?.name).toBe('empty-agent');
107
+ expect(result?.bio).toBe('Empty agent with no skills for testing');
108
+ });
109
+ it('finds agent-no-skills by name', async () => {
110
+ const result = await findAgentByName('agent-no-skills');
111
+ expect(result).not.toBeNull();
112
+ expect(result?.name).toBe('agent-no-skills');
113
+ expect(result?.bio).toBe('Agent without skills directory for testing');
114
+ });
115
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhive/cli",
3
- "version": "0.5.5",
3
+ "version": "0.6.1",
4
4
  "description": "CLI for bootstrapping zHive AI Agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "scripts": {
19
19
  "build": "tsc -p tsconfig.build.json",
20
- "dev": "tsx src/index.tsx",
20
+ "dev": "tsx src/index.ts",
21
21
  "start": "node dist/index.js",
22
22
  "deploy": "node scripts/deploy.cjs",
23
23
  "analyze": "tsx src/sandbox/analyze.ts",
@@ -30,11 +30,12 @@
30
30
  "@ai-sdk/google": "^3.0.0",
31
31
  "@ai-sdk/openai": "^3.0.25",
32
32
  "@ai-sdk/xai": "^3.0.0",
33
- "@zhive/sdk": "^0.5.2",
34
33
  "@openrouter/ai-sdk-provider": "^0.4.0",
34
+ "@zhive/sdk": "^0.5.3",
35
35
  "ai": "^6.0.71",
36
36
  "axios": "^1.6.0",
37
37
  "chalk": "^5.3.0",
38
+ "commander": "^14.0.3",
38
39
  "dotenv": "^16.0.0",
39
40
  "fs-extra": "^11.2.0",
40
41
  "ink": "^5.1.0",