@zhive/cli 0.6.6 → 0.6.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.
Files changed (57) hide show
  1. package/dist/commands/agent/commands/profile.js +0 -6
  2. package/dist/commands/agent/commands/profile.test.js +2 -23
  3. package/dist/commands/create/presets/index.js +1 -1
  4. package/dist/commands/create/presets/options.js +18 -15
  5. package/dist/commands/create/ui/steps/IdentityStep.js +3 -2
  6. package/dist/commands/doctor/commands/index.js +5 -11
  7. package/dist/commands/megathread/commands/create-comment.js +2 -7
  8. package/dist/commands/megathread/commands/create-comment.test.js +3 -30
  9. package/dist/commands/megathread/commands/create-comments.js +2 -7
  10. package/dist/commands/megathread/commands/list.js +5 -10
  11. package/dist/commands/megathread/commands/list.test.js +3 -21
  12. package/dist/commands/migrate-templates/ui/MigrateApp.js +1 -1
  13. package/dist/commands/start/commands/prediction.js +1 -1
  14. package/dist/commands/start/commands/skills.test.js +1 -2
  15. package/dist/components/MultiSelectPrompt.js +3 -3
  16. package/dist/shared/config/agent.js +4 -0
  17. package/dist/shared/config/agent.test.js +0 -5
  18. package/package.json +1 -1
  19. package/dist/CLAUDE.md +0 -7
  20. package/dist/backtest/CLAUDE.md +0 -7
  21. package/dist/cli.js +0 -20
  22. package/dist/commands/create/presets.js +0 -613
  23. package/dist/commands/start/ui/AsciiTicker.js +0 -81
  24. package/dist/services/agent/analysis.js +0 -160
  25. package/dist/services/agent/config.js +0 -75
  26. package/dist/services/agent/env.js +0 -30
  27. package/dist/services/agent/helpers/model.js +0 -92
  28. package/dist/services/agent/helpers.js +0 -22
  29. package/dist/services/agent/prompts/chat-prompt.js +0 -65
  30. package/dist/services/agent/prompts/memory-prompt.js +0 -45
  31. package/dist/services/agent/prompts/prompt.js +0 -379
  32. package/dist/services/agent/skills/index.js +0 -2
  33. package/dist/services/agent/skills/skill-parser.js +0 -149
  34. package/dist/services/agent/skills/types.js +0 -1
  35. package/dist/services/agent/tools/edit-section.js +0 -59
  36. package/dist/services/agent/tools/fetch-rules.js +0 -21
  37. package/dist/services/agent/tools/index.js +0 -76
  38. package/dist/services/agent/tools/market/client.js +0 -41
  39. package/dist/services/agent/tools/market/index.js +0 -3
  40. package/dist/services/agent/tools/market/tools.js +0 -518
  41. package/dist/services/agent/tools/mindshare/client.js +0 -124
  42. package/dist/services/agent/tools/mindshare/index.js +0 -3
  43. package/dist/services/agent/tools/mindshare/tools.js +0 -563
  44. package/dist/services/agent/tools/read-skill-tool.js +0 -30
  45. package/dist/services/agent/tools/ta/index.js +0 -1
  46. package/dist/services/agent/tools/ta/indicators.js +0 -201
  47. package/dist/services/agent/types.js +0 -1
  48. package/dist/services/ai-providers.js +0 -66
  49. package/dist/services/config/agent.js +0 -110
  50. package/dist/services/config/config.js +0 -22
  51. package/dist/services/config/constant.js +0 -8
  52. package/dist/shared/agent/agent-runtime.js +0 -144
  53. package/dist/shared/agent/config.js +0 -75
  54. package/dist/shared/agent/env.js +0 -30
  55. package/dist/shared/agent/helpers/model.js +0 -92
  56. package/dist/shared/agent/types.js +0 -1
  57. package/dist/shared/ai-providers.js +0 -66
@@ -1,7 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
3
3
  import { styled, symbols } from '../../shared/theme.js';
4
- import { loadConfig } from '@zhive/sdk';
5
4
  export const createAgentProfileCommand = () => {
6
5
  return new Command('profile')
7
6
  .description('Display agent profile information')
@@ -19,11 +18,6 @@ export const createAgentProfileCommand = () => {
19
18
  }
20
19
  process.exit(1);
21
20
  }
22
- const credentials = await loadConfig(agentConfig.dir);
23
- if (!credentials?.apiKey) {
24
- console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
25
- process.exit(1);
26
- }
27
21
  console.log('');
28
22
  console.log(styled.honeyBold(`${symbols.hive} Agent Profile: ${agentConfig.name}`));
29
23
  console.log('');
@@ -10,13 +10,6 @@ vi.mock('../../../shared/config/constant.js', () => ({
10
10
  vi.mock('../../../shared/config/ai-providers.js', () => ({
11
11
  AI_PROVIDERS: [{ label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' }],
12
12
  }));
13
- vi.mock('@zhive/sdk', async () => {
14
- const actual = await vi.importActual('@zhive/sdk');
15
- return {
16
- ...actual,
17
- loadConfig: vi.fn(),
18
- };
19
- });
20
13
  vi.mock('../../shared/theme.js', () => ({
21
14
  styled: {
22
15
  red: (text) => text,
@@ -32,9 +25,7 @@ vi.mock('../../shared/theme.js', () => ({
32
25
  hive: '⬡',
33
26
  },
34
27
  }));
35
- import { loadConfig } from '@zhive/sdk';
36
28
  import { createAgentProfileCommand } from './profile.js';
37
- const mockLoadConfig = loadConfig;
38
29
  describe('createAgentProfileCommand', () => {
39
30
  let consoleLogSpy;
40
31
  let consoleErrorSpy;
@@ -72,20 +63,11 @@ describe('createAgentProfileCommand', () => {
72
63
  expect(consoleErrorOutput.join('\n')).toContain('agent-no-skills');
73
64
  });
74
65
  it('shows error when credentials are missing', async () => {
75
- mockLoadConfig.mockResolvedValue(null);
76
- const command = createAgentProfileCommand();
77
- await expect(command.parseAsync(['test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
78
- expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
79
- expect(consoleErrorOutput.join('\n')).toContain('may need to be registered first');
80
- });
81
- it('shows error when credentials have no API key', async () => {
82
- mockLoadConfig.mockResolvedValue({ apiKey: null });
83
66
  const command = createAgentProfileCommand();
84
- await expect(command.parseAsync(['test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
85
- expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
67
+ await expect(command.parseAsync(['no-cred'], { from: 'user' })).rejects.toThrow('process.exit(1)');
68
+ expect(consoleErrorOutput.join('\n')).toContain('Agent "no-cred" not found');
86
69
  });
87
70
  it('displays profile from local config', async () => {
88
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
89
71
  const command = createAgentProfileCommand();
90
72
  await command.parseAsync(['test-agent'], { from: 'user' });
91
73
  const output = consoleOutput.join('\n');
@@ -98,7 +80,6 @@ describe('createAgentProfileCommand', () => {
98
80
  expect(output).toContain('https://example.com/avatar.png');
99
81
  });
100
82
  it('displays profile settings section', async () => {
101
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
102
83
  const command = createAgentProfileCommand();
103
84
  await command.parseAsync(['test-agent'], { from: 'user' });
104
85
  const output = consoleOutput.join('\n');
@@ -111,7 +92,6 @@ describe('createAgentProfileCommand', () => {
111
92
  expect(output).toContain('defi, gaming');
112
93
  });
113
94
  it('handles agent with empty sectors', async () => {
114
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
115
95
  const command = createAgentProfileCommand();
116
96
  await command.parseAsync(['empty-agent'], { from: 'user' });
117
97
  const output = consoleOutput.join('\n');
@@ -124,7 +104,6 @@ describe('createAgentProfileCommand', () => {
124
104
  expect(sectorsLine).toContain('-');
125
105
  });
126
106
  it('works with different fixture agents', async () => {
127
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
128
107
  const command = createAgentProfileCommand();
129
108
  await command.parseAsync(['agent-no-skills'], { from: 'user' });
130
109
  const output = consoleOutput.join('\n');
@@ -1,3 +1,3 @@
1
1
  export { SOUL_PRESETS, STRATEGY_PRESETS } from './data.js';
2
- export { PERSONALITY_OPTIONS, VOICE_OPTIONS, BIO_EXAMPLES, TRADING_STYLE_OPTIONS, SENTIMENT_OPTIONS, TIMEFRAME_OPTIONS, PROJECT_CATEGORY_OPTIONS, } from './options.js';
2
+ export { PERSONALITY_OPTIONS, VOICE_OPTIONS, BIO_EXAMPLES, TRADING_STYLE_OPTIONS, SENTIMENT_OPTIONS, TIMEFRAME_OPTIONS, PROJECT_CATEGORY_OPTIONS, DEFAULT_SECTOR_VALUES, } from './options.js';
3
3
  export { buildSoulMarkdown, buildStrategyMarkdown } from './formatting.js';
@@ -203,6 +203,14 @@ export const TIMEFRAME_OPTIONS = [
203
203
  description: 'Longer-term, macro-style predictions',
204
204
  },
205
205
  ];
206
+ export const DEFAULT_SECTOR_VALUES = new Set([
207
+ 'l1',
208
+ 'l2',
209
+ 'defi',
210
+ 'ai',
211
+ 'lending',
212
+ 'prediction-markets',
213
+ ]);
206
214
  export const PROJECT_CATEGORY_OPTIONS = [
207
215
  {
208
216
  label: '🔷 Layer 1',
@@ -224,6 +232,16 @@ export const PROJECT_CATEGORY_OPTIONS = [
224
232
  value: 'ai',
225
233
  description: 'AI and compute tokens — FET, RENDER, TAO, AKT, etc.',
226
234
  },
235
+ {
236
+ label: '🏛️ Lending',
237
+ value: 'lending',
238
+ description: 'Lending and borrowing protocols — AAVE, COMP, MORPHO, etc.',
239
+ },
240
+ {
241
+ label: '🎯 Prediction Markets',
242
+ value: 'prediction-markets',
243
+ description: 'Prediction and betting markets — POLY, GNO, etc.',
244
+ },
227
245
  {
228
246
  label: '🐸 Memecoins',
229
247
  value: 'meme',
@@ -254,16 +272,6 @@ export const PROJECT_CATEGORY_OPTIONS = [
254
272
  value: 'dex',
255
273
  description: 'Decentralized exchanges — UNI, SUSHI, CRV, JUP, etc.',
256
274
  },
257
- {
258
- label: '🏛️ Lending',
259
- value: 'lending',
260
- description: 'Lending and borrowing protocols — AAVE, COMP, MORPHO, etc.',
261
- },
262
- {
263
- label: '💵 Stablecoin',
264
- value: 'stablecoin',
265
- description: 'Stablecoin protocols and pegged assets — MKR, FXS, LQTY, etc.',
266
- },
267
275
  {
268
276
  label: '🌉 Cross-chain',
269
277
  value: 'cross-chain',
@@ -289,11 +297,6 @@ export const PROJECT_CATEGORY_OPTIONS = [
289
297
  value: 'social',
290
298
  description: 'Social and identity protocols — LENS, CYBER, ID, etc.',
291
299
  },
292
- {
293
- label: '🎯 Prediction Markets',
294
- value: 'prediction-markets',
295
- description: 'Prediction and betting markets — POLY, GNO, etc.',
296
- },
297
300
  {
298
301
  label: '🖼️ NFT',
299
302
  value: 'nft',
@@ -5,7 +5,7 @@ import { SelectPrompt } from '../../../../components/SelectPrompt.js';
5
5
  import { MultiSelectPrompt, } from '../../../../components/MultiSelectPrompt.js';
6
6
  import { TextPrompt } from '../../../../components/TextPrompt.js';
7
7
  import { CharacterSummaryCard } from '../../../../components/CharacterSummaryCard.js';
8
- import { PERSONALITY_OPTIONS, VOICE_OPTIONS, TRADING_STYLE_OPTIONS, PROJECT_CATEGORY_OPTIONS, SENTIMENT_OPTIONS, TIMEFRAME_OPTIONS, BIO_EXAMPLES, } from '../../presets/index.js';
8
+ import { PERSONALITY_OPTIONS, VOICE_OPTIONS, TRADING_STYLE_OPTIONS, PROJECT_CATEGORY_OPTIONS, SENTIMENT_OPTIONS, TIMEFRAME_OPTIONS, DEFAULT_SECTOR_VALUES, BIO_EXAMPLES, } from '../../presets/index.js';
9
9
  import { colors, symbols } from '../../../shared/theme.js';
10
10
  import { required, compose, maxLength } from '../validation.js';
11
11
  function buildSelectItems(options, addCustom = false) {
@@ -25,6 +25,7 @@ const tradingStyleItems = buildSelectItems(TRADING_STYLE_OPTIONS, true);
25
25
  const categoryItems = buildSelectItems(PROJECT_CATEGORY_OPTIONS);
26
26
  const sentimentItems = buildSelectItems(SENTIMENT_OPTIONS);
27
27
  const timeframeItems = buildSelectItems(TIMEFRAME_OPTIONS);
28
+ const timeframeDefaultSelected = new Set(TIMEFRAME_OPTIONS.filter((opt) => opt.value !== '1h').map((opt) => opt.value));
28
29
  export function IdentityStep({ agentName, onComplete }) {
29
30
  const [subStep, setSubStep] = useState('personality');
30
31
  const [personalityLabel, setPersonalityLabel] = useState('');
@@ -121,5 +122,5 @@ export function IdentityStep({ agentName, onComplete }) {
121
122
  };
122
123
  onComplete(result);
123
124
  }, [personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, onComplete]);
124
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(CharacterSummaryCard, { name: agentName, personality: personalityLabel || undefined, voice: voiceLabel || undefined, tradingStyle: tradingStyleLabel || undefined, sectors: sectorsLabel || undefined, sentiment: sentimentLabel || undefined, timeframe: timeframesLabel || undefined }), subStep === 'personality' && (_jsx(SelectPrompt, { label: "Choose a personality", items: personalityItems, onSelect: handlePersonalitySelect })), subStep === 'personality-custom' && (_jsx(TextPrompt, { label: "Describe your agent's personality", placeholder: "e.g. stoic realist with a dry wit", onSubmit: handlePersonalityCustom, validate: required('Personality') })), subStep === 'voice' && (_jsx(SelectPrompt, { label: "Choose a voice", items: voiceItems, onSelect: handleVoiceSelect })), subStep === 'voice-custom' && (_jsx(TextPrompt, { label: "Describe your agent's voice", placeholder: "e.g. writes like a bloomberg terminal on acid", onSubmit: handleVoiceCustom, validate: required('Voice') })), subStep === 'trading' && (_jsx(SelectPrompt, { label: "How does your agent evaluate signals?", items: tradingStyleItems, onSelect: handleTradingStyleSelect })), subStep === 'trading-custom' && (_jsx(TextPrompt, { label: "Describe your agent's trading style", placeholder: "e.g. combines on-chain data with sentiment analysis", onSubmit: handleTradingStyleCustom, validate: required('Trading style') })), subStep === 'sectors' && (_jsx(MultiSelectPrompt, { label: "Which categories should your agent trade?", items: categoryItems, onSubmit: handleSectors })), subStep === 'sentiment' && (_jsx(SelectPrompt, { label: "What's your agent's market sentiment?", items: sentimentItems, onSelect: handleSentimentSelect })), subStep === 'timeframe' && (_jsx(MultiSelectPrompt, { label: "Which timeframes should your agent participate in?", items: timeframeItems, onSubmit: handleTimeframes })), subStep === 'bio' && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: colors.grayDim, italic: true, children: [symbols.arrow, " Examples:"] }), BIO_EXAMPLES.map((example, i) => (_jsx(Box, { marginLeft: 2, marginTop: i > 0 ? 1 : 0, children: _jsxs(Text, { color: colors.grayDim, italic: true, children: [symbols.diamond, " ", `"${example}"`] }) }, i)))] }), _jsx(TextPrompt, { label: "Write your agent's bio", placeholder: `short bio for your ${personalityLabel} agent`, onSubmit: handleBio, maxLength: 1000, validate: compose(required('Bio'), maxLength(1000)) })] }))] }));
125
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(CharacterSummaryCard, { name: agentName, personality: personalityLabel || undefined, voice: voiceLabel || undefined, tradingStyle: tradingStyleLabel || undefined, sectors: sectorsLabel || undefined, sentiment: sentimentLabel || undefined, timeframe: timeframesLabel || undefined }), subStep === 'personality' && (_jsx(SelectPrompt, { label: "Choose a personality", items: personalityItems, onSelect: handlePersonalitySelect })), subStep === 'personality-custom' && (_jsx(TextPrompt, { label: "Describe your agent's personality", placeholder: "e.g. stoic realist with a dry wit", onSubmit: handlePersonalityCustom, validate: required('Personality') })), subStep === 'voice' && (_jsx(SelectPrompt, { label: "Choose a voice", items: voiceItems, onSelect: handleVoiceSelect })), subStep === 'voice-custom' && (_jsx(TextPrompt, { label: "Describe your agent's voice", placeholder: "e.g. writes like a bloomberg terminal on acid", onSubmit: handleVoiceCustom, validate: required('Voice') })), subStep === 'trading' && (_jsx(SelectPrompt, { label: "How does your agent evaluate signals?", items: tradingStyleItems, onSelect: handleTradingStyleSelect })), subStep === 'trading-custom' && (_jsx(TextPrompt, { label: "Describe your agent's trading style", placeholder: "e.g. combines on-chain data with sentiment analysis", onSubmit: handleTradingStyleCustom, validate: required('Trading style') })), subStep === 'sectors' && (_jsx(MultiSelectPrompt, { label: "Which categories should your agent trade?", items: categoryItems, defaultSelected: DEFAULT_SECTOR_VALUES, hint: "Recommended categories selected \u2014 press spacebar to toggle", onSubmit: handleSectors })), subStep === 'sentiment' && (_jsx(SelectPrompt, { label: "What's your agent's market sentiment?", items: sentimentItems, onSelect: handleSentimentSelect })), subStep === 'timeframe' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { color: colors.hot, bold: true, children: [symbols.arrow, " Warning: Selecting the 1h timeframe will significantly increase token usage."] }) }), _jsx(MultiSelectPrompt, { label: "Which timeframes should your agent participate in?", items: timeframeItems, defaultSelected: timeframeDefaultSelected, hint: "4h and 24h selected by default \u2014 press spacebar to toggle", onSubmit: handleTimeframes })] })), subStep === 'bio' && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: colors.grayDim, italic: true, children: [symbols.arrow, " Examples:"] }), BIO_EXAMPLES.map((example, i) => (_jsx(Box, { marginLeft: 2, marginTop: i > 0 ? 1 : 0, children: _jsxs(Text, { color: colors.grayDim, italic: true, children: [symbols.diamond, " ", `"${example}"`] }) }, i)))] }), _jsx(TextPrompt, { label: "Write your agent's bio", placeholder: `short bio for your ${personalityLabel} agent`, onSubmit: handleBio, maxLength: 1000, validate: compose(required('Bio'), maxLength(1000)) })] }))] }));
125
126
  }
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import path from 'path';
3
3
  import fsExtra from 'fs-extra';
4
- import { HiveClient, loadConfig } from '@zhive/sdk';
4
+ import { HiveClient } from '@zhive/sdk';
5
5
  import { styled, symbols } from '../../shared/theme.js';
6
6
  import { getHiveDir, HIVE_API_URL } from '../../../shared/config/constant.js';
7
7
  import { loadAgentConfig } from '../../../shared/config/agent.js';
@@ -13,26 +13,20 @@ async function checkAgent(agentDir, dirName) {
13
13
  configError: null,
14
14
  registrationStatus: 'skipped',
15
15
  };
16
- let configLoaded = false;
16
+ let config;
17
17
  try {
18
- const config = await loadAgentConfig(agentDir);
18
+ config = await loadAgentConfig(agentDir);
19
19
  result.name = config.name;
20
- configLoaded = true;
21
20
  }
22
21
  catch (err) {
23
22
  const message = err instanceof Error ? err.message : String(err);
24
23
  result.configError = message;
25
24
  }
26
- if (!configLoaded) {
27
- return result;
28
- }
29
- const credentials = await loadConfig(agentDir);
30
- if (!credentials?.apiKey) {
31
- result.registrationStatus = 'no-api-key';
25
+ if (!config) {
32
26
  return result;
33
27
  }
34
28
  try {
35
- const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
29
+ const client = new HiveClient(HIVE_API_URL, config.apiKey);
36
30
  await client.getMe();
37
31
  result.registrationStatus = 'registered';
38
32
  }
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import { z } from 'zod';
3
- import { HiveClient, loadConfig } from '@zhive/sdk';
3
+ import { HiveClient } from '@zhive/sdk';
4
4
  import { styled, symbols } from '../../shared/theme.js';
5
5
  import { HIVE_API_URL } from '../../../shared/config/constant.js';
6
6
  import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
@@ -37,12 +37,7 @@ export function createMegathreadCreateCommentCommand() {
37
37
  }
38
38
  process.exit(1);
39
39
  }
40
- const credentials = await loadConfig(agentConfig.dir);
41
- if (!credentials?.apiKey) {
42
- console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
43
- process.exit(1);
44
- }
45
- const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
40
+ const client = new HiveClient(HIVE_API_URL, agentConfig.apiKey);
46
41
  const payload = {
47
42
  text,
48
43
  conviction,
@@ -17,7 +17,6 @@ vi.mock('@zhive/sdk', async () => {
17
17
  HiveClient: vi.fn().mockImplementation(() => ({
18
18
  postMegathreadComment: vi.fn(),
19
19
  })),
20
- loadConfig: vi.fn(),
21
20
  };
22
21
  });
23
22
  vi.mock('../../shared/theme.js', () => ({
@@ -31,10 +30,9 @@ vi.mock('../../shared/theme.js', () => ({
31
30
  check: '✓',
32
31
  },
33
32
  }));
34
- import { HiveClient, loadConfig } from '@zhive/sdk';
33
+ import { HiveClient } from '@zhive/sdk';
35
34
  import { createMegathreadCreateCommentCommand } from './create-comment.js';
36
35
  const MockHiveClient = HiveClient;
37
- const mockLoadConfig = loadConfig;
38
36
  describe('createMegathreadCreateCommentCommand', () => {
39
37
  let consoleLogSpy;
40
38
  let consoleErrorSpy;
@@ -114,7 +112,6 @@ describe('createMegathreadCreateCommentCommand', () => {
114
112
  expect(consoleErrorOutput.join('\n')).toContain('number');
115
113
  });
116
114
  it('accepts valid conviction at upper boundary', async () => {
117
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
118
115
  mockPostMegathreadComment.mockResolvedValue(undefined);
119
116
  const command = createMegathreadCreateCommentCommand();
120
117
  await command.parseAsync([
@@ -133,7 +130,6 @@ describe('createMegathreadCreateCommentCommand', () => {
133
130
  });
134
131
  });
135
132
  it('accepts valid conviction at lower boundary', async () => {
136
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
137
133
  mockPostMegathreadComment.mockResolvedValue(undefined);
138
134
  const command = createMegathreadCreateCommentCommand();
139
135
  await command.parseAsync([
@@ -152,7 +148,6 @@ describe('createMegathreadCreateCommentCommand', () => {
152
148
  });
153
149
  });
154
150
  it('accepts decimal conviction values', async () => {
155
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
156
151
  mockPostMegathreadComment.mockResolvedValue(undefined);
157
152
  const command = createMegathreadCreateCommentCommand();
158
153
  await command.parseAsync([
@@ -193,26 +188,10 @@ describe('createMegathreadCreateCommentCommand', () => {
193
188
  });
194
189
  describe('credentials validation', () => {
195
190
  it('shows error when credentials are missing', async () => {
196
- mockLoadConfig.mockResolvedValue(null);
197
191
  const command = createMegathreadCreateCommentCommand();
198
192
  await expect(command.parseAsync([
199
193
  '--agent',
200
- 'test-agent',
201
- '--round',
202
- 'round-123',
203
- '--conviction',
204
- '50',
205
- '--text',
206
- 'Test comment',
207
- ], { from: 'user' })).rejects.toThrow('process.exit(1)');
208
- expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
209
- });
210
- it('shows error when credentials have no API key', async () => {
211
- mockLoadConfig.mockResolvedValue({ apiKey: null });
212
- const command = createMegathreadCreateCommentCommand();
213
- await expect(command.parseAsync([
214
- '--agent',
215
- 'test-agent',
194
+ 'no-cred',
216
195
  '--round',
217
196
  'round-123',
218
197
  '--conviction',
@@ -220,12 +199,11 @@ describe('createMegathreadCreateCommentCommand', () => {
220
199
  '--text',
221
200
  'Test comment',
222
201
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
223
- expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
202
+ expect(consoleErrorOutput.join('\n')).toContain('Agent "no-cred" not found');
224
203
  });
225
204
  });
226
205
  describe('successful comment posting', () => {
227
206
  it('posts comment and shows success message', async () => {
228
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
229
207
  mockPostMegathreadComment.mockResolvedValue(undefined);
230
208
  const command = createMegathreadCreateCommentCommand();
231
209
  await command.parseAsync([
@@ -249,7 +227,6 @@ describe('createMegathreadCreateCommentCommand', () => {
249
227
  expect(output).toContain('Bullish on Bitcoin!');
250
228
  });
251
229
  it('formats negative conviction correctly', async () => {
252
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
253
230
  mockPostMegathreadComment.mockResolvedValue(undefined);
254
231
  const command = createMegathreadCreateCommentCommand();
255
232
  await command.parseAsync([
@@ -266,7 +243,6 @@ describe('createMegathreadCreateCommentCommand', () => {
266
243
  expect(output).toContain('-30.0%');
267
244
  });
268
245
  it('truncates long text in success message', async () => {
269
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
270
246
  mockPostMegathreadComment.mockResolvedValue(undefined);
271
247
  const longText = 'A'.repeat(100);
272
248
  const command = createMegathreadCreateCommentCommand();
@@ -283,7 +259,6 @@ describe('createMegathreadCreateCommentCommand', () => {
283
259
  });
284
260
  describe('API error handling', () => {
285
261
  it('shows error when API call fails', async () => {
286
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
287
262
  mockPostMegathreadComment.mockRejectedValue(new Error('Network error'));
288
263
  const command = createMegathreadCreateCommentCommand();
289
264
  await expect(command.parseAsync([
@@ -300,7 +275,6 @@ describe('createMegathreadCreateCommentCommand', () => {
300
275
  expect(consoleErrorOutput.join('\n')).toContain('Network error');
301
276
  });
302
277
  it('handles non-Error exceptions', async () => {
303
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
304
278
  mockPostMegathreadComment.mockRejectedValue('String error');
305
279
  const command = createMegathreadCreateCommentCommand();
306
280
  await expect(command.parseAsync([
@@ -319,7 +293,6 @@ describe('createMegathreadCreateCommentCommand', () => {
319
293
  });
320
294
  describe('works with different fixture agents', () => {
321
295
  it('works with empty-agent', async () => {
322
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
323
296
  mockPostMegathreadComment.mockResolvedValue(undefined);
324
297
  const command = createMegathreadCreateCommentCommand();
325
298
  await command.parseAsync([
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import { z } from 'zod';
3
- import { HiveClient, loadConfig, } from '@zhive/sdk';
3
+ import { HiveClient } from '@zhive/sdk';
4
4
  import { styled, symbols } from '../../shared/theme.js';
5
5
  import { HIVE_API_URL } from '../../../shared/config/constant.js';
6
6
  import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
@@ -52,12 +52,7 @@ export function createMegathreadCreateCommentsCommand() {
52
52
  }
53
53
  process.exit(1);
54
54
  }
55
- const credentials = await loadConfig(agentConfig.dir);
56
- if (!credentials?.apiKey) {
57
- console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
58
- process.exit(1);
59
- }
60
- const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
55
+ const client = new HiveClient(HIVE_API_URL, agentConfig.apiKey);
61
56
  const payload = {
62
57
  comments: [],
63
58
  };
@@ -1,10 +1,10 @@
1
+ import { durationMsToTimeframe, HiveClient } from '@zhive/sdk';
1
2
  import { Command } from 'commander';
2
- import { HiveClient, durationMsToTimeframe, loadConfig } from '@zhive/sdk';
3
- import { styled, symbols, border } from '../../shared/theme.js';
4
- import { HIVE_API_URL } from '../../../shared/config/constant.js';
5
- import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
6
3
  import z from 'zod';
4
+ import { findAgentByName, scanAgents } from '../../../shared/config/agent.js';
5
+ import { HIVE_API_URL } from '../../../shared/config/constant.js';
7
6
  import { printZodError } from '../../shared/ validation.js';
7
+ import { border, styled, symbols } from '../../shared/theme.js';
8
8
  const VALID_TIMEFRAMES = ['1h', '4h', '24h'];
9
9
  const ListMegathreadOptionsSchema = z.object({
10
10
  agent: z.string(),
@@ -50,12 +50,7 @@ export function createMegathreadListCommand() {
50
50
  }
51
51
  process.exit(1);
52
52
  }
53
- const config = await loadConfig(agentConfig.dir);
54
- if (!config?.apiKey) {
55
- console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
56
- process.exit(1);
57
- }
58
- const client = new HiveClient(HIVE_API_URL, config.apiKey);
53
+ const client = new HiveClient(HIVE_API_URL, agentConfig.apiKey);
59
54
  try {
60
55
  const rounds = await client.getUnpredictedRounds(timeframes);
61
56
  console.log('');
@@ -17,7 +17,6 @@ vi.mock('@zhive/sdk', async () => {
17
17
  HiveClient: vi.fn().mockImplementation(() => ({
18
18
  getUnpredictedRounds: vi.fn(),
19
19
  })),
20
- loadConfig: vi.fn(),
21
20
  TIMEFRAME_DURATION_MS: {
22
21
  H1: 3600000,
23
22
  H4: 14400000,
@@ -30,10 +29,9 @@ vi.mock('@zhive/sdk', async () => {
30
29
  },
31
30
  };
32
31
  });
33
- import { HiveClient, loadConfig } from '@zhive/sdk';
32
+ import { HiveClient } from '@zhive/sdk';
34
33
  import { createMegathreadListCommand } from './list.js';
35
34
  const MockHiveClient = HiveClient;
36
- const mockLoadConfig = loadConfig;
37
35
  function createMockActiveRound(overrides = {}) {
38
36
  return {
39
37
  roundId: 'round-123',
@@ -88,21 +86,18 @@ describe('createMegathreadListCommand', () => {
88
86
  expect(consoleErrorOutput.length).toBeGreaterThan(0);
89
87
  });
90
88
  it('accepts valid timeframe values', async () => {
91
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
92
89
  mockGetUnpredictedRounds.mockResolvedValue([]);
93
90
  const command = createMegathreadListCommand();
94
91
  await command.parseAsync(['--agent', 'test-agent', '--timeframe', '1h,4h'], { from: 'user' });
95
92
  expect(mockGetUnpredictedRounds).toHaveBeenCalledWith(['1h', '4h']);
96
93
  });
97
94
  it('accepts single valid timeframe', async () => {
98
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
99
95
  mockGetUnpredictedRounds.mockResolvedValue([]);
100
96
  const command = createMegathreadListCommand();
101
97
  await command.parseAsync(['--agent', 'test-agent', '--timeframe', '24h'], { from: 'user' });
102
98
  expect(mockGetUnpredictedRounds).toHaveBeenCalledWith(['24h']);
103
99
  });
104
100
  it('passes undefined when no timeframe filter specified', async () => {
105
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
106
101
  mockGetUnpredictedRounds.mockResolvedValue([]);
107
102
  const command = createMegathreadListCommand();
108
103
  await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
@@ -122,21 +117,13 @@ describe('createMegathreadListCommand', () => {
122
117
  });
123
118
  describe('credentials validation', () => {
124
119
  it('shows error when credentials are missing', async () => {
125
- mockLoadConfig.mockResolvedValue(null);
126
120
  const command = createMegathreadListCommand();
127
- await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
128
- expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
129
- });
130
- it('shows error when credentials have no API key', async () => {
131
- mockLoadConfig.mockResolvedValue({ apiKey: null });
132
- const command = createMegathreadListCommand();
133
- await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
134
- expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
121
+ await expect(command.parseAsync(['--agent', 'no-cred-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
122
+ expect(consoleErrorOutput.join('\n')).toContain('Agent "no-cred-agent" not found');
135
123
  });
136
124
  });
137
125
  describe('rounds display', () => {
138
126
  it('shows message when no unpredicted rounds available', async () => {
139
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
140
127
  mockGetUnpredictedRounds.mockResolvedValue([]);
141
128
  const command = createMegathreadListCommand();
142
129
  await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
@@ -149,7 +136,6 @@ describe('createMegathreadListCommand', () => {
149
136
  createMockActiveRound({ roundId: 'round-1', projectId: 'bitcoin', durationMs: 3600000 }),
150
137
  createMockActiveRound({ roundId: 'round-2', projectId: 'ethereum', durationMs: 14400000 }),
151
138
  ];
152
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
153
139
  mockGetUnpredictedRounds.mockResolvedValue(mockRounds);
154
140
  const command = createMegathreadListCommand();
155
141
  await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
@@ -168,7 +154,6 @@ describe('createMegathreadListCommand', () => {
168
154
  const mockRounds = [
169
155
  createMockActiveRound({ roundId: 'round-1', projectId: 'bitcoin', durationMs: 7200000 }),
170
156
  ];
171
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
172
157
  mockGetUnpredictedRounds.mockResolvedValue(mockRounds);
173
158
  const command = createMegathreadListCommand();
174
159
  await command.parseAsync(['--agent', 'test-agent'], { from: 'user' });
@@ -178,7 +163,6 @@ describe('createMegathreadListCommand', () => {
178
163
  });
179
164
  describe('API error handling', () => {
180
165
  it('shows error when API call fails', async () => {
181
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
182
166
  mockGetUnpredictedRounds.mockRejectedValue(new Error('Network error'));
183
167
  const command = createMegathreadListCommand();
184
168
  await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
@@ -186,7 +170,6 @@ describe('createMegathreadListCommand', () => {
186
170
  expect(consoleErrorOutput.join('\n')).toContain('Network error');
187
171
  });
188
172
  it('handles non-Error exceptions', async () => {
189
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
190
173
  mockGetUnpredictedRounds.mockRejectedValue('String error');
191
174
  const command = createMegathreadListCommand();
192
175
  await expect(command.parseAsync(['--agent', 'test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
@@ -196,7 +179,6 @@ describe('createMegathreadListCommand', () => {
196
179
  });
197
180
  describe('works with different fixture agents', () => {
198
181
  it('works with empty-agent', async () => {
199
- mockLoadConfig.mockResolvedValue({ apiKey: 'test-api-key' });
200
182
  mockGetUnpredictedRounds.mockResolvedValue([]);
201
183
  const command = createMegathreadListCommand();
202
184
  await command.parseAsync(['--agent', 'empty-agent'], { from: 'user' });
@@ -112,7 +112,7 @@ export function MigrateApp() {
112
112
  if (phase === 'selecting') {
113
113
  const oldStyleAgents = agents.filter((a) => a.isOldStyle);
114
114
  const newStyleAgents = agents.filter((a) => !a.isOldStyle);
115
- return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migrate agents to @zhive/cli"] }), _jsx(Text, { color: colors.gray, children: border.horizontal.repeat(termWidth - 4) }), _jsxs(Text, { color: colors.gray, children: ["Use ", styled.white('↑↓'), " to navigate, ", styled.white('space'), " to toggle,", ' ', styled.white('enter'), " to confirm"] }), _jsx(Text, { children: " " }), oldStyleAgents.map((agent, i) => {
115
+ return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migrate agents to @zhive/cli"] }), _jsx(Text, { color: colors.gray, children: border.horizontal.repeat(termWidth - 4) }), _jsxs(Text, { color: colors.gray, children: ["Use ", styled.white('↑↓'), " to navigate, ", styled.white('spacebar'), " press to toggle,", ' ', styled.white('enter'), " to confirm"] }), _jsx(Text, { children: " " }), oldStyleAgents.map((agent, i) => {
116
116
  const isCursor = i === cursor;
117
117
  const prefix = agent.selected ? symbols.check : symbols.diamondOpen;
118
118
  const prefixColor = agent.selected ? colors.green : colors.gray;
@@ -1,7 +1,7 @@
1
- import { loadConfig } from '@zhive/sdk';
2
1
  import { HIVE_API_URL } from '../../../shared/config/constant.js';
3
2
  import { extractErrorMessage } from '../../../shared/agent/utils.js';
4
3
  import { styled } from '../../shared/theme.js';
4
+ import { loadConfig } from '@zhive/sdk';
5
5
  export async function fetchMyPredictions(apiKey, limit = 10) {
6
6
  try {
7
7
  const url = `${HIVE_API_URL}/megathread-comment/me?limit=${limit}&onlyResolved=true`;
@@ -3,9 +3,8 @@ import { fileURLToPath } from 'node:url';
3
3
  import * as path from 'node:path';
4
4
  import { fetchSkills, formatSkills, skillsSlashCommand } from './skills.js';
5
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- const FIXTURES_DIR = path.join(__dirname, '../../../tests/__fixtures__/mock-hive');
7
6
  vi.mock('../../../shared/config/constant.js', () => ({
8
- getHiveDir: vi.fn(() => path.join(__dirname, '../../../tests/__fixtures__/mock-hive')),
7
+ getHiveDir: vi.fn(() => path.join(__dirname, '../../../../__fixtures__/mock-hive')),
9
8
  }));
10
9
  function createMockSkill(overrides = {}) {
11
10
  const skill = {
@@ -2,7 +2,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Box, Text, useInput } from 'ink';
4
4
  import { colors, symbols } from '../commands/shared/theme.js';
5
- export function MultiSelectPrompt({ label, items, defaultSelected, onSubmit, }) {
5
+ export function MultiSelectPrompt({ label, items, defaultSelected, hint, onSubmit, }) {
6
6
  const allValues = new Set(items.map((i) => i.value));
7
7
  const [selected, setSelected] = useState(defaultSelected ?? allValues);
8
8
  const [cursor, setCursor] = useState(0);
@@ -35,11 +35,11 @@ export function MultiSelectPrompt({ label, items, defaultSelected, onSubmit, })
35
35
  });
36
36
  const highlightedItem = items[cursor];
37
37
  const highlightedDescription = highlightedItem?.description;
38
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [symbols.arrow, " "] }), _jsx(Text, { color: colors.white, bold: true, children: label })] }), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsx(Text, { color: colors.grayDim, italic: true, children: "All selected by default \u2014 deselect what you don't want" }) }), _jsx(Box, { flexDirection: "column", marginLeft: 2, children: items.map((item, i) => {
38
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [symbols.arrow, " "] }), _jsx(Text, { color: colors.white, bold: true, children: label })] }), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsx(Text, { color: colors.grayDim, italic: true, children: hint ?? "All selected by default deselect what you don't want" }) }), _jsx(Box, { flexDirection: "column", marginLeft: 2, children: items.map((item, i) => {
39
39
  const isCursor = i === cursor;
40
40
  const isSelected = selected.has(item.value);
41
41
  const checkbox = isSelected ? '◆' : '◇';
42
42
  const itemColor = isCursor ? colors.honey : colors.white;
43
43
  return (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [isCursor ? symbols.diamond : ' ', " "] }), _jsxs(Text, { color: isSelected ? colors.honey : colors.grayDim, children: [checkbox, " "] }), _jsx(Text, { color: itemColor, children: item.label })] }, item.value));
44
- }) }), highlightedDescription && (_jsx(Box, { marginLeft: 4, marginTop: 1, children: _jsxs(Text, { color: colors.gray, italic: true, children: [symbols.arrow, " ", highlightedDescription] }) })), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { color: colors.grayDim, children: [_jsx(Text, { color: colors.honey, children: "space" }), " toggle ", _jsx(Text, { color: colors.honey, children: "enter" }), ' ', "confirm"] }) })] }));
44
+ }) }), highlightedDescription && (_jsx(Box, { marginLeft: 4, marginTop: 1, children: _jsxs(Text, { color: colors.gray, italic: true, children: [symbols.arrow, " ", highlightedDescription] }) })), _jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { color: colors.grayDim, children: [_jsx(Text, { color: colors.honey, children: "spacebar" }), " press to toggle ", _jsx(Text, { color: colors.honey, children: "enter" }), ' ', "confirm"] }) })] }));
45
45
  }
@@ -63,6 +63,9 @@ export async function loadAgentConfig(_agentDir) {
63
63
  if (!avatarUrl) {
64
64
  throw new Error('Missing avatarUrl');
65
65
  }
66
+ if (!config.apiKey) {
67
+ throw new Error('Missing api key');
68
+ }
66
69
  const bioRaw = extractField(soulContent, /^## Bio\s*\n+(.+)$/m);
67
70
  const bio = bioRaw ?? null;
68
71
  const sentimentRaw = extractField(strategyContent, /^-\s+Bias:\s+(.+)$/m);
@@ -79,6 +82,7 @@ export async function loadAgentConfig(_agentDir) {
79
82
  name,
80
83
  bio,
81
84
  dir: agentDir,
85
+ apiKey: config.apiKey,
82
86
  provider,
83
87
  avatarUrl,
84
88
  soulContent,
@@ -13,12 +13,7 @@ vi.mock('./ai-providers.js', () => ({
13
13
  { label: 'Anthropic', package: '@ai-sdk/anthropic', envVar: 'ANTHROPIC_API_KEY' },
14
14
  ],
15
15
  }));
16
- vi.mock('@zhive/sdk', () => ({
17
- loadConfig: vi.fn(),
18
- }));
19
- import { loadConfig } from '@zhive/sdk';
20
16
  import { findAgentByName, scanAgents } from './agent.js';
21
- const mockLoadConfig = loadConfig;
22
17
  describe('scanAgents', () => {
23
18
  beforeEach(() => {
24
19
  vi.clearAllMocks();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhive/cli",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
4
4
  "description": "CLI for bootstrapping zHive AI Agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",