@link-assistant/hive-mind 1.39.0 → 1.40.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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/claude.lib.mjs +30 -8
- package/src/config.lib.mjs +5 -2
- package/src/version-info.lib.mjs +142 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.40.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9df62ed: fix: increase activity timeout to 1hr, fix idle tracking, improve graceful kill (#1510)
|
|
8
|
+
|
|
9
|
+
## 1.40.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 6b8465a: feat: add browsers, browser tools, and missing software to /version command
|
|
14
|
+
|
|
3
15
|
## 1.39.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/package.json
CHANGED
package/src/claude.lib.mjs
CHANGED
|
@@ -853,21 +853,27 @@ export const executeClaudeCommand = async params => {
|
|
|
853
853
|
let lastEventTime = null;
|
|
854
854
|
let activityTimeoutId = null;
|
|
855
855
|
let isActivityTimeout = false;
|
|
856
|
+
// Issue #1510: Separate SIGTERM (graceful) and SIGKILL (force) phases to allow
|
|
857
|
+
// capturing final output from the process during graceful shutdown
|
|
856
858
|
const forceExitOnTimeout = async () => {
|
|
857
859
|
if (forceExitTriggered) return;
|
|
858
860
|
forceExitTriggered = true;
|
|
859
|
-
await log(`⚠️ Stream timeout —
|
|
861
|
+
await log(`⚠️ Stream timeout — sending SIGTERM for graceful shutdown (Issue #1280, #1510)`, { verbose: true });
|
|
860
862
|
try {
|
|
861
863
|
if (execCommand.kill) {
|
|
862
864
|
execCommand.kill('SIGTERM');
|
|
863
|
-
// Issue #1346: Follow up with SIGKILL after
|
|
865
|
+
// Issue #1346/#1510: Follow up with SIGKILL after 5s if still alive
|
|
866
|
+
// Increased from 2s to 5s to give more time for final output capture
|
|
864
867
|
const t = setTimeout(() => {
|
|
865
868
|
try {
|
|
866
|
-
if (!execCommand.result?.code)
|
|
869
|
+
if (!execCommand.result?.code) {
|
|
870
|
+
log(`⚠️ Process did not exit after SIGTERM, sending SIGKILL`, { verbose: true });
|
|
871
|
+
execCommand.kill('SIGKILL');
|
|
872
|
+
}
|
|
867
873
|
} catch {
|
|
868
874
|
/* exited */
|
|
869
875
|
}
|
|
870
|
-
},
|
|
876
|
+
}, 5000);
|
|
871
877
|
t.unref();
|
|
872
878
|
}
|
|
873
879
|
} catch (e) {
|
|
@@ -892,8 +898,8 @@ export const executeClaudeCommand = async params => {
|
|
|
892
898
|
activityTimeoutId = setTimeout(async () => {
|
|
893
899
|
if (!forceExitTriggered && !resultEventReceived) {
|
|
894
900
|
isActivityTimeout = true;
|
|
895
|
-
const idleSeconds = lastEventTime ? Math.round((Date.now() - lastEventTime) / 1000) : 'unknown';
|
|
896
|
-
await log(`\n⚠️ No stream output for ${timeouts.streamActivityMs / 1000}s after previous activity (idle: ${idleSeconds}
|
|
901
|
+
const idleSeconds = lastEventTime ? `${Math.round((Date.now() - lastEventTime) / 1000)}s` : 'unknown';
|
|
902
|
+
await log(`\n⚠️ No stream output for ${timeouts.streamActivityMs / 1000}s after previous activity (idle: ${idleSeconds}) — force-killing (Issue #1472)`, { level: 'warning' });
|
|
897
903
|
await forceExitOnTimeout();
|
|
898
904
|
}
|
|
899
905
|
}, timeouts.streamActivityMs);
|
|
@@ -901,7 +907,8 @@ export const executeClaudeCommand = async params => {
|
|
|
901
907
|
}
|
|
902
908
|
};
|
|
903
909
|
for await (const chunk of execCommand.stream()) {
|
|
904
|
-
|
|
910
|
+
// Issue #1510: Continue processing stream after SIGTERM to capture final output
|
|
911
|
+
// The stream will naturally end when the process exits (SIGTERM) or is force-killed (SIGKILL after 5s)
|
|
905
912
|
if (!firstChunkReceived) {
|
|
906
913
|
// Issue #1472/#1475: Clear startup timeout on first output
|
|
907
914
|
firstChunkReceived = true;
|
|
@@ -922,12 +929,14 @@ export const executeClaudeCommand = async params => {
|
|
|
922
929
|
if (!line.trim()) continue;
|
|
923
930
|
try {
|
|
924
931
|
const data = sanitizeObjectStrings(JSON.parse(line));
|
|
932
|
+
// Issue #1510: Track last event time for all modes (not just interactive)
|
|
933
|
+
// so activity timeout can report accurate idle duration
|
|
934
|
+
lastEventTime = Date.now();
|
|
925
935
|
if (interactiveHandler) {
|
|
926
936
|
if (!interactiveHandler._firstEventLogged) {
|
|
927
937
|
interactiveHandler._firstEventLogged = true;
|
|
928
938
|
await log(`🔌 Interactive mode: First event received (type: ${data.type || 'unknown'}) — stream is active`, { verbose: true });
|
|
929
939
|
}
|
|
930
|
-
lastEventTime = Date.now();
|
|
931
940
|
try {
|
|
932
941
|
await interactiveHandler.processEvent(data);
|
|
933
942
|
} catch (interactiveError) {
|
|
@@ -1193,6 +1202,19 @@ export const executeClaudeCommand = async params => {
|
|
|
1193
1202
|
const retryMode = isStartupTimeout ? ' (fresh start)' : ' (session preserved)';
|
|
1194
1203
|
await log(`\n⚠️ ${errorLabel} detected. Retry ${retryCount + 1}/${maxRetries} in ${delayLabel}${retryMode}${notRetryableHint}...`, { level: 'warning' });
|
|
1195
1204
|
await log(` Error: ${isStartupTimeout ? `No output from Claude CLI within ${timeouts.streamStartupMs / 1000}s` : isActivityTimeout ? `No output for ${timeouts.streamActivityMs / 1000}s after previous activity` : lastMessage.substring(0, 200)}`, { verbose: true });
|
|
1205
|
+
// Issue #1510: Post PR comment when force-killing and auto-resuming so reviewers can follow the session lifecycle
|
|
1206
|
+
if ((isActivityTimeout || isStartupTimeout) && owner && repo && prNumber && $) {
|
|
1207
|
+
try {
|
|
1208
|
+
const timeoutType = isActivityTimeout ? 'activity' : 'startup';
|
|
1209
|
+
const sessionInfo = sessionId ? `\nSession ID: \`${sessionId}\`` : '';
|
|
1210
|
+
const resumeInfo = isStartupTimeout ? 'Session will be restarted (fresh start).' : `Session will be resumed with \`--resume\` (context preserved).`;
|
|
1211
|
+
const commentBody = `## :warning: Session Force-Killed (${timeoutType} timeout)\n\nThe working session was force-killed due to ${timeoutType} timeout (no stream output for ${isActivityTimeout ? timeouts.streamActivityMs / 1000 : timeouts.streamStartupMs / 1000}s).\n\n**Auto-resuming**: Retry ${retryCount + 1}/${maxRetries} in ${delayLabel}. ${resumeInfo}${sessionInfo}\n\n*This is an automated notification — the session will continue automatically.*`;
|
|
1212
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
1213
|
+
await log(` Posted force-kill notification to PR #${prNumber}`, { verbose: true });
|
|
1214
|
+
} catch (commentError) {
|
|
1215
|
+
await log(` Warning: Could not post force-kill comment to PR: ${commentError.message}`, { verbose: true });
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1196
1218
|
// Activity timeout preserves session (work was started), startup timeout does not (no session created)
|
|
1197
1219
|
if (!isStartupTimeout && sessionId && !argv.resume) argv.resume = sessionId;
|
|
1198
1220
|
await waitWithCountdown(delay, log);
|
package/src/config.lib.mjs
CHANGED
|
@@ -63,8 +63,11 @@ export const timeouts = {
|
|
|
63
63
|
// after at least one event was received, the process is considered hung mid-session.
|
|
64
64
|
// This catches the case where Claude CLI starts producing output but then stops (e.g., the
|
|
65
65
|
// original Issue #1472 where CLI was stuck for 4.5h with all output arriving only at CTRL+C).
|
|
66
|
-
//
|
|
67
|
-
|
|
66
|
+
// Issue #1510: Increased from 300000ms (5 min) to 3600000ms (1 hour) because Claude Code can
|
|
67
|
+
// legitimately wait for long-running operations (docker builds, CI polls, large compilations).
|
|
68
|
+
// The 5-minute timeout was force-killing sessions during `sleep 300 && gh run view ...` commands.
|
|
69
|
+
// Default: 3600000ms (1 hour). Set to 0 to disable. Configurable via environment variable.
|
|
70
|
+
streamActivityMs: parseIntWithDefault('HIVE_MIND_STREAM_ACTIVITY_MS', 3600000),
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
// Auto-continue configurations
|
package/src/version-info.lib.mjs
CHANGED
|
@@ -50,7 +50,17 @@ const VERSION_COMMANDS = [
|
|
|
50
50
|
|
|
51
51
|
// Browser Automation
|
|
52
52
|
{ key: 'playwright', command: 'playwright --version 2>&1' },
|
|
53
|
+
{ key: 'playwrightTest', command: "npm list -g @playwright/test --depth=0 2>&1 | grep @playwright/test | awk '{print $2}'" },
|
|
53
54
|
{ key: 'playwrightMcp', command: "npm list -g @playwright/mcp --depth=0 2>&1 | grep @playwright/mcp | awk '{print $2}'" },
|
|
55
|
+
{ key: 'playwrightMcpStatus', command: 'timeout 5 claude mcp list 2>&1 | grep -i playwright | head -1' },
|
|
56
|
+
{ key: 'puppeteerBrowsers', command: "npm list -g @puppeteer/browsers --depth=0 2>&1 | grep @puppeteer/browsers | awk '{print $2}'" },
|
|
57
|
+
|
|
58
|
+
// Browsers (installed via Playwright)
|
|
59
|
+
{ key: 'chrome', command: 'google-chrome --version 2>&1' },
|
|
60
|
+
{ key: 'chromium', command: 'chromium --version 2>&1', fallbacks: ['chromium-browser --version 2>&1'] },
|
|
61
|
+
{ key: 'firefox', command: 'firefox --version 2>&1' },
|
|
62
|
+
{ key: 'msedge', command: 'microsoft-edge --version 2>&1', fallbacks: ['microsoft-edge-stable --version 2>&1'] },
|
|
63
|
+
{ key: 'webkit', command: "ls ~/.cache/ms-playwright/ 2>/dev/null | grep -oE 'webkit-[0-9]+' | head -1" },
|
|
54
64
|
|
|
55
65
|
// JavaScript/Node.js ecosystem
|
|
56
66
|
{ key: 'bun', command: 'bun --version 2>&1' },
|
|
@@ -103,10 +113,33 @@ const VERSION_COMMANDS = [
|
|
|
103
113
|
{ key: 'make', command: 'make --version 2>&1 | head -n1' },
|
|
104
114
|
{ key: 'cmake', command: 'cmake --version 2>&1 | head -n1' },
|
|
105
115
|
|
|
116
|
+
// Ruby ecosystem
|
|
117
|
+
{ key: 'ruby', command: 'ruby --version 2>&1' },
|
|
118
|
+
{ key: 'rbenv', command: 'rbenv --version 2>&1' },
|
|
119
|
+
|
|
120
|
+
// Kotlin
|
|
121
|
+
{ key: 'kotlin', command: 'kotlin -version 2>&1' },
|
|
122
|
+
|
|
123
|
+
// Swift
|
|
124
|
+
{ key: 'swift', command: 'swift --version 2>&1 | head -n1' },
|
|
125
|
+
|
|
126
|
+
// R
|
|
127
|
+
{ key: 'r', command: 'R --version 2>&1 | head -n1' },
|
|
128
|
+
|
|
106
129
|
// Development Tools
|
|
107
130
|
{ key: 'git', command: 'git --version 2>&1' },
|
|
108
131
|
{ key: 'gh', command: 'gh --version 2>&1 | head -n1' },
|
|
132
|
+
{ key: 'glab', command: 'glab --version 2>&1 | head -n1' },
|
|
109
133
|
{ key: 'brew', command: 'brew --version 2>&1 | head -n1' },
|
|
134
|
+
{ key: 'nasm', command: 'nasm --version 2>&1' },
|
|
135
|
+
{ key: 'fasm', command: 'fasm 2>&1 | head -n1' },
|
|
136
|
+
{ key: 'curl', command: 'curl --version 2>&1 | head -n1' },
|
|
137
|
+
{ key: 'wget', command: 'wget --version 2>&1 | head -n1' },
|
|
138
|
+
{ key: 'zip', command: 'zip --version 2>&1 | head -n2 | tail -n1' },
|
|
139
|
+
{ key: 'unzip', command: 'unzip -v 2>&1 | head -n1' },
|
|
140
|
+
{ key: 'expect', command: 'expect -version 2>&1' },
|
|
141
|
+
{ key: 'screen', command: 'screen --version 2>&1' },
|
|
142
|
+
{ key: 'xvfb', command: 'Xvfb -version 2>&1 | head -n1', fallbacks: ['dpkg -l xvfb 2>/dev/null | grep xvfb | head -1'] },
|
|
110
143
|
];
|
|
111
144
|
|
|
112
145
|
/**
|
|
@@ -202,7 +235,17 @@ export async function getVersionInfo(verbose = false, processVersion = null) {
|
|
|
202
235
|
|
|
203
236
|
// Browser Automation
|
|
204
237
|
playwright: versions.playwright,
|
|
238
|
+
playwrightTest: versions.playwrightTest,
|
|
205
239
|
playwrightMcp: versions.playwrightMcp,
|
|
240
|
+
playwrightMcpStatus: versions.playwrightMcpStatus,
|
|
241
|
+
puppeteerBrowsers: versions.puppeteerBrowsers,
|
|
242
|
+
|
|
243
|
+
// Browsers
|
|
244
|
+
chrome: versions.chrome,
|
|
245
|
+
chromium: versions.chromium,
|
|
246
|
+
firefox: versions.firefox,
|
|
247
|
+
msedge: versions.msedge,
|
|
248
|
+
webkit: versions.webkit,
|
|
206
249
|
|
|
207
250
|
// JavaScript/Node.js
|
|
208
251
|
node: versions.node,
|
|
@@ -246,6 +289,19 @@ export async function getVersionInfo(verbose = false, processVersion = null) {
|
|
|
246
289
|
elan: versions.elan,
|
|
247
290
|
lake: versions.lake,
|
|
248
291
|
|
|
292
|
+
// Ruby
|
|
293
|
+
ruby: versions.ruby,
|
|
294
|
+
rbenv: versions.rbenv,
|
|
295
|
+
|
|
296
|
+
// Kotlin
|
|
297
|
+
kotlin: versions.kotlin,
|
|
298
|
+
|
|
299
|
+
// Swift
|
|
300
|
+
swift: versions.swift,
|
|
301
|
+
|
|
302
|
+
// R
|
|
303
|
+
r: versions.r,
|
|
304
|
+
|
|
249
305
|
// C/C++
|
|
250
306
|
gcc: versions.gcc,
|
|
251
307
|
gpp: versions.gpp,
|
|
@@ -258,7 +314,17 @@ export async function getVersionInfo(verbose = false, processVersion = null) {
|
|
|
258
314
|
// Development Tools
|
|
259
315
|
git: versions.git,
|
|
260
316
|
gh: versions.gh,
|
|
317
|
+
glab: versions.glab,
|
|
261
318
|
brew: versions.brew,
|
|
319
|
+
nasm: versions.nasm,
|
|
320
|
+
fasm: versions.fasm,
|
|
321
|
+
curl: versions.curl,
|
|
322
|
+
wget: versions.wget,
|
|
323
|
+
zip: versions.zip,
|
|
324
|
+
unzip: versions.unzip,
|
|
325
|
+
expect: versions.expect,
|
|
326
|
+
screen: versions.screen,
|
|
327
|
+
xvfb: versions.xvfb,
|
|
262
328
|
|
|
263
329
|
// Platform
|
|
264
330
|
platform: versions.platform,
|
|
@@ -407,7 +473,7 @@ export function formatVersionMessage(versions) {
|
|
|
407
473
|
|
|
408
474
|
if (perlLines.length > 0) {
|
|
409
475
|
lines.push('');
|
|
410
|
-
lines.push('
|
|
476
|
+
lines.push('*🐪 Perl*');
|
|
411
477
|
lines.push(...perlLines);
|
|
412
478
|
}
|
|
413
479
|
|
|
@@ -435,6 +501,38 @@ export function formatVersionMessage(versions) {
|
|
|
435
501
|
lines.push(...leanLines);
|
|
436
502
|
}
|
|
437
503
|
|
|
504
|
+
// === Ruby ===
|
|
505
|
+
const rubyLines = [];
|
|
506
|
+
addVersionLine(rubyLines, 'Ruby', versions.ruby);
|
|
507
|
+
addVersionLine(rubyLines, 'Rbenv', versions.rbenv);
|
|
508
|
+
|
|
509
|
+
if (rubyLines.length > 0) {
|
|
510
|
+
lines.push('');
|
|
511
|
+
lines.push('*💎 Ruby*');
|
|
512
|
+
lines.push(...rubyLines);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// === Kotlin ===
|
|
516
|
+
if (versions.kotlin) {
|
|
517
|
+
lines.push('');
|
|
518
|
+
lines.push('*🟣 Kotlin*');
|
|
519
|
+
addVersionLine(lines, 'Kotlin', versions.kotlin);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// === Swift ===
|
|
523
|
+
if (versions.swift) {
|
|
524
|
+
lines.push('');
|
|
525
|
+
lines.push('*🦅 Swift*');
|
|
526
|
+
addVersionLine(lines, 'Swift', versions.swift);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// === R ===
|
|
530
|
+
if (versions.r) {
|
|
531
|
+
lines.push('');
|
|
532
|
+
lines.push('*📊 R*');
|
|
533
|
+
addVersionLine(lines, 'R', versions.r);
|
|
534
|
+
}
|
|
535
|
+
|
|
438
536
|
// === C/C++ ===
|
|
439
537
|
const cppLines = [];
|
|
440
538
|
addVersionLine(cppLines, 'GCC', versions.gcc);
|
|
@@ -444,20 +542,60 @@ export function formatVersionMessage(versions) {
|
|
|
444
542
|
addVersionLine(cppLines, 'LLD', versions.lld);
|
|
445
543
|
addVersionLine(cppLines, 'Make', versions.make);
|
|
446
544
|
addVersionLine(cppLines, 'CMake', versions.cmake);
|
|
545
|
+
addVersionLine(cppLines, 'NASM', versions.nasm);
|
|
546
|
+
addVersionLine(cppLines, 'FASM', versions.fasm);
|
|
447
547
|
|
|
448
548
|
if (cppLines.length > 0) {
|
|
449
549
|
lines.push('');
|
|
450
|
-
lines.push('*🔨 C/C
|
|
550
|
+
lines.push('*🔨 C/C++/Assembly*');
|
|
451
551
|
lines.push(...cppLines);
|
|
452
552
|
}
|
|
453
553
|
|
|
554
|
+
// === Browsers ===
|
|
555
|
+
const browserLines = [];
|
|
556
|
+
addVersionLine(browserLines, 'Google Chrome', versions.chrome);
|
|
557
|
+
addVersionLine(browserLines, 'Chromium', versions.chromium);
|
|
558
|
+
addVersionLine(browserLines, 'Firefox', versions.firefox);
|
|
559
|
+
addVersionLine(browserLines, 'Microsoft Edge', versions.msedge);
|
|
560
|
+
addVersionLine(browserLines, 'WebKit', versions.webkit);
|
|
561
|
+
|
|
562
|
+
if (browserLines.length > 0) {
|
|
563
|
+
lines.push('');
|
|
564
|
+
lines.push('*🌐 Browsers*');
|
|
565
|
+
lines.push(...browserLines);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// === Browser Automation ===
|
|
569
|
+
const browserAutoLines = [];
|
|
570
|
+
addVersionLine(browserAutoLines, 'Playwright', versions.playwright);
|
|
571
|
+
addVersionLine(browserAutoLines, 'Playwright Test', versions.playwrightTest);
|
|
572
|
+
addVersionLine(browserAutoLines, 'Playwright MCP', versions.playwrightMcp);
|
|
573
|
+
if (versions.playwrightMcpStatus) {
|
|
574
|
+
browserAutoLines.push(`• Playwright MCP in Claude Code: \`${versions.playwrightMcpStatus}\``);
|
|
575
|
+
} else if (versions.playwrightMcp) {
|
|
576
|
+
browserAutoLines.push('• Playwright MCP in Claude Code: `not configured`');
|
|
577
|
+
}
|
|
578
|
+
addVersionLine(browserAutoLines, 'Puppeteer Browsers', versions.puppeteerBrowsers);
|
|
579
|
+
|
|
580
|
+
if (browserAutoLines.length > 0) {
|
|
581
|
+
lines.push('');
|
|
582
|
+
lines.push('*🎭 Browser Automation*');
|
|
583
|
+
lines.push(...browserAutoLines);
|
|
584
|
+
}
|
|
585
|
+
|
|
454
586
|
// === Development Tools ===
|
|
455
587
|
const toolLines = [];
|
|
456
588
|
addVersionLine(toolLines, 'Git', versions.git);
|
|
457
589
|
addVersionLine(toolLines, 'GitHub CLI', versions.gh);
|
|
458
|
-
addVersionLine(toolLines, '
|
|
459
|
-
addVersionLine(toolLines, 'Playwright MCP', versions.playwrightMcp);
|
|
590
|
+
addVersionLine(toolLines, 'GitLab CLI', versions.glab);
|
|
460
591
|
addVersionLine(toolLines, 'Homebrew', versions.brew);
|
|
592
|
+
addVersionLine(toolLines, 'cURL', versions.curl);
|
|
593
|
+
addVersionLine(toolLines, 'Wget', versions.wget);
|
|
594
|
+
addVersionLine(toolLines, 'Zip', versions.zip);
|
|
595
|
+
addVersionLine(toolLines, 'Unzip', versions.unzip);
|
|
596
|
+
addVersionLine(toolLines, 'Expect', versions.expect);
|
|
597
|
+
addVersionLine(toolLines, 'Screen', versions.screen);
|
|
598
|
+
addVersionLine(toolLines, 'Xvfb', versions.xvfb);
|
|
461
599
|
|
|
462
600
|
if (toolLines.length > 0) {
|
|
463
601
|
lines.push('');
|