@cmgmyr/hive 1.2.0 → 1.2.2
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/README.md +1 -1
- package/dist/build-info.json +1 -1
- package/dist/cli.js +1 -1
- package/dist/help.js +1 -1
- package/dist/index.js +2 -2
- package/dist/result.js +19 -2
- package/dist/scheduler.js +11 -1
- package/dist/strictInput.js +24 -3
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center"><img src="https://raw.githubusercontent.com/cmgmyr/hive/main/docs/assets/logo-card.svg" alt="hive" height="112"></p>
|
|
2
2
|
<p align="center"><a href="https://www.npmjs.com/package/@cmgmyr/hive"><img src="https://img.shields.io/npm/v/%40cmgmyr%2Fhive?label=npm" alt="npm"></a> <a href="https://github.com/cmgmyr/hive/actions/workflows/ci.yml"><img src="https://github.com/cmgmyr/hive/actions/workflows/ci.yml/badge.svg" alt="CI"></a></p>
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Run a crew of Claude Code and Codex workers from one lead session: visible tmux panes, exact state from hooks, one local SQLite store.
|
|
5
5
|
|
|
6
6
|
- Visible workers: each one is a real tmux pane you can read and type into.
|
|
7
7
|
- Exact state: workers report it through their own CLI's hooks, so nothing polls.
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.2.
|
|
1
|
+
{"version":"1.2.2","sha":"df20075","dirty":false,"build_id":"2f8ecccc-4d22-424a-8c24-c48551ed2441"}
|
package/dist/cli.js
CHANGED
|
@@ -41,7 +41,7 @@ import { checkoutRoot, createProfile, forkProfile, PROFILE_FILES, ProfileError,
|
|
|
41
41
|
import { COMMENT_COUNT_SQL, getTodoDetail, listTodoSummaries, OPEN_BLOCKERS_SQL, TODO_STATUSES, } from "./tools/todos.js";
|
|
42
42
|
import { createPad, getActivePadByName, listActivePads, overwritePadContent, } from "./tools/pads.js";
|
|
43
43
|
function usage() {
|
|
44
|
-
console.log(`hive —
|
|
44
|
+
console.log(`hive — run a crew of Claude Code and Codex workers from one lead session
|
|
45
45
|
|
|
46
46
|
Usage:
|
|
47
47
|
hive --version version, short sha, and dirty marker for this build
|
package/dist/help.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function helpOverview() {
|
|
2
|
-
return `HIVE —
|
|
2
|
+
return `HIVE — a lead session and a crew of Claude Code and Codex workers, sharing one local store
|
|
3
3
|
|
|
4
4
|
Many workers, one shared memory. Multiple Claude sessions (and humans) share
|
|
5
5
|
one project-scoped state store: pads, todos, kv, leases.
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { registerKv } from "./tools/kv.js";
|
|
|
10
10
|
import { registerLeases } from "./tools/leases.js";
|
|
11
11
|
import { registerWakes } from "./tools/wakes.js";
|
|
12
12
|
import { registerPrompts } from "./prompts.js";
|
|
13
|
-
import { startScheduler } from "./scheduler.js";
|
|
13
|
+
import { schedulerIntervalMs, startScheduler } from "./scheduler.js";
|
|
14
14
|
import { enforceStrictInput } from "./strictInput.js";
|
|
15
15
|
const server = enforceStrictInput(new McpServer({ name: "hive", version: "0.1.0" }, {
|
|
16
16
|
instructions: `Hive gives Claude Code sessions shared, persistent state for coordinating work on a project: pads for plans and findings, todos for tracked work with blockers, kv for small shared values, leases for claiming shared work areas, agents for spawning and directing other sessions, and wake-ups for scheduled or idle-triggered check-ins. Search for these tools when a task needs to persist past this conversation, hand off to or watch another session, or track multi-step work with dependencies.
|
|
@@ -30,5 +30,5 @@ registerKv(server);
|
|
|
30
30
|
registerLeases(server);
|
|
31
31
|
registerWakes(server);
|
|
32
32
|
registerPrompts(server);
|
|
33
|
-
startScheduler();
|
|
33
|
+
startScheduler(schedulerIntervalMs());
|
|
34
34
|
await server.connect(new StdioServerTransport());
|
package/dist/result.js
CHANGED
|
@@ -16,6 +16,23 @@ export function registrationNoticeText(notice) {
|
|
|
16
16
|
`project_select.`);
|
|
17
17
|
}
|
|
18
18
|
const reportedBuilds = new Set();
|
|
19
|
+
const pendingNotices = new WeakMap();
|
|
20
|
+
function addNotice(result, text, build) {
|
|
21
|
+
result.content.push({ type: "text", text });
|
|
22
|
+
const pending = pendingNotices.get(result) ?? { texts: [] };
|
|
23
|
+
pending.texts.push(text);
|
|
24
|
+
if (build)
|
|
25
|
+
pending.build = build;
|
|
26
|
+
pendingNotices.set(result, pending);
|
|
27
|
+
}
|
|
28
|
+
export function noticeFor(result) {
|
|
29
|
+
return pendingNotices.get(result)?.texts.join("\n");
|
|
30
|
+
}
|
|
31
|
+
export function releaseNotice(result) {
|
|
32
|
+
const build = pendingNotices.get(result)?.build;
|
|
33
|
+
if (build)
|
|
34
|
+
reportedBuilds.delete(build);
|
|
35
|
+
}
|
|
19
36
|
export async function appendRunningBuildNotice(result, session) {
|
|
20
37
|
try {
|
|
21
38
|
const change = runningBuildChange();
|
|
@@ -30,8 +47,8 @@ export async function appendRunningBuildNotice(result, session) {
|
|
|
30
47
|
}
|
|
31
48
|
if (reportedBuilds.has(change.disk.build_id))
|
|
32
49
|
return result;
|
|
33
|
-
result.content.push({ type: "text", text: runningBuildNotice(change, session) });
|
|
34
50
|
reportedBuilds.add(change.disk.build_id);
|
|
51
|
+
addNotice(result, runningBuildNotice(change, session), change.disk.build_id);
|
|
35
52
|
}
|
|
36
53
|
catch {
|
|
37
54
|
}
|
|
@@ -52,7 +69,7 @@ export async function run(fn) {
|
|
|
52
69
|
}
|
|
53
70
|
const notice = takeRegistrationNotice();
|
|
54
71
|
if (notice) {
|
|
55
|
-
result
|
|
72
|
+
addNotice(result, registrationNoticeText(notice));
|
|
56
73
|
}
|
|
57
74
|
return appendRunningBuildNotice(result);
|
|
58
75
|
}
|
package/dist/scheduler.js
CHANGED
|
@@ -43,7 +43,7 @@ function bestEffortRun(sql, ...params) {
|
|
|
43
43
|
}
|
|
44
44
|
let ticking = false;
|
|
45
45
|
let schedulerInterval;
|
|
46
|
-
export function startScheduler(intervalMs =
|
|
46
|
+
export function startScheduler(intervalMs = DEFAULT_SCHEDULER_INTERVAL_MS) {
|
|
47
47
|
schedulerInterval = setInterval(() => {
|
|
48
48
|
void tick();
|
|
49
49
|
}, intervalMs).unref();
|
|
@@ -1642,3 +1642,13 @@ async function deliver(timer, note, choices, typedSeen, firstHeldAt) {
|
|
|
1642
1642
|
if (!strandedTextWouldHold)
|
|
1643
1643
|
recordTyped();
|
|
1644
1644
|
}
|
|
1645
|
+
export const DEFAULT_SCHEDULER_INTERVAL_MS = 3000;
|
|
1646
|
+
const MIN_SCHEDULER_INTERVAL_MS = 100;
|
|
1647
|
+
// Node clamps a setInterval delay above this to 1ms.
|
|
1648
|
+
const MAX_SCHEDULER_INTERVAL_MS = 2_147_483_647;
|
|
1649
|
+
export function schedulerIntervalMs(raw = process.env.HIVE_SCHEDULER_INTERVAL_MS) {
|
|
1650
|
+
const parsed = Number(raw);
|
|
1651
|
+
return raw && Number.isInteger(parsed) && parsed >= MIN_SCHEDULER_INTERVAL_MS && parsed <= MAX_SCHEDULER_INTERVAL_MS
|
|
1652
|
+
? parsed
|
|
1653
|
+
: DEFAULT_SCHEDULER_INTERVAL_MS;
|
|
1654
|
+
}
|
package/dist/strictInput.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { noticeFor, releaseNotice } from "./result.js";
|
|
2
3
|
function isBuiltSchema(value) {
|
|
3
4
|
return typeof value === "object" && value !== null && ("_zod" in value || "_def" in value);
|
|
4
5
|
}
|
|
@@ -14,11 +15,15 @@ function strictSchemaFor(toolName, inputSchema) {
|
|
|
14
15
|
}
|
|
15
16
|
// The SDK requires structuredContent whenever outputSchema is declared but never populates it itself,
|
|
16
17
|
// so this stays scoped to declaring tools rather than attached blanket in src/result.ts's ok().
|
|
18
|
+
// Claude Code shows the model structuredContent and drops content for these tools, so a notice
|
|
19
|
+
// appended to content reaches it only as structuredContent.hive_notice.
|
|
17
20
|
function withStructuredContent(name, cb) {
|
|
18
21
|
return async (...args) => {
|
|
19
22
|
const result = (await cb(...args));
|
|
20
|
-
if (result.isError
|
|
23
|
+
if (result.isError)
|
|
21
24
|
return result;
|
|
25
|
+
if (result.structuredContent)
|
|
26
|
+
return withNotice(result);
|
|
22
27
|
const first = result.content[0];
|
|
23
28
|
const text = first?.type === "text" ? first.text : undefined;
|
|
24
29
|
let parsed;
|
|
@@ -30,8 +35,9 @@ function withStructuredContent(name, cb) {
|
|
|
30
35
|
}
|
|
31
36
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
32
37
|
result.structuredContent = parsed;
|
|
33
|
-
return result;
|
|
38
|
+
return withNotice(result);
|
|
34
39
|
}
|
|
40
|
+
releaseNotice(result);
|
|
35
41
|
return {
|
|
36
42
|
content: [
|
|
37
43
|
{
|
|
@@ -45,8 +51,23 @@ function withStructuredContent(name, cb) {
|
|
|
45
51
|
};
|
|
46
52
|
};
|
|
47
53
|
}
|
|
54
|
+
function withNotice(result) {
|
|
55
|
+
const notice = noticeFor(result);
|
|
56
|
+
if (notice && result.structuredContent)
|
|
57
|
+
result.structuredContent = { ...result.structuredContent, hive_notice: notice };
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
function withNoticeField(outputSchema) {
|
|
61
|
+
return outputSchema === undefined || isBuiltSchema(outputSchema)
|
|
62
|
+
? outputSchema
|
|
63
|
+
: { ...outputSchema, hive_notice: z.string().optional() };
|
|
64
|
+
}
|
|
48
65
|
export function enforceStrictInput(server) {
|
|
49
66
|
const register = server.registerTool.bind(server);
|
|
50
|
-
server.registerTool = ((name, config, cb) => register(name, {
|
|
67
|
+
server.registerTool = ((name, config, cb) => register(name, {
|
|
68
|
+
...config,
|
|
69
|
+
inputSchema: strictSchemaFor(name, config.inputSchema),
|
|
70
|
+
outputSchema: withNoticeField(config.outputSchema),
|
|
71
|
+
}, (config.outputSchema ? withStructuredContent(name, cb) : cb)));
|
|
51
72
|
return server;
|
|
52
73
|
}
|
package/dist/version.js
CHANGED
|
@@ -27,7 +27,7 @@ export function runningBuildChange() {
|
|
|
27
27
|
if (!loadedBuild)
|
|
28
28
|
return null;
|
|
29
29
|
const stat = statSync(join(moduleDir, "build-info.json"));
|
|
30
|
-
const signature = `${stat.ino}:${stat.size}:${stat.mtimeMs}`;
|
|
30
|
+
const signature = `${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
|
|
31
31
|
if (signature !== diskSignature) {
|
|
32
32
|
diskBuild = readRunningBuild();
|
|
33
33
|
diskSignature = signature;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmgmyr/hive",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "Run a crew of Claude Code and Codex workers from one lead session: visible tmux panes, exact state from hooks, one local SQLite store.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hive": "dist/cli.js",
|