MCPcopy
hub / github.com/claude-code-best/claude-code / executeInBackground

Function executeInBackground

src/utils/hooks.ts:185–266  ·  view source on GitHub ↗
({
  processId,
  hookId,
  shellCommand,
  asyncResponse,
  hookEvent,
  hookName,
  command,
  asyncRewake,
  pluginId,
}: {
  processId: string
  hookId: string
  shellCommand: ShellCommand
  asyncResponse: AsyncHookJSONOutput
  hookEvent: HookEvent | 'StatusLine' | 'FileSuggestion'
  hookName: string
  command: string
  asyncRewake?: boolean
  pluginId?: string
})

Source from the content-addressed store, hash-verified

183}
184
185function executeInBackground({
186 processId,
187 hookId,
188 shellCommand,
189 asyncResponse,
190 hookEvent,
191 hookName,
192 command,
193 asyncRewake,
194 pluginId,
195}: {
196 processId: string
197 hookId: string
198 shellCommand: ShellCommand
199 asyncResponse: AsyncHookJSONOutput
200 hookEvent: HookEvent | 'StatusLine' | 'FileSuggestion'
201 hookName: string
202 command: string
203 asyncRewake?: boolean
204 pluginId?: string
205}): boolean {
206 if (asyncRewake) {
207 // asyncRewake hooks bypass the registry entirely. On completion, if exit
208 // code 2 (blocking error), enqueue as a task-notification so it wakes the
209 // model via useQueueProcessor (idle) or gets injected mid-query via
210 // queued_command attachments (busy).
211 //
212 // NOTE: We deliberately do NOT call shellCommand.background() here, because
213 // it calls taskOutput.spillToDisk() which breaks in-memory stdout/stderr
214 // capture (getStderr() returns '' in disk mode). The StreamWrappers stay
215 // attached and pipe data into the in-memory TaskOutput buffers. The abort
216 // handler already no-ops on 'interrupt' reason (user submitted a new
217 // message), so the hook survives new prompts. A hard cancel (Escape) WILL
218 // kill the hook via the abort handler, which is the desired behavior.
219 void shellCommand.result.then(async result => {
220 // result resolves on 'exit', but stdio 'data' events may still be
221 // pending. Yield to I/O so the StreamWrapper data handlers drain into
222 // TaskOutput before we read it.
223 await new Promise(resolve => setImmediate(resolve))
224 const stdout = await shellCommand.taskOutput.getStdout()
225 const stderr = shellCommand.taskOutput.getStderr()
226 shellCommand.cleanup()
227 emitHookResponse({
228 hookId,
229 hookName,
230 hookEvent,
231 output: stdout + stderr,
232 stdout,
233 stderr,
234 exitCode: result.code,
235 outcome: result.code === 0 ? 'success' : 'error',
236 })
237 if (result.code === 2) {
238 enqueuePendingNotification({
239 value: wrapInSystemReminder(
240 `Stop hook blocking error from command "${hookName}": ${stderr || stdout}`,
241 ),
242 mode: 'task-notification',

Callers 1

execCommandHookFunction · 0.85

Calls 8

emitHookResponseFunction · 0.85
wrapInSystemReminderFunction · 0.85
registerPendingAsyncHookFunction · 0.85
getStdoutMethod · 0.80
getStderrMethod · 0.80
cleanupMethod · 0.45
backgroundMethod · 0.45

Tested by

no test coverage detected