@hive-org/cli 0.0.9 → 0.0.10
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/dist/agent/hooks/useAgent.js +0 -4
- package/dist/agent/process-lifecycle.js +3 -34
- package/dist/agent/run-headless.js +68 -0
- package/dist/create/steps/DoneStep.js +1 -1
- package/dist/index.js +50 -14
- package/dist/start/AgentProcessManager.js +1 -34
- package/dist/start/start-command.js +0 -1
- package/package.json +1 -1
|
@@ -7,7 +7,6 @@ import { editSectionTool } from '../edit-section.js';
|
|
|
7
7
|
import { fetchRulesTool } from '../fetch-rules.js';
|
|
8
8
|
import { loadMemory } from '@hive-org/sdk';
|
|
9
9
|
import { processSignalAndSummarize, extractAndSaveMemory } from '../analysis.js';
|
|
10
|
-
import { registerShutdownAgent } from '../process-lifecycle.js';
|
|
11
10
|
import { getModel } from '../model.js';
|
|
12
11
|
export function useAgent() {
|
|
13
12
|
const [connected, setConnected] = useState(false);
|
|
@@ -153,9 +152,6 @@ export function useAgent() {
|
|
|
153
152
|
},
|
|
154
153
|
});
|
|
155
154
|
agentRef.current = agent;
|
|
156
|
-
registerShutdownAgent(async () => {
|
|
157
|
-
await agent.stop();
|
|
158
|
-
});
|
|
159
155
|
await agent.start();
|
|
160
156
|
setConnected(true);
|
|
161
157
|
const bio = config.bio ?? '';
|
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { symbols } from './theme.js';
|
|
3
|
-
let _shutdownAgent = null;
|
|
4
|
-
let _shuttingDown = false;
|
|
5
|
-
export function registerShutdownAgent(fn) {
|
|
6
|
-
_shutdownAgent = fn;
|
|
7
|
-
}
|
|
8
|
-
export function clearShutdownAgent() {
|
|
9
|
-
_shutdownAgent = null;
|
|
10
|
-
}
|
|
11
3
|
const restoreScreen = () => {
|
|
12
4
|
process.stdout.write('\x1b[?1049l');
|
|
13
5
|
};
|
|
14
|
-
|
|
15
|
-
if (_shuttingDown) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
_shuttingDown = true;
|
|
19
|
-
if (_shutdownAgent) {
|
|
20
|
-
try {
|
|
21
|
-
await _shutdownAgent();
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
// Best-effort memory save on shutdown
|
|
25
|
-
}
|
|
26
|
-
_shutdownAgent = null;
|
|
27
|
-
}
|
|
6
|
+
const exitImmediately = (exitCode = 0) => {
|
|
28
7
|
restoreScreen();
|
|
29
8
|
process.exit(exitCode);
|
|
30
9
|
};
|
|
@@ -41,16 +20,6 @@ export function setupProcessLifecycle() {
|
|
|
41
20
|
// The alternate buffer is position-based — no reflow, no ghosts.
|
|
42
21
|
process.stdout.write('\x1b[?1049h');
|
|
43
22
|
process.on('exit', restoreScreen);
|
|
44
|
-
process.on('SIGINT', () =>
|
|
45
|
-
|
|
46
|
-
restoreScreen();
|
|
47
|
-
process.exit(1);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
process.on('SIGTERM', () => {
|
|
51
|
-
gracefulShutdown().catch(() => {
|
|
52
|
-
restoreScreen();
|
|
53
|
-
process.exit(1);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
23
|
+
process.on('SIGINT', () => exitImmediately(0));
|
|
24
|
+
process.on('SIGTERM', () => exitImmediately(0));
|
|
56
25
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { HiveAgent } from '@hive-org/sdk';
|
|
2
|
+
import { loadMemory } from '@hive-org/sdk';
|
|
3
|
+
import { loadAgentConfig } from './config.js';
|
|
4
|
+
import { processSignalAndSummarize } from './analysis.js';
|
|
5
|
+
function timestamp() {
|
|
6
|
+
const now = new Date();
|
|
7
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
8
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
9
|
+
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
10
|
+
const ts = `${hours}:${minutes}:${seconds}`;
|
|
11
|
+
return ts;
|
|
12
|
+
}
|
|
13
|
+
export async function runHeadless() {
|
|
14
|
+
const { HIVE_API_URL } = await import('../config.js');
|
|
15
|
+
const config = await loadAgentConfig();
|
|
16
|
+
const initialMemory = await loadMemory();
|
|
17
|
+
let memoryRef = initialMemory;
|
|
18
|
+
const soulContent = config.soulContent;
|
|
19
|
+
const strategyContent = config.strategyContent;
|
|
20
|
+
let predictionCount = 0;
|
|
21
|
+
const agent = new HiveAgent(HIVE_API_URL, {
|
|
22
|
+
name: config.name,
|
|
23
|
+
avatarUrl: config.avatarUrl,
|
|
24
|
+
bio: config.bio ?? undefined,
|
|
25
|
+
predictionProfile: config.predictionProfile,
|
|
26
|
+
pollIntervalMs: 5000,
|
|
27
|
+
pollLimit: 20,
|
|
28
|
+
onPollEmpty: () => {
|
|
29
|
+
console.log(`[${timestamp()}] idle — no new threads`);
|
|
30
|
+
},
|
|
31
|
+
onStop: async () => { },
|
|
32
|
+
onNewThread: async (thread) => {
|
|
33
|
+
try {
|
|
34
|
+
const threadPreview = thread.text.length > 80 ? thread.text.slice(0, 80) + '\u2026' : thread.text;
|
|
35
|
+
console.log(`[${timestamp()}] signal c/${thread.project_id} · ${thread.id.slice(0, 8)}`);
|
|
36
|
+
console.log(` "${threadPreview}"`);
|
|
37
|
+
console.log(`[${timestamp()}] analyzing...`);
|
|
38
|
+
const result = await processSignalAndSummarize(thread, agent.recentComments, memoryRef, soulContent, strategyContent);
|
|
39
|
+
if (result.skip) {
|
|
40
|
+
console.log(`[${timestamp()}] skipped — outside expertise`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await agent.postComment(thread.id, {
|
|
44
|
+
thread_id: thread.id,
|
|
45
|
+
text: result.summary,
|
|
46
|
+
conviction: result.conviction,
|
|
47
|
+
}, thread.text);
|
|
48
|
+
predictionCount += 1;
|
|
49
|
+
const sign = result.conviction >= 0 ? '+' : '';
|
|
50
|
+
console.log(`[${timestamp()}] predicted [${sign}${result.conviction}%] "${result.summary}" (${predictionCount} total)`);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
54
|
+
const message = raw.length > 120 ? raw.slice(0, 120) + '\u2026' : raw;
|
|
55
|
+
console.error(`[${timestamp()}] error: ${message}`);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const shutdown = async () => {
|
|
60
|
+
console.log(`[${config.name}] shutting down...`);
|
|
61
|
+
await agent.stop();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
};
|
|
64
|
+
process.on('SIGINT', () => void shutdown());
|
|
65
|
+
process.on('SIGTERM', () => void shutdown());
|
|
66
|
+
await agent.start();
|
|
67
|
+
console.log(`[${config.name}] agent online — "${config.bio ?? ''}"`);
|
|
68
|
+
}
|
|
@@ -10,5 +10,5 @@ export function DoneStep({ projectDir }) {
|
|
|
10
10
|
const termWidth = process.stdout.columns || 60;
|
|
11
11
|
const boxWidth = Math.min(termWidth - 4, 60);
|
|
12
12
|
const line = border.horizontal.repeat(boxWidth - 2);
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.topLeft, line, border.topRight] }) }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: border.vertical }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Agent created successfully!"] }), _jsx(Text, { children: ' '.repeat(Math.max(0, boxWidth - 32)) }), _jsx(Text, { color: colors.honey, children: border.vertical })] }), _jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.bottomLeft, line, border.bottomRight] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [_jsx(Text, { color: colors.white, bold: true, children: "Next steps:" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Text, { color: colors.gray, children: [" 1. ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli@latest start" })] }) }),
|
|
13
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.topLeft, line, border.topRight] }) }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: border.vertical }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Agent created successfully!"] }), _jsx(Text, { children: ' '.repeat(Math.max(0, boxWidth - 32)) }), _jsx(Text, { color: colors.honey, children: border.vertical })] }), _jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.bottomLeft, line, border.bottomRight] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [_jsx(Text, { color: colors.white, bold: true, children: "Next steps:" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Text, { color: colors.gray, children: [" 1. ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli@latest start" })] }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.grayDim, children: " Fine-tune SOUL.md and STRATEGY.md by chatting with your agent during" }), _jsx(Text, { color: colors.grayDim, children: " a run, or edit them directly at:" }), _jsxs(Text, { color: colors.grayDim, children: [" ", _jsx(Text, { color: colors.white, children: projectDir })] })] })] })] }));
|
|
14
14
|
}
|
package/dist/index.js
CHANGED
|
@@ -14,8 +14,9 @@ const HELP_TEXT = `@hive-org/cli v${pkg.version}
|
|
|
14
14
|
Usage:
|
|
15
15
|
npx @hive-org/cli@latest create [agent-name] Scaffold a new Hive agent
|
|
16
16
|
npx @hive-org/cli@latest list List existing agents
|
|
17
|
-
npx @hive-org/cli@latest start
|
|
17
|
+
npx @hive-org/cli@latest start Start an agent (auto-detects agent dir)
|
|
18
18
|
npx @hive-org/cli@latest start-all Start all agents
|
|
19
|
+
npx @hive-org/cli@latest run Run agent headless (no TUI, used by start-all)
|
|
19
20
|
npx @hive-org/cli@latest migrate-templates Migrate old-style agents
|
|
20
21
|
npx @hive-org/cli@latest --help Show this help message
|
|
21
22
|
npx @hive-org/cli@latest --version Print version
|
|
@@ -55,20 +56,32 @@ else if (command === 'migrate-templates') {
|
|
|
55
56
|
const { waitUntilExit } = render(React.createElement(MigrateApp));
|
|
56
57
|
await waitUntilExit();
|
|
57
58
|
}
|
|
59
|
+
else if (command === 'run') {
|
|
60
|
+
// Headless agent run — no TUI, just console output.
|
|
61
|
+
// Used by start-all to spawn agents as child processes.
|
|
62
|
+
const { access } = await import('fs/promises');
|
|
63
|
+
const { join } = await import('path');
|
|
64
|
+
const isAgentDir = await access(join(process.cwd(), 'SOUL.md'))
|
|
65
|
+
.then(() => true)
|
|
66
|
+
.catch(() => false);
|
|
67
|
+
if (!isAgentDir) {
|
|
68
|
+
console.error('Error: "run" must be called from an agent directory (with SOUL.md)');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
await import('dotenv/config');
|
|
72
|
+
const { runHeadless } = await import('./agent/run-headless.js');
|
|
73
|
+
await runHeadless();
|
|
74
|
+
}
|
|
58
75
|
else if (command === 'start') {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const picked = selectedAgent;
|
|
69
|
-
const { showHoneycombBoot } = await import('./agent/components/HoneycombBoot.js');
|
|
70
|
-
await showHoneycombBoot(picked.name);
|
|
71
|
-
process.chdir(picked.dir);
|
|
76
|
+
// Detect if cwd is an agent directory (has SOUL.md).
|
|
77
|
+
// When called via agent's "npm start", cwd is the agent dir.
|
|
78
|
+
const { access } = await import('fs/promises');
|
|
79
|
+
const { join } = await import('path');
|
|
80
|
+
const isAgentDir = await access(join(process.cwd(), 'SOUL.md'))
|
|
81
|
+
.then(() => true)
|
|
82
|
+
.catch(() => false);
|
|
83
|
+
if (isAgentDir) {
|
|
84
|
+
// Direct agent run — cwd is already the agent directory.
|
|
72
85
|
await import('dotenv/config');
|
|
73
86
|
const { setupProcessLifecycle } = await import('./agent/process-lifecycle.js');
|
|
74
87
|
const { App } = await import('./agent/app.js');
|
|
@@ -76,6 +89,29 @@ else if (command === 'start') {
|
|
|
76
89
|
const { waitUntilExit } = render(React.createElement(App));
|
|
77
90
|
await waitUntilExit();
|
|
78
91
|
}
|
|
92
|
+
else {
|
|
93
|
+
// Interactive agent selection
|
|
94
|
+
await showWelcome();
|
|
95
|
+
let selectedAgent = null;
|
|
96
|
+
const { waitUntilExit: waitForSelect } = render(React.createElement(SelectAgentApp, {
|
|
97
|
+
onSelect: (agent) => {
|
|
98
|
+
selectedAgent = agent;
|
|
99
|
+
},
|
|
100
|
+
}));
|
|
101
|
+
await waitForSelect();
|
|
102
|
+
if (selectedAgent) {
|
|
103
|
+
const picked = selectedAgent;
|
|
104
|
+
const { showHoneycombBoot } = await import('./agent/components/HoneycombBoot.js');
|
|
105
|
+
await showHoneycombBoot(picked.name);
|
|
106
|
+
process.chdir(picked.dir);
|
|
107
|
+
await import('dotenv/config');
|
|
108
|
+
const { setupProcessLifecycle } = await import('./agent/process-lifecycle.js');
|
|
109
|
+
const { App } = await import('./agent/app.js');
|
|
110
|
+
setupProcessLifecycle();
|
|
111
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
112
|
+
await waitUntilExit();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
79
115
|
}
|
|
80
116
|
else {
|
|
81
117
|
console.error(`Unknown command: ${command}\n`);
|
|
@@ -2,7 +2,6 @@ import { spawn } from 'child_process';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
const FORCE_KILL_TIMEOUT_MS = 5_000;
|
|
5
|
-
const SHUTDOWN_TIMEOUT_MS = 10_000;
|
|
6
5
|
const ABSOLUTE_TIMEOUT_MS = FORCE_KILL_TIMEOUT_MS + 2_000;
|
|
7
6
|
export class AgentProcessManager {
|
|
8
7
|
constructor() {
|
|
@@ -64,41 +63,9 @@ export class AgentProcessManager {
|
|
|
64
63
|
agent.child = null;
|
|
65
64
|
this._spawnPiped(name);
|
|
66
65
|
}
|
|
67
|
-
async shutdownAll() {
|
|
68
|
-
const children = [];
|
|
69
|
-
for (const agent of this._agents.values()) {
|
|
70
|
-
if (agent.child) {
|
|
71
|
-
children.push(agent.child);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (children.length === 0) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const waitForAll = children.map((child) => new Promise((resolve) => {
|
|
78
|
-
if (child.exitCode !== null) {
|
|
79
|
-
resolve();
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
child.on('exit', () => resolve());
|
|
83
|
-
// Absolute safety timeout
|
|
84
|
-
setTimeout(() => resolve(), SHUTDOWN_TIMEOUT_MS + 2_000);
|
|
85
|
-
}));
|
|
86
|
-
for (const child of children) {
|
|
87
|
-
child.kill('SIGTERM');
|
|
88
|
-
}
|
|
89
|
-
const forceKillTimer = setTimeout(() => {
|
|
90
|
-
for (const agent of this._agents.values()) {
|
|
91
|
-
if (agent.child && agent.child.exitCode === null) {
|
|
92
|
-
agent.child.kill('SIGKILL');
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}, SHUTDOWN_TIMEOUT_MS);
|
|
96
|
-
await Promise.all(waitForAll);
|
|
97
|
-
clearTimeout(forceKillTimer);
|
|
98
|
-
}
|
|
99
66
|
_spawnPiped(name) {
|
|
100
67
|
const agentDir = path.join(this._agentsDir, name);
|
|
101
|
-
const child = spawn('
|
|
68
|
+
const child = spawn('npx', ['@hive-org/cli@latest', 'run'], {
|
|
102
69
|
cwd: agentDir,
|
|
103
70
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
104
71
|
});
|