mirror of
https://github.com/affaan-m/ECC.git
synced 2026-11-07 16:45:53 +00:00
Claude Code passes a PostToolUse hook the result of its tool as tool_response. For Bash, that is an object with stdout, stderr, interrupted and isImage. Three hooks read only tool_output, the shape that the Cursor adapter builds, so under Claude Code they never saw any output:
- post-bash-pr-created never printed the PR URL and the review command after `gh pr create`;
- governance-capture never ran its secret detection on command output, such as that of `cat .env`;
- session-activity-tracker logged an empty output_summary for every tool.
Add getToolResult and getToolResultText to hook-input.js. They read tool_response first, then tool_output. Use them in the three hooks. governance-capture now also reads the adapter's { output }, where it used to accept only a string.
94 lines
3.0 KiB
JavaScript
94 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
const { StringDecoder } = require('string_decoder');
|
|
|
|
const DEFAULT_MAX_STDIN = 1024 * 1024;
|
|
|
|
function resolveMaxStdin(value, options = {}) {
|
|
const writeDiagnostic = options.writeDiagnostic || (() => {});
|
|
if (value === undefined || value === '') return DEFAULT_MAX_STDIN;
|
|
|
|
const parsed = Number(value);
|
|
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
writeDiagnostic(
|
|
'[Hook] ECC_HOOK_INPUT_MAX_BYTES must be a positive safe integer; using the 1 MiB default\n'
|
|
);
|
|
return DEFAULT_MAX_STDIN;
|
|
}
|
|
if (parsed > DEFAULT_MAX_STDIN) {
|
|
writeDiagnostic(
|
|
'[Hook] ECC_HOOK_INPUT_MAX_BYTES exceeds the 1 MiB safety maximum; clamping to 1 MiB\n'
|
|
);
|
|
return DEFAULT_MAX_STDIN;
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
function readStdinRaw(stream = process.stdin, options = {}) {
|
|
const maxStdin = options.maxStdin || DEFAULT_MAX_STDIN;
|
|
const decoder = new StringDecoder('utf8');
|
|
let raw = '';
|
|
let acceptedBytes = 0;
|
|
let truncated = options.truncated === true;
|
|
|
|
return new Promise(resolve => {
|
|
let settled = false;
|
|
stream.on('data', chunk => {
|
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
if (typeof options.onChunk === 'function') options.onChunk(buffer);
|
|
const remaining = Math.max(0, maxStdin - acceptedBytes);
|
|
const accepted = buffer.subarray(0, remaining);
|
|
if (accepted.length > 0) {
|
|
raw += decoder.write(accepted);
|
|
acceptedBytes += accepted.length;
|
|
}
|
|
if (accepted.length < buffer.length) truncated = true;
|
|
});
|
|
const finish = () => {
|
|
if (settled) return;
|
|
settled = true;
|
|
if (!truncated) raw += decoder.end();
|
|
resolve({ raw, truncated });
|
|
};
|
|
const finishIncomplete = () => {
|
|
if (settled) return;
|
|
truncated = true;
|
|
finish();
|
|
};
|
|
stream.once('end', finish);
|
|
// A transport error or premature close can leave a syntactically plausible
|
|
// prefix behind. Mark it incomplete so safety hooks remain fail closed.
|
|
stream.once('error', finishIncomplete);
|
|
stream.once('close', finishIncomplete);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* The result a PostToolUse payload carries for its tool. Claude Code sends it as
|
|
* `tool_response`, which for Bash is an object with `stdout` and `stderr`; the
|
|
* Cursor adapter and older callers send `tool_output`, a string or `{ output }`.
|
|
*/
|
|
function getToolResult(input) {
|
|
return input?.tool_response ?? input?.tool_output;
|
|
}
|
|
|
|
/**
|
|
* The text of that result: a string as it is, an adapted `output`, or the
|
|
* `stdout` and `stderr` of a Bash result. An empty string when it has no text.
|
|
*/
|
|
function getToolResultText(input) {
|
|
const result = getToolResult(input);
|
|
if (typeof result === 'string') return result;
|
|
if (!result || typeof result !== 'object') return '';
|
|
if (typeof result.output === 'string') return result.output;
|
|
return [result.stdout, result.stderr].filter(part => typeof part === 'string' && part !== '').join('\n');
|
|
}
|
|
|
|
module.exports = {
|
|
DEFAULT_MAX_STDIN,
|
|
getToolResult,
|
|
getToolResultText,
|
|
readStdinRaw,
|
|
resolveMaxStdin
|
|
};
|