Skip to content

Commit 7a87e43

Browse files
author
Dietrich Gebert
committed
fix(hooks): write hook output synchronously so process.exit cannot drop it
1 parent ce8f0e9 commit 7a87e43

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

‎hooks/ponytail-runtime.js‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,16 @@ function cursorRuleNotice(rule) {
101101
'delete that rule so hooks.json can manage the level.';
102102
}
103103

104+
// Synchronous: every hook calls process.exit() right after this, and on macOS a pipe
105+
// write is async, so a pending write died with the process (#1043).
106+
function writeOut(text) {
107+
fs.writeSync(1, text);
108+
}
109+
104110
function writeHookOutput(event, mode, context = '') {
105111
if (isCopilot) {
106112
// Copilot reads additionalContext on SessionStart; ignores output elsewhere.
107-
process.stdout.write(JSON.stringify(
113+
writeOut(JSON.stringify(
108114
event === 'SessionStart' && context ? { additionalContext: context } : {}));
109115
return;
110116
}
@@ -122,7 +128,7 @@ function writeHookOutput(event, mode, context = '') {
122128
additionalContext: context,
123129
};
124130
}
125-
process.stdout.write(JSON.stringify(output));
131+
writeOut(JSON.stringify(output));
126132
return;
127133
}
128134
if (isQoder || isCodeBuddy) {
@@ -136,7 +142,7 @@ function writeHookOutput(event, mode, context = '') {
136142
additionalContext: context,
137143
};
138144
}
139-
process.stdout.write(JSON.stringify(output));
145+
writeOut(JSON.stringify(output));
140146
return;
141147
}
142148
if (isCursor) {
@@ -148,17 +154,17 @@ function writeHookOutput(event, mode, context = '') {
148154
if (!context) return;
149155
const output = { additional_context: context };
150156
if (event === 'UserPromptSubmit') output.continue = true;
151-
process.stdout.write(JSON.stringify(output));
157+
writeOut(JSON.stringify(output));
152158
return;
153159
}
154160
// Native Claude: SessionStart accepts raw stdout, but SubagentStart needs the
155161
// hookSpecificOutput JSON form or the context is dropped.
156162
if (event === 'SubagentStart') {
157-
process.stdout.write(JSON.stringify(
163+
writeOut(JSON.stringify(
158164
{ hookSpecificOutput: { hookEventName: event, additionalContext: context } }));
159165
return;
160166
}
161-
process.stdout.write(context);
167+
writeOut(context);
162168
}
163169

164170
module.exports = {

0 commit comments

Comments
 (0)