@shanesaravia/hive 0.1.0

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +21 -0
  3. package/README.md +417 -0
  4. package/dist/bin/hive-emit.js +75 -0
  5. package/dist/bin/hive.js +506 -0
  6. package/node_modules/@hive/shared/dist/index.d.ts +2 -0
  7. package/node_modules/@hive/shared/dist/index.js +2 -0
  8. package/node_modules/@hive/shared/dist/status.d.ts +12 -0
  9. package/node_modules/@hive/shared/dist/status.js +52 -0
  10. package/node_modules/@hive/shared/dist/types.d.ts +384 -0
  11. package/node_modules/@hive/shared/dist/types.js +14 -0
  12. package/node_modules/@hive/shared/package.json +18 -0
  13. package/package.json +72 -0
  14. package/packages/server/dist/api/rest.js +793 -0
  15. package/packages/server/dist/api/ws.js +37 -0
  16. package/packages/server/dist/config.js +24 -0
  17. package/packages/server/dist/control/codexRuntime.js +169 -0
  18. package/packages/server/dist/control/killer.js +25 -0
  19. package/packages/server/dist/control/launcher.js +114 -0
  20. package/packages/server/dist/control/messaging.js +75 -0
  21. package/packages/server/dist/control/nativeCommands.js +29 -0
  22. package/packages/server/dist/control/permissionPark.js +23 -0
  23. package/packages/server/dist/control/providerModels.js +53 -0
  24. package/packages/server/dist/events/eventsStore.js +55 -0
  25. package/packages/server/dist/health/deriveAlerts.js +55 -0
  26. package/packages/server/dist/hooks/hookIngest.js +90 -0
  27. package/packages/server/dist/hooks/hookSpool.js +33 -0
  28. package/packages/server/dist/hooks/setupHooks.js +102 -0
  29. package/packages/server/dist/index.js +88 -0
  30. package/packages/server/dist/messages/messagesStore.js +211 -0
  31. package/packages/server/dist/missions/missionsStore.js +283 -0
  32. package/packages/server/dist/paths/pathResolver.js +167 -0
  33. package/packages/server/dist/plans/plansStore.js +212 -0
  34. package/packages/server/dist/policies/policiesStore.js +61 -0
  35. package/packages/server/dist/reports/githubPublisher.js +21 -0
  36. package/packages/server/dist/reports/missionReport.js +16 -0
  37. package/packages/server/dist/roster/rosterBuilder.js +243 -0
  38. package/packages/server/dist/security/originPolicy.js +31 -0
  39. package/packages/server/dist/skills/skillDiscovery.js +69 -0
  40. package/packages/server/dist/templates/templateDiscovery.js +97 -0
  41. package/packages/server/dist/watch/jobsWatcher.js +224 -0
  42. package/packages/server/dist/watch/sessionsWatcher.js +65 -0
  43. package/packages/web/dist/assets/index-CrKMFCkZ.js +11 -0
  44. package/packages/web/dist/assets/index-gEGU_lr3.css +2 -0
  45. package/packages/web/dist/favicon.svg +12 -0
  46. package/packages/web/dist/index.html +14 -0
  47. package/templates/agents/hive-orchestrator.md +42 -0
@@ -0,0 +1,384 @@
1
+ export type SessionStatus = "idle" | "waiting" | "busy";
2
+ /** One entry per live `claude` CLI process, read from ~/.claude/sessions/<pid>.json */
3
+ export interface ClaudeSession {
4
+ pid: number;
5
+ sessionId: string;
6
+ cwd: string;
7
+ startedAt: number;
8
+ procStart?: string;
9
+ version?: string;
10
+ peerProtocol?: number;
11
+ kind?: string;
12
+ entrypoint?: string;
13
+ messagingSocketPath?: string;
14
+ name?: string;
15
+ agent?: string;
16
+ jobId?: string;
17
+ status: SessionStatus;
18
+ updatedAt: number;
19
+ statusUpdatedAt?: number;
20
+ waitingFor?: string;
21
+ }
22
+ export interface JobFanEntry {
23
+ id: string;
24
+ kind: string;
25
+ label: string;
26
+ startedAt: number;
27
+ doneAt?: number;
28
+ /** Optional telemetry exposed by newer provider/job records. */
29
+ updatedAt?: number;
30
+ tokens?: number;
31
+ terminationReason?: string;
32
+ }
33
+ export interface JobChild {
34
+ id: string;
35
+ href: string;
36
+ kind: string;
37
+ }
38
+ /** One entry per background job, read from ~/.claude/jobs/<jobId>/state.json */
39
+ export interface ClaudeJob {
40
+ state: string;
41
+ detail?: string;
42
+ tempo?: string;
43
+ inFlight?: {
44
+ tasks: number;
45
+ queued: number;
46
+ kinds: string[];
47
+ };
48
+ fan: JobFanEntry[];
49
+ tokens?: number;
50
+ needs?: string;
51
+ output?: unknown;
52
+ /**
53
+ * Hive-augmented (not part of the daemon's state.json): the latest assistant
54
+ * text from timeline.jsonl. Blocked turns keep only a terse question in
55
+ * needs/detail — the full conversational reply lives in the timeline.
56
+ */
57
+ lastText?: string;
58
+ children: JobChild[];
59
+ intent?: string;
60
+ name?: string;
61
+ nameSource?: string;
62
+ sessionId?: string;
63
+ resumeSessionId?: string;
64
+ daemonShort?: string;
65
+ cliVersion?: string;
66
+ cwd?: string;
67
+ createdAt?: string;
68
+ updatedAt?: string;
69
+ worktreePath?: string;
70
+ worktreeBranch?: string;
71
+ originCwd?: string;
72
+ backend?: string;
73
+ template?: string;
74
+ }
75
+ export interface MissionMessage {
76
+ id: string;
77
+ role: "user" | "assistant";
78
+ text: string;
79
+ createdAt: number;
80
+ jobId: string;
81
+ }
82
+ /** @deprecated Use MissionMessage. */
83
+ export type ThreadMessage = MissionMessage;
84
+ export interface WorkerActivity extends JobFanEntry {
85
+ jobId: string;
86
+ jobState: string;
87
+ jobUpdatedAt?: number;
88
+ }
89
+ export interface MissionSummary {
90
+ id: string;
91
+ objective: string;
92
+ acceptanceCriteria: string[];
93
+ repository?: string;
94
+ compactSummary?: string;
95
+ currentState?: string;
96
+ lifecycleStatus: MissionLifecycleStatus;
97
+ mode: MissionMode;
98
+ budgets?: MissionBudgets;
99
+ template?: {
100
+ id: string;
101
+ name: string;
102
+ version: number;
103
+ source: "built-in" | "user" | "repository";
104
+ };
105
+ policy?: MissionPolicy;
106
+ provider?: MissionProvider;
107
+ model?: string;
108
+ /** Repositories beyond the working directory this mission also touches; each grants --add-dir access on every turn. */
109
+ additionalRepositories?: string[];
110
+ }
111
+ export type MissionProvider = "claude" | "codex";
112
+ export interface MissionPolicy {
113
+ allowedRoots: string[];
114
+ allowedTools: string[];
115
+ networkAccess: boolean;
116
+ commits: boolean;
117
+ pushes: boolean;
118
+ pullRequests: boolean;
119
+ releases: boolean;
120
+ destructiveActions: boolean;
121
+ }
122
+ export interface MissionBudgets {
123
+ maxWorkers?: number;
124
+ maxConcurrentWorkers?: number;
125
+ maxTokens?: number;
126
+ maxElapsedMinutes?: number;
127
+ }
128
+ export interface MissionTemplate {
129
+ id: string;
130
+ name: string;
131
+ description: string;
132
+ version: number;
133
+ source: "built-in" | "user" | "repository";
134
+ mode: MissionMode;
135
+ worktree: boolean;
136
+ managerInstructions: string;
137
+ stages: Array<{
138
+ id: string;
139
+ title: string;
140
+ role: WorkerRole;
141
+ dependsOn?: string[];
142
+ }>;
143
+ gates: CompletionGate[];
144
+ budgets?: MissionBudgets;
145
+ permissions: {
146
+ commits: boolean;
147
+ pushes: boolean;
148
+ pullRequests: boolean;
149
+ releases: boolean;
150
+ destructiveActions: boolean;
151
+ network: boolean;
152
+ };
153
+ }
154
+ export type MissionMode = "orchestrated" | "direct";
155
+ export type MissionControlAction = "status_summary" | "plan_revision" | "review" | "test_pass";
156
+ export type TaskControlAction = "retry" | "cancel" | "replace_worker" | "reassign" | "priority";
157
+ export interface ClaudeSkillSummary {
158
+ name: string;
159
+ description?: string;
160
+ argumentHint?: string;
161
+ source: "project" | "user" | "plugin" | "built-in" | "claude";
162
+ }
163
+ export interface NativeCommand {
164
+ name: string;
165
+ description: string;
166
+ /** account: runs standalone; session: resumes the mission's session (forked) to inspect it. */
167
+ scope: "account" | "session";
168
+ }
169
+ /**
170
+ * Native Claude CLI slash commands Hive supports mid-conversation. They hang
171
+ * when sent as a background-session prompt (they open interactive dialogs),
172
+ * so the server intercepts them and runs `claude -p` out-of-band instead.
173
+ * Only commands verified to print a text report in headless mode belong here.
174
+ */
175
+ export declare const NATIVE_COMMANDS: NativeCommand[];
176
+ export type MissionLifecycleStatus = "active" | "ready_for_review" | "paused" | "completed" | "failed" | "archived";
177
+ export interface MissionMessagesPage {
178
+ messages: MissionMessage[];
179
+ hasMore: boolean;
180
+ nextBefore?: number;
181
+ }
182
+ export type PlanItemStatus = "queued" | "working" | "blocked" | "reviewing" | "completed" | "failed" | "cancelled";
183
+ export type WorkerRole = "investigation" | "implementation" | "testing" | "review" | "browser_qa" | "documentation" | "custom";
184
+ export interface MissionPlanTask {
185
+ id: string;
186
+ title: string;
187
+ description?: string;
188
+ assignment?: string;
189
+ context?: string;
190
+ role?: WorkerRole;
191
+ blocker?: string;
192
+ unblock?: string;
193
+ status: PlanItemStatus;
194
+ weight: number;
195
+ dependsOn: string[];
196
+ workerId?: string;
197
+ owner?: string;
198
+ startedAt?: number;
199
+ updatedAt?: number;
200
+ branch?: string;
201
+ worktree?: string;
202
+ changedFiles?: string[];
203
+ testState?: "not_run" | "running" | "passed" | "failed";
204
+ subtasks?: MissionPlanTask[];
205
+ evidence: string[];
206
+ }
207
+ export interface MissionPlanPhase {
208
+ id: string;
209
+ title: string;
210
+ description?: string;
211
+ milestone?: string;
212
+ status: PlanItemStatus;
213
+ acceptanceCriteria: string[];
214
+ dependsOn: string[];
215
+ tasks: MissionPlanTask[];
216
+ }
217
+ export interface MissionPlanProgress {
218
+ completedWeight: number;
219
+ totalWeight: number;
220
+ percent?: number;
221
+ completedTasks: number;
222
+ totalTasks: number;
223
+ }
224
+ export type CompletionGateType = "test" | "typecheck" | "lint" | "ci" | "review" | "pr" | "screenshot" | "documentation" | "user_approval" | "custom";
225
+ export type CompletionGateStatus = "pending" | "satisfied" | "failed" | "waived";
226
+ export interface CompletionGate {
227
+ id: string;
228
+ label: string;
229
+ type: CompletionGateType;
230
+ status: CompletionGateStatus;
231
+ required: boolean;
232
+ evidence: string[];
233
+ waiver?: {
234
+ reason: string;
235
+ waivedAt: number;
236
+ };
237
+ }
238
+ export interface MissionPlan {
239
+ missionId: string;
240
+ revision: number;
241
+ updatedAt: number;
242
+ phases: MissionPlanPhase[];
243
+ gates: CompletionGate[];
244
+ layout: "flat" | "phased";
245
+ approvalStatus: "proposed" | "approved" | "rejected";
246
+ approvalReason?: string;
247
+ progress: MissionPlanProgress;
248
+ }
249
+ export type DerivedStatus = "working" | "idle" | "stalled" | "offline" | "waiting_on_you" | "error" | "done";
250
+ export type HiveEventSource = "hook" | "custom";
251
+ export type HiveActivityKind = "file_read" | "file_write" | "command" | "test" | "agent" | "decision" | "output" | "tool" | "lifecycle";
252
+ export type DecisionKind = "product" | "permission" | "destructive" | "merge" | "scope" | "question";
253
+ export interface MissionDecision {
254
+ id: string;
255
+ missionId: string;
256
+ missionName: string;
257
+ kind: DecisionKind;
258
+ question: string;
259
+ context?: string;
260
+ choices: string[];
261
+ recommendation?: string;
262
+ impact?: string;
263
+ /** Present for historical/resolved decisions; pending inbox items omit it. */
264
+ answer?: string;
265
+ /** Mission context so an inbox answer can be given knowing what it's for. */
266
+ missionObjective?: string;
267
+ repository?: string;
268
+ createdAt: number;
269
+ }
270
+ export interface MissionAlert {
271
+ id: string;
272
+ severity: "info" | "warning" | "critical";
273
+ title: string;
274
+ explanation: string;
275
+ evidence: string;
276
+ recovery: string;
277
+ workerId?: string;
278
+ }
279
+ /** Merged timeline entry — either a raw Claude Code hook firing or a Hive custom lifecycle event. */
280
+ export interface HiveEvent {
281
+ ts: number;
282
+ sessionId: string;
283
+ jobId?: string;
284
+ source: HiveEventSource;
285
+ /** For source: 'hook' — the raw hook event name (PreToolUse, PostToolUse, Stop, ...). */
286
+ hookEventName?: string;
287
+ toolName?: string;
288
+ activityKind?: HiveActivityKind;
289
+ filePath?: string;
290
+ command?: string;
291
+ artifactPath?: string;
292
+ outcome?: "success" | "failure";
293
+ /** For source: 'custom' — one of the orchestrator lifecycle phases. */
294
+ phase?: "delegating" | "worker_started" | "worker_reported" | "reviewing" | "blocked_on_user" | "ready_for_review" | "plan_updated" | "decision_resolved" | "resuming" | "custom";
295
+ detail: string;
296
+ targetWorker?: string;
297
+ targetTask?: string;
298
+ decisionId?: string;
299
+ decisionKind?: DecisionKind;
300
+ choices?: string[];
301
+ recommendation?: string;
302
+ impact?: string;
303
+ context?: string;
304
+ /** Bounded, recursively sanitized source payload for opt-in debugging. */
305
+ rawPayload?: unknown;
306
+ }
307
+ export interface OrchestratorNode {
308
+ /** Stable Hive identity, independent of Claude job and process IDs. */
309
+ missionId: string;
310
+ /** @deprecated Compatibility alias for clients predating the Mission model. */
311
+ threadId: string;
312
+ mission: MissionSummary;
313
+ jobId: string;
314
+ jobIds: string[];
315
+ jobHistory: Array<{
316
+ jobId: string;
317
+ sessionId?: string;
318
+ resumedFrom?: string;
319
+ state: string;
320
+ createdAt?: string;
321
+ updatedAt?: string;
322
+ tokens: number;
323
+ }>;
324
+ sessionId: string;
325
+ pid: number;
326
+ name: string;
327
+ status: DerivedStatus;
328
+ /** Durable product state, independent of any Claude process or job. */
329
+ lifecycleStatus: MissionLifecycleStatus;
330
+ /** Current observed runtime state. `status` is retained as a compatibility alias. */
331
+ activityStatus: DerivedStatus;
332
+ /** Latest Claude turn exited normally; the durable mission may still remain active. */
333
+ turnCompleted: boolean;
334
+ /** Milliseconds since the last meaningful runtime update. */
335
+ inactiveForMs: number;
336
+ waitingFor?: string;
337
+ worktreePath?: string;
338
+ worktreeBranch?: string;
339
+ tokens: number;
340
+ workers: WorkerActivity[];
341
+ messages: MissionMessage[];
342
+ hasMoreMessages: boolean;
343
+ detail?: string;
344
+ intent?: string;
345
+ inFlight: {
346
+ tasks: number;
347
+ queued: number;
348
+ kinds: string[];
349
+ };
350
+ recentEvents: HiveEvent[];
351
+ alerts: MissionAlert[];
352
+ plan?: MissionPlan;
353
+ prs: JobChild[];
354
+ stale: boolean;
355
+ /** epoch ms — used to sort newest-first in the fleet view */
356
+ createdAt: number;
357
+ /** Start of the latest Claude job/turn in this mission. */
358
+ runStartedAt: number;
359
+ updatedAt: number;
360
+ }
361
+ export interface SessionNode {
362
+ jobId?: string;
363
+ sessionId: string;
364
+ pid: number;
365
+ name: string;
366
+ status: DerivedStatus;
367
+ waitingFor?: string;
368
+ cwd: string;
369
+ }
370
+ export interface FleetSnapshot {
371
+ orchestrators: OrchestratorNode[];
372
+ decisions: MissionDecision[];
373
+ other: SessionNode[];
374
+ generatedAt: number;
375
+ }
376
+ /** Payload accepted by POST /events (emitted by bin/hive-emit.ts). */
377
+ export interface HiveEmitPayload {
378
+ sessionId: string;
379
+ jobId?: string;
380
+ phase: NonNullable<HiveEvent["phase"]>;
381
+ detail: string;
382
+ targetWorker?: string;
383
+ targetTask?: string;
384
+ }
@@ -0,0 +1,14 @@
1
+ // Shapes below are derived from live inspection of real ~/.claude/sessions/<pid>.json
2
+ // and ~/.claude/jobs/<jobId>/state.json files on 2026-08-19/20. Fields marked optional
3
+ // were absent on at least one real record; treat unknown fields defensively.
4
+ /**
5
+ * Native Claude CLI slash commands Hive supports mid-conversation. They hang
6
+ * when sent as a background-session prompt (they open interactive dialogs),
7
+ * so the server intercepts them and runs `claude -p` out-of-band instead.
8
+ * Only commands verified to print a text report in headless mode belong here.
9
+ */
10
+ export const NATIVE_COMMANDS = [
11
+ { name: "usage", description: "Show subscription usage limits and what is consuming them", scope: "account" },
12
+ { name: "cost", description: "Show the cost and duration breakdown for Claude usage", scope: "account" },
13
+ { name: "context", description: "Show this mission session's context window usage", scope: "session" },
14
+ ];
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@hive/shared",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "src/index.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc -p tsconfig.json",
13
+ "typecheck": "tsc --noEmit"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^5.6.0"
17
+ }
18
+ }
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@shanesaravia/hive",
3
+ "private": false,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "description": "Provider-neutral local mission control for Claude Code, Codex, and agent fleets.",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/shanesaravia/hive.git"
11
+ },
12
+ "homepage": "https://github.com/shanesaravia/hive#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/shanesaravia/hive/issues"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "workspaces": [
20
+ "packages/*"
21
+ ],
22
+ "bin": {
23
+ "hive": "./dist/bin/hive.js",
24
+ "hive-emit": "./dist/bin/hive-emit.js"
25
+ },
26
+ "files": [
27
+ "dist/bin/*.js",
28
+ "packages/server/dist/**/*.js",
29
+ "packages/web/dist/**",
30
+ "templates/agents/*.md",
31
+ "README.md",
32
+ "CHANGELOG.md",
33
+ "LICENSE"
34
+ ],
35
+ "bundledDependencies": [
36
+ "@hive/shared"
37
+ ],
38
+ "scripts": {
39
+ "predev": "npm run build --workspace=@hive/shared",
40
+ "dev": "npm run dev --workspace=packages/server & npm run dev --workspace=packages/web",
41
+ "prebuild": "node scripts/clean-build.mjs",
42
+ "build": "npm run build --workspaces --if-present",
43
+ "postbuild": "npm run build:cli",
44
+ "build:cli": "tsc -p tsconfig.bin.json",
45
+ "package:check": "node scripts/check-package.mjs",
46
+ "smoke:package": "node scripts/smoke-package.mjs",
47
+ "prepack": "npm run build && npm run package:check",
48
+ "start": "node packages/server/dist/index.js",
49
+ "pretest": "npm run build --workspace=@hive/shared",
50
+ "test": "npm run test --workspaces --if-present",
51
+ "posttest": "npm run test:cli",
52
+ "test:cli": "tsx --test bin/*.test.ts",
53
+ "typecheck": "npm run typecheck --workspaces --if-present && tsc -p tsconfig.bin.json --noEmit",
54
+ "hive": "tsx bin/hive.ts"
55
+ },
56
+ "dependencies": {
57
+ "@fastify/cors": "^10.0.0",
58
+ "@fastify/static": "^10.1.3",
59
+ "@fastify/websocket": "^11.0.0",
60
+ "@hive/shared": "*",
61
+ "better-sqlite3": "^12.11.1",
62
+ "fastify": "^5.0.0"
63
+ },
64
+ "devDependencies": {
65
+ "typescript": "^5.6.0",
66
+ "tsx": "^4.19.0",
67
+ "@types/node": "^22.0.0"
68
+ },
69
+ "engines": {
70
+ "node": ">=20"
71
+ }
72
+ }