@zhive/cli 0.5.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 +118 -0
- package/dist/agent/analysis.js +160 -0
- package/dist/agent/app.js +122 -0
- package/dist/agent/chat-prompt.js +65 -0
- package/dist/agent/commands/registry.js +12 -0
- package/dist/agent/components/AsciiTicker.js +81 -0
- package/dist/agent/components/CommandInput.js +65 -0
- package/dist/agent/components/HoneycombBoot.js +291 -0
- package/dist/agent/components/Spinner.js +37 -0
- package/dist/agent/config.js +75 -0
- package/dist/agent/edit-section.js +59 -0
- package/dist/agent/fetch-rules.js +21 -0
- package/dist/agent/helpers.js +22 -0
- package/dist/agent/hooks/useAgent.js +480 -0
- package/dist/agent/memory-prompt.js +47 -0
- package/dist/agent/model.js +92 -0
- package/dist/agent/objects.js +1 -0
- package/dist/agent/process-lifecycle.js +18 -0
- package/dist/agent/prompt.js +353 -0
- package/dist/agent/run-headless.js +189 -0
- package/dist/agent/skills/index.js +2 -0
- package/dist/agent/skills/skill-parser.js +149 -0
- package/dist/agent/skills/types.js +1 -0
- package/dist/agent/theme.js +41 -0
- package/dist/agent/tools/index.js +76 -0
- package/dist/agent/tools/market/client.js +41 -0
- package/dist/agent/tools/market/index.js +3 -0
- package/dist/agent/tools/market/tools.js +518 -0
- package/dist/agent/tools/mindshare/client.js +124 -0
- package/dist/agent/tools/mindshare/index.js +3 -0
- package/dist/agent/tools/mindshare/tools.js +563 -0
- package/dist/agent/tools/read-skill-tool.js +30 -0
- package/dist/agent/tools/ta/index.js +1 -0
- package/dist/agent/tools/ta/indicators.js +201 -0
- package/dist/agent/types.js +1 -0
- package/dist/agents.js +110 -0
- package/dist/ai-providers.js +66 -0
- package/dist/avatar.js +34 -0
- package/dist/backtest/default-backtest-data.js +200 -0
- package/dist/backtest/fetch.js +41 -0
- package/dist/backtest/import.js +106 -0
- package/dist/backtest/index.js +10 -0
- package/dist/backtest/results.js +113 -0
- package/dist/backtest/runner.js +134 -0
- package/dist/backtest/storage.js +11 -0
- package/dist/backtest/types.js +1 -0
- package/dist/commands/create/ai-generate.js +126 -0
- package/dist/commands/create/commands/index.js +10 -0
- package/dist/commands/create/generate.js +73 -0
- package/dist/commands/create/presets/data.js +225 -0
- package/dist/commands/create/presets/formatting.js +81 -0
- package/dist/commands/create/presets/index.js +3 -0
- package/dist/commands/create/presets/options.js +307 -0
- package/dist/commands/create/presets/types.js +1 -0
- package/dist/commands/create/presets.js +613 -0
- package/dist/commands/create/ui/CreateApp.js +172 -0
- package/dist/commands/create/ui/steps/ApiKeyStep.js +89 -0
- package/dist/commands/create/ui/steps/AvatarStep.js +16 -0
- package/dist/commands/create/ui/steps/DoneStep.js +14 -0
- package/dist/commands/create/ui/steps/IdentityStep.js +125 -0
- package/dist/commands/create/ui/steps/NameStep.js +148 -0
- package/dist/commands/create/ui/steps/ScaffoldStep.js +59 -0
- package/dist/commands/create/ui/steps/SoulStep.js +21 -0
- package/dist/commands/create/ui/steps/StrategyStep.js +20 -0
- package/dist/commands/create/ui/steps/StreamingGenerationStep.js +56 -0
- package/dist/commands/create/ui/validation.js +34 -0
- package/dist/commands/create/validate-api-key.js +27 -0
- package/dist/commands/install.js +50 -0
- package/dist/commands/list/commands/index.js +7 -0
- package/dist/commands/list/ui/ListApp.js +79 -0
- package/dist/commands/migrate-templates/commands/index.js +9 -0
- package/dist/commands/migrate-templates/migrate.js +87 -0
- package/dist/commands/migrate-templates/ui/MigrateApp.js +132 -0
- package/dist/commands/run/commands/index.js +17 -0
- package/dist/commands/run/run-headless.js +111 -0
- package/dist/commands/shared/theme.js +57 -0
- package/dist/commands/shared/welcome.js +304 -0
- package/dist/commands/start/commands/backtest.js +35 -0
- package/dist/commands/start/commands/index.js +62 -0
- package/dist/commands/start/commands/prediction.js +73 -0
- package/dist/commands/start/commands/skills.js +44 -0
- package/dist/commands/start/commands/skills.test.js +140 -0
- package/dist/commands/start/hooks/types.js +1 -0
- package/dist/commands/start/hooks/useAgent.js +177 -0
- package/dist/commands/start/hooks/useChat.js +266 -0
- package/dist/commands/start/hooks/usePollActivity.js +45 -0
- package/dist/commands/start/hooks/utils.js +152 -0
- package/dist/commands/start/services/backtest/default-backtest-data.js +200 -0
- package/dist/commands/start/services/backtest/fetch.js +42 -0
- package/dist/commands/start/services/backtest/import.js +109 -0
- package/dist/commands/start/services/backtest/index.js +10 -0
- package/dist/commands/start/services/backtest/results.js +113 -0
- package/dist/commands/start/services/backtest/runner.js +103 -0
- package/dist/commands/start/services/backtest/storage.js +11 -0
- package/dist/commands/start/services/backtest/types.js +1 -0
- package/dist/commands/start/services/command-registry.js +13 -0
- package/dist/commands/start/ui/AsciiTicker.js +81 -0
- package/dist/commands/start/ui/CommandInput.js +65 -0
- package/dist/commands/start/ui/HoneycombBoot.js +291 -0
- package/dist/commands/start/ui/PollText.js +23 -0
- package/dist/commands/start/ui/PredictionsPanel.js +88 -0
- package/dist/commands/start/ui/SelectAgentApp.js +93 -0
- package/dist/commands/start/ui/Spinner.js +29 -0
- package/dist/commands/start/ui/SpinnerContext.js +20 -0
- package/dist/commands/start/ui/app.js +36 -0
- package/dist/commands/start-all/AgentProcessManager.js +98 -0
- package/dist/commands/start-all/commands/index.js +24 -0
- package/dist/commands/start-all/ui/Dashboard.js +91 -0
- package/dist/components/AsciiTicker.js +81 -0
- package/dist/components/CharacterSummaryCard.js +33 -0
- package/dist/components/CodeBlock.js +11 -0
- package/dist/components/ColoredStats.js +18 -0
- package/dist/components/Header.js +10 -0
- package/dist/components/HoneycombLoader.js +190 -0
- package/dist/components/InputGuard.js +6 -0
- package/dist/components/MultiSelectPrompt.js +45 -0
- package/dist/components/SelectPrompt.js +20 -0
- package/dist/components/Spinner.js +16 -0
- package/dist/components/StepIndicator.js +31 -0
- package/dist/components/StreamingText.js +50 -0
- package/dist/components/TextPrompt.js +28 -0
- package/dist/components/stdout-spinner.js +48 -0
- package/dist/config.js +28 -0
- package/dist/create/CreateApp.js +153 -0
- package/dist/create/ai-generate.js +147 -0
- package/dist/create/generate.js +73 -0
- package/dist/create/steps/ApiKeyStep.js +97 -0
- package/dist/create/steps/AvatarStep.js +16 -0
- package/dist/create/steps/BioStep.js +14 -0
- package/dist/create/steps/DoneStep.js +14 -0
- package/dist/create/steps/IdentityStep.js +163 -0
- package/dist/create/steps/NameStep.js +71 -0
- package/dist/create/steps/ScaffoldStep.js +58 -0
- package/dist/create/steps/SoulStep.js +58 -0
- package/dist/create/steps/StrategyStep.js +58 -0
- package/dist/create/validate-api-key.js +47 -0
- package/dist/create/welcome.js +304 -0
- package/dist/index.js +60 -0
- package/dist/list/ListApp.js +79 -0
- package/dist/load-agent-env.js +30 -0
- package/dist/migrate-templates/MigrateApp.js +131 -0
- package/dist/migrate-templates/migrate.js +86 -0
- package/dist/presets.js +613 -0
- package/dist/shared/agent/agent-runtime.js +144 -0
- package/dist/shared/agent/analysis.js +171 -0
- package/dist/shared/agent/helpers.js +1 -0
- package/dist/shared/agent/prompts/chat-prompt.js +60 -0
- package/dist/shared/agent/prompts/megathread.js +202 -0
- package/dist/shared/agent/prompts/memory-prompt.js +47 -0
- package/dist/shared/agent/prompts/prompt.js +18 -0
- package/dist/shared/agent/skills/index.js +2 -0
- package/dist/shared/agent/skills/skill-parser.js +167 -0
- package/dist/shared/agent/skills/skill-parser.test.js +190 -0
- package/dist/shared/agent/skills/types.js +1 -0
- package/dist/shared/agent/tools/edit-section.js +60 -0
- package/dist/shared/agent/tools/execute-skill-tool.js +134 -0
- package/dist/shared/agent/tools/fetch-rules.js +22 -0
- package/dist/shared/agent/tools/formatting.js +48 -0
- package/dist/shared/agent/tools/index.js +87 -0
- package/dist/shared/agent/tools/market/client.js +41 -0
- package/dist/shared/agent/tools/market/index.js +3 -0
- package/dist/shared/agent/tools/market/tools.js +497 -0
- package/dist/shared/agent/tools/mindshare/client.js +124 -0
- package/dist/shared/agent/tools/mindshare/index.js +3 -0
- package/dist/shared/agent/tools/mindshare/tools.js +167 -0
- package/dist/shared/agent/tools/read-skill-tool.js +30 -0
- package/dist/shared/agent/tools/ta/index.js +1 -0
- package/dist/shared/agent/tools/ta/indicators.js +201 -0
- package/dist/shared/agent/types.js +1 -0
- package/dist/shared/agent/utils.js +43 -0
- package/dist/shared/config/agent.js +177 -0
- package/dist/shared/config/ai-providers.js +156 -0
- package/dist/shared/config/config.js +22 -0
- package/dist/shared/config/constant.js +8 -0
- package/dist/shared/config/env-loader.js +30 -0
- package/dist/shared/types.js +1 -0
- package/dist/start/AgentProcessManager.js +98 -0
- package/dist/start/Dashboard.js +92 -0
- package/dist/start/SelectAgentApp.js +81 -0
- package/dist/start/StartApp.js +189 -0
- package/dist/start/patch-headless.js +101 -0
- package/dist/start/patch-managed-mode.js +142 -0
- package/dist/start/start-command.js +24 -0
- package/dist/theme.js +54 -0
- package/package.json +68 -0
- package/templates/components/HoneycombBoot.tsx +343 -0
- package/templates/fetch-rules.ts +23 -0
- package/templates/skills/mindshare/SKILL.md +197 -0
- package/templates/skills/ta/SKILL.md +179 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { colors, animation } from '../theme.js';
|
|
3
|
+
const BOOT_TOTAL_FRAMES = 58;
|
|
4
|
+
const BOOT_FRAME_MS = 80;
|
|
5
|
+
const DURATION_MS = BOOT_TOTAL_FRAMES * BOOT_FRAME_MS;
|
|
6
|
+
const NUM_BEES = 4;
|
|
7
|
+
const NUM_STREAMS = 5;
|
|
8
|
+
const SCRAMBLE_CHARS = '\u2B21\u2B22\u25C6\u25C7\u2591\u2592!@#$%01';
|
|
9
|
+
const BOOT_MESSAGES = [
|
|
10
|
+
{ prefix: '\u2B21', text: 'Initializing {name} agent...', frame: 30 },
|
|
11
|
+
{ prefix: '\u25C6', text: 'Loading personality matrix...', frame: 36 },
|
|
12
|
+
{ prefix: '\u25C7', text: 'Connecting to the hive...', frame: 42 },
|
|
13
|
+
{ prefix: '\u2713', text: 'Neural link established', frame: 48 },
|
|
14
|
+
];
|
|
15
|
+
// ─── Private helpers ─────────────────────────────────
|
|
16
|
+
function isHexEdge(r, c) {
|
|
17
|
+
const rowInHex = ((r % animation.HEX_H) + animation.HEX_H) % animation.HEX_H;
|
|
18
|
+
const isOddHex = Math.floor(r / animation.HEX_H) % 2 === 1;
|
|
19
|
+
const colOffset = isOddHex ? animation.HEX_W / 2 : 0;
|
|
20
|
+
const colInHex = (((c - colOffset) % animation.HEX_W) + animation.HEX_W) % animation.HEX_W;
|
|
21
|
+
if (rowInHex === 0 || rowInHex === animation.HEX_H - 1) {
|
|
22
|
+
return colInHex >= 2 && colInHex <= 5;
|
|
23
|
+
}
|
|
24
|
+
if (rowInHex === 1 || rowInHex === 2) {
|
|
25
|
+
return colInHex === 1 || colInHex === 6;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// ─── Raw ANSI boot animation ────────────────────────
|
|
30
|
+
export function showHoneycombBoot(agentName) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const cols = process.stdout.columns || 60;
|
|
33
|
+
const gridRows = process.stdout.rows || 24;
|
|
34
|
+
let frame = 0;
|
|
35
|
+
// Init bees
|
|
36
|
+
const bees = [];
|
|
37
|
+
for (let i = 0; i < NUM_BEES; i++) {
|
|
38
|
+
bees.push({
|
|
39
|
+
r: Math.floor(Math.random() * gridRows),
|
|
40
|
+
c: Math.floor(Math.random() * cols),
|
|
41
|
+
vr: Math.random() > 0.5 ? 1 : -1,
|
|
42
|
+
vc: Math.random() > 0.5 ? 1 : -1,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// Init stream columns
|
|
46
|
+
const streamCols = [];
|
|
47
|
+
const spacing = Math.floor(cols / (NUM_STREAMS + 1));
|
|
48
|
+
for (let i = 1; i <= NUM_STREAMS; i++) {
|
|
49
|
+
streamCols.push(spacing * i);
|
|
50
|
+
}
|
|
51
|
+
let pulses = [];
|
|
52
|
+
// Text positioning
|
|
53
|
+
const centerR = Math.floor(gridRows / 2) - 2;
|
|
54
|
+
const centerC = Math.floor(cols / 2);
|
|
55
|
+
const nameText = `\u2B21 ${agentName} agent \u2B21`;
|
|
56
|
+
const nameStart = Math.max(0, centerC - Math.floor(nameText.length / 2));
|
|
57
|
+
const msgStartRow = centerR + 4;
|
|
58
|
+
// Quiet zone around text: no animation renders here
|
|
59
|
+
const PADDING_H = 3;
|
|
60
|
+
const PADDING_V = 1;
|
|
61
|
+
const longestMsg = BOOT_MESSAGES.reduce((max, m) => Math.max(max, m.prefix.length + 1 + m.text.replace('{name}', agentName).length), 0);
|
|
62
|
+
const msgLeftEdge = Math.floor((cols - longestMsg) / 2);
|
|
63
|
+
const msgRightEdge = msgLeftEdge + longestMsg;
|
|
64
|
+
const quietLeft = Math.min(nameStart, msgLeftEdge) - PADDING_H;
|
|
65
|
+
const quietRight = Math.max(nameStart + nameText.length, msgRightEdge) + PADDING_H;
|
|
66
|
+
const quietTop = (centerR - 1) - PADDING_V;
|
|
67
|
+
const quietBottom = msgStartRow + BOOT_MESSAGES.length + PADDING_V;
|
|
68
|
+
// Hide cursor
|
|
69
|
+
process.stdout.write('\x1b[?25l');
|
|
70
|
+
// Clear screen
|
|
71
|
+
process.stdout.write('\x1b[2J');
|
|
72
|
+
function renderFrame() {
|
|
73
|
+
// Move cursor to top-left
|
|
74
|
+
process.stdout.write('\x1b[H');
|
|
75
|
+
// Advance bees every other frame
|
|
76
|
+
if (frame > 0 && frame % 2 === 0) {
|
|
77
|
+
for (const bee of bees) {
|
|
78
|
+
bee.r += bee.vr;
|
|
79
|
+
bee.c += bee.vc;
|
|
80
|
+
if (bee.r <= 0 || bee.r >= gridRows - 1) {
|
|
81
|
+
bee.vr *= -1;
|
|
82
|
+
bee.r = Math.max(0, Math.min(gridRows - 1, bee.r));
|
|
83
|
+
}
|
|
84
|
+
if (bee.c <= 0 || bee.c >= cols - 1) {
|
|
85
|
+
bee.vc *= -1;
|
|
86
|
+
bee.c = Math.max(0, Math.min(cols - 1, bee.c));
|
|
87
|
+
}
|
|
88
|
+
if (Math.random() > 0.3) {
|
|
89
|
+
bee.vc = Math.random() > 0.5 ? 1 : -1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Spawn pulses
|
|
94
|
+
if (frame % 4 === 0) {
|
|
95
|
+
for (let i = 0; i < 3; i++) {
|
|
96
|
+
const pr = Math.floor(Math.random() * gridRows);
|
|
97
|
+
const pc = Math.floor(Math.random() * cols);
|
|
98
|
+
if (isHexEdge(pr, pc)) {
|
|
99
|
+
const pulseColors = [colors.green, colors.red, colors.honey];
|
|
100
|
+
const color = pulseColors[Math.floor(Math.random() * pulseColors.length)];
|
|
101
|
+
pulses.push({ r: pr, c: pc, ttl: 8, color });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
pulses = pulses.filter((p) => p.ttl > 0).map((p) => ({ ...p, ttl: p.ttl - 1 }));
|
|
105
|
+
}
|
|
106
|
+
// Build grid: char + color pairs
|
|
107
|
+
const charGrid = [];
|
|
108
|
+
const colorGrid = [];
|
|
109
|
+
for (let r = 0; r < gridRows; r++) {
|
|
110
|
+
const chars = [];
|
|
111
|
+
const clrs = [];
|
|
112
|
+
for (let c = 0; c < cols; c++) {
|
|
113
|
+
// Skip animation in quiet zone around text
|
|
114
|
+
const inQuietZone = r >= quietTop && r <= quietBottom && c >= quietLeft && c < quietRight;
|
|
115
|
+
if (inQuietZone) {
|
|
116
|
+
chars.push(' ');
|
|
117
|
+
clrs.push(colors.grayDim);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const hexEdge = isHexEdge(r, c);
|
|
121
|
+
// Scanning wave
|
|
122
|
+
const scanRow = frame % (gridRows + 6);
|
|
123
|
+
const dist = Math.abs(r - scanRow);
|
|
124
|
+
if (hexEdge && dist === 0) {
|
|
125
|
+
chars.push('\u2B22');
|
|
126
|
+
clrs.push(colors.honey);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (hexEdge && dist <= 1) {
|
|
130
|
+
chars.push('\u2B21');
|
|
131
|
+
clrs.push(colors.honey);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// Data streams
|
|
135
|
+
let isStream = false;
|
|
136
|
+
if (frame >= 8) {
|
|
137
|
+
for (const sc of streamCols) {
|
|
138
|
+
if (c === sc) {
|
|
139
|
+
const streamOffset = ((frame - 8) * 2 + sc) % (gridRows * 3);
|
|
140
|
+
const streamDist = (((r - streamOffset) % gridRows) + gridRows) % gridRows;
|
|
141
|
+
if (streamDist < 6) {
|
|
142
|
+
const charIdx = (frame + r) % animation.DATA_CHARS.length;
|
|
143
|
+
const streamChar = animation.DATA_CHARS[charIdx];
|
|
144
|
+
chars.push(streamChar);
|
|
145
|
+
if (streamDist === 0) {
|
|
146
|
+
clrs.push(colors.white);
|
|
147
|
+
}
|
|
148
|
+
else if (streamDist < 3) {
|
|
149
|
+
clrs.push(colors.green);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
clrs.push(colors.grayDim);
|
|
153
|
+
}
|
|
154
|
+
isStream = true;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (isStream)
|
|
161
|
+
continue;
|
|
162
|
+
// Default
|
|
163
|
+
if (hexEdge) {
|
|
164
|
+
chars.push('\u00B7');
|
|
165
|
+
clrs.push(colors.grayDim);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
chars.push(' ');
|
|
169
|
+
clrs.push(colors.grayDim);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
charGrid.push(chars);
|
|
173
|
+
colorGrid.push(clrs);
|
|
174
|
+
}
|
|
175
|
+
// Overlay pulses (skip quiet zone)
|
|
176
|
+
for (const pulse of pulses) {
|
|
177
|
+
if (pulse.r >= 0 && pulse.r < gridRows && pulse.c >= 0 && pulse.c < cols) {
|
|
178
|
+
const inQuietZone = pulse.r >= quietTop &&
|
|
179
|
+
pulse.r <= quietBottom &&
|
|
180
|
+
pulse.c >= quietLeft &&
|
|
181
|
+
pulse.c < quietRight;
|
|
182
|
+
if (inQuietZone)
|
|
183
|
+
continue;
|
|
184
|
+
const brightness = pulse.ttl / 8;
|
|
185
|
+
const cell = charGrid[pulse.r][pulse.c];
|
|
186
|
+
if (cell === '\u00B7' || cell === ' ') {
|
|
187
|
+
charGrid[pulse.r][pulse.c] = brightness > 0.5 ? '\u2B21' : '\u00B7';
|
|
188
|
+
colorGrid[pulse.r][pulse.c] = pulse.color;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Overlay bees (skip quiet zone)
|
|
193
|
+
for (const bee of bees) {
|
|
194
|
+
const br = Math.max(0, Math.min(gridRows - 1, Math.round(bee.r)));
|
|
195
|
+
const bc = Math.max(0, Math.min(cols - 1, Math.round(bee.c)));
|
|
196
|
+
const inQuietZone = br >= quietTop && br <= quietBottom && bc >= quietLeft && bc < quietRight;
|
|
197
|
+
if (!inQuietZone) {
|
|
198
|
+
charGrid[br][bc] = '\u25C6';
|
|
199
|
+
colorGrid[br][bc] = colors.honey;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Overlay agent name with scramble→reveal effect
|
|
203
|
+
if (frame >= 22) {
|
|
204
|
+
const scrambleProgress = Math.min(1, (frame - 22) / 8);
|
|
205
|
+
// Top/bottom border lines around name
|
|
206
|
+
for (let c = nameStart; c < nameStart + nameText.length && c < cols; c++) {
|
|
207
|
+
if (centerR - 1 >= 0) {
|
|
208
|
+
charGrid[centerR - 1][c] = '\u2500';
|
|
209
|
+
colorGrid[centerR - 1][c] = colors.honey;
|
|
210
|
+
}
|
|
211
|
+
if (centerR + 1 < gridRows) {
|
|
212
|
+
charGrid[centerR + 1][c] = '\u2500';
|
|
213
|
+
colorGrid[centerR + 1][c] = colors.honey;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Name text with scramble effect
|
|
217
|
+
for (let i = 0; i < nameText.length; i++) {
|
|
218
|
+
const c = nameStart + i;
|
|
219
|
+
if (c >= cols)
|
|
220
|
+
break;
|
|
221
|
+
const charThreshold = i / nameText.length;
|
|
222
|
+
if (charThreshold <= scrambleProgress) {
|
|
223
|
+
charGrid[centerR][c] = nameText[i];
|
|
224
|
+
colorGrid[centerR][c] = colors.honey;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const scrambleIdx = Math.floor(Math.random() * SCRAMBLE_CHARS.length);
|
|
228
|
+
charGrid[centerR][c] = SCRAMBLE_CHARS[scrambleIdx];
|
|
229
|
+
colorGrid[centerR][c] = colors.gray;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Overlay typewriter boot messages
|
|
234
|
+
for (let idx = 0; idx < BOOT_MESSAGES.length; idx++) {
|
|
235
|
+
const msg = BOOT_MESSAGES[idx];
|
|
236
|
+
if (frame < msg.frame)
|
|
237
|
+
continue;
|
|
238
|
+
const r = msgStartRow + idx;
|
|
239
|
+
if (r < 0 || r >= gridRows)
|
|
240
|
+
continue;
|
|
241
|
+
const fullText = `${msg.prefix} ${msg.text.replace('{name}', agentName)}`;
|
|
242
|
+
const msgCol = Math.floor((cols - fullText.length) / 2);
|
|
243
|
+
const visibleChars = Math.min(fullText.length, (frame - msg.frame) * 3);
|
|
244
|
+
const isCheckmark = msg.prefix === '\u2713';
|
|
245
|
+
const msgColor = isCheckmark ? colors.green : colors.honey;
|
|
246
|
+
for (let i = 0; i < visibleChars; i++) {
|
|
247
|
+
const c = msgCol + i;
|
|
248
|
+
if (c < 0 || c >= cols)
|
|
249
|
+
continue;
|
|
250
|
+
charGrid[r][c] = fullText[i];
|
|
251
|
+
colorGrid[r][c] = msgColor;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Render to stdout
|
|
255
|
+
let output = '';
|
|
256
|
+
for (let r = 0; r < gridRows; r++) {
|
|
257
|
+
let line = '';
|
|
258
|
+
let runColor = colorGrid[r][0];
|
|
259
|
+
let runChars = '';
|
|
260
|
+
for (let c = 0; c < cols; c++) {
|
|
261
|
+
const curColor = colorGrid[r][c];
|
|
262
|
+
const curChar = charGrid[r][c];
|
|
263
|
+
if (curColor === runColor) {
|
|
264
|
+
runChars += curChar;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
line += chalk.hex(runColor)(runChars);
|
|
268
|
+
runColor = curColor;
|
|
269
|
+
runChars = curChar;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (runChars.length > 0) {
|
|
273
|
+
line += chalk.hex(runColor)(runChars);
|
|
274
|
+
}
|
|
275
|
+
output += line;
|
|
276
|
+
if (r < gridRows - 1) {
|
|
277
|
+
output += '\n';
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
process.stdout.write(output);
|
|
281
|
+
frame++;
|
|
282
|
+
}
|
|
283
|
+
const timer = setInterval(renderFrame, BOOT_FRAME_MS);
|
|
284
|
+
setTimeout(() => {
|
|
285
|
+
clearInterval(timer);
|
|
286
|
+
// Clear screen, show cursor, move to top
|
|
287
|
+
process.stdout.write('\x1b[2J\x1b[H\x1b[?25h');
|
|
288
|
+
resolve();
|
|
289
|
+
}, DURATION_MS);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import { Text } from 'ink';
|
|
4
|
+
import { colors } from '../theme.js';
|
|
5
|
+
const SPINNER_FRAMES = ['\u25D0', '\u25D3', '\u25D1', '\u25D2'];
|
|
6
|
+
export function Spinner({ label }) {
|
|
7
|
+
const [frame, setFrame] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const timer = setInterval(() => {
|
|
10
|
+
setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
|
|
11
|
+
}, 120);
|
|
12
|
+
return () => {
|
|
13
|
+
clearInterval(timer);
|
|
14
|
+
};
|
|
15
|
+
}, []);
|
|
16
|
+
return (_jsxs(Text, { children: [_jsx(Text, { color: colors.honey, children: SPINNER_FRAMES[frame] }), _jsxs(Text, { color: colors.gray, children: [" ", label] })] }));
|
|
17
|
+
}
|
|
18
|
+
export function TypewriterText({ text, color, speed = 25, }) {
|
|
19
|
+
const [visible, setVisible] = useState(0);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (visible >= text.length)
|
|
22
|
+
return;
|
|
23
|
+
const timer = setTimeout(() => {
|
|
24
|
+
setVisible((prev) => Math.min(prev + 2, text.length));
|
|
25
|
+
}, speed);
|
|
26
|
+
return () => {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
};
|
|
29
|
+
}, [visible, text, speed]);
|
|
30
|
+
return _jsx(Text, { color: color, children: text.slice(0, visible) });
|
|
31
|
+
}
|
|
32
|
+
export function PollText({ text, color, animate, }) {
|
|
33
|
+
if (animate) {
|
|
34
|
+
return _jsx(TypewriterText, { text: text, color: color });
|
|
35
|
+
}
|
|
36
|
+
return _jsx(Text, { color: color, children: text });
|
|
37
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
async function loadMarkdownFile(filename) {
|
|
4
|
+
const filePath = path.join(process.cwd(), filename);
|
|
5
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
6
|
+
return content;
|
|
7
|
+
}
|
|
8
|
+
function extractField(content, pattern) {
|
|
9
|
+
const match = content.match(pattern);
|
|
10
|
+
if (match === null) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const value = match[1].trim();
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
const VALID_SENTIMENTS = [
|
|
17
|
+
'very-bullish',
|
|
18
|
+
'bullish',
|
|
19
|
+
'neutral',
|
|
20
|
+
'bearish',
|
|
21
|
+
'very-bearish',
|
|
22
|
+
];
|
|
23
|
+
const VALID_TIMEFRAMES = ['1h', '4h', '24h'];
|
|
24
|
+
function parseSentiment(raw) {
|
|
25
|
+
if (raw !== null && VALID_SENTIMENTS.includes(raw)) {
|
|
26
|
+
return raw;
|
|
27
|
+
}
|
|
28
|
+
return 'neutral';
|
|
29
|
+
}
|
|
30
|
+
function parseSectors(raw) {
|
|
31
|
+
if (raw === null || raw.trim() === '') {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const sectors = raw
|
|
35
|
+
.split(',')
|
|
36
|
+
.map((s) => s.trim())
|
|
37
|
+
.filter((s) => s.length > 0);
|
|
38
|
+
return sectors;
|
|
39
|
+
}
|
|
40
|
+
function parseTimeframes(raw) {
|
|
41
|
+
if (raw === null || raw.trim() === '') {
|
|
42
|
+
return ['1h', '4h', '24h'];
|
|
43
|
+
}
|
|
44
|
+
const parsed = raw
|
|
45
|
+
.split(',')
|
|
46
|
+
.map((t) => t.trim())
|
|
47
|
+
.filter((t) => VALID_TIMEFRAMES.includes(t));
|
|
48
|
+
if (parsed.length === 0) {
|
|
49
|
+
return ['1h', '4h', '24h'];
|
|
50
|
+
}
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
export async function loadAgentConfig() {
|
|
54
|
+
const soulContent = await loadMarkdownFile('SOUL.md');
|
|
55
|
+
const strategyContent = await loadMarkdownFile('STRATEGY.md');
|
|
56
|
+
const name = extractField(soulContent, /^#\s+Agent:\s+(.+)$/m);
|
|
57
|
+
if (name === null) {
|
|
58
|
+
throw new Error('Could not parse agent name from SOUL.md. Expected "# Agent: <name>" as the first heading.');
|
|
59
|
+
}
|
|
60
|
+
const avatarUrl = extractField(soulContent, /^## Avatar\s*\n+(https?:\/\/.+)$/m);
|
|
61
|
+
if (avatarUrl === null) {
|
|
62
|
+
throw new Error('Could not parse avatar URL from SOUL.md. Expected a valid URL under "## Avatar".');
|
|
63
|
+
}
|
|
64
|
+
const bioRaw = extractField(soulContent, /^## Bio\s*\n+(.+)$/m);
|
|
65
|
+
const bio = bioRaw ?? null;
|
|
66
|
+
const sentimentRaw = extractField(strategyContent, /^-\s+Bias:\s+(.+)$/m);
|
|
67
|
+
const sectorsRaw = extractField(strategyContent, /^-\s+Sectors:\s+(.+)$/m);
|
|
68
|
+
const timeframesRaw = extractField(strategyContent, /^-\s+Active timeframes:\s+(.+)$/m);
|
|
69
|
+
const agentProfile = {
|
|
70
|
+
sentiment: parseSentiment(sentimentRaw),
|
|
71
|
+
sectors: parseSectors(sectorsRaw),
|
|
72
|
+
timeframes: parseTimeframes(timeframesRaw),
|
|
73
|
+
};
|
|
74
|
+
return { name, bio, avatarUrl, soulContent, strategyContent, agentProfile };
|
|
75
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { tool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
export function replaceSection(fileContent, heading, newContent) {
|
|
6
|
+
const lines = fileContent.split('\n');
|
|
7
|
+
const headingLine = `## ${heading}`;
|
|
8
|
+
let startIdx = -1;
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
if (lines[i].trim() === headingLine) {
|
|
11
|
+
startIdx = i;
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (startIdx === -1) {
|
|
16
|
+
throw new Error(`Section "## ${heading}" not found in file.`);
|
|
17
|
+
}
|
|
18
|
+
let endIdx = lines.length;
|
|
19
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
20
|
+
const trimmed = lines[i].trim();
|
|
21
|
+
if (trimmed.startsWith('## ') || trimmed.startsWith('# ')) {
|
|
22
|
+
endIdx = i;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const before = lines.slice(0, startIdx + 1);
|
|
27
|
+
const after = lines.slice(endIdx);
|
|
28
|
+
const trimmedContent = newContent.trim();
|
|
29
|
+
const newSection = ['', ...trimmedContent.split('\n'), ''];
|
|
30
|
+
const result = [...before, ...newSection, ...after].join('\n');
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
export const editSectionTool = tool({
|
|
34
|
+
description: 'Edit a section of SOUL.md or STRATEGY.md. Only call AFTER user confirms.',
|
|
35
|
+
inputSchema: z.object({
|
|
36
|
+
file: z.enum(['SOUL.md', 'STRATEGY.md']),
|
|
37
|
+
section: z.string().describe('Exact ## heading name, e.g. "Personality", "Conviction Style"'),
|
|
38
|
+
content: z.string().describe('New content for the section (without the ## heading line)'),
|
|
39
|
+
}),
|
|
40
|
+
execute: async ({ file, section, content }) => {
|
|
41
|
+
const filePath = path.join(process.cwd(), file);
|
|
42
|
+
let fileContent;
|
|
43
|
+
try {
|
|
44
|
+
fileContent = await fs.readFile(filePath, 'utf-8');
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return `Error: ${file} not found in current directory.`;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const updated = replaceSection(fileContent, section, content);
|
|
51
|
+
await fs.writeFile(filePath, updated, 'utf-8');
|
|
52
|
+
return `Updated "${section}" section in ${file}.`;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
return `Error: ${message}`;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { tool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const RULES_URL = 'https://hive.z3n.dev/RULES.md';
|
|
4
|
+
export const fetchRulesTool = tool({
|
|
5
|
+
description: 'Fetch the rules of the Hive game. Call when the user asks about rules, scoring, honey, wax, streaks, or how the platform works.',
|
|
6
|
+
inputSchema: z.object({}),
|
|
7
|
+
execute: async () => {
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch(RULES_URL);
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
return `Error: failed to fetch rules (HTTP ${response.status}).`;
|
|
12
|
+
}
|
|
13
|
+
const rules = await response.text();
|
|
14
|
+
return rules;
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18
|
+
return `Error: could not reach Hive to fetch rules. ${message}`;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function formatTime(date) {
|
|
2
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
3
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
4
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
5
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
6
|
+
}
|
|
7
|
+
export function convictionColor(conviction) {
|
|
8
|
+
if (conviction > 0)
|
|
9
|
+
return 'green';
|
|
10
|
+
if (conviction < 0)
|
|
11
|
+
return 'red';
|
|
12
|
+
return 'gray';
|
|
13
|
+
}
|
|
14
|
+
export function stripCodeFences(text) {
|
|
15
|
+
const trimmed = text.trim();
|
|
16
|
+
const fencePattern = /^```(?:markdown|md)?\s*\n([\s\S]*?)\n```$/;
|
|
17
|
+
const match = trimmed.match(fencePattern);
|
|
18
|
+
if (match) {
|
|
19
|
+
return match[1].trim();
|
|
20
|
+
}
|
|
21
|
+
return trimmed;
|
|
22
|
+
}
|