@link-assistant/hive-mind 1.47.1 → 1.47.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/github.lib.mjs +2 -2
- package/src/hive.mjs +3 -3
- package/src/lib.mjs +92 -15
- package/src/solve.mjs +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.47.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7afe67b: Fix ghPrView false positive on "Could not resolve" in PR body causing "Failed to get PR details" error on fork PRs, and add stdio log interceptor for terminal/log output parity
|
|
8
|
+
|
|
3
9
|
## 1.47.1
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
package/src/github.lib.mjs
CHANGED
|
@@ -1328,7 +1328,7 @@ export async function ghPrView({ prNumber, owner, repo, jsonFields = 'headRefNam
|
|
|
1328
1328
|
const stderr = prResult.stderr ? prResult.stderr.toString() : '';
|
|
1329
1329
|
const code = prResult.code || 0;
|
|
1330
1330
|
let data = null;
|
|
1331
|
-
if (code === 0 && stdout && !
|
|
1331
|
+
if (code === 0 && stdout && !(stderr && stderr.includes('Could not resolve'))) {
|
|
1332
1332
|
try {
|
|
1333
1333
|
data = JSON.parse(stdout);
|
|
1334
1334
|
} catch {
|
|
@@ -1368,7 +1368,7 @@ export async function ghIssueView({ issueNumber, owner, repo, jsonFields = 'numb
|
|
|
1368
1368
|
const stderr = issueResult.stderr ? issueResult.stderr.toString() : '';
|
|
1369
1369
|
const code = issueResult.code || 0;
|
|
1370
1370
|
let data = null;
|
|
1371
|
-
if (code === 0 && stdout && !
|
|
1371
|
+
if (code === 0 && stdout && !(stderr && stderr.includes('Could not resolve'))) {
|
|
1372
1372
|
try {
|
|
1373
1373
|
data = JSON.parse(stdout);
|
|
1374
1374
|
} catch {
|
package/src/hive.mjs
CHANGED
|
@@ -95,7 +95,7 @@ if (isDirectExecution) {
|
|
|
95
95
|
const fs = (await withTimeout(use('fs'), 30000, 'loading fs')).promises;
|
|
96
96
|
// Import shared library functions
|
|
97
97
|
const lib = await import('./lib.mjs');
|
|
98
|
-
const { log, setLogFile, getAbsoluteLogPath, formatTimestamp, cleanErrorMessage, cleanupTempDirectories, setupVerboseLogInterceptor } = lib;
|
|
98
|
+
const { log, setLogFile, getAbsoluteLogPath, formatTimestamp, cleanErrorMessage, cleanupTempDirectories, setupVerboseLogInterceptor, setupStdioLogInterceptor } = lib;
|
|
99
99
|
const yargsConfigLib = await import('./hive.config.lib.mjs');
|
|
100
100
|
const { createYargsConfig } = yargsConfigLib;
|
|
101
101
|
const claudeLib = await import('./claude.lib.mjs');
|
|
@@ -309,8 +309,8 @@ if (isDirectExecution) {
|
|
|
309
309
|
// Set global verbose mode
|
|
310
310
|
global.verboseMode = argv.verbose;
|
|
311
311
|
|
|
312
|
-
// Issue #1466:
|
|
313
|
-
|
|
312
|
+
setupVerboseLogInterceptor(); // Issue #1466: capture [VERBOSE] output in log files
|
|
313
|
+
setupStdioLogInterceptor(); // Issue #1549: capture ALL terminal output in log file
|
|
314
314
|
|
|
315
315
|
// Use the universal GitHub URL parser
|
|
316
316
|
if (githubUrl) {
|
package/src/lib.mjs
CHANGED
|
@@ -99,18 +99,24 @@ export const log = async (message, options = {}) => {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// Write to console based on level
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
// Set guard flag to prevent stdio interceptor from double-logging (issue #1549)
|
|
103
|
+
_writingFromLog = true;
|
|
104
|
+
try {
|
|
105
|
+
switch (level) {
|
|
106
|
+
case 'error':
|
|
107
|
+
console.error(message);
|
|
108
|
+
break;
|
|
109
|
+
case 'warning':
|
|
110
|
+
case 'warn':
|
|
111
|
+
console.warn(message);
|
|
112
|
+
break;
|
|
113
|
+
case 'info':
|
|
114
|
+
default:
|
|
115
|
+
console.log(message);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
_writingFromLog = false;
|
|
114
120
|
}
|
|
115
121
|
};
|
|
116
122
|
|
|
@@ -134,20 +140,90 @@ export const setupVerboseLogInterceptor = () => {
|
|
|
134
140
|
|
|
135
141
|
const originalConsoleLog = console.log.bind(console);
|
|
136
142
|
console.log = (...args) => {
|
|
137
|
-
// Always call original console.log first
|
|
138
|
-
originalConsoleLog(...args);
|
|
139
|
-
|
|
140
143
|
// If a log file is set and the message looks like a [VERBOSE] log, append to file
|
|
144
|
+
// and set guard flag to prevent stdio interceptor from double-logging (issue #1549)
|
|
141
145
|
if (logFile && args.length > 0) {
|
|
142
146
|
const firstArg = String(args[0]);
|
|
143
147
|
if (firstArg.includes('[VERBOSE]')) {
|
|
144
148
|
const message = args.map(a => String(a)).join(' ');
|
|
145
149
|
const logMessage = `[${new Date().toISOString()}] [VERBOSE] ${message}`;
|
|
150
|
+
_writingFromLog = true;
|
|
151
|
+
fs.appendFile(logFile, logMessage + '\n').catch(() => {
|
|
152
|
+
// Silent fail to avoid infinite loops
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Always call original console.log (with guard flag set if [VERBOSE])
|
|
158
|
+
try {
|
|
159
|
+
originalConsoleLog(...args);
|
|
160
|
+
} finally {
|
|
161
|
+
_writingFromLog = false;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Issue #1549: Intercept process.stdout.write and process.stderr.write to capture
|
|
168
|
+
* ALL terminal output in the log file, ensuring 100% parity between terminal and log.
|
|
169
|
+
*
|
|
170
|
+
* The command-stream library uses process.stdout.write/process.stderr.write directly
|
|
171
|
+
* when mirror:true (the default). console.log/console.error also end up calling these.
|
|
172
|
+
* By intercepting at the write() level, we capture everything regardless of source:
|
|
173
|
+
* - command-stream mirror output (e.g., gh CLI JSON responses)
|
|
174
|
+
* - console.log() / console.error() calls
|
|
175
|
+
* - process.stdout.write() / process.stderr.write() direct calls
|
|
176
|
+
*
|
|
177
|
+
* To avoid double-logging (since the log() function already writes to the log file AND
|
|
178
|
+
* calls console.log which calls process.stdout.write), we use a guard flag
|
|
179
|
+
* `_writingFromLog` to skip interception when the write originates from log().
|
|
180
|
+
*
|
|
181
|
+
* This ensures the log file is a complete record of all terminal output.
|
|
182
|
+
* Call this once after setLogFile() to enable the interceptor.
|
|
183
|
+
*/
|
|
184
|
+
let stdioInterceptorInstalled = false;
|
|
185
|
+
let _writingFromLog = false; // Guard flag to prevent double-logging from log()
|
|
186
|
+
export const setupStdioLogInterceptor = () => {
|
|
187
|
+
if (stdioInterceptorInstalled) return;
|
|
188
|
+
stdioInterceptorInstalled = true;
|
|
189
|
+
|
|
190
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
191
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
192
|
+
|
|
193
|
+
process.stdout.write = (chunk, encoding, callback) => {
|
|
194
|
+
// Always write to terminal first
|
|
195
|
+
const result = originalStdoutWrite(chunk, encoding, callback);
|
|
196
|
+
|
|
197
|
+
// Also append to log file if set, but skip if this write originated from log()
|
|
198
|
+
if (logFile && !_writingFromLog) {
|
|
199
|
+
const text = typeof chunk === 'string' ? chunk : chunk.toString(encoding || 'utf8');
|
|
200
|
+
if (text.trim()) {
|
|
201
|
+
const logMessage = `[${new Date().toISOString()}] [STDOUT] ${text.replace(/\n$/, '')}`;
|
|
202
|
+
fs.appendFile(logFile, logMessage + '\n').catch(() => {
|
|
203
|
+
// Silent fail to avoid infinite loops
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
process.stderr.write = (chunk, encoding, callback) => {
|
|
212
|
+
// Always write to terminal first
|
|
213
|
+
const result = originalStderrWrite(chunk, encoding, callback);
|
|
214
|
+
|
|
215
|
+
// Also append to log file if set, but skip if this write originated from log()
|
|
216
|
+
if (logFile && !_writingFromLog) {
|
|
217
|
+
const text = typeof chunk === 'string' ? chunk : chunk.toString(encoding || 'utf8');
|
|
218
|
+
if (text.trim()) {
|
|
219
|
+
const logMessage = `[${new Date().toISOString()}] [STDERR] ${text.replace(/\n$/, '')}`;
|
|
146
220
|
fs.appendFile(logFile, logMessage + '\n').catch(() => {
|
|
147
221
|
// Silent fail to avoid infinite loops
|
|
148
222
|
});
|
|
149
223
|
}
|
|
150
224
|
}
|
|
225
|
+
|
|
226
|
+
return result;
|
|
151
227
|
};
|
|
152
228
|
};
|
|
153
229
|
|
|
@@ -611,6 +687,7 @@ export default {
|
|
|
611
687
|
displayFormattedError,
|
|
612
688
|
cleanupTempDirectories,
|
|
613
689
|
setupVerboseLogInterceptor,
|
|
690
|
+
setupStdioLogInterceptor,
|
|
614
691
|
};
|
|
615
692
|
|
|
616
693
|
/**
|
package/src/solve.mjs
CHANGED
|
@@ -48,7 +48,7 @@ const fs = (await use('fs')).promises;
|
|
|
48
48
|
const crypto = (await use('crypto')).default;
|
|
49
49
|
const memoryCheck = await import('./memory-check.mjs');
|
|
50
50
|
const lib = await import('./lib.mjs');
|
|
51
|
-
const { log, setLogFile, getLogFile, getAbsoluteLogPath, cleanErrorMessage, formatAligned, getVersionInfo, setupVerboseLogInterceptor } = lib;
|
|
51
|
+
const { log, setLogFile, getLogFile, getAbsoluteLogPath, cleanErrorMessage, formatAligned, getVersionInfo, setupVerboseLogInterceptor, setupStdioLogInterceptor } = lib;
|
|
52
52
|
const githubLib = await import('./github.lib.mjs');
|
|
53
53
|
const { sanitizeLogContent, attachLogToGitHub, getToolDisplayName } = githubLib;
|
|
54
54
|
const validation = await import('./solve.validation.lib.mjs');
|
|
@@ -111,8 +111,8 @@ try {
|
|
|
111
111
|
}
|
|
112
112
|
global.verboseMode = argv.verbose;
|
|
113
113
|
|
|
114
|
-
// Issue #1466:
|
|
115
|
-
|
|
114
|
+
setupVerboseLogInterceptor(); // Issue #1466: capture [VERBOSE] output in log files
|
|
115
|
+
setupStdioLogInterceptor(); // Issue #1549: capture ALL terminal output in log file
|
|
116
116
|
|
|
117
117
|
// Early logs go to cwd; custom log dir takes effect after argv is parsed
|
|
118
118
|
|