@zhive/cli 0.5.4 → 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.
- package/README.md +5 -5
- package/dist/CLAUDE.md +7 -0
- package/dist/backtest/CLAUDE.md +7 -0
- package/dist/cli.js +20 -0
- package/dist/commands/agent/commands/index.js +7 -0
- package/dist/commands/agent/commands/profile.js +40 -0
- package/dist/commands/agent/commands/profile.test.js +137 -0
- package/dist/commands/create/commands/index.js +10 -5
- package/dist/commands/list/commands/index.js +8 -3
- package/dist/commands/megathread/commands/create-comment.js +99 -0
- package/dist/commands/megathread/commands/create-comment.test.js +480 -0
- package/dist/commands/megathread/commands/index.js +9 -0
- package/dist/commands/megathread/commands/list.js +102 -0
- package/dist/commands/megathread/commands/list.test.js +206 -0
- package/dist/commands/migrate-templates/commands/index.js +9 -4
- package/dist/commands/run/commands/index.js +17 -12
- package/dist/commands/run/run-headless.js +2 -1
- package/dist/commands/start/commands/index.js +37 -32
- package/dist/commands/start/hooks/useAgent.js +2 -1
- package/dist/commands/start/services/backtest/runner.js +1 -1
- package/dist/commands/start-all/commands/index.js +22 -17
- package/dist/index.js +26 -57
- 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/analysis.js +10 -13
- package/dist/shared/agent/config.js +75 -0
- package/dist/shared/agent/env.js +30 -0
- package/dist/shared/agent/handler.js +129 -0
- package/dist/shared/agent/helpers/model.js +92 -0
- package/dist/shared/agent/prompts/prompt.js +1 -1
- package/dist/shared/agent/runtime.js +15 -0
- package/dist/shared/ai-providers.js +66 -0
- package/dist/shared/config/agent.js +19 -0
- package/dist/shared/config/agent.test.js +115 -0
- package/dist/shared/config/ai-providers.js +27 -21
- package/package.json +4 -3
- 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/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 `~/.
|
|
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 `~/.
|
|
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 `~/.
|
|
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 `~/.
|
|
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, `~/.
|
|
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
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
5
|
-
|
|
6
|
-
|
|
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
|
+
}
|