@zhive/cli 0.6.1 → 0.6.3

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.
@@ -4,37 +4,14 @@ 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, loadAgentCredentials, scanAgents } from '../../../shared/config/agent.js';
7
+ import { printZodError } from '../../shared/ validation.js';
7
8
  const CreateCommentOptionsSchema = z.object({
8
9
  agent: z.string().min(1),
9
10
  round: z.string().min(1),
10
- conviction: z.string().transform((val, ctx) => {
11
- const num = parseFloat(val);
12
- if (isNaN(num)) {
13
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Must be a number. Got: ${val}` });
14
- return z.NEVER;
15
- }
16
- if (num < -100 || num > 100) {
17
- ctx.addIssue({
18
- code: 'custom',
19
- message: `Must be between -100 and 100. Got: ${val}`,
20
- });
21
- return z.NEVER;
22
- }
23
- return num;
24
- }),
11
+ conviction: z.coerce.number().min(-100).max(100),
25
12
  text: z.string().min(1),
26
13
  token: z.string().min(1),
27
- duration: z.string().transform((val, ctx) => {
28
- const num = parseInt(val, 10);
29
- if (isNaN(num) || num <= 0) {
30
- ctx.addIssue({
31
- code: 'custom',
32
- message: `Must be a positive number. Got: ${val}`,
33
- });
34
- return z.NEVER;
35
- }
36
- return num;
37
- }),
14
+ timeframe: z.enum(['1h', '4h', '24h']),
38
15
  });
39
16
  export function createMegathreadCreateCommentCommand() {
40
17
  return new Command('create-comment')
@@ -44,17 +21,14 @@ export function createMegathreadCreateCommentCommand() {
44
21
  .requiredOption('--conviction <number>', 'Conviction score (-100 to 100)')
45
22
  .requiredOption('--text <text>', 'Comment text')
46
23
  .requiredOption('--token <tokenId>', 'Token/project ID')
47
- .requiredOption('--duration <ms>', 'Round duration in milliseconds')
24
+ .requiredOption('--timeframe <tf>', 'Timeframe (1h, 4h, 24h)')
48
25
  .action(async (options) => {
49
26
  const parseResult = CreateCommentOptionsSchema.safeParse(options);
50
27
  if (!parseResult.success) {
51
- const errors = parseResult.error.issues
52
- .map((e) => `${e.path.join('.')}: ${e.message}`)
53
- .join(', ');
54
- console.error(styled.red(`${symbols.cross} Validation error: ${errors}`));
28
+ printZodError(parseResult);
55
29
  process.exit(1);
56
30
  }
57
- const { agent: agentName, round: roundId, conviction, text, token, duration, } = parseResult.data;
31
+ const { agent: agentName, round: roundId, conviction, text, token, timeframe, } = parseResult.data;
58
32
  const agentConfig = await findAgentByName(agentName);
59
33
  if (!agentConfig) {
60
34
  const agents = await scanAgents();
@@ -76,8 +50,6 @@ export function createMegathreadCreateCommentCommand() {
76
50
  const payload = {
77
51
  text,
78
52
  conviction,
79
- tokenId: token,
80
- roundDuration: duration,
81
53
  };
82
54
  try {
83
55
  await client.postMegathreadComment(roundId, payload);
@@ -8,9 +8,7 @@ vi.mock('../../../shared/config/constant.js', () => ({
8
8
  HIVE_API_URL: 'http://localhost:6969',
9
9
  }));
10
10
  vi.mock('../../../shared/config/ai-providers.js', () => ({
11
- AI_PROVIDERS: [
12
- { label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' },
13
- ],
11
+ AI_PROVIDERS: [{ label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' }],
14
12
  }));
15
13
  vi.mock('@zhive/sdk', async () => {
16
14
  const actual = await vi.importActual('@zhive/sdk');
@@ -83,11 +81,11 @@ describe('createMegathreadCreateCommentCommand', () => {
83
81
  'Test comment',
84
82
  '--token',
85
83
  'bitcoin',
86
- '--duration',
87
- '3600000',
84
+ '--timeframe',
85
+ '1h',
88
86
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
89
- expect(consoleErrorOutput.join('\n')).toContain('conviction: Must be between -100 and 100');
90
- expect(consoleErrorOutput.join('\n')).toContain('Got: 150');
87
+ expect(consoleErrorOutput.join('\n')).toContain('conviction');
88
+ expect(consoleErrorOutput.join('\n')).toContain('100');
91
89
  });
92
90
  it('shows error when conviction is too low', async () => {
93
91
  const command = createMegathreadCreateCommentCommand();
@@ -102,11 +100,11 @@ describe('createMegathreadCreateCommentCommand', () => {
102
100
  'Test comment',
103
101
  '--token',
104
102
  'bitcoin',
105
- '--duration',
106
- '3600000',
103
+ '--timeframe',
104
+ '1h',
107
105
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
108
- expect(consoleErrorOutput.join('\n')).toContain('conviction: Must be between -100 and 100');
109
- expect(consoleErrorOutput.join('\n')).toContain('Got: -150');
106
+ expect(consoleErrorOutput.join('\n')).toContain('conviction');
107
+ expect(consoleErrorOutput.join('\n')).toContain('-100');
110
108
  });
111
109
  it('shows error when conviction is not a number', async () => {
112
110
  const command = createMegathreadCreateCommentCommand();
@@ -121,11 +119,11 @@ describe('createMegathreadCreateCommentCommand', () => {
121
119
  'Test comment',
122
120
  '--token',
123
121
  'bitcoin',
124
- '--duration',
125
- '3600000',
122
+ '--timeframe',
123
+ '1h',
126
124
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
127
- expect(consoleErrorOutput.join('\n')).toContain('conviction: Must be a number');
128
- expect(consoleErrorOutput.join('\n')).toContain('Got: abc');
125
+ expect(consoleErrorOutput.join('\n')).toContain('conviction');
126
+ expect(consoleErrorOutput.join('\n')).toContain('number');
129
127
  });
130
128
  it('accepts valid conviction at upper boundary', async () => {
131
129
  mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
@@ -142,14 +140,12 @@ describe('createMegathreadCreateCommentCommand', () => {
142
140
  'Test comment',
143
141
  '--token',
144
142
  'bitcoin',
145
- '--duration',
146
- '3600000',
143
+ '--timeframe',
144
+ '1h',
147
145
  ], { from: 'user' });
148
146
  expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
149
147
  text: 'Test comment',
150
148
  conviction: 100,
151
- tokenId: 'bitcoin',
152
- roundDuration: 3600000,
153
149
  });
154
150
  });
155
151
  it('accepts valid conviction at lower boundary', async () => {
@@ -167,14 +163,12 @@ describe('createMegathreadCreateCommentCommand', () => {
167
163
  'Test comment',
168
164
  '--token',
169
165
  'bitcoin',
170
- '--duration',
171
- '3600000',
166
+ '--timeframe',
167
+ '1h',
172
168
  ], { from: 'user' });
173
169
  expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
174
170
  text: 'Test comment',
175
171
  conviction: -100,
176
- tokenId: 'bitcoin',
177
- roundDuration: 3600000,
178
172
  });
179
173
  });
180
174
  it('accepts decimal conviction values', async () => {
@@ -192,19 +186,17 @@ describe('createMegathreadCreateCommentCommand', () => {
192
186
  'Test comment',
193
187
  '--token',
194
188
  'bitcoin',
195
- '--duration',
196
- '3600000',
189
+ '--timeframe',
190
+ '1h',
197
191
  ], { from: 'user' });
198
192
  expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
199
193
  text: 'Test comment',
200
194
  conviction: 25.5,
201
- tokenId: 'bitcoin',
202
- roundDuration: 3600000,
203
195
  });
204
196
  });
205
197
  });
206
- describe('duration validation', () => {
207
- it('shows error when duration is negative', async () => {
198
+ describe('timeframe validation', () => {
199
+ it('shows error for invalid timeframe value', async () => {
208
200
  const command = createMegathreadCreateCommentCommand();
209
201
  await expect(command.parseAsync([
210
202
  '--agent',
@@ -217,15 +209,15 @@ describe('createMegathreadCreateCommentCommand', () => {
217
209
  'Test comment',
218
210
  '--token',
219
211
  'bitcoin',
220
- '--duration',
221
- '-1',
212
+ '--timeframe',
213
+ '2h',
222
214
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
223
- expect(consoleErrorOutput.join('\n')).toContain('duration: Must be a positive number');
224
- expect(consoleErrorOutput.join('\n')).toContain('Got: -1');
225
215
  });
226
- it('shows error when duration is zero', async () => {
216
+ it('accepts 1h timeframe', async () => {
217
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
218
+ mockPostMegathreadComment.mockResolvedValue(undefined);
227
219
  const command = createMegathreadCreateCommentCommand();
228
- await expect(command.parseAsync([
220
+ await command.parseAsync([
229
221
  '--agent',
230
222
  'test-agent',
231
223
  '--round',
@@ -236,15 +228,19 @@ describe('createMegathreadCreateCommentCommand', () => {
236
228
  'Test comment',
237
229
  '--token',
238
230
  'bitcoin',
239
- '--duration',
240
- '0',
241
- ], { from: 'user' })).rejects.toThrow('process.exit(1)');
242
- expect(consoleErrorOutput.join('\n')).toContain('duration: Must be a positive number');
243
- expect(consoleErrorOutput.join('\n')).toContain('Got: 0');
231
+ '--timeframe',
232
+ '1h',
233
+ ], { from: 'user' });
234
+ expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
235
+ text: 'Test comment',
236
+ conviction: 50,
237
+ });
244
238
  });
245
- it('shows error when duration is not a number', async () => {
239
+ it('accepts 4h timeframe', async () => {
240
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
241
+ mockPostMegathreadComment.mockResolvedValue(undefined);
246
242
  const command = createMegathreadCreateCommentCommand();
247
- await expect(command.parseAsync([
243
+ await command.parseAsync([
248
244
  '--agent',
249
245
  'test-agent',
250
246
  '--round',
@@ -255,11 +251,36 @@ describe('createMegathreadCreateCommentCommand', () => {
255
251
  'Test comment',
256
252
  '--token',
257
253
  'bitcoin',
258
- '--duration',
259
- 'abc',
260
- ], { from: 'user' })).rejects.toThrow('process.exit(1)');
261
- expect(consoleErrorOutput.join('\n')).toContain('duration: Must be a positive number');
262
- expect(consoleErrorOutput.join('\n')).toContain('Got: abc');
254
+ '--timeframe',
255
+ '4h',
256
+ ], { from: 'user' });
257
+ expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
258
+ text: 'Test comment',
259
+ conviction: 50,
260
+ });
261
+ });
262
+ it('accepts 24h timeframe', async () => {
263
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
264
+ mockPostMegathreadComment.mockResolvedValue(undefined);
265
+ const command = createMegathreadCreateCommentCommand();
266
+ await command.parseAsync([
267
+ '--agent',
268
+ 'test-agent',
269
+ '--round',
270
+ 'round-123',
271
+ '--conviction',
272
+ '50',
273
+ '--text',
274
+ 'Test comment',
275
+ '--token',
276
+ 'bitcoin',
277
+ '--timeframe',
278
+ '24h',
279
+ ], { from: 'user' });
280
+ expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
281
+ text: 'Test comment',
282
+ conviction: 50,
283
+ });
263
284
  });
264
285
  });
265
286
  describe('agent validation', () => {
@@ -276,8 +297,8 @@ describe('createMegathreadCreateCommentCommand', () => {
276
297
  'Test comment',
277
298
  '--token',
278
299
  'bitcoin',
279
- '--duration',
280
- '3600000',
300
+ '--timeframe',
301
+ '1h',
281
302
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
282
303
  expect(consoleErrorOutput.join('\n')).toContain('Agent "non-existent" not found');
283
304
  expect(consoleErrorOutput.join('\n')).toContain('Available agents:');
@@ -301,8 +322,8 @@ describe('createMegathreadCreateCommentCommand', () => {
301
322
  'Test comment',
302
323
  '--token',
303
324
  'bitcoin',
304
- '--duration',
305
- '3600000',
325
+ '--timeframe',
326
+ '1h',
306
327
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
307
328
  expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
308
329
  });
@@ -320,8 +341,8 @@ describe('createMegathreadCreateCommentCommand', () => {
320
341
  'Test comment',
321
342
  '--token',
322
343
  'bitcoin',
323
- '--duration',
324
- '3600000',
344
+ '--timeframe',
345
+ '1h',
325
346
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
326
347
  expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
327
348
  });
@@ -342,14 +363,12 @@ describe('createMegathreadCreateCommentCommand', () => {
342
363
  'Bullish on Bitcoin!',
343
364
  '--token',
344
365
  'bitcoin',
345
- '--duration',
346
- '3600000',
366
+ '--timeframe',
367
+ '1h',
347
368
  ], { from: 'user' });
348
369
  expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
349
370
  text: 'Bullish on Bitcoin!',
350
371
  conviction: 50,
351
- tokenId: 'bitcoin',
352
- roundDuration: 3600000,
353
372
  });
354
373
  const output = consoleOutput.join('\n');
355
374
  expect(output).toContain('Comment posted successfully');
@@ -373,8 +392,8 @@ describe('createMegathreadCreateCommentCommand', () => {
373
392
  'Bearish outlook',
374
393
  '--token',
375
394
  'ethereum',
376
- '--duration',
377
- '14400000',
395
+ '--timeframe',
396
+ '4h',
378
397
  ], { from: 'user' });
379
398
  const output = consoleOutput.join('\n');
380
399
  expect(output).toContain('-30.0%');
@@ -395,15 +414,13 @@ describe('createMegathreadCreateCommentCommand', () => {
395
414
  longText,
396
415
  '--token',
397
416
  'bitcoin',
398
- '--duration',
399
- '3600000',
417
+ '--timeframe',
418
+ '1h',
400
419
  ], { from: 'user' });
401
420
  // Verify full text was sent to API
402
421
  expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
403
422
  text: longText,
404
423
  conviction: 25,
405
- tokenId: 'bitcoin',
406
- roundDuration: 3600000,
407
424
  });
408
425
  // Verify truncated display
409
426
  const output = consoleOutput.join('\n');
@@ -426,8 +443,8 @@ describe('createMegathreadCreateCommentCommand', () => {
426
443
  'Test comment',
427
444
  '--token',
428
445
  'bitcoin',
429
- '--duration',
430
- '3600000',
446
+ '--timeframe',
447
+ '1h',
431
448
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
432
449
  expect(consoleErrorOutput.join('\n')).toContain('Failed to post comment');
433
450
  expect(consoleErrorOutput.join('\n')).toContain('Network error');
@@ -447,8 +464,8 @@ describe('createMegathreadCreateCommentCommand', () => {
447
464
  'Test comment',
448
465
  '--token',
449
466
  'bitcoin',
450
- '--duration',
451
- '3600000',
467
+ '--timeframe',
468
+ '1h',
452
469
  ], { from: 'user' })).rejects.toThrow('process.exit(1)');
453
470
  expect(consoleErrorOutput.join('\n')).toContain('Failed to post comment');
454
471
  expect(consoleErrorOutput.join('\n')).toContain('String error');
@@ -470,8 +487,8 @@ describe('createMegathreadCreateCommentCommand', () => {
470
487
  'Test comment',
471
488
  '--token',
472
489
  'bitcoin',
473
- '--duration',
474
- '3600000',
490
+ '--timeframe',
491
+ '1h',
475
492
  ], { from: 'user' });
476
493
  const output = consoleOutput.join('\n');
477
494
  expect(output).toContain('Comment posted successfully');
@@ -1,47 +1,43 @@
1
1
  import { Command } from 'commander';
2
- import { HiveClient, TIMEFRAME_DURATION_MS, Timeframe } from '@zhive/sdk';
2
+ import { HiveClient, durationMsToTimeframe } from '@zhive/sdk';
3
3
  import { styled, symbols, border } from '../../shared/theme.js';
4
4
  import { HIVE_API_URL } from '../../../shared/config/constant.js';
5
5
  import { findAgentByName, loadAgentCredentials, scanAgents } from '../../../shared/config/agent.js';
6
+ import z from 'zod';
7
+ import { printZodError } from '../../shared/ validation.js';
6
8
  const VALID_TIMEFRAMES = ['1h', '4h', '24h'];
7
- const DURATION_MS_TO_TIMEFRAME = {
8
- [TIMEFRAME_DURATION_MS[Timeframe.H1]]: Timeframe.H1,
9
- [TIMEFRAME_DURATION_MS[Timeframe.H4]]: Timeframe.H4,
10
- [TIMEFRAME_DURATION_MS[Timeframe.H24]]: Timeframe.H24,
11
- };
12
- function durationMsToTimeframe(durationMs) {
13
- const result = DURATION_MS_TO_TIMEFRAME[durationMs];
14
- return result;
15
- }
16
- function parseTimeframes(raw) {
17
- const parts = raw.split(',').map((t) => t.trim());
18
- const invalid = parts.filter((t) => !VALID_TIMEFRAMES.includes(t));
19
- if (invalid.length > 0) {
20
- return null;
21
- }
22
- const parsed = parts;
23
- return parsed;
24
- }
9
+ const ListMegathreadOptionsSchema = z.object({
10
+ agent: z.string(),
11
+ timeframe: z
12
+ .string()
13
+ .optional()
14
+ .transform((val, ctx) => {
15
+ if (!val)
16
+ return undefined;
17
+ const parsed = val.split(',').map((t) => t.trim());
18
+ const invalidParts = parsed.filter((t) => !VALID_TIMEFRAMES.includes(t));
19
+ if (invalidParts.length > 0) {
20
+ ctx.addIssue({
21
+ code: 'custom',
22
+ message: `Invalid. valid values are [${VALID_TIMEFRAMES.join(', ')}]`,
23
+ });
24
+ return z.NEVER;
25
+ }
26
+ return parsed;
27
+ }),
28
+ });
25
29
  export function createMegathreadListCommand() {
26
30
  const program = new Command('list')
27
31
  .description('List unpredicted megathread rounds of an agent')
28
32
  .requiredOption('--agent <name>', 'Agent name')
29
33
  .option('--timeframe <timeframes>', 'Filter by timeframes (comma-separated: 1h,4h,24h)')
30
34
  .action(async (options) => {
31
- const { agent: agentName, timeframe: timeframeOption } = options;
32
- let timeframes;
33
- if (timeframeOption) {
34
- const parsed = parseTimeframes(timeframeOption);
35
- if (parsed === null) {
36
- const invalidParts = timeframeOption
37
- .split(',')
38
- .map((t) => t.trim())
39
- .filter((t) => !VALID_TIMEFRAMES.includes(t));
40
- console.error(styled.red(`${symbols.cross} Invalid timeframes: ${invalidParts.join(', ')}. Valid values: 1h, 4h, 24h`));
41
- process.exit(1);
42
- }
43
- timeframes = parsed;
35
+ const parseResult = ListMegathreadOptionsSchema.safeParse(options);
36
+ if (!parseResult.success) {
37
+ printZodError(parseResult);
38
+ process.exit(1);
44
39
  }
40
+ const { agent: agentName, timeframe: timeframes } = parseResult.data;
45
41
  const agentConfig = await findAgentByName(agentName);
46
42
  if (!agentConfig) {
47
43
  const agents = await scanAgents();
@@ -70,11 +66,11 @@ export function createMegathreadListCommand() {
70
66
  console.log('');
71
67
  return;
72
68
  }
73
- const headers = ['Round ID', 'Token', 'Timeframe'];
69
+ const headers = ['Round ID', 'Token', 'Timeframe', 'PriceAtStart'];
74
70
  const rows = rounds.map((r) => {
75
71
  const tf = durationMsToTimeframe(r.durationMs);
76
72
  const timeframeStr = tf ?? `${r.durationMs}ms`;
77
- return [r.roundId, r.projectId, timeframeStr];
73
+ return [r.roundId, r.projectId, timeframeStr, r.priceAtStart];
78
74
  });
79
75
  const colWidths = headers.map((h, i) => {
80
76
  const dataMax = Math.max(...rows.map((row) => String(row[i]).length));
@@ -39,6 +39,8 @@ function createMockActiveRound(overrides = {}) {
39
39
  roundId: 'round-123',
40
40
  projectId: 'bitcoin',
41
41
  durationMs: 3600000,
42
+ snapTimeMs: Date.now(),
43
+ priceAtStart: null,
42
44
  ...overrides,
43
45
  };
44
46
  }
@@ -78,13 +80,12 @@ describe('createMegathreadListCommand', () => {
78
80
  it('shows error for invalid timeframe value', async () => {
79
81
  const command = createMegathreadListCommand();
80
82
  await expect(command.parseAsync(['--agent', 'test-agent', '--timeframe', '2h'], { from: 'user' })).rejects.toThrow('process.exit(1)');
81
- expect(consoleErrorOutput.join('\n')).toContain('Invalid timeframes: 2h');
82
- expect(consoleErrorOutput.join('\n')).toContain('Valid values: 1h, 4h, 24h');
83
+ expect(consoleErrorOutput.length).toBeGreaterThan(0);
83
84
  });
84
85
  it('shows error for multiple invalid timeframes', async () => {
85
86
  const command = createMegathreadListCommand();
86
87
  await expect(command.parseAsync(['--agent', 'test-agent', '--timeframe', '2h,5h'], { from: 'user' })).rejects.toThrow('process.exit(1)');
87
- expect(consoleErrorOutput.join('\n')).toContain('Invalid timeframes: 2h, 5h');
88
+ expect(consoleErrorOutput.length).toBeGreaterThan(0);
88
89
  });
89
90
  it('accepts valid timeframe values', async () => {
90
91
  mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
@@ -0,0 +1,5 @@
1
+ import { styled, symbols } from './theme.js';
2
+ export const printZodError = (result) => {
3
+ const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
4
+ console.error(styled.red(`${symbols.cross} Validation error: ${errors}`));
5
+ };
@@ -29,8 +29,7 @@ export function formatPredictions(predictions) {
29
29
  const conviction = `${sign}${pred.conviction.toFixed(1)}%`;
30
30
  const outcome = getOutcomeStr(pred);
31
31
  const date = new Date(pred.created_at).toLocaleDateString();
32
- const roundStart = pred.round_id.split('@Z')[0];
33
- const durationMs = new Date(pred.resolved_at).getTime() - new Date(roundStart).getTime();
32
+ const durationMs = pred.duration_ms ?? 0;
34
33
  const duration = { 3600000: '1h', 14400000: '4h', 86400000: '24h' }[durationMs] || '??';
35
34
  return { name: pred.project_id, duration, conviction, outcome, date };
36
35
  });
@@ -43,7 +43,7 @@ const megathreadPostedActivityFormatter = {
43
43
  return `[${sign}${item.conviction.toFixed(2)}%] "${item.summary}"`;
44
44
  },
45
45
  getDetail(item) {
46
- return item.id.split('@')[0];
46
+ return item.timestamp.toISOString();
47
47
  },
48
48
  format(item) {
49
49
  const lines = [];
@@ -61,7 +61,7 @@ const megathreadErrorActivityFormatter = {
61
61
  return item.errorMessage;
62
62
  },
63
63
  getDetail(item) {
64
- return item.id.split('@')[0];
64
+ return item.timestamp.toISOString();
65
65
  },
66
66
  format(item) {
67
67
  const pad = ' '.repeat(13);
@@ -74,7 +74,7 @@ const megathreadActivityFormatter = {
74
74
  return `${projectTag} \u00B7 ${item.timeframe} round`;
75
75
  },
76
76
  getDetail(item) {
77
- return item.id.split('@')[0];
77
+ return item.timestamp.toISOString();
78
78
  },
79
79
  format(item) {
80
80
  const mainLine = ` ${time(item)}${chalk.hex(colors.controversial)(symbols.hive)} ${chalk.hex(colors.controversial)(this.getText(item))}`;
@@ -33,7 +33,7 @@ async function run({ round, runtime, reporter, recentComments, }) {
33
33
  const timeframe = calculateTimeframe(round);
34
34
  reporter.onRoundStart(round, timeframe);
35
35
  // ── Fetch prices ──────────────────────────────
36
- const roundStartTimestamp = round.roundId.split('@Z')[0];
36
+ const roundStartTimestamp = new Date(round.snapTimeMs).toISOString();
37
37
  const { priceAtStart, currentPrice } = await fetchRoundPrices(round.projectId, roundStartTimestamp);
38
38
  if (priceAtStart !== undefined) {
39
39
  reporter.onPriceInfo(priceAtStart, currentPrice);
@@ -87,8 +87,6 @@ export function createMegathreadRoundBatchHandler(getAgent, runtime, reporter) {
87
87
  await agent.postMegathreadComment(round.roundId, {
88
88
  text: data.summary,
89
89
  conviction: data.conviction,
90
- tokenId: round.projectId,
91
- roundDuration: round.durationMs,
92
90
  });
93
91
  const timeframe = calculateTimeframe(round);
94
92
  reporter.onPosted(round, data.conviction, data.summary, timeframe, data.usage);
@@ -113,8 +111,6 @@ export function createMegathreadRoundHandler(getAgent, runtime, reporter) {
113
111
  await agent.postMegathreadComment(round.roundId, {
114
112
  text: result.summary,
115
113
  conviction: result.conviction,
116
- tokenId: round.projectId,
117
- roundDuration: round.durationMs,
118
114
  });
119
115
  const timeframe = calculateTimeframe(round);
120
116
  reporter.onPosted(round, result.conviction, result.summary, timeframe, result.usage);
@@ -6,12 +6,16 @@ export const HIVE_FRONTEND_URL = 'https://www.zhive.ai';
6
6
  export function getHiveDir() {
7
7
  const homeDir = os.homedir();
8
8
  const zhiveDir = path.join(homeDir, '.zhive');
9
- const legacyDir = path.join(homeDir, '.hive');
10
- if (fs.existsSync(zhiveDir)) {
11
- return zhiveDir;
12
- }
13
- if (fs.existsSync(legacyDir)) {
14
- return legacyDir;
9
+ const pathToCheck = [
10
+ path.join(homeDir, '.zhive'),
11
+ path.join(homeDir, '.hive'), // legacy hive dir
12
+ path.join(homeDir, '.openclaw', '.zhive'),
13
+ path.join(homeDir, '.openclaw', 'workspace', '.zhive'),
14
+ ];
15
+ for (const p of pathToCheck) {
16
+ if (fs.existsSync(p)) {
17
+ return p;
18
+ }
15
19
  }
16
20
  return zhiveDir;
17
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhive/cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "CLI for bootstrapping zHive AI Agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "@ai-sdk/openai": "^3.0.25",
32
32
  "@ai-sdk/xai": "^3.0.0",
33
33
  "@openrouter/ai-sdk-provider": "^0.4.0",
34
- "@zhive/sdk": "^0.5.3",
34
+ "@zhive/sdk": "^0.5.5",
35
35
  "ai": "^6.0.71",
36
36
  "axios": "^1.6.0",
37
37
  "chalk": "^5.3.0",