@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
@@ -1,3 +1,4 @@
1
+ import { loadCredentials, } from '@zhive/sdk';
1
2
  import axios from 'axios';
2
3
  import fsExtra from 'fs-extra';
3
4
  import * as fs from 'fs/promises';
@@ -174,3 +175,21 @@ function parseTimeframes(raw) {
174
175
  }
175
176
  return parsed;
176
177
  }
178
+ export async function findAgentByName(name) {
179
+ const agents = await scanAgents();
180
+ const agent = agents.find((a) => a.name === name);
181
+ if (!agent) {
182
+ return null;
183
+ }
184
+ return agent;
185
+ }
186
+ export function getCredentialsPath(agentDir, agentName) {
187
+ const sanitized = agentName.replace(/[^a-zA-Z0-9-_]/g, '-');
188
+ const credPath = path.join(agentDir, `hive-${sanitized}.json`);
189
+ return credPath;
190
+ }
191
+ export async function loadAgentCredentials(agentDir, agentName) {
192
+ const credPath = getCredentialsPath(agentDir, agentName);
193
+ const credentials = await loadCredentials(credPath);
194
+ return credentials;
195
+ }
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect, vi, beforeEach } 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('./constant.js', () => ({
7
+ getHiveDir: vi.fn(() => FIXTURES_DIR),
8
+ HIVE_API_URL: 'http://localhost:6969',
9
+ }));
10
+ vi.mock('./ai-providers.js', () => ({
11
+ AI_PROVIDERS: [
12
+ { label: 'OpenAI', package: '@ai-sdk/openai', envVar: 'OPENAI_API_KEY' },
13
+ { label: 'Anthropic', package: '@ai-sdk/anthropic', envVar: 'ANTHROPIC_API_KEY' },
14
+ ],
15
+ }));
16
+ vi.mock('@zhive/sdk', () => ({
17
+ loadCredentials: vi.fn(),
18
+ }));
19
+ import { loadCredentials } from '@zhive/sdk';
20
+ import { findAgentByName, getCredentialsPath, loadAgentCredentials, scanAgents, } from './agent.js';
21
+ const mockLoadCredentials = loadCredentials;
22
+ describe('getCredentialsPath', () => {
23
+ it('constructs correct path with simple agent name', () => {
24
+ const result = getCredentialsPath('/mock/.zhive/agents/my-agent', 'my-agent');
25
+ expect(result).toBe('/mock/.zhive/agents/my-agent/hive-my-agent.json');
26
+ });
27
+ it('sanitizes special characters in agent name', () => {
28
+ const result = getCredentialsPath('/mock/.zhive/agents/my-agent', 'my@agent!test');
29
+ expect(result).toBe('/mock/.zhive/agents/my-agent/hive-my-agent-test.json');
30
+ });
31
+ it('allows underscores and hyphens in agent name', () => {
32
+ const result = getCredentialsPath('/mock/.zhive/agents/test_agent', 'test_agent-v2');
33
+ expect(result).toBe('/mock/.zhive/agents/test_agent/hive-test_agent-v2.json');
34
+ });
35
+ });
36
+ describe('loadAgentCredentials', () => {
37
+ beforeEach(() => {
38
+ vi.clearAllMocks();
39
+ });
40
+ it('delegates to loadCredentials with correct path', async () => {
41
+ const mockCredentials = { apiKey: 'test-api-key' };
42
+ mockLoadCredentials.mockResolvedValue(mockCredentials);
43
+ const result = await loadAgentCredentials('/mock/.zhive/agents/test-agent', 'test-agent');
44
+ expect(mockLoadCredentials).toHaveBeenCalledWith('/mock/.zhive/agents/test-agent/hive-test-agent.json');
45
+ expect(result).toEqual(mockCredentials);
46
+ });
47
+ it('returns null when loadCredentials returns null', async () => {
48
+ mockLoadCredentials.mockResolvedValue(null);
49
+ const result = await loadAgentCredentials('/mock/.zhive/agents/test-agent', 'test-agent');
50
+ expect(result).toBeNull();
51
+ });
52
+ });
53
+ describe('scanAgents', () => {
54
+ beforeEach(() => {
55
+ vi.clearAllMocks();
56
+ });
57
+ it('discovers all valid agents from fixtures directory', async () => {
58
+ const result = await scanAgents();
59
+ expect(result).toHaveLength(3);
60
+ const names = result.map((a) => a.name).sort();
61
+ expect(names).toEqual(['agent-no-skills', 'empty-agent', 'test-agent']);
62
+ });
63
+ it('loads agent config with correct properties', async () => {
64
+ const result = await scanAgents();
65
+ const testAgent = result.find((a) => a.name === 'test-agent');
66
+ expect(testAgent).toBeDefined();
67
+ expect(testAgent?.bio).toBe('Test agent for CLI testing');
68
+ expect(testAgent?.avatarUrl).toBe('https://example.com/avatar.png');
69
+ expect(testAgent?.agentProfile.sentiment).toBe('bullish');
70
+ expect(testAgent?.agentProfile.timeframes).toEqual(['1h', '4h']);
71
+ expect(testAgent?.agentProfile.sectors).toEqual(['defi', 'gaming']);
72
+ });
73
+ it('loads agent with different sentiment', async () => {
74
+ const result = await scanAgents();
75
+ const bearishAgent = result.find((a) => a.name === 'agent-no-skills');
76
+ expect(bearishAgent).toBeDefined();
77
+ expect(bearishAgent?.agentProfile.sentiment).toBe('bearish');
78
+ expect(bearishAgent?.agentProfile.timeframes).toEqual(['1h']);
79
+ expect(bearishAgent?.agentProfile.sectors).toEqual(['infrastructure']);
80
+ });
81
+ it('handles agent with empty sectors', async () => {
82
+ const result = await scanAgents();
83
+ const emptyAgent = result.find((a) => a.name === 'empty-agent');
84
+ expect(emptyAgent).toBeDefined();
85
+ expect(emptyAgent?.agentProfile.sentiment).toBe('neutral');
86
+ expect(emptyAgent?.agentProfile.sectors).toEqual([]);
87
+ });
88
+ });
89
+ describe('findAgentByName', () => {
90
+ beforeEach(() => {
91
+ vi.clearAllMocks();
92
+ });
93
+ it('returns null when agent name does not match', async () => {
94
+ const result = await findAgentByName('non-existent-agent');
95
+ expect(result).toBeNull();
96
+ });
97
+ it('returns agent when name matches', async () => {
98
+ const result = await findAgentByName('test-agent');
99
+ expect(result).not.toBeNull();
100
+ expect(result?.name).toBe('test-agent');
101
+ expect(result?.dir).toBe(path.join(FIXTURES_DIR, 'agents', 'test-agent'));
102
+ });
103
+ it('finds empty-agent by name', async () => {
104
+ const result = await findAgentByName('empty-agent');
105
+ expect(result).not.toBeNull();
106
+ expect(result?.name).toBe('empty-agent');
107
+ expect(result?.bio).toBe('Empty agent with no skills for testing');
108
+ });
109
+ it('finds agent-no-skills by name', async () => {
110
+ const result = await findAgentByName('agent-no-skills');
111
+ expect(result).not.toBeNull();
112
+ expect(result?.name).toBe('agent-no-skills');
113
+ expect(result?.bio).toBe('Agent without skills directory for testing');
114
+ });
115
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhive/cli",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "CLI for bootstrapping zHive AI Agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "scripts": {
19
19
  "build": "tsc -p tsconfig.build.json",
20
- "dev": "tsx src/index.tsx",
20
+ "dev": "tsx src/index.ts",
21
21
  "start": "node dist/index.js",
22
22
  "deploy": "node scripts/deploy.cjs",
23
23
  "analyze": "tsx src/sandbox/analyze.ts",
@@ -30,11 +30,12 @@
30
30
  "@ai-sdk/google": "^3.0.0",
31
31
  "@ai-sdk/openai": "^3.0.25",
32
32
  "@ai-sdk/xai": "^3.0.0",
33
- "@zhive/sdk": "^0.5.2",
34
33
  "@openrouter/ai-sdk-provider": "^0.4.0",
34
+ "@zhive/sdk": "^0.5.2",
35
35
  "ai": "^6.0.71",
36
36
  "axios": "^1.6.0",
37
37
  "chalk": "^5.3.0",
38
+ "commander": "^14.0.3",
38
39
  "dotenv": "^16.0.0",
39
40
  "fs-extra": "^11.2.0",
40
41
  "ink": "^5.1.0",
package/dist/agent/app.js DELETED
@@ -1,122 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useRef } from 'react';
3
- import { Box, Text, useStdout } from 'ink';
4
- import chalk from 'chalk';
5
- import { useAgent } from './hooks/useAgent.js';
6
- import { colors, symbols, border } from './theme.js';
7
- import { formatTime, convictionColor } from './helpers.js';
8
- import { Spinner, PollText } from './components/Spinner.js';
9
- import { AsciiTicker } from './components/AsciiTicker.js';
10
- import { CommandInput } from './components/CommandInput.js';
11
- // ─── Format a settled poll item as a chalk string for stdout ───
12
- function formatSettledItem(item) {
13
- const time = chalk.gray.dim(`${formatTime(item.timestamp)} `);
14
- const lines = [];
15
- if (item.type === 'online') {
16
- lines.push(` ${time}${chalk.hex(colors.honey)(symbols.hive)} ${chalk.white(item.text)}`);
17
- }
18
- if (item.type === 'signal' || item.type === 'megathread') {
19
- const isMega = item.type === 'megathread';
20
- const accent = isMega ? colors.controversial : colors.cyan;
21
- const hiveColor = isMega ? colors.controversial : colors.honey;
22
- let mainLine = ` ${time}${chalk.hex(hiveColor)(symbols.hive)} ${chalk.hex(accent)(item.text)}`;
23
- if (item.status === 'skipped') {
24
- mainLine += chalk.hex(colors.honey)(` ${symbols.diamondOpen} skipped`);
25
- if (item.tokenUsage) {
26
- let tokenInfo = ` ${symbols.circle} ${item.tokenUsage.inputTokens.toLocaleString()} in`;
27
- if (item.tokenUsage.cacheReadTokens > 0) {
28
- tokenInfo += ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`;
29
- }
30
- else if (item.tokenUsage.cacheWriteTokens > 0) {
31
- tokenInfo += ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`;
32
- }
33
- tokenInfo += ` \u00b7 ${item.tokenUsage.outputTokens.toLocaleString()} out`;
34
- if (item.tokenUsage.toolCalls > 0) {
35
- tokenInfo += ` \u00b7 tools: ${item.tokenUsage.toolNames.join(', ')}`;
36
- }
37
- mainLine += chalk.gray.dim(tokenInfo);
38
- }
39
- }
40
- lines.push(mainLine);
41
- if (item.status === 'posted' && item.result) {
42
- const pad = ' '.repeat(13);
43
- const cColor = isMega ? colors.controversial : convictionColor(item.conviction ?? 0);
44
- lines.push(`${pad}${chalk.hex(cColor)(symbols.diamond)} ${chalk.white(item.result)}`);
45
- if (item.url) {
46
- lines.push(`${' '.repeat(15)}${chalk.gray.dim(`url: ${item.url}`)}`);
47
- }
48
- if (item.tokenUsage) {
49
- let tokenInfo = `tokens: ${item.tokenUsage.inputTokens.toLocaleString()} in`;
50
- if (item.tokenUsage.cacheReadTokens > 0) {
51
- tokenInfo += ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`;
52
- }
53
- else if (item.tokenUsage.cacheWriteTokens > 0) {
54
- tokenInfo += ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`;
55
- }
56
- tokenInfo += ` \u00b7 ${item.tokenUsage.outputTokens.toLocaleString()} out`;
57
- lines.push(`${' '.repeat(15)}${chalk.gray.dim(tokenInfo)}`);
58
- if (item.tokenUsage.toolCalls > 0) {
59
- const toolInfo = `tools: ${item.tokenUsage.toolCalls} tools (${item.tokenUsage.toolNames.join(', ')})`;
60
- lines.push(`${' '.repeat(15)}${chalk.gray.dim(toolInfo)}`);
61
- }
62
- }
63
- }
64
- if (item.status === 'error' && item.result) {
65
- const pad = ' '.repeat(13);
66
- lines.push(`${pad}${chalk.hex(colors.red)(`${symbols.cross} ${item.result}`)}`);
67
- }
68
- }
69
- if (item.type === 'idle') {
70
- lines.push(` ${time}${chalk.gray(`${symbols.circle} ${item.text}`)}`);
71
- }
72
- if (item.type === 'error') {
73
- lines.push(` ${time}${chalk.hex(colors.red)(`${symbols.cross} ${item.text}`)}`);
74
- }
75
- return lines.join('\n');
76
- }
77
- // ─── Main TUI App ────────────────────────────────────
78
- export function App() {
79
- const { connected, agentName, modelInfo, pollActivity, chatActivity, input, chatStreaming, chatBuffer, predictionCount, termWidth, setInput, handleChatSubmit, } = useAgent();
80
- const { write } = useStdout();
81
- // When stdin is not a TTY (piped by hive-cli start), skip interactive input
82
- const isInteractive = process.stdin.isTTY === true;
83
- const boxWidth = termWidth;
84
- // Split poll items: settled items are written to stdout via useStdout (scrollback),
85
- // active items render in the dynamic Ink section (need spinner animation).
86
- const settledStatuses = new Set(['posted', 'skipped', 'error', undefined]);
87
- const settledTypes = new Set(['idle', 'online', 'error']);
88
- const isSettled = (item) => {
89
- if (settledTypes.has(item.type))
90
- return true;
91
- return settledStatuses.has(item.status) && item.status !== undefined;
92
- };
93
- const settledPollItems = pollActivity.filter(isSettled);
94
- const activePollItems = pollActivity.filter((item) => !isSettled(item));
95
- const visibleChatActivity = chatActivity.slice(-5);
96
- // Write settled items to stdout permanently (scrollback history).
97
- // Track how many we've already written so each item is written once.
98
- const writtenCountRef = useRef(0);
99
- useEffect(() => {
100
- const newCount = settledPollItems.length;
101
- if (newCount > writtenCountRef.current) {
102
- const newItems = settledPollItems.slice(writtenCountRef.current);
103
- for (const item of newItems) {
104
- const formatted = formatSettledItem(item);
105
- write(formatted + '\n');
106
- }
107
- writtenCountRef.current = newCount;
108
- }
109
- }, [settledPollItems.length, write]);
110
- const statsText = predictionCount > 0 ? ` ${border.horizontal.repeat(3)} ${predictionCount} predicted` : '';
111
- const connectedDisplay = connected ? 'Connected to the Hive' : 'connecting...';
112
- const nameDisplay = `${agentName} agent`;
113
- const headerFill = Math.max(0, boxWidth - nameDisplay.length - connectedDisplay.length - 12 - statsText.length);
114
- return (_jsxs(Box, { flexDirection: "column", width: boxWidth, children: [_jsx(AsciiTicker, { rows: 2, step: predictionCount }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: `${border.topLeft}${border.horizontal} ${symbols.hive} ` }), _jsxs(Text, { color: colors.white, bold: true, children: [agentName, " agent"] }), _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), _jsx(Text, { color: connected ? colors.green : colors.honey, children: connected ? 'Connected to the Hive' : 'connecting...' }), statsText && _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), statsText && _jsxs(Text, { color: colors.honey, children: [predictionCount, " predicted"] }), _jsxs(Text, { color: colors.gray, children: [' ', border.horizontal.repeat(Math.max(0, headerFill)), border.topRight] })] }), modelInfo && (_jsxs(Box, { paddingLeft: 1, children: [_jsxs(Text, { color: colors.gray, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.cyan, children: modelInfo.modelId }), _jsxs(Text, { color: colors.gray, children: [" ", '\u00d7', " "] }), _jsx(Text, { color: colors.white, children: "zData" })] })), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 2, children: [!connected && _jsx(Spinner, { label: "Initiating neural link..." }), activePollItems.map((item, i) => {
115
- const isMega = item.type === 'megathread';
116
- const accentColor = isMega ? colors.controversial : colors.cyan;
117
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: isMega ? colors.controversial : colors.honey, children: [symbols.hive, " "] }), _jsx(PollText, { color: accentColor, text: item.text, animate: false }), _jsx(Text, { children: " " }), _jsx(Spinner, { label: "analyzing..." })] }), item.detail && (_jsx(Box, { marginLeft: 13, children: _jsx(PollText, { color: colors.gray, text: `"${item.detail}"`, animate: false }) }))] }, `active-${item.id ?? i}`));
118
- })] }), (chatActivity.length > 0 || chatStreaming) && (_jsxs(_Fragment, { children: [_jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.teeLeft, `${border.horizontal.repeat(2)} chat with ${agentName} agent `, border.horizontal.repeat(Math.max(0, boxWidth - agentName.length - 22)), border.teeRight] }) }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 2, maxHeight: 8, children: [visibleChatActivity.map((item, i) => (_jsxs(Box, { children: [item.type === 'chat-user' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.white, bold: true, children: ["you:", ' '] }), _jsx(Text, { color: colors.white, children: item.text })] })), item.type === 'chat-agent' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, bold: true, children: [agentName, " agent:", ' '] }), _jsx(Text, { color: colors.white, wrap: "wrap", children: item.text })] })), item.type === 'chat-error' && (_jsx(Box, { children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", item.text] }) }))] }, i))), chatStreaming && chatBuffer && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, bold: true, children: [agentName, " agent:", ' '] }), _jsx(Text, { color: colors.white, wrap: "wrap", children: chatBuffer })] }))] })] })), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [isInteractive ? border.teeLeft : border.bottomLeft, border.horizontal.repeat(boxWidth - 2), isInteractive ? border.teeRight : border.bottomRight] }) }), isInteractive && (_jsxs(_Fragment, { children: [_jsx(Box, { paddingLeft: 1, children: _jsx(CommandInput, { value: input, onChange: setInput, onSubmit: (val) => {
119
- setInput('');
120
- void handleChatSubmit(val);
121
- }, placeholder: chatStreaming ? 'thinking...' : `chat with ${agentName} agent...` }) }), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.bottomLeft, border.horizontal.repeat(boxWidth - 2), border.bottomRight] }) })] }))] }));
122
- }
@@ -1,12 +0,0 @@
1
- export const SLASH_COMMANDS = [
2
- { name: '/skills', description: 'List available skills' },
3
- { name: '/help', description: 'Show available commands' },
4
- { name: '/clear', description: 'Clear chat history' },
5
- { name: '/memory', description: 'Show current memory state' },
6
- { name: '/backtest', description: 'Run agent against test set (/backtest <num> fetches from API)' },
7
- ];
8
- export function filterCommands(prefix) {
9
- const lowerPrefix = prefix.toLowerCase();
10
- const filtered = SLASH_COMMANDS.filter((cmd) => cmd.name.toLowerCase().startsWith(lowerPrefix));
11
- return filtered;
12
- }
@@ -1,81 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { colors, animation } from '../theme.js';
5
- function buildTickerChars(step) {
6
- const stepStr = String(step).padStart(2, '0');
7
- const digits = stepStr.split('');
8
- return animation.HEX_CHARS + digits.join('') + '\u25AA\u25AB\u2591\u2592';
9
- }
10
- function buildRow(cols, frame, rowIndex, tickerChars) {
11
- const segments = [];
12
- const isSecondRow = rowIndex === 1;
13
- const scrollSpeed = isSecondRow ? 3 : 2;
14
- const direction = isSecondRow ? -1 : 1;
15
- const sinFreq = isSecondRow ? 0.4 : 0.3;
16
- const sinPhase = isSecondRow ? -0.4 : 0.6;
17
- const wrapLen = cols * 2;
18
- for (let c = 0; c < cols; c++) {
19
- const scrolledC = ((direction === 1)
20
- ? (c + frame * scrollSpeed) % wrapLen
21
- : (cols - c + frame * scrollSpeed) % wrapLen);
22
- const charIdx = scrolledC % tickerChars.length;
23
- const char = tickerChars[charIdx];
24
- const isHex = char === '\u2B21' || char === '\u2B22';
25
- const pulseHit = Math.sin((c + frame * sinPhase) * sinFreq) > 0.5;
26
- // Edge fade: dim the outermost 4 columns
27
- const edgeDist = Math.min(c, cols - 1 - c);
28
- if (edgeDist < 2) {
29
- segments.push({ char: '\u00B7', color: colors.grayDim });
30
- continue;
31
- }
32
- if (edgeDist < 4) {
33
- segments.push({ char, color: colors.grayDim });
34
- continue;
35
- }
36
- if (pulseHit && isHex) {
37
- segments.push({ char, color: colors.honey });
38
- }
39
- else if (pulseHit) {
40
- segments.push({ char, color: colors.green });
41
- }
42
- else {
43
- segments.push({ char, color: colors.grayDim });
44
- }
45
- }
46
- return segments;
47
- }
48
- function renderSegments(segments) {
49
- const elements = [];
50
- let runColor = segments[0]?.color ?? colors.grayDim;
51
- let runChars = '';
52
- for (let i = 0; i < segments.length; i++) {
53
- const seg = segments[i];
54
- if (seg.color === runColor) {
55
- runChars += seg.char;
56
- }
57
- else {
58
- elements.push(_jsx(Text, { color: runColor, children: runChars }, `${elements.length}`));
59
- runColor = seg.color;
60
- runChars = seg.char;
61
- }
62
- }
63
- if (runChars.length > 0) {
64
- elements.push(_jsx(Text, { color: runColor, children: runChars }, `${elements.length}`));
65
- }
66
- return elements;
67
- }
68
- export function AsciiTicker({ rows = 1, step = 1 }) {
69
- const [frame, setFrame] = useState(0);
70
- const cols = process.stdout.columns || 60;
71
- const tickerChars = buildTickerChars(step);
72
- useEffect(() => {
73
- const timer = setInterval(() => {
74
- setFrame((prev) => prev + 1);
75
- }, animation.TICK_MS);
76
- return () => {
77
- clearInterval(timer);
78
- };
79
- }, []);
80
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: renderSegments(buildRow(cols, frame, 0, tickerChars)) }), rows === 2 && (_jsx(Text, { children: renderSegments(buildRow(cols, frame, 1, tickerChars)) }))] }));
81
- }
@@ -1,65 +0,0 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { useState, useCallback, useMemo } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- import TextInput from 'ink-text-input';
5
- import { colors, symbols } from '../theme.js';
6
- import { filterCommands } from '../commands/registry.js';
7
- export function CommandInput({ value, onChange, onSubmit, placeholder, }) {
8
- const [selectedIndex, setSelectedIndex] = useState(0);
9
- // Determine if autocomplete should be active
10
- const isAutocompleteActive = value.startsWith('/') && !value.includes(' ');
11
- // Get filtered commands
12
- const filteredCommands = useMemo(() => {
13
- if (!isAutocompleteActive) {
14
- return [];
15
- }
16
- const commands = filterCommands(value);
17
- return commands;
18
- }, [value, isAutocompleteActive]);
19
- // Reset selection when filtered commands change
20
- const commandCount = filteredCommands.length;
21
- const safeSelectedIndex = commandCount > 0 ? Math.min(selectedIndex, commandCount - 1) : 0;
22
- const handleChange = useCallback((newValue) => {
23
- onChange(newValue);
24
- setSelectedIndex(0);
25
- }, [onChange]);
26
- const handleSubmit = useCallback((submittedValue) => {
27
- // If autocomplete is active and there are matches, complete first
28
- if (isAutocompleteActive && filteredCommands.length > 0) {
29
- const selected = filteredCommands[safeSelectedIndex];
30
- if (selected && submittedValue !== selected.name) {
31
- onChange(selected.name);
32
- onSubmit(selected.name);
33
- return;
34
- }
35
- }
36
- onSubmit(submittedValue);
37
- }, [isAutocompleteActive, filteredCommands, safeSelectedIndex, onChange, onSubmit]);
38
- useInput((input, key) => {
39
- if (!isAutocompleteActive || filteredCommands.length === 0) {
40
- return;
41
- }
42
- if (key.upArrow) {
43
- setSelectedIndex((prev) => (prev > 0 ? prev - 1 : commandCount - 1));
44
- }
45
- else if (key.downArrow) {
46
- setSelectedIndex((prev) => (prev < commandCount - 1 ? prev + 1 : 0));
47
- }
48
- else if (key.tab) {
49
- // Tab to complete without submitting
50
- const selected = filteredCommands[safeSelectedIndex];
51
- if (selected) {
52
- onChange(selected.name);
53
- }
54
- }
55
- else if (key.escape) {
56
- // Escape to clear input
57
- onChange('');
58
- setSelectedIndex(0);
59
- }
60
- }, { isActive: isAutocompleteActive && filteredCommands.length > 0 });
61
- return (_jsxs(Box, { flexDirection: "column", children: [isAutocompleteActive && filteredCommands.length > 0 && (_jsx(Box, { flexDirection: "column", marginBottom: 0, marginLeft: 2, children: filteredCommands.map((cmd, index) => {
62
- const isSelected = index === safeSelectedIndex;
63
- return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.honey : colors.gray, children: [isSelected ? symbols.diamond : ' ', ' '] }), _jsx(Text, { color: isSelected ? colors.honey : colors.white, children: cmd.name }), _jsxs(Text, { color: colors.gray, children: [" - ", cmd.description] })] }, cmd.name));
64
- }) })), _jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [symbols.arrow, " "] }), _jsx(TextInput, { value: value, onChange: handleChange, onSubmit: handleSubmit, placeholder: placeholder })] })] }));
65
- }