Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@hive-org/cli

Package Overview
Dependencies
Maintainers
2
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hive-org/cli - npm Package Compare versions

Comparing version
0.1.8
to
0.1.9
+30
dist/load-agent-env.js
import { readFileSync } from 'fs';
import { AI_PROVIDER_ENV_VARS } from './ai-providers.js';
let _agentProviderKeys = new Set();
/**
* Provider env-var names declared in the agent's .env file.
* Used by getModel() to prioritize the agent's chosen provider
* over keys inherited from the shell.
*/
export function getAgentProviderKeys() {
return _agentProviderKeys;
}
/**
* Load the agent's .env with provider-key priority.
*
* 1. Parse .env to discover which provider keys the agent declared.
* 2. Load .env with override so the agent's values win for the same key.
* 3. getModel() uses getAgentProviderKeys() to check those providers first,
* falling back to shell-inherited keys if the agent has none.
*/
export async function loadAgentEnv() {
try {
const content = readFileSync('.env', 'utf-8');
_agentProviderKeys = new Set(AI_PROVIDER_ENV_VARS.filter((key) => new RegExp(`^${key}=`, 'm').test(content)));
}
catch {
_agentProviderKeys = new Set();
}
const { config } = await import('dotenv');
config({ override: true });
}
+20
-10
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Box, Text } from 'ink';
import { Box, Static, Text } from 'ink';
import { useAgent } from './hooks/useAgent.js';

@@ -11,8 +11,18 @@ import { colors, symbols, border } from './theme.js';

export function App() {
const { connected, agentName, pollActivity, chatActivity, input, chatStreaming, chatBuffer, predictionCount, termWidth, setInput, handleChatSubmit, } = useAgent();
const { connected, agentName, modelInfo, pollActivity, chatActivity, input, chatStreaming, chatBuffer, predictionCount, termWidth, setInput, handleChatSubmit, } = useAgent();
// When stdin is not a TTY (piped by hive-cli start), skip interactive input
const isInteractive = process.stdin.isTTY === true;
const boxWidth = termWidth;
const visiblePollActivity = pollActivity.slice(-10);
const visibleChatActivity = chatActivity.slice(-3);
// Split poll items: settled items go into <Static> (rendered once, scrollable),
// active items render normally (need spinner animation).
const settledStatuses = new Set(['posted', 'skipped', 'error', undefined]);
const settledTypes = new Set(['idle', 'online', 'error']);
const isSettled = (item) => {
if (settledTypes.has(item.type))
return true;
return settledStatuses.has(item.status) && item.status !== undefined;
};
const settledPollItems = pollActivity.filter(isSettled);
const activePollItems = pollActivity.filter((item) => !isSettled(item));
const visibleChatActivity = chatActivity.slice(-5);
const statsText = predictionCount > 0 ? ` ${border.horizontal.repeat(3)} ${predictionCount} predicted` : '';

@@ -22,10 +32,10 @@ const connectedDisplay = connected ? 'Connected to the Hive' : 'connecting...';

const headerFill = Math.max(0, boxWidth - nameDisplay.length - connectedDisplay.length - 12 - statsText.length);
return (_jsxs(Box, { flexDirection: "column", width: boxWidth, children: [_jsx(AsciiTicker, { rows: 2, step: predictionCount }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: `${border.topLeft}${border.horizontal} ${symbols.hive} ` }), _jsxs(Text, { color: colors.white, bold: true, children: [agentName, " agent"] }), _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), _jsx(Text, { color: connected ? colors.green : colors.honey, children: connected ? 'Connected to the Hive' : 'connecting...' }), statsText && _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), statsText && _jsxs(Text, { color: colors.honey, children: [predictionCount, " predicted"] }), _jsxs(Text, { color: colors.gray, children: [' ', border.horizontal.repeat(Math.max(0, headerFill)), border.topRight] })] }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 8, maxHeight: 24, children: [!connected && _jsx(Spinner, { label: "Initiating neural link..." }), visiblePollActivity.map((item, i) => {
const isNewest = i === visiblePollActivity.length - 1 && item.status !== 'analyzing';
return (_jsxs(Box, { flexDirection: "column", width: boxWidth, children: [_jsx(AsciiTicker, { rows: 2, step: predictionCount }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: `${border.topLeft}${border.horizontal} ${symbols.hive} ` }), _jsxs(Text, { color: colors.white, bold: true, children: [agentName, " agent"] }), _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), _jsx(Text, { color: connected ? colors.green : colors.honey, children: connected ? 'Connected to the Hive' : 'connecting...' }), statsText && _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), statsText && _jsxs(Text, { color: colors.honey, children: [predictionCount, " predicted"] }), _jsxs(Text, { color: colors.gray, children: [' ', border.horizontal.repeat(Math.max(0, headerFill)), border.topRight] })] }), modelInfo && (_jsxs(Box, { paddingLeft: 1, children: [_jsxs(Text, { color: colors.gray, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.cyan, children: modelInfo.modelId }), _jsxs(Text, { color: colors.gray, children: [" ", '\u00d7', " "] }), _jsx(Text, { color: colors.white, children: "zData" })] })), _jsx(Static, { items: settledPollItems, children: (item, i) => {
const isMega = item.type === 'megathread';
const accentColor = isMega ? colors.controversial : colors.cyan;
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [item.type === 'online' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: colors.honey, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.white, children: item.text })] })), (item.type === 'signal' || item.type === 'megathread') && (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: isMega ? colors.controversial : colors.honey, children: [symbols.hive, " "] }), _jsx(Text, { color: accentColor, children: item.text }), item.status === 'skipped' && (_jsxs(Text, { color: colors.honey, children: [" ", symbols.diamondOpen, " skipped"] })), item.status === 'skipped' && item.tokenUsage && (_jsxs(Text, { color: colors.gray, dimColor: true, children: [' ', symbols.circle, " ", item.tokenUsage.inputTokens.toLocaleString(), " in", item.tokenUsage.cacheReadTokens > 0 && ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`, item.tokenUsage.cacheReadTokens === 0 && item.tokenUsage.cacheWriteTokens > 0 && ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`, ' \u00b7 ', item.tokenUsage.outputTokens.toLocaleString(), " out", item.tokenUsage.toolCalls > 0 && ` \u00b7 tools: ${item.tokenUsage.toolNames.join(', ')}`] }))] }), item.status === 'posted' && item.result && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginLeft: 13, children: [_jsxs(Text, { color: isMega ? colors.controversial : convictionColor(item.conviction ?? 0), children: [symbols.diamond, ' '] }), _jsx(Text, { color: colors.white, children: item.result })] }), _jsxs(Box, { flexDirection: "column", marginLeft: 15, children: [item.url && (_jsxs(Text, { color: colors.gray, dimColor: true, children: ["url: ", item.url] })), item.tokenUsage && (_jsxs(Text, { color: colors.gray, dimColor: true, children: ["tokens: ", item.tokenUsage.inputTokens.toLocaleString(), " in", item.tokenUsage.cacheReadTokens > 0 && ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`, item.tokenUsage.cacheReadTokens === 0 && item.tokenUsage.cacheWriteTokens > 0 && ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`, ' \u00b7 ', item.tokenUsage.outputTokens.toLocaleString(), " out"] })), item.tokenUsage && item.tokenUsage.toolCalls > 0 && (_jsxs(Text, { color: colors.gray, dimColor: true, children: ["tools: ", item.tokenUsage.toolCalls, " tools (", item.tokenUsage.toolNames.join(', '), ")"] }))] })] })), item.status === 'error' && item.result && (_jsx(Box, { marginLeft: 13, children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", item.result] }) }))] })), item.type === 'idle' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: colors.gray, children: [symbols.circle, " ", item.text] })] })), item.type === 'error' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", item.text] })] }))] }, `settled-${i}`));
} }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 2, children: [!connected && _jsx(Spinner, { label: "Initiating neural link..." }), activePollItems.map((item, i) => {
const isMega = item.type === 'megathread';
const accentColor = isMega ? colors.controversial : colors.cyan;
return (_jsxs(Box, { flexDirection: "column", children: [item.type === 'online' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: colors.honey, children: [symbols.hive, " "] }), _jsx(PollText, { color: colors.white, text: item.text, animate: isNewest })] })), (item.type === 'signal' || item.type === 'megathread') && (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: isMega ? colors.controversial : colors.honey, children: [symbols.hive, " "] }), _jsx(PollText, { color: accentColor, text: item.text, animate: isNewest }), item.status === 'analyzing' && (_jsx(Text, { children: " " })), item.status === 'analyzing' && (_jsx(Spinner, { label: "analyzing..." })), item.status === 'skipped' && (_jsxs(Text, { color: colors.honey, children: [" ", symbols.diamondOpen, " skipped"] })), item.status === 'skipped' && item.tokenUsage && (_jsxs(Text, { color: colors.gray, dimColor: true, children: [' ', symbols.circle, " ", item.tokenUsage.inputTokens.toLocaleString(), " in", item.tokenUsage.cacheReadTokens > 0 && ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`, item.tokenUsage.cacheReadTokens === 0 && item.tokenUsage.cacheWriteTokens > 0 && ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`, ' \u00b7 ', item.tokenUsage.outputTokens.toLocaleString(), " out", item.tokenUsage.toolCalls > 0 && ` \u00b7 tools: ${item.tokenUsage.toolNames.join(', ')}`] }))] }), item.status === 'analyzing' && item.detail && (_jsx(Box, { marginLeft: 13, children: _jsx(PollText, { color: colors.gray, text: `"${item.detail}"`, animate: false }) })), item.status === 'posted' && item.result && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginLeft: 13, children: [_jsxs(Text, { color: isMega ? colors.controversial : convictionColor(item.conviction ?? 0), children: [symbols.diamond, ' '] }), _jsx(PollText, { color: colors.white, text: item.result, animate: isNewest })] }), item.url && (_jsx(Box, { marginLeft: 15, children: _jsx(Text, { color: colors.gray, dimColor: true, children: item.url }) })), item.tokenUsage && (_jsxs(Box, { flexDirection: "column", marginLeft: 15, children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: ["tokens: ", item.tokenUsage.inputTokens.toLocaleString(), " in", item.tokenUsage.cacheReadTokens > 0 && ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`, item.tokenUsage.cacheReadTokens === 0 && item.tokenUsage.cacheWriteTokens > 0 && ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`, ' \u00b7 ', item.tokenUsage.outputTokens.toLocaleString(), " out", item.tokenUsage.toolCalls > 0 && ` \u00b7 tools: ${item.tokenUsage.toolNames.join(', ')}`] }), item.tokenUsage.toolResults.map((tr, j) => {
const preview = tr.result.length > 200 ? tr.result.slice(0, 200) + '\u2026' : tr.result;
return (_jsxs(Text, { color: colors.gray, dimColor: true, children: [tr.toolName, ": ", preview] }, j));
})] }))] })), item.status === 'error' && item.result && (_jsx(Box, { marginLeft: 13, children: _jsx(PollText, { color: colors.red, text: `${symbols.cross} ${item.result}`, animate: isNewest }) }))] })), item.type === 'idle' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsx(PollText, { color: colors.gray, text: `${symbols.circle} ${item.text}`, animate: isNewest })] })), item.type === 'error' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsx(PollText, { color: colors.red, text: `${symbols.cross} ${item.text}`, animate: isNewest })] }))] }, i));
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: isMega ? colors.controversial : colors.honey, children: [symbols.hive, " "] }), _jsx(PollText, { color: accentColor, text: item.text, animate: false }), _jsx(Text, { children: " " }), _jsx(Spinner, { label: "analyzing..." })] }), item.detail && (_jsx(Box, { marginLeft: 13, children: _jsx(PollText, { color: colors.gray, text: `"${item.detail}"`, animate: false }) }))] }, `active-${item.id ?? i}`));
})] }), (chatActivity.length > 0 || chatStreaming) && (_jsxs(_Fragment, { children: [_jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.teeLeft, `${border.horizontal.repeat(2)} chat with ${agentName} agent `, border.horizontal.repeat(Math.max(0, boxWidth - agentName.length - 22)), border.teeRight] }) }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 2, maxHeight: 8, children: [visibleChatActivity.map((item, i) => (_jsxs(Box, { children: [item.type === 'chat-user' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.white, bold: true, children: ["you:", ' '] }), _jsx(Text, { color: colors.white, children: item.text })] })), item.type === 'chat-agent' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, bold: true, children: [agentName, " agent:", ' '] }), _jsx(Text, { color: colors.white, wrap: "wrap", children: item.text })] })), item.type === 'chat-error' && (_jsx(Box, { children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", item.text] }) }))] }, i))), chatStreaming && chatBuffer && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, bold: true, children: [agentName, " agent:", ' '] }), _jsx(Text, { color: colors.white, wrap: "wrap", children: chatBuffer })] }))] })] })), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [isInteractive ? border.teeLeft : border.bottomLeft, border.horizontal.repeat(boxWidth - 2), isInteractive ? border.teeRight : border.bottomRight] }) }), isInteractive && (_jsxs(_Fragment, { children: [_jsx(Box, { paddingLeft: 1, children: _jsx(CommandInput, { value: input, onChange: setInput, onSubmit: (val) => {

@@ -32,0 +42,0 @@ setInput('');

@@ -12,3 +12,3 @@ import { HiveAgent, loadMemory } from '@hive-org/sdk';

import { fetchRulesTool } from '../fetch-rules.js';
import { getModel } from '../model.js';
import { getModel, resolveModelInfo } from '../model.js';
import { getAllTools, getReadSkillTool, getSkillMetadataList, initializeSkills, } from '../tools/index.js';

@@ -19,2 +19,3 @@ export function useAgent() {

const [agentBio, setAgentBio] = useState('');
const [modelInfo, setModelInfo] = useState(null);
const [pollActivity, setPollActivity] = useState([]);

@@ -109,2 +110,4 @@ const [chatActivity, setChatActivity] = useState([]);

strategyContentRef.current = config.strategyContent;
const resolvedModelInfo = resolveModelInfo();
setModelInfo(resolvedModelInfo);
const skillRegistry = await initializeSkills(process.cwd());

@@ -394,6 +397,14 @@ const allTools = getAllTools();

let fullResponse = '';
let lastFlushTime = 0;
const THROTTLE_MS = 80;
for await (const chunk of result.textStream) {
fullResponse += chunk;
setChatBuffer(fullResponse);
const now = Date.now();
if (now - lastFlushTime >= THROTTLE_MS) {
setChatBuffer(fullResponse);
lastFlushTime = now;
}
}
// Always flush the final value
setChatBuffer(fullResponse);
// Surface tool results when the model didn't produce follow-up text

@@ -427,2 +438,3 @@ const steps = await result.steps;

agentBio,
modelInfo,
pollActivity,

@@ -429,0 +441,0 @@ chatActivity,

@@ -0,5 +1,7 @@

import { AI_PROVIDERS } from '../ai-providers.js';
import { getAgentProviderKeys } from '../load-agent-env.js';
const PROVIDERS = [
{
label: 'Anthropic',
envVar: 'ANTHROPIC_API_KEY',
defaultModel: 'claude-haiku-4-5',
load: async (modelId) => {

@@ -11,4 +13,4 @@ const { anthropic } = await import('@ai-sdk/anthropic');

{
label: 'OpenAI',
envVar: 'OPENAI_API_KEY',
defaultModel: 'gpt-4o',
load: async (modelId) => {

@@ -20,4 +22,4 @@ const { openai } = await import('@ai-sdk/openai');

{
label: 'Google',
envVar: 'GOOGLE_GENERATIVE_AI_API_KEY',
defaultModel: 'gemini-3-pro-preview',
load: async (modelId) => {

@@ -29,4 +31,4 @@ const { google } = await import('@ai-sdk/google');

{
label: 'xAI',
envVar: 'XAI_API_KEY',
defaultModel: 'grok-4',
load: async (modelId) => {

@@ -38,4 +40,4 @@ const { xai } = await import('@ai-sdk/xai');

{
label: 'OpenRouter',
envVar: 'OPENROUTER_API_KEY',
defaultModel: 'anthropic/claude-3.5-sonnet',
load: async (modelId) => {

@@ -48,2 +50,21 @@ const { createOpenRouter } = await import('@openrouter/ai-sdk-provider');

];
export function resolveModelInfo() {
const overrideModel = process.env.HIVE_MODEL;
const agentKeys = getAgentProviderKeys();
const sortedProviders = [
...PROVIDERS.filter((p) => agentKeys.has(p.envVar)),
...PROVIDERS.filter((p) => !agentKeys.has(p.envVar)),
];
for (const provider of sortedProviders) {
const keyValue = process.env[provider.envVar];
if (keyValue && keyValue.trim().length > 0) {
const centralProvider = AI_PROVIDERS.find((p) => p.envVar === provider.envVar);
const runtimeModel = centralProvider?.models.runtime ?? 'unknown';
const modelId = overrideModel ?? runtimeModel;
const source = agentKeys.has(provider.envVar) ? '.env' : 'shell';
return { provider: provider.label, modelId, source };
}
}
return { provider: 'unknown', modelId: 'unknown', source: 'unknown' };
}
let _modelPromise = null;

@@ -55,7 +76,16 @@ export function getModel() {

_modelPromise = (async () => {
const overrideModel = process.env.HIVE_MODEL;
for (const provider of PROVIDERS) {
const info = resolveModelInfo();
if (info.provider === 'unknown') {
throw new Error('No AI provider API key found in environment. ' +
'Set one of: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY');
}
const agentKeys = getAgentProviderKeys();
const sortedProviders = [
...PROVIDERS.filter((p) => agentKeys.has(p.envVar)),
...PROVIDERS.filter((p) => !agentKeys.has(p.envVar)),
];
for (const provider of sortedProviders) {
const keyValue = process.env[provider.envVar];
if (keyValue && keyValue.trim().length > 0) {
const modelId = overrideModel ?? provider.defaultModel;
const modelId = info.modelId;
const model = await provider.load(modelId);

@@ -65,6 +95,5 @@ return model;

}
throw new Error('No AI provider API key found in environment. ' +
'Set one of: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY');
throw new Error('Unreachable: resolveModelInfo succeeded but no provider found');
})();
return _modelPromise;
}
import chalk from 'chalk';
import { symbols } from './theme.js';
const restoreScreen = () => {
process.stdout.write('\x1b[?1049l');
};
const exitImmediately = (exitCode = 0) => {
restoreScreen();
process.exit(exitCode);

@@ -17,10 +13,7 @@ };

});
// Use alternate screen buffer (like vim/htop) to prevent text reflow on resize.
// The normal buffer reflows wrapped lines when the terminal width changes, which
// desyncs Ink's internal line counter and produces ghost copies of the UI.
// The alternate buffer is position-based — no reflow, no ghosts.
process.stdout.write('\x1b[?1049h');
process.on('exit', restoreScreen);
// No alternate screen buffer — normal buffer allows terminal scrollback
// so users can scroll up to see historical poll activity.
// <Static> items from Ink flow into the scrollback naturally.
process.on('SIGINT', () => exitImmediately(0));
process.on('SIGTERM', () => exitImmediately(0));
}
import { HiveAgent } from '@hive-org/sdk';
import { loadMemory } from '@hive-org/sdk';
import { loadAgentConfig } from './config.js';
import { resolveModelInfo } from './model.js';
import { HIVE_FRONTEND_URL } from '../config.js';

@@ -75,3 +76,3 @@ import { processSignalAndSummarize, processMegathreadRound, } from './analysis.js';

console.log(` tools: ${toolNames} (${result.usage.toolCalls} calls)`);
logToolResults(result.usage.toolResults);
// logToolResults(result.usage.toolResults);
}

@@ -117,3 +118,3 @@ else {

console.log(` tools: ${toolNames} (${result.usage.toolCalls} calls)`);
logToolResults(result.usage.toolResults);
// logToolResults(result.usage.toolResults);
}

@@ -159,3 +160,5 @@ else {

await agent.start();
console.log(`[${config.name}] agent online — "${config.bio ?? ''}"`);
const modelInfoResult = resolveModelInfo();
console.log(`[${config.name}] ${modelInfoResult.modelId} \u00d7 zData`);
console.log(`[${config.name}] agent online \u2014 "${config.bio ?? ''}"`);
}

@@ -6,5 +6,4 @@ export const AI_PROVIDERS = [

package: '@ai-sdk/openai',
importLine: "import { openai } from '@ai-sdk/openai';",
modelCall: "openai('gpt-5.2')",
envVar: 'OPENAI_API_KEY',
models: { validation: 'gpt-4o-mini', generation: 'gpt-5-mini', runtime: 'gpt-5-mini' },
},

@@ -15,5 +14,8 @@ {

package: '@ai-sdk/anthropic',
importLine: "import { anthropic } from '@ai-sdk/anthropic';",
modelCall: "anthropic('claude-opus-4-5')",
envVar: 'ANTHROPIC_API_KEY',
models: {
validation: 'claude-haiku-4-5-20251001',
generation: 'claude-haiku-4-5',
runtime: 'claude-haiku-4-5',
},
},

@@ -24,5 +26,8 @@ {

package: '@ai-sdk/google',
importLine: "import { google } from '@ai-sdk/google';",
modelCall: "google('gemini-3-pro-preview')",
envVar: 'GOOGLE_GENERATIVE_AI_API_KEY',
models: {
validation: 'gemini-2.0-flash',
generation: 'gemini-3-flash-preview',
runtime: 'gemini-3-flash-preview',
},
},

@@ -33,5 +38,8 @@ {

package: '@ai-sdk/xai',
importLine: "import { xai } from '@ai-sdk/xai';",
modelCall: "xai('grok-4')",
envVar: 'XAI_API_KEY',
models: {
validation: 'grok-2',
generation: 'grok-4-1-fast-reasoning',
runtime: 'grok-4-1-fast-reasoning',
},
},

@@ -42,7 +50,16 @@ {

package: '@openrouter/ai-sdk-provider',
importLine: "import { createOpenRouter } from '@openrouter/ai-sdk-provider';",
modelCall: "(createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY }).chat('anthropic/claude-3.5-sonnet'))",
envVar: 'OPENROUTER_API_KEY',
models: {
validation: 'openai/gpt-4o-mini',
generation: 'openai/gpt-5.1-mini',
runtime: 'openai/gpt-5.1-mini',
},
},
];
/**
* All env-var names used by AI providers.
* Used to clear shell-inherited keys before loading an agent's .env,
* so only the agent's chosen provider is active.
*/
export const AI_PROVIDER_ENV_VARS = AI_PROVIDERS.map((p) => p.envVar);
export function getProvider(id) {

@@ -49,0 +66,0 @@ const provider = AI_PROVIDERS.find((p) => p.id === id);

@@ -7,15 +7,18 @@ import { streamText } from 'ai';

import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { getProvider } from '../ai-providers.js';
import { SOUL_PRESETS, STRATEGY_PRESETS, buildSoulMarkdown, buildStrategyMarkdown, } from '../presets.js';
function buildModel(providerId, apiKey) {
const provider = getProvider(providerId);
const modelId = provider.models.generation;
switch (providerId) {
case 'openai':
return createOpenAI({ apiKey })('gpt-4o');
return createOpenAI({ apiKey })(modelId);
case 'anthropic':
return createAnthropic({ apiKey })('claude-haiku-4-5');
return createAnthropic({ apiKey })(modelId);
case 'google':
return createGoogleGenerativeAI({ apiKey })('gemini-2.0-flash');
return createGoogleGenerativeAI({ apiKey })(modelId);
case 'xai':
return createXai({ apiKey })('grok-2');
return createXai({ apiKey })(modelId);
case 'openrouter':
return createOpenRouter({ apiKey }).chat('anthropic/claude-3.5-sonnet');
return createOpenRouter({ apiKey }).chat(modelId);
}

@@ -22,0 +25,0 @@ }

@@ -7,2 +7,3 @@ import { generateText } from 'ai';

import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { getProvider } from '../ai-providers.js';
/**

@@ -33,14 +34,16 @@ * Make a lightweight test call to validate the user's API key.

function buildTestModel(providerId, apiKey) {
const provider = getProvider(providerId);
const modelId = provider.models.validation;
switch (providerId) {
case 'openai':
return createOpenAI({ apiKey })('gpt-4o-mini');
return createOpenAI({ apiKey })(modelId);
case 'anthropic':
return createAnthropic({ apiKey })('claude-haiku-4-5-20251001');
return createAnthropic({ apiKey })(modelId);
case 'google':
return createGoogleGenerativeAI({ apiKey })('gemini-2.0-flash');
return createGoogleGenerativeAI({ apiKey })(modelId);
case 'xai':
return createXai({ apiKey })('grok-2');
return createXai({ apiKey })(modelId);
case 'openrouter':
return createOpenRouter({ apiKey }).chat('openai/gpt-4o-mini');
return createOpenRouter({ apiKey }).chat(modelId);
}
}

@@ -10,2 +10,3 @@ #!/usr/bin/env node

import { showWelcome } from './create/welcome.js';
import { loadAgentEnv } from './load-agent-env.js';
const require = createRequire(import.meta.url);

@@ -72,3 +73,3 @@ const pkg = require('../package.json');

}
await import('dotenv/config');
await loadAgentEnv();
const { runHeadless } = await import('./agent/run-headless.js');

@@ -87,3 +88,3 @@ await runHeadless();

// Direct agent run — cwd is already the agent directory.
await import('dotenv/config');
await loadAgentEnv();
const { setupProcessLifecycle } = await import('./agent/process-lifecycle.js');

@@ -110,3 +111,3 @@ const { App } = await import('./agent/app.js');

process.chdir(picked.dir);
await import('dotenv/config');
await loadAgentEnv();
const { setupProcessLifecycle } = await import('./agent/process-lifecycle.js');

@@ -113,0 +114,0 @@ const { App } = await import('./agent/app.js');

{
"name": "@hive-org/cli",
"version": "0.1.8",
"version": "0.1.9",
"description": "CLI for bootstrapping Hive AI Agents",

@@ -5,0 +5,0 @@ "type": "module",