hungry-ghost-hive 0.43.0 → 0.43.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.
Files changed (133) hide show
  1. package/dist/cli/commands/agents.d.ts.map +1 -1
  2. package/dist/cli/commands/agents.js +4 -11
  3. package/dist/cli/commands/agents.js.map +1 -1
  4. package/dist/cli/commands/approach.d.ts.map +1 -1
  5. package/dist/cli/commands/approach.js +2 -6
  6. package/dist/cli/commands/approach.js.map +1 -1
  7. package/dist/cli/commands/init.d.ts.map +1 -1
  8. package/dist/cli/commands/init.js +9 -0
  9. package/dist/cli/commands/init.js.map +1 -1
  10. package/dist/cli/commands/init.test.js +3 -0
  11. package/dist/cli/commands/init.test.js.map +1 -1
  12. package/dist/cli/commands/manager/index.d.ts +2 -27
  13. package/dist/cli/commands/manager/index.d.ts.map +1 -1
  14. package/dist/cli/commands/manager/index.js +23 -1519
  15. package/dist/cli/commands/manager/index.js.map +1 -1
  16. package/dist/cli/commands/manager/manager-utils.d.ts +9 -0
  17. package/dist/cli/commands/manager/manager-utils.d.ts.map +1 -0
  18. package/dist/cli/commands/manager/manager-utils.js +49 -0
  19. package/dist/cli/commands/manager/manager-utils.js.map +1 -0
  20. package/dist/cli/commands/manager/pr-sync-orchestrator.d.ts +7 -0
  21. package/dist/cli/commands/manager/pr-sync-orchestrator.d.ts.map +1 -0
  22. package/dist/cli/commands/manager/pr-sync-orchestrator.js +537 -0
  23. package/dist/cli/commands/manager/pr-sync-orchestrator.js.map +1 -0
  24. package/dist/cli/commands/manager/qa-review-handler.d.ts +15 -0
  25. package/dist/cli/commands/manager/qa-review-handler.d.ts.map +1 -0
  26. package/dist/cli/commands/manager/qa-review-handler.js +290 -0
  27. package/dist/cli/commands/manager/qa-review-handler.js.map +1 -0
  28. package/dist/cli/commands/manager/stuck-story-helpers.d.ts +32 -0
  29. package/dist/cli/commands/manager/stuck-story-helpers.d.ts.map +1 -0
  30. package/dist/cli/commands/manager/stuck-story-helpers.js +163 -0
  31. package/dist/cli/commands/manager/stuck-story-helpers.js.map +1 -0
  32. package/dist/cli/commands/manager/stuck-story-processor.d.ts +8 -0
  33. package/dist/cli/commands/manager/stuck-story-processor.d.ts.map +1 -0
  34. package/dist/cli/commands/manager/stuck-story-processor.js +392 -0
  35. package/dist/cli/commands/manager/stuck-story-processor.js.map +1 -0
  36. package/dist/cli/commands/manager/tech-lead-lifecycle.d.ts +3 -0
  37. package/dist/cli/commands/manager/tech-lead-lifecycle.d.ts.map +1 -0
  38. package/dist/cli/commands/manager/tech-lead-lifecycle.js +141 -0
  39. package/dist/cli/commands/manager/tech-lead-lifecycle.js.map +1 -0
  40. package/dist/cli/commands/my-stories.d.ts.map +1 -1
  41. package/dist/cli/commands/my-stories.js +5 -20
  42. package/dist/cli/commands/my-stories.js.map +1 -1
  43. package/dist/cli/commands/pr.js +7 -22
  44. package/dist/cli/commands/pr.js.map +1 -1
  45. package/dist/cli/commands/progress.d.ts.map +1 -1
  46. package/dist/cli/commands/progress.js +2 -5
  47. package/dist/cli/commands/progress.js.map +1 -1
  48. package/dist/cli/commands/resume.d.ts.map +1 -1
  49. package/dist/cli/commands/resume.js +3 -6
  50. package/dist/cli/commands/resume.js.map +1 -1
  51. package/dist/cli/commands/status.d.ts.map +1 -1
  52. package/dist/cli/commands/status.js +2 -5
  53. package/dist/cli/commands/status.js.map +1 -1
  54. package/dist/cli/commands/stories.d.ts.map +1 -1
  55. package/dist/cli/commands/stories.js +2 -5
  56. package/dist/cli/commands/stories.js.map +1 -1
  57. package/dist/cluster/adapters.d.ts +3 -2
  58. package/dist/cluster/adapters.d.ts.map +1 -1
  59. package/dist/cluster/adapters.js +2 -11
  60. package/dist/cluster/adapters.js.map +1 -1
  61. package/dist/cluster/cluster-http-server.d.ts +20 -0
  62. package/dist/cluster/cluster-http-server.d.ts.map +1 -0
  63. package/dist/cluster/cluster-http-server.js +140 -0
  64. package/dist/cluster/cluster-http-server.js.map +1 -0
  65. package/dist/cluster/heartbeat-manager.d.ts +24 -0
  66. package/dist/cluster/heartbeat-manager.d.ts.map +1 -0
  67. package/dist/cluster/heartbeat-manager.js +74 -0
  68. package/dist/cluster/heartbeat-manager.js.map +1 -0
  69. package/dist/cluster/raft-state-machine.d.ts +48 -0
  70. package/dist/cluster/raft-state-machine.d.ts.map +1 -0
  71. package/dist/cluster/raft-state-machine.js +207 -0
  72. package/dist/cluster/raft-state-machine.js.map +1 -0
  73. package/dist/cluster/runtime.d.ts +5 -29
  74. package/dist/cluster/runtime.d.ts.map +1 -1
  75. package/dist/cluster/runtime.js +58 -406
  76. package/dist/cluster/runtime.js.map +1 -1
  77. package/dist/integrations/jira/sync.d.ts +2 -5
  78. package/dist/integrations/jira/sync.d.ts.map +1 -1
  79. package/dist/integrations/jira/sync.js +116 -178
  80. package/dist/integrations/jira/sync.js.map +1 -1
  81. package/dist/utils/cli-helpers.d.ts +19 -0
  82. package/dist/utils/cli-helpers.d.ts.map +1 -0
  83. package/dist/utils/cli-helpers.js +51 -0
  84. package/dist/utils/cli-helpers.js.map +1 -0
  85. package/dist/utils/cli-helpers.test.d.ts +2 -0
  86. package/dist/utils/cli-helpers.test.d.ts.map +1 -0
  87. package/dist/utils/cli-helpers.test.js +100 -0
  88. package/dist/utils/cli-helpers.test.js.map +1 -0
  89. package/dist/utils/github-cli.d.ts +3 -0
  90. package/dist/utils/github-cli.d.ts.map +1 -0
  91. package/dist/utils/github-cli.js +4 -0
  92. package/dist/utils/github-cli.js.map +1 -0
  93. package/dist/utils/pr-sync.d.ts.map +1 -1
  94. package/dist/utils/pr-sync.js +1 -2
  95. package/dist/utils/pr-sync.js.map +1 -1
  96. package/dist/utils/story-status.d.ts +19 -0
  97. package/dist/utils/story-status.d.ts.map +1 -0
  98. package/dist/utils/story-status.js +58 -0
  99. package/dist/utils/story-status.js.map +1 -0
  100. package/dist/utils/story-status.test.d.ts +2 -0
  101. package/dist/utils/story-status.test.d.ts.map +1 -0
  102. package/dist/utils/story-status.test.js +65 -0
  103. package/dist/utils/story-status.test.js.map +1 -0
  104. package/package.json +1 -1
  105. package/src/cli/commands/agents.ts +3 -11
  106. package/src/cli/commands/approach.ts +2 -7
  107. package/src/cli/commands/init.test.ts +4 -0
  108. package/src/cli/commands/init.ts +9 -0
  109. package/src/cli/commands/manager/index.ts +166 -2236
  110. package/src/cli/commands/manager/manager-utils.ts +85 -0
  111. package/src/cli/commands/manager/pr-sync-orchestrator.ts +659 -0
  112. package/src/cli/commands/manager/qa-review-handler.ts +399 -0
  113. package/src/cli/commands/manager/stuck-story-helpers.ts +255 -0
  114. package/src/cli/commands/manager/stuck-story-processor.ts +604 -0
  115. package/src/cli/commands/manager/tech-lead-lifecycle.ts +210 -0
  116. package/src/cli/commands/my-stories.ts +5 -30
  117. package/src/cli/commands/pr.ts +6 -22
  118. package/src/cli/commands/progress.ts +2 -7
  119. package/src/cli/commands/resume.ts +3 -6
  120. package/src/cli/commands/status.ts +2 -5
  121. package/src/cli/commands/stories.ts +2 -5
  122. package/src/cluster/adapters.ts +3 -12
  123. package/src/cluster/cluster-http-server.ts +187 -0
  124. package/src/cluster/heartbeat-manager.ts +112 -0
  125. package/src/cluster/raft-state-machine.ts +267 -0
  126. package/src/cluster/runtime.ts +71 -515
  127. package/src/integrations/jira/sync.ts +157 -215
  128. package/src/utils/cli-helpers.test.ts +138 -0
  129. package/src/utils/cli-helpers.ts +61 -0
  130. package/src/utils/github-cli.ts +4 -0
  131. package/src/utils/pr-sync.ts +1 -3
  132. package/src/utils/story-status.test.ts +74 -0
  133. package/src/utils/story-status.ts +62 -0
@@ -0,0 +1,392 @@
1
+ // Licensed under the Hungry Ghost Hive License. See LICENSE.
2
+ import chalk from 'chalk';
3
+ import { execa } from 'execa';
4
+ import { join } from 'path';
5
+ import { syncStatusForStory } from '../../../connectors/project-management/operations.js';
6
+ import { queryAll, withTransaction } from '../../../db/client.js';
7
+ import { getAgentById } from '../../../db/queries/agents.js';
8
+ import { createLog } from '../../../db/queries/logs.js';
9
+ import { createPullRequest, getOpenPullRequestsByStory, } from '../../../db/queries/pull-requests.js';
10
+ import { getStoriesByStatus, updateStory } from '../../../db/queries/stories.js';
11
+ import { AgentState } from '../../../state-detectors/types.js';
12
+ import { captureTmuxPane } from '../../../tmux/manager.js';
13
+ import { agentStates, detectAgentState } from './agent-monitoring.js';
14
+ import { assessCompletionFromOutput } from './done-intelligence.js';
15
+ import { getMaxStuckNudgesPerStory, getScreenStaticInactivityThresholdMs, sendManagerNudge, verboseLogCtx, } from './manager-utils.js';
16
+ import { findSessionForAgent } from './session-resolution.js';
17
+ import { clearHumanIntervention, getSessionStaticUnchangedForMs, isClassifierTimeoutReason, markClassifierTimeoutForHumanIntervention, markDoneFalseForHumanIntervention, shouldDeferStuckReminderUntilStaticWindow, shouldIncludeProgressUpdates, shouldTreatUnknownAsStuckWaiting, } from './stuck-story-helpers.js';
18
+ import { TMUX_CAPTURE_LINES_SHORT } from './types.js';
19
+ const DONE_INFERENCE_CONFIDENCE_THRESHOLD = 0.82;
20
+ export async function nudgeStuckStories(ctx) {
21
+ const stuckThresholdMs = Math.max(1, ctx.config.manager.stuck_threshold_ms);
22
+ const staticInactivityThresholdMs = getScreenStaticInactivityThresholdMs(ctx.config);
23
+ const maxStuckNudgesPerStory = getMaxStuckNudgesPerStory(ctx.config);
24
+ const waitingNudgeCooldownMs = Math.max(ctx.config.manager.nudge_cooldown_ms, staticInactivityThresholdMs);
25
+ const staleUpdatedAt = new Date(Date.now() - stuckThresholdMs).toISOString();
26
+ // Phase 1: Read stuck stories and agents (brief lock)
27
+ const candidates = await ctx.withDb(async (db) => {
28
+ const stuckStories = queryAll(db.db, `SELECT * FROM stories
29
+ WHERE status = 'in_progress'
30
+ AND updated_at < ?`, [staleUpdatedAt]).filter(story => !['merged', 'completed'].includes(story.status));
31
+ verboseLogCtx(ctx, `nudgeStuckStories: candidates=${stuckStories.length}, staleBefore=${staleUpdatedAt}, thresholdMs=${stuckThresholdMs}`);
32
+ const result = [];
33
+ for (const story of stuckStories) {
34
+ verboseLogCtx(ctx, `nudgeStuckStories: evaluating story=${story.id}`);
35
+ if (!story.assigned_agent_id) {
36
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=no_assigned_agent`);
37
+ continue;
38
+ }
39
+ const agent = getAgentById(db.db, story.assigned_agent_id);
40
+ if (!agent) {
41
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=missing_agent`);
42
+ continue;
43
+ }
44
+ const agentSession = findSessionForAgent(ctx.hiveSessions, agent);
45
+ if (!agentSession) {
46
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=no_agent_session`);
47
+ continue;
48
+ }
49
+ result.push({
50
+ story,
51
+ agent,
52
+ sessionName: agentSession.name,
53
+ cliTool: (agent.cli_tool || 'claude'),
54
+ });
55
+ }
56
+ return result;
57
+ });
58
+ // Phase 2: Tmux captures, AI classifier, nudges (no lock held)
59
+ for (const candidate of candidates) {
60
+ const { story, agent, sessionName, cliTool } = candidate;
61
+ const now = Date.now();
62
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} session=${sessionName} cli=${cliTool}`);
63
+ const trackedState = agentStates.get(sessionName);
64
+ if (trackedState &&
65
+ [
66
+ AgentState.ASKING_QUESTION,
67
+ AgentState.AWAITING_SELECTION,
68
+ AgentState.PLAN_APPROVAL,
69
+ AgentState.PERMISSION_REQUIRED,
70
+ AgentState.USER_DECLINED,
71
+ ].includes(trackedState.lastState)) {
72
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=waiting_for_human state=${trackedState.lastState}`);
73
+ continue;
74
+ }
75
+ if (trackedState && now - trackedState.lastNudgeTime < waitingNudgeCooldownMs) {
76
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=nudge_to_ai_window remainingMs=${waitingNudgeCooldownMs - (now - trackedState.lastNudgeTime)}`);
77
+ continue;
78
+ }
79
+ const output = await captureTmuxPane(sessionName, TMUX_CAPTURE_LINES_SHORT);
80
+ const stateResult = detectAgentState(output, cliTool);
81
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} detected state=${stateResult.state}, waiting=${stateResult.isWaiting}, needsHuman=${stateResult.needsHuman}`);
82
+ if (stateResult.needsHuman) {
83
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=needs_human`);
84
+ continue;
85
+ }
86
+ const sessionUnchangedForMs = getSessionStaticUnchangedForMs(sessionName, now);
87
+ const unknownLooksStuck = shouldTreatUnknownAsStuckWaiting({
88
+ state: stateResult.state,
89
+ isWaiting: stateResult.isWaiting,
90
+ sessionUnchangedForMs,
91
+ staticInactivityThresholdMs,
92
+ });
93
+ if (stateResult.state === AgentState.THINKING) {
94
+ if (trackedState && (trackedState.storyStuckNudgeCount || 0) > 0) {
95
+ trackedState.storyStuckNudgeCount = 0;
96
+ }
97
+ clearHumanIntervention(sessionName);
98
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=thinking state=${stateResult.state}`);
99
+ continue;
100
+ }
101
+ if (!stateResult.isWaiting && !unknownLooksStuck) {
102
+ if (trackedState && (trackedState.storyStuckNudgeCount || 0) > 0) {
103
+ trackedState.storyStuckNudgeCount = 0;
104
+ }
105
+ clearHumanIntervention(sessionName);
106
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=not_waiting state=${stateResult.state}`);
107
+ continue;
108
+ }
109
+ if (unknownLooksStuck) {
110
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} action=unknown_state_stuck_heuristic unchangedMs=${sessionUnchangedForMs}`);
111
+ }
112
+ if (shouldDeferStuckReminderUntilStaticWindow({
113
+ state: stateResult.state,
114
+ sessionUnchangedForMs,
115
+ staticInactivityThresholdMs,
116
+ })) {
117
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=done_inference_static_window remainingMs=${staticInactivityThresholdMs - sessionUnchangedForMs}`);
118
+ continue;
119
+ }
120
+ else {
121
+ const completionAssessment = await assessCompletionFromOutput(ctx.config, sessionName, story.id, output);
122
+ const aiSaysDone = completionAssessment.done &&
123
+ completionAssessment.confidence >= DONE_INFERENCE_CONFIDENCE_THRESHOLD;
124
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} doneInference done=${completionAssessment.done}, confidence=${completionAssessment.confidence.toFixed(2)}, aiSaysDone=${aiSaysDone}, reason=${completionAssessment.reason}`);
125
+ if (isClassifierTimeoutReason(completionAssessment.reason)) {
126
+ await markClassifierTimeoutForHumanIntervention(ctx, sessionName, story.id, completionAssessment.reason, agent.id);
127
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} action=classifier_timeout_escalation session=${sessionName}`);
128
+ continue;
129
+ }
130
+ clearHumanIntervention(sessionName);
131
+ if (aiSaysDone) {
132
+ const progressed = await autoProgressDoneStory(ctx, story, agent, sessionName, completionAssessment.reason, completionAssessment.confidence);
133
+ if (progressed) {
134
+ ctx.counters.autoProgressed++;
135
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} action=auto_progressed`);
136
+ continue;
137
+ }
138
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} auto_progress_failed`);
139
+ }
140
+ else {
141
+ const stuckNudgesSent = trackedState?.storyStuckNudgeCount || 0;
142
+ if (stuckNudgesSent >= maxStuckNudgesPerStory) {
143
+ await markDoneFalseForHumanIntervention(ctx, sessionName, story.id, completionAssessment.reason, agent.id);
144
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} action=done_false_escalation session=${sessionName}`);
145
+ continue;
146
+ }
147
+ }
148
+ }
149
+ const stuckNudgesSent = trackedState?.storyStuckNudgeCount || 0;
150
+ if (stuckNudgesSent >= maxStuckNudgesPerStory) {
151
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} skip=stuck_nudge_limit reached=${stuckNudgesSent}/${maxStuckNudgesPerStory}`);
152
+ continue;
153
+ }
154
+ if (stateResult.state === AgentState.WORK_COMPLETE) {
155
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} action=mandatory_completion_signal session=${sessionName}`);
156
+ const completionSignalLines = [
157
+ `# MANDATORY COMPLETION SIGNAL: execute now for ${story.id}`,
158
+ `hive pr submit -b $(git rev-parse --abbrev-ref HEAD) -s ${story.id} --from ${sessionName}`,
159
+ `hive my-stories complete ${story.id}`,
160
+ ];
161
+ if (shouldIncludeProgressUpdates(ctx.config)) {
162
+ completionSignalLines.push(`hive progress ${story.id} -m "PR submitted to merge queue" --from ${sessionName} --done`);
163
+ }
164
+ else {
165
+ completionSignalLines.push('# project_management.provider is none; skip hive progress in this workspace.');
166
+ }
167
+ completionSignalLines.push('# Do not stop at a summary. Completion requires the commands above.');
168
+ await sendManagerNudge(ctx, sessionName, completionSignalLines.join('\n'));
169
+ ctx.counters.nudged++;
170
+ if (trackedState) {
171
+ trackedState.lastNudgeTime = now;
172
+ trackedState.storyStuckNudgeCount = (trackedState.storyStuckNudgeCount || 0) + 1;
173
+ }
174
+ else {
175
+ agentStates.set(sessionName, {
176
+ lastState: stateResult.state,
177
+ lastStateChangeTime: now,
178
+ lastNudgeTime: now,
179
+ storyStuckNudgeCount: 1,
180
+ });
181
+ }
182
+ continue;
183
+ }
184
+ verboseLogCtx(ctx, `nudgeStuckStories: story=${story.id} action=stuck_reminder session=${sessionName}`);
185
+ await sendManagerNudge(ctx, sessionName, `# REMINDER: Story ${story.id} has been in progress for a while.
186
+ # If stuck, escalate to your Senior or Tech Lead.
187
+ # If done, submit your PR: hive pr submit -b $(git rev-parse --abbrev-ref HEAD) -s ${story.id} --from ${sessionName}
188
+ # Then mark complete: hive my-stories complete ${story.id}`);
189
+ ctx.counters.nudged++;
190
+ if (trackedState) {
191
+ trackedState.lastNudgeTime = now;
192
+ trackedState.storyStuckNudgeCount = (trackedState.storyStuckNudgeCount || 0) + 1;
193
+ }
194
+ else {
195
+ agentStates.set(sessionName, {
196
+ lastState: stateResult.state,
197
+ lastStateChangeTime: now,
198
+ lastNudgeTime: now,
199
+ storyStuckNudgeCount: 1,
200
+ });
201
+ }
202
+ }
203
+ }
204
+ export async function autoProgressDoneStory(ctx, story, agent, sessionName, reason, confidence) {
205
+ verboseLogCtx(ctx, `autoProgressDoneStory: story=${story.id}, session=${sessionName}, confidence=${confidence.toFixed(2)}`);
206
+ // Resolve branch name outside lock (involves git operations)
207
+ const branch = await resolveStoryBranchName(ctx.root, story, agent, msg => verboseLogCtx(ctx, `resolveStoryBranchName: story=${story.id} ${msg}`));
208
+ // DB operations under brief lock
209
+ const action = await ctx.withDb(async (db, scheduler) => {
210
+ const openPRs = getOpenPullRequestsByStory(db.db, story.id);
211
+ verboseLogCtx(ctx, `autoProgressDoneStory: story=${story.id}, openPRs=${openPRs.length}`);
212
+ if (openPRs.length > 0) {
213
+ if (story.status !== 'pr_submitted') {
214
+ updateStory(db.db, story.id, { status: 'pr_submitted' });
215
+ createLog(db.db, {
216
+ agentId: 'manager',
217
+ storyId: story.id,
218
+ eventType: 'STORY_PROGRESS_UPDATE',
219
+ message: `Auto-progressed ${story.id} to pr_submitted (existing PR detected)`,
220
+ metadata: {
221
+ session_name: sessionName,
222
+ recovery: 'done_inference_existing_pr',
223
+ reason,
224
+ confidence,
225
+ open_pr_count: openPRs.length,
226
+ },
227
+ });
228
+ db.save();
229
+ await syncStatusForStory(ctx.root, db.db, story.id, 'pr_submitted');
230
+ verboseLogCtx(ctx, `autoProgressDoneStory: story=${story.id} status moved to pr_submitted`);
231
+ }
232
+ return 'existing_pr';
233
+ }
234
+ if (!branch) {
235
+ verboseLogCtx(ctx, `autoProgressDoneStory: story=${story.id} action=failed_no_branch`);
236
+ return 'no_branch';
237
+ }
238
+ await withTransaction(db.db, () => {
239
+ updateStory(db.db, story.id, { status: 'pr_submitted', branchName: branch });
240
+ createPullRequest(db.db, {
241
+ storyId: story.id,
242
+ teamId: story.team_id || null,
243
+ branchName: branch,
244
+ submittedBy: sessionName,
245
+ });
246
+ createLog(db.db, {
247
+ agentId: 'manager',
248
+ storyId: story.id,
249
+ eventType: 'PR_SUBMITTED',
250
+ message: `Auto-submitted PR for ${story.id} after AI completion inference`,
251
+ metadata: {
252
+ session_name: sessionName,
253
+ recovery: 'done_inference_auto_submit',
254
+ reason,
255
+ confidence,
256
+ branch,
257
+ },
258
+ });
259
+ }, () => db.save());
260
+ await syncStatusForStory(ctx.root, db.db, story.id, 'pr_submitted');
261
+ await scheduler.checkMergeQueue();
262
+ db.save();
263
+ verboseLogCtx(ctx, `autoProgressDoneStory: story=${story.id} action=auto_submitted branch=${branch}`);
264
+ return 'auto_submitted';
265
+ });
266
+ // Tmux notifications (no lock needed)
267
+ if (action === 'existing_pr') {
268
+ await sendManagerNudge(ctx, sessionName, `# AUTO-PROGRESS: Manager inferred ${story.id} is complete (confidence ${confidence.toFixed(2)}), detected existing PR, and moved story to PR-submitted state.`);
269
+ verboseLogCtx(ctx, `autoProgressDoneStory: story=${story.id} action=existing_pr_progressed`);
270
+ return true;
271
+ }
272
+ if (action === 'no_branch') {
273
+ return false;
274
+ }
275
+ await sendManagerNudge(ctx, sessionName, `# AUTO-PROGRESS: Manager inferred ${story.id} is complete (confidence ${confidence.toFixed(2)}), auto-submitted branch ${branch} to merge queue.`);
276
+ return true;
277
+ }
278
+ async function resolveStoryBranchName(root, story, agent, log) {
279
+ if (story.branch_name && story.branch_name.trim().length > 0) {
280
+ log?.(`source=story.branch_name value=${story.branch_name.trim()}`);
281
+ return story.branch_name.trim();
282
+ }
283
+ if (!agent.worktree_path) {
284
+ log?.('source=worktree skip=no_worktree_path');
285
+ return null;
286
+ }
287
+ const worktreeDir = join(root, agent.worktree_path);
288
+ try {
289
+ const result = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: worktreeDir });
290
+ const branch = result.stdout.trim();
291
+ if (!branch || branch === 'HEAD') {
292
+ log?.(`source=git_rev_parse invalid_branch=${branch || '(empty)'}`);
293
+ return null;
294
+ }
295
+ log?.(`source=git_rev_parse value=${branch}`);
296
+ return branch;
297
+ }
298
+ catch {
299
+ log?.(`source=git_rev_parse failed cwd=${worktreeDir}`);
300
+ return null;
301
+ }
302
+ }
303
+ export async function nudgeQAFailedStories(ctx) {
304
+ // Phase 1: Read QA-failed stories and agents (brief lock)
305
+ const candidates = await ctx.withDb(async (db) => {
306
+ const qaFailedStories = getStoriesByStatus(db.db, 'qa_failed').filter(story => !['merged', 'completed'].includes(story.status));
307
+ verboseLogCtx(ctx, `nudgeQAFailedStories: candidates=${qaFailedStories.length}`);
308
+ const result = [];
309
+ for (const story of qaFailedStories) {
310
+ if (!story.assigned_agent_id) {
311
+ verboseLogCtx(ctx, `nudgeQAFailedStories: story=${story.id} skip=no_assigned_agent`);
312
+ continue;
313
+ }
314
+ const agent = getAgentById(db.db, story.assigned_agent_id);
315
+ if (!agent || agent.status !== 'working') {
316
+ verboseLogCtx(ctx, `nudgeQAFailedStories: story=${story.id} skip=agent_not_working status=${agent?.status || 'missing'}`);
317
+ continue;
318
+ }
319
+ const agentSession = findSessionForAgent(ctx.hiveSessions, agent);
320
+ if (!agentSession) {
321
+ verboseLogCtx(ctx, `nudgeQAFailedStories: story=${story.id} skip=no_session`);
322
+ continue;
323
+ }
324
+ result.push({
325
+ storyId: story.id,
326
+ sessionName: agentSession.name,
327
+ cliTool: (agent.cli_tool || 'claude'),
328
+ });
329
+ }
330
+ return result;
331
+ });
332
+ // Phase 2: Tmux captures and nudges (no lock needed)
333
+ for (const candidate of candidates) {
334
+ const output = await captureTmuxPane(candidate.sessionName, TMUX_CAPTURE_LINES_SHORT);
335
+ const stateResult = detectAgentState(output, candidate.cliTool);
336
+ if (stateResult.isWaiting &&
337
+ !stateResult.needsHuman &&
338
+ stateResult.state !== AgentState.THINKING) {
339
+ verboseLogCtx(ctx, `nudgeQAFailedStories: story=${candidate.storyId} nudge session=${candidate.sessionName} state=${stateResult.state}`);
340
+ await sendManagerNudge(ctx, candidate.sessionName, `# REMINDER: Story ${candidate.storyId} failed QA review!
341
+ # You must fix the issues and resubmit the PR.
342
+ # Check the QA feedback and address all concerns.
343
+ hive pr queue`);
344
+ }
345
+ else {
346
+ verboseLogCtx(ctx, `nudgeQAFailedStories: story=${candidate.storyId} skip=not_ready waiting=${stateResult.isWaiting} needsHuman=${stateResult.needsHuman} state=${stateResult.state}`);
347
+ }
348
+ }
349
+ }
350
+ export async function recoverUnassignedQAFailedStories(ctx) {
351
+ const result = await ctx.withDb(async (db, scheduler) => {
352
+ const recoverableStories = queryAll(db.db, `
353
+ SELECT * FROM stories
354
+ WHERE status = 'qa_failed'
355
+ AND assigned_agent_id IS NULL
356
+ `);
357
+ if (recoverableStories.length === 0)
358
+ return null;
359
+ verboseLogCtx(ctx, `recoverUnassignedQAFailedStories: recovered=${recoverableStories.length}`);
360
+ await withTransaction(db.db, () => {
361
+ for (const story of recoverableStories) {
362
+ updateStory(db.db, story.id, { status: 'planned', assignedAgentId: null });
363
+ createLog(db.db, {
364
+ agentId: 'manager',
365
+ storyId: story.id,
366
+ eventType: 'ORPHANED_STORY_RECOVERED',
367
+ message: `Recovered QA-failed story ${story.id} (unassigned) back to planned`,
368
+ metadata: { from_status: 'qa_failed', to_status: 'planned' },
369
+ });
370
+ }
371
+ }, () => db.save());
372
+ for (const story of recoverableStories) {
373
+ await syncStatusForStory(ctx.root, db.db, story.id, 'planned');
374
+ }
375
+ // Proactively re-assign recovered work so it does not stall until manual `hive assign`.
376
+ const assignmentResult = await scheduler.assignStories();
377
+ verboseLogCtx(ctx, `recoverUnassignedQAFailedStories.assignStories: assigned=${assignmentResult.assigned}, errors=${assignmentResult.errors.length}`);
378
+ db.save();
379
+ if (assignmentResult.assigned > 0) {
380
+ await scheduler.flushJiraQueue();
381
+ db.save();
382
+ }
383
+ return { recoverableCount: recoverableStories.length, assignmentResult };
384
+ });
385
+ if (result) {
386
+ console.log(chalk.yellow(` Recovered ${result.recoverableCount} QA-failed unassigned story(ies), assigned ${result.assignmentResult.assigned}`));
387
+ if (result.assignmentResult.errors.length > 0) {
388
+ console.log(chalk.yellow(` Assignment errors during QA-failed recovery: ${result.assignmentResult.errors.length}`));
389
+ }
390
+ }
391
+ }
392
+ //# sourceMappingURL=stuck-story-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-story-processor.js","sourceRoot":"","sources":["../../../../src/cli/commands/manager/stuck-story-processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ManagerCheckContext } from './types.js';
2
+ export declare function restartStaleTechLead(ctx: ManagerCheckContext): Promise<void>;
3
+ //# sourceMappingURL=tech-lead-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tech-lead-lifecycle.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/manager/tech-lead-lifecycle.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAKtD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsLlF"}
@@ -0,0 +1,141 @@
1
+ // Licensed under the Hungry Ghost Hive License. See LICENSE.
2
+ import chalk from 'chalk';
3
+ import { getCliRuntimeBuilder, resolveRuntimeModelForCli } from '../../../cli-runtimes/index.js';
4
+ import { loadConfig } from '../../../config/loader.js';
5
+ import { getAgentsByType, updateAgent } from '../../../db/queries/agents.js';
6
+ import { createLog } from '../../../db/queries/logs.js';
7
+ import { getRequirementsByStatus } from '../../../db/queries/requirements.js';
8
+ import { getAllTeams } from '../../../db/queries/teams.js';
9
+ import { AgentState } from '../../../state-detectors/types.js';
10
+ import { captureTmuxPane, isTmuxSessionRunning, killTmuxSession, spawnTmuxSession, } from '../../../tmux/manager.js';
11
+ import { findHiveRoot as findHiveRootFromDir, getHivePaths } from '../../../utils/paths.js';
12
+ import { generateTechLeadPrompt } from '../req.js';
13
+ import { detectAgentState } from './agent-monitoring.js';
14
+ import { verboseLogCtx } from './manager-utils.js';
15
+ import { isTechLeadRestartOnCooldown } from './restart-cooldown.js';
16
+ import { TMUX_CAPTURE_LINES_SHORT } from './types.js';
17
+ const techLeadLastRestartByAgentId = new Map();
18
+ export async function restartStaleTechLead(ctx) {
19
+ const maxAgeHours = ctx.config.manager.tech_lead_max_age_hours;
20
+ const maxAgeMs = maxAgeHours * 60 * 60 * 1000;
21
+ const now = Date.now();
22
+ // Phase 1: Read tech lead agents (brief lock)
23
+ const techLeads = await ctx.withDb(async (db) => {
24
+ const leads = getAgentsByType(db.db, 'tech_lead');
25
+ verboseLogCtx(ctx, `restartStaleTechLead: found ${leads.length} tech lead agent(s)`);
26
+ return leads.map(tl => ({
27
+ id: tl.id,
28
+ tmuxSession: tl.tmux_session,
29
+ cliTool: (tl.cli_tool || 'claude'),
30
+ createdAt: tl.created_at,
31
+ }));
32
+ });
33
+ // Phase 2: Check sessions and restart (tmux I/O outside lock, DB writes under brief lock)
34
+ for (const techLead of techLeads) {
35
+ if (!techLead.tmuxSession) {
36
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} skip=no_tmux_session`);
37
+ continue;
38
+ }
39
+ const sessionRunning = await isTmuxSessionRunning(techLead.tmuxSession);
40
+ if (!sessionRunning) {
41
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} skip=session_not_running session=${techLead.tmuxSession}`);
42
+ continue;
43
+ }
44
+ const createdAt = new Date(techLead.createdAt).getTime();
45
+ const ageMs = now - createdAt;
46
+ const ageHours = ageMs / (60 * 60 * 1000);
47
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} age=${ageHours.toFixed(2)}h threshold=${maxAgeHours}h`);
48
+ if (ageMs < maxAgeMs) {
49
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} skip=not_stale remainingMs=${maxAgeMs - ageMs}`);
50
+ continue;
51
+ }
52
+ const cooldown = isTechLeadRestartOnCooldown(techLeadLastRestartByAgentId.get(techLead.id), now, maxAgeHours);
53
+ if (cooldown.onCooldown) {
54
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} skip=cooldown cooldownHours=${cooldown.cooldownHours} remainingMs=${cooldown.remainingMs}`);
55
+ continue;
56
+ }
57
+ const output = await captureTmuxPane(techLead.tmuxSession, TMUX_CAPTURE_LINES_SHORT);
58
+ const stateResult = detectAgentState(output, techLead.cliTool);
59
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} state=${stateResult.state} waiting=${stateResult.isWaiting} needsHuman=${stateResult.needsHuman}`);
60
+ if (!stateResult.isWaiting ||
61
+ stateResult.needsHuman ||
62
+ stateResult.state === AgentState.THINKING) {
63
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} skip=not_safe_state state=${stateResult.state}`);
64
+ continue;
65
+ }
66
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} action=restarting session=${techLead.tmuxSession}`);
67
+ // Kill the existing session (tmux I/O, no lock)
68
+ await killTmuxSession(techLead.tmuxSession);
69
+ // Spawn a new session with the same configuration (tmux I/O, no lock)
70
+ const hiveRoot = findHiveRootFromDir(ctx.root);
71
+ if (!hiveRoot) {
72
+ verboseLogCtx(ctx, `restartStaleTechLead: techLead=${techLead.id} error=hive_root_not_found`);
73
+ continue;
74
+ }
75
+ const paths = getHivePaths(hiveRoot);
76
+ const config = loadConfig(paths.hiveDir);
77
+ const agentConfig = config.models.tech_lead;
78
+ const cliTool = agentConfig.cli_tool;
79
+ const safetyMode = agentConfig.safety_mode;
80
+ const model = resolveRuntimeModelForCli(agentConfig.model, cliTool);
81
+ const runtimeBuilder = getCliRuntimeBuilder(cliTool);
82
+ const commandArgs = runtimeBuilder.buildSpawnCommand(model, safetyMode);
83
+ // Look up active requirement and teams to provide context to the restarted tech lead
84
+ const initialPrompt = await ctx.withDb(async (db) => {
85
+ const planningReqs = getRequirementsByStatus(db.db, 'planning');
86
+ const inProgressReqs = getRequirementsByStatus(db.db, 'in_progress');
87
+ const activeReq = planningReqs[0] ?? inProgressReqs[0] ?? null;
88
+ const teams = getAllTeams(db.db);
89
+ if (activeReq) {
90
+ return generateTechLeadPrompt(activeReq.id, activeReq.title, activeReq.description, teams, activeReq.godmode === 1, activeReq.target_branch || 'main');
91
+ }
92
+ return `You are the Tech Lead of Hive, an AI development team orchestrator.
93
+
94
+ You have been restarted to refresh your context. No active requirement is currently being planned.
95
+
96
+ ## Next Steps
97
+
98
+ 1. Check the current status of the Hive workspace:
99
+ \`\`\`bash
100
+ hive status
101
+ \`\`\`
102
+
103
+ 2. Check your inbox for messages from developers:
104
+ \`\`\`bash
105
+ hive msg inbox hive-tech-lead
106
+ \`\`\`
107
+
108
+ 3. If there are pending requirements, begin planning them. If all work is complete, monitor for new requirements.`;
109
+ });
110
+ await spawnTmuxSession({
111
+ sessionName: techLead.tmuxSession,
112
+ workDir: ctx.root,
113
+ commandArgs,
114
+ initialPrompt,
115
+ });
116
+ // DB writes under brief lock
117
+ await ctx.withDb(async (db) => {
118
+ createLog(db.db, {
119
+ agentId: 'manager',
120
+ eventType: 'AGENT_SPAWNED',
121
+ status: 'info',
122
+ message: `Tech lead ${techLead.id} restarted for context freshness (age: ${ageHours.toFixed(1)}h)`,
123
+ metadata: {
124
+ agent_id: techLead.id,
125
+ tmux_session: techLead.tmuxSession,
126
+ age_hours: ageHours,
127
+ threshold_hours: maxAgeHours,
128
+ restart_reason: 'context_freshness',
129
+ },
130
+ });
131
+ updateAgent(db.db, techLead.id, {
132
+ status: 'working',
133
+ createdAt: new Date().toISOString(),
134
+ });
135
+ db.save();
136
+ });
137
+ techLeadLastRestartByAgentId.set(techLead.id, now);
138
+ console.log(chalk.green(` Tech lead ${techLead.id} restarted for context freshness (age: ${ageHours.toFixed(1)}h)`));
139
+ }
140
+ }
141
+ //# sourceMappingURL=tech-lead-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tech-lead-lifecycle.js","sourceRoot":"","sources":["../../../../src/cli/commands/manager/tech-lead-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"my-stories.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/my-stories.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,gBAAgB,SA0GzB,CAAC"}
1
+ {"version":3,"file":"my-stories.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/my-stories.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,gBAAgB,SA0GzB,CAAC"}
@@ -5,6 +5,7 @@ import { syncStatusForStory } from '../../connectors/project-management/operatio
5
5
  import { queryAll, queryOne, run } from '../../db/client.js';
6
6
  import { createLog } from '../../db/queries/logs.js';
7
7
  import { createStory, getStoryDependencies, updateStory } from '../../db/queries/stories.js';
8
+ import { requireAgentBySession, requireStory } from '../../utils/cli-helpers.js';
8
9
  import { withHiveContext, withReadOnlyHiveContext } from '../../utils/with-hive-context.js';
9
10
  export const myStoriesCommand = new Command('my-stories')
10
11
  .description('View and manage stories assigned to an agent')
@@ -93,17 +94,9 @@ myStoriesCommand
93
94
  .action(async (storyId, options) => {
94
95
  await withHiveContext(async ({ root, db }) => {
95
96
  // Find agent by session
96
- const agent = queryOne(db.db, "SELECT id FROM agents WHERE tmux_session = ? AND status != 'terminated'", [options.session]);
97
- if (!agent) {
98
- console.error(chalk.red(`No agent found with session: ${options.session}`));
99
- process.exit(1);
100
- }
97
+ const agent = requireAgentBySession(db.db, options.session);
101
98
  // Check story exists and is available
102
- const story = queryOne(db.db, 'SELECT * FROM stories WHERE id = ?', [storyId]);
103
- if (!story) {
104
- console.error(chalk.red(`Story not found: ${storyId}`));
105
- process.exit(1);
106
- }
99
+ const story = requireStory(db.db, storyId);
107
100
  if (story.assigned_agent_id && story.assigned_agent_id !== agent.id) {
108
101
  console.error(chalk.red(`Story already assigned to another agent.`));
109
102
  process.exit(1);
@@ -137,11 +130,7 @@ myStoriesCommand
137
130
  .description('Mark a story as complete (ready for review)')
138
131
  .action(async (storyId) => {
139
132
  await withHiveContext(async ({ root, db }) => {
140
- const story = queryOne(db.db, 'SELECT * FROM stories WHERE id = ?', [storyId]);
141
- if (!story) {
142
- console.error(chalk.red(`Story not found: ${storyId}`));
143
- process.exit(1);
144
- }
133
+ requireStory(db.db, storyId);
145
134
  run(db.db, `
146
135
  UPDATE stories
147
136
  SET status = 'review', updated_at = datetime('now')
@@ -178,11 +167,7 @@ myStoriesCommand
178
167
  process.exit(1);
179
168
  }
180
169
  await withHiveContext(async ({ db }) => {
181
- const agent = queryOne(db.db, "SELECT id, team_id FROM agents WHERE tmux_session = ? AND status != 'terminated'", [options.session]);
182
- if (!agent) {
183
- console.error(chalk.red(`No agent found with session: ${options.session}`));
184
- process.exit(1);
185
- }
170
+ const agent = requireAgentBySession(db.db, options.session);
186
171
  if (!agent.team_id) {
187
172
  console.error(chalk.red('This agent is not attached to a team, so a team refactor story cannot be created.'));
188
173
  process.exit(1);