vibebuddy 0.2.0 → 0.3.1
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/package.json +1 -1
- package/src/bin.js +1 -1
- package/src/hook-template.mjs +39 -10
- package/src/mcp-template.mjs +15 -0
package/package.json
CHANGED
package/src/bin.js
CHANGED
|
@@ -159,7 +159,7 @@ async function cmdInit(flags) {
|
|
|
159
159
|
(entry.hooks ?? []).some(
|
|
160
160
|
(h) => typeof h.command === 'string' && h.command.endsWith(`hook.mjs" ${ev}`)
|
|
161
161
|
);
|
|
162
|
-
for (const ev of ['SessionStart', 'PostToolUse', 'Stop', 'Notification']) {
|
|
162
|
+
for (const ev of ['SessionStart', 'UserPromptSubmit', 'PostToolUse', 'Stop', 'SessionEnd', 'Notification']) {
|
|
163
163
|
settings.hooks[ev] ??= [];
|
|
164
164
|
if (settings.hooks[ev].some((e) => isOurs(e, ev))) continue;
|
|
165
165
|
settings.hooks[ev].push({
|
package/src/hook-template.mjs
CHANGED
|
@@ -5,6 +5,27 @@ import fs from 'node:fs';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
|
|
8
|
+
// read the hook payload from stdin (Claude Code pipes JSON), briefly and safely
|
|
9
|
+
function readStdin(timeoutMs = 300) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
let data = '';
|
|
12
|
+
const done = () => resolve(data);
|
|
13
|
+
const t = setTimeout(done, timeoutMs);
|
|
14
|
+
try {
|
|
15
|
+
process.stdin.on('data', (c) => (data += c));
|
|
16
|
+
process.stdin.on('end', () => {
|
|
17
|
+
clearTimeout(t);
|
|
18
|
+
done();
|
|
19
|
+
});
|
|
20
|
+
process.stdin.on('error', () => {});
|
|
21
|
+
process.stdin.resume();
|
|
22
|
+
} catch {
|
|
23
|
+
clearTimeout(t);
|
|
24
|
+
done();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
8
29
|
export async function run(argv = process.argv.slice(2)) {
|
|
9
30
|
const home = process.env.VB_HOME || path.join(os.homedir(), '.vibebuddy');
|
|
10
31
|
let cfg;
|
|
@@ -19,15 +40,30 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
19
40
|
PostToolUse: 'heartbeat',
|
|
20
41
|
SubagentStop: 'heartbeat',
|
|
21
42
|
Stop: 'stop',
|
|
43
|
+
SessionEnd: 'session_end',
|
|
22
44
|
Notification: 'waiting_input',
|
|
23
45
|
'codex-notify': 'stop',
|
|
24
46
|
};
|
|
25
47
|
const type = MAP[argv[0]];
|
|
26
48
|
if (!type) return;
|
|
27
49
|
|
|
28
|
-
//
|
|
50
|
+
// each concurrent agent session gets its own presence lane
|
|
51
|
+
let sessionId = null;
|
|
52
|
+
if (argv[0] === 'codex-notify') {
|
|
53
|
+
try {
|
|
54
|
+
const j = JSON.parse(argv[1] ?? '{}');
|
|
55
|
+
sessionId = j['thread-id'] ?? j.thread_id ?? j['turn-id'] ?? null;
|
|
56
|
+
} catch {}
|
|
57
|
+
} else {
|
|
58
|
+
try {
|
|
59
|
+
const j = JSON.parse((await readStdin()) || '{}');
|
|
60
|
+
sessionId = j.session_id ?? j.sessionId ?? null;
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// heartbeats are frequent — throttle to one per 20s per session via an mtime file
|
|
29
65
|
if (type === 'heartbeat') {
|
|
30
|
-
const f = path.join(home, '.
|
|
66
|
+
const f = path.join(home, `.hb-${String(sessionId ?? 'x').replace(/[^\w-]/g, '').slice(0, 24)}`);
|
|
31
67
|
try {
|
|
32
68
|
if (Date.now() - fs.statSync(f).mtimeMs < 20_000) return;
|
|
33
69
|
} catch {}
|
|
@@ -36,13 +72,6 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
36
72
|
} catch {}
|
|
37
73
|
}
|
|
38
74
|
|
|
39
|
-
// drain stdin so the agent's pipe never blocks (Claude Code sends hook payload JSON)
|
|
40
|
-
try {
|
|
41
|
-
process.stdin.resume();
|
|
42
|
-
process.stdin.on('data', () => {});
|
|
43
|
-
process.stdin.on('error', () => {});
|
|
44
|
-
} catch {}
|
|
45
|
-
|
|
46
75
|
const ac = new AbortController();
|
|
47
76
|
const timer = setTimeout(() => ac.abort(), 3000);
|
|
48
77
|
timer.unref?.();
|
|
@@ -50,7 +79,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
50
79
|
await fetch(cfg.server + '/api/agent/event', {
|
|
51
80
|
method: 'POST',
|
|
52
81
|
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + cfg.token },
|
|
53
|
-
body: JSON.stringify({ type, agent_kind: cfg.agent_kind || 'claude-code' }),
|
|
82
|
+
body: JSON.stringify({ type, agent_kind: cfg.agent_kind || 'claude-code', session_id: sessionId }),
|
|
54
83
|
signal: ac.signal,
|
|
55
84
|
});
|
|
56
85
|
} catch {
|
package/src/mcp-template.mjs
CHANGED
|
@@ -67,6 +67,16 @@ const TOOLS = [
|
|
|
67
67
|
required: ['game_id', 'move'],
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
|
+
{
|
|
71
|
+
name: 'get_game',
|
|
72
|
+
description:
|
|
73
|
+
'Read the current board of a VibeBuddy game: cells array (0 empty, 1 player_a, 2 player_b), whose turn, status. gomoku: 15x15 row-major, 5 in a row wins. connect4: 7x6 row-major with gravity, 4 in a row wins.',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: { game_id: { type: 'number' } },
|
|
77
|
+
required: ['game_id'],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
70
80
|
];
|
|
71
81
|
|
|
72
82
|
function loadConfig() {
|
|
@@ -134,6 +144,11 @@ async function handleToolCall(cfg, name, args = {}) {
|
|
|
134
144
|
if (r.offline) return { error: 'vibebuddy unreachable — move not played' };
|
|
135
145
|
return r.data;
|
|
136
146
|
}
|
|
147
|
+
if (name === 'get_game') {
|
|
148
|
+
const r = await callServer(cfg, 'GET', `/api/games/${Number(args.game_id)}`);
|
|
149
|
+
if (r.offline) return { error: 'vibebuddy unreachable' };
|
|
150
|
+
return r.data;
|
|
151
|
+
}
|
|
137
152
|
return { error: `unknown tool: ${name}` };
|
|
138
153
|
}
|
|
139
154
|
|