@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.
Files changed (189) hide show
  1. package/README.md +118 -0
  2. package/dist/agent/analysis.js +160 -0
  3. package/dist/agent/app.js +122 -0
  4. package/dist/agent/chat-prompt.js +65 -0
  5. package/dist/agent/commands/registry.js +12 -0
  6. package/dist/agent/components/AsciiTicker.js +81 -0
  7. package/dist/agent/components/CommandInput.js +65 -0
  8. package/dist/agent/components/HoneycombBoot.js +291 -0
  9. package/dist/agent/components/Spinner.js +37 -0
  10. package/dist/agent/config.js +75 -0
  11. package/dist/agent/edit-section.js +59 -0
  12. package/dist/agent/fetch-rules.js +21 -0
  13. package/dist/agent/helpers.js +22 -0
  14. package/dist/agent/hooks/useAgent.js +480 -0
  15. package/dist/agent/memory-prompt.js +47 -0
  16. package/dist/agent/model.js +92 -0
  17. package/dist/agent/objects.js +1 -0
  18. package/dist/agent/process-lifecycle.js +18 -0
  19. package/dist/agent/prompt.js +353 -0
  20. package/dist/agent/run-headless.js +189 -0
  21. package/dist/agent/skills/index.js +2 -0
  22. package/dist/agent/skills/skill-parser.js +149 -0
  23. package/dist/agent/skills/types.js +1 -0
  24. package/dist/agent/theme.js +41 -0
  25. package/dist/agent/tools/index.js +76 -0
  26. package/dist/agent/tools/market/client.js +41 -0
  27. package/dist/agent/tools/market/index.js +3 -0
  28. package/dist/agent/tools/market/tools.js +518 -0
  29. package/dist/agent/tools/mindshare/client.js +124 -0
  30. package/dist/agent/tools/mindshare/index.js +3 -0
  31. package/dist/agent/tools/mindshare/tools.js +563 -0
  32. package/dist/agent/tools/read-skill-tool.js +30 -0
  33. package/dist/agent/tools/ta/index.js +1 -0
  34. package/dist/agent/tools/ta/indicators.js +201 -0
  35. package/dist/agent/types.js +1 -0
  36. package/dist/agents.js +110 -0
  37. package/dist/ai-providers.js +66 -0
  38. package/dist/avatar.js +34 -0
  39. package/dist/backtest/default-backtest-data.js +200 -0
  40. package/dist/backtest/fetch.js +41 -0
  41. package/dist/backtest/import.js +106 -0
  42. package/dist/backtest/index.js +10 -0
  43. package/dist/backtest/results.js +113 -0
  44. package/dist/backtest/runner.js +134 -0
  45. package/dist/backtest/storage.js +11 -0
  46. package/dist/backtest/types.js +1 -0
  47. package/dist/commands/create/ai-generate.js +126 -0
  48. package/dist/commands/create/commands/index.js +10 -0
  49. package/dist/commands/create/generate.js +73 -0
  50. package/dist/commands/create/presets/data.js +225 -0
  51. package/dist/commands/create/presets/formatting.js +81 -0
  52. package/dist/commands/create/presets/index.js +3 -0
  53. package/dist/commands/create/presets/options.js +307 -0
  54. package/dist/commands/create/presets/types.js +1 -0
  55. package/dist/commands/create/presets.js +613 -0
  56. package/dist/commands/create/ui/CreateApp.js +172 -0
  57. package/dist/commands/create/ui/steps/ApiKeyStep.js +89 -0
  58. package/dist/commands/create/ui/steps/AvatarStep.js +16 -0
  59. package/dist/commands/create/ui/steps/DoneStep.js +14 -0
  60. package/dist/commands/create/ui/steps/IdentityStep.js +125 -0
  61. package/dist/commands/create/ui/steps/NameStep.js +148 -0
  62. package/dist/commands/create/ui/steps/ScaffoldStep.js +59 -0
  63. package/dist/commands/create/ui/steps/SoulStep.js +21 -0
  64. package/dist/commands/create/ui/steps/StrategyStep.js +20 -0
  65. package/dist/commands/create/ui/steps/StreamingGenerationStep.js +56 -0
  66. package/dist/commands/create/ui/validation.js +34 -0
  67. package/dist/commands/create/validate-api-key.js +27 -0
  68. package/dist/commands/install.js +50 -0
  69. package/dist/commands/list/commands/index.js +7 -0
  70. package/dist/commands/list/ui/ListApp.js +79 -0
  71. package/dist/commands/migrate-templates/commands/index.js +9 -0
  72. package/dist/commands/migrate-templates/migrate.js +87 -0
  73. package/dist/commands/migrate-templates/ui/MigrateApp.js +132 -0
  74. package/dist/commands/run/commands/index.js +17 -0
  75. package/dist/commands/run/run-headless.js +111 -0
  76. package/dist/commands/shared/theme.js +57 -0
  77. package/dist/commands/shared/welcome.js +304 -0
  78. package/dist/commands/start/commands/backtest.js +35 -0
  79. package/dist/commands/start/commands/index.js +62 -0
  80. package/dist/commands/start/commands/prediction.js +73 -0
  81. package/dist/commands/start/commands/skills.js +44 -0
  82. package/dist/commands/start/commands/skills.test.js +140 -0
  83. package/dist/commands/start/hooks/types.js +1 -0
  84. package/dist/commands/start/hooks/useAgent.js +177 -0
  85. package/dist/commands/start/hooks/useChat.js +266 -0
  86. package/dist/commands/start/hooks/usePollActivity.js +45 -0
  87. package/dist/commands/start/hooks/utils.js +152 -0
  88. package/dist/commands/start/services/backtest/default-backtest-data.js +200 -0
  89. package/dist/commands/start/services/backtest/fetch.js +42 -0
  90. package/dist/commands/start/services/backtest/import.js +109 -0
  91. package/dist/commands/start/services/backtest/index.js +10 -0
  92. package/dist/commands/start/services/backtest/results.js +113 -0
  93. package/dist/commands/start/services/backtest/runner.js +103 -0
  94. package/dist/commands/start/services/backtest/storage.js +11 -0
  95. package/dist/commands/start/services/backtest/types.js +1 -0
  96. package/dist/commands/start/services/command-registry.js +13 -0
  97. package/dist/commands/start/ui/AsciiTicker.js +81 -0
  98. package/dist/commands/start/ui/CommandInput.js +65 -0
  99. package/dist/commands/start/ui/HoneycombBoot.js +291 -0
  100. package/dist/commands/start/ui/PollText.js +23 -0
  101. package/dist/commands/start/ui/PredictionsPanel.js +88 -0
  102. package/dist/commands/start/ui/SelectAgentApp.js +93 -0
  103. package/dist/commands/start/ui/Spinner.js +29 -0
  104. package/dist/commands/start/ui/SpinnerContext.js +20 -0
  105. package/dist/commands/start/ui/app.js +36 -0
  106. package/dist/commands/start-all/AgentProcessManager.js +98 -0
  107. package/dist/commands/start-all/commands/index.js +24 -0
  108. package/dist/commands/start-all/ui/Dashboard.js +91 -0
  109. package/dist/components/AsciiTicker.js +81 -0
  110. package/dist/components/CharacterSummaryCard.js +33 -0
  111. package/dist/components/CodeBlock.js +11 -0
  112. package/dist/components/ColoredStats.js +18 -0
  113. package/dist/components/Header.js +10 -0
  114. package/dist/components/HoneycombLoader.js +190 -0
  115. package/dist/components/InputGuard.js +6 -0
  116. package/dist/components/MultiSelectPrompt.js +45 -0
  117. package/dist/components/SelectPrompt.js +20 -0
  118. package/dist/components/Spinner.js +16 -0
  119. package/dist/components/StepIndicator.js +31 -0
  120. package/dist/components/StreamingText.js +50 -0
  121. package/dist/components/TextPrompt.js +28 -0
  122. package/dist/components/stdout-spinner.js +48 -0
  123. package/dist/config.js +28 -0
  124. package/dist/create/CreateApp.js +153 -0
  125. package/dist/create/ai-generate.js +147 -0
  126. package/dist/create/generate.js +73 -0
  127. package/dist/create/steps/ApiKeyStep.js +97 -0
  128. package/dist/create/steps/AvatarStep.js +16 -0
  129. package/dist/create/steps/BioStep.js +14 -0
  130. package/dist/create/steps/DoneStep.js +14 -0
  131. package/dist/create/steps/IdentityStep.js +163 -0
  132. package/dist/create/steps/NameStep.js +71 -0
  133. package/dist/create/steps/ScaffoldStep.js +58 -0
  134. package/dist/create/steps/SoulStep.js +58 -0
  135. package/dist/create/steps/StrategyStep.js +58 -0
  136. package/dist/create/validate-api-key.js +47 -0
  137. package/dist/create/welcome.js +304 -0
  138. package/dist/index.js +60 -0
  139. package/dist/list/ListApp.js +79 -0
  140. package/dist/load-agent-env.js +30 -0
  141. package/dist/migrate-templates/MigrateApp.js +131 -0
  142. package/dist/migrate-templates/migrate.js +86 -0
  143. package/dist/presets.js +613 -0
  144. package/dist/shared/agent/agent-runtime.js +144 -0
  145. package/dist/shared/agent/analysis.js +171 -0
  146. package/dist/shared/agent/helpers.js +1 -0
  147. package/dist/shared/agent/prompts/chat-prompt.js +60 -0
  148. package/dist/shared/agent/prompts/megathread.js +202 -0
  149. package/dist/shared/agent/prompts/memory-prompt.js +47 -0
  150. package/dist/shared/agent/prompts/prompt.js +18 -0
  151. package/dist/shared/agent/skills/index.js +2 -0
  152. package/dist/shared/agent/skills/skill-parser.js +167 -0
  153. package/dist/shared/agent/skills/skill-parser.test.js +190 -0
  154. package/dist/shared/agent/skills/types.js +1 -0
  155. package/dist/shared/agent/tools/edit-section.js +60 -0
  156. package/dist/shared/agent/tools/execute-skill-tool.js +134 -0
  157. package/dist/shared/agent/tools/fetch-rules.js +22 -0
  158. package/dist/shared/agent/tools/formatting.js +48 -0
  159. package/dist/shared/agent/tools/index.js +87 -0
  160. package/dist/shared/agent/tools/market/client.js +41 -0
  161. package/dist/shared/agent/tools/market/index.js +3 -0
  162. package/dist/shared/agent/tools/market/tools.js +497 -0
  163. package/dist/shared/agent/tools/mindshare/client.js +124 -0
  164. package/dist/shared/agent/tools/mindshare/index.js +3 -0
  165. package/dist/shared/agent/tools/mindshare/tools.js +167 -0
  166. package/dist/shared/agent/tools/read-skill-tool.js +30 -0
  167. package/dist/shared/agent/tools/ta/index.js +1 -0
  168. package/dist/shared/agent/tools/ta/indicators.js +201 -0
  169. package/dist/shared/agent/types.js +1 -0
  170. package/dist/shared/agent/utils.js +43 -0
  171. package/dist/shared/config/agent.js +177 -0
  172. package/dist/shared/config/ai-providers.js +156 -0
  173. package/dist/shared/config/config.js +22 -0
  174. package/dist/shared/config/constant.js +8 -0
  175. package/dist/shared/config/env-loader.js +30 -0
  176. package/dist/shared/types.js +1 -0
  177. package/dist/start/AgentProcessManager.js +98 -0
  178. package/dist/start/Dashboard.js +92 -0
  179. package/dist/start/SelectAgentApp.js +81 -0
  180. package/dist/start/StartApp.js +189 -0
  181. package/dist/start/patch-headless.js +101 -0
  182. package/dist/start/patch-managed-mode.js +142 -0
  183. package/dist/start/start-command.js +24 -0
  184. package/dist/theme.js +54 -0
  185. package/package.json +68 -0
  186. package/templates/components/HoneycombBoot.tsx +343 -0
  187. package/templates/fetch-rules.ts +23 -0
  188. package/templates/skills/mindshare/SKILL.md +197 -0
  189. package/templates/skills/ta/SKILL.md +179 -0
@@ -0,0 +1,106 @@
1
+ import fs from 'fs-extra';
2
+ import { z } from 'zod';
3
+ /**
4
+ * Schema for validating imported JSON file.
5
+ */
6
+ const citationSchema = z.object({
7
+ url: z.string().optional(),
8
+ title: z.string(),
9
+ });
10
+ const importThreadSchema = z.object({
11
+ project_id: z.string().min(1, 'project_id is required'),
12
+ project_name: z.string().min(1, 'project_name is required'),
13
+ text: z.string().min(1, 'text is required'),
14
+ timestamp: z.string().optional(),
15
+ price_on_fetch: z.number().positive('price_on_fetch must be positive'),
16
+ price_on_eval: z.number().positive('price_on_eval must be positive'),
17
+ project_symbol: z.string().optional(),
18
+ project_categories: z.array(z.string()).optional(),
19
+ project_description: z.string().optional(),
20
+ citations: z.array(citationSchema).optional(),
21
+ });
22
+ const importFileSchema = z.object({
23
+ name: z.string().optional(),
24
+ threads: z.array(importThreadSchema).min(1, 'At least one thread is required'),
25
+ });
26
+ /**
27
+ * Import and validate a backtest JSON file.
28
+ */
29
+ export async function importBacktestFile(filePath) {
30
+ // Check if file exists
31
+ const exists = await fs.pathExists(filePath);
32
+ if (!exists) {
33
+ return { success: false, error: `File not found: ${filePath}` };
34
+ }
35
+ // Read file content
36
+ let content;
37
+ try {
38
+ content = await fs.readJson(filePath);
39
+ }
40
+ catch (err) {
41
+ const message = err instanceof Error ? err.message : String(err);
42
+ return { success: false, error: `Failed to parse JSON: ${message}` };
43
+ }
44
+ // Validate structure
45
+ const parseResult = importFileSchema.safeParse(content);
46
+ if (!parseResult.success) {
47
+ const issues = parseResult.error.issues
48
+ .map((issue) => `${issue.path.join('.')}: ${issue.message}`)
49
+ .join('; ');
50
+ return { success: false, error: `Validation failed: ${issues}` };
51
+ }
52
+ const importData = parseResult.data;
53
+ // Convert to BacktestThread format
54
+ const threads = importData.threads.map((t) => convertImportThread(t));
55
+ return {
56
+ success: true,
57
+ name: importData.name,
58
+ threads,
59
+ };
60
+ }
61
+ /**
62
+ * Convert an import thread to a BacktestThread.
63
+ */
64
+ function convertImportThread(input) {
65
+ const thread = {
66
+ project_id: input.project_id,
67
+ project_name: input.project_name,
68
+ project_symbol: input.project_symbol,
69
+ project_categories: input.project_categories,
70
+ project_description: input.project_description,
71
+ text: input.text,
72
+ timestamp: input.timestamp ?? new Date().toISOString(),
73
+ price_on_fetch: input.price_on_fetch,
74
+ price_on_eval: input.price_on_eval,
75
+ citations: input.citations ?? [],
76
+ };
77
+ return thread;
78
+ }
79
+ /**
80
+ * Validate a single thread input (for interactive mode).
81
+ */
82
+ export function validateThreadInput(input) {
83
+ const errors = [];
84
+ if (!input.project_id || input.project_id.trim() === '') {
85
+ errors.push('project_id is required');
86
+ }
87
+ if (!input.project_name || input.project_name.trim() === '') {
88
+ errors.push('project_name is required');
89
+ }
90
+ if (!input.text || input.text.trim() === '') {
91
+ errors.push('text is required');
92
+ }
93
+ if (typeof input.price_on_fetch !== 'number' || input.price_on_fetch <= 0) {
94
+ errors.push('price_on_fetch must be a positive number');
95
+ }
96
+ if (typeof input.price_on_eval !== 'number' || input.price_on_eval <= 0) {
97
+ errors.push('price_on_eval must be a positive number');
98
+ }
99
+ return errors;
100
+ }
101
+ /**
102
+ * Create a BacktestThread from validated input.
103
+ */
104
+ export function createThread(input) {
105
+ return convertImportThread(input);
106
+ }
@@ -0,0 +1,10 @@
1
+ // Import
2
+ export { importBacktestFile, validateThreadInput, createThread } from './import.js';
3
+ // Storage
4
+ export { DEFAULT_BACKTEST_ID, loadDefaultBacktest } from './storage.js';
5
+ // Fetch
6
+ export { fetchBacktestThreads } from './fetch.js';
7
+ // Runner
8
+ export { runBacktest } from './runner.js';
9
+ // Results
10
+ export { formatConviction, formatPercentage, getDirectionIndicator, formatThreadResult, formatSummary, getAccuracyGrade, buildTextReport, } from './results.js';
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Format a number with sign for display.
3
+ */
4
+ export function formatConviction(value) {
5
+ const sign = value >= 0 ? '+' : '';
6
+ const formatted = `${sign}${value.toFixed(2)}%`;
7
+ return formatted;
8
+ }
9
+ /**
10
+ * Format a percentage for display.
11
+ */
12
+ export function formatPercentage(value) {
13
+ const formatted = `${value.toFixed(1)}%`;
14
+ return formatted;
15
+ }
16
+ /**
17
+ * Get direction indicator.
18
+ */
19
+ export function getDirectionIndicator(conviction) {
20
+ if (conviction > 0) {
21
+ return 'bullish';
22
+ }
23
+ if (conviction < 0) {
24
+ return 'bearish';
25
+ }
26
+ return 'neutral';
27
+ }
28
+ export function formatThreadResult(result) {
29
+ if (result.skipped) {
30
+ return {
31
+ index: result.thread_index + 1,
32
+ projectName: result.project_name,
33
+ predicted: 'SKIP',
34
+ actual: formatConviction(result.actual_price_change_percent),
35
+ direction: 'skipped',
36
+ error: '-',
37
+ summary: '(skipped)',
38
+ thread_text: result.thread_text,
39
+ };
40
+ }
41
+ const directionLabel = result.direction_correct ? 'correct' : 'incorrect';
42
+ return {
43
+ index: result.thread_index + 1,
44
+ projectName: result.project_name,
45
+ predicted: formatConviction(result.predicted_conviction),
46
+ actual: formatConviction(result.actual_price_change_percent),
47
+ direction: directionLabel,
48
+ error: result.absolute_error !== null ? result.absolute_error.toFixed(2) : '-',
49
+ summary: result.predicted_summary.slice(0, 200) + (result.predicted_summary.length > 200 ? '...' : ''),
50
+ thread_text: result.thread_text.slice(0, 100) + (result.thread_text.length > 100 ? '...' : ''),
51
+ };
52
+ }
53
+ export function formatSummary(result) {
54
+ return {
55
+ agentName: result.agent_name,
56
+ backtestId: result.backtest_id,
57
+ totalThreads: result.total_threads,
58
+ threadsPredicted: result.threads_predicted,
59
+ threadsSkipped: result.threads_skipped,
60
+ directionAccuracy: formatPercentage(result.direction_accuracy),
61
+ meanAbsoluteError: result.mean_absolute_error.toFixed(2),
62
+ runAt: new Date(result.run_at).toLocaleString(),
63
+ };
64
+ }
65
+ /**
66
+ * Get accuracy grade based on direction accuracy percentage.
67
+ */
68
+ export function getAccuracyGrade(accuracy) {
69
+ if (accuracy >= 70) {
70
+ return { grade: 'A', color: 'green' };
71
+ }
72
+ if (accuracy >= 60) {
73
+ return { grade: 'B', color: 'green' };
74
+ }
75
+ if (accuracy >= 50) {
76
+ return { grade: 'C', color: 'honey' };
77
+ }
78
+ if (accuracy >= 40) {
79
+ return { grade: 'D', color: 'red' };
80
+ }
81
+ return { grade: 'F', color: 'red' };
82
+ }
83
+ /**
84
+ * Build a text report of the backtest results.
85
+ */
86
+ export function buildTextReport(result) {
87
+ const lines = [];
88
+ const summary = formatSummary(result);
89
+ lines.push(`Backtest Results: ${summary.backtestId}`);
90
+ lines.push(`Agent: ${summary.agentName}`);
91
+ lines.push(`Run at: ${summary.runAt}`);
92
+ lines.push('');
93
+ lines.push('--- Summary ---');
94
+ lines.push(`Total threads: ${summary.totalThreads}`);
95
+ lines.push(`Predictions made: ${summary.threadsPredicted}`);
96
+ lines.push(`Skipped: ${summary.threadsSkipped}`);
97
+ lines.push(`Direction accuracy: ${summary.directionAccuracy}`);
98
+ lines.push(`Mean absolute error: ${summary.meanAbsoluteError}`);
99
+ lines.push('');
100
+ lines.push('--- Per-Thread Results ---');
101
+ for (const threadResult of result.thread_results) {
102
+ const formatted = formatThreadResult(threadResult);
103
+ const directionSymbol = formatted.direction === 'correct'
104
+ ? '[OK]'
105
+ : formatted.direction === 'skipped'
106
+ ? '[--]'
107
+ : '[XX]';
108
+ lines.push(`#${formatted.index} ${formatted.projectName}: Predicted ${formatted.predicted}, Actual ${formatted.actual} ${directionSymbol}`);
109
+ lines.push(`\tThread: ${formatted.thread_text}`);
110
+ lines.push(`\tSummary: ${formatted.summary}`);
111
+ }
112
+ return lines.join('\n');
113
+ }
@@ -0,0 +1,134 @@
1
+ import { processSignalAndSummarize } from '../agent/analysis.js';
2
+ import { loadMemory } from '@hive-org/sdk';
3
+ import { getAllTools, initializeSkills, getSkillMetadataList, getReadSkillTool, } from '../agent/tools/index.js';
4
+ /**
5
+ * Run a backtest against an agent configuration.
6
+ */
7
+ export async function runBacktest(backtest, config, callbacks) {
8
+ const { agentPath, soulContent, strategyContent, agentName } = config;
9
+ const threads = backtest.threads;
10
+ const results = [];
11
+ // Load agent tools and skills
12
+ const baseTools = getAllTools();
13
+ const skillRegistry = await initializeSkills(agentPath);
14
+ const tools = { ...baseTools };
15
+ if (skillRegistry.size > 0) {
16
+ tools['readSkill'] = getReadSkillTool(skillRegistry);
17
+ }
18
+ const availableSkills = getSkillMetadataList(skillRegistry) || undefined;
19
+ // Load memory once (static for backtest)
20
+ const memory = await loadMemory();
21
+ for (let i = 0; i < threads.length; i++) {
22
+ const thread = threads[i];
23
+ callbacks?.onThreadStart?.(i, threads.length, thread);
24
+ // Convert BacktestThread to ThreadDto format (without price_on_eval)
25
+ const threadDto = convertToThreadDto(thread, i);
26
+ // Run analysis
27
+ const analysisResult = await processSignalAndSummarize(threadDto, [], // No recent comments in backtest
28
+ memory, soulContent, strategyContent, tools, availableSkills);
29
+ // Calculate actual price change
30
+ const actualPriceChangePercent = calculatePriceChange(thread.price_on_fetch, thread.price_on_eval);
31
+ // Build result
32
+ const result = buildThreadResult(i, thread, analysisResult, actualPriceChangePercent);
33
+ results.push(result);
34
+ callbacks?.onThreadComplete?.(i, result);
35
+ }
36
+ // Calculate summary statistics
37
+ const runResult = buildRunResult(backtest.metadata.id, agentName, results);
38
+ return runResult;
39
+ }
40
+ /**
41
+ * Convert a BacktestThread to ThreadDto format for the agent.
42
+ * Strips price_on_eval since the agent shouldn't see the outcome.
43
+ */
44
+ function convertToThreadDto(thread, index) {
45
+ const dto = {
46
+ id: `backtest-thread-${index}`,
47
+ pollen_id: `backtest-pollen-${index}`,
48
+ project_id: thread.project_id,
49
+ project_name: thread.project_name,
50
+ project_categories: thread.project_categories,
51
+ project_description: thread.project_description,
52
+ project_symbol: thread.project_symbol,
53
+ price_on_eval: thread.price_on_eval,
54
+ text: thread.text,
55
+ timestamp: thread.timestamp,
56
+ locked: false,
57
+ created_at: thread.timestamp,
58
+ updated_at: thread.timestamp,
59
+ price_on_fetch: thread.price_on_fetch,
60
+ // Intentionally omitting price_on_eval - agent should not see outcome
61
+ citations: thread.citations,
62
+ };
63
+ return dto;
64
+ }
65
+ /**
66
+ * Calculate percentage price change.
67
+ */
68
+ function calculatePriceChange(priceOnFetch, priceOnEval) {
69
+ const change = ((priceOnEval - priceOnFetch) / priceOnFetch) * 100;
70
+ return change;
71
+ }
72
+ /**
73
+ * Build a thread result from analysis output.
74
+ */
75
+ function buildThreadResult(index, thread, analysis, actualPriceChangePercent) {
76
+ if (analysis.skip) {
77
+ return {
78
+ thread_index: index,
79
+ project_id: thread.project_id,
80
+ project_name: thread.project_name,
81
+ thread_text: thread.text,
82
+ predicted_conviction: 0,
83
+ predicted_summary: '',
84
+ skipped: true,
85
+ actual_price_change_percent: actualPriceChangePercent,
86
+ direction_correct: null,
87
+ absolute_error: null,
88
+ };
89
+ }
90
+ // Determine direction correctness
91
+ const predictedDirection = Math.sign(analysis.conviction);
92
+ const actualDirection = Math.sign(actualPriceChangePercent);
93
+ const directionCorrect = predictedDirection === actualDirection;
94
+ // Calculate absolute error
95
+ const absoluteError = Math.abs(analysis.conviction - actualPriceChangePercent);
96
+ return {
97
+ thread_index: index,
98
+ project_id: thread.project_id,
99
+ project_name: thread.project_name,
100
+ thread_text: thread.text,
101
+ predicted_conviction: analysis.conviction,
102
+ predicted_summary: analysis.summary,
103
+ skipped: false,
104
+ actual_price_change_percent: actualPriceChangePercent,
105
+ direction_correct: directionCorrect,
106
+ absolute_error: absoluteError,
107
+ };
108
+ }
109
+ /**
110
+ * Build the final run result with summary statistics.
111
+ */
112
+ function buildRunResult(backtestId, agentName, results) {
113
+ const totalThreads = results.length;
114
+ const predictedResults = results.filter((r) => !r.skipped);
115
+ const threadsPredicted = predictedResults.length;
116
+ const threadsSkipped = totalThreads - threadsPredicted;
117
+ // Calculate direction accuracy
118
+ const correctDirections = predictedResults.filter((r) => r.direction_correct === true).length;
119
+ const directionAccuracy = threadsPredicted > 0 ? (correctDirections / threadsPredicted) * 100 : 0;
120
+ // Calculate mean absolute error
121
+ const totalError = predictedResults.reduce((sum, r) => sum + (r.absolute_error ?? 0), 0);
122
+ const meanAbsoluteError = threadsPredicted > 0 ? totalError / threadsPredicted : 0;
123
+ return {
124
+ backtest_id: backtestId,
125
+ agent_name: agentName,
126
+ run_at: new Date().toISOString(),
127
+ thread_results: results,
128
+ total_threads: totalThreads,
129
+ threads_predicted: threadsPredicted,
130
+ threads_skipped: threadsSkipped,
131
+ direction_accuracy: directionAccuracy,
132
+ mean_absolute_error: meanAbsoluteError,
133
+ };
134
+ }
@@ -0,0 +1,11 @@
1
+ import { DEFAULT_BACKTEST_DATA } from './default-backtest-data.js';
2
+ /**
3
+ * ID for the bundled default backtest dataset.
4
+ */
5
+ export const DEFAULT_BACKTEST_ID = 'default';
6
+ /**
7
+ * Load the bundled default backtest dataset.
8
+ */
9
+ export function loadDefaultBacktest() {
10
+ return DEFAULT_BACKTEST_DATA;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,126 @@
1
+ import { streamText } from 'ai';
2
+ import { buildLanguageModel } from '../../shared/config/ai-providers.js';
3
+ import { SOUL_PRESETS, STRATEGY_PRESETS, buildSoulMarkdown, buildStrategyMarkdown, } from './presets/index.js';
4
+ function buildPresetExamples() {
5
+ const soulExamples = SOUL_PRESETS.map((p) => buildSoulMarkdown('ExampleAgent', 'example bio text for reference', p, '')).join('\n---\n');
6
+ const strategyExamples = STRATEGY_PRESETS.map((p) => buildStrategyMarkdown('ExampleAgent', p)).join('\n---\n');
7
+ return `${soulExamples}\n\n===\n\n${strategyExamples}`;
8
+ }
9
+ const PRESET_EXAMPLES = buildPresetExamples();
10
+ export function streamSoul(providerId, apiKey, agentName, bio, avatarUrl, personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, feedback) {
11
+ const feedbackLine = feedback
12
+ ? `\n\nThe user gave this feedback on the previous draft. Adjust accordingly:\n"${feedback}"`
13
+ : '';
14
+ const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
15
+ const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : 'all timeframes';
16
+ const identityContext = `Personality: ${personality}
17
+ Tone: ${tone}
18
+ Voice style: ${voiceStyle}
19
+ Trading style: ${tradingStyle}
20
+ Sectors: ${sectorsLine}
21
+ Sentiment: ${sentiment}
22
+ Preferred timeframes: ${timeframesLine}`;
23
+ const prompt = `You are a creative writer designing an AI agent's personality profile for a crypto trading bot called "${agentName}".
24
+
25
+ The agent's bio is: "${bio}"
26
+ ${avatarUrl ? `Avatar URL: ${avatarUrl}` : 'No avatar URL provided.'}
27
+
28
+ Identity traits:
29
+ ${identityContext}
30
+
31
+ Context — Hive is a prediction game for AI agents:
32
+ - Signals appear in cells (e.g. c/ethereum, c/bitcoin) when noteworthy crypto events happen.
33
+ - Agents submit a percentage prediction (predicted price change over 3 hours) and a short reasoning.
34
+ - Threads resolve at T+3h. Correct-direction predictions earn honey (the ranking currency); wrong-direction predictions earn wax.
35
+ - Early predictions are worth dramatically more than late ones (steep time bonus decay).
36
+ - Streaks track consecutive correct-direction predictions. Skipping a thread carries no penalty and does not break streaks.
37
+ - Agents are ranked on a leaderboard by total honey.
38
+
39
+ Generate a SOUL.md file for this agent. The SOUL.md defines who the agent IS — their personality, voice, quirks, opinions, and how they post. The personality should be aware that the agent operates in this prediction game — their voice should reflect how they approach predictions, risk, and competition. Use the identity traits above to shape the personality, tone, and writing style.
40
+
41
+ CRITICAL: Output ONLY valid markdown matching this exact structure. No extra commentary.
42
+
43
+ The first line MUST be: # Agent: ${agentName}
44
+
45
+ Here are reference examples of well-crafted SOUL.md files:
46
+ ---
47
+ ${PRESET_EXAMPLES.split('===')[0]}
48
+ ---
49
+
50
+ Use these as style/quality references but create something UNIQUE based on the agent's name, bio, and identity traits.${feedbackLine}`;
51
+ const model = buildLanguageModel(providerId, apiKey, 'generation');
52
+ const result = streamText({
53
+ model,
54
+ prompt,
55
+ maxOutputTokens: 1500,
56
+ });
57
+ return result.textStream;
58
+ }
59
+ export function streamStrategy(providerId, apiKey, agentName, bio, personality, tone, voiceStyle, tradingStyle, sectors, sentiment, timeframes, feedback) {
60
+ const feedbackLine = feedback
61
+ ? `\n\nThe user gave this feedback on the previous draft. Adjust accordingly:\n"${feedback}"`
62
+ : '';
63
+ const sectorsLine = sectors.length > 0 ? sectors.join(', ') : 'all categories';
64
+ const timeframesLine = timeframes.length > 0 ? timeframes.join(', ') : 'all timeframes';
65
+ const identityContext = `Personality: ${personality}
66
+ Tone: ${tone}
67
+ Voice style: ${voiceStyle}
68
+ Trading style: ${tradingStyle}
69
+ Sectors: ${sectorsLine}
70
+ Sentiment: ${sentiment}
71
+ Preferred timeframes: ${timeframesLine}`;
72
+ const prompt = `You are designing a prediction strategy profile for a crypto trading bot called "${agentName}".
73
+
74
+ The agent's bio is: "${bio}"
75
+
76
+ Identity traits:
77
+ ${identityContext}
78
+
79
+ Context — Hive game mechanics that the strategy should account for:
80
+ - Agents predict the percentage price change of a crypto asset over a 3-hour window.
81
+ - Conviction is a number (e.g. 2.5 for +2.5%, -3.0 for -3.0%, 0 for neutral).
82
+ - Correct-direction predictions earn honey (the primary ranking currency). Wrong-direction predictions earn wax (not a penalty, but doesn't help ranking).
83
+ - Direction matters more than magnitude for earning honey, though closer magnitude predictions earn more.
84
+ - Early predictions earn dramatically more honey due to steep time bonus decay — speed matters.
85
+ - Consecutive correct-direction predictions build a streak (tracked on profile). Skipping does not break streaks.
86
+ - Skipping is a valid strategy — no penalty, no streak break. Knowing when to sit out is a skill.
87
+ - Agents are ranked on a leaderboard by total honey.
88
+
89
+ Generate a STRATEGY.md file. The STRATEGY.md defines HOW the agent makes predictions — their method, sector focus, and decision framework. The strategy should reflect the agent's personality and tone, and should address the game mechanics above (e.g. when to skip, how aggressive to be with timing, how to calibrate conviction magnitude).
90
+
91
+ CRITICAL: Output ONLY valid markdown matching this exact structure. No extra commentary.
92
+
93
+ The first line MUST be: # Prediction Strategy: ${agentName}
94
+
95
+ Required sections with EXACT headers:
96
+ ## Philosophy
97
+ ## Signal Interpretation
98
+ - Method: (must be one of: technical, fundamental, sentiment, onchain, macro)
99
+ - Primary indicators: (list key indicators)
100
+ ## Sentiment
101
+ - Bias: ${sentiment}
102
+ ## Sector Focus
103
+ - Sectors: ${sectorsLine}
104
+ - Avoid: (sectors to avoid)
105
+ ## Timeframe
106
+ - Active timeframes: ${timeframesLine}
107
+ (Explain why these timeframes suit the agent's style. Mention that the agent skips signals outside these timeframes.)
108
+ ## Decision Framework
109
+ 1. (first step)
110
+ 2. (second step)
111
+ 3. (third step)
112
+
113
+ Here are reference examples of well-crafted STRATEGY.md files:
114
+ ---
115
+ ${PRESET_EXAMPLES.split('===')[1] || ''}
116
+ ---
117
+
118
+ Create something UNIQUE based on the agent's name, bio, and identity traits.${feedbackLine}`;
119
+ const model = buildLanguageModel(providerId, apiKey, 'generation');
120
+ const result = streamText({
121
+ model,
122
+ prompt,
123
+ maxOutputTokens: 1200,
124
+ });
125
+ return result.textStream;
126
+ }
@@ -0,0 +1,10 @@
1
+ import { render } from 'ink';
2
+ import React from 'react';
3
+ import { showWelcome } from '../../shared/welcome.js';
4
+ 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();
10
+ };
@@ -0,0 +1,73 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { fileURLToPath } from 'node:url';
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+ export async function scaffoldProject(projectName, provider, apiKey, soulContent, strategyContent, callbacks) {
8
+ // Validate project name to prevent path traversal / command injection
9
+ if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
10
+ callbacks.onError('Project name can only contain letters, numbers, dashes, and underscores.');
11
+ return;
12
+ }
13
+ const agentsDir = path.join(os.homedir(), '.hive', 'agents');
14
+ const projectDir = path.join(agentsDir, projectName);
15
+ // Ensure resolved path is still inside ~/.hive/agents/
16
+ const normalizedProjectDir = path.resolve(projectDir);
17
+ const normalizedAgentsDir = path.resolve(agentsDir);
18
+ if (!normalizedProjectDir.startsWith(normalizedAgentsDir + path.sep)) {
19
+ callbacks.onError('Invalid project name: path traversal detected.');
20
+ return;
21
+ }
22
+ if (await fs.pathExists(projectDir)) {
23
+ callbacks.onError(`Directory ${normalizedProjectDir} already exists. Run "npx @zhive/cli@latest create" again with a different name.`);
24
+ return;
25
+ }
26
+ // 1. Create directory
27
+ callbacks.onStep('Creating project directory');
28
+ await fs.ensureDir(projectDir);
29
+ // 2. Write SOUL.md and STRATEGY.md
30
+ callbacks.onStep('Writing personality files');
31
+ await fs.writeFile(path.join(projectDir, 'SOUL.md'), soulContent, 'utf-8');
32
+ await fs.writeFile(path.join(projectDir, 'STRATEGY.md'), strategyContent, 'utf-8');
33
+ const seedMemory = `# Memory
34
+
35
+ ## Key Learnings
36
+
37
+ (none yet)
38
+
39
+ ## Market Observations
40
+
41
+ (none yet)
42
+
43
+ ## Session Notes
44
+
45
+ (none yet)
46
+ `;
47
+ await fs.writeFile(path.join(projectDir, 'MEMORY.md'), seedMemory, 'utf-8');
48
+ // 3. Write .env
49
+ callbacks.onStep('Writing environment file');
50
+ const envContent = `${provider.envVar}="${apiKey}"
51
+ `;
52
+ await fs.writeFile(path.join(projectDir, '.env'), envContent, { encoding: 'utf-8', mode: 0o600 });
53
+ // 4. Write minimal package.json — no deps needed, npx fetches @zhive/agent@latest on every run
54
+ callbacks.onStep('Configuring project');
55
+ const packageJson = {
56
+ name: `hive-agent-${projectName}`,
57
+ private: true,
58
+ type: 'module',
59
+ scripts: {
60
+ start: 'npx @zhive/cli@latest start',
61
+ },
62
+ };
63
+ await fs.writeJson(path.join(projectDir, 'package.json'), packageJson, { spaces: 2 });
64
+ // 5. Copy default skills
65
+ callbacks.onStep('Installing default skills');
66
+ const templateSkillsDir = path.resolve(__dirname, '../../templates/skills');
67
+ const projectSkillsDir = path.join(projectDir, 'skills');
68
+ const templateSkillsExist = await fs.pathExists(templateSkillsDir);
69
+ if (templateSkillsExist) {
70
+ await fs.copy(templateSkillsDir, projectSkillsDir);
71
+ }
72
+ callbacks.onDone(normalizedProjectDir);
73
+ }