@zhive/cli 0.6.1 → 0.6.2
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/dist/CLAUDE.md +7 -0
- package/dist/backtest/CLAUDE.md +7 -0
- package/dist/cli.js +20 -0
- package/dist/commands/megathread/commands/create-comment.js +8 -33
- package/dist/commands/megathread/commands/create-comment.test.js +91 -56
- package/dist/commands/megathread/commands/list.js +30 -34
- package/dist/commands/megathread/commands/list.test.js +3 -3
- package/dist/commands/shared/ validation.js +5 -0
- package/dist/{agent → services/agent}/analysis.js +5 -5
- package/dist/{load-agent-env.js → services/agent/env.js} +1 -1
- package/dist/{agent → services/agent/helpers}/model.js +2 -2
- package/dist/{agent → services/agent/prompts}/memory-prompt.js +20 -22
- package/dist/{agent → services/agent/prompts}/prompt.js +80 -54
- package/dist/{agent → services/agent}/tools/market/client.js +1 -1
- package/dist/{agent → services/agent}/tools/mindshare/client.js +1 -1
- package/dist/{agents.js → services/config/agent.js} +2 -2
- package/dist/{config.js → services/config/config.js} +1 -7
- package/dist/services/config/constant.js +8 -0
- package/dist/shared/agent/config.js +75 -0
- package/dist/shared/agent/env.js +30 -0
- package/dist/shared/agent/helpers/model.js +92 -0
- package/dist/shared/ai-providers.js +66 -0
- package/dist/shared/config/constant.js +10 -6
- package/package.json +2 -2
- package/dist/agent/app.js +0 -122
- package/dist/agent/commands/registry.js +0 -12
- package/dist/agent/components/AsciiTicker.js +0 -81
- package/dist/agent/components/CommandInput.js +0 -65
- package/dist/agent/components/HoneycombBoot.js +0 -291
- package/dist/agent/components/Spinner.js +0 -37
- package/dist/agent/hooks/useAgent.js +0 -480
- package/dist/agent/objects.js +0 -1
- package/dist/agent/process-lifecycle.js +0 -18
- package/dist/agent/run-headless.js +0 -189
- package/dist/agent/theme.js +0 -41
- package/dist/avatar.js +0 -34
- package/dist/backtest/default-backtest-data.js +0 -200
- package/dist/backtest/fetch.js +0 -41
- package/dist/backtest/import.js +0 -106
- package/dist/backtest/index.js +0 -10
- package/dist/backtest/results.js +0 -113
- package/dist/backtest/runner.js +0 -134
- package/dist/backtest/storage.js +0 -11
- package/dist/backtest/types.js +0 -1
- package/dist/commands/install.js +0 -50
- package/dist/commands/start/ui/PollText.js +0 -23
- package/dist/commands/start/ui/PredictionsPanel.js +0 -88
- package/dist/commands/start/ui/SpinnerContext.js +0 -20
- package/dist/components/InputGuard.js +0 -6
- package/dist/components/stdout-spinner.js +0 -48
- package/dist/create/CreateApp.js +0 -153
- package/dist/create/ai-generate.js +0 -147
- package/dist/create/generate.js +0 -73
- package/dist/create/steps/ApiKeyStep.js +0 -97
- package/dist/create/steps/AvatarStep.js +0 -16
- package/dist/create/steps/BioStep.js +0 -14
- package/dist/create/steps/DoneStep.js +0 -14
- package/dist/create/steps/IdentityStep.js +0 -163
- package/dist/create/steps/NameStep.js +0 -71
- package/dist/create/steps/ScaffoldStep.js +0 -58
- package/dist/create/steps/SoulStep.js +0 -58
- package/dist/create/steps/StrategyStep.js +0 -58
- package/dist/create/validate-api-key.js +0 -47
- package/dist/create/welcome.js +0 -304
- package/dist/list/ListApp.js +0 -79
- package/dist/migrate-templates/MigrateApp.js +0 -131
- package/dist/migrate-templates/migrate.js +0 -86
- package/dist/presets.js +0 -613
- package/dist/start/AgentProcessManager.js +0 -98
- package/dist/start/Dashboard.js +0 -92
- package/dist/start/SelectAgentApp.js +0 -81
- package/dist/start/StartApp.js +0 -189
- package/dist/start/patch-headless.js +0 -101
- package/dist/start/patch-managed-mode.js +0 -142
- package/dist/start/start-command.js +0 -24
- package/dist/theme.js +0 -54
- /package/dist/{agent → services/agent}/config.js +0 -0
- /package/dist/{agent → services/agent}/helpers.js +0 -0
- /package/dist/{agent → services/agent/prompts}/chat-prompt.js +0 -0
- /package/dist/{agent → services/agent}/skills/index.js +0 -0
- /package/dist/{agent → services/agent}/skills/skill-parser.js +0 -0
- /package/dist/{agent → services/agent}/skills/types.js +0 -0
- /package/dist/{agent → services/agent/tools}/edit-section.js +0 -0
- /package/dist/{agent → services/agent/tools}/fetch-rules.js +0 -0
- /package/dist/{agent → services/agent}/tools/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/read-skill-tool.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/indicators.js +0 -0
- /package/dist/{agent → services/agent}/types.js +0 -0
- /package/dist/{ai-providers.js → services/ai-providers.js} +0 -0
package/dist/CLAUDE.md
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import { createCreateCommand } from './commands/create/commands/index.js';
|
|
5
|
+
import { createListCommand } from './commands/list/commands/index.js';
|
|
6
|
+
import { createStartCommand } from './commands/start/commands/index.js';
|
|
7
|
+
import { createStartAllCommand } from './commands/start-all/commands/index.js';
|
|
8
|
+
import { createRunCommand } from './commands/run/commands/index.js';
|
|
9
|
+
import { createMigrateTemplatesCommand } from './commands/migrate-templates/commands/index.js';
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const packageJson = require('../package.json');
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program.name('@zhive/cli').version(packageJson.version);
|
|
14
|
+
program.addCommand(createCreateCommand());
|
|
15
|
+
program.addCommand(createListCommand());
|
|
16
|
+
program.addCommand(createStartCommand());
|
|
17
|
+
program.addCommand(createStartAllCommand());
|
|
18
|
+
program.addCommand(createRunCommand());
|
|
19
|
+
program.addCommand(createMigrateTemplatesCommand());
|
|
20
|
+
program.parse(process.argv);
|
|
@@ -1,40 +1,17 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { HiveClient } from '@zhive/sdk';
|
|
3
|
+
import { HiveClient, TIMEFRAME_DURATION_MS } 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.
|
|
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
|
-
|
|
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('--
|
|
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
|
-
|
|
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,
|
|
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();
|
|
@@ -73,6 +47,7 @@ export function createMegathreadCreateCommentCommand() {
|
|
|
73
47
|
process.exit(1);
|
|
74
48
|
}
|
|
75
49
|
const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
|
|
50
|
+
const duration = TIMEFRAME_DURATION_MS[timeframe];
|
|
76
51
|
const payload = {
|
|
77
52
|
text,
|
|
78
53
|
conviction,
|
|
@@ -83,11 +83,11 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
83
83
|
'Test comment',
|
|
84
84
|
'--token',
|
|
85
85
|
'bitcoin',
|
|
86
|
-
'--
|
|
87
|
-
'
|
|
86
|
+
'--timeframe',
|
|
87
|
+
'1h',
|
|
88
88
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
89
|
-
expect(consoleErrorOutput.join('\n')).toContain('conviction
|
|
90
|
-
expect(consoleErrorOutput.join('\n')).toContain('
|
|
89
|
+
expect(consoleErrorOutput.join('\n')).toContain('conviction');
|
|
90
|
+
expect(consoleErrorOutput.join('\n')).toContain('100');
|
|
91
91
|
});
|
|
92
92
|
it('shows error when conviction is too low', async () => {
|
|
93
93
|
const command = createMegathreadCreateCommentCommand();
|
|
@@ -102,11 +102,11 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
102
102
|
'Test comment',
|
|
103
103
|
'--token',
|
|
104
104
|
'bitcoin',
|
|
105
|
-
'--
|
|
106
|
-
'
|
|
105
|
+
'--timeframe',
|
|
106
|
+
'1h',
|
|
107
107
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
108
|
-
expect(consoleErrorOutput.join('\n')).toContain('conviction
|
|
109
|
-
expect(consoleErrorOutput.join('\n')).toContain('
|
|
108
|
+
expect(consoleErrorOutput.join('\n')).toContain('conviction');
|
|
109
|
+
expect(consoleErrorOutput.join('\n')).toContain('-100');
|
|
110
110
|
});
|
|
111
111
|
it('shows error when conviction is not a number', async () => {
|
|
112
112
|
const command = createMegathreadCreateCommentCommand();
|
|
@@ -121,11 +121,11 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
121
121
|
'Test comment',
|
|
122
122
|
'--token',
|
|
123
123
|
'bitcoin',
|
|
124
|
-
'--
|
|
125
|
-
'
|
|
124
|
+
'--timeframe',
|
|
125
|
+
'1h',
|
|
126
126
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
127
|
-
expect(consoleErrorOutput.join('\n')).toContain('conviction
|
|
128
|
-
expect(consoleErrorOutput.join('\n')).toContain('
|
|
127
|
+
expect(consoleErrorOutput.join('\n')).toContain('conviction');
|
|
128
|
+
expect(consoleErrorOutput.join('\n')).toContain('number');
|
|
129
129
|
});
|
|
130
130
|
it('accepts valid conviction at upper boundary', async () => {
|
|
131
131
|
mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
@@ -142,8 +142,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
142
142
|
'Test comment',
|
|
143
143
|
'--token',
|
|
144
144
|
'bitcoin',
|
|
145
|
-
'--
|
|
146
|
-
'
|
|
145
|
+
'--timeframe',
|
|
146
|
+
'1h',
|
|
147
147
|
], { from: 'user' });
|
|
148
148
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
149
149
|
text: 'Test comment',
|
|
@@ -167,8 +167,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
167
167
|
'Test comment',
|
|
168
168
|
'--token',
|
|
169
169
|
'bitcoin',
|
|
170
|
-
'--
|
|
171
|
-
'
|
|
170
|
+
'--timeframe',
|
|
171
|
+
'1h',
|
|
172
172
|
], { from: 'user' });
|
|
173
173
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
174
174
|
text: 'Test comment',
|
|
@@ -192,8 +192,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
192
192
|
'Test comment',
|
|
193
193
|
'--token',
|
|
194
194
|
'bitcoin',
|
|
195
|
-
'--
|
|
196
|
-
'
|
|
195
|
+
'--timeframe',
|
|
196
|
+
'1h',
|
|
197
197
|
], { from: 'user' });
|
|
198
198
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
199
199
|
text: 'Test comment',
|
|
@@ -203,8 +203,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
205
|
});
|
|
206
|
-
describe('
|
|
207
|
-
it('shows error
|
|
206
|
+
describe('timeframe validation', () => {
|
|
207
|
+
it('shows error for invalid timeframe value', async () => {
|
|
208
208
|
const command = createMegathreadCreateCommentCommand();
|
|
209
209
|
await expect(command.parseAsync([
|
|
210
210
|
'--agent',
|
|
@@ -217,15 +217,15 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
217
217
|
'Test comment',
|
|
218
218
|
'--token',
|
|
219
219
|
'bitcoin',
|
|
220
|
-
'--
|
|
221
|
-
'
|
|
220
|
+
'--timeframe',
|
|
221
|
+
'2h',
|
|
222
222
|
], { 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
223
|
});
|
|
226
|
-
it('
|
|
224
|
+
it('accepts 1h timeframe', async () => {
|
|
225
|
+
mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
226
|
+
mockPostMegathreadComment.mockResolvedValue(undefined);
|
|
227
227
|
const command = createMegathreadCreateCommentCommand();
|
|
228
|
-
await
|
|
228
|
+
await command.parseAsync([
|
|
229
229
|
'--agent',
|
|
230
230
|
'test-agent',
|
|
231
231
|
'--round',
|
|
@@ -236,15 +236,21 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
236
236
|
'Test comment',
|
|
237
237
|
'--token',
|
|
238
238
|
'bitcoin',
|
|
239
|
-
'--
|
|
240
|
-
'
|
|
241
|
-
], { from: 'user' })
|
|
242
|
-
expect(
|
|
243
|
-
|
|
239
|
+
'--timeframe',
|
|
240
|
+
'1h',
|
|
241
|
+
], { from: 'user' });
|
|
242
|
+
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
243
|
+
text: 'Test comment',
|
|
244
|
+
conviction: 50,
|
|
245
|
+
tokenId: 'bitcoin',
|
|
246
|
+
roundDuration: 3600000,
|
|
247
|
+
});
|
|
244
248
|
});
|
|
245
|
-
it('
|
|
249
|
+
it('accepts 4h timeframe', async () => {
|
|
250
|
+
mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
251
|
+
mockPostMegathreadComment.mockResolvedValue(undefined);
|
|
246
252
|
const command = createMegathreadCreateCommentCommand();
|
|
247
|
-
await
|
|
253
|
+
await command.parseAsync([
|
|
248
254
|
'--agent',
|
|
249
255
|
'test-agent',
|
|
250
256
|
'--round',
|
|
@@ -255,11 +261,40 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
255
261
|
'Test comment',
|
|
256
262
|
'--token',
|
|
257
263
|
'bitcoin',
|
|
258
|
-
'--
|
|
259
|
-
'
|
|
260
|
-
], { from: 'user' })
|
|
261
|
-
expect(
|
|
262
|
-
|
|
264
|
+
'--timeframe',
|
|
265
|
+
'4h',
|
|
266
|
+
], { from: 'user' });
|
|
267
|
+
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
268
|
+
text: 'Test comment',
|
|
269
|
+
conviction: 50,
|
|
270
|
+
tokenId: 'bitcoin',
|
|
271
|
+
roundDuration: 14400000,
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
it('accepts 24h timeframe', async () => {
|
|
275
|
+
mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
276
|
+
mockPostMegathreadComment.mockResolvedValue(undefined);
|
|
277
|
+
const command = createMegathreadCreateCommentCommand();
|
|
278
|
+
await command.parseAsync([
|
|
279
|
+
'--agent',
|
|
280
|
+
'test-agent',
|
|
281
|
+
'--round',
|
|
282
|
+
'round-123',
|
|
283
|
+
'--conviction',
|
|
284
|
+
'50',
|
|
285
|
+
'--text',
|
|
286
|
+
'Test comment',
|
|
287
|
+
'--token',
|
|
288
|
+
'bitcoin',
|
|
289
|
+
'--timeframe',
|
|
290
|
+
'24h',
|
|
291
|
+
], { from: 'user' });
|
|
292
|
+
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
293
|
+
text: 'Test comment',
|
|
294
|
+
conviction: 50,
|
|
295
|
+
tokenId: 'bitcoin',
|
|
296
|
+
roundDuration: 86400000,
|
|
297
|
+
});
|
|
263
298
|
});
|
|
264
299
|
});
|
|
265
300
|
describe('agent validation', () => {
|
|
@@ -276,8 +311,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
276
311
|
'Test comment',
|
|
277
312
|
'--token',
|
|
278
313
|
'bitcoin',
|
|
279
|
-
'--
|
|
280
|
-
'
|
|
314
|
+
'--timeframe',
|
|
315
|
+
'1h',
|
|
281
316
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
282
317
|
expect(consoleErrorOutput.join('\n')).toContain('Agent "non-existent" not found');
|
|
283
318
|
expect(consoleErrorOutput.join('\n')).toContain('Available agents:');
|
|
@@ -301,8 +336,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
301
336
|
'Test comment',
|
|
302
337
|
'--token',
|
|
303
338
|
'bitcoin',
|
|
304
|
-
'--
|
|
305
|
-
'
|
|
339
|
+
'--timeframe',
|
|
340
|
+
'1h',
|
|
306
341
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
307
342
|
expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
|
|
308
343
|
});
|
|
@@ -320,8 +355,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
320
355
|
'Test comment',
|
|
321
356
|
'--token',
|
|
322
357
|
'bitcoin',
|
|
323
|
-
'--
|
|
324
|
-
'
|
|
358
|
+
'--timeframe',
|
|
359
|
+
'1h',
|
|
325
360
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
326
361
|
expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
|
|
327
362
|
});
|
|
@@ -342,8 +377,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
342
377
|
'Bullish on Bitcoin!',
|
|
343
378
|
'--token',
|
|
344
379
|
'bitcoin',
|
|
345
|
-
'--
|
|
346
|
-
'
|
|
380
|
+
'--timeframe',
|
|
381
|
+
'1h',
|
|
347
382
|
], { from: 'user' });
|
|
348
383
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
349
384
|
text: 'Bullish on Bitcoin!',
|
|
@@ -373,8 +408,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
373
408
|
'Bearish outlook',
|
|
374
409
|
'--token',
|
|
375
410
|
'ethereum',
|
|
376
|
-
'--
|
|
377
|
-
'
|
|
411
|
+
'--timeframe',
|
|
412
|
+
'4h',
|
|
378
413
|
], { from: 'user' });
|
|
379
414
|
const output = consoleOutput.join('\n');
|
|
380
415
|
expect(output).toContain('-30.0%');
|
|
@@ -395,8 +430,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
395
430
|
longText,
|
|
396
431
|
'--token',
|
|
397
432
|
'bitcoin',
|
|
398
|
-
'--
|
|
399
|
-
'
|
|
433
|
+
'--timeframe',
|
|
434
|
+
'1h',
|
|
400
435
|
], { from: 'user' });
|
|
401
436
|
// Verify full text was sent to API
|
|
402
437
|
expect(mockPostMegathreadComment).toHaveBeenCalledWith('round-123', {
|
|
@@ -426,8 +461,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
426
461
|
'Test comment',
|
|
427
462
|
'--token',
|
|
428
463
|
'bitcoin',
|
|
429
|
-
'--
|
|
430
|
-
'
|
|
464
|
+
'--timeframe',
|
|
465
|
+
'1h',
|
|
431
466
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
432
467
|
expect(consoleErrorOutput.join('\n')).toContain('Failed to post comment');
|
|
433
468
|
expect(consoleErrorOutput.join('\n')).toContain('Network error');
|
|
@@ -447,8 +482,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
447
482
|
'Test comment',
|
|
448
483
|
'--token',
|
|
449
484
|
'bitcoin',
|
|
450
|
-
'--
|
|
451
|
-
'
|
|
485
|
+
'--timeframe',
|
|
486
|
+
'1h',
|
|
452
487
|
], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
453
488
|
expect(consoleErrorOutput.join('\n')).toContain('Failed to post comment');
|
|
454
489
|
expect(consoleErrorOutput.join('\n')).toContain('String error');
|
|
@@ -470,8 +505,8 @@ describe('createMegathreadCreateCommentCommand', () => {
|
|
|
470
505
|
'Test comment',
|
|
471
506
|
'--token',
|
|
472
507
|
'bitcoin',
|
|
473
|
-
'--
|
|
474
|
-
'
|
|
508
|
+
'--timeframe',
|
|
509
|
+
'1h',
|
|
475
510
|
], { from: 'user' });
|
|
476
511
|
const output = consoleOutput.join('\n');
|
|
477
512
|
expect(output).toContain('Comment posted successfully');
|
|
@@ -1,47 +1,43 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { HiveClient,
|
|
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
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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,7 @@ function createMockActiveRound(overrides = {}) {
|
|
|
39
39
|
roundId: 'round-123',
|
|
40
40
|
projectId: 'bitcoin',
|
|
41
41
|
durationMs: 3600000,
|
|
42
|
+
priceAtStart: null,
|
|
42
43
|
...overrides,
|
|
43
44
|
};
|
|
44
45
|
}
|
|
@@ -78,13 +79,12 @@ describe('createMegathreadListCommand', () => {
|
|
|
78
79
|
it('shows error for invalid timeframe value', async () => {
|
|
79
80
|
const command = createMegathreadListCommand();
|
|
80
81
|
await expect(command.parseAsync(['--agent', 'test-agent', '--timeframe', '2h'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
81
|
-
expect(consoleErrorOutput.
|
|
82
|
-
expect(consoleErrorOutput.join('\n')).toContain('Valid values: 1h, 4h, 24h');
|
|
82
|
+
expect(consoleErrorOutput.length).toBeGreaterThan(0);
|
|
83
83
|
});
|
|
84
84
|
it('shows error for multiple invalid timeframes', async () => {
|
|
85
85
|
const command = createMegathreadListCommand();
|
|
86
86
|
await expect(command.parseAsync(['--agent', 'test-agent', '--timeframe', '2h,5h'], { from: 'user' })).rejects.toThrow('process.exit(1)');
|
|
87
|
-
expect(consoleErrorOutput.
|
|
87
|
+
expect(consoleErrorOutput.length).toBeGreaterThan(0);
|
|
88
88
|
});
|
|
89
89
|
it('accepts valid timeframe values', async () => {
|
|
90
90
|
mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { generateText, Output, ToolLoopAgent } from 'ai';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { buildAnalystPrompt, buildMegathreadPrompt } from './prompt.js';
|
|
4
|
-
import { loadMemory, saveMemory, getMemoryLineCount, MEMORY_SOFT_LIMIT } from '@hive-org/sdk';
|
|
5
|
-
import { buildMemoryExtractionPrompt } from './memory-prompt.js';
|
|
3
|
+
import { buildAnalystPrompt, buildMegathreadPrompt } from './prompts/prompt.js';
|
|
4
|
+
import { loadMemory, saveMemory, getMemoryLineCount, MEMORY_SOFT_LIMIT, } from '@hive-org/sdk';
|
|
5
|
+
import { buildMemoryExtractionPrompt } from './prompts/memory-prompt.js';
|
|
6
6
|
import { stripCodeFences } from './helpers.js';
|
|
7
|
-
import { getModel } from './model.js';
|
|
7
|
+
import { getModel } from './helpers/model.js';
|
|
8
8
|
// ─── Cache Helpers ─────────────────────────────────
|
|
9
9
|
function cacheableSystem(content) {
|
|
10
10
|
const message = {
|
|
@@ -146,7 +146,7 @@ export async function extractAndSaveMemory(sessionMessages) {
|
|
|
146
146
|
const model = await getModel();
|
|
147
147
|
const { text } = await generateText({
|
|
148
148
|
model,
|
|
149
|
-
|
|
149
|
+
prompt,
|
|
150
150
|
});
|
|
151
151
|
const cleaned = stripCodeFences(text);
|
|
152
152
|
await saveMemory(cleaned);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AI_PROVIDERS } from '
|
|
2
|
-
import { getAgentProviderKeys } from '../
|
|
1
|
+
import { AI_PROVIDERS } from '../../ai-providers.js';
|
|
2
|
+
import { getAgentProviderKeys } from '../env.js';
|
|
3
3
|
const PROVIDERS = [
|
|
4
4
|
{
|
|
5
5
|
label: 'Anthropic',
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
export function buildMemoryExtractionPrompt(context) {
|
|
2
2
|
const { currentMemory, sessionMessages, lineCount } = context;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
let sessionSection = '';
|
|
4
|
+
if (sessionMessages.length > 0) {
|
|
5
|
+
const listed = sessionMessages
|
|
6
|
+
.map((m) => `${m.role === 'user' ? 'Operator' : 'Agent'}: ${m.content}`)
|
|
7
|
+
.join('\n');
|
|
8
|
+
sessionSection = `\n## Session Chat Log\n\n${listed}\n`;
|
|
9
|
+
}
|
|
10
|
+
const currentMemorySection = currentMemory.trim().length > 0
|
|
11
|
+
? `\n## Current MEMORY.md\n\n\`\`\`markdown\n${currentMemory}\n\`\`\`\n`
|
|
12
|
+
: '\n## Current MEMORY.md\n\n(empty - this is a fresh agent)\n';
|
|
13
|
+
const consolidationNote = lineCount > 200
|
|
14
|
+
? `\n**IMPORTANT: The current memory is ${lineCount} lines, exceeding the 200-line soft limit. Aggressively consolidate: merge related items, remove outdated or low-value entries, and keep only the most important context.**\n`
|
|
15
|
+
: '';
|
|
16
|
+
const prompt = `You are an AI trading agent's memory system. Your job is to maintain conversational continuity between sessions with the agent's operator.
|
|
17
|
+
${currentMemorySection}${consolidationNote}
|
|
18
|
+
## Session Activity
|
|
19
|
+
${sessionSection}
|
|
20
|
+
## Instructions
|
|
5
21
|
|
|
6
|
-
Review the session
|
|
22
|
+
Review the session chat log above and update the agent's MEMORY.md file. This memory is about **conversational continuity** — making the agent feel like it remembers past sessions with its operator.
|
|
7
23
|
|
|
8
24
|
Focus on extracting:
|
|
9
25
|
1. **Topics discussed** — what subjects came up in conversation (e.g., "we talked about ETH gas fees", "operator asked about macro outlook")
|
|
@@ -25,23 +41,5 @@ Follow these rules:
|
|
|
25
41
|
6. **Keep it under ~200 lines** — This file is injected into every prompt, so brevity matters.
|
|
26
42
|
|
|
27
43
|
Output the complete updated MEMORY.md content. Start with \`# Memory\` as the top-level header. Output ONLY the markdown content, no code fences or explanation.`;
|
|
28
|
-
|
|
29
|
-
let sessionSection = '';
|
|
30
|
-
if (sessionMessages.length > 0) {
|
|
31
|
-
const listed = sessionMessages
|
|
32
|
-
.map((m) => `${m.role === 'user' ? 'Operator' : 'Agent'}: ${m.content}`)
|
|
33
|
-
.join('\n');
|
|
34
|
-
sessionSection = `\n## Session Chat Log\n\n${listed}\n`;
|
|
35
|
-
}
|
|
36
|
-
const currentMemorySection = currentMemory.trim().length > 0
|
|
37
|
-
? `\n## Current MEMORY.md\n\n\`\`\`markdown\n${currentMemory}\n\`\`\`\n`
|
|
38
|
-
: '\n## Current MEMORY.md\n\n(empty - this is a fresh agent)\n';
|
|
39
|
-
const consolidationNote = lineCount > 200
|
|
40
|
-
? `\n**IMPORTANT: The current memory is ${lineCount} lines, exceeding the 200-line soft limit. Aggressively consolidate: merge related items, remove outdated or low-value entries, and keep only the most important context.**\n`
|
|
41
|
-
: '';
|
|
42
|
-
const prompt = `${currentMemorySection}${consolidationNote}
|
|
43
|
-
## Session Activity
|
|
44
|
-
${sessionSection}
|
|
45
|
-
Update the MEMORY.md based on the session activity above.`;
|
|
46
|
-
return { system, prompt };
|
|
44
|
+
return prompt;
|
|
47
45
|
}
|