@cmgmyr/hive 1.11.8 → 1.11.9
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/claude-plugin/README.md +1 -1
- package/dist/build-info.json +1 -1
- package/dist/claudeWake.js +10 -3
- package/dist/cli.js +6 -6
- package/dist/help.js +3 -1
- package/dist/leadHandoff.js +6 -3
- package/dist/leadHandoffCli.js +3 -3
- package/dist/leadMessageDelivery.js +2 -2
- package/dist/projectYml.js +13 -5
- package/dist/scheduler.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ hive is a personal daily-driver tool, released low-key. It is single-user by des
|
|
|
57
57
|
|
|
58
58
|
| Page | What's there |
|
|
59
59
|
|---|---|
|
|
60
|
-
| [Architecture](docs/architecture.md) |
|
|
60
|
+
| [Architecture](docs/architecture.md) | Eight diagrams: process topology, module layering, spawn sequence, wake lifecycle, worker state (with the lead handoff), project scoping, store and server identity |
|
|
61
61
|
| [Patterns](docs/patterns.md) | Standing trades, refused approaches, evidence standards, and guard shapes distilled from the project's own decisions and dead-ends |
|
|
62
62
|
| [Concepts](docs/concepts.md) | Vocabulary, why not subagents, identity, the workflow, project scope, the shared store |
|
|
63
63
|
| [Daily driver](docs/daily-driver.md) | A day with hive, starting a session, watching workers, wake-ups |
|
package/claude-plugin/README.md
CHANGED
|
@@ -51,4 +51,4 @@ path is ever written into a committed file.
|
|
|
51
51
|
|
|
52
52
|
## The crew mod
|
|
53
53
|
|
|
54
|
-
`crew/` is a second, separate plugin (`hive-crew`) that `hive lead` loads with `--plugin-dir`
|
|
54
|
+
`crew/` is a second, separate plugin (`hive-crew`) that `hive lead` loads with `--plugin-dir` for Claude leads by default. Set `lead_sidebar: false` to disable it. It is not part of the symlink install above and workers never load it. See [docs/crew.md](../docs/crew.md).
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.11.
|
|
1
|
+
{"version":"1.11.9","sha":"5d08b61","dirty":false,"build_id":"c6006fe410e3627175581a5b66e7d8fbb68b92f1b2edc49a00ce1c9256bacc30"}
|
package/dist/claudeWake.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createConnection } from "node:net";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { db } from "./db.js";
|
|
4
|
-
import {
|
|
3
|
+
import { dataDir, db } from "./db.js";
|
|
4
|
+
import { harnessFor } from "./harnesses.js";
|
|
5
|
+
import { loadProjectYml, quietMessagingEnabled } from "./projectYml.js";
|
|
6
|
+
import { shellQuote } from "./tmux.js";
|
|
5
7
|
export { DELIVERY_METHOD } from "./wakeSql.js";
|
|
6
8
|
export const CROSS_SESSION_CLOSE = "</cross-session-message>";
|
|
7
9
|
export const ENVELOPE_OPEN = '<cross-session-message from="';
|
|
@@ -23,7 +25,12 @@ function claudeWakeFrame(sender, text) {
|
|
|
23
25
|
}
|
|
24
26
|
export function quietMessagingOn(projectId) {
|
|
25
27
|
const project = db.prepare("SELECT path FROM projects WHERE id = ?").get(projectId);
|
|
26
|
-
return project !== undefined && loadProjectYml(project.path).config
|
|
28
|
+
return project !== undefined && quietMessagingEnabled(loadProjectYml(project.path).config);
|
|
29
|
+
}
|
|
30
|
+
export function quietMessagingHooksLaunched(projectId, command) {
|
|
31
|
+
const path = join(dataDir, `lead-${projectId}-hooks.json`);
|
|
32
|
+
const args = harnessFor("claude").briefDelivery?.settingsArgs(path);
|
|
33
|
+
return args !== undefined && command.includes(args.map(shellQuote).join(" "));
|
|
27
34
|
}
|
|
28
35
|
export function promptMarkerEvidenceSql(markerAt) {
|
|
29
36
|
const evPrompt = "(CASE WHEN json_valid(ev.payload) THEN COALESCE(json_extract(ev.payload, '$.prompt'), '') ELSE ev.payload END)";
|
package/dist/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ import { adoptableWindow, applyLayout, claimInitialWindow, configureHiveWindow,
|
|
|
38
38
|
import { collectCrew, crewStoreProblem } from "./crew.js";
|
|
39
39
|
import { describeVisibility, formatLocal, processCounts } from "./dashboard.js";
|
|
40
40
|
import { runningCommandRow, runningCommandRows, snapshotProcesses, stopAllProcesses, stopLine, stopProcess, renderStart, STOP_REASONS, } from "./processes.js";
|
|
41
|
-
import { activeProfile, agentVarKeys, BUILT_IN_PROJECT_YML, configHash, loadProjectYml, mergedProjectVars, NO_PROFILE, PROJECT_YML_KEYS, resolveCommandDir, resolveFirstMessage, } from "./projectYml.js";
|
|
41
|
+
import { activeProfile, agentVarKeys, BUILT_IN_PROJECT_YML, configHash, leadSidebarEnabled, loadProjectYml, mergedProjectVars, NO_PROFILE, PROJECT_YML_KEYS, quietMessagingEnabled, resolveCommandDir, resolveFirstMessage, } from "./projectYml.js";
|
|
42
42
|
import { appendClaudeLeadArgs, crewPluginDir, isTrusted, leadIdentityEnv, renderLeadPosture } from "./leadLaunch.js";
|
|
43
43
|
import { handoffStatusSegment } from "./leadHandoff.js";
|
|
44
44
|
import { cmdLeadHandoff, runHandoffGrace, runHandoffStart } from "./leadHandoffCli.js";
|
|
@@ -421,7 +421,7 @@ async function cmdLead(argv) {
|
|
|
421
421
|
for (const w of warnings)
|
|
422
422
|
console.log(`! ${w}`);
|
|
423
423
|
const session = sessionName();
|
|
424
|
-
const hooksPath = ensureLeadHooksFile(project.id, config
|
|
424
|
+
const hooksPath = ensureLeadHooksFile(project.id, quietMessagingEnabled(config));
|
|
425
425
|
let leadCommand = "claude";
|
|
426
426
|
if (!config) {
|
|
427
427
|
console.log("No hive.yml here; starting a plain claude lead. Add a hive.yml to define project commands and a custom lead (run hive --help for the format).");
|
|
@@ -464,7 +464,7 @@ async function cmdLead(argv) {
|
|
|
464
464
|
projectId: project.id,
|
|
465
465
|
projectName: project.name,
|
|
466
466
|
hooksPath,
|
|
467
|
-
sidebarPluginDir: config
|
|
467
|
+
sidebarPluginDir: leadSidebarEnabled(config) && leadHarness.name === "claude" ? crewPluginDir() : null,
|
|
468
468
|
posture: renderedPosture,
|
|
469
469
|
firstMessage,
|
|
470
470
|
}));
|
|
@@ -749,13 +749,13 @@ const HIVE_YML_TEMPLATE = `# hive project config. Read by \`hive lead\` from the
|
|
|
749
749
|
|
|
750
750
|
# context_checkpoint_percent: null # unset means off; integer 1-100 to enable
|
|
751
751
|
|
|
752
|
-
# lead_turn_budget: {warn: 300, stop: 600} # optional
|
|
752
|
+
# lead_turn_budget: {warn: 300, stop: 600} # optional thresholds; auto_handoff defaults on
|
|
753
753
|
|
|
754
754
|
# dashboard: true # write .hive/dashboard.html (default: false)
|
|
755
755
|
|
|
756
|
-
# lead_sidebar:
|
|
756
|
+
# lead_sidebar: false # disable the crew pane in a claude lead; restart the lead
|
|
757
757
|
|
|
758
|
-
# quiet_messaging:
|
|
758
|
+
# quiet_messaging: false # use typed wakes and worker messages instead of one row; restart the lead
|
|
759
759
|
|
|
760
760
|
# vars: # substituted into the profile runbook
|
|
761
761
|
# repo: owner/name # {{repo}}
|
package/dist/help.js
CHANGED
|
@@ -253,7 +253,9 @@ Delivery contract:
|
|
|
253
253
|
and the next action. Plain English, no markup.
|
|
254
254
|
Waking your OWN lead pane is the exception: it is not cold, so carry the
|
|
255
255
|
action and the ids and point at where the detail lives.
|
|
256
|
-
With lead_turn_budget
|
|
256
|
+
With a lead_turn_budget, auto_handoff is on by default. Set
|
|
257
|
+
lead_turn_budget.auto_handoff to false to keep the lead in this session.
|
|
258
|
+
When enabled, a lead past its warn budget gets
|
|
257
259
|
a "hive lead handoff request #N" wake: follow its steps, then run
|
|
258
260
|
hive lead-handoff. A human prompt during the grace postpones it.
|
|
259
261
|
|
package/dist/leadHandoff.js
CHANGED
|
@@ -11,8 +11,11 @@ import { readTurnCount } from "./turnCount.js";
|
|
|
11
11
|
export { casHandoff, HELD_REASON_HANDOFF, handoffHoldsWake, readActiveHandoff, readHandoff, readHandoffGate, SUCCESSOR_NOT_READY, } from "./leadHandoffState.js";
|
|
12
12
|
export const HANDOFF_PAD_NAME = "hive-lead-handoff";
|
|
13
13
|
export const sha256 = (text) => createHash("sha256").update(text).digest("hex");
|
|
14
|
+
function autoHandoffEnabled(budget) {
|
|
15
|
+
return budget !== null && (budget.auto_handoff ?? true);
|
|
16
|
+
}
|
|
14
17
|
export function handoffEligibility(lead, budget, snapshot = readLeadSafetySnapshot(lead.id)) {
|
|
15
|
-
if (budget === null || budget
|
|
18
|
+
if (budget === null || !autoHandoffEnabled(budget))
|
|
16
19
|
return { eligible: false, why: "auto_handoff is not enabled" };
|
|
17
20
|
if (harnessFor(lead.command).name !== "claude")
|
|
18
21
|
return { eligible: false, why: "the lead is not a Claude lead" };
|
|
@@ -274,7 +277,7 @@ export function driveLeadHandoff(actorId, budgetFor) {
|
|
|
274
277
|
settleAfterRespawn(row, lead);
|
|
275
278
|
return;
|
|
276
279
|
}
|
|
277
|
-
if (budget
|
|
280
|
+
if (!autoHandoffEnabled(budget)) {
|
|
278
281
|
failHandoff(row.id, "lead_turn_budget.auto_handoff is no longer on");
|
|
279
282
|
return;
|
|
280
283
|
}
|
|
@@ -364,7 +367,7 @@ export function padProblem(pad, projectId, revision) {
|
|
|
364
367
|
}
|
|
365
368
|
// The final quiet check. A screen or probe that cannot be read waits; it never establishes quiet.
|
|
366
369
|
export async function respawnVerdict(row, lead, budget) {
|
|
367
|
-
if (budget
|
|
370
|
+
if (!autoHandoffEnabled(budget))
|
|
368
371
|
return { fail: "lead_turn_budget.auto_handoff is no longer on" };
|
|
369
372
|
if (lead === undefined || lead.pane_pid !== row.predecessor_pane_pid || lead.tmux_target !== row.pane_target) {
|
|
370
373
|
return { fail: "the lead row no longer names the pane this handoff was requested for" };
|
package/dist/leadHandoffCli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { cliPath } from "./dispatcher.js";
|
|
|
7
7
|
import { FIRST_MESSAGE_SHA_ENV, firstMessageDigest } from "./firstMessage.js";
|
|
8
8
|
import { ensureLeadHooksFile } from "./hooks.js";
|
|
9
9
|
import { appendClaudeLeadArgs, crewPluginDir, isTrusted, leadIdentityEnv, renderLeadPosture } from "./leadLaunch.js";
|
|
10
|
-
import { loadProjectYml } from "./projectYml.js";
|
|
10
|
+
import { leadSidebarEnabled, loadProjectYml, quietMessagingEnabled } from "./projectYml.js";
|
|
11
11
|
import { buildEnvFlags } from "./spawn.js";
|
|
12
12
|
import { panePidForRecord, rowOwnership, shellQuote, tmux, TmuxTimeoutError } from "./tmux.js";
|
|
13
13
|
import { automationBlocker, failHandoff, missingHeadings, padProblem, readPad, RESPAWN_WAIT_LIMIT_SECONDS, respawnVerdict, runningLeadByActor, sha256, successorPrompt, } from "./leadHandoff.js";
|
|
@@ -152,8 +152,8 @@ async function successorLaunch(row, lead, attempt, token) {
|
|
|
152
152
|
harness,
|
|
153
153
|
projectId: lead.project_id,
|
|
154
154
|
projectName: lead.name,
|
|
155
|
-
hooksPath: ensureLeadHooksFile(lead.project_id, config
|
|
156
|
-
sidebarPluginDir: config
|
|
155
|
+
hooksPath: ensureLeadHooksFile(lead.project_id, quietMessagingEnabled(config)),
|
|
156
|
+
sidebarPluginDir: leadSidebarEnabled(config) ? crewPluginDir() : null,
|
|
157
157
|
posture: renderLeadPosture(config).rendered,
|
|
158
158
|
firstMessage,
|
|
159
159
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dataDir, db } from "./db.js";
|
|
2
|
-
import { CROSS_SESSION_CLOSE, promptMarkerEvidenceSql, postClaudeWake, quietMessagingOn, socketGraceDue, socketSenderAddress, SOCKET_MESSAGE_FOOTER, } from "./claudeWake.js";
|
|
2
|
+
import { CROSS_SESSION_CLOSE, promptMarkerEvidenceSql, quietMessagingHooksLaunched, postClaudeWake, quietMessagingOn, socketGraceDue, socketSenderAddress, SOCKET_MESSAGE_FOOTER, } from "./claudeWake.js";
|
|
3
3
|
import { harnessFor, paneClassifierFor } from "./harnesses.js";
|
|
4
4
|
import { LEAD_MESSAGE_THRESHOLD, MESSAGE_METHOD, MESSAGE_RETENTION, MESSAGE_STATUS, renderLeadMessage, senderTag, shortenedSendNote, storeLeadMessage, } from "./leadMessage.js";
|
|
5
5
|
import { readHandoffGate } from "./leadHandoffState.js";
|
|
@@ -30,7 +30,7 @@ function eligible(options) {
|
|
|
30
30
|
const sender = db
|
|
31
31
|
.prepare("SELECT 1 AS hit FROM agents WHERE project_id = ? AND actor_id = ? AND status = 'running' AND kind = 'agent'")
|
|
32
32
|
.get(projectId, fromActor);
|
|
33
|
-
return sender !== undefined && quietMessagingOn(projectId);
|
|
33
|
+
return sender !== undefined && quietMessagingOn(projectId) && quietMessagingHooksLaunched(projectId, target.command);
|
|
34
34
|
}
|
|
35
35
|
function registeredSocket(target) {
|
|
36
36
|
return (target.claude_messaging_socket !== "" &&
|
package/dist/projectYml.js
CHANGED
|
@@ -18,8 +18,8 @@ export const BUILT_IN_PROJECT_YML = Object.freeze({
|
|
|
18
18
|
context_checkpoint_percent: null,
|
|
19
19
|
lead_turn_budget: null,
|
|
20
20
|
dashboard: false,
|
|
21
|
-
lead_sidebar:
|
|
22
|
-
quiet_messaging:
|
|
21
|
+
lead_sidebar: true,
|
|
22
|
+
quiet_messaging: true,
|
|
23
23
|
vars: Object.freeze({}),
|
|
24
24
|
processes: Object.freeze({}),
|
|
25
25
|
});
|
|
@@ -33,6 +33,12 @@ export function activeProfile(config) {
|
|
|
33
33
|
const name = config?.profile;
|
|
34
34
|
return name == null || name === NO_PROFILE ? null : name;
|
|
35
35
|
}
|
|
36
|
+
export function quietMessagingEnabled(config) {
|
|
37
|
+
return config?.quiet_messaging ?? BUILT_IN_PROJECT_YML.quiet_messaging;
|
|
38
|
+
}
|
|
39
|
+
export function leadSidebarEnabled(config) {
|
|
40
|
+
return config?.lead_sidebar ?? BUILT_IN_PROJECT_YML.lead_sidebar;
|
|
41
|
+
}
|
|
36
42
|
// First entry is the default; absent or empty means claude only (todo 526).
|
|
37
43
|
export function allowedAgents(config) {
|
|
38
44
|
const list = config?.agents;
|
|
@@ -184,8 +190,10 @@ function applyLayer(root, path, source, config, sources, warnings) {
|
|
|
184
190
|
if (value.auto_handoff !== undefined) {
|
|
185
191
|
if (typeof value.auto_handoff === "boolean")
|
|
186
192
|
budget.auto_handoff = value.auto_handoff;
|
|
187
|
-
else
|
|
193
|
+
else {
|
|
194
|
+
budget.auto_handoff = false;
|
|
188
195
|
warn(`lead_turn_budget.auto_handoff must be true or false; ignoring "${String(value.auto_handoff)}", so automatic handoff stays off.`);
|
|
196
|
+
}
|
|
189
197
|
}
|
|
190
198
|
config.lead_turn_budget = budget;
|
|
191
199
|
sources.lead_turn_budget = source;
|
|
@@ -210,7 +218,7 @@ function applyLayer(root, path, source, config, sources, warnings) {
|
|
|
210
218
|
if (has("lead_sidebar")) {
|
|
211
219
|
const value = root.lead_sidebar;
|
|
212
220
|
if (value == null) {
|
|
213
|
-
config.lead_sidebar =
|
|
221
|
+
config.lead_sidebar = BUILT_IN_PROJECT_YML.lead_sidebar;
|
|
214
222
|
sources.lead_sidebar = source;
|
|
215
223
|
}
|
|
216
224
|
else if (typeof value === "boolean") {
|
|
@@ -223,7 +231,7 @@ function applyLayer(root, path, source, config, sources, warnings) {
|
|
|
223
231
|
if (has("quiet_messaging")) {
|
|
224
232
|
const value = root.quiet_messaging;
|
|
225
233
|
if (value == null) {
|
|
226
|
-
config.quiet_messaging =
|
|
234
|
+
config.quiet_messaging = BUILT_IN_PROJECT_YML.quiet_messaging;
|
|
227
235
|
sources.quiet_messaging = source;
|
|
228
236
|
}
|
|
229
237
|
else if (typeof value === "boolean") {
|
package/dist/scheduler.js
CHANGED
|
@@ -23,7 +23,7 @@ import { evaluateLeadWatch, leadSubscription } from "./leadWatch.js";
|
|
|
23
23
|
import { describeLiveTasks, describeOneTask, liveBackgroundTasks, STALL_BOUND_SECONDS, } from "./backgroundTasks.js";
|
|
24
24
|
import { MESSAGE_MAX_ROWS, MESSAGE_RETENTION } from "./leadMessage.js";
|
|
25
25
|
import { PANE_LEASE_SETTLE_MS, releasePaneLease, tryAcquirePaneLease } from "./leaseStore.js";
|
|
26
|
-
import { CROSS_SESSION_CLOSE, postClaudeWake, promptMarkerEvidenceSql, quietMessagingOn, socketGraceDue, socketSenderAddress, SOCKET_WAKE_FOOTER, } from "./claudeWake.js";
|
|
26
|
+
import { CROSS_SESSION_CLOSE, quietMessagingHooksLaunched, postClaudeWake, promptMarkerEvidenceSql, quietMessagingOn, socketGraceDue, socketSenderAddress, SOCKET_WAKE_FOOTER, } from "./claudeWake.js";
|
|
27
27
|
import { driveLeadHandoff } from "./leadHandoff.js";
|
|
28
28
|
import { handoffHoldsWake } from "./leadHandoffState.js";
|
|
29
29
|
import { FIRST_MESSAGE_LOG_STATE, humanPromptSql, readLeadTurnState } from "./leadState.js";
|
|
@@ -1949,7 +1949,8 @@ function socketRoute(timer, owner) {
|
|
|
1949
1949
|
timer.deliver_messaging_pane_pid !== "" &&
|
|
1950
1950
|
timer.deliver_messaging_pane_pid === timer.deliver_pane_pid &&
|
|
1951
1951
|
timer.deliver_row_target === timer.deliver_pane &&
|
|
1952
|
-
quietMessagingOn(timer.project_id)
|
|
1952
|
+
quietMessagingOn(timer.project_id) &&
|
|
1953
|
+
quietMessagingHooksLaunched(timer.project_id, timer.deliver_command));
|
|
1953
1954
|
}
|
|
1954
1955
|
function socketStillRegistered(timer, owner) {
|
|
1955
1956
|
requireStillOwned(owner.rowId, owner.identity, `wake #${timer.id} socket delivery`);
|
package/package.json
CHANGED