@zhive/cli 0.6.6 → 0.6.7

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 (57) hide show
  1. package/dist/commands/agent/commands/profile.js +0 -6
  2. package/dist/commands/agent/commands/profile.test.js +2 -23
  3. package/dist/commands/create/presets/index.js +1 -1
  4. package/dist/commands/create/presets/options.js +18 -15
  5. package/dist/commands/create/ui/steps/IdentityStep.js +3 -2
  6. package/dist/commands/doctor/commands/index.js +5 -11
  7. package/dist/commands/megathread/commands/create-comment.js +2 -7
  8. package/dist/commands/megathread/commands/create-comment.test.js +3 -30
  9. package/dist/commands/megathread/commands/create-comments.js +2 -7
  10. package/dist/commands/megathread/commands/list.js +5 -10
  11. package/dist/commands/megathread/commands/list.test.js +3 -21
  12. package/dist/commands/migrate-templates/ui/MigrateApp.js +1 -1
  13. package/dist/commands/start/commands/prediction.js +1 -1
  14. package/dist/commands/start/commands/skills.test.js +1 -2
  15. package/dist/components/MultiSelectPrompt.js +3 -3
  16. package/dist/shared/config/agent.js +4 -0
  17. package/dist/shared/config/agent.test.js +0 -5
  18. package/package.json +1 -1
  19. package/dist/CLAUDE.md +0 -7
  20. package/dist/backtest/CLAUDE.md +0 -7
  21. package/dist/cli.js +0 -20
  22. package/dist/commands/create/presets.js +0 -613
  23. package/dist/commands/start/ui/AsciiTicker.js +0 -81
  24. package/dist/services/agent/analysis.js +0 -160
  25. package/dist/services/agent/config.js +0 -75
  26. package/dist/services/agent/env.js +0 -30
  27. package/dist/services/agent/helpers/model.js +0 -92
  28. package/dist/services/agent/helpers.js +0 -22
  29. package/dist/services/agent/prompts/chat-prompt.js +0 -65
  30. package/dist/services/agent/prompts/memory-prompt.js +0 -45
  31. package/dist/services/agent/prompts/prompt.js +0 -379
  32. package/dist/services/agent/skills/index.js +0 -2
  33. package/dist/services/agent/skills/skill-parser.js +0 -149
  34. package/dist/services/agent/skills/types.js +0 -1
  35. package/dist/services/agent/tools/edit-section.js +0 -59
  36. package/dist/services/agent/tools/fetch-rules.js +0 -21
  37. package/dist/services/agent/tools/index.js +0 -76
  38. package/dist/services/agent/tools/market/client.js +0 -41
  39. package/dist/services/agent/tools/market/index.js +0 -3
  40. package/dist/services/agent/tools/market/tools.js +0 -518
  41. package/dist/services/agent/tools/mindshare/client.js +0 -124
  42. package/dist/services/agent/tools/mindshare/index.js +0 -3
  43. package/dist/services/agent/tools/mindshare/tools.js +0 -563
  44. package/dist/services/agent/tools/read-skill-tool.js +0 -30
  45. package/dist/services/agent/tools/ta/index.js +0 -1
  46. package/dist/services/agent/tools/ta/indicators.js +0 -201
  47. package/dist/services/agent/types.js +0 -1
  48. package/dist/services/ai-providers.js +0 -66
  49. package/dist/services/config/agent.js +0 -110
  50. package/dist/services/config/config.js +0 -22
  51. package/dist/services/config/constant.js +0 -8
  52. package/dist/shared/agent/agent-runtime.js +0 -144
  53. package/dist/shared/agent/config.js +0 -75
  54. package/dist/shared/agent/env.js +0 -30
  55. package/dist/shared/agent/helpers/model.js +0 -92
  56. package/dist/shared/agent/types.js +0 -1
  57. package/dist/shared/ai-providers.js +0 -66
@@ -1,30 +0,0 @@
1
- import { tool } from 'ai';
2
- import { z } from 'zod';
3
- /**
4
- * Create a tool that allows the agent to read skill knowledge.
5
- * Returns the full SKILL.md body content when called.
6
- */
7
- export function createReadSkillTool(skillRegistry) {
8
- const readSkillTool = tool({
9
- description: 'Read a skill to get detailed knowledge and instructions. Call this when you need specialized expertise for analysis. Use the skill ID from the available skills list.',
10
- inputSchema: z.object({
11
- skillId: z
12
- .string()
13
- .describe('The skill ID to read (from the available skills list in your prompt)'),
14
- }),
15
- execute: async ({ skillId }) => {
16
- const skill = skillRegistry.get(skillId);
17
- if (skill === undefined) {
18
- const availableIds = Array.from(skillRegistry.keys());
19
- if (availableIds.length === 0) {
20
- return `Skill "${skillId}" not found. No skills are currently available.`;
21
- }
22
- const idList = availableIds.join(', ');
23
- return `Skill "${skillId}" not found. Available skills: ${idList}`;
24
- }
25
- const output = `# ${skill.metadata.name}\n\n${skill.body}`;
26
- return output;
27
- },
28
- });
29
- return readSkillTool;
30
- }
@@ -1 +0,0 @@
1
- export { computeSMA, computeEMA, computeRSI, computeMACD, computeBollingerBands } from './indicators.js';
@@ -1,201 +0,0 @@
1
- /**
2
- * Extract close prices from OHLC data.
3
- */
4
- function extractClosePrices(data) {
5
- const result = [];
6
- for (const point of data) {
7
- const timestamp = point[0];
8
- const close = point[4];
9
- result.push({ timestamp, close });
10
- }
11
- return result;
12
- }
13
- /**
14
- * Compute Simple Moving Average (SMA).
15
- * SMA = sum of close prices over period / period
16
- */
17
- export function computeSMA(data, period) {
18
- const prices = extractClosePrices(data);
19
- const result = [];
20
- if (prices.length < period) {
21
- return result;
22
- }
23
- for (let i = period - 1; i < prices.length; i++) {
24
- let sum = 0;
25
- for (let j = i - period + 1; j <= i; j++) {
26
- sum += prices[j].close;
27
- }
28
- const sma = sum / period;
29
- result.push({
30
- timestamp: prices[i].timestamp,
31
- value: sma,
32
- });
33
- }
34
- return result;
35
- }
36
- /**
37
- * Compute Exponential Moving Average (EMA).
38
- * EMA = (Close - Previous EMA) * multiplier + Previous EMA
39
- * multiplier = 2 / (period + 1)
40
- */
41
- export function computeEMA(data, period) {
42
- const prices = extractClosePrices(data);
43
- const result = [];
44
- if (prices.length < period) {
45
- return result;
46
- }
47
- const multiplier = 2 / (period + 1);
48
- let initialSum = 0;
49
- for (let i = 0; i < period; i++) {
50
- initialSum += prices[i].close;
51
- }
52
- let ema = initialSum / period;
53
- result.push({
54
- timestamp: prices[period - 1].timestamp,
55
- value: ema,
56
- });
57
- for (let i = period; i < prices.length; i++) {
58
- ema = (prices[i].close - ema) * multiplier + ema;
59
- result.push({
60
- timestamp: prices[i].timestamp,
61
- value: ema,
62
- });
63
- }
64
- return result;
65
- }
66
- /**
67
- * Compute Relative Strength Index (RSI).
68
- * RSI = 100 - (100 / (1 + RS))
69
- * RS = Average Gain / Average Loss over period
70
- */
71
- export function computeRSI(data, period) {
72
- const prices = extractClosePrices(data);
73
- const result = [];
74
- if (prices.length < period + 1) {
75
- return result;
76
- }
77
- const gains = [];
78
- const losses = [];
79
- for (let i = 1; i < prices.length; i++) {
80
- const change = prices[i].close - prices[i - 1].close;
81
- if (change > 0) {
82
- gains.push(change);
83
- losses.push(0);
84
- }
85
- else {
86
- gains.push(0);
87
- losses.push(Math.abs(change));
88
- }
89
- }
90
- let avgGain = 0;
91
- let avgLoss = 0;
92
- for (let i = 0; i < period; i++) {
93
- avgGain += gains[i];
94
- avgLoss += losses[i];
95
- }
96
- avgGain /= period;
97
- avgLoss /= period;
98
- const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;
99
- const rsi = 100 - 100 / (1 + rs);
100
- result.push({
101
- timestamp: prices[period].timestamp,
102
- value: rsi,
103
- });
104
- for (let i = period; i < gains.length; i++) {
105
- avgGain = (avgGain * (period - 1) + gains[i]) / period;
106
- avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
107
- const currentRs = avgLoss === 0 ? 100 : avgGain / avgLoss;
108
- const currentRsi = 100 - 100 / (1 + currentRs);
109
- result.push({
110
- timestamp: prices[i + 1].timestamp,
111
- value: currentRsi,
112
- });
113
- }
114
- return result;
115
- }
116
- /**
117
- * Compute MACD (Moving Average Convergence Divergence).
118
- * MACD Line = Fast EMA - Slow EMA
119
- * Signal Line = EMA of MACD Line
120
- * Histogram = MACD Line - Signal Line
121
- */
122
- export function computeMACD(data, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
123
- const fastEma = computeEMA(data, fastPeriod);
124
- const slowEma = computeEMA(data, slowPeriod);
125
- const result = [];
126
- if (fastEma.length === 0 || slowEma.length === 0) {
127
- return result;
128
- }
129
- const fastMap = new Map();
130
- for (const point of fastEma) {
131
- fastMap.set(point.timestamp, point.value);
132
- }
133
- const macdLine = [];
134
- for (const slow of slowEma) {
135
- const fast = fastMap.get(slow.timestamp);
136
- if (fast !== undefined) {
137
- macdLine.push({
138
- timestamp: slow.timestamp,
139
- value: fast - slow.value,
140
- });
141
- }
142
- }
143
- if (macdLine.length < signalPeriod) {
144
- return result;
145
- }
146
- const signalMultiplier = 2 / (signalPeriod + 1);
147
- let signalSum = 0;
148
- for (let i = 0; i < signalPeriod; i++) {
149
- signalSum += macdLine[i].value;
150
- }
151
- let signal = signalSum / signalPeriod;
152
- result.push({
153
- timestamp: macdLine[signalPeriod - 1].timestamp,
154
- macd: macdLine[signalPeriod - 1].value,
155
- signal,
156
- histogram: macdLine[signalPeriod - 1].value - signal,
157
- });
158
- for (let i = signalPeriod; i < macdLine.length; i++) {
159
- signal = (macdLine[i].value - signal) * signalMultiplier + signal;
160
- result.push({
161
- timestamp: macdLine[i].timestamp,
162
- macd: macdLine[i].value,
163
- signal,
164
- histogram: macdLine[i].value - signal,
165
- });
166
- }
167
- return result;
168
- }
169
- /**
170
- * Compute Bollinger Bands.
171
- * Middle Band = SMA
172
- * Upper Band = SMA + (stdDev * standard deviation)
173
- * Lower Band = SMA - (stdDev * standard deviation)
174
- */
175
- export function computeBollingerBands(data, period = 20, stdDevMultiplier = 2) {
176
- const prices = extractClosePrices(data);
177
- const result = [];
178
- if (prices.length < period) {
179
- return result;
180
- }
181
- for (let i = period - 1; i < prices.length; i++) {
182
- let sum = 0;
183
- for (let j = i - period + 1; j <= i; j++) {
184
- sum += prices[j].close;
185
- }
186
- const sma = sum / period;
187
- let squaredDiffSum = 0;
188
- for (let j = i - period + 1; j <= i; j++) {
189
- const diff = prices[j].close - sma;
190
- squaredDiffSum += diff * diff;
191
- }
192
- const stdDev = Math.sqrt(squaredDiffSum / period);
193
- result.push({
194
- timestamp: prices[i].timestamp,
195
- upper: sma + stdDevMultiplier * stdDev,
196
- middle: sma,
197
- lower: sma - stdDevMultiplier * stdDev,
198
- });
199
- }
200
- return result;
201
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,66 +0,0 @@
1
- export const AI_PROVIDERS = [
2
- {
3
- id: 'openai',
4
- label: 'OpenAI',
5
- package: '@ai-sdk/openai',
6
- envVar: 'OPENAI_API_KEY',
7
- models: { validation: 'gpt-4o-mini', generation: 'gpt-5-mini', runtime: 'gpt-5-mini' },
8
- },
9
- {
10
- id: 'anthropic',
11
- label: 'Anthropic',
12
- package: '@ai-sdk/anthropic',
13
- envVar: 'ANTHROPIC_API_KEY',
14
- models: {
15
- validation: 'claude-haiku-4-5-20251001',
16
- generation: 'claude-haiku-4-5',
17
- runtime: 'claude-haiku-4-5',
18
- },
19
- },
20
- {
21
- id: 'google',
22
- label: 'Google',
23
- package: '@ai-sdk/google',
24
- envVar: 'GOOGLE_GENERATIVE_AI_API_KEY',
25
- models: {
26
- validation: 'gemini-2.0-flash',
27
- generation: 'gemini-3-flash-preview',
28
- runtime: 'gemini-3-flash-preview',
29
- },
30
- },
31
- {
32
- id: 'xai',
33
- label: 'xAI',
34
- package: '@ai-sdk/xai',
35
- envVar: 'XAI_API_KEY',
36
- models: {
37
- validation: 'grok-2',
38
- generation: 'grok-4-1-fast-reasoning',
39
- runtime: 'grok-4-1-fast-reasoning',
40
- },
41
- },
42
- {
43
- id: 'openrouter',
44
- label: 'OpenRouter',
45
- package: '@openrouter/ai-sdk-provider',
46
- envVar: 'OPENROUTER_API_KEY',
47
- models: {
48
- validation: 'openai/gpt-4o-mini',
49
- generation: 'openai/gpt-5.1-mini',
50
- runtime: 'openai/gpt-5.1-mini',
51
- },
52
- },
53
- ];
54
- /**
55
- * All env-var names used by AI providers.
56
- * Used to clear shell-inherited keys before loading an agent's .env,
57
- * so only the agent's chosen provider is active.
58
- */
59
- export const AI_PROVIDER_ENV_VARS = AI_PROVIDERS.map((p) => p.envVar);
60
- export function getProvider(id) {
61
- const provider = AI_PROVIDERS.find((p) => p.id === id);
62
- if (!provider) {
63
- throw new Error(`Unknown AI provider: ${id}`);
64
- }
65
- return provider;
66
- }
@@ -1,110 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import os from 'os';
4
- import axios from 'axios';
5
- import { AI_PROVIDERS } from '../ai-providers.js';
6
- import { HIVE_API_URL } from './constant.js';
7
- function extractField(content, pattern) {
8
- const match = content.match(pattern);
9
- if (match === null) {
10
- return null;
11
- }
12
- const value = match[1].trim();
13
- return value;
14
- }
15
- export async function scanAgents() {
16
- const agentsDir = path.join(os.homedir(), '.hive', 'agents');
17
- const exists = await fs.pathExists(agentsDir);
18
- if (!exists) {
19
- return [];
20
- }
21
- const entries = await fs.readdir(agentsDir, { withFileTypes: true });
22
- const agents = [];
23
- for (const entry of entries) {
24
- if (!entry.isDirectory()) {
25
- continue;
26
- }
27
- const soulPath = path.join(agentsDir, entry.name, 'SOUL.md');
28
- const soulExists = await fs.pathExists(soulPath);
29
- if (!soulExists) {
30
- continue;
31
- }
32
- const agentDir = path.join(agentsDir, entry.name);
33
- const provider = await detectProvider(agentDir);
34
- const stat = await fs.stat(soulPath);
35
- const soulContent = await fs.readFile(soulPath, 'utf-8');
36
- const parsedName = extractField(soulContent, /^#\s+Agent:\s+(.+)$/m);
37
- const avatarUrl = extractField(soulContent, /^## Avatar\s*\n+(https?:\/\/.+)$/m);
38
- const bio = extractField(soulContent, /^## Bio\s*\n+(.+)$/m);
39
- agents.push({
40
- name: parsedName ?? entry.name,
41
- dir: agentDir,
42
- provider,
43
- created: stat.birthtime,
44
- avatar_url: avatarUrl ?? undefined,
45
- bio: bio ?? undefined,
46
- });
47
- }
48
- return agents;
49
- }
50
- export function sortByHoney(rows) {
51
- const sorted = [...rows].sort((a, b) => (b.stats?.honey ?? 0) - (a.stats?.honey ?? 0));
52
- return sorted;
53
- }
54
- export function sortAgentsByHoney(agents, statsMap) {
55
- const sorted = [...agents].sort((a, b) => {
56
- const honeyA = statsMap.get(a.name)?.honey ?? 0;
57
- const honeyB = statsMap.get(b.name)?.honey ?? 0;
58
- return honeyB - honeyA;
59
- });
60
- return sorted;
61
- }
62
- export async function fetchBulkStats(names) {
63
- const statsMap = new Map();
64
- if (names.length === 0) {
65
- return statsMap;
66
- }
67
- try {
68
- const response = await axios.post(`${HIVE_API_URL}/agent/by-names`, { names });
69
- for (const agent of response.data) {
70
- statsMap.set(agent.name, {
71
- honey: agent.honey ?? 0,
72
- wax: agent.wax ?? 0,
73
- win_rate: agent.win_rate ?? 0,
74
- confidence: agent.confidence ?? 0,
75
- total_comments: agent.total_comments ?? 0,
76
- });
77
- }
78
- }
79
- catch {
80
- // API unreachable — return empty map, CLI will show dashes
81
- }
82
- return statsMap;
83
- }
84
- async function detectProvider(agentDir) {
85
- // Try old-style detection: check package.json dependencies
86
- const pkgPath = path.join(agentDir, 'package.json');
87
- const pkgExists = await fs.pathExists(pkgPath);
88
- if (pkgExists) {
89
- const pkg = await fs.readJson(pkgPath);
90
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
91
- for (const provider of AI_PROVIDERS) {
92
- if (deps[provider.package]) {
93
- return provider.label;
94
- }
95
- }
96
- }
97
- // New-style detection: check .env for provider API keys
98
- const envPath = path.join(agentDir, '.env');
99
- const envExists = await fs.pathExists(envPath);
100
- if (envExists) {
101
- const envContent = await fs.readFile(envPath, 'utf-8');
102
- for (const provider of AI_PROVIDERS) {
103
- const pattern = new RegExp(`^${provider.envVar}=.+`, 'm');
104
- if (pattern.test(envContent)) {
105
- return provider.label;
106
- }
107
- }
108
- }
109
- return 'unknown';
110
- }
@@ -1,22 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { getHiveDir } from './constant.js';
4
- export async function readConfig() {
5
- try {
6
- const configPath = path.join(getHiveDir(), 'config.json');
7
- const config = (await fs.readJson(configPath));
8
- if (!config.providerId || !config.apiKey) {
9
- return null;
10
- }
11
- return config;
12
- }
13
- catch {
14
- return null;
15
- }
16
- }
17
- export async function writeConfig(config) {
18
- const hiveDir = getHiveDir();
19
- await fs.ensureDir(hiveDir);
20
- const configPath = path.join(hiveDir, 'config.json');
21
- await fs.writeJson(configPath, config, { spaces: 2, mode: 0o600 });
22
- }
@@ -1,8 +0,0 @@
1
- import path from 'path';
2
- import os from 'os';
3
- export const HIVE_API_URL = 'https://api.zhive.ai';
4
- export const HIVE_FRONTEND_URL = 'https://www.zhive.ai';
5
- export function getHiveDir() {
6
- const hiveDir = path.join(os.homedir(), '.hive');
7
- return hiveDir;
8
- }
@@ -1,144 +0,0 @@
1
- import { loadMemory } from '@zhive/sdk';
2
- import { loadAgentConfig } from '../config/agent.js';
3
- import { processMegathreadRound, screenMegathreadRound, } from './analysis.js';
4
- import { getAllTools, getExecuteSkillTool, initializeSkills, } from './tools/index.js';
5
- import { getMarketClient } from './tools/market/index.js';
6
- import { extractErrorMessage } from './utils.js';
7
- import { getModel } from '../config/ai-providers.js';
8
- export async function initializeAgentRuntime(agentDir) {
9
- const config = await loadAgentConfig(agentDir);
10
- const memory = await loadMemory(agentDir);
11
- const model = await getModel();
12
- const skillRegistry = await initializeSkills(agentDir);
13
- const tools = getAllTools();
14
- const executeSkillTool = getExecuteSkillTool(skillRegistry, model);
15
- const allTools = { ...tools, executeSkillTool };
16
- const runtime = { config, memory, tools: allTools, skills: skillRegistry, model };
17
- return runtime;
18
- }
19
- export const fetchPrice = async (projectId, timestamp) => {
20
- const client = getMarketClient();
21
- const response = await client.getPrice(projectId, timestamp);
22
- return response.price ?? undefined;
23
- };
24
- export async function fetchRoundPrices(projectId, roundTimestamp, currentTime) {
25
- let priceAtStart;
26
- let currentPrice;
27
- try {
28
- const client = getMarketClient();
29
- [priceAtStart, currentPrice] = await Promise.all([
30
- fetchPrice(projectId, roundTimestamp),
31
- fetchPrice(projectId, currentTime ?? new Date().toISOString()),
32
- ]);
33
- }
34
- catch {
35
- // Price fetch failed — both stay undefined
36
- }
37
- return {
38
- priceAtStart,
39
- currentPrice,
40
- };
41
- }
42
- const calculateTimeframe = (round) => {
43
- const hours = Math.round(round.durationMs / 3_600_000);
44
- const timeframe = hours >= 1 ? `${hours}h` : `${Math.round(round.durationMs / 60_000)}m`;
45
- return timeframe;
46
- };
47
- async function run({ round, runtime, reporter, recentComments, }) {
48
- const timeframe = calculateTimeframe(round);
49
- reporter.onRoundStart(round, timeframe);
50
- // ── Fetch prices ──────────────────────────────
51
- const roundStartTimestamp = round.roundId.split('@Z')[0];
52
- const { priceAtStart, currentPrice } = await fetchRoundPrices(round.projectId, roundStartTimestamp);
53
- if (priceAtStart !== undefined) {
54
- reporter.onPriceInfo(priceAtStart, currentPrice);
55
- }
56
- // ── Quick screen (cheap engage check) ───────
57
- const screenResult = await screenMegathreadRound(round.projectId, runtime.config.strategyContent);
58
- if (!screenResult.engage) {
59
- reporter.onScreenResult?.(round, screenResult);
60
- return { skip: true, usage: screenResult.usage, screenResult };
61
- }
62
- reporter.onResearching(round.projectId);
63
- // ── Run analysis ──────────────────────────────
64
- const result = await processMegathreadRound({
65
- projectId: round.projectId,
66
- durationMs: round.durationMs,
67
- recentComments,
68
- agentRuntime: runtime,
69
- priceAtStart,
70
- currentPrice,
71
- });
72
- reporter.onToolsUsed(result.usage.toolNames, result.usage.toolCalls);
73
- if (result.skip) {
74
- reporter.onSkipped(round, result.usage);
75
- }
76
- return result;
77
- }
78
- export function createMegathreadRoundBatchHandler(getAgent, runtime, reporter) {
79
- const handler = async (rounds) => {
80
- const agent = getAgent();
81
- const promises = [];
82
- // report item in order that it is polled to prevent out-of-order write to stdout
83
- for (const round of rounds) {
84
- promises.push(run({ round, runtime, reporter, recentComments: agent.recentComments }));
85
- }
86
- const results = await Promise.allSettled(promises);
87
- for (let i = 0; i < results.length; i++) {
88
- const round = rounds[i];
89
- const result = results[i];
90
- if (result.status === 'rejected') {
91
- const raw = extractErrorMessage(result.reason);
92
- const message = raw.length > 120 ? raw.slice(0, 120) + '\u2026' : raw;
93
- reporter.onError(round, message);
94
- continue;
95
- }
96
- const data = result.value;
97
- if (data.skip) {
98
- continue;
99
- }
100
- // TODO: we can optimized this by create method to commit this in batch in hive sdk.
101
- // postMegathreadComment cannot be run concurrently so we need to call it one by one.
102
- await agent.postMegathreadComment(round.roundId, {
103
- text: data.summary,
104
- conviction: data.conviction,
105
- tokenId: round.projectId,
106
- roundDuration: round.durationMs,
107
- });
108
- const timeframe = calculateTimeframe(round);
109
- reporter.onPosted(round, data.conviction, data.summary, timeframe, data.usage);
110
- }
111
- };
112
- return handler;
113
- }
114
- export function createMegathreadRoundHandler(getAgent, runtime, reporter) {
115
- const handler = async (round) => {
116
- const agent = getAgent();
117
- try {
118
- const result = await run({
119
- round,
120
- reporter,
121
- recentComments: agent.recentComments,
122
- runtime,
123
- });
124
- if (result.skip) {
125
- return;
126
- }
127
- // ── Post comment ──────────────────────────────
128
- await agent.postMegathreadComment(round.roundId, {
129
- text: result.summary,
130
- conviction: result.conviction,
131
- tokenId: round.projectId,
132
- roundDuration: round.durationMs,
133
- });
134
- const timeframe = calculateTimeframe(round);
135
- reporter.onPosted(round, result.conviction, result.summary, timeframe, result.usage);
136
- }
137
- catch (err) {
138
- const raw = extractErrorMessage(err);
139
- const message = raw.length > 120 ? raw.slice(0, 120) + '\u2026' : raw;
140
- reporter.onError(round, message);
141
- }
142
- };
143
- return handler;
144
- }
@@ -1,75 +0,0 @@
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
- }