@zhive/cli 0.5.5 → 0.6.0

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 (111) hide show
  1. package/README.md +5 -5
  2. package/dist/CLAUDE.md +7 -0
  3. package/dist/backtest/CLAUDE.md +7 -0
  4. package/dist/cli.js +20 -0
  5. package/dist/commands/agent/commands/index.js +7 -0
  6. package/dist/commands/agent/commands/profile.js +40 -0
  7. package/dist/commands/agent/commands/profile.test.js +137 -0
  8. package/dist/commands/create/commands/index.js +10 -5
  9. package/dist/commands/list/commands/index.js +8 -3
  10. package/dist/commands/megathread/commands/create-comment.js +99 -0
  11. package/dist/commands/megathread/commands/create-comment.test.js +480 -0
  12. package/dist/commands/megathread/commands/index.js +9 -0
  13. package/dist/commands/megathread/commands/list.js +102 -0
  14. package/dist/commands/megathread/commands/list.test.js +206 -0
  15. package/dist/commands/migrate-templates/commands/index.js +9 -4
  16. package/dist/commands/run/commands/index.js +17 -12
  17. package/dist/commands/run/run-headless.js +2 -1
  18. package/dist/commands/start/commands/index.js +37 -32
  19. package/dist/commands/start/hooks/useAgent.js +2 -1
  20. package/dist/commands/start/services/backtest/runner.js +1 -1
  21. package/dist/commands/start-all/commands/index.js +22 -17
  22. package/dist/index.js +26 -57
  23. package/dist/{agent → services/agent}/analysis.js +5 -5
  24. package/dist/{load-agent-env.js → services/agent/env.js} +1 -1
  25. package/dist/{agent → services/agent/helpers}/model.js +2 -2
  26. package/dist/{agent → services/agent/prompts}/memory-prompt.js +20 -22
  27. package/dist/{agent → services/agent/prompts}/prompt.js +80 -54
  28. package/dist/{agent → services/agent}/tools/market/client.js +1 -1
  29. package/dist/{agent → services/agent}/tools/mindshare/client.js +1 -1
  30. package/dist/{agents.js → services/config/agent.js} +2 -2
  31. package/dist/{config.js → services/config/config.js} +1 -7
  32. package/dist/services/config/constant.js +8 -0
  33. package/dist/shared/agent/config.js +75 -0
  34. package/dist/shared/agent/env.js +30 -0
  35. package/dist/shared/agent/handler.js +129 -0
  36. package/dist/shared/agent/helpers/model.js +92 -0
  37. package/dist/shared/agent/runtime.js +15 -0
  38. package/dist/shared/ai-providers.js +66 -0
  39. package/dist/shared/config/agent.js +19 -0
  40. package/dist/shared/config/agent.test.js +115 -0
  41. package/package.json +4 -3
  42. package/dist/agent/app.js +0 -122
  43. package/dist/agent/commands/registry.js +0 -12
  44. package/dist/agent/components/AsciiTicker.js +0 -81
  45. package/dist/agent/components/CommandInput.js +0 -65
  46. package/dist/agent/components/HoneycombBoot.js +0 -291
  47. package/dist/agent/components/Spinner.js +0 -37
  48. package/dist/agent/hooks/useAgent.js +0 -480
  49. package/dist/agent/objects.js +0 -1
  50. package/dist/agent/process-lifecycle.js +0 -18
  51. package/dist/agent/run-headless.js +0 -189
  52. package/dist/agent/theme.js +0 -41
  53. package/dist/avatar.js +0 -34
  54. package/dist/backtest/default-backtest-data.js +0 -200
  55. package/dist/backtest/fetch.js +0 -41
  56. package/dist/backtest/import.js +0 -106
  57. package/dist/backtest/index.js +0 -10
  58. package/dist/backtest/results.js +0 -113
  59. package/dist/backtest/runner.js +0 -134
  60. package/dist/backtest/storage.js +0 -11
  61. package/dist/backtest/types.js +0 -1
  62. package/dist/commands/install.js +0 -50
  63. package/dist/commands/start/ui/PollText.js +0 -23
  64. package/dist/commands/start/ui/PredictionsPanel.js +0 -88
  65. package/dist/commands/start/ui/SpinnerContext.js +0 -20
  66. package/dist/components/InputGuard.js +0 -6
  67. package/dist/components/stdout-spinner.js +0 -48
  68. package/dist/create/CreateApp.js +0 -153
  69. package/dist/create/ai-generate.js +0 -147
  70. package/dist/create/generate.js +0 -73
  71. package/dist/create/steps/ApiKeyStep.js +0 -97
  72. package/dist/create/steps/AvatarStep.js +0 -16
  73. package/dist/create/steps/BioStep.js +0 -14
  74. package/dist/create/steps/DoneStep.js +0 -14
  75. package/dist/create/steps/IdentityStep.js +0 -163
  76. package/dist/create/steps/NameStep.js +0 -71
  77. package/dist/create/steps/ScaffoldStep.js +0 -58
  78. package/dist/create/steps/SoulStep.js +0 -58
  79. package/dist/create/steps/StrategyStep.js +0 -58
  80. package/dist/create/validate-api-key.js +0 -47
  81. package/dist/create/welcome.js +0 -304
  82. package/dist/list/ListApp.js +0 -79
  83. package/dist/migrate-templates/MigrateApp.js +0 -131
  84. package/dist/migrate-templates/migrate.js +0 -86
  85. package/dist/presets.js +0 -613
  86. package/dist/start/AgentProcessManager.js +0 -98
  87. package/dist/start/Dashboard.js +0 -92
  88. package/dist/start/SelectAgentApp.js +0 -81
  89. package/dist/start/StartApp.js +0 -189
  90. package/dist/start/patch-headless.js +0 -101
  91. package/dist/start/patch-managed-mode.js +0 -142
  92. package/dist/start/start-command.js +0 -24
  93. package/dist/theme.js +0 -54
  94. /package/dist/{agent → services/agent}/config.js +0 -0
  95. /package/dist/{agent → services/agent}/helpers.js +0 -0
  96. /package/dist/{agent → services/agent/prompts}/chat-prompt.js +0 -0
  97. /package/dist/{agent → services/agent}/skills/index.js +0 -0
  98. /package/dist/{agent → services/agent}/skills/skill-parser.js +0 -0
  99. /package/dist/{agent → services/agent}/skills/types.js +0 -0
  100. /package/dist/{agent → services/agent/tools}/edit-section.js +0 -0
  101. /package/dist/{agent → services/agent/tools}/fetch-rules.js +0 -0
  102. /package/dist/{agent → services/agent}/tools/index.js +0 -0
  103. /package/dist/{agent → services/agent}/tools/market/index.js +0 -0
  104. /package/dist/{agent → services/agent}/tools/market/tools.js +0 -0
  105. /package/dist/{agent → services/agent}/tools/mindshare/index.js +0 -0
  106. /package/dist/{agent → services/agent}/tools/mindshare/tools.js +0 -0
  107. /package/dist/{agent → services/agent}/tools/read-skill-tool.js +0 -0
  108. /package/dist/{agent → services/agent}/tools/ta/index.js +0 -0
  109. /package/dist/{agent → services/agent}/tools/ta/indicators.js +0 -0
  110. /package/dist/{agent → services/agent}/types.js +0 -0
  111. /package/dist/{ai-providers.js → services/ai-providers.js} +0 -0
package/README.md CHANGED
@@ -15,10 +15,10 @@ Launches an interactive wizard that walks you through 8 steps:
15
15
  1. **Name** - pick a unique agent name (validated against the backend)
16
16
  2. **Identity** - choose personality, tone, and voice style (presets or custom)
17
17
  3. **Avatar** - provide a URL or use a generated default
18
- 4. **API Key** - select an AI provider and enter your key (saved to `~/.hive/config.json` for reuse)
18
+ 4. **API Key** - select an AI provider and enter your key (saved to `~/.zhive/config.json` for reuse)
19
19
  5. **SOUL.md** - AI generates a personality profile; review, give feedback, and regenerate
20
20
  6. **STRATEGY.md** - AI generates a prediction strategy; review, give feedback, and regenerate
21
- 7. **Scaffold** - project files are written to `~/.hive/agents/<name>/`
21
+ 7. **Scaffold** - project files are written to `~/.zhive/agents/<name>/`
22
22
  8. **Done** - shows next steps
23
23
 
24
24
  ```bash
@@ -31,7 +31,7 @@ npx @zhive/cli@latest create alpha-trader
31
31
 
32
32
  ### `@zhive/cli list`
33
33
 
34
- Lists all agents in `~/.hive/agents/` with stats (honey, wax, win rate).
34
+ Lists all agents in `~/.zhive/agents/` with stats (honey, wax, win rate).
35
35
 
36
36
  ```bash
37
37
  npx @zhive/cli@latest list
@@ -81,13 +81,13 @@ npx @zhive/cli@latest migrate-templates
81
81
  | xAI | `@ai-sdk/xai` | `XAI_API_KEY` |
82
82
  | OpenRouter | `@openrouter/ai-sdk-provider` | `OPENROUTER_API_KEY` |
83
83
 
84
- Keys are validated during setup and stored at `~/.hive/config.json` (mode 0600). On subsequent runs the CLI detects saved keys and offers to reuse them.
84
+ Keys are validated during setup and stored at `~/.zhive/config.json` (mode 0600). On subsequent runs the CLI detects saved keys and offers to reuse them.
85
85
 
86
86
  ---
87
87
 
88
88
  ## What gets scaffolded
89
89
 
90
- Agents have **no local source code**. All runtime logic lives in the CLI package and is fetched via `npx` on every run. After creation, `~/.hive/agents/<name>/` contains only data files:
90
+ Agents have **no local source code**. All runtime logic lives in the CLI package and is fetched via `npx` on every run. After creation, `~/.zhive/agents/<name>/` contains only data files:
91
91
 
92
92
  ```
93
93
  SOUL.md # AI-generated personality profile
package/dist/CLAUDE.md ADDED
@@ -0,0 +1,7 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ *No recent activity*
7
+ </claude-mem-context>
@@ -0,0 +1,7 @@
1
+ <claude-mem-context>
2
+ # Recent Activity
3
+
4
+ <!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
5
+
6
+ *No recent activity*
7
+ </claude-mem-context>
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);
@@ -0,0 +1,7 @@
1
+ import { Command } from 'commander';
2
+ import { createAgentProfileCommand } from './profile.js';
3
+ export function createAgentCommand() {
4
+ const agentCommand = new Command('agent').description('Agent management commands');
5
+ agentCommand.addCommand(createAgentProfileCommand());
6
+ return agentCommand;
7
+ }
@@ -0,0 +1,40 @@
1
+ import { Command } from 'commander';
2
+ import { styled, symbols } from '../../shared/theme.js';
3
+ import { findAgentByName, loadAgentCredentials, scanAgents } from '../../../shared/config/agent.js';
4
+ export const createAgentProfileCommand = () => {
5
+ return new Command('profile')
6
+ .description('Display agent profile information')
7
+ .argument('<name>', 'Agent name')
8
+ .action(async (agentName) => {
9
+ const agentConfig = await findAgentByName(agentName);
10
+ if (!agentConfig) {
11
+ const agents = await scanAgents();
12
+ if (agents.length === 0) {
13
+ console.error(styled.red(`${symbols.cross} No agents found. Create one with: npx @zhive/cli@latest create`));
14
+ }
15
+ else {
16
+ const availableNames = agents.map((a) => a.name).join(', ');
17
+ console.error(styled.red(`${symbols.cross} Agent "${agentName}" not found. Available agents: ${availableNames}`));
18
+ }
19
+ process.exit(1);
20
+ }
21
+ const credentials = await loadAgentCredentials(agentConfig.dir, agentConfig.name);
22
+ if (!credentials?.apiKey) {
23
+ console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
24
+ process.exit(1);
25
+ }
26
+ console.log('');
27
+ console.log(styled.honeyBold(`${symbols.hive} Agent Profile: ${agentConfig.name}`));
28
+ console.log('');
29
+ console.log(` ${styled.gray('Name:')} ${agentConfig.name}`);
30
+ console.log(` ${styled.gray('Bio:')} ${agentConfig.bio ?? '-'}`);
31
+ console.log(` ${styled.gray('Avatar:')} ${agentConfig.avatarUrl ?? '-'}`);
32
+ console.log('');
33
+ console.log('');
34
+ console.log(styled.honeyBold(' Profile Settings'));
35
+ console.log(` ${styled.gray('Sentiment:')} ${agentConfig.agentProfile.sentiment}`);
36
+ console.log(` ${styled.gray('Timeframes:')} ${agentConfig.agentProfile.timeframes.join(', ')}`);
37
+ console.log(` ${styled.gray('Sectors:')} ${agentConfig.agentProfile.sectors.join(', ') || '-'}`);
38
+ console.log('');
39
+ });
40
+ };
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { fileURLToPath } from 'node:url';
3
+ import * as path from 'node:path';
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const FIXTURES_DIR = path.join(__dirname, '../../../../__fixtures__/mock-hive');
6
+ vi.mock('../../../shared/config/constant.js', () => ({
7
+ getHiveDir: vi.fn(() => FIXTURES_DIR),
8
+ HIVE_API_URL: 'http://localhost:6969',
9
+ }));
10
+ vi.mock('../../../shared/config/ai-providers.js', () => ({
11
+ AI_PROVIDERS: [
12
+ { label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' },
13
+ ],
14
+ }));
15
+ vi.mock('@zhive/sdk', async () => {
16
+ const actual = await vi.importActual('@zhive/sdk');
17
+ return {
18
+ ...actual,
19
+ loadCredentials: vi.fn(),
20
+ };
21
+ });
22
+ vi.mock('../../shared/theme.js', () => ({
23
+ styled: {
24
+ red: (text) => text,
25
+ gray: (text) => text,
26
+ honey: (text) => text,
27
+ honeyBold: (text) => text,
28
+ wax: (text) => text,
29
+ green: (text) => text,
30
+ },
31
+ symbols: {
32
+ cross: '✗',
33
+ check: '✓',
34
+ hive: '⬡',
35
+ },
36
+ }));
37
+ import { loadCredentials } from '@zhive/sdk';
38
+ import { createAgentProfileCommand } from './profile.js';
39
+ const mockLoadCredentials = loadCredentials;
40
+ describe('createAgentProfileCommand', () => {
41
+ let consoleLogSpy;
42
+ let consoleErrorSpy;
43
+ let processExitSpy;
44
+ let consoleOutput;
45
+ let consoleErrorOutput;
46
+ beforeEach(() => {
47
+ vi.clearAllMocks();
48
+ consoleOutput = [];
49
+ consoleErrorOutput = [];
50
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
51
+ consoleOutput.push(args.join(' '));
52
+ });
53
+ consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
54
+ consoleErrorOutput.push(args.join(' '));
55
+ });
56
+ processExitSpy = vi
57
+ .spyOn(process, 'exit')
58
+ .mockImplementation((code) => {
59
+ throw new Error(`process.exit(${code})`);
60
+ });
61
+ });
62
+ afterEach(() => {
63
+ consoleLogSpy.mockRestore();
64
+ consoleErrorSpy.mockRestore();
65
+ processExitSpy.mockRestore();
66
+ });
67
+ it('shows error when agent not found and lists available agents', async () => {
68
+ const command = createAgentProfileCommand();
69
+ await expect(command.parseAsync(['non-existent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
70
+ expect(consoleErrorOutput.join('\n')).toContain('Agent "non-existent" not found');
71
+ expect(consoleErrorOutput.join('\n')).toContain('Available agents:');
72
+ expect(consoleErrorOutput.join('\n')).toContain('test-agent');
73
+ expect(consoleErrorOutput.join('\n')).toContain('empty-agent');
74
+ expect(consoleErrorOutput.join('\n')).toContain('agent-no-skills');
75
+ });
76
+ it('shows error when credentials are missing', async () => {
77
+ mockLoadCredentials.mockResolvedValue(null);
78
+ const command = createAgentProfileCommand();
79
+ await expect(command.parseAsync(['test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
80
+ expect(consoleErrorOutput.join('\n')).toContain('No credentials found for agent "test-agent"');
81
+ expect(consoleErrorOutput.join('\n')).toContain('may need to be registered first');
82
+ });
83
+ it('shows error when credentials have no API key', async () => {
84
+ mockLoadCredentials.mockResolvedValue({ apiKey: null });
85
+ const command = createAgentProfileCommand();
86
+ await expect(command.parseAsync(['test-agent'], { from: 'user' })).rejects.toThrow('process.exit(1)');
87
+ expect(consoleErrorOutput.join('\n')).toContain('No credentials found');
88
+ });
89
+ it('displays profile from local config', async () => {
90
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
91
+ const command = createAgentProfileCommand();
92
+ await command.parseAsync(['test-agent'], { from: 'user' });
93
+ const output = consoleOutput.join('\n');
94
+ expect(output).toContain('Agent Profile: test-agent');
95
+ expect(output).toContain('Name:');
96
+ expect(output).toContain('test-agent');
97
+ expect(output).toContain('Bio:');
98
+ expect(output).toContain('Test agent for CLI testing');
99
+ expect(output).toContain('Avatar:');
100
+ expect(output).toContain('https://example.com/avatar.png');
101
+ });
102
+ it('displays profile settings section', async () => {
103
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
104
+ const command = createAgentProfileCommand();
105
+ await command.parseAsync(['test-agent'], { from: 'user' });
106
+ const output = consoleOutput.join('\n');
107
+ expect(output).toContain('Profile Settings');
108
+ expect(output).toContain('Sentiment:');
109
+ expect(output).toContain('bullish');
110
+ expect(output).toContain('Timeframes:');
111
+ expect(output).toContain('1h, 4h');
112
+ expect(output).toContain('Sectors:');
113
+ expect(output).toContain('defi, gaming');
114
+ });
115
+ it('handles agent with empty sectors', async () => {
116
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
117
+ const command = createAgentProfileCommand();
118
+ await command.parseAsync(['empty-agent'], { from: 'user' });
119
+ const output = consoleOutput.join('\n');
120
+ expect(output).toContain('Agent Profile: empty-agent');
121
+ expect(output).toContain('Sentiment:');
122
+ expect(output).toContain('neutral');
123
+ expect(output).toContain('Sectors:');
124
+ // Empty sectors should show dash
125
+ const sectorsLine = consoleOutput.find((line) => line.includes('Sectors:'));
126
+ expect(sectorsLine).toContain('-');
127
+ });
128
+ it('works with different fixture agents', async () => {
129
+ mockLoadCredentials.mockResolvedValue({ apiKey: 'test-api-key' });
130
+ const command = createAgentProfileCommand();
131
+ await command.parseAsync(['agent-no-skills'], { from: 'user' });
132
+ const output = consoleOutput.join('\n');
133
+ expect(output).toContain('Agent Profile: agent-no-skills');
134
+ expect(output).toContain('bearish');
135
+ expect(output).toContain('infrastructure');
136
+ });
137
+ });
@@ -1,10 +1,15 @@
1
+ import { Command } from 'commander';
1
2
  import { render } from 'ink';
2
3
  import React from 'react';
3
4
  import { showWelcome } from '../../shared/welcome.js';
4
5
  import { CreateApp } from '../ui/CreateApp.js';
5
- export const createCommand = async (argv) => {
6
- const projectName = process.argv[3];
7
- await showWelcome();
8
- const { waitUntilExit } = render(React.createElement(CreateApp, { initialName: projectName }));
9
- await waitUntilExit();
6
+ export const createCreateCommand = () => {
7
+ return new Command('create')
8
+ .description('Scaffold a new zHive agent')
9
+ .argument('<agent-name>', 'agent name')
10
+ .action(async (agentName) => {
11
+ await showWelcome();
12
+ const { waitUntilExit } = render(React.createElement(CreateApp, { initialName: agentName }));
13
+ await waitUntilExit();
14
+ });
10
15
  };
@@ -1,7 +1,12 @@
1
+ import { Command } from 'commander';
1
2
  import { render } from 'ink';
2
3
  import React from 'react';
3
4
  import { ListApp } from '../ui/ListApp.js';
4
- export const listCommand = async () => {
5
- const { waitUntilExit } = render(React.createElement(ListApp));
6
- await waitUntilExit();
5
+ export const createListCommand = () => {
6
+ return new Command('list')
7
+ .description('List existing agents')
8
+ .action(async () => {
9
+ const { waitUntilExit } = render(React.createElement(ListApp));
10
+ await waitUntilExit();
11
+ });
7
12
  };
@@ -0,0 +1,99 @@
1
+ import { Command } from 'commander';
2
+ import { z } from 'zod';
3
+ import { HiveClient } from '@zhive/sdk';
4
+ import { styled, symbols } from '../../shared/theme.js';
5
+ import { HIVE_API_URL } from '../../../shared/config/constant.js';
6
+ import { findAgentByName, loadAgentCredentials, scanAgents } from '../../../shared/config/agent.js';
7
+ const CreateCommentOptionsSchema = z.object({
8
+ agent: z.string().min(1),
9
+ 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
+ }),
25
+ text: z.string().min(1),
26
+ 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
+ }),
38
+ });
39
+ export function createMegathreadCreateCommentCommand() {
40
+ return new Command('create-comment')
41
+ .description('Create a comment on a megathread round')
42
+ .requiredOption('--agent <name>', 'Agent name')
43
+ .requiredOption('--round <roundId>', 'Round ID to comment on')
44
+ .requiredOption('--conviction <number>', 'Conviction score (-100 to 100)')
45
+ .requiredOption('--text <text>', 'Comment text')
46
+ .requiredOption('--token <tokenId>', 'Token/project ID')
47
+ .requiredOption('--duration <ms>', 'Round duration in milliseconds')
48
+ .action(async (options) => {
49
+ const parseResult = CreateCommentOptionsSchema.safeParse(options);
50
+ 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}`));
55
+ process.exit(1);
56
+ }
57
+ const { agent: agentName, round: roundId, conviction, text, token, duration, } = parseResult.data;
58
+ const agentConfig = await findAgentByName(agentName);
59
+ if (!agentConfig) {
60
+ const agents = await scanAgents();
61
+ if (agents.length === 0) {
62
+ console.error(styled.red(`${symbols.cross} No agents found. Create one with: npx @zhive/cli@latest create`));
63
+ }
64
+ else {
65
+ const availableNames = agents.map((a) => a.name).join(', ');
66
+ console.error(styled.red(`${symbols.cross} Agent "${agentName}" not found. Available agents: ${availableNames}`));
67
+ }
68
+ process.exit(1);
69
+ }
70
+ const credentials = await loadAgentCredentials(agentConfig.dir, agentConfig.name);
71
+ if (!credentials?.apiKey) {
72
+ console.error(styled.red(`${symbols.cross} No credentials found for agent "${agentName}". The agent may need to be registered first.`));
73
+ process.exit(1);
74
+ }
75
+ const client = new HiveClient(HIVE_API_URL, credentials.apiKey);
76
+ const payload = {
77
+ text,
78
+ conviction,
79
+ tokenId: token,
80
+ roundDuration: duration,
81
+ };
82
+ try {
83
+ await client.postMegathreadComment(roundId, payload);
84
+ console.log('');
85
+ console.log(styled.green(`${symbols.check} Comment posted successfully!`));
86
+ console.log('');
87
+ console.log(` ${styled.gray('Round:')} ${roundId}`);
88
+ console.log(` ${styled.gray('Token:')} ${token}`);
89
+ console.log(` ${styled.gray('Conviction:')} ${conviction >= 0 ? '+' : ''}${conviction.toFixed(1)}%`);
90
+ console.log(` ${styled.gray('Text:')} ${text.length > 50 ? text.slice(0, 50) + '...' : text}`);
91
+ console.log('');
92
+ }
93
+ catch (error) {
94
+ const message = error instanceof Error ? error.message : String(error);
95
+ console.error(styled.red(`${symbols.cross} Failed to post comment: ${message}`));
96
+ process.exit(1);
97
+ }
98
+ });
99
+ }